I have a class that stores blocks, and I would like to run a block later. How can I run it?
class StoreBlocks
##blockarray = []
def initialize
#storedblock = yield
##blockarray << self
end
attr_reader :storedblock
def self.getblock
##blockarray
end
end
StoreBlocks.new do
# cool codey stuff
end
# do other things
How would I run StoreBlocks.getblock[0].storedblock?
= yield would assign the return value of passed block, not the block itself. You would need to change your class to something like this:
class StoreBlocks
attr_accessor :storedblock
##blockarray
def initialize &block
#storedblock = block
##blockarray << self
end
def self.getblock
##blockarray
end
end
& means that block passed to initialize will be converted to Proc and assigned to argument named block, and than you can store it in instance variable. You could than call it like:
StoreBlocks.getblock[0].storedblock.call()
yield will imediately call the block passed to initalize. This means that #storeblock will contain the result of calling yield not the block itself. FYI, you are not putting #storedblock into ##blockarray
If you want to store a block you can do something like this -
class BlockStore
def initialize
#blocks ||= []
end
def add_block(&block)
#blocks << block
end
def run_all_blocks
#blocks.each do |block|
block.call
end
end
end
block_store = BlockStore.new
block_store.add_block do
puts 'Hello!'
end
block_store.add_block do
puts 'World!'
end
puts 'About to run all blocks'
block_store.run_all_blocks
This gives you an output like the below. I've output 'About to run all blocks' before running the blocks to show that they are stored and then run later -
➜ ruby blocks.rb
About to run all blocks
Hello!
World!
Related
Ruby has the File class that can be initialized using the normal new() method, or using open() and passing a block. How would I write a class that behaved like this?
File.open("myfile.txt","r") do |f|
...
end
This is a simple example of passing a block to new/open method
class Foo
def initialize(args, &block)
if block_given?
p block.call(args) # or do_something
else
#do_something else
end
end
def self.open(args, &block)
if block_given?
a = new(args, &block) # or do_something
else
#do_something else
end
ensure
a.close
end
def close
puts "closing"
end
end
Foo.new("foo") { |x| "This is #{x} in new" }
# >> "This is foo in new"
Foo.open("foo") { |x| "This is #{x} in open" }
# >> "This is foo in open"
# >> closing
The general outline of File.open is something like this:
def open(foo, bar)
f = do_opening_stuff(foo, bar)
begin
yield f
ensure
do_closing_stuff(f)
end
end
It is yield that invokes the block passed by the caller. Putting do_closing_stuff within an ensure guarantees that the file gets closed even if the block raises an exception.
More nitty-gritty on the various ways of calling blocks here: http://innig.net/software/ruby/closures-in-ruby
You can create a class method which creates an instance, yields it, and then then performs cleanup after the yield.
class MyResource
def self.open(thing, otherthing)
r = self.new(thing, otherthing)
yield r
r.close
end
def initialize(thing, otherthing)
#thing = thing
#otherthing = otherthing
end
def do_stuff
puts "Doing stuff with #{#thing} and #{#otherthing}"
end
def close
end
end
Now, you can either use it with a constructor:
r = MyResource.new(1, 2)
r.do_stuff
r.close
or using a block, which automatically closes the object:
MyResource.open(1, 2) do |r|
r.do_stuff
end
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
Assuming I have the following proc:
a = Proc.new do
puts "start"
yield
puts "end"
end
Also assuming I pass a to another method which subsequently calls instance_eval on another class with that block, how can I now pass a block onto the end of that method which gets yielded in a.
For example:
def do_something(a,&b)
AnotherClass.instance_eval(&a) # how can I pass b to a here?
end
a = Proc.new do
puts "start"
yield
puts "end"
end
do_something(a) do
puts "this block is b!"
end
Output should of course should be:
start
this block is b!
end
How can I pass the secondary block to a in the instance_eval?
I need something like this for the basis of a Ruby templating system I'm working on.
You can't use yield in a. Rather, you have to pass a Proc object. This would be the new code:
def do_something(a,&b)
AnotherClass.instance_exec(b, &a)
end
a = Proc.new do |b|
puts "start"
b.call
puts "end"
end
do_something(a) do
puts "this block is b!"
end
yield is only for methods. In this new code, I used instance_exec (new in Ruby 1.9) which allows you to pass parameters to the block. Because of that, we can pass the Proc object b as a parameter to a, which can call it with Proc#call().
a=Proc.new do |b|
puts "start"
b.call
puts "end"
end
def do_something(a,&b)
AnotherClass.instance_eval { a.call(b) }
end
I'm not sure that's the right title for this question, but I don't know how else to ask it. I have classes that need to be registered globally so they can be called later. I have most of it working except for a very important part. When the child inherits from the parent class, it registers a new instance, but when the on_message class method is called, I can't figure out how to set the instance variables that I need.
class MyExtension < ExtensionBase
on_message '/join (.+)' do |username|
# this will be a callback function used later
end
end
class ExtensionBase
def self.inherited(child)
MainAppModule.registered_extensions << child.new
end
def self.on_message(string, &block)
# these need to be set on child instance
#regex = Regexp.new(string)
#on_message_callback = block
end
def exec(message)
args = #regex.match(message).captures
#on_message_callback.call(args)
end
end
# somewhere else in the code, I find the class that I need...
MainAppModule.registered_extensions.each do |child|
puts child.regex.inspect # this is nil and I dont want it to be
if message =~ child.regex
return child.exec(message)
end
end
How can I design this so that the #regex will be set so I can access it within the loop?
I finally found a solution that works, and I have added now the whole code that is executable. Just store the code e.g. in file callexample.rb and call it by ruby callexample.rb
The main difference of my solution to the question is that the call to on_message now creates the instance with the relevant arguments and registers the created instance. Therefore I have deleted the method inherited because I don't need it any more.
I have added some puts statements to demonstrate in which order the code works.
class MainAppModule ## Added class
##registered_extensions = []
def self.registered_extensions; ##registered_extensions; end
end
class ExtensionBase
attr_reader :regex
def self.on_message(string, &block)
MainAppModule.registered_extensions << self.new(string, block)
end
def initialize(string, block)
#regex = Regexp.new(string)
#on_message_callback = block
end
def exec(message)
args = #regex.match(message).captures
#on_message_callback.call(args)
end
end
class MyExtension < ExtensionBase
on_message '/join (.+)' do |username|
# this will be a callback function used later
puts "Callback of #{self} called."
"returnvalue"
end
end
# somewhere else in the code, I find the class that I need...
MainAppModule.registered_extensions.each do |child|
puts "Value of regex: #{child.regex}" # this is no more nil
message = '/join something'
if message =~ child.regex
puts "On match evalue 'child.exec(message)' to: #{child.exec(message)}"
end
end
I'm writing a function to dynamically create classes. I run into a problem with the *vars variable (below), where inside the block passed to Class::new, the "def initialize" method cannot see the value of *vars (and thus Ruby reports a unbound variable error on *vars).
What to do?
Thanks!
class MyParentClass
def do_something_with(*args)
end
def do_something_else_with(*vars)
end
end
def create_class(class_name,*vars)
new_class = Class::new(MyParentClass) do
def initialize(*args)
super
do_something_with(args)
do_something_else_with(vars)
end
end
Object::const_set(class_name.intern,new_class)
end
# Ruby: Error: *vars is unbound variable
# My notes: *vars is in scope inside the do..end block passed to Class::new, but cannot be seen inside def initialize (why?) . And, how to fix this?
I am not sure what are you trying to achieve with this contraption, but after some changes it works (well, depending on your definition of "to work"):
class MyParentClass
def do_something_with(*args)
puts "something #{args.inspect}"
end
def do_something_else_with(*vars)
puts "else #{vars.inspect}"
end
end
def create_class(class_name,*vars)
new_class = Class::new(MyParentClass) do
define_method :initialize do |*args|
super()
do_something_with(*args)
do_something_else_with(*vars)
end
end
Object::const_set(class_name.intern,new_class)
end
create_class :MyClass, 1, :foo, :bar
MyClass.new(2, :baz)
The trick to make vars visible is to define the constructor using a closure.