チュートリアルをやる。マイクロポスト編
これは以下のチュートリアルをやったことのログである。
ミニメッセージを投稿できるようにしたい。
そのためのモデルををつくる。
- データ検証する
- 発言したユーザが破棄されれば自動で破棄される。
マイクロポストモデル
rails generate model Micropost content:string user_id:integer
マイグレーションファイルが生成されるので
複合indexを付け加える。
db/migrate/20150917014646_create_microposts.rb
class CreateMicroposts < ActiveRecord::Migration def change create_table :microposts do |t| t.string :content t.integer :user_id t.timestamps end add_index :microposts, [:user_id, :created_at] end end
マグレーション
bundle exec rake db:migrate bundle exec rake db:test:prepare
Userモデルとマイクロポストモデルの関連付け
複数のマイクロポストがユーザと関連づいている。
- マイクロポスト側は所属先(belong_to)を持つ
- ユーザは発言したマイクロポストを複数所有する(has_many)
app/models/micropost.rb
class Micropost < ActiveRecord::Base belongs_to :user default_scope -> { order('created_at DESC') } validates :user_id, presence: true end
created_atに関して降順にならぶようにしている。
app/models/user.rb
class User < ActiveRecord::Base has_many :microposts
ユーザが削除されるとマイクロポストも破棄されるようにする。
class User < ActiveRecord::Base has_many :microposts, dependent: :destroy ....
のように:destroyオプションをつければ良い。
マイクロポストのメッセージ数を制限する。
class Micropost < ActiveRecord::Base .... validates :content, presence: true, length: { maximum: 140 } end
最大140字までに制限。
ユーザページにマイクロポストを表示させる。
ユーザのshow画面の持っているマイクロポストを表示させる。
マイクロポストの部分はパーシャルページで作る。
app/views/users/show.html.erb
<% provide(:title, @user.name) %> <div class="row"> ... <aside class="span4"> ... </aside> <div class="span8"> <% if @user.microposts.any? %> <h3>Microposts (<%= @user.microposts.count %>)</h3> <ol class="microposts"> <%= render @microposts %> </ol> <%= will_paginate @microposts %> <% end %> </div> </div>
app/views/microposts/_micropost.html.erb
<li> <span class="content"><%= micropost.content %></span> <span class="timestamp"> Posted <%= time_ago_in_words(micropost.created_at) %> ago. </span> </li>
コントロ−ラをでデータを入れる。
app/controllers/users_controller.rb
class UsersController < ApplicationController ... def show @user = User.find(params[:id]) @microposts = @user.microposts.paginate(page: params[:page]) end ....
マイクロポストのサンプルを作る。
lib/tasks/sample_data.rake
namespace :db do desc "Fill database with sample data" task populate: :environment do admin = User.create!(name: "Example User", email: "example@railstutorial.jp", password: "foobar", password_confirmation: "foobar", admin: true) 99.times do |n| name = Faker::Name.name email = "example-#{n+1}@railstutorial.jp" password = "password" User.create!(name: name, email: email, password: password, password_confirmation: password) end users = User.all(limit: 6) 50.times do content = Faker::Lorem.sentence(5) users.each { |user| user.microposts.create!(content: content)} end end end
データベース更新
bundle exec rake db:reset bundle exec rake db:populate bundle exec rake db:test:prepare
ローカルサーバで確認する。
rails server
CSS指定
app/assets/stylesheets/custom.css.scss
.... /* microposts */ .microposts { list-style: none; margin: 10px 0 0 0; li { padding: 10px 0; border-top: 1px solid #e8e8e8; } } .content { display: block; } .timestamp { color: $grayLight; } .gravatar { float: left; margin-right: 10px; } aside { textarea { height: 100px; margin-bottom: 5px; } }
マイクロポスト操作用のコントローラ作成
ルーティング
config/routes.rb
SampleApp::Application.routes.draw do resources :users resources :sessions, only: [:new, :create, :destroy] resources :microposts, only: [:create, :destroy] ....
作成と破棄しかいらない。
signed_in_userメソッドの移動
Userコントローラで使用していたがMicropostコントローラでも使いたいので
SessionsHelperに引っ越す。
module SessionsHelper ... def current_user?(user) user == current_user end def signed_in_user unless signed_in? store_location redirect_to signin_url, notice: "Please sign in." unless signed_in? end end .... end
Micropostsコントローラ作成
app/controllers/microposts_controller.rb
class MicropostsController < ApplicationController before_action :signed_in_user, only: [:create, :destroy] def index end def create @micropost = current_user.microposts.build(micropost_params) if @micropost.save flash[:success] = "Micropost created!" redirect_to root_url else render 'static_pages/home' end end def destroy end end
Micropost作成画面作成
app/controllers/microposts_controller.rb
class MicropostsController < ApplicationController ... def create @micropost = current_user.microposts.build(micropost_params) if @micropost.save flash[:success] = "Micropost created!" redirect_to root_url else render 'static_pages/home' end end .. private def micropost_params params.require(:micropost).permit(:content) end end
ビュー
<% if signed_in? %> <div class="row"> <aside class="span4"> <section> <%= render 'shared/user_info' %> </section> <section> <%= render 'shared/micropost_form' %> </section> </aside> </div> <% else %> <div class="center hero-unit"> <h1>Welcome to the Sample App</h1> <h2> This is the home page for the <a href="http://railstutorial.jp/">Ruby on Rails Tutorial</a> sample application. </h2> <%= link_to "Sign up now!", signup_path, class: "btn btn-large btn-primary" %> </div> <%= link_to image_tag("rails.png", alt: "Rails"), 'http://rubyonrails.org/' %> <% end %>
サインインしていたらマイクロポスト作成画面(パーシャル)がでるようになっている。
ユーザ情報パーシャル
app/views/shared/_user_info.html.erb
<a href="<%= user_path(current_user) %>"> <%= gravatar_for current_user, size: 52 %> </a> <h1> <%= current_user.name %> </h1> <span> <%= link_to "view my profile", current_user %> </span> <span> <%= pluralize(current_user.microposts.count, "micropost") %> </span>
マイクロポスト作成用パーシャル
app/views/shared/_micropost_form.html.erb
<%= form_for(@micropost) do |f| %> <%= render 'shared/error_messages', object: f.object %> <div class="field"> <%= f.text_area :content, placeholder: "Compose new micropost..." %> </div> <%= f.submit "Post", class: "btn btn-large btn-primary" %> <% end %>
@micropostをもらう。
app/controllers/static_pages_controller.rb
class StaticPagesController < ApplicationController def home @micropost = current_user.microposts.build if signed_in? end .... end
エラー表示の修正
<% if object.errors.any? %> <div id="error_explanation"> <div class="alert alert-error"> The form contains <%= pluralize(object.errors.count, "error") %>. </div> <ul> <% object.errors.full_messages.each do |msg| %> <li>* <%= msg %></li> <% end %> </ul> </div> <% end %>
@userをobjectに変換。(@micropostも渡せるようにしたため)
同様に他でエラーメッセージに@userを渡しているところを修正する。
app/views/users/new.html.erb
<% provide(:title, 'Sign up') %> <h1>Sign up</h1> <div class="row"> <div class="span6 offset3"> <%= form_for(@user) do |f| %> <%= render 'shared/error_messages', object: f.object %> ... <% end %> </div> </div>
app/views/users/edit.html.erb
<% provide(:title, "Edit user") %> <h1>Update your profile</h1> <div class="row"> <div class="span6 offset3"> <%= form_for(@user) do |f| %> <%= render 'shared/error_messages', object: f.object %> ... <% end %> <%= gravatar_for @user %> <a href="http://gravatar.com/emails">change</a> </div> </div>
ローカルで確認
rails server
ポスト画面とポストした一覧(フィード)が同時に表示したい
指定したユーザidのマイクロポストを取得できるようにする。
class User < ActiveRecord::Base ... def feed # このコードは準備段階です。 # 完全な実装は第11章「ユーザーをフォローする」を参照してください。 Micropost.where("user_id = ?", id) end .... end app/controllers/static_pages_controller.rb >|ruby| class StaticPagesController < ApplicationController def home if signed_in? @micropost = current_user.microposts.build @feed_items = current_user.feed.paginate(page: params[:page]) end end ... end
フィード全体のパーシャル
app/views/shared/_feed.html.erb
<% if @feed_items.any? %> <ol class="microposts"> <%= render partial: 'shared/feed_item', collection: @feed_items %> </ol> <%= will_paginate @feed_items %> <% end %>
フィード一個のパーシャル
app/views/shared/_feed_item.html.erb
<li id="<%= feed_item.id %>"> <%= link_to gravatar_for(feed_item.user), feed_item.user %> <span class="user"> <%= link_to feed_item.user.name, feed_item.user %> </span> <span class="content"><%= feed_item.content %></span> <span class="timestamp"> Posted <%= time_ago_in_words(feed_item.created_at) %> ago. </span> </li>
homeページにフィード追加
app/views/static_pages/home.html.erb
<% if signed_in? %> <div class="row"> ... <div class="span8"> <h3>Micropost Feed</h3> <%= render 'shared/feed' %> </div> </div> <% else %> ... <% end %>
ここまででローカルサーバで確認
rails server
しかし、このままでは投稿が失敗すると@feed_itemsがないため落ちる。
これを回避するために失敗時に空のインスタンスを入れておく。
app/controllers/microposts_controller.rb
class MicropostsController < ApplicationController ... def create @micropost = current_user.microposts.build(micropost_params) if @micropost.save flash[:success] = "Micropost created!" redirect_to root_url else @feed_items = [] render 'static_pages/home' end end ... end
マイクロポストの削除
自分の発言のみ削除できるようにする。
app/views/microposts/_micropost.html.erb
>|html
<%= micropost.content %>
<% if current_user?(micropost.user) %>
<%= link_to "delete", micropost, method: :delete,
data: { confirm: "You sure?" },
title: micropost.content %>
<% end %>
フィードにも
app/views/shared/_feed_item.html.erb
<li id="<%= feed_item.id %>"> <%= link_to gravatar_for(feed_item.user), feed_item.user %> <span class="user"> <%= link_to feed_item.user.name, feed_item.user %> </span> <span class="content"><%= feed_item.content %></span> <span class="timestamp"> Posted <%= time_ago_in_words(feed_item.created_at) %> ago. </span> <% if current_user?(feed_item.user) %> <%= link_to "delete", feed_item, method: :delete, data: { confirm: "You sure?" }, title: feed_item.content %> <% end %> </li>
destroyアクション
app/controllers/microposts_controller.rb
class MicropostsController < ApplicationController before_action :signed_in_user, only: [:create, :destroy] before_action :correct_user, only: :destroy def index end .... def destroy @micropost.destroy redirect_to root_url end private def micropost_params params.require(:micropost).permit(:content) end def correct_user @micropost = current_user.microposts.find_by(id: params[:id]) redirect_to root_url if @micropost.nil? end end
ローカルサーバで確認する
rails server