Stack Level too deep with method_added ruby












1















I have created a module to hook methods before a method call in a class :



module Hooks

def self.included(base)
base.send :extend, ClassMethods
end


module ClassMethods
# everytime we add a method to the class we check if we must redifine it
def method_added(method)
if @hooker_before.present? && @methods_to_hook_before.include?(method)
hooked_method = instance_method(@hooker_before)
@methods_to_hook_before.each do |method_name|
begin
method_to_hook = instance_method(method_name)
rescue NameError => e
return
end
define_method(method_name) do |*args, &block|
hooked_method.bind(self).call
method_to_hook.bind(self).(*args, &block) ## your old code in the method of the class
end
end
end
end

def before(*methods_to_hooks, hookers)
@methods_to_hook_before = methods_to_hooks
@hooker_before = hookers[:call]
end
end
end


I have included the module in one of my class :



require_relative 'hooks'

class Block
include Indentation
include Hooks
attr_accessor :file, :indent
before :generate, call: :indent
# after :generate, call: :write_end

def initialize(file, indent=nil)
self.file = file
self.indent = indent
end

def generate
yield
end
end


this Block class is parent to another class that is implementing its own version of the generate method and that is actually implemented.



When my code is running method_added is actually called with method :generate as argument in some kind of infinite loop. I can't figure out why method_added is caught in this infinite loop. Do you know what's wrong with this code ?
Here's a link to the full code :
link to code on github










share|improve this question


















  • 1





    The tiniest of points: you can write base.send :extend, ClassMethods as simply base.extend ClassMethods.

    – Cary Swoveland
    Nov 26 '18 at 21:44
















1















I have created a module to hook methods before a method call in a class :



module Hooks

def self.included(base)
base.send :extend, ClassMethods
end


module ClassMethods
# everytime we add a method to the class we check if we must redifine it
def method_added(method)
if @hooker_before.present? && @methods_to_hook_before.include?(method)
hooked_method = instance_method(@hooker_before)
@methods_to_hook_before.each do |method_name|
begin
method_to_hook = instance_method(method_name)
rescue NameError => e
return
end
define_method(method_name) do |*args, &block|
hooked_method.bind(self).call
method_to_hook.bind(self).(*args, &block) ## your old code in the method of the class
end
end
end
end

def before(*methods_to_hooks, hookers)
@methods_to_hook_before = methods_to_hooks
@hooker_before = hookers[:call]
end
end
end


I have included the module in one of my class :



require_relative 'hooks'

class Block
include Indentation
include Hooks
attr_accessor :file, :indent
before :generate, call: :indent
# after :generate, call: :write_end

def initialize(file, indent=nil)
self.file = file
self.indent = indent
end

def generate
yield
end
end


this Block class is parent to another class that is implementing its own version of the generate method and that is actually implemented.



When my code is running method_added is actually called with method :generate as argument in some kind of infinite loop. I can't figure out why method_added is caught in this infinite loop. Do you know what's wrong with this code ?
Here's a link to the full code :
link to code on github










share|improve this question


















  • 1





    The tiniest of points: you can write base.send :extend, ClassMethods as simply base.extend ClassMethods.

    – Cary Swoveland
    Nov 26 '18 at 21:44














1












1








1








I have created a module to hook methods before a method call in a class :



module Hooks

def self.included(base)
base.send :extend, ClassMethods
end


module ClassMethods
# everytime we add a method to the class we check if we must redifine it
def method_added(method)
if @hooker_before.present? && @methods_to_hook_before.include?(method)
hooked_method = instance_method(@hooker_before)
@methods_to_hook_before.each do |method_name|
begin
method_to_hook = instance_method(method_name)
rescue NameError => e
return
end
define_method(method_name) do |*args, &block|
hooked_method.bind(self).call
method_to_hook.bind(self).(*args, &block) ## your old code in the method of the class
end
end
end
end

def before(*methods_to_hooks, hookers)
@methods_to_hook_before = methods_to_hooks
@hooker_before = hookers[:call]
end
end
end


I have included the module in one of my class :



require_relative 'hooks'

class Block
include Indentation
include Hooks
attr_accessor :file, :indent
before :generate, call: :indent
# after :generate, call: :write_end

def initialize(file, indent=nil)
self.file = file
self.indent = indent
end

def generate
yield
end
end


this Block class is parent to another class that is implementing its own version of the generate method and that is actually implemented.



When my code is running method_added is actually called with method :generate as argument in some kind of infinite loop. I can't figure out why method_added is caught in this infinite loop. Do you know what's wrong with this code ?
Here's a link to the full code :
link to code on github










share|improve this question














I have created a module to hook methods before a method call in a class :



module Hooks

def self.included(base)
base.send :extend, ClassMethods
end


module ClassMethods
# everytime we add a method to the class we check if we must redifine it
def method_added(method)
if @hooker_before.present? && @methods_to_hook_before.include?(method)
hooked_method = instance_method(@hooker_before)
@methods_to_hook_before.each do |method_name|
begin
method_to_hook = instance_method(method_name)
rescue NameError => e
return
end
define_method(method_name) do |*args, &block|
hooked_method.bind(self).call
method_to_hook.bind(self).(*args, &block) ## your old code in the method of the class
end
end
end
end

def before(*methods_to_hooks, hookers)
@methods_to_hook_before = methods_to_hooks
@hooker_before = hookers[:call]
end
end
end


I have included the module in one of my class :



require_relative 'hooks'

class Block
include Indentation
include Hooks
attr_accessor :file, :indent
before :generate, call: :indent
# after :generate, call: :write_end

def initialize(file, indent=nil)
self.file = file
self.indent = indent
end

def generate
yield
end
end


this Block class is parent to another class that is implementing its own version of the generate method and that is actually implemented.



When my code is running method_added is actually called with method :generate as argument in some kind of infinite loop. I can't figure out why method_added is caught in this infinite loop. Do you know what's wrong with this code ?
Here's a link to the full code :
link to code on github







ruby module hook






share|improve this question













share|improve this question











share|improve this question




share|improve this question










asked Nov 26 '18 at 18:46









David GeismarDavid Geismar

82821439




82821439








  • 1





    The tiniest of points: you can write base.send :extend, ClassMethods as simply base.extend ClassMethods.

    – Cary Swoveland
    Nov 26 '18 at 21:44














  • 1





    The tiniest of points: you can write base.send :extend, ClassMethods as simply base.extend ClassMethods.

    – Cary Swoveland
    Nov 26 '18 at 21:44








1




1





The tiniest of points: you can write base.send :extend, ClassMethods as simply base.extend ClassMethods.

– Cary Swoveland
Nov 26 '18 at 21:44





The tiniest of points: you can write base.send :extend, ClassMethods as simply base.extend ClassMethods.

– Cary Swoveland
Nov 26 '18 at 21:44












1 Answer
1






active

oldest

votes


















6














You've caused an infinite recursion because you're calling define_method inside method_added. The stack trace (which you haven't provided unfortunately) should show this.



A slightly ugly workaround to resolve this could be to explicitly set a variable (e.g. @_adding_a_method) and use it as a guard clause for method_added:



module ClassMethods
def method_added(method)
return if @_adding_a_method

if @hooker_before.present? && @methods_to_hook_before.include?(method)
# ...

@_adding_a_method = true
define_method(method_name) do |*args, &block|
# ...
end
@_adding_a_method = false

# ...
end
end
end


However, taking a step back, I'm not really sure what this module is trying to achieve. Couldn't you just achieve this with Module#prepend instead of this meta-programming?



This code reminds me of what you might find in an old Ruby 1.8/1.9 tutorial on advanced meta-programming techniques; Module#prepend makes such workarounds redundant for the most part.






share|improve this answer
























  • An alternative to the @_adding_a_method option is to remove the method name from @methods_to_hook_before before calling define_method. Instead of @methods_to_hook_before.each do |method_name|, use something like while method_name = @methods_to_hook_before.shift

    – showaltb
    Nov 26 '18 at 20:16











Your Answer






StackExchange.ifUsing("editor", function () {
StackExchange.using("externalEditor", function () {
StackExchange.using("snippets", function () {
StackExchange.snippets.init();
});
});
}, "code-snippets");

StackExchange.ready(function() {
var channelOptions = {
tags: "".split(" "),
id: "1"
};
initTagRenderer("".split(" "), "".split(" "), channelOptions);

StackExchange.using("externalEditor", function() {
// Have to fire editor after snippets, if snippets enabled
if (StackExchange.settings.snippets.snippetsEnabled) {
StackExchange.using("snippets", function() {
createEditor();
});
}
else {
createEditor();
}
});

function createEditor() {
StackExchange.prepareEditor({
heartbeatType: 'answer',
autoActivateHeartbeat: false,
convertImagesToLinks: true,
noModals: true,
showLowRepImageUploadWarning: true,
reputationToPostImages: 10,
bindNavPrevention: true,
postfix: "",
imageUploader: {
brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
allowUrls: true
},
onDemand: true,
discardSelector: ".discard-answer"
,immediatelyShowMarkdownHelp:true
});


}
});














draft saved

draft discarded


















StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53487250%2fstack-level-too-deep-with-method-added-ruby%23new-answer', 'question_page');
}
);

Post as a guest















Required, but never shown

























1 Answer
1






active

oldest

votes








1 Answer
1






active

oldest

votes









active

oldest

votes






active

oldest

votes









6














You've caused an infinite recursion because you're calling define_method inside method_added. The stack trace (which you haven't provided unfortunately) should show this.



A slightly ugly workaround to resolve this could be to explicitly set a variable (e.g. @_adding_a_method) and use it as a guard clause for method_added:



module ClassMethods
def method_added(method)
return if @_adding_a_method

if @hooker_before.present? && @methods_to_hook_before.include?(method)
# ...

@_adding_a_method = true
define_method(method_name) do |*args, &block|
# ...
end
@_adding_a_method = false

# ...
end
end
end


However, taking a step back, I'm not really sure what this module is trying to achieve. Couldn't you just achieve this with Module#prepend instead of this meta-programming?



This code reminds me of what you might find in an old Ruby 1.8/1.9 tutorial on advanced meta-programming techniques; Module#prepend makes such workarounds redundant for the most part.






share|improve this answer
























  • An alternative to the @_adding_a_method option is to remove the method name from @methods_to_hook_before before calling define_method. Instead of @methods_to_hook_before.each do |method_name|, use something like while method_name = @methods_to_hook_before.shift

    – showaltb
    Nov 26 '18 at 20:16
















6














You've caused an infinite recursion because you're calling define_method inside method_added. The stack trace (which you haven't provided unfortunately) should show this.



A slightly ugly workaround to resolve this could be to explicitly set a variable (e.g. @_adding_a_method) and use it as a guard clause for method_added:



module ClassMethods
def method_added(method)
return if @_adding_a_method

if @hooker_before.present? && @methods_to_hook_before.include?(method)
# ...

@_adding_a_method = true
define_method(method_name) do |*args, &block|
# ...
end
@_adding_a_method = false

# ...
end
end
end


However, taking a step back, I'm not really sure what this module is trying to achieve. Couldn't you just achieve this with Module#prepend instead of this meta-programming?



This code reminds me of what you might find in an old Ruby 1.8/1.9 tutorial on advanced meta-programming techniques; Module#prepend makes such workarounds redundant for the most part.






share|improve this answer
























  • An alternative to the @_adding_a_method option is to remove the method name from @methods_to_hook_before before calling define_method. Instead of @methods_to_hook_before.each do |method_name|, use something like while method_name = @methods_to_hook_before.shift

    – showaltb
    Nov 26 '18 at 20:16














6












6








6







You've caused an infinite recursion because you're calling define_method inside method_added. The stack trace (which you haven't provided unfortunately) should show this.



A slightly ugly workaround to resolve this could be to explicitly set a variable (e.g. @_adding_a_method) and use it as a guard clause for method_added:



module ClassMethods
def method_added(method)
return if @_adding_a_method

if @hooker_before.present? && @methods_to_hook_before.include?(method)
# ...

@_adding_a_method = true
define_method(method_name) do |*args, &block|
# ...
end
@_adding_a_method = false

# ...
end
end
end


However, taking a step back, I'm not really sure what this module is trying to achieve. Couldn't you just achieve this with Module#prepend instead of this meta-programming?



This code reminds me of what you might find in an old Ruby 1.8/1.9 tutorial on advanced meta-programming techniques; Module#prepend makes such workarounds redundant for the most part.






share|improve this answer













You've caused an infinite recursion because you're calling define_method inside method_added. The stack trace (which you haven't provided unfortunately) should show this.



A slightly ugly workaround to resolve this could be to explicitly set a variable (e.g. @_adding_a_method) and use it as a guard clause for method_added:



module ClassMethods
def method_added(method)
return if @_adding_a_method

if @hooker_before.present? && @methods_to_hook_before.include?(method)
# ...

@_adding_a_method = true
define_method(method_name) do |*args, &block|
# ...
end
@_adding_a_method = false

# ...
end
end
end


However, taking a step back, I'm not really sure what this module is trying to achieve. Couldn't you just achieve this with Module#prepend instead of this meta-programming?



This code reminds me of what you might find in an old Ruby 1.8/1.9 tutorial on advanced meta-programming techniques; Module#prepend makes such workarounds redundant for the most part.







share|improve this answer












share|improve this answer



share|improve this answer










answered Nov 26 '18 at 20:07









Tom LordTom Lord

15.3k22951




15.3k22951













  • An alternative to the @_adding_a_method option is to remove the method name from @methods_to_hook_before before calling define_method. Instead of @methods_to_hook_before.each do |method_name|, use something like while method_name = @methods_to_hook_before.shift

    – showaltb
    Nov 26 '18 at 20:16



















  • An alternative to the @_adding_a_method option is to remove the method name from @methods_to_hook_before before calling define_method. Instead of @methods_to_hook_before.each do |method_name|, use something like while method_name = @methods_to_hook_before.shift

    – showaltb
    Nov 26 '18 at 20:16

















An alternative to the @_adding_a_method option is to remove the method name from @methods_to_hook_before before calling define_method. Instead of @methods_to_hook_before.each do |method_name|, use something like while method_name = @methods_to_hook_before.shift

– showaltb
Nov 26 '18 at 20:16





An alternative to the @_adding_a_method option is to remove the method name from @methods_to_hook_before before calling define_method. Instead of @methods_to_hook_before.each do |method_name|, use something like while method_name = @methods_to_hook_before.shift

– showaltb
Nov 26 '18 at 20:16




















draft saved

draft discarded




















































Thanks for contributing an answer to Stack Overflow!


  • Please be sure to answer the question. Provide details and share your research!

But avoid



  • Asking for help, clarification, or responding to other answers.

  • Making statements based on opinion; back them up with references or personal experience.


To learn more, see our tips on writing great answers.




draft saved


draft discarded














StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53487250%2fstack-level-too-deep-with-method-added-ruby%23new-answer', 'question_page');
}
);

Post as a guest















Required, but never shown





















































Required, but never shown














Required, but never shown












Required, but never shown







Required, but never shown

































Required, but never shown














Required, but never shown












Required, but never shown







Required, but never shown







Popular posts from this blog

Contact image not getting when fetch all contact list from iPhone by CNContact

count number of partitions of a set with n elements into k subsets

A CLEAN and SIMPLE way to add appendices to Table of Contents and bookmarks