いいねと思った Rails メソッド 10

# ActiveRecord

# 1. `create_with`

`create` するときの条件を追加できます。

例えば、"ユーザーから「男」を探して、いなかったら「男」、「山田太郎」を追加したい" とき

```rb
User.create_with(name: 'Yamada Taro').find_or_create_by(sex: 'man')
```

こんな感じで `find_or_create` を使う際に便利です。

# 2. `update(attributes)`

内部的には、`attribute` をオブジェクトにセットして、`save` メソッドを呼んでいるだけ。
なので、これを利用すると `update_or_create_by` が作れます。

```rb
User.find_or_initialize_by(name: 'Yamada Taro').update(sex: 'man')
```

`initialize` しないで

```rb
User.find_or_create_by(name: 'Yamada Taro').update(sex: 'man')
```

とすると、INSERT した後に UPDATE するので、`find_or_initialize_by` した方がちょっとお得。

# 3. `extending(*modules, &block)`

ActiveRecord_Relation に対して、exetend することが出来ます。

例えば

```rb
Partner.identifiers
#=> NoMethodError: undefined method `identifiers'

module Identifiers
  def identifiers
    pluck(:identifier)
  end
end

partner = Partner.all.extending(Identifiers)
partner.identifiers
#=> ["partner1", "partner2", "partner3", "partner4"]
user = User.all.extending(Identifiers)
user.identifiers
#=> ["user1", "user2", "user3"... ]
```

このように、再利用することができます。

引数には、ブロックを渡すことも出来ます。

```rb
partner = Partner.all.extending do
  def identifiers
    pluck(:identifier)
  end
end
```

ブロックによる更なる拡張も可能です。

```rb
partner = Partner.all.extending(Identifiers) do
  def names
    pluck(:name)
  end
end
```

# 4. `destroy_all` と `delete_all`

`destroy_all` は条件にあったものを関連テーブルも含めて削除する。`DELETE` は主キーによって行うので、`DELETE` の手前で `SELECT` が発行されます。そのため、対象の `DELETE` は一つずつ行われます。

`delete_all` は関連テーブルは削除しないが、条件にあったものを bulk でまるっと消します。関連データを削除しない場合は、こっちの方が効率がいい。

# 5. `except`, `only`, `unscope`

指定した条件を外す系のメソッド達

## `except(*skips)`

```rb
User.select(:id).except(:select)
#=> SELECT `users`.* FROM `users`
```

## `only`

```rb
partner.select(:id).where(id: 1).only(:where)
#=> SELECT `partners`.* FROM `partners`  WHERE `partners`.`id` = 1
```

## `unscope(*args)`

```rb
# 例えば
1. User.order('email DESC').unscope(:order)
#== User.all

2. User.order('email DESC').select('id').where(name: "John")
.unscope(:order, :select, :where)
#== User.all

3. User.where(name: "John", active: true)
.unscope(where: :name) 
#== User.where(active: true)

# こんな感じでも使える
has_many :comments, -> { unscope where: :trashed }
```

`except` も似たメソッドだが、`merge` で使えません。

```rb
User.order('email').merge(User.except(:order))
    == User.order('email')
User.order('email').merge(User.unscope(:order))
    == User.all
```

こういう感じで、chain を外す事も出来るんですね。

# ActiveSupport

# 6. `squish`

`String` クラス
両端の空白文字を取り除き、文字列内の連続する空白文字をスペース一つに置き換える。

```rb
" foo   bar    boo     ".squish    #=> "foo bar boo"
" foo   bar\n\n\n\tboo\t\n".squish #=> "foo bar boo"
```

## squish!

破壊的変更も！ちなみに中身は

```rb
def squish!
  gsub!(/\A[[:space:]]+/, '')
  gsub!(/[[:space:]]+\z/, '')
  gsub!(/[[:space:]]+/, ' ')
  self
end
```

こんなの用意されてるの知らなくて、自分で正規表現作って `gsub` してました。

# 7. `remove(*patterns), remove!(*patterns)`

`String` クラス
こちらは pattern にマッチしたものを削除する！

```rb
"foo bar test".remove(" test") #=> "foo bar"
```

# 8. `all_week, all_month, all_quarter, all_year`

`Date`, `DateAndTime`, `Time` で使える。
週、月、四半期、年の Range を返す。
中見るとこんな感じ

```rb
beginning_of_week(start_day)..end_of_week(start_day)
```

業務のコードでわざわざ、`beginning_of_month` と `end_of_month` 計算して、範囲指定しているところがあったので、`all_month` 一発でいけたんだなあと。

# 9. `advance(options)`

`Date`, `DateAndTime`, `Time` で使える。
key を指定して、まるっと足し算を行う
key は `keys: :years, :months, :weeks, :days`

```rb
today = Date.today #=> Sat, 09 May 2015
today.advance({years: 1, months: 1, weeks: 1, days: 1}) #=> Fri, 17 Jun 2016
```

なんかちょろっと手元で試したいときとかに使えそうだなあと。

# 10. `with_options`

`Object` クラス
重複しているオプションがあったら、まとめることが出来ます。

例えば

```rb
class Account < ActiveRecord::Base
  has_many :customers, dependent: :destroy
  has_many :products,  dependent: :destroy
  has_many :invoices,  dependent: :destroy
  has_many :expenses,  dependent: :destroy
end
```

こんな感じで、同じような条件が続く事ってよくあると思うのですが、

```rb
class Account < ActiveRecord::Base
  with_options dependent: :destroy do |assoc|
    assoc.has_many :customers
    assoc.has_many :products
    assoc.has_many :invoices
    assoc.has_many :expenses
  end
end
```

と書けます。
特に、レシーバーが必要ない場合は

```rb
with_options dependent: :destroy do
  has_many :customers
  has_many :products
  has_many :invoices
  has_many :expenses
end
```

省略可能です。

ちなみに

```rb
with_options if: :persisted?, length: {minimum: 50} do
  validates :content, if: -> { content.present? }
end
```

と同じオプションをブロック内でも定義した場合は

```rb
validates :content, length: {minimum: 50}, if: -> { content.present? }
```

とブロック内で定義した方が優先されます。

以上、なんのつながりも無くランダムに選んだ、メソッド 10 でした。