Rails - Associations - What am I referring to? - ruby-on-rails

I'm new to Rails and have been struggling with the naming conventions to work out what I'm actually referring to. With many items named very similarly (e.g. model class, db table, controller) it isn't always obvious from the examples which object is being referred to.
In the guides for associations, it uses posts and comments as examples. Can you help me understand what is actually being referred to?
With the association
has_many :comments
What is :comments? Is this the model class or the controller?
When referring to the associated object:
#post.comments.build
What is comments in this example? Is it the class, the controller, or the instance variable?

Comments is a model, not a controller. This is true in both of your examples. In the first it is a definition of a relationship between two models. In the second it is a new object of type comment which is created and populated with a reference to the post.
A rule of thumb you can use is that models will typically not reference controllers, but other models.

Models represent the real objects.(Hence they are called models). Comments, posts are the actual object representations.
has_many :comments is just a way of saying a post has many comments. So it is referring to the model.
Controllers are where an action takes place for the model. As you go along you will realize there can be multiple controllers for a given model. Controllers is a place where you decide what to do on various events. Hence any route will get mapped to a controller action.
#post.comment.build is saying for the model post which has may comments, build an instance of the model comment. This will lead to triggering of a function in the controller.

Related

Managing polymorphic data in Rails

I have an application where a User can create many Links, and each Link can store different type of data, depending on what type of Link it is. For example, a TelephoneLinkData stores a telephone number, an EmailLinkData stores an email address, a subject and a body. Each Link also has some fields in common, such as a reference to the user and a name.
I've tried to map this into ActiveRecord as cleanly as I can. Currently, I have a polymorphic relationship from Link to its data-type:
class Link < ApplicationRecord
belongs_to :user
belongs_to :link_data, polymorphic: true
...
class EmailLinkData < ApplicationRecord
has_one :link, as: :link_data
accepts_nested_attributes_for :links
...
Technically, I think this would be described as a reverse polymorphic relationship as instead of a class having possibly different parent classes, what I'm trying to model is a class having multiple possible different child classes. This works fine, and I'm able to create Links through the various *LinkData controllers, but what I'd really want to do is have the Link act as the primary source of interaction for the user, so that the user manages their links through the /links path. For example, I would like the API to allow a User to create a link by posting to /links with the data for the LinkData nested in the link_data field
I've looked around for other ways to model this relationship, and the most common other suggestion seems to be Single-Table Inheritance, but the majority of my columns will differ between LinkData classes, so that feels like the wrong abstraction.
Is there a more idiomatic way to model this data structure?
As is always the case, the best choice depends on the business or application needs, so it's difficult to provide a recommendation without knowing more about what you're trying to do.
It sounds like you prefer the MTI approach, essentially using actual foreign keys and an XOR constraint to the Link table instead of a type column. That's a totally reasonable (although not as common) alternative to a polymorphic association.
However, I think there was a bit of a misunderstanding in your question.
Technically, I think this would be described as a reverse polymorphic relationship as instead of a class having possibly different parent classes...
A polymorphic association in Ruby/Rails doesn't have anything to do with class inheritance (e.g. parents and children). You might be thinking of Single table inheritance. A polymorphic association allows one class (e.g. a Link) to be associated a record in any other table (e.g. the various classes of LinkData) via two fields, a association_id and association_type. These associated classes need not be related to each other. For example, a common use case might be the acts_as_commentable gem, that allows you to add a comment to any other object, and the comment would have a polymorphic association with the other classes.
In the second part of your question you mention that you'd like the User to interact with Link's via a single controller.
I would like the API to allow a User to create a link by posting to /links with the data for the LinkData nested in the link_data field
There's nothing stopping you from implementing this using the initially proposed data model. ActiveRecord may not handle this completely for you out of the box, but you can imagine implementing a link_data= method on the Link class that would create the appropriate associated object.
I'd say the pros/cons of using a polymorphic association would be...
Pros:
easy to setup and use
easy to make required (validate presence of / not null)
easy to associate with a new class
Cons:
no referential / database integrity
have to migrate data if you change a class name
And using the MTI approach is basically the opposite. A bit harder to setup and use, harder to add a new association/table, harder to ensure exactly one association exists... but the long term data quality benefits are significant.
I was able to get things to work the way I wanted to using multiple table inheritance, based largely on this chapter: https://danchak99.wordpress.com/enterprise-rails/chapter-10-multiple-table-inheritance/

Ruby: inheritance or something else

I've a quick and quite basic question to ask.
I would like to create a new model which has a parameter that can be one of several model types.
Ex: the param 'targeted_object' can be either an instance of Model A or an instance of Model B.
For the moment I don't think I need a similar behavior for Model A and Model B, so my first guess is to create a Master model for Model A and Model B named TargetableObject: create inheritance.
But is it the best way to do this or I need to make something else regarding that I presume for now no related behavior for Master object children?
Thanks
If I understand correctly, Polymorphic associations could be what you need.
From the rails guides:
With polymorphic associations, a model can belong to more than one other model, on a single association. For example, you might have a picture model that belongs to either an employee model or a product model.

How to structure this so I get all the benefits from STI with none of the consequences? (Pretty irresponsible, I know.)

Say I have the following example of associations in a Rails app:
I'm considering combining the *Posting models under STI. One problem with STI is the potential for many attributes that are only related to one subclass (i.e., a lot of denormalized nil values). This is especially worrisome when your subclasses and going to evolve and grow in the future. I've read a few related posts (such as this), however, as you can see in my example, the potential subclass-specific fields will not necessarily be just attributes, but rather, a lot of belongs_to associations.
My question is, how could I restructure this to use STI from a Posting model for all the common attributes/methods (of which there will be quite a few in my actual app), but keep the unique subclass-specific attributes and belongs_to associations from piling up in the Posting model? Also, the ability to access #board.postings and work with those standard methods is important.
For example, I've thought about moving the type-specific attributes to another model:
class CarPosting < Posting
has_one: car_posting_detail
end
class CarPostingDetail < ActiveRecord::Base
belongs_to :car_posting
belongs_to :car_make
belongs_to :car_model
end
Although, this starts to create a lot of joins, I'm not sure I have the has_one/belongs_to declarations in the right direction, and you have to start chaining calls (e.g., #posting.car_posting_detail.car_make).
Are there other design patterns you have seen for accomplishing this?
You basically have to 2 options for accomplishing inheritance.
First, you can use rails STI as you suggested. The downside is that end up with nil attribute for the child classes that do not use all of the fields. Your idea to reduce this by adding type-specific attributes to another model is a great way to reduce this. However, you should keep the implementation as DRY as possible by defining a has_one :detail for the Posting. Then you can simply assign specific detail types in the Posting childs. For example, CarPosting's detail would be CarPostingDetail. This is convenient because then all Posting children will have their details accessed identically, but will still have different details. So the query now looks like #posting.detail.car_make. To take this one step further, you can define a custom helper method in your Posting model to grab each attribute in the current Posting's detail and create an accessor for it. Now the entire detail layer is transparent and you can simply access those attributes by saying #posting.car_make.
Second, you can use an abstract class. This is essentially the reverse of STI. You create an abstract model class which can never be instantiated. Thus, you cannot define any relationships in the Posting class because it has no table. Each child of the abstract Posting class has its own separate table. The main advantage of doing this would be the ability to define methods for all of your Posting types without copy and pasting them into every model. So this options is better if there are some overlapping functionality across the models, but very little data overlap.
You could use polymorphic associations for this.
Post model belongs_to :postable, :polymorphic => true
car, event and all the other "postable" classes would have this relationship
has_many :posts, as: :postable
Post would hold the postable_id and postable_type
More info here
http://guides.rubyonrails.org/association_basics.html#polymorphic-associations

Which MVC layer should handle a child claiming a toy?

I have two models in my app: a child that has_many :toys and a toy that belongs_to :child. I made the db migration I needed for this to work (added child_id to toys table).
At first, children exist on their own, and toys exist on their own (with no association). At the start of every day in kindergarten, no child owns any toys. To play with a toy, a child must claim it first, and become its owner. So, now I need to somehow implement a child.claim(toy) method, and here I get stuck. Specifically:
Should this go into the child controller or model? Or maybe it should be somehow split between both?
If it should go into the controller, should it correspond to one of the CRUD actions or be its own thing as def claim(toy)?
Edit 1: The child is the user and is logged on via the browser. (Today's kids can do amazing things)
Actually you don't need a claim method if child is the user. You can have a claim_toy method in your controller. In you toys index view for each toy you can give a link as follows.
<%= link_to "claim", claim_toy_path(:toy_id => toy.id) %>
Your controller method will look something like this.
def claim_toy
toy = Toy.find(params[:toy_id])
current_child.toys << toy
end
simple enough. This is not a restful solution by the way.
A Child is a Foreign Key on Toy
There are certainly other ways to do this, but based on your original question, the simplest solution is to make your behavior consistent with the fact that a child is associated with a toy in the Toy table.
Simplest Solution
Setting aside arguments about what one should do in a perfect OOP/MVC design, the sensible place to make the change is in a controller since when a child claims a toy, the claim is processed by a an #update (or perhaps even a #claim) action on the Toy controller.
The child is the user and is logged on via the browser.
In your case, the session already knows who the child is, so adding the foreign key to the Toy model is trivial. The controller is the entity that receives the params for the associated model, so it's the correct place to tell the Toy model what attributes to update for a given toy.
There are certainly more complicated solutions, but they are of dubious merit based on the information provided in your original post. As always, your mileage may vary.
I'd build a separate class handling all the logic regarding a child and a toy. Call it context, call it concern, but do it.
class ToyChild # or ToyInteraction or ChildContext::Toys...
attr_reader :toy, :child
def initialize(toy, chid)
#toy = toy
#child = child
end
def associate
toy.child = child
# could be more difficult: you should check if the child has not enough toys, if the toy is not already assigned to another child etc...
#I'd avoid saving here since you may want to perform other operations
end
def foo
#code here
end
end
And in controller:
assoc = ToyChild.new(toy, child).associate
assoc.save
This style of coding:
is easier to test
splits responsibilities clearly
keeps things dry (no specific code in controllers)

Rails convention

I'm really new to Rails (for coldfusion) and before I even start coding I want to make sure I understand the convention and apply it right.
One of my concern is the following situation using Rails convention:
I create a table called users
I create a model called User.cfc
I create a controller called Users.cfc
create a register page so I will add a method called register in the controller Users.cfc since its specifically related to the model User.
But now lets say I create a method that call multiple model then where should I put that method?
Example:
I'll take facebook wall post for this example. For each of my post many comments can be added. So I could create a method name postMessage so in that method I would call the model "Post" and also the model "Postcomment" so my question is should I put the method postMessage in the Controller "Posts" or "Postcomments"?
That really depends on the purpose of the method. If it's a user who's looking at his collection of Widgets, you might create a "widgets" method in the Users controller.
On the other hand, if you want to list all the users who bid on Widget #32, then you might add a "users" method to the Widgets controller.
There is no definite rule with these types of things. While you generally want a 1-to-1 correlation between Models and Controllers in Rails, there are exceptions. You may have some Models without their own Controllers (e.g. Login, EmailAddress), and some Controllers with no associated models (e.g. Home, Admin).

Resources