Ruby on Rails実践講座 - Twitterの簡易クローンを開発する
Twitterの簡易クローンを作りながら、Railsの基本を学習します。
開始する
$ cd ~/workspace
$ rails new tsubuyaki $ cd tsubuyaki | 26 | 26 | # Spring speeds up development by keeping your application running in the background. Read more: https://github.com/rails/spring |
| 27 | 27 | gem 'spring', group: :development |
| 28 | 28 | |
| 29 | gem 'sorcery' | |
| 30 | gem 'haml-rails' | |
| 31 | gem 'bootstrap-sass' | |
| 32 | ||
| 29 | 33 | # Use ActiveModel has_secure_password |
| 30 | 34 | # gem 'bcrypt', '~> 3.1.7' |
| 31 | 35 | |
| @@ -26,6 +26,10 @@ gem 'sdoc', '~> 0.4.0', group: :doc |
$ bundle install | 19 | 19 | # The default locale is :en and all translations from config/locales/*.rb,yml are auto loaded. |
| 20 | 20 | # config.i18n.load_path += Dir[Rails.root.join('my', 'locales', '*.{rb,yml}').to_s] |
| 21 | 21 | # config.i18n.default_locale = :de |
| 22 | ||
| 23 | config.sass.preferred_syntax = :sass | |
| 22 | 24 | end |
| 23 | 25 | end |
| @@ -19,5 +19,7 @@ class Application < Rails::Application |
$ rails g sorcery:install $ rails g scaffold user -s --no-stylesheets --skip-migration $ rails g migration add_name_and_screen_name_and_bio_to_users name:string screen_name:string bio:string $ rake db:migrate $ rails generate controller registrations new | 13 | 13 | *= require_tree . |
| 14 | 14 | *= require_self |
| 15 | 15 | */ |
| 16 | ||
| 17 | @import bootstrap | |
| @@ -13,3 +13,5 @@ |
| 6 | 6 | <%= javascript_include_tag 'application', 'data-turbolinks-track' => true %> |
| 7 | 7 | <%= csrf_meta_tags %> |
| 8 | 8 | </head> |
| 9 | <body> | |
| 9 | <body id="application"> | |
| 10 | 10 | |
| 11 | <%= yield %> | |
| 11 | <div> | |
| 12 | <%= yield %> | |
| 13 | </div> | |
| 12 | 14 | |
| 13 | 15 | </body> |
| 14 | 16 | </html> |
| @@ -6,9 +6,11 @@ |
| 1 | 1 | Rails.application.routes.draw do |
| 2 | get 'registrations/new' | |
| 2 | resource :registrations, only: [:new, :create] | |
| 3 | 3 | |
| 4 | 4 | # The priority is based upon order of creation: first created -> highest priority. |
| 5 | 5 | # See how all your routes lay out with "rake routes". |
| @@ -1,5 +1,5 @@ |
| 1 | 1 | class User < ActiveRecord::Base |
| 2 | 2 | authenticates_with_sorcery! |
| 3 | ||
| 4 | validates :name, presence: true, uniqueness: { case_sensitive: false }, format: { with: /\A[a-z][a-z0-9]+\z/ }, length: { in: 4..24 } | |
| 5 | validates :screen_name, length: { maximum: 140 } | |
| 6 | validates :bio, length: { maximum: 200 } | |
| 7 | validates :email, presence: true, uniqueness: { case_sensitive: false } | |
| 8 | validates :password, confirmation: true, length: { in: 6..24 }, if: :password | |
| 9 | validates :password_confirmation, presence: true, if: :password | |
| 3 | 10 | end |
| @@ -1,3 +1,10 @@ |
| 1 | 1 | class RegistrationsController < ApplicationController |
| 2 | 2 | def new |
| 3 | @user = User.new | |
| 3 | 4 | end |
| 4 | 5 | end |
| @@ -1,4 +1,5 @@ |
| 1 | 1 | %h1 Registrations#new |
| 2 | 2 | %p Find me in app/views/registrations/new.html.haml |
| 3 | ||
| 4 | = form_for @user, url: registrations_path, method: :post do |f| | |
| 5 | = f.label :name | |
| 6 | = f.text_field :name | |
| 7 | = f.label :email | |
| 8 | = f.text_field :email | |
| 9 | = f.label :password | |
| 10 | = f.password_field :password | |
| 11 | = f.label :password_confirmation | |
| 12 | = f.password_field :password_confirmation | |
| 13 | = f.submit | |
| @@ -1,2 +1,13 @@ |
| 1 | 1 | Rails.application.routes.draw do |
| 2 | 2 | resource :registrations, only: [:new, :create] |
| 3 | 3 | |
| 4 | root to: 'registrations#new' | |
| 5 | ||
| 4 | 6 | # The priority is based upon order of creation: first created -> highest priority. |
| 5 | 7 | # See how all your routes lay out with "rake routes". |
| 6 | 8 | |
| @@ -1,6 +1,8 @@ |
| 2 | 2 | def new |
| 3 | 3 | @user = User.new |
| 4 | 4 | end |
| 5 | ||
| 6 | def create | |
| 7 | @user = User.new(params_user) | |
| 8 | ||
| 9 | if @user.save | |
| 10 | redirect_to root_url | |
| 11 | else | |
| 12 | render :new | |
| 13 | end | |
| 14 | end | |
| 15 | ||
| 16 | private | |
| 17 | ||
| 18 | def params_user | |
| 19 | params.require(:user).permit(:name, :email, :password, :password_confirmation) | |
| 20 | end | |
| 5 | 21 | end |
| @@ -2,4 +2,20 @@ class RegistrationsController < ApplicationController |
| 1 | 1 | %h1 Registrations#new |
| 2 | 2 | %p Find me in app/views/registrations/new.html.haml |
| 3 | 3 | |
| 4 | - @user.errors.each do |attr, message| | |
| 5 | .alert.alert-danger= message | |
| 6 | ||
| 4 | 7 | = form_for @user, url: registrations_path, method: :post do |f| |
| 5 | 8 | = f.label :name |
| 6 | 9 | = f.text_field :name |
| @@ -1,6 +1,9 @@ |
| 18 | 18 | |
| 19 | 19 | # The default locale is :en and all translations from config/locales/*.rb,yml are auto loaded. |
| 20 | 20 | # config.i18n.load_path += Dir[Rails.root.join('my', 'locales', '*.{rb,yml}').to_s] |
| 21 | # config.i18n.default_locale = :de | |
| 21 | config.i18n.default_locale = :ja | |
| 22 | 22 | |
| 23 | 23 | config.sass.preferred_syntax = :sass |
| 24 | 24 | end |
| @@ -18,7 +18,7 @@ class Application < Rails::Application |
$ rails s | 2 | 2 | activerecord: |
| 3 | 3 | errors: |
| 4 | 4 | models: |
| 5 | user: | |
| 6 | attributes: | |
| 7 | name: | |
| 8 | blank: ユーザー名は空ではいけません | |
| 9 | taken: ユーザー名は既に利用されています | |
| 10 | invalid: ユーザー名には半角英数字のみ利用できます | |
| 11 | too_long: ユーザー名は24文字まで利用できます | |
| 12 | too_short: ユーザー名は最短で4文字必要です | |
| 13 | email: | |
| 14 | blank: メールアドレスが空です | |
| 15 | taken: メールアドレスが既に使用済みです | |
| 16 | password: | |
| 17 | blank: パスワードが空です | |
| 18 | too_long: パスワードは24文字以内で利用できます | |
| 19 | too_short: パスワードは6文字以上で利用できます | |
| 20 | password_confirmation: | |
| 21 | blank: 確認用パスワードが空です | |
| 22 | confirmation: 確認用パスワードが一致しません | |
| 23 | bio: | |
| 24 | too_long: プロフィールは200文字以内でなければなりません | |
| @@ -2,3 +2,23 @@ ja: |
| 1 | !!! | |
| 2 | %html | |
| 3 | %head | |
| 4 | %title Tsubuyaki22Nov | |
| 5 | = stylesheet_link_tag 'application', media: 'all', 'data-turbolinks-track' => true | |
| 6 | = javascript_include_tag 'application', 'data-turbolinks-track' => true | |
| 7 | = csrf_meta_tags | |
| 8 | %body#registrations | |
| 9 | = yield | |
| @@ -0,0 +1,9 @@ |
| 15 | 15 | */ |
| 16 | 16 | |
| 17 | 17 | @import bootstrap |
| 18 | ||
| 19 | html, body | |
| 20 | width: 100% | |
| 21 | height: 100% | |
| 22 | ||
| 23 | .clear | |
| 24 | clear: both | |
| @@ -15,3 +15,10 @@ |
| 1 | 1 | // Place all the styles related to the registrations controller here. |
| 2 | 2 | // They will automatically be included in application.css. |
| 3 | // You can use Sass here: http://sass-lang.com/ | |
| 3 | // You can use Sass (SCSS) here: http://sass-lang.com/ | |
| 4 | ||
| 5 | #registrations | |
| 6 | background-image: asset-url("back.jpg") | |
| 7 | background-position: center | |
| 8 | background-size: cover | |
| 9 | background-color: transparent | |
| 10 | background-repeat: no-repeat | |
| 11 | background-color: #050911 | |
| 12 | #registrations-new | |
| 13 | width: 100% | |
| 14 | height: 100% | |
| 15 | .left-content | |
| 16 | margin-top: 45px | |
| 17 | padding: 0px 45px | |
| 18 | color: #fff | |
| 19 | h1 | |
| 20 | font-size: 50px | |
| 21 | margin: 0px | |
| 22 | margin-top: 10px | |
| 23 | .ex | |
| 24 | font-size: 18px | |
| 25 | margin: 45px 0px | |
| 26 | .right-content | |
| 27 | padding: 0px 45px | |
| 28 | form | |
| 29 | margin: 45px 0px | |
| 30 | padding: 30px 35px | |
| 31 | border: 1px solid #fff | |
| 32 | border-radius: 6px | |
| 33 | background-color: rgba(0, 0, 0, 0.3) | |
| 34 | color: #fff | |
| 35 | h2 | |
| 36 | margin: 0px 0px 20px 0px | |
| 37 | border-bottom: 1px solid rgba(255, 255, 255, 0.5) | |
| 38 | padding-bottom: 10px | |
| 39 | font-size: 20px | |
| 40 | .control-label | |
| 41 | font-weight: 400 | |
| 42 | .btn | |
| 43 | width: 100% | |
| 44 | padding: 12px | |
| 45 | margin-top: 10px | |
| 46 | margin-bottom: 10px | |
| 47 | font-size: 16px | |
| @@ -1,3 +1,47 @@ |
| 1 | %h1 Registrations#new | |
| 2 | %p Find me in app/views/registrations/new.html.haml | |
| 3 | ||
| 4 | - @user.errors.each do |attr, message| | |
| 5 | .alert.alert-danger= message | |
| 6 | ||
| 7 | = form_for @user, url: registrations_path, method: :post do |f| | |
| 8 | = f.label :name | |
| 9 | = f.text_field :name | |
| 10 | = f.label :email | |
| 11 | = f.text_field :email | |
| 12 | = f.label :password | |
| 13 | = f.password_field :password | |
| 14 | = f.label :password_confirmation | |
| 15 | = f.password_field :password_confirmation | |
| 16 | = f.submit | |
| 1 | #registrations-new | |
| 2 | .col-xs-6.left-content | |
| 3 | %h1 #Tsubuyaki | |
| 4 | %p.ex | |
| 5 | このサイトは、シラバスのRails学習コースの完成形サンプルアプリです。Microsoft Azure上で運用されています。 | |
| 6 | %p.by | |
| 7 | 開発者: @ohataken デザイン: @keito0904 企画: @saiwaki14 | |
| 8 | .col-xs-6.right-content | |
| 9 | = form_for @user, url: registrations_path, method: :post do |f| | |
| 10 | %h2 | |
| 11 | 会員登録する | |
| 12 | - @user.errors.each do |attr, message| | |
| 13 | .alert.alert-danger= message | |
| 14 | .form-group | |
| 15 | = f.label :name, "ユーザーID: @cyllabus_jp", class: "control-label" | |
| 16 | = f.text_field :name, class: "form-control" | |
| 17 | .form-group | |
| 18 | = f.label :email,"メールアドレス: hello@cyllabus.jp", class: "control-label" | |
| 19 | = f.text_field :email, class: "form-control" | |
| 20 | .form-group | |
| 21 | = f.label :password, "パスワード: 6文字以上", class: "control-label" | |
| 22 | = f.password_field :password, class: "form-control" | |
| 23 | .form-group | |
| 24 | = f.label :password_confirmation, "確認用パスワード: 6文字以上", class: "control-label" | |
| 25 | = f.password_field :password_confirmation, class: "form-control" | |
| 26 | = f.submit "登録する", class: "btn btn-primary" | |
| 27 | .clear | |
| @@ -1,16 +1,27 @@ |
$ rails g controller sessions new | 1 | 1 | Rails.application.routes.draw do |
| 2 | get 'sessions/new' | |
| 3 | ||
| 4 | 2 | resource :registrations, only: [:new, :create] |
| 3 | resource :sessions, only: [:new, :create, :destroy] | |
| 5 | 4 | |
| 6 | 5 | root to: 'registrations#new' |
| 7 | 6 | |
| @@ -1,7 +1,6 @@ |
| 45 | 45 | margin-top: 10px |
| 46 | 46 | margin-bottom: 10px |
| 47 | 47 | font-size: 16px |
| 48 | .login | |
| 49 | color: #fff | |
| 50 | text-decoration: underline | |
| @@ -45,3 +45,6 @@ |
| 24 | 24 | = f.label :password_confirmation, "確認用パスワード: 6文字以上", class: "control-label" |
| 25 | 25 | = f.password_field :password_confirmation, class: "form-control" |
| 26 | 26 | = f.submit "登録する", class: "btn btn-primary" |
| 27 | = link_to "ログインする", new_sessions_path, class: "login pull-right" | |
| 27 | 28 | .clear |
| @@ -24,4 +24,5 @@ |
| 1 | 1 | class SessionsController < ApplicationController |
| 2 | 2 | def new |
| 3 | @user = User.new | |
| 3 | 4 | end |
| 4 | 5 | end |
| @@ -1,4 +1,5 @@ |
| 1 | 1 | %h1 Sessions#new |
| 2 | 2 | %p Find me in app/views/sessions/new.html.haml |
| 3 | ||
| 4 | = form_for @user, url: sessions_path, method: :post do |f| | |
| 5 | = f.label :email | |
| 6 | = f.text_field :email | |
| 7 | = f.label :password | |
| 8 | = f.password_field :password | |
| 9 | = f.submit | |
| @@ -1,2 +1,9 @@ |
| 2 | 2 | def new |
| 3 | 3 | @user = User.new |
| 4 | 4 | end |
| 5 | ||
| 6 | def create | |
| 7 | email = params_user[:email] | |
| 8 | password = params_user[:password] | |
| 9 | ||
| 10 | if login(email, password) | |
| 11 | redirect_to root_url, notice: "successfully logged in." | |
| 12 | else | |
| 13 | @user = User.new(email: email) | |
| 14 | render :new | |
| 15 | end | |
| 16 | end | |
| 17 | ||
| 18 | private | |
| 19 | ||
| 20 | def params_user | |
| 21 | params.require(:user).permit(:email, :password) | |
| 22 | end | |
| 5 | 23 | end |
| @@ -2,4 +2,22 @@ class SessionsController < ApplicationController |
| 1 | 1 | %h1 Sessions#new |
| 2 | 2 | %p Find me in app/views/sessions/new.html.haml |
| 3 | 3 | |
| 4 | - if action_name == "create" | |
| 5 | .alert.alert-danger | |
| 6 | メールアドレスまたはパスワードが正しくありません。 | |
| 7 | ||
| 4 | 8 | = form_for @user, url: sessions_path, method: :post do |f| |
| 5 | 9 | = f.label :email |
| 6 | 10 | = f.text_field :email |
| @@ -1,6 +1,10 @@ |
| 1 | !!! | |
| 2 | %html | |
| 3 | %head | |
| 4 | %title Tsubuyaki18Oct | |
| 5 | = stylesheet_link_tag 'application', media: 'all', 'data-turbolinks-track' => true | |
| 6 | = javascript_include_tag 'application', 'data-turbolinks-track' => true | |
| 7 | = csrf_meta_tags | |
| 8 | %body#sessions | |
| 9 | = yield | |
| @@ -0,0 +1,9 @@ |
| 1 | 1 | // Place all the styles related to the sessions controller here. |
| 2 | 2 | // They will automatically be included in application.css. |
| 3 | // You can use Sass here: http://sass-lang.com/ | |
| 3 | // You can use Sass (SCSS) here: http://sass-lang.com/ | |
| 4 | ||
| 5 | #sessions | |
| 6 | display: table | |
| 7 | widows: 100% | |
| 8 | height: 100% | |
| 9 | background-color: #f5f5f5 | |
| 10 | #sessions-new | |
| 11 | display: table-cell | |
| 12 | vertical-align: middle | |
| 13 | .login-content | |
| 14 | margin: 0 auto | |
| 15 | float: none | |
| 16 | form | |
| 17 | border: 1px solid #ddd | |
| 18 | border-bottom-width: 3px | |
| 19 | border-radius: 6px | |
| 20 | background-color: #fff | |
| 21 | h2 | |
| 22 | margin: 0px | |
| 23 | padding: 12px | |
| 24 | text-align: center | |
| 25 | border-bottom: 2px solid #ddd | |
| 26 | font-size: 18px | |
| 27 | font-weight: 600 | |
| 28 | .form-body | |
| 29 | padding: 25px 30px | |
| 30 | .control-label | |
| 31 | font-weight: 400 | |
| 32 | .btn | |
| 33 | width: 100% | |
| 34 | margin: 10px 0px | |
| 35 | .signup | |
| 36 | text-decoration: underline | |
| @@ -1,3 +1,36 @@ |
| 1 | %h1 Sessions#new | |
| 2 | %p Find me in app/views/sessions/new.html.haml | |
| 3 | ||
| 4 | - if action_name == "create" | |
| 5 | .alert.alert-danger | |
| 6 | メールアドレスまたはパスワードが正しくありません。 | |
| 7 | ||
| 8 | = form_for @user, url: sessions_path, method: :post do |f| | |
| 9 | = f.label :email | |
| 10 | = f.text_field :email | |
| 11 | = f.label :password | |
| 12 | = f.password_field :password | |
| 13 | = f.submit | |
| 1 | #sessions-new | |
| 2 | .col-xs-5.login-content | |
| 3 | - if action_name == "create" | |
| 4 | .alert.alert-danger | |
| 5 | メールアドレスまたはパスワードが正しくありません。 | |
| 6 | = form_for @user, url: sessions_path, method: :post do |f| | |
| 7 | %h2 | |
| 8 | ログイン | |
| 9 | .form-body | |
| 10 | .form-group | |
| 11 | = f.label :email,"メールアドレス:hello@cyllabus.jp", class: "control-label" | |
| 12 | = f.text_field :email, class: "form-control" | |
| 13 | .form-group | |
| 14 | = f.label :password, "パスワード:6文字以上", class: "control-label" | |
| 15 | = f.password_field :password, class: "form-control" | |
| 16 | = f.submit "ログイン", class: "btn btn-success" | |
| 17 | = link_to root_path, class: "pull-right signup" do | |
| 18 | 会員登録する | |
| 19 | .clear | |
| @@ -1,13 +1,19 @@ |
$ rails g scaffold user -s --no-stylesheets --skip-migration resource :registrations, only: [:new, :create]
resource :sessions, only: [:new, :create, :destroy]
resources :users, only: [:index, :show] | 1 | 1 | class UsersController < ApplicationController |
| 2 | before_action :set_user, only: [:show, :edit, :update, :destroy] | |
| 3 | ||
| 4 | 2 | # GET /users |
| 5 | 3 | # GET /users.json |
| 6 | 4 | def index |
| @@ -1,6 +1,4 @@ | ||
| 10 | 8 | # GET /users/1 |
| 11 | 9 | # GET /users/1.json |
| 12 | 10 | def show |
| 11 | @user = User.find(params[:id]) | |
| 13 | 12 | end |
| 14 | 13 | end |
| @@ -10,5 +8,6 @@ def index |
%h1 Listing users
%table
%tr
%th
- @users.each do |user|
%tr
%td= link_to user.name, user | 1 | 1 | %p#notice= notice |
| 2 | 2 | |
| 3 | ||
| 4 | = link_to 'Edit', edit_user_path(@user) | |
| 5 | \| | |
| 6 | 3 | = link_to 'Back', users_path |
| @@ -1,6 +1,3 @@ |
| 22 | 22 | |
| 23 | 23 | .clear |
| 24 | 24 | clear: both |
| 25 | ||
| 26 | #users-content | |
| 27 | float: none | |
| 28 | margin: 0 auto 35px | |
| 29 | padding: 0px | |
| 30 | background-color: #fff | |
| 31 | border: 1px solid #ddd | |
| 32 | border-radius: 6px | |
| 33 | border-bottom-width: 3px | |
| 34 | .list-group | |
| 35 | margin-bottom: -1px | |
| 36 | .list-group-item | |
| 37 | border: none | |
| 38 | border-bottom: 1px solid #ddd | |
| 39 | margin-bottom: 0px | |
| 40 | &:last-child | |
| 41 | .user | |
| 42 | .user-name | |
| 43 | a | |
| 44 | color: #555 | |
| 45 | .user-id | |
| 46 | font-size: 14px | |
| 47 | color: #aaa | |
| 48 | font-weight: 400 | |
| 49 | .time | |
| 50 | font-size: 12px | |
| 51 | color: #aaa | |
| 52 | font-weight: 400 | |
| 53 | .user-list | |
| 54 | border-bottom-width: 2px | |
| 55 | h2 | |
| 56 | font-size: 20px | |
| 57 | margin: 0px | |
| @@ -22,3 +22,36 @@ html, body |
| 1 | %h1 Listing users | |
| 2 | ||
| 3 | %table | |
| 4 | %tr | |
| 5 | %th | |
| 6 | ||
| 7 | - @users.each do |user| | |
| 8 | %tr | |
| 9 | %td= link_to 'Show', user | |
| 1 | .col-xs-8#users-content | |
| 2 | .list-group | |
| 3 | .list-group-item.user-list | |
| 4 | %h2 ユーザー一覧 | |
| 5 | = div_for @users, class: "list-group-item" do |u| | |
| 6 | %h4.user | |
| 7 | %span.user-name | |
| 8 | = link_to u.name, user_path(u) | |
| 9 | %span.user-id | |
| 10 | @#{u.name} | |
| 11 | .bio | |
| 12 | = u.bio | |
| @@ -1,9 +1,12 @@ |
| 1 | %p#notice= notice | |
| 2 | ||
| 3 | = link_to 'Back', users_path | |
| 1 | .col-xs-8#users-content | |
| 2 | .user-info | |
| 3 | %span.user-name | |
| 4 | = @user.name | |
| 5 | %span.user-id | |
| 6 | @#{@user.name} | |
| 7 | .bio | |
| 8 | = @user.bio | |
| @@ -1,3 +1,8 @@ |
| 55 | 55 | h2 |
| 56 | 56 | font-size: 20px |
| 57 | 57 | margin: 0px |
| 58 | .user-info | |
| 59 | padding: 20px 35px | |
| 60 | .user-name | |
| 61 | font-size: 28px | |
| 62 | font-weight: 600 | |
| 63 | .user-id | |
| 64 | font-size: 18px | |
| 65 | color: #aaa | |
| 66 | .bio | |
| 67 | margin-top: 5px | |
| 68 | .nav.nav-tabs | |
| 69 | border-top: 1px solid #ddd | |
| 70 | border-bottom: 2px solid #ddd | |
| 71 | li | |
| 72 | a | |
| 73 | border: none | |
| 74 | &:hover | |
| 75 | background-color: #fff | |
| 76 | .text | |
| 77 | .num | |
| 78 | font-size: 28px | |
| @@ -55,3 +55,24 @@ html, body |
| 1 | .navbar.navbar-default | |
| 2 | .container | |
| 3 | .navbar-header | |
| 4 | = link_to '#Tsubuyaki', root_url, class: "navbar-brand" | |
| 5 | - if logged_in? | |
| 6 | %ul.nav.navbar-nav | |
| 7 | %li= link_to "ユーザー一覧", users_path | |
| 8 | %ul.nav.navbar-nav.navbar-right | |
| 9 | %li= link_to current_user.name, current_user | |
| 10 | %li= link_to "ログアウト", sessions_path, method: :delete | |
| 11 | - else | |
| 12 | %ul.nav.navbar-nav.navbar-right | |
| 13 | %li= link_to "会員登録", new_registrations_path | |
| 14 | %li= link_to "ログイン", new_sessions_path | |
| @@ -0,0 +1,14 @@ |
| 9 | 9 | <body id="application"> |
| 10 | 10 | |
| 11 | 11 | <div> |
| 12 | <%= render partial: "layouts/header" %> | |
| 12 | 13 | <%= yield %> |
| 13 | 14 | </div> |
| 14 | 15 | |
| @@ -9,6 +9,7 @@ |
$ rails g controller settings edit | 1 | 1 | Rails.application.routes.draw do |
| 2 | 2 | |
| 3 | get 'settings/edit' | |
| 4 | ||
| 5 | 3 | resource :registrations, only: [:new, :create] |
| 6 | 4 | resource :sessions, only: [:new, :create, :destroy] |
| 5 | resource :settings, only: [:edit, :update] | |
| 7 | 6 | resources :users, only: [:index, :show] |
| 8 | 7 | |
| 9 | 8 | root to: 'registrations#new' |
| @@ -1,9 +1,8 @@ |
| 1 | 1 | class SettingsController < ApplicationController |
| 2 | 2 | def edit |
| 3 | @user = current_user | |
| 3 | 4 | end |
| 4 | 5 | end |
| @@ -1,4 +1,5 @@ |
| 2 | 2 | def edit |
| 3 | 3 | @user = current_user |
| 4 | 4 | end |
| 5 | ||
| 6 | def update | |
| 7 | @user = current_user | |
| 8 | @user.update_attributes(params_user) | |
| 9 | redirect_to user_url(@user) | |
| 10 | end | |
| 11 | ||
| 12 | private | |
| 13 | ||
| 14 | def params_user | |
| 15 | params.require(:user).permit(:name, :screen_name, :bio) | |
| 16 | end | |
| 5 | 17 | end |
| @@ -2,4 +2,16 @@ class SettingsController < ApplicationController |
| 1 | 1 | %h1 Settings#edit |
| 2 | 2 | %p Find me in app/views/settings/edit.html.haml |
| 3 | ||
| 4 | = form_for @user, url: settings_path, method: :put do |f| | |
| 5 | = f.label :name | |
| 6 | = f.text_field :name | |
| 7 | = f.label :screen_name | |
| 8 | = f.text_field :screen_name | |
| 9 | = f.label :bio | |
| 10 | = f.text_area :bio | |
| 11 | = f.submit | |
| @@ -1,2 +1,11 @@ |
| 7 | 7 | %li= link_to "ユーザー一覧", users_path |
| 8 | 8 | %ul.nav.navbar-nav.navbar-right |
| 9 | 9 | %li= link_to current_user.name, current_user |
| 10 | %li= link_to "設定", edit_settings_path | |
| 10 | 11 | %li= link_to "ログアウト", sessions_path, method: :delete |
| 11 | 12 | - else |
| 12 | 13 | %ul.nav.navbar-nav.navbar-right |
| @@ -7,6 +7,7 @@ |
| 1 | 1 | module UsersHelper |
| 2 | def render_user_screen_name user | |
| 3 | if user.screen_name.blank? | |
| 4 | user.name | |
| 5 | else | |
| 6 | user.screen_name | |
| 7 | end | |
| 8 | end | |
| 2 | 9 | end |
| @@ -1,2 +1,9 @@ |
| 5 | 5 | = div_for @users, class: "list-group-item" do |u| |
| 6 | 6 | %h4.user |
| 7 | 7 | %span.user-name |
| 8 | = link_to u.name, user_path(u) | |
| 8 | = link_to render_user_screen_name(u), user_path(u) | |
| 9 | 9 | %span.user-id |
| 10 | 10 | @#{u.name} |
| 11 | 11 | .bio |
| @@ -5,7 +5,7 @@ |
| 1 | 1 | .col-xs-8#users-content |
| 2 | 2 | .user-info |
| 3 | 3 | %span.user-name |
| 4 | = @user.name | |
| 4 | = render_user_screen_name(@user) | |
| 5 | 5 | %span.user-id |
| 6 | 6 | @#{@user.name} |
| 7 | 7 | .bio |
| @@ -1,7 +1,7 @@ |
| 1 | %h1 Settings#edit | |
| 2 | %p Find me in app/views/settings/edit.html.haml | |
| 3 | ||
| 4 | = form_for @user, url: settings_path, method: :put do |f| | |
| 5 | = f.label :name | |
| 6 | = f.text_field :name | |
| 7 | = f.label :screen_name | |
| 8 | = f.text_field :screen_name | |
| 9 | = f.label :bio | |
| 10 | = f.text_area :bio | |
| 11 | = f.submit | |
| 1 | #settings-edit | |
| 2 | .col-xs-6.settings-content | |
| 3 | = form_for @user, url: settings_path, method: :put do |f| | |
| 4 | %h2 | |
| 5 | 設定 | |
| 6 | .form-body | |
| 7 | .form-group | |
| 8 | = f.label :name, "ユーザーID: @cyllabus_jp", class: "control-label" | |
| 9 | = f.text_field :name, class: "form-control" | |
| 10 | .form-group | |
| 11 | = f.label :screen_name, "表示名: シラバス", class: "control-label" | |
| 12 | = f.text_field :screen_name, class: "form-control" | |
| 13 | .form-group | |
| 14 | = f.label :bio, "プロフィール(200文字)", class: "control-label" | |
| 15 | = f.text_area :bio, class: "form-control" | |
| 16 | = f.submit "送信する", class: "btn btn-success" | |
| @@ -1,11 +1,16 @@ |
| 1 | 1 | // Place all the styles related to the settings controller here. |
| 2 | 2 | // They will automatically be included in application.css. |
| 3 | 3 | // You can use Sass here: http://sass-lang.com/ |
| 4 | ||
| 5 | #settings-edit | |
| 6 | .settings-content | |
| 7 | float: none | |
| 8 | margin: 0 auto 35px | |
| 9 | form | |
| 10 | border: 1px solid #ddd | |
| 11 | border-bottom-width: 3px | |
| 12 | border-radius: 6px | |
| 13 | background-color: #fff | |
| 14 | h2 | |
| 15 | margin: 0px | |
| 16 | padding: 12px | |
| 17 | text-align: center | |
| 18 | border-bottom: 2px solid #ddd | |
| 19 | font-size: 18px | |
| 20 | font-weight: 600 | |
| 21 | .form-body | |
| 22 | padding: 25px 30px | |
| 23 | .control-label | |
| 24 | font-weight: 400 | |
| 25 | textarea | |
| 26 | height: 100px | |
| 27 | .btn | |
| 28 | width: 100% | |
| 29 | margin: 10px 0px | |
| @@ -1,3 +1,29 @@ |
| 5 | 5 | |
| 6 | 6 | def update |
| 7 | 7 | @user = current_user |
| 8 | @user.update_attributes(params_user) | |
| 9 | redirect_to user_url(@user) | |
| 8 | if @user.update_attributes(params_user) | |
| 9 | redirect_to user_url(@user) | |
| 10 | else | |
| 11 | render :edit | |
| 12 | end | |
| 10 | 13 | end |
| 11 | 14 | |
| 12 | 15 | private |
| @@ -5,8 +5,11 @@ def edit |
| 1 | 1 | #settings-edit |
| 2 | 2 | .col-xs-6.settings-content |
| 3 | - @user.errors.each do |attr, message| | |
| 4 | .alert.alert-danger= message | |
| 3 | 5 | = form_for @user, url: settings_path, method: :put do |f| |
| 4 | 6 | %h2 |
| 5 | 7 | 設定 |
| @@ -1,5 +1,7 @@ |
| 20 | 20 | width: 100% |
| 21 | 21 | height: 100% |
| 22 | 22 | |
| 23 | #application | |
| 24 | background-color: #f5f5f5 | |
| 25 | ||
| 23 | 26 | .clear |
| 24 | 27 | clear: both |
| 25 | 28 | |
| @@ -20,6 +20,9 @@ html, body |
| 15 | 15 | end |
| 16 | 16 | end |
| 17 | 17 | |
| 18 | def destroy | |
| 19 | logout | |
| 20 | redirect_to root_url | |
| 21 | end | |
| 22 | ||
| 18 | 23 | private |
| 19 | 24 | |
| 20 | 25 | def params_user |
| @@ -15,6 +15,11 @@ def create |
| 1 | 1 | class UsersController < ApplicationController |
| 2 | before_filter :require_login | |
| 3 | ||
| 2 | 4 | # GET /users |
| 3 | 5 | # GET /users.json |
| 4 | 6 | def index |
| @@ -1,4 +1,6 @@ |
| 6 | 6 | def update |
| 7 | 7 | @user = current_user |
| 8 | 8 | if @user.update_attributes(params_user) |
| 9 | redirect_to user_url(@user) | |
| 9 | redirect_to user_url(@user), notice: "プロフィールを更新しました" | |
| 10 | 10 | else |
| 11 | 11 | render :edit |
| 12 | 12 | end |
| @@ -6,7 +6,7 @@ def edit |
| 1 | 1 | .col-xs-8#users-content |
| 2 | 2 | .user-info |
| 3 | - if notice | |
| 4 | .alert.alert-info= notice | |
| 3 | 5 | %span.user-name |
| 4 | 6 | = render_user_screen_name(@user) |
| 5 | 7 | %span.user-id |
| @@ -1,5 +1,7 @@ |
| 8 | 8 | password = params_user[:password] |
| 9 | 9 | |
| 10 | 10 | if login(email, password) |
| 11 | redirect_to root_url, notice: "successfully logged in." | |
| 11 | redirect_to root_url, notice: "サインインしました" | |
| 12 | 12 | else |
| 13 | 13 | @user = User.new(email: email) |
| 14 | 14 | render :new |
| @@ -8,7 +8,7 @@ def create |
| 17 | 17 | |
| 18 | 18 | def destroy |
| 19 | 19 | logout |
| 20 | redirect_to root_url | |
| 20 | redirect_to root_url, notice: "サインアウトしました" | |
| 21 | 21 | end |
| 22 | 22 | |
| 23 | 23 | private |
| @@ -17,7 +17,7 @@ def create |
| 5 | 5 | このサイトは、シラバスのRails学習コースの完成形サンプルアプリです。Microsoft Azure上で運用されています。 |
| 6 | 6 | %p.by |
| 7 | 7 | 開発者: @ohataken デザイン: @keito0904 企画: @saiwaki14 |
| 8 | - if notice | |
| 9 | %p | |
| 10 | .alert.alert-info= notice | |
| 8 | 11 | .col-xs-6.right-content |
| 9 | 12 | = form_for @user, url: registrations_path, method: :post do |f| |
| 10 | 13 | %h2 |
| @@ -5,6 +5,9 @@ |
$ rails g scaffold tweet user_id:integer content:string --no-stylesheets | 5 | 5 | t.string :content |
| 6 | 6 | |
| 7 | 7 | t.timestamps |
| 8 | ||
| 9 | t.index :user_id | |
| 10 | t.index :created_at | |
| 8 | 11 | end |
| 9 | 12 | end |
| 10 | 13 | end |
| @@ -5,6 +5,9 @@ def change |
$ rake db:migrate | 4 | 4 | = link_to '#Tsubuyaki', root_url, class: "navbar-brand" |
| 5 | 5 | - if logged_in? |
| 6 | 6 | %ul.nav.navbar-nav |
| 7 | %li= link_to "全ツイート一覧", tweets_path | |
| 7 | 8 | %li= link_to "ユーザー一覧", users_path |
| 8 | 9 | %ul.nav.navbar-nav.navbar-right |
| 9 | 10 | %li= link_to current_user.name, current_user |
| @@ -4,6 +4,7 @@ |
| 7 | 7 | @user = User.new(params_user) |
| 8 | 8 | |
| 9 | 9 | if @user.save |
| 10 | login(@user.email, @user.password) | |
| 10 | 11 | redirect_to root_url |
| 11 | 12 | else |
| 12 | 13 | render :new |
| @@ -7,6 +7,7 @@ def create |
| 1 | 1 | Rails.application.routes.draw do |
| 2 | 2 | |
| 3 | resources :tweets | |
| 4 | ||
| 5 | 3 | resource :registrations, only: [:new, :create] |
| 6 | 4 | resource :sessions, only: [:new, :create, :destroy] |
| 7 | 5 | resource :settings, only: [:edit, :update] |
| 8 | 6 | resources :users, only: [:index, :show] |
| 7 | resources :tweets | |
| 9 | 8 | |
| 10 | 9 | root to: 'registrations#new' |
| 11 | 10 | |
| @@ -1,11 +1,10 @@ |
| 25 | 25 | # POST /tweets.json |
| 26 | 26 | def create |
| 27 | 27 | @tweet = Tweet.new(tweet_params) |
| 28 | @tweet.user = current_user | |
| 28 | 29 | |
| 29 | 30 | respond_to do |format| |
| 30 | 31 | if @tweet.save |
| @@ -25,6 +25,7 @@ def edit | ||
| 69 | 70 | |
| 70 | 71 | # Never trust parameters from the scary internet, only allow the white list through. |
| 71 | 72 | def tweet_params |
| 72 | params.require(:tweet).permit(:user_id, :content) | |
| 73 | params.require(:tweet).permit(:content) | |
| 73 | 74 | end |
| 74 | 75 | end |
| @@ -69,6 +70,6 @@ def set_tweet |
| 1 | 1 | class TweetsController < ApplicationController |
| 2 | before_filter :require_login, except: [:index] | |
| 2 | 3 | before_action :set_tweet, only: [:show, :edit, :update, :destroy] |
| 3 | 4 | |
| 4 | 5 | # GET /tweets |
| @@ -1,4 +1,5 @@ |
| 1 | 1 | class Tweet < ActiveRecord::Base |
| 2 | belongs_to :user | |
| 2 | 3 | end |
| @@ -1,2 +1,3 @@ |
| 1 | 1 | class User < ActiveRecord::Base |
| 2 | 2 | authenticates_with_sorcery! |
| 3 | 3 | |
| 4 | has_many :tweets, dependent: :destroy | |
| 5 | ||
| 4 | 6 | validates :name, presence: true, uniqueness: { case_sensitive: false }, format: { with: /\A[a-z][a-z0-9]+\z/ }, length: { in: 4..24 } |
| 5 | 7 | validates :screen_name, length: { maximum: 140 } |
| 6 | 8 | validates :bio, length: { maximum: 200 } |
| @@ -1,6 +1,8 @@ |
class Tweet < ActiveRecord::Base
belongs_to :user
validates :user, presence: true
validates :content, presence: true, length: { in: 1..140}
end
| 1 | %h1 Listing tweets | |
| 2 | ||
| 3 | %table | |
| 4 | %tr | |
| 5 | %th User | |
| 6 | %th Content | |
| 7 | %th | |
| 8 | %th | |
| 9 | %th | |
| 10 | ||
| 11 | - @tweets.each do |tweet| | |
| 12 | %tr | |
| 13 | %td= tweet.user_id | |
| 14 | %td= tweet.content | |
| 15 | %td= link_to 'Show', tweet | |
| 16 | %td= link_to 'Edit', edit_tweet_path(tweet) | |
| 17 | %td= link_to 'Destroy', tweet, :method => :delete, :data => { :confirm => 'Are you sure?' } | |
| 18 | ||
| 19 | %br | |
| 20 | ||
| 21 | = link_to 'New Tweet', new_tweet_path | |
| @@ -1,21 +0,0 @@ |
| 1 | #tweets-content | |
| 2 | .container | |
| 3 | .row | |
| 4 | .col-xs-4.left-content | |
| 5 | - if logged_in? | |
| 6 | .panel.panel-default | |
| 7 | .panel-body | |
| 8 | = link_to user_path(current_user), class: "user" do | |
| 9 | %span.user-name | |
| 10 | = render_user_screen_name current_user | |
| 11 | %span.user-id | |
| 12 | @#{current_user.name} | |
| 13 | .user-activity | |
| 14 | .row | |
| 15 | .col-xs-4.tweets-num | |
| 16 | .text | |
| 17 | つぶやき | |
| 18 | .num | |
| 19 | = link_to current_user.tweets.count, user_path(current_user) | |
| 20 | .col-xs-4.follow-num | |
| 21 | .text | |
| 22 | フォロー | |
| 23 | .num | |
| 24 | 0 | |
| 25 | .col-xs-4.follower-num | |
| 26 | .text | |
| 27 | フォロワー | |
| 28 | .num | |
| 29 | 0 | |
| 30 | .col-xs-8.right-content | |
| 31 | .list-group | |
| 32 | = div_for @tweets, class: "list-group-item" do |t| | |
| 33 | %h4.user | |
| 34 | %span.user-name | |
| 35 | = link_to render_user_screen_name(t.user), user_path(t.user) | |
| 36 | %span.user-id | |
| 37 | @#{t.user.name} | |
| 38 | %span.time.pull-right | |
| 39 | = distance_of_time_in_words_to_now(t.created_at) | |
| 40 | .clear | |
| 41 | .tweet-content | |
| 42 | %p | |
| 43 | = t.content | |
| @@ -0,0 +1,43 @@ |
| 79 | 79 | .text |
| 80 | 80 | .num |
| 81 | 81 | font-size: 28px |
| 82 | ||
| 83 | #tweets-content | |
| 84 | .left-content | |
| 85 | .panel | |
| 86 | box-shadow: none | |
| 87 | border-bottom-width: 3px | |
| 88 | .user | |
| 89 | .user-name | |
| 90 | font-size: 20px | |
| 91 | font-weight: 600 | |
| 92 | color: #555 | |
| 93 | .user-id | |
| 94 | color: #aaa | |
| 95 | &:hover | |
| 96 | text-decoration: none | |
| 97 | border-bottom: 1px solid #555 | |
| 98 | .user-activity | |
| 99 | text-align: center | |
| 100 | .row | |
| 101 | border-bottom: 1px solid #ddd | |
| 102 | padding: 10px 0px | |
| 103 | .text | |
| 104 | font-size: 12px | |
| 105 | color: #777 | |
| 106 | .num | |
| 107 | font-size: 18px | |
| 108 | .right-content | |
| 109 | .list-group-item:last-child | |
| 110 | border-bottom-width: 3px | |
| 111 | .list-group-item | |
| 112 | .user | |
| 113 | .user-name | |
| 114 | a | |
| 115 | color: #555 | |
| 116 | .user-id | |
| 117 | font-size: 14px | |
| 118 | color: #aaa | |
| 119 | font-weight: 400 | |
| 120 | .time | |
| 121 | font-size: 12px | |
| 122 | color: #aaa | |
| 123 | font-weight: 400 | |
| 124 | .tweet-content | |
| 125 | font-size: 16px | |
| 126 | .content-footer | |
| 127 | a | |
| 128 | font-size: 12px | |
| 129 | margin-right: 10px | |
| @@ -79,3 +79,51 @@ html, body |
| 22 | 22 | confirmation: 確認用パスワードが一致しません |
| 23 | 23 | bio: |
| 24 | 24 | too_long: プロフィールは200文字以内でなければなりません |
| 25 | date: | |
| 26 | abbr_day_names: | |
| 27 | - 日 | |
| 28 | - 月 | |
| 29 | - 火 | |
| 30 | - 水 | |
| 31 | - 木 | |
| 32 | - 金 | |
| 33 | - 土 | |
| 34 | abbr_month_names: | |
| 35 | - | |
| 36 | - 1月 | |
| 37 | - 2月 | |
| 38 | - 3月 | |
| 39 | - 4月 | |
| 40 | - 5月 | |
| 41 | - 6月 | |
| 42 | - 7月 | |
| 43 | - 8月 | |
| 44 | - 9月 | |
| 45 | - 10月 | |
| 46 | - 11月 | |
| 47 | - 12月 | |
| 48 | day_names: | |
| 49 | - 日曜日 | |
| 50 | - 月曜日 | |
| 51 | - 火曜日 | |
| 52 | - 水曜日 | |
| 53 | - 木曜日 | |
| 54 | - 金曜日 | |
| 55 | - 土曜日 | |
| 56 | formats: | |
| 57 | default: ! '%Y/%m/%d' | |
| 58 | long: ! '%Y年%m月%d日(%a)' | |
| 59 | short: ! '%m/%d' | |
| 60 | month_names: | |
| 61 | - | |
| 62 | - 1月 | |
| 63 | - 2月 | |
| 64 | - 3月 | |
| 65 | - 4月 | |
| 66 | - 5月 | |
| 67 | - 6月 | |
| 68 | - 7月 | |
| 69 | - 8月 | |
| 70 | - 9月 | |
| 71 | - 10月 | |
| 72 | - 11月 | |
| 73 | - 12月 | |
| 74 | order: | |
| 75 | - :year | |
| 76 | - :month | |
| 77 | - :day | |
| 78 | datetime: | |
| 79 | distance_in_words: | |
| 80 | about_x_hours: | |
| 81 | one: 約1時間 | |
| 82 | other: 約%{count}時間 | |
| 83 | about_x_months: | |
| 84 | one: 約1ヶ月 | |
| 85 | other: 約%{count}ヶ月 | |
| 86 | about_x_years: | |
| 87 | one: 約1年 | |
| 88 | other: 約%{count}年 | |
| 89 | almost_x_years: | |
| 90 | one: 1年弱 | |
| 91 | other: ! '%{count}年弱' | |
| 92 | half_a_minute: 30秒前後 | |
| 93 | less_than_x_minutes: | |
| 94 | one: 1分以内 | |
| 95 | other: ! '%{count}分未満' | |
| 96 | less_than_x_seconds: | |
| 97 | one: 1秒以内 | |
| 98 | other: ! '%{count}秒未満' | |
| 99 | over_x_years: | |
| 100 | one: 1年以上 | |
| 101 | other: ! '%{count}年以上' | |
| 102 | x_days: | |
| 103 | one: 1日 | |
| 104 | other: ! '%{count}日' | |
| 105 | x_minutes: | |
| 106 | one: 1分 | |
| 107 | other: ! '%{count}分' | |
| 108 | x_months: | |
| 109 | one: 1ヶ月 | |
| 110 | other: ! '%{count}ヶ月' | |
| 111 | x_seconds: | |
| 112 | one: 1秒 | |
| 113 | other: ! '%{count}秒' | |
| 114 | prompts: | |
| 115 | day: 日 | |
| 116 | hour: 時 | |
| 117 | minute: 分 | |
| 118 | month: 月 | |
| 119 | second: 秒 | |
| 120 | year: 年 | |
| 121 | errors: | |
| 122 | format: ! '%{attribute}%{message}' | |
| 123 | messages: | |
| 124 | accepted: を受諾してください。 | |
| 125 | blank: を入力してください。 | |
| 126 | present: は入力しないでください。 | |
| 127 | confirmation: と%{attribute}の入力が一致しません。 | |
| 128 | empty: を入力してください。 | |
| 129 | equal_to: は%{count}にしてください。 | |
| 130 | even: は偶数にしてください。 | |
| 131 | exclusion: は予約されています。 | |
| 132 | greater_than: は%{count}より大きい値にしてください。 | |
| 133 | greater_than_or_equal_to: は%{count}以上の値にしてください。 | |
| 134 | inclusion: は一覧にありません。 | |
| 135 | invalid: は不正な値です。 | |
| 136 | less_than: は%{count}より小さい値にしてください。 | |
| 137 | less_than_or_equal_to: は%{count}以下の値にしてください。 | |
| 138 | not_a_number: は数値で入力してください。 | |
| 139 | not_an_integer: は整数で入力してください。 | |
| 140 | odd: は奇数にしてください。 | |
| 141 | record_invalid: バリデーションに失敗しました。 %{errors} | |
| 142 | restrict_dependent_destroy: ! '%{record}が存在しているので削除できません。' | |
| 143 | taken: はすでに存在します。 | |
| 144 | too_long: は%{count}文字以内で入力してください。 | |
| 145 | too_short: は%{count}文字以上で入力してください。 | |
| 146 | wrong_length: は%{count}文字で入力してください。 | |
| 147 | other_than: "は%{count}以外の値にしてください。" | |
| 148 | template: | |
| 149 | body: 次の項目を確認してください。 | |
| 150 | header: | |
| 151 | one: ! '%{model}にエラーが発生しました。' | |
| 152 | other: ! '%{model}に%{count}個のエラーが発生しました。' | |
| 153 | helpers: | |
| 154 | select: | |
| 155 | prompt: 選択してください。 | |
| 156 | submit: | |
| 157 | create: 登録する | |
| 158 | submit: 保存する | |
| 159 | update: 更新する | |
| 160 | number: | |
| 161 | currency: | |
| 162 | format: | |
| 163 | delimiter: ! ',' | |
| 164 | format: ! '%n%u' | |
| 165 | precision: 0 | |
| 166 | separator: . | |
| 167 | significant: false | |
| 168 | strip_insignificant_zeros: false | |
| 169 | unit: 円 | |
| 170 | format: | |
| 171 | delimiter: ! ',' | |
| 172 | precision: 3 | |
| 173 | separator: . | |
| 174 | significant: false | |
| 175 | strip_insignificant_zeros: false | |
| 176 | human: | |
| 177 | decimal_units: | |
| 178 | format: ! '%n %u' | |
| 179 | units: | |
| 180 | billion: 十億 | |
| 181 | million: 百万 | |
| 182 | quadrillion: 千兆 | |
| 183 | thousand: 千 | |
| 184 | trillion: 兆 | |
| 185 | unit: '' | |
| 186 | format: | |
| 187 | delimiter: '' | |
| 188 | precision: 3 | |
| 189 | significant: true | |
| 190 | strip_insignificant_zeros: true | |
| 191 | storage_units: | |
| 192 | format: ! '%n%u' | |
| 193 | units: | |
| 194 | byte: バイト | |
| 195 | gb: ギガバイト | |
| 196 | kb: キロバイト | |
| 197 | mb: メガバイト | |
| 198 | tb: テラバイト | |
| 199 | percentage: | |
| 200 | format: | |
| 201 | delimiter: '' | |
| 202 | format: "%n%" | |
| 203 | precision: | |
| 204 | format: | |
| 205 | delimiter: '' | |
| 206 | support: | |
| 207 | array: | |
| 208 | last_word_connector: と | |
| 209 | two_words_connector: と | |
| 210 | words_connector: と | |
| 211 | time: | |
| 212 | am: 午前 | |
| 213 | formats: | |
| 214 | default: ! '%Y/%m/%d %H:%M:%S' | |
| 215 | long: ! '%Y年%m月%d日(%a) %H時%M分%S秒 %z' | |
| 216 | short: ! '%y/%m/%d %H:%M' | |
| 217 | pm: 午後 | |
| @@ -22,3 +22,196 @@ ja: |
| 6 | 6 | # GET /tweets.json |
| 7 | 7 | def index |
| 8 | 8 | @tweets = Tweet.all |
| 9 | @tweet = Tweet.new | |
| 9 | 10 | end |
| 10 | 11 | |
| 11 | 12 | # GET /tweets/1 |
| @@ -6,6 +6,7 @@ class TweetsController < ApplicationController |
| 27 | 27 | フォロワー |
| 28 | 28 | .num |
| 29 | 29 | 0 |
| 30 | = form_for @tweet do |f| | |
| 31 | .form-group | |
| 32 | = f.text_area :content, class: "form-control" | |
| 33 | = f.submit "つぶやく", class: "btn btn-success" | |
| 30 | 34 | .col-xs-8.right-content |
| 31 | 35 | .list-group |
| 32 | 36 | = div_for @tweets, class: "list-group-item" do |t| |
| @@ -27,6 +27,10 @@ |
| 105 | 105 | color: #777 |
| 106 | 106 | .num |
| 107 | 107 | font-size: 18px |
| 108 | .new_tweet | |
| 109 | margin-top: 20px | |
| 110 | textarea | |
| 111 | width: 100% !important | |
| 112 | .btn | |
| 113 | width: 100% | |
| 108 | 114 | .right-content |
| 109 | 115 | .list-group-item:last-child |
| 110 | 116 | border-bottom-width: 3px |
| @@ -105,6 +105,12 @@ html, body |
| 31 | 31 | |
| 32 | 32 | respond_to do |format| |
| 33 | 33 | if @tweet.save |
| 34 | format.html { redirect_to @tweet, notice: 'Tweet was successfully created.' } | |
| 34 | format.html { redirect_to tweets_url, notice: 'Tweet was successfully created.' } | |
| 35 | 35 | format.json { render :show, status: :created, location: @tweet } |
| 36 | 36 | else |
| 37 | 37 | format.html { render :new } |
| @@ -31,7 +31,7 @@ def create |
| 34 | 34 | format.html { redirect_to tweets_url, notice: 'Tweet was successfully created.' } |
| 35 | 35 | format.json { render :show, status: :created, location: @tweet } |
| 36 | 36 | else |
| 37 | format.html { render :new } | |
| 37 | @tweets = Tweet.all | |
| 38 | format.html { render :index } | |
| 38 | 39 | format.json { render json: @tweet.errors, status: :unprocessable_entity } |
| 39 | 40 | end |
| 40 | 41 | end |
| @@ -34,7 +34,8 @@ def create |
| 27 | 27 | フォロワー |
| 28 | 28 | .num |
| 29 | 29 | 0 |
| 30 | - @tweet.errors.full_messages.each do |message| | |
| 31 | .alert.alert-danger= message | |
| 30 | 32 | = form_for @tweet do |f| |
| 31 | 33 | .form-group |
| 32 | 34 | = f.text_area :content, class: "form-control" |
| @@ -27,6 +27,8 @@ |
| 3 | 3 | |
| 4 | 4 | validates :user, presence: true |
| 5 | 5 | validates :content, presence: true, length: { in: 1..140 } |
| 6 | ||
| 7 | default_scope -> { order(created_at: :desc) } | |
| 6 | 8 | end |
| @@ -3,4 +3,6 @@ class Tweet < ActiveRecord::Base |
| 34 | 34 | = f.text_area :content, class: "form-control" |
| 35 | 35 | = f.submit "つぶやく", class: "btn btn-success" |
| 36 | 36 | .col-xs-8.right-content |
| 37 | - if notice | |
| 38 | .alert.alert-info= notice | |
| 37 | 39 | .list-group |
| 38 | 40 | = div_for @tweets, class: "list-group-item" do |t| |
| 39 | 41 | %h4.user |
| @@ -34,6 +34,8 @@ |
| 8 | 8 | @#{@user.name} |
| 9 | 9 | .bio |
| 10 | 10 | = @user.bio |
| 11 | ||
| 12 | %ul.nav.nav-tabs.nav-justified | |
| 13 | %li.active | |
| 14 | = link_to user_path(@user) do | |
| 15 | .text | |
| 16 | つぶやき | |
| 17 | .num | |
| 18 | #{@user.tweets.count} | |
| 19 | %li | |
| 20 | = link_to nil do | |
| 21 | .text | |
| 22 | フォロー | |
| 23 | .num | |
| 24 | 0 | |
| 25 | %li | |
| 26 | = link_to nil do | |
| 27 | .text | |
| 28 | フォロワー | |
| 29 | .num | |
| 30 | 0 | |
| 31 | %li | |
| 32 | = link_to nil do | |
| 33 | .text | |
| 34 | お気に入り | |
| 35 | .num | |
| 36 | 0 | |
| 37 | ||
| 38 | .list-group | |
| 39 | - if @user.tweets.empty? | |
| 40 | .list-group-item | |
| 41 | まだツイートはありません | |
| 42 | = div_for @user.tweets, class: "list-group-item" do |t| | |
| 43 | %h4.user | |
| 44 | %span.user-name | |
| 45 | = link_to t.user.name, user_path(t.user) | |
| 46 | %span.user-id | |
| 47 | @#{t.user.name} | |
| 48 | %span.time.pull-right | |
| 49 | = distance_of_time_in_words_to_now(t.created_at) | |
| 50 | .clear | |
| 51 | .tweet-content | |
| 52 | %p | |
| 53 | = t.content | |
| @@ -8,3 +8,46 @@ |
| 1 | 1 | class RegistrationsController < ApplicationController |
| 2 | 2 | def new |
| 3 | 3 | @user = User.new |
| 4 | redirect_to tweets_url if logged_in? | |
| 4 | 5 | end |
| 5 | 6 | |
| 6 | 7 | def create |
| @@ -1,6 +1,7 @@ |
| 1 | 1 | class SessionsController < ApplicationController |
| 2 | 2 | def new |
| 3 | 3 | @user = User.new |
| 4 | redirect_to tweets_url if logged_in? | |
| 4 | 5 | end |
| 5 | 6 | |
| 6 | 7 | def create |
| @@ -1,6 +1,7 @@ |
| 8 | 8 | |
| 9 | 9 | if @user.save |
| 10 | 10 | login(@user.email, @user.password) |
| 11 | redirect_to root_url | |
| 11 | redirect_to tweets_url | |
| 12 | 12 | else |
| 13 | 13 | render :new |
| 14 | 14 | end |
| @@ -8,7 +8,7 @@ def create |
| 8 | 8 | password = params_user[:password] |
| 9 | 9 | |
| 10 | 10 | if login(email, password) |
| 11 | redirect_to root_url, notice: "サインインしました" | |
| 11 | redirect_to tweets_url, notice: "サインインしました" | |
| 12 | 12 | else |
| 13 | 13 | @user = User.new(email: email) |
| 14 | 14 | render :new |
| @@ -8,7 +8,7 @@ def create |
$ rails g model favorite user_id:integer tweet_id:integer | 5 | 5 | t.integer :tweet_id |
| 6 | 6 | |
| 7 | 7 | t.timestamps |
| 8 | ||
| 9 | t.index :user_id | |
| 10 | t.index :tweet_id | |
| 11 | t.index :created_at | |
| 8 | 12 | end |
| 9 | 13 | end |
| 10 | 14 | end |
| @@ -5,6 +5,10 @@ def change |
$ rake db:migrate | 4 | 4 | resource :sessions, only: [:new, :create, :destroy] |
| 5 | 5 | resource :settings, only: [:edit, :update] |
| 6 | 6 | resources :users, only: [:index, :show] |
| 7 | resources :tweets | |
| 7 | resources :tweets do | |
| 8 | resource :favorites, only: [:create, :destroy] | |
| 9 | end | |
| 8 | 10 | |
| 9 | 11 | root to: 'registrations#new' |
| 10 | 12 | |
| @@ -4,7 +4,9 @@ |
| 3 | 3 | resource :registrations, only: [:new, :create] |
| 4 | 4 | resource :sessions, only: [:new, :create, :destroy] |
| 5 | 5 | resource :settings, only: [:edit, :update] |
| 6 | resources :users, only: [:index, :show] | |
| 6 | ||
| 7 | resources :users, only: [:index, :show] do | |
| 8 | get :favorites, on: :member | |
| 9 | end | |
| 10 | ||
| 7 | 11 | resources :tweets do |
| 8 | 12 | resource :favorites, only: [:create, :destroy] |
| 9 | 13 | end |
| @@ -3,7 +3,11 @@ |
| 1 | class FavoritesController < ApplicationController | |
| 2 | end | |
| @@ -0,0 +1,2 @@ |
| 1 | 1 | class FavoritesController < ApplicationController |
| 2 | before_filter :require_login | |
| 3 | ||
| 4 | def create | |
| 5 | @tweet = Tweet.find(params[:tweet_id]) | |
| 6 | @favorite = current_user.favorites.build(tweet: @tweet) | |
| 7 | ||
| 8 | if @favorite.save | |
| 9 | redirect_to tweets_url, notice: "お気に入りに登録しました" | |
| 10 | else | |
| 11 | redirect_to tweets_url, alert: "このツイートはお気に入りに登録できません" | |
| 12 | end | |
| 13 | end | |
| 2 | 14 | end |
| @@ -1,2 +1,14 @@ |
| 11 | 11 | redirect_to tweets_url, alert: "このツイートはお気に入りに登録できません" |
| 12 | 12 | end |
| 13 | 13 | end |
| 14 | ||
| 15 | def destroy | |
| 16 | @favorite = current_user.favorites.find_by!(tweet_id: params[:tweet_id]) | |
| 17 | @favorite.destroy | |
| 18 | redirect_to tweets_url, notice: "お気に入りを解除しました" | |
| 19 | end | |
| 14 | 20 | end |
| @@ -11,4 +11,10 @@ def create |
| 12 | 12 | def show |
| 13 | 13 | @user = User.find(params[:id]) |
| 14 | 14 | end |
| 15 | ||
| 16 | def favorites | |
| 17 | @user = User.find(params[:id]) | |
| 18 | end | |
| 15 | 19 | end |
| @@ -12,4 +12,8 @@ def index |
| 1 | 1 | class Favorite < ActiveRecord::Base |
| 2 | belongs_to :user | |
| 3 | belongs_to :tweet | |
| 2 | 4 | end |
| @@ -1,2 +1,4 @@ |
| 1 | 1 | class Tweet < ActiveRecord::Base |
| 2 | 2 | belongs_to :user |
| 3 | has_many :favorites, dependent: :destroy | |
| 3 | 4 | |
| 4 | 5 | validates :user, presence: true |
| 5 | 6 | validates :content, presence: true, length: { in: 1..140 } |
| @@ -1,5 +1,6 @@ |
| 2 | 2 | authenticates_with_sorcery! |
| 3 | 3 | |
| 4 | 4 | has_many :tweets, dependent: :destroy |
| 5 | has_many :favorites, dependent: :destroy | |
| 5 | 6 | |
| 6 | 7 | validates :name, presence: true, uniqueness: { case_sensitive: false }, format: { with: /\A[a-z][a-z0-9]+\z/ }, length: { in: 4..24 } |
| 7 | 8 | validates :screen_name, length: { maximum: 140 } |
| @@ -2,6 +2,7 @@ class User < ActiveRecord::Base |
| 1 | 1 | class Favorite < ActiveRecord::Base |
| 2 | 2 | belongs_to :user |
| 3 | 3 | belongs_to :tweet |
| 4 | ||
| 5 | validates :user, presence: true | |
| 6 | validates :user_id, uniqueness: { scope: :tweet_id } | |
| 7 | validates :tweet, presence: true | |
| 4 | 8 | end |
| @@ -1,4 +1,8 @@ |
| 4 | 4 | |
| 5 | 5 | validates :user, presence: true |
| 6 | 6 | validates :content, presence: true, length: { in: 1..140 } |
| 7 | ||
| 8 | def favorited_by? user | |
| 9 | favorites.where(user_id: user.id).exists? | |
| 10 | end | |
| 7 | 11 | end |
| @@ -4,4 +4,8 @@ class Tweet < ActiveRecord::Base |
| 49 | 49 | .tweet-content |
| 50 | 50 | %p |
| 51 | 51 | = t.content |
| 52 | .content-footer | |
| 53 | = link_to "お気に入りに登録", tweet_favorites_path(t), method: :post | |
| @@ -49,3 +49,5 @@ |
| 50 | 50 | %p |
| 51 | 51 | = t.content |
| 52 | 52 | .content-footer |
| 53 | = link_to "お気に入りに登録", tweet_favorites_path(t), method: :post | |
| 53 | - if t.favorited_by? current_user | |
| 54 | = link_to "お気に入りの解除", tweet_favorites_path(t), method: :delete | |
| 55 | - else | |
| 56 | = link_to "お気に入りに登録", tweet_favorites_path(t), method: :post | |
| @@ -50,4 +50,7 @@ |
| 1 | _header.html.haml | |
| 2 | .user-info | |
| 3 | - if notice | |
| 4 | .alert.alert-info= notice | |
| 5 | %span.user-name | |
| 6 | = render_user_screen_name(@user) | |
| 7 | %span.user-id | |
| 8 | @#{@user.name} | |
| 9 | .bio | |
| 10 | = @user.bio | |
| 11 | ||
| 12 | %ul.nav.nav-tabs.nav-justified | |
| 13 | %li.active | |
| 14 | = link_to user_path(@user) do | |
| 15 | .text | |
| 16 | つぶやき | |
| 17 | .num | |
| 18 | #{@user.tweets.count} | |
| 19 | %li | |
| 20 | = link_to nil do | |
| 21 | .text | |
| 22 | フォロー | |
| 23 | .num | |
| 24 | 0 | |
| 25 | %li | |
| 26 | = link_to nil do | |
| 27 | .text | |
| 28 | フォロワー | |
| 29 | .num | |
| 30 | 0 | |
| 31 | %li | |
| 32 | = link_to nil do | |
| 33 | .text | |
| 34 | お気に入り | |
| 35 | .num | |
| 36 | 0 | |
| @@ -1,0 +1,35 @@ |
| 1 | 1 | .col-xs-8#users-content |
| 2 | .user-info | |
| 3 | - if notice | |
| 4 | .alert.alert-info= notice | |
| 5 | %span.user-name | |
| 6 | = render_user_screen_name(@user) | |
| 7 | %span.user-id | |
| 8 | @#{@user.name} | |
| 9 | .bio | |
| 10 | = @user.bio | |
| 11 | ||
| 12 | %ul.nav.nav-tabs.nav-justified | |
| 13 | %li.active | |
| 14 | = link_to user_path(@user) do | |
| 15 | .text | |
| 16 | つぶやき | |
| 17 | .num | |
| 18 | #{@user.tweets.count} | |
| 19 | %li | |
| 20 | = link_to nil do | |
| 21 | .text | |
| 22 | フォロー | |
| 23 | .num | |
| 24 | 0 | |
| 25 | %li | |
| 26 | = link_to nil do | |
| 27 | .text | |
| 28 | フォロワー | |
| 29 | .num | |
| 30 | 0 | |
| 31 | %li | |
| 32 | = link_to nil do | |
| 33 | .text | |
| 34 | お気に入り | |
| 35 | .num | |
| 36 | 0 | |
| 37 | 2 | |
| 38 | 3 | .list-group |
| 39 | 4 | - if @user.tweets.empty? |
| @@ -1,39 +1,4 @@ |
| 28 | 28 | .num |
| 29 | 29 | 0 |
| 30 | 30 | %li |
| 31 | = link_to nil do | |
| 31 | = link_to favorites_user_path(@user) do | |
| 32 | 32 | .text |
| 33 | 33 | お気に入り |
| 34 | 34 | .num |
| @@ -28,7 +28,7 @@ |
| 1 | 1 | .col-xs-8#users-content |
| 2 | ||
| 2 | = render partial: "users/header" | |
| 3 | 3 | .list-group |
| 4 | 4 | - if @user.tweets.empty? |
| 5 | 5 | .list-group-item |
| @@ -1,5 +1,5 @@ |
| 9 | 9 | = @user.bio |
| 10 | 10 | |
| 11 | 11 | %ul.nav.nav-tabs.nav-justified |
| 12 | %li.active | |
| 12 | %li{ class: action_name == "show" ? "active" : nil } | |
| 13 | 13 | = link_to user_path(@user) do |
| 14 | 14 | .text |
| 15 | 15 | つぶやき |
| @@ -9,7 +9,7 @@ | ||
| 27 | 27 | フォロワー |
| 28 | 28 | .num |
| 29 | 29 | 0 |
| 30 | %li | |
| 30 | %li{ class: action_name == "favorites" ? "active" : nil } | |
| 31 | 31 | = link_to favorites_user_path(@user) do |
| 32 | 32 | .text |
| 33 | 33 | お気に入り |
| @@ -27,7 +27,7 @@ |
| 1 | _tweet.html.haml | |
| 1 | = div_for @user.tweets, class: "list-group-item" do |t| | |
| 2 | %h4.user | |
| 3 | %span.user-name | |
| 4 | = link_to t.user.name, user_path(t.user) | |
| 5 | %span.user-id | |
| 6 | @#{t.user.name} | |
| 7 | %span.time.pull-right | |
| 8 | = distance_of_time_in_words_to_now(t.created_at) | |
| 9 | .clear | |
| 10 | .tweet-content | |
| 11 | %p | |
| 12 | = t.content | |
| @@ -1,0 +1,12 @@ |
| 4 | 4 | - if @user.tweets.empty? |
| 5 | 5 | .list-group-item |
| 6 | 6 | まだツイートはありません |
| 7 | = div_for @user.tweets, class: "list-group-item" do |t| | |
| 8 | %h4.user | |
| 9 | %span.user-name | |
| 10 | = link_to t.user.name, user_path(t.user) | |
| 11 | %span.user-id | |
| 12 | @#{t.user.name} | |
| 13 | %span.time.pull-right | |
| 14 | = distance_of_time_in_words_to_now(t.created_at) | |
| 15 | .clear | |
| 16 | .tweet-content | |
| 17 | %p | |
| 18 | = t.content | |
| @@ -4,15 +4,3 @@ |
| 1 | = div_for @user.tweets, class: "list-group-item" do |t| | |
| 1 | = div_for tweet, class: "list-group-item" do |t| | |
| 2 | 2 | %h4.user |
| 3 | 3 | %span.user-name |
| 4 | 4 | = link_to t.user.name, user_path(t.user) |
| @@ -1,4 +1,4 @@ |
| 4 | 4 | - if @user.tweets.empty? |
| 5 | 5 | .list-group-item |
| 6 | 6 | まだツイートはありません |
| 7 | - @user.tweets.each do |tweet| | |
| 8 | = render partial: "users/tweet", object: tweet | |
| 7 | = render partial: "users/tweet", collection: @user.tweets | |
| @@ -4,5 +4,4 @@ |
.col-xs-8#users-content
= render partial: "users/header"
.list-group
- if @user.favorites.empty?
.list-group-item
まだお気に入りに登録されたツイートはありません
= render partial: "users/tweet", collection: @user.favorites.map{|f| f.tweet} $ rails g model follow follower_id:integer inverse_follower_id:integer | 5 | 5 | t.integer :inverse_follower_id |
| 6 | 6 | |
| 7 | 7 | t.timestamps |
| 8 | ||
| 9 | t.index :follower_id | |
| 10 | t.index :inverse_follower_id | |
| 8 | 11 | end |
| 9 | 12 | end |
| 10 | 13 | end |
| @@ -5,6 +5,9 @@ def change |
$ rake db:migrate | 5 | 5 | resource :settings, only: [:edit, :update] |
| 6 | 6 | |
| 7 | 7 | resources :users, only: [:index, :show] do |
| 8 | resource :follows, only: [:create, :destroy] | |
| 8 | 9 | get :favorites, on: :member |
| 9 | 10 | end |
| 10 | 11 | |
| @@ -5,6 +5,7 @@ |
| 7 | 7 | resources :users, only: [:index, :show] do |
| 8 | 8 | resource :follows, only: [:create, :destroy] |
| 9 | 9 | get :favorites, on: :member |
| 10 | get :follows, on: :member | |
| 10 | 11 | end |
| 11 | 12 | |
| 12 | 13 | resources :tweets do |
| @@ -7,6 +7,7 @@ |
| 8 | 8 | resource :follows, only: [:create, :destroy] |
| 9 | 9 | get :favorites, on: :member |
| 10 | 10 | get :follows, on: :member |
| 11 | get :followers, on: :member | |
| 11 | 12 | end |
| 12 | 13 | |
| 13 | 14 | resources :tweets do |
| @@ -8,6 +8,7 @@ |
| 13 | 13 | |
| 14 | 14 | resources :tweets do |
| 15 | 15 | resource :favorites, only: [:create, :destroy] |
| 16 | get :timeline, on: :collection | |
| 16 | 17 | end |
| 17 | 18 | |
| 18 | 19 | root to: 'registrations#new' |
| @@ -13,6 +13,7 @@ |
| 1 | class FollowsController < ApplicationController | |
| 2 | end | |
| @@ -0,0 +1,2 @@ |
| 1 | 1 | class FollowsController < ApplicationController |
| 2 | before_filter :require_login | |
| 3 | ||
| 4 | def create | |
| 5 | @user = User.find(params[:user_id]) | |
| 6 | ||
| 7 | if @user.inverse_follows.create(follower: current_user) | |
| 8 | redirect_to tweets_url, notice: "フォローしました" | |
| 9 | else | |
| 10 | redirect_to tweets_url, alert: "フォローできません" | |
| 11 | end | |
| 12 | end | |
| 2 | 13 | end |
| @@ -1,2 +1,13 @@ |
| 10 | 10 | redirect_to tweets_url, alert: "フォローできません" |
| 11 | 11 | end |
| 12 | 12 | end |
| 13 | ||
| 14 | def destroy | |
| 15 | @user = User.find(params[:user_id]) | |
| 16 | follow = @user.inverse_follows.find_by(follower: current_user.id) | |
| 17 | follow.destroy | |
| 18 | redirect_to tweets_url, notice: "フォローを解除しました" | |
| 19 | end | |
| 13 | 20 | end |
| @@ -10,4 +10,11 @@ def create |
| 16 | 16 | def favorites |
| 17 | 17 | @user = User.find(params[:id]) |
| 18 | 18 | end |
| 19 | ||
| 20 | def follows | |
| 21 | @user = User.find(params[:id]) | |
| 22 | end | |
| 19 | 23 | end |
| @@ -16,4 +16,8 @@ def show |
| 20 | 20 | def follows |
| 21 | 21 | @user = User.find(params[:id]) |
| 22 | 22 | end |
| 23 | ||
| 24 | def followers | |
| 25 | @user = User.find(params[:id]) | |
| 26 | end | |
| 23 | 27 | end |
| @@ -20,4 +20,8 @@ def favorites |
| 9 | 9 | @tweet = Tweet.new |
| 10 | 10 | end |
| 11 | 11 | |
| 12 | def timeline | |
| 13 | @tweets = Tweet.eager_load(user: :inverse_follows).where(follows: { follower_id: current_user.id }) | |
| 14 | @tweet = Tweet.new | |
| 15 | end | |
| 16 | ||
| 12 | 17 | # GET /tweets/1 |
| 13 | 18 | # GET /tweets/1.json |
| 14 | 19 | def show |
| @@ -9,6 +9,11 @@ def index |
| 1 | 1 | class Follow < ActiveRecord::Base |
| 2 | belongs_to :follower, class_name: User | |
| 3 | belongs_to :inverse_follower, class_name: User | |
| 2 | 4 | end |
| @@ -1,2 +1,4 @@ |
| 4 | 4 | has_many :tweets, dependent: :destroy |
| 5 | 5 | has_many :favorites, dependent: :destroy |
| 6 | 6 | |
| 7 | has_many :follows, foreign_key: :follower_id | |
| 8 | has_many :inverse_followers, through: :follows | |
| 9 | has_many :inverse_follows, foreign_key: :inverse_follower_id, class_name: Follow | |
| 10 | has_many :followers, through: :inverse_follows | |
| 11 | ||
| 7 | 12 | validates :name, presence: true, uniqueness: { case_sensitive: false }, format: { with: /\A[a-z][a-z0-9]+\z/ }, length: { in: 4..24 } |
| 8 | 13 | validates :screen_name, length: { maximum: 140 } |
| 9 | 14 | validates :bio, length: { maximum: 200 } |
| @@ -4,6 +4,11 @@ class User < ActiveRecord::Base |
| 1 | 1 | class Follow < ActiveRecord::Base |
| 2 | 2 | belongs_to :follower, class_name: User |
| 3 | 3 | belongs_to :inverse_follower, class_name: User |
| 4 | ||
| 5 | validates :follower, presence: true | |
| 6 | validates :inverse_follower, presence: true | |
| 7 | validates :inverse_follower_id, uniqueness: { scope: :follower_id } | |
| 4 | 8 | end |
| @@ -1,4 +1,8 @@ |
| 15 | 15 | validates :email, presence: true, uniqueness: { case_sensitive: false } |
| 16 | 16 | validates :password, confirmation: true, length: { in: 6..24 }, if: :password |
| 17 | 17 | validates :password_confirmation, presence: true, if: :password |
| 18 | ||
| 19 | def followed_by? user | |
| 20 | inverse_follows.where(follower_id: user.id).exists? | |
| 21 | end | |
| 18 | 22 | end |
| @@ -15,4 +15,8 @@ class User < ActiveRecord::Base |
| 54 | 54 | = link_to "お気に入りの解除", tweet_favorites_path(t), method: :delete |
| 55 | 55 | - else |
| 56 | 56 | = link_to "お気に入りに登録", tweet_favorites_path(t), method: :post |
| 57 | - if t.user.followed_by? current_user | |
| 58 | = link_to "フォロー解除", user_follows_path(t.user), method: :delete | |
| 59 | - else | |
| 60 | = link_to "フォロー", user_follows_path(t.user), method: :post | |
| @@ -54,3 +54,7 @@ |
| 16 | 16 | .num |
| 17 | 17 | #{@user.tweets.count} |
| 18 | 18 | %li |
| 19 | = link_to nil do | |
| 19 | = link_to follows_user_path(@user) do | |
| 20 | 20 | .text |
| 21 | 21 | フォロー |
| 22 | 22 | .num |
| @@ -16,7 +16,7 @@ |
| 1 | .col-xs-8#users-content | |
| 2 | = render partial: "users/header" | |
| 3 | .list-group | |
| @@ -0,0 +1,3 @@ |
| 1 | = div_for user, class: "list-group-item" do |u| | |
| 2 | %h4.user | |
| 3 | %span.user-name | |
| 4 | = link_to u.name, user_path(u) | |
| 5 | %span.user-id | |
| 6 | @#{u.name} | |
| 7 | .bio | |
| 8 | = u.bio | |
| 9 | .follow-unfollow | |
| 10 | - if u.followed_by? current_user | |
| 11 | = link_to "フォロー解除", user_follows_path(u), method: :delete | |
| 12 | - else | |
| 13 | = link_to "フォロー", user_follows_path(u), method: :post | |
| @@ -0,0 +1,13 @@ |
| 1 | 1 | .col-xs-8#users-content |
| 2 | 2 | = render partial: "users/header" |
| 3 | 3 | .list-group |
| 4 | = render @user.inverse_followers | |
| @@ -1,3 +1,4 @@ |
| 22 | 22 | .num |
| 23 | 23 | 0 |
| 24 | 24 | %li |
| 25 | = link_to nil do | |
| 25 | = link_to followers_user_path(@user) do | |
| 26 | 26 | .text |
| 27 | 27 | フォロワー |
| 28 | 28 | .num |
| @@ -22,7 +22,7 @@ |
| 1 | .col-xs-8#users-content | |
| 2 | = render partial: "users/header" | |
| 3 | .list-group | |
| 4 | = render @user.inverse_followers | |
| @@ -0,0 +1,4 @@ |
| 1 | 1 | .col-xs-8#users-content |
| 2 | 2 | = render partial: "users/header" |
| 3 | 3 | .list-group |
| 4 | = render @user.inverse_followers | |
| 4 | = render @user.followers | |
| @@ -1,4 +1,4 @@ |
| 15 | 15 | つぶやき |
| 16 | 16 | .num |
| 17 | 17 | #{@user.tweets.count} |
| 18 | %li | |
| 18 | %li{ class: action_name == "follows" ? "active" : nil } | |
| 19 | 19 | = link_to follows_user_path(@user) do |
| 20 | 20 | .text |
| 21 | 21 | フォロー |
| 22 | 22 | .num |
| 23 | 23 | 0 |
| 24 | %li | |
| 24 | %li{ class: action_name == "followers" ? "active" : nil } | |
| 25 | 25 | = link_to followers_user_path(@user) do |
| 26 | 26 | .text |
| 27 | 27 | フォロワー |
| @@ -15,13 +15,13 @@ |
| 20 | 20 | .text |
| 21 | 21 | フォロー |
| 22 | 22 | .num |
| 23 | 0 | |
| 23 | #{@user.inverse_followers.count} | |
| 24 | 24 | %li{ class: action_name == "followers" ? "active" : nil } |
| 25 | 25 | = link_to followers_user_path(@user) do |
| 26 | 26 | .text |
| 27 | 27 | フォロワー |
| 28 | 28 | .num |
| 29 | 0 | |
| 29 | #{@user.followers.count} | |
| 30 | 30 | %li{ class: action_name == "favorites" ? "active" : nil } |
| 31 | 31 | = link_to favorites_user_path(@user) do |
| 32 | 32 | .text |
| 33 | 33 | お気に入り |
| 34 | 34 | .num |
| 35 | 0 | |
| 35 | #{@user.favorites.count} | |
| @@ -20,16 +20,16 @@ |
| 2 | 2 | .list-group |
| 3 | 3 | .list-group-item.user-list |
| 4 | 4 | %h2 ユーザー一覧 |
| 5 | = div_for @users, class: "list-group-item" do |u| | |
| 6 | %h4.user | |
| 7 | %span.user-name | |
| 8 | = link_to render_user_screen_name(u), user_path(u) | |
| 9 | %span.user-id | |
| 10 | @#{u.name} | |
| 11 | .bio | |
| 12 | = u.bio | |
| 5 | = render partial: "users/user", collection: @users | |
| @@ -2,11 +2,4 @@ |
| 21 | 21 | .text |
| 22 | 22 | フォロー |
| 23 | 23 | .num |
| 24 | 0 | |
| 24 | = link_to current_user.inverse_followers.count, follows_user_path(current_user) | |
| 25 | 25 | .col-xs-4.follower-num |
| 26 | 26 | .text |
| 27 | 27 | フォロワー |
| 28 | 28 | .num |
| 29 | 0 | |
| 29 | = link_to current_user.followers.count, followers_user_path(current_user) | |
| 30 | 30 | - @tweet.errors.full_messages.each do |message| |
| 31 | 31 | .alert.alert-danger= message |
| 32 | 32 | = form_for @tweet do |f| |
| @@ -21,12 +21,12 @@ |
| 1 | _panel.html.haml | |
| 1 | - if logged_in? | |
| 2 | .panel.panel-default | |
| 3 | .panel-body | |
| 4 | = link_to user_path(current_user), class: "user" do | |
| 5 | %span.user-name | |
| 6 | = render_user_screen_name current_user | |
| 7 | %span.user-id | |
| 8 | @#{current_user.name} | |
| 9 | .user-activity | |
| 10 | .row | |
| 11 | .col-xs-4.tweets-num | |
| 12 | .text | |
| 13 | つぶやき | |
| 14 | .num | |
| 15 | = link_to current_user.tweets.count, user_path(current_user) | |
| 16 | .col-xs-4.follow-num | |
| 17 | .text | |
| 18 | フォロー | |
| 19 | .num | |
| 20 | = link_to current_user.inverse_followers.count, follows_user_path(current_user) | |
| 21 | .col-xs-4.follower-num | |
| 22 | .text | |
| 23 | フォロワー | |
| 24 | .num | |
| 25 | = link_to current_user.followers.count, followers_user_path(current_user) | |
| 26 | - @tweet.errors.full_messages.each do |message| | |
| 27 | .alert.alert-danger= message | |
| 28 | = form_for @tweet do |f| | |
| 29 | .form-group | |
| 30 | = f.text_area :content, class: "form-control" | |
| 31 | = f.submit "つぶやく", class: "btn btn-success" | |
| @@ -1,0 +1,31 @@ |
| 2 | 2 | .container |
| 3 | 3 | .row |
| 4 | 4 | .col-xs-4.left-content |
| 5 | - if logged_in? | |
| 6 | .panel.panel-default | |
| 7 | .panel-body | |
| 8 | = link_to user_path(current_user), class: "user" do | |
| 9 | %span.user-name | |
| 10 | = render_user_screen_name current_user | |
| 11 | %span.user-id | |
| 12 | @#{current_user.name} | |
| 13 | .user-activity | |
| 14 | .row | |
| 15 | .col-xs-4.tweets-num | |
| 16 | .text | |
| 17 | つぶやき | |
| 18 | .num | |
| 19 | = link_to current_user.tweets.count, user_path(current_user) | |
| 20 | .col-xs-4.follow-num | |
| 21 | .text | |
| 22 | フォロー | |
| 23 | .num | |
| 24 | = link_to current_user.inverse_followers.count, follows_user_path(current_user) | |
| 25 | .col-xs-4.follower-num | |
| 26 | .text | |
| 27 | フォロワー | |
| 28 | .num | |
| 29 | = link_to current_user.followers.count, followers_user_path(current_user) | |
| 30 | - @tweet.errors.full_messages.each do |message| | |
| 31 | .alert.alert-danger= message | |
| 32 | = form_for @tweet do |f| | |
| 33 | .form-group | |
| 34 | = f.text_area :content, class: "form-control" | |
| 35 | = f.submit "つぶやく", class: "btn btn-success" | |
| 36 | 5 | .col-xs-8.right-content |
| 37 | 6 | - if notice |
| 38 | 7 | .alert.alert-info= notice |
| @@ -2,37 +2,6 @@ |
| 2 | 2 | .container |
| 3 | 3 | .row |
| 4 | 4 | .col-xs-4.left-content |
| 5 | = render partial: "panel" | |
| 5 | 6 | .col-xs-8.right-content |
| 6 | 7 | - if notice |
| 7 | 8 | .alert.alert-info= notice |
| @@ -2,6 +2,7 @@ |
| 19 | 19 | .tweet-content |
| 20 | 20 | %p |
| 21 | 21 | = t.content |
| 22 | .content-footer | |
| 23 | - if t.favorited_by? current_user | |
| 24 | = link_to "お気に入りの解除", tweet_favorites_path(t), method: :delete | |
| 25 | - else | |
| 26 | = link_to "お気に入りに登録", tweet_favorites_path(t), method: :post | |
| 27 | - if t.user.followed_by? current_user | |
| 28 | = link_to "フォロー解除", user_follows_path(t.user), method: :delete | |
| 29 | - else | |
| 30 | = link_to "フォロー", user_follows_path(t.user), method: :post | |
| @@ -19,12 +19,3 @@ |
| 10 | 10 | .tweet-content |
| 11 | 11 | %p |
| 12 | 12 | = t.content |
| 13 | .content-footer | |
| 14 | - if t.favorited_by? current_user | |
| 15 | = link_to "お気に入りの解除", tweet_favorites_path(t), method: :delete | |
| 16 | - else | |
| 17 | = link_to "お気に入りに登録", tweet_favorites_path(t), method: :post | |
| 18 | - if t.user.followed_by? current_user | |
| 19 | = link_to "フォロー解除", user_follows_path(t.user), method: :delete | |
| 20 | - else | |
| 21 | = link_to "フォロー", user_follows_path(t.user), method: :post | |
| @@ -10,3 +10,12 @@ |
| 7 | 7 | - if notice |
| 8 | 8 | .alert.alert-info= notice |
| 9 | 9 | .list-group |
| 10 | = div_for @tweets, class: "list-group-item" do |t| | |
| 11 | %h4.user | |
| 12 | %span.user-name | |
| 13 | = link_to render_user_screen_name(t.user), user_path(t.user) | |
| 14 | %span.user-id | |
| 15 | @#{t.user.name} | |
| 16 | %span.time.pull-right | |
| 17 | = distance_of_time_in_words_to_now(t.created_at) | |
| 18 | .clear | |
| 19 | .tweet-content | |
| 20 | %p | |
| 21 | = t.content | |
| 10 | = render partial: "users/tweet", collection: @tweets | |
| @@ -7,15 +7,4 @@ |
| 1 | #tweets-content | |
| 2 | .container | |
| 3 | .row | |
| 4 | .col-xs-4.left-content | |
| 5 | = render partial: "panel" | |
| 6 | .col-xs-8.right-content | |
| 7 | - if notice | |
| 8 | .alert.alert-info= notice | |
| 9 | .list-group | |
| 10 | = render partial: "users/tweet", collection: @tweets | |
| @@ -0,0 +1,10 @@ |
| 7 | 7 | - if notice |
| 8 | 8 | .alert.alert-info= notice |
| 9 | 9 | .list-group |
| 10 | .list-group-item | |
| 11 | .btn-group | |
| 12 | = link_to "すべてのつぶやき", tweets_path, class: "active btn btn-primary" | |
| 13 | = link_to "フォローしている人のつぶやき", timeline_tweets_path, class: "btn btn-default" | |
| 10 | 14 | = render partial: "users/tweet", collection: @tweets |
| @@ -7,4 +7,8 @@ |
| 7 | 7 | - if notice |
| 8 | 8 | .alert.alert-info= notice |
| 9 | 9 | .list-group |
| 10 | .list-group-item | |
| 11 | .btn-group | |
| 12 | = link_to "すべてのつぶやき", tweets_path, class: "active btn btn-primary" | |
| 13 | = link_to "フォローしている人のつぶやき", timeline_tweets_path, class: "btn btn-default" | |
| 10 | 14 | = render partial: "users/tweet", collection: @tweets |
| @@ -7,4 +7,8 @@ |
| 6 | 6 | @favorite = current_user.favorites.build(tweet: @tweet) |
| 7 | 7 | |
| 8 | 8 | if @favorite.save |
| 9 | redirect_to tweets_url, notice: "お気に入りに登録しました" | |
| 9 | redirect_to request.referer, notice: "お気に入りに登録しました" | |
| 10 | 10 | else |
| 11 | redirect_to tweets_url, alert: "このツイートはお気に入りに登録できません" | |
| 11 | redirect_to request.referer, alert: "このツイートはお気に入りに登録できません" | |
| 12 | 12 | end |
| 13 | 13 | end |
| 14 | 14 | |
| 15 | 15 | def destroy |
| 16 | 16 | @favorite = current_user.favorites.find_by!(tweet_id: params[:tweet_id]) |
| 17 | 17 | @favorite.destroy |
| 18 | redirect_to tweets_url, notice: "お気に入りを解除しました" | |
| 18 | redirect_to request.referer, notice: "お気に入りを解除しました" | |
| 19 | 19 | end |
| 20 | 20 | end |
| @@ -6,15 +6,15 @@ def create |
| 5 | 5 | @user = User.find(params[:user_id]) |
| 6 | 6 | |
| 7 | 7 | if @user.inverse_follows.create(follower: current_user) |
| 8 | redirect_to tweets_url, notice: "フォローしました" | |
| 8 | redirect_to request.referer, notice: "フォローしました" | |
| 9 | 9 | else |
| 10 | redirect_to tweets_url, alert: "フォローできません" | |
| 10 | redirect_to request.referer, alert: "フォローできません" | |
| 11 | 11 | end |
| 12 | 12 | end |
| 13 | 13 | |
| @@ -5,9 +5,9 @@ def create | ||
| 15 | 15 | @user = User.find(params[:user_id]) |
| 16 | 16 | follow = @user.inverse_follows.find_by(follower: current_user.id) |
| 17 | 17 | follow.destroy |
| 18 | redirect_to tweets_url, notice: "フォローを解除しました" | |
| 18 | redirect_to request.referer, notice: "フォローを解除しました" | |
| 19 | 19 | end |
| 20 | 20 | end |
| @@ -15,6 +15,6 @@ def destroy |