STI and routes

I would like to contribute to Rails. Specifically I’d like to add a feature, and before I write any code I’d like to receive some feedback from you guys. When using Single Table Inheritance, all path/url helpers for the parent resource will not work for the child resources. For example:

class User < ActiveRecord::Base end class Student < User end class Professor < User end u = User.create

s = Student.create p = Professor.create

link_to ‘User’, u # user_path(u) link_to ‘Student’, s # student_path(s) link_to 'Professor, p # professor_path(p)

form_for u # user_path(u) form_for s # student_path(s) form_for p # professor_path(p)

Let’s see what happens when I add a nested resource named posts to User

class User < ActiveRecord::Base has_many :posts end

form_for [ u, Post.new ] # user_posts_path form_for [ s, Post.new ] # student_posts_path form_for [ p, Post.new ] # professor_posts_path

It might look good and obvious, but now see the routes:

resources :users do resources :posts

end

resources :students do resources :posts

end

resources :professors do resources :posts

end

And if I add other nested resources the repetition will grow larger and larger. On StackOverflow I was answered to use a loop

[ :users, :students, :professors ].each do |name| resources name do resources :posts end end

While this might be the intended behaviour, I can’t see a reason why the nested post route needs to have 3 helpers when it is owned from the parent. I was also suggested to use the url option in form_for to specify the parent path. However this will not work when polymorphic associations are used.

My solution? Instead of creating tons of unnecessary paths, extend the :as option of resources to be an Array, and allow many path helpers to be mapped to the same route. So that we can write:

resources :user, as: [ ‘users’, ‘students’, ‘professors’ ] do resources :posts end

This code should generate all the set of routes for the users resources. And generate 3 sets of path helpers. users_path, students_path and professors_path will all point to /users for example.

Do you think this will be useful to someone other than me? Do you believe that DHH would allow such a thing :slight_smile: ?

Thank you for your attention.

Why not use becomes?

link_to “posts”, [student.becomes(User), :posts]

That would force the object to become a User, so you would only need routes for users and not its subclasses.

:frowning: I don’t find this very Object Oriented. It actually builds a User Object, so methods on Student will not work.

Also it’s bound to be forgotten

form_for professor.becomes(User) do |f|

f.text_field :name # Oh, I forgot that I had defaulted name to “Prof.” in the Professor class.

end

The way I’ve handled this in the past is to do a

class Student

def self.model_name

ActiveModel::Name.new(User) # or superclass.model_name if you are feeling clever

end

end

Does this have any side effect? If not it makes my proposal totally unuseful.

Francesco,

This technique:

class Student < User

def self.model_name

superclass.model_name

end

end

seems to work rather well in general or the use-case you described: generating URLs for subclass instances as though they were baseclass instances.