Hierarchy within one active record table? - ruby-on-rails

I've been banging my head against this for over a day so here we go:
I have a User model. But there are 3 types of user (more actually, but let's get this done). Let's say there is Talent, Managers, and Directors. But they are all users. A manager has many Talents, Talent has one Manager. Director has many Managers, Manager has one Director. Talent has one Director through Manager.
Obviously, with three separate models, this would be trivial. But they are all users and it is necessary to keep them in the User model.
What is the easiest way of doing this? If I have to use a gem, so be it, but I'd rather not...
Thank you!

Single Table Inheritance is most fitting to your problem description:
Single table inheritance Active Record allows inheritance by storing
the name of the class in a column that by default is named “type” (can
be changed by overwriting Base.inheritance_column). This means that an
inheritance looking like this:
class Company < ActiveRecord::Base; end
class Firm < Company; end
class Client < Company; end
class PriorityClient < Client; end
When you do Firm.create(name: "37signals"), this record will be saved in
the companies table with type = “Firm”. You can then fetch this row
again using Company.where(name: '37signals').first and it will return
a Firm object.
Be aware that because the type column is an attribute on the record
every new subclass will instantly be marked as dirty and the type
column will be included in the list of changed attributes on the
record. This is different from non Single Table Inheritance(STI)
classes:
Company.new.changed? # => false
Firm.new.changed? # => true
Firm.new.changes # => {"type"=>["","Firm"]} If you don't have a
type column defined in your table, single-table inheritance won't be
triggered. In that case, it'll work just like normal subclasses with
no special magic for differentiating between them or reloading the
right type with find.

Related

Rails destroy method for subsclass models not working as expected

I am trying to build a rails site for games. I have created 2 models, Table and Player. To accomodate for different types of games for each table, I decided to subclass the Table and Player model. For example, I have a Blackjack class that extends Table model and BlackjackPlayer class that extends Player model.
To create my Table and Player models:
rails g resource Table type:string data:text
rails g resource Table type:string data:text table_id:integer
I included the column type to accommodate for the subclass. Player model has table_id because each table can have many players
After creating the base Table and Player class, I created a subclass for each. Lets use Blackjack for example. Here is my blackjack.rb file in models directory:
class Blackjack < Table
has_many :blackjackPlayers, foreign_key: "table_id"
end
Here is my blackjack_player.rb file in models directory
class BlackjackPlayer < Player
belongs_to :blackjack, foreign_key: "table_id"
end
I naively expected subclassing to be as simple as this, but I am wrong. Here is what I don't understand. In rails console, I create a few objects:
table = Blackjack.create
table.blackjackPlayers.create
table.blackjackPlayers.create
When I print out table.blackjackPlayers.length, I correctly get 2.
However, when I destroy a blackjackPlayer, the row destruction does not seem to propagate correctly:
table.blackjackPlayers[0].destroy
After the destroy, I still get 2 when I print out table.blackjackPlayers.length but I was expecting 1 instead. The row is correctly removed from the Player table.
Why is this happening and how can I get subclassing to work the way its supposed to? I'm using rails 4
If you use destroy then you use
table.blackjackPlayers.length
It does not look in the database again for a refresh.
You need to look at the database again for the updated count. Try:
table.reload
table.blackjackPlayers.length
or
BlackJack.count
BlackjackPlayer.count
And see the real count.
Did some more testing/digging and found out the problem. When you retrieve an object from the database, it lives in the memory. When you delete an item from the database, the object from the memory does not change and thus the length will not change. You would need to retrieve the object from the database again in order to have the updated records.

Organization model extends user model

I have User model and Organization model. The only difference is that organization has_many users, all other properties are same. I don't want to put it in one table/model. How can I remove tons of code duplicating in this models? Should I use Concerns? I think, it will be not normal if User model will looks like :
class User < ActiveRecord::Base
include user_concern
end
So, how can I extend user model in other model? And how to generate this model with rails g with all User's fields inside?
beware STI
I would keep with concerns rather than using STI. STI often causes more problem that it solves (type mismatches, form urls, etc), plus inheritance won't make sense, here : an user is not a kind of company, and a company is not a kind of user.
That's a naming problem
I think your problem is a naming one. Obviously, your concern should not be "UserConcern". The question is : what kind of methods do you group in that concern ?
Are your methods about social relation between users ? Then, you need a Socializable concern. Are they about subscribing to mailing list ? Then you need a Subscribable concern.
It's ok to have several ones, even if they have a single method in it, because developers won't wonder "what the hell does this do ?" if all concerns are correctly named.
What to duplicate anyway
You should also probably let class level method calls out concerns.
If it's ok for scopes to be embedded in concerns (after all, they resolve in method definitions), it feels less natural to me to put relations in there.
It's ok to duplicate #has_many :foos, we do it all the time in separate models, and it's already difficult enough to get an idea of table schema from a model without hiding more information.
You could use single table inheritance (STI) for this. To get it to work, your model needs a type-field of type string, in which ActiveRecord stores the actual type of your record. Then, you just extend your base model for this.
migration
add_column :users, :type, :string
models
class User < ActiveRecord::Base and class Organisation < User.
ActiveRecord will now fill your type-field with the model-name, and store both in your users table (since this is the one the organisation model is inheriting from).
Have a look at the according section on http://api.rubyonrails.org/classes/ActiveRecord/Base.html .
However, in your case, I'd create a base model, e.g. Address, and then extend User and Organisation from it, to maintain semantically correct models.

How to save 2 id in joint table for many-to-many relationship in rails 3.1

There are two models. One is rfq and another one is standard. It is like:
class Rfq << ActiveRecord::Base
has_and_belongs_to_many :standards
end
class Standard << ActiveRecord::Base
has_and_belongs_to_many :rfqs
end
Table rfqs_standards has been created. My question is when creating rfq, how to save the paid of rfq_id and standard_id in table rfqs_standards automatically.
Thought adding accepts_nested_attributes_for :standard in rfq model. However since there is no real attributes (but just pair of id) saved for this many-to-many relationship, this seems not the right way.
Both rfq and standard was declared in routes.rb as resources :rfqs and resources :standards.
The procedure is when creating rfq, standard will be picked up via a drop down list. Then rfq is saved and at the same time, a new entry in joint table is created. When creating new standard, there is no need to create entry in joint table.
Any suggestion to save the id in joint table? Thanks.
this is easier than you might think because it's handled automatically by ActiveRecord.
When you say "has_and_belongs_to_many", you're telling AR to associate those two models with a many-to-many relationship using the table, and for the most part you no longer need to worry about the join table. When you add an instance of Standard to an Rfq's list of standards, this will be done for you.
Here's an example:
rfq = Rfq.create
standard = Standard.create
rfq.standards << standard
We've created each of the objects, and the third line creates the connection, saving a new record in the rfqs_standards table with the proper ids. rqf.standards looks and acts like a normal array, but when you assign objects to it, ActiveRecord does the database work for you.
After creating the records, you could have also done:
standard.rfqs << rfq
You could also do both at the same time:
rfq = Rfq.create
standard rfq.standards.create
This created an rfq, then created a standard that is automatically connected to the rfq. You can do the same thing in reverse:
standard = Standard.create
rfq = standard.rfqs.create
I hope this helps!
UPDATE: Since you mentioned forms and automatic saving, read my article on nested attributes that shows how to implement that, including full code samples.

Inheritance from ActiveRecord Object

Lets say there is a activerecord class called user, which is representative of user table of database.
But I have different type of users which have
special functions
special variables
custom relations (Employer has_many companies, Employee belongs_to company :)
But also these users have a lot of functionality in common. So what I want to do is create classes for each different type of user then inherit them from user class.
User < ActiveRecord::Base
Employer < User
Employee < User
Customer < User
What is the best way of doing something like that?
Thanks
A lot of applications start out with a User model of some sort. Over time, as different kinds of users emerge, it might make sense to make a greater distinction between them. Admin and Guest classes are introduced, as subclasses of User. Now, the shared behavior can reside in User, and subtype behavior can be pushed down to subclasses. However, all user data can still reside in the users table.
All you need to do is add a type column to the users table that will hold the name of the class to be instantiated for a given row. Active Record takes care of instantiating the kind of object when it loads it from the database.
This technique is called Single Table Inheritance or STI (for short).
A very good recent article about STI is here: http://code.alexreisner.com/articles/single-table-inheritance-in-rails.html
Have a look to this thread on models subclassing:
Subclassing models in Rails

How does the Rails' single table inheritance works?

I have a user table, and a teacher that I newly created. The teacher is sub class of user, so, I use scaffold generator to generate the teacher table, than, I modify the model to do teacher is subclass of user. After all that, I did a db:migrate. Then, I go to
http://localhost:3000/teachers/new
It shows an error:
undefined method `teacherSalary' for #<Teacher:0x103331900>
So, my question is what did I do wrong? I want to create a page for doing user register, the user can ONLY be a teacher / student. But I can't add a teacher record ... ... Moreover, I go to
http://localhost:3000/users/new
I want to have a combo box that allow user register their user to be a "teacher" or a "student". But everything seems not work like I expected. What I need to do? Thank you very very much for your help.
Within your database you should have a single table called users. This table should have a string column which by default is called type. If you use another name for this column then you will have to set the inheritance column name manually using self.inheritance_column = "column_name"
Within your application you have three models, User, Student and Teacher. User inherits from ActiveRecord::Base as usual, Student and Teacher both inherit from User.
You should then be able to instantiate new Teacher and Student objects. Internally this works by writing the model name to the type field on the user tables and then when you use Student.find it adds a clause to the SQL to only return rows where the type = 'Student'
You can add shared behaviour to the User class, e.g. validations etc then add additional behaviour to the inherited classes.
A fuller description of how STI works can be found in Martin Fowlers Book(Patterns of Enterprise Application Architecture).
I found this definition really handy:
STI means one table contains the data of more than one model, usually differentiated by the "type" column. ("users" table contains data for the models "Teacher", ""Pupil", "Employee", "Assistant", etc.)
Keeps similar models in the same table instead of creating new ones.
A Polymorphic Association means that one model can be associated with more than one other model(Comment can belong to post, image, file, user_type...)
To prevent foreign key conflicts, the association is reperesented with the *_id and *_type columns instead of only *_id.
For what you have here , I am not sure if STI is the best way go . STI should generally be used when there is a OO like inheritance and the Models have the same Attribute but different behaviour . In your case Teacher and Student can sure have a few shared attributed , but they are also bound to have different ones as well .
You might want to experiment with a polymorphic association as well .

Resources