get mongoid <-> fabrication not working habtm - ruby-on-rails-3

I have a question to the
"has_and_belongs_to_many" and "accept_nested_attributes_for"
in context to fabrication and mongoid
I have a Location which can have many services
class Location
include Mongoid::Document
field :name
field :service
has_and_belongs_to_many :services, inverse_of: :locations, autosave: true, dependent: :delete
accepts_nested_attributes_for :services
attr_accessible :services, :name
class Service
include Mongoid::Document
field :name, type: String
has_and_belongs_to_many :locations, inverse_of: :services, autosave: true
accepts_nested_attributes_for :locations
attr_accessible :name, :icon, :description
on my Fabrication Files I have this
Fabricator(:service) do
initialize_with { Location.produce(:location) }
name "Service Name"
description "Lorem ipsum est lauda en radios"
location
end
Fabricator(:location) do
name "Special Club"
service
end
In this case my rspec hang up.
Can someone provide a working example with *accept_nested_attributes* and / or *has_and_belongs_to_many* with mongoid and the fabrication gem (which works "out of the box" with mongoid?
any suggestions?
I am working with mongoid3

This all depends on how you are using Fabrication.
Nesting parameters for testing controllers is a world of hurt and you will need some hand crafting for your params hash.
For models you should try this:
Fabricator(:service) do
name "Service Name"
description "Lorem ipsum est lauda en radios"
locations {[Fabricate.build(:location)]}
end
Fabricator(:location) do
name "Special Club"
service
end
You probably don't need the :inverse_of for your relations.

Related

Aliasing a referenced relationship field in Mongoid

In the Mongoid model below, how do I alias the belongs_to relationship field?
class Contact
field :nm, :as => :name, :type => String # field aliasing
embeds_one :address, :store_as => :ad # embedded document aliasing
belongs_to :account # referenced relation doesn't support store_as
end
I want to store the account id in a field called ac instead of account_id.
You can use :foreign_key to specify the mongodb field name.
belongs_to :account, foreign_key: :ac
However, if you want to use account_id, you need to declare its alias:
alias :account_id :ac
or defining account_id before belongs_to:
field :account_id, as: :ac
Mongoid allows to use arbitrary name for a relationship by using of 'inverse_of'
If an inverse is not required, like a belongs_to or has_and_belongs_to_many, ensure that :inverse_of => nil is set on the
relation. If the inverse is needed, most likely the inverse cannot be
figured out from the names of the relations and you will need to
explicitly tell Mongoid on the relation what the inverse is.
So, for use 'ac' as an alias it's necessary to add inverse_of:
class Contact
field :nm, :as => :name, :type => String # field aliasing
embeds_one :address, :store_as => :ad # embedded document aliasing
belongs_to :ac, class_name: 'Account', inverse_of: :contact
end
class Account
has_one :contact, class_name: 'Contact', inverse_of: :ac
end

saving embeds_many in mongoid

I have a embeds_many association and when I save the parent document the children are not being saved.
class User
include Mongoid::Document
embeds_many :user_missions
attr_accessible :user_missions_attributes
accepts_nested_attributes_for :user_missions, allow_destroy: true
end
class UserMission
include Mongoid::Document
embedded_in :user, :inverse_of => :user_missions
has_one :mission, autosave: true
validates_presence_of :mission
attr_accessible :mission_title
def mission_title
mission.try(:title)
end
def mission_title=(title)
self.mission = Mission.find_or_create_by(:title => title) if title.present?
end
end
Here is the spec I'm failing:
it "should save mission to user_mission when created" do
user_mission = UserMission.new
user = create(:user)
user.user_missions << user_mission
user_mission.mission_title = "Created Title"
user.save!
#user_mission.save!
User.first.user_missions[0].mission.title.should == "Created Title"
end
I get:
undefined method `title' for nil:NilClass
When I comment in the line #user_mission.save! it works. The problem is I need this to work in a form and I thought Mongoid automatically saved embedded document's fields.
How do I get the parent User document to save the embedded UserMission's data?
Thanks
UPDATE
Here is the Mission model I forgot to add (wasn't sure if it was important):
class Mission
include Mongoid::Document
belongs_to :user_mission, :inverse_of => :mission
attr_accessible :title
field :title, type: String
validates_presence_of :title
field :lowercase_title
before_create :lower_title_case
field :description, type: String
private
def lower_title_case
self.lowercase_title = self.title.downcase
end
end
AFAIK, embedded documents can't have referenced relations. So the calls to embedded_in and has_one are contradictory.
Other than that, have you tried reordering the lines in your test so that the user gets created first, and then you create user_misison through the user?
user = create(:user)
user_mission = user.user_missions.new
user_mission.mission_title = "Created Title"
user.save!
It looks like what you're trying to do is similar to an SQL JOIN table. If this is what you're trying to achieve, it would be better to take advantage of Mongoid's awesome N-N referenced mapping (unless you need to store extra data in the 'join' collection). I'd do something like:
class User
include Mongoid::Document
has_and_belongs_to_many :missions
end
class Mission
include Mongoid::Document
has_and_belongs_to_many :users
end
If you want to create missions through users, then turn on autosave on the Mission side of the relation.
EDIT: After seeing your Mission model, I realize you're trying to directly reference UserMission (belongs_to :user_mission), an embedded document, directly from your Mission model. Like I said earlier, not doable. You need some relation between the top level docs, User and Mission. You could probably get things to work by changing that line to:
has_many :users
and then changing has_one :mission from the User model to:
belongs_to :mission
You won't be able to use autosave from the User side though.
EDIT: Corrected for proper way to show right way to do 1-n relation from users to missions.

Does accepts_nested_attributes_for work with belongs_to?

I have been getting all kinds of conflicting information regarding this basic question, and the answer is pretty crucial to my current problems. So, very simply, in Rails 3, is it allowed or not allowed to use accepts_nested_attributes_for with a belongs_to relationship?
class User < ActiveRecord::Base
belongs_to :organization
accepts_nested_attributes_for :organization
end
class Organization < ActiveRecord::Base
has_many :users
end
In a view:
= form_for #user do |f|
f.label :name, "Name"
f.input :name
= f.fields_for :organization do |o|
o.label :city, "City"
o.input :city
f.submit "Submit"
Nested attributes appear to work fine for a belongs_to association as of Rails 4. It might have been changed in an earlier version of Rails, but I tested in 4.0.4 and it definitely works as expected.
The doc epochwolf cited states in the first line "Nested attributes allow you to save attributes on associated records through the parent." (my emphasis).
You might be interested in this other SO question which is along the same lines as this one. It describes two possible solutions: 1) moving the accepts_nested_attributes to the other side of the relationship (in this case, Organization), or 2) using the build method to build the Organization in the User before rendering the form.
I also found a gist that describes a potential solution for using accepts_nested_attributes with a belongs_to relationship if you're willing to deal with a little extra code. This uses the build method as well.
For belongs_to association in Rails 3.2, nested model needs the following two steps:
(1) Add new attr_accessible to your child-model (User model).
accepts_nested_attributes_for :organization
attr_accessible :organization_attributes
(2) Add #user.build_organization to your child-controller (User controller) in order to create column organization.
def new
#user = User.new
#user.build_organization
end
For Ruby on Rails 5.2.1
class User < ActiveRecord::Base
belongs_to :organization
accepts_nested_attributes_for :organization
end
class Organization < ActiveRecord::Base
has_many :users
end
Just got to your controller, suppose to be "users_controller.rb":
Class UsersController < ApplicationController
def new
#user = User.new
#user.build_organization
end
end
And the view just as Nick did:
= form_for #user do |f|
f.label :name, "Name"
f.input :name
= f.fields_for :organization do |o|
o.label :city, "City"
o.input :city
f.submit "Submit"
At end we see that #user3551164 have already solved, but now (Ruby on Rails 5.2.1) we don't need the attr_accessible :organization_attributes

MongoID, embedding a document in multiple documents

I have a model Address like following
class Address
include Mongoid::Document
field :line1
field :city
# more fields like this
embedded_in :user, :inverse_of => :permanent_address
embedded_in :user, :inverse_of => :current_address
embedded_in :college, :inverse_of => :address
end
There are models College and User which embed address
class College
include Mongoid::Document
references_many :users
embeds_one :address
# some fields and more code
end
class User
include Mongoid::Document
referenced_in :college, :inverse_of => :users
embeds_one :permanent_address, :class_name => "Address"
embeds_one :current_address, :class_name => "Address"
# fields and more code
end
I am getting some problems with the above setup. I am using single form to ask for current and permanent address along with some more information, but only current_address is getting saved and that too with the data I populate in permanent_address.
Parameters:
{"utf8"=>"✓",
"authenticity_token"=>"KdOLvzmKyX341SSTc1SoUG6QIP9NplbAwkQkcx8cgdk=",
"user"=> {
"personal_info_attributes"=>{...},
"nick_names_attributes"=>{...},
"current_address_attributes"=>{
"line1"=>"",
"area"=>"",
"country"=>"USA",
"postal_code"=>"sd",
"city"=>"",
"state"=>"",
"landmark"=>"",
"id"=>"4d891397932ecf36a4000064"
},
"permanent_address_attributes"=>{
"line1"=>"",
"area"=>"asd",
"country"=>"india",
"postal_code"=>"",
"city"=>"",
"state"=>"",
"landmark"=>""
},
"commit"=>"Submit", "id"=>"4d8903d6932ecf32cf000001"}
MONGODB alma_connect['users'].find({:_id=>BSON::ObjectId('4d8903d6932ecf32cf000001')})
MONGODB alma_connect['users'].update({"_id"=>BSON::ObjectId('4d8903d6932ecf32cf000001')},
{"$set"=>{
"current_address"=>{
"line1"=>"",
"area"=>"asd",
"country"=>"india",
"postal_code"=>"",
"city"=>"",
"state"=>"",
"landmark"=>"",
"_id"=>BSON::ObjectId('4d8916e9932ecf381f000005')}}})
I am not sure if this is something I am doing wrong here or there is some other problem. I am using Rails 3.0.4 and MongoID 2.0.0.rc.7
Update:
I upgraded to mongoid 2.0.1 and changed my user to include inverse of options in address.
class User
include Mongoid::Document
referenced_in :college, :inverse_of => :users
embeds_one :permanent_address, :class_name => "Address", :inverse_of => :permanent_address
embeds_one :current_address, :class_name => "Address", :inverse_of => :current_address
# fields and more code
end
I know the inverse of names doesn't make sense, but the main point here is just to make them different or if you have good names for relations in your embedded class(like :current_user, :permanent_user), you should use that for inverse of.
Looks good to me. I've a similar setup and it works as expected.

Embedded Document not being added

Having trouble adding an embedded document. I am trying to add a tag which is embedded in the user.
user.rb
class User
include Mongoid::Document
field :name
validates_presence_of :name
validates_uniqueness_of :name, :email, :case_sensitive => false
attr_accessible :name, :email, :password, :password_confirmation
embeds_many :tags
embeds_many :tasks
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable, :validatable
end
tag.rb
class Tag
include Mongoid::Document
field :name
embedded_in :user, :inverse_of => :tags
references_many :tasks
end
tags_controller.rb
def create
##user = User.find(:first, :conditions => {:_id => "4d3ae09bf5c4930b2b000004"} )
#user = current_user
#tag = Tag.new(params[:tag])
#user.tags << #tag
#tag.save
redirect_to #tag, :notice => "Tag created!"
end
This is the output to the server when I try to create a new tag.
Started POST "/tags" for 127.0.0.1 at 2011-02-18 13:46:03 -0500
Processing by TagsController#create as HTML Parameters: {"utf8"=>"✓", "authenticity_token"=>"6p+Jova7Hol2v5LRReSp2fhNJ967EwkeIzAWyrChQRE=", "tag"=>{"name"=>"general"}, "commit"=>"Create Tag"}
db['users'].find({:_id=>BSON::ObjectId('4d39cd63f5c4930708000001')}, {}).limit(-1) MONGODB
db['users'].update({"_id"=>BSON::ObjectId('4d39cd63f5c4930708000001')}, {"$push"=>{"tags"=>{"name"=>"general", "_id"=>BSON::ObjectId('4d5ebe6bf5c493554d000002')}}}) Redirected to
http://localhost:3000/tags/4d5ebe6bf5c493554d000002 Completed 302 Found in 5ms
Not really sure what the issue is or where to start. It actually looks like the user is found then an update is being made to tags but it is not successful.
Thanks
The Tags class in your model is embedded inside of user (via the embeds_many association), rather than a table on its own. So following the updates in your controller, you should have something like this:
> db.users.find()
{
_id: ObjectId('4d39cd63f5c4930708000001'),
tags: [
{
_id: ObjectId('4d5ebe6bf5c493554d000002'),
name: "General"
}
]
}
Using MongoID, you can also have Tags appear in their own collection by replacing "embeds_many" with "references_many".
In the comments above, you'll see that the issue berek-bryan was having had to do with where the tag was being added. He expected the tag to be added in its own collection, hence the question. Actually, the tags were being added right into his users collection.