clive 0.8.1 → 1.0.0

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.
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