I'm essentially trying to do this
module Foo
##do_something = Proc.new { puts "something" }
end
Foo::do_something.()
The properties need to be procs/lambdas so that they can be passed as arguments to another proc/lambda/method:
def other_method(do_something)
do_something.()
end
other_method(Foo::do_something)
Currently this is working:
module Foo
DO_SOMETHING = Proc.new { puts "something" }
end
Foo::DO_SOMETHING.()
But I'm guessing this won't pass code review. Is this the only way?
You could simply create an accessor for the class variable:
module Foo
def self.do_something
##do_something
end
##do_something = Proc.new { puts "something" }
end
Foo::do_something.()
something
or any of the following:
Foo::do_something[]
Foo::do_something.call
Foo::do_something.yield
Related
I was wondering how to pass a block to a method which will make the method return on yield.
The naive aproach doesn't work:
def run(&block)
block.call
end
run { return :foo } # => LocalJumpError
Wrapping in another proc has the same effect:
def run(&block)
proc { block.call }.call
end
run { return :bar } # => LocalJumpError
So I thought that the return statement is bound to the receiver of the current binding. However, trying it out with instance_eval proved me wrong:
class ProcTest
def run(&block)
puts "run: #{[binding.local_variables, binding.receiver]}"
instance_eval(&block)
end
end
pt = ProcTest.new
binding_inspector = proc { puts "proc: #{[binding.local_variables, binding.receiver]}" }
puts "main: #{[binding.local_variables, binding.receiver]}"
# => main: [[:pt, :binding_inspector], main]
binding_inspector.call
# => proc: [[:pt, :binding_inspector], main]
pt.run(&binding_inspector)
# => run: [[:block], #<ProcTest:0x007f4987b06508>]
# => proc: [[:pt, :binding_inspector], #<ProcTest:0x007f4987b06508>]
pt.run { return :baz }
# => run: [[:block], #<ProcTest:0x007f4987b06508>]
# => LocalJumpError
So the questions are:
How can this be done?
How is the return context tied to the return statement. Is this connection accessible via the language's API?
Was this implemented in such manner intentionally? If yes - why? If no - what are the obstacles to fix it?
I thought that the return statement is bound to the receiver of the current binding.
Only methods have an receiver. return is not a method:
defined? return #=> "expression"
Trying to invoke it as a method doesn't work:
def foo
send(:return, 123)
end
foo #=> undefined method `return'
trying it out with instance_eval proved me wrong
Though instance_eval evaluates the block in the context of the receiver (so you have access to the receivers instance methods and instance variables):
class MyClass
def foo(&block)
#var = 123
instance_eval(&block)
end
end
MyClass.new.foo { instance_variables }
#=> [:#var]
... it does not evaluate the block in the current binding (so you don't have access to any local variables):
class MyClass
def foo(&block)
var = 123
instance_eval(&block)
end
end
MyClass.new.foo { local_variables }
#=> []
How can this be done?
You could use eval, but that requires a string:
def foo
var = 123
eval yield
nil
end
foo { "return var * 2" }
#=> 246
Or by passing the binding to the block (again using eval):
def foo
var = 123
yield binding
nil
end
foo { |b| b.eval "return var * 2" }
#=> 246
return in a block returns from the enclosing method when the block is defined (ie, the closure in which the block is created). In your example, there is no enclosing block to return from, hence your exception.
This is easily demonstrated:
def foo(&block)
puts yield
puts "we won't get here"
end
def bar
foo { return "hi from the block"; puts "we never get here" }
puts "we never get here either"
end
puts bar # => "hi from the block" (only printed once; the puts in `foo` is not executed)
Return in a proc will immediately return out of the proc, not out of the method on the stack under the proc:
def foo(&block)
puts yield
puts "we will get here"
end
def bar
foo &->{ return "hi from the proc"; puts "we never get here" }
puts "we will get here too"
end
puts bar
# hi from the proc # puts from foo
# we will get here # puts from foo
# we will get here too # puts from bar
Because of these behaviors, there is no way to achieve your desired behavior, in which a return in the given block will execute a return in the method from which the block is invoked, unless the block was defined within that scope, since doing so would require one of the existing behaviors not work.
You could achieve something like this with throw...catch, which is kinda-sorta useful as a way to zip up the stack from an arbitrary depth, but you can't return arbitrary values with it:
def foo(&block)
yield
puts "we won't get here"
end
catch(:escape) do
foo &->{ throw :escape }
end
According to the documentation for modules and classes, calling super (without arguments or parentheses) calls the parent method with the same arguments:
When used without any arguments super uses the arguments given to the subclass method.
Assigning a new value to the "argument variable" seems to alter this behavior:
class MyClass
def foo(arg)
puts "MyClass#foo(#{arg.inspect})"
end
end
class MySubclass < MyClass
def foo(arg)
puts "MySubclass#foo(#{arg.inspect})"
super
arg = 'new value'
super
end
end
MySubclass.new.foo('inital value')
Output:
MySubclass#foo("inital value")
MyClass#foo("inital value")
MyClass#foo("new value") # <- not the argument given to MySubclass#foo
Is this expected?
Update
This seems to be the expected behavior for positional and keyword arguments, but it doesn't work for block arguments:
class MyClass
def foo(&block)
puts "MyClass#foo { #{block.call.inspect} }"
end
end
class MySubclass < MyClass
def foo(&block)
puts "MySubclass#foo { #{block.call.inspect} }"
super
block = Proc.new { 'new value' }
super
end
end
MySubclass.new.foo { 'initial value' }
Output:
MySubclass#foo { "initial value" }
MyClass#foo { "initial value" }
MyClass#foo { "initial value" }
Lets take one example from the Ruby core:
Keyword2
class Base
def single(a) a end
def double(a, b) [a,b] end
def array(*a) a end
def optional(a = 0) a end
def keyword(**a) a end
end
class Keyword2 < Base
def keyword(foo: "keyword2")
foo = "changed1"
x = super
foo = "changed2"
y = super
[x, y]
end
end
Now, see the test case :-
def test_keyword2
assert_equal([{foo: "changed1"}, {foo: "changed2"}], Keyword2.new.keyword)
end
Above example exactly mathes the keyword documentation.
Called with no arguments and no empty argument list, super calls the appropriate method with the same arguments, and the same code block, as those used to call the current method. Called with an argument list or arguments, it calls the appropriate methods with exactly the specified arguments (including none, in the case of an empty argument list indicated by empty parentheses).
same arguments means it is saying the current values of argument variables.test_super.rb files contains all the varieties of stuffs we can do with super in Ruby.
No, it work with block too (taken from core) :
a = Class.new do
def foo
yield
end
end
b = Class.new(a) do
def foo
super{
"b"
}
end
end
b.new.foo{"c"} # => "b"
But, have no idea why the below is giving "c"? This is actually the updated question of the OP:
c = Class.new do
def foo(&block)
block.call
end
end
d = Class.new(c) do
def foo(&block)
block = -> { "b" }
super
end
end
d.new.foo{"c"} # => "c"
It seems to be the expected behavior, based on the RubySpec anyway.
module RestArgsWithSuper
class A
def a(*args)
args
end
end
class B < A
def a(*args)
args << "foo"
super
end
end
end
(language/fixtures/super.rb).
It's then expected that the arguments are modified:
it "passes along modified rest args when they weren't originally empty" do
Super::RestArgsWithSuper::B.new.a("bar").should == ["bar", "foo"]
end
(language/super_spec.rb)
It's the expected behaviour. Technically, arg is the same argument, it just points to another value.
This answer might explain it better: https://stackoverflow.com/a/1872159/163640
Heres what I have/want:
module Observable
def observers; #observers; end
def trigger(event, *args)
good = true
return good unless (#observers ||= {})[event]
#obersvers[event].each { |e| good = false and break unless e.call(self, args) }
good
end
def on(event, &block)
#obersvers ||= {}
#obersvers[event] ||= []
#observers[event] << block
end
end
class Item < Thing
include Observable
def pickup(pickuper)
return unless trigger(:before_pick_up, pickuper)
pickuper.add_to_pocket self
trigger(:after_pick_up, pickuper)
end
def drop(droper)
return unless trigger(:before_drop, droper)
droper.remove_from_pocket self
trigger(:after_drop, droper)
end
# Lots of other methods
end
# How it all should work
Item.new.on(:before_pickup) do |item, pickuper|
puts "Hey #{pickuper} thats my #{item}"
return false # The pickuper never picks up the object
end
While starting on trying to create a game in Ruby, I thought it would be great if it could be based all around Observers and Events. The problem is have to write all of these triggers seems to be a waste, as it seems like a lot of duplicated code. I feel there must be some meta programming method out there to wrap methods with functionality.
Ideal Sceanrio:
class CustomBaseObject
class << self
### Replace with correct meta magic
def public_method_called(name, *args, &block)
return unless trigger(:before_+name.to_sym, args)
yield block
trigger(:after_+name.to_sym, args)
end
###
end
end
And then I have all of my object inherit from this Class.
I'm still new to Ruby's more advanced meta programming subjects, so any knowledge about this type of thing would be awesome.
There are a several ways to do it with the help of metaprogramming magic. For example, you can define a method like this:
def override_public_methods(c)
c.instance_methods(false).each do |m|
m = m.to_sym
c.class_eval %Q{
alias #{m}_original #{m}
def #{m}(*args, &block)
puts "Foo"
result = #{m}_original(*args, &block)
puts "Bar"
result
end
}
end
end
class CustomBaseObject
def test(a, &block)
puts "Test: #{a}"
yield
end
end
override_public_methods(CustomBaseObject)
foo = CustomBaseObject.new
foo.test(2) { puts 'Block!' }
# => Foo
Test: 2
Block!
Bar
In this case, you figure out all the required methods defined in the class by using instance_methods and then override them.
Another way is to use so-called 'hook' methods:
module Overrideable
def self.included(c)
c.instance_methods(false).each do |m|
m = m.to_sym
c.class_eval %Q{
alias #{m}_original #{m}
def #{m}(*args, &block)
puts "Foo"
result = #{m}_original(*args, &block)
puts "Bar"
result
end
}
end
end
end
class CustomBaseObject
def test(a, &block)
puts "Test: #{a}"
yield
end
include Overrideable
end
The included hook, defined in this module, is called when you include that module. This requires that you include the module at the end of the class definition, because included should know about all the already defined methods. I think it's rather ugly :)
I want to be able to define a block, and later evaluate that block from within a dynamically generated module/class. It seems like I could accomplish this somehow using eval and block.binding, but I haven't figured it out.
I have this as the base:
def define_module(name, &block)
name = name.to_s.gsub(/\/(.?)/) { "::#{$1.upcase}" }.gsub(/(?:^|_)(.)/) { $1.upcase }
parts = name.split("::")
parts.each_with_index do |part, index|
sub_name = parts[0..index].join("::")
eval("module #{sub_name}; end")
end
clazz = eval(name)
clazz.class_eval(&block) if block_given?
clazz
end
def add_module(name, &block)
module_block = block
define_module(name).class_eval <<-EOF
def self.included(base)
base.class_eval do
# something like this, I'm stuck
instance_eval(&#{module_block})
end
end
EOF
end
And I want to use it like this:
add_module("My::Library") do
def a_method
"added 'a_method'"
end
end
class ::User
include My::Library
end
user = ::User.new
assert_equal "added 'a_method'", user.a_method
Is there any way to do something like that?
This works:
def add_module(name, &block)
define_module(name).class_eval do
class << self; self; end.send(:define_method, :included) { |base|
base.class_eval(&block)
}
end
end
add_module("My::Library") do
def a_method
"added 'a_method'"
end
end
class ::User
include My::Library
end
user = ::User.new
user.a_method #=> "added a_method"
EDIT:
Why don't you just do this instead? Much simpler, and it's actually the job of a module:
def add_module(name, &block)
define_module(name).class_eval(&block)
end
In Ruby, is there a way to redefine a method of a particular instance of a class using a proc? For example:
class Foo
def bar()
return "hello"
end
end
x = Foo.new
y = Foo.new
(Something like):
y.method(:bar) = lambda { return "goodbye" }
x.bar
y.bar
Producing:
hello
goodbye
Thanks.
def define_singleton_method_by_proc(obj, name, block)
metaclass = class << obj; self; end
metaclass.send(:define_method, name, block)
end
p = proc { "foobar!" }
define_singleton_method_by_proc(y, :bar, p)
or, if you want to monkey-patch Object to make it easy
class Object
# note that this method is already defined in Ruby 1.9
def define_singleton_method(name, callable = nil, &block)
block ||= callable
metaclass = class << self; self; end
metaclass.send(:define_method, name, block)
end
end
p = proc { "foobar!" }
y.define_singleton_method(:bar, p)
#or
y.define_singleton_method(:bar) do
"foobar!"
end
or, if you want to define your proc inline, this may be more readable
class << y
define_method(:bar, proc { "foobar!" })
end
or,
class << y
define_method(:bar) { "foobar!" }
end
this is the most readable, but probably doesn't fit your needs
def y.bar
"goodbye"
end
This question is highly related
I'm not sure what version of Ruby this was added in (at least 1.8.7), but there seems to be an even simpler way of doing this:
str1 = "Hello"
str2 = "Goodbye"
def str1.to_spanish
"Hola"
end
puts str1 # => Hello
puts str1.to_spanish # => Hola
puts str2 # => Goodbye
puts str2.to_spanish # => Throws a NoMethodError
Learnt about this whilst reading the Ruby Koans (about_class_methods.rb lesson).
I'm still not entirely sure what the purpose of this is since it seems a bit dangerous to me.
You can use the syntax class <<object to get an object's "singleton class" (that's a special parent class belonging only to that object) and define methods only for that instance. For example:
str1 = "Hello"
str2 = "Foo"
class <<str1
def to_spanish
'Hola'
end
end
Now if you do str1.to_spanish, it will return "Hola", but str2.to_spanish will give you a NoMethodFound exception.