Rails destroy method for subsclass models not working as expected - ruby-on-rails

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.

Related

Hierarchy within one active record table?

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.

cloning a model in 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.

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.

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