Mongoid persistance problems with a 1 to many association - ruby-on-rails

I have the following model:
class Bill
. . . some fields . . .
belongs_to :sponsor, :class_name => "Legislator"
end
class Legislator
.. .some fields . . .
has_many :bills
end
I get this strange behavior, but I am sure this is something simple:
Loading development environment (Rails 3.0.7)
b = Bill.first
l = Legislator.first
l.bills << b
l.save
=> true
(I can view l.bills, but l.bills.all.to_a.count is 0)
l.govtrack_id
=> 400001
ruby-1.9.2-p180 :007 > Legislator.where(govtrack_id: 400001).first.bills
=> []
So I can create the association and view it. The save is successful, but when I retrieve the object, the association is gone . . . no errors. I'm confused, what am I missing?

You're missing inverse_of on your Legislator model. I ran a quick test (to make sure there wasn't a Mongoid issue). My models were thus:
class Bill
include Mongoid::Document
include Mongoid::Timestamps
field :name
belongs_to :sponsor, :class_name => "Legislator"
end
class Legislator
include Mongoid::Document
include Mongoid::Timestamps
field :govtrack_id
has_many :bills, :inverse_of => :sponsor
end
And console output from the test:
ruby-1.9.2-p180 > Bill.create(:name => "A new bill")
=> #<Bill _id: 4e0822636a4f1d11c1000001, _type: nil, created_at: 2011-06-27 06:25:39 UTC, updated_at: 2011-06-27 06:25:39 UTC, name: "A new bill", sponsor_id: nil>
ruby-1.9.2-p180 > Legislator.create(:govtrack_id => "400123")
=> #<Legislator _id: 4e0822786a4f1d11c1000002, _type: nil, created_at: 2011-06-27 06:26:00 UTC, updated_at: 2011-06-27 06:26:00 UTC, govtrack_id: "400123">
ruby-1.9.2-p180 > l = Legislator.first
ruby-1.9.2-p180 > l.bills << Bill.first
=> [#<Bill _id: 4e0822636a4f1d11c1000001, _type: nil, created_at: 2011-06-27 06:25:39 UTC, updated_at: 2011-06-27 06:26:08 UTC, name: "A new bill", sponsor_id: BSON::ObjectId('4e0822786a4f1d11c1000002')>]
ruby-1.9.2-p180 > l.save!
=> true
ruby-1.9.2-p180 > Bill.first.sponsor.govtrack_id
=> "400123"
ruby-1.9.2-p180 > Legislator.first.bills
=> [#<Bill _id: 4e0822636a4f1d11c1000001, _type: nil, created_at: 2011-06-27 06:25:39 UTC, updated_at: 2011-06-27 06:26:08 UTC, name: "A new bill", sponsor_id: BSON::ObjectId('4e0822786a4f1d11c1000002')>]

Related

Rails 5 field _type not generated in mongoid

I have class & subclass with one document looks like :
class Core::User
include Mongoid::Document
include Mongoid::Timestamps
store_in collection: 'users'
end
class Core::Moderator < Core::User
end
I tried to add a user from console
2.4.2 :002 > user = Core::User.new(email: 'email#domain.com', name: 'new user')
=> #<Core::User _id: BSON::ObjectId('5a015465fe37a86430b1e0ff'), created_at: nil, email: "email#domain.com", name: "new_user", updated_at: nil>
2.4.2 :003 > user.save
=> true
2.4.2 :004 > user._type
NoMethodError: undefined method `_type' for #<Core::User:0x0000000003e77ea0>
from (irb):4
And then add new moderator :
2.4.2 :005 > moderator = Core::Moderator.new(email: 'email2#domail.com', name: 'new moderator')
#<Core::Moderator _id: BSON::ObjectId('5a015600fe37a86430b1e100'), created_at: nil, email: "email2#domain.com", name: "new moderator", updated_at: nil>
2.4.2 :006 > moderator.save
=> true
2.4.2 :007 > moderator._type
=> "Core::Moderator"
Next add new user again :
2.4.2 :008 > user = Core::User.new(email: 'email3#domain.com', name: 'new user 2')
=> #<Core::User _id: BSON::ObjectId('5a015704fe37a86430b1e101'), created_at: nil, email: "email3#domain.com", name: "new user 2", updated_at: nil>
2.4.2 :009 > user.save
=> true
2.4.2 :010 > user._type
=> "Core::User"
Why I should create subclass first to get field _type on parent class? Every I start new console and create new user (Core::User) the field _type not generated.
I user Ruby 2.4.2, Rails 5.1.4, Mongoid 6.2.1
In order for inheritance to work as expected in Mongoid you need to set preload_models: true. Otherwise the model cannot know that it has subclasses.
# config/mongoid.yml
development:
# ...
options:
# ...
# Preload all models in development, needed when models use
# inheritance. (default: false)
preload_models: true

Rails new objects are nil

I'm playing with rails again and found this behavior, when i create a new instance of a Post model with some attributes it tells me that all attributes are nil, why it is happening?
Loading development environment (Rails 4.0.0)
2.0.0-p451 :001 > a = Post.new(title: "Rails", content: "Rails Post")
=> #<Post id: nil, title: nil, content: nil, author: nil, rating: nil, created_at: nil, updated_at: nil>
2.0.0-p451 :002 > a.title
=> "Rails"
2.0.0-p451 :004 > a.content
=> "Rails Post"
2.0.0-p451 :005 > a.inspect
=> "#<Post id: nil, title: nil, content: nil, author: nil, rating: nil, created_at: nil, updated_at: nil>"
2.0.0-p451 :006 > a.errors.messages
=> {}
2.0.0-p451 :007 > a.valid?
=> true
class Post < ActiveRecord::Base
attr_accessor :title, :content, :author, :rating
end
You are defining attr_accessor for all your properties, which is a shortcut for defining getters and setters for an instance variable of the same name like so:
def content
#content
end
def content=(new_content)
#content = new_content
end
Rails will also auto-generate you methods with these names, for every database field that your model has. These methods will conflict with each other.
When you call post.content = 'foo', instead of calling the Rails-generated method that will internally set your model's content attribute to 'foo', you're calling the attr_accessor-defined method which will set the instance variable #content to 'foo'.
The output of inspect is iterating over the Rails-defined model attributes, not the instance variables.
Did you actually mean to declare these attributes as attr_accessible instead of attr_accessor?

Monitoring process interference Factory's instance initialization

In my project, the user will have many items, which have an onshelf_at attribute default to DateTime.now at its creation.
# item.rb
class Item < ActiveRecord::Base
before_create :calculate_onshelf_time
default_scope :order => 'items.onshelf_at DESC'
def calculate_onshelf_time
self.onshelf_at = DateTime.now
end
end
In the user model test, I tried to convince myself the retrieved order is indeed items.onshelf_at DESC. So I made the following snippet, but the result turned out to be reverse. (namely [#item1, #item2])
# spec/models/user_spec.rb
before :each do
#user = User.create(#attr)
#item1 = Factory(:item, :owner=>#user, :onshelf_at => 2.days.ago, :created_at => 2.days.ago)
#item2 = Factory(:item, :owner=>#user, :onshelf_at => 1.day.ago, :created_at => 1.day.ago)
end
it "should have the right items in the right order" do
#user.items.should == [#item2, #item1]
end
I checked the console, and found that onshelf_at wasn't listening to the instance initialization of Factory Girl. In its stead, it followed before_create rule, and valued to the time when test was run!
Failure/Error: #user.items.should == [#item2, #item1]
expected: [#<Item id: 2, description: "this is an item", img_link: "http://www.example.com/photos/some_pic.jpg", category_id: 5, onshelf: true, created_at: "2011-11-20 11:19:15", updated_at: "2011-11-21 11:19:15", onshelf_at: "2011-11-21 11:19:15", owner_id: 1>, #<Item id: 1, description: "this is an item", img_link: "http://www.example.com/photos/some_pic.jpg", category_id: 5, onshelf: true, created_at: "2011-11-19 11:19:15", updated_at: "2011-11-21 11:19:15", onshelf_at: "2011-11-21 11:19:15", owner_id: 1>]
got: [#<Item id: 1, description: "this is an item", img_link: "http://www.example.com/photos/some_pic.jpg", category_id: 5, onshelf: true, created_at: "2011-11-19 11:19:15", updated_at: "2011-11-21 11:19:15", onshelf_at: "2011-11-21 11:19:15", owner_id: 1>, #<Item id: 2, description: "this is an item", img_link: "http://www.example.com/photos/some_pic.jpg", category_id: 5, onshelf: true, created_at: "2011-11-20 11:19:15", updated_at: "2011-11-21 11:19:15", onshelf_at: "2011-11-21 11:19:15", owner_id: 1>] (using ==)
How can I fix this?
A quick fix would be to add a condition to your calculate_onshelf_time method:
self.onshelf_at = DateTime.now if self.onshelf_at.nil?
But - you don't even need all this. If you can (that is, if you have control over the schema), replace the onshelf_at attribute with a created_at column, which will automatically be set by Rails at the time of creation. If you're using migrations:
create_table :foo do |t|
# ...
t.timestamps
end
will add created_at and updated_at timestamps to the model.

Rails ActiveRecord :counter_cache not updated if assocation not set up before create. Intentional?

I've implemented a belongs_to relation with :counter_cache => true and I notice that the counter cache does not get updated if the relation was not set up before the initial save.
For instance, say a company has_many employees. If I do
company.employees << Employee.new(:name => "Joe")
The counter gets updated correctly but if I do
company.employees << Employee.create(:name => "Joe")
The counter remains unchanged.
For more details, here are the models:
class Employee < ActiveRecord::Base
belongs_to :company, :counter_cache => true
end
class Company < ActiveRecord::Base
has_many :employees
end
And here's a Rails Console session that demonstrates this:
Loading development environment (Rails 3.0.5)
ruby-1.9.2-p180 :001 > company_a = Company.create(:name => "ACME")
=> #<Company id: 1, name: "ACME", created_at: "2011-07-22 01:31:39", updated_at: "2011-07-22 01:31:39", employees_count: 0>
ruby-1.9.2-p180 :002 > company_a.employees << Employee.new(:name => "Bob")
=> [#<Employee id: 1, company_id: 1, name: "Bob", created_at: "2011-07-22 01:31:59", updated_at: "2011-07-22 01:31:59">]
ruby-1.9.2-p180 :003 > company_a.reload
=> #<Company id: 1, name: "ACME", created_at: "2011-07-22 01:31:39", updated_at: "2011-07-22 01:31:39", employees_count: 1>
ruby-1.9.2-p180 :004 > company_a.employees << Employee.create(:name => "Joe")
=> [#<Employee id: 1, company_id: 1, name: "Bob", created_at: "2011-07-22 01:31:59", updated_at: "2011-07-22 01:31:59">, #<Employee id: 2, company_id: 1, name: "Joe", created_at: "2011-07-22 01:32:28", updated_at: "2011-07-22 01:32:28">]
ruby-1.9.2-p180 :005 > company_a.reload
=> #<Company id: 1, name: "ACME", created_at: "2011-07-22 01:31:39", updated_at: "2011-07-22 01:31:39", employees_count: 1>
The documentation does say that the counter is incremented/decremented when the object is created/destroyed but I was thinking it should monitor updates as well to be useful. Otherwise, say, moving employees between companies would quickly result in counters that are totally off.
Is this the expected behavior? If so, what's the rationale? And if not, am I doing something wrong? I tried this in Rails 3.0.5 and Ruby 1.9.2
Thanks!

weird behavior with acts_as_taggable_on

Edit
The instructions on Github instruct you to use the gemcutter source for the gem. Currently, this installs version 2.0.5 which includes the bug I've detailed below.
#Vlad Zloteanu demonstrates that 1.0.5 does not include the bug. I have also tried with 1.0.5 and confirm that the bug does not exist in this version. People struggling with acts_as_taggable_on and owned tags on 2.x, rollback and wait for a fix..
For some reason, tags aren't showing up on a taggable object when an tagger is specified.
testing the post
class Post < ActiveRecord::Base
acts_as_taggable_on :tags
belongs_to :user
end
>> p = Post.first
=> #<Post id: 1, ...>
>> p.is_taggable?
=> true
>> p.tag_list = "foo, bar"
=> "foo, bar"
>> p.save
=> true
>> p.tags
=> [#<Tag id: 1, name: "foo">, #<Tag id: 2, name: "bar">]
testing the user
class User < ActiveRecord::Base
acts_as_tagger
has_many :posts
end
>> u = User.first
=> #<User id: 1, ...>
>> u.is_tagger?
=> true
>> u.tag(p, :with => "hello, world", :on => :tags)
=> true
>> u.owned_tags
=> [#<Tag id: 3, name: "hello">, #<Tag id: 4, name: "world">]
refresh the post
>> p = Post.first
=> #<Post id: 1 ...>
>> p.tags
=> [#<Tag id: 2, name: "bar">, #<Tag id: 1, name: "foo">]
Where's the hello and world tags? Miraculously, if I modify the database directly to set tagger_id and tagger_type to NULL, the two missing tags will show up. I suspect there's something wrong with my User model? What gives?
EDIT
Even stranger:
Post.tagged_with("hello")
#=> #<Post id: 1, ...>
It finds the post! So it can read the tag from the database! How come it's not showing up with Post#tags or Post#tag_list?
I recreated your project, using exactly the same classes.
This is my result:
>> Post.create
=> #<Post id: 1, created_at: "2010-05-18 09:16:36", updated_at: "2010-05-18 09:16:36">
>> p = Post.first
=> #<Post id: 1, created_at: "2010-05-18 09:16:36", updated_at: "2010-05-18 09:16:36">
>> p.is_taggable?
=> true
>> p.tag_list = "foo, bar"
=> "foo, bar"
>> p.save
=> true
>> p.tags
=> [#<Tag id: 1, name: "foo">, #<Tag id: 2, name: "bar">]
>> User.create
=> #<User id: 1, created_at: "2010-05-18 09:17:02", updated_at: "2010-05-18 09:17:02">
>> u = User.first
=> #<User id: 1, created_at: "2010-05-18 09:17:02", updated_at: "2010-05-18 09:17:02">
>> u.is_tagger?
=> true
>> u.tag(p, :with => "hello, world", :on => :tags)
=> true
>> u.owned_tags
=> [#<Tag id: 3, name: "hello">, #<Tag id: 4, name: "world">]
>> p = Post.first
=> #<Post id: 1, created_at: "2010-05-18 09:16:36", updated_at: "2010-05-18 09:16:36">
>> p.tags
=> [#<Tag id: 1, name: "foo">, #<Tag id: 2, name: "bar">, #<Tag id: 3, name: "hello">, #<Tag id: 4, name: "world">]
Therefore, I can not replicate your bug. I've tried it with both mysql and sqlite.
This is from my env file:
config.gem "mbleigh-acts-as-taggable-on", :source => "http://gems.github.com", :lib => "acts-as-taggable-on"
This is my gem version:
gem list | grep taggable
mbleigh-acts-as-taggable-on (1.0.5)
Can you post your gem version? Can you try to upgrade your gem? What DB are you using?
If it doesn't work, can you also post the output from tail -f log/development.log ?
EDIT: I'm using Rails 2.3.5
mbleigh released a commit that resolve this problem.
To obtain ALL the tags you can use owner_tags_on :
p = Post.first
p.owner_tags_on(nil, :tags )
This is the commit: http://github.com/mbleigh/acts-as-taggable-on/commit/3d707c25d45b5cc680cf3623d15ff59856457ea9
bye,
Alessandro DS

Resources