Mongoid embedded keys - ruby-on-rails

I've got a Tag model:
class Tag
include Mongoid::Document
embedded_in :taggable, :polymorphic => true
key :title
field :title, :type => String
end
Before this model was embedded_in, having key :title forced the id to be based on the title. For some reason now that it's embedded, the ids go back to things like 4fb42e1f5d9a1e68f100000d. Any ideas how to have the key be based on the title?

I can get ids specified by key with what you have specified, exactly.
Maybe you have a problem with your encapsulating model that you didn't share?
The following works for me with Ruby 1.9.3, Rails 3.2.3, Mongoid 2.4.9.
class Item
include Mongoid::Document
embeds_many :tags, as: :taggable
key :name
field :name, :type => String
end
test/unit/tag_test.rb
require 'test_helper'
class TagTest < ActiveSupport::TestCase
def setup
Item.delete_all
#Tag.delete_all
end
test "key title" do
item = Item.create(name: 'book')
assert_equal(1, Item.count)
assert_equal('book', Item.where(name: 'book').first[:_id])
tag = Tag.new(title: 'scifi')
item.tags << tag
assert_equal('scifi', Item.where(name: 'book').first.tags.first[:_id])
puts Item.all.to_a.first.to_json
end
end
test output
Run options: --name=test_key_title
# Running tests:
{"_id":"book","name":"book","tags":[{"_id":"scifi","title":"scifi"}]}
.
Finished tests in 0.010775s, 92.8074 tests/s, 278.4223 assertions/s.
1 tests, 3 assertions, 0 failures, 0 errors, 0 skips

Related

ActiveRecord association not being set in the database?

I’m sure this is a “duh" newbie-type question, but I’ve been at it for days and cannot figure out why my code isn’t setting a relationship in the database correctly. I have a simple belongs_to relationship between two models.
class Pod < ActiveRecord::Base
belongs_to :instigator, :class_name => “User"
attr_accessor :instigator, :instigator_id, :title
validates_presence_of :instigator, :title
validates_associated :instigator
end
and
class User < ActiveRecord::Base
has_many :instigated_pods, inverse_of: :instigator, :class_name => "Pod", as: "instigator"
end
Then I want to test them with rspec and Factory Girl using (what I think) are, again, pretty simple factories
FactoryGirl.define do
factory :pod do
title "Test pod"
instigator
end
factory :user, aliases: [:instigator] do
username
end
end
With this setup, most tests pass, but my PodsController update test kept failing, and I finally found a test that shows why.
require 'rails_helper'
RSpec.describe Pod, type: :model do
it "saves the relationship to the database" do
pod = FactoryGirl.create(:pod)
expect(pod.save).to be_truthy
expect(pod.reload.instigator).to_not be_nil # passes - cached?
pod_from_database = Pod.find(pod.id)
expect(pod_from_database.instigator).to_not be_nil # <- fails
end
end
It seems that something is preventing the pod.instigator_id from being set in the database, so the relationship isn’t persisting. And I have no clue why!!!
I tried setting validates_presence_of :instigator_id, but that makes most of the standard rspec tests fail, and I saw this from the Rails Guides:
If you want to be sure that an association is present, you'll need to test
whether the associated object itself is present, and not the foreign key used
to map the association.
class LineItem < ActiveRecord::Base
belongs_to :order
validates :order, presence: true
end
In order to validate associated records whose presence is required, you must
specify the :inverse_of option for the association:
class Order < ActiveRecord::Base
has_many :line_items, inverse_of: :order
end
Any help straightening this out would be appreciated!
While I’m not altogether clear why, it appears that removing the line
attr_accessor :instigator, :instigator_id, :title
solves the problem. It appears to be blocking writing these attributes. Any indicator why would be appreciated!
attr_accessor is a ruby method that makes a getter and a setter(make them able to be read and to be written), this is not Database fields which called attr_accessible in Rails 3.
what you did is declare an ruby methods this is why it doesn't worked.
read more here:
http://rubyinrails.com/2014/03/17/what-is-attr_accessor-in-rails/

Mongoid has_many association and factorygirl

I am using mongoid for the first time and I am having a problem creating a has_many association in a factory for my specs.
The scenario is this:
I have the a group class:
class Group
include Mongoid::Document
field :name, :type => String
end
And I have an exercise class. An exercise can belong to many groups. The exercise class is currently defined like this:
class Exercise
include Mongoid::Document
field :name, :type => String
field :description, :type => String
has_many :groups
validates_presence_of :name, :description
end
I want to use factorygirl to create instances for specs. I am struggling with how to do this.
Currently my exercise factory looks like this;
FactoryGirl.define do
factory :exercise do
name "Preacher curls"
description "Do something"
after(:build) do |exercise|
exercise.groups << FactoryGirl.build(:group)
end
end
end
This causes the following error:
NoMethodError:
undefined method `=' for #<Group _id: 4fbc6f5a26a3181742000004, _type: nil, name: "Arms">
How can I create the exercise factory correctly to add the group_ids?
Try to add
belongs_to :exercise
into your Group class
It should look like this:
class Group
include Mongoid::Document
field :name, :type => String
belongs_to :exercise
end

How to validate existing invalid model via nested attributes?

It seems Rails will only validate an existing invalid nested model if the nested model's attributes have changed.
With the following models:
class Person < ActiveRecord::Base
has_many :addresses
accepts_nested_attributes_for :addresses
end
class Address < ActiveRecord::Base
belongs_to :person
validates_presence_of :street
end
The following code for example, will save and return true:
p = Person.first
p.update_attributes({:first_name => "Bryan", :addresses_attributes=>{"0"=>{:street=>"", :id => 1}})
Is there a way to validate the nested model as if it's attributes have changed? ( while retaining errors )
It works for me -- here's a test-case I created to prove it
require 'test_helper'
class PersonTest < ActiveSupport::TestCase
test "update address" do
expected_new_address="pandascout"
person = Person.create(name: "jwo")
address = person.addresses.create(street: "123 Elm")
person.update_attributes({:addresses_attributes=>{"0"=>{:street=>expected_new_address, :id=>address.id}}})
assert_equal expected_new_address, person.addresses.first.street
end
end
The only thing I can think of is you have a reference to "address" and you need to reload it.

Single Table Inheritance with Sunspot/Solr and Rails

I have following classes in a Rails app:
class Post
include Mongoid::Document
include Sunspot::Mongoid
belongs_to :user
searchable do
text :name
string :user_id
end
end
class Post::Image < Post
searchable do
text :location
time :taken_at
end
end
class Post::Link < Post
searchable do
text :href
end
end
As far as I know, calling following should just work.
Post.search do
fulltext('something')
with(:user_id, user.id)
end
But it does not. It returns an empty result. Calling Post::Link.search does also not work:
# => Sunspot::UnrecognizedFieldError: No field configured for Post::Link with name 'user_id'
Any suggestions?

nested mass assignment with mongoid

with a has_one/belongs_to relationship, i cannot seem to update nested records via mass assignment.
models:
class ProductVariation
include Mongoid::Document
has_one :shipping_profile, :inverse_of => :variation
field :quantity
attr_accessible :shipping_profile_attributes
accepts_nested_attributes_for :shipping_profile
end
class ShippingProfile
include Mongoid::Document
belongs_to :variation, :class_name => "ProductVariation"
field :weight, :type => Float
attr_accessible :weight
end
controller:
#variation = ProductVariation.find(params[:id])
#variation.update_attributes(params[:product_variation])
post request:
Parameters:{
"product_variation"=>{
"quantity"=>"13",
"shipping_profile_attributes"=>{
"weight"=>"66",
"id"=>"4dae758ce1607c1d18000074"
}
},
"id"=>"4dae758ce1607c1d18000073"
}
mongo query:
MONGODB app_development['product_variations'].update({"_id"=>BSON::ObjectId('4dae758ce1607c1d18000073')}, {"$set"=>{"quantity"=>13, "updated_at"=>2011-04-28 06:59:17 UTC}})
and i dont even get a mongo update query if the product_variation doesnt have any changed attributes... what am i missing here?
Working models and a unit test are below to demonstrate that you can update child parameters and save to the database
through the parent as intended via accepts_nested_attributes_for and the autosave: true option for relations.
The Mongoid documentation says that an error will be raised for an attempt to set a protected field via mass assignment,
but this is out of date.
Instead, messages like the following are printed to the log file.
WARNING: Can't mass-assign protected attributes: id
You should carefully look for for these messages in the appropriate log file to diagnose your problem.
This will help you notice that you have a nested id field for the shipping profile in your parameters,
and this seems to cause the weight to be rejected as well, probably along with all child parameters.
After adding "attr_accessible :id" to the ShippingProfile model, the weight now gets assigned.
You also need to add "attr_accessible :quantity" (and I've added :id for the unit test) to the ProductVariation model
The next issue is that you need "autosave: true" appended to the has_one relation
in order to have the child updated through the parent,
otherwise you will have to save the child manually.
You might also be interested in sanitize_for_mass_assignment, which can be used to launder out ids.
include ActiveModel::MassAssignmentSecurity
p sanitize_for_mass_assignment(params['product_variation'], :default)
The unit test should make the whole subject clear, I'll leave the controller work to you.
Hope that this is clear and that it helps.
class ProductVariation
include Mongoid::Document
has_one :shipping_profile, :inverse_of => :variation, autosave: true
field :quantity
accepts_nested_attributes_for :shipping_profile
attr_accessible :id
attr_accessible :quantity
attr_accessible :shipping_profile_attributes
end
class ShippingProfile
include Mongoid::Document
belongs_to :variation, :class_name => "ProductVariation"
field :weight, :type => Float
attr_accessible :id
attr_accessible :weight
end
test/unit/product_varitation_test.rb
require 'test_helper'
class ProductVariationTest < ActiveSupport::TestCase
def setup
ProductVariation.delete_all
ShippingProfile.delete_all
end
test "mass assignment" do
params = {
"product_variation"=>{
"quantity"=>"13",
"shipping_profile_attributes"=>{
"weight"=>"66",
"id"=>"4dae758ce1607c1d18000074"
}
},
"id"=>"4dae758ce1607c1d18000073"
}
product_variation_id = params['id']
shipping_profile_id = params['product_variation']['shipping_profile_attributes']['id']
product_variation = ProductVariation.create("id" => product_variation_id)
shipping_profile = ShippingProfile.create("id" => shipping_profile_id)
product_variation.shipping_profile = shipping_profile
assert_equal(1, ProductVariation.count)
assert_equal(1, ShippingProfile.count)
product_variation.update_attributes(params['product_variation'])
assert_equal('13', ProductVariation.find(product_variation_id)['quantity'])
assert_equal(66.0, ShippingProfile.find(shipping_profile_id)['weight'])
p ProductVariation.find(product_variation_id)
p ShippingProfile.find(shipping_profile_id)
end
end
test output
Run options: --name=test_mass_assignment
# Running tests:
#<ProductVariation _id: 4dae758ce1607c1d18000073, _type: nil, quantity: "13">
#<ShippingProfile _id: 4dae758ce1607c1d18000074, _type: nil, variation_id: BSON::ObjectId('4dae758ce1607c1d18000073'), weight: 66.0>
.
Finished tests in 0.014682s, 68.1106 tests/s, 272.4424 assertions/s.
1 tests, 4 assertions, 0 failures, 0 errors, 0 skips
Process finished with exit code 0

Resources