155

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.

Theo
  • 131,503
  • 21
  • 160
  • 205
theschmitzer
  • 12,190
  • 11
  • 41
  • 49

10 Answers10

173

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.

James A. Rosen
  • 64,193
  • 61
  • 179
  • 261
pauliephonic
  • 2,127
  • 1
  • 13
  • 9
  • 2
    People are starting to move away from fat model. I like to think of my model as a data structure. Then I write some Ruby object that implements the behaviour, initializing it with the model (it treats the model as it's data in the same way you might treat strings and arrays as data in objects outside of Rails). [Here's](http://arrrrcamp.be/videos/2011/corey-haines---fast-rails-tests/) a good video with an example of this technique. – Joshua Cheek Dec 15 '11 at 11:13
  • @AdamDonahue I'm not sure fat anything can be seen as a good thing. Tonnes of responsibilities are better off belonging in services. – fatuhoku Dec 08 '16 at 11:30
38

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.

Karol Selak
  • 4,248
  • 6
  • 35
  • 65
jcoby
  • 4,210
  • 2
  • 29
  • 25
  • Actually do we not also put sign_in method in Helper? As RoR Tutorial suggested here >>>http://ruby.railstutorial.org/book/ruby-on-rails-tutorial#sec-a_working_sign_in_method – Ivan Wang Nov 15 '13 at 15:20
14

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.

Søren Spelling Lund
  • 1,622
  • 1
  • 13
  • 18
12

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

maddin2code
  • 1,334
  • 1
  • 14
  • 16
7

The Rails way is to have skinny controllers and fat models.

John Topley
  • 113,588
  • 46
  • 195
  • 237
7

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.

srboisvert
  • 12,679
  • 15
  • 63
  • 87
4

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.

James A. Rosen
  • 64,193
  • 61
  • 179
  • 261
  • uhmmm... Yuk. Can you please add some good reasons/scenarios for when you would do this. This breaks KISS and YAGNI and is very smelly (just to toss in one more cliche) – Sixty4Bit Oct 02 '08 at 02:52
  • 2
    1) Rails does a lot of magic to copy the controller's instance variables to your view instance. 2) The suggested implementation also only loads foo if it's accessed, which may save some work some of the time. The important answer really is 1), though. – webmat Oct 04 '08 at 18:53
  • 11
    *Sigh* This is terrible. Rails instance variable sharing is a feature not an anti-pattern. It's an ubiquitous, low-mental-overhead syntactic sugar that rarely if ever causes real-world problems. If you don't like it, fine, but coding around it with a baroque non-standard structure makes things infinitely worse. In this case you are effectively making foo a global (per controller anyway) variable. Attempting to correct a perceived abuse of variable scoping by dramatically increasing the scope is extremely ironic. – gtd Jan 15 '10 at 06:05
  • 1
    I'm not buying it, dasil003. The scope of `foo` and of `@foo` are the same -- they are both scoped to the pair. Additionally, by using the getter version, I can change how that `Foo` object is found/stored/cached without changing how the view accesses it. – James A. Rosen Jan 15 '10 at 13:33
  • 1
    I think you mean "pass instance variables" anti-pattern. An instance var will leak state for the entire render, even in deeply nested partials. Your solution also leaks state, but is slightly better than an instance var because it doesn't allow reassignment. Passing a local is actually the best because it's like calling a method; the local can't be seen by partials. See [this answer](http://stackoverflow.com/a/16536162/498594). – Kelvin Sep 18 '13 at 18:44
  • The code in this answer doesn't have any local variables, neither in the "bad" nor "good" version. What do you mean by local variables? – Andrew Grimm May 31 '15 at 03:39
  • When you have `foo` as a helper method in view, you can only read from it, while if you'd use `@foo` - you can read/write... so it looks like using `foo` as a helper method is safer. – maicher Feb 03 '17 at 15:47
  • In MVC, controllers are orchestrators of views. They share the same scope for a reason. This solution proposes adding a game of telephone to every view just to prevent accidental variable reassignment within views. I've been coding Rails apps for 10 years and I've never accidentally done that. The Rails convention is to assign instance variables in controllers, then let views decide how to use them. This gives me the flexibility to use instance vars directly in partials or to pass them down to shared partials as locals, improving partial code reuse. Why bloat your controller for pedantry? – armchairdj Apr 16 '18 at 15:14
2

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.

Paul Wicks
  • 62,960
  • 55
  • 119
  • 146
0

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.

Anutosh
  • 29
  • 2
-1

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