Recent 3.2.9 release bugfix has brought an issue for through
associations.inverse_of
usage can solve the problem only partially (polymorphic associations are still affected).
UPDATE: patch that introduced this issue has just been reverted in rails master.
Few days ago Rails 3.2.9 was released.
Among the bugfixes a fix for through
associations is present, and since 3.2.9 join model instances are built even though the object is not saved yet.
To get the point take a look at these simple models with User
and Team
joined with each other through
UserMembership
:
class User < ActiveRecord::Base has_many :user_memberships has_many :teams, through: :user_memberships end class Team < ActiveRecord::Base has_many :user_memberships has_many :users, through: :user_memberships end class UserMembership < ActiveRecord::Base belongs_to :team belongs_to :user validates :team, :user, presence: true end
Before Rails 3.2.9 this code does not build any UserMembership
instances:
team = Team.new(users: [User.first]) team.valid? #=> true team.user_memberships #=> []
But since Rails 3.2.9 UserMembership
instance is built:
team = Team.new(users: [User.first]) team.valid? #=> false team.user_memberships #=> [#] team.user_memberships.first.team #=> nil
Oh, have you noticed that team
is invalid now? Aahh, now when it get’s complicated.
Built instance of a join UserMembership
model
has no team
set and that’s why it’s invalid (it has validate :team, :user, presence: true
).
As soon as join model instance is invalid team
is invalid as well.
To fix this validation issues in this particular case the one should use inverse_of
for the associations like this:
class User < ActiveRecord::Base has_many :user_memberships, :inverse_of => :user has_many :teams, through: :user_memberships end class Team < ActiveRecord::Base has_many :user_memberships, :inverse_of => :team has_many :users, through: :user_memberships end team = Team.new(users: [User.first]) team.valid? #=> true team.user_memberships.first.valid? #=> true team.user_memberships.first.team #=> #
inverse_of
takes care of associations objects in-memory synchronization, so now UserMembership
instance has a team
and it’s valid.
Unfortunately polymorphic associations are still affected – follow this github discussion for more info.
That said, if you’ve just upgraded to 3.2.9 and your tests are failing review the associations and fix them whenever it’s possible by using inverse_of
.