bitgirder-platform 0.1.7
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/LICENSE.txt +176 -0
- data/bin/ensure-test-db +117 -0
- data/bin/install-mysql +375 -0
- data/bin/tomcat7 +569 -0
- data/lib/bitgirder/concurrent.rb +400 -0
- data/lib/bitgirder/core.rb +1235 -0
- data/lib/bitgirder/etl.rb +58 -0
- data/lib/bitgirder/event/file.rb +485 -0
- data/lib/bitgirder/event/logger/testing.rb +140 -0
- data/lib/bitgirder/event/logger.rb +137 -0
- data/lib/bitgirder/event/testing.rb +88 -0
- data/lib/bitgirder/http.rb +255 -0
- data/lib/bitgirder/io/testing.rb +33 -0
- data/lib/bitgirder/io.rb +959 -0
- data/lib/bitgirder/irb.rb +35 -0
- data/lib/bitgirder/mysql.rb +60 -0
- data/lib/bitgirder/ops/java.rb +117 -0
- data/lib/bitgirder/ops/ruby.rb +235 -0
- data/lib/bitgirder/testing.rb +152 -0
- data/lib/doc-gen0.rb +0 -0
- data/lib/doc-gen1.rb +0 -0
- data/lib/doc-gen10.rb +0 -0
- data/lib/doc-gen11.rb +0 -0
- data/lib/doc-gen12.rb +0 -0
- data/lib/doc-gen13.rb +0 -0
- data/lib/doc-gen14.rb +0 -0
- data/lib/doc-gen15.rb +0 -0
- data/lib/doc-gen16.rb +0 -0
- data/lib/doc-gen17.rb +14 -0
- data/lib/doc-gen18.rb +0 -0
- data/lib/doc-gen19.rb +0 -0
- data/lib/doc-gen2.rb +0 -0
- data/lib/doc-gen20.rb +182 -0
- data/lib/doc-gen21.rb +0 -0
- data/lib/doc-gen3.rb +0 -0
- data/lib/doc-gen4.rb +0 -0
- data/lib/doc-gen5.rb +0 -0
- data/lib/doc-gen6.rb +0 -0
- data/lib/doc-gen7.rb +0 -0
- data/lib/doc-gen8.rb +0 -0
- data/lib/doc-gen9.rb +0 -0
- data/lib/mingle/bincodec.rb +512 -0
- data/lib/mingle/codec.rb +54 -0
- data/lib/mingle/http.rb +156 -0
- data/lib/mingle/io/stream.rb +142 -0
- data/lib/mingle/io.rb +160 -0
- data/lib/mingle/json.rb +257 -0
- data/lib/mingle/service.rb +110 -0
- data/lib/mingle-em.rb +92 -0
- data/lib/mingle.rb +2917 -0
- metadata +100 -0
|
@@ -0,0 +1,1235 @@
|
|
|
1
|
+
# Placed here so that the rest of the libs and executables in our codebase
|
|
2
|
+
# needn't worry about requiring rubygems when being run in ruby 1.8.
|
|
3
|
+
require 'rubygems' if RUBY_VERSION < "1.9"
|
|
4
|
+
|
|
5
|
+
module BitGirder
|
|
6
|
+
module Core
|
|
7
|
+
|
|
8
|
+
ENV_BITGIRDER_DEBUG = "BITGIRDER_DEBUG"
|
|
9
|
+
|
|
10
|
+
EXIT_SUCCESS = 0
|
|
11
|
+
EXIT_FAILURE = 1
|
|
12
|
+
|
|
13
|
+
class RubyVersions
|
|
14
|
+
|
|
15
|
+
def self.when_geq( ver, val = nil )
|
|
16
|
+
if RUBY_VERSION >= ver
|
|
17
|
+
yield( val )
|
|
18
|
+
else
|
|
19
|
+
val
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def self.is_19x?
|
|
24
|
+
RUBY_VERSION >= "1.9"
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def self.when_19x( val = nil, &blk )
|
|
28
|
+
self.when_geq( "1.9", val, &blk )
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def self.jruby?
|
|
32
|
+
RUBY_PLATFORM == "java"
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
class BitGirderLogger
|
|
37
|
+
|
|
38
|
+
require 'time'
|
|
39
|
+
require 'thread'
|
|
40
|
+
|
|
41
|
+
private_class_method :new
|
|
42
|
+
|
|
43
|
+
# Our log levels
|
|
44
|
+
CODE = :CODE
|
|
45
|
+
CONSOLE = :CONSOLE
|
|
46
|
+
WARN = :WARN
|
|
47
|
+
DEFAULT = CONSOLE
|
|
48
|
+
|
|
49
|
+
attr_reader :level
|
|
50
|
+
|
|
51
|
+
def initialize
|
|
52
|
+
@lock = Mutex.new
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
# makes a string msg from argv, which can either be an exception and a
|
|
56
|
+
# message or just a message
|
|
57
|
+
private
|
|
58
|
+
def make_msg( argv )
|
|
59
|
+
|
|
60
|
+
case len = argv.length
|
|
61
|
+
|
|
62
|
+
when 1 then argv.shift
|
|
63
|
+
|
|
64
|
+
when 2
|
|
65
|
+
e, msg = *argv
|
|
66
|
+
msg << "\n" << e.message.to_s
|
|
67
|
+
|
|
68
|
+
bt = e.backtrace
|
|
69
|
+
( msg << "\n" << bt.join( "\n" ) ) if bt
|
|
70
|
+
|
|
71
|
+
msg
|
|
72
|
+
|
|
73
|
+
else raise ArgumentError, "Wrong number of arguments: #{len}"
|
|
74
|
+
end
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
def self.level_of( lev )
|
|
78
|
+
case lev
|
|
79
|
+
when CODE then 1
|
|
80
|
+
when CONSOLE then 2
|
|
81
|
+
when WARN then 3
|
|
82
|
+
else raise "Invalid level: #{lev}"
|
|
83
|
+
end
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
private
|
|
87
|
+
def send_msg( lev, time, argv )
|
|
88
|
+
|
|
89
|
+
if self.class.level_of( lev ) >= self.class.level_of( @level )
|
|
90
|
+
str = make_msg( argv )
|
|
91
|
+
@lock.synchronize { puts "[#{time.iso8601( 6 )}]: #{lev}: #{str}" }
|
|
92
|
+
end
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
public
|
|
96
|
+
def code( *argv )
|
|
97
|
+
send_msg( CODE, Time.now, argv )
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
alias debug code
|
|
101
|
+
|
|
102
|
+
public
|
|
103
|
+
def warn( *argv )
|
|
104
|
+
send_msg( WARN, Time.now, argv )
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
public
|
|
108
|
+
def console( *argv )
|
|
109
|
+
send_msg( CONSOLE, Time.now, argv )
|
|
110
|
+
end
|
|
111
|
+
|
|
112
|
+
public
|
|
113
|
+
def is_debug?
|
|
114
|
+
@level == CODE
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
public
|
|
118
|
+
def level=( lev )
|
|
119
|
+
|
|
120
|
+
@level =
|
|
121
|
+
case lev
|
|
122
|
+
when CODE, CONSOLE, WARN then lev
|
|
123
|
+
else raise ArgumentError, "Unknown level: #{lev}"
|
|
124
|
+
end
|
|
125
|
+
end
|
|
126
|
+
|
|
127
|
+
def self.is_debug_env_set?
|
|
128
|
+
( ENV[ ENV_BITGIRDER_DEBUG ] or "" ).strip =~ /^(true|yes)$/
|
|
129
|
+
end
|
|
130
|
+
|
|
131
|
+
@logger = new
|
|
132
|
+
@logger.level = self.is_debug_env_set? ? CODE : DEFAULT
|
|
133
|
+
|
|
134
|
+
def self.get_logger
|
|
135
|
+
@logger
|
|
136
|
+
end
|
|
137
|
+
|
|
138
|
+
class <<self
|
|
139
|
+
|
|
140
|
+
[ :debug, :code, :warn, :console ].each do |meth|
|
|
141
|
+
|
|
142
|
+
define_method( meth ) do |*argv|
|
|
143
|
+
get_logger.send( meth, *argv )
|
|
144
|
+
end
|
|
145
|
+
end
|
|
146
|
+
end
|
|
147
|
+
end
|
|
148
|
+
|
|
149
|
+
module Reflect
|
|
150
|
+
|
|
151
|
+
# Returns an array of Symbol regardless of ruby version (1.8 uses String,
|
|
152
|
+
# 1.9 Symbols)
|
|
153
|
+
def self.instance_methods_of( v )
|
|
154
|
+
|
|
155
|
+
res = v.instance_methods
|
|
156
|
+
|
|
157
|
+
if res.size > 0 && res[ 0 ].is_a?( String )
|
|
158
|
+
res = res.map { |s| s.to_sym }
|
|
159
|
+
end
|
|
160
|
+
|
|
161
|
+
res
|
|
162
|
+
end
|
|
163
|
+
end
|
|
164
|
+
|
|
165
|
+
module BitGirderMethods
|
|
166
|
+
|
|
167
|
+
module_function
|
|
168
|
+
|
|
169
|
+
def code( *argv )
|
|
170
|
+
BitGirderLogger.get_logger.code( *argv )
|
|
171
|
+
end
|
|
172
|
+
|
|
173
|
+
def console( *argv )
|
|
174
|
+
BitGirderLogger.get_logger.console( *argv )
|
|
175
|
+
end
|
|
176
|
+
|
|
177
|
+
alias debug code
|
|
178
|
+
|
|
179
|
+
def warn( *argv )
|
|
180
|
+
BitGirderLogger.get_logger.warn( *argv )
|
|
181
|
+
end
|
|
182
|
+
|
|
183
|
+
# Each constant corresponds to a prefix in an error message appropriate to
|
|
184
|
+
# that type
|
|
185
|
+
#
|
|
186
|
+
PARAM_TYPE_ARG = "Argument"
|
|
187
|
+
PARAM_TYPE_ENVVAR = "Environment Variable"
|
|
188
|
+
PARAM_TYPE_KEY = "Value for key"
|
|
189
|
+
|
|
190
|
+
def check_fail_prefix( name, param_type )
|
|
191
|
+
|
|
192
|
+
# Since we'll most often be using symbols instead of strings for arg
|
|
193
|
+
# parameter names we special case displaying them as strings
|
|
194
|
+
name_str =
|
|
195
|
+
if param_type == PARAM_TYPE_ARG && name.is_a?( Symbol )
|
|
196
|
+
%{"#{name_str}"}
|
|
197
|
+
else
|
|
198
|
+
name.inspect
|
|
199
|
+
end
|
|
200
|
+
|
|
201
|
+
"#{param_type} #{name.inspect}"
|
|
202
|
+
end
|
|
203
|
+
|
|
204
|
+
def not_nil( val, name = nil, param_type = PARAM_TYPE_ARG )
|
|
205
|
+
|
|
206
|
+
if val.nil?
|
|
207
|
+
if name
|
|
208
|
+
raise "#{check_fail_prefix( name, param_type )} cannot be nil"
|
|
209
|
+
else
|
|
210
|
+
raise "Value is nil"
|
|
211
|
+
end
|
|
212
|
+
else
|
|
213
|
+
val
|
|
214
|
+
end
|
|
215
|
+
end
|
|
216
|
+
|
|
217
|
+
def compares_to( val,
|
|
218
|
+
comp_target,
|
|
219
|
+
comp_type,
|
|
220
|
+
name = nil,
|
|
221
|
+
param_type = PARAM_TYPE_ARG )
|
|
222
|
+
|
|
223
|
+
if val.send( comp_type, comp_target )
|
|
224
|
+
val
|
|
225
|
+
else
|
|
226
|
+
raise "#{check_fail_prefix( name, param_type )}' is out of range " \
|
|
227
|
+
"(#{comp_target} #{comp_type} #{val})"
|
|
228
|
+
end
|
|
229
|
+
end
|
|
230
|
+
|
|
231
|
+
def nonnegative( val, name = nil, param_type = PARAM_TYPE_ARG )
|
|
232
|
+
compares_to( val, 0, :>=, name, param_type )
|
|
233
|
+
end
|
|
234
|
+
|
|
235
|
+
def positive( val, name = nil, param_type = PARAM_TYPE_ARG )
|
|
236
|
+
compares_to( val, 0, :>, name, param_type )
|
|
237
|
+
end
|
|
238
|
+
|
|
239
|
+
# changes "some-arg" to :some_arg
|
|
240
|
+
def ext_to_sym( str )
|
|
241
|
+
|
|
242
|
+
not_nil( str, "str" )
|
|
243
|
+
|
|
244
|
+
# do str.to_s in case it's some random string-ish thing (like a sym) but
|
|
245
|
+
# not an actual String
|
|
246
|
+
str.to_s.gsub( /-/, "_" ).to_sym
|
|
247
|
+
end
|
|
248
|
+
|
|
249
|
+
# Returns two arrays (both possibly empty) of stuff in argv before the first
|
|
250
|
+
# '--', if any, and stuff after, if any, including any subsequent
|
|
251
|
+
# appearances of '--'
|
|
252
|
+
def split_argv( argv )
|
|
253
|
+
|
|
254
|
+
not_nil( argv, :argv )
|
|
255
|
+
|
|
256
|
+
first, last = [], nil
|
|
257
|
+
|
|
258
|
+
argv.each do |arg|
|
|
259
|
+
|
|
260
|
+
if last then last << arg
|
|
261
|
+
else
|
|
262
|
+
if arg == "--" then last = []; else first << arg end
|
|
263
|
+
end
|
|
264
|
+
end
|
|
265
|
+
|
|
266
|
+
[ first, last ]
|
|
267
|
+
end
|
|
268
|
+
|
|
269
|
+
# changes "some-arg" to SomeArg (we can add in namespace qualifiers too if
|
|
270
|
+
# needed, for instance 'some-mod/some-other-mod/some-class' becomes
|
|
271
|
+
# SomeMod::SomeOtherMod::SomeClass)
|
|
272
|
+
def ext_to_class_name( str )
|
|
273
|
+
|
|
274
|
+
not_nil( str, "str" )
|
|
275
|
+
|
|
276
|
+
str = str.to_s
|
|
277
|
+
|
|
278
|
+
res = ""
|
|
279
|
+
|
|
280
|
+
if str.size > 0
|
|
281
|
+
res = str[ 0 ].upcase +
|
|
282
|
+
str[ 1 .. -1 ].gsub( /-(.)/ ) { |m| m[ 1 ].upcase }
|
|
283
|
+
end
|
|
284
|
+
|
|
285
|
+
res
|
|
286
|
+
end
|
|
287
|
+
|
|
288
|
+
# Changes SomeClass to :some_class
|
|
289
|
+
def class_name_to_sym( cls_name )
|
|
290
|
+
|
|
291
|
+
not_nil( cls_name, "cls_name" )
|
|
292
|
+
|
|
293
|
+
if cls_name.size == 0
|
|
294
|
+
raise "Name is empty string"
|
|
295
|
+
else
|
|
296
|
+
str = cls_name[ 0 ].downcase +
|
|
297
|
+
cls_name[ 1 .. -1 ].
|
|
298
|
+
gsub( /([A-Z])/ ) { |m| "_#{m.downcase}" }
|
|
299
|
+
|
|
300
|
+
str.to_sym
|
|
301
|
+
end
|
|
302
|
+
end
|
|
303
|
+
|
|
304
|
+
# Changes :some_sym to "some-sym"
|
|
305
|
+
def sym_to_ext_id( sym )
|
|
306
|
+
not_nil( sym, "sym" ).to_s.gsub( /_/, "-" )
|
|
307
|
+
end
|
|
308
|
+
|
|
309
|
+
# Changes :some_sym to --some-sym ('cli' == 'command line interface')
|
|
310
|
+
def sym_to_cli_switch( sym )
|
|
311
|
+
"--" + sym_to_ext_id( sym )
|
|
312
|
+
end
|
|
313
|
+
|
|
314
|
+
# helper method to do the sym-conversion part only (no validity checking
|
|
315
|
+
# done on either the sym or val)
|
|
316
|
+
def set_var( sym, val )
|
|
317
|
+
instance_variable_set( "@#{sym}".to_sym, val )
|
|
318
|
+
end
|
|
319
|
+
|
|
320
|
+
# It is assumed that map responds to []= and is not nil. The 'key' parameter
|
|
321
|
+
# may be nil. Returns the value if key is present and not nil
|
|
322
|
+
#
|
|
323
|
+
def has_key( map, key, param_typ = PARAM_TYPE_KEY )
|
|
324
|
+
not_nil( map[ key ], key, param_typ )
|
|
325
|
+
end
|
|
326
|
+
|
|
327
|
+
def has_keys( map, *keys )
|
|
328
|
+
keys.inject( [] ) { |arr, key| arr << has_key( map, key ) }
|
|
329
|
+
end
|
|
330
|
+
|
|
331
|
+
def has_env( key )
|
|
332
|
+
has_key( ENV, key, PARAM_TYPE_ENVVAR )
|
|
333
|
+
end
|
|
334
|
+
|
|
335
|
+
def set_from_key( map, *syms )
|
|
336
|
+
syms.each { |sym| set_var( sym, has_key( map, sym ) ) }
|
|
337
|
+
end
|
|
338
|
+
|
|
339
|
+
def unpack_argv_hash( argh, vars, expct = false )
|
|
340
|
+
|
|
341
|
+
vars.inject( [] ) do |arr, sym|
|
|
342
|
+
|
|
343
|
+
val = argh[ sym ]
|
|
344
|
+
|
|
345
|
+
if val != nil
|
|
346
|
+
arr << val
|
|
347
|
+
else
|
|
348
|
+
raise "Missing parameter: #{sym} in arg hash" if expct
|
|
349
|
+
end
|
|
350
|
+
|
|
351
|
+
arr
|
|
352
|
+
end
|
|
353
|
+
end
|
|
354
|
+
|
|
355
|
+
def unpack_argv_array( arr, vars, trace )
|
|
356
|
+
|
|
357
|
+
if arr.size == vars.size
|
|
358
|
+
arr
|
|
359
|
+
else
|
|
360
|
+
msg = "wrong number of arguments (#{arr.size} for #{vars.size})"
|
|
361
|
+
raise( Exception, msg, trace )
|
|
362
|
+
end
|
|
363
|
+
end
|
|
364
|
+
|
|
365
|
+
def argv_to_argh( argv, syms )
|
|
366
|
+
|
|
367
|
+
argv = unpack_argv_array( argv, syms, caller )
|
|
368
|
+
|
|
369
|
+
argh = {}
|
|
370
|
+
argv.size.times { |i| argh[ syms[ i ] ] = argv[ i ] }
|
|
371
|
+
|
|
372
|
+
argh
|
|
373
|
+
end
|
|
374
|
+
|
|
375
|
+
def to_bool( obj )
|
|
376
|
+
|
|
377
|
+
case obj
|
|
378
|
+
|
|
379
|
+
when true then true
|
|
380
|
+
when false then false
|
|
381
|
+
when nil then false
|
|
382
|
+
when /^\s*(true|yes)\s*$/i then true
|
|
383
|
+
when /^\s*(false|no)\s*$/i then false
|
|
384
|
+
else raise "Invalid boolean string: #{obj}"
|
|
385
|
+
end
|
|
386
|
+
end
|
|
387
|
+
|
|
388
|
+
def raisef( *argv )
|
|
389
|
+
|
|
390
|
+
raise if argv.empty?
|
|
391
|
+
|
|
392
|
+
cls = nil
|
|
393
|
+
|
|
394
|
+
case v = argv.first
|
|
395
|
+
when Exception then raise v
|
|
396
|
+
when Class then cls = argv.shift
|
|
397
|
+
end
|
|
398
|
+
|
|
399
|
+
argv2 = cls == nil ? [] : [ cls ]
|
|
400
|
+
argv2 << sprintf( *argv ) unless argv.empty?
|
|
401
|
+
|
|
402
|
+
raise *argv2
|
|
403
|
+
end
|
|
404
|
+
end
|
|
405
|
+
|
|
406
|
+
class BitGirderAttribute
|
|
407
|
+
|
|
408
|
+
include BitGirderMethods
|
|
409
|
+
|
|
410
|
+
class InvalidModifier < RuntimeError; end
|
|
411
|
+
|
|
412
|
+
PROCESSOR_BOOLEAN = lambda { |v| BitGirderMethods.to_bool( v ) }
|
|
413
|
+
|
|
414
|
+
PROCESSOR_SYMBOL = lambda { |v| v == nil ? nil : v.to_sym }
|
|
415
|
+
|
|
416
|
+
# If v is not nil we return calling to_i() on it; if it is nil we return it
|
|
417
|
+
# as-is since nil is used to communicate to the user of the attribute that
|
|
418
|
+
# no value whatsoever was provided (nil.to_i otherwise would return 0)
|
|
419
|
+
PROCESSOR_INTEGER = lambda { |v| v == nil ? nil : v.to_i }
|
|
420
|
+
|
|
421
|
+
# See PROCESSOR_INTEGER
|
|
422
|
+
PROCESSOR_FLOAT = lambda { |v| v == nil ? nil : v.to_f }
|
|
423
|
+
|
|
424
|
+
VALIDATION_NOT_NIL = lambda { |val| raise "Missing value" if val == nil }
|
|
425
|
+
|
|
426
|
+
# Implies not nil
|
|
427
|
+
VALIDATION_NOT_EMPTY = lambda { |val|
|
|
428
|
+
|
|
429
|
+
VALIDATION_NOT_NIL.call( val )
|
|
430
|
+
raise "Need at least one" if val.empty?
|
|
431
|
+
}
|
|
432
|
+
|
|
433
|
+
VALIDATION_NONNEGATIVE = lambda { |val| val >= 0 or raise "value < 0" }
|
|
434
|
+
|
|
435
|
+
VALIDATION_POSITIVE = lambda { |val|
|
|
436
|
+
|
|
437
|
+
VALIDATION_NOT_NIL.call( val )
|
|
438
|
+
val > 0 or raise "value <= 0"
|
|
439
|
+
}
|
|
440
|
+
|
|
441
|
+
VALIDATION_FILE_EXISTS = lambda { |val|
|
|
442
|
+
|
|
443
|
+
VALIDATION_NOT_NIL.call( val )
|
|
444
|
+
|
|
445
|
+
unless File.exist?( val )
|
|
446
|
+
raise "File or directory #{val} does not exist"
|
|
447
|
+
end
|
|
448
|
+
}
|
|
449
|
+
|
|
450
|
+
VALIDATION_OPT_FILE_EXISTS = lambda { |val|
|
|
451
|
+
val && VALIDATION_FILE_EXISTS.call( val )
|
|
452
|
+
}
|
|
453
|
+
|
|
454
|
+
ATTR_MODIFIERS = [
|
|
455
|
+
:identifier,
|
|
456
|
+
:default,
|
|
457
|
+
:description,
|
|
458
|
+
:validation,
|
|
459
|
+
:processor,
|
|
460
|
+
:is_list,
|
|
461
|
+
:list_validation,
|
|
462
|
+
:required,
|
|
463
|
+
:mutable
|
|
464
|
+
]
|
|
465
|
+
|
|
466
|
+
attr_reader *ATTR_MODIFIERS
|
|
467
|
+
|
|
468
|
+
# Rather than have a class def silently fail on a mis-typed attribute
|
|
469
|
+
# modifier (":processr" instead of ":processor") we explicitly check and
|
|
470
|
+
# fail if any of the supplied mods are not one expected by this class
|
|
471
|
+
private
|
|
472
|
+
def check_valid_modifiers( supplied )
|
|
473
|
+
|
|
474
|
+
supplied.each do |key|
|
|
475
|
+
ATTR_MODIFIERS.include?( key ) or
|
|
476
|
+
raise InvalidModifier.new(
|
|
477
|
+
"Invalid attribute modifier: #{key.inspect}" )
|
|
478
|
+
end
|
|
479
|
+
end
|
|
480
|
+
|
|
481
|
+
private
|
|
482
|
+
def get_processor( p )
|
|
483
|
+
|
|
484
|
+
case p
|
|
485
|
+
when :boolean then PROCESSOR_BOOLEAN
|
|
486
|
+
when :symbol then PROCESSOR_SYMBOL
|
|
487
|
+
when :integer then PROCESSOR_INTEGER
|
|
488
|
+
when :float then PROCESSOR_FLOAT
|
|
489
|
+
|
|
490
|
+
when Class
|
|
491
|
+
if p.ancestors.include?( BitGirderClass )
|
|
492
|
+
lambda { |o| p.as_instance( o ) }
|
|
493
|
+
else
|
|
494
|
+
raise ArgumentError, "Not a #{BitGirderClass}: #{p}"
|
|
495
|
+
end
|
|
496
|
+
|
|
497
|
+
else p
|
|
498
|
+
end
|
|
499
|
+
end
|
|
500
|
+
|
|
501
|
+
private
|
|
502
|
+
def get_validation( v )
|
|
503
|
+
|
|
504
|
+
case v
|
|
505
|
+
when :not_nil then VALIDATION_NOT_NIL
|
|
506
|
+
when :nonnegative then VALIDATION_NONNEGATIVE
|
|
507
|
+
when :positive then VALIDATION_POSITIVE
|
|
508
|
+
when :file_exists then VALIDATION_FILE_EXISTS
|
|
509
|
+
when :not_empty then VALIDATION_NOT_EMPTY
|
|
510
|
+
when :opt_file_exists then VALIDATION_OPT_FILE_EXISTS
|
|
511
|
+
else v
|
|
512
|
+
end
|
|
513
|
+
end
|
|
514
|
+
|
|
515
|
+
public
|
|
516
|
+
def initialize( argh )
|
|
517
|
+
|
|
518
|
+
check_valid_modifiers( argh.keys )
|
|
519
|
+
|
|
520
|
+
set_from_key( argh, :identifier )
|
|
521
|
+
|
|
522
|
+
[ :default, :description, :validation,
|
|
523
|
+
:is_list, :list_validation, :required, :mutable ].each do |sym|
|
|
524
|
+
set_var( sym, argh[ sym ] )
|
|
525
|
+
end
|
|
526
|
+
|
|
527
|
+
@validation = get_validation( @validation )
|
|
528
|
+
@list_validation = get_validation( @list_validation )
|
|
529
|
+
|
|
530
|
+
@processor = get_processor( argh[ :processor ] )
|
|
531
|
+
|
|
532
|
+
@id_sym = :"@#@identifier"
|
|
533
|
+
end
|
|
534
|
+
|
|
535
|
+
public
|
|
536
|
+
def get_instance_value( inst )
|
|
537
|
+
inst.instance_variable_get( @id_sym )
|
|
538
|
+
end
|
|
539
|
+
end
|
|
540
|
+
|
|
541
|
+
class BitGirderClassDefinition
|
|
542
|
+
|
|
543
|
+
extend BitGirderMethods
|
|
544
|
+
include BitGirderMethods
|
|
545
|
+
|
|
546
|
+
REQUIRED_ATTRS =
|
|
547
|
+
[ :cls, :attrs, :attr_syms, :decl_order, :instance_mappers ]
|
|
548
|
+
|
|
549
|
+
attr_reader *REQUIRED_ATTRS
|
|
550
|
+
|
|
551
|
+
def initialize( opts )
|
|
552
|
+
|
|
553
|
+
REQUIRED_ATTRS.each do |attr|
|
|
554
|
+
|
|
555
|
+
val = BitGirderMethods.has_key( opts, attr )
|
|
556
|
+
instance_variable_set( :"@#{attr}", val )
|
|
557
|
+
end
|
|
558
|
+
end
|
|
559
|
+
|
|
560
|
+
public
|
|
561
|
+
def add_attr( opts )
|
|
562
|
+
|
|
563
|
+
unless opts.key?( :required ) || opts.key?( :default )
|
|
564
|
+
opts[ :required ] = true
|
|
565
|
+
end
|
|
566
|
+
|
|
567
|
+
attr = BitGirderAttribute.new( opts )
|
|
568
|
+
|
|
569
|
+
if @attrs.key?( attr.identifier )
|
|
570
|
+
raise "Attribute #{attr.identifier.inspect} already defined"
|
|
571
|
+
else
|
|
572
|
+
ident = attr.identifier
|
|
573
|
+
@attrs[ ident ] = attr
|
|
574
|
+
@attr_syms << "@#{ident}".to_sym
|
|
575
|
+
@decl_order << ident
|
|
576
|
+
@cls.send( attr.mutable ? :attr_accessor : :attr_reader, ident )
|
|
577
|
+
end
|
|
578
|
+
end
|
|
579
|
+
|
|
580
|
+
private
|
|
581
|
+
def get_supplied_attr_value( hash, ident )
|
|
582
|
+
hash[ ident ] || hash[ ident.to_s ]
|
|
583
|
+
end
|
|
584
|
+
|
|
585
|
+
private
|
|
586
|
+
def get_default_val( attr )
|
|
587
|
+
|
|
588
|
+
case d = attr.default
|
|
589
|
+
when Proc then d.call
|
|
590
|
+
when Class then d.new
|
|
591
|
+
when Array, Hash then d.clone
|
|
592
|
+
else d == nil && attr.is_list ? [] : d
|
|
593
|
+
end
|
|
594
|
+
end
|
|
595
|
+
|
|
596
|
+
private
|
|
597
|
+
def apply_processor( attr, val )
|
|
598
|
+
|
|
599
|
+
if p = attr.processor
|
|
600
|
+
if attr.is_list
|
|
601
|
+
val = val.map { |elt| p.call( elt ) }
|
|
602
|
+
else
|
|
603
|
+
val = p.call( val )
|
|
604
|
+
end
|
|
605
|
+
else
|
|
606
|
+
val
|
|
607
|
+
end
|
|
608
|
+
end
|
|
609
|
+
|
|
610
|
+
private
|
|
611
|
+
def get_initial_value( hash, ident, attr )
|
|
612
|
+
|
|
613
|
+
# Get the val as the caller-supplied one, which we allow to be either
|
|
614
|
+
# the symbol or string form, supplying the default as needed; note that
|
|
615
|
+
# we use key membership and explicit tests for nil instead of boolean
|
|
616
|
+
# operators since we want to allow the value 'false' from the caller or
|
|
617
|
+
# as a default
|
|
618
|
+
val = hash.key?( ident ) ? hash[ ident ] : hash[ ident.to_s ]
|
|
619
|
+
|
|
620
|
+
if val == nil
|
|
621
|
+
val = get_default_val( attr ) # could still end up being nil
|
|
622
|
+
else
|
|
623
|
+
val = apply_processor( attr, val )
|
|
624
|
+
end
|
|
625
|
+
|
|
626
|
+
val
|
|
627
|
+
end
|
|
628
|
+
|
|
629
|
+
private
|
|
630
|
+
def validate_list_attr_val( attr, val )
|
|
631
|
+
|
|
632
|
+
if list_func = attr.list_validation
|
|
633
|
+
list_func.call( val )
|
|
634
|
+
end
|
|
635
|
+
|
|
636
|
+
if func = attr.validation
|
|
637
|
+
val.each { |v| func.call( v ) }
|
|
638
|
+
end
|
|
639
|
+
end
|
|
640
|
+
|
|
641
|
+
private
|
|
642
|
+
def validate_scalar_attr_val( attr, val )
|
|
643
|
+
|
|
644
|
+
if ( func = attr.validation ) && ( attr.required || ( ! val.nil? ) )
|
|
645
|
+
func.call( val )
|
|
646
|
+
end
|
|
647
|
+
end
|
|
648
|
+
|
|
649
|
+
private
|
|
650
|
+
def validate_attr_val( attr, val, trace, listener )
|
|
651
|
+
|
|
652
|
+
begin
|
|
653
|
+
|
|
654
|
+
if attr.is_list || attr.list_validation
|
|
655
|
+
validate_list_attr_val( attr, val )
|
|
656
|
+
else
|
|
657
|
+
validate_scalar_attr_val( attr, val )
|
|
658
|
+
end
|
|
659
|
+
|
|
660
|
+
val
|
|
661
|
+
|
|
662
|
+
rescue Exception => e
|
|
663
|
+
|
|
664
|
+
listener.call( attr, e )
|
|
665
|
+
raise e, "#{attr.identifier}: #{e.message}", trace
|
|
666
|
+
end
|
|
667
|
+
end
|
|
668
|
+
|
|
669
|
+
private
|
|
670
|
+
def set_attr_value( inst, hash, ident, attr, trace, listener )
|
|
671
|
+
|
|
672
|
+
val = get_initial_value( hash, ident, attr )
|
|
673
|
+
|
|
674
|
+
validate_attr_val( attr, val, trace, listener )
|
|
675
|
+
|
|
676
|
+
inst.instance_variable_set( :"@#{attr.identifier}", val )
|
|
677
|
+
end
|
|
678
|
+
|
|
679
|
+
private
|
|
680
|
+
def impl_initialize( inst )
|
|
681
|
+
|
|
682
|
+
if inst.respond_to?( meth = :impl_initialize, true )
|
|
683
|
+
inst.send( meth )
|
|
684
|
+
end
|
|
685
|
+
end
|
|
686
|
+
|
|
687
|
+
public
|
|
688
|
+
def init_instance( inst, argv )
|
|
689
|
+
|
|
690
|
+
argh =
|
|
691
|
+
if argv.size == 0
|
|
692
|
+
{}
|
|
693
|
+
else
|
|
694
|
+
if argv.size == 1 and ( arg = argv[ 0 ] ).is_a?( Hash )
|
|
695
|
+
arg
|
|
696
|
+
else
|
|
697
|
+
argv_to_argh( argv, @decl_order )
|
|
698
|
+
end
|
|
699
|
+
end
|
|
700
|
+
|
|
701
|
+
trace = caller
|
|
702
|
+
listener = BitGirderClassDefinition.get_validation_listener
|
|
703
|
+
|
|
704
|
+
@attrs.each_pair { |ident, attr|
|
|
705
|
+
set_attr_value( inst, argh, ident, attr, trace, listener )
|
|
706
|
+
}
|
|
707
|
+
|
|
708
|
+
impl_initialize( inst )
|
|
709
|
+
end
|
|
710
|
+
|
|
711
|
+
public
|
|
712
|
+
def hash_instance( inst )
|
|
713
|
+
@decl_order.map { |id| @attrs[ id ].get_instance_value( inst ) }.hash
|
|
714
|
+
end
|
|
715
|
+
|
|
716
|
+
@@class_defs = {}
|
|
717
|
+
@@validation_listener = nil
|
|
718
|
+
|
|
719
|
+
def self.get_class_defs
|
|
720
|
+
@@class_defs.dup
|
|
721
|
+
end
|
|
722
|
+
|
|
723
|
+
def self.get_validation_listener
|
|
724
|
+
|
|
725
|
+
if res = @@validation_listener
|
|
726
|
+
@@validation_listener = nil
|
|
727
|
+
res
|
|
728
|
+
else
|
|
729
|
+
lambda { |attr, e| } # no-op is default
|
|
730
|
+
end
|
|
731
|
+
end
|
|
732
|
+
|
|
733
|
+
def self.validation_listener=( l )
|
|
734
|
+
|
|
735
|
+
BitGirderMethods.not_nil( l, :l )
|
|
736
|
+
raise "A validation listener is already set" if @@validation_listener
|
|
737
|
+
|
|
738
|
+
@@validation_listener = l
|
|
739
|
+
end
|
|
740
|
+
|
|
741
|
+
def self.default_instance_mappers_for( cls )
|
|
742
|
+
|
|
743
|
+
[
|
|
744
|
+
InstanceMapper.new(
|
|
745
|
+
:processor => lambda { |val| val.is_a?( cls ) && val } ),
|
|
746
|
+
|
|
747
|
+
InstanceMapper.new(
|
|
748
|
+
:processor => lambda { |val|
|
|
749
|
+
# Use send in case cls made it private
|
|
750
|
+
val.is_a?( Hash ) && cls.send( :new, val )
|
|
751
|
+
}
|
|
752
|
+
)
|
|
753
|
+
]
|
|
754
|
+
end
|
|
755
|
+
|
|
756
|
+
def self.for_class( cls )
|
|
757
|
+
|
|
758
|
+
unless res = @@class_defs[ cls ]
|
|
759
|
+
|
|
760
|
+
attrs, decl_order =
|
|
761
|
+
if cls == BitGirderClass || cls == BitGirderError
|
|
762
|
+
[ {}, [] ]
|
|
763
|
+
else
|
|
764
|
+
cd = self.for_class( cls.superclass )
|
|
765
|
+
[ Hash.new.merge( cd.attrs ), Array.new( cd.decl_order ) ]
|
|
766
|
+
end
|
|
767
|
+
|
|
768
|
+
@@class_defs[ cls ] = res = new(
|
|
769
|
+
:cls => cls,
|
|
770
|
+
:attrs => attrs,
|
|
771
|
+
:attr_syms => attrs.keys.map { |id| "@#{id}".to_sym },
|
|
772
|
+
:decl_order => decl_order,
|
|
773
|
+
:instance_mappers => self.default_instance_mappers_for( cls )
|
|
774
|
+
)
|
|
775
|
+
end
|
|
776
|
+
|
|
777
|
+
res
|
|
778
|
+
end
|
|
779
|
+
|
|
780
|
+
def self.code( *argv )
|
|
781
|
+
BitGirderLogger.code( *argv )
|
|
782
|
+
end
|
|
783
|
+
|
|
784
|
+
def self.init_instance( inst, argv )
|
|
785
|
+
self.for_class( inst.class ).init_instance( inst, argv )
|
|
786
|
+
end
|
|
787
|
+
end
|
|
788
|
+
|
|
789
|
+
class InstanceMapper
|
|
790
|
+
|
|
791
|
+
include BitGirderMethods
|
|
792
|
+
|
|
793
|
+
attr_reader :processor
|
|
794
|
+
|
|
795
|
+
# Does nil and other checks for public frontends
|
|
796
|
+
def initialize( opts )
|
|
797
|
+
|
|
798
|
+
not_nil( opts, :opts )
|
|
799
|
+
@processor = has_key( opts, :processor )
|
|
800
|
+
end
|
|
801
|
+
end
|
|
802
|
+
|
|
803
|
+
module BitGirderStructure
|
|
804
|
+
|
|
805
|
+
private
|
|
806
|
+
def impl_initialize; end
|
|
807
|
+
|
|
808
|
+
def initialize( *argv )
|
|
809
|
+
BitGirderClassDefinition.init_instance( self, argv )
|
|
810
|
+
end
|
|
811
|
+
|
|
812
|
+
public
|
|
813
|
+
def ==( other )
|
|
814
|
+
|
|
815
|
+
return true if self.equal?( other )
|
|
816
|
+
return false unless other.class == self.class
|
|
817
|
+
|
|
818
|
+
! self.class.get_class_def.attr_syms.find do |sym|
|
|
819
|
+
|
|
820
|
+
other_val = other.instance_variable_get( sym )
|
|
821
|
+
self_val = instance_variable_get( sym )
|
|
822
|
+
|
|
823
|
+
other_val != self_val
|
|
824
|
+
end
|
|
825
|
+
end
|
|
826
|
+
|
|
827
|
+
alias eql? ==
|
|
828
|
+
|
|
829
|
+
def self.included( cls )
|
|
830
|
+
|
|
831
|
+
cls.class_eval do
|
|
832
|
+
|
|
833
|
+
def self.get_class_def
|
|
834
|
+
BitGirderClassDefinition.for_class( self )
|
|
835
|
+
end
|
|
836
|
+
|
|
837
|
+
def self.bg_attr( id, opts = {} )
|
|
838
|
+
|
|
839
|
+
argh = {
|
|
840
|
+
:identifier => id,
|
|
841
|
+
:validation => :not_nil
|
|
842
|
+
}.
|
|
843
|
+
merge( opts )
|
|
844
|
+
|
|
845
|
+
self.get_class_def.add_attr( argh )
|
|
846
|
+
end
|
|
847
|
+
|
|
848
|
+
def self.map_instances( opts )
|
|
849
|
+
|
|
850
|
+
im = InstanceMapper.new( opts ) # Validation in InstanceMapper
|
|
851
|
+
self.get_class_def.instance_mappers.unshift( im )
|
|
852
|
+
end
|
|
853
|
+
|
|
854
|
+
def self.map_instance_of( *classes, &blk )
|
|
855
|
+
|
|
856
|
+
self.map_instances(
|
|
857
|
+
:processor => lambda { |val|
|
|
858
|
+
classes.find { |cls| val.is_a?( cls ) } &&
|
|
859
|
+
blk.call( val )
|
|
860
|
+
}
|
|
861
|
+
)
|
|
862
|
+
end
|
|
863
|
+
|
|
864
|
+
def self.bg_abstract( meth )
|
|
865
|
+
|
|
866
|
+
define_method( meth ) do
|
|
867
|
+
raise "Abstract method #{self.class}##{meth} " \
|
|
868
|
+
"not implemented"
|
|
869
|
+
end
|
|
870
|
+
end
|
|
871
|
+
|
|
872
|
+
def self.as_instance( val )
|
|
873
|
+
|
|
874
|
+
return val if val.is_a?( self )
|
|
875
|
+
|
|
876
|
+
BitGirderMethods.not_nil( val, :val )
|
|
877
|
+
|
|
878
|
+
cd = self.get_class_def
|
|
879
|
+
|
|
880
|
+
cd.instance_mappers.each do |im|
|
|
881
|
+
( res = im.processor.call( val ) ) && ( return res )
|
|
882
|
+
end
|
|
883
|
+
|
|
884
|
+
raise TypeError,
|
|
885
|
+
"Don't know how to convert #{val.class} to #{self}"
|
|
886
|
+
end
|
|
887
|
+
|
|
888
|
+
def self.install_hash
|
|
889
|
+
|
|
890
|
+
cd = self.get_class_def
|
|
891
|
+
define_method( :hash ) { cd.hash_instance( self ) }
|
|
892
|
+
end
|
|
893
|
+
end
|
|
894
|
+
end
|
|
895
|
+
end
|
|
896
|
+
|
|
897
|
+
class BitGirderClass
|
|
898
|
+
|
|
899
|
+
include BitGirderMethods
|
|
900
|
+
extend BitGirderMethods
|
|
901
|
+
|
|
902
|
+
include BitGirderStructure
|
|
903
|
+
end
|
|
904
|
+
|
|
905
|
+
class BitGirderError < StandardError
|
|
906
|
+
|
|
907
|
+
include BitGirderMethods
|
|
908
|
+
include BitGirderStructure
|
|
909
|
+
end
|
|
910
|
+
|
|
911
|
+
class BitGirderCliApplication < BitGirderClass
|
|
912
|
+
|
|
913
|
+
require 'optparse'
|
|
914
|
+
|
|
915
|
+
bg_attr :app_class,
|
|
916
|
+
:description => "Class object to be run as application"
|
|
917
|
+
|
|
918
|
+
bg_attr :main_method,
|
|
919
|
+
:description => "Entry method to application",
|
|
920
|
+
:default => :run,
|
|
921
|
+
:processor => lambda { |str| BitGirderMethods::ext_to_sym( str ) }
|
|
922
|
+
|
|
923
|
+
def initialize( opts = {} )
|
|
924
|
+
|
|
925
|
+
super( opts )
|
|
926
|
+
raise "No such method: #@main_method" unless respond_to?( @main_method )
|
|
927
|
+
|
|
928
|
+
@verbose = BitGirderLogger.is_debug_env_set?
|
|
929
|
+
end
|
|
930
|
+
|
|
931
|
+
private
|
|
932
|
+
def create_base_parser
|
|
933
|
+
|
|
934
|
+
p = OptionParser.new
|
|
935
|
+
|
|
936
|
+
p.on( "-v", "--[no-]verbose", "Run verbosely" ) { |flag|
|
|
937
|
+
|
|
938
|
+
@verbose = flag
|
|
939
|
+
|
|
940
|
+
BitGirderLogger.get_logger.level = to_bool( flag ) ?
|
|
941
|
+
BitGirderLogger::CODE : BitGirderLogger::DEFAULT
|
|
942
|
+
}
|
|
943
|
+
|
|
944
|
+
p.on( "-h", "--help", "Print this help" ) {
|
|
945
|
+
puts p
|
|
946
|
+
exit 0
|
|
947
|
+
}
|
|
948
|
+
end
|
|
949
|
+
|
|
950
|
+
private
|
|
951
|
+
def default_to_s( defl )
|
|
952
|
+
|
|
953
|
+
case defl
|
|
954
|
+
when STDOUT then "stdout"
|
|
955
|
+
when STDIN then "stdin"
|
|
956
|
+
when STDERR then "stderr"
|
|
957
|
+
else defl.to_s
|
|
958
|
+
end
|
|
959
|
+
end
|
|
960
|
+
|
|
961
|
+
private
|
|
962
|
+
def get_opt_parser_argv( attr )
|
|
963
|
+
|
|
964
|
+
argv = []
|
|
965
|
+
|
|
966
|
+
switch_str = attr.identifier.to_s.gsub( /_/, "-" )
|
|
967
|
+
|
|
968
|
+
if attr.processor == BitGirderAttribute::PROCESSOR_BOOLEAN
|
|
969
|
+
argv << "--[no-]#{switch_str}"
|
|
970
|
+
else
|
|
971
|
+
argv << "--#{switch_str} VAL"
|
|
972
|
+
end
|
|
973
|
+
|
|
974
|
+
if desc = attr.description
|
|
975
|
+
|
|
976
|
+
# attr.default could be the boolean false, which we still want to
|
|
977
|
+
# display
|
|
978
|
+
unless ( defl = attr.default ) == nil
|
|
979
|
+
desc += " (Default: #{default_to_s( defl )})"
|
|
980
|
+
end
|
|
981
|
+
|
|
982
|
+
argv << desc
|
|
983
|
+
end
|
|
984
|
+
|
|
985
|
+
argv
|
|
986
|
+
end
|
|
987
|
+
|
|
988
|
+
private
|
|
989
|
+
def parse_arg( argh )
|
|
990
|
+
|
|
991
|
+
attr = argh[ :attr ]
|
|
992
|
+
ident = attr.identifier
|
|
993
|
+
|
|
994
|
+
prev = argh[ :argh ][ ident ] || attr.default # could be nil either way
|
|
995
|
+
|
|
996
|
+
val = argh[ :arg ]
|
|
997
|
+
# val = attr.processor.call( val ) if attr.processor
|
|
998
|
+
|
|
999
|
+
if attr.is_list
|
|
1000
|
+
if prev == nil
|
|
1001
|
+
val = [ val ]
|
|
1002
|
+
else
|
|
1003
|
+
val = ( prev << val )
|
|
1004
|
+
end
|
|
1005
|
+
end
|
|
1006
|
+
|
|
1007
|
+
argh[ :argh ][ ident ] = val
|
|
1008
|
+
end
|
|
1009
|
+
|
|
1010
|
+
private
|
|
1011
|
+
def configure_parser
|
|
1012
|
+
|
|
1013
|
+
p = create_base_parser
|
|
1014
|
+
argh = {}
|
|
1015
|
+
|
|
1016
|
+
cd = BitGirderClassDefinition.for_class( @app_class )
|
|
1017
|
+
( cd.attrs.keys - [ :main_method, :app_class ] ).each do |ident|
|
|
1018
|
+
|
|
1019
|
+
attr = cd.attrs[ ident ]
|
|
1020
|
+
argv = get_opt_parser_argv( attr )
|
|
1021
|
+
|
|
1022
|
+
p.on( *argv ) { |arg|
|
|
1023
|
+
parse_arg( :arg => arg, :argh => argh, :attr => attr )
|
|
1024
|
+
}
|
|
1025
|
+
end
|
|
1026
|
+
|
|
1027
|
+
[ p, argh ]
|
|
1028
|
+
end
|
|
1029
|
+
|
|
1030
|
+
private
|
|
1031
|
+
def create_app_obj( argh )
|
|
1032
|
+
|
|
1033
|
+
BitGirderClassDefinition.validation_listener =
|
|
1034
|
+
lambda { |attr, e|
|
|
1035
|
+
|
|
1036
|
+
warn( e, "Validation of attr #{attr} failed" ) if @verbose
|
|
1037
|
+
cli_opt = sym_to_cli_switch( attr.identifier )
|
|
1038
|
+
raise "#{cli_opt}: #{e.message}"
|
|
1039
|
+
}
|
|
1040
|
+
|
|
1041
|
+
@app_class.new( argh )
|
|
1042
|
+
end
|
|
1043
|
+
|
|
1044
|
+
private
|
|
1045
|
+
def make_run_ctx( argv_remain )
|
|
1046
|
+
|
|
1047
|
+
{ :argv_remain => argv_remain, :verbose => @verbose }
|
|
1048
|
+
end
|
|
1049
|
+
|
|
1050
|
+
private
|
|
1051
|
+
def fail_run_main( e )
|
|
1052
|
+
|
|
1053
|
+
log = BitGirderLogger.get_logger
|
|
1054
|
+
|
|
1055
|
+
if @verbose
|
|
1056
|
+
log.warn( e, "App failed" )
|
|
1057
|
+
else
|
|
1058
|
+
STDERR.puts( e.message )
|
|
1059
|
+
end
|
|
1060
|
+
|
|
1061
|
+
exit EXIT_FAILURE
|
|
1062
|
+
end
|
|
1063
|
+
|
|
1064
|
+
private
|
|
1065
|
+
def run_main( app_obj, argv_remain )
|
|
1066
|
+
|
|
1067
|
+
meth = app_obj.class.instance_method( @main_method )
|
|
1068
|
+
|
|
1069
|
+
args =
|
|
1070
|
+
case arity = meth.arity
|
|
1071
|
+
when 0 then []
|
|
1072
|
+
when 1 then [ make_run_ctx( argv_remain ) ]
|
|
1073
|
+
else raise "Invalid arity for #{meth}: #{arity}"
|
|
1074
|
+
end
|
|
1075
|
+
|
|
1076
|
+
begin
|
|
1077
|
+
meth.bind( app_obj ).call( *args )
|
|
1078
|
+
rescue Exception => e
|
|
1079
|
+
fail_run_main( e )
|
|
1080
|
+
end
|
|
1081
|
+
end
|
|
1082
|
+
|
|
1083
|
+
public
|
|
1084
|
+
def run( argv = ARGV )
|
|
1085
|
+
|
|
1086
|
+
p, argh = configure_parser
|
|
1087
|
+
|
|
1088
|
+
begin
|
|
1089
|
+
p.parse!( argv = argv.dup )
|
|
1090
|
+
app_obj = create_app_obj( argh )
|
|
1091
|
+
rescue SystemExit => e
|
|
1092
|
+
skip_main = true
|
|
1093
|
+
rescue Exception => e
|
|
1094
|
+
|
|
1095
|
+
puts e
|
|
1096
|
+
puts p.to_s
|
|
1097
|
+
|
|
1098
|
+
if @verbose || BitGirderLogger.is_debug_env_set?
|
|
1099
|
+
puts e.backtrace.join( "\n" )
|
|
1100
|
+
end
|
|
1101
|
+
|
|
1102
|
+
exit EXIT_FAILURE
|
|
1103
|
+
end
|
|
1104
|
+
|
|
1105
|
+
run_main( app_obj, argv ) unless skip_main
|
|
1106
|
+
end
|
|
1107
|
+
|
|
1108
|
+
def self.run( cls )
|
|
1109
|
+
|
|
1110
|
+
BitGirderMethods::not_nil( cls, :cls )
|
|
1111
|
+
self.new( :app_class => cls ).run( ARGV )
|
|
1112
|
+
end
|
|
1113
|
+
end
|
|
1114
|
+
|
|
1115
|
+
class ObjectPath
|
|
1116
|
+
|
|
1117
|
+
include BitGirderMethods
|
|
1118
|
+
extend BitGirderMethods
|
|
1119
|
+
|
|
1120
|
+
attr_reader :parent
|
|
1121
|
+
|
|
1122
|
+
# Not to be called by public methods; all paths should be derived from a
|
|
1123
|
+
# parent path or initialized with get_root or get_root_list
|
|
1124
|
+
private_class_method :new
|
|
1125
|
+
def initialize( parent )
|
|
1126
|
+
@parent = parent # parent nil ==> this is a root path
|
|
1127
|
+
end
|
|
1128
|
+
|
|
1129
|
+
def descend( elt )
|
|
1130
|
+
DictionaryPath.send( :new, self, not_nil( elt, "elt" ) )
|
|
1131
|
+
end
|
|
1132
|
+
|
|
1133
|
+
def start_list
|
|
1134
|
+
ListPath.send( :new, self, 0 )
|
|
1135
|
+
end
|
|
1136
|
+
|
|
1137
|
+
# Returns a path starting with the specified root element
|
|
1138
|
+
def self.get_root( root_elt )
|
|
1139
|
+
|
|
1140
|
+
not_nil( root_elt, :root_elt )
|
|
1141
|
+
DictionaryPath.send( :new, nil, root_elt )
|
|
1142
|
+
end
|
|
1143
|
+
|
|
1144
|
+
def self.get_root_list
|
|
1145
|
+
ListPath.send( :new, nil, 0 )
|
|
1146
|
+
end
|
|
1147
|
+
|
|
1148
|
+
private
|
|
1149
|
+
def collect_path
|
|
1150
|
+
|
|
1151
|
+
res = [ node = self ]
|
|
1152
|
+
|
|
1153
|
+
# This loop is correct on the assumption that the caller obtained this
|
|
1154
|
+
# instance by the accepted means
|
|
1155
|
+
while node = node.parent
|
|
1156
|
+
res.unshift( node )
|
|
1157
|
+
end
|
|
1158
|
+
|
|
1159
|
+
res
|
|
1160
|
+
end
|
|
1161
|
+
|
|
1162
|
+
public
|
|
1163
|
+
def format( formatter = DefaultObjectPathFormatter.new )
|
|
1164
|
+
|
|
1165
|
+
not_nil( formatter, "formatter" )
|
|
1166
|
+
|
|
1167
|
+
formatter.format_path_start( str = "" )
|
|
1168
|
+
|
|
1169
|
+
collect_path.each_with_index do |elt, idx|
|
|
1170
|
+
|
|
1171
|
+
case elt
|
|
1172
|
+
|
|
1173
|
+
when DictionaryPath
|
|
1174
|
+
formatter.format_separator( str ) unless idx == 0
|
|
1175
|
+
formatter.format_key( str, elt.key )
|
|
1176
|
+
|
|
1177
|
+
when ListPath then formatter.format_list_index( str, elt.index )
|
|
1178
|
+
|
|
1179
|
+
else raise "Unexpected path type: #{elt.class}"
|
|
1180
|
+
end
|
|
1181
|
+
end
|
|
1182
|
+
|
|
1183
|
+
str
|
|
1184
|
+
end
|
|
1185
|
+
end
|
|
1186
|
+
|
|
1187
|
+
class DictionaryPath < ObjectPath
|
|
1188
|
+
|
|
1189
|
+
attr_reader :key
|
|
1190
|
+
|
|
1191
|
+
def initialize( parent, key )
|
|
1192
|
+
|
|
1193
|
+
super( parent )
|
|
1194
|
+
@key = not_nil( key, "key" )
|
|
1195
|
+
end
|
|
1196
|
+
end
|
|
1197
|
+
|
|
1198
|
+
class ListPath < ObjectPath
|
|
1199
|
+
|
|
1200
|
+
attr_reader :index
|
|
1201
|
+
|
|
1202
|
+
def initialize( parent, index )
|
|
1203
|
+
|
|
1204
|
+
super( parent )
|
|
1205
|
+
@index = nonnegative( index, "index" )
|
|
1206
|
+
end
|
|
1207
|
+
|
|
1208
|
+
def next
|
|
1209
|
+
ListPath.send( :new, self.parent, @index + 1 )
|
|
1210
|
+
end
|
|
1211
|
+
end
|
|
1212
|
+
|
|
1213
|
+
# Subclasses can override methods to customize as desired
|
|
1214
|
+
class DefaultObjectPathFormatter
|
|
1215
|
+
|
|
1216
|
+
public; def format_path_start( str ); end
|
|
1217
|
+
|
|
1218
|
+
public
|
|
1219
|
+
def format_separator( str )
|
|
1220
|
+
str << "."
|
|
1221
|
+
end
|
|
1222
|
+
|
|
1223
|
+
public
|
|
1224
|
+
def format_key( str, key )
|
|
1225
|
+
str << key.to_s
|
|
1226
|
+
end
|
|
1227
|
+
|
|
1228
|
+
public
|
|
1229
|
+
def format_list_index( str, indx )
|
|
1230
|
+
str << "[ #{indx} ]"
|
|
1231
|
+
end
|
|
1232
|
+
end
|
|
1233
|
+
|
|
1234
|
+
end
|
|
1235
|
+
end
|