aboutsummaryrefslogtreecommitdiffstats
path: root/railties/lib/rails/vendor/thor-0.11.6/lib/thor/parser/arguments.rb
blob: fb5d965e06349e974c08aee2e32982ca57bcfcc5 (plain) (blame)
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
132
133
134
135
136
137
138
139
140
141
142
143
144
145
class Thor
  class Arguments #:nodoc:
    NUMERIC = /(\d*\.\d+|\d+)/

    # Receives an array of args and returns two arrays, one with arguments
    # and one with switches.
    #
    def self.split(args)
      arguments = []

      args.each do |item|
        break if item =~ /^-/
        arguments << item
      end

      return arguments, args[Range.new(arguments.size, -1)]
    end

    def self.parse(base, args)
      new(base).parse(args)
    end

    # Takes an array of Thor::Argument objects.
    #
    def initialize(arguments=[])
      @assigns, @non_assigned_required = {}, []
      @switches = arguments

      arguments.each do |argument|
        if argument.default
          @assigns[argument.human_name] = argument.default
        elsif argument.required?
          @non_assigned_required << argument
        end
      end
    end

    def parse(args)
      @pile = args.dup

      @switches.each do |argument|
        break unless peek
        @non_assigned_required.delete(argument)
        @assigns[argument.human_name] = send(:"parse_#{argument.type}", argument.human_name)
      end

      check_requirement!
      @assigns
    end

    private

      def peek
        @pile.first
      end

      def shift
        @pile.shift
      end

      def unshift(arg)
        unless arg.kind_of?(Array)
          @pile.unshift(arg)
        else
          @pile = arg + @pile
        end
      end

      def current_is_value?
        peek && peek.to_s !~ /^-/
      end

      # Runs through the argument array getting strings that contains ":" and
      # mark it as a hash:
      #
      #   [ "name:string", "age:integer" ]
      #
      # Becomes:
      #
      #   { "name" => "string", "age" => "integer" }
      #
      def parse_hash(name)
        return shift if peek.is_a?(Hash)
        hash = {}

        while current_is_value? && peek.include?(?:)
          key, value = shift.split(':')
          hash[key] = value
        end
        hash
      end

      # Runs through the argument array getting all strings until no string is
      # found or a switch is found.
      #
      #   ["a", "b", "c"]
      #
      # And returns it as an array:
      #
      #   ["a", "b", "c"]
      #
      def parse_array(name)
        return shift if peek.is_a?(Array)
        array = []

        while current_is_value?
          array << shift
        end
        array
      end

      # Check if the peel is numeric ofrmat and return a Float or Integer.
      # Otherwise raises an error.
      #
      def parse_numeric(name)
        return shift if peek.is_a?(Numeric)

        unless peek =~ NUMERIC && $& == peek
          raise MalformattedArgumentError, "expected numeric value for '#{name}'; got #{peek.inspect}"
        end

        $&.index('.') ? shift.to_f : shift.to_i
      end

      # Parse string, i.e., just return the current value in the pile.
      #
      def parse_string(name)
        shift
      end

      # Raises an error if @non_assigned_required array is not empty.
      #
      def check_requirement!
        unless @non_assigned_required.empty?
          names = @non_assigned_required.map do |o|
            o.respond_to?(:switch_name) ? o.switch_name : o.human_name
          end.join("', '")

          class_name = self.class.name.split('::').last.downcase
          raise RequiredArgumentMissingError, "no value provided for required #{class_name} '#{names}'"
        end
      end

  end
end