Override rails helpers with access to original
There's a better way than any of your listed options. Just use super
:
module AwesomeHelper
def stylesheet_link_tag(*sources)
if @be_awesome
awesome_stylesheet_link_tag *sources
else
super
end
end
end
Overriding stylesheet_link_tag
in AwesomeHelper will ensure that, when stylesheet_link_tag
gets invoked, Ruby will encounter it in the method lookup path before it hits ActionView::Helpers::AssetTagHelper
. If @be_awesome
is true
, you get to take charge and stop things right there, and if not, the call to super
without parentheses will transparently pass through all the arguments up to the Rails implementation. This way you don't have to worry about the Rails core team moving things around on you!
Override module_function inside ruby class with access to original
Here's a workaround. You can re-open the module, make an unbound reference to the original instance method, then redefine it to call the original method (with some altered behavior).
First, the original definition:
module Foo
def bar(arg)
"self=#{self}, arg=#{arg}"
end
module_function :bar
end
Next, reopening and redefining the method:
module Foo
OrigBarMethod = instance_method(:bar)
def bar(arg)
Foo::OrigBarMethod.bind(self).call(arg + 1)
end
module_function :bar
end
puts Foo.bar(1) # => "self=Foo, arg=2"
I use bind(self)
so that the original method can still make use of self
, for example:
class MyClass
include Foo
end
MyClass.new.send(:bar, 1) # => "self=#<MyClass:0x00007fb66a86cbf8>, arg=2"
Is it possible to override Rails path helper methods?
Yes, DRY is the "Rails way" to do things. If you're repeating this method over and over again, it makes sense to create a view helper for it. Instead of modifying the path helpers, I'd simply wrap rails link_to
method.
You can do something quick and easy like this:
# app/helpers/application_helper.rb
def link_to_applicant(applicant)
link_to applicant.name, company_job_applicant_path(applicant.job.company, applicant.job, applicant)
end
# link_to(@applicant)
#=> <a href="/companies/jobs/applicants/123">Peter Nixey</a>
Alternatively, you can roll in some extra support for the link_to
method
def link_to_applicant(applicant, html_options={})
link_to applicant.name, company_job_applicant_path(applicant.job.company, applicant.job, applicant), html_options
end
# link_to_applicant(@applicant, :id=>"applicant-#{@applicant.id}")
#=> <a id="applicant-123" href="companies/jobs/applicants/123">Peter Nixey</a>
If you want to fully support all the features provided by link_to
, you can see how they permit for multiple function signatures here
# rails link_to source code
def link_to(*args, &block)
if block_given?
options = args.first || {}
html_options = args.second
link_to(capture(&block), options, html_options)
else
name = args[0]
options = args[1] || {}
html_options = args[2]
html_options = convert_options_to_data_attributes(options, html_options)
url = url_for(options)
href = html_options['href']
tag_options = tag_options(html_options)
href_attr = "href=\"#{html_escape(url)}\"" unless href
"<a #{href_attr}#{tag_options}>#{html_escape(name || url)}</a>".html_safe
end
end
RSpec notes
If you'd like to write tests for your view helpers in RSpec, follow this guide:
https://www.relishapp.com/rspec/rspec-rails/docs/helper-specs/helper-spec
Override image_tag rails helper
I finally did it using the deprecated alias_method_chain
.
config/initializers/asset_tag_helper.rb
module ActionView::Helpers::AssetTagHelper
# Override the native image_tag helper method.
# Automatically add data-fallback
def image_tag_with_fallback(source, options = {})
ext = File.extname(source)
fallback_ext = 'png'
# Allow custom extension, even if it will probably always be "png".
if options.key? 'fallback_ext'
fallback_ext = options.fallback_ext
options.delete :fallback_ext
end
if ext == '.svg'
# If fallback is provided, don't override it.
if !(options.key?('data') && options.data.key?('fallback'))
# Ensure to have an object.
if !options.key?('data')
options['data'] = {}
end
# Replace the extension by the fallback extension and use the asset_path helper to get the right path.
options['data']['fallback'] = asset_path (source.sub ext, '.' + fallback_ext)
end
end
image_tag_without_fallback(source, options) # calling the original helper
end
alias_method_chain :image_tag, :fallback
end
If you have a better solution or any improvment about the current solution, please share.
I saw that I could also use super
, but I didn't understand how neither where write the code.
Override rails translations helper
It would seem that there is some confusion between the functionalities of:
I18n.translate
, from the I18n gem andActionView::Helpers::TranslationHelper#translate
, from Rails
I18n.translate
does not perform the "lazy lookups" (ie scoping the lookup key to the current partial if it begins with a dot) that you are expecting. That is a feature of ActionView::Helpers::TranslationHelper#translate
, along with some others.
Your method is overriding ActionView::Helpers::TranslationHelper#translate
, without a call to super
to get lazy loading. So, if you want to persist on overriding the method, I think you may want:
# my_helper.rb
module MyHelper
def translate(key, options = {})
# If you don't want to ignore options[:locale] if it's passed in,
# change to options[:locale] ||= MY_LOCALE
options[:locale] = MY_LOCALE
super(key, options)
end
alias :t :translate
end
Personally though, I'd rather use t(:my_key, locale: :my_locale)
every time in my views without the override, or at most have a separate helper method that wraps around a call to ActionView::Helpers::TranslationHelper#translate
with the extra business logic of forcing a specific locale.
Can't override Spree's helper's method
Did you have this in your application.rb
?
config.to_prepare do
# Load application's model / class decorators
Dir.glob(File.join(File.dirname(__FILE__), "../app/**/*_decorator*.rb")) do |c|
Rails.configuration.cache_classes ? require(c) : load(c)
end
# Load application's view overrides
Dir.glob(File.join(File.dirname(__FILE__), "../app/overrides/*.rb")) do |c|
Rails.configuration.cache_classes ? require(c) : load(c)
end
end
Overriding Rails Default Routing Helpers
You can define to_param
on your model. It's return value is going to be used in generated URLs as the id.
class Thing
def to_param
name
end
end
The you can adapt your routes to scope your resource like so
scope "/something" do
resources :things
end
Alternatively, you could also use sub-resources is applicable.
Finally you need to adapt your controller as Thing.find(params[:id])
will not work obviously.
class ThingsController < ApplicationController
def show
@thing = Thing.where(:name => params[:id).first
end
end
You probably want to make sure that the name
of your Thing
is unique as you will observe strange things if it is not.
To save the hassle from implementing all of this yourself, you might also be interested in friendly_id which gives you this and some additional behavior (e.g. for using generated slugs)
Related Topics
How to Make a Post Request with Open-Uri
How to Execute Custom Actions After Successful Sign in with Devise
How to Check the Database Type in a Rails Migration
Which Ruby Version am I Really Running
Jekyll Serve Dependency Error - Could Not Open 'Lib Curl'
How to Decode Rijndael in Ruby (Encoded in Vb.Net)
Ruby Variable (Array) Assignment Misunderstanding (With Push Method)
How to Get a Single Column's Values into an Array
Testing If a Hash Has Any of a Number of Keys
How to Convert a Ruby Object to JSON
Handling Exceptions Raised in a Ruby Thread
Rails Article Helper - "A" or "An"
How to Push to Faye Server from Rails Controller