summaryrefslogtreecommitdiff
path: root/lib/slop/types.rb
blob: d68fe7670edc8555dd5dc5f2732f2fc43531d479 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
module Slop
  # Cast the option argument to a String.
  class StringOption < Option
    def call(value)
      value.to_s
    end
  end

  # Cast the option argument to a symbol.
  class SymbolOption < Option
    def call(value)
      value.to_sym
    end
  end

  # Cast the option argument to true or false.
  # Override default_value to default to false instead of nil.
  # This option type does not expect an argument. However, the API
  # supports value being passed. This is to ensure it can capture
  # an explicit false value
  class BoolOption < Option
    attr_accessor :explicit_value

    FALSE_VALUES = [false, 'false', 'no', 'off', '0'].freeze
    TRUE_VALUES = [true, 'true', 'yes', 'on', '1'].freeze
    VALID_VALUES = (FALSE_VALUES + TRUE_VALUES).freeze

    def valid?(value)
      # If we don't want to validate the type, then we don't care if the value
      # is valid or not. Otherwise we would prevent boolean flags followed by
      # arguments from being parsed correctly.
      return true unless config[:validate_type]

      return true if value.is_a?(String) && value.start_with?("--")
      value.nil? || VALID_VALUES.include?(value)
    end

    def call(value)
      self.explicit_value = value
      !force_false?
    end

    def value
      if force_false?
        false
      else
        super
      end
    end

    def force_false?
      FALSE_VALUES.include?(explicit_value)
    end

    def default_value
      config[:default] || false
    end

    def expects_argument?
      false
    end
  end
  BooleanOption = BoolOption

  # Cast the option argument to an Integer.
  class IntegerOption < Option
    INT_STRING_REGEXP = /\A[+-]?\d+\z/.freeze

    def valid?(value)
      value =~ INT_STRING_REGEXP
    end

    def call(value)
      value.to_i
    end
  end
  IntOption = IntegerOption

  # Cast the option argument to a Float.
  class FloatOption < Option
    FLOAT_STRING_REGEXP = /\A[+-]?(?:0|[1-9]\d*)(?:\.\d*)?(?:[eE][+-]?\d+)?\z/.freeze

    def valid?(value)
      value =~ FLOAT_STRING_REGEXP
    end

    def call(value)
      value.to_f
    end
  end

  # Collect multiple items into a single Array. Support
  # arguments separated by commas or multiple occurences.
  class ArrayOption < Option
    def call(value)
      @value ||= []
      if delimiter
        @value.concat value.split(delimiter, limit)
      else
        @value << value
      end
    end

    def default_value
      config[:default] || []
    end

    def delimiter
      config.fetch(:delimiter, ",")
    end

    def limit
      config[:limit] || 0
    end
  end

  # Cast the option argument to a Regexp.
  class RegexpOption < Option
    def call(value)
      Regexp.new(value)
    end
  end

  # An option that discards the return value, inherits from Bool
  # since it does not expect an argument.
  class NullOption < BoolOption
    def null?
      true
    end
  end
end