I am working on upgrading an app to Rails 5, and #asset_path now raises if the url is nil. I'm trying to monkey patch that method with a version that will work like Rails 4 so that I can get my tests passing.
I've spent hours on this, and I'm going crazy. For some reason no matter what I do, I can't monkey patch the module. I thought this initializer would work:
module ActionView
module Helpers
module AssetUrlHelper
alias asset_path_raise_on_nil asset_path
def asset_path(source, options = {})
return '' if source.nil?
asset_path_raise_on_nil(source, options)
end
end
end
end
I also tried putting my method in another module and includeing, prepending, and appending it to both ActionView::Helpers::AssetUrlHelper and ActionView::Helpers::AssetTagHelper.
No matter what I do, I can't get my method to be executed. The only way I can alter the method is to bundle open actionview and changing the actual method.
I figured out that it is because #asset_path is simply an alias. I needed to override the method which the alias points at:
module ActionView
module Helpers
module AssetTagHelper
alias_method :path_to_asset_raise_on_nil, :path_to_asset
def path_to_asset(source, options = {})
return '' if source.nil?
path_to_asset_raise_on_nil(source, options)
end
end
end
end
Related
Rails 6.1 has released an improvement for a tag_helper (specifically for the rich_text_area from ActionText), that I would require now, for my Rails 6.0.x app. Basically the improvement is a very small change in just one line of code, so it should be simple to just monkey patch the current rails method and get the improvement now, right?
Specifically I'm trying to monkey patch the following ActionText tag helper method (link to Github rails/rails) with the following code, but the code is not applied. What am I doing wrong?
lib/core_ext/rich_text_area.rb
module ActionView::Helpers
class Tags::ActionText < Tags::Base
def render
options = #options.stringify_keys
debugger
add_default_name_and_id(options)
options["input"] ||= dom_id(object, [options["id"], :trix_input].compact.join("_")) if object
#template_object.rich_text_area_tag(options.delete("name"), options.fetch("value") { editable_value }, options.except("value"))
end
end
end
Added the following to a file in config/initializers
Dir[File.join(Rails.root, 'lib', 'core_ext', '*.rb')].each { |l| require l }
You can monkey patch in a cleaner way something like this in your lib/core_ext/rich_text_area.rb file:
require 'action_text/tag_helper'
module ActionTextOverride
def render
options = #options.stringify_keys
add_default_name_and_id(options)
options['input'] ||= dom_id(object, [options['id'], :trix_input].compact.join('_')) if object
#template_object.rich_text_area_tag(options.delete('name'), options.fetch('value') { editable_value }, options.except('value'))
end
end
class ActionView::Helpers::Tags::ActionText
prepend ActionTextOverride
end
Note: The error you were getting RailsError: uninitialized constant ActionView::Helpers::Tags::ActionText (NameError) when trying to use class_eval can be solved by using require 'action_text/tag_helper'
Source: When monkey patching an instance method, can you call the overridden method from the new implementation?
I'm creating a gem to add a new helper method for rails forms. My gem is a single file
lib/rails_json_field.rb
that looks like this:
require 'action_view/helpers'
require 'action_view/context'
require 'securerandom'
module ActionView
module Helpers
class FormBuilder
include ActionView::Helpers::FormTagHelper
include ActionView::Helpers::JavaScriptHelper
include ActionView::Context
def json_field_tag(method, options = {})
#function code here
end
end
end
end
ActiveSupport.on_load(:action_view) do
include ActionView::Helpers::FormBuilder
end
However when I use the method like so:
= f.json_field_tag(:some_method)
I receive the following error:
ActionView::Template::Error (undefined method `json_field_tag' for #<ActionView::Helpers::FormBuilder:0x007ffa84ab52a8>)
How do I make the method available on ActionView::Helpers::FormBuilder ?
You have defined the following class:
RailsJsonField::ActionView::Helpers::FormBuilder
You meant to monkeypatch the following class:
ActionView::Helpers::FormBuilder
That's why the error message is telling you the method is undefined; you have defined it within a class within your custom module, not within the specified class:
undefined method `json_field_tag' for #<ActionView::Helpers::FormBuilder
It's only defined in RailsJsonField::ActionView::Helpers::FormBuilder, so you get the above error.
If you want to properly monkeypatch the original code then you should look at the original code to ensure your code looks like their code:
module ActionView
module Helpers
class FormBuilder
def json_field_tag(method, options = {})
# function code here
end
end
end
end
It would be better to define this as an initializer in your Rails app, e.g., in config/initializers/json_field_tag.rb. Once you have the code working as a simple patch, then you can focus on developing it into a standalone gem that enhances ActionView.
After searching, I found a different gem that adds a FormBuilder method. I used their repo as a guide to structure my own. For others with this questions, you can view my repo and their repo here respectively:
https://github.com/dyeje/rails_json_field
https://github.com/Brantron/john_hancock
I'm updating my rails app and I need to refactor a method that is using alias_method_chain because it is deprecated. The message says to use module#prepend as recommended by Rails 5. Here is the helper that I'm trying to refactor:
module ActiveSupport
module NumberHelper
def number_to_delimited_with_unicode_infinity(number, options = {})
result = number_to_delimited_without_unicode_infinity(number, options)
result.sub(/^Infinity$/, "∞")
end
alias_method_chain :number_to_delimited, :unicode_infinity
end
end
If anyone know how I can refactor with super or some other way let me know thank you!
This works for me. I don't know why they used alias_method_chain to begin with but this gets rid of the deprecation warning with the same functionality.
module ActiveSupport
module NumberHelper
def number_to_delimited(number, options = {})
number.to_s.sub(/^Infinity$/, "∞")
end
end
end
In your case this solution seems to be fine. If you have to have a monkey patch with reference to original method then you can do it creating an alias before patching:
module ActiveSupport
module NumberHelper
# create alias to original method
alias :original_number_to_delimited :number_to_delimited
def number_to_delimited(number, options = {})
result = original_number_to_delimited(number, options)
result.sub(/^Infinity$/, "∞")
end
end
end
I need to monkey-patch one of the Rails core classes, specifically ActionView::Helpers::UrlHelper::ClassMethods.link_to method. As far as I remember there are some events fired when parts of Rails are loaded, how to add handlers for them? Or should I just put the code into initializer?
link_to does not appear to be in ClassMethods. From here.
In config/initializers/url_helper_extensions.rb
module ActionView
module Helpers
module UrlHelper
alias_method :_link_to, :link_to
def link_to
# Your code ...
# Call original method if you want.
_link_to
end
end
end
end
I'm trying to move an app to rails 3.1. Many of my tests break, because the submit buttons no longer have an id. the Release Notes (see sect. "5.3 - Action View") confirm it:
The submit form helper does not generate an id “object_name_id” anymore.
Here's the relevant commit that changed action_view/helpers/form_helper.rb.
I want have the old behaviour back without messing with the installed gem or changing all my views by hand. So I try to monkey patch it:
# this is config/initializers/FormHelperMonkeypatch.rb
module ActionView
module Helpers
module FormHelper # <-- this is the line phoet repaired, see his answer below
# code from rails 3.0
def submit(value=nil, options={})
value, options = nil, value if value.is_a?(Hash)
value ||= submit_default_value
#template.submit_tag(value, options.reverse_merge(:id => "#{object_name}_submit"))
end
end
end
end
I restarted my server, but I see no effect of my patch. What am I doing wrong?
you are editing the wrong place. use this:
module ActionView
module Helpers
class FormBuilder
# code from rails 3.0
def submit(value=nil, options={})
value, options = nil, value if value.is_a?(Hash)
value ||= submit_default_value
#template.submit_tag(value, options.reverse_merge(:id => "#{object_name}_submit"))
end
end
end
end