jwood / tenacity

A database client independent way of managing relationships between models backed by different databases.
MIT License
118 stars 17 forks source link

Problems with t_has_many #33

Closed mattiassvedhem closed 12 years ago

mattiassvedhem commented 12 years ago

When I follow this pattern:

wheel_1 = Wheel.create
wheel_2 = Wheel.create
wheel_3 = Wheel.create
wheels = [wheel_1, wheel_2, wheel_3]
car.wheels = wheels
car.save

Tenacity doesn't update the car_id in wheels. And if I try to access car.wheels I just get an empty array.

Seems to work flawless with t_has_one.

I tried to use the auto_save option, but i reckon it's not for this purpose? I also tried to manually specify the foreign_key, but that didn't help either.

This was tested with a single model and these gems:

mongo (1.4.0) 
mongoid (2.2.4)

bson (1.4.0) 
bson_ext (1.4.0) 

mysql2 (0.3.10)
activerecord (3.1.1)
mattiassvedhem commented 12 years ago

If I create car_id and add the association manually it does work. However it's not persisted. I use devise for authentication and if I log out the car_id will be wiped from the wheel object. This might be a problem with devise or mongoid though?

mattiassvedhem commented 12 years ago

It's strange though cause it's only the car_id that's wiped. Indicating that it is indeed a problem with Tenacity.

jwood commented 12 years ago

Hi. I'm sorry, but I'm unable to reproduce this error.

My Gemfile

source 'http://rubygems.org'

gem 'rails', '3.1.1'

gem 'tenacity', '0.5.3'

gem 'mysql2', '0.3.10'

gem 'couchrest'
gem 'couchrest_extended_document'

gem 'mongo', '1.4.0'
gem 'mongoid', '2.2.4'
gem 'bson', '1.4.0'
gem 'bson_ext', '1.4.0'

With those dependencies, I am able to do the following in rails console:

[~/dev/personal/tenacity_rails3] $ rails console
Loading development environment (Rails 3.1.1)
ruby-1.8.7-p352 :001 > car = Car.create
  Wheel Load (0.3ms)  SELECT `wheels`.* FROM `wheels` WHERE (car_id = '4ecb9ffc904f562ced000001')
 => #<Car _id: 4ecb9ffc904f562ced000001, _type: nil> 
ruby-1.8.7-p352 :002 > wheel_1 = Wheel.create
   (0.1ms)  BEGIN
  SQL (0.2ms)  INSERT INTO `wheels` (`car_id`, `created_at`, `updated_at`) VALUES (NULL, '2011-11-22 13:13:33', '2011-11-22 13:13:33')
   (0.5ms)  COMMIT
 => #<Wheel id: 4, car_id: nil, created_at: "2011-11-22 13:13:33", updated_at: "2011-11-22 13:13:33"> 
ruby-1.8.7-p352 :003 > wheel_2 = Wheel.create
   (0.1ms)  BEGIN
  SQL (0.1ms)  INSERT INTO `wheels` (`car_id`, `created_at`, `updated_at`) VALUES (NULL, '2011-11-22 13:13:33', '2011-11-22 13:13:33')
   (0.1ms)  COMMIT
 => #<Wheel id: 5, car_id: nil, created_at: "2011-11-22 13:13:33", updated_at: "2011-11-22 13:13:33"> 
ruby-1.8.7-p352 :004 > wheel_3 = Wheel.create
   (0.1ms)  BEGIN
  SQL (0.1ms)  INSERT INTO `wheels` (`car_id`, `created_at`, `updated_at`) VALUES (NULL, '2011-11-22 13:13:33', '2011-11-22 13:13:33')
   (0.2ms)  COMMIT
 => #<Wheel id: 6, car_id: nil, created_at: "2011-11-22 13:13:33", updated_at: "2011-11-22 13:13:33"> 
ruby-1.8.7-p352 :005 > wheels = [wheel_1, wheel_2, wheel_3]
 => [#<Wheel id: 4, car_id: nil, created_at: "2011-11-22 13:13:33", updated_at: "2011-11-22 13:13:33">, #<Wheel id: 5, car_id: nil, created_at: "2011-11-22 13:13:33", updated_at: "2011-11-22 13:13:33">, #<Wheel id: 6, car_id: nil, created_at: "2011-11-22 13:13:33", updated_at: "2011-11-22 13:13:33">] 
ruby-1.8.7-p352 :006 > car.wheels = wheels
 => [#<Wheel id: 4, car_id: nil, created_at: "2011-11-22 13:13:33", updated_at: "2011-11-22 13:13:33">, #<Wheel id: 5, car_id: nil, created_at: "2011-11-22 13:13:33", updated_at: "2011-11-22 13:13:33">, #<Wheel id: 6, car_id: nil, created_at: "2011-11-22 13:13:33", updated_at: "2011-11-22 13:13:33">] 
ruby-1.8.7-p352 :007 > car.save
  Wheel Load (0.2ms)  SELECT `wheels`.* FROM `wheels` WHERE (car_id = '4ecb9ffc904f562ced000001')
  Wheel Load (0.2ms)  SELECT `wheels`.* FROM `wheels` WHERE `wheels`.`id` = 4 LIMIT 1
   (0.1ms)  BEGIN
   (0.2ms)  UPDATE `wheels` SET `updated_at` = '2011-11-22 13:13:33', `car_id` = '4ecb9ffc904f562ced000001' WHERE `wheels`.`id` = 4
   (0.2ms)  COMMIT
  Wheel Load (0.2ms)  SELECT `wheels`.* FROM `wheels` WHERE `wheels`.`id` = 5 LIMIT 1
   (0.0ms)  BEGIN
   (0.1ms)  UPDATE `wheels` SET `updated_at` = '2011-11-22 13:13:33', `car_id` = '4ecb9ffc904f562ced000001' WHERE `wheels`.`id` = 5
   (0.2ms)  COMMIT
  Wheel Load (0.2ms)  SELECT `wheels`.* FROM `wheels` WHERE `wheels`.`id` = 6 LIMIT 1
   (0.0ms)  BEGIN
   (0.2ms)  UPDATE `wheels` SET `updated_at` = '2011-11-22 13:13:33', `car_id` = '4ecb9ffc904f562ced000001' WHERE `wheels`.`id` = 6
   (0.2ms)  COMMIT
 => true 
ruby-1.8.7-p352 :008 > car
 => #<Car _id: 4ecb9ffc904f562ced000001, _type: nil> 
ruby-1.8.7-p352 :009 > exit
[~/dev/personal/tenacity_rails3] $ 
[~/dev/personal/tenacity_rails3] $ rails console
Loading development environment (Rails 3.1.1)
ruby-1.8.7-p352 :001 > 
ruby-1.8.7-p352 :002 >   car = Car.find "4ecb9ffc904f562ced000001"
 => #<Car _id: 4ecb9ffc904f562ced000001, _type: nil> 
ruby-1.8.7-p352 :003 > car.wheels
   (0.3ms)  SELECT id FROM wheels WHERE car_id = '4ecb9ffc904f562ced000001'
  Wheel Load (0.2ms)  SELECT `wheels`.* FROM `wheels` WHERE (id in (4,5,6))
 => [#<Wheel id: 4, car_id: "4ecb9ffc904f562ced000001", created_at: "2011-11-22 13:13:33", updated_at: "2011-11-22 13:13:33">, #<Wheel id: 5, car_id: "4ecb9ffc904f562ced000001", created_at: "2011-11-22 13:13:33", updated_at: "2011-11-22 13:13:33">, #<Wheel id: 6, car_id: "4ecb9ffc904f562ced000001", created_at: "2011-11-22 13:13:33", updated_at: "2011-11-22 13:13:33">] 
ruby-1.8.7-p352 :004 > 

Can you explain what you mean by "this was tested with a single model"?

John

jwood commented 12 years ago

And, here are my classes:

class Car
  include Mongoid::Document
  include Tenacity

  t_has_many :wheels
  t_has_one :dashboard
end
class Wheel < ActiveRecord::Base
  include Tenacity

  t_belongs_to :car
end
mattiassvedhem commented 12 years ago

What I mean with "this was tested with a single model" is that I didn't use STI or Polymorphic associations. You are right, it does work that way.

I have the t_belongs_to call in the Mongoid model.

class Wheel
  include Mongoid::Document
  include Tenacity

  t_belongs_to :car
end
class Car < ActiveRecord::Base
  include Tenacity

  t_has_many :wheels
end

This works:

irb(main):022:0> car = Car.first
  Car Load (0.8ms)  SELECT `cars`.* FROM `cars` LIMIT 1
=> #<Car id: 1, name: "Ferrari">
irb(main):023:0> wheel_1 = Wheel.last
=> #<Wheel _id: 4ebe6a8d66eaa7e4aa000002, _type: nil, name: "Wheel1", car_id: nil>
irb(main):024:0> wheel_2 = Wheel.first
=> #<Wheel _id: 4ebba80866eaa75c7a000003, _type: nil, name: "Wheel2", car_id: nil>
irb(main):025:0> wheels = [wheel_1, wheel_2]
=> [#<Wheel _id: 4ebe6a8d66eaa7e4aa000002, _type: nil, name: "Wheel1", car_id: nil>, #<Wheel _id: 4ebba80866eaa75c7a000003, _type: nil, name: "Wheel2", car_id: nil>]
irb(main):026:0> car.wheels = wheels
=> [#<Wheel _id: 4ebe6a8d66eaa7e4aa000002, _type: nil, name: "Wheel1", car_id: nil>, #<Wheel _id: 4ebba80866eaa75c7a000003, _type: nil, name: "Wheel2", car_id: nil, slug: "haket">]
irb(main):027:0> car.save
   (0.2ms)  BEGIN
  Car Load (0.6ms)  SELECT `cars`.* FROM `cars` WHERE `cars`.`id` = 1 LIMIT 1
  Car Load (0.4ms)  SELECT `cars`.* FROM `cars` WHERE `cars`.`id` = 1 LIMIT 1
   (0.2ms)  COMMIT
=> true
irb(main):028:0>
irb(main):029:0> car.wheels
  User Load (0.5ms)  SELECT `cars`.* FROM `cars` LIMIT 1
=> [#<Wheel _id: 4ebba80866eaa75c7a000003, _type: nil, name: "Wheel1", car_id: 1>, #<Wheel _id: 4ebe6a8d66eaa7e4aa000002, _type: nil, name: "Wheel2", car_id: 1>]

This doesn't set the car_id:

irb(main):015:0> wheel = Wheel.create
=> #<Wheel _id: 4ecba73166eaa75371000002, _type: nil, name: nil, car_id: nil>
irb(main):016:0> car.wheels << wheel
=> [#<Wheel _id: 4ebe6a8d66eaa7e4aa000002, _type: nil, name: "Wheel1", car_id: 1>, #<Wheel _id: 4ebba80866eaa75c7a000003, _type: nil, name: "Wheel2", car_id: 1>, #<Wheel _id: 4ecba73166eaa75371000002, _type: nil, name: nil, car_id: nil>]
irb(main):017:0>

irb(main):017:0> car.save
   (0.2ms)  BEGIN
  Car Load (0.6ms)  SELECT `cars`.* FROM `cars` WHERE `cars`.`id` = 1 LIMIT 1
  Car Load (0.4ms)  SELECT `cars`.* FROM `cars` WHERE `cars`.`id` = 1 LIMIT 1
   (0.2ms)  ROLLBACK
Mongoid::Errors::DocumentNotFound: translation missing: sv_SE.mongoid.errors.messages.document_not_found
    from /Users/yeggeps/.rbenv/versions/1.9.2-p290/lib/ruby/gems/1.9.1/gems/mongoid-2.2.4/lib/mongoid/document.rb:149:in `reload'
    from /Users/yeggeps/.rbenv/versions/1.9.2-p290/lib/ruby/gems/1.9.1/gems/tenacity-0.5.3/lib/tenacity/orm_ext/mongoid.rb:112:in `_t_reload'
    from /Users/yeggeps/.rbenv/versions/1.9.2-p290/lib/ruby/gems/1.9.1/gems/tenacity-0.5.3/lib/tenacity/associate_proxy.rb:52:in `method_missing'
    from /Users/yeggeps/.rbenv/versions/1.9.2-p290/lib/ruby/gems/1.9.1/gems/tenacity-0.5.3/lib/tenacity/associations/has_many.rb:142:in `block in establish_relationship_in_target_objects'
    from /Users/yeggeps/.rbenv/versions/1.9.2-p290/lib/ruby/gems/1.9.1/gems/tenacity-0.5.3/lib/tenacity/associates_proxy.rb:61:in `block in method_missing'
    from /Users/yeggeps/.rbenv/versions/1.9.2-p290/lib/ruby/gems/1.9.1/gems/tenacity-0.5.3/lib/tenacity/associates_proxy.rb:61:in `each'
    from /Users/yeggeps/.rbenv/versions/1.9.2-p290/lib/ruby/gems/1.9.1/gems/tenacity-0.5.3/lib/tenacity/associates_proxy.rb:61:in `method_missing'
    from /Users/yeggeps/.rbenv/versions/1.9.2-p290/lib/ruby/gems/1.9.1/gems/tenacity-0.5.3/lib/tenacity/associations/has_many.rb:141:in `establish_relationship_in_target_objects'
    from /Users/yeggeps/.rbenv/versions/1.9.2-p290/lib/ruby/gems/1.9.1/gems/tenacity-0.5.3/lib/tenacity/associations/has_many.rb:109:in `_t_save_associates'
    from /Users/yeggeps/.rbenv/versions/1.9.2-p290/lib/ruby/gems/1.9.1/gems/tenacity-0.5.3/lib/tenacity/orm_ext/activerecord.rb:90:in `block in _t_initialize_has_many_association'
    from /Users/yeggeps/.rbenv/versions/1.9.2-p290/lib/ruby/gems/1.9.1/gems/activesupport-3.1.1/lib/active_support/callbacks.rb:422:in `_run_save_callbacks'
    from /Users/yeggeps/.rbenv/versions/1.9.2-p290/lib/ruby/gems/1.9.1/gems/activesupport-3.1.1/lib/active_support/callbacks.rb:81:in `run_callbacks'
    from /Users/yeggeps/.rbenv/versions/1.9.2-p290/lib/ruby/gems/1.9.1/gems/activerecord-3.1.1/lib/active_record/callbacks.rb:264:in `create_or_update'
    from /Users/yeggeps/.rbenv/versions/1.9.2-p290/lib/ruby/gems/1.9.1/gems/activerecord-3.1.1/lib/active_record/persistence.rb:37:in `save'
    from /Users/yeggeps/.rbenv/versions/1.9.2-p290/lib/ruby/gems/1.9.1/gems/activerecord-3.1.1/lib/active_record/validations.rb:50:in `save'
    from /Users/yeggeps/.rbenv/versions/1.9.2-p290/lib/ruby/gems/1.9.1/gems/activerecord-3.1.1/lib/active_record/attribute_methods/dirty.rb:22:in `save'
    from /Users/yeggeps/.rbenv/versions/1.9.2-p290/lib/ruby/gems/1.9.1/gems/activerecord-3.1.1/lib/active_record/transactions.rb:241:in `block (2 levels) in save'
    from /Users/yeggeps/.rbenv/versions/1.9.2-p290/lib/ruby/gems/1.9.1/gems/activerecord-3.1.1/lib/active_record/transactions.rb:295:in `block in with_transaction_returning_status'
    from /Users/yeggeps/.rbenv/versions/1.9.2-p290/lib/ruby/gems/1.9.1/gems/activerecord-3.1.1/lib/active_record/connection_adapters/abstract/database_statements.rb:192:in `transaction'
    from /Users/yeggeps/.rbenv/versions/1.9.2-p290/lib/ruby/gems/1.9.1/gems/activerecord-3.1.1/lib/active_record/transactions.rb:208:in `transaction'
    from /Users/yeggeps/.rbenv/versions/1.9.2-p290/lib/ruby/gems/1.9.1/gems/activerecord-3.1.1/lib/active_record/transactions.rb:293:in `with_transaction_returning_status'
    from /Users/yeggeps/.rbenv/versions/1.9.2-p290/lib/ruby/gems/1.9.1/gems/activerecord-3.1.1/lib/active_record/transactions.rb:241:in `block in save'
    from /Users/yeggeps/.rbenv/versions/1.9.2-p290/lib/ruby/gems/1.9.1/gems/activerecord-3.1.1/lib/active_record/transactions.rb:252:in `rollback_active_record_state!'
    from /Users/yeggeps/.rbenv/versions/1.9.2-p290/lib/ruby/gems/1.9.1/gems/activerecord-3.1.1/lib/active_record/transactions.rb:240:in `save'
    from (irb):17
    from /Users/yeggeps/.rbenv/versions/1.9.2-p290/lib/ruby/gems/1.9.1/gems/railties-3.1.1/lib/rails/commands/console.rb:45:in `start'
    from /Users/yeggeps/.rbenv/versions/1.9.2-p290/lib/ruby/gems/1.9.1/gems/railties-3.1.1/lib/rails/commands/console.rb:8:in `start'
    from /Users/yeggeps/.rbenv/versions/1.9.2-p290/lib/ruby/gems/1.9.1/gems/railties-3.1.1/lib/rails/commands.rb:40:in `<top (required)>'
    from script/rails:6:in `require'
    from script/rails:6:in `<main>'irb(main):018:0>

In the proccess I discovered that associations to Devise models dissapear when I destroy a session and create a new one. Maybe I shall start a new ticket about that.

Thanks.

jwood commented 12 years ago

Sorry, I still cannot reproduce the error.

I changed my Wheel and Car models to look like what you have listed above. I then tried to reproduce the error using the steps described. In each case, car.wheels << wheel successfully added a wheel association to the car, and the car_id was set successfully.

Is there any way you can create a failing test case on a branch that I can take a look at?

John

mattiassvedhem commented 12 years ago

I created a fresh project where << works without problems. The problems I have is probably related to some other gem in combination with Tenacity. I would think it's Devise since all associations are destroyed on session destroy / session create.

jwood commented 12 years ago

If you can identify which gem is causing the issue, I could look at that gem's source and try to identify the conflict.

jwood commented 12 years ago

Closing for now. Feel free to re-open if you identify the gem that is causing the conflict.