strelka 0.11.0 → 0.12.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.
- checksums.yaml +4 -4
- checksums.yaml.gz.sig +0 -0
- data.tar.gz.sig +0 -0
- data/History.rdoc +8 -0
- data/Manifest.txt +5 -0
- data/README.rdoc +25 -10
- data/Rakefile +2 -2
- data/bin/strelka +2 -411
- data/lib/strelka.rb +5 -10
- data/lib/strelka/cli.rb +393 -0
- data/lib/strelka/command/config.rb +35 -0
- data/lib/strelka/command/discover.rb +29 -0
- data/lib/strelka/command/start.rb +40 -0
- data/lib/strelka/discovery.rb +151 -97
- data/spec/strelka/cli_spec.rb +85 -0
- data/spec/strelka/discovery_spec.rb +69 -81
- data/spec/strelka_spec.rb +0 -12
- metadata +49 -45
- metadata.gz.sig +0 -0
data/lib/strelka.rb
CHANGED
@@ -13,6 +13,7 @@ require 'configurability/config'
|
|
13
13
|
# == Author/s
|
14
14
|
#
|
15
15
|
# * Michael Granger <ged@FaerieMUD.org>
|
16
|
+
# * Mahlon E. Smith <mahlon@martini.nu>
|
16
17
|
#
|
17
18
|
# :title: Strelka Web Application Framework
|
18
19
|
# :main: README.rdoc
|
@@ -24,10 +25,10 @@ module Strelka
|
|
24
25
|
log_as :strelka
|
25
26
|
|
26
27
|
# Library version constant
|
27
|
-
VERSION = '0.
|
28
|
+
VERSION = '0.12.0'
|
28
29
|
|
29
30
|
# Version-control revision constant
|
30
|
-
REVISION = %q$Revision:
|
31
|
+
REVISION = %q$Revision: d7a7144b3fd9 $
|
31
32
|
|
32
33
|
require 'strelka/mixins'
|
33
34
|
require 'strelka/constants'
|
@@ -68,14 +69,8 @@ module Strelka
|
|
68
69
|
### Look up the application class of +appname+, optionally limiting it to the gem
|
69
70
|
### named +gemname+. Returns the first matching class, or raises an exception if no
|
70
71
|
### app class was found.
|
71
|
-
def self::App( appname
|
72
|
-
|
73
|
-
raise LoadError, "Can't find the %s app." % [ appname ] unless path
|
74
|
-
|
75
|
-
apps = Strelka::Discovery.load( path ) or
|
76
|
-
raise ScriptError "Loading %s didn't define a Strelka::App class." % [ path ]
|
77
|
-
|
78
|
-
return apps.first
|
72
|
+
def self::App( appname )
|
73
|
+
return Strelka::Discovery.load( appname )
|
79
74
|
end
|
80
75
|
|
81
76
|
end # module Strelka
|
data/lib/strelka/cli.rb
ADDED
@@ -0,0 +1,393 @@
|
|
1
|
+
# -*- ruby -*-
|
2
|
+
#encoding: utf-8
|
3
|
+
|
4
|
+
require 'loggability'
|
5
|
+
require 'highline'
|
6
|
+
require 'gli'
|
7
|
+
|
8
|
+
require 'strelka' unless defined?( Strelka )
|
9
|
+
require 'strelka/constants'
|
10
|
+
require 'strelka/mixins'
|
11
|
+
|
12
|
+
|
13
|
+
# The command-line interface to Strelka.
|
14
|
+
module Strelka::CLI
|
15
|
+
extend Strelka::MethodUtilities,
|
16
|
+
Loggability,
|
17
|
+
GLI::App
|
18
|
+
include Strelka::Constants
|
19
|
+
|
20
|
+
|
21
|
+
# Write logs to Strelka's logger
|
22
|
+
log_to :strelka
|
23
|
+
|
24
|
+
|
25
|
+
# Make a HighLine color scheme
|
26
|
+
COLOR_SCHEME = HighLine::ColorScheme.new do |scheme|
|
27
|
+
scheme[:header] = [ :bold, :yellow ]
|
28
|
+
scheme[:subheader] = [ :bold, :white ]
|
29
|
+
scheme[:key] = [ :white ]
|
30
|
+
scheme[:value] = [ :bold, :white ]
|
31
|
+
scheme[:error] = [ :red ]
|
32
|
+
scheme[:warning] = [ :yellow ]
|
33
|
+
scheme[:message] = [ :reset ]
|
34
|
+
end
|
35
|
+
|
36
|
+
|
37
|
+
#
|
38
|
+
# GLI
|
39
|
+
#
|
40
|
+
|
41
|
+
# Set up global[:description] and options
|
42
|
+
program_desc 'Strelka'
|
43
|
+
|
44
|
+
# The command version
|
45
|
+
version Strelka::VERSION
|
46
|
+
|
47
|
+
# Use an OpenStruct for options instead of a Hash
|
48
|
+
use_openstruct( true )
|
49
|
+
|
50
|
+
# Subcommand options are independent of global[:ones]
|
51
|
+
subcommand_option_handling :normal
|
52
|
+
|
53
|
+
# Strict argument validation
|
54
|
+
arguments :strict
|
55
|
+
|
56
|
+
|
57
|
+
# Custom parameter types
|
58
|
+
accept Array do |value|
|
59
|
+
value.strip.split(/\s*,\s*/)
|
60
|
+
end
|
61
|
+
accept Pathname do |value|
|
62
|
+
Pathname( value.strip )
|
63
|
+
end
|
64
|
+
|
65
|
+
|
66
|
+
# Global options
|
67
|
+
desc "Specify the config file to load"
|
68
|
+
flag [:c, :config], type: Pathname
|
69
|
+
|
70
|
+
desc "Override the Strelka data directory"
|
71
|
+
flag [:D, :datadir], default_value: '.', type: Pathname
|
72
|
+
|
73
|
+
desc 'Enable debugging output'
|
74
|
+
switch [:d, :debug]
|
75
|
+
|
76
|
+
desc 'Enable verbose output'
|
77
|
+
switch [:v, :verbose]
|
78
|
+
|
79
|
+
desc 'Set log level to LEVEL (one of %s)' % [Loggability::LOG_LEVELS.keys.join(', ')]
|
80
|
+
default_value Loggability[self].level
|
81
|
+
flag [:l, :loglevel], must_match: Loggability::LOG_LEVELS.keys
|
82
|
+
|
83
|
+
desc "Don't actually do anything, just show what would happen."
|
84
|
+
switch [:n, 'dry-run']
|
85
|
+
|
86
|
+
desc "Write the output to FILE instead of STDERR. Specify " +
|
87
|
+
"'-' to write command output to STDOUT instead."
|
88
|
+
flag [:o, 'output-file'], type: Pathname
|
89
|
+
|
90
|
+
desc "Additional Ruby libs to require before doing anything."
|
91
|
+
flag [:r, 'requires'], type: Array
|
92
|
+
|
93
|
+
|
94
|
+
#
|
95
|
+
# GLI Event callbacks
|
96
|
+
#
|
97
|
+
|
98
|
+
pre do |global, command, options, args|
|
99
|
+
if loglevel = global[:loglevel]
|
100
|
+
Loggability.level = loglevel.to_sym
|
101
|
+
else
|
102
|
+
Loggability.level = :fatal
|
103
|
+
end
|
104
|
+
|
105
|
+
# Set the datadir override if it's given
|
106
|
+
if global.datadir
|
107
|
+
self.log.debug "Using data dir option: %s" % [ global.datadir ]
|
108
|
+
Strelka::Discovery.local_data_dirs = global.datadir
|
109
|
+
end
|
110
|
+
|
111
|
+
# Include a 'lib' directory if there is one
|
112
|
+
$LOAD_PATH.unshift( 'lib' ) if File.directory?( 'lib' )
|
113
|
+
|
114
|
+
self.require_additional_libs( global.requires ) if global.requires
|
115
|
+
self.load_config( global )
|
116
|
+
self.install_highline_colorscheme
|
117
|
+
self.setup_output( global )
|
118
|
+
|
119
|
+
true
|
120
|
+
end
|
121
|
+
|
122
|
+
|
123
|
+
# Close the output file immediately after the command executes
|
124
|
+
post do |*|
|
125
|
+
self.outfile.close if self.outfile
|
126
|
+
end
|
127
|
+
|
128
|
+
|
129
|
+
##
|
130
|
+
# Registered subcommand modules
|
131
|
+
singleton_attr_accessor :subcommand_modules
|
132
|
+
|
133
|
+
##
|
134
|
+
# The IO opened to the output file
|
135
|
+
singleton_attr_accessor :outfile
|
136
|
+
|
137
|
+
|
138
|
+
### Overridden -- Add registered subcommands immediately before running.
|
139
|
+
def self::run( * )
|
140
|
+
self.add_registered_subcommands
|
141
|
+
super
|
142
|
+
end
|
143
|
+
|
144
|
+
|
145
|
+
### Add the specified +mod+ule containing subcommands to the 'strelka' command.
|
146
|
+
def self::register_subcommands( mod )
|
147
|
+
self.subcommand_modules ||= []
|
148
|
+
self.subcommand_modules.push( mod )
|
149
|
+
mod.extend( GLI::App, GLI::AppSupport, Strelka::Constants, Loggability )
|
150
|
+
mod.log_to( :strelka )
|
151
|
+
end
|
152
|
+
|
153
|
+
|
154
|
+
### Add the commands from the registered subcommand modules.
|
155
|
+
def self::add_registered_subcommands
|
156
|
+
self.subcommand_modules ||= []
|
157
|
+
self.subcommand_modules.each do |mod|
|
158
|
+
merged_commands = mod.commands.merge( self.commands )
|
159
|
+
self.commands.update( merged_commands )
|
160
|
+
command_objs = self.commands_declaration_order | self.commands.values
|
161
|
+
self.commands_declaration_order.replace( command_objs )
|
162
|
+
end
|
163
|
+
end
|
164
|
+
|
165
|
+
|
166
|
+
### Return the HighLine prompt used by the command to communicate with the
|
167
|
+
### user.
|
168
|
+
def self::prompt
|
169
|
+
@prompt ||= HighLine.new( $stdin, $stderr )
|
170
|
+
end
|
171
|
+
|
172
|
+
|
173
|
+
### If the command's output was redirected to a file, return the open File object
|
174
|
+
### for it.
|
175
|
+
def self::outfile
|
176
|
+
return @outfile
|
177
|
+
end
|
178
|
+
|
179
|
+
|
180
|
+
### Discard the existing HighLine prompt object if one existed. Mostly useful for
|
181
|
+
### testing.
|
182
|
+
def self::reset_prompt
|
183
|
+
@prompt = nil
|
184
|
+
end
|
185
|
+
|
186
|
+
|
187
|
+
### Load any additional Ruby libraries given with the -r global option.
|
188
|
+
def self::require_additional_libs( requires)
|
189
|
+
requires.each do |path|
|
190
|
+
path = "strelka/#{path}" unless path.start_with?( 'strelka/' )
|
191
|
+
require( path )
|
192
|
+
end
|
193
|
+
end
|
194
|
+
|
195
|
+
|
196
|
+
### Install the color scheme used by HighLine
|
197
|
+
def self::install_highline_colorscheme
|
198
|
+
HighLine.color_scheme = HighLine::ColorScheme.new do |cs|
|
199
|
+
cs[:headline] = [ :bold, :white, :on_black ]
|
200
|
+
cs[:success] = [ :green ]
|
201
|
+
cs[:error] = [ :bold, :red ]
|
202
|
+
cs[:highlight] = [ :bold, :yellow ]
|
203
|
+
cs[:search_hit] = [ :black, :on_white ]
|
204
|
+
cs[:prompt] = [ :cyan ]
|
205
|
+
cs[:even_row] = [ :bold ]
|
206
|
+
cs[:odd_row] = [ :normal ]
|
207
|
+
end
|
208
|
+
end
|
209
|
+
|
210
|
+
|
211
|
+
### Load the config file using either strelka-base's config-loader if available, or
|
212
|
+
### fall back to DEFAULT_CONFIG_FILE
|
213
|
+
def self::load_config( global={} )
|
214
|
+
Strelka.load_config( global.config ) if global.config
|
215
|
+
|
216
|
+
# Set up the logging formatter
|
217
|
+
Loggability.format_with( :color ) if $stdout.tty?
|
218
|
+
end
|
219
|
+
|
220
|
+
|
221
|
+
### Set up the output levels and globals based on the associated +global+ options.
|
222
|
+
def self::setup_output( global )
|
223
|
+
|
224
|
+
# Turn on Ruby debugging and/or verbosity if specified
|
225
|
+
if global[:n]
|
226
|
+
$DRYRUN = true
|
227
|
+
Loggability.level = :warn
|
228
|
+
else
|
229
|
+
$DRYRUN = false
|
230
|
+
end
|
231
|
+
|
232
|
+
if global[:verbose]
|
233
|
+
$VERBOSE = true
|
234
|
+
Loggability.level = :info
|
235
|
+
end
|
236
|
+
|
237
|
+
if global[:debug]
|
238
|
+
$DEBUG = true
|
239
|
+
Loggability.level = :debug
|
240
|
+
end
|
241
|
+
|
242
|
+
if (( filename = global[:o] ))
|
243
|
+
if filename.to_s == '-'
|
244
|
+
@prompt = HighLine.new( $stdin, $stdout )
|
245
|
+
else
|
246
|
+
@outfile = filename.open( 'w', encoding: 'utf-8' )
|
247
|
+
HighLine.use_color = false
|
248
|
+
@prompt = HighLine.new( $stdin, @outfile )
|
249
|
+
end
|
250
|
+
end
|
251
|
+
end
|
252
|
+
|
253
|
+
|
254
|
+
# Write the error to the log on exceptions.
|
255
|
+
on_error do |exception|
|
256
|
+
case exception
|
257
|
+
when OptionParser::ParseError, GLI::CustomExit
|
258
|
+
self.log.debug( exception )
|
259
|
+
else
|
260
|
+
self.log.error( exception )
|
261
|
+
end
|
262
|
+
|
263
|
+
exception.backtrace.each {|frame| self.log.debug(frame) }
|
264
|
+
|
265
|
+
true
|
266
|
+
end
|
267
|
+
|
268
|
+
|
269
|
+
#
|
270
|
+
# GLI subcommands
|
271
|
+
#
|
272
|
+
|
273
|
+
|
274
|
+
# Convenience module for subcommand registration syntax sugar.
|
275
|
+
module Subcommand
|
276
|
+
|
277
|
+
### Extension callback -- register the extending object as a subcommand.
|
278
|
+
def self::extended( mod )
|
279
|
+
Strelka::CLI.log.debug "Registering subcommands from %p" % [ mod ]
|
280
|
+
Strelka::CLI.register_subcommands( mod )
|
281
|
+
end
|
282
|
+
|
283
|
+
|
284
|
+
###############
|
285
|
+
module_function
|
286
|
+
###############
|
287
|
+
|
288
|
+
### Get the prompt (a Highline object)
|
289
|
+
def prompt
|
290
|
+
return Strelka::CLI.prompt
|
291
|
+
end
|
292
|
+
|
293
|
+
|
294
|
+
### Return the specified +text+ as a Highline::String for convenient formatting,
|
295
|
+
### color, etc.
|
296
|
+
def hl( text )
|
297
|
+
return HighLine::String.new( text.to_s )
|
298
|
+
end
|
299
|
+
|
300
|
+
|
301
|
+
### Return the specified +string+ in the 'headline' ANSI color.
|
302
|
+
def headline_string( string )
|
303
|
+
return hl( string ).color( :headline )
|
304
|
+
end
|
305
|
+
|
306
|
+
|
307
|
+
### Return the specified +string+ in the 'highlight' ANSI color.
|
308
|
+
def highlight_string( string )
|
309
|
+
return hl( string ).color( :highlight )
|
310
|
+
end
|
311
|
+
|
312
|
+
|
313
|
+
### Return the specified +string+ in the 'success' ANSI color.
|
314
|
+
def success_string( string )
|
315
|
+
return hl( string ).color( :success )
|
316
|
+
end
|
317
|
+
|
318
|
+
|
319
|
+
### Return the specified +string+ in the 'error' ANSI color.
|
320
|
+
def error_string( string )
|
321
|
+
return hl( string ).color( :error )
|
322
|
+
end
|
323
|
+
|
324
|
+
|
325
|
+
### Output a table with the given +rows+.
|
326
|
+
def display_table( rows )
|
327
|
+
colwidths = rows.transpose.map do |colvals|
|
328
|
+
colvals.map {|val| visible_chars(val) }.max
|
329
|
+
end
|
330
|
+
|
331
|
+
rows.each do |row|
|
332
|
+
row_string = row.zip( colwidths ).inject( '' ) do |accum, (val, colsize)|
|
333
|
+
padding = ' ' * (colsize - visible_chars(val) + 2)
|
334
|
+
accum + val.to_s + padding
|
335
|
+
end
|
336
|
+
|
337
|
+
Strelka::CLI.prompt.say( row_string + "\n" )
|
338
|
+
end
|
339
|
+
end
|
340
|
+
|
341
|
+
|
342
|
+
### Return the count of visible (i.e., non-control) characters in the given +string+.
|
343
|
+
def visible_chars( string )
|
344
|
+
return string.to_s.gsub(/\e\[.*?m/, '').scan( /\P{Cntrl}/ ).size
|
345
|
+
end
|
346
|
+
|
347
|
+
|
348
|
+
### In dry-run mode, output the description instead of running the provided block and
|
349
|
+
### return the +return_value+.
|
350
|
+
### If dry-run mode is not enabled, yield to the block.
|
351
|
+
def unless_dryrun( description, return_value=true )
|
352
|
+
if $DRYRUN
|
353
|
+
self.log.warn( "DRYRUN> #{description}" )
|
354
|
+
return return_value
|
355
|
+
else
|
356
|
+
return yield
|
357
|
+
end
|
358
|
+
end
|
359
|
+
alias_method :unless_dry_run, :unless_dryrun
|
360
|
+
|
361
|
+
end # module Subcommand
|
362
|
+
|
363
|
+
|
364
|
+
### Register one or more subcommands with the 'strelka' command shell. The given
|
365
|
+
### block will be evaluated in the context of Strelka::CLI.
|
366
|
+
def self::register( &block )
|
367
|
+
self.instance_eval( &block )
|
368
|
+
end
|
369
|
+
|
370
|
+
|
371
|
+
### Custom command loader. The default one is silly.
|
372
|
+
def self::load_commands( path )
|
373
|
+
self.log.debug "Load commands from %s" % [ path ]
|
374
|
+
Pathname.glob( path + '*.rb' ).each do |rbfile|
|
375
|
+
self.log.debug " loading %s..." % [ rbfile ]
|
376
|
+
require( rbfile )
|
377
|
+
end
|
378
|
+
end
|
379
|
+
|
380
|
+
|
381
|
+
# Load commands from any files in the specified directory relative to LOAD_PATHs
|
382
|
+
def self::commands_from( subdir )
|
383
|
+
$LOAD_PATH.map {|path| Pathname(path) }.each do |libdir|
|
384
|
+
command_dir = libdir.expand_path + subdir
|
385
|
+
load_commands( command_dir )
|
386
|
+
end
|
387
|
+
end
|
388
|
+
|
389
|
+
|
390
|
+
commands_from 'strelka/command'
|
391
|
+
|
392
|
+
end # class Strelka::CLICommand
|
393
|
+
|
@@ -0,0 +1,35 @@
|
|
1
|
+
# -*- ruby -*-
|
2
|
+
#encoding: utf-8
|
3
|
+
|
4
|
+
require 'strelka/cli' unless defined?( Strelka::CLI )
|
5
|
+
|
6
|
+
|
7
|
+
# Command to start a Strelka application
|
8
|
+
module Strelka::CLI::Config
|
9
|
+
extend Strelka::CLI::Subcommand
|
10
|
+
|
11
|
+
desc 'Dump a config file for the specified GEM (or local apps)'
|
12
|
+
arg :GEM, :optional
|
13
|
+
command :config do |cmd|
|
14
|
+
|
15
|
+
cmd.action do |globals, options, args|
|
16
|
+
gemname = args.shift
|
17
|
+
discovery_name = gemname || ''
|
18
|
+
|
19
|
+
prompt.say( headline_string "Dumping config for %s" % [ gemname || 'local apps' ] )
|
20
|
+
discovered_apps = Strelka::Discovery.discover_apps
|
21
|
+
|
22
|
+
raise ArgumentError, "No apps discovered" unless discovered_apps.key?( discovery_name )
|
23
|
+
|
24
|
+
discovered_apps[ discovery_name ].each do |apppath|
|
25
|
+
prompt.say " loading %s (%s)" % [ apppath, apppath.basename('.rb') ]
|
26
|
+
Strelka::Discovery.load( apppath )
|
27
|
+
end
|
28
|
+
|
29
|
+
prompt.say " dumping config:"
|
30
|
+
$stdout.puts Configurability.default_config.dump
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
end # module Strelka::CLI::Config
|
35
|
+
|