Hey guys.
I have a rather weird question. I want to write a function that
programatically determines the arguments passed to its caller. Say, if i
do
$stuff = []
def bar(arg)
foo
end
def foo
first_arg = …
$stuff << first_arg
end
bar(42)
By this point, $stuff would be [42]
I know it is quite the contrived question, but I’m not interested in
doing
something practical. Any leads?
Stefan K. [email protected] writes:
foo
end
def foo
first_arg = …
$stuff << first_arg
end
bar(42)
I don’t think this is possible.
By this point, $stuff would be [42]
But to get this result you could write:
(def bar(*args)
($stuff = args)
end)
(bar 42)
$stuff → [42]
first_arg = …
$stuff << first_arg
end
bar(42)
By this point, $stuff would be [42]
I think the closest you’re going to get is for the calling function to
pass
its binding so that foo can read from it:
def bar(*args)
foo(binding)
end
=> nil
def foo(env)
puts env.eval(“args.first”)
end
=> nil
bar ‘something’
something
I don’t think you can do this transparently. You can’t even refer to the
argument list within a single method (I’m thinking of the ‘arguments’
object
in JavaScript) without mentioning the arguments by name.
So, suppose I want to get evil and go dig into frames and so on. I’ve
seen
ruby-debug do something close. Is there a part of the standard library
that
allows you to introspect that way (if so, I could not find it), or a gem
maybe? And if not and I’m bend on writing C code to achieve it, where
should
I start looking?
On Sun, Jan 25, 2009 at 9:30 PM, Stefan K. [email protected]
wrote:
So, suppose I want to get evil and go dig into frames and so on. I’ve seen
ruby-debug do something close. Is there a part of the standard library that
allows you to introspect that way (if so, I could not find it), or a gem
maybe? And if not and I’m bend on writing C code to achieve it, where should
I start looking?
Depends, for methods with *args and optional arguments you have
definitely some heavy lifting to do. For all other methods however
it’s easy:
method( caller.first.sub(/.*in ./,“”).sub(/.$/,“”) ).arity
Probably not good enough?
cheers
Robert
James C. wrote:
first_arg = …
$stuff << first_arg
end
bar(42)
By this point, $stuff would be [42]
I think the closest you’re going to get is for the calling function to
pass
its binding so that foo can read from it:
In which case, it might as well just pass its args instead
There is the Binding.of_caller hack which you can find through a google
search.
Maybe you could combine this with Kernel#local_variables or
Binding#local_variables to do what you want? That’s what debug.rb does
when /^\s*l(?:ocal)?\s*$/
var_list(eval("local_variables", binding), binding)
and just use eval to read them:
def var_list(ary, binding)
ary.sort!
for v in ary
stdout.printf " %s => %s\n", v, eval(v, binding).inspect
end
end
Thanks for the advice. I would have never had thought of something as
straightforward as evaling ‘local_variables’ in the binding.
Unfortunatelly,
I believe that this doesn’t yield the arguments if they are not named.
I’m actually trying, out of curiosity, to implement a very
straightforward
idea:
%w{more chunky bacon}.each { puts it.length }
Like, have it to actually be the ‘implicit blog argument’, not unlike
perl’s $_. Again, I’m doing that out of curiosity. Any ideas how I can
accomplish it without touching the interpreter’s C code?
And I have to override Array#each. I’ll also need to override #map,
#select
and so on. Not a good deal
On Tue, Jan 27, 2009 at 7:46 PM, Robert K.
2009/1/27 Stefan K. [email protected]:
perl’s $_. Again, I’m doing that out of curiosity. Any ideas how I can
accomplish it without touching the interpreter’s C code?
IMHO you can’t because the current value is only known by method
#each. Wait, you could cook something up using thread local variables
18:41:58 tmp$ ./it.rb
4
6
5
18:42:41 tmp$ cat it.rb
#!/bin/env ruby
def it
Thread.current[:each].last rescue nil
end
class Array
alias _each each
def each(&b)
stack = Thread.current[:each] ||= []
_each do |val|
stack.push val
begin
b.call
ensure
stack.pop
end
end
end
end
%w{more chunky bacon}.each { puts it.length }
18:42:42 tmp$
But note that you have to change all implementations of #each which
is difficult to achieve because classes can spring into existence all
the time. Alternatively write a global each which receives the
Enumerable as argument.
Cheers
robert