As we know, wo can pass a method to a iterator method by a &: prefix.
For example:
["a", "b"].map(&:upcase) #=> ["A", "B"]
def rettwo
2
end
["a", "b"].map(&:rettwo) #=> [2, 2]
Here is the problem, when I write a method, pass a method with &: prefix to it, I got a error message: "ArgumentError: no receiver given".
Let me show the code:
def a_simple_method &proc
puts proc.class # it shows `Proc`
proc.call
end
def a_iterator_method
puts yield
end
a_simple_method &:rettwo #=> ArgumentError: no receiver given
a_iterator_method &:rettwo #=> ArgumentError: no receiver given
What do I missing, How the map like method of Array handle it
Here's what works. Explanation below.
class String
def rettwo
self + self
end
end
def a_simple_method &proc
proc.call('a')
end
def a_iterator_method
yield 'b'
end
a_simple_method(&:rettwo) # => "aa"
a_iterator_method(&:rettwo) # => "bb"
The &: construct is called Symbol#to_proc. It turns symbol into a proc. This proc expects a receiver as a first argument. The remaining arguments are used to call the proc. You're not passing any arguments, hence the "receiver not given" error.
Here's a demonstration of additional arguments:
class String
def say name
"#{self} #{name}"
end
end
def a_simple_method &proc
proc.call('hello', 'ruby')
end
a_simple_method(&:say) # => "hello ruby"
Here's a definition of Symbol#to_proc from some blog post from 2008. Modern Symbol#to_proc seems to be implemented in C, but this can still help the understanding.
class Symbol
def to_proc
Proc.new { |*args| args.shift.__send__(self, *args) }
end
end
Related
I would like to create an object that accepts method names and prints them out. I should be able to call any method on it. For example,
obj.hello("was")
# => called hello with argument 'was'
obj.ok(["df", 1])
# => called ok with argument ["df", 1]
I don't want to define hello or ok in advance.
Is that possible?
Easy:
class Noop
def method_missing(m, *args)
puts "#{m} #{args.inspect}"
end
end
Noop.new.foo
# => foo []
Noop.new.bar(1,2,3)
# => bar [1, 2, 3]
method_missing is called on every Ruby object when you call a method that does not exist. It usually ends up being handled by Object (the superclass of everything) which raises NoMethodError.
Note that this does not apply to the methods provided by its ancestors (Class, Module, Object, Kernel, BasicObject) which you can inspect by:
class Noop
puts self.instance_methods.inspect
puts self.methods.inspect
def method_missing(m, *args)
puts "#{m} #{args.inspect}"
end
end
I understand that
def a(&block)
block.call(self)
end
and
def a()
yield self
end
lead to the same result, if I assume that there is such a block a {}. My question is - since I stumbled over some code like that, whether it makes any difference or if there is any advantage of having (if I do not use the variable/reference block otherwise):
def a(&block)
yield self
end
This is a concrete case where i do not understand the use of &block:
def rule(code, name, &block)
#rules = [] if #rules.nil?
#rules << Rule.new(code, name)
yield self
end
The only advantage I can think of is for introspection:
def foo; end
def bar(&blk); end
method(:foo).parameters #=> []
method(:bar).parameters #=> [[:block, :blk]]
IDEs and documentation generators could take advantage of this. However, it does not affect Ruby's argument passing. When calling a method, you can pass or omit a block, regardless of whether it is declared or invoked.
The main difference between
def pass_block
yield
end
pass_block { 'hi' } #=> 'hi'
and
def pass_proc(&blk)
blk.call
end
pass_proc { 'hi' } #=> 'hi'
is that, blk, an instance of Proc, is an object and therefore can be passed to other methods. By contrast, blocks are not objects and therefore cannot be passed around.
def pass_proc(&blk)
puts "blk.is_a?(Proc)=#{blk.is_a?(Proc)}"
receive_proc(blk)
end
def receive_proc(proc)
proc.call
end
pass_proc { 'ho' }
blk.is_a?(Proc)=true
#=> "ho"
I'm trying to add an instance method foo to Ruby's Array class
so when it's invoked, the array's string elements are changed to string "foo".
This can be done easily by monkey patching Ruby's String and Array classes.
class String
def foo
replace "foo"
end
end
class Array
def foo
self.each {|x| x.foo if x.respond_to? :foo }
end
end
a = ['a', 1, 'b']
a.foo
puts a.join(", ") # you get 'foo, 1, foo' as expected
Now I'm trying to rewrite the above using Ruby 2's refinements feature.
I'm using Ruby version 2.2.2.
The following works (in a file, eg. ruby test.rb, but not in irb for some reason)
module M
refine String do
def foo
replace "foo"
end
end
end
using M
s = ''
s.foo
puts s # you get 'foo'
However, I can't get it to work when adding foo onto the Array class.
module M
refine String do
def foo
replace "foo"
end
end
end
using M
module N
refine Array do
def foo
self.each {|x| x.foo if x.respond_to? :foo }
end
end
end
using N
a = ['a', 1, 'b']
a.foo
puts a.join(", ") # you get 'a, 1, b', not 'foo, 1, foo' as expected
There're two issues:
After you refine a class with a new method, respond_to? does not work even when you can invoke
the method on an object. Try adding puts 'yes' if s.respond_to? :foo
as the last line in the second code snippet, you'll see 'yes' is not printed.
In my Array refinement, the String#foo is out of scope. If you remove if x.respond_to? :foo from
the Array#foo, you'll get the error undefined method 'foo' for "a":String (NoMethodError). So the question is: how do you make the String#foo refinement visible inside the Array#foo refinement?
How do I overcome these two issues so I can get this to work?
(Please don't offer alternative solutions that don't involve refinement, because this is a theoretical exercise so I can learn how to use refinement).
Thank you.
The respond_to? method does not work and this is documented
here.
The problem is that you can only activate a refinement at top-level
and they are lexical in scope.
One solution would be:
module N
refine String do
def foo
replace 'foobar'
end
end
refine Array do
def foo
self.each do |x|
x.foo rescue x
end
end
end
end
using N
a = ['a', 1, 'b']
p a.foo
puts a.join(", ") # foo, 1, foo
Taking up your example again, a simple solution could be to override the respond_to? method in refinement block :
module M
refine String do
def foo
replace "foo"
end
def respond_to?(name,all=false)
list_methods = self.methods.concat [:foo]
list_methods.include? name
end
end
refine Array do
def foo
self.each {|x| x.foo if x.respond_to? :foo }
end
end
end
using M
a = ['a', 1, 'b']
a.foo
puts a.join(", ") # you get 'foo, 1, foo'
instance_eval method change self in its block, eg:
class D; end
d = D.new
d.instance_eval do
puts self # print something like #<D:0x8a6d9f4>, not 'main'!
end
If we define a method ourself(or any other methods(other than instance_eval) which takes a block), when print self, we will get 'main', which is different from instance_eval method.eg:
[1].each do |e|
puts self # print 'main'
end
How can i define a method(which takes a block) like instance_eval?
Thanks in advance.
You can write a method that accepts a proc argument, and then pass that as a proc argument to instance_eval.
class Foo
def bar(&b)
# Do something here first.
instance_eval &b
# Do something else here afterward, call it again, etc.
end
end
Foo.new.bar { puts self }
Yields
#<Foo:0x100329f00>
It's obvious:
class Object
def your_method(*args, &block)
instance_eval &block
end
end
receiver = Object.new
receiver.your_method do
puts self #=> it will print the self of receiver
end
Is there equivalent of python __getattr__ in ruby (for finding methods at least)?
class X(object):
def __getattr__(self, name):
return lambda x: print("Calling " + name + ": " + x)
x = X()
x.some_method("some args")
So it could be something like:
class X
# .. ??? ..
def default_action(method_name, x)
puts "Calling {method_name}: {x}"
end
end
x = X.new()
x.some_method("some args")
Yes. If an object does not respond to a message, Ruby will send a method_missing message with the message selector and the arguments to the receiver:
class X
def method_missing(selector, *args, &blk)
puts "The message was #{selector.inspect}."
puts "The arguments were #{args.map(&:inspect).join(', ')}."
puts "And there was #{blk ? 'a' : 'no'} block."
super
end
end
x = X.new
x.some_method('some args', :some_other_args, 42)
# The message was :some_method.
# The arguments were "some args", :some_other_args, 42.
# And there was no block.
# NoMethodError: undefined method `some_method'
x.some_other_method do end
# The message was :some_other_method.
# The arguments were .
# And there was a block.
# NoMethodError: undefined method `some_other_method'
Note that if you define method_missing, you should also define respond_to_missing? accordingly. Otherwise you get strange behavior like this:
x.respond_to?(:foo) # => false
x.foo # Works. Huh?
In this particular case, we handle all messages, therefore we can simply define it as follows:
class X; def respond_to_missing?(*) true end end
x.respond_to?(:foo) # => true
class X
def method_missing(sym,*args)
puts "Method #{sym} called with #{args}"
end
end
a = X.new
a.blah("hello","world")
#=> Method blah called with ["hello", "world"]
IIRC, you can define method_missing in ruby classes to handle this. Sorry that I can't provide specifics.
class Test
def say
puts "hi"
end
end
and you can invoke say method by
obj = Test.new
obj.send "say"
and checking the method availability using
obj.respond_to? "say"
finally, put together all
if (obj.respond_to? "say")
obj.send "say"
end