# Overcoming dynamic block evaluation

**URL:** <https://www.ruby-forum.com/t/overcoming-dynamic-block-evaluation/118305>\
**Category:** Ruby\
**Created:** [October 25, 2007, 3:00pm UTC](https://www.ruby-forum.com/t/overcoming-dynamic-block-evaluation/118305 "2007-10-25T15:00:46Z")\
**Posts on this page:** 3\
**Page:** 1

<div class="post-metadata">

**Author:** ![braver](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/b/a88e57/32.png) [@braver](https://www.ruby-forum.com/u/braver)\
**Post date:** [October 25, 2007, 3:00pm UTC](https://www.ruby-forum.com/t/overcoming-dynamic-block-evaluation/118305/1 "2007-10-25T15:00:46Z")

</div>

Greetings – I’m writing a reusable options module for my scripts.  
All scripts in the same project share most of the common command-line  
options. I’m relying on the optparse module to do it. The desired  
functionality is a report on all options being used, structured so  
that it’s also defined incrementally next to each options, and  
possibility to disable some of the reusable options and add some new  
ones.

This is problem with optparse: how to report all  
options in verbose mode, including the defaults for those not set –  
but the opt.on(…) { block } is triggered only by the actual option.  
Moreover, it’s evaluated in the parse() call context, which makes  
using variables in those blocks problematic. Consider the following  
toy program I wrote to develop a better, reusable option processing.

#!/usr/bin/env ruby

# 

# Created by Alexy K. on 2007-10-24.

# Copyright © 2007. All rights reserved.

require ‘optparse’

DIR\_CODES = %w[central subdir shotgun same cwd]  
DIR\_CODE\_ALIASES = { :gun =\> :shotgun, :working =\> :cwd }

class Options

def initialize(no=nil,xdefs=nil,&block)  
@o = { # default values  
:file\_ext =\> “.txt”,  
:dir\_kind =\> :shotgun,  
:dirout =\> “kuku”  
}  
# inject new defaults, if any – hash add, h1+h2?  
xdefs.each { |k,v| @o[k] = v } if xdefs

```
 opt = nil
 report = []
 begin
   files = OptionParser.new do |opt|
     opt.banner = "Preved!"

     unless no[o=:verbose]
       help = "run verbosely"
       short = "-v"
       # trying to replace late-binding below with
       ref_o = lambda { o } # but ref_o would be different in

```

another opt block!  
opt.on(short, “–[no-]#{o}”, help) { |val| @o[:verbose] =  
val }  
report \<\< [o,short,help]  
end

```
     unless no[o=:dirout]
       help="directory location for the output files"
       short = "-d"
       opt.on(short, "--#{o} DIR",
         DIR_CODES.map { |x| x.to_sym }, help) { |val| @o[:dirout]

```

= val }  
report \<\< [o,short,help]  
end

```
     code_list = (DIR_CODE_ALIASES.keys + DIR_CODES).map{|x|

```

x.to\_sym}.join(’,’)  
unless no[o=:moredir]  
help=“more dirs with aliases, #{code\_list}”  
short = “-m”  
opt.on(short, “–#{o} DIR”,  
DIR\_CODES.map{|x|x.to\_sym}, DIR\_CODE\_ALIASES, help) { |  
val| @o[o] = val }  
report \<\< [o,short,help]  
end

```
     yield opt, @o, report if block

   end.parse(*ARGV) # parse it! remainder -> files

   report.each do |o,short,help|
     val = @o[o] || "unset"
     STDERR.printf "--%s\t%s,\t%s: %s\n", o, short, help, val
   end

 rescue OptionParser::InvalidOption => o
   puts "#{o}"
   puts opt.help
   exit(1)
 rescue OptionParser::AmbiguousArgument => o
   puts "#{o}"
   exit(1)
 end

```

end # initialize

def method\_missing (m, \*a, &block)  
@o[m.to\_sym]  
end  
end # Options

def main  
no = {:moredir =\> 1}  
xdefaults = { :extra =\> “hurry to see!” }  
puts  
o = Options.new(no, xdefaults) do |opt,hash,report|  
name = :extra  
help = “extra option, added in the new script”  
short = “-x”  
opt.on(short,"–#{name} X",help) { |val| hash[:extra] = val }  
report \<\< [name,short,help]  
end  
puts  
puts “verbose =\> #{o.verbose}”  
puts “file\_ext =\> #{o.file\_ext}”  
puts “dir\_kind =\> #{o.dir\_kind}”  
puts “dir\_more =\> #{o.dir\_more}”  
puts “extra =\> #{o.extra}”  
end

if $0 == **FILE**  
main  
end

The idea was that each option has a name, by which it is referred to  
in an internal hash @o. The hash parameter called `no’ contains those  
options from the preexistng Options instance, which we want to disable.  
Additional options are defined in the block given to Options.new;  
their defaults are passed as a hash to that constructor. Also one can  
override predefined defaults through the same extra defaults hash.  
So far so good.

But notice the assignment in disablement check,

unless no[o=:verbose]

– I planned on using o uniformly later, perhaps abstracting more out  
of my newly uniform opt.on sections. Yet I was getting an obscure  
error there when the block looked like

{ |val| @o[o] = val }

– o turned out to be the name of the option from the _last_ opt.on  
block! All these blocks are evaluated later, in parse(). So I had to  
write

{ |val| @o[:verbose] = val }

explicitly. Played with lambda and Proc, yet they, too, are only  
evaluated later. We want to get at _that very o_, but we can’t even  
stick a binding into the block, as it’ll be only evaluated later!  
Notice we can stick o into the parameter strings given to each  
opt.on() as they are evaluated right away. I was wondering about  
getting the name back from an internal value for long, yet it requires  
getting into optparse internals and is not a clean solution to  
overcoming the dynamic binding in this case.

Is there a way to “constantize” o in @o[o], when o==:verbose, to be  
equivalent to @o[:verbose] – replacing variable reference by it  
literal value in a location in the program text?

BTW, Python’s optparse allows default definitions…  
Cheers,  
Alexy

---

<div class="post-metadata">

**Author:** ![braver](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/b/a88e57/32.png) [@braver](https://www.ruby-forum.com/u/braver)\
**Post date:** [October 25, 2007, 3:16pm UTC](https://www.ruby-forum.com/t/overcoming-dynamic-block-evaluation/118305/2 "2007-10-25T15:16:00Z")

</div>

2007/10/25, Alexy K. [removed\_email\_address@domain.invalid](mailto:removed_email_address@domain.invalid):

> but the opt.on(…) { block } is triggered only by the actual option.
> 
> ```
> }
> help = "run verbosely"
> help="directory location for the output files"
> help="more dirs with aliases, #{code_list}"
> 
> puts "#{o}"
> 
> ```
> 
> no = {:moredir =\> 1}  
> puts “verbose =\> #{o.verbose}”  
> The idea was that each option has a name, by which it is referred to
> 
> { |val| @o[:verbose] = val }  
> Is there a way to “constantize” o in @o[o], when o==:verbose, to be  
> equivalent to @o[:verbose] – replacing variable reference by it  
> literal value in a location in the program text?
> 
> BTW, Python’s optparse allows default definitions…  
> Cheers,  
> Alexy

Unfortunately I don’t have time to go through all of this, but did you  
consider to define a block with the default options like this?

DEFAULT\_OPTS = lambda do |opts|  
opts.on(“-v”, “–[no-]verbose”, “Run verbosely”) do |v|  
options.verbose = v  
end  
…  
end

…

opts = OptionParser.new do |opts|  
DEFAULT\_OPTS[opts]

opts.on(“–type [TYPE]”, [:text, :binary, :auto],  
“Select transfer type (text, binary, auto)”) do |t|  
options.transfer\_type = t  
end

end

Kind regards

robert

---

<div class="post-metadata">

**Author:** ![braver](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/b/a88e57/32.png) [@braver](https://www.ruby-forum.com/u/braver)\
**Post date:** [October 25, 2007, 3:36pm UTC](https://www.ruby-forum.com/t/overcoming-dynamic-block-evaluation/118305/3 "2007-10-25T15:36:38Z")

</div>

> end  
> Well, I need to add textual reporting and report on all options, both  
> set and unset. By “default” options I mean not those common to all  
> scripts, but those which are not mentioned explicitly on the command  
> line, thus their blocks never get triggered. So we cannot add  
> reporting lines there as puts. In my solution reporting lines are  
> added to an enclosing unless statement, and they are added  
> to a list which is walked in the reporting loop later.

The question really is about overcoming the fact that the opt.on  
blocks are evaluated later and my initial trick of referring to  
options on a uniform way as o stumbles upon that deferred evaluation,  
when o was long redefined.

Cheers,  
Alexy
