[ANN] parseargs-0.0.0

URLS

   http://raa.ruby-lang.org/search.rhtml?search=parseargs
   http://codeforpeople.com/lib/ruby/parseargs

ABOUT

   parseargs is a library that faciltates the parsing of arguments and keywords
   from method paramters, setting of default values, validation, contraints via
   class based or duck based typing, and coercion/convincing to type/ducktype
   when possible.

HISTORY

   0.0.0

     initial version

AUTHOR

   ara [dot] t [dot] howard [at] noaa [dot] gov

SAMPLES

   <========< sample/a.rb >========>

   ~ > ruby -e'puts(IO::read(ARGV.shift))' sample/a.rb

     require 'parseargs'
     include ParseArgs

···

#
     # simple use will declare which required and optional arguments a method can
     # accept. default values may be specifed if a hash is given as the argument
     # specification. an exception is thrown if required arguments are not given at
     # method invocation.
     #

     def method(*a)
       pa =
         parseargs(a) {
           required_argument :a
           optional_argument :b => 2
         }

       puts "#{ pa.a }#{ pa.b }"
     end

     method 4

   ~ > ruby sample/a.rb

     42

   <========< sample/b.rb >========>

   ~ > ruby -e'puts(IO::read(ARGV.shift))' sample/b.rb

     require 'parseargs'
     include ParseArgs

     #
     # keywords can be declared in exactly the same way, and can be given at
     # invocation as either strings or symbols. default values may also be named for
     # both arguments and keywords. note that a required keyword with a default
     # specified is really an optional keyword :wink:
     #

     def method(*a)
       pa =
         parseargs(a) {
           required_argument :a
           required_keyword :b, :default => 2
           optional_keyword :c
         }

       puts "#{ pa.a }#{ pa.b }"
     end

     method 4, 'b' => 2

   ~ > ruby sample/b.rb

     42

   <========< sample/c.rb >========>

   ~ > ruby -e'puts(IO::read(ARGV.shift))' sample/c.rb

     require 'parseargs'
     include ParseArgs

     #
     # several abbreviations exist to make the declaration more compact.
     #

     def method(*a)
       pa =
         parseargs(a) {
           r_arg :a
           r_kw :b
           o_kw :c
         }

       if pa.c
         puts "#{ pa.c }"
       else
         puts "#{ pa.a }#{ pa.b }"
       end
     end

     method 4, :b => 2
     method 4, :b => 2, 'c' => 42

   ~ > ruby sample/c.rb

     42

   <========< sample/d.rb >========>

   ~ > ruby -e'puts(IO::read(ARGV.shift))' sample/d.rb

     require 'parseargs'
     include ParseArgs

     #
     # many keywords or arguments can be specified at once using a list
     #

     def method(*a)
       pa =
         parseargs(a) {
           r_arg 'req_arg'
           o_kw ('a'..'z').to_a
         }

       kw = pa.a ? pa.a : pa.z

       puts "#{ pa.req_arg }#{ kw }"
     end

     method 4, 'a' => 2
     method 4, 'z' => 2

   ~ > ruby sample/d.rb

     42

   <========< sample/e.rb >========>

   ~ > ruby -e'puts(IO::read(ARGV.shift))' sample/e.rb

     require 'parseargs'
     include ParseArgs

     #
     # a single, or list of types may be given and the argument of invocation must be
     # one of those types as reports by '==='
     #

     def method(*a)
       pa =
         parseargs(a) {
           req_arg 'number', 'types' => [Float, Fixnum]
         }

       p pa.number
     end

     method 42
     method 42.0
     method '42.0'

   ~ > ruby sample/e.rb

     42
     42.0
     ./lib/parseargs.rb:112:in `value=': value given not of type(Float,Fixnum) in 'number=' (TypeError)
       from ./lib/parseargs.rb:296:in `parse'
       from ./lib/parseargs.rb:291:in `each'
       from ./lib/parseargs.rb:291:in `parse'
       from ./lib/parseargs.rb:393:in `parseargs'
       from ./lib/parseargs.rb:376:in `parseargs'
       from sample/e.rb:11:in `method'
       from sample/e.rb:20

   <========< sample/f.rb >========>

   ~ > ruby -e'puts(IO::read(ARGV.shift))' sample/f.rb

     require 'parseargs'
     include ParseArgs

     #
     # failure of an argument to match a given type will cause coercion to be applied
     # if specified. the coerce argument may be either a method name or a proc that
     # receives the obj to be coerced. in either case the return value is used as
     # the new argument.
     #

     def method(*a)
       pa =
         parseargs(a) {
           arg :a, :types => Fixnum, :coerce => 'to_i'
           kw :b, :types => Fixnum, :coerce => lambda{|obj| obj.to_i}
         }

       p [pa.a, pa.b]
     end

     method 4, :b => 2
     method '4', :b => '2'

   ~ > ruby sample/f.rb

     [4, 2]

   <========< sample/g.rb >========>

   ~ > ruby -e'puts(IO::read(ARGV.shift))' sample/g.rb

     require 'parseargs'
     include ParseArgs

     #
     # ducktyping is supported as a method name, or list of method names - all of
     # which an argument must respond_to? in order for an exception not to be thrown.
     # this is a very simple ducktype signature.
     #

     def method(*a)
       pa =
         parseargs(a) {
           r_arg 'string', 'ducktype' => [:upcase, :downcase]
         }

       puts pa.string.upcase.downcase
     end

     method ['42']

   ~ > ruby sample/g.rb

     42

   <========< sample/h.rb >========>

   ~ > ruby -e'puts(IO::read(ARGV.shift))' sample/h.rb

     require 'parseargs'
     include ParseArgs

     #
     # a kind of ducktype 'coercion' - somthing i'm calling 'convincing' - may be
     # applied in order to convince an object that it really can play a certain role.
     # an argument may be 'convinced' via a module, which will be used to extend the
     # object on the fly, or a block which is passed the object and expected to
     # modify it's singleton class in such a way as to allow the object to become a
     # valid argument
     #

     module M
       def quack
         2
       end
     end

     def method(*a)
       pa =
         parseargs(a) {
           ra 'a', 'ducktype' => :quack,
              'convince' => lambda{|obj| class << obj; def quack; 4; end; end}

           ra 'b', 'ducktype' => :quack,
              'convince' => M
         }

       puts "#{ pa.a.quack }#{ pa.b.quack }"
     end

     method 'any ol', 'objects'

   ~ > ruby sample/h.rb

     42

   <========< sample/i.rb >========>

   ~ > ruby -e'puts(IO::read(ARGV.shift))' sample/i.rb

     require 'parseargs'

     #
     # of course all this would be kind of silly for the simple cases shown - but it
     # can become very powerful as a form of meta-programming aid or when many
     # kewords are to be supported
     #

     class Command
       include ParseArgs

       KEYWORDS = %w(
         verbose
         quiet
         log
         help
         info
         force
         recursive
         stdin
         stdout
         stderr
       )

       PARSER =
         ParseArgs::Parser::new {
           KEYWORDS.each{|kw| optional_keyword kw}
         }

       def initialize cmd
         @cmd = cmd
       end

       def execute(*argv)
         opts = PARSER.parse argv
         p @cmd => opts.select{|k,v| not v.nil?}
       end

       def background(*argv)
         opts = PARSER.parse argv
         p @cmd => opts.select{|k,v| not v.nil?}
       end

     end

     foo = Command::new 'foo.exe'
     foo.execute 'verbose' => true, 'stdin' => 'input.txt'
     bar = Command::new 'bar.exe'
     bar.execute 'verbose' => false, 'stdout' => 'output.txt'

   ~ > ruby sample/i.rb

     {"foo.exe"=>[["stdin", "input.txt"], ["verbose", true]]}
     {"bar.exe"=>[["stdout", "output.txt"], ["verbose", false]]}

CAVEATS

   this library is experimental.

enjoy.

-a
--

email :: ara [dot] t [dot] howard [at] noaa [dot] gov
phone :: 303.497.6469
My religion is very simple. My religion is kindness.
--Tenzin Gyatso

===============================================================================

Ara.T.Howard wrote:

ABOUT

  parseargs is a library that faciltates the parsing of arguments and keywords
  from method paramters, setting of default values, validation, contraints via
  class based or duck based typing, and coercion/convincing to type/ducktype
  when possible.

Interesting. I look forward to checking this out.

HISTORY

  0.0.0

Hmm, I think I used a prior version of it... :wink:

Hal

Hello,

This looks like a nice tool!
Looking at your samples made me think about a few interesting details
about Ruby. To start the discussion, below is your posted (a.rb)
sample:

     def method(*a)
       pa =
         parseargs(a) {
           required_argument :a
           optional_argument :b => 2
         }

       puts "#{ pa.a }#{ pa.b }"
     end

     method 4

   ~ > ruby sample/a.rb

     42

This looks like a fairly reasonable API, but one thing really struck
me was that your variables needed to be referenced with the pa
instance. This is understandable, but I kind of feel that if I were
to use this API a lot I would really rather have parseargs just go
ahead and set the variables in the current method's binding. Thus I
can just reference a and b without the need to use pa.

I was going to post a patch to your code, but decided it might be
better to flesh out this idea. Below is some sample code that will
display how I implemented a simple example of how you could remove the
need to reference pa.

···

On Wed, 01 Jun 2005 00:20:25 +0900, Ara.T.Howard <Ara.T.Howard@noaa.gov> wrote:

------
def parse_data_hash(data, bd = binding)
   data.each do |k,v|
     vid = v.object_id
     eval("#{k.to_s} = ObjectSpace._id2ref(#{vid})", bd)
   end
end

def parse_test(data_hash)
   parse_data_hash(data_hash)
   puts local_variables.join(",")

   puts "Explicit binding passed"
   parse_data_hash(data_hash, binding)
   puts local_variables
   local_variables.each do |k|
     print "#{k} => "
     eval("print #{k}.inspect")
     puts
   end
end

parse_test({:a => [1,2,3], :b => "hello"})
------

Writing this brought out a few really interesting details about Ruby
that I would like to discuss.

1) I had to use ObjectSpace._id2ref in order to achieve the ability to
set variable a to the passed Array. I did not like using this method,
even without the warning not to use it found in the "Ruby in a
Nutshell" book. Interestingly this warning is not found in the RDoc
and ri generated description. Is there a better way to do this?

2) In parse_data_hash I attempted to make passing the binding
optional. As you see in the parse_test that does not work, because it
is assigning the binding to the binding of the parse_data_hash method
and not from its' caller.

2.1 ) eval currently makes passing the binding optional. Is there a
way to do this in Ruby or is this something only available to the
internal implementation?

2.2) I was thinking it might be interesting if you could get the
binding of the caller from calling "caller". Of course, having this
ability opens your self up to a lot of dangerous usages and possibly
some powerful usages too. One usage would be a way to get the binding
of the caller for parse_data_hash. I am wondering if this is a bad
idea and if any other languages allow you to do this?

2.3) Does anyone have an example of how you can use eval with a Proc
object instead of an binding? The docs say you can do this, but does
not provide an example and my test below does not work either..

----
p = Proc.new { x = "Hello" }
eval("puts x", p)
t.rb:1: undefined local variable or method `x' for main:Object (NameError)
         from t.rb:1
----

I have some other fuzzy ideas about the binding and method objects,
but I better save that for another day when I can unfuzz them.
Although, one quick idea is if it would be possible in the future to
save the binding to disk or send it to another Ruby process?

Cheers,
Zev Blut

  ~ > ruby -e'puts(IO::read(ARGV.shift))' sample/a.rb

If this is to avoid 'cat', then you could also use

~ > ruby -e'puts *ARGF' sample/a.rb

Hal Fulton wrote:

Ara.T.Howard wrote:

HISTORY

  0.0.0

Hmm, I think I used a prior version of it... :wink:

Was that 0.0.0.0, or 0.0.0.0.0? :wink:

Ruby folks are so conservative in version numbering that it wouldn't be surprising to see negative versions... "Your library looks nice, but I'm not going to use use it until it goes positive."

Interesting. I look forward to checking this out.

i'd love some feedback. i end up writing the code the library contains plenty
so it seemed like a good candidate for a general purpose lib.

HISTORY

  0.0.0

Hmm, I think I used a prior version of it... :wink:

lol. :wink:

-a

···

On Wed, 1 Jun 2005, Hal Fulton wrote:
--

email :: ara [dot] t [dot] howard [at] noaa [dot] gov
phone :: 303.497.6469
My religion is very simple. My religion is kindness.
--Tenzin Gyatso

===============================================================================

the problem here is that the variables will only be __available__ through eval
either. eg. you'd have to

   p 'eval a'

and could never

   p a

i seem to recall a way of working around this but can't remember the thread...
if i can figure it out i'll add this feature. currently parsearges supports
   an undoccumented feature, if you

     parseargs(argv => receiver) {
       arg 'a'
       kw 'b'
     }

then receiver will be used to 'receive' the arguments. this works by

   begin
     receiver.send '=', name, value
   rescue NoMethodError
     begin
       receiver.send "#{ name }=", value
     rescue NoMethodError
       receiver.send "#{ name }", value
     end
   end

eg. any object that responds_to? =, #{name}=, or #{name} can be used.
examples would be

     parseargs(argv => a_hash) {
       arg 'a'
       kw 'b'
     }

or

     parseargs(argv => a_open_struct) {
       arg 'a'
       kw 'b'
     }

and those objects will be parsed 'into'. this means that, if a way can be
found to 'set the variable a, given the string "a", in the current scope' i
could have the following work fine

     parseargs(argv => binding) {
       arg 'a'
       kw 'b'
     }

     p a
     p b

to accomplish what you want.

so. how can one do this??

   eval 'a = 42'
   p a

i don't know how...

-a

···

On Wed, 1 Jun 2005, Zev Blut wrote:

Hello,

This looks like a nice tool! Looking at your samples made me think about a
few interesting details about Ruby. To start the discussion, below is your
posted (a.rb) sample:

On Wed, 01 Jun 2005 00:20:25 +0900, Ara.T.Howard <Ara.T.Howard@noaa.gov> > wrote:

     def method(*a)
       pa =
         parseargs(a) {
           required_argument :a
           optional_argument :b => 2
         }

       puts "#{ pa.a }#{ pa.b }"
     end

     method 4

   ~ > ruby sample/a.rb

     42

This looks like a fairly reasonable API, but one thing really struck me was
that your variables needed to be referenced with the pa instance. This is
understandable, but I kind of feel that if I were to use this API a lot I
would really rather have parseargs just go ahead and set the variables in
the current method's binding. Thus I can just reference a and b without the
need to use pa.

I was going to post a patch to your code, but decided it might be better to
flesh out this idea. Below is some sample code that will display how I
implemented a simple example of how you could remove the need to reference
pa.

------
def parse_data_hash(data, bd = binding)
data.each do |k,v|
   vid = v.object_id
   eval("#{k.to_s} = ObjectSpace._id2ref(#{vid})", bd)
end
end

def parse_test(data_hash)
parse_data_hash(data_hash)
puts local_variables.join(",")

puts "Explicit binding passed"
parse_data_hash(data_hash, binding)
puts local_variables
local_variables.each do |k|
   print "#{k} => "
   eval("print #{k}.inspect")
   puts
end
end

parse_test({:a => [1,2,3], :b => "hello"})
------

--

email :: ara [dot] t [dot] howard [at] noaa [dot] gov
phone :: 303.497.6469
My religion is very simple. My religion is kindness.
--Tenzin Gyatso

===============================================================================

i'll take it :wink:

-a

···

On Fri, 3 Jun 2005, Sean O'Halpin wrote:

  ~ > ruby -e'puts(IO::read(ARGV.shift))' sample/a.rb

If this is to avoid 'cat', then you could also use

~ > ruby -e'puts *ARGF' sample/a.rb

--

email :: ara [dot] t [dot] howard [at] noaa [dot] gov
phone :: 303.497.6469
My religion is very simple. My religion is kindness.
--Tenzin Gyatso

===============================================================================

i use the libtool/ld versioning system:

   interface.implementation.age

the first two are pretty self explanatory, the age bit need examples to
explain.

   first of all, interface - age > 0 demonstrates backwards compatibility. so
   a lib like

     3.0.2

   supports interfaces

     3
     2
     1

   but NOT interface zero. a lib like

     7.4.2

   supports interfaces

     7
     6
     5

   and so on.

the idea is that if you are using somethign arrayfields 3.4.0 in your code
alot and see the version bump up to 3.5.0 you will know that only the
implementation, not the interface, to the code has changed and can upgrade
with some confidence. if, however, you saw the version go from 3.4.0 to 4.0.1
you know that the interface has changed and you should upgrade with caution.
an interface change might be something major like changing a method signature
or name, or it could be something minor like adding a new method (but all olds
one stay the same) - in either case the version gives you certain hints.

i'd be curious to know the rational behind other ruby project's version
numbers and what information can be gleaned from them.

for mor about the versioning i use see

   http://codeforpeople.com/lib/ruby/library/library-0.0.0/doc/

cheers.

-a

···

On Wed, 1 Jun 2005, Joel VanderWerf wrote:

Hal Fulton wrote:

Ara.T.Howard wrote:

HISTORY

  0.0.0

Hmm, I think I used a prior version of it... :wink:

Was that 0.0.0.0, or 0.0.0.0.0? :wink:

Ruby folks are so conservative in version numbering that it wouldn't be
surprising to see negative versions... "Your library looks nice, but I'm not
going to use use it until it goes positive."

--

email :: ara [dot] t [dot] howard [at] noaa [dot] gov
phone :: 303.497.6469
My religion is very simple. My religion is kindness.
--Tenzin Gyatso

===============================================================================

Joel VanderWerf wrote:

Hal Fulton wrote:

Ara.T.Howard wrote:

HISTORY

  0.0.0

Hmm, I think I used a prior version of it... :wink:

Was that 0.0.0.0, or 0.0.0.0.0? :wink:

Ruby folks are so conservative in version numbering that it wouldn't be surprising to see negative versions... "Your library looks nice, but I'm not going to use use it until it goes positive."

And the version numbers of ideas (read: vapour ware) might well be imaginary numbers....
"I' won't think about using it, before it becomes real." :slight_smile:

Happy rubying

Stephan

Hello,

···

On Thu, 02 Jun 2005 05:30:28 +0900, Ara.T.Howard <Ara.T.Howard@noaa.gov> wrote:

the problem here is that the variables will only be __available__ through eval
either. eg. you'd have to

   p 'eval a'

and could never

   p a

i seem to recall a way of working around this but can't remember the thread...
if i can figure it out i'll add this feature.

Wow I am quite surprised at this! It is quite odd that adding a
variable through the use of eval, makes the variable appear in the
local_variables method, but that you cannot access the variable unless
you use eval... Below is a simpler example showing how using eval
modifies the the local_variables response.

----------------------------------------------------------------------
puts "[" + local_variables.join(" , ") + "]"
#=>
eval 'a = 42'
puts "[" + local_variables.join(" , ") + "]"
#=> [a]
puts a
#=> undefined local variable or method `a' for main:Object (NameError)
----------------------------------------------------------------------

If you cannot access a in your binding, why would eval add a to the
local_variables? Can anyone explain this? I would like to know.

Thanks,
Zev Blut

i use the libtool/ld versioning system:

interface.implementation.age

the first two are pretty self explanatory, the age bit need examples to
explain.

I hadn't seen age used like that before. Interesting.

i'd be curious to know the rational behind other ruby project's version
numbers and what information can be gleaned from them.

The recommended RubyGems versioning policy is summarized at
http://docs.rubygems.org/read/chapter/7\. The main purpose of the policy is
to allow reasonable version comparisons for dependency declarations,
especially the use of the "approximately greater than" version operator (see
http://docs.rubygems.org/read/chapter/16#page76\)

The libtool/ld policy would work gems as well, except that RubyGems wouldn't
know the fine distinctions provided by the age field.

···

On Tuesday 31 May 2005 11:16 pm, Ara.T.Howard wrote:

--
-- Jim Weirich jim@weirichhouse.org http://onestepback.org
-----------------------------------------------------------------
"Beware of bugs in the above code; I have only proved it correct,
not tried it." -- Donald Knuth (in a memo to Peter van Emde Boas)

Zev Blut wrote:

If you cannot access a in your binding, why would eval add a to the
local_variables? Can anyone explain this? I would like to know.

It is a curious side effect of the way Ruby is interpreted. Perhaps it is a performance optimization? It doesn't really have to be this way, but it won't affect you (much) in practical terms.

Variables that are "visible" when a method is compiled are added to a table somewhere in the AST, and only those variables can be accessed directly in that method. Clearly, this table is not the same one that "eval" consults so we have, to put it diplomatically, a little disconnect. Eval can find the variable when asked, but the interpreter simply refuses to go looking for it in the first place.

Now, ask yourself if you really care. Are you going to be writing code where you know the name of a variable ahead of time, but only initialize it inside an eval statement? If so, there are two simple workarounds:

  1. Initialize the variable before the eval.

     x = nil # make "x" visible beforehand
     eval "x = 1"
     print "#{x}\n"

  2. Assign the eval-initialized variable to a "visible" variable.

     eval "y = 2"
     y = eval "y" # make "y" visible afterwards
     print "#{y}\n"

···

--
Glenn Parker | glenn.parker-AT-comcast.net | <http://www.tetrafoil.com/&gt;

Hello,

the problem here is that the variables will only be __available__ through eval
either. eg. you'd have to

   p 'eval a'

and could never

   p a

i seem to recall a way of working around this but can't remember the thread...
if i can figure it out i'll add this feature.

Wow I am quite surprised at this!

everyone is! :wink:

It is quite odd that adding a variable through the use of eval, makes the
variable appear in the local_variables method, but that you cannot access
the variable unless you use eval... Below is a simpler example showing how
using eval modifies the the local_variables response.

----------------------------------------------------------------------
puts "[" + local_variables.join(" , ") + "]"
#=>
eval 'a = 42'
puts "[" + local_variables.join(" , ") + "]"
#=> [a]
puts a
#=> undefined local variable or method `a' for main:Object (NameError)
----------------------------------------------------------------------

If you cannot access a in your binding, why would eval add a to the
local_variables? Can anyone explain this? I would like to know.

i didn't know about that, but it suggests a way to rig method_missing in
Object to make it __seem__ like a variable has been created in scope:

for example:

   harp:~ > cat a.rb
   require 'binding_of_caller'

   class Object
     def create_var name, env = nil
       Binding::of_caller do |bnd|
         eval("#{ name }=nil", env || bnd)
       end
     end
     def set_var name, value, env = nil
       Binding::of_caller do |bnd|
         eval("#{ name }=ObjectSpace::_id2ref(#{ value.object_id })", env || bnd)
       end
     end
     def get_var name, env = nil
       Binding::of_caller do |bnd|
         eval("#{ name }", env || bnd)
       end
     end
     alias __method_missing__ method_missing
     def method_missing(m,*a,&b)
       Binding::of_caller do |bnd|
         lv = eval 'local_variables', bnd
         name = m.to_s
         if lv.include? name
           return(get_var(name, bnd))
         else
           __method_missing__(m,*a,&b)
         end
       end
     end
   end

   set_var 'a', 42
   p a

   harp:~ > ruby a.rb
   42

wow! it __can__ be done. now someone try to break it please! :wink:

-a

···

On Thu, 2 Jun 2005, Zev Blut wrote:

On Thu, 02 Jun 2005 05:30:28 +0900, Ara.T.Howard <Ara.T.Howard@noaa.gov> > wrote:

--

email :: ara [dot] t [dot] howard [at] noaa [dot] gov
phone :: 303.497.6469
My religion is very simple. My religion is kindness.
--Tenzin Gyatso

===============================================================================

thanks for the input jim. i dunno about switching all my libs though ;-( the
age thing is really handy so that applications can say "give me the more
recent version that supports my interface" and end up getting v17.2.16 even if
the initial application was built against v3.2.2. on the other hand there is
no ruby 'linker' other than require_gem so maybe i ought to convert...

cheers.

-a

···

On Wed, 1 Jun 2005, Jim Weirich wrote:

On Tuesday 31 May 2005 11:16 pm, Ara.T.Howard wrote:

i use the libtool/ld versioning system:

interface.implementation.age

the first two are pretty self explanatory, the age bit need examples to
explain.

I hadn't seen age used like that before. Interesting.

i'd be curious to know the rational behind other ruby project's version
numbers and what information can be gleaned from them.

The recommended RubyGems versioning policy is summarized at
http://docs.rubygems.org/read/chapter/7\. The main purpose of the policy is
to allow reasonable version comparisons for dependency declarations,
especially the use of the "approximately greater than" version operator (see
http://docs.rubygems.org/read/chapter/16#page76\)

The libtool/ld policy would work gems as well, except that RubyGems wouldn't
know the fine distinctions provided by the age field.

--

email :: ara [dot] t [dot] howard [at] noaa [dot] gov
phone :: 303.497.6469
My religion is very simple. My religion is kindness.
--Tenzin Gyatso

===============================================================================

Glenn Parker said:

  2. Assign the eval-initialized variable to a "visible" variable.

     eval "y = 2"
     y = eval "y" # make "y" visible afterwards
     print "#{y}\n"

Actually, you don't even have to actually assign anything to y, you just
need to make sure that the Ruby interpreter sees an assignment to y before
the print. It's a subtle distinction, but consider the following code:

  eval "y = 2"
  y = 0 if false
  puts y # => 2 (not 0)

···

--
-- Jim Weirich jim@weirichhouse.org http://onestepback.org
-----------------------------------------------------------------
"Beware of bugs in the above code; I have only proved it correct,
not tried it." -- Donald Knuth (in a memo to Peter van Emde Boas)

Ara.T.Howard schrieb:

wow! it __can__ be done. now someone try to break it please! :wink:

Ask and thou shalt receive:

(previous code)

   def a; "ouch"; end

  set_var 'a', 42
  p a

You get a name clash between methods and local variables. Otherwise, your code should work fine, AFAIKT.

Regards,
Pit

Hello,

Sorry for the late reply.

···

On Thu, 02 Jun 2005 21:33:29 +0900, Glenn Parker <glenn.parker@comcast.net> wrote:

Zev Blut wrote:

If you cannot access a in your binding, why would eval add a to the
local_variables? Can anyone explain this? I would like to know.

It is a curious side effect of the way Ruby is interpreted. Perhaps it is a performance optimization? It doesn't really have to be this way, but it won't affect you (much) in practical terms.

Variables that are "visible" when a method is compiled are added to a table somewhere in the AST, and only those variables can be accessed directly in that method. Clearly, this table is not the same one that "eval" consults so we have, to put it diplomatically, a little disconnect. Eval can find the variable when asked, but the interpreter simply refuses to go looking for it in the first place.

Thanks a lot for the explanation! It certainly makes sense now.

Cheers,
Zev

Jim Weirich wrote:

Actually, you don't even have to actually assign anything to y, you just
need to make sure that the Ruby interpreter sees an assignment to y before
the print. It's a subtle distinction, but consider the following code:

  eval "y = 2"
  y = 0 if false
  puts y # => 2 (not 0)

OK, but you have to admit that "y = 0 if false" is Dark Magic. Who is going to understand what is going on there? An optimizing interpreter might justifiably toss the entire statement.

···

--
Glenn Parker | glenn.parker-AT-comcast.net | <http://www.tetrafoil.com/&gt;

hmm. i guess here

   def a; 'forty-two'; end

   set_var 'a', 42
   p a

we get the method. and here

   def a; 'forty-two'; end

   a = 42
   p a

we get the local_variable. so in either case you get a name clash - but the
sense of it is reversed with set_var. i wonder if this is un-intuitive enough
to render it useless?

cheers.

-a

···

On Fri, 3 Jun 2005, Pit Capitain wrote:

Ara.T.Howard schrieb:

wow! it __can__ be done. now someone try to break it please! :wink:

Ask and thou shalt receive:

(previous code)

def a; "ouch"; end

  set_var 'a', 42
  p a

You get a name clash between methods and local variables. Otherwise, your code should work fine, AFAIKT.

--

email :: ara [dot] t [dot] howard [at] noaa [dot] gov
phone :: 303.497.6469
My religion is very simple. My religion is kindness.
--Tenzin Gyatso

===============================================================================