Ok, I’ve been banging my head against this one for a little while, so I want to make sure to share the solution.
In my Ruby on Rails project, I have a RegisteredUser class and a Person class. They have a one-to-one relationship with each other that I was trying to represent like so:
class Person < ActiveRecord::Base has_one :registered_users end class RegisteredUser < ActiveRecord::Base belongs_to :person end
see the problem? I didn’t either for a while, but maybe the title of this post will give you a clue. The problem is with the pleurality of my has_one. It should be registered_user instead of registered_users. You really have to watch the plurality of relationships in Rails. If it’s one-to-one, then both sides better be singular. If it’s one-to-many, then the many side better be plural and the one side better be singular. It all makes a difference.
The error message I was getting was simply the not-very-helpful:
test_add_reporter_by_name(PersonControllerTest):
NoMethodError: undefined method `registered_user' for #<person :0x48ff2c4>
d:/ruby/lib/ruby/gems/1.8/gems/activerecord-1.15.1/lib/active_record/base.rb:1848:in `method_missing'
D:/dev/rails/mystats/config/../app/controllers/person_controller.rb:54:in `add_reporter_by_name'
d:/ruby/lib/ruby/gems/1.8/gems/actionpack-1.13.1/lib/action_controller/base.rb:1095:in `send'
d:/ruby/lib/ruby/gems/1.8/gems/actionpack-1.13.1/lib/action_controller/base.rb:1095:in `perform_action_without_filters'
...snip...
d:/ruby/lib/ruby/gems/1.8/gems/actionpack-1.13.1/lib/action_controller/test_process.rb:353:in `post'
test/functional/person_controller_test.rb:79:in `test_add_reporter_by_name'
One other gotcha I ran into…if you have a has_and_belongs_to_many relationship (ie. a many-to-many) then you need to make sure your join table in between does not have an identity column. So, in my case I have another many-to-many relationship between RegisteredUser and Person that I call “authorized reporters”. This relationship says which RegisteredUsers are allowed to report a status for which People:
has_and_belongs_to_many :authorized_reporters, :class_name =>'RegisteredUser', :join_table => 'authorized_reporters'
In between I have a join table called authorized_reporters:
class CreateAuthorizedReporters < ActiveRecord::Migration
def self.up
create_table :authorized_reporters, :id => false do |t|
t.column :registered_user_id, :integer, :null => false
t.column :person_id, :integer, :null => false
end
end
def self.down
drop_table :authorized_reporters
end
end
There is one very important part of that migration: “, :id => false”. Without it, the many-to-many works in one direction, but not in the other (I don’t remember which direction works).