PythonとかPHPと触ってた僕が、自力でRailsチュートリアルをやり始めたが挫折。
そんな僕が社内研修(座学)でRailsを使えるようになりました。
その後の実習にてアプリケーションを作った時に、「もっと早く知っていれば」「そんな技があるのか・・・」と思ったTipsです。
Rails本体だけでなく、gemにも触れます。
目次
- Rails Model 編
- Rails ルーティング 編
- gem 編
- 自動化編
- 参考リンク集
Rails Model 編
こんな感じの状況で説明します
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
class User < ActiveRecord::Base
has_many :tweets
scope :men, -> { where(is_man: true) }
scope :women, -> { where(is_man: false) }
end
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_man
がtrue
"
scope :men, -> { where(is_man: true) }
"ユーザでis_man
がfalse
"
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) }
のように、 joins
と merge
を組み合わせて書くことができます
Usre.men
を条件として組み合わせたいので、
joins(:user)
で UserModel と join
して、
merge
を使うことで User.men
の条件を追加します
ご指摘ありがとうございました
遅くなってしまいましたが、追記させていただきました
Rails ルーティング 編
resourcesの入れ子
ユーザとそのツイートを表す時。
resources :tweets
resources :users
このようにしちゃうとうまく扱えない。
ユーザとツイートには包含関係があるので、resourcesも入れ子にする。
resources :users do
resources :tweets
end
こうすると
-
/users/1/tweets
でユーザID1のツイート一覧 -
/users/2/tweets/30
でユーザID2でツイートID30のツイート
みたいに扱える。
ちなみに、resourcesだけでなく、get,postなども入れ子にできる。
resources :users do
get :tweets, to: 'tweets#index'
post :follow, to: 'users#follow'
end
gem 編
ここで紹介するものは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行いれるだけです。
とやると、サーバの処理が一時停止してコンソールが立ち上がります。
ここで変数を見たり、DBを確認したり、だいたい全部できます。
便利!
詳しくはこちら:http://blog.toshimaru.net/rails-pry-byebug/
リポジトリ:https://github.com/deivid-rodriguez/pry-byebug
pry
pryはirbのリッチなやつです。
irbよりも多機能かつ色付きなのではかどります。
ドキュメントを見れたり、riコマンドが使えます。
また、タブキーによる補完があります。
ソースの表示が上の画像みたいにカラーに。
多機能すぎるのでもっと詳しい人の参考:今更聞けないpryの使い方と便利プラグイン集 - Qiita
hirb
Modelの出力を表形式にしてくれます。
なので直感的に結果を理解できます。
使い方
コンソールでHirb.enable
と入力します。
インストール
gem 'hirb'
gem 'hirb-unicode' # 日本語を扱う場合には必要
pryと併用する場合にはひと手間加えます。
Railsのルートディレクトリ(Gemfileがある場所)に次のファイルを作成します。
# .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
- リポジトリ:cldwalker/hirb
erd
プロジェクトのModelの定義やschema(とかその辺)からDBをER図にしてくれます。
pdfやpngなど出力形式やカラムの指定も結構自由にできるみたいです。
インストール
外部モジュールを使っているので、インストールの時に以下が必要です。
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
この辺で設定の例を見る。
- Capistrano で Rails アプリケーションの自動デプロイ - Qiita
- Capistrano3でUnicorn+Nginxな環境にRailsをデプロイする:初心者向け - Qiita
- 【入門】Capistrano3で自動デプロイ
まとめ
結構ボリュームが大きくなってしまいました
参考リンク集
- pry-byebug を使ってRailsアプリをステップ実行する - Hack Your Design!
- deivid-rodriguez/pry-byebug
- 今更聞けないpryの使い方と便利プラグイン集 - Qiita
- cldwalker/hirb
- voormedia/rails-erd
- Rails ERDの導入と実行
- Chef脱落者が、Itamaeで快適インフラ生活する話 - Qiita
- Chef-soloからItamaeに完全移行した話 - Qiita
- Itamae + rbenvでCentOSにRuby環境を構築 - Qiita
- zaru/itamae-rails
- Infrastructure as Code 再考 - Gosuke Miyashita
- 入門 Capistrano 3 ~ 全ての手作業を生まれる前に消し去りたい | GREE Engineers' Blog
- capistrano 3 をできるだけシンプルにサーバーにコマンドを流し込むツールとして使いこなす - Qiita
- Capistrano で Rails アプリケーションの自動デプロイ - Qiita
- Capistrano3でUnicorn+Nginxな環境にRailsをデプロイする:初心者向け - Qiita
- 【入門】Capistrano3で自動デプロイ