jwood / tenacity

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

Model with more than one t_belongs_to association causes error #35

Closed raphaelcm closed 12 years ago

raphaelcm commented 12 years ago

Steps to reproduce:

  1. Create a model with two t_belongs_to associations, e.g.:
class TenacityTest
  include Mongoid::Document
  include Mongoid::Timestamps
  include Mongoid::Slug
  include Tenacity

  t_belongs_to :organization
  t_belongs_to :avatar
end
  1. Try to create an instance of that class, e.g:
ree-1.8.7-2011.03 :031 > tt = TenacityTest.new

Expected behavior: tt object should have getters and setters for both organization and avatar.

Actual behavior:

NoMethodError: undefined method `_t_id_type' for #<Class:0xa82c79c>
    from /var/bundler/turtle/ruby/1.8/gems/activerecord-3.0.7/lib/active_record/base.rb:1009:in `method_missing'
    from /var/bundler/turtle/ruby/1.8/gems/tenacity-0.5.4/lib/tenacity/orm_ext/helpers.rb:9:in `id_class_for'
    from /var/bundler/turtle/ruby/1.8/gems/tenacity-0.5.4/lib/tenacity/orm_ext/mongoid.rb:94:in `_t_initialize_belongs_to_association'
    from /var/bundler/turtle/ruby/1.8/gems/tenacity-0.5.4/lib/tenacity/associations/belongs_to.rb:36:in `initialize_belongs_to_association'
    from /var/bundler/turtle/ruby/1.8/gems/tenacity-0.5.4/lib/tenacity/class_methods.rb:274:in `t_belongs_to'
    from /home/me/app/models/tenacity_test.rb:8
    from /var/bundler/turtle/ruby/1.8/gems/activesupport-3.0.7/lib/active_support/dependencies.rb:454:in `load'
    from /var/bundler/turtle/ruby/1.8/gems/activesupport-3.0.7/lib/active_support/dependencies.rb:454:in `load_file'
    from /var/bundler/turtle/ruby/1.8/gems/activesupport-3.0.7/lib/active_support/dependencies.rb:596:in `new_constants_in'
    from /var/bundler/turtle/ruby/1.8/gems/activesupport-3.0.7/lib/active_support/dependencies.rb:453:in `load_file'
    from /var/bundler/turtle/ruby/1.8/gems/activesupport-3.0.7/lib/active_support/dependencies.rb:340:in `require_or_load'
    from /var/bundler/turtle/ruby/1.8/gems/activesupport-3.0.7/lib/active_support/dependencies.rb:491:in `old_load_missing_constant'
    from /home/me/config/initializers/rails_patches.rb:10:in `load_missing_constant'
    from /var/bundler/turtle/ruby/1.8/gems/activesupport-3.0.7/lib/active_support/dependencies.rb:183:in `const_missing'
    from /var/bundler/turtle/ruby/1.8/gems/activesupport-3.0.7/lib/active_support/dependencies.rb:181:in `each'
    from /var/bundler/turtle/ruby/1.8/gems/activesupport-3.0.7/lib/active_support/dependencies.rb:181:in `const_missing'
    from (irb):28ree-1.8.7-2011.03 :029 > tt = TenacityTest.new

NOTE: TenacityTest works just fine with only one t_belongs_to association.

raphaelcm commented 12 years ago

Using Tenacity 0.5.4. Same issue occurs for MongoMapper-backed models. Haven't tested with any others.

jwood commented 12 years ago

Hi.

Could you please provide the class definitions for organization and avatar?

Thanks, John

raphaelcm commented 12 years ago

They're both standard-issue ActiveRecord models that inherit from ActiveRecord::Base:

class Avatar < ActiveRecord::Base

  has_attached_file (paperclip file parameters)

end

class Organization < ActiveRecord::Base

  # some vanilla AR associations and validations

end
jwood commented 12 years ago

The Tenacity module needs to be included in the target models as well. It adds some behavior to the models that allows tenacity to figure out how to setup the foreign keys.

Please include Tenacity in your Avatar and Organization models and see if that fixes the problem.

raphaelcm commented 12 years ago

Thanks, that resolved that but now I face a different issue:

class TenacityTest
  include Mongoid::Document
  include Mongoid::Timestamps
  include Mongoid::Slug
  include Tenacity

  t_belongs_to :organization #works
  t_belongs_to :avatar #works
  t_belongs_to :slideshow, :class_name => "Community::Slideshow" #fails
end

Slideshow is a standard AR model, the only difference is the namespacing and a custom table name.

class Community::Slideshow < ActiveRecord::Base
  include Tenacity
  set_table_name 'community_slideshows'

  #...

end

Here's the exception:

> TenacityTest.new
NameError: wrong constant name Community::Slideshow
    from /var/bundler/turtle/ruby/1.8/gems/tenacity-0.5.4/lib/tenacity/association.rb:73:in `const_get'
    from /var/bundler/turtle/ruby/1.8/gems/tenacity-0.5.4/lib/tenacity/association.rb:73:in `associate_class'
    from /var/bundler/turtle/ruby/1.8/gems/tenacity-0.5.4/lib/tenacity/orm_ext/helpers.rb:9:in `id_class_for'
    from /var/bundler/turtle/ruby/1.8/gems/tenacity-0.5.4/lib/tenacity/orm_ext/mongoid.rb:94:in `_t_initialize_belongs_to_association'
    from /var/bundler/turtle/ruby/1.8/gems/tenacity-0.5.4/lib/tenacity/associations/belongs_to.rb:36:in `initialize_belongs_to_association'
    from /var/bundler/turtle/ruby/1.8/gems/tenacity-0.5.4/lib/tenacity/class_methods.rb:274:in `t_belongs_to'
    from /my/app/models/tenacity_test.rb:9
    ...
jwood commented 12 years ago

I just pushed a commit that should address this issue

c24a35aeb57f0cd3719352ac4f63781d4cf0adef

Would you mind testing it out? If it solves your issue, I'll cut a new version of Tenacity.

raphaelcm commented 12 years ago

Thanks for such a fast response! I'll test it out now. Hopefully I can familiarize myself enough with the codebase to contribute some fixes down the line.

raphaelcm commented 12 years ago

Ok, just tested it out. Getting a different error now. Using same class definitions as above:

> t = TenacityTest.new
 => #<TenacityTest _id: 4f7b50c5e628f82349000005, created_at: nil, updated_at: nil, community/slideshow_id: nil, avatar_id: nil, _type: nil, organization_id: nil>
> t.avatar = Avatar.last
> t.organization = Organization.last
> t.slideshow = Community::Slideshow.last
> t
 => #<TenacityTest _id: 4f7b50c5e628f82349000005, created_at: nil, updated_at: nil, community/slideshow_id: 80145, avatar_id: 280, _type: nil, organization_id: 15189>
> t.valid?
 => true 
> t.save
NoMethodError: undefined method `find_by_id' for #<Class:0xab501a8>
    from /var/bundler/turtle/ruby/1.8/gems/activerecord-3.0.7/lib/active_record/base.rb:984:in `method_missing'
    from /var/bundler/turtle/ruby/1.8/bundler/gems/tenacity-c24a35aeb57f/lib/tenacity/orm_ext/activerecord.rb:60:in `_t_find'
    from /var/bundler/turtle/ruby/1.8/bundler/gems/tenacity-c24a35aeb57f/lib/tenacity/instance_methods.rb:23:in `_t_verify_associates_exist'
    from /var/bundler/turtle/ruby/1.8/bundler/gems/tenacity-c24a35aeb57f/lib/tenacity/instance_methods.rb:19:in `each'
    from /var/bundler/turtle/ruby/1.8/bundler/gems/tenacity-c24a35aeb57f/lib/tenacity/instance_methods.rb:19:in `_t_verify_associates_exist'
    from /var/bundler/turtle/ruby/1.8/bundler/gems/tenacity-c24a35aeb57f/lib/tenacity/orm_ext/mongoid.rb:79:in `_callback_before_1683'
    from /var/bundler/turtle/ruby/1.8/gems/activesupport-3.0.7/lib/active_support/callbacks.rb:422:in `_run_save_callbacks'
    from /var/bundler/turtle/ruby/1.8/gems/activesupport-3.0.7/lib/active_support/callbacks.rb:94:in `send'
    from /var/bundler/turtle/ruby/1.8/gems/activesupport-3.0.7/lib/active_support/callbacks.rb:94:in `run_callbacks'
    from /var/bundler/turtle/ruby/1.8/gems/mongoid-2.2.0/lib/mongoid/persistence/insertion.rb:24:in `prepare'
    from /var/bundler/turtle/ruby/1.8/gems/mongoid-2.2.0/lib/mongoid/persistence/insertion.rb:22:in `tap'
    from /var/bundler/turtle/ruby/1.8/gems/mongoid-2.2.0/lib/mongoid/persistence/insertion.rb:22:in `prepare'
    from /var/bundler/turtle/ruby/1.8/gems/mongoid-2.2.0/lib/mongoid/persistence/operations/insert.rb:26:in `persist'
    from /var/bundler/turtle/ruby/1.8/gems/mongoid-2.2.0/lib/mongoid/persistence.rb:44:in `insert'
    from /var/bundler/turtle/ruby/1.8/gems/mongoid-2.2.0/lib/mongoid/persistence.rb:149:in `save'
    from (irb):36
jwood commented 12 years ago

Interesting. This implies that the ActiveRecord based class in the association does not respond to find_by_id. Does either Avatar, Organization, or Community::Slideshow not have a field named id in their respective table? Perhaps one uses a primary key of a different name?

raphaelcm commented 12 years ago

Ah yes. Community::Slideshow is a legacy model and its PK is show_id. I created a class method find_by_id that wraps find_by_show_id and it works!

One question though, the foreign key field is community/slideshow_id. Shouldn't it be slideshow_id (to be consistent with how AR names FK fields)?

jwood commented 12 years ago

Yeah, it probably should. As you may have guessed, it looks like Tenacity doesn't handle name spaced classes well. For now, you should be able to do the following

t_belongs_to :slideshow, :class_name => "Community::Slideshow", :foreign_key => "slideshow_id"

raphaelcm commented 12 years ago

Great. Thanks so much for your help.

jwood commented 12 years ago

No problem. I'll take a stab at fixing the foreign key issue before cutting the new version of Tenacity.

Let me know if you run into any other issues.

jwood commented 12 years ago

I just pushed a fix that should take care of the foreign key issue (ac4e7ff7b98f3c203a9f357415a3b926297d3c54). Would you mind giving it a try before I cut the new version of Tenacity?

raphaelcm commented 12 years ago

Works! Thanks again.

> t = TenacityTest.new
 => #<TenacityTest _id: 4f7ef650e628f802e1000002, slideshow_id: nil, created_at: nil, updated_at: nil, avatar_id: nil, _type: nil, organization_id: nil> 
> t.slideshow = Community::Slideshow.last
 => #<Community::Slideshow id: 78, department_id: 162, created_at: "2012-02-22 17:39:58", updated_at: "2012-04-06 13:45:22", slideshowable_id: "4f7da1b6e628f85988000001", slideshowable_type: "CampusHub"> 
> t
 => #<TenacityTest _id: 4f7ef650e628f802e1000002, slideshow_id: 78, created_at: nil, updated_at: nil, avatar_id: nil, _type: nil, organization_id: nil>
jwood commented 12 years ago

Fixed in version 0.5.5