Alder Green
2006-May-08 21:29 UTC
[Rails] Creating a "Foo has_many bars" association where bar isn''t a model.
Hi Let''s say we have model Foo. Each Foo instance can have several bars. Those bars are primitive, so they shouldn''t be models. For example, Foo might be a type of convention, and the bars might be years the convention was held in. Naively, we would have a conventions_years date, and put: has_many :years inside class Convention. But then we''d get an error, since for has_many to work, there must be a Year model. Which is silly: why would we have a Year model, then get the actual year by Year#year...? So what''s the Railish take on this? Can you have a has_many association with the "many" as simple values, not full models? What did I miss? -Alder
Jeremy Kemper
2006-May-08 22:16 UTC
[Rails] Creating a "Foo has_many bars" association where bar isn''t a model.
On May 8, 2006, at 2:29 PM, Alder Green wrote:> Let''s say we have model Foo. Each Foo instance can have several bars. > Those bars are primitive, so they shouldn''t be models. For example, > Foo might be a type of convention, and the bars might be years the > convention was held in. Naively, we would have a conventions_years > date, and put: > > has_many :years > > inside class Convention. But then we''d get an error, since for > has_many to work, there must be a Year model. Which is silly: why > would we have a Year model, then get the actual year by Year#year...? > > So what''s the Railish take on this? Can you have a has_many > association with the "many" as simple values, not full models? What > did I miss?This is a case of persisting "values objects" rather than "entities." (I like to think of the difference as "would I compare these by value or by id?") Active Record provides the composed_of and serialize class methods to map columns to custom classes or to store them as YAML. Using serialize: # create table conventions (id serial primary key, years text); class Convention serialize :years, Set end foscon = Convention.new foscon.years << 2005 foscon.save! foscon[''years''] == foscon.years.to_yaml Using composed_of: Currency = Struct.new(:code) # create table markets (id serial primary key, currency_code char (3)); class Market composed_of :currency, :mapping => %w(currency_code code) end japan = Market.new(:currency => Currency.new(''JPY'')) japan.save! japan.currency_code == ''JPY'' japan.currency == Currency.new(''JPY'') Active Record does not handle mappings such as: # create table conventions (id serial primary key) # create table conventions_years (convention_id not null primary key references conventions(id), year) class Conventions # Returns a Set of 2001, 2002, etc. has_many :years, :collection => Set, :value => ''year'' # However, you may: has_many :convention_years def years() convention_years.map { |y| y.year }.to_set end end Best, jeremy
Alder Green
2006-May-09 08:10 UTC
[Rails] Creating a "Foo has_many bars" association where bar isn''t a model.
On 5/9/06, Jeremy Kemper <jeremy@bitsweat.net> wrote:> On May 8, 2006, at 2:29 PM, Alder Green wrote: > > Let''s say we have model Foo. Each Foo instance can have several bars. > > Those bars are primitive, so they shouldn''t be models. For example, > > Foo might be a type of convention, and the bars might be years the > > convention was held in. Naively, we would have a conventions_years > > date, and put: > > > > has_many :years > > > > inside class Convention. But then we''d get an error, since for > > has_many to work, there must be a Year model. Which is silly: why > > would we have a Year model, then get the actual year by Year#year...? > > > > So what''s the Railish take on this? Can you have a has_many > > association with the "many" as simple values, not full models? What > > did I miss? > > This is a case of persisting "values objects" rather than > "entities." (I like to think of the difference as "would I compare > these by value or by id?") > > Active Record provides the composed_of and serialize class methods to > map columns to custom classes or to store them as YAML. > > > Using serialize: > > # create table conventions (id serial primary key, years text); > class Convention > serialize :years, Set > end > > foscon = Convention.new > foscon.years << 2005 > foscon.save! > foscon[''years''] == foscon.years.to_yamlNice. A small problem here is that after foscon = Convention.new foscon.years is nil, so it doesn''t have the << (or most any other) method. You can ammend this with foscon.years = Set.new but I''m sure there''s a more proper common practice?> Using composed_of: > > Currency = Struct.new(:code) > > # create table markets (id serial primary key, currency_code char > (3)); > class Market > composed_of :currency, :mapping => %w(currency_code code) > end > > japan = Market.new(:currency => Currency.new(''JPY'')) > japan.save! > japan.currency_code == ''JPY'' > japan.currency == Currency.new(''JPY'') > > > Active Record does not handle mappings such as: > > # create table conventions (id serial primary key) > # create table conventions_years (convention_id not null primary > key references conventions(id), year) > class Conventions > # Returns a Set of 2001, 2002, etc. > has_many :years, :collection => Set, :value => ''year'' > > # However, you may: > has_many :convention_years > def years() convention_years.map { |y| y.year }.to_set end > end > > > Best, > jeremy > _______________________________________________ > Rails mailing list > Rails@lists.rubyonrails.org > http://lists.rubyonrails.org/mailman/listinfo/rails >The rest of your response acquainted me with a comprehensive set of tools relevant to the task. Thanks, Jeremy! -Alder