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 routese.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 outsidercheck 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 commentThat'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
How to Use (Ruby) Rgeo to Transform (Unproject) Coordinates
Automatically Logging Exceptions in Ruby
How to Add Usr/Local/Bin to Path Environment Variable on Ubuntu 12.0.4
Resizing an Image with Mini_Magick
Rails: How Does "New" Action Called "Create" Action
Rake Aborted: Could Not Find Rspec
How to Programmatically Remove "Singleton Information" on an Instance to Make It Marshal
Time Availability Comparison, Using Ruby on Rails
Need Help Installing Ruby 2.7.2 on Mac
What Is The Purpose of Setting Ruby Block Local Variables When Blocks Have Their Own Scope Already
Sidekiq to Execute at Specific Time in Every Timezones
How to Deploy a Test App on Dreamhost Rails 3.0.4
Sqlite3::Sqlexception: Duplicate Column Name While Migrating
How to Access HTML Request Parameters for a .Rhtml Page Served by Webrick
How Does The "#Map(&Proc)" Idiom Work When Introspecting Module Classes