Summary
--------------------------------------------------------------------
I feel that it should be possible to set a collection directly to an
array and have those changes propagated to the database. It should
perform a two-way diff between the old array and the new, adding and
removing records from the n-n table as appropriate.
Details
--------------------------------------------------------------------
I have an ACL-type setup with tables ''users'',
''roles'', and
''users_roles''; the models for each setup the habtm
relationship between
them:
class User < ActiveRecord::Base
has_and_belongs_to_many :roles
#...
end
class Role < ActiveRecord::Base
has_and_belongs_to_many :users
#...
end
At runtime, this sets up a users= setter method for a Role:
irb(main):025:0> sa = Role.find 6
=> #<Role:0x2474f34
@attributes={"name"=>"Super-Admin",
"id"=>"6"}>
irb(main):026:0> sa.methods.select{|n|n=~/user/}.sort
=> ["add_users", "has_users?",
"remove_users", "users", "users=",
"users_count"]
Using the #<<, and #remove_users methods (and #clear) do work as
desired:
irb(main):032:0> sa.users << User.find_all
=> [#<User:0x245d154 @errors=#<ActiveRecord::Errors:0x245c90c
@errors={}, @base=#<User:0x245d154 ...>>,
@attributes={"zip"=>"80303",
"nickname"=>"Phrogz",
"disabled_flag"=>"f",
"lastname"=>"Kistner",
"firstname"=>"Gavin",
"id"=>"1", "phone"=>nil,
"password"=>"foo",
"login"=>"phrogz",
"email"=>"gavin-XtLdkLkwz3ZWk0Htik3J/w@public.gmane.org"},
@new_record_before_save=nil>]
irb(main):033:0> sa = Role.find 6
=> #<Role:0x2450f94
@attributes={"name"=>"Super-Admin",
"id"=>"6"}>
irb(main):034:0> sa.users
=> [#<User:0x244e7f8
@attributes={"zip"=>"80303",
"nickname"=>"Phrogz",
"role_id"=>"6",
"disabled_flag"=>"f",
"lastname"=>"Kistner",
"firstname"=>"Gavin",
"id"=>"1", "phone"=>nil,
"user_id"=>"1",
"password"=>"foo",
"login"=>"phrogz",
"email"=>"gavin-XtLdkLkwz3ZWk0Htik3J/w@public.gmane.org"}>]
I expect to be able to set this collection directly, but this does not
work. For example:
irb(main):027:0> sa.users
=> []
irb(main):028:0> sa.users = User.find_all
=> [#<User:0x246bc18
@attributes={"zip"=>"80303",
"nickname"=>"Phrogz",
"disabled_flag"=>"f",
"lastname"=>"Kistner",
"firstname"=>"Gavin",
"id"=>"1", "phone"=>nil,
"password"=>"foo",
"login"=>"phrogz",
"email"=>"gavin-XtLdkLkwz3ZWk0Htik3J/w@public.gmane.org"}>]
irb(main):029:0> sa.save => true
irb(main):030:0> sa = Role.find 6
=> #<Role:0x2462528
@attributes={"name"=>"Super-Admin",
"id"=>"6"}>
irb(main):031:0> sa.users
=> []
I can understand why this feature might not have been implemented,
but given the presence of a #users= method, I expected to be able to
set it to an array. This should be possible given an implementation
of the following code (albeit in the generic case for any habtm):
class Role
def users=( new_array )
self.users << ( new_array - self.users )
self.remove_users( self.users - new_array )
end
end
--
(-, /\ \/ / /\/