climate 0.6.0 → 0.7.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/README.md +65 -0
- data/lib/climate/command.rb +72 -18
- data/lib/climate/version.rb +1 -1
- metadata +76 -86
data/README.md
CHANGED
@@ -62,3 +62,68 @@ There is a working example, `example.rb` that you can test out with
|
|
62
62
|
or
|
63
63
|
|
64
64
|
ruby -rrubygems -rclimate example.rb --help
|
65
|
+
|
66
|
+
# rack-middleware like filtering
|
67
|
+
|
68
|
+
Quite often in commands you might want some shared logic to do with setting up
|
69
|
+
or tearing down an environment for your child commands. For instance, you might
|
70
|
+
want to setup an application environment, start a database transaction, or check
|
71
|
+
some arguments are semantically correct in a way that is common based on a parent
|
72
|
+
command, i.e:
|
73
|
+
|
74
|
+
class Parent < Climate::Command('parent')
|
75
|
+
def run(chain)
|
76
|
+
setup_logger
|
77
|
+
begin
|
78
|
+
chain.run
|
79
|
+
rescue => e
|
80
|
+
logger.error("oh noes!")
|
81
|
+
raise
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
class Child < Climate::Command('child')
|
87
|
+
subcommand_of Parent
|
88
|
+
|
89
|
+
def run
|
90
|
+
if all_ok?
|
91
|
+
do_the_thing
|
92
|
+
else
|
93
|
+
raise 'it went badly'
|
94
|
+
end
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
The `run` method on non-leaf commands can also omit the chain argument, in which
|
99
|
+
case you are not responsible for calling the next link in the chain, but you can
|
100
|
+
still do some setup.
|
101
|
+
|
102
|
+
# Accessing your parent command at execution time
|
103
|
+
|
104
|
+
In the case where you have some parent command that gets all the top level
|
105
|
+
options for your application, (i.e. config location, log mode), you may need to
|
106
|
+
access this from your child commands. You can do this with the `ancestor`
|
107
|
+
instance method, or by proxying the method to your child class with
|
108
|
+
`expose_ancestor_method`. For example:
|
109
|
+
|
110
|
+
class Parent < Climate::Command('parent')
|
111
|
+
def useful_stuff ; end
|
112
|
+
def handy_service ; end
|
113
|
+
end
|
114
|
+
|
115
|
+
class Child < Climate::Command('child')
|
116
|
+
subcommand_of Parent
|
117
|
+
|
118
|
+
expose_ancestor_method Parent, :handy_service
|
119
|
+
|
120
|
+
def run
|
121
|
+
# finds the parent command instance so you can interrogate it
|
122
|
+
ancestor(Parent).useful_stuff
|
123
|
+
|
124
|
+
# handy_service was defined as an instance method using the
|
125
|
+
# #expose_ancestor_method above, but ultimately it is syntactic sugar
|
126
|
+
# for the above
|
127
|
+
handy_service.send_the_things
|
128
|
+
end
|
129
|
+
end
|
data/lib/climate/command.rb
CHANGED
@@ -24,14 +24,52 @@ module Climate
|
|
24
24
|
#
|
25
25
|
class Command
|
26
26
|
|
27
|
+
# Chain is a helper class for allowing parent commands to participate in
|
28
|
+
# command execution
|
29
|
+
class Chain
|
30
|
+
|
31
|
+
def initialize(commands)
|
32
|
+
@commands = commands
|
33
|
+
end
|
34
|
+
|
35
|
+
def empty?
|
36
|
+
@commands.empty?
|
37
|
+
end
|
38
|
+
|
39
|
+
def run
|
40
|
+
raise "Can't call run on an empty command chain" if empty?
|
41
|
+
|
42
|
+
next_command = @commands.shift
|
43
|
+
|
44
|
+
begin
|
45
|
+
if next_command.method(:run).arity == 1
|
46
|
+
next_command.run(self)
|
47
|
+
else
|
48
|
+
result = next_command.run
|
49
|
+
# we ignore the result from a run method with arity == 0 unless
|
50
|
+
# it is the last command in the chain. Really this is only a
|
51
|
+
# convenience for testing, so we can assert the chain was followed
|
52
|
+
# properly
|
53
|
+
if empty?
|
54
|
+
result
|
55
|
+
else
|
56
|
+
self.run
|
57
|
+
end
|
58
|
+
end
|
59
|
+
rescue Climate::CommandError => e
|
60
|
+
e.command_class = next_command.class if e.command_class.nil?
|
61
|
+
raise
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
27
66
|
class << self
|
28
67
|
include ParsingMethods
|
29
68
|
|
30
|
-
#
|
31
|
-
# arguments
|
69
|
+
# Recursively construct an array of commands
|
32
70
|
# @param [Array<String>] argv A list of arguments, ARGV style
|
33
71
|
# @param [Hash] options see {#initialize}
|
34
|
-
def
|
72
|
+
def build(argv, options={})
|
35
73
|
begin
|
36
74
|
instance = new(argv, options)
|
37
75
|
rescue Trollop::HelpNeeded
|
@@ -39,18 +77,24 @@ module Climate
|
|
39
77
|
end
|
40
78
|
|
41
79
|
if subcommands.empty?
|
42
|
-
|
43
|
-
instance.run
|
44
|
-
rescue Climate::CommandError => e
|
45
|
-
# make it easier on users
|
46
|
-
e.command_class = self if e.command_class.nil?
|
47
|
-
raise
|
48
|
-
end
|
80
|
+
[instance]
|
49
81
|
else
|
50
|
-
|
82
|
+
[instance, *find_and_build_subcommand(instance, options)]
|
51
83
|
end
|
52
84
|
end
|
53
85
|
|
86
|
+
def run(argv, options={})
|
87
|
+
command_instance_list = build(argv, options)
|
88
|
+
final_command = command_instance_list.last
|
89
|
+
|
90
|
+
if ! final_command.has_run?
|
91
|
+
raise NotImplementedError, "leaf command #{final_command} must implement #run"
|
92
|
+
end
|
93
|
+
|
94
|
+
runnable_commands = command_instance_list.select {|command| command.has_run? }
|
95
|
+
Chain.new(runnable_commands).run
|
96
|
+
end
|
97
|
+
|
54
98
|
def ancestors(exclude_self=false)
|
55
99
|
our_list = exclude_self ? [] : [self]
|
56
100
|
parent.nil?? our_list : parent.ancestors + our_list
|
@@ -125,9 +169,21 @@ module Climate
|
|
125
169
|
def has_subcommands? ; not subcommands.empty? ; end
|
126
170
|
def subcommands ; @subcommands ||= [] ; end
|
127
171
|
|
172
|
+
def expose_ancestor_method(ancestor_class, method_name)
|
173
|
+
define_method(method_name) do |*args|
|
174
|
+
ancestor(ancestor_class).send(method_name, *args)
|
175
|
+
end
|
176
|
+
end
|
177
|
+
|
178
|
+
def expose_ancestor_methods(ancestor_class, *method_names)
|
179
|
+
method_names.each do |method_name|
|
180
|
+
expose_ancestor_method(ancestor_class, method_name)
|
181
|
+
end
|
182
|
+
end
|
183
|
+
|
128
184
|
private
|
129
185
|
|
130
|
-
def
|
186
|
+
def find_and_build_subcommand(parent, options)
|
131
187
|
command_name, *arguments = parent.leftovers
|
132
188
|
|
133
189
|
if command_name.nil?
|
@@ -138,7 +194,7 @@ module Climate
|
|
138
194
|
found = subcommands.find {|c| c.command_name == command_name }
|
139
195
|
|
140
196
|
if found
|
141
|
-
found.
|
197
|
+
found.build(arguments, options.merge(:parent => parent))
|
142
198
|
else
|
143
199
|
raise UnknownCommandError.new(command_name, parent)
|
144
200
|
end
|
@@ -147,7 +203,7 @@ module Climate
|
|
147
203
|
end
|
148
204
|
|
149
205
|
# Create an instance of this command to be run. You'll probably want to use
|
150
|
-
# {Command.run
|
206
|
+
# {Command.run
|
151
207
|
# @param [Array<String>] arguments ARGV style arguments to be parsed
|
152
208
|
# @option options [Command] :parent The parent command, made available as {#parent}
|
153
209
|
# @option options [IO] :stdout stream to use as stdout, defaulting to `$stdout`
|
@@ -202,10 +258,8 @@ module Climate
|
|
202
258
|
end
|
203
259
|
end
|
204
260
|
|
205
|
-
|
206
|
-
|
207
|
-
def run
|
208
|
-
raise NotImplementedError, "Leaf commands must implement a run method"
|
261
|
+
def has_run?
|
262
|
+
self.methods.include? :run
|
209
263
|
end
|
210
264
|
|
211
265
|
def exit(status)
|
data/lib/climate/version.rb
CHANGED
metadata
CHANGED
@@ -1,95 +1,95 @@
|
|
1
|
-
--- !ruby/object:Gem::Specification
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
2
|
name: climate
|
3
|
-
version: !ruby/object:Gem::Version
|
4
|
-
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.7.0
|
5
5
|
prerelease:
|
6
|
-
segments:
|
7
|
-
- 0
|
8
|
-
- 6
|
9
|
-
- 0
|
10
|
-
version: 0.6.0
|
11
6
|
platform: ruby
|
12
|
-
authors:
|
7
|
+
authors:
|
13
8
|
- Nick Griffiths
|
14
9
|
autorequire:
|
15
10
|
bindir: bin
|
16
11
|
cert_chain: []
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
- !ruby/object:Gem::Dependency
|
12
|
+
date: 2013-12-10 00:00:00.000000000 Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
21
15
|
name: trollop
|
22
|
-
|
23
|
-
requirement: &id001 !ruby/object:Gem::Requirement
|
16
|
+
requirement: !ruby/object:Gem::Requirement
|
24
17
|
none: false
|
25
|
-
requirements:
|
18
|
+
requirements:
|
26
19
|
- - ~>
|
27
|
-
- !ruby/object:Gem::Version
|
28
|
-
|
29
|
-
segments:
|
30
|
-
- 2
|
31
|
-
- 0
|
32
|
-
version: "2.0"
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: '2.0'
|
33
22
|
type: :runtime
|
34
|
-
version_requirements: *id001
|
35
|
-
- !ruby/object:Gem::Dependency
|
36
|
-
name: minitest
|
37
23
|
prerelease: false
|
38
|
-
|
24
|
+
version_requirements: !ruby/object:Gem::Requirement
|
25
|
+
none: false
|
26
|
+
requirements:
|
27
|
+
- - ~>
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
version: '2.0'
|
30
|
+
- !ruby/object:Gem::Dependency
|
31
|
+
name: minitest
|
32
|
+
requirement: !ruby/object:Gem::Requirement
|
39
33
|
none: false
|
40
|
-
requirements:
|
41
|
-
- -
|
42
|
-
- !ruby/object:Gem::Version
|
43
|
-
|
44
|
-
segments:
|
45
|
-
- 0
|
46
|
-
version: "0"
|
34
|
+
requirements:
|
35
|
+
- - ! '>='
|
36
|
+
- !ruby/object:Gem::Version
|
37
|
+
version: '0'
|
47
38
|
type: :development
|
48
|
-
version_requirements: *id002
|
49
|
-
- !ruby/object:Gem::Dependency
|
50
|
-
name: yard
|
51
39
|
prerelease: false
|
52
|
-
|
40
|
+
version_requirements: !ruby/object:Gem::Requirement
|
41
|
+
none: false
|
42
|
+
requirements:
|
43
|
+
- - ! '>='
|
44
|
+
- !ruby/object:Gem::Version
|
45
|
+
version: '0'
|
46
|
+
- !ruby/object:Gem::Dependency
|
47
|
+
name: yard
|
48
|
+
requirement: !ruby/object:Gem::Requirement
|
53
49
|
none: false
|
54
|
-
requirements:
|
55
|
-
- -
|
56
|
-
- !ruby/object:Gem::Version
|
57
|
-
|
58
|
-
segments:
|
59
|
-
- 0
|
60
|
-
version: "0"
|
50
|
+
requirements:
|
51
|
+
- - ! '>='
|
52
|
+
- !ruby/object:Gem::Version
|
53
|
+
version: '0'
|
61
54
|
type: :development
|
62
|
-
version_requirements: *id003
|
63
|
-
- !ruby/object:Gem::Dependency
|
64
|
-
name: popen4
|
65
55
|
prerelease: false
|
66
|
-
|
56
|
+
version_requirements: !ruby/object:Gem::Requirement
|
67
57
|
none: false
|
68
|
-
requirements:
|
69
|
-
- -
|
70
|
-
- !ruby/object:Gem::Version
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
58
|
+
requirements:
|
59
|
+
- - ! '>='
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0'
|
62
|
+
- !ruby/object:Gem::Dependency
|
63
|
+
name: popen4
|
64
|
+
requirement: !ruby/object:Gem::Requirement
|
65
|
+
none: false
|
66
|
+
requirements:
|
67
|
+
- - ! '>='
|
68
|
+
- !ruby/object:Gem::Version
|
69
|
+
version: '0'
|
75
70
|
type: :development
|
76
|
-
|
77
|
-
|
78
|
-
|
71
|
+
prerelease: false
|
72
|
+
version_requirements: !ruby/object:Gem::Requirement
|
73
|
+
none: false
|
74
|
+
requirements:
|
75
|
+
- - ! '>='
|
76
|
+
- !ruby/object:Gem::Version
|
77
|
+
version: '0'
|
78
|
+
description: Library, not a framework, for building command line interfaces to your
|
79
|
+
ruby application
|
80
|
+
email:
|
79
81
|
- nicobrevin@gmail.com
|
80
|
-
executables:
|
82
|
+
executables:
|
81
83
|
- climate
|
82
84
|
extensions: []
|
83
|
-
|
84
85
|
extra_rdoc_files: []
|
85
|
-
|
86
|
-
|
87
|
-
- lib/climate/version.rb
|
86
|
+
files:
|
87
|
+
- lib/climate/command.rb
|
88
88
|
- lib/climate/help/man.rb
|
89
89
|
- lib/climate/command_compat.rb
|
90
|
-
- lib/climate/command.rb
|
91
90
|
- lib/climate/parser.rb
|
92
91
|
- lib/climate/errors.rb
|
92
|
+
- lib/climate/version.rb
|
93
93
|
- lib/climate/script.rb
|
94
94
|
- lib/climate/help.rb
|
95
95
|
- lib/climate.rb
|
@@ -97,37 +97,27 @@ files:
|
|
97
97
|
- bin/climate
|
98
98
|
homepage:
|
99
99
|
licenses: []
|
100
|
-
|
101
100
|
post_install_message:
|
102
101
|
rdoc_options: []
|
103
|
-
|
104
|
-
require_paths:
|
102
|
+
require_paths:
|
105
103
|
- lib
|
106
|
-
required_ruby_version: !ruby/object:Gem::Requirement
|
104
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
107
105
|
none: false
|
108
|
-
requirements:
|
109
|
-
- -
|
110
|
-
- !ruby/object:Gem::Version
|
111
|
-
|
112
|
-
|
113
|
-
- 0
|
114
|
-
version: "0"
|
115
|
-
required_rubygems_version: !ruby/object:Gem::Requirement
|
106
|
+
requirements:
|
107
|
+
- - ! '>='
|
108
|
+
- !ruby/object:Gem::Version
|
109
|
+
version: '0'
|
110
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
116
111
|
none: false
|
117
|
-
requirements:
|
118
|
-
- -
|
119
|
-
- !ruby/object:Gem::Version
|
120
|
-
|
121
|
-
segments:
|
122
|
-
- 0
|
123
|
-
version: "0"
|
112
|
+
requirements:
|
113
|
+
- - ! '>='
|
114
|
+
- !ruby/object:Gem::Version
|
115
|
+
version: '0'
|
124
116
|
requirements: []
|
125
|
-
|
126
117
|
rubyforge_project:
|
127
|
-
rubygems_version: 1.8.
|
118
|
+
rubygems_version: 1.8.25
|
128
119
|
signing_key:
|
129
120
|
specification_version: 3
|
130
121
|
summary: Library for building command line interfaces
|
131
122
|
test_files: []
|
132
|
-
|
133
123
|
has_rdoc:
|