update embedded documents mongodb with mongoid - ruby-on-rails

I am having problem updating the embedded documents in mongodb.
I have a following scenario.
A User model has address as the embedded docs.
I am able to embed the address to the parent model ie; User model but i still cant figure out how to update the address embedded even though i have the _id of the address embedded
Please help
Thanks

You have to retrieve the embedded document from the parent and then make the update operation, e.g:
address = user.address
address.update_attributes(:street => "foo")

There's another solution. If there's a many-to-many relationship between the Person and Preference classes, then:ruby-1.9.2-p0 > Person.count
=> 0
ruby-1.9.2-p0 > Preference.count
=> 0
ruby-1.9.2-p0 > person = Person.create
=> #< Person _id: 4cd353e92b58af214b000006, preference_ids: []>
ruby-1.9.2-p0 > pref = Preference.create
=> #< Preference _id: 4cd353ee2b58af214b000007, person_ids: [], name: nil>
ruby-1.9.2-p0 >
ruby-1.9.2-p0 > person.preferences << pref
=> true
ruby-1.9.2-p0 > Preference.first.people.count
=> 1
ruby-1.9.2-p0 > Person.first.preferences.count
=> 1
ruby-1.9.2-p0 >
ruby-1.9.2-p0 > person.preferences.first.name = 'foobar'
=> "foobar"
ruby-1.9.2-p0 > person.preferences.first.save
=> true
ruby-1.9.2-p0 > pref.reload
=> #< Preference _id: 4cd353ee2b58af214b000007, person_ids: [BSON::ObjectId('4cd353e92b58af214b000006')], name: "foobar">
ruby-1.9.2-p0 > pref.name
=> "foobar"

Related

Mongoid not detecting change to localized field when setting field with xxxx_translations

Gem Versions
I am using Rails 3.2.18 and Mongoid 3.1.6.
The problem
I need to set a value for any locale in a localized field, not just the one that is currently in I18n.locale. I am setting it using title_translations['en'] = 'some title', but when doing this, mongoid does not detect the change (not dirty) and so nothing is saved.
The Model
I have a mongoid model with a localized field like the following:
class ApiMethod
include Mongoid::Document
field :title, type: String, localize: true
attr_accessible :title, :title_translations
end
Console Example
Below is an example of setting the title two ways, one as title = 'xxx' and one as title_translations['en'] = 'xxx' and the result of testing if the object has changed.
2.1.2 :083 > a = ApiMethod.first
=> #<ApiMethod _id: 5541cb17fb6fc67028000006, title: {"en"=>"Dataset Catalog"}>
2.1.2 :084 > a.title
=> "Dataset Catalog"
2.1.2 :085 > a.title_translations
=> {"en"=>"Dataset Catalog"}
2.1.2 :086 > a.title = 'new title'
=> "new title"
2.1.2 :087 > a.changed?
=> true ## YEA! - THIS IS CORRECT
2.1.2 :088 > a.changed
=> ["title"]
2.1.2 :089 > a.reload
=> #<ApiMethod _id: 5541cb17fb6fc67028000006, title: {"en"=>"Dataset Catalog"}>
2.1.2 :090 > a.title_translations['en'] = 'this is a new title'
=> "this is a new title"
2.1.2 :091 > a.title
=> "this is a new title"
2.1.2 :092 > a.title_translations
=> {"en"=>"this is a new title"}
2.1.2 :093 > a.changed?
=> false ## BOO! - THIS IS NOT CORRECT
Please Help!
Am I doing something wrong or missing something? Is it not possible to set a localized field value for a locale that is not the current I18n.locale value?
Thank you for your help!
Update
I just tried something else and this works. Instead of setting the title_translations for a specific locale, I tried setting the entire title_translations object:
2.1.2 :114 > a = ApiMethod.only(:title).first
=> #<ApiMethod _id: 5541cb17fb6fc67028000006, title: {"en"=>"Dataset Catalog"}>
2.1.2 :115 > x = a.title_translations.dup
=> {"en"=>"Dataset Catalog"}
2.1.2 :116 > x['en'] = 'this is a new title'
=> "this is a new title"
2.1.2 :117 > x
=> {"en"=>"this is a new title"}
2.1.2 :118 > a.title_translations = x
=> {"en"=>"this is a new title"}
2.1.2 :119 > a.title_translations
=> {"en"=>"this is a new title"}
2.1.2 :120 > a.changed?
=> true
2.1.2 :121 > a.title_change
=> [{"en"=>"Dataset Catalog"}, {"en"=>"this is a new title"}]
So it seems like if I replace the entire value for title_translations and not just a specific locale, the change is detected. While it is nice that this works, it is not ideal. Thoughts?

Why I18n translations change when local variable with a selected translation key changes?

This is above me today. Hope someone can explain this to me!
Assume you have a rails project with a en.yml file with the following contents:
en:
foo:
foo: foo
bar: bar
Assigning the results of I18n.t(:foo) to a local variable, you get a Hash:
2.0.0-p353 :001 > a = I18n.t(:foo)
=> {:foo=>"foo", :bar=>"bar"}
And now, changing a value for a key in this Hash causes changes in I18n.t('foo.foo'):
2.0.0-p353 :005 > a[:foo] = 'bar'
=> "bar"
2.0.0-p353 :006 > I18n.t(:foo)
=> {:foo=>"bar", :bar=>"bar"}
So, for the question to be clear - why changing a[:foo] from 'foo' to 'bar', causes the changes in I18n.t('foo.foo')?
Thanks in advance!
After
a = I18n.t(:foo)
The reference a does not hold a copy, but reference to the same Hash. Altering the Hash at a modifies the same Hash at I18n.t(:foo).
This is not special behavior of I18n.t, rather this is normal behavior for Ruby.
> a
=> {:foo=>:bar, :baz=>:qux}
> b = a
=> {:foo=>:bar, :baz=>:qux}
> b[:foo] = 1
=> 1
> b
=> {:foo=>1, :baz=>:qux}
> a
=> {:foo=>1, :baz=>:qux}

Ruby on Rails Dirty method _was not working with serialized field

I have a model that looks like this:
class WorkRequest < ActiveRecord::Base
attr_accessible :upload, :assigned_to_staff
serialize :assigned_to_staff, Array
before_save :set_old_staff
def set_old_staff
#old_staff = self.assigned_to_staff_was
end
def staff_changed?
!self.assigned_to_staff.empty? && self.assigned_to_staff != #old_staff
end
end
I'm trying to make use of self.assigned_to_was to track when a staff assignment change takes place. I'm noticing that the serialized field behaves differently than a regular field. Console output below shows differing behavior in :upload (text string field) and the serialized :assigned_to_staff:
1.9.2-p320 :002 > wr.upload
=> nil
1.9.2-p320 :003 > wr.upload_was
=> nil
1.9.2-p320 :004 > wr.upload = "Yes"
=> "Yes"
1.9.2-p320 :005 > wr.upload_was
=> nil
compared to:
1.9.2-p320 :006 > wr.assigned_to_staff
=> []
1.9.2-p320 :007 > wr.assigned_to_staff_was
=> []
1.9.2-p320 :008 > wr.assigned_to_staff << User.last.name
User Load (0.2ms) SELECT `users`.* FROM `users` ORDER BY `users`.`id` DESC LIMIT 1
=> ["last5, first5"]
1.9.2-p320 :009 > wr.assigned_to_staff_was
=> ["last5, first5"]
Can anyone explain this discrepancy and or suggest a workaround?
It appears that serialization doesn't fully implement all methods of the host class. Overrides are provided for getters and setters, but not concatenation.

Mongoid - how to get second record?

I can do
1.9.3p125 :122 > User.first
=> #<StandardUser _id: 4f849..
and
1.9.3p125 :124 > User.last
and
1.9.3p125 :125 > User.count
=> 5
but I can't find any way to get at the other records (2 thru 4).
User.skip(1).first returns you second document.

Ruby on Rails question regarding scope

I have the following named scope in my model
class User
scope :single_action, where("parent_id = ?", nil)
end
Even though there are records with parent_id with nil values, they do not seem to be available.
ruby-1.9.2-p0 > User.select("name")
=> [#<User parent_id: nil, name: "John">, #<Task parent_id: 1, name: "Felix">, #<Task parent_id: nil, name: "John Felix">]
I tried using the active record query to do the same, but it also returns empty resultset
ruby-1.9.2-p0 > User.where("parent_id = ?",nil)
=> []
I have defined few other scopes which seems to be working fine.
My Rails version is 3.0.7 and Ruby version is Ruby 1.9.2
Could you please help me to solve this problem.
Thanks :)
Change that to :
User.where(:parent_id => nil)
You can see the difference (you were actually trying to match the string 'NULL' instead of checking for the value being NULL):
ruby-1.9.2-p180 :031 > User.where("remember_token = ?", nil)
=> []
ruby-1.9.2-p180 :032 > User.where(:remember_token => nil)
=> [#<User id: 232255501, .......
ruby-1.9.2-p180 :029 > User.where(:remember_token => nil).to_sql
=> "SELECT `users`.* FROM `users` WHERE (`users`.`remember_token` IS NULL)"
ruby-1.9.2-p180 :030 > User.where("remember_token = ?", nil).to_sql
=> "SELECT `users`.* FROM `users` WHERE (remember_token = NULL)

Resources