clive 0.3.1 → 0.4.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.
data/.gitignore CHANGED
@@ -21,5 +21,5 @@ pkg
21
21
  ## PROJECT::SPECIFIC
22
22
  .yardoc
23
23
  doc
24
- ideas.rb
25
24
  research
25
+ cov
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.3.1
1
+ 0.4.0
@@ -5,11 +5,11 @@
5
5
 
6
6
  Gem::Specification.new do |s|
7
7
  s.name = %q{clive}
8
- s.version = "0.3.1"
8
+ s.version = "0.4.0"
9
9
 
10
10
  s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
11
  s.authors = ["Joshua Hawxwell"]
12
- s.date = %q{2010-08-21}
12
+ s.date = %q{2010-09-19}
13
13
  s.description = %q{Clive is a DSL for creating a command line interface. It is for people who, like me, love OptionParser's syntax and love GLI's commands.}
14
14
  s.email = %q{m@hawx.me}
15
15
  s.extra_rdoc_files = [
@@ -22,30 +22,75 @@ require 'clive/bool'
22
22
  # end
23
23
  # c.parse(ARGV)
24
24
  #
25
+ # @example Non-simple Example
26
+ #
27
+ # opts = {}
28
+ # c = Clive.new do
29
+ # bool(:v, :verbose, "Run verbosely") {|i| opts[:verbose] = i}
30
+ #
31
+ # command :add, "Add a new project" do
32
+ # opts[:add] = {}
33
+ #
34
+ # switch(:force, "Force overwrite") {opts[:add][:force] = true}
35
+ # flag :framework, "Add framework" do |i|
36
+ # opts[:add][:framework] ||= []
37
+ # opts[:add][:framework] << i
38
+ # end
39
+ #
40
+ # command :init, "Initialize the project after creating" do
41
+ # switch(:m, :minimum, "Use minimum settings") {opts[:add][:min] = true}
42
+ # flag(:w, :width) {|i| opts[:add][:width] = i.to_i}
43
+ # end
44
+ #
45
+ # end
46
+ #
47
+ # switch :version, "Show version" do
48
+ # puts "1.0.0"
49
+ # exit
50
+ # end
51
+ # end
52
+ # ARGV = c.parse(ARGV)
53
+ #
25
54
  class Clive
26
-
55
+
56
+ # This is the base command, the only way it differs from a normal command
57
+ # is that it has no name and it's block is executed immediately on creation.
58
+ #
59
+ # @return [Command] the base command
60
+ #
27
61
  attr_accessor :base
28
62
 
29
63
  def initialize(&block)
30
64
  @base = Command.new(true, &block)
31
65
  end
32
66
 
67
+ # Parse the base command
33
68
  def parse(argv)
34
69
  @base.run(argv)
35
70
  end
36
71
 
72
+ # @group Base Proxy Methods
73
+
74
+ # @see Command#switches
75
+ # @return [Array] switches in +base+
37
76
  def switches
38
77
  @base.switches
39
78
  end
40
79
 
80
+ # @see Command#commands
81
+ # @return [Array] commands in +base+
41
82
  def commands
42
83
  @base.commands
43
84
  end
44
85
 
86
+ # @see Command#flags
87
+ # @return [Array] flags in +base+
45
88
  def flags
46
89
  @base.flags
47
90
  end
48
91
 
92
+ # @see Command#bools
93
+ # @return [Array] bools in +base+
49
94
  def bools
50
95
  @base.bools
51
96
  end
@@ -1,13 +1,14 @@
1
1
  class Clive
2
2
 
3
- # A switch which can be triggered with either --no-verbose or --verbose
4
- # for example.
3
+ # A switch which can be triggered with either --no-[name] and --[name].
4
+ # The 'truthness' of this is then passed to the block.
5
5
  class Bool < Option
6
6
  attr_accessor :truth
7
7
 
8
8
  # Creates a new Bool switch instance. A boolean switch has a truth,
9
9
  # this determines what is passed to the block. They should be created
10
10
  # in pairs so one can be +--something+ the other +--no-something+.
11
+ # NOTE: this does not happen within this class!
11
12
  #
12
13
  # +short+ and/or +desc+ can be omitted when creating a Boolean, all
13
14
  # other arguments must be present.
@@ -19,6 +20,7 @@ class Clive
19
20
  # @param [String] desc description of use/purpose
20
21
  #
21
22
  # @yield [Boolean] A block to be run when the switch is triggered
23
+ # @raise [MissingLongName] raises when a long name is not given
22
24
  #
23
25
  def initialize(*args, truth, &block)
24
26
  @names = []
@@ -35,6 +37,7 @@ class Clive
35
37
  end
36
38
  end
37
39
 
40
+ # booleans require a long name to add --no- to
38
41
  unless @names.find_all {|i| i.length > 1}.length > 0
39
42
  raise MissingLongName, @names[0]
40
43
  end
@@ -48,6 +51,8 @@ class Clive
48
51
  @block.call(@truth)
49
52
  end
50
53
 
54
+ # @param [Integer] width the total ideal width of help
55
+ # @param [Integer] prepend the number of spaces to add before each line
51
56
  # @return [String] summary for help or nil if +@truth = false+
52
57
  def summary(width=30, prepend=5)
53
58
  return nil unless @truth
@@ -98,8 +98,15 @@ class Clive
98
98
  when :switch
99
99
  v.run
100
100
  when :flag
101
- raise MissingArgument.new(v.sort_name) unless i[2] || v.optional
102
- v.run(i[2])
101
+ args = i[2..-1]
102
+ opt_args = v.arg_num(true)
103
+ nec_args = v.arg_num(false)
104
+ # check for missing args
105
+ if args.size < nec_args
106
+ raise MissingArgument.new(v.sort_name)
107
+ end
108
+
109
+ v.run(args)
103
110
  when :argument
104
111
  r << v
105
112
  end
@@ -170,8 +177,8 @@ class Clive
170
177
  else
171
178
  if k == :word
172
179
  # add to last flag?
173
- if r.last && r.last[0] == :flag && r.last[2].nil?
174
- r.last[2] = v
180
+ if r.last && r.last[0] == :flag && r.last.size - 2 < r.last[1].args.size
181
+ r.last.push(v)
175
182
  else
176
183
  r << [:argument, v]
177
184
  end
@@ -261,6 +268,7 @@ class Clive
261
268
  if @options.length > 0
262
269
  summary << "\n Options:\n"
263
270
  @options.sort.each do |i|
271
+ next if i.names.include?("help")
264
272
  summary << i.summary(width, prepend) << "\n" if i.summary
265
273
  end
266
274
  end
@@ -1,6 +1,6 @@
1
1
  class Clive
2
2
 
3
- # general problem
3
+ # General problem
4
4
  class CliveError < StandardError
5
5
  attr_accessor :args
6
6
 
@@ -26,21 +26,27 @@ class Clive
26
26
 
27
27
  end
28
28
 
29
- # general problem with input
29
+ # General problem with input
30
30
  class ParseError < CliveError
31
31
  def reason; "parse error"; end
32
32
  end
33
33
 
34
- # a flag has a missing argument
34
+ # A flag has a missing argument
35
35
  class MissingArgument < ParseError
36
36
  def reason; "missing argument"; end
37
37
  end
38
38
 
39
- # a option that wasn't defined has been found
39
+ # A flag has a wrong argument
40
+ class InvalidArgument < ParseError
41
+ def reason; "invalid argument"; end
42
+ end
43
+
44
+ # An option that wasn't defined has been found
40
45
  class InvalidOption < ParseError
41
46
  def reason; "invalid option"; end
42
47
  end
43
48
 
49
+ # Long name is missing for bool
44
50
  class MissingLongName < CliveError
45
51
  def reason; "missing long name"; end
46
52
  end
@@ -2,7 +2,7 @@ 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.
5
+ # If passed a Symbol or String will get the option or command with that name.
6
6
  # Otherwise does what you expect of an Array (see ::Array#[])
7
7
  #
8
8
  # @param [Symbol, String, Integer, Range] val name or index of item to return
@@ -16,5 +16,38 @@ class Clive
16
16
  end
17
17
  end
18
18
 
19
+ # Attempts to fill +self+ with values from +input+, giving priority to
20
+ # true, then false. If insufficient input to fill all false will use nil.
21
+ #
22
+ # @param [Array] input array of values to fill +self+ with
23
+ # @return [Array] filled array
24
+ #
25
+ # @example
26
+ #
27
+ # [true, false, false, true].optimise_fill(["a", "b", "c"])
28
+ # #=> ["a", "b", nil, "c"]
29
+ #
30
+ #
31
+ def optimise_fill(input)
32
+ match = self
33
+ diff = input.size - match.reject{|i| i == false}.size
34
+
35
+ result = []
36
+ match.each_index do |i|
37
+ curr_item = match[i]
38
+ if curr_item == true
39
+ result << input.shift
40
+ else
41
+ if diff > 0
42
+ result << input.shift
43
+ diff -= 1
44
+ else
45
+ result << nil
46
+ end
47
+ end
48
+ end
49
+ result
50
+ end
51
+
19
52
  end
20
53
  end
@@ -1,44 +1,75 @@
1
1
  class Clive
2
2
 
3
- # A switch that takes an argument, with or without an equals
3
+ # A switch that takes one or more arguments.
4
4
  # eg. wget --tries=10
5
5
  # wget -t 10
6
6
  #
7
7
  class Flag < Option
8
- attr_accessor :arg_name, :optional
8
+ attr_accessor :args
9
9
 
10
- # Creates a new Flag instance.
10
+ # Creates a new Flag instance. A flag is a switch that can take one or more
11
+ # arguments.
11
12
  #
12
- # +short+ _or_ +long+ can be omitted but not both.
13
+ # +short+ *or* +long+ can be omitted but not both.
14
+ # +args+ can also be omitted (is "ARG" by default)
13
15
  #
14
- # @overload flag(short, long, desc, &block)
15
- # Creates a new flag
16
+ # @overload flag(short, long, args, desc, &block)
16
17
  # @param [Symbol] short single character for short flag, eg. +:t+ => +-t 10+
17
18
  # @param [Symbol] long longer switch to be used, eg. +:tries+ => +--tries=10+
19
+ # @param [String, Array] args
20
+ # either a string showing the arguments to be given, eg.
21
+ #
22
+ # "FROM" # single arg, or
23
+ # "FROM TO" # two args, or
24
+ # "[VIA]" # optional arg surrounded by square brackets
25
+ #
26
+ # or an array of acceptable inputs, eg.
27
+ #
28
+ # ["large", "medium", "small"] # will only accept these args
29
+ #
18
30
  # @param [String] desc the description for the flag
19
31
  #
20
32
  # @yield [String] A block to be run if switch is triggered
21
33
  #
22
34
  def initialize(*args, &block)
23
- @names = []
24
- @optional = false
25
- @arg_name = "ARG"
35
+ @names = []
36
+ @args = []
26
37
 
38
+ # Need to be able to make each arg_name optional or not
39
+ # and allow for type in future
27
40
  args.each do |i|
28
41
  if i.is_a? String
29
- if i =~ /^[\[\]A-Z0-9]+$/
30
- @arg_name = i
42
+ if i =~ /\A(([A-Z0-9\[\]]+)\s?)+\Z/
43
+ i.split(' ').each do |arg|
44
+ type = String
45
+ if arg[0] == "["
46
+ @args << {
47
+ :name => arg[1..arg.length-2],
48
+ :optional => true,
49
+ :type => type
50
+ }
51
+ else
52
+ @args << {
53
+ :name => arg,
54
+ :optional => false,
55
+ :type => type
56
+ }
57
+ end
58
+ end
31
59
  else
32
60
  @desc = i
33
61
  end
34
62
  else
35
- @names << i.to_s
63
+ if i.class == Symbol
64
+ @names << i.to_s
65
+ else
66
+ @args = i
67
+ end
36
68
  end
37
69
  end
38
70
 
39
- if @arg_name[0] == "["
40
- @arg_name = @arg_name[1..@arg_name.length-2]
41
- @optional = true
71
+ if @args == []
72
+ @args = [{:name => "ARG", :optional => false, :type => String}]
42
73
  end
43
74
 
44
75
  @block = block
@@ -46,20 +77,45 @@ class Clive
46
77
 
47
78
  # Runs the block that was given with an argument
48
79
  #
49
- # @param [String] arg argument to pass to the block
50
- def run(arg)
51
- @block.call(arg)
80
+ # @param [Array] args arguments to pass to the block
81
+ # @raise [InvalidArgument] only if +args+ is an array of acceptable inputs
82
+ # and a match is not found.
83
+ def run(args)
84
+ if @args[0].is_a? Hash
85
+ args = Clive::Array.new(@args.collect {|i| !i[:optional]}).optimise_fill(args)
86
+ else # list
87
+ unless @args.include? args[0]
88
+ raise InvalidArgument.new(args)
89
+ end
90
+ end
91
+ @block.call(*args)
92
+ end
93
+
94
+ # @param [Boolean] optional whether to include optional arguments
95
+ # @return [Integer] number of arguments this takes
96
+ def arg_num(optional)
97
+ if @args[0].is_a? Hash
98
+ @args.find_all {|i| i[:optional] == optional }.size
99
+ else
100
+ 1
101
+ end
52
102
  end
53
103
 
54
104
  # @return [String] summary for help
55
105
  def summary(width=30, prepend=5)
56
106
  n = names_to_strings.join(', ')
57
- if @optional
58
- n << " [#{@arg_name}]"
107
+ a = nil
108
+ if @args[0].is_a? Hash
109
+ a = @args.map {|i| i[:name]}.join(' ')
110
+ if @optional
111
+ n << " [#{a}]"
112
+ else
113
+ n << " #{a}"
114
+ end
59
115
  else
60
- n << " #{@arg_name}"
116
+ n << " {" << @args.join(', ') << "}"
61
117
  end
62
-
118
+
63
119
  spaces = width-n.length
64
120
  spaces = 1 if spaces < 1
65
121
  s = spaces(spaces)
@@ -1,16 +1,17 @@
1
1
  class Clive
2
2
 
3
+ # @abstract Subclass and override {#initialize} and {#run} to create a new Option class.
3
4
  class Option
4
5
  attr_accessor :names, :desc, :block
5
6
 
6
- def initialize(desc, *names, &block)
7
- @names = names
8
- @desc = desc
9
- @block = block
7
+ def initialize(*args, &block)
8
+ # assign name and description
9
+ # @block = block
10
10
  end
11
11
 
12
12
  def run
13
- @block.call
13
+ # call the block!
14
+ # @block.call
14
15
  end
15
16
 
16
17
  def summary(width=30, prepend=5)
@@ -22,7 +23,8 @@ class Clive
22
23
  "#{p}#{n}#{s}#{@desc}"
23
24
  end
24
25
 
25
- # Convert the names to strings, depending on length
26
+ # Convert the names to strings, if name is single character appends
27
+ # +-+, else appends +--+.
26
28
  #
27
29
  # @param [Boolean] bool whether to add [no-] to long
28
30
  #
@@ -69,6 +71,7 @@ class Clive
69
71
  r
70
72
  end
71
73
 
74
+ # Compare options based on Option#sort_name
72
75
  def <=>(other)
73
76
  self.sort_name <=> other.sort_name
74
77
  end
@@ -11,13 +11,17 @@ class Clive
11
11
  # ["Value", "--verbose", "-r"]
12
12
  #
13
13
  class Tokens < Array
14
-
14
+
15
15
  TOKEN_KEYS = [:word, :short, :long]
16
16
 
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] args
21
+ # pass either
22
+ # ["command", "--flag"]
23
+ # # or
24
+ # [[:word, "command"], [:long, "flag"]]
21
25
  # @return [Tokens]
22
26
  #
23
27
  def initialize(args=[])
@@ -51,7 +55,10 @@ class Clive
51
55
  arr
52
56
  end
53
57
 
54
- # Creates an array of tokens based on +self+
58
+ # Creates an array of tokens based on +self+.
59
+ # Strings beginning with a -, eg. -n become [:short, "n"].
60
+ # Strings beginning with --, eg. --verbose become [:long, "verbose"].
61
+ # Strings which begin with neither become [:word, "value"].
55
62
  #
56
63
  # @return [::Array] the tokens that are held
57
64
  def tokens
@@ -78,10 +85,12 @@ class Clive
78
85
  t
79
86
  end
80
87
 
88
+ # @see #tokens
81
89
  def self.to_tokens(arr)
82
90
  Tokens.new(arr).tokens
83
91
  end
84
92
 
93
+ # @see #array
85
94
  def self.to_array(tokens)
86
95
  Tokens.new(tokens).array
87
96
  end
@@ -48,6 +48,7 @@ c = Clive.new do
48
48
 
49
49
  end
50
50
 
51
+ p ARGV
51
52
  p c.parse(ARGV)
52
53
  p options
53
54
 
@@ -1,10 +1,13 @@
1
+ require 'duvet'
2
+ Duvet.start :filter => 'clive/lib'
3
+
1
4
  require 'test/unit'
2
5
  require 'shoulda'
3
6
  require 'rr'
4
7
 
5
8
  $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
6
9
  $LOAD_PATH.unshift(File.dirname(__FILE__))
7
- require 'clive'
10
+ require File.join(File.dirname(__FILE__), '..', 'lib', 'clive')
8
11
 
9
12
  class Test::Unit::TestCase
10
13
  include RR::Adapters::TestUnit
@@ -155,6 +155,12 @@ class TestClive < Test::Unit::TestCase
155
155
  assert_equal ["argument"], args
156
156
  end
157
157
 
158
+ should "recognise a quoted argument" do
159
+ c = Clive.new {}
160
+ args = c.parse ['a quoted argument']
161
+ assert_equal ["a quoted argument"], args
162
+ end
163
+
158
164
  should "recognise multiple arguments" do
159
165
  c = Clive.new do
160
166
  bool(:v, :verbose, "Run verbosely") {}
@@ -6,7 +6,7 @@ class TestFlag < Test::Unit::TestCase
6
6
 
7
7
  setup do
8
8
  @c = Clive.new do
9
- flag(:t, :type, "TEXT", "Change type to TYPE") {|i| $stdout.puts(i)}
9
+ flag(:t, :type, "TEXT", "Change type to TYPE") {|i| puts(i)}
10
10
  end
11
11
  end
12
12
 
@@ -15,14 +15,14 @@ class TestFlag < Test::Unit::TestCase
15
15
  @c.parse(["--type", "text"])
16
16
  end
17
17
 
18
- should "have an arg name" do
19
- assert_equal "TEXT", @c.flags["t"].arg_name
18
+ should "have a arguments" do
19
+ r = {:name => "TEXT", :optional => false, :type => String}
20
+ assert_equal r, @c.flags["t"].args[0]
20
21
  end
21
22
 
22
23
  should "not be optional" do
23
- assert_equal false, @c.flags["t"].optional
24
+ assert_equal false, @c.flags["t"].args[0][:optional]
24
25
  end
25
-
26
26
  end
27
27
 
28
28
  context "A new flag with default" do
@@ -37,11 +37,12 @@ class TestFlag < Test::Unit::TestCase
37
37
  end
38
38
 
39
39
  should "have an arg name" do
40
- assert_equal "TEXT", @c.flags["t"].arg_name
40
+ r = {:name => "TEXT", :optional => true, :type => String}
41
+ assert_equal r, @c.flags["t"].args[0]
41
42
  end
42
43
 
43
44
  should "be optional" do
44
- assert_equal true, @c.flags["t"].optional
45
+ assert_equal true, @c.flags["t"].args[0][:optional]
45
46
  end
46
47
 
47
48
  should "pass an argument to block" do
@@ -55,4 +56,105 @@ class TestFlag < Test::Unit::TestCase
55
56
  end
56
57
  end
57
58
 
59
+ context "A new flag with multiple arguments" do
60
+
61
+ setup do
62
+ @c = Clive.new do
63
+ flag(:s, :send, "FROM TO", "Send the message from FROM to TO") do |from, to|
64
+ puts from
65
+ puts to
66
+ end
67
+ end
68
+ end
69
+
70
+ should "pass two arguments to the block" do
71
+ mock($stdout).puts("John")
72
+ mock($stdout).puts("Dave")
73
+ @c.parse(["--send", "John", "Dave"])
74
+ end
75
+
76
+ should "have a useful summary" do
77
+ s = "-s, --send FROM TO Send the message from FROM to TO"
78
+ assert_equal s, @c.flags['s'].summary(0, 0)
79
+ end
80
+
81
+ should "work for more than two arguments" do
82
+ mock($stdout).puts("from: Josh, to: John, via: Hong Kong, because: It's far away")
83
+ c = Clive.new do
84
+ flag(:s, :send, "FROM TO VIA BECAUSE", "Send the message...etc") do |f, t, v, b|
85
+ puts "from: #{f}, to: #{t}, via: #{v}, because: #{b}"
86
+ end
87
+ end
88
+ c.parse ['-s', 'Josh', 'John', "Hong Kong", "It's far away"]
89
+ end
90
+
91
+ context "and optional arguments" do
92
+ setup do
93
+ @c = Clive.new do
94
+ flag(:s, :send, "FROM [VIA] TO [BECAUSE]", "Send the message...etc") do |f, v, t, b|
95
+ s = "from: #{f}"
96
+ s << ", via: #{v}" if v
97
+ s << ", to: #{t}"
98
+ s << ", because: #{b}" if b
99
+ puts s
100
+ end
101
+ end
102
+ end
103
+
104
+ should "correctly parse argument string" do
105
+ r = [
106
+ {:name => "FROM", :optional => false, :type => String},
107
+ {:name => "VIA", :optional => true, :type => String},
108
+ {:name => "TO", :optional => false, :type => String},
109
+ {:name => "BECAUSE", :optional => true, :type => String}
110
+ ]
111
+ assert_equal r, @c.flags[:s].args
112
+ end
113
+
114
+ should "give all arguments when found in input" do
115
+ mock($stdout).puts("from: Josh, via: Hong Kong, to: John, because: It's far away")
116
+ @c.parse ["-s", "Josh", "Hong Kong", "John", "It's far away"]
117
+ end
118
+
119
+ should "try to give only needed arguments if some missing" do
120
+ mock($stdout).puts("from: Josh, to: John")
121
+ @c.parse ["-s", "Josh", "John"]
122
+ end
123
+
124
+ should "fill optional arguments starting from left" do
125
+ mock($stdout).puts("from: Josh, via: Hong Kong, to: John")
126
+ @c.parse ["-s", "Josh", "Hong Kong", "John"]
127
+ end
128
+
129
+ end
130
+ end
131
+
132
+ context "A new flag with a list of acceptable arguments" do
133
+
134
+ setup do
135
+ @c = Clive.new do
136
+ flag(:t, :type, ["large", "medium", "small"], "Choose the type to use") {}
137
+ end
138
+ end
139
+
140
+ should "raise error when correct argument not found" do
141
+ assert_raise Clive::InvalidArgument do
142
+ @c.parse(["--type", "apple"])
143
+ end
144
+ end
145
+
146
+ should "not raise error when correct argument given" do
147
+ @c.parse(["--type", "large"])
148
+ end
149
+
150
+ should "make list arguments" do
151
+ r = ["large", "medium", "small"]
152
+ assert_equal r, @c.flags['t'].args
153
+ end
154
+
155
+ should "have a useful summary" do
156
+ s = "-t, --type {large, medium, small} Choose the type to use"
157
+ assert_equal s, @c.flags['t'].summary(0, 0)
158
+ end
159
+ end
58
160
  end
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: clive
3
3
  version: !ruby/object:Gem::Version
4
- hash: 17
4
+ hash: 15
5
5
  prerelease: false
6
6
  segments:
7
7
  - 0
8
- - 3
9
- - 1
10
- version: 0.3.1
8
+ - 4
9
+ - 0
10
+ version: 0.4.0
11
11
  platform: ruby
12
12
  authors:
13
13
  - Joshua Hawxwell
@@ -15,7 +15,7 @@ autorequire:
15
15
  bindir: bin
16
16
  cert_chain: []
17
17
 
18
- date: 2010-08-21 00:00:00 +01:00
18
+ date: 2010-09-19 00:00:00 +01:00
19
19
  default_executable:
20
20
  dependencies:
21
21
  - !ruby/object:Gem::Dependency