1
\$\begingroup\$

I needed to add existing models to a has-many relation using nested attributes so I overwrote thumbnails_attributes=:

class Gallery < ActiveRecord::Base
 has_many :thumbnails, dependent: :nullify
 accepts_nested_attributes_for :thumbnails
 def thumbnails_attributes=(thumbnails_attributes)
 ids = thumbnails_attributes.map { |t| t['id'] }
 (ids - thumbnail_ids).each do |id|
 thumbnail = Thumbnail.find id
 thumbnails << thumbnail
 end
 super(thumbnails_attributes)
 end
end
class Thumbnail < ActiveRecord::Base
 belongs_to :gallery
end

Given the following data (as fixture):

thumbnails.yml

one:
 gallery: one
two:
 gallery:

galleries.yml

one:
 name: MyString

I can now run:

galleries(:one).update(thumbnails_attributes: ['id' => thumbnails(:two).id])

and galleries(:one).thumbnails will return both thumbnails.

While it works pretty well, I wanted to ask for reviews and ways to improve/clean this code.

The only issue I have is with include? (which is used by assert_includes in my tests):

galleries(:one).update(thumbnails_attributes: ['id' => thumbnails(:two).id])
galleries(:one).thumbnails.include? thumbnails(:two) # false
galleries(:one).thumbnails # [#<Thumbnail one>, #<Thumbnail two>]
galleries(:one).thumbnails.include? thumbnails(:two) # true

It looks like I have to reload the relation for include? to work, but I haven't found a fix nor a way to properly test it.

galleries(:one).thumbnails.to_a.include? thumbnails(:two) works, include? doesn't appear to load the association.

Phrancis
20.5k6 gold badges69 silver badges155 bronze badges
asked Nov 3, 2014 at 12:51
\$\endgroup\$

1 Answer 1

3
\$\begingroup\$

Couple of things to improve here:

Firstly - do not use fixtures, use factories. There is a neat library allowing you to do this with ease called FactoryGirl (for rails you will need FactoryGirlRails). Having this in place you can define factories like:

FactoryGirl.define do
 factory :thumbnail do
 name 'Thumbnail name if you have such a field'
 other_field :other_value
 end
end

And then use it as:

thumbnail = FactoryGirl.create :thumbnail

One advantage of factories is that it does not load all the data into the database for each test, but rather allows you tell it what is needed instead.

There are quite a lot of options you can add to your factories, like dynamic field values (useful when you have uniqueness validations), building parameters, building associated objects etc. All are well documented on gem github wiki.

Secondly, accepts_nested_attributes_for already provides you with a method to assign objects using their ids:

@model.children_ids << [1,2,3]

or even modify the whole association in one go

@model.children_ids = [1,2,3]

This is usually more useful in terms of web forms. If you have a form where client can select multiple objects from a list from db, all you need to do is to name those fields children_ids and rails will take care of persisting the association.

answered Nov 5, 2014 at 10:32
\$\endgroup\$
1
  • \$\begingroup\$ The children_ids array is exactly what I was looking for, thank you. FactoryGirl was quite a burden to setup (I had to use database_cleaner and minitest-around) but might prove useful in the future. \$\endgroup\$ Commented Nov 27, 2014 at 14:24

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.