Can two or more inter-related models have one common controller in rails - ruby-on-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.

Related

Rails STI: transfer records from one model to another

I have a model called Coupons
Then I have two child models CouponApplicationsand ApprovedCoupons.
The last two inherit from Couponsvia an STI architecture.
Now I want to realise the following:
A user sees CouponApplications
He clicks on an Approve button which makes the CouponApplications ApprovedCoupons
I realise that I could simply update the typecolumn of the Couponsrecord to change types. However, there are several Concerns, hooks etc in the ApprovedCoupons model which are happening after creation so this is not so easy. In fact, I want to create a complete new record to trigger those Concerns, hooks etc.
So I wrote this which I consider really bad:
#coupon_application = CouponApplication.find(params[:id])
#approved_coupon = ApprovedCoupon.new
# copy/paste attributes except the ID as this would be considered a duplication
#approved_coupon.attributes = #coupon_application.attributes.except("id")
# set the new type
#approved_coupon.update_attributes(type: "Advertisement")
#approved_coupon.save
I hope you understand what I want to achieve. It works this way but I doubt this is clean code.
To summarize:
I want to change the Coupontype from CouponApplication to
ApprovedCoupon
I still want to trigger the Concerns, hooks etc. in my ApprovedCoupon model so I decided to create a new ApprovedCoupon
record instead of just changing the type.
Is there any better approach?
You could add an approve method to your CouponApplication model like this:
class CouponApplication < Coupon
...
def approve
data = attributes.except('id', 'created_at', 'updated_at')
ApprovedCoupon.create(data.merge(type: 'Advertisement'))
end
end
Now your code can be simplified to this:
#coupon_application = CouponApplication.find(params[:id])
#approved_coupon = #coupon_application.approve

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)

Ruby: Variables scope questions

Hey Rails newbie here.
I used to have a lot of stuff going on in one of my controllers. Someone told me that its good practice to have "fat models and thin controllers" So I was moving some things over to the model.
In my controller's show method I used to have some # variables that I would use in my view. Now I have those variables in a method in my model. Will I still be able to access those in my view? If so do I have to make any adjustments?
Thanks
You will have to create an instance of your model in the controller as an # variable. You can then call the methods from that inside the view.
e.g. imagine you used to have some long bunch of logic in your controller which calculated a credit score for a customer culminating in
#credit_score = credit_score
and you've now moved this into a credit_score method on the Customer model.
You now just need
#customer = Customer.find...
in the controller
and you can the use <%= #customer.credit_score %> within the view.
This is what people mean by fat models and thin controllers. If you'd like some more advice then it's best to update the question with some specifics from your app.
The common practice is to define the variables of this kind in controllers:
#object = Model.new
to later use it in form_for or something like that. Some people use Model.new directly in views instead. That's somewhat unusual but still makes sense, especially knowing that Rails just loops through all of the instance variables in your controller every time to make them available in 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.

Rails Model, View, Controller, and Helper: what goes where?

In Ruby on Rails Development (or MVC in general), what quick rule should I follow as to where to put logic.
Please answer in the affirmative - With Do put this here, rather than Don't put that there.
MVC
Controller: Put code here that has to do with working out what a user wants, and deciding what to give them, working out whether they are logged in, whether they should see certain data, etc. In the end, the controller looks at requests and works out what data (Models) to show and what Views to render. If you are in doubt about whether code should go in the controller, then it probably shouldn't. Keep your controllers skinny.
View: The view should only contain the minimum code to display your data (Model), it shouldn't do lots of processing or calculating, it should be displaying data calculated (or summarized) by the Model, or generated from the Controller. If your View really needs to do processing that can't be done by the Model or Controller, put the code in a Helper. Lots of Ruby code in a View makes the pages markup hard to read.
Model: Your model should be where all your code that relates to your data (the entities that make up your site e.g. Users, Post, Accounts, Friends etc.) lives. If code needs to save, update or summarise data related to your entities, put it here. It will be re-usable across your Views and Controllers.
To add to pauliephonic's answer:
Helper: functions to make creating the view easier. For example, if you're always iterating over a list of widgets to display their price, put it into a helper (along with a partial for the actual display). Or if you have a piece of RJS that you don't want cluttering up the view, put it into a helper.
The MVC pattern is really only concerned with UI and nothing else. You shouldn't put any complex business logic in the controller as it controls the view but not the logic. The Controller should concern itself with selecting the proper view and delegate more complex stuff to the domain model (Model) or the business layer.
Domain Driven Design has a concept of Services which is a place you stick logic which needs to orchestrate a number of various types of objects which generally means logic which doesn't naturally belong on a Model class.
I generally think of the Service layer as the API of my applications. My Services layers usually map pretty closely to the requirements of the application I'm creating thus the Service layer acts as a simplification of the more complex interactions found in the lower levels of my app, i.e. you could accomplish the same goal bypassing the Service layers but you'd have to pull a lot more levers to make it work.
Note that I'm not talking about Rails here I'm talking about a general architectural style which addresses your particular problem.
Perfect explanations here already, one very simple sentence as conclusion and easy to remember:
We need SMART Models, THIN Controllers, and DUMB Views.
http://c2.com/cgi/wiki?ModelViewController
The Rails way is to have skinny controllers and fat models.
Do put stuff related to authorization/access control in the controller.
Models are all about your data. Validation, Relationships, CRUD, Business Logic
Views are about showing your data. Display and getting input only.
Controllers are about controlling what data goes from your model to your view (and which view) and from your view to your model. Controllers can also exist without models.
I like to think of the controller as a security guard/receptionist who directs you the customer(request) to the appropriate counter where you ask a teller (view) a question. The teller (view) then goes and gets the answer from a manager (model), who you never see. You the request then go back to the security guard/receptionist (controller) and wait until you are directed to go another teller (view) who tells you the answer the manager (model) told them in response to the other teller's (view) question.
Likewise if you want to tell the teller (view) something then largely the same thing happens except the second teller will tell you whether the manager accepted your information. It is also possible that the security guard/receptionist (controller) may have told you to take a hike since you were not authorized to tell the manager that information.
So to extend the metaphor, in my stereotyped and unrealistic world, tellers (views) are pretty but empty-headed and often believe anything you tell them, security guard/receptionists are minimally polite but are not very knowledgeable but they know where people should and shouldn't go and managers are really ugly and mean but know everything and can tell what is true and what isn't.
One thing that helps separate properly is avoiding the "pass local variables from controller to view" anti-pattern. Instead of this:
# app/controllers/foos_controller.rb:
class FoosController < ApplicationController
def show
#foo = Foo.find(...)
end
end
#app/views/foos/show.html.erb:
...
<%= #foo.bar %>
...
Try moving it to a getter that is available as a helper method:
# app/controllers/foos_controller.rb:
class FoosController < ApplicationController
helper_method :foo
def show
end
protected
def foo
#foo ||= Foo.find(...)
end
end
#app/views/foos/show.html.erb:
...
<%= foo.bar %>
...
This makes it easier to modify what gets put in "#foo" and how it is used. It increases separation between controller and view without making them any more complicated.
Well, it sort of depends upon what the logic has to deal with...
Often, it makes sense to push more things into your models, leaving controllers small. This ensures that this logic can easily be used from anywhere you need to access the data that your model represents. Views should contain almost no logic. So really, in general, you should strive to make it so that you Don't Repeat Yourself.
Also, a quick bit of google reveals a few more concrete examples of what goes where.
Model: validation requirements, data relationships, create methods, update methods, destroy methods, find methods (note that you should have not only the generic versions of these methods, but if there is something you are doing a lot, like finding people with red hair by last name, then you should extract that logic so that all you have to do is call the find_redH_by_name("smith") or something like that)
View: This should be all about formatting of data, not the processing of data.
Controller: This is where data processing goes. From the internet: "The controller’s purpose is to respond to the action requested by the user, take any parameters the user has set, process the data, interact with the model, and then pass the requested data, in final form, off to the view."
Hope that helps.
In simple terms, generally,
Models will have all the codes related to table(s), their simple or complex relationships (think them as sql queries involving multiple tables), manipulation of the data/variables to arrive at a result using the business logic.
Controllers will have code/pointers towards the relevant models for the job requested.
Views will accept the user input/interaction and display the resultant response.
Any major deviation from these will put unwanted strain on that part and the overall application performance may get impacted.
Testing, Testing ...
Put as much logic as possible in the model and then you will be able to test it properly. Unit tests test the data and the way it is formed by testing the model, and functional tests test the way it is routed or controlled by testing the controllers, so it follows that you can't test the integrity of the data unless it is in the model.
j

Resources