cloning a model in Rails - ruby-on-rails

I have a model in Rails that I would like to use as a basis for another.
For example model: parent will be the template for model child.
I can see two possible options:
(1) Inherit from the first model and then add additional columns
Class Parent < ActiveRecord::Base
Class Child < Parent
(2) Copy the model.rb file and add new features
Class Child < ActiveRecord::Base
In both cases the "Rails" part of the model is created, but what about the database table? I could create the table using create table child as select * from parent where 1=2 and then create migrations to add the additional columns, but it doesn't seem like the "Rails way".
Is there an easy way to create a migration based on an existing table. or am I barking up the wrong tree entirely?

Your (1) is called single table inheritance (STI). Basically you use one table that has both the fields of the parent and the child. You'll also need a column called type to identify the type of the object.
Without more details I can't say if it's a good idea to use STI in your case, but (2) copying model.rb certainly doesn't seem right.

Related

Create a model based on a query instead of a table

As a first approximation, I would like to know if it would work to define a rails model based on a database view instead of a real table.
Of course, I want to use that model only to query and not to insert or update.
If that's possible, is it necessary to define the view? Can I instead specify somehow a query (so I do not need to actually create the view).
Considering the view already exists and the database.yml configuration is set correctly (i.e. the view is somehow inside the database you defined there) and its name is View.
The model:
# view.rb
def View < ApplicationRecord
self.primary_key = 'id'
end
Then you can query like:
View.where(column_name: 'value')

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.

Rails Single Table Inheritance using Foreign Key (ID)

I have to model an association structure and the association is divided into divisions/subdivisions/sections etc. So I've created a simple Entity-Attribute Model:
I'd like to use rail's single-table-inheritance but it seems like this works only if the type column is a string. My question is how to achieve this with my approach? Since I'm using a foreign key as "type" I'd have to query the "type name" first. Has anybody done this before?
I would recommend adding a String "type" attribute to your structure table to satisfy single table inheritance, and to add before_save callbacks to set correct values on either table.
Say you have a StructureType with name "Basic". In Rails that means you'd want to have class hierarchy:
Structure < ActiveRecord::Base
BasicStructure < Structure
In Structure class add:
before_create :set_structure_type_fk
def set_structure_type_fk
self.structure_type = StructureType.find_by_name(\
self.class.name.gsub(/Structure/, '').downcase)
end
Hope this helps.
Note that this approach means that StructureType.name should be immutable: once created it should never be changed, except by a database migration that updates both tables correspondingly.
K

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 .

Rails Generate Model from Existing Table?

I'm very new to the rails framework and want to know how to generate a model based on an existing table. For example, I have a table named person and want to generate the model based on the columns from that table. However, whenever I use "ruby script/generate model Person --skip-migration it creates an empty table named people and creates the model after that. Is there a way to generate a model after a table named person?
Thanks.
Rails is very opinionated, so if you have a table called "person" and you want the corresponding model to be called Person, you need to tell Rails explicitly not to be so clever (otherwise, it will assume that it needs to look for the plural of the model name for the table name).
class Person < ActiveRecord::Base
set_table_name 'person'
end
If your table's primary key isn't called "id", then you'll need to specify that, too...
set_primary_key 'person_id'
You may also need to specify a different autoincrement sequence name, depending on your database.
There's not a way that I know of to automatically generate a model from an existing legacy table, but this should get you most of the way there.

Resources