I have a question about Ruby blocks.
For example I have a Ruby Class:
class NewClass
def initialize
#a = 1
end
def some_method
puts #a
end
end
When I do something like that:
NewClass.new do |c|
c.some_method
end
Everything is good, but is there any possibilities to do that somehow like:
NewClass.new do
some_method
end
Any ideas?
Your current code will just ignore the block anyway since you don't yield to it. For what you are trying to do in your first example you need the yield self idiom in initialize.
For why you need a block variable in the first place, think about what the receiver for some_method would be in your second example. Without an explicit receiver it's the top level main (unless this code is part of some other class of course, where that enclosing class would be self). See Dave Thomas' blog post Changing self in Ruby (or Yehuda Katz' post as pointed out by Niklas B. in the comments) for more info on that topic (the comments clear up the "proc invocation" part).
Edit: all that said, this seems to work, but I prefer the yield self version and example 1:
class NewClass
def initialize
#a = 1
end
def some_method
puts "Hello: ##a"
end
def self.build(&block)
x = self.new
x.instance_eval(&block)
x
end
end
NewClass.build do
some_method
end
This allows you to execute the block without a block variable and will return the new instance of the class for assigning to a variable etc.
class NewClass
def initialize(&block)
#a = 1
instance_eval(&block)
end
def some_method
puts #a
end
end
NewClass.new do
some_method
end
Using instance_eval should do the job. But unless you know what you are doing (and it's not just out of laziness) I'd advice against it, and go with your original approach.
def initialize(&block)
#a = 1
self.instance_eval(&block) if block_given?
end
Related
In Rails we can define a class like:
class Test < ActiveRecord::Base
before_initialize :method
end
and when calling Test.new, method() will be called on the instance. I'm trying to learn more about Ruby and class methods like this, but I'm having trouble trying to implement this in plain Ruby.
Here's what I have so far:
class LameAR
def self.before_initialize(*args, &block)
# somehow store the symbols or block to be called on init
end
def new(*args)
## Call methods/blocks here
super(*args)
end
end
class Tester < LameAR
before_initialize :do_stuff
def do_stuff
puts "DOING STUFF!!"
end
end
I'm trying to figure out where to store the blocks in self.before_initialize. I originally tried an instance variable like #before_init_methods, but that instance variable wouldn't exist in memory at that point, so I couldn't store or retrieve from it. I'm not sure how/where could I store these blocks/procs/symbols during the class definition, to later be called inside of new.
How could I implement this? (Either having before_initialize take a block/proc/list of symbols, I don't mind at this point, just trying to understand the concept)
For a comprehensive description, you can always check the Rails source; it is itself implemented in 'plain Ruby', after all. (But it handles lots of edge cases, so it's not great for getting a quick overview.)
The quick version is:
module MyCallbacks
def self.included(klass)
klass.extend(ClassMethods) # we don't have ActiveSupport::Concern either
end
module ClassMethods
def initialize_callbacks
#callbacks ||= []
end
def before_initialize(&block)
initialize_callbacks << block
end
end
def initialize(*)
self.class.initialize_callbacks.each do |callback|
instance_eval(&callback)
end
super
end
end
class Tester
include MyCallbacks
before_initialize { puts "hello world" }
end
Tester.new
Left to the reader:
arguments
calling methods by name
inheritance
callbacks aborting a call and supplying the return value
"around" callbacks that wrap the original invocation
conditional callbacks (:if / :unless)
subclasses selectively overriding/skipping callbacks
inserting new callbacks elsewhere in the sequence
... but eliding all of those is what [hopefully] makes this implementation more approachable.
One way would be by overriding Class#new:
class LameAR
def self.before_initialize(*symbols_or_callables, &block)
#before_init_methods ||= []
#before_init_methods.concat(symbols_or_callables)
#before_init_methods << block if block
nil
end
def self.new(*args, &block)
obj = allocate
#before_init_methods.each do |symbol_or_callable|
if symbol_or_callable.is_a?(Symbol)
obj.public_send(symbol_or_callable)
else
symbol_or_callable.(obj)
end
end
obj.__send__(:initialize, *args, &block)
end
end
class Tester < LameAR
before_initialize :do_stuff
def do_stuff
puts "DOING STUFF!!"
end
end
I am working on a project of context-oriented programming in ruby. And I come to this problem:
Suppose that I have a class Klass:
class Klass
def my_method
proceed
end
end
I also have a proc stored inside a variable impl. And impl contains { puts "it works!" }.
From somewhere outside Klass, I would like to define a method called proceed inside the method my_method. So that if a call Klass.new.my_method, I get the result "it works".
So the final result should be something like that:
class Klass
def my_method
def proceed
puts "it works!"
end
proceed
end
end
Or if you have any other idea to make the call of proceed inside my_method working, it's also good. But the proceed of another method (let's say my_method_2) isn't the same as my_method.
In fact, the proceed of my_method represent an old version of my_method. And the proceed of my_method_2 represent an old version of my_method_2.
Thanks for your help
Disclaimer: you are doing it wrong!
There must be more robust, elegant and rubyish way to achieve what you want. If you still want to abuse metaprogramming, here you go:
class Klass
def self.proceeds
#proceeds ||= {}
end
def def_proceed
self.class.proceeds[caller.first[/`.*?'/]] = Proc.new
end
def proceed *args
self.class.proceeds[caller.first[/`.*?'/]].(*args)
end
def m_1
def_proceed { puts 1 }
proceed
end
def m_2
def_proceed { puts 2 }
proceed
end
end
inst = Klass.new
inst.m_1
#⇒ 1
inst.m_2
#⇒ 2
What you in fact need, is Module#prepend and call super from there.
One way of doing that is to construct a hash whose keys are the names of the methods calling proceed and whose values are procs that represent the implementations of proceed for each method calling it.
class Klass
singleton_class.send(:attr_reader, :proceeds)
#proceeds = {}
def my_method1(*args)
proceed(__method__,*args)
end
def my_method2(*args)
proceed(__method__,*args)
end
def proceed(m, *args)
self.class.proceeds[m].call(*args)
end
end
def define_proceed(m, &block)
Klass.proceeds[m] = Proc.new &block
end
define_proceed(:my_method1) { |*arr| arr.sum }
define_proceed(:my_method2) { |a,b| "%s-%s" % [a,b] }
k = Klass.new
k.my_method1(1,2,3) #=> 6
k.my_method2("cat", "dog") #=> "cat-dog"
I can take a block of code, instance_exec it, and get the proper result. I would like to take a method off a different object and call one of it's methods in my scope. When I take a method from a different object, turn it into a proc, and then instance_exec it, I don't get the expected result. Code follows.
class Test1
def ohai(arg)
"magic is #{#magic} and arg is #{arg}"
end
end
class Test2
def initialize
#magic = "MAGICAL!"
end
def scope_checking
#magic
end
def do_it
ohai = Test1.new.method(:ohai)
self.instance_exec("foobar", &ohai)
end
end
describe "Test2 and scopes" do
before do
#t2 = Test2.new
end
it "has MAGICAL! in #magic" do
#t2.scope_checking.should == "MAGICAL!"
end
# This one fails :(
it "works like I expect converting a method to a proc" do
val = #t2.do_it
val.should == "magic is MAGICAL! and arg is foobar"
end
it "should work like I expect" do
val = #t2.instance_exec do
"#{#magic}"
end
val.should == "MAGICAL!"
end
end
It seems that, in Ruby, methods defined using def some_method are bound permanently to the class they're defined in.
So, when you call .to_proc on them they keep the binding of their original implementation, and you cannot rebind them. Well, you can, but only to an object of the same type as the first one. It's possible I could do some fancyness with inheritance, but I don't think so.
The solution becomes instead of using methods, I just put actual Procs into variables and use them then, as they're not bound until execution time.
not sure how good of an idea this is, but this passes your tests:
class Test1
def ohai(arg, binding)
eval('"magic is #{#magic} "', binding).to_s + "and arg is #{arg}"
end
end
class Test2
def initialize
#magic = "MAGICAL!"
end
def scope_checking
#magic
end
def get_binding
return binding()
end
def do_it
self.instance_exec(get_binding) {|binding| Test1.new.ohai("foobar", binding) }
end
end
I want to intercept method calls on a ruby-class and being able to do something before and after the actual execution of the method. I tried the following code, but get the error:
MethodInterception.rb:16:in before_filter': (eval):2:inalias_method': undefined method
say_hello' for classHomeWork'
(NameError)
from (eval):2:in `before_filter'
Can anybody help me to do it right?
class MethodInterception
def self.before_filter(method)
puts "before filter called"
method = method.to_s
eval_string = "
alias_method :old_#{method}, :#{method}
def #{method}(*args)
puts 'going to call former method'
old_#{method}(*args)
puts 'former method called'
end
"
puts "going to call #{eval_string}"
eval(eval_string)
puts "return"
end
end
class HomeWork < MethodInterception
before_filter(:say_hello)
def say_hello
puts "say hello"
end
end
I just came up with this:
module MethodInterception
def method_added(meth)
return unless (#intercepted_methods ||= []).include?(meth) && !#recursing
#recursing = true # protect against infinite recursion
old_meth = instance_method(meth)
define_method(meth) do |*args, &block|
puts 'before'
old_meth.bind(self).call(*args, &block)
puts 'after'
end
#recursing = nil
end
def before_filter(meth)
(#intercepted_methods ||= []) << meth
end
end
Use it like so:
class HomeWork
extend MethodInterception
before_filter(:say_hello)
def say_hello
puts "say hello"
end
end
Works:
HomeWork.new.say_hello
# before
# say hello
# after
The basic problem in your code was that you renamed the method in your before_filter method, but then in your client code, you called before_filter before the method was actually defined, thus resulting in an attempt to rename a method which doesn't exist.
The solution is simple: Don't Do That™!
Well, okay, maybe not so simple. You could simply force your clients to always call before_filter after they have defined their methods. However, that is bad API design.
So, you have to somehow arrange for your code to defer the wrapping of the method until it actually exists. And that's what I did: instead of redefining the method inside the before_filter method, I only record the fact that it is to be redefined later. Then, I do the actual redefining in the method_added hook.
There is a tiny problem in this, because if you add a method inside of method_added, then of course it will immediately get called again and add the method again, which will lead to it being called again, and so on. So, I need to guard against recursion.
Note that this solution actually also enforces an ordering on the client: while the OP's version only works if you call before_filter after defining the method, my version only works if you call it before. However, it is trivially easy to extend so that it doen't suffer from that problem.
Note also that I made some additional changes that are unrelated to the problem, but that I think are more Rubyish:
use a mixin instead of a class: inheritance is a very valuable resource in Ruby, because you can only inherit from one class. Mixins, however, are cheap: you can mix in as many as you want. Besides: can you really say that Homework IS-A MethodInterception?
use Module#define_method instead of eval: eval is evil. 'Nuff said. (There was absolutely no reason whatsoever to use eval in the first place, in the OP's code.)
use the method wrapping technique instead of alias_method: the alias_method chain technique pollutes the namespace with useless old_foo and old_bar methods. I like my namespaces clean.
I just fixed some of the limitations I mentioned above, and added a few more features, but am too lazy to rewrite my explanations, so I repost the modified version here:
module MethodInterception
def before_filter(*meths)
return #wrap_next_method = true if meths.empty?
meths.delete_if {|meth| wrap(meth) if method_defined?(meth) }
#intercepted_methods += meths
end
private
def wrap(meth)
old_meth = instance_method(meth)
define_method(meth) do |*args, &block|
puts 'before'
old_meth.bind(self).(*args, &block)
puts 'after'
end
end
def method_added(meth)
return super unless #intercepted_methods.include?(meth) || #wrap_next_method
return super if #recursing == meth
#recursing = meth # protect against infinite recursion
wrap(meth)
#recursing = nil
#wrap_next_method = false
super
end
def self.extended(klass)
klass.instance_variable_set(:#intercepted_methods, [])
klass.instance_variable_set(:#recursing, false)
klass.instance_variable_set(:#wrap_next_method, false)
end
end
class HomeWork
extend MethodInterception
def say_hello
puts 'say hello'
end
before_filter(:say_hello, :say_goodbye)
def say_goodbye
puts 'say goodbye'
end
before_filter
def say_ahh
puts 'ahh'
end
end
(h = HomeWork.new).say_hello
h.say_goodbye
h.say_ahh
Less code was changed from original. I modified only 2 line.
class MethodInterception
def self.before_filter(method)
puts "before filter called"
method = method.to_s
eval_string = "
alias_method :old_#{method}, :#{method}
def #{method}(*args)
puts 'going to call former method'
old_#{method}(*args)
puts 'former method called'
end
"
puts "going to call #{eval_string}"
class_eval(eval_string) # <= modified
puts "return"
end
end
class HomeWork < MethodInterception
def say_hello
puts "say hello"
end
before_filter(:say_hello) # <= change the called order
end
This works well.
HomeWork.new.say_hello
#=> going to call former method
#=> say hello
#=> former method called
Jörg W Mittag's solution is pretty nice. If you want something more robust (read well tested) the best resource would be the rails callbacks module.
class A
def a
puts 'in #a'
end
end
class B < A
def a
b()
end
def b
# here i want to call A#a.
end
end
class B < A
alias :super_a :a
def a
b()
end
def b
super_a()
end
end
There's no nice way to do it, but you can do A.instance_method(:a).bind(self).call, which will work, but is ugly.
You could even define your own method in Object to act like super in java:
class SuperProxy
def initialize(obj)
#obj = obj
end
def method_missing(meth, *args, &blk)
#obj.class.superclass.instance_method(meth).bind(#obj).call(*args, &blk)
end
end
class Object
private
def sup
SuperProxy.new(self)
end
end
class A
def a
puts "In A#a"
end
end
class B<A
def a
end
def b
sup.a
end
end
B.new.b # Prints in A#a
If you don't explicitly need to call A#a from B#b, but rather need to call A#a from B#a, which is effectively what you're doing by way of B#b (unless you're example isn't complete enough to demonstrate why you're calling from B#b, you can just call super from within B#a, just like is sometimes done in initialize methods. I know this is kind of obvious, I just wanted to clarify for any Ruby new-comers that you don't have to alias (specifically this is sometimes called an "around alias") in every case.
class A
def a
# do stuff for A
end
end
class B < A
def a
# do some stuff specific to B
super
# or use super() if you don't want super to pass on any args that method a might have had
# super/super() can also be called first
# it should be noted that some design patterns call for avoiding this construct
# as it creates a tight coupling between the classes. If you control both
# classes, it's not as big a deal, but if the superclass is outside your control
# it could change, w/o you knowing. This is pretty much composition vs inheritance
end
end