Rails architecture, better create a a new model or add a boolean value to existing model? - ruby-on-rails

I have an article model article.rb (with related images):
attr_accessible :name, :content, :image_ids, :altname1
I now want to create another kind of articles, that are just for cities. They will appear in a completely different section of the website and are not related to all other articles. But the model is exactly the same.
Is it better to create a new model cityarticle.rb or is it better to add a new boolean column (cityarticle with true and false as options) into the existing article model. I'd then add a new action to the controller:
def cityarticles
#cityarticles = Article.where("cityarticle = ?", true)
end
For me it's easier to keep just one model, but there might be good reasons for a new model? How about performance?

Some questions to ask yourself: In what other ways will these types of articles be different? Will different people have access to create/edit/delete them? How do I create a city article and not let it accidentally be saved as a non-city article, or vice versa? Am I using attr_accessible or strong_parameters to prevent users from overriding the article type with params passed in? Will the different types have different validation rules (maybe requiring a city name for city articles)?
How hard will it be to get just the articles I want? How likely am I to forget to specify what kind of articles to show and show all of them?
Based on answers to questions like those, you should be able to decide what data structure will work best for you. If you use Rails' STI pattern, you could wind up with one table but two different models, as one solution. Performance is probably not the main consideration, but if the two types of articles are never going to be queried together, it might be slightly better in terms of performance to split them into separate tables.

New model represents a new "entity" on the System.
Say CityArticles extends Article
It should be a new Model for code clarity and extensibility to increment functionality over the "CityArticles".
You can make a new table scaffold or migration:
rails g scaffold CityArticles references:article
Or making Article class polimorfic association. Read http://teachmetocode.com/articles/ruby-on-rails-what-are-polymorphic-associations/

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/

Can two or more inter-related models have one common controller in rails

am a newbie in rails and I want your help. I want to build a simple project in rails that includes more than one model that are inter-related. So in this case, is it possible to create only one controller that corresponds to all the models or should I create a controller for each model? And please I want reasons for your answers. Thank you.
Of course..
You could easily have something like:
#animals_controller.rb
def animals
#dogs = Dog.all
#cats = Cat.all
#cheetahs = Cheetah.all
end
BUT...
Imagine having an update, edit, new, show, create and destroy for each of these models in the same controller!!
That would be a mess - and you would end up with a complex file that would be (mildly said) annoying to maintain.
Therefore, for your own sake, and because it is good practice, you normally create a corresponding controller for each of your models(animals): model: dog.rb, controller: dogs_controller.rb, model: cat.rb controller: cats_controller.rb etc...
so you, after a few months abroad, can come back and continue your work without thinking What the ....
I would recommend looking into the MVC (for example on youtube) and really understanding how it connects the database with your view and so on.
Understand the data-flow and you will find yourself with a powerfull toll later on when you're debugging, construction forms, creating ajax-calls, custom attributes and so on.
ous,
You need to create a controller for each model in order to have CRUD options. It depends on what the models will do if you don't want a controller, for example if model just does some calculations and you don't really need to pass data to your view then you might not need it.

has_many :through model names, controller and attributes best practices?

Disclaimer: I really spent time thinking about names of models and variables. If you also do, this question is for you.
I have a Rails project which contains two models: User and Project.
They are connected by the model ProjectsUser, which is a connection model in a many-to-many relationship. This model also holds the role of a user in the given project, along with other attributes such as tier and departments. So this is a has_many :through relationship.
Given this scenario, here is everything that always bothered me on all my rails projects since I started developing on it:
Should I use a ProjectsUserController or better add the relevant actions on UserController and ProjectController? At some point, I want to assign users to a project, or even changing the role of a user in a given project. Is it a better practice to leave those actions on the connection controller, or use the model controllers?
Should I write a method to get the role of a user for a given project? This is basically if I should have a method User#role_for(project) or not. Since this method basically is getting the information from the projects_user object it could make more sense to always let this explicity on the code, since most of the times I'll have the project and the user, but not the projects_user. Is this line of thinking correct, or maybe the problem is that I'm should have more project_user on my code than I really do? Are there good caveats for this?
Should I try to rename my table to a non-standard name if it is not obvious? Ok, I got that if I have the models User and NewsSite I should use has_many :subscriptions, but the thing is that naming those models in real life cases are usually harder, by my experience. When the name ends up not being that obvious (for exemple, in my case, maybe project_participation as #wonderingtomato suggested) is for the best, or in those cases it is better to fall back to the ProjectsUser approach?
One extra cookie for pointing beautiful open source Rails code, or by book indications that might help with my kind of questions.
I would use a specific controller. Even if now the interaction sounds simple, you can't know if in the future you'll need to add more advanced features.
I've been handling these kind of relationships in several projects, and using a controller for the join model has always paid off.
You can structure it this way, for example:
index should expect a params[:project_id], so that you can display only the index of users for a specific project.
create is where you add new users, that is where you create new join models.
update is to modify a value on an existing join model, for example when you want to update the role of a user in a project.
destroy is where you remove users from the project, that is where you delete the corresponding join models.
You might not need a show and edit actions, if you decide to manage everything in the index view.
Also, I'd suggest to choose a different name. Rails relies heavily on naming conventions, and projects_users is the default name for the join_table you would use with a has_and_belongs_to_many association. In theory you can use it for an independent model (and a has_many through:), but it's not immediately clear and you might break something. In addiction, it will confuse the hell out of any new programmer that could join the project in the future (personal experience).
What about calling the model something like project_participation?
If you haven't built a lot of functionality yet, and don't have yet that table in production, changing it now will save you a lot of headaches in the future.
update
1) I stand by what I said earlier: your join model is a full fledged record, it holds state, can be fetched, modified (by the user) and destroyed.
A dedicated controller is the way to go. Also, this controller should handle all the operations that modify the join model, that is that alter its properties.
2) You can define User#role_for(project), just remember that it should properly handle the situation where the user is not participating to the project.
You can also make it explicit with something like:
#user.project_participations.where(project_id: #project.id).first.try(:role)
# or...
ProjectParticipation.find_by(project_id: #project.id, user_id: #user.id).try(:role)
But I'd say that encapsulating this logic in a method (on one of the two models) would be better.
3) You are already using a non standard name for your table. What I mean is that it's the default name for a different kind of association (has_and_belongs_to_many), not the one you are using (has_many through:).
Ask yourself this: is the table backing an actual model? If yes, that model represents something in the real world, and thus should have an appropriate name. If, on the other hand, the table is not backing a model (e.g. it's a join table), then you should combine the names of the tables (models) it's joining.
In my mind, REST doesn't always have to map directly to DB records. A conceptual resource here is the association of Projects to Users. Implementation would be different depending on your persistence layer, but a RESTful API would be standard.
Convention over Configuration in Rails is a great helper, but it isn't necessarily applicable to every case 100% of the way through the stack. There doesn't need to be a 1-to-1 mapping between controllers, models, and their respective names. At the app-level, particularly, I want my routes/controllers to represent the public view of the API, not the internal implementation details of the persistence and domain layers.
You might have a UserProjectsController which you can perform CRUD on to add/remove project associations to users, and it will do the appropriate record manipulation without being overly bound to the DB implementation. Note the naming, where the route might be /user/:id/projects, so it's clear you are manipulating not Users or Projects, but their associations.
I think thinking about this sort of thing (both before and after the fact) is what leads to better designs.
I too start with the model and think about the application structurally. The next step in my oppinion is to build the user interface to make sense based on what makes it easy and useful for the user (spending more effort on things that matter more). So if it makes sense for the user to separately edit the ProjectsUser objects then the ProjectsUsersController is the way to go. More likely editing the join model objects as part of the Project (or User depending on the structure of you app) will be a better fit for the user. In that case using a nested form and editing via the controller (and model) that's the main model referenced by the form is better. The controller really lives to serve the UI, so decisions about it should be dependent on the UI.
Yes, if it makes your code simpler or more readable. If you use role more than once I suspect it will.
I would actually name that model something like Member, or ProjectMember (or Membership). It defines a relationship between a user and a project, so its name should reflect what relationship that is. In the occasions where such a name is too unwieldly or too hard to define then falling back to something like ProjectUser is reasonable (but not ProjectsUser). But I definitely like finding a more meaningful name when possible.

Access Attributes of a "related" model from the first

I have a rails application that has several models. One particular model is the "focus" of the application, and it has several one to many, and several many to many relationships defined.
I have created logic to export the fields to a CSV file, and within the model I have defined a couple methods someone showed me to facilitate this. Here are the two methods:
def self.csv_header
fields = attr_order.*.to_s & content_columns.*.name
fields -= %w{created_at updated_at created_on updated_on deleted_at}
fields.reject! { |f| never_show? f }
fields
end
def to_csv
self.class.csv_header.map { |h| send(h) }
end
However, in my primary model (called patient) I need to include fields from some of the other one-to-many models (e.g. home_address, which contains street, city, state, zip, etc.). Is this possible to keep inside the patient model? I have set up logic in my controller which can add the other model's information, but it seems like it would be much cleaner to let the patient model grab all the additional information it needs from the other models and add it to the export rows.
Most of your work should be done in the models anyways, in my opinion. Keep the controllers thin, and the models fat, vs the other way around.
If you need to then access some attributes - say Patient has a 1-to-1 relationship with Address, then feel free to do so! Just do something like the data something like:
fields += HomeAddress.csv_header
home_address.rb
def self.csv_header
... pretty much the same thing as Patient.csv_header
end
So, you're not keeping the data in the Patient model, but rather, you're keeping the data where it belongs and just being able to access it.

Learning Basic rails: Where to put conditional business logic?

So Im trying to teach myself rails and having some trouble with figuring out where logic goes.
In my exercise, I have a payment model.
class pyament
Integer Product_Type
String Product_name
There are rules for handling payments
if the product_Type is physical, do this, if virtual do that
if the product_name is book, do this
if the product_name is cow, do that
What I cant figure out is where to put these rules.
Do I make a method in the model called process that runs these rules? Does this go logic go in the controller? Im just not clear on this.
Any insight would be appreciated.
Thanks
You should definitely keep this logic in the model, and in fact, if the logic is significantly different between different types you should use multiple models with Single Table Inheritance.
See:
http://joemcglynn.wordpress.com/2009/12/11/rails-sti-part-1/
http://joemcglynn.wordpress.com/2009/12/12/rails-single-table-inheritance-part-2/
Basically the idea is this: You're already defining Product Type -- the 'type' column is the main feature of an STI table.
With STI instead of having one model with tons and tons of conditional logic or multiple models, you have several VERY SIMILAR models with VERY SIMILAR data but somewhat different logic, so all those related models can share the same table, and inherit from a common class.
For instance:
class Product < ActiveRecord::Base
...
common logic goes here
...
end
class PhysicalProduct < Product
...
physical-product-specific logic goes here
...
end
class VirtualProduct < Product
...
virtual-product-specific logic goes here
...
end
So, in this way you can create a method like product.deliver which is defined by default in the product model to trigger shipping a product -- but in the VirtualProduct model is overridden to trigger emailing a download link instead.
ActiveRecord handles all of this very nicely (see the linked articles above for a walkthrough), and the majority of your forms and links and controllers etc. will continue to work the same way they currently do.
In general you always want to keep as much logic as possible in the models instead of the controllers, because models are easier to test, and easier to debug. In your situation STI is a nice way to keep this branching logic in the models an out of the controllers and views.
For starters, this list is a nice jumping off point.
I agree that the data manipulation / business rules best fit in the model. Keep the views and controllers clean so your model can contain all the storage structures and logical rules.
Generally you want your business logic in the models. "Fat Model, Skinny Controller" is the cool Rails phrase used to describe this. See this post for more info, and don't pay attention to the out-dated Rails syntax.

Resources