Search code examples
rubyrequireoptparse

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.


Solution

  • The 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.