はじめに
Railsを使ってコードを書いていると、「どんなメソッドが使えるんだっけ?」 「もっと便利なメソッドないのか?」と思うことがあると思います。
そんな時に、私がよく使うのがObject.methods
や、特定のメソッドを探すObject.methods.grep()
になるのですが、この記事ではそこで見つけた意外と知らないかもしれないメソッドなどを紹介していきたいと思います。
Ruby編
itself
selfを返します。
'test'.itself # 'test'
配列自身でgroup_byする時などに綺麗に書けます。
array = [4, 7, 3, 7, 7, 4, 1, 1]
array.group_by(&:itself) # {4=>[4, 4], 7=>[7, 7, 7], 3=>[3], 1=>[1, 1]}
参考URL: https://goo.gl/E4mv1b
allocate
クラスのインスタンスの作成を行いますが、initializeは呼ばれません。
class Cat
attr_accessor :name
def initialize
@name = "NoName"
end
end
cat1 = Cat.new
p cat1.name # "NoName"
cat2 = Cat.allocate
p cat2.name # nil
参考URL: http://ref.xaio.jp/ruby/classes/class/allocate
tap
ブロックにレシーバを入れて実行し、戻り値はレシーバ自身というメソッドです。
irb(main):001:0> array = [1, 2, 3, 4, 5]
=> [1, 2, 3, 4, 5]
irb(main):002:0> array.tap{ |a| p a }.collect{ |a| a.to_s }
[1, 2, 3, 4, 5]
=> ["1", "2", "3", "4", "5"]
Rails編
increment, decrement
特定のレコードの値を増やしたり、減らしたりできます。
[1] pry(main)> user = User.find(1)
User Load (5.6ms) SELECT `users`.* FROM `users` WHERE `users`.`id` = 1 LIMIT 1
=> #<User id: 1, email: "hoge@hoge.com", created_at: "2016-10-28 15:40:00", updated_at: "2016-10-28 15:40:00", point: 0>
[2] pry(main)> user.point
=> 0
[3] pry(main)> user.increment(:point, 1000)
=> #<User id: 1, email: "hoge@hoge.com", created_at: "2016-10-28 15:40:00", updated_at: "2016-10-28 15:40:00", point: 1000>
[4] pry(main)> user.save
(0.2ms) BEGIN
SQL (5.6ms) UPDATE `users` SET `point` = 1000, `updated_at` = '2016-10-29 00:22:49' WHERE `users`.`id` = 1
(10.9ms) COMMIT
=> true
[5] pry(main)> user.decrement(:point, 500)
=> #<User id: 1, email: "hoge@hoge.com", created_at: "2016-10-28 15:40:00", updated_at: "2016-10-29 00:22:49", point: 500>
increment!, decrement!を使うとsaveはいらなくなります。
[6] pry(main)> user.increment!(:point, 2000)
(0.2ms) BEGIN
SQL (0.3ms) UPDATE `users` SET `point` = 2500, `updated_at` = '2016-10-29 00:27:20' WHERE `users`.`id` = 1
(7.1ms) COMMIT
=> true
[7] pry(main)> user.decrement!(:point, 1000)
(0.2ms) BEGIN
SQL (0.3ms) UPDATE `users` SET `point` = 1500, `updated_at` = '2016-10-29 00:28:12' WHERE `users`.`id` = 1
(6.3ms) COMMIT
=> true
※ increment!ですが、rails5から挙動が少し変わっています。
参考URL: http://qiita.com/kano-e/items/f186a24aaa6db8d19903
update_column
updated_atの値を更新せず、値を上書きしたい時に使います。
バリデーションやコールバックはスキップされます。
[1] pry(main)> article = Article.find(1)
Article Load (19.2ms) SELECT `articles`.* FROM `articles` WHERE `articles`.`id` = 1 LIMIT 1
=> #<Article:0x007f92156caf58
id: 1,
user_id: 1,
text: "サンプルテキスト1",
created_at: Fri, 28 Oct 2016 15:40:00 UTC +00:00,
updated_at: Fri, 28 Oct 2016 15:40:00 UTC +00:00>
[2] pry(main)> article.update_column(:text, "updated_atは更新しないよ")
SQL (28.3ms) UPDATE `articles` SET `articles`.`text` = 'updated_atは更新しないよ' WHERE `articles`.`id` = 1
=> true
[3] pry(main)> article
=> #<Article:0x007f92156caf58
id: 1,
user_id: 1,
text: "updated_atは更新しないよ",
created_at: Fri, 28 Oct 2016 15:40:00 UTC +00:00,
updated_at: Fri, 28 Oct 2016 15:40:00 UTC +00:00>
[4] pry(main)> article.update_attribute(:text, "updated_atを更新するよ")
(0.2ms) BEGIN
SQL (6.0ms) UPDATE `articles` SET `text` = 'updated_atを更新するよ', `updated_at` = '2016-10-29 01:08:05' WHERE `articles`.`id` = 1
(11.0ms) COMMIT
=> true
[5] pry(main)> article
=> #<Article:0x007f92156caf58
id: 1,
user_id: 1,
text: "updated_atを更新するよ",
created_at: Fri, 28 Oct 2016 15:40:00 UTC +00:00,
updated_at: Sat, 29 Oct 2016 01:08:05 UTC +00:00>
touch
updated_atを現在時刻でアップデートできます。
[1] pry(main)> article = Article.find(1)
Article Load (0.4ms) SELECT `articles`.* FROM `articles` WHERE `articles`.`id` = 1 LIMIT 1
=> #<Article:0x007f92142ed440
id: 1,
user_id: 1,
text: "サンプルテキスト1",
created_at: Sat, 29 Oct 2016 02:32:16 UTC +00:00,
updated_at: Sat, 29 Oct 2016 02:32:16 UTC +00:00>
[2] pry(main)> article.touch
(0.2ms) BEGIN
SQL (0.3ms) UPDATE `articles` SET `articles`.`updated_at` = '2016-10-29 02:36:13' WHERE `articles`.`id` = 1
(2.7ms) COMMIT
=> true
[3] pry(main)> article.updated_at
=> Sat, 29 Oct 2016 02:36:13 UTC +00:00
in?
引数で与えられた要素の中にレシーバが含まれているか判定するメソッドになります。
下の例は配列ですが、文字列に対しても使えます。
[1] pry(main)> array = ["Tanaka", "Sato", "Suzuki"]
=> ["Tanaka", "Sato", "Suzuki"]
[2] pry(main)> "Tanaka".in?(array)
=> true
in?はinclude?のレシーバと引数を逆にしたもののようです。
定義部分のソースコードを見てみると分かります。
def in?(another_object)
another_object.include?(self)
rescue NoMethodError
raise ArgumentError.new("The parameter passed to #in? must respond to #include?")
end
presence_in
in?メソッドはtrue, falseを返すのに対して、presence_inはtrueの場合、自身を返して、falseの場合nilを返します。
[1] pry(main)> array = ["Tanaka", "Sato", "Suzuki"]
=> ["Tanaka", "Sato", "Suzuki"]
[2] pry(main)> "Tanaka".presence_in(array)
=> "Tanaka"
[3] pry(main)> "Takahashi".presence_in(array)
=> nil
こちらもソースコードを読んでみるとよくわかります。
def presence_in(another_object)
in?(another_object) ? self : nil
end
take
引数で指定した件数のレコードを取得します。
[1] pry(main)> Article.take(3)
Article Load (0.3ms) SELECT `articles`.* FROM `articles` LIMIT 3
=> [#<Article:0x007f9219993f38
id: 1,
user_id: 1,
text: "サンプルテキスト1",
created_at: Sat, 29 Oct 2016 01:12:54 UTC +00:00,
updated_at: Sat, 29 Oct 2016 01:12:54 UTC +00:00>,
#<Article:0x007f92199911e8
id: 2,
user_id: 1,
text: "サンプルテキスト2",
created_at: Sat, 29 Oct 2016 01:12:54 UTC +00:00,
updated_at: Sat, 29 Oct 2016 01:12:54 UTC +00:00>,
#<Article:0x007f9219991058
id: 3,
user_id: 1,
text: "サンプルテキスト3",
created_at: Sat, 29 Oct 2016 01:12:54 UTC +00:00,
updated_at: Sat, 29 Oct 2016 01:12:54 UTC +00:00>]
many?
条件を満たす要素が2つ以上ある場合にtrueが返ります。
[1] pry(main)> article = Article.where(id: 1)
Article Load (0.3ms) SELECT `articles`.* FROM `articles` WHERE `articles`.`id` = 1
=> [#<Article:0x007f9215621750
id: 1,
user_id: 1,
text: "サンプルテキスト1",
created_at: Sat, 29 Oct 2016 01:12:54 UTC +00:00,
updated_at: Sat, 29 Oct 2016 01:12:54 UTC +00:00>]
[2] pry(main)> article.many?
=> false
[3] pry(main)> article = Article.where(id: [1, 2])
Article Load (0.3ms) SELECT `articles`.* FROM `articles` WHERE `articles`.`id` IN (1, 2)
=> [#<Article:0x007f9215c8e070
id: 1,
user_id: 1,
text: "サンプルテキスト1",
created_at: Sat, 29 Oct 2016 01:12:54 UTC +00:00,
updated_at: Sat, 29 Oct 2016 01:12:54 UTC +00:00>,
#<Article:0x007f9215c8dcb0
id: 2,
user_id: 1,
text: "サンプルテキスト2",
created_at: Sat, 29 Oct 2016 01:12:54 UTC +00:00,
updated_at: Sat, 29 Oct 2016 01:12:54 UTC +00:00>]
[4] pry(main)> article.many?
=> true
似たようなメソッドで、all?
,one?
,any?
やnone?
などもありますね。
カラム名?
カラムの値がnil, ""の時はfalseを返します。
[1] pry(main)> article = Article.find(1)
Article Load (1.0ms) SELECT `articles`.* FROM `articles` WHERE `articles`.`id` = 1 LIMIT 1
=> #<Article:0x007f9219a28d90 id: 1, user_id: 1, text: nil, created_at: Sat, 29 Oct 2016 01:12:54 UTC +00:00, updated_at: Sat, 29 Oct 2016 01:33:22 UTC +00:00>
[2] pry(main)> article.text
=> nil
[3] pry(main)> article.text?
=> false
[4] pry(main)> article.text = ""
=> ""
[5] pry(main)> article.text?
=> false
[6] pry(main)> article.text = []
=> []
[7] pry(main)> article.text?
=> true
[8] pry(main)> article.text = false
=> false
[9] pry(main)> article.text?
=> true
changed?
インスタンスが変わったかどうか調べることができます。
changes
を用いるとどのカラムの値が変わったのかわかります。
[1] pry(main)> article = Article.find(3)
Article Load (0.3ms) SELECT `articles`.* FROM `articles` WHERE `articles`.`id` = 3 LIMIT 1
=> #<Article:0x007f9219268358 id: 3, user_id: 1, text: "サンプルテキスト3", created_at: Sat, 29 Oct 2016 01:12:54 UTC +00:00, updated_at: Sat, 29 Oct 2016 01:12:54 UTC +00:00>
[2] pry(main)> article.changed?
=> false
[3] pry(main)> article.text = "変わるかな"
=> "変わるかな"
[4] pry(main)> article.changed?
=> true
[5] pry(main)> article.changes
=> {"text"=>["サンプルテキスト3", "変わるかな"]}
※ rails5.1からは非推奨になっています。
カラム名_changed?
カラムの値が変わったか調べることができます。
カラム名_change
を用いると変更前と変更後の値を取得できます。
[1] pry(main)> article = Article.find(2)
Article Load (0.3ms) SELECT `articles`.* FROM `articles` WHERE `articles`.`id` = 2 LIMIT 1
=> #<Article:0x007f9214277b50 id: 2, user_id: 1, text: "サンプルテキスト2", created_at: Sat, 29 Oct 2016 01:12:54 UTC +00:00, updated_at: Sat, 29 Oct 2016 01:12:54 UTC +00:00>
[2] pry(main)> article.text
=> "サンプルテキスト2"
[3] pry(main)> article.text_changed?
=> false
[4] pry(main)> article.text = "上書き"
=> "上書き"
[5] pry(main)> article.text_changed?
=> true
[6] pry(main)> article.text_change
=> ["サンプルテキスト2", "上書き"]
カラム名_was
を用いると上書き前のテキストが表示されます。
[6] pry(main)> article.text_was
=> "サンプルテキスト2"
※ こちらもrails5.1からは非推奨になっています。
カラム名_will_change!
そのカラムの値がすでに変更されたものとして扱われることになります。
それに対して、restore_カラム名!
をすると変更されていないものとして扱えます。
[1] pry(main)> article = Article.find(3)
Article Load (0.3ms) SELECT `articles`.* FROM `articles` WHERE `articles`.`id` = 3 LIMIT 1
=> #<Article:0x007f92155f30a8 id: 3, user_id: 1, text: "サンプルテキスト3", created_at: Sat, 29 Oct 2016 01:12:54 UTC +00:00, updated_at: Sat, 29 Oct 2016 01:12:54 UTC +00:00>
[2] pry(main)> article.changed?
=> false
[3] pry(main)> article.text_changed?
=> false
[4] pry(main)> article.text_will_change!
=> "サンプルテキスト3"
[5] pry(main)> article.changed?
=> true
[6] pry(main)> article.text_changed?
=> true
collection_singular_ids
リレーションがhas_manyの場合、例えば、user.article_ids
とするとuser.articles.pluck(:id)
とuser.articles.map(&:id)
と同じ結果が返ってきます。
[1] pry(main)> user = User.find(1)
User Load (0.5ms) SELECT `users`.* FROM `users` WHERE `users`.`id` = 1 LIMIT 1
=> #<User id: 1, email: "hoge@hoge.com", created_at: "2016-10-29 01:12:54", updated_at: "2016-10-29 01:12:54", point: 0>
[2] pry(main)> user.article_ids
(0.4ms) SELECT `articles`.id FROM `articles` WHERE `articles`.`user_id` = 1
=> [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
[3] pry(main)> user.articles.pluck(:id)
(0.3ms) SELECT `articles`.`id` FROM `articles` WHERE `articles`.`user_id` = 1
=> [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
[4] pry(main)> user.articles.map(&:id)
Article Load (0.3ms) SELECT `articles`.* FROM `articles` WHERE `articles`.`user_id` = 1
=> [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
before_add_for_collection
リレーションがhas_manyの場合、コールバックをProcとして取得できます。
after_add, before_remove, after_removeに関しても同様です。
class User < ActiveRecord::Base
has_many :articles, before_add: :output_message
def output_message(article)
puts "#{article}を作ります。"
end
end
[1] pry(main)> user = User.find(1)
User Load (0.3ms) SELECT `users`.* FROM `users` WHERE `users`.`id` = 1 LIMIT 1
=> #<User id: 1, email: "hoge@hoge.com", created_at: "2016-10-29 02:32:16", updated_at: "2016-10-29 02:32:16", point: 0>
[2] pry(main)> user.before_add_for_articles
=> [#<Proc:0x007f921578d1c0@/Users/hiroki/.rbenv/versions/2.1.3/lib/ruby/gems/2.1.0/gems/activerecord-4.2.6/lib/active_record/associations/builder/collection_association.rb:50 (lambda)>]
to_param
URLのidの部分にid以外のものを指定できるようになります。
class Article < ActiveRecord::Base
def to_param
text
end
end
article = Article.find_by(text: 'sample')
article_path(article) # => "/articles/sample"
まとめ
生命、宇宙、そして万物についての究極の疑問の答えは「42」である。
[1] pry(main)> Article.forty_two
Article Load (0.7ms) SELECT `articles`.* FROM `articles` ORDER BY `articles`.`id` ASC LIMIT 1 OFFSET 41
=> #<Article:0x007f9215fc6170 id: 42, user_id: 1, text: "サンプルテキスト42", created_at: Sat, 29 Oct 2016 02:32:16 UTC +00:00, updated_at: Sat, 29 Oct 2016 02:32:16 UTC +00:00>