alias_method_chain is deprecated - Rails 5 upgrade - ruby-on-rails

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

Related

Why can't I override #asset_path from ActionView?

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

how to replace alias_method_chain in activerecord-sqlserver-adapter gem?

I need to connect a secondary server( Microsoft SQL Server) to pull info from specifically for one form I have in my rails app. I use sqlite3 for my other forms. Is there a way to solve this issue? The issue is with the gem itself as the alias_method_chain is deprecated. Are there any gems alternatives I can implement, or a way to rewrite the gem file itself? here is the piece of code it is throwing errors for:
module ActiveRecord
def self.included(klass)
klass.extend ClassMethods
class << klass
alias_method_chain :reset_column_information, :sqlserver_cache_support
alias_method_chain :add_order!, :sqlserver_unique_checking
alias_method_chain :add_limit!, :sqlserver_order_checking
end
end
. I tried to use the prepend method with no luck. Thank you

Ruby refinements and hooks

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

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)

Legacy table with column named "class" in Rails

I've got a legacy table that my rails application shares with another application. It has a column called "class". The first time I reference any attribute in that model, I get an error. Subsequent references to attributes work. Is there a good workaround for this, or should I just go modify the other application that uses this table (ugh)?
>> Member::Ssg.find(:first)
=> #<Member::Ssg ssg_key: #<BigDecimal:10b169688,'0.253E3',4(8)>, org_id: 2, academic_year: 2006, class: true, next_due_date: "2011-06-01", submitted_date: "2006-02-13", notes: nil, owner_id: "1">
>> Member::Ssg.find(:first).notes
NoMethodError: undefined method `generated_methods' for true:TrueClass
from /Library/Ruby/Gems/1.8/gems/activerecord-2.3.8/lib/active_record/attribute_methods.rb:247:in `method_missing'
from (irb):2
>> Member::Ssg.find(:first).notes
=> nil
SOLUTION:
I went with a combination of the Bellmyer solution and adding the code below to my model
class << self
def instance_method_already_implemented?(method_name)
return true if method_name == 'class'
super
end
end
NOTE: Please see the updated solution at the end of this answer. Leaving the original outdated solution for historic reasons.
This has come up often enough (legacy column names interfering with ruby/rails) that I might just make a plugin out of this. Here's how you can fix it right away, though. Create this file in your app:
# lib/bellmyer/create_alias.rb
module Bellmyer
module CreateAlias
def self.included(base)
base.extend CreateAliasMethods
end
module CreateAliasMethods
def create_alias old_name, new_name
define_method new_name.to_s do
self.read_attribute old_name.to_s
end
define_method new_name.to_s + "=" do |value|
self.write_attribute old_name.to_s, value
end
end
end
end
end
And now, in your model:
class Member < ActiveRecord::Base
include Bellmyer::CreateAlias
create_alias 'class', 'class_name'
end
The first parameter to create_alias is the old method name, and the second parameter is the new name you want to call it, that won't interfere with rails. It basically uses the read_attribute and write_attribute methods to interact with the column instead of the ruby methods that get defined by ActiveRecord. Just be sure to use the new name for the field everywhere, like so:
member.class_name = 'helper'
This works with ruby 1.8, but I haven't tested with ruby 1.9 yet. I hope this helps!
UPDATE: I've found a better solution that works in Rails 3, the safe_attributes gem. I've written a blog post explaining how to use it, with example code snippets, and a full sample app you can download from github and play around with. Here's the link:
Legacy Database Column Names in Rails 3
The following works in Rails 6.0.2.2
class ReasonCode < ApplicationRecord
class << self
def instance_method_already_implemented?(method_name)
return true if method_name == 'class'
super
end
end
def as_json(options={})
add_class = attributes.keys.include?('class')
if add_class
if options[:only]
add_class = Array(options[:only]).map(&:to_s).include?('class')
elsif Array(options[:except])
add_class = Array(options[:except]).map(&:to_s).exclude?('class')
end
end
options[:except] = Array(options[:except])
options[:except].push('class')
json = super(options)
json['class'] = attributes['class'] if add_class
json
end
end
Adapted from this answer https://www.ruby-forum.com/t/activerecord-column-with-reserved-name-class/125705/2. The as_json method was added because rendering the record as json gave a SystemStackError (stack level too deep). I followed the serialization code in the Rails repo to only render the class attribute if specified in the as_json options.

Resources