Mongoid many-to-many, is this normal? - ruby-on-rails

Here are my models:
class User
include Mongoid::Document
include Mongoid::Timestamps
references_many :roles, :stored_as => :array, :inverse_of => :users
...
end
class Role
include Mongoid::Document
field :name, :type => String
references_many :users, :stored_as => :array, :inverse_of => :roles
...
end
I first create the roles via seed, rake db:seed. My seed file contains:
puts '*** Add default roles'
[
{ :name => 'User' },
{ :name => 'Artist' }
].each do |h|
Role.create(h)
end
The roles are created successfully. However, when I add a role to a user, I do:
foobar = User.first
foobar.roles.create(:name => 'User')
I notice 2 things:
1) It adds the role as a reference in the User collection.
2) It creates a 3rd role in the Role collection.
This is kind of strange because now I have 3 roles: User, Artist and User. The 2nd User collection has a user_ids reference which contains foobar's id.
Is this normal?

I think you rather want to do:
foobar = User.first
foobar.roles << Role.find(:name => 'User')
foobar.save
This way a role object is not created, but a reference is added to an already existing record.

Related

Rails Nested Resources Creation

In RoR, whenever you create a nested resource, is there to set attributes during creation of a resource with a parent association, within the model?
I have this Role model that may belong_to and have_many other roles.
employee = Role.find_by_slug :employee
employee.role
=> nil
employee.roles
=> [...more roles...]
waitress = employee.roles.create(slug: :waitress)
=> #<Role id...
waitress.role
=> #<Role slug: 'employee'...
waitress.roles
=> []
The role model has a boolean attribute of subtype. Whenever I create a role from an existing role, I'd like for subtype to be set to true.
employee.subtype
=> false
And waitress would look like this:
waitress.subtype
=> true
Whenever I create a role from an existing role, I'd like for subtype to be set to true.
#app/models/Role.rb
class Role < ActiveRecord::Base
belongs_to :role
has_many :roles
validate :role_exists, if: "role_id.present?"
before_create :set_subtype, if: "role_id.present?"
private
def set_subtype
self.subtype = true
end
def role_exists
errors.add(:role_id, "Invalid") unless Role.exists? role_id
end
end
The above will require another db request; it's only for create & it will happen when the model is invoked (IE you can call it whatever you like when you need it).
--
An alternative to this would be to use acts_as_tree or a similar hierarchy gem.
AAT adds a parent_id column in your db, to which it will then append a series of instance methods you can call (parent, child, etc).
This would permit you to get rid of the has_many :roles, and replace it with a children instance method:
#app/models/role.rb
class Role < ActiveRecord::Base
acts_as_tree order: "slug"
#no need to have "subtype" column or has_many :roles etc
end
root = Role.create slug: "employee"
child1 = root.children.create slug: "waitress"
subchild1 = child1.children.create slug: "VIP_only"
root.parent # => nil
child1.parent # => root
root.children # => [child1]
root.children.first.children.first # => subchild1
According to your description, a given Role is considered a subtype if it has no parent role. In this case, simply add the following method to Role:
def subtype?
!self.role.nil?
end
The following changes did the trick for me:
from:
has_many :roles
to:
has_many :roles do
def create(*args, &block)
args[0][:subtype] = true
super(*args, &block)
end
end

Belongs_to different field name

I've inherited quite a weird table layout:
callbacks
id, note, user
admin
id, name, password
In callbacks, the user is set to the name of the admin rather than the actual ID. Now I need to be able to call callbacks.user and have rails lookup the admin with that name and then bind it to that record.
I have a model for admin that is called users
How would I go about that?
You can override the default methods.
def user
User.find_by_name(user_name)
end
def user=(obj)
self.user_name = obj.name
end
def user_name
self[:user]
end
def user_name=(name)
self[:user] = name
end
Other option , to make it work with belongs_to, there is primary_key option but need to have a different name than the attribute user
# Callback.rb
belongs_to :user_model , :class => "User", :foreign_key => :user, :primary_key => :name
# User.rb
has_one :callback , :foreign_key => :user, :primary_key => :name

Mongoid nested has_one problem

I am trying to update a nested has_one model using mongoid but it will not persist the has_one association
im running Rails 3.07 & Mongoid 2.2
widget model
class Widget
include Mongoid::Document
embeds_many :permissions, :default => []
end
permission model
class Permission
include Mongoid::Document
field :admin, :type => Boolean, :default => false
has_one :user
embedded_in :widget
end
user model
class User
include Mongoid::Document
belongs_to :permission
end
Heres the results im getting from rails console;
#widget.permissions << Permission.new(:user => current_user)
=> [#<Permission _id: 4e5aced1c155df4b33000001, _type: nil, admin: false>]
#widget.save
=> true
#widget.permissions.first.user
=> #<User _id: 4e5ac71ec155df470f000001, _type: nil, email: "ada ..... >
Appears as if the user is saved, however it is not persisted to mongo.
The permission is being saved but has no user.
Any ideas?
Should you be using "embedded_in" rather than "belongs_to" in the User model?

mongoid references_and_referenced_in_many

i have a strange problem. i am new to mongoid, so i am having trouble determining if it is me or mongoid at fault... presenting my code is perhaps the best explanation (minus the fields/validations/etc.)
class User
include Mongoid::Document
embeds_one :profile, :class_name => "UserProfile"
references_and_referenced_in_many :roles
end
class UserProfile
include Mongoid::Document
embedded_in :user
end
class Role
include Mongoid::Document
references_and_referenced_in_many :users
end
with the following associations, when i create instances of these objects like so...
user = User.new :username => 'username',
:email => 'user#domain.com',
:password => 'password'
user.build_profile :first_name => 'John',
:last_name => 'Doe',
:birthday => Date.new(1980, 1, 1)
user.roles << Role.new(:name => 'Administrator')
user.save
...i can view this user with User.first or user
...i can view the profile with User.first.profile and user.profile
...i can view roles with user.roles but i CANNOT view them with User.first.roles.
another strange thing is user.roles.count AND User.first.roles.count both return 0, even though when i view user.roles, it returns [#<Role _id: 4d8c0173e1607cdeae00002c, name: "Administrator", user_ids: [BSON::ObjectId('4d8c0173e1607cdeae00002a')]>]. (User.first.roles returns an empty array)
this seems like a bug.
use :autosave => true for the relational association
references_and_referenced_in_many :roles, :autosave => true
or you can explicitly save the role as
role = Role.new(:name => 'Administrator')
user.roles << role
role.save
user.save
This is due to changes in mongoid.2.0.0.rc.1 + listed here.
Relational associations no longer
autosave when the parent relation is
created. Previously a save on a new
document which had a references_many
or references_one association loaded
would save the relations on it's first
save. In order to get this
functionality back, an :autosave =>
true option must be provided to the
macro (This only applies to
references_many and references_one)

Help me find out why new and build method doesnt work

I have problem with my associations. I have n:n relation and everything going good but if i want initialize new object and then save it, it will by save with out associations. For example.
Models:
class User
has_many :users_in_organizations, :class_name => 'UserInOrganization'
has_many :organizations,:through => :users_in_organizations
end
#Attributes [:user_id, :organization_id, :user_role]
class UserInOrganization
set_table_name 'users_in_organizations'
belongs_to :user
belongs_to :organization
end
class Organization
has_many :users_in_organizations, :class_name => 'UserInOrganization'
has_many :users, :through => :users_in_organizations
end
this work fine but the problem is
org = User.first.organizations.new(:name => 'Test') # new || build is the same
org.save # => true
User.first.organizations # => []
Organization.all # => ['Test']
but if I use create then it works
org = User.first.organizations.create(:name => 'Test')
User.first.organizations # => ['Test']
Organization.all # => ['Test']
Can anybody tell me what i am doing wrong?
Thank You :)
If you want it working for new method, try this:
u = User.first
u.organizations.new :name => "new organozation"
u.save
u.organizations.size
=> 1
When you do org = User.first.organizations.new :name => "test" then you assign to org only organization and you save only that object. It doesn't save associated objects. That's why it doesn't work that way.
When you call create it saves created objects to db, using new or build doesn't save it to db.

Resources