Where to put create model and association logic in Rails? - ruby-on-rails

I have a model A with some association to B via a join table AB. In the create action for the A controller I create the join model if the optional params are present for the association, otherwise only A is created without the AB association.
If I leave all the logic in the controller then the controller gets pretty fat and a pain to test and the logic cannot be re-used easily. If I try to move the logic to the model A then I just have a random method for create_with_AB or something similar. It doesn't feel right.
It feels like there should be some layer in between? I'm not sure if there is a common pattern used to handle a situation like this?

Your example does not seem very complex and can be done using nested attributes as mentioned in the comment. For more complex stuff, there is nothing provided by the framework and you have to come up with your own abstraction.
Have a look at service objects:
https://www.engineyard.com/blog/keeping-your-rails-controllers-dry-with-services
https://medium.com/selleo/essential-rubyonrails-patterns-part-1-service-objects-1af9f9573ca1
Again since these are community abstractions, there is no standard way of implementing service objects - some people use classes with call method, some use modules, etc. How you use them depends on your definition of clean code.

Related

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.

Is that a proper way to refactor ActiveRecord fat models?

If for example I've this ActiveRecord model:
app/models/order.rb
class Order < ActiveRecord::Base
# model logic
end
require "lib/someclass.rb"
lib/somelass.rb
class Order
before_save :something
# more logic here
end
Is that a good way to refactor/extract logic from model?
Or maybe use concern class, service class or something else?
Like someone told me a long time ago:
Code refactoring is not a matter of randomly moving code around.
In your example that is exactly what you are doing: moving code into another file
Why is it bad?
By moving code around like this, you are making your original class more complicated since the logic is randomly split into several other classes. Of course it looks better, less code in one file is visually better but that's all.
Prefer composition to inheritance. Using mixins like this is asking to "cleaning" a messy room by dumping the clutter into six separate junk drawers and slamming them shut. Sure, it looks cleaner at the surface, but the junk drawers actually make it harder to identify and implement the decompositions and extractions necessary to clarify the domain model.
What should I do then?
You should ask yourself:
Which code goes together and could be part of a new class / module ?
Where does it makes sense to extract code to somewhere else ?
Do I have some piece of code that is shared across my application ?
Can I extract recurrent patterns in my code base ?
Extract Service Object
I reach for Service Objects when an action meets one or more of these criteria:
The action is complex
The action reaches across multiple models
The action interacts with an external service
The action is not a core concern of the underlying model
There are multiple ways of performing the action
Extract Form Objects
When multiple model can be updated by a a single form submission, you might want to create a Form Object.
This enable to put all the form logic (name conventions, validations and so on) into one place.
Extract Query Objects
You should extract complex SQL/NoSQL queries into their own class. Each Query Object is responsible for returning a result set based on the criterias / business rules.
Extract Presenters / Decorators
Extract views logic into presenters. Your model should not deal with specific views logic. Moreover, it will enable you to use your presenter in multiple views.
More on decorators
Thanks to this blog post to help me putting these together.
Keeping the code in the same class moves logic, it doesn't extract it.
Externalizing callback declaration is misleading and potentially dangerous. Callbacks are abused enough; forcing readers to hunt down related files is cruel.
There's no general way to answer the question as asked; the "best" refactors depends on what's actually being refactoried. Lifecycle information should be obvious and precise, though.
Concerns, services, decorators, facades, etc. are good mechanisms that support refactoring. Without knowing what's being refactored it's impossible to provide meaningful advice regarding what's "best".

What's the convention in decoupling components in Rails

Using rails generate model, I created two models/tables, policeman and policewoman. They are lists of officers with many attributes (age, time in the field, case solved, etc.). If I wanted to do a computation that determines who should get the next promotion to Sargent what is the conventional way to do it in rails?
I would want to create a new class just to deal with this situation, and hide all the complex computation (compare attributes between both lists) away from the caller. So maybe in a controller, say Captain, under the method show I would do
class Captain < ApplicationController
def show
promotion = Promotion.new
#ideal_sargent = promotion.sargent(Policeman.find(:all),Policewoman.find(:all))
end
end
Where do I create this Promotion class? Do I use rails generate controller to make it? Or maybe make a gem for it? Or even put it all in a model (I hear thin controllers fat models)?
EDIT:
Maybe this? If so, how do I create a model without a migration file being automatically made?
Your general idea of decoupling it from models and from controllers is a good one. Although you confuse it a bit with controllers, generators and gems...
What you want to do is:
introduce a service object which is plain ruby object
put the logic for calculating promotion order inside it
have it completely decoupled from controller and loosely coupled to police officers models
The interface to use it would be basically as you have already described:
# prepare some officers to choose from
officers = [PoliceWoman.find(1)] + PoliceMan.all
# returns the first ranking officer for promotion
to_be_promoted = SargentPromotionService.new.find_top_candidate(*officers)
Where to put this service model? I suppose it contains application specific logic, that isn't really useful outside of application. Therefore we put it in the app folder. But where in the app?
A good practice is to setup the app/domain folder. There you can put all the app specific domain models (service, policy, value objects, etc...). All you need to setup this folder is add it to the autoload paths inside the config/application.rb:
config.autoload_paths += %W(#{config.root}/app/domain)
This way you have a clear separation of rails models (take care of persistence) and domain models - where you should put most of the application specific code. If your app is really simple and small you could also skip the app/domain folder and just use the app/models. And if you follow the approach to use plain ruby objects with loose coupling, you will get easily testable, maintainable, flexible and reusable code. =)
For this purpose, I wouldn't create two tables to modeling the data. I would use a single table named polices, and keep a column as gender and another one as rank to differ policeman and policewoman, and different rank. Then I would put promote function as a class method inside the Police modal.
class Police < ActiveRecord::Base
def self.promote(param1, param2)
....
end
end
In this way you can incapsulate the business logic inside the promote function, the caller can invoke it without knowing any complex computation in it. Police.promote(a,b)

Best practice question - Working straight with Linq to sql classes

This is possibly a bit of a stupid question, but I am getting confused due to the ASP.NET MVC book I am currently reading...
Working with Linq-To-SQL it seems to say that it is not good practice to pass the Linq-to-SQL objects straight to the controller, but that each object should be modelled separately first and this should be passed between the controller and the repository.
Say, I have a database of products. Linq-to-SQl creates a product class for me with Name, Price and Whatnotelse properties. I could pass that straight from repository to controller and then view, but instead it seems to recommend that I use and third class, say Product_Entity, with also Name, Price etc. properties and pass that to the controller.
I fail to see the benefit of this approach, except possibly for adding attributes to the properties... But apart from that it seems to have more drawbacks than benefits. Say each product has manufacturer information as well, I don't see how I can model that easily in my third class.
Is this approach really best practice? Or did I misunderstand all that? If so, why is it bad to work straight off the linq-to-sql generated objects? And how do you deal with relationships between objects in y
The huge benefit to this other class you create is that, to use your example, it doesn't necessarily map to either a product or a manufacturer. Think about it like this:
Your Linq to SQL classes are meant for talking in the "data" domain.
Your "data" classes (the ones you're having trouble with) are meant for talking in the "application" domain.
Let's take an example. Suppose in your MVC application you wanted to show a grid of information about products. You want to see their Name, Price (from the Product table) and their Country of Manufacture and Manufacturer name (from the Manufacturer table). What would you name this class? Product_Manufacturer? What if later on you wanted to add properties from yet a third table such as product discounts? Instead of thinking about these objects in purely the data domain, think about them with regard to your application.
So instead of Product_Manufacturer, what about calling it ProductSummaryItem? Each property of the ProductSummaryItem class would map 1:1 with a field shown in your grid on the UI. Your controller would perform the mapping between the information in the data domain (Product, Manufacturer) with the custom class you'd created in the application domain (ProductSummaryItem).
By doing this, you get some awesome benefits:
1) Writing your views becomes really, really simple. All you have to do to display your data is loop through the ProductSummaryItems and wrap them in and tags, and you're done. It also allows for simple aggregation. Say for example you wanted to add a field called ProductsSoldLastYear to your ProductSummaryItem class. You could do that very simply in your views because all it is to them is another property.
2) Since the view is trivial and there's mapping logic in the controller, it becomes much easier to test the controller's output because it's customized to what the view is going to see.
3) Since the ProductSummaryItem class only has the data it needs, your queries can potentially become much faster because they only need to query for the fields that would populate your ProductSummaryItem object, and nothing else. This overhead can become overbearing the more data-domain objects make up your ProductSummaryItem object.
This pattern is called Model View ViewModel (MVVM) and is hugely popular with MVC as well as in frameworks like WPF.
The argument against MVVM is that you have to somewhat reimplement simple classes for CRUD operations. Fair enough, I guess, but you can use a tool like automapper to help out with things like that. I think you'll find fairly quickly, though, that using the MVVM pattern even for CRUD pays dividends, because before you know it, even with simple classes, you'll start wishing you had extra fields which can easily drive your views.

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