LoginSignup
430
378

More than 5 years have passed since last update.

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

Last updated at Posted at 2015-06-05

ActiveRecord

1. create_with

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

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

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

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

2. update(attributes)

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

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

initialize しないで

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

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

3. extending(*modules, &block)

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

例えば

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"... ]

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

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

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

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

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

4. destroy_alldelete_all

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

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

5. except, only, unscope

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

except(*skips)

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

only

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

unscope(*args)

# 例えば
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 で使えません。

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

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

ActiveSupport

6. squish

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

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

squish!

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

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

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

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

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

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

8. all_week, all_month, all_quarter, all_year

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

beginning_of_week(start_day)..end_of_week(start_day)

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

9. advance(options)

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

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 クラス
重複しているオプションがあったら、まとめることが出来ます。

例えば

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

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

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

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

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

省略可能です。

ちなみに

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

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

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

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

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

430
378
0

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
430
378