railstutorial やったメモ
これなに
railstutorial 読んだ個人のメモで、中身は railstutorial のままだし人の為にならないやつ
経緯とか
Web系だし Ruby とか RoR 教養として学びたかった
そもそも Ruby ほとんど知らないしこの辺読んで、 s-99 を60までだが Ruby でやったりした
で、railstutorial 読みつつ考えながら写経した感じ
s-99 やってて思ったのは Scala みたいにコレクション関数多かったりとかで、ありがたいとかそんな感じ
railstutorial 分量多くて3日ぐらいかかったしそこそこ長い、けどまぁ普通にもっとやる必要あるし、gem知らなさ過ぎるからつらい感じ
全部通して書いたらテスト143個とか出来てたし、3章から11章が繋がってて git で適当にコミットしてたら3桁ぐらいコミット数あった感じ
rails/rails-dev-box · GitHub あったからこれいじって使ってた
あれだな
大体困ったら spork 再起動させるか `rake test:prepare` とか
後一応 bootstrap が古かったのと、bcryptあたりで少し詰まったぐらい
最初
環境作る
$ gem install rails # 雛形作りたい $ rails new MyApp --skip-bundle
bundle
開発環境は
$ bundle install --without production
以降rvm使ってるから `bundle exec` 省く
rails generate
scaffoldとか、個々にmodelとかcontollerとか
注意として、コントローラは複数形、モデルは単数形、scaffoldは単数形
$ rails g scaffold CamelCaseModelName name:string $ rails generate controller Users new $ rails generate model User name:string email:string
rspec使う時はgenerate に --no-test-framework つけて、rspecのテストを以下で生成
$ rails g rspec:controller ... $ rails g rspec:integration ... $ rails g rspec:model ... $ rails g rspec:scaffold ...
つか大体ヘルプ見ろって話
$ rails g --help $ rails g scaffold --help
3章
rspec入れる
$ rails generate rspec:install
rspec使う際にgeneraterでテストページを作成しないようにする
$ rails generate controller StaticPages home help --no-test-framework
キャメルケースでジェネレータに渡すとスネークケースのものが出来る
rake db:rollback について
migrationファイルが無い場合は削除がされないため、rails destroy modelする前にrollbackする必要がある
誤って先にrails destroy modelなどでmigration fileを消してしまった場合、再度作成した上でrollbackしても良いが、手で消す際にはdb/schema.rbも変更されているためrake db:schema:dumpでdb/schema.rbも更新すべき
rspec integration test
$ rails g rspec:integration static_pages
spec/spec_helper.rb で Capybara を使う事を指定
RSpec.configure do |config| . . config.include Capybara::DSL end
テストコード例
require 'spec_helper' describe "StaticPages" do describe "index page" do it do visit '/static_pages/home' expect(page).to have_content('Home') end end end
application.html.erb を使った構造化
application.html.erb
<title>Ruby on Rails Tutorial Sample App | <%= yield(:title) %></title> <%= yield %>
各種ファイル
<% provide(:title, 'Contact') %> <!-- yield(:title) --> <h1>Contact</h1> <!-- ここが yield -->
guard を用いたテストの自動化
Gemfile
gem 'guard-rspec' gem 'growl' # for OS X
guardの初期化
$ guard init rspec
Guardfileに追記する例
`all_after_pass: false`で落ちた際に他のテストが実行されないようにする
require 'active_support/inflector' guard 'rspec', all_after_pass: false do . # Custom Rails Tutorial specs watch(%r{^app/controllers/(.+)_(controller)\.rb$}) do |m| ["spec/routing/#{m[1]}_routing_spec.rb", "spec/#{m[2]}s/#{m[1]}_#{m[2]}_spec.rb", "spec/acceptance/#{m[1]}_spec.rb", (m[1][/_pages/] ? "spec/requests/#{m[1]}_spec.rb" : "spec/requests/#{m[1].singularize}_pages_spec.rb")] end watch(%r{^app/views/(.+)/}) do |m| (m[1][/_pages/] ? "spec/requests/#{m[1]}_spec.rb" : "spec/requests/#{m[1].singularize}_pages_spec.rb") end watch(%r{^app/controllers/sessions_controller\.rb$}) do |m| "spec/requests/authentication_pages_spec.rb" end . end
実行
$ guard
sporkを用いたテストの高速化
テスト用のサーバを立ち上げ環境を読み込んでおくもの
どっちかってっとguardよりこっちの方が嬉しい?
Gemfile
gem 'spork-rails', '4.0.0' gem 'guard-spork', '1.5.0' gem 'childprocess', '0.3.6'
初期化
$ spork --bootstrap
設定は`spec/spec_helper.rb`に prefork 云々書くのと、`.rspec`に`--drb`を追記
require 'spork' Spork.prefork do ENV["RAILS_ENV"] ||= 'test' RSpec.configure do |config| ...
実行
$ spork
注意として、routes増やした時とか spork 再起動しないとテストの際に No route matches になる
guard使う際は他[http://railstutorial.jp/chapters/static-pages?version=4.0#sec-spork_and_guard:title=Guardfileの変更も必要
4章
application全体の views で使えるhelper関数をapplication_helperで定義
ここで書かれたものは全ての views から呼び出せて、 controllers からは呼べない
5章
views の便利関数
<%= link_to "sample app", '#', id: "logo" %> <%= link_to image_tag("rails.png", alt: "Rails"), 'http://rubyonrails.org/' %>
bootstrap-sass と Asset Pipeline の互換性を保つ
config/application.rb
module SampleApp class Application < Rails::Application ... config.assets.precompile += %w(*.png *.jpg *.jpeg *.gif) end end
Assets Pipeline
app/assets/stylesheets 以下に置かれたスタイルシートは application.css の一部として勝手にインクルードされる
app/assets/stylesheets/custom.css.scss のようなファイルに対し、拡張子を見て勝手に sass で処理する
custom.css.scss に対し、 bootstrap を使う事を記述する
@import "bootstrap";
このまま custom.css.scss に色々書くと、 application.css に入るため、全ページ共通のレイアウトが適用される
view の構造化
再利用するものはrender使う
app/views/layouts/_header.html.erb とか書いて、application.html.erbから以下のようにして使う
<%= render 'layouts/header' %>
Asset
- app/assets: 現在のアプリケーション固有のアセット
- lib/assets: あなたの開発チームによって作成されたライブラリ用のアセット
- vendor/assets: サードパーティのアセット
app/assets/stylesheets/application.css
*= require_tree . <- app/assets/stylesheetsの全てのCSSファイルをapplication.cssに突っ込む
*= require_self <- このファイルに書かれた内容も含める
プリプロセッサは拡張子を見て勝手に実行される
foobar.js.erb.coffee の場合、 coffeeのコンパイルの後に erbのコンパイルが実行される
routes の書き換え
以下の場合、 `/static_pages/about` にアクセスしても404
- get 'static_pages/home' - get 'static_pages/about' + root 'static_pages#home' + match '/about', to: 'static_pages#about', via: 'get'
routes関連の便利メソッド
上記routesに関して
root_path => '/' root_url => 'http://localhost:3000/' about_path => '/about' about_url => 'http://localhost:3000/about'
以下のような感じで使える
<%= link_to "About", about_path %>
`rake routes` した時のPREFIXに_pathや_urlを付けれる
specをもっと綺麗に書く
テストヘルパーは spec/support/*.rb, spec/support/*/*.rb に書く
spec/spec_helper.rb を見ればその辺のファイルが勝手に読み込まれる事が書いてある
もし spec/helpers/application_helper_spec.rb とかでhelperのテストをしてるのなら spec/support/*.rb の中身は `include ApplicationHelper` と1行書くだけでもいい
注意として、sporkを使ってる場合はsupportを書いた後にsporkサーバを再起動する
it のブロック内に対して subject で指定されたものがテストの対象となる
describe で作ったブロック内で before { visit *_path } とかして subject { page } とかすると describe 毎に it 内の page が変化して楽 (Capybara 使ってる場合)
同じような内容に関するテストについては、shared example使うともっとまとめたりも出来る
before について、ネストされたブロックにおける before は 全てのブロック毎に、親ブロックの before が毎回実行された後に子ブロックの before が実行される
6章
モデルの作成
$ rails generate model User name:string email:string --no-test-framework
$ rails generate rspec:model User
dbにmigrate
$ rake db:migrate
戻すのは `rake db:rollback`
ActiveRecord
データベースのテスト
db/development.sqlite3 を db/test.sqlite3 に反映するやつ
migrationした後に毎度実行する
$ rake db:test:prepare
データベースを空にするのは
$ rake db:reset
モデルの検証
先に色々
- エラーは user.erros.full_messages とかで中見れる
- user.valid? で妥当かどうか調べれる
検証追加は validates 関数の第二引数にハッシュを加えまくる
validates :name, presence: true, length: { maximum: 50 }
- 一覧
注意: uniqueness: true でも連続クリックとかで通ってしまうので一意性はDBのスキーマに定義させるべし
あるて~用のmigrationファイルのじぇねれーと
$ rails generate migration add_index_to_users_email
add_{index/カラム名}_to_{テーブル名} って感じ
以下見たいのも出来る
$ rails generate migration add_age_to_users age:integer
modelの保存前に実行するやつとか
class User < ActiveRecord::Base before_save { self.email = email.downcase } end
普通のパスワードログイン
モデルでパスワードの制限与えるのが慣習らしく、以下の2つの事を行う
- Gemfile に bcrypt 追加して、users テーブルに password_digest:string カラム持たせる
- モデルに `has_secure_password` という1行を加える
- モデルに自動的に password と password_confirmation という属性が加わる
ユーザ認証について、例としてemailをIDに、パスワード認証する方法
user = User.find_by(email: email)
current_user = user.authenticate(password)
authenticate の返り値はユーザもしくはfalse
undefined method `cost' for BCrypt::Engine:Class ってので少し詰まったけど、これでいけた
http://stackoverflow.com/questions/21853579/rails-nomethoderror-undefined-method-cost-for-bcryptengineclass
テストが遅い
BCryptのコストファクターをテスト環境向けに再定義する。
config/environments/test.rb
SampleApp::Application.configure do ... # bcrypt'のコスト関数を下げることでテストの速度を向上させる。 ActiveModel::SecurePassword.min_cost = true end
rspec の更なる構造化
一応、モデルをDBと連携してテストする際、saveしなければ保存されないので注意
- let について
- let(:found_user) { user }
- スコープの狭いローカル変数を作れる
- specify について
- it と等しく、英語としての正しさのために用いる
- its について、英語通り
- its(:name) { should_not be_blank }
7章
Rails.env
RAILS_ENV の値を取って色んな情報をviewに表示する
application.html.erb
<%= debug(params) if Rails.env.development? %>
RAILS_ENV は test, development, production の3つがデフォルトで用意されてる
rails console は RAILS_ENV の値を引数に渡せる
$ rails console test
rails sever の場合は --environment で付け加える
production 環境に migrate する時は以下な感じ
$ RAILS_ENV=production rake db:migrate
routes++
`resource :users` という記述はRESTスタイルのURLを追加する
controller
パラメータは `params[:id]` とすることで文字列で取得出来る
`User.find(params[:id])` とかすると勝手に整数型に変換される
FactoryGirl
- gem に追加
- spec/factories.rb
FactoryGirl.define do factory :user do name "Michael Hartl" email "michael@example.com" password "foobar" password_confirmation "foobar" end end
- テスト
オプションで少し値変えるとかも出来る
let(:user) { FactoryGirl.create(:user) } let(:wrong_user) { FactoryGirl.create(:user, email: "wrong@example.com") }
gravatarの画像を取得する
gravatar の仕様に沿って、userページ用のヘルパー関数を作る
app/helpers/users_helper.rb
module UsersHelper # 与えられたユーザーのGravatar (http://gravatar.com/) を返す。 def gravatar_for(user) gravatar_id = Digest::MD5::hexdigest(user.email.downcase) gravatar_url = "https://secure.gravatar.com/avatar/#{gravatar_id}" image_tag(gravatar_url, alt: user.name, class: "gravatar") end end
Railsのform_forメソッド
<%= form_for(@user) do |f| %> <%= f.label :name %> <%= f.text_field :name %> <%= f.label :password %> <%= f.password_field :password %> <%= f.submit "Create my account", class: "btn" %> <% end %>
Capybara を使ってフォームの送信テスト
change はブロックの実行前と後でクエリを吐いて比較を行うもの
by は値の増減値
上で作成したformに対して、以下のように扱う事が出来る
before do visit signup_path fill_in "Name", with: "Example User" end it "should create a user" do let(:submit) { "Create my account" } expect { click_button submit }.to change(User, :count).by(1) end
create に関して
以前のRailsではモデルに attr_accessible を利用することで、パラメータに制限をかけていた
現在のRails4.0ではコントローラ層で create に入れる値を検証する(Strong Parameters)
create に params をそのまま渡すとエラーが発生するようになっている
方法は以下の通り
params.require(:user).permit(:name, :email, :password, :password_confirmation)
private 関数にすると便利
def create @user = User.new(user_params) ... end private def user_params params.require(:user).permit(:name, :email, :password, :password_confirmation) end
formのエラー表示
app/views/shared/_error_messages.html.erb とか作って form_for の次の行へ `<%= render 'shared/error_messages' %>` 突っ込む
<% if @user.errors.any? %> <div id="error_explanation"> <div class="alert alert-error"> The for contains <%= pluralize(@user.errors.count, "errors") %>. </div> <ul> <% @user.errors.full_messages.each do |msg| %> <li>* <%= msg %></li> <% end %> </ul> </div> <% end %>
flush
flash は ハッシュで表される
<% flash.each do |key, value| %> <%= content_tag(:div, value, class: "alert alert-#{key}") %> <% end %>
controller
if @user.save flash[:success] = "Welcome to the Sample App!" redirect_to @user ...
上記のflushは次のリンク先へ遷移した時も永続的に残るが、1つの画面のみで表示させたい時は以下のものを用いる
flash.now[:success] = "Welcome to the Sample App!"
8章
ユーザのログインの状態を cookie を使って保持する
ページ遷移先においてもサインイン状態を保持するために、 session 関数を用いるのだが、Railsの session は cookie を用いているため、このような取り扱いになる
永続的なセッションを作るために、サインインしたユーザに恒久的な識別子を与える設計をここでは採用する
識別子は User モデルの属性として table に保持する
なお、トークンは暗号化したものを保存しておき、新しいセッションを作成するたびにトークンは更新されるべきである
RESTfulの一部を実装する
routest
resources :sessions, only: [:new,:create, :destroy]
Capybara++
it { should have_selector('div.alert.alert-error', text: 'Invalid') } it { should have_link('Sign out', href: signout_path) }
モデルの無いform
form_forはモデルオブジェクトを渡さなくても使える
ressources という resources に準拠させる場合、以下のような引数を渡せば良い
form_for(:session, url: sessions_path)
注意: ユーザ登録フォームは `form_for` を使うのが一般的であるが、その他のフォームについては `form_tag` を使う
コントローラで利用出来るヘルパー
通常 Helper は各種 view で自動的に include されるものであって、コントローラでは利用出来ない
そこで、ヘルパーを全てのコントローラで利用出来る用にするには、以下のように ApplicationController へ include させれば良い
app/controllers/application_controller.rb
class ApplicationController < ActionController::Base ... include SessionsHelper end
セキュアなトークンをユーザに付与させる
before_create には関数名シンボルを渡せる
ランダムトークン生成は `SecureRandom.urlsafe_base64`
class User < ActiveRecord::Base before_create :create_remember_token ... def User.new_remember_token SecureRandom.urlsafe_base64 end def User.encrypt(token) Digest::SHA1.hexdigest(token.to_s) end private def create_remember_token self.remember_token = User.encrypt(User.new_remember_token) end end
ログインユーザを取得するSessionsHelperについて
ゲッターでは @current_user がいなければ取得する、的なコードになる
module SessionsHelper def sign_in(user) remember_token = User.new_remember_token cookies.permanent[:remember_token] = remember_token user.update_attribute(:remember_token, User.encrypt(remember_token)) self.current_user = user end def sign_out self.current_user = nil cookies.delete(:remember_token) end def current_user=(user) @current_user = user end def current_user remember_token = User.encrypt(cookies[:remember_token]) @current_user ||= User.find_by(remember_token: remember_token) end end
この SessionsHelper が ApplicationController へ include されている場合、どこからでも current_user 関数を呼び出せ、ログイン状態が取得出来る
Cucumber を使う
Cucumber はBDDツール
あんま使いたくない
9章
Capybara-
get とか patch とか指定して直接リクエストを投げて page に突っ込む事とか出来る
describe "submitting to the update action" do before { patch user_path(user) } specify { expect(response).to redirect_to(signin_path) } end
ユーザが他のユーザの設定変更するとかの、複数の状態使うテストとかする際に用いる感じ
あれだ、こっちの方が慣れてて良いなぁって思ったけど、テスト落ちる時のログが辛い
before_action
コントローラのアクションを呼ぶ前に認可とか出来るやつ
only で特定のメソッドのみに適用
ついでに redirect_to にはオプションで flash の値とか渡せる
class UsersController < ApplicationController before_action :signed_in_user, only: [:edit, :update] ... private # Before actions def signed_in_user redirect_to signin_url, notice: "Please sign in." unless signed_in? end
フレンドリーフォワーディング
ログインの要るページにアクセスしてログインした場合に、行きたかったページへ redirect するやつ
はてなグループにないやつ
適当な場所で session に request.url して、ログイン後にそれ見て中身消したりそんな感じ
Rakeのタスクを追加する
適当なユーザ作りまくるやつとかこんな感じ (faker 使ってるけど別にいらん)
lib/tasks/sample_data.rake
namespace :db do desc "Fill database with sample data" task populate: :environment do User.create!(name: "Example User", email: "example@railstutorial.jp", password: "foobar", password_confirmation: "foobar") 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 end end
FactoryGirl++
sequences ってメソッドがあって、複数のモデル一度に作れるっぽい
spec/factories.rb にはこんな感じで書く
FactoryGirl.define do factory :user do sequence(:name) { |n| "Person #{n}" } sequence(:email) { |n| "person_#{n}@example.com"} password "foobar" password_confirmation "foobar" end end
こんな感じで使うらしい
before(:all) { 30.times { FactoryGirl.create(:user) } }
ページネーション
Gemfile: will_pagenate 使う感じ
kaminari? とか有名なんじゃなかったっけ知らん
gem 'will_paginate' gem 'bootstrap-will_paginate'
これ追加してると、勝手にモデルクラスに `paginate` ってメソッドが生える
使う際は、controllerにこんな感じ
def index @users = User.paginate(page: params[:page]) end
view は `<%= will_pagenate %>` 入れるだけで、入れたとこがページャになる感じ
ただし、これは users のコンテキスト以下だからであり、もし user が microposts 持っててそれをページングする際は `<%= will_pagenate @microposts %>` とかって書く
Rails の views++
_user.html.erb とか作ったら <%= render user %> とかするんだけど、
<ul class="users"> <% @users.each do |user| %> <%= render user %> <% end %> </ul>
は
<ul class="users"> <%= render @users %> </ul>
って書ける
Capybara++
`match: :first` : 初めに見つけたリンクをクリックするようになる
click_link('delete', match: :first)
10章
migration++
migration時にindex張るのこんな感じらしい
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
1:多関連
user が複数の microposts を持つ関連がある時、以下のようなメソッドを生やす
メソッド | 用途 |
---|---|
micropost.user | マイクロポストに関連付けられたユーザーオブジェクトを返す。 |
user.microposts | ユーザーのマイクロポストの配列を返す。 |
user.microposts.create(arg) | マイクロポストを作成する (user_id = user.id)。 |
user.microposts.create!(arg) | マイクロポストを作成する (失敗した場合は例外を発生する)。 |
user.microposts.build(arg) | 新しいMicropostオブジェクトを返す (user_id = user.id)。 |
新しい micropost を増やす際には、 build を使えば user_id を指定せずに済む
micropost に user_id:integer を持たせつつ、以下のような記述をモデルに加える事で1:多が出来る
class Micropost < ActiveRecord::Base belongs_to :user end class User < ActiveRecord::Base has_many :microposts end
`has_many :microposts, dependent: :destroy` とかすると、 user が削除された時に関連する microposts も全て消される
1:多に対するFactoryGirl設定
以下の様な記述にすることで、micropost 生成時に user を渡せば良い
factory :micropost do content "Lorem ipsum" user end
使い方
FactoryGirl.create(:micropost, user: @user, created_at: 1.day.ago)
Rspec++
let は遅延評価で let! は即時評価される
model の default_scope
例えば以下のようにすると、デフォルトで `created_at` 順のオーダーで引かれる
class Micropost < ActiveRecord::Base default_scope -> { order('created_at DESC') } end
-> について
Proc とか lambda と呼ばれるらしい
ブロックを遅延評価する際に `-> { puts "foo }` って記述する
明示的に直接呼びたいとかなら、 `-> { puts "foo" }.call` とかすればいい
render に対して引数を与える
と、直接 partial の中で以下の場合だと object が使える
f.object は `form_for(@user) do |f|` における @user
<%= render 'shared/error_messages', object: f.object %>
ActiveRecord の where でバインディング
Micropost.where("user_id = ?", id)
render partial++
- collection などのパラメータ渡す時は、 partial
- を明示しなくてはならない
- partial は
- collection などの引数が無い場合には省略が可能となる
ちなみに :collection パラメータは、この partial を繰り返し表示するもの
<%= render partial: 'shared/feed_item', collection: @feed_items %>
11章
多対多の関連を表す
migration++
複数インデックスでユニークにする
add_index :relationships, [:follower_id, :folloed_id], unique: true
関連に *_id 以外のものを使う
:foreign_key ってパラメータ使う
has_many :relationships, foreign_key: "follower_id", dependent: :destroy
モデル内でこんな風に使えて、一応 self は省略可
self.relationships.create!(followed_id: other_user.id)
多対多の関連を繋ぐ
これは User <-> User を多対多で繋いでいる例
class Relationship < ActiveRecord::Base belongs_to :follower, class_name: "User" belongs_to :followed, class_name: "User" end
この段階で多対多の関連は作れてるけど、 user.followed_users と user.followers とかが出来ないから作りたい
has_many through
follower_id と関連付けてるから followerd_users を取る方が簡単
:sourceパラメーターを使用し、followed_users 配列の元は followed_id の集合であることを明示的に伝える
`has_many :symbol_name, through: :relation_table_name, source: :relation_table_column_name` ってとこか
class User < ActiveRecord::Base has_many :followed_users, through: :relationships, source: :followed end
follower の取得
チュートリアルで魔法がどうとか言ってるウケる
方針として、 relationships テーブルについて、カラムが反対になったものが存在してれば follower 一覧が取得出来るので、それをエミュレートする感じ
で、それを使って followers を取得するって、なんだこれ... `Relationship.select(User).join(User, follower_id: :user_id).where(followed_id = ?, self.user_id)` とかで良いんじゃないの...(文法知らんから適当)
has_many :reverse_relationships, foreign_key: "followed_id", class_name: "Relationship", dependent: :destroy has_many :followers, through: :reverse_relationships, source: :follower
本当は user_id は relationship.follower_id に関連してるけど、 ruby 上で relationship.followerd_id に関連付けさせるのが上記の reverse_relationships って所か
引数の :class_name は内場合、 :reverse_relationships から逆引きして ReverseRelationShip クラスを探しに行く
routes++
こういうのを作る
Prefix | Verb | URI Pattern | Controller#Action |
---|---|---|---|
following_user | GET | /users/:id/following(.:format) | users#following |
followers_user | GET | /users/:id/followers(.:format) | users#followers |
resources :users do member do get :following, :followers end end
もし `/users/tigers` を作りたいなら 上のところの `member` を `collection` に変えたりしたら出来る
controlelr++
明示的に view テンプレートを変更/指定する
render 'show_follow'
form の Ajax 対応
`form_for ..., remote: true` するだけで、
<form action="/relationships/117" class="edit_relationship" data-remote="true" id="edit_relationship_117" method="post"> ... </form>
のように、 data-remote="true" がついた form が出来て、これは Rails 用のうんたらかんたら
Ajax のテストはコントローラのテストを行うけど、他は一般的に結合テスト重視
テスト、こんな感じ
xhr :delete, :destroy, id: relationship.id expect(response).to be_success expect do xhr :post, :create, relationship: { followed_id: other_user.id } end.to change(Relationship, :count).by(1)
controller の Ajax 対応
respond_to を使い、リクエストの種類に応じたレスポンスを生成する
def controller_method @user = User.find(params[:id]) respond_to do |format| format.html { redirect_to @user } format.js # controller_method.js.erb とか呼ぶ end end
フォロー追加ボタン押した際の create.js.erb はこんな感じ
erb のコンパイル入って、 unfollow ボタンが入った form が展開されるので、Rails が自動的に提供する jQuery を用いて挿入を行う
app/views/relationships/create.js.erb
$("#follow_form").html("<%= escape_javascript(render('users/unfollow')) %>") $("#followers").html('<%= @user.followers.count %>')
ActiveRecord++
has_many に対して、`User.first.followed_user_ids` のようにモデルの配列返すものに *_ids とかつけるとidの配列が返ってくる
バインディングはハッシュのようにも行える
followed_user_ids = user.followed_user_ids where("user_id IN (:followed_user_ids) OR user_id = :user_id", followed_user_ids: followed_user_ids, user_id: user)
タイムラインを取得しようとする場合、これは2回クエリを引いているため今回の場合においては効率が悪い
そこで、サブクエリを用いる方法に変更すると、上のコードは以下のようになる
followed_user_ids = "SELECT followed_id FROM relationships WHERE follower_id = :user_id" where("user_id IN (#{followed_user_ids}) OR user_id = :user_id", user_id: user.id)
*1:本当は `rake db:prepare` が良いんだけど、やってもschemaがtestDBに突っ込まれないなんでだろう