clive 0.2.3 → 0.3.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.
@@ -0,0 +1,48 @@
1
+ class 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
+ # a flag has a missing argument
35
+ class MissingArgument < ParseError
36
+ def reason; "missing argument"; end
37
+ end
38
+
39
+ # a option that wasn't defined has been found
40
+ class InvalidOption < ParseError
41
+ def reason; "invalid option"; end
42
+ end
43
+
44
+ class MissingLongName < CliveError
45
+ def reason; "missing long name"; end
46
+ end
47
+
48
+ end
@@ -2,24 +2,15 @@ class Clive
2
2
 
3
3
  class Array < ::Array
4
4
 
5
- # If passed a Symbol or String will get the item with that name,
6
- # checks #long and #short if available or #name. Otherwise does
7
- # what you expect of an Array (see Array#[])
5
+ # If passed a Symbol or String will get the item with that name.
6
+ # Otherwise does what you expect of an Array (see ::Array#[])
8
7
  #
9
8
  # @param [Symbol, String, Integer, Range] val name or index of item to return
10
9
  # @return the item that has been found
11
10
  def [](val)
12
11
  val = val.to_s if val.is_a? Symbol
13
12
  if val.is_a? String
14
- if self[0].respond_to?(:name)
15
- self.find_all {|i| i.name == val}[0]
16
- elsif self[0].respond_to?(:long)
17
- if val.length == 1
18
- self.find_all {|i| i.short == val}[0]
19
- else
20
- self.find_all {|i| i.long == val}[0]
21
- end
22
- end
13
+ self.find_all {|i| i.names.include?(val)}[0]
23
14
  else
24
15
  super
25
16
  end
@@ -0,0 +1,71 @@
1
+ class Clive
2
+
3
+ # A switch that takes an argument, with or without an equals
4
+ # eg. wget --tries=10
5
+ # wget -t 10
6
+ #
7
+ class Flag < Option
8
+ attr_accessor :arg_name, :optional
9
+
10
+ # Creates a new Flag instance.
11
+ #
12
+ # +short+ _or_ +long+ can be omitted but not both.
13
+ #
14
+ # @overload flag(short, long, desc, &block)
15
+ # Creates a new flag
16
+ # @param [Symbol] short single character for short flag, eg. +:t+ => +-t 10+
17
+ # @param [Symbol] long longer switch to be used, eg. +:tries+ => +--tries=10+
18
+ # @param [String] desc the description for the flag
19
+ #
20
+ # @yield [String] A block to be run if switch is triggered
21
+ #
22
+ def initialize(*args, &block)
23
+ @names = []
24
+ @optional = false
25
+ @arg_name = "ARG"
26
+
27
+ args.each do |i|
28
+ if i.is_a? String
29
+ if i =~ /^[\[\]A-Z0-9]+$/
30
+ @arg_name = i
31
+ else
32
+ @desc = i
33
+ end
34
+ else
35
+ @names << i.to_s
36
+ end
37
+ end
38
+
39
+ if @arg_name[0] == "["
40
+ @arg_name = @arg_name[1..@arg_name.length-2]
41
+ @optional = true
42
+ end
43
+
44
+ @block = block
45
+ end
46
+
47
+ # Runs the block that was given with an argument
48
+ #
49
+ # @param [String] arg argument to pass to the block
50
+ def run(arg)
51
+ @block.call(arg)
52
+ end
53
+
54
+ # @return [String] summary for help
55
+ def summary(width=30, prepend=5)
56
+ n = names_to_strings.join(', ')
57
+ if @optional
58
+ n << " [#{@arg_name}]"
59
+ else
60
+ n << " #{@arg_name}"
61
+ end
62
+
63
+ spaces = width-n.length
64
+ spaces = 1 if spaces < 1
65
+ s = spaces(spaces)
66
+ p = spaces(prepend)
67
+ "#{p}#{n}#{s}#{@desc}"
68
+ end
69
+
70
+ end
71
+ end
@@ -0,0 +1,77 @@
1
+ class Clive
2
+
3
+ class Option
4
+ attr_accessor :names, :desc, :block
5
+
6
+ def initialize(desc, *names, &block)
7
+ @names = names
8
+ @desc = desc
9
+ @block = block
10
+ end
11
+
12
+ def run
13
+ @block.call
14
+ end
15
+
16
+ def summary(width=30, prepend=5)
17
+ n = names_to_strings.join(', ')
18
+ spaces = width-n.length
19
+ spaces = 1 if spaces < 1
20
+ s = spaces(spaces)
21
+ p = spaces(prepend)
22
+ "#{p}#{n}#{s}#{@desc}"
23
+ end
24
+
25
+ # Convert the names to strings, depending on length
26
+ #
27
+ # @param [Boolean] bool whether to add [no-] to long
28
+ #
29
+ # @example
30
+ #
31
+ # @names = ['v', 'verbose']
32
+ # names_to_strings
33
+ # #=> ['-v', '--verbose']
34
+ #
35
+ def names_to_strings(bool=false)
36
+ r = []
37
+ @names.each do |i|
38
+ next if i.nil?
39
+ if i.length == 1 # short
40
+ r << "-#{i}"
41
+ else # long
42
+ if bool
43
+ r << "--[no-]#{i}"
44
+ else
45
+ r << "--#{i}"
46
+ end
47
+ end
48
+ end
49
+ r
50
+ end
51
+
52
+ # Create a string of +n+ spaces
53
+ def spaces(n)
54
+ s = ''
55
+ (0...n).each {s << ' '}
56
+ s
57
+ end
58
+
59
+ # Tries to get the short name, if not choose lowest alphabetically
60
+ #
61
+ # @return [String] name to sort by
62
+ def sort_name
63
+ r = @names.sort[0]
64
+ @names.each do |i|
65
+ if i.length == 1
66
+ r = i
67
+ end
68
+ end
69
+ r
70
+ end
71
+
72
+ def <=>(other)
73
+ self.sort_name <=> other.sort_name
74
+ end
75
+
76
+ end
77
+ end
@@ -0,0 +1,40 @@
1
+ class Clive
2
+
3
+ # A string that takes no argument, beginning with one or two dashes
4
+ # eg. ruby --version
5
+ # ruby -v
6
+ #
7
+ class Switch < Option
8
+
9
+ # Create a new Switch instance.
10
+ #
11
+ # +short+ _or_ +long+ may be omitted but not both.
12
+ #
13
+ # @overload switch(short, long, desc, &block)
14
+ # Creates a new switch
15
+ # @param [Symbol] short single character for short switch, eg. +:v+ => +-v+
16
+ # @param [Symbol] long longer switch to be used, eg. +:verbose+ => +--verbose+
17
+ # @param [String] desc the description for the switch
18
+ #
19
+ # @yield A block to run if the switch is triggered
20
+ #
21
+ def initialize(*args, &block)
22
+ @names = []
23
+ args.each do |i|
24
+ case i
25
+ when Symbol
26
+ @names << i.to_s
27
+ when String
28
+ @desc = i
29
+ end
30
+ end
31
+ @block = block
32
+ end
33
+
34
+ # Runs the block that was given
35
+ def run
36
+ @block.call
37
+ end
38
+
39
+ end
40
+ end
@@ -17,7 +17,7 @@ class Clive
17
17
  # Create a new Tokens instance. Pass either an array of tokens
18
18
  # or a plain array, they will be converted correctly.
19
19
  #
20
- # @param [Array]
20
+ # @param [::Array]
21
21
  # @return [Tokens]
22
22
  #
23
23
  def initialize(args=[])
@@ -32,7 +32,7 @@ class Clive
32
32
  # Turn +@tokens+ into an array, this ensures that shorts are split
33
33
  # as is expected
34
34
  #
35
- # @return [Array] array representation of tokens held
35
+ # @return [::Array] array representation of tokens held
36
36
  def array
37
37
  return [] unless self.tokens
38
38
  arr = []
@@ -53,7 +53,7 @@ class Clive
53
53
 
54
54
  # Creates an array of tokens based on +self+
55
55
  #
56
- # @return [Array] the tokens that are held
56
+ # @return [::Array] the tokens that are held
57
57
  def tokens
58
58
  t = []
59
59
  self.each do |i|
@@ -97,7 +97,7 @@ class Clive
97
97
 
98
98
  # Test whether an array is a token
99
99
  #
100
- # @param [Array]
100
+ # @param [::Array]
101
101
  # @return [Boolean]
102
102
  #
103
103
  # @example
@@ -9,15 +9,23 @@ c = Clive.new do
9
9
  options[:verbose] = true
10
10
  }
11
11
 
12
- flag(:t, :type, "Type to choose") {
13
- p "hi"
14
- }
12
+ flag(:t, :type, "TYPE", "Change type to TYPE") do |i|
13
+ options[:type] = i
14
+ end
15
15
 
16
16
  flag(:long_only, "A flag with only a long") {}
17
17
 
18
+ flag(:p, :print, "[TEXT]", "Print TEXT, defaults to 'hey'") do |i|
19
+ i ||= 'hey'
20
+ p i
21
+ end
22
+
23
+ flag(:z, :apple, "More applez") {}
24
+
18
25
  command(:add, "Add something") {
19
26
 
20
- p "adding stuff"
27
+ header "Usage: bin_test add [options]\n" +
28
+ "Use add to add things to add, which in turn can be added."
21
29
 
22
30
  switch(:f, :full, "Use full") {
23
31
  options[:full] = true
@@ -27,7 +35,15 @@ c = Clive.new do
27
35
  switch(:e, :empty, "Use empty") {
28
36
  options[:full] = false
29
37
  }
38
+
39
+ footer "If the header was a little confusing, don't worry the footer thought so too."
40
+
41
+ }
30
42
 
43
+ command(:remove, :delete, "Get rid of something") {
44
+ switch(:f, :force, "Force") {
45
+ p "deleting! now"
46
+ }
31
47
  }
32
48
 
33
49
  end
@@ -6,17 +6,23 @@ class TestBoolean < Test::Unit::TestCase
6
6
 
7
7
  setup do
8
8
  @c = Clive.new do
9
- boolean(:v, :verbose, "Run verbosely") {|i| $stdout.puts(i)}
9
+ bool(:v, :verbose, "Run verbosely") {|i| puts(i)}
10
10
  end
11
11
  end
12
12
 
13
13
  should "create two switches" do
14
- assert_equal 2, @c.booleans.length
14
+ assert_equal 2, @c.bools.length
15
+ end
16
+
17
+ should "raise error when no long name given" do
18
+ assert_raise Clive::MissingLongName do
19
+ Clive::Bool.new(:v, "Run verbosely") {}
20
+ end
15
21
  end
16
22
 
17
23
  context "the true switch" do
18
24
  should "have a short name" do
19
- assert_equal "v", @c.switches["verbose"].short
25
+ assert_contains @c.bools["verbose"].names, "v"
20
26
  end
21
27
 
22
28
  should "pass true to the block" do
@@ -25,13 +31,13 @@ class TestBoolean < Test::Unit::TestCase
25
31
  end
26
32
 
27
33
  should "create summary" do
28
- assert_equal "-v, --[no-]verbose Run verbosely", @c.switches["verbose"].summary(0, 0)
34
+ assert_equal "-v, --[no-]verbose Run verbosely", @c.bools["verbose"].summary(0, 0)
29
35
  end
30
36
  end
31
37
 
32
38
  context "the false switch" do
33
39
  should "not have short name" do
34
- assert_equal nil, @c.switches["no-verbose"].short
40
+ assert_does_not_contain @c.bools["no-verbose"].names, "v"
35
41
  end
36
42
 
37
43
  should "pass false to the block" do
@@ -40,9 +46,10 @@ class TestBoolean < Test::Unit::TestCase
40
46
  end
41
47
 
42
48
  should "not create summary" do
43
- assert_equal nil, @c.switches["no-verbose"].summary
49
+ assert_equal nil, @c.bools["no-verbose"].summary
44
50
  end
45
51
  end
46
52
 
47
53
  end
54
+
48
55
  end
@@ -30,8 +30,8 @@ class TestClive < Test::Unit::TestCase
30
30
  c = Clive.new do
31
31
  boolean(:verbose) {}
32
32
  end
33
- assert_equal 3, c.switches.length # plus help
34
- assert_instance_of Clive::Boolean, c.switches[0]
33
+ assert_equal 2, c.bools.length
34
+ assert_instance_of Clive::Bool, c.bools[0]
35
35
  end
36
36
 
37
37
  context "When parsing input" do
@@ -216,7 +216,40 @@ class TestClive < Test::Unit::TestCase
216
216
  r = {:v => true, :add => {:full => true, :init => {:base => true, :name => 'Works'}} }
217
217
  assert_equal r, opts
218
218
  end
219
+
220
+ should "parse the one in the readme" do
221
+ opts = {}
222
+ c = Clive.new do
223
+ bool(:v, :verbose, "Run verbosely") {|i| opts[:verbose] = i}
224
+
225
+ command(:add, "Add a new project") do
226
+ opts[:add] = {}
227
+
228
+ switch(:force, "Force overwrite") {opts[:add][:force] = true}
229
+ flag(:framework, "Add framework") do |i|
230
+ opts[:add][:framework] ||= []
231
+ opts[:add][:framework] << i
232
+ end
233
+
234
+ command(:init, "Initialize the project after creating") do
235
+ switch(:m, :minimum, "Use minimum settings") {opts[:add][:min] = true}
236
+ flag(:w, :width) {|i| opts[:add][:width] = i.to_i}
237
+ end
238
+
239
+ end
240
+
241
+ switch(:version, "Show version") do
242
+ puts "1.0.0"
243
+ exit
244
+ end
245
+ end
246
+ argv = %w(-v add --framework=blueprint init -m -w 200 ~/Desktop/new_thing ~/Desktop/another_thing)
247
+ args = c.parse(argv)
248
+
249
+ opts_r = {:add => {:min => true, :width => 200, :framework => ["blueprint"]}, :verbose => true}
250
+ assert_equal opts_r, opts
251
+ assert_equal ["~/Desktop/new_thing", "~/Desktop/another_thing"], args
252
+ end
219
253
 
220
254
  end
221
-
222
255
  end