Extending model to include "helper" methods - ruby-on-rails

I have a kind of social network website.
I have a logic to create the path for the user, and select an avatar for each user described in UsersHelper methods user_path(user) and user_avatar(user).
Instead I want to have methods like user.path and user.avatar, but I don't want to have that code inside the model file.
I tried extending the User class inside the helper:
module UsersHelper
class User
def avatar
...
end
end
end
That doesn't work - the method I added aren't recognized (I'm guessing Rails dynamically generates the ActiveRecord class on demand so my methods don't apply?)
I'd really appreciate ideas

First, there's a reason helpers are separated from models. According to the MVC pattern your model shouldn't know anything about your helpers, and not vice versa either (the latter is usually ignored in Rails, though).
But, if you want to do this, you need to do class ::User in model UsersHelper, or the following:
module UsersHelper
end
class User
end
The reason for this is that Ruby namespaces classes. So you defined UsersHelper::User, while your model is called User. Calling the class ::User or defining it outside the module forces it into the root namespace.
However, as I said, this is not the "correct" way to do it. A better way would be how you're already doing it, or maybe using a decorator pattern.

Draper is an awesome little gem that does an excellent job of achieving the functionality you're looking for (adding view / presentation specific code while still making it "feel" like the model you're working with). We've removed almost all of our model-specific helpers after starting to use draper.
Basically, it works by defining decorators, which work like a wrapper around your model. A decorator looks and feels like the model it's decorating, but can have additional functionality defined on top of it. In addition, you can restrict access to certain fields and lock stuff down if you like.
For your example, adding a decorator would be as simple as:
(in app/decorators/user_decorator.rb)
class UserDecorator < ApplicationDecorator
decorates :user
def avatar
# your implementation
end
(in your controller)
def index
respond_with UserDecorator.decorate(User.all)
end
(in your views)
<div class='avatar'><%= user.avatar %></div>
<div class='name'><%= user.name %></div>

Helpers are intended to use with the views, not with the models.
If you wish to have something like user.avatar, you have to add it to your model.
If you want to stick it in the helpers, then in the UsersHelper add a user_avatar method.

Related

Helper methods for models in Rails

Is there a proper place for helper methods for models in Rails? There are helper methods for controllers and views, but I'm not sure where the best place to put model helper methods. Aside from adding a method to ActiveRecord::Base, which I'd prefer not to.
UPDATE: It seems Concerns make a lot of sense. Here's an example of what I want. Certain models can never be deleted, so I add a callback that always throws an exception:
before_destroy :nope
def nope
raise 'Deleting not allowed'
end
With concerns, I could do something like this?
class MyModel < ActiveRecord::Base
include Undeletable
end
module Undeletable
extend ActiveSupport::Concern
included do
before_destroy :nope
end
def nope
raise 'Deleting not allowed'
end
end
Is this the Rails way of doing this?
If you want to use a helper_method my_helper_method inside a model, you can write
ApplicationController.helpers.my_helper_method
If you need a bit more flexibility, for example if you also need to override some methods, you can do this:
class HelperProxy < ActionView::Base
include ApplicationController.master_helper_module
def current_user
#let helpers act like we're a guest
nil
end
def self.instance
#instance ||= new
end
end
and then use with
HelperProxy.instance.my_helper_method
If you have strong nerves, you can also try to include the ApplicationController.master_helper_module directly into your model.
via : makandracards's post.
For your reference: http://railscasts.com/episodes/132-helpers-outside-views
If what you are asking is where to put code that is shared across multiple models in rails 4.2, then the standard answer has to be to use Concerns: How to use concerns in Rails 4
However, there are some good arguments (e.g. this) to just using standard rails module includes, and extends as marek-lipka suggests.
I would strongly recommend NOT using ApplicationController helper methods in a model, as you'll be importing a lot unnecessary baggage along with it. Doing so is usually a bad smell in my opinion, as it means you are not separating the MVC elements, and there is too much interdependency in your app.
If you need to modify a model object by adding a method that is just used within a view, then have a look at decorators. For example https://github.com/drapergem/draper

How can I decorate STI models with modularized decorators using Draper?

I'm using Draper for general view-layer decorators.
I have some console-related, human-readability functionality I'd like to pull into new decorators.
My first thought was to put them in a module, e.g., Human::XxxDecorator, keeping them isolated from my normal view-layer decorators: they're just for rails c debugging/hacking/testing work.
This works fine at the top level; I can explicitly decorate a model with the namespaced decorator.
Now I need to decorate a collection of STI vehicles. I'm not sure what the best way to create vehicle-type-specific decorators in the same module of decorators, e.g., I have:
Human::CarDecorator
Human::TruckDecorator
Human::MotorcycleDecorator
I'm not sure how to get from, e.g.,
pry ยป dfleet = Human::FleetDecorator.new(Fleet.find(1))
to its embedded collections of vehicles, each with an appropriate decorator from the Human module. The naive approach using decorates doesn't work; I get:
Draper::UninferrableDecoratorError: Could not infer a decorator for Vehicle
The combination of:
Decorators from a specific module, and
Appropriate decorators for the STI models
is throwing things off.
Before digging into the Draper decorator inference code (I'm only assuming that's the best place to start), is this a problem that's already been solved and I'm missing something?
As I wrote in comments, remove the builtin decoration of your vehicles, and code yours:
def vehicles
object.vehicles.map do |v|
# logic to instantiate proper decorator
end
end
Hack incoming:
module Human
class FleetDecorator < Draper::Decorator
decorates_association :vehicles, with: ::Human::VehicleDecoratorDispatcher
end
class VehicleDecoratorDispatcher < Draper::Decorator
def initialize(*args)
super
#object = ... # here you build the proper decorator based on the rules on #object
end
end
end
But I doubt this is clearer...
You could use constantize:
def dfleet
dfleet_decorator_class.new(object.dfleet)
end
def dfleet_decorator_class
"#{object.dfleet.class}Decorator".constantize
end
Use decorates: . Here is an example: CLICK

Putting presentation logic in controller is a good practice in Ruby?

Some recommendation [1] suggest you to use
<%= current_user.welcome_message %>
instead of
<% if current_user.admin? %>
<%= current_user.admin_welcome_message %>
<% else %>
<%= current_user.user_welcome_message %>
<% end %>
But the problem is you must have the decision logic somewhere in your code.
My understanding is putting the decision in template is better than controller as it make your controller more clean. Is it correct?
Are there better way to handle this?
http://robots.thoughtbot.com/post/27572137956/tell-dont-ask
You are not the first to wonder this. If views and controllers should have little to no logic, and the model should be presentation agnostic, where does presentation logic belong?
Turns out we can use an old technique called the decorator pattern. The idea is to wrap your model object with another class that contains your presentation logic. This wrapper class is called the decorator. The decorator abstracts away logic from your view, while keeping your models isolated from their presentation.
Draper is an excellent gem that helps define decorators.
The sample code you gave could be abstracted like so:
Pass a decorator to the view with #user = UserDecorator.new current_user in your controller.
Your decorator could look as below.
class UserDecorator
decorates :user
def welcome_message
if user.admin?
"Welcome back, boss"
else
"Welcome, #{user.first_name}"
end
end
end
And your view would simply contain #user.welcome_message
Notice that the model itself doesn't contain the logic to create the messages. Instead, the decorator wraps the model and translates model data into a presentable form.
Hope this helps!
I would use a helper for this. Suppose you have to translate the welcome-message, based on some locale.
In the app/helper/user_helper.rb write
module UserHelper
def welcome_message(user)
if user.admin?
I18n.t("admin_welcome_message", :name => user.name)
else
I18n.t("user_welcome_message", :name => user.name)
end
end
end
and in your view you can then just write
<%= welcome_message(user) %>
Note that the decorator/presenter offers a really clean object-oriented approach, but imho using a helper is much simpler and sufficient.
No, you don't want any conditionals at all in the user class nor the controller. The point of that example on the blog post is to make reference to polymorphism, just good old fashioned OO design.
# in application_controller for example
def current_user
if signed_in?
User.find(session[:user_id])
else
Guest.new
end
end
#app/models/user.rb
class User
def welcome_message
"hello #{name}"
end
end
#app/models/guest.rb
class Guest
def welcome_message
"welcome newcomer"
end
end
...you get the idea.
Only, instead of littering your model with presentation-only methods, create a decorator that acts as a presenter:
require 'delegate'
class UserPresenter < SimpleDelegator
def welcome_message
"hello #{name}"
end
end
And now current_user looks like so:
# application_controller
def current_user
if signed_in?
UserPresenter.new(User.find(session[:user_id]))
else
Guest.new
end
end
Decorate the user model and add the welcome_message to it directly. Yes, this may involve some kind of conditional statement at some point.
http://robots.thoughtbot.com/post/14825364877/evaluating-alternative-decorator-implementations-in
In my opinion, if the text is the only thing that changes, it doesn't belong in a view. If you needed to restructure the page, that's presentation logic. This, this is just data being different.
I think you should watch the railscasts episode on Presenters for the answer.
Logic in the view is hard to maintain, we should put the business logic in the model and all view logic in helpers.
If you want your code to be in Object Oriented fashion, make use of Decorators (object oriented way of helpers)
Best Example : https://github.com/jcasimir/draper
Put the code defining current_user.welcome_message in _app/helpers/application_helper.rb_, it will then be accessible by any view rendered with the application layout.
Another option is to define a custom helper module, one which is not necessarily associated with a given view or controller (See the video I linked below), and include it in the modules of the view/controllers you wish to have that functionality in.
This is not something that is black and white. But, from what you have described it sounds like this is code that is obtrusive to stick in your application_controller.rb and it is not code with functionality which justifies it's own controller, the most effective and efficient option may be to create a custom helper module and include it in the helpers you wish to have that functionality. That said, this is ultimately a judgement call which the designer of the application (i.e. you) needs to decide upon.
Here is a good article outlining helper modules from May, 2011
Here is is a RailsCast outlining custom helper modules (i.e. custom as in modules not necessarily associated with a given controller or view). Short, sweet, and to the point.
You can define helper method for that stuff. I don't think it is a good Idea to make a welcome sentences in a model, but in the controller too. But you should try to make you views clean from code, and if you can use helpers for that then you should to.
A good practice would be to have real View instances. Rails parody of MVP (there is difference, look it up) unfortunately seems to pretend that views are templates. That is wrong.
Views are supposed to contain the presentation logic in MVC and MVC-inspired patterns. They are also supposed to manipulate multiple templates and make decision on which templates to employ to represent the state and information from the model layer (yes, model is a layer not an ORM instance).
So, to answer the question: presentation logic has no place in controllers.

Multiple Rails Helpers w/ Same Method Name

I have two different helper files (photos_helper & comments_helper) w/ that have a method named actions_for. How can I explicitly call which helper method that I need? I know that I could just rename one of them but I would prefer to keep them the same. I tried PhotosHelper::actions_for but that doesn't seem to work.
In Rails 3 all helpers are always (in Rails 3.1 a patch exists to selectively allow helpers again) included. What's happening behind the scenes:
class YourView
include ApplicationHelper
include UserHelper
include ProjectHelper
...
end
So depending on the order Rails includes them, any of your actions_for methods will be used. There is no way you can explicitly chose one of them.
If you would have to explicitly call ProjectHelper.action_for, you could also name your methods project_action_for - simplest solution.
Make both of them a Class Method
module LoginsHelper
def self.your_method_name
"LoginsHelper"
end
end
AND
module UsersHelper
def self.your_method_name
"UsersHelper"
end
end
Then in View
LoginsHelper.your_method_name #Gives 'LoginsHelper'
AND
UsersHelper.your_method_name #Gives 'UsersHelper'

Separate rails helper methods

I have a couple of controllers with associated helper modules. I got some helper methods that should behave similarly across different modules, meaning:
module UserHelper
..
def destroy(user)
link_to t(:destroy_user), user ...
end
end
module PhotosHelper
..
def destroy(photo)
link_to t(:destroy_photo), photo ...
end
end
I didn't know (realize) that all of these helper modules are included by default, (which is ok, I guess,) and it doesn't matter what view you're calling the helper method from.
What is the best way to separate the rest of the helpers from my current controller/view so that, when controller_name == 'photos', only Photos and Application helpers are used?
The concept of Helpers is not really clear to me. Why not just have a single ApplicationController if all helpers are already mixed in? Is it just for "logical separation"?
I mean, of course, there's a number of workarounds. But is it just me, or it really feels like there's no reason to include all of the helpers globally?
If you called clear_helpers at ApplicationController class, they won't share among different helper classes
clear_helpers()
Clears up all existing helpers in this class, only keeping the helper with the same name as this class.
ref: http://api.rubyonrails.org/classes/AbstractController/Helpers/ClassMethods.html#method-i-clear_helpers
You could put the common methods on the ApplicationHelper and pass the resource as a parameter.
So then use the specific helper resource for specific methods only.
Methods (helpers) that you can share anyway, goes to ApplicationHelper file.

Resources