respond_to?() from top level of script - ruby

I can use respond_to?() from the main object in irb:
irb(main):001:0> def foo
irb(main):002:1> "hi"
irb(main):003:1> end
=> nil
irb(main):004:0> respond_to?(:foo)
=> true
irb(main):005:0> self
=> main
But when I put this into a script, it doesn't seem work as I'd expect:
$ cat test.rb
#! /usr/local/bin/ruby
def foo
"hi"
end
puts respond_to?(:foo)
puts self
$ ./test.rb
false
main
What's going on here?
EDIT:
The irb behavior works for me in 1.9.3, but not in 2.2.0. But regardless, is it possible to use respond_to?() as such from a script?
As an alternative, I can catch a NoMethodError from a call to send(), but that would also catch such exceptions from inside a valid method as well, which makes error handling a little convoluted.

Methods defined at the top level are made private methods of Object and by default respond_to? only returns true for public methods. To check for private and protected methods, set the include_all argument to true:
def foo
"hi"
end
puts respond_to?(:foo, true)
puts self
Now when the script is run, respond_to?(:foo, true) should return true:
$ ./test.rb
true
main

Related

Ruby messages to self

If I run the following Ruby code:
class Foo
def foo=(something)
puts "It's a #{something}"
end
def foo_assign
self.foo = "bar"
end
end
f = Foo.new
f.foo_assign
The output is: It's a bar.
On the other hand, if I run the code:
class Foo
def foo=(something)
puts "It's a #{something}"
end
def foo_assign
foo = "bar"
end
end
f = Foo.new
f.foo_assign
There is no output. How can that be given that foo and self.foo are equivalent?
foo = "bar" is ambiguous in the foo_assign method. Ruby thinks you are trying to assign a local variable and not call the method, so that's what it does for you. (imagine setting bar = "foo" on the next line, it would still work, even though there is no bar= method). In cases where it's ambiguous what you are trying to do or call, you have to use self.. Someone better than I might be able to explain this all better or more clearly, but that's what is happening here.

Ruby refinements subtleties

There is a pretty good documentation of the current implementation of refinements in ruby here:
http://ruby-doc.org//core-2.2.0/doc/syntax/refinements_rdoc.html,
but there are some strange corner cases.
First, include module is orthogonal to using module (one include the instance method of module while the other activates the refinement). But there is a trick to include a refinement module itself, see
Better way to turn a ruby class into a module than using refinements?.
def to_module(klass)
Module.new do
#note that we return the refinement module itself here
return refine(klass) {
yield if block_given?
}
end
end
class Base
def foo
"foo"
end
end
class Receiver
include to_module(Base) {
def foo
"refined " + super
end
}
end
Receiver.new.foo #=> "refined foo"
Strangely this refinement module can't be used with using!
m=to_module(Base) {}
m.class #=> Module
using m
#=>TypeError: wrong argument type Class (expected Module)
So using only work on the enclosing module of the refinement modules.
Secondly I wanted to use the above yield trick to be able to pass a Proc to refine (even through it only accepts a block), without resorting to converting the Proc back to source as in
https://www.new-bamboo.co.uk/blog/2014/02/05/refinements-under-the-knife/.
But using yield as in the include example does not work:
def ref_module1(klass)
Module.new do
refine(klass) {
yield
}
end
end
class Receiver1
using ref_module1(Base) {
def foo
"refined " + super
end
}
def bar
Base.new.foo
end
end
Receiver1.new.bar #=> NoMethodError: super: no superclass method `foo'
We see that Receiver1 still use Bar#foo and not the refined method.
Howewer we can use module_eval instead:
def ref_module2(klass,&b)
Module.new do
refine(klass) {
module_eval(&b)
}
end
end
class Receiver2
using ref_module2(Base) {
def foo
"refined " + super
end
}
def bar
Base.new.foo
end
end
Receiver2.new.bar #=> "refined foo"
I don't quite understand why module_eval works here and not the yield method. Inside the refinement block, the 'default_definee' is the refinement module, so module_eval which puts the 'default_definee' to self='the refinement module' should not affect it. And indeed in the 'include' example at the beginning, I get the same result when I use module_eval or a direct yield.
Can anyone explain this behavior?
Context (or binding) is the reason why module_eval works and yield doesn't in your last set of examples. It actually has nothing to do with refinements, as demonstrated below.
Starting with module_eval:
class Foo
def run(&block)
self.class.module_eval(&block)
end
end
foo = Foo.new
foo.run {
def hello
"hello"
end
}
puts foo.hello # => "hello"
puts hello => # '<main>': undefined method 'hello' for main:Object (NameError)
In Foo#run we call module_eval on Foo. This switches the context (self) to be Foo. The result is much like we had simple defined hello inside of class Foo originally.
Now let's take a look at yield:
class Foo
def run
yield
end
end
foo = Foo.new
foo.run {
def hello
"hello"
end
}
puts hello # => "hello"
puts foo.hello # => '<main>': private method 'hello' called for ...
yield simply invokes the block in its original context, which in this example would be <main>. When the block is invoked, the end result is exactly the same as if the method were defined at the top level normally:
class Foo
def run
yield
end
end
foo = Foo.new
def hello
"hello"
end
puts hello # => "hello"
puts foo.hello # => '<main>': private method 'hello' called for ...
You might notice that foo seems to have the hello method in the yield examples. This is a side effect of defining hello as a method at the top level. It turns out that <main> is just an instance of Object, and defining top level methods is really just defining private methods on Object which nearly everything else ends up inheriting. You can see this by opening up irb and running the following:
self # => main
self.class # => Object
def some_method
end
"string".method(:some_method) # => #<Method: String(Object)#some_method>
Now back to your examples.
Here's what happens in the yield example:
def ref_module1(klass)
Module.new do
refine(klass) {
yield
}
end
end
class Receiver1
# like my yield example, this block is going to
# end up being invoked in its original context
using ref_module1(Base) {
def foo
"I'm defined on Receiver1"
end
}
def bar
# calling foo here will simply call the original
# Base#foo method
Base.new.foo
end
end
# as expected, if we call Receiver1#bar
# we get the original Base#foo method
Receiver1.new.bar # => "foo"
# since the block is executed in its original context
# the method gets defined in Receiver1 -- its original context
Receiver1.new.foo # => "I'm defined on Receiver1"
As for module_eval, it works in your examples because it causes the block to be run in the context of the new module, rather than on the Receiver1 class.

Aliasing a setter method

class Test
attr_accessor :something
end
class Test
alias :old_something= :something=
def something=(a)
a += 2 # do something with the argument
old_something=(a) # then pass it on
end
end
I would expect that if I said
t = Test.new
t.something = 3
puts t.something
It would print out 5. But it prints nil. Why does this not work?
The form
foo = bar
assigns to a local variable. You need to make it explicit that you want to call a method:
self.foo = bar
Edit: #Jörg's answer is probably the real problem, but my original answer below may be helpful as well. Redacted some misleading details from my old answer.
If you drop the def something= entirely, you'll get a setter for something which has an alias of old_something:
class Test
attr_accessor :something
end
class Test
alias :old_something= :something=
end
1.9.3p327 :001 > require "./test.rb"
=> true
1.9.3p327 :002 > t = Test.new
=> #<Test:0x000000018eb740>
1.9.3p327 :003 > t.something = "blah"
=> "blah"
1.9.3p327 :004 > t.something
=> "blah"
1.9.3p327 :005 > t.old_something = "moo"
=> "moo"
1.9.3p327 :006 > t.something
=> "moo"
The reason is that #something is never assigned to anything.
This code will do what you are trying to do:
class Test
attr_accessor :something
end
class Test
alias :old_something= :something=
def something=(a)
#something = a += 2 # do something with the argument
old_something=(a) # then pass it on
end
end
t = Test.new
t.something=( 3)
puts t.something
The difference was assinging the instance variable #something to a value, and then allowing your code to increment it by the a variable that was passed in.

Ruby: How to map a function to a hash

I can't figure out how to assign a function call to a ruby hash.
What I want to do is to assign a function to a hash key, and later
call this function using the classic hash lookout syntax.
def Foo()
puts "bar"
end
puts "Assigning"
test = { "foo" => Foo() }
puts "Executing"
test["foo"]
This code fails, function Foo is called after puts "Assign", during hash creation, and nothing happens after puts "Executing"
def Foo()
puts "bar"
end
puts "Assigning"
test = { "foo" => Foo }
puts "Executing"
test["foo"]
with this code I receive an uninitialized constant Foo (NameError).
Finally with
def Foo()
puts "bar"
end
puts "Assigning"
test = { "foo" => :Foo }
puts "Executing"
test["foo"]
I get not outputs.
Any suggestions?
Thanks to all for answres and suggestions.
What I'm going to do is to test
if a hash based approach to call function is faster than
an equivalent code based on if / case statements.
funcs["foo"].call
fatser than
if func_name == "foo" then
Foo()
elsif ...
...
end
or
case func_name
when "foo"
Foo()
when ...
...
end
Obviously for a big number of functions (~150) and hundreds of
calling cycles
you could use lambda's instead of methods. Two options here:
hash = {:foo => lambda { puts 'bar } }
hash[:foo].call
the second (more complicated) is this:
irb(main):001:0> class Hash
irb(main):002:1> alias :orig_anc :'[]'
irb(main):003:1>
irb(main):004:1* def [](key)
irb(main):005:2> if orig_anc(key).is_a? Proc
irb(main):006:3> orig_anc(key).call
irb(main):007:3> else
irb(main):008:3* orig_anc(key)
irb(main):009:3> end
irb(main):010:2> end
irb(main):011:1> end
=> nil
irb(main):012:0> h = {:hello => 'world', :foo => lambda { puts 'bar' }}
=> {:hello=>"world", :foo=>#<Proc:0x843224c#(irb):12 (lambda)>}
irb(main):013:0> h[:hello]
=> "world"
irb(main):014:0> h[:foo]
bar
=> nil
irb(main):015:0>
The second one just allows you to skip using 'call' method
There is no easy possibility to make your function execute simply by retrieving the hash key withput overriding Hash's [] method, as Vlad pointed out, i.e.
def foo
puts "hi"
end
... # magic
test["foo"] # outputs hi
won't work. What you can do, though, is assign the method reference using Object#method and then invoke it using call:
def foo
puts "hi"
end
test = { "foo" => method(:foo) }
test["foo"].call # => hi
First things first, start method names with lower case letters. Then, moving to first example, Ruby is eager so test = { "foo" => Foo() } Foo is called.
In the second example, variables starting with an uppercase letter are considered constants, so Ruby looks for one and do not bother looking for a method. Take into account that if foo where a method then foo would be called as in the first example.
Third example: test["foo"] returns :Foo, a symbol. Nowhere in your code Foo() is invoked.
*Suggestions
1.- Take a look at identifiers nomenclature in Ruby.
2.- You may took a look at lambda, Proc and Object#method
3.- If you have some money to spare and don't mind buying a pair of books, give Programming Ruby and Metaprogramming in Ruby a chance (both can be bought in PragProg.
You could try using the hash constructor with a block, so
def foo
puts "hi"
end
test = Hash.new do |hash, key|
send(key) if respond_to?(key)
end
test["foo"] # prints "hi", returns nil

Including methods into blocks

Anyone know how to get this to work if it's possible?
class Foo
def self.go(&block)
class << block
include Bar
end
puts "Within Foo#go: #{block.methods.include? 'baz'}"
block.call
end
end
module Bar
def baz
puts "I'm happily in my place!"
end
end
Foo.go {
puts "Within actual block: #{methods.include? 'baz'}"
baz
}
This gets the output:
Within Foo#go: true
Within actual block: false
NameError: undefined local variable or method ‘baz’ for main:Object
EDIT: when I print out the block's class in Foo#go, it's Proc, but when I print it out within the Proc, it's Object. Could this be related?
You can't do this. The reason for what you're seeing is that there are two different contexts here. One is the context of the block, which closes over the context where it's defined. The other is the context of the Proc object wrapper, which is just the same as any other object context and completely unrelated to the context of the block itself.
I think the closest you'll get is to instance_eval the block using a context object that has the methods you want, but then the block won't have access to the self that existed where it was defined. It's up to you whether that makes sense for the method you want to write.
The other option is to pass the block an actual receiver for the baz method.
You could use eval with Proc#binding:
module Bar
def baz
puts "hi from baz!"
end
end
def go(&block)
eval('include Bar', block.binding)
block[]
end
baz #=> NameError
go { baz } #=> "hi from baz!"
baz #=> "hi from baz!"
But unless you use a mixin/mixout framework (like mixico or mixology), you'll be placing the methods from the included module into the lexical scope, so they'll still be accessible once the block returns.
require 'rubygems'
require 'mixico'
module Bar
def baz
puts "hi from baz!"
end
end
def go(&block)
Module.mix_eval(Bar, &block)
end
baz #=> NameError
go { baz } #=> "hi from baz!"
baz #=> NameError
Here's a good article on different ways to use a DSL from within a block.
Another alternative, following on from rampion, is to dup the context of the block before mixing into it, this way you're not messing up the context after you've finished.
module Bar
def baz
puts "hi from baz!"
end
end
def go(&block)
dup_context = eval('self', block.binding).dup
dup_context.send(:include, Bar)
dup_context.instance_eval &block
end
Note this will only be useful to you if you're not running any mutator methods in the block

Resources