2 alias_method_chain -> stack level too deep - ruby-on-rails

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

Related

Override Redmine core methode

I want to override a method from Redmine Core using a Patch in a plugin but I'm not able to make it working :-(.
I want to change the behavior of the method try_to_login from the Redmine core.
Here is my Init.rb:
require 'redmine'
require 'user_patch'
ActionDispatch::Callbacks.to_prepare do
require_dependency 'project'
require_dependency 'principal'
require_dependency 'user'
unless User.included_modules.include? UserPatch
User.send :include, UserPatch
end
end
Redmine::Plugin.register :security do
name 'Security plugin'
author 'Author name'
description 'This is a plugin for Redmine'
version '0.0.1'
url 'http://example.com/path/to/plugin'
author_url 'http://example.com/about'
end
and my patch file:
module UserPatch
def self.included(base)
base.send(:include, InstanceMethods)
base.class_eval do
alias_method :try_to_login, :try_to_login_with_patch
end
end
module InstanceMethods
# Returns the user that matches provided login and password, or nil
def try_to_login_with_patch(login, password, active_only=true)
<do somthing ...>
end
end
end
Any idea what is wrong in this ?
Thanks you in avance.
Regards,
Aniss
i've done this already in my plugin:
https://github.com/berti92/mega_calendar
init.rb - within Redmin::Plugin.register...
Rails.configuration.to_prepare do
UsersController.send(:include, UsersControllerPatch)
end
in the lib folder create a file "issues_controller_patch.rb"
module IssuesControllerPatch
def self.included(base)
base.class_eval do
# Insert overrides here, for example:
def create_with_plugin
create_without_plugin
## your code ##
end
def update_with_plugin
update_without_plugin
## your code ##
end
alias_method_chain :update, :plugin
alias_method_chain :create, :plugin
end
end
end
create_without_plugin means the original code will be executed. If you dont want it, then delete the create_without_plugin and update_without_plugin

How to define custom class and use it in helper

I want to define a class and let many helpers use.
I can include MvaasPortal moude in fine,
Then I can new the object , but can not use any methods of the object,
It's so strange.
If I can not use the methods in the object, why I can new the object.
Ruby is so strange.
#portal = Portal.new
There is no methods in #portal object
mvaas_portal.rb
module MvaasPortal
module InstanceMethods
class Portal
def initialize(server_url)
~~~~
end
def query_server(body_to_send={},session_id=nil)
~~~
end
end
end
def self.included(receiver)
receiver.send :include, InstanceMethods
end
end
If you're using rails, you can use ActiveSupport::Concern : http://api.rubyonrails.org/classes/ActiveSupport/Concern.html
If don't, take a look at the first example on the link.
Moreover, your namespace is a little bit weird and misses some context. Here is an example with a dummy method :
require 'active_support/concern'
module MvaasPortal
include ActiveSupport::Concern
def an_instance_method
puts "Here!"
end
end
class Portal
include MvaasPortal
end
Portal.new.an_instance_method
=> "Here!"

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

How do I include an instance method inside a before_save callback in a plugin?

I'm creating a plugin and am having a hard time defining a before_save filter that calls an instance method I've just defined. Here's a quick sample:
module ValidatesAndFormatsPhones
def self.included(base)
base.send :extend, ClassMethods
end
module ClassMethods
def validates_and_formats_phones(field_names = [:phone])
send :include, InstanceMethods
# the following variations on calls to :format_phone_fields fail
before_save send(:format_phone_fields, field_names)
before_save format_phone_fields(field_names)
before_save lambda { send(:format_phone_fields, field_names) }
# EACH OF THE ABOVE RETURNS 'undefined_method :format_phone_fields'
end
end
module InstanceMethods
def format_phone_fields(fields = [:phone], *args)
do stuff...
end
end
end
ActiveRecord::Base.send :include, ValidatesAndFormatsPhones
I guess the question is, how do I change the context to the instance, instead of the class?
I'd prefer to call the instance method because the class shouldn't really have a method called 'format_phone_fields' but the instance should.
Thanks!
Include your method at the right moment: when you're extending the base class:
module ValidatesAndFormatsPhones
def self.included(base)
base.send :extend, ClassMethods
base.send :include, InstanceMethods
end
module ClassMethods
def validates_and_formats_phones(field_names = [:phone])
before_save {|r| r.format_phone_fields(field_names)}
end
end
module InstanceMethods
def format_phone_fields(fields = [:phone], *args)
# do stuff...
end
end
end
ActiveRecord::Base.send :include, ValidatesAndFormatsPhones
I haven't run the code, but it should work. I've done similar things often enough.
as you are using callback macros, you can only pass a symbol for the method you want to run, passing arguments is not possible. the 'workaround' from the rails documentation is to use a 'method string' that gets evaluated in the right context:
before_save 'self.format_phone_fields(....)'
another possibility: store your field names as a class variable and access that one in your instance, then you can use before_save :format_phone_fields

Rails - alias_method_chain with a 'attribute=' method

I'd like to 'add on' some code on a model's method via a module, when it is included. I think I should use alias_method_chain, but I don't know how to use it, since my 'aliased method' is one of those methods ending on the '=' sign:
class MyModel < ActiveRecord::Base
def foo=(value)
... do stuff with value
end
end
So this is what my module looks right now:
module MyModule
def self.included(base)
base.send(:include, InstanceMethods)
base.class_eval do
alias_method_chain 'foo=', :bar
end
end
module InstanceMethods
def foo=_with_bar(value) # ERROR HERE
... do more stuff with value
end
end
end
I get an error on the function definition. How do get around this?
alias_method_chain is a simple, two-line method:
def alias_method_chain( target, feature )
alias_method "#{target}_without_#{feature}", target
alias_method target, "#{target}_with_#{feature}"
end
I think the answer you want is to simply make the two alias_method calls yourself in this case:
alias_method :foo_without_bar=, :foo=
alias_method :foo=, :foo_with_bar=
And you would define your method like so:
def foo_with_bar=(value)
...
end
Ruby symbols process the trailing = and ? of method names without a problem.

Resources