From: Robert Klemme Date: 2011-09-26T18:20:48+09:00 Subject: Re: Looking for better/familiar approach to command line opts On Sun, Sep 25, 2011 at 10:48 PM, Perl J. wrote: > So I guess the warning to the reader upfront is...  I'm a bit of a Perl > hack who should have moved to Ruby a decade ago and just couldn't let go > of Perl.  Perl still does stuff that I use extensively that I can't > (take the time to) figure out how to do in Ruby, so... that's why I'm > here. > > I've got a small framework I wrote in Perl I've been using for years > that is lightweight and yet powerful.   A lot of it centers around the > use of AUTOLOAD, which some Perlers say is bad -- I say it's extremely > powerful if used properly, and actually, AUTOLOAD forms a lot of the > backbone for my framework.  And honestly, it's helped me do some things > in Perl that I see in Ruby.  So there's some irony here in that I wrote > a framework to give me some of the power I see in Ruby in Perl. :-) > > With all that as a backdrop, here's how I can process command line > arguments in Perl (*everything* I do in Perl is OO!!) and I want the > same thing in Ruby.  OptParser in Ruby is WAY more than I'm looking for > -- not as easy as what I'm used to (or maybe I don't understand > OptParser, which is likely). > > In Perl, I can do this: > > options = MyFramework::Options->new( >   sourcePath => 's:' >   destPath   => 'd:' >   verbose    => 'v' > ); > > What ends up happening behind the scenes is a class instance is created > dynamically that essentially gives me the ability to say: > > ## I actually have a puts() I created in Perl as well, > ## borrowed from Ruby! > puts( >   "Source path.......: " . $options->sourcePath, >   "Destination path..: " . $options->destPath, >   "Verbose is........: " . ($options->verbose ? 'ON' : 'OFF'), > ); > > So I have a getter for sourcePath, a getter for destPath and a getter > for verbose, automagically created for that instance loaded with the > values from the command line.  If I said: > > whatever = MyFramework::Options->new( >   foo => 'f:', >   bar => 'b:', > ); > > Then I would have an instance of 'whatever' with foo and bar as getters, > etc. with the values of 'f' and 'b' from the command line loaded > appropriately. > > Now how to do this in Ruby?  Like I said, the OptParser seems WAY too > complicated for what I want and am trying to do.  It seems like you > still are coding some kind of OptParser class for the specific options > you want -- I don't want to create classes that mirror the command line > options.  I want to tell the class instance at the time I create it > (kind of like OpenStruct) what it looks like and it just creates the > right instance.  I want to be able to do this in Ruby, assuming I would > "define" this dynamically on the fly using maybe a hash as initial > input... I like the flexibility of hashes: > > options = Options.new({ >   :sPath   => 's:', >   :dPath   => 'd:', >   :verbose => 'v', > }) > > puts "Source path.......: #{options.sPath}" > puts "Destination path..: #{options.dPath}" > puts "Verbose is........: #{options.verbose ? 'ON' : 'OFF'}" > > What this is saying is "map the value of 's' from the command line into > @sPath, map the value of 'd' from the command line into @dPath, and map > whether 'v' exists, true or false, into @verbose."  Now how much more > simple can you get??!! > > Again, the warning of "trying to do Ruby stuff in a Perl way" comes to > me, but what I do in Perl is so stinking simple, it's unbelievable, and > this is one reason I haven't switched over to Ruby yet.  But now I'm > trying. > > Things I've tried: A lot of different combinations of... > -- missing_method() > -- define_method() > (I don't understand why a lot of d_m() examples use self.class.send()!!) > -- Struct() > -- OpenStruct() > > So far, requiring "optparse" and doing ARGV.getopts() does some of what > I want.  This is as close as I've gotten and it's not working: > > require "optparse" > > class Options >   attr_reader :cmdline, :options_list >   def initialize( options ) >      @cmdline = ARGV.join( ' ' ) >      @options_list = options.values.join >      params = ARGV.getopts( @options_list ) >      options.each do |key,value| >         ## Why do I need to send( :define_method, ... ) here? >         ## Why can't I just say self.define_method? >         ## This seems like it would create proper closure >         ## on params[] elements?! >         self.class.send( :define_method, key ) { params[value] } >      end >   end > end > > options = Options.new({ >   :sPath   => 's:', >   :dPath   => 'd:', >   :verbose => 'v', > }) > > If someone can tell me how to do this using OptParser, so be it.  But > from an encapsulation standpoint, I'd like to be able to call it as I do > above.  If the innards of the Options class use OptParser, fine. Does this what you need? 11:15:19 Temp$ ./opt.rb -s abc.def -- -x {:source_path=>"abc.def"} 11:15:22 Temp$ ./opt.rb -s abc.def -- -x {:source_path=>"abc.def"} ["-x"] 11:15:28 Temp$ ./opt.rb -s abc.def {:source_path=>"abc.def"} [] 11:15:31 Temp$ ./opt.rb -s abc.def -d . {:source_path=>"abc.def", :dest_path=>"."} [] 11:15:36 Temp$ ./opt.rb -s abc.def -d . -v {:source_path=>"abc.def", :dest_path=>".", :verbose=>true} [] 11:15:39 Temp$ ./opt.rb -s abc.def -d . -v a b c {:source_path=>"abc.def", :dest_path=>".", :verbose=>true} ["a", "b", "c"] https://gist.github.com/1241917 Kind regards robert -- remember.guy do |as, often| as.you_can - without end http://blog.rubybestpractices.com/