How to cope with non-persisted and persisted associated objects in Rails at the same time?
I will try to explain this very simple. So, I skipped any code which is not necessary to illustrate the problem.
Let's assume a model:
class Model < ActiveRecord::Base
# Join class storing grants for users, e.g. 'admin', 'default'
has_many :user_grants
# Grant a user a certain grant
def grant_user(user, grant)
self.user_grants.build(:user => user, :grant => grant)
end
# Some action is done by a user, but ensure user has required grants
def action(user, action_type)
unless user_grants.find_by_user_id_and_grant(user, GrantEnum::ADMIN)
raise ModelUserNotAllowedError.new
end
...action code...
end
I skipped the code for User and some other models because they are not relevant to illustrate...
We instantiate the code:
1: @model = Model.new
2: @user = User.new
3: @model.grant_user(@user, GrantEnum::ADMIN)
4: @model.action(@user, ActionEnum::SOMETHING)
Line 4. will raise a ModelUserNotAllowedError because the required grant build in line 3 is not persisted at this time, but finder methods only work on persisted data. From a logical point of view, the error shouldn't be raised because the user is suffiecent granted.
I am using self.user_grants.build because the Model may not be persisted when calling grant_user.
So, what are the options to get this working:
1.) Implement code that only uses association.create instead of association.build to ensure f开发者_StackOverflowinder methods are "up-to-date". This also requires to save parents first.
2.) Implement code that checks both persisted data by using finder methods and non-persisted data by looping through the assication, e.g. given the above example, one may do to check non-persisted data:
user_grants.each do |user_grant|
if user_grant.grant.eql?(GrantEnum::ADMIN)
return true
end
end
However, proposal number 2 seems to be a bad idea. So, let's forget it...
Are there any other options? e.g. Is there a way to get finder methods to check non-persisted data as well? Hope to get some new insights...
unless user_grants.select {|user_grant| user == user && grant == GrantEnum::ADMIN}.present?
raise ModelUserNotAllowedError.new
end
精彩评论