climate 0.2.0 → 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.
- 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
|