Ruby On Rails Helpers -- Using instance variables in helpers - ruby-on-rails

I have a helper for a controller:
module CourseStepsHelper
def current_quiz_result
#course_step.step.step_quiz.quiz_attempts.where(:patient_id => current_user.id, :step_quiz_id => #course_step.step.step_quiz.id).first
end
end
It has access to #course_step which is defined in the CourseSteps controller show "action". Is this common practice to use instance variables in helpers?

Depending on the level of detail for this quiz result you may actually want to use a partial. In which case the syntax would be:
<%= render :partial => 'quiz/results', :locals => { :quiz => #quiz } %>
If it's relatively simple and you think it should be in a helper you should make simply make quiz a parameter. Requiring views to provide a specific instance variable to use your helper would likely be frowned upon by other developers.
def quiz_result(quiz) # no need to call it "current" when we supply quiz
# do some stuff
end
It also looks to me that you may want to restructure your models in some way. As you can see I implemented my examples with a Quiz class. I'm not sure what your data model looks like but when you are calling properties that are nested that deep it's generally a sign that something is wrong.

I haven't seen a good argument presented for either case, and stumbled onto this question when I was searching for an answer. Personally, I've been using the instance variables in helper methods where it's possible as this is the dryest approach across both helper and view. Rather than passing the instance variable from my view and defining my helper method to accept it, I can just use it directly in the helper. Slightly less typing, anyway...

Related

How to make a model aware of its controller in Rails?

I am making a Rails application, and i would like to be able use a model object passed to a view to get the URL of some action on this object, like this, for example:
link_to object.public_send(attribute),
{ :controller => object.controller_path,
:action => :show,
:id => object.id }
What would be a good way to do this? Can it be done with a decorator like Draper? Are there some examples online?
Update. I have thought about this and decided that a decorator is not a good place to keep controller information. It is not decorator's responsibility. A decorator should only know to render formatted data with markup. For now i have created a module called Accessor where i try to mix models with controller and routing awareness. I still wonder if there is a better way to do.
If you don't mind having another instance variable on your view, you can implement this using a very simple class (no need for decorators).
class MyRouter
def initialize(controller, object)
#controller = controller
#object = object
end
def url_for(action_name)
controller.url_for(object, :action => action_name)
end
end
On your controllers:
class AController
def edit
#router = MyRouter.new(self, object)
render 'shared_view'
end
end
class BController
def edit
#router = MyRouter.new(self, object)
render 'shared_view'
end
end
And on your shared view:
<%= #router.url_for(:show) # Varies with the controller that rendered the view %>
Of course, this assumes that the controller you want as target is the same controller that renders the view, which might not be true. Still, using this pattern you can accommodate a more complex logic that suits your needs (having multiple Router classes, for instance), without having to change the view.
I've found a very interesting solution in Objects on Rails by Avdi Grimm: Exhibits for REST. In short, his idea is to apply multiple Ruby's SimpleDelegators as decorators with various functions.

Where do I put this helper function, in the model or post_helper or?

All my controllers inherit from applicatin_controller.rb, and I added:
helper :all
I want to use this function in my view to make url's like:
/post/some-title
instead of using the ID int he url
def post_path_for(post)
post_path(:id => post.title_parameterize)
end
This is rails 3.
Can't you use "to_param" in your model to change that without having to write a helper?
http://apidock.com/rails/ActiveRecord/Base/to_param
It depends on how you want to use it.
If it's in helper, you could call <%= post_path_for post %> in your view.
If it's in model, with small change you could call it like this: <%= post.path %>
Although second way is shorter, I usually put such functions in helpers, for the sake of separation of logic and presentation.
I agree with Nikita. It sounds like a helper to me. I use helpers for anything that is meant for display. It sounds like you want this available to all views. If that is the case, I would place it in helpers/application_helper.rb
+1 for Robin's to_param method. By using to_param, you could use the built-in url helpers (like link_to, url_for, ...natively)
For the other point, you mentioned you put it in controller and wanted to use it in view. You need the following line:
helper_method :post_path_for

Rails - Abstract/Shared Views

I have a few objects that are incredibly similar. In fact, they use STI and store all of the data in the same table in the DB. Currently, every time I add a feature or fix a bug, I have to update it in 3 different places, so I would like to DRY my code a little bit. The most common code duplication is in the views. I have seen people using render :template => 'shared/something' to render their common views, the problem is, I have many of these, but only for one particular object (Shipments), so I would prefer something like render :template => 'shipments/shared/something' or render :template => 'abstract_shipments/something'. More importantly though, I would like any of the actual classes to be able to override the template if it needs to.
Do y'all have any suggestions on how to go about this? Thanks very much for any answers!
The inherit_views plugin might do the job: http://github.com/ianwhite/inherit_views/tree/master. I've used it successfully before.
You could then have a common base controller class for all controllers needing to render shipments and have any common templates in that base controller's views folder with any specific overrides in the individual controllers' views folders.
Classes (models at least) don't - or shouldn't - know anything about how they're displayed. That's fundamental to the separation of concerns provided by the MVC pattern.
I think I'd try something like this:
Use a single controller for all the STI models, ShipmentsController or something similar. Apart from it seeming a logical thing to do, all your views will end up in the same place, which ought to help.
As for views, how about a "main" one for the common parts, and one partial for each set of subclass-specific fields? Use a case in your view, or probably more cleanly, a helper method, something like
def partial_name_for_subclass(obj)
if obj.is_a?(CrudeOilShipment) # or kind_of? or test the type column or use respond_to?
'crude_oil_shipment'
# etc
end
end
DRYer, consider using a convention (Rails loves conventions):
<%= render :partial => #shipment.class.name.downcase, :locals => { :item => #shipment } %>
where you have a partial for each subclass.
Hope some of that helps/makes sense...

How much code in a rails view is ok?

I know that it is best to keep code out of the presentation layer. But, I am wondering how much is considered "acceptable". For example I populate an html select box with this line of code.
CodesecureProject.find(:all,:order => 'name').collect {|project| [project.name, project.id] }
Right now I have this line of code embedded in the form. What I am wondering if the community thinks if this is to much code and it should be first stored in an instance variable on the controller then the variable used in the form.
I'm not going to say I'd never do it (I'd be lying) but the code example given would make me nervous. I think I'd be more inclined to deliver the data to the select box from my controller. A helper method is another option if I notice I'm doing something more than once. I'm more likely to see the duplication in the controller than across distinct views.
If I'm using the same HTML component across multiple views, then I might find myself reaching for partials or wrapping the whole thing in a custom helper: project_select() or some such.
The more I work in the MVC world the more I find myself avoiding code in views. I have a feeling that some kind of Zen mastery will be achieved if I reach the zero code state, although the value of that in anything but philosophical terms is highly debatable.
I use the following static method in a Site model to achieve something similar:
class Site
def self.select_options
Site.find(:all, :order => 'UPPER(name)').collect {|s| [s.name, s.id]}
end
def
Then in my Domain view I call:
<%= f.select :site_id, Site.select_options %>
This works really well for these circumstances.
In your instance, you might try:
class CodesecureProject
def self.select_options
CodesecureProject.find(:all, :order => 'name').collect {|p| [p.name, p.id]}
end
end
And then call it through the view with:
<%= f.select :codesecure_project_id, CodesecureProject.select_options %>
I have a lot of the same code in my projects except I try to don't do any finds. In your case I would make an named scope
named_scope :order, lambda { |order| {:order => order}}
and make the code:
CodesecureProject.order(:name).collect {|project| [project.name, project.id] }
It's a little cleaner.
If you got a lot of select boxes which need a name and an id (I sure do sometimes), you could also try making a helper that excepts a ModelName and returns the array you need.
def magic_for_select(model)
model.all.collect{|instance| [instance.name, instance.id]}
end
I would go a bit further than Maran. Generally I do the following:
Create a named_scope in the model to execute the find.
Call the named_scope from the controller and store the results in an instance variable.
Only put the instance variable in the view.
I would only use a helper if absolutely necessary. When going back over your code later, it's easier to make sense of things if you see your controller setting up the data that the view needs, rather than the view calling the helper (yet another file to look at).

Can Rails Routing Helpers (i.e. mymodel_path(model)) be Used in Models?

Say I have a Rails Model called Thing. Thing has a url attribute that can optionally be set to a URL somewhere on the Internet. In view code, I need logic that does the following:
<% if thing.url.blank? %>
<%= link_to('Text', thing_path(thing)) %>
<% else %>
<%= link_to('Text', thing.url) %>
<% end %>
This conditional logic in the view is ugly. Of course, I could build a helper function, which would change the view to this:
<%= thing_link('Text', thing) %>
That solves the verbosity problem, but I would really prefer having the functionality in the model itself. In which case, the view code would be:
<%= link_to('Text', thing.link) %>
This, obviously, would require a link method on the model. Here's what it would need to contain:
def link
(self.url.blank?) ? thing_path(self) : self.url
end
To the point of the question, thing_path() is an undefined method inside Model code. I'm assuming it's possible to "pull in" some helper methods into the model, but how? And is there a real reason that routing only operates at the controller and view layers of the app? I can think of lots of cases where model code may need to deal with URLs (integrating with external systems, etc).
In Rails 3 and higher:
Rails.application.routes.url_helpers
e.g.
Rails.application.routes.url_helpers.posts_path
Rails.application.routes.url_helpers.posts_url(:host => "example.com")
I've found the answer regarding how to do this myself. Inside the model code, just put:
For Rails <= 2:
include ActionController::UrlWriter
For Rails 3:
include Rails.application.routes.url_helpers
This magically makes thing_path(self) return the URL for the current thing, or other_model_path(self.association_to_other_model) return some other URL.
You may also find the following approach cleaner than including every method:
class Thing
delegate :url_helpers, to: 'Rails.application.routes'
def url
url_helpers.thing_path(self)
end
end
Any logic having to do with what is displayed in the view should be delegated to a helper method, as methods in the model are strictly for handling data.
Here is what you could do:
# In the helper...
def link_to_thing(text, thing)
(thing.url?) ? link_to(text, thing_path(thing)) : link_to(text, thing.url)
end
# In the view...
<%= link_to_thing("text", #thing) %>
I really like following clean solution.
class Router
include Rails.application.routes.url_helpers
def self.default_url_options
ActionMailer::Base.default_url_options
end
end
router = Router.new
router.posts_url # http://localhost:3000/posts
router.posts_path # /posts
It's from http://hawkins.io/2012/03/generating_urls_whenever_and_wherever_you_want/
While there might be a way I would tend to keep that kind of logic out of the Model. I agree that you shouldn't put that in the view (keep it skinny) but unless the model is returning a url as a piece of data to the controller, the routing stuff should be in the controller.
(Edit: Forget my previous babble...)
Ok, there might be situations where you would go either to the model or to some other url... But I don't really think this belongs in the model, the view (or maybe the model) sounds more apropriate.
About the routes, as far as I know the routes is for the actions in controllers (wich usually "magically" uses a view), not directly to views. The controller should handle all requests, the view should present the results and the model should handle the data and serve it to the view or controller. I've heard a lot of people here talking about routes to models (to the point I'm allmost starting to beleave it), but as I understand it: routes goes to controllers. Of course a lot of controllers are controllers for one model and is often called <modelname>sController (e.g. "UsersController" is the controller of the model "User").
If you find yourself writing nasty amounts of logic in a view, try to move the logic somewhere more appropriate; request and internal communication logic probably belongs in the controller, data related logic may be placed in the model (but not display logic, which includes link tags etc.) and logic that is purely display related would be placed in a helper.

Resources