climate 0.2.0 → 0.3.0
Sign up to get free protection for your applications and to get access to all the features.
- data/lib/climate.rb +0 -2
- data/lib/climate/command.rb +35 -5
- data/lib/climate/help.rb +2 -2
- data/lib/climate/parser.rb +94 -20
- data/lib/climate/version.rb +1 -1
- metadata +4 -6
- data/lib/climate/argument.rb +0 -28
- data/lib/climate/option.rb +0 -54
data/lib/climate.rb
CHANGED
data/lib/climate/command.rb
CHANGED
@@ -14,11 +14,11 @@ module Climate
|
|
14
14
|
|
15
15
|
# Create an instance of this command class and run it against the given
|
16
16
|
# arguments
|
17
|
-
# @param [Array<String>]
|
17
|
+
# @param [Array<String>] argv A list of arguments, ARGV style
|
18
18
|
# @param [Hash] options see {#initialize}
|
19
|
-
def run(
|
19
|
+
def run(argv, options={})
|
20
20
|
begin
|
21
|
-
instance = new(
|
21
|
+
instance = new(argv, options)
|
22
22
|
rescue Trollop::HelpNeeded
|
23
23
|
raise HelpNeeded.new(self)
|
24
24
|
end
|
@@ -47,6 +47,13 @@ module Climate
|
|
47
47
|
@name
|
48
48
|
end
|
49
49
|
|
50
|
+
# because we've extended Class.name, we expose the original method
|
51
|
+
# under another name
|
52
|
+
# FIXME: surely there is a saner way of doing this?
|
53
|
+
def class_name
|
54
|
+
Class.method(:name).unbind.bind(self).call
|
55
|
+
end
|
56
|
+
|
50
57
|
# Register this class as being a subcommand of another {Command} class
|
51
58
|
# @param [Command] parent_class The parent we hang off of
|
52
59
|
def subcommand_of(parent_class)
|
@@ -64,6 +71,15 @@ module Climate
|
|
64
71
|
end
|
65
72
|
end
|
66
73
|
|
74
|
+
# Call this during class definition time if you don't want any of the
|
75
|
+
# usual command line parsing to happen
|
76
|
+
def disable_parsing
|
77
|
+
@parsing_disabled = true
|
78
|
+
end
|
79
|
+
|
80
|
+
# Returns true if parsing is disabled
|
81
|
+
attr_accessor :parsing_disabled
|
82
|
+
|
67
83
|
# Set the parent of this command
|
68
84
|
attr_accessor :parent
|
69
85
|
|
@@ -116,16 +132,24 @@ module Climate
|
|
116
132
|
# @option options [IO] :stdout stream to use as stdout, defaulting to `$stdout`
|
117
133
|
# @option options [IO] :stderr stream to use as stderr, defaulting to `$stderr`
|
118
134
|
# @option options [IO] :stdin stream to use as stdin, defaulting to `$stdin`
|
119
|
-
def initialize(
|
135
|
+
def initialize(argv, options={})
|
136
|
+
@argv = argv.clone
|
120
137
|
@parent = options[:parent]
|
121
138
|
|
122
139
|
@stdout = options[:stdout] || $stdout
|
123
140
|
@stderr = options[:stderr] || $stderr
|
124
141
|
@stdin = options[:stdin] || $stdin
|
125
142
|
|
126
|
-
|
143
|
+
if ! self.class.parsing_disabled
|
144
|
+
@arguments, @options, @leftovers = self.class.parse(argv)
|
145
|
+
end
|
127
146
|
end
|
128
147
|
|
148
|
+
# @return [Array]
|
149
|
+
# The original list of unparsed argv style arguments that were given to
|
150
|
+
# the command
|
151
|
+
attr_accessor :argv
|
152
|
+
|
129
153
|
# @return [Hash]
|
130
154
|
# Options that were parsed from the command line
|
131
155
|
attr_accessor :options
|
@@ -156,5 +180,11 @@ module Climate
|
|
156
180
|
parent.ancestor(ancestor_class)
|
157
181
|
end
|
158
182
|
end
|
183
|
+
|
184
|
+
# Run the command, must be implemented by all commands that are not parent
|
185
|
+
# commands (leaf commands)
|
186
|
+
def run
|
187
|
+
raise NotImplementedError, "Leaf commands must implement a run method"
|
188
|
+
end
|
159
189
|
end
|
160
190
|
end
|
data/lib/climate/help.rb
CHANGED
@@ -101,7 +101,7 @@ module Climate
|
|
101
101
|
end
|
102
102
|
|
103
103
|
def puts(string='')
|
104
|
-
string.split("\n").each do |line|
|
104
|
+
wrap(string).split("\n").each do |line|
|
105
105
|
@output.puts((' ' * spaces) + line)
|
106
106
|
end
|
107
107
|
end
|
@@ -129,7 +129,7 @@ module Climate
|
|
129
129
|
|
130
130
|
string.split("\n\n").map { |para|
|
131
131
|
|
132
|
-
words = para.split(/[\n ]
|
132
|
+
words = para.split(/[\n ]+/)
|
133
133
|
words[1..-1].inject([words.first]) { |m, v|
|
134
134
|
new_last_line = m.last + " " + v
|
135
135
|
|
data/lib/climate/parser.rb
CHANGED
@@ -1,9 +1,97 @@
|
|
1
1
|
module Climate
|
2
2
|
|
3
|
+
# Keeps the description munging code in one place
|
4
|
+
module Described
|
5
|
+
attr_reader :name
|
6
|
+
|
7
|
+
def initialize(name, description, *rest)
|
8
|
+
@name = name
|
9
|
+
@description = description
|
10
|
+
end
|
11
|
+
|
12
|
+
def description
|
13
|
+
(@description || '').gsub(/\{default\}/, default.to_s)
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
# Wraps the properties supplied to Trollop::Parser#opt for some OO over
|
18
|
+
# engineered sweetness
|
19
|
+
class Option
|
20
|
+
include Described
|
21
|
+
attr_reader :options
|
22
|
+
|
23
|
+
def initialize(name, description, options={})
|
24
|
+
@options = options
|
25
|
+
super
|
26
|
+
end
|
27
|
+
|
28
|
+
def type ; spec[:type] ; end
|
29
|
+
def long ; spec[:long] ; end
|
30
|
+
def short ; spec[:short] ; end
|
31
|
+
def default ; spec[:default] ; end
|
32
|
+
|
33
|
+
def optional? ; spec.has_key?(:default) ; end
|
34
|
+
def required? ; ! optional? ; end
|
35
|
+
|
36
|
+
def spec ; @specs ||= parser.specs[@name] ; end
|
37
|
+
|
38
|
+
def parser
|
39
|
+
@parser ||= Trollop::Parser.new.tap {|p| add_to(p) }
|
40
|
+
end
|
41
|
+
|
42
|
+
def long_usage
|
43
|
+
type == :flag ? "--#{long}" : "--#{long}=<#{type}>"
|
44
|
+
end
|
45
|
+
|
46
|
+
def short_usage
|
47
|
+
short && (type == :flag ? "-#{short}" : "-#{short}<#{type}>")
|
48
|
+
end
|
49
|
+
|
50
|
+
def usage(options={})
|
51
|
+
help = short_usage || long_usage
|
52
|
+
|
53
|
+
if options[:with_long] && (long_usage != help)
|
54
|
+
help = [help, long_usage].compact.join(options.fetch(:separator, '|'))
|
55
|
+
end
|
56
|
+
|
57
|
+
if optional? && !options.fetch(:hide_optional, false)
|
58
|
+
"[#{help}]"
|
59
|
+
else
|
60
|
+
help
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
def add_to(parser)
|
65
|
+
parser.opt(@name, @description, @options)
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
# argument definition is stored in these
|
70
|
+
class Argument
|
71
|
+
include Described
|
72
|
+
attr_reader :default
|
73
|
+
|
74
|
+
def initialize(name, description, options={})
|
75
|
+
super
|
76
|
+
@required = options.fetch(:required, ! options.has_key?(:default))
|
77
|
+
@multi = options.fetch(:multi, false)
|
78
|
+
@default = options.fetch(:default, nil)
|
79
|
+
end
|
80
|
+
|
81
|
+
def required? ; @required ; end
|
82
|
+
def optional? ; ! required? ; end
|
83
|
+
def multi? ; @multi ; end
|
84
|
+
|
85
|
+
def usage
|
86
|
+
string = "<#{name}>"
|
87
|
+
string += '...' if multi?
|
88
|
+
optional?? "[#{string}]" : string
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
3
92
|
module ParsingMethods
|
4
93
|
|
5
94
|
def arg(*args)
|
6
|
-
|
7
95
|
arg = Argument.new(*args)
|
8
96
|
|
9
97
|
raise DefinitionError, "can not define more arguments after a multi " +
|
@@ -24,27 +112,13 @@ module Climate
|
|
24
112
|
end
|
25
113
|
|
26
114
|
def trollop_parser
|
27
|
-
|
28
|
-
|
29
|
-
parser.stop_on @stop_on
|
115
|
+
Trollop::Parser.new.tap do |parser|
|
116
|
+
parser.stop_on @stop_on
|
30
117
|
|
31
|
-
|
32
|
-
|
33
|
-
max_length = cli_arguments.map { |h| h.name.to_s.length }.max
|
34
|
-
cli_arguments.each do |argument|
|
35
|
-
parser.banner(" " + argument.name.to_s.rjust(max_length) + " - #{argument.description}")
|
118
|
+
cli_options.each do |option|
|
119
|
+
option.add_to(parser)
|
36
120
|
end
|
37
121
|
end
|
38
|
-
|
39
|
-
parser.banner ""
|
40
|
-
cli_options.each do |option|
|
41
|
-
option.add_to(parser)
|
42
|
-
end
|
43
|
-
parser
|
44
|
-
end
|
45
|
-
|
46
|
-
def help_banner(out=$stdout)
|
47
|
-
trollop_parser.educate(out)
|
48
122
|
end
|
49
123
|
|
50
124
|
def parse_arguments(args, command=self)
|
@@ -69,7 +143,7 @@ module Climate
|
|
69
143
|
end
|
70
144
|
|
71
145
|
arg_list.zip(args).map do |argument, arg_value|
|
72
|
-
|
146
|
+
arg_value ||= argument.default
|
73
147
|
if argument.required? && arg_value.nil?
|
74
148
|
raise MissingArgumentError.new(argument.name, command)
|
75
149
|
end
|
data/lib/climate/version.rb
CHANGED
metadata
CHANGED
@@ -1,13 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: climate
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
hash:
|
4
|
+
hash: 19
|
5
5
|
prerelease:
|
6
6
|
segments:
|
7
7
|
- 0
|
8
|
-
-
|
8
|
+
- 3
|
9
9
|
- 0
|
10
|
-
version: 0.
|
10
|
+
version: 0.3.0
|
11
11
|
platform: ruby
|
12
12
|
authors:
|
13
13
|
- Nick Griffiths
|
@@ -15,7 +15,7 @@ autorequire:
|
|
15
15
|
bindir: bin
|
16
16
|
cert_chain: []
|
17
17
|
|
18
|
-
date: 2012-
|
18
|
+
date: 2012-08-08 00:00:00 Z
|
19
19
|
dependencies:
|
20
20
|
- !ruby/object:Gem::Dependency
|
21
21
|
name: trollop
|
@@ -85,9 +85,7 @@ extra_rdoc_files: []
|
|
85
85
|
files:
|
86
86
|
- lib/climate/version.rb
|
87
87
|
- lib/climate/help/man.rb
|
88
|
-
- lib/climate/argument.rb
|
89
88
|
- lib/climate/command.rb
|
90
|
-
- lib/climate/option.rb
|
91
89
|
- lib/climate/parser.rb
|
92
90
|
- lib/climate/errors.rb
|
93
91
|
- lib/climate/script.rb
|
data/lib/climate/argument.rb
DELETED
@@ -1,28 +0,0 @@
|
|
1
|
-
module Climate
|
2
|
-
class Argument
|
3
|
-
|
4
|
-
attr_reader :name
|
5
|
-
attr_reader :description
|
6
|
-
|
7
|
-
def initialize(name, description, options={})
|
8
|
-
@name = name
|
9
|
-
@description = description
|
10
|
-
@required = options.fetch(:required, true)
|
11
|
-
@multi = options.fetch(:multi, false)
|
12
|
-
end
|
13
|
-
|
14
|
-
def required? ; @required ; end
|
15
|
-
def optional? ; ! required? ; end
|
16
|
-
def multi? ; @multi ; end
|
17
|
-
|
18
|
-
def usage
|
19
|
-
string = "<#{name}>"
|
20
|
-
string += '...' if multi?
|
21
|
-
optional?? "[#{string}]" : string
|
22
|
-
end
|
23
|
-
|
24
|
-
def formatted
|
25
|
-
required?? name.to_s.upcase : "[#{name.to_s.upcase}]"
|
26
|
-
end
|
27
|
-
end
|
28
|
-
end
|
data/lib/climate/option.rb
DELETED
@@ -1,54 +0,0 @@
|
|
1
|
-
module Climate
|
2
|
-
class Option
|
3
|
-
|
4
|
-
attr_reader :name
|
5
|
-
attr_reader :description
|
6
|
-
attr_reader :options
|
7
|
-
|
8
|
-
def initialize(name, description, options={})
|
9
|
-
@name = name
|
10
|
-
@description = description
|
11
|
-
@options = options
|
12
|
-
end
|
13
|
-
|
14
|
-
def type ; spec[:type] ; end
|
15
|
-
def long ; spec[:long] ; end
|
16
|
-
def short ; spec[:short] ; end
|
17
|
-
def default ; spec[:default] ; end
|
18
|
-
|
19
|
-
def optional? ; spec.has_key?(:default) ; end
|
20
|
-
def required? ; ! optional? ; end
|
21
|
-
|
22
|
-
def spec ; @specs ||= parser.specs[@name] ; end
|
23
|
-
|
24
|
-
def parser
|
25
|
-
@parser ||= Trollop::Parser.new.tap {|p| add_to(p) }
|
26
|
-
end
|
27
|
-
|
28
|
-
def long_usage
|
29
|
-
type == :flag ? "--#{long}" : "--#{long}=<#{type}>"
|
30
|
-
end
|
31
|
-
|
32
|
-
def short_usage
|
33
|
-
short && (type == :flag ? "-#{short}" : "-#{short}<#{type}>")
|
34
|
-
end
|
35
|
-
|
36
|
-
def usage(options={})
|
37
|
-
help = short_usage || long_usage
|
38
|
-
|
39
|
-
if options[:with_long] && (long_usage != help)
|
40
|
-
help = [help, long_usage].compact.join(options.fetch(:separator, '|'))
|
41
|
-
end
|
42
|
-
|
43
|
-
if optional? && !options.fetch(:hide_optional, false)
|
44
|
-
"[#{help}]"
|
45
|
-
else
|
46
|
-
help
|
47
|
-
end
|
48
|
-
end
|
49
|
-
|
50
|
-
def add_to(parser)
|
51
|
-
parser.opt(@name, @description, @options)
|
52
|
-
end
|
53
|
-
end
|
54
|
-
end
|