Broken Rails Routes After Implementing Single Table Inheritance

Broken Rails Routes after implementing Single Table Inheritance

first generate controllers for your models...

rails generate controller Persons
rails generate controller Teachers
rails generate controller Students
rails generate controller Outsiders

then in routes.rb (rails 3)

resources :persons
resources :teachers
resources :students
resources :outsiders

gives you REST routes

e.g.

persons GET    /persons(.:format) {:action=>"index", :controller=>"persons"}
new_person GET /person/new(.:format) {:action=>"new", :controller=>"persons"}
edit_person GET /persons/:id/edit(.:format) {:action=>"edit", :controller=>"persons"}
person GET /persons/:id(.:format) {:action=>"show", :controller=>"persons"}
persons POST /spersons(.:format) {:action=>"create", :controller=>"persons"}
person PUT /persons/:id(.:format) {:action=>"update", :controller=>"persons"}
person DELETE /persons/:id(.:format) {:action=>"destroy", :controller=>"persons"}

the same for teacher, student and outsider

check rake routes
or rake routes | grep teachers

Rails: Using Devise with single table inheritance

I just ran into the exact scenario (with class names changed) as outlined in the question. Here's my solution (Devise 2.2.3, Rails 3.2.13):

in config/routes.rb:

devise_for :accounts, :controllers => { :sessions => 'sessions' }, :skip => :registrations
devise_for :users, :companies, :skip => :sessions

in app/controllers/sessions_controller.rb:

class SessionsController < Devise::SessionsController
def create
rtn = super
sign_in(resource.type.underscore, resource.type.constantize.send(:find, resource.id)) unless resource.type.nil?
rtn
end
end

Note: since your Accounts class will still be :registerable the default links in views/devise/shared/_links.erb will try to be emitted, but new_registration_path(Accounts) won't work (we :skip it in the route drawing) and cause an error. You'll have to generate the devise views and manually remove it.

Hat-tip to https://groups.google.com/forum/?fromgroups=#!topic/plataformatec-devise/s4Gg3BjhG0E for pointing me in the right direction.

Form helpers in case of Single Table Inheritance

Except adding a dropdown list of type selection in the form, there's nothing more to do. You can create a user in the normal way, like:

@user = Person.new params[:user]

But the type attribute could not be mass assigned, so you have to assign it separately.

@user.type = sanitize_user_type params[:user][:type]

The method sanitize_user_type is used to validate user input value.

The route for creating new user doesn't need to change. Whether other routes need to change or not depend on your requirement. Actually you can add the routes for Teacher, Student, Outsider and relative controllers, so that you can build restful urls.

STI, one controller

First. Add some new routes:

resources :highlights, :controller => "ads", :type => "Highlight"
resources :bargains, :controller => "ads", :type => "Bargain"

And fix some actions in AdsController. For example:

def new
@ad = Ad.new()
@ad.type = params[:type]
end

For best approach for all this controller job look this comment

That's all. Now you can go to localhost:3000/highlights/new and new Highlight will be initialized.

Index action can look like this:

def index
@ads = Ad.where(:type => params[:type])
end

Go to localhost:3000/highlights and list of highlights will appear.

Same way for bargains: localhost:3000/bargains

etc

URLS

<%= link_to 'index', :highlights %>
<%= link_to 'new', [:new, :highlight] %>
<%= link_to 'edit', [:edit, @ad] %>
<%= link_to 'destroy', @ad, :method => :delete %>

for being polymorphic :)

<%= link_to 'index', @ad.class %>

Single Table Inheritance: Optional Plugin module and migrations

The STI table contains the intersection of all attributes for both the parent model and all children models. Note that in STI, BasePlugin is a model, not a module. External plugins are generally provided as gems.

But the key thing is that BasePlugin doesn't need to have all of it's attributes defined when it is first created. If you later add a FooPlugin child and use a migration to add columns to the base_plugins table to add attributes to FooPlugin, BasePlugin will be able to see those columns. ActiveRecord uses introspection to populate the database columns into the ActiveRecord object at startup, so BasePlugin.attribute_names will show you all columns, even if they are only used by the child type.

STI and uninitialized constant - after machine fixed

I was wrong about it working on my backup dev box. I mean it worked, then it didn't. This part is actually a bit foggy.

The solution was to change the parent (Foo) class this:

scope :some_scope, chain_of.other.scopes

To this:

scope :some_scope, -> { chain_of.other.scopes }

As it happens, the last in the chain of scopes references the child class (Bar) like so -- which is where the error was occurring:

Bar.inheritance_column

I have scopes besides this one in the very same class which chain other scopes without being inside of a lambda. I am still mystified by this. I solved it only by trial and error...walking backwards in small steps until I found the happy place.



Related Topics



Leave a reply



Submit