I have a multi domain app talking to a legacy database.
In that DB I have two tables with different names, lets call them USER_A and USER_B. Their structure and data types are exactly the same, the only difference is that they get their data from different domains.
Now, I would like to have a single scaffold (model/controller/view) that, depending on the domain, maps to the right DB table.
Domain A would work with a model/controller called User which maps internally to the db table USER_A, and Domain B would work with the same model/controller User but maps to the table USER_B.
I would also like to use resource :user in my routes to access the model the rails way.
So somehow I need to overwrite the model on initialization but I am not quite sure how to go about it.
How would one go about this using Rails ActiveRecord?
I don't have a multitable DB ready to test with, so this is an educated guess at the solution:
# respective models
class User < ActiveRecord::Base
end
class DomainAUser < User
self.table_name = "USER_A"
end
class DomainBUser < User
self.table_name = "USER_B"
end
# controller
def set_user
#user = if request.subdomain(0) == "DomainA"
DomainAUser.find(params[:id])
else
DomainBUser.find(params[:id])
end
end
Edit: Here's an alternative bit of metaprogramming hackery which does the subclass instantization within the parent class itself. Tested and working.
I really wouldn't want to maintain something like this though.
# model
class User < ActiveRecord::Base
def self.for_domain(domain_suffix)
class_eval "class DomainUser < User; self.table_name='user_#{domain_suffix}'; end"
"User::DomainUser".constantize
end
end
# controller
User.for_domain("a").new
Related
I am trying to model this scenario with Rails
There are three types of ApplicationForms- FormA, FormB and FormC
This scenario is modeled using Single Table inheritance. And each form has a method tranform_to_pdf overwritten, which returns a map(header-->value).
application_forms(id, name, type)
Now it is possible to have multiple revisions for each form, for example, FormA has Rev1, Rev2, etc. Each revision might have modifications to the number of fields present etc.
What would be the best way to modify existing rails model to reflect revisions?
Thanks in Advance!
I would delegate the work to a second set of objects. Assuming you have a forms table with 3 fields (id, the polymorphic type and the revision (version) ). I would write something like:
class Form < ActiveRecord::Base
end
class FormA < Form
def transform_to_pdf
end
private
REVISION_TO_IMPLEMENTATION_MAP = {
1 => FormAImplementationV1,
2 => FormAImplementationV2
}
def implementation
klass = REVISION_TO_IMPLEMENTATION_MAP[revision]
klass.new(self)
end
end
class FormAImplementation
def initialize(model)
end
end
class FormAImplementationV1 < FormAImplementation
def transform_to_pdf
end
end
class FormAImplementationV2 < FormAImplementation
def transform_to_pdf
end
end
However I think this could quickly become unwieldy and it would quickly make sense to implement another solution, either with meta-data or by storing revision and field in your database. I would need to see more code though...
I have the class User, and subclasses Admin and Student.
Student should have additional dedicated columns. Please let me know how I can do this using STI in Ruby on Rails.
Thanks!
Also, how do I populate the users table?
In Rails, typically you'd have type column on the User class. Now in your subclasses you'd inherit from the User class as such:
class User
end
class Admin < User
end
class Student < User
end
This way you can take advantage of the Rails STI and still be able to flexibly create methods for your subclasses.
Find more information here
However to keep it a bit organized, you could put the subclasses in a folder under your models, as such
#models/users/admin.rb
module Users
class Admin < User
end
end
#model/users/student.rb
module Users
class Student < User
end
end
Now to use your classes, you'd do Users::Student.find(id)
UPDATE
In response to the comment, I think for the columns that would be specific to the student, you'd be better served by an association, say Student.has_one :grade or something of sorts, this way you'd have successfully abstracted your user object to deal with the common User methods. But to create a row for Student and Admin
You could do Users::Student.create(params) or Users::Admin.create(params) and Rails knows how to deal with the STI
I have the a basic events table, and want to have sub-tables for each event type (hiking, party, riverrun, etc).
I see a lot of old (2011/2012) posts regarding CTI, MTI and STI. Some solutions worked for Heroku, while others did not.
What is the "current" Rails way of doing this type of thing? Has this been added to Rails 4.x? Is there a magical Gem that handles this (with Postgres on Heroku)?
Some information if it helps:
In the future, there will be between 20-50 events, and each sub-table might be as many as 80 columns. The site is hosted on Heroku. Running Rails 4.0.2
STI - Single Table Inheritance is what you are looking for.
http://api.rubyonrails.org/classes/ActiveRecord/Base.html#class-ActiveRecord::Base-label-Single+table+inheritance
http://api.rubyonrails.org/classes/ActiveRecord/Inheritance.html
You create your model as always but you add an attribute even_type as a string to your database. (default ist "type")
You let the EventModel know, that this will be the inheritance column.
class Event < ActiveRecord::Base
self.inheritance_column = :event_type
# dont forget your scopes to easy access them by type
# Event.party or Event.ultrafestival
scope :party, -> { where(event_type: 'Party') }
scope :ultrafestival, -> { where(event_type: 'UltraFestival') }
scope :tomorrowland, -> { where(event_type: 'TomorrowLand') }
def host
raise "needs to be implemented in your sub-class!"
end
end
Than you create some subclasses. Make sure those are inheriting from Event
class Party < Event
end
class UltraFestival < Event
end
class Tomorrowland < Event
end
Basically, all Objects are Events! So if you go Event.all you'll get them all! Technically an Object can be something else. All those "Events" will be stored in the same table, they will be differed by the event_type which will be in this example "party", "ultra_festival" or "tomorrowland".
Now you can add some special stuff to each of those classes for example
class Party < Event
def host
"private homeparty PTY"
end
def publish_photostream
end
def call_a_cleaning_woman
end
end
class UltraFestival < Event
attr_accessor :location
def host
"UMF Festival Corp."
end
def is_in_europe?
end
def is_in_asia?
end
end
class Tomorrowland < Event
def host
"ID&T"
end
def download_after_movie!
end
end
This is the standard Rails way - since years. Of course its working on every hoster and with postgres.
// edit:
if your sub-events need some special tables in the database, then you need to go MTI, multi-table-inheritance.
I have model User and model Recruiter. Currently, these are two separate tables, but I want to make them one.
Current:
User: id, username, password, name
Recruiter: id, user_id
Ideal:
User: id, username, password, role (recruiter, admin)
I understand the basics of STI. What I'm wondering is, when I perform methods on the new Recruiter controller (that inherits from User) how do I make sure all my methods are calling on users that are only a recruiter? Thus, queries along the lines of... SELECT * FROM users WHERE role = 'recruiter' for everything.
That is something rails takes care of for you, out of the box. You do not have to manually query on a particular type of user, just query on the right model.
I must also mention that by default rails assumes that your sti_column is called type, but can be overridden to role easily.
Let's admit you have your 2 classes:
class User < ActiveRecord::Base
end
class Recruiter < User
end
Rails will automagically add a type column in the users table so that in your controller, if you do something like this:
class RecruitersController < ApplicationController
def index
#recruiters = Recruiter.all
end
end
Rails will automatically fetch the records with type = 'Recruiter' and you don't even have to set this manually. If you do:
Recruiter.new(name: 'John').save
A new User will be created in database with the field type set to 'Recruiter'.
you would define your models something like this:
class User < ActiveRecord::Base
...
end
class Recruiter < User
...
def initialize
# ... special initialization for recruiters / you could put it here
super
end
...
end
and to create a new recruiter, you would do this:
Recruiter.create(:name => "John Smith")
and because of the type attribute in the STI user table (set to 'Recruiter'), the record will be for a recruiter.
You could put the special initialization for the STI models either in the model's initializer, or in a before filter with a if-cascade checking the type.
An initializer is probably much cleaner.
Have you tried has_one association?
http://api.rubyonrails.org/classes/ActiveRecord/Associations/ClassMethods.html#method-i-has_one
I am looking at improving the performance of a controller action in our Rails application. After looking at some perf counters I now know that the problem lies with the way we have multiple authorization checks peppered throughout our model code. They look something like:
Class Company < ActiveRecord::Base
def member?(user)
#look up a table to check for membership if #is_member does not exist else return #is_member
end
def employee?(user)
#look up a table to check for membership
end
def manager?(user)
#look up a table to check for membership
end
end
class SomeModel < ActiveRecord::Base
def some_method
do_something if current_company.employee?(current_user)
end
end
Since there are a bunch of places where we do a check similar to some_method, requests typically end up hitting the database a LOT of times. This seems like a wasteful way of doing things. What are the ways to speed up such authorization checks? (Assuming that caching is the way to go here)
Since authorizations change rarely, and it appears you have a finite list of memberships to account for, you could:
Add column to the user database for each role
create a callback on the role model to calculate memberships for the user
That way, you only calculate memberships once, when the role is saved. Everything else is a simple/fast column lookup.