Stack Level too deep with method_added ruby
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
add a comment |
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
1
The tiniest of points: you can writebase.send :extend, ClassMethods
as simplybase.extend ClassMethods
.
– Cary Swoveland
Nov 26 '18 at 21:44
add a comment |
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
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
ruby module hook
asked Nov 26 '18 at 18:46
David GeismarDavid Geismar
82821439
82821439
1
The tiniest of points: you can writebase.send :extend, ClassMethods
as simplybase.extend ClassMethods
.
– Cary Swoveland
Nov 26 '18 at 21:44
add a comment |
1
The tiniest of points: you can writebase.send :extend, ClassMethods
as simplybase.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
add a comment |
1 Answer
1
active
oldest
votes
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.
An alternative to the@_adding_a_method
option is to remove the method name from@methods_to_hook_before
before callingdefine_method
. Instead of@methods_to_hook_before.each do |method_name|
, use something likewhile method_name = @methods_to_hook_before.shift
– showaltb
Nov 26 '18 at 20:16
add a comment |
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
});
}
});
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
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
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.
An alternative to the@_adding_a_method
option is to remove the method name from@methods_to_hook_before
before callingdefine_method
. Instead of@methods_to_hook_before.each do |method_name|
, use something likewhile method_name = @methods_to_hook_before.shift
– showaltb
Nov 26 '18 at 20:16
add a comment |
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.
An alternative to the@_adding_a_method
option is to remove the method name from@methods_to_hook_before
before callingdefine_method
. Instead of@methods_to_hook_before.each do |method_name|
, use something likewhile method_name = @methods_to_hook_before.shift
– showaltb
Nov 26 '18 at 20:16
add a comment |
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.
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.
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 callingdefine_method
. Instead of@methods_to_hook_before.each do |method_name|
, use something likewhile method_name = @methods_to_hook_before.shift
– showaltb
Nov 26 '18 at 20:16
add a comment |
An alternative to the@_adding_a_method
option is to remove the method name from@methods_to_hook_before
before callingdefine_method
. Instead of@methods_to_hook_before.each do |method_name|
, use something likewhile 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
add a comment |
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.
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
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
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
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
1
The tiniest of points: you can write
base.send :extend, ClassMethods
as simplybase.extend ClassMethods
.– Cary Swoveland
Nov 26 '18 at 21:44