From: Albert Chou Date: 2003-04-04T12:16:54+09:00 Subject: snippet: modifications to GetoptLong to allow processing of method arguments This is a multi-part message in MIME format. ------_=_NextPart_001_01C2FA58.7B3754C8 Content-Type: text/plain; charset="iso-8859-1" Content-Transfer-Encoding: quoted-printable I don't know exactly how useful any of you out there will find the following, but I thought it was interesting enough to share. The main modification is to allow the 'get' and 'each' methods to take an argument -- an array of strings akin to ARGV. If this argument is present, it is processed by the Getoptlong object instead of ARGV. If the argument is not present, ARGV is processed as in the original version. I overrode the existing get method, copying it almost verbatim except for basically substituting the passed-in argument for ARGV everywhere. The following code can be put in your code to gain the enhancement in behavior. Why did I do this? I had an existing program whose code was not encapsulated in a class, and I needed to do so in order to enable someone else to use this code. I didn't want to lose the ability to flexibly process arbitrary arguments, so I decided to see how hard it would be to modify Getoptlong. As it turns out, it was pretty easy. Within 90 minutes I had worked out all the little kinks (e.g., is it *args or args in my method definition?), resulting in a new version which was 100% compatible with existing clients' uses of the code and also cleanly usable as an object by new clients. Al class GetoptLong # Give ourselves read access to some of GetoptLong's internal data structures # (makes printing the usage information for programs easier). attr_reader :canonical_names, :argument_flags alias :original_get :get # Changed so that we can specify an array of arguments to process, # which defaults to ARGV for backward compatibility. def get( arguments ) if arguments.empty? then arguments =3D ARGV end option_name, option_argument =3D nil, '' # Check status. return nil if @error !=3D nil case @status when STATUS_YET @status =3D STATUS_STARTED when STATUS_TERMINATED return nil end # # Get next option argument. # if 0 < @rest_singles.length argument =3D '-' + @rest_singles elsif (arguments.length =3D=3D 0) terminate return nil elsif @ordering =3D=3D PERMUTE while 0 < arguments.length && arguments[0] !~ /^-./ @non_option_arguments.push(arguments.shift) end if arguments.length =3D=3D 0 terminate return nil end argument =3D arguments.shift elsif @ordering =3D=3D REQUIRE_ORDER if (arguments[0] !~ /^-./) terminate return nil end argument =3D arguments.shift else argument =3D arguments.shift end # # Check the special argument `--'. # `--' indicates the end of the option list. # if argument =3D=3D '--' && @rest_singles.length =3D=3D 0 terminate return nil end # # Check for long and short options. # if argument =3D~ /^(--[^=3D]+)/ && @rest_singles.length =3D=3D 0 # # This is a long style option, which start with `--'. # pattern =3D $1 if @canonical_names.include?(pattern) option_name =3D pattern else # # The option `option_name' is not registered in `@canonical_names'. # It may be an abbreviated. # match_count =3D 0 @canonical_names.each_key do |key| if key.index(pattern) =3D=3D 0 option_name =3D key match_count +=3D 1 end end if 2 <=3D match_count set_error(AmbigousOption, "option `#{argument}' is ambiguous") elsif match_count =3D=3D 0 set_error(InvalidOption, "unrecognized option `#{argument}'") end end # # Check an argument to the option. # if @argument_flags[option_name] =3D=3D REQUIRED_ARGUMENT if argument =3D~ /=3D(.*)$/ option_argument =3D $1 elsif 0 < arguments.length option_argument =3D arguments.shift else set_error(MissingArgument, "option `#{argument}' requires an argument") end elsif @argument_flags[option_name] =3D=3D OPTIONAL_ARGUMENT if argument =3D~ /=3D(.*)$/ option_argument =3D $1 elsif 0 < arguments.length && arguments[0] !~ /^-./ option_argument =3D arguments.shift else option_argument =3D '' end elsif argument =3D~ /=3D(.*)$/ set_error(NeedlessArgument, "option `#{option_name}' doesn't allow an argument") end elsif argument =3D~ /^(-(.))(.*)/ # # This is a short style option, which start with `-' (not `--'). # Short options may be catinated (e.g. `-l -g' is equivalent to # `-lg'). # option_name, ch, @rest_singles =3D $1, $2, $3 if @canonical_names.include?(option_name) # # The option `option_name' is found in `@canonical_names'. # Check its argument. # if @argument_flags[option_name] =3D=3D REQUIRED_ARGUMENT if 0 < @rest_singles.length option_argument =3D @rest_singles @rest_singles =3D '' elsif 0 < arguments.length option_argument =3D arguments.shift else # 1003.2 specifies the format of this message. set_error(MissingArgument, "option requires an argument -- #{ch}") end elsif @argument_flags[option_name] =3D=3D OPTIONAL_ARGUMENT if 0 < @rest_singles.length option_argument =3D @rest_singles @rest_singles =3D '' elsif 0 < arguments.length && arguments[0] !~ /^-./ option_argument =3D arguments.shift else option_argument =3D '' end end else # # This is an invalid option. # 1003.2 specifies the format of this message. # if ENV.include?('POSIXLY_CORRECT') set_error(InvalidOption, "illegal option -- #{ch}") else set_error(InvalidOption, "invalid option -- #{ch}") end end else # # This is a non-option argument. # Only RETURN_IN_ORDER falled into here. # return '', argument end return @canonical_names[option_name], option_argument end # Make sure get_option is an alias for the new get method defined above! alias :get_option :get alias :original_each :each # Changed so that we can specify an array of arguments to process, # which defaults to ARGV for backward compatibility. def each( arguments =3D nil ) loop do if arguments.nil? then option_name, option_argument =3D get_option else option_name, option_argument =3D get_option( arguments ) end break if option_name =3D=3D nil yield option_name, option_argument end end end --=20 Albert Davidson Chou, QA Manager TeaLeaf Technology, Inc. (415) 932-5031 AChou@TeaLeaf.com | http://www.TeaLeaf.com/ Evaluate TeaLeaf Technology today: http://www.TeaLeaf.com/demo/ ------_=_NextPart_001_01C2FA58.7B3754C8 Content-Type: text/html; charset="iso-8859-1" Content-Transfer-Encoding: quoted-printable snippet: modifications to GetoptLong to allow processing of = method arguments

I don't know exactly how useful any of you out there = will find the following, but I thought it was interesting enough to = share.  The main modification is to allow the 'get' and 'each' = methods to take an argument -- an array of strings akin to ARGV.  = If this argument is present, it is processed by the Getoptlong object = instead of ARGV.  If the argument is not present, ARGV is processed = as in the original version.  I overrode the existing get method, = copying it almost verbatim except for basically substituting the = passed-in argument for ARGV everywhere.  The following code can be = put in your code to gain the enhancement in behavior.

Why did I do this?  I had an existing program = whose code was not encapsulated in a class, and I needed to do so in = order to enable someone else to use this code.  I didn't want to = lose the ability to flexibly process arbitrary arguments, so I decided = to see how hard it would be to modify Getoptlong.  As it turns out, = it was pretty easy.  Within 90 minutes I had worked out all the = little kinks (e.g., is it *args or args in my method definition?), = resulting in a new version which was 100% compatible with existing = clients' uses of the code and also cleanly usable as an object by new = clients.


Al


class GetoptLong
  # Give ourselves read access to some of = GetoptLong's internal data structures
  # (makes printing the usage information for = programs easier).
  attr_reader :canonical_names, = :argument_flags

  alias :original_get :get
  # Changed so that we can specify an array of = arguments to process,
  # which defaults to ARGV for backward = compatibility.
  def get( arguments )
    if arguments.empty? then
      arguments =3D = ARGV
    end

    option_name, option_argument =3D = nil, ''

    # Check status.
    return nil if @error !=3D = nil
    case @status
    when STATUS_YET
      @status =3D = STATUS_STARTED
    when STATUS_TERMINATED
      return nil
    end

    #
    # Get next option argument.
    #
    if 0 < = @rest_singles.length
      argument =3D '-' + = @rest_singles
    elsif (arguments.length =3D=3D = 0)
      terminate
      return nil
    elsif @ordering =3D=3D = PERMUTE
      while 0 < = arguments.length && arguments[0] !~ /^-./
        = @non_option_arguments.push(arguments.shift)
      end
      if arguments.length = =3D=3D 0
    terminate
    return nil
      end
      argument =3D = arguments.shift
    elsif @ordering =3D=3D = REQUIRE_ORDER
      if (arguments[0] !~ = /^-./)
    terminate
    return nil
      end
      argument =3D = arguments.shift
    else
      argument =3D = arguments.shift
    end

    #
    # Check the special argument = `--'.
    # `--' indicates the end of the = option list.
    #
    if argument =3D=3D '--' && = @rest_singles.length =3D=3D 0
      terminate
      return nil
    end

    #
    # Check for long and short = options.
    #
    if argument =3D~ /^(--[^=3D]+)/ = && @rest_singles.length =3D=3D 0
      #
      # This is a long style = option, which start with `--'.
      #
      pattern =3D $1
      if = @canonical_names.include?(pattern)
        = option_name =3D pattern
      else
        #
        # The = option `option_name' is not registered in `@canonical_names'.
        # It may = be an abbreviated.
        #
        = match_count =3D 0
        = @canonical_names.each_key do |key|
          if = key.index(pattern) =3D=3D 0
          &nbs= p; option_name =3D key
          &nbs= p; match_count +=3D 1
          = end
        end
        if 2 = <=3D match_count
          = set_error(AmbigousOption, "option `#{argument}' is = ambiguous")
        elsif = match_count =3D=3D 0
          = set_error(InvalidOption, "unrecognized option = `#{argument}'")
        end
      end

      #
      # Check an argument to = the option.
      #
      if = @argument_flags[option_name] =3D=3D REQUIRED_ARGUMENT
        if = argument =3D~ /=3D(.*)$/
          = option_argument =3D $1
        elsif 0 = < arguments.length
          = option_argument =3D arguments.shift
        = else
          = set_error(MissingArgument,
          &nbs= p;     "option `#{argument}' requires an = argument")
        end
      elsif = @argument_flags[option_name] =3D=3D OPTIONAL_ARGUMENT
        if = argument =3D~ /=3D(.*)$/
          = option_argument =3D $1
        elsif 0 = < arguments.length && arguments[0] !~ /^-./
          = option_argument =3D arguments.shift
        = else
          = option_argument =3D ''
        end
      elsif argument =3D~ = /=3D(.*)$/
        = set_error(NeedlessArgument,
          = "option `#{option_name}' doesn't allow an argument")
      end

    elsif argument =3D~ = /^(-(.))(.*)/
      #
      # This is a short = style option, which start with `-' (not `--').
      # Short options may be = catinated (e.g. `-l -g' is equivalent to
      # `-lg').
      #
      option_name, ch, = @rest_singles =3D $1, $2, $3

      if = @canonical_names.include?(option_name)
        #
        # The = option `option_name' is found in `@canonical_names'.
        # Check = its argument.
        #
        if = @argument_flags[option_name] =3D=3D REQUIRED_ARGUMENT
          if 0 = < @rest_singles.length
          &nbs= p; option_argument =3D @rest_singles
          &nbs= p; @rest_singles =3D ''
          elsif 0 = < arguments.length
          &nbs= p; option_argument =3D arguments.shift
          = else
          &nbs= p; # 1003.2 specifies the format of this message.
          &nbs= p; set_error(MissingArgument, "option requires an argument -- = #{ch}")
          = end
        elsif = @argument_flags[option_name] =3D=3D OPTIONAL_ARGUMENT
          if 0 = < @rest_singles.length
          &nbs= p; option_argument =3D @rest_singles
          &nbs= p; @rest_singles =3D ''
          elsif 0 = < arguments.length && arguments[0] !~ /^-./
          &nbs= p; option_argument =3D arguments.shift
          = else
          &nbs= p; option_argument =3D ''
          = end
        end
      else
        #
        # This is = an invalid option.
        # 1003.2 = specifies the format of this message.
        #
        if = ENV.include?('POSIXLY_CORRECT')
          = set_error(InvalidOption, "illegal option -- #{ch}")
        = else
          = set_error(InvalidOption, "invalid option -- #{ch}")
        end
      end
    else
      #
      # This is a non-option = argument.
      # Only RETURN_IN_ORDER = falled into here.
      #
      return '', = argument
    end

    return = @canonical_names[option_name], option_argument
  end

  # Make sure get_option is an alias for the new = get method defined above!
  alias :get_option :get

  alias :original_each :each
  # Changed so that we can specify an array of = arguments to process,
  # which defaults to ARGV for backward = compatibility.
  def each( arguments =3D nil )
    loop do
      if arguments.nil? = then
        = option_name, option_argument =3D get_option
      else
        = option_name, option_argument =3D get_option( arguments )
      end
      break if option_name = =3D=3D nil
      yield option_name, = option_argument
    end
  end
end

--
Albert Davidson Chou, QA Manager
TeaLeaf Technology, Inc.
(415) 932-5031
AChou@TeaLeaf.com | http://www.TeaLeaf.com/

  Evaluate TeaLeaf Technology today:
  http://www.TeaLeaf.com/demo/

------_=_NextPart_001_01C2FA58.7B3754C8--