Ruby refinements and hooks - ruby-on-rails

I'm trying to use ruby refinements to apply rails hooks.
I want to avoid monkey patching. When monkey patching it would work as such
ActiveRecord::Base.class_eval do
after_find do
# do something with
my_method
end
def my_method
# something useful
end
end
I've been able to have the class method by doing something like such:
module ActiveRecordRefinements
refine ActiveRecord::Base.singleton_class do
def my_method
#something cool
end
end
end
But I can't run the hook. I tried using self.used(klass) but don't seem to be able to get the syntax just right.
Any help is welcome.
Thanks.

There is a reason you're not using ActiveSupport Callbacks?
Take a look here: http://api.rubyonrails.org/classes/ActiveSupport/Callbacks.html

Related

alias_method_chain is deprecated - Rails 5 upgrade

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

Safe and best way to monkey patch a rails gem

I have tried, seriously. Many questions out there but many developers say "It dont work for me"; I'm one of them -- said to say.
I was reading up on the best way to monkey-patch a rails gem. I've found few but decided to use this method.
I want to monkey-patch the xeroizer gem but rather the invoice.rb model.
# lib/xeroizer/invoice/invoice_url.rb
module Xeroizer
module Invoice
module InvoiceUrl
def invoice_url(id)
#application.http_get(#application.client, "#{url}/#{CGI.escape(id)}/OnlineInvoice")
end
end
end
end
Going with the "this method" link, I assume this should work, but it dosent.
Controller:
include Xeroizer::Invoice::InvoiceUrl
# Invoice.include Xeroizer::Invoice::InvoiceUrl
def some_method
# #xero is in a private method. It's here for short demonstration
#xero = Xeroizer::PrivateApplication.new("MY_CONSUMER_KEY", "MY_SECRET_KEY", "#{Rails.root}/privatekey.pem")
Rails.logger = #xero.Invoice.invoice_url("ad61ea97-b9e9-4a1e-b754-7c19e62f8cd7")
end
undefined method `invoice_url' for Xeroizer::Record::InvoiceModel
How do you add custom methods to a rails gem's class?
Assuming you are trying to monkey-patch Xeroizer::Record::InvoiceModel with Xeroizer::Invoice::InvoiceUrl, you might just do the following right after the first mention of Xeroizer::Record::InvoiceModel (to make Rails to autoload it):
Xeroizer::Record::InvoiceModel.prepend Xeroizer::Invoice::InvoiceUrl
This will override original invoice_url method. The original one still might be called from a prepended using super.

Monkey patching Rails

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

Spree module decorator

I'm using Spree Commerce for my Online Shop. I want to change some behaviour during the checkout process, that is defined in app/models/spree/order/checkout.rb inside the spree gem. So I made a checkout_decorator.rb at the same point in my application.
The problem is, that my changes aren't loaded. And another problem is, that everything inside the module is inside one method, the def self.included(klass) method. So I think I have to overwrite the whole file, instead of just one method. Here is what my decorator looks like:
checkout_decorator.rb
Spree::Order::Checkout.module_eval do
def self.included(klass)
klass.class_eval do
class_attribute :next_event_transitions
class_attribute :previous_states
class_attribute :checkout_flow
class_attribute :checkout_steps
def self.define_state_machine!
# here i want to make some changes
end
# and the other methods are also include here
# for readability, i don't show them here
end
end
end
The original file checkout.rb from the spree gem looks like this:
module Spree
class Order < ActiveRecord::Base
module Checkout
def self.included(klass)
klass.class_eval do
class_attribute :next_event_transitions
class_attribute :previous_states
class_attribute :checkout_flow
class_attribute :checkout_steps
def self.checkout_flow(&block)
if block_given?
#checkout_flow = block
define_state_machine!
else
#checkout_flow
end
end
def self.define_state_machine!
# some code
end
# and other methods that are not shown here
end
end
end
end
end
So my questions are: Why does this not work? Is module_eval the right way to do this? I tried class_eval but it doesn't work either. How can I solve this?
The module_eval method isn't going to work for you.
You should look at the Spree Checkout Flow Documentation for some good examples on how to customize the checkout flow. This is the recommended way for customizing the checkout flow as you won't need to copy/paste a whole bunch of code.
The namespacing isn't right.
Try Spree::Order::Checkout.class_eval do
tl;dr: Overwrite the method you want in the Spree::Order class instead of the Spree::Order::Checkout module.
You mentioned that in the original file (spree_core-3.2.0.rc3/app/models/spree/order/checkout.rb) there's a method wrapping the entire module.
def self.included(klass)
klass.class_eval do
This method is called when the module is included in a class, and does its own class_eval to add the module's methods to instances of the class including it.
So since (spree_core-3.2.0.rc3/app/models/spree/order.rb) has this line:
include Spree::Order::Checkout
We can add a decorator to the order class itself (app/models/spree/order_decorator.rb)

Ruby/Rails: alias_method practices

I'm trying to override Rails' "fields_for" method, which I'm currently doing as follows:
module ActionView::Helpers::FormHelper
include ActionView::Helpers::FormTagHelper
alias_method :original_fields_for, :fields_for
def fields_for(<my arguments>)
# Some extra stuff
# ...
output.safe_concat original_fields_for(<my other arguments>)
end
end
The functionality works just fine, but I'm starting to suspect that my use of alias_method isn't the most elegant. Most especially, if I were to package this functionality into a gem, and there were another gem that overrode fields_for, am I write in thinking either my new fields_for OR the alternate fields_for would be skipped?
Assuming so, what's the correct way to go about slapping in some extra functionality to an existing rails method?
Cheers...
this seems like exactly the situation that alias_method_chain is meant for (although I don't know offhand if it will work on a Module - have only used it on AR::Base)
You'd just do
module ActionView::Helpers::FormHelper
include ActionView::Helpers::FormTagHelper
alias_method_chain :fields_for, :honeypot
def fields_for_with_honeypot(<my arguments>)
# Some extra stuff
# ...
output.safe_concat fields_for_without_honeypot(<my other arguments>)
end
end
interesting idea to do this to fields_for, but it should work.
There is a minor controversy around a_m_c you should be aware of - this post sums it up well http://erniemiller.org/2011/02/03/when-to-use-alias_method_chain/
In this case, I don't think you can use super because you want to monkey-patch form_for without modifying the calling code/views.

Resources