Rails Model, View, Controller, and Helper: What Goes Where

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

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.

Where to place Rails code that is not a model, view, controller or helper?

If the code is something like modules with utility methods in these could be placed in the lib folder. Alternatively you could create a common superclass for some of your controllers if they share behaviour.

Please post an example of the kind of code you're thinking of.

Where should I put this, in the model, a helper, or a controller?

Calculating the average belongs in the model:

class School < ActiveRecord::Base

...

def average_review_rating
return nil if reviews.blank?
reviews.average(:rating)
end

end

Rounding the average belongs in the view, because it is formatting. Put calculation in the model, and formatting in the view (or a helper).

<%= @school.average_review_rating.round(1) if @school.average_review_rating %>

This can be shorted considerably using the andand gem.

<%= @school.average_review_rating.andand.round(1) %>

You may wish to push the rounding into the helper, where it can be independently tested:

class SchoolHelper

def format_rating(n)
n.andand.round(1)
end

end

which is used like this:

<%= format_rating(@school.average_review_rating) %>

Helper method or Model or Controller?

The general answer depends on how often you're going to use it:

  • helper: used often but only in views or controllers
  • model: used often anywhere the model can be used (other models)
  • controller: used rarely and only for specific actions.

However in your case, the answer is none of the above, and stop trying to reinvent the wheel. About 95% of the things people try to do with Rails are tasks that others have already done. There's a very good chance that it's either in the Rails Core or exist in either gem or plugin form.

What you're trying to do has already been done, and is built into the Rails core. It's a ActionView::Helpers::FormOpitionsHelper method called collection_select

collection_select does exactly what you want to do, it's also much more robust than a single purpose method.

It has the form of

collection_select(object, method, collection, value_method,
text_method, select_options = {}, html_options)

value_method and text_method are sent to each item in collection to get the select value and the display text for each select option. It is not required that either are column names.

Use it like this:

<% form_for @whatever do |form| %>
<%= form.collection_select :user_id, User.all, :id,
:full_name, :prompt => "Select a User" %>
<% end %>

Using View page, Controller and Helper

You no need to connect views and helper as by default all the helper modules are included in the views.
And do include the helper in you controller. Helper is a module and controller is a class. Just include the module in the class.

To get the clear picture please post exactly your structure.

Rails : dynamically add helper to controller and access it in view

Create your own render controller.

module Core
class RenderController < ActionController::Base
include Core::ApplicationHelper
include ::YourCustomHelper
end
end

then use it for render_to_string

::Core::RenderController.new.render_to_string('agreement_files/shared/a.html.erb', locals:  { a: @a , b: @b })

Rails - helper method in controller available in the model?

You can do something like this to access the instance variable declared in controller from model

class User < ActiveRecord::Base
cattr_accessor :current_user
end

class ApplicationController < ActionController::Base
before_filter :current_user

def current_user
@current_user ||= User.find(session[:user_id])
User.current_user = @current_user
end
end
end

Rails: Helpers and Models - where to organize code

Your specific example is including a business rule in the sense that if the instance of the model is both purchased and confirmed then the proper status is "purchased" not "confirmed"

So in your example, I'd definitely put the method in the model since it is coding one of your applications business rules.

A different example:

def status_string
case status
when 0: "Purchased"
when 1: "Confirmed"
else
"Pending"
end
end

In this case, the status_string method could be reasonably defined either in a View Helper or Model--it has nothing to do with any business rules, it is changing the representation of the value. I'd put it in the model since I tend to only put html-related sw into the View Helpers. But depending on your internationalization scheme, a similar method might be better placed in the View Helper.

A good example of a View Helper is an application-wide method to transform date time values into the standard representation for your app. Eg

# application_helper.rb
def date_long_s(d)
d.strftime("%A, %b *%d, %Y *%I:%M %p")
end

Get data from Helper or use Model in Rails

I would go with (1) your helper method for now. It's simple and straightforward. As I said in the comments, you could use a decorator around your model (using draper for e.g.) to add what I'd consider quite view-specific logic if you are tempted towards option (2).

One note on your helper methods - use pluck instead of collect so you don't select columns or instantiate a bunch of objects you don't need.

Also, order defaults to asc, so you can shorten the whole thing to:

def users_for_select
User.order(:email).pluck(:email, :id)
end


Related Topics



Leave a reply



Submit