RoR: defining a class inside a view helper file - ruby-on-rails

I have a view helper file, app/helpers/analysis_helper.rb, whose toplevel methods I've been using in various view files. Works fine. I then defined an AnalysisSummary class inside analysis_helper.rb to package up some view-specific functionality.
However, when I try to instantiate an AnalysisSummary in a view file, I get the error:
uninitialized constant ActionView::CompiledTemplates::AnalysisSummary
Perhaps Rails is telling me that I shouldn't be defining a class inside a helper file? If so, where would you suggest parking AnalysisSummary? It's not a controller, it's not a model...
Thanks.

In Railscasts #213 (Revised) (subscribers only link, alas), Ryan Bates provides an example of how (and why) you might include a class within a helper. The basic gist is this:
# app/helpers/calendar_helper.rb
module CalendarHelper
def calendar(date = Date.today)
Calendar.new(self, date).render
end
class Calendar
def render
# Calendar, render thyself
end
# ... additional methods called by #render
end
end
To those opposed to classes within helpers, what do you make of Ryan's choice? Helpers are for generating markup, right? So if all the logic within class pertains to rendering (rather complicated) HTML, I would think that what it does (as opposed to what it is) makes it appropriate for inclusion in a helper.

Why does it need to be a class? Why not just a collection of methods? That's what a helper is: a collection of helpful methods. Business logic does not belong in helpers. You can place your code in a module within the helper file if you want to give some more structure and organization, though.
You can put classes in app/models without it having to be an ActiveRecord class, but you should seriously consider what the purpose of your class is before you place it there.
If it concerns only rendering the view, and not accessing data directly, it belongs in the view or a view helper.

You can call the class by explicitely mentioning the helper name
ApplicationHelper::AnalysisSummary.new
But I dont think it is a good idea to have classes in helpers.

It's a module then :) Definately do not define classes inside helpers. Jsut use a simple module to do the job.

Related

Rails Model method that builds a string of links

I have a method on a model called Photo. I have it finding a selection of things from elsewhere in my app. All I need it to do at the end is to create a string of links that I can then output later on when the method is called on an instance.
My code is:
cars.map { |c| link_to(c.name, c) }.join(" AND ")
But i'm hitting this error:
undefined method `link_to' for #<Photo
Any ideas how to fix this?
link_to is a view helper which means it's only available in Rails views by default because it's a router / request concern.
If you specifically want to use link_to you have to include it or reference it directly.
See this SO answer
include ActionView::Helpers::UrlHelper
...
cars.map { |c| link_to(c.name, c) }.join(" AND ")
There are other ways of getting paths than using link_to that I would recommend you consider:
It's arguable that the Rails team would tell you to use UrlFor as the tip in that link suggests:
Tip: If you need to generate URLs from your models or some other place, then ActionController::UrlFor is what you're looking for. Read on for an introduction. In general, this module should not be included on its own, as it is usually included by url_helpers (as in Rails.application.routes.url_helpers).
UrlFor also allows one to access methods that have been auto-generated from named routes.
class User < ActiveRecord::Base
include Rails.application.routes.url_helpers
def base_uri
# named_route method that uses UrlFor
user_path(self)
end
end
User.find(1).base_uri # => "/users/1"
create your own concern to bring in route helpers via ActionMailer as this article suggests
As you may see if you scroll through other SO questions about including view helpers in models, there is pushback on using router and request -based methods outside of controllers and views because it violates the principles of MVC.
I think your use case can give you some peace of mind about this, but it's worth knowing the water is murky and some people may advise you otherwise.
The traditional Rails wisdom (and what I'm about to give you here) is that models should not be creating HTML. They also shouldn't have methods that return HTML. Creating HTML <a> tags should be done much closer to the user interface: in a view template or maybe in a view helper. One reason is that the particular way the hyperlink should be generated is a concern of the view. (Does it need a nofollow attribute? class attributes? This will change, even from one view to another.) And the model should not have any knowledge of these details.
When you do generate links in the views, then you have access to all the helpers such as link_to.
Instead, as I understand it, a model should be responsible for returning its own data. Maybe in your case that'd be an array of dicts of :label, :url. I.e., pure data that'd be easy to pass to link_to.
Hope that helps!

Can I use other helpers methods in application_helper.rb?

I have a question about the availability of methods defined in app/helpers.
Can I share methods in helpers, (for example, use methods defined in my_helper.rb in application_helper.rb), or are they restricted to views ?
You should be able to do that if you have this in controller or application_controller:
helper :all
But it seems a little too complex to have helpers that call out to other helpers, generally helper methods should be short simple and have little dependencies, they should do one simple thing for the view, each. Makes it harder to test I suppose if you have helper methods that call out to other helpers.
If the problem you are trying to solve with this is sufficiently complex I'd suggest trying to move it to a module in the lib directory and then include that module in the helpers that need the common functionality.

Rails helper for views and controllers?

I know there are ways to make helpers available to controllers, but that this is generally bad form. How should the following be re-worked to avoid this?
I have a Contacts helper called "fullname" that combines a contact's first and last names.
A layout partial called subheader is rendered in the application layout, and contains this code:
<section id="subheader"><%= #subheader %></section>
The controllers set the value of #subheader.
The issue is that I often want "fullname" in #subheader. But this means accessing the helper from the controller. Should this fullname method reside somewhere else?
With something like fullname I usually just define a method on the model itself.
If the fullname method did some type of HTML formatting then I would keep those parts inside a helper.
Since the title of your question might attract viewers looking for a different answer, I'm going to answer the question of "can I use rails helpers in my controllers"? There are legitimate uses for this, so here's the cleanest way to do so:
In the helpers file, wrap the code you want to access in a model like so:
module HelperModuleName
[helper code here]
end
Wrapping only the code you need to use in your controllers (as opposed to including all helpers using include ApplicationHelper) is a good idea because it reduces the possibility for method name clashes and forces you to be more deliberate about your choice to use helpers in controllers.
Now include the module in your application_controller.rb:
class ApplicationController < ActionController::Base
protect_from_forgery
include HelperModuleName
end

using helpers properly in ruby on rails

It looks like you cannot use a helper in a controller even if both of them belong to the same class. For example: XYZHelper and XYZController...
I was under the impression that if the prefix is the same "XYZ" then the method in the helper can be used in the controller and in the view, but I think this is not the case.
So how do I remove some common functionality from a controller and place it in a helper. I want to place that piece of code in a helper because other controllers may be using it. What is the best way to approach this.
Thanks,
Jai.
There are a few ways you could share some code between controllers:
Application controller: If the code in question is an action/method which ought to be in a controller, but could be used by several controllers (or all of them), then this might be a place to put it.
the 'lib' directory. just a general purpose place to put code which should be shared.
Put it in the model. This may or may not be applicable, but its worth taking a good look at the code you're trying to move and thinking about whether it is something which makes sense on a model (instead of a controller or random class/module in lib).
Follow Pete's guidelines. If you still need to expose the methods then do the following:
Add the methods to ApplicationController class and register the methods as helper methods by calling helper_method.
class ApplicationController < ActionController::Base
helper_method :foo, :bar
private
def foo
"foo"
end
def bar
"bar"
end
end

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

I want to share code not related to views between several controllers in my Rails app. Where in the directory structure should I place it?
EDIT: the code in question if something all controllers use to determine how they render the model data
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.
If it's "something all controllers use", I would place it in a base class used by all controllers. The "application_controller" comes to mind.
I have been creating modules in lib to provide code to other classes.
Here's an abbreviated example module that defines values for views that can be instantiated from different controllers.
module ControllerDefaultValues
def default_value_for_some_controller()
#controller_name = "some_controller"
end
end
To use this, simply include the module in your class:
class SearchesController
include ControllerDefaultValues
#
def search_some_controller
default_value_for_some_controller()
render(:partial => "search_results")
end
end
The main benefit of this method is it keeps your controllers directory focused on controllers and your models directory focused on logic.
Personally i think its fine, if its controller related to put it in your controllers directory, but, labelled as a module, e.g. app/controllers/what_am_i_module.rb since it is specific to the application in development.
lib seems like a place to me where i would put framework/core enhancements or monkey patches which are non-specific to the application.
If it's going to be used by all your controllers, stick it in application_controller.rb
All your controllers inherit from ApplicationController, so they'll all have access to them.

Resources