Override model method in Rails - ruby-on-rails

I have this in config/initializers/my_app_model.rb
module MyAppModel
def test
'test'
end
end
ActiveRecord::Base.send :include, MyAppModel
And in app/models/namespace/my_model.rb
class Namespace::MyModel < Namespace
def test
'changed!'
end
end
How can I make test to return changed!? It currently returns test.

From some posts I read, Rails loads models as they are asked, what I did was to make sure that model file was loaded just after initializer:
module MyAppModel
def test
'test'
end
end
ActiveRecord::Base.send :include, MyAppModel
require Rails.root.join 'app/models/namespace/my_model'

Related

how to include module for MiniTest

I have a class Company that include GrowthRate.
models/company.rb
class Company < ActiveRecord::Base
include GrowthRate
end
In the growth_rate.rb, I add some methods for Array.
models/company/growth_rate.rb
module Company::GrowthRate
extend ActiveSupport::Concern
end
module Company::GrowthRate::Array
def growth_rate
# calculate growth rate
end
end
class Array
include Company::GrowthRate::Array
end
And I want to test the method of Array by MiniTest.
test/models/company/growth_rate_test.rb
require 'test_helper'
class CompanyTest < ActiveSupport::TestCase
include Company::GrowthRate
test 'test for adjusted_growth_rate' do
array = [1, 0.9]
Array.stub :growth_rate, 1 do
# assert_equal
end
end
end
But the test ends up with a name error.
NameError: undefined method `growth_rate' for `Company::GrowthRate::Array'
How can I include the method for MiniTest?
test/test_helper.rb
ENV['RAILS_ENV'] ||= 'test'
require File.expand_path('../../config/environment', __FILE__)
require 'rails/test_help'
require 'minitest/mock'
class ActiveSupport::TestCase
# Setup all fixtures in test/fixtures/*.yml for all tests in alphabetical order.
fixtures :all
end
You'll want to use the ActiveSupport included block.
For your example, I imagine it can't find the method b/c you're not requiring that file anywhere.
module PolymorphicTest
extend ActiveSupport::Concern
included do
test 'some cool polymorphic test' do
assert private_helper_method
end
private
def private_helper_method
# do stuff
end
end
end
Minitest also doesn't autoload these, so you'll need to make sure they're included in a require in each test or in the test_helper.
If you need me to break this down more, please ask.
I think you have to move the models/company/growth_rate.rb into app/model/concerns folder with filename = 'growth_rate.rb'
then don't use Company class to prevent conflict class name
module GrowthRate
extend ActiveSupport::Concern
# ...
end
Now you can include it to your Company model
then create array.rb file in the config/initializers folder which contains
class Array
def growth_rate
# calculate growth rate
end
end
this file will loaded only once by rails, it's good to adding custom method to Array class if you want to
now you can remove include Company::GrowthRate from test/models/company/growth_rate_test.rb

Extending ActionView Renderere

I am trying to create a gem to extend Rails ActionView Renderer to print an HTML comment with the partial view name bring rendered.
I tried the normal way:
module MyGem
class Engine < ::Rails::Engine
initializer 'mygem.initialize' do
::ActiveSupport.on_load(:action_view) do
::ActionView::Renderer.send :include, MyGem::ViewRenderer
end
end
end
end
And then in mygem/lib/view_renderer.rb:
module MyGem
module ViewRenderer
module InstanceMethods
def render(context, options)
puts "here" # Just to test it was included and it doesn't print
if options.key?(:partial)
render_partial(context, options)
else
render_template(context, options)
end
end
end
def self.included(base)
base.send :include, InstanceMethods
end
end
end
However, when I use render from my views, the test line added doesn't work.
Any idea what am I doing wrong?
This is how I have done it in the past, maybe it will help.
module MyModule
def self.included(base)
def render
#do stuff
end
end
end
ActionController::Base.send :include, MyModule
Then just require the file

2 alias_method_chain -> stack level too deep

I have developed two plugins and they both modify the same method of the same class via alias_method_chain that result into stack level too deep error.
first plugin:
Rails.configuration.to_prepare do
require 'issue_patch'
Issue.send :include, IssuePatch
end
module IssuePatch
module InstanceMethods
def editable_custom_field_values_with_sort(user=nil)
editable_custom_field_values_without_sort(user).sort
end
end
def self.included(receiver)
receiver.send :include, InstanceMethods
receiver.class_eval do
alias_method_chain :editable_custom_field_values, :sort
end
end
end
Second plugin modify the class same way but with different feature:
Rails.configuration.to_prepare do
require 'issue_patch'
Issue.send :include, IssuePatch
end
module IssuePatch
module InstanceMethods
def editable_custom_field_values_with_some_stuff(user=nil)
editable_custom_field_values_without_some_stuff(user).select { |c| c.have_stuff? }
end
end
def self.included(receiver)
receiver.send :include, InstanceMethods
receiver.class_eval do
alias_method_chain :editable_custom_field_values, :some_stuff
end
end
end
When I trying to call this method I got:
ActionView::Template::Error (stack level too deep):
plugins/my_plugin/lib/issue_patch.rb:8
One possible but hack solution is simple monkey patch the redmine code with first plugin feature so the second plugin can alias it without any error.
So how I can fix this error?
The problem was:
I define two modules with the same name IssuePatch so the first module overwrites the second but
Issue.send :include, IssuePatch
still exists in 2 places (for each plugin) and the same module (doesn't matter which one overwrites the other) so the same module were included 2 times and the same alias_method_chain were called 2 times.
The solution: I just add separate module for each plugin and included them like this:
Issue.send :include, FirstPlugin::IssuePatch
Issue.send :include, SecondPlugin::IssuePatch

Overwriting Rails Test Test::Unit::TestCase

I am trying to overwrite/modify the teardown function of a Test::Unit::TestCase test.
During the teardown of the test (after it has finished), I want to do some extra stuff.
I tried this, but it doesn't work (keeps executing the original teardown):
module Test
module Unit
class TestCase
def teardown_modified
# do modifications
teardown_original
end
alias teardown_original teardown
alias teardown teardown_modified
end
end
end
Do you want it in one TestCase or in all?
If you need a change for all TestCases:
gem 'test-unit'
require 'test/unit'
module Test
module Unit
module Fixture
alias :run_teardown_old :run_teardown
def run_teardown
# do modifications
puts "In modified teardown"
run_teardown_old
end #def run_teardown
end #module Fixture
end #module Unit
end #module Test
class MyTest < Test::Unit::TestCase
def teardown
puts "In teardown"
end
def test_4()
assert_equal(2,1+1)
end
end
You might find that using alias_method_chain produces better results:
class Test::Unit::TestCase
def teardown_with_hacks
teardown_without_hacks
end
alias_method_chain :teardown, :hacks
end
This sets up a lot of the stuff for you automatically.

Extending ActiveRecord::Base in Rails does not work in the test environment

When I add the following block of code in environments.rb, ActiveRecord::Base extends the module in the development environment but not in the test environment.
require "active_record_patch"
ActiveRecord::Base.send(:extend, ModelExtensions)
The library file which contains the module is as follows:
module ModelExtensions
def human_name_for(attr_hash)
# do something
end
end
Loading ./script/server and ./script/console seems fine on the development environment. But on the test environment, the following error occurs:
/home/test/rails_app/vendor/rails/activerecord/lib/active_record/base.rb:1959:in `method_missing':NoMethodError: undefined method `human_name_for' for #<Class:0x4a8d33>
For the solution, I modified the module and included the module to ActiveRecord::Base on the lib file itself:
module HumanAttributes
module ClassMethods
def human_name_for(attr_hash)
unless attr_hash.nil?
##human_names = attr_hash
class << self
def human_attribute_name key
##human_names[key.to_sym] || super unless key.nil?
end
end
end
end
end
end
module ActiveRecord
class Base
extend HumanAttributes::ClassMethods
end
end
This makes human_name_for accessible by any class extending from ActiveRecord::Base on all environments.
Just remember to require the file on the top of model file.
This works for me.
module ModelExtensions
def human_name_for(attr_hash)
# do something
end
end
In environment.rb
include ModelExtensions
ActiveRecord.extend(ModelExtensions)
Then this works ArObject.human_name _for(:asd)

Resources