Override Rails Helpers with Access to Original

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 and
  • ActionView::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



Leave a reply



Submit