Using Rails 3.2
I'm trying to call expire_fragment, the Rails view method, from a helper but getting an error:
undefined method `expire_fragment' for #<#<Class:0x00000118977110>:0x00000103b853b8>
I'm trying to conditionally clear the cache. This is the helper method call in my view
clear_cache_keys_if(params[:cc], [#product, :search_filters])
And in the helper
def clear_cache_keys_if(condition, keys = [])
if condition
keys.each do |key|
expire_fragment(key)
end
end
end
I would have thought that the Rails fragment caching methods would be accessible in a helper module, but that doesn't seem to be the case.
I changed it to
controller.expire_fragment(key)
And that worked.
This method is available in the view. It's available from the controller. I don't really understand why it is not available in a helper. What am I missing here? Why isn't it available in the helper and what is the best way to expose it? Thanks
Related
I have some helpers that are defined on runtime that are specific for a single call, e.g. a single instance of a controller (the next call could have different helper methods). Is there a robust way to add a helper method to an instance of a controller and it's view only, without adding the helper to other instances and views of this controller?
To define a helper for ALL instances, you could use the .helper_method method, e.g.
class Article < ApplicationController
helper_method :my_helper
def my_helper
# do something
end
end
I digged around in the source code, and found the (fairly private looking) #_helpers method which returns a module that contains all helpers for this instance. I could now use some meta programming to define my methods on this module
def index
_helpers.define_singleton_method(:my_helper) do
# do something
end
end
But I don't like this approach because I'm using a clearly private intended method that could easily change in the future (see the leading _).
If I only needed the helper inside the controller instance only, I could just call #define_singleton_method on the instance directly, but this doesn't make it available to the view.
So I'm looking for an official "Rails way" to define a helper for a single instance of a controller and it's view, like Rails provides with it's class method .helper_method.
I'm not sure if there is an official Rails way of doing this.
You could create an anonymous module and extend from that. Since this solution uses pure Ruby, you'll have to extend both the controller and view.
before_action :set_helpers, only: :index
def index
# ...
end
private
def set_helpers
#helpers = Module.new do |mod|
define_method(:my_helper) do
# do something
end
end
extend(#helpers)
end
<% extend(#helpers) %>
I have a fee column in my model and it is an integer type, so I try to create a tiny helper to add a dollar sign neatly in front. Which means, instead of writing:
span = "$#{#object.fee}"
I can write something like
span = #object.fee.dollar
So I created the tiny helper.
module ApplicationHelper
def self.dollar
"$#{self.try(:to_s)}"
end
end
I am not sure where to put it, so basically it's now showing
undefined method `dollar' for 180:Fixnum
number_to_currency()
Rails 4.2 has this ActionView::Helper
number_to_currency(1234567890.506)
Helper
If you want to implement this as a helper, this works
module ApplicationHelper
def dollar(amount)
amount = number_to_currency(amount)
end
end
Invoke
<%= dollar(your_var_here) %>
Rails spec for number_to_currency()
http://api.rubyonrails.org/classes/ActionView/Helpers/NumberHelper.html#method-i-number_to_currency
Note: Other versions of Rails may have this function, you'd have to check your version.
module ApplicationHelper
def dollar(amount)
"$#{amount}"
end
end
and then:
span = dollar #object.fee
I think it's because you're in a helper, so you can't refer to self.
You can do it in your Model, or in the helper do :
def print_dollar(your_value)
Or, you can also use : number_to_currency(dollar, :unit => "$"), which will render it the way you want.
Hope it help
Your helpers are included in the view context, so you need two changes:
def dollar - because it's included in the renderer, you don't need self
Call it as dollar(#object.fee) - it's not included on the object, but in your view. If you want to call it as #object.dollar, declare the method in whatever class #object is.
Additionally, the number_to_currency helper already exists and is quite robust. Perhaps you want to use that.
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'
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.
My ApplicationController exposes a method (e.g. sort_direction) to the view templates by using helper_method :sort_direction. I then use this method in another method (e.g. sort_link) of a view helper (application_helper.rb).
When testing the sort_link method with RSpec (in application_helper_spec.rb) I have to stub sort_direction as the test seems to run complete independent from the controllers (and thereby by its to the view templates exposed methods).
Unfortunately I could not find out how to stub that sort_direction method of the controller. I always get "undefined method".
Here is what I tried so far (inside application_helper_spec.rb):
helper.stub(:sort_direction)
controller.stub(:sort_direction)
view.stub(:sort_direction)
self.stub(:sort_direction)
Any suggestions how I can stub that method?
Here the error I get:
NoMethodError:
undefined method `sort_direction' for #<RSpec::Core::ExampleGroup::Nested_1::Nested_1:0xb641434c>
David Chelimsky solved that problem here: http://groups.google.com/group/rspec/browse_thread/thread/cc44ca12c6816053
Simply call in the spec all methods on the helper object:
it "should work" do
helper.stub(:sort_direction)
helper.sort_link(...).should == ...
end