#3088 - [PATCH] Make has_one with :conditions hash scope build or creation of object with those conditions

Hi guys,

I created a patch on Lighthouse [1] that makes has_one associations with conditions provided with a hash be built or created with those conditions. As it is explained in the doc added:

"Record creation from the association is scoped if a hash is used. has_one :account, :conditions => {:enabled => true} will create an enabled account with @company.create_account or @company.build_account"

I needed to support this in an app I'm working on, and I think it's a small patch that might be useful for others too. The ticket has been there with a couple of days so I thought that maybe through this message someone will be interested and comment on it :slight_smile:

Thanks!

[1] https://rails.lighthouseapp.com/projects/8994/tickets/3088-make-has_one-with-conditions-hash-scope-build-or-creation-of-object-with-those-conditions

I like the concept and think it should be expanded to other associations. We already define certain attributes of associated objects for selecting, the symmetry of having build/create defaults is handy.

I could use it on a current project right now: has_many :trial_memberships, :conditions => {:state => 'trial', :starts_on => Time.now}

@company.trial_memberships.build(other_opts_that_merge) or @company.trial_memberships.create

would set initial state and starts_on values for objects associated through these associations.

Comments: 1) :conditions is a bad name choice (even if the concept is not expanded to other association types). Other associations already use :conditions to define database selects (example from the docs: has_many :approved_comments, :class_name => 'Comment', :conditions => ['approved = ?', true])

Use :build_options, :build_attributes, :initial_attributes, etc.

2) HasMany and HABTM Associations already have a mechanism that could be used for setting initial attributes (the Association callbacks). has_many : trial_memberships, :before_add => :set_initial_state_to_trial, :set_starts_at_to_now

has_one/belongs_to don't have these callbacks.

I'd love to see all associations get callbacks. these could be used internally for setting initial attributes on associated objects, making has_many : trial_memberships, :initial_attriubtes => {:state => 'trial', :starts_at => Time.now} a shorthand syntax for has_many : trial_memberships, :after_add => :set_initial_state_to_trial, :set_starts_at_to_now

-Trek

Isn't this stuff that is taken care of by :default on a column when creating migrations?

I like the concept and think it should be expanded to other associations. We already define certain attributes of associated objects for selecting, the symmetry of having build/create defaults is handy.

I could use it on a current project right now: has_many :trial_memberships, :conditions => {:state => 'trial', :starts_on => Time.now}

@company.trial_memberships.build(other_opts_that_merge) or @company.trial_memberships.create

would set initial state and starts_on values for objects associated through these associations.

Actually, I think that behaviour is already there for has_many and habtm (and what this patch does is extend the same behavior to has_one):

:conditions - Specify the conditions that the associated objects must meet in order to be included as a WHERE SQL fragment, such as price > 5 AND name LIKE ‘B%’. Record creations from the association are scoped if a hash is used. has_many :posts, :conditions => {:published => true} will create published posts with @blog.posts.create or @blog.posts.build

Comments: 1) :conditions is a bad name choice (even if the concept is not expanded to other association types). Other associations already use :conditions to define database selects (example from the docs: has_many :approved_comments, :class_name => 'Comment', :conditions => ['approved = ?', true])

Use :build_options, :build_attributes, :initial_attributes, etc.

2) HasMany and HABTM Associations already have a mechanism that could be used for setting initial attributes (the Association callbacks). has_many : trial_memberships, :before_add => :set_initial_state_to_trial, :set_starts_at_to_now

has_one/belongs_to don't have these callbacks.

I'd love to see all associations get callbacks. these could be used internally for setting initial attributes on associated objects, making has_many : trial_memberships, :initial_attriubtes => {:state => 'trial', :starts_at => Time.now} a shorthand syntax for has_many : trial_memberships, :after_add => :set_initial_state_to_trial, :set_starts_at_to_now

I feel that callbacks for has_one/belongs_to associations might be too much, but maybe it could be a good progression from this patch.

Thanks for looking at it and sharing your comments :slight_smile:

Isn't this stuff that is taken care of by :default on a column when creating migrations?

If you do it at the database level, then all your records will have those defaults. On the contrary it's just that has_one association that you might want to have some defaults based on the conditions you have. For example with "has_one :account, :conditions => {:enabled => true}", you only want the account from that model to be enabled by default, but for a regular account you would still want it to be false.

Oooops, you're totally right. I guess I've always used the array syntax for :conditions. In that case, :conditions is definitely the best name for the option and the patch brings has_one into alignment with other association types.

Clearly I haven't been using this feature on the collection association types, but now that I know about it, I'll be refactoring away some sloppier code. I'd like to have it available for has_one +1.