早く知ってたら良かったrailsの技

# はじめに

自分が rails をさわり始めたときはバージョン1からバージョン2に変わるあたりだったのですが、バージョン2が出た年を振り返るとなんと2007年でした。
月日の流れが速い事に驚く中、早く知ってたら良かったのになぁって事をつらつらとまとめてみました。
最近 rails さわり始めてみたよ！って方の参考になれば良いなと思います。
今回は便利な gem とかではなく、素のrailsで出来ることを挙げています。
ちなみにバージョンは以下の環境です。

``` 
About your application's environment
Ruby version              2.1.3-p242 (x86_64-darwin14.0)
RubyGems version          2.2.2
Rack version              1.5
Rails version             4.1.8
```

# app 以下のディレクトリ構成は追加しても良いんだよ

rails new したときの `app` 以下のディレクトリ構成は以下のようになっています。

``` 
- app/
 |+ assets/
 |+ controllers/
 |+ helpers/
 |+ mailers/
 |+ models/
 |+ views/
```

最初はこの通りに作らなきゃいけないのかなって思っていたのですが、最初から用意されている枠に合わない物は追加しても大丈夫です。
例えば自作のバリデーターをまとめる `validators` とか、バックグラウンドで動くワーカーが入ってる `workers` とかですね。

``` 
- app/                         
 |+ assets/
 |+ controllers/
 |+ decorators/
 |+ helpers/
 |+ jobs/
 |+ mailers/
 |+ models/
 |+ queries/
 |+ uploaders/
 |+ validators/
 |+ views/
 |+ workers/
```

それでも、`app` 以下じゃ無いよなーって思うような、汎用的なユーティリティとかは `lib` 以下に配置することもあります。
ちょっと気をつけるのがロードパス。
`app` 以下の一階層分は勝手に呼んでくれるのですが、例えば `app/workers/concerns` なんてディレクトリを作ったとすると、そこまでは読んでくれません。
なので `config/application.rb` でロードするように設定しましょう。
下の例では式展開を使っているので `%w` では無く、大文字の `%W` であることにも注意です。
深い階層まで一気に追加する場合は、「lib 以下もロードパスに追加」で使っている方法も使えますよ。

``` ruby
require File.expand_path('../boot', __FILE__)
require 'rails/all'
Bundler.require(*Rails.groups)

module SampleApp
  class Application < Rails::Application
    # app 以下の独自ディレクトリも読み込む
    config.autoload_paths += %W(#{config.root}/app/jobs/concerns #{config.root}/app/workers/concerns)
    # lib 以下もロードパスに追加
    config.autoload_paths += Dir["#{config.root}/lib/**/"]
    # 中略
  end
end
```

# ネストしたリソースの routes は shallow を検討しよう

ここでは、説明のためにサンプルとなるアプリケーションを作ります。
ユーザー `User` は複数のノート `Note` を持っているアプリケーションをイメージしてください。

``` 
./bin/rails g scaffold user name:string
./bin/rails g scaffold note user:references title:string body:text
```

さて、リソースがネストしているので、`config/routes.rb` にもそのように記述します。

``` ruby
Rails.application.routes.draw do
  resources :users do
    resources :notes
  end
end
```

そうするとルーティングは以下のようになります。

``` 
        Prefix Verb   URI Pattern                              Controller#Action
    user_notes GET    /users/:user_id/notes(.:format)          notes#index
               POST   /users/:user_id/notes(.:format)          notes#create
 new_user_note GET    /users/:user_id/notes/new(.:format)      notes#new
edit_user_note GET    /users/:user_id/notes/:id/edit(.:format) notes#edit
     user_note GET    /users/:user_id/notes/:id(.:format)      notes#show
               PATCH  /users/:user_id/notes/:id(.:format)      notes#update
               PUT    /users/:user_id/notes/:id(.:format)      notes#update
               DELETE /users/:user_id/notes/:id(.:format)      notes#destroy
         users GET    /users(.:format)                         users#index
               POST   /users(.:format)                         users#create
      new_user GET    /users/new(.:format)                     users#new
     edit_user GET    /users/:id/edit(.:format)                users#edit
          user GET    /users/:id(.:format)                     users#show
               PATCH  /users/:id(.:format)                     users#update
               PUT    /users/:id(.:format)                     users#update
               DELETE /users/:id(.:format)                     users#destroy
```

これでルーティングもネストしたリソースに沿った物になりました。
ただ、特定のノートを参照しようとしたとき、ノートIDさえ分かれば対象は一意に決まるはずですが、その場合でもユーザーIDが必要になってしまいます。
そこで、`shallow: true` を指定すると、、、

``` ruby
Rails.application.routes.draw do
  resources :users, shallow: true do
    resources :notes
  end
end
```

``` 
       Prefix Verb   URI Pattern                         Controller#Action
   user_notes GET    /users/:user_id/notes(.:format)     notes#index
              POST   /users/:user_id/notes(.:format)     notes#create
new_user_note GET    /users/:user_id/notes/new(.:format) notes#new
    edit_note GET    /notes/:id/edit(.:format)           notes#edit
         note GET    /notes/:id(.:format)                notes#show
              PATCH  /notes/:id(.:format)                notes#update
              PUT    /notes/:id(.:format)                notes#update
              DELETE /notes/:id(.:format)                notes#destroy
        users GET    /users(.:format)                    users#index
              POST   /users(.:format)                    users#create
     new_user GET    /users/new(.:format)                users#new
    edit_user GET    /users/:id/edit(.:format)           users#edit
         user GET    /users/:id(.:format)                users#show
              PATCH  /users/:id(.:format)                users#update
              PUT    /users/:id(.:format)                users#update
              DELETE /users/:id(.:format)                users#destroy
```

これで `notes#show` の時に冗長だったユーザーID指定が不要になりました。
ちょっと注意なのが `form_for` に渡すパスです。
例えば、`notes#create` の時は `/users/:user_id/notes` で、`notes#update` の時は `/notes/:id` とユーザーIDの有無が異なります。
これだと `form_for(@note) do` のままだと `notes#new` の時に `undefined method notes_path` なんて言われちゃいます。
（`shallow` オプションを指定しない場合は `form_for([@user, @note])` のように指定すれば良い）
そこで、`form_for` は以下のように設定します。

``` ruby
form_for(@note, url: polymorphic_path([@user, @note])) do |f|
```

ちなみにコントローラーの `new`, `edit` はこんな感じ

``` ruby
# GET /notes/new
def new
  @user = User.find params[:user_id]
  @note = @user.notes.build
end

# GET /notes/1/edit
def edit
end
```

こうすることで、
新規作成時には `action="/users/1/notes"`、編集時には `action="/notes/1"` とちゃんと指定できます。

# nilかも？って時には ||, && とか presense とか try とか便利だよ

例えば、`User` クラスには名前 `name` という変数があるとき、`name` が `nil` だったら値を設定したいなんて時は、

``` ruby
user.name = 'anonymous' unless user.name
```

こうせずに

``` ruby
user.name ||= 'anonymous'
```

こんな感じで書くことが出来るんですね。
ruby だとこれを「nilガード」なんて呼ばれています。
分かりやすく式を展開すれば下の意味と同じです。

``` ruby
user.name = (user.name || 'anonymous')
```

もちろん使いどころは nilガードだけじゃありません。
`User` クラスにニックネーム `nickname` もあったとして、
「ニックネームが設定されていればそちらを、されていない（nilなら）名前を出したい」なんて時はこうすれば良いわけです。

``` ruby
user.nickname || user.name
```

同じようなユースケースで `presence` が使える場合もあります。
`presence` は `present?` メソッドが真なら `self` を、偽なら `nil` を返すメソッドです。
`present?` は `nil`, `false`, 空の配列, 空のハッシュ, 空の文字列, 特定文字列のみ文字列を判定してくれるメソッドです。
ここでの特定文字列とは 正規表現 `/\A[[:space:]]*\z/` で表される物です。

`user.name` で `nil` だけじゃ無くて空白文字も判定したいんだって時に

``` ruby
user.name.present? ? user.name : 'anonymous'
```

`presence` を使うと

``` ruby
user.name.presence || 'anonymous'
```

と書けるわけですね。
次に、少し例を変えて `User` は `Note` を `has_many` で持っているとしましょう。
こんな感じです。

``` ruby
class User < ActiveRecord::Base
  has_many :notes
end

class Note < ActiveRecord::Base
  belongs_to :user
end
```

さて、`Note` からユーザー名を表示したいとして、でも必ずしも対応するユーザーが存在するか分からないとき、

``` ruby
note.user.name if note.user
```

とか

``` ruby
note.user && note.user.name
```

みたいに書くことも出来るのですが、こんな時には `try` が使えます。
`try` は引数でメソッド名を渡して実行するもので、ただし対象が`nil` の時には実行されずに `nil` を返してくれる物です。
実装はシンプルで、`Object` と `NilClass` に `try` が用意されているんですね。

``` ruby
class Object
  def try(*a, &b)
    if a.empty? && block_given?
      yield self
    else
      public_send(*a, &b) if respond_to?(a.first)
    end
  end
end

class NilClass
  def try(*args)
    nil
  end
end
```

`try` だとメソッド名の打ち間違いなども `nil` で返ってきますが、
存在しないメソッドの場合は `NoMethodError` を返してくれる `try!` も用意されています。

# delegate も使ってみよう

上の例では `note.user.name` とメソッドの呼び出しをしていますが、
オブジェクト指向プログラミングの界隈だと、「デメテルの法則」に反しているとも言われます。

それじゃあ、といって

``` ruby
class Note < ActiveRecord::Base
  belongs_to :user
  
  def user_name
    user.try :name
  end
end
```

ノートからユーザー名を取得するメソッドを定義しても良いのですが、数が多くなると大変になっちゃいますね。
そんなときには `delegate` が使えます。

``` ruby
class Note < ActiveRecord::Base
  belongs_to :user
  delegate :name, to: :user, prefix: :author, allow_nil: true
end
```

こうすると `note.author_name` が呼び出せるようになります。
`delegate` の使い方は、委譲するメソッド名、移譲先(`to`)、さらにはメソッドのプレフィックス(`prefix`)、そしてnilを許可するかどうか(`allow_nil`)です。
`prefix: false` なら `note.name` と呼び出すことになりますし、`prefix: true` なら `note.user_name` となります。
`allow_nil: true` としておけば `note.user` が `nil` の場合でもエラーにならないので便利ですね。
さらに、移譲先は関連以外でも定数、クラス変数、インスタンス変数でも大丈夫で、多段の delegate をすることも可能です。

```ruby
class Foo
  CONSTANT
  @@class_val

  def initialize
    @instance_val
  end
  
  delegate :foo, to: :CONSTANT
  delegate :bar, to: :@@class_val
  delegate :baz, to: :@instance_val
end
```

ちなみにプレーンなruby にも委譲の為の `Forwardable` モジュールが用意されており、使い方も似ています。
`Forwardable` の方はメソッドのリネームが出来る所が長所ですが、
`ActiveSupport` の `delegate` に用意されている `prefix` と `allow_nil` が使い勝手が良いので、rails だと `ActiveSupport` の `delegate` をよく使います。

# クラスマクロは自分で作れる

クラスマクロって例えば `attr_accessor` みたいなやつですね。

```rb
class User
  attr_accessor :name
end
```

これは用意されている物を使うだけで無く、自分で作ることも出来るんです。
「マクロとか難しそう」という訳では無く、その実態はただのクラスメソッドなので怖くないよ。

```rb
class User
  attr_accessor :name
  def self.suffix attr, value
    define_method("#{attr}_with_suffix") {
      name = instance_variable_get "@#{attr}"
      "#{name} #{value}" if name.present?
    }
    alias_method_chain attr, :suffix
  end
end

class Boy < User
  suffix :name, 'くん'
end

class Girl < User
  suffix :name, 'さん'
end
```

オマケで `alias_method_chain` の例も入れてみたよ。
クラスマクロは `def self.suffix attr, value` の部分で、この定義によって継承したクラスでクラスマクロが使えるようになっている訳です。

```
> girl = Girl.new
=> #<Girl:0x007fb9314610f8>
> girl.name = 'はなこ'
=> "はなこ"

> girl.name_with_suffix
=> "はなこ さん"
> girl.name
=> "はなこ さん"
> girl.name_without_suffix
=> "はなこ"
```

実行してみた結果が上の様子です。`>` が入力、`=>` が出力です。
クラスマクロによって、使うと `"#{attr}_with_suffix"` というメソッドが作られるようになっています。
つまり Boy, Girl クラスで `suffix :name, 'さん'` と使ったことによって、
`name_with_suffix` メソッドがマクロによって定義されているのです。
さらにマクロによって定義したメソッドの内容は、「名前が設定されていれば、後ろにマクロで指定した文字列を追加する」という物だったので、`girl.name_with_suffix` によって「はなこ さん」と返ってきている訳です。
この例だと実は `girl.name` と呼び出すだけで `girl.name_with_suffix` と同じ結果を得ることが出来ます。
これは、`alias_method_chain` によって、元々の `name` メソッドが `name_without_suffix` として待避され、`name_with_suffix` として定義したメソッドが `name` としても呼び出せるようになっているからなのです。

クラスマクロを定義する場所は、今回のように継承元クラスや、
`ActiveSupport::Concern` でモジュール化するなど、いろいろな方法があります。

# super do で操作を差し込む

さて、下のようなクラスを作ってみます。

```rb
class User
  attr_accessor :age

  def initialize
    @age = 0
  end

  def birthday
    puts 'happy birthday!'
    @age += 1
  end
end
```

`User#birthday` メソッドは、「happy birthday!」とメッセージを表示し、
年齢に1を加えてその値を返す物です。

```rb
class Boy < User
  def birthday
    super
    puts 'some process...'
  end
end
```

Userクラスを継承したBoyクラスにおいて、`birthday`メソッドをオーバーライドしようとしたとき、
`super` によって継承元のメソッドを呼び出すことが可能です。
しかし、「メッセージの表示」と「年齢の加算」の間に処理を挟みたい時にはどうしましょう。
そのために `super` を使わずにオーバーライドしてしまうと、メッセージの定義が重複するなどしてメンテナンス性が下がってしまいます。
そんなときには継承元クラスに処理を差し込めるポイントを作っておきましょう。

```rb
class User
  attr_accessor :age

  def initialize
    @age = 0
  end

  def birthday
    puts 'happy birthday!'
    yield if block_given?
    @age += 1
  end
end

class Boy < User
  def birthday
    super do
      puts 'some process...'
    end
  end
end
```

Userクラスに `yield if block_given?` を用意したので、
継承したクラスにおいて `birthday` メソッドをオーバーライドしたとき、
ブロックによる `super` を呼び出したときに、そこに処理を差し込むことが可能になります。

ログイン処理などによく用いられる [plataformatec/devise](https://github.com/plataformatec/devise) では、
例えば `Devise::SessionsController` 等において、「ログインしたときにそのユーザーに何か処理したいな」ってケースのために処理差し込みのポイントを用意してくれていたりします。

```rb
class Devise::SessionsController < DeviseController
  # POST /resource/sign_in
  def create
    self.resource = warden.authenticate!(auth_options)
    set_flash_message(:notice, :signed_in) if is_flashing_format?
    sign_in(resource_name, resource)
    yield resource if block_given?
    respond_with resource, location: after_sign_in_path_for(resource)
  end
end
```

# active model は使えるやつ

rails アプリを作成していると、DBには永続化しないんだけど、ActiveRecordと同じような使い勝手のモデルが欲しいなーと思うことがあります。
例えば検索用のフォームなどですね。 
view で form_for 使いたいし、validate も同じように定義したいからです。

そんなときには `ActiveModel` の出番です。

```rb
class UserSearchForm
  include ActiveModel::Model

  attr_accessor :name, :age
  validates :name, presence: true
end
```

使い方もとても簡単。`include ActiveModel::Model` するだけです。
これで `validates` なども使えるようになっています。

# おわりに

便利そうな Rails Tips をまとめてみました。誰かのお役に立てば幸いです。
他にも思いついたら加筆していきたいと思いますし、
こんなのも早く知ってたら良かった！と思った物があれば是非教えてくださいな。