How to mixin and call link_to from controller in Rails? - ruby-on-rails

This seems like a noob question, but the simple answer is eluding me. I need to call link_to in an ActionController method to spit out an HTML link. ActionView::Helpers::UrlHelper.link_to calls url_for, but this calls the AV module's version instead of the controller's. I managed to coerce this into doing what I intended by putting
#FIXME there must be a better way to mixin link_to
alias_method :self_url_for, :url_for
include ActionView::Helpers::UrlHelper
alias_method :url_for, :self_url_for
in the controller. But, I'm still not sure why it works exactly. Could someone please explain the method scope and hiding that's happening here? What's a better way to mix in link_to (or generally, to include only some methods from a module) so I can call it in the controller (generating a flash string with a link is the use case.)
Please, no lectures about MVC--if anything, link_to should be in a module separate from url_for. Judging from the amount of noise on this, lots of people run into this seemingly trivial snag and end up wasting an hour doing it the "Rails way" when really what is wanted is a one minute hack to make my app work now. Is there a "Rails way" to do this with helpers perhaps? Or a better ruby way?

Compatible with Rails 3,4 and 5:
view_context.link_to

This doesn't really answer your question but there is an easier way
For Rails 5, use the helpers proxy
helpers.link_to '...', '...'
For Rails 3 and 4, since you are using the helper from a controller you can use the view_context
# in the controller code
view_context.link_to '...', '...'
# instead of using your mixin code
link_to '...', '...'
For Rails 2, since you are using the helper from a controller you can actually access the #template member variable of the controller, the #template is the view and already has the UrlHelper mixed in
# in the controller code
#template.link_to '...', '...'
# instead of using your mixin code
link_to '...', '...'
if you need to use the urlhelper from code other than the controller, your solution is probably the way to go

I still had problems using my own helper methods that use built-in helper methods in a controller with ActionController.helper.my_method.
Obviously using render_to_string for each flash would work, but I don't want to create so many small partials for each flash.
My solution was to create a little helper for controllers to execute code in a partial.
The helper method in the controller:
def h(&block)
render_to_string(:partial => 'helper', :locals => {:block => block})
end
The HAML partial (helper.html.haml):
= instance_eval &block
Same would work in ERB (helper.html.erb):
<%= instance_eval &block %>
Use it like this in your controller to call the my_custom_helper_function that's defined in a helper:
redirect_to url, :notice => h{my_custom_helper_function}

You can do it like this:
ActionController.helpers.link_to("Click Me!", awesome_path)
But really, a better place to generate that link might be in a helper module where UrlHelper and other view-related helpers are already included.
[update]
This approach is outdated and no longer works.

Related

Using helper methods in views

I'm having some trouble realising how the helper methods should be used in views. For example, take these parts of code:
Mycontrollers_helper.rb
module MycontrollersHelper
def destroy_everything
Model.destroy_all
redirect_to root_path
end
end
How should it be used in the view then ? Let's say adding the method to a button in the view:
<%= button_to 'Destroy all', destroy_everything, method => :post %>
Is just writing a method in the helper.rb file enough or does it require some additional lines in the controller it refers to ? Is this even the correct syntax for something like this ?
Helpers in rails actually view helpers. So they are meant to provide some help to render your views.
If you want to delete something, and then redirect to some action, just use a controller action for that.
I think you are taking about view helper, which you want to call from your view template.
You can call your view helper with the name of the method.
Calling destroy_everything will works fine if this helper is included in your controller.
Update:
If you write your helper method in application helper then you don't need to worry about load/ include the helper.

Rails: Proper way to add functionality to rails methods

I'm just starting to tinker with extending the rails framework, and as an experiment, I thought I'd add some extra info inside the form_for helper. Specifically, when form_for is called, I'd like to generate an extra h1 tag such as:
# regular form_for <form> opening tag
<h1>Woohoo! It's added!</h1>
# tags fed into form_for via &proc
# form_for close <form> tag
At the moment I've added a /lib file that opens up ActiveRecord::FormHelper and overrides "form for". Needless to say writing out the whole form_for method with just the one added line added is dog ugly...but I can't call super() because, well, instead of inheriting from the method I'd like to super(), I've just overwritten it in /lib.
So, assuming I stubbornly want the functionality to be called via the same form_for tag (instead of, for example extended_form_for), what's the standard way for calling back to the original form_for method I'm overwriting? alias_method_chain? Thought I'd ask before I cement in some potentially lousy practices. If any hardened veterans could give an example I'd be appreciative.
Cheers
You could override form_for in your ApplicationHelper:
module ApplicationHelper
def form_for(*)
content_tag(:h1, "Woohoo! It's added!") + super
end
end
alias_method_chain is by far the simplest way to overwrite the method while still being able to call the original method. So in your lib file you'll want something like this:
def form_for_with_header(...)
form_for_without_header(...)
content_tag(:h1, "Header tag here")
# etc...
end

Ruby On Rails Helpers -- Using instance variables in helpers

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...

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

How to render a Partial from a Model in Rails 2.3.5

I have a Rails 2.3.5 application and Im trying to render several Partials from within a Model (i know, i know -- im not supposed to). The reason im doing this is im integrating a Comet server (APE) into my Rails app and need to push updates out based on the Model's events (ex. after_create).
I have tried doing this:
ActionView::Base.new(Rails::Configuration.new.view_path).render(:partial => "pages/show", :locals => {:page => self})
Which allows me to render simple partials that don't user helpers, however if I try to user a link_to in my partial, i receive an error stating:
undefined method `url_for' for nil:NilClass
I've made sure that the object being passed into the "project_path(project)" is not nil. I've also tried including:
include ActionView::Helpers::UrlHelper
include ActionController::UrlWriter
in the Module that contains the method that makes the above "render" call.
Does anyone know how to work around this?
Thanks
We use the render_anywhere gem and have been happy with it.
From the README:
require 'render_anywhere'
class AnyClass
include RenderAnwhere
def build_html
html = render :template => 'normal/template/reference',
:layout => 'application'
html
end
end
Including these two modules should be enough. Maybe you forgot to set default_url_options[:host]? Without it you can use _path helpers, but not _url ones.
Include these modules and check out if it works in irb, maybe it will lead you to right solution.

Resources