AcceptsNestedIds
Conundrum:
You want to save associations (has_many
or has_many :through
) by ID, but Rails is totally not doing what you expect it to.
Solution:
This gem.
What the hell am I talking about?
Let's take a typical scenario.
class Project < ActiveRecord::Base
has_many :project_users
has_many :users, through: :project_users
validates :name, presence: true
attribute :user_ids
end
When creating or updating a Project, you can select a list of User IDs from a select list. Great.
project = Project.first
project.name = ""
project.user_ids = [1,2,3]
project.save!
Oh shit.
Thats right: Rails went ahead and associated those Users, even through the save failed. Because it didn't even wait until the save!
to associate them. It happened right here: project.user_ids = [1,2,3]
No one wants this. But it happens all the time.
Get to the point already
AcceptsNestedIds defers the saving of ID-based associations to a model's after_save
callback. In the example above, no User associations would have been created when using this gem.
Bonus
Ever need audit trail functionality? Its easy, using ActiveModel::Dirty and its related methods (changes?
, etc). However, what you won't get out-of-the-box is dirty tracking for associated attributes. Because why would you?
AcceptsNestedIds adds dirty tracking for ID-based associations:
project = Project.first
project.user_ids = [1,2,3]
project.changes # => "user_ids" => [[], [1,2,3]]
Beauty.
Installation
Add this line to your application's Gemfile:
gem 'accepts_nested_ids'
And then execute:
$ bundle
Or install it yourself as:
$ gem install accepts_nested_ids
Usage
When your association is conventionally named:
class Project < ActiveRecord::Base
include AcceptsNestedIds
has_many :project_users
has_many :users, through: :project_users
accepts_nested_ids_for :users
attribute :user_ids
end
project.user_ids = [...]
When your association has a custom name:
class Project < ActiveRecord::Base
include AcceptsNestedIds
has_many :project_users
has_many :included_users, through: :project_users, source: :user
accepts_nested_ids_for included_users: "User"
attribute :user_ids
end
project.included_user_ids = [...]
When using a custom association name, you must specify the actual class name as a second arg, in this case "User".
Mix and match as desired:
class Project < ActiveRecord::Base
include AcceptsNestedIds
has_many :documents
has_many :project_users
has_many :included_users, through: :project_users, source: :user
accepts_nested_ids_for :documents, included_users: "User"
attribute :user_ids
end
project.document_ids = [...]
project.included_user_ids = [...]
Development
After checking out the repo, run bin/setup
to install dependencies. Then, run rake rspec
to run the tests. You can also run bin/console
for an interactive prompt that will allow you to experiment.
To install this gem onto your local machine, run bundle exec rake install
. To release a new version, update the version number in version.rb
, and then run bundle exec rake release
, which will create a git tag for the version, push git commits and tags, and push the .gem
file to rubygems.org.
Requirements
Requires Activemodel & Activerecord 6.1.1
or higher
Contributing
Bug reports and pull requests are welcome on GitHub at https://github.com/uberllama/accepts_nested_ids.
License
The gem is available as open source under the terms of the MIT License.