mozsalles mozsalles - 28 days ago 8
Ruby Question

Requiring a file that is also a program with it's own option parser

My problem is: the required file intercepts the option parser.

I'm writing a ruby program in 3 files, one (connect.rb) that handles connection to a certain site, and another 2 (populate.rb and update.rb) that require the first one to collect data from that site.

connect.rb can be ran to configure and test the connection, as such:

$ruby connect.rb --adduser myuser -p 1234 --test
Created user myuser
Connecting as myuser...
Connection test OK


On the other hand, populate.rb has a 'require connect.rb' line, but it also can be ran and has it's own options.

It used to be that connect.rb had no options, and it all worked fine, but since I added options to connect.rb with optparse, the option parser in populate.rb and update.rb doesn't work anymore. If I run 'ruby populate.rb -h', it shows the options available to connect.rb, not the ones in populate.rb. If I use options that connect.rb accepts, like '-t', the option is executed as if in connect.rb. If I use options that connect.rb doesn't accept, but populate.rb should, it shows a "invalid option" error and exits.

Basically, I want connect.rb to behave differently when I run it solo from when I require it in another file. When I run it solo, I want it to accept options and do things; but when it's required in another file, I want just the methods to be available, and I want a different set of options.

Is that even possible? Is there a way to tell how it's been called, or some other way to prevent option interception? I'm using optparse in all my files.

Answer

They way I solved something similar to this is to check if the file is being run solo like this:

# connect.rb    

# wrap logic in a module
module Connect
  def self.run(args: ARGV)
    args # access to command line options
  end
end

# run if solo
Connect.run if $0 == __FILE__

In the last conditional $0 is a Ruby global set to the name of the executed file and __FILE__ is the name of the current file. If both are the same then you know the file is running solo i.e. ruby connect.rb.

With that setup you can run connect.rb solo and it'll work fine. Then, when you require it in your other file populate.rb You need to explicitly run it:

# popoulate.rb
require 'connect'
Connect.run ARGV.dup # dup the commandline options in case other files mutate them

Hope that helps.

Comments