117
90

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

Rails.composed_of

Last updated at Posted at 2014-04-04

はじめに

調べ物をしていたら、複数カラムをバリューオブジェクトに構成することで仮想的に一つのカラムとして扱う、composed_ofなるものを見つけたので備忘録します。

住所-よくあるパターン

class User
  attr_accessor :city_address, :town_address, :building_address
  def address
    city_address + town_address + building_address
  end
end

attr_accessorの部分は実際にはカラムになってると思ってください。
上のパターンにはいくつかの問題があります。

  • addressのような整形用メソッドがモデルを汚してしまう
  • 本来意味を持つのはaddressなのに、メソッドで返ってくるのは単なる文字列
  • 一旦addressを取得しても、そこからcity_addressを取り出すのは容易ではない

住所-改善されたパターン

根本的な問題は、addressは本当はAddressクラスのオブジェクトであるべきなのにDBの都合でそうなっていない、という点にあります。そこで、addressメソッドでAddressクラスのオブジェクトを返すような実装が思い浮かびます。

class User
  attr_accessor :city_address, :town_address, :building_address
  def address
    Address.new(city_address, town_address, building_address)
  end
end

class Address
  attr_reader :city, :town, :building
  def initialize(city, town, building)
    @city, @town, @building = city, town, building
  end
end

ここで、Addressクラスが単なるRubyオブジェクト(PORO, Plain Old Ruby Object)であることが重要です。これで、address自体が意味を持ちますし、cityの取り出しもできます。

住所-composed_ofを使うパターン

addressメソッドはいいのですが、どうにも単純なメソッドです。これはもしかして、 Rails Wayで楽ができるのではないでしょうか。

class User
  attr_accessor :city_address, :town_address, :building_address
  composed_of :address, mapping: [%w(city_address city), %w(town_address town), %w(building_address building)]
end

class Address
  attr_reader :city, :town, :building
  def initialize(city, town, building)
    @city, @town, @building = city, town, building
  end
end

composed_ofを使うことで、よりシンプルに書けるようになりました。
mappingオプションには、配列の配列を指定します。内側の配列は2要素で、まずカラム名を、次にバリューオブジェクトの属性名を指定します。これにより、user.city_addressuser.address.cityにマップされるようになります。

まとめ

composed_ofを使ってみたわけですが、実は重要なのはcomposed_ofではなく、バリューオブジェクトの方なのかな、と思ったりします。要は、アプリケーション内でそれ自体の意味があるものをそれ自体のクラスにくくりだすということです。
これは最近思っていることですが、「アプリケーション内でPOROが多く使われているほど、そのアプリケーションは見通しが良い」ということは考えられないでしょうか。この点についてはもっと熟達した方々の意見を待ちたいと思います。

【追記:参考資料】
http://api.rubyonrails.org/classes/ActiveRecord/Aggregations/ClassMethods.html
http://blog.codeclimate.com/blog/2012/10/17/7-ways-to-decompose-fat-activerecord-models/

117
90
2

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
117
90

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?