clive 0.8.1 → 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (52) hide show
  1. data/LICENSE +1 -1
  2. data/README.md +328 -227
  3. data/lib/clive.rb +130 -50
  4. data/lib/clive/argument.rb +170 -0
  5. data/lib/clive/arguments.rb +139 -0
  6. data/lib/clive/arguments/parser.rb +210 -0
  7. data/lib/clive/base.rb +189 -0
  8. data/lib/clive/command.rb +342 -444
  9. data/lib/clive/error.rb +66 -0
  10. data/lib/clive/formatter.rb +57 -141
  11. data/lib/clive/formatter/colour.rb +37 -0
  12. data/lib/clive/formatter/plain.rb +172 -0
  13. data/lib/clive/option.rb +185 -75
  14. data/lib/clive/option/runner.rb +163 -0
  15. data/lib/clive/output.rb +141 -16
  16. data/lib/clive/parser.rb +180 -87
  17. data/lib/clive/struct_hash.rb +109 -0
  18. data/lib/clive/type.rb +117 -0
  19. data/lib/clive/type/definitions.rb +170 -0
  20. data/lib/clive/type/lookup.rb +23 -0
  21. data/lib/clive/version.rb +3 -3
  22. data/spec/clive/a_cli_spec.rb +245 -0
  23. data/spec/clive/argument_spec.rb +148 -0
  24. data/spec/clive/arguments/parser_spec.rb +35 -0
  25. data/spec/clive/arguments_spec.rb +191 -0
  26. data/spec/clive/command_spec.rb +276 -209
  27. data/spec/clive/formatter/colour_spec.rb +129 -0
  28. data/spec/clive/formatter/plain_spec.rb +129 -0
  29. data/spec/clive/option/runner_spec.rb +92 -0
  30. data/spec/clive/option_spec.rb +149 -23
  31. data/spec/clive/output_spec.rb +86 -2
  32. data/spec/clive/parser_spec.rb +201 -81
  33. data/spec/clive/struct_hash_spec.rb +82 -0
  34. data/spec/clive/type/definitions_spec.rb +312 -0
  35. data/spec/clive/type_spec.rb +107 -0
  36. data/spec/clive_spec.rb +60 -0
  37. data/spec/extras/expectations.rb +86 -0
  38. data/spec/extras/focus.rb +22 -0
  39. data/spec/helper.rb +35 -0
  40. metadata +56 -36
  41. data/lib/clive/bool.rb +0 -67
  42. data/lib/clive/exceptions.rb +0 -54
  43. data/lib/clive/flag.rb +0 -199
  44. data/lib/clive/switch.rb +0 -31
  45. data/lib/clive/tokens.rb +0 -141
  46. data/spec/clive/bool_spec.rb +0 -54
  47. data/spec/clive/flag_spec.rb +0 -117
  48. data/spec/clive/formatter_spec.rb +0 -108
  49. data/spec/clive/switch_spec.rb +0 -14
  50. data/spec/clive/tokens_spec.rb +0 -38
  51. data/spec/shared_specs.rb +0 -16
  52. data/spec/spec_helper.rb +0 -12
@@ -0,0 +1,86 @@
1
+ module MiniTest::Assertions
2
+
3
+ def assert_true obj, msg = nil
4
+ msg = message(msg) { "Expected #{mu_pp(obj)} to be true" }
5
+ assert(obj == true, msg)
6
+ end
7
+
8
+ def assert_false obj, msg = nil
9
+ msg = message(msg) { "Expected #{mu_pp(obj)} to be false" }
10
+ assert(obj == false, msg)
11
+ end
12
+
13
+ def assert_has o1, op, o2 = UNDEFINED, msg = nil
14
+ return assert_predicate o1, op, msg if UNDEFINED == o2
15
+ msg = message(msg) { "Expected #{mu_pp(o1)} to have #{op} #{mu_pp(o2)}" }
16
+ assert o1.__send__(op, o2), msg
17
+ end
18
+
19
+ def refute_has o1, op, o2 = UNDEFINED, msg = nil
20
+ return refute_predicate o1, op, msg if UNDEFINED == o2
21
+ msg = message(msg) { "Expected #{mu_pp(o1)} to not have #{op} #{mu_pp(o2)}" }
22
+ refute o1.__send__(op, o2), msg
23
+ end
24
+
25
+ def assert_has_option obj, name, msg = nil
26
+ msg = message(msg) { "Expected #{mu_pp(obj)} to have the option #{name}" }
27
+ assert obj.has_option?(name), msg
28
+ end
29
+
30
+ def assert_has_command obj, name, msg = nil
31
+ msg = message(msg) { "Expected #{mu_pp(obj)} to have the command #{name}" }
32
+ assert obj.has_command?(name), msg
33
+ end
34
+
35
+ end
36
+
37
+
38
+ module MiniTest::Expectations
39
+
40
+ # this { ... }.must_raise ExceptionalException
41
+ alias_method :this, :proc
42
+
43
+ infect_an_assertion :assert_has, :must_have, :reverse
44
+ infect_an_assertion :refute_has, :wont_have, :reverse
45
+
46
+ infect_an_assertion :assert_true, :must_be_true, :unary
47
+ infect_an_assertion :assert_false, :must_be_false, :unary
48
+
49
+ alias_method :wont_be_false, :must_be_true
50
+ alias_method :wont_be_true, :must_be_false
51
+
52
+ infect_an_assertion :assert_has_option, :must_have_option, :reverse
53
+ infect_an_assertion :assert_has_command, :must_have_command, :reverse
54
+
55
+ # @example
56
+ #
57
+ # arg.must_be_argument :name => :arg, :optional => true, :type => Integer
58
+ #
59
+ def must_be_argument(opts)
60
+ opts.each do |k,v|
61
+ self.instance_variable_get("@#{k}").must_equal v
62
+ end
63
+ end
64
+
65
+ end
66
+
67
+ class Hash
68
+
69
+ # @example
70
+ #
71
+ # hsh = {:a => 5}
72
+ # hsh.must_contain :a => 5
73
+ #
74
+ def must_contain(kv)
75
+ kv.all? {|k,v| self[k].must_equal v }
76
+ end
77
+
78
+ # @example
79
+ #
80
+ # hsh = {:a => 5}
81
+ # hsh.wont_contain :b => 5
82
+ #
83
+ def wont_contain(kv)
84
+ kv.any? {|k,v| self[k].wont_equal v }
85
+ end
86
+ end
@@ -0,0 +1,22 @@
1
+ # Allows you to write
2
+ #
3
+ # it 'does something', :focus => true do
4
+ # # ...
5
+ # end
6
+ #
7
+ # and when ENV['FOCUS'] is set to 'true' it will only run tests with
8
+ # `:focus => true`.
9
+ #
10
+ # $ FOCUS=true bundler exec rake
11
+ #
12
+
13
+ FOCUS_MODE = ENV['FOCUS'] == 'true'
14
+
15
+ class MiniTest::Spec
16
+ class << self; alias_method :__old_it, :it; end
17
+ def self.it(desc="anonymous", opts={}, &block)
18
+ if (FOCUS_MODE == true && opts[:focus] == true) || FOCUS_MODE == false
19
+ __old_it(desc, &block)
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,35 @@
1
+ $: << File.dirname(__FILE__) + '/..'
2
+
3
+ if RUBY_VERSION >= "1.9"
4
+ begin
5
+ require 'duvet'
6
+ Duvet.start :filter => 'lib/clive'
7
+ rescue LoadError
8
+ # it doesn't really matter
9
+ end
10
+ end
11
+
12
+ require 'lib/clive'
13
+ require 'rubygems'
14
+ require 'shellwords'
15
+
16
+ class Clive::Command
17
+ def self.create(*args, &block)
18
+ i = new(*args, &block)
19
+ i.run_block({})
20
+ i
21
+ end
22
+ end
23
+
24
+ def s(str)
25
+ Shellwords.split(str)
26
+ end
27
+
28
+ gem 'minitest' # use latest version
29
+ require 'minitest/autorun'
30
+ require 'minitest/pride'
31
+
32
+ require 'extras/expectations'
33
+ require 'extras/focus'
34
+
35
+ require 'mocha'
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: clive
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.8.1
4
+ version: 1.0.0
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,34 +9,33 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2011-05-26 00:00:00.000000000 +01:00
13
- default_executable:
12
+ date: 2012-01-30 00:00:00.000000000 Z
14
13
  dependencies:
15
14
  - !ruby/object:Gem::Dependency
16
- name: ast_ast
17
- requirement: &2156527100 !ruby/object:Gem::Requirement
15
+ name: minitest
16
+ requirement: &2157118780 !ruby/object:Gem::Requirement
18
17
  none: false
19
18
  requirements:
20
19
  - - ~>
21
20
  - !ruby/object:Gem::Version
22
- version: 0.2.1
23
- type: :runtime
21
+ version: '2.6'
22
+ type: :development
24
23
  prerelease: false
25
- version_requirements: *2156527100
24
+ version_requirements: *2157118780
26
25
  - !ruby/object:Gem::Dependency
27
- name: attr_plus
28
- requirement: &2156526180 !ruby/object:Gem::Requirement
26
+ name: mocha
27
+ requirement: &2157118180 !ruby/object:Gem::Requirement
29
28
  none: false
30
29
  requirements:
31
30
  - - ~>
32
31
  - !ruby/object:Gem::Version
33
- version: '0.3'
34
- type: :runtime
32
+ version: '0.10'
33
+ type: :development
35
34
  prerelease: false
36
- version_requirements: *2156526180
35
+ version_requirements: *2157118180
37
36
  description: ! " Clive provides a DSL for building command line interfaces. It
38
- allows \n you to define commands, switches, flags (switches with options) and
39
- \n boolean switches, it then parses the input and runs the correct blocks.\n"
37
+ allows \n you to define commands and options, which can also take arguments,
38
+ \n and then runs the correct stuff!\n"
40
39
  email: m@hawx.me
41
40
  executables: []
42
41
  extensions: []
@@ -44,30 +43,43 @@ extra_rdoc_files: []
44
43
  files:
45
44
  - README.md
46
45
  - LICENSE
47
- - lib/clive/bool.rb
46
+ - lib/clive/argument.rb
47
+ - lib/clive/arguments/parser.rb
48
+ - lib/clive/arguments.rb
49
+ - lib/clive/base.rb
48
50
  - lib/clive/command.rb
49
- - lib/clive/exceptions.rb
50
- - lib/clive/flag.rb
51
+ - lib/clive/error.rb
52
+ - lib/clive/formatter/colour.rb
53
+ - lib/clive/formatter/plain.rb
51
54
  - lib/clive/formatter.rb
55
+ - lib/clive/option/runner.rb
52
56
  - lib/clive/option.rb
53
57
  - lib/clive/output.rb
54
58
  - lib/clive/parser.rb
55
- - lib/clive/switch.rb
56
- - lib/clive/tokens.rb
59
+ - lib/clive/struct_hash.rb
60
+ - lib/clive/type/definitions.rb
61
+ - lib/clive/type/lookup.rb
62
+ - lib/clive/type.rb
57
63
  - lib/clive/version.rb
58
64
  - lib/clive.rb
59
- - spec/clive/bool_spec.rb
65
+ - spec/clive/a_cli_spec.rb
66
+ - spec/clive/argument_spec.rb
67
+ - spec/clive/arguments/parser_spec.rb
68
+ - spec/clive/arguments_spec.rb
60
69
  - spec/clive/command_spec.rb
61
- - spec/clive/flag_spec.rb
62
- - spec/clive/formatter_spec.rb
70
+ - spec/clive/formatter/colour_spec.rb
71
+ - spec/clive/formatter/plain_spec.rb
72
+ - spec/clive/option/runner_spec.rb
63
73
  - spec/clive/option_spec.rb
64
74
  - spec/clive/output_spec.rb
65
75
  - spec/clive/parser_spec.rb
66
- - spec/clive/switch_spec.rb
67
- - spec/clive/tokens_spec.rb
68
- - spec/shared_specs.rb
69
- - spec/spec_helper.rb
70
- has_rdoc: true
76
+ - spec/clive/struct_hash_spec.rb
77
+ - spec/clive/type/definitions_spec.rb
78
+ - spec/clive/type_spec.rb
79
+ - spec/clive_spec.rb
80
+ - spec/extras/expectations.rb
81
+ - spec/extras/focus.rb
82
+ - spec/helper.rb
71
83
  homepage: http://github.com/hawx/clive
72
84
  licenses: []
73
85
  post_install_message:
@@ -88,19 +100,27 @@ required_rubygems_version: !ruby/object:Gem::Requirement
88
100
  version: '0'
89
101
  requirements: []
90
102
  rubyforge_project:
91
- rubygems_version: 1.6.2
103
+ rubygems_version: 1.8.11
92
104
  signing_key:
93
105
  specification_version: 3
94
106
  summary: A DSL for building command line interfaces.
95
107
  test_files:
96
- - spec/clive/bool_spec.rb
108
+ - spec/clive/a_cli_spec.rb
109
+ - spec/clive/argument_spec.rb
110
+ - spec/clive/arguments/parser_spec.rb
111
+ - spec/clive/arguments_spec.rb
97
112
  - spec/clive/command_spec.rb
98
- - spec/clive/flag_spec.rb
99
- - spec/clive/formatter_spec.rb
113
+ - spec/clive/formatter/colour_spec.rb
114
+ - spec/clive/formatter/plain_spec.rb
115
+ - spec/clive/option/runner_spec.rb
100
116
  - spec/clive/option_spec.rb
101
117
  - spec/clive/output_spec.rb
102
118
  - spec/clive/parser_spec.rb
103
- - spec/clive/switch_spec.rb
104
- - spec/clive/tokens_spec.rb
105
- - spec/shared_specs.rb
106
- - spec/spec_helper.rb
119
+ - spec/clive/struct_hash_spec.rb
120
+ - spec/clive/type/definitions_spec.rb
121
+ - spec/clive/type_spec.rb
122
+ - spec/clive_spec.rb
123
+ - spec/extras/expectations.rb
124
+ - spec/extras/focus.rb
125
+ - spec/helper.rb
126
+ has_rdoc:
@@ -1,67 +0,0 @@
1
- module Clive
2
-
3
- # A switch which can be triggered with either --no-[name] and --[name].
4
- # The 'truth' of this is then passed to the block.
5
- #
6
- class Bool < Option
7
- attr_accessor :truth
8
-
9
- # Creates a new Bool switch instance. A boolean switch has a truth,
10
- # this determines what is passed to the block. They should be created
11
- # in pairs so one can be +--something+ the other +--no-something+.
12
- # NOTE: this does not happen within this class!
13
- #
14
- # +short+ and/or +desc+ can be omitted when creating a Boolean, all
15
- # other arguments must be present.
16
- #
17
- # @param names [Array[Symbol]]
18
- # Names that the boolean switch can be called with, must include
19
- # a long name (eg. 2 or more characters) so that the --no- can
20
- # be prefixed.
21
- #
22
- # @param desc [String]
23
- # A description of the bool.
24
- #
25
- # @param truth [true, false]
26
- # Truth of the switch to create.
27
- #
28
- # @yield [true, false] A block to be run when the switch is triggered
29
- # @raise [MissingLongName] Raised when a long name is not given
30
- #
31
- def initialize(names, desc, truth, &block)
32
- @names = []
33
- names.each do |i|
34
- if truth
35
- @names << i.to_s
36
- else
37
- @names << "no-#{i.to_s}" if i.to_s.length > 1
38
- end
39
- end
40
-
41
- # booleans require a long name to add --no- to
42
- unless @names.find_all {|i| i.length > 1}.length > 0
43
- raise MissingLongName, @names[0]
44
- end
45
-
46
- @desc = desc
47
- @truth = truth
48
- @block = block
49
- end
50
-
51
- # Run the block with the switches truth.
52
- def run
53
- @block.call(@truth)
54
- end
55
-
56
- # Should only return a hash when this is the 'true' switch.
57
- # @see Clive::Option#to_h
58
- def to_h
59
- if @truth
60
- {'names' => names_to_strings(true), 'desc' => @desc}
61
- else
62
- nil
63
- end
64
- end
65
-
66
- end
67
- end
@@ -1,54 +0,0 @@
1
- module Clive
2
-
3
- # General problem
4
- class CliveError < StandardError
5
- attr_accessor :args
6
-
7
- def initialize(*args)
8
- @args = args
9
- end
10
-
11
- def self.filter_backtrace(array)
12
- unless $DEBUG
13
- array = [$0]
14
- end
15
- array
16
- end
17
-
18
- def set_backtrace(array)
19
- super(self.class.filter_backtrace(array))
20
- end
21
-
22
- def message
23
- self.reason + ': ' + args.join(' ')
24
- end
25
- alias_method :to_s, :message
26
-
27
- end
28
-
29
- # General problem with input
30
- class ParseError < CliveError
31
- def reason; "parse error"; end
32
- end
33
-
34
- # Long name is missing for bool
35
- class MissingLongName < CliveError
36
- def reason; "missing long name"; end
37
- end
38
-
39
- # A flag has a missing argument
40
- class MissingArgument < ParseError
41
- def reason; "missing argument"; end
42
- end
43
-
44
- # A flag has a wrong argument
45
- class InvalidArgument < ParseError
46
- def reason; "invalid argument"; end
47
- end
48
-
49
- # An option that wasn't defined has been found
50
- class NoOptionError < ParseError
51
- def reason; "invalid option"; end
52
- end
53
-
54
- end
@@ -1,199 +0,0 @@
1
- module Clive
2
-
3
- # A switch that takes one or more arguments.
4
- # eg. wget --tries=10
5
- # wget -t 10
6
- #
7
- class Flag < Option
8
- attr_reader :args
9
-
10
- # Creates a new Flag instance. A flag is a switch that can take one or more
11
- # arguments.
12
- #
13
- # @param names [Array[Symbol]]
14
- # An array of names the flag can be invoked by. Can contain a long name
15
- # and/or a short name.
16
- #
17
- # @param desc [String]
18
- # A description of the flag.
19
- #
20
- # @param arguments [String, Array, Range]
21
- # Either, a string showing the arguments to be given, eg.
22
- #
23
- # "FROM" # single argument required, or
24
- # "[FROM]" # single optional argument, or
25
- # "FROM TO" # multiple arguments required, or
26
- # "FROM [VIA] TO" # multiple arguments with optional argument
27
- #
28
- # OR an array of acceptable inputs, eg.
29
- #
30
- # %w(large small medium) # will only accept these arguments
31
- #
32
- # OR a range, showing the acceptable inputs, eg.
33
- # 1..10 #=> means 1, 2, 3, ..., 8, 9, 10
34
- #
35
- # @yield [String]
36
- # A block to be run if switch is triggered, will always be passed a string
37
- #
38
- def initialize(names, desc, arguments, &block)
39
- @names = names.map(&:to_s)
40
- self.args = (arguments || "ARG")
41
-
42
- @desc = desc
43
- @block = block
44
- end
45
-
46
- def args=(val)
47
- case val
48
- when String
49
- if val[-3..-1] == "..."
50
- @args = {:type => :splat, :base_name => val[0..-4]}
51
-
52
- else
53
- @args = {:type => :list, :arguments => []}
54
- val.split(' ').each do |arg|
55
- optional = false
56
- if arg[0] == "["
57
- optional = true
58
- arg = arg[1..-2]
59
- end
60
-
61
- @args[:arguments] << {:name => arg, :optional => optional}
62
- end
63
- end
64
- when Range
65
- @args = {:type => :range, :range => val}
66
- when Array
67
- @args = {:type => :choice, :items => val}
68
- end
69
- end
70
-
71
- # Runs the block that was given with an argument
72
- #
73
- # @param [Array] args arguments to pass to the block
74
- # @raise [InvalidArgument] only if +args+ is an array of acceptable inputs
75
- # and a match is not found.
76
- def run(args)
77
- case @args[:type]
78
- when :list
79
- args = optimise_fill(args, @args[:arguments].map {|i| !i[:optional] })
80
- when :choice
81
- unless @args[:items].map{|i| i.to_s}.include?(args[0])
82
- raise InvalidArgument.new(args)
83
- end
84
- when :range
85
- unless @args[:range].to_a.map {|i| i.to_s}.include?(args[0])
86
- raise InvalidArgument.new(args)
87
- end
88
- when :splat
89
- args = [args]
90
- end
91
- @block.call(*args)
92
- end
93
-
94
-
95
- # @param type [Symbol]
96
- # Can be passed three things; :all, returns size of all arguments; :optional
97
- # returns all optional arguments; :mandatory, returns size of mandatory arguments.
98
- def arg_size(type=:all)
99
- case @args[:type]
100
- when :list
101
- case type
102
- when :all
103
- @args[:arguments].size
104
- when :optional
105
- @args[:arguments].find_all {|i| i[:optional] == true }.size
106
- when :mandatory
107
- @args[:arguments].find_all {|i| i[:optional] == false }.size
108
- end
109
-
110
- when :choice, :range
111
- (type == :optional) ? 0 : 1
112
-
113
- when :splat
114
- case type
115
- when :all
116
- 1.0/0 # Infinity!
117
- when :optional
118
- 1.0/0 # Infinity!
119
- when :mandatory
120
- 1
121
- end
122
- end
123
- end
124
-
125
- def args_to_string
126
- case @args[:type]
127
- when :list
128
- r = []
129
- @args[:arguments].each do |arg|
130
- if arg[:optional]
131
- r << "[" + arg[:name] + "]"
132
- else
133
- r << "<" + arg[:name] + ">"
134
- end
135
- end
136
- r.join(' ')
137
- when :choice, :range
138
- ""
139
- when :splat
140
- "<#{@args[:base_name]}1> ..."
141
- end
142
- end
143
-
144
- def options_to_string
145
- case @args[:type]
146
- when :list, :splat
147
- ''
148
- when :choice
149
- '(' + @args[:items].join(', ') + ')'
150
- when :range
151
- '(' + @args[:range].to_s + ')'
152
- end
153
- end
154
-
155
- def to_h
156
- {
157
- 'names' => names_to_strings,
158
- 'desc' => @desc,
159
- 'args' => args_to_string,
160
- 'options' => options_to_string
161
- }
162
- end
163
-
164
-
165
- # Attempts to fill +self+ with values from +input+, giving priority to
166
- # true, then false. If insufficient input to fill all false will use nil.
167
- #
168
- # @param [Array] input array of values to fill +self+ with
169
- # @return [Array] filled array
170
- #
171
- # @example
172
- #
173
- # [true, false, false, true].optimise_fill(["a", "b", "c"])
174
- # #=> ["a", "b", nil, "c"]
175
- #
176
- #
177
- def optimise_fill(input, match)
178
- diff = input.size - match.reject{|i| i == false}.size
179
-
180
- result = []
181
- match.each_index do |i|
182
- curr_item = match[i]
183
- if curr_item == true
184
- result << input.shift
185
- else
186
- if diff > 0
187
- result << input.shift
188
- diff -= 1
189
- else
190
- result << nil
191
- end
192
- end
193
- end
194
- result
195
- end
196
-
197
-
198
- end
199
- end