2

When making a relation like belongs_to :author I've always used the option :class_name => 'User' but I never liked it. It isn't semantic, IMO.

So I was thinking about an alias for my User model. "Well", I thought, "a model is simply a constant, so let's put another name to it.

Then I wrote Author = User and saved it in app/models/author.rb.

Looked good, and worked well for all purposes except one. When I tried to use my relation, say post.build_author I get uninitialized constant Post::Author.

Why can't ruby find my constant Author ?

Seems like Post can't reach it, so I tried this:

class Post
  def author_class_test
    Author
  end
end

=> Post.new.author_class_test
=> User(...)

So, I assume Post can "see" Author. But not when working with relations, does anybody knows why is that ?

Thanks in advance.

Update.

So, out of curiosity I tried this:

class Post
  Author = User
  ...
end

Then again Post.new.build_author which again got me uninitialized constant Post::Author. But at least now I know he's lying to me. :P

The trace ends here: https://github.com/rails/rails/blob/master/activerecord/lib/active_record/inheritance.rb#L142

I'm starting to think it's an edge case which ActiveRecord does not considers.

2 Answers 2

0

One way that I see you could do this is by creating a module where you store those aliases.

For instance:

module UserAliases
  Author = User
end

class User < ActiveRecord::Base
  include UserAliases
  # ...
end

Put the module somewhere in your app, require it and include it in your model and that should do the trick. Let me know if that works.

Sign up to request clarification or add additional context in comments.

1 Comment

After doing a bit of digging I found the following suggesting similar approaches stackoverflow.com/questions/11154584/alias-for-rails-model and stackoverflow.com/questions/7795809/class-alias-in-ruby about pure Ruby object aliases that describe what you did.
0

You should use Class.new.

You can see in the backtrace, that rails is attempting to build the type of the association using compute_type. In order for compute_type to work, the class name has to be correct.

Let's talk about what exactly we're doing when we define a class.

class A
  def self.foo
    puts "#{self.name} foo"
  end
end

This is the most common method to define a class, but it really just an alias for passing a block to Class.new. Class.new also accepts a parameter, which you can provide another class. When you pass a class to Class.new, it produces a new class with the passed in class set as its superclass. An example will clear this up:

B = Class.new(A)
C = A

Here we've got B and C which both share the same behavior as A. However, there are some important distinctions between assigning A to C and setting A as B's superclass. The one which is relevant to you is this:

A.foo
B.foo
C.foo

#=> A foo
#=> B foo
#=> A foo

You'll see when we use assignment, C is not taking the name you're providing it. When we use Class.new we tell B to take the name we've given it, not the same name as the assigning class.

You're essentially creating a class that behaves like User even when you ask it what it's name is, but you're still calling the association Author without setting the class name.

Do this instead:

Author = Class.new(User)

3 Comments

Makes sense, but I think I tried inheritance before with no avail. I'll try again tomorrow at work, thanks!
No need for Class.new, just use inheritance the way you're used to: class Author < User; end
The problem is, they aren't interchangeable, for instance if I have a user and tries to assign to an author relation Rails raises ActiveRecord::AssociationTypeMismatch: Author(#70347245104340) expected, got User(#70347245222060).

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.