LoginSignup
106

More than 5 years have passed since last update.

Railsで痒い所に手が届くTips8選【脱初心者】

Last updated at Posted at 2016-07-26

PythonとかPHPと触ってた僕が、自力でRailsチュートリアルをやり始めたが挫折。
そんな僕が社内研修(座学)でRailsを使えるようになりました。
その後の実習にてアプリケーションを作った時に、「もっと早く知っていれば」「そんな技があるのか・・・」と思ったTipsです。
Rails本体だけでなく、gemにも触れます。

目次

Rails Model 編

こんな感じの状況で説明します

schema.rb
ActiveRecord::Schema.define(version: 20160725113659) do
  create_table "users", force: :cascade do |t|
    t.string   "name"
    t.datetime "created_at",                null: false
    t.datetime "updated_at",                null: false
    t.boolean  "is_man",     default: true
  end
end

UserModel
class User < ActiveRecord::Base
  has_many :tweets
  scope :men, -> { where(is_man: true) }
  scope :women, -> { where(is_man: false) }
 end
TweetModel
class Tweet < ActiveRecord::Base
  belongs_to :user

  # Userモデルをjoin
  scope :with_user, -> { includes(:user).references(:users) }
  # 男性によるツイート
  #scope :by_men, -> { with_user.where(User.arel_table[:is_man].eq(true)) } # 追記: 非推奨
  scope :by_men, -> { joins(:user).merge(User.men) }
end
[13] pry(main)> User.all
  User Load (0.2ms)  SELECT "users".* FROM "users"
=> [#<User:0x007fc9c9dde948 id: 1, name: "山田花子", is_man: false>,
 #<User:0x007fc9c9dde628 id: 2, name: "鈴木太郎", is_man: true>,
 #<User:0x007fc9c9dde2b8 id: 3, name: "伊藤二郎", is_man: true>]
[14] pry(main)> Tweet.all
  Tweet Load (0.1ms)  SELECT "tweets".* FROM "tweets"
=> [#<Tweet:0x007fc9cb0712e0 id: 1, containts: "{:containts=>\"ツイートは140文字まで\"}", user_id: 1>,
 #<Tweet:0x007fc9cb070c00 id: 2, containts: "{:containts=>\"実際は違うけどね\"}", user_id: 2>]

scope

SQLのクエリのエイリアス的なやつです。
共通で使うクエリをモデルのstaticなメソッドのように使え、メソッドチェーンも使えるので直感的に複雑なクエリをつくれます。

Userモデルに定義したこの2つのscope
"ユーザでis_mantrue"
scope :men, -> { where(is_man: true) }
"ユーザでis_manfalse"
scope :women, -> { where(is_man: false) }

実際にはこんな感じ

[1] pry(main)> User.men
  User Load (0.5ms)  SELECT "users".* FROM "users" WHERE "users"."is_man" = ?  [["is_man", "t"]]
=> [#<User:0x007fc9cd2039d0 id: 2, name: "鈴木太郎", is_man: true>,
 #<User:0x007fc9cd200208 id: 3, name: "伊藤二郎", is_man: true>]
[2] pry(main)> User.women
  User Load (0.1ms)  SELECT "users".* FROM "users" WHERE "users"."is_man" = ?  [["is_man", "f"]]
=> [#<User:0x007fc9cd0638f0 id: 1, name: "山田花子", is_man: false>]

scopeの{}中の条件でUserモデルに対してクエリを実行できます。
さらに、メソッドチェーンもできます。例は次項にて。

arel_table

Modelに対してちょっと込み入ったSQLクエリを投げたい時に便利です。

"男性のツイート一覧"をこう書けます。
scope :by_men, -> { with_user.where(User.arel_table[:is_man].eq(true)) }
このように、
- where句に対して別テーブルのキーを指定したい時(.where(User.arel_table[:is_man].eq(true))
- カラム指定で文字列を使いたくない時(User.arel_table[:is_man].eq(true)

[6] pry(main)> Tweet.by_men
  SQL (0.2ms)  SELECT "tweets"."id" AS t0_r0, "tweets"."containts" AS t0_r1, "tweets"."user_id" AS t0_r2, "tweets"."created_at" AS t0_r3, "tweets"."updated_at" AS t0_r4, "users"."id" AS t1_r0, "users"."name" AS t1_r1, "users"."created_at" AS t1_r2, "users"."updated_at" AS t1_r3, "users"."is_man" AS t1_r4 FROM "tweets" LEFT OUTER JOIN "users" ON "users"."id" = "tweets"."user_id" WHERE "users"."is_man" = 't'
=> [#<Tweet:0x007fc9c9f34fb8 id: 2, containts: "{:containts=>\"実際は違うけどね\"}", user_id: 2>]

上の例は何気にメソッドチェーンをしていて、
自分で定義したwith_userメソッドにwhereメソッドをつなげて書いています。
このように、scopeをうまく使うと複雑なメソッドを直感的に書くことができます。
ただし、使いすぎるとよくわからなくなるので注意です。

追記

arel_table についてコメントでご指摘いただきました
https://qiita.com/tkhr/items/9b79bce27f20191eb78f#comment-637a5f85bae3d8d6f3fa

arel_table は ActiveRecord 標準のクエリインターフェースではないので使用するのは推奨されない、とのことでした

scope :by_men, -> { with_user.where(User.arel_table[:is_man].eq(true)) }

scope :by_men, -> { joins(:user).merge(User.men) }

のように、 joinsmerge を組み合わせて書くことができます

Usre.men を条件として組み合わせたいので、
joins(:user) で UserModel と join して、
merge を使うことで User.men の条件を追加します

ご指摘ありがとうございました
遅くなってしまいましたが、追記させていただきました :bow:

Rails ルーティング 編

resourcesの入れ子

ユーザとそのツイートを表す時。

routes.rb
  resources :tweets
  resources :users

スクリーンショット 2016-07-26 9.54.54.png

このようにしちゃうとうまく扱えない。

ユーザとツイートには包含関係があるので、resourcesも入れ子にする。

routes.rb
  resources :users do
    resources :tweets
  end

スクリーンショット_2016-07-26_9_55_47.png

こうすると

  • /users/1/tweetsでユーザID1のツイート一覧
  • /users/2/tweets/30でユーザID2でツイートID30のツイート

みたいに扱える。

ちなみに、resourcesだけでなく、get,postなども入れ子にできる。

routes.rb
  resources :users do
    get :tweets, to: 'tweets#index'
    post :follow, to: 'users#follow'
  end

スクリーンショット_2016-07-26_10_09_50.png

gem 編

ここで紹介するものはGemfileに次のように書けば:thumbsup:

Gemfile
group :development do
  gem 'pry-byebug'   # pry-byebug
  gem 'pry-rails'    # pry
  gem 'hirb'         # hirb
  gem 'hirb-unicode' # hirb
  gem 'rails-erd'    # erd
end

pry-byebug

railsで使えるデバッガで、ブレークポイントの設定やステップ実行ができます。
アプリを動かして好きなタイミングで動作を一時停止して変数の中身を見たり、
1行づつ順に実行して変数の値を追うことができます。
つまり、いろんなところにpを置いて変数の中身を確認するプリントデバッグとはおさらばです。

使い方は
29行目のように、処理を中断したいところにbinding.pryと1行いれるだけです。
スクリーンショット 2016-07-24 22.10.00.png

とやると、サーバの処理が一時停止してコンソールが立ち上がります。
ここで変数を見たり、DBを確認したり、だいたい全部できます。
スクリーンショット 2016-07-24 22.08.41.png

便利!

詳しくはこちら:http://blog.toshimaru.net/rails-pry-byebug/
リポジトリ:https://github.com/deivid-rodriguez/pry-byebug

pry

pryはirbのリッチなやつです。
irbよりも多機能かつ色付きなのではかどります。
ドキュメントを見れたり、riコマンドが使えます。
また、タブキーによる補完があります。
ソースの表示が上の画像みたいにカラーに。

多機能すぎるのでもっと詳しい人の参考:今更聞けないpryの使い方と便利プラグイン集 - Qiita

hirb

Modelの出力を表形式にしてくれます。
なので直感的に結果を理解できます。

こんな感じ。
スクリーンショット 2016-07-25 21.30.09.png

使い方

コンソールでHirb.enableと入力します。

インストール

Gemfile
gem 'hirb'
gem 'hirb-unicode' # 日本語を扱う場合には必要

pryと併用する場合にはひと手間加えます。
Railsのルートディレクトリ(Gemfileがある場所)に次のファイルを作成します。

.pryrc
# .pryrc
begin
  require 'hirb'
rescue LoadError
  # Missing goodies, bummer
end

if defined? Hirb
  # Slightly dirty hack to fully support in-session Hirb.disable/enable toggling
  Hirb::View.instance_eval do
    def enable_output_method
      @output_method = true
      @old_print = Pry.config.print
      Pry.config.print = proc do |*args|
        Hirb::View.view_or_page_output(args[1]) || @old_print.call(*args)
      end
    end

    def disable_output_method
      Pry.config.print = @old_print
      @output_method = nil
    end
  end

  Hirb.enable
end

erd

プロジェクトのModelの定義やschema(とかその辺)からDBをER図にしてくれます。
pdfやpngなど出力形式やカラムの指定も結構自由にできるみたいです。

erd.png
voormedia/rails-erdより

インストール

外部モジュールを使っているので、インストールの時に以下が必要です。

brew install graphviz

使い方

プロジェクトのルートディレクトリにPDF形式で出力されます。

bundle exec erd

いろいろオプションがあるので
詳しくはこちら:Rails ERDの導入と実行
リポジトリ:voormedia/rails-erd

自動化編

itamae

サーバ環境をコードで管理して、自動で構築してくれるツールです。
解説記事を読むと「Chef」という単語が目に付きますが、itamaeはChefのラッパー的なツールです。
Chefだといろいろできすぎてしまうので、機能を制限してわかりやすくしてくれたのがitamaeです。

解説記事を読みながら実際のレシピをカスタマイズしていきました。
解説記事は運用の細かいところまで解説してくださっているので、あまりよくわからないところは実際に動かしてみて理解するのも手だと思います。

この記事を参考に
Chef脱落者が、Itamaeで快適インフラ生活する話 - Qiita
Chef-soloからItamaeに完全移行した話 - Qiita
Itamae + rbenvでCentOSにRuby環境を構築 - Qiita

これをカスタマイズする
zaru/itamae-rails

やり方でやりました。

補足:
Infrastracture as Code という考え方があります。
サーバ環境をコードによって管理することで、バージョン管理できて、誰がやっても同じ環境が作れる→Happy!
あと、個人的にはサーバ環境がコードとしてまとまっているというのが嬉しいです。何を入れたかとか忘れちゃうし、環境作ってる間に何入れたのかわからなくなっちゃうし。
詳しくは Infrastructure as Code 再考 - Gosuke Miyashita

Capistrano

Capistranoなるデプロイツールというものがあります。こいつは衝撃でした。
開発中はデプロイのことは頭にありませんが、「AWSのEC2で公開してやるぜ!」とかなった時に、途端に負荷が跳ね上がる。
これを勝手によしなにしてくれるというものですね。CIに近づく一歩です。

解説記事はたくさんあります。もちろんQiitaにも。
ですが、記事にはそれぞれに環境の差があるので例を組み合わせるだけではなかなかうまくいきませんでした。
でも、この記事でCapistranoの全貌を大まかに把握してから、例を見ると応用が利くようになり、夢の自動化を達成しました。
入門 Capistrano 3 ~ 全ての手作業を生まれる前に消し去りたい | GREE Engineers' Blog
あとは、この辺でざっくりした具体例を見る。
capistrano 3 をできるだけシンプルにサーバーにコマンドを流し込むツールとして使いこなす - Qiita

この辺で設定の例を見る。

まとめ

結構ボリュームが大きくなってしまいました

参考リンク集

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
106