I just ran across a weird glitch (IMHO) in find_or_create. The arguments passed to it are *not* added to the conditions for the 'first' part. This is odd, given that it's intended to replace find_or_create_by_* methods, which *did* use the specified values as conditions.

I'm unsure on whether this behavior is entirely undesirable, but it's definitely not what I had expected in my use case (ensuring that a join table record exists). Perhaps there should be a variant (find_or_create_exactly, perhaps?) that uses the supplied attributes as additional conditions.

If nothing else, the documentation should be updated to reflect this scenario - the last case in the examples *almost* describes this scenario, but the block form used there makes it unclear what:

User.where(:first_name => 'Scarlett').first_or_create(:last_name => "O'Hara")

would do.

I also found that DataMapper implements the behavior I was expecting in their first_or_create method:

https://github.com/datamapper/dm-core/blob/master/lib/dm-core/model.rb#L448

Thoughts?

--Matt Jones


--
You received this message because you are subscribed to the Google Groups "Ruby on Rails: Core" group.
To post to this group, send email to rubyonrails-core@googlegroups.com.
To unsubscribe from this group, send email to rubyonrails-core+unsubscribe@googlegroups.com.
For more options, visit this group at http://groups.google.com/group/rubyonrails-core?hl=en.

Search Discussions

  • Andrés Mejía at Jul 24, 2012 at 8:05 pm
    I would definitely expect

    User.where(:first_name => 'Scarlett').first_or_create(:last_name =>
    "O'Hara")

    to call

    User.create(:first_name => 'Scarlett', :last_name => "O'Hara")

    if that's not what's happening then I think it's a bug and should be fixed.
    Matt, do you have a minimal app that shows the problem? I would like to
    write a test case for Rails that shows this strange behavior.

    On Tue, Jul 24, 2012 at 2:43 PM, Matt Jones wrote:

    I just ran across a weird glitch (IMHO) in find_or_create. The arguments
    passed to it are *not* added to the conditions for the 'first' part. This
    is odd, given that it's intended to replace find_or_create_by_* methods,
    which *did* use the specified values as conditions.

    I'm unsure on whether this behavior is entirely undesirable, but it's
    definitely not what I had expected in my use case (ensuring that a join
    table record exists). Perhaps there should be a variant
    (find_or_create_exactly, perhaps?) that uses the supplied attributes as
    additional conditions.

    If nothing else, the documentation should be updated to reflect this
    scenario - the last case in the examples *almost* describes this scenario,
    but the block form used there makes it unclear what:

    User.where(:first_name => 'Scarlett').first_or_create(:last_name =>
    "O'Hara")

    would do.

    I also found that DataMapper implements the behavior I was expecting in
    their first_or_create method:

    https://github.com/datamapper/dm-core/blob/master/lib/dm-core/model.rb#L448

    Thoughts?

    --Matt Jones


    --
    You received this message because you are subscribed to the Google Groups
    "Ruby on Rails: Core" group.
    To post to this group, send email to rubyonrails-core@googlegroups.com.
    To unsubscribe from this group, send email to
    rubyonrails-core+unsubscribe@googlegroups.com.
    For more options, visit this group at
    http://groups.google.com/group/rubyonrails-core?hl=en.
    --
    You received this message because you are subscribed to the Google Groups "Ruby on Rails: Core" group.
    To post to this group, send email to rubyonrails-core@googlegroups.com.
    To unsubscribe from this group, send email to rubyonrails-core+unsubscribe@googlegroups.com.
    For more options, visit this group at http://groups.google.com/group/rubyonrails-core?hl=en.
  • Matt Jones at Jul 24, 2012 at 8:29 pm

    On Jul 24, 2012, at 4:04 PM, Andrés Mejía wrote:

    I would definitely expect

    User.where(:first_name => 'Scarlett').first_or_create(:last_name => "O'Hara")

    to call

    User.create(:first_name => 'Scarlett', :last_name => "O'Hara")

    if that's not what's happening then I think it's a bug and should be fixed. Matt, do you have a minimal app that shows the problem? I would like to write a test case for Rails that shows this strange behavior.
    Nope, that's not exactly what I observed; I'll try again. That code *does* call the create correctly, if there are no users with the correct first_name. The confusing part to me was that it wasn't quite the same as this:

    User.where(:first_name => 'Scarlett').find_or_create_by_last_name("O'Hara")

    The latter includes a condition on last_name in the find, where the former does not.

    Given that the dynamic form is deprecated (targeted for removal in 4.1 - see active_record_deprecated_finders for details), it's worth either matching the old behavior or clearly documenting the difference to avoid confused upgraders.

    --Matt Jones


    --
    You received this message because you are subscribed to the Google Groups "Ruby on Rails: Core" group.
    To post to this group, send email to rubyonrails-core@googlegroups.com.
    To unsubscribe from this group, send email to rubyonrails-core+unsubscribe@googlegroups.com.
    For more options, visit this group at http://groups.google.com/group/rubyonrails-core?hl=en.
  • Jon Leighton at Jul 24, 2012 at 9:37 pm

    On 24/07/12 21:29, Matt Jones wrote:
    On Jul 24, 2012, at 4:04 PM, Andrés Mejía wrote:

    I would definitely expect

    User.where(:first_name => 'Scarlett').first_or_create(:last_name => "O'Hara")

    to call

    User.create(:first_name => 'Scarlett', :last_name => "O'Hara")

    if that's not what's happening then I think it's a bug and should be fixed. Matt, do you have a minimal app that shows the problem? I would like to write a test case for Rails that shows this strange behavior.
    Nope, that's not exactly what I observed; I'll try again. That code *does* call the create correctly, if there are no users with the correct first_name. The confusing part to me was that it wasn't quite the same as this:

    User.where(:first_name => 'Scarlett').find_or_create_by_last_name("O'Hara")

    The latter includes a condition on last_name in the find, where the former does not.
    This behaviour is intentional. The dynamic version did actually
    previously take an options hash of stuff that would get passed to
    create. So:

    User.where(:first_name =>
    'Scarlett').find_or_create_by_last_name("O'Hara", :age => 32)

    would do:

    User.where(:first_name => "Scarlett", :last_name => "O'Hara")

    and then:

    User.create(:first_name => "Scarlett", :last_name => "O'Hara", :age => 32)

    I believe the rationale is simply that you can put all of your
    conditions in a where()

    --
    http://jonathanleighton.com/

    --
    You received this message because you are subscribed to the Google Groups "Ruby on Rails: Core" group.
    To post to this group, send email to rubyonrails-core@googlegroups.com.
    To unsubscribe from this group, send email to rubyonrails-core+unsubscribe@googlegroups.com.
    For more options, visit this group at http://groups.google.com/group/rubyonrails-core?hl=en.
  • Matt Jones at Jul 25, 2012 at 5:36 pm

    On Jul 24, 2012, at 5:42 PM, Jon Leighton wrote:
    On 24/07/12 21:29, Matt Jones wrote:


    Nope, that's not exactly what I observed; I'll try again. That code *does* call the create correctly, if there are no users with the correct first_name. The confusing part to me was that it wasn't quite the same as this:

    User.where(:first_name => 'Scarlett').find_or_create_by_last_name("O'Hara")

    The latter includes a condition on last_name in the find, where the former does not.
    This behaviour is intentional. The dynamic version did actually previously take an options hash of stuff that would get passed to create. So:

    User.where(:first_name => 'Scarlett').find_or_create_by_last_name("O'Hara", :age => 32)

    would do:

    User.where(:first_name => "Scarlett", :last_name => "O'Hara")

    and then:

    User.create(:first_name => "Scarlett", :last_name => "O'Hara", :age => 32)

    I believe the rationale is simply that you can put all of your conditions in a where()
    Who's got two thumbs and didn't know about this feature? THIS GUY! :)

    I also noted, after actually RTFM, that this works too:

    User.find_or_create_by_last_name(:last_name => "O'Hara', :age => 32)

    where the named parameter in the dynamic matcher is selected out of the hash that's passed in.

    Any objections to adding an explicit "converting from dynamic finders" section to the documentation for first_or_create? I'll try to get something together this weekend.

    Thanks,

    --Matt Jones

    --
    You received this message because you are subscribed to the Google Groups "Ruby on Rails: Core" group.
    To post to this group, send email to rubyonrails-core@googlegroups.com.
    To unsubscribe from this group, send email to rubyonrails-core+unsubscribe@googlegroups.com.
    For more options, visit this group at http://groups.google.com/group/rubyonrails-core?hl=en.

Related Discussions

Discussion Navigation
viewthread | post
Discussion Overview
grouprubyonrails-core @
categoriesrubyonrails
postedJul 24, '12 at 7:43p
activeJul 25, '12 at 5:36p
posts5
users3
websiterubyonrails.org
irc#RubyOnRails

People

Translate

site design / logo © 2022 Grokbase