子育て中の勉強ログなど

結婚引越し出産で離職した30代が再就職目指して色々頑張るブログ。まさかりは優しく投げてください。

アソシエーション時の配列はArrayクラスではない

Railsのアソシエーションにて親モデルと子モデルの紐付けはインスタンス配列で参照できます。 アソシエーションの設定を行った後ならば、このインスタンス配列自体で削除や追加を行うと、Railsが自動的に紐付け(外部キーに親モデルのidを入れる)を行ってくれます。

# report.rb
class Report < ApplicationRecord
  has_many :comments
  ...
end

# comment.rb
class Report < ApplicationRecord
  belongs_to :report
  ...
end
r1 = Report.find(1)
comments_at_r1 = r1.comments
puts comments_at_r1 #=> [#<Comment:0x000... id: 1, ...>, #<Comment:0x000... id: 2, ...>, <Comment:0x000... id: 3, ...>]
#  この配列に追加したり削除したりすることで紐付けを行なってくれる。
new_comment = Comment.new(content: "test")
comments_at_r1 << new_comment #外部キーに自動でreport_id: 1を入れてくれる!

しかし、そのインスタンス配列はクラスはArrayではありません。ActiveRecord::Associations::CollectionProxyです。

なので、Arrayでは使えるけど、ActiveRecord::Associations::CollectionProxyでは使えないメソッドがあります。 delete_atとかdelete_ifとかはダメ。(他の候補挙げてくれるので優しい)

comments_at_r1.delete_at(1)
#=> `method_missing': undefined method `delete_at' for #<ActiveRecord::Associations::CollectionProxy ...
#Did you mean?  delete_all delete delete_by

あれ、と思ったらクラスを確認したり、methodsメソッドで使えるメソッドを確認しよう!というのが今回の気づきです。

irb(main):010:0> mentions.class
=> Mention::ActiveRecord_Associations_CollectionProxy
irb(main):011:0> mentions.class.superclass
=> ActiveRecord::Associations::CollectionProxy
irb(main):012:0> mentions.class.superclass.superclass # 全部見るならancestorsの方が便利?
=> ActiveRecord::Relation
irb(main):013:0> mentions.class.superclass.superclass.superclass
=> Object

irb(main):0014:0> mentions.methods
=>
[:strict_loading_value=,
 :create_with!,
 :from!,
 :distinct_value=,...