wtch 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,9 @@
1
+ class File #:nodoc:
2
+
3
+ unless File.respond_to?(:binread)
4
+ def self.binread(file)
5
+ File.open(file, 'rb') { |f| f.read }
6
+ end
7
+ end
8
+
9
+ end
@@ -0,0 +1,75 @@
1
+ class Thor
2
+ module CoreExt #:nodoc:
3
+
4
+ # A hash with indifferent access and magic predicates.
5
+ #
6
+ # hash = Thor::CoreExt::HashWithIndifferentAccess.new 'foo' => 'bar', 'baz' => 'bee', 'force' => true
7
+ #
8
+ # hash[:foo] #=> 'bar'
9
+ # hash['foo'] #=> 'bar'
10
+ # hash.foo? #=> true
11
+ #
12
+ class HashWithIndifferentAccess < ::Hash #:nodoc:
13
+
14
+ def initialize(hash={})
15
+ super()
16
+ hash.each do |key, value|
17
+ self[convert_key(key)] = value
18
+ end
19
+ end
20
+
21
+ def [](key)
22
+ super(convert_key(key))
23
+ end
24
+
25
+ def []=(key, value)
26
+ super(convert_key(key), value)
27
+ end
28
+
29
+ def delete(key)
30
+ super(convert_key(key))
31
+ end
32
+
33
+ def values_at(*indices)
34
+ indices.collect { |key| self[convert_key(key)] }
35
+ end
36
+
37
+ def merge(other)
38
+ dup.merge!(other)
39
+ end
40
+
41
+ def merge!(other)
42
+ other.each do |key, value|
43
+ self[convert_key(key)] = value
44
+ end
45
+ self
46
+ end
47
+
48
+ protected
49
+
50
+ def convert_key(key)
51
+ key.is_a?(Symbol) ? key.to_s : key
52
+ end
53
+
54
+ # Magic predicates. For instance:
55
+ #
56
+ # options.force? # => !!options['force']
57
+ # options.shebang # => "/usr/lib/local/ruby"
58
+ # options.test_framework?(:rspec) # => options[:test_framework] == :rspec
59
+ #
60
+ def method_missing(method, *args, &block)
61
+ method = method.to_s
62
+ if method =~ /^(\w+)\?$/
63
+ if args.empty?
64
+ !!self[$1]
65
+ else
66
+ self[$1] == args.first
67
+ end
68
+ else
69
+ self[method]
70
+ end
71
+ end
72
+
73
+ end
74
+ end
75
+ end
@@ -0,0 +1,100 @@
1
+ class Thor
2
+ module CoreExt #:nodoc:
3
+
4
+ if RUBY_VERSION >= '1.9'
5
+ class OrderedHash < ::Hash
6
+ end
7
+ else
8
+ # This class is based on the Ruby 1.9 ordered hashes.
9
+ #
10
+ # It keeps the semantics and most of the efficiency of normal hashes
11
+ # while also keeping track of the order in which elements were set.
12
+ #
13
+ class OrderedHash #:nodoc:
14
+ include Enumerable
15
+
16
+ Node = Struct.new(:key, :value, :next, :prev)
17
+
18
+ def initialize
19
+ @hash = {}
20
+ end
21
+
22
+ def [](key)
23
+ @hash[key] && @hash[key].value
24
+ end
25
+
26
+ def []=(key, value)
27
+ if node = @hash[key]
28
+ node.value = value
29
+ else
30
+ node = Node.new(key, value)
31
+
32
+ if @first.nil?
33
+ @first = @last = node
34
+ else
35
+ node.prev = @last
36
+ @last.next = node
37
+ @last = node
38
+ end
39
+ end
40
+
41
+ @hash[key] = node
42
+ value
43
+ end
44
+
45
+ def delete(key)
46
+ if node = @hash[key]
47
+ prev_node = node.prev
48
+ next_node = node.next
49
+
50
+ next_node.prev = prev_node if next_node
51
+ prev_node.next = next_node if prev_node
52
+
53
+ @first = next_node if @first == node
54
+ @last = prev_node if @last == node
55
+
56
+ value = node.value
57
+ end
58
+
59
+ @hash.delete(key)
60
+ value
61
+ end
62
+
63
+ def keys
64
+ self.map { |k, v| k }
65
+ end
66
+
67
+ def values
68
+ self.map { |k, v| v }
69
+ end
70
+
71
+ def each
72
+ return unless @first
73
+ yield [@first.key, @first.value]
74
+ node = @first
75
+ yield [node.key, node.value] while node = node.next
76
+ self
77
+ end
78
+
79
+ def merge(other)
80
+ hash = self.class.new
81
+
82
+ self.each do |key, value|
83
+ hash[key] = value
84
+ end
85
+
86
+ other.each do |key, value|
87
+ hash[key] = value
88
+ end
89
+
90
+ hash
91
+ end
92
+
93
+ def empty?
94
+ @hash.empty?
95
+ end
96
+ end
97
+ end
98
+
99
+ end
100
+ end
@@ -0,0 +1,30 @@
1
+ class Thor
2
+ # Thor::Error is raised when it's caused by wrong usage of thor classes. Those
3
+ # errors have their backtrace supressed and are nicely shown to the user.
4
+ #
5
+ # Errors that are caused by the developer, like declaring a method which
6
+ # overwrites a thor keyword, it SHOULD NOT raise a Thor::Error. This way, we
7
+ # ensure that developer errors are shown with full backtrace.
8
+ #
9
+ class Error < StandardError
10
+ end
11
+
12
+ # Raised when a task was not found.
13
+ #
14
+ class UndefinedTaskError < Error
15
+ end
16
+
17
+ # Raised when a task was found, but not invoked properly.
18
+ #
19
+ class InvocationError < Error
20
+ end
21
+
22
+ class UnknownArgumentError < Error
23
+ end
24
+
25
+ class RequiredArgumentMissingError < InvocationError
26
+ end
27
+
28
+ class MalformattedArgumentError < InvocationError
29
+ end
30
+ end
@@ -0,0 +1,168 @@
1
+ class Thor
2
+ module Invocation
3
+ def self.included(base) #:nodoc:
4
+ base.extend ClassMethods
5
+ end
6
+
7
+ module ClassMethods
8
+ # This method is responsible for receiving a name and find the proper
9
+ # class and task for it. The key is an optional parameter which is
10
+ # available only in class methods invocations (i.e. in Thor::Group).
11
+ def prepare_for_invocation(key, name) #:nodoc:
12
+ case name
13
+ when Symbol, String
14
+ Thor::Util.find_class_and_task_by_namespace(name.to_s, !key)
15
+ else
16
+ name
17
+ end
18
+ end
19
+ end
20
+
21
+ # Make initializer aware of invocations and the initialization args.
22
+ def initialize(args=[], options={}, config={}, &block) #:nodoc:
23
+ @_invocations = config[:invocations] || Hash.new { |h,k| h[k] = [] }
24
+ @_initializer = [ args, options, config ]
25
+ super
26
+ end
27
+
28
+ # Receives a name and invokes it. The name can be a string (either "task" or
29
+ # "namespace:task"), a Thor::Task, a Class or a Thor instance. If the task
30
+ # cannot be guessed by name, it can also be supplied as second argument.
31
+ #
32
+ # You can also supply the arguments, options and configuration values for
33
+ # the task to be invoked, if none is given, the same values used to
34
+ # initialize the invoker are used to initialize the invoked.
35
+ #
36
+ # When no name is given, it will invoke the default task of the current class.
37
+ #
38
+ # ==== Examples
39
+ #
40
+ # class A < Thor
41
+ # def foo
42
+ # invoke :bar
43
+ # invoke "b:hello", ["José"]
44
+ # end
45
+ #
46
+ # def bar
47
+ # invoke "b:hello", ["José"]
48
+ # end
49
+ # end
50
+ #
51
+ # class B < Thor
52
+ # def hello(name)
53
+ # puts "hello #{name}"
54
+ # end
55
+ # end
56
+ #
57
+ # You can notice that the method "foo" above invokes two tasks: "bar",
58
+ # which belongs to the same class and "hello" which belongs to the class B.
59
+ #
60
+ # By using an invocation system you ensure that a task is invoked only once.
61
+ # In the example above, invoking "foo" will invoke "b:hello" just once, even
62
+ # if it's invoked later by "bar" method.
63
+ #
64
+ # When class A invokes class B, all arguments used on A initialization are
65
+ # supplied to B. This allows lazy parse of options. Let's suppose you have
66
+ # some rspec tasks:
67
+ #
68
+ # class Rspec < Thor::Group
69
+ # class_option :mock_framework, :type => :string, :default => :rr
70
+ #
71
+ # def invoke_mock_framework
72
+ # invoke "rspec:#{options[:mock_framework]}"
73
+ # end
74
+ # end
75
+ #
76
+ # As you noticed, it invokes the given mock framework, which might have its
77
+ # own options:
78
+ #
79
+ # class Rspec::RR < Thor::Group
80
+ # class_option :style, :type => :string, :default => :mock
81
+ # end
82
+ #
83
+ # Since it's not rspec concern to parse mock framework options, when RR
84
+ # is invoked all options are parsed again, so RR can extract only the options
85
+ # that it's going to use.
86
+ #
87
+ # If you want Rspec::RR to be initialized with its own set of options, you
88
+ # have to do that explicitely:
89
+ #
90
+ # invoke "rspec:rr", [], :style => :foo
91
+ #
92
+ # Besides giving an instance, you can also give a class to invoke:
93
+ #
94
+ # invoke Rspec::RR, [], :style => :foo
95
+ #
96
+ def invoke(name=nil, *args)
97
+ if name.nil?
98
+ warn "[Thor] Calling invoke() without argument is deprecated. Please use invoke_all instead.\n#{caller.join("\n")}"
99
+ return invoke_all
100
+ end
101
+
102
+ args.unshift(nil) if Array === args.first || NilClass === args.first
103
+ task, args, opts, config = args
104
+
105
+ klass, task = _retrieve_class_and_task(name, task)
106
+ raise "Expected Thor class, got #{klass}" unless klass <= Thor::Base
107
+
108
+ args, opts, config = _parse_initialization_options(args, opts, config)
109
+ klass.send(:dispatch, task, args, opts, config)
110
+ end
111
+
112
+ # Invoke the given task if the given args.
113
+ def invoke_task(task, *args) #:nodoc:
114
+ current = @_invocations[self.class]
115
+
116
+ unless current.include?(task.name)
117
+ current << task.name
118
+ task.run(self, *args)
119
+ end
120
+ end
121
+
122
+ # Invoke all tasks for the current instance.
123
+ def invoke_all #:nodoc:
124
+ self.class.all_tasks.map { |_, task| invoke_task(task) }
125
+ end
126
+
127
+ # Invokes using shell padding.
128
+ def invoke_with_padding(*args)
129
+ with_padding { invoke(*args) }
130
+ end
131
+
132
+ protected
133
+
134
+ # Configuration values that are shared between invocations.
135
+ def _shared_configuration #:nodoc:
136
+ { :invocations => @_invocations }
137
+ end
138
+
139
+ # This method simply retrieves the class and task to be invoked.
140
+ # If the name is nil or the given name is a task in the current class,
141
+ # use the given name and return self as class. Otherwise, call
142
+ # prepare_for_invocation in the current class.
143
+ def _retrieve_class_and_task(name, sent_task=nil) #:nodoc:
144
+ case
145
+ when name.nil?
146
+ [self.class, nil]
147
+ when self.class.all_tasks[name.to_s]
148
+ [self.class, name.to_s]
149
+ else
150
+ klass, task = self.class.prepare_for_invocation(nil, name)
151
+ [klass, task || sent_task]
152
+ end
153
+ end
154
+
155
+ # Initialize klass using values stored in the @_initializer.
156
+ def _parse_initialization_options(args, opts, config) #:nodoc:
157
+ stored_args, stored_opts, stored_config = @_initializer
158
+
159
+ args ||= stored_args.dup
160
+ opts ||= stored_opts.dup
161
+
162
+ config ||= {}
163
+ config = stored_config.merge(_shared_configuration).merge!(config)
164
+
165
+ [ args, opts, config ]
166
+ end
167
+ end
168
+ end
@@ -0,0 +1,4 @@
1
+ require 'thor/parser/argument'
2
+ require 'thor/parser/arguments'
3
+ require 'thor/parser/option'
4
+ require 'thor/parser/options'
@@ -0,0 +1,67 @@
1
+ class Thor
2
+ class Argument #:nodoc:
3
+ VALID_TYPES = [ :numeric, :hash, :array, :string ]
4
+
5
+ attr_reader :name, :description, :required, :type, :default, :banner
6
+ alias :human_name :name
7
+
8
+ def initialize(name, description=nil, required=true, type=:string, default=nil, banner=nil)
9
+ class_name = self.class.name.split("::").last
10
+
11
+ raise ArgumentError, "#{class_name} name can't be nil." if name.nil?
12
+ raise ArgumentError, "Type :#{type} is not valid for #{class_name.downcase}s." if type && !valid_type?(type)
13
+
14
+ @name = name.to_s
15
+ @description = description
16
+ @required = required || false
17
+ @type = (type || :string).to_sym
18
+ @default = default
19
+ @banner = banner || default_banner
20
+
21
+ validate! # Trigger specific validations
22
+ end
23
+
24
+ def usage
25
+ required? ? banner : "[#{banner}]"
26
+ end
27
+
28
+ def required?
29
+ required
30
+ end
31
+
32
+ def show_default?
33
+ case default
34
+ when Array, String, Hash
35
+ !default.empty?
36
+ else
37
+ default
38
+ end
39
+ end
40
+
41
+ protected
42
+
43
+ def validate!
44
+ raise ArgumentError, "An argument cannot be required and have default value." if required? && !default.nil?
45
+ end
46
+
47
+ def valid_type?(type)
48
+ self.class::VALID_TYPES.include?(type.to_sym)
49
+ end
50
+
51
+ def default_banner
52
+ case type
53
+ when :boolean
54
+ nil
55
+ when :string, :default
56
+ human_name.upcase
57
+ when :numeric
58
+ "N"
59
+ when :hash
60
+ "key:value"
61
+ when :array
62
+ "one two three"
63
+ end
64
+ end
65
+
66
+ end
67
+ end