AssociationTypeMismatch: RoleType expected, got RoleType


#1

I am experiencing strange problems with a HABTM relationship in my
application.

The two models concerned are Role and RoleType. When attempting to set
the RoleTypes on a Role within my controllers I receive an error with
the attached stack trace (see bottom).

Unfortunately I am unable to reproduce this error with a unit test, or
a functional test. I’ve even tried creating a new project to test the
exact bit of code, and still had no success reproducing the error.
I’ve also checked out the code on another computer and run up against
the same problems. This leads me to believe that the problem lies
somewhere I haven’t considered (i.e. outside the models /
controllers / migrations etc).

I’m basically looking for any pointers anyone might be able to offer
me. I’ve spent two and a half days (and counting) banging my head
against the wall on this one. I’m aware that this problem appears
similar to the below link but since there is no response to that
thread, and since the stack trace is quite different I thought I’d try
a post of my own.

http://groups.google.com/group/rubyonrails-talk/browse_thread/thread/149262683b0c078f/f1c81a1f756226d1?lnk=gst&q=AssociationTypeMismatch+&rnum=1#f1c81a1f756226d1

I’ve simplified the code to attempt to isolate the issue; the specific
bit of code I’m trying to run is:

role = Role.find(1)
role_type = RoleType.find(1)
role.role_types = [role_type]

The exception is raised on the last line as I try to assign the list
of RoleTypes to the Role.

I’m running Lighttpd under XP with Rails 1.1.6 and Ruby 1.8.5

Thanks in advance for any assistance!

Hugh

My Role model:

class Role < ActiveRecord::Base
has_and_belongs_to_many :role_types
has_and_belongs_to_many :users
has_many :permissions

ROLE_TYPE_REVIEWER = ‘reviewer’
ROLE_TYPE_CONCLUSION_REVIEWER = ‘conclusion_reviewer’
ROLE_TYPE_COMMUNITY_REVIEWER = ‘community_reviewer’
ROLE_TYPE_INTEREST_TAG_REVIEWER = ‘interest_tag_reviewer’
ROLE_TYPE_BATCH_ADMINISTRATOR = ‘batch_administrator’
ROLE_TYPE_USER_ADMINISTRATOR = ‘user_administrator’
ROLE_TYPE_USER_GROUP_ADMINISTRATOR = ‘user_group_administrator’
ROLE_TYPE_CITIZEN_ADMINISTRATOR = ‘citizen_administrator’
ROLE_TYPE_INTEREST_TAG_ADMINISTRATOR = ‘interest_tag_administrator’
ROLE_TYPE_COMMUNITY_ADMINISTRATOR = ‘community_administrator’
ROLE_TYPE_ROLES_ADMINISTRATOR = ‘role_administrator’
ROLE_TYPE_QUESTIONS_ADMINISTRATOR = ‘question_administrator’

ROLE_TYPE_INTEREST_TAG_PROPOSER = ‘interest_tag_proposer’
ROLE_TYPE_COMMUNITY_PROPOSER = ‘community_proposer’
ROLE_TYPE_QUESTIONS_PROPOSER = ‘question_proposer’

ROLE_TYPES = {
ROLE_TYPE_REVIEWER => ‘Reviewer’,
ROLE_TYPE_CONCLUSION_REVIEWER => ‘Conclusion Reviewer’,
ROLE_TYPE_COMMUNITY_REVIEWER => ‘Community Reviewer’,
ROLE_TYPE_INTEREST_TAG_REVIEWER => ‘Interest Tag Reviewer’,
ROLE_TYPE_BATCH_ADMINISTRATOR => ‘Batch Administrator’,
ROLE_TYPE_USER_ADMINISTRATOR => ‘User Administrator’,
ROLE_TYPE_USER_GROUP_ADMINISTRATOR => ‘User Group Administrator’,
ROLE_TYPE_CITIZEN_ADMINISTRATOR => ‘Citizen Administrator’,
ROLE_TYPE_INTEREST_TAG_ADMINISTRATOR => ‘Interest Tag
Administrator’,
ROLE_TYPE_COMMUNITY_ADMINISTRATOR => ‘Community Administrator’,
ROLE_TYPE_ROLES_ADMINISTRATOR => ‘Role Administrator’,
ROLE_TYPE_QUESTIONS_ADMINISTRATOR => ‘Question Administrator’,
ROLE_TYPE_INTEREST_TAG_PROPOSER => ‘Interest Tag Proposer’,
ROLE_TYPE_COMMUNITY_PROPOSER => ‘Community Proposer’,
ROLE_TYPE_QUESTIONS_PROPOSER => ‘Question Proposer’
}
ROLE_NAMES = [
ROLE_TYPE_REVIEWER,
ROLE_TYPE_CONCLUSION_REVIEWER,
ROLE_TYPE_COMMUNITY_REVIEWER,
ROLE_TYPE_INTEREST_TAG_REVIEWER,
ROLE_TYPE_BATCH_ADMINISTRATOR,
ROLE_TYPE_USER_ADMINISTRATOR,
ROLE_TYPE_USER_GROUP_ADMINISTRATOR,
ROLE_TYPE_CITIZEN_ADMINISTRATOR,
ROLE_TYPE_INTEREST_TAG_ADMINISTRATOR,
ROLE_TYPE_COMMUNITY_ADMINISTRATOR,
ROLE_TYPE_ROLES_ADMINISTRATOR,
ROLE_TYPE_QUESTIONS_ADMINISTRATOR,
ROLE_TYPE_INTEREST_TAG_PROPOSER,
ROLE_TYPE_COMMUNITY_PROPOSER,
ROLE_TYPE_QUESTIONS_PROPOSER
]

CONTROLLERS = {
:batches => BatchesController.public_instance_methods(false),
:citizens => CitizensController.public_instance_methods(false),
:communities =>
CommunitiesController.public_instance_methods(false),
:interest_tags =>
InterestTagsController.public_instance_methods(false),
:login => LoginController.public_instance_methods(false),
:questions => QuestionsController.public_instance_methods(false),
:roles => RolesController.public_instance_methods(false),
:user_groups =>
UserGroupsController.public_instance_methods(false),
:users => UsersController.public_instance_methods(false),
:welcome => WelcomeController.public_instance_methods(false)
}

def alter_permissions(values)
result = self
if values
# delete all current permissions and create new ones
self.permissions.each { |permission|
permission.destroy
}
values.each {|value|
value_split = value.split(’-’)
self.permissions << Permission.new(:controller =>
value_split[0], :page_action => value_split[1])
}
result = self.save!
end
return result
end
end

My Role migration:

class CreateRoles < ActiveRecord::Migration
def self.up
create_table :roles do |t|
t.column :name, :string
t.column :restricted, :boolean, :null => false, :default =>
false
end
Role.create(:name => ‘Role 1’)
end

def self.down
drop_table :roles
end
end

My RoleType model:

class RoleType < ActiveRecord::Base
has_and_belongs_to_many :roles
end

My RoleType migration:

class CreateRoleTypes < ActiveRecord::Migration
def self.up
create_table :role_types do |t|
t.column :name, :string
end
RoleType.create(:name => ‘Role 1’)
end

def self.down
drop_table :role_types
end
end

The exception’s stack trace:

ActiveRecord::AssociationTypeMismatch (RoleType expected, got
RoleType):
h:/ruby/lib/ruby/gems/1.8/gems/activerecord-1.15.1/lib/
active_record/associations/association_proxy.rb:148:in
raise_on_type_mismatch' h:/ruby/lib/ruby/gems/1.8/gems/activerecord-1.15.1/lib/ active_record/associations/association_collection.rb:137:inreplace’
h:/ruby/lib/ruby/gems/1.8/gems/activerecord-1.15.1/lib/
active_record/associations/association_collection.rb:137:in each' h:/ruby/lib/ruby/gems/1.8/gems/activerecord-1.15.1/lib/ active_record/associations/association_collection.rb:137:inreplace’
h:/ruby/lib/ruby/gems/1.8/gems/activerecord-1.15.1/lib/
active_record/associations.rb:944:in role_types=' h:/ruby/lib/ruby/gems/1.8/gems/activerecord-1.15.1/lib/ active_record/associations.rb:954:insend’
h:/ruby/lib/ruby/gems/1.8/gems/activerecord-1.15.1/lib/
active_record/associations.rb:954:in role_type_ids=' /app/controllers/roles_controller.rb:50:inupdate’
h:/ruby/lib/ruby/gems/1.8/gems/actionpack-1.13.1/lib/
action_controller/base.rb:1095:in send' h:/ruby/lib/ruby/gems/1.8/gems/actionpack-1.13.1/lib/ action_controller/base.rb:1095:inperform_action_without_filters’
h:/ruby/lib/ruby/gems/1.8/gems/actionpack-1.13.1/lib/
action_controller/filters.rb:632:in call_filter' h:/ruby/lib/ruby/gems/1.8/gems/actionpack-1.13.1/lib/ action_controller/filters.rb:638:incall_filter’
h:/ruby/lib/ruby/gems/1.8/gems/actionpack-1.13.1/lib/
action_controller/filters.rb:438:in call' h:/ruby/lib/ruby/gems/1.8/gems/actionpack-1.13.1/lib/ action_controller/filters.rb:637:incall_filter’
h:/ruby/lib/ruby/gems/1.8/gems/actionpack-1.13.1/lib/
action_controller/filters.rb:638:in call_filter' h:/ruby/lib/ruby/gems/1.8/gems/actionpack-1.13.1/lib/ action_controller/filters.rb:438:incall’
h:/ruby/lib/ruby/gems/1.8/gems/actionpack-1.13.1/lib/
action_controller/filters.rb:637:in call_filter' h:/ruby/lib/ruby/gems/1.8/gems/actionpack-1.13.1/lib/ action_controller/filters.rb:638:incall_filter’
h:/ruby/lib/ruby/gems/1.8/gems/actionpack-1.13.1/lib/
action_controller/filters.rb:438:in call' h:/ruby/lib/ruby/gems/1.8/gems/actionpack-1.13.1/lib/ action_controller/filters.rb:637:incall_filter’
h:/ruby/lib/ruby/gems/1.8/gems/actionpack-1.13.1/lib/
action_controller/filters.rb:619:in perform_action_without_benchmark' h:/ruby/lib/ruby/gems/1.8/gems/actionpack-1.13.1/lib/ action_controller/benchmarking.rb:66:inperform_action_without_rescue’
h:/ruby/lib/ruby/1.8/benchmark.rb:293:in measure' h:/ruby/lib/ruby/gems/1.8/gems/actionpack-1.13.1/lib/ action_controller/benchmarking.rb:66:inperform_action_without_rescue’
h:/ruby/lib/ruby/gems/1.8/gems/actionpack-1.13.1/lib/
action_controller/rescue.rb:83:in perform_action' h:/ruby/lib/ruby/gems/1.8/gems/actionpack-1.13.1/lib/ action_controller/base.rb:430:insend’
h:/ruby/lib/ruby/gems/1.8/gems/actionpack-1.13.1/lib/
action_controller/base.rb:430:in process_without_filters' h:/ruby/lib/ruby/gems/1.8/gems/actionpack-1.13.1/lib/ action_controller/filters.rb:624:inprocess_without_session_management_support’
h:/ruby/lib/ruby/gems/1.8/gems/actionpack-1.13.1/lib/
action_controller/session_management.rb:114:in process' h:/ruby/lib/ruby/gems/1.8/gems/actionpack-1.13.1/lib/ action_controller/base.rb:330:inprocess’
h:/ruby/lib/ruby/gems/1.8/gems/rails-1.2.1/lib/dispatcher.rb:41:in
dispatch' h:/ruby/lib/ruby/gems/1.8/gems/scgi_rails-0.4.3/bin/scgi_service: 23:inprocess_request’
h:/ruby/lib/ruby/1.8/thread.rb:135:in synchronize' h:/ruby/lib/ruby/gems/1.8/gems/scgi_rails-0.4.3/bin/scgi_service: 21:inprocess_request’
h:/ruby/lib/ruby/gems/1.8/gems/scgi_rails-0.4.3/lib/scgi.rb:291:in
read_header' h:/ruby/lib/ruby/gems/1.8/gems/scgi_rails-0.4.3/lib/scgi.rb:253:inhandle_client’
h:/ruby/lib/ruby/gems/1.8/gems/scgi_rails-0.4.3/lib/scgi.rb:234:in
initialize' h:/ruby/lib/ruby/gems/1.8/gems/scgi_rails-0.4.3/lib/scgi.rb:234:innew’
h:/ruby/lib/ruby/gems/1.8/gems/scgi_rails-0.4.3/lib/scgi.rb:234:in
handle_client' h:/ruby/lib/ruby/gems/1.8/gems/scgi_rails-0.4.3/lib/scgi.rb:188:inlisten’
h:/ruby/lib/ruby/gems/1.8/gems/scgi_rails-0.4.3/lib/scgi.rb:186:in
initialize' h:/ruby/lib/ruby/gems/1.8/gems/scgi_rails-0.4.3/lib/scgi.rb:186:innew’
h:/ruby/lib/ruby/gems/1.8/gems/scgi_rails-0.4.3/lib/scgi.rb:186:in
listen' h:/ruby/lib/ruby/gems/1.8/gems/scgi_rails-0.4.3/lib/scgi.rb:412:inrun’
h:/ruby/lib/ruby/gems/1.8/gems/scgi_rails-0.4.3/bin/scgi_service:
61
h:/ruby/bin/scgi_service:18:in `load’
h:/ruby/bin/scgi_service:18


#2

Try

@user.role_types << role


#3

Elad,

Using:

@role.role_types << RoleType.find(1)

instead of

@role.role_types = [RoleType.find(1)]

gives exactly the same error. Thanks for the suggestion though!


#4

I do not see it. You are going to have to look at the source of
associations.rb and association_proxy.rb to see what they are doing.

Michael


#5

Oops. Error in the second code snippet. The line
“role.role_type.delete tc” should be “role.role_types.delete
role_type”.


#6

I am having this exactly this problem as well. I will follow Michael
Latta’s advice and poke around in the rails code.

The first time I encountered it I was trying to delete a relationship.
I worked around the problem by changing

role = Role.find(1) # Already has association with RoleType obj

with id 1.
role_type = RoleType.find(1)
role.role_types.delete role_type

to

role = Role.find(1)  # Already has association with RoleType obj

with id 1.
for role_type in role.role_types
if role_type.id == params[:roll_type.id].to_i
role.role_type.delete tc
break
end
end

Kloogey, I know, but I just wanted to get past the exception. The
workaround’s not going to help me when adding a relationship, though.


#7

I have had a bit of a look in association_proxy, and can not see why,
but it appears that line 147 is misbehaving.

association_proxy.rb:
147 unless record.is_a?(@reflection.klass)
148 raise ActiveRecord::AssociationTypeMismatch,
“#{@reflection.class_name} expected, got #{record.class}”
149 end

If I stick this debug in before 147:
logger.debug “Reflection:#{@reflection.klass.inspect}”
logger.debug “Record:#{record.class.inspect}”

I get:
Reflection:RoleType
Record:RoleType

out in the log, which looks correct to me. If I hack the raising of
the exception out, then this action works fine. I haven’t checked it’s
effect on the rest of the app, since it doesn’t sound like a good
solution to me.

The most perculiar thing (which I have only just noticed) is that if I
resubmit a request that has failed immediately after restarting
Lighttpd, it works! So it looks like it’s something to do with the
initialisation of classes going wrong or something.

This rings true with the fact that I get warnings over constants
already being initialised when I run roles_controller_test.rb, or is
that normal for a functional test?

Perhaps that sheds some light on the situation…

Cheers, Hugh


#8

Frederick

You’re exactly right, I hadn’t thought to look at object_ids but they
are indeed different. It seems that @reflection.klass.object_id
remains the same between requests, but record.class.object_id changes.
At first I thought it was @reflection that was at fault, but after
reviewing the same debug performing another operation between two
models with a HABTM relationship I now see that the object_id of the
record’s class should be the same.

So it sounds exactly like Rails is for some reason reloading the class
on every request for the record object.

A minute ago, after reading your tip about requiring the class, I
added the line

require File.dirname(__FILE__)+'/role_type.rb'

to role.rb, since I knew that that model was behaving properly. And
hey presto, it worked! So for now I have a workaround what I consider
to be a rails bug. And, I’m very happy to be able to move on after 4
days of head banging.

Many thanks for your help Frederick… I think I might go on to file a
bug report now.

Hugh

On Mar 20, 12:37 pm, “removed_email_address@domain.invalid” removed_email_address@domain.invalid


#9

Guys,

I had the exact same problem with Rails 1.2.3…have been banging my
head
on the desk for a few hours and just found your post. I appreciate you
documenting the fix.

Has this been filed as a bug report?

Thanks,
Jake


#10

In my application, the classes have different object id’s, that’s why
the is_a? method returns false. This leads to another workaround,
where you change the test in association_proxy.rb, line 147 from

unless record.is_a?(@reflection.klass)

to

if record.class.name != @reflection.klass.name

which would be less dangerous that commenting out the assertion
entirely.

I also made the assertion go away by inserting ‘require role_type’ in
the class definition of an object of global scope that is instantiated
by environment.rb. I’m sure it’s bad form to have an object of global
scope that is instantiated by environment.rb; I do plan to fix that.

I knew to try adding a require because I’ve had trouble with Rails
unloading classes. I haven’t investigating what Rails does along those
lines, but I only had the problem when running the web application,
not in the test environment. I’d try to instantiate one of my models
and I would get an exception along the lines of " was part of
the tree but was unloaded".

My conjecture is that the RoleType class object that was used to set
@reflection.klass was subsequently unloaded, and then somehow in the
magic of Rails a new RoleType class object came into being, and
everything is actually fine, but those circumstances lead to the
exception being raised.

I’m fine with my solution for now, though at some point I’ll need to
actually understand what Rails is doing with unloading and loading the
model classes.

F


#11

We also ran into this issue, but only recently. I have a hunch it’s one
of the more recent plugins/gems we’ve installed not playing nicely with
RoR, but we have also been unable to trace down the core issue.

The following patch to association_proxy.rb preserves the semantics of
is_? by forcing an evaluation of the class name:

From
/activerecord-1.15.3/lib/active_record/associations/association_proxy.rb:146

    def raise_on_type_mismatch(record)
      unless record.is_a?(eval(@reflection.class_name))
        raise ActiveRecord::AssociationTypeMismatch,

“#{@reflection.class_name} #{@reflection.klass} #{
record.is_a?(eval(@reflection.class_name)) } expected, got
#{record.class}”
end
end

This also resolved the reloading issue for us. We are running memcached
w/ cached_model in dev - any similarity to your environments? This might
explain how an ID is reused across requests… although trying to revert
to ActiveRecord::Base instead of CachedModel did not avoid the
exception.

Jake C. wrote:

Guys,

I had the exact same problem with Rails 1.2.3…have been banging my
head
on the desk for a few hours and just found your post. I appreciate you
documenting the fix.

Has this been filed as a bug report?

Thanks,
Jake


#12

Hi All,

I also came across this issue, with the message “User expected, got
User”, which is a very helpful msg of rails of course… :wink:

Anyways, there was a ticket for this, so more info is to be found
here:

http://dev.rubyonrails.org/ticket/8246

Note that I added Michael’s patch. +1 it to get it into core.

Cheers,
Lawrence


#13

ActiveRecord::AssociationTypeMismatch in ClistingsController#create

Category(#-612714068) expected, got String(#-607696388)

Rails.root: /home/gbolahan/sites/spotvilla
Application Trace | Framework Trace | Full Trace

app/controllers/clistings_controller.rb:45:in new' app/controllers/clistings_controller.rb:45:increate’

i have implemented collection select but i intend to create a new new
model with my Clisting model.
how do i get around this error.


#14

On 18 November 2011 10:10, gbolahan a. removed_email_address@domain.invalid wrote:

i have implemented collection select but i intend to create a new new
model with my Clisting model.
how do i get around this error.

Posting some of the code around clistings_controller.rb line 45 would
help.
(although you might find you’re doing this:
clisting.category = params[:clisting][:category]
rather than:
clisting.category =
Category.find_or_create_by_name(params[:clisting][:category])
or
clisting.category_id = params[:clisting][:category]
or some other error relating to passing the Category association a
string… assuming your Clisting has an association to Category…)


#15

Michael Bryzek wrote:

This also resolved the reloading issue for us. We are running memcached
w/ cached_model in dev - any similarity to your environments? This might
explain how an ID is reused across requests… although trying to revert
to ActiveRecord::Base instead of CachedModel did not avoid the
exception.

Jake C. wrote:

Guys,

I had the exact same problem with Rails 1.2.3…have been banging my
head
on the desk for a few hours and just found your post. I appreciate you
documenting the fix.

Has this been filed as a bug report?

Thanks,
Jake

I’ve just applied Michael’s patch and sanity is restored. I’m not using
memcache and the wierd thing for me is that this bug had arisen in some
code which I had tested to death a few weeks back.

The problem I was getting was with a simple has_many …

class Dataset < ActiveRecord::Base
has_many :mapping, :dependent => :destroy
end

The code I’m using to create the parent and class is

record = Dataset.new

some attribute-setting lines omitted for clarity

each “header” corresponds to a new mapping for this dataset

@header.each do |h|
m = Mapping.new

some lines omitted for clarity

record.mapping << m
end

record.save

This all worked fine the first time through the code. Subsequent calls
to this code raised the type mismatch with “Mapping expected, got
Mapping”

This was driving me nuts. All is well now.


#16

On 18 November 2011 10:34, gbolahan a. removed_email_address@domain.invalid wrote:

let me post some code around the Clisting controller

def create
@categories = Category.all
@clisting = Clisting.new(params[:clisting])

Like I said, you’re likely to be passing a
params[:clisting][:category] value (which you probably should have
named “category_id” in your form) into your new Clisting. The clisting
expects a Category object in the category association, not a string
from params.

Do some debugging in this area, and see if that’s part of the problem.


#17

let me post some code around the Clisting controller

def create
@categories = Category.all
@clisting = Clisting.new(params[:clisting])

respond_to do |format|
  if @clisting.save
    format.html { redirect_to @clisting, :notice => 'Clisting was 

successfully created.’ }
format.json { render :json => @clisting,:status => created,
:location => @clisting }
else


#18

undefined method `model_name’ for NilClass:Class

Extracted source (around line #1):

1: <%= form_for(@category) do |f| %>
2: <% if @category.errors.any? %>
3:


4:

<%= pluralize(@category.errors.count, “error”) %>
prohibited this category from being saved:

this is another error i encounterd while trying to create a new category
in my category model.

can someone pls help out on this


#19

On 18 November 2011 14:10, gbolahan a. removed_email_address@domain.invalid wrote:

undefined method `model_name’ for NilClass:Class

Extracted source (around line #1):

1: <%= form_for(@category) do |f| %>

this is another error i encounterd while trying to create a new category
in my category model.

If it’s a different problem, please start a different thread.
(this thread would be a good place for an update on your last
problem… hint)

can someone pls help out on this

It pretty much tells you all you need to know - @category is most likely
nil.


#20

On 18 November 2011 14:17, gbolahan a. removed_email_address@domain.invalid wrote:

undefined method `model_name’ for NilClass:Class

Extracted source (around line #1):

1: <%= form_for(@category) do |f| %>

As I think I mentioned in a previous mail, if you see ‘nil’ in an
error message it quite likely indicates that something is nil. Since
the only variable on that line is @category I think there is a strong
possibility that it is nil. The reason for that particular error is
that it is trying to find the name of the model that @category is, but
as it is nil the error is shown.

Colin


Posted via http://www.ruby-forum.com/.


You received this message because you are subscribed to the Google G. “Ruby
on Rails: Talk” group.
To post to this group, send email to removed_email_address@domain.invalid.
To unsubscribe from this group, send email to
removed_email_address@domain.invalid.
For more options, visit this group at
http://groups.google.com/group/rubyonrails-talk?hl=en.


gplus.to/clanlaw