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.

Arthur Corenzan
  • 901
  • 9
  • 17

2 Answers2

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.

Marc Lainez
  • 3,070
  • 11
  • 16
  • After doing a bit of digging I found the following suggesting similar approaches http://stackoverflow.com/questions/11154584/alias-for-rails-model and http://stackoverflow.com/questions/7795809/class-alias-in-ruby about pure Ruby object aliases that describe what you did. – Marc Lainez Aug 14 '14 at 07:01
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)
Matthew
  • 5,435
  • 6
  • 25
  • 29
  • Makes sense, but I think I tried inheritance before with no avail. I'll try again tomorrow at work, thanks! – Arthur Corenzan Aug 17 '14 at 17:37
  • No need for `Class.new`, just use inheritance the way you're used to: `class Author < User; end` – Alex Aug 20 '14 at 21:08
  • 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)`. – Arthur Corenzan Aug 21 '14 at 01:19