shellopts 2.0.0.pre.14 → 2.0.2
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
- data/.gitignore +2 -1
- data/.ruby-version +1 -1
- data/README.md +201 -267
- data/TODO +37 -5
- data/doc/format.rb +95 -0
- data/doc/grammar.txt +27 -0
- data/doc/syntax.rb +110 -0
- data/doc/syntax.txt +10 -0
- data/lib/ext/array.rb +62 -0
- data/lib/ext/forward_to.rb +15 -0
- data/lib/ext/lcs.rb +34 -0
- data/lib/shellopts/analyzer.rb +130 -0
- data/lib/shellopts/ansi.rb +8 -0
- data/lib/shellopts/args.rb +29 -21
- data/lib/shellopts/argument_type.rb +139 -0
- data/lib/shellopts/dump.rb +158 -0
- data/lib/shellopts/formatter.rb +292 -92
- data/lib/shellopts/grammar.rb +375 -0
- data/lib/shellopts/interpreter.rb +103 -0
- data/lib/shellopts/lexer.rb +175 -0
- data/lib/shellopts/parser.rb +293 -0
- data/lib/shellopts/program.rb +279 -0
- data/lib/shellopts/renderer.rb +227 -0
- data/lib/shellopts/stack.rb +7 -0
- data/lib/shellopts/token.rb +44 -0
- data/lib/shellopts/version.rb +1 -1
- data/lib/shellopts.rb +360 -3
- data/main +1180 -0
- data/shellopts.gemspec +8 -14
- metadata +86 -41
- data/lib/ext/algorithm.rb +0 -14
- data/lib/ext/ruby_env.rb +0 -8
- data/lib/shellopts/ast/command.rb +0 -112
- data/lib/shellopts/ast/dump.rb +0 -28
- data/lib/shellopts/ast/option.rb +0 -15
- data/lib/shellopts/ast/parser.rb +0 -106
- data/lib/shellopts/constants.rb +0 -88
- data/lib/shellopts/exceptions.rb +0 -21
- data/lib/shellopts/grammar/analyzer.rb +0 -76
- data/lib/shellopts/grammar/command.rb +0 -87
- data/lib/shellopts/grammar/dump.rb +0 -56
- data/lib/shellopts/grammar/lexer.rb +0 -56
- data/lib/shellopts/grammar/option.rb +0 -55
- data/lib/shellopts/grammar/parser.rb +0 -78
data/main
ADDED
@@ -0,0 +1,1180 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require 'bundler'
|
4
|
+
Bundler.setup
|
5
|
+
|
6
|
+
require 'shellopts'
|
7
|
+
|
8
|
+
include ShellOpts
|
9
|
+
|
10
|
+
|
11
|
+
SPEC = %(
|
12
|
+
-a,alpha @ Brief comment for -a and --alpha options
|
13
|
+
Longer and more elaborate description of the --alpha option
|
14
|
+
|
15
|
+
-b,beta=ARG
|
16
|
+
@ Alternative style of brief comment
|
17
|
+
|
18
|
+
Longer and more elaborate description of the --beta option
|
19
|
+
)
|
20
|
+
|
21
|
+
opts, args = ShellOpts.process(SPEC, ARGV)
|
22
|
+
#puts "opts.alpha?: #{opts.alpha?.inspect}"
|
23
|
+
#puts "opts.alpha: #{opts.alpha.inspect}"
|
24
|
+
#puts "opts.beta?: #{opts.beta?.inspect}"
|
25
|
+
#puts "opts.beta: #{opts.beta.inspect}"
|
26
|
+
#exit
|
27
|
+
#ShellOpts::ShellOpts.brief
|
28
|
+
#exit
|
29
|
+
|
30
|
+
|
31
|
+
|
32
|
+
|
33
|
+
|
34
|
+
|
35
|
+
|
36
|
+
# Standard options
|
37
|
+
# -h,help
|
38
|
+
# --version
|
39
|
+
#
|
40
|
+
# Message options
|
41
|
+
# -q,quiet
|
42
|
+
# -v,verbose
|
43
|
+
|
44
|
+
# ShellOpts.parse(spec, argv)
|
45
|
+
# ShellOpts.stdopt(spec, argv)
|
46
|
+
# ShellOpts.msgopt(spec, argv)
|
47
|
+
|
48
|
+
|
49
|
+
|
50
|
+
SPEC = %(
|
51
|
+
# Comment
|
52
|
+
|
53
|
+
@ Program brief
|
54
|
+
|
55
|
+
This should end up in the DESCRIPTION section in the @help format. Bla bla
|
56
|
+
bla. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod
|
57
|
+
tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam,
|
58
|
+
quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo
|
59
|
+
consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse
|
60
|
+
cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non
|
61
|
+
proident, sunt in culpa qui officia deserunt mollit anim id est laborum
|
62
|
+
|
63
|
+
Here comes some code
|
64
|
+
|
65
|
+
if this_is_printed_correctly?
|
66
|
+
puts "Success"
|
67
|
+
end
|
68
|
+
|
69
|
+
Here is a paragraph
|
70
|
+
|
71
|
+
Here is another paragraph
|
72
|
+
|
73
|
+
OPTIONS
|
74
|
+
|
75
|
+
This should be in the OPTIONS section (not supported for now - now it is!)
|
76
|
+
|
77
|
+
-a,all
|
78
|
+
-b,beta Brief inline comment
|
79
|
+
+v,verbose -h,help -version @ Multi option line. Option group.
|
80
|
+
-c
|
81
|
+
@ Alternative brief.
|
82
|
+
-f,file=FILE
|
83
|
+
Indented comment
|
84
|
+
|
85
|
+
Some free text not related to a single options. Eg. an introduction to the
|
86
|
+
next set of options and some more text to make this wrap
|
87
|
+
|
88
|
+
--multiple --options --on --one --line --with --brief
|
89
|
+
@ Brief for multi-option line - aka. option group. Lorem ipsum dolor sit amet, consectetur adipiscing elit
|
90
|
+
|
91
|
+
Common comment for previous multi-option line. Lorem ipsum dolor sit amet,
|
92
|
+
consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et
|
93
|
+
dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation
|
94
|
+
ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure
|
95
|
+
dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat
|
96
|
+
nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in
|
97
|
+
culpa qui officia deserunt mollit anim id est laborum
|
98
|
+
|
99
|
+
Here comes some more code:
|
100
|
+
|
101
|
+
if something
|
102
|
+
this
|
103
|
+
else
|
104
|
+
that
|
105
|
+
end
|
106
|
+
|
107
|
+
And some more text
|
108
|
+
|
109
|
+
|
110
|
+
# --multiple
|
111
|
+
# --options
|
112
|
+
# --on
|
113
|
+
# --multiple
|
114
|
+
# --lines
|
115
|
+
# Common comment for previous multi-option lines (not doable, but this is how
|
116
|
+
# the previous multi-option line will be rendered in one of the formats).
|
117
|
+
# However, the same is not true for commands that can't have a common comment
|
118
|
+
|
119
|
+
|
120
|
+
# A comment that should not be included in the source (useful to out-comment
|
121
|
+
# sections of source)
|
122
|
+
#
|
123
|
+
# The following blank line should be ignored
|
124
|
+
|
125
|
+
-l=MODE:short,long
|
126
|
+
Another indented comment. The following blank line should be included
|
127
|
+
|
128
|
+
But not if it is the last blank line.
|
129
|
+
\\hep! initiates a line but is not a command because it is escaped
|
130
|
+
|
131
|
+
a_code_example()
|
132
|
+
if something == 42
|
133
|
+
# Something that looks like a command
|
134
|
+
doit!
|
135
|
+
|
136
|
+
# Something that looks like a option
|
137
|
+
--i_miss_this
|
138
|
+
end
|
139
|
+
|
140
|
+
-- ARG1 ARG2
|
141
|
+
|
142
|
+
cmd! @ Brief description of command
|
143
|
+
Description of command.
|
144
|
+
|
145
|
+
Another paragraph.
|
146
|
+
Another line in paragraph
|
147
|
+
|
148
|
+
-c,copt @ Inline comment
|
149
|
+
-d,dopt @ Brief and nested comment
|
150
|
+
Even more nested comment
|
151
|
+
-a,all @ Duplicate
|
152
|
+
|
153
|
+
Here is some text where
|
154
|
+
|
155
|
+
the next line is indented (interpreted as code)
|
156
|
+
|
157
|
+
subcmd! -i,inc ++ SUB ARGS @ Brief description of sub-command
|
158
|
+
A description
|
159
|
+
|
160
|
+
This text should be included too
|
161
|
+
|
162
|
+
++ CMD_ARG1 CMD_ARG2
|
163
|
+
|
164
|
+
Longer indented text is not related to the previous
|
165
|
+
description of arguments but is considered code
|
166
|
+
|
167
|
+
is this code
|
168
|
+
|
169
|
+
++ CMD_ARG_A CMD_ARG_B
|
170
|
+
|
171
|
+
with something that is not a a description
|
172
|
+
|
173
|
+
# -- GLOBAL ARGS
|
174
|
+
|
175
|
+
--another-global-option
|
176
|
+
|
177
|
+
a regular paragraph
|
178
|
+
)
|
179
|
+
|
180
|
+
ONELINE = "-a,all -b,beta --verbose -h,help -v,version -c -f=FILE --multiple --options --on --one --line -l=MODE:short,long? cmd! -c,copt -d,dopt cmd.subcmd! -- GLOBAL ARGS"
|
181
|
+
|
182
|
+
SPEC2 = %(
|
183
|
+
@ Program brief
|
184
|
+
|
185
|
+
text text text
|
186
|
+
|
187
|
+
OPTIONS
|
188
|
+
|
189
|
+
Some option intro text
|
190
|
+
|
191
|
+
-a
|
192
|
+
Option brief 1. Help text
|
193
|
+
|
194
|
+
-b Option brief 2
|
195
|
+
|
196
|
+
-c @ Option brief 3
|
197
|
+
|
198
|
+
-d
|
199
|
+
@ Option brief 4
|
200
|
+
|
201
|
+
text text text
|
202
|
+
|
203
|
+
COMMANDS
|
204
|
+
|
205
|
+
Some command intro text
|
206
|
+
|
207
|
+
cmd1! -- ARG1 ARG2 Command brief 1
|
208
|
+
cmd2! @ Command brief 2
|
209
|
+
cmd3!
|
210
|
+
First-line option brief. After the first dot
|
211
|
+
|
212
|
+
)
|
213
|
+
|
214
|
+
# #{$stderr.puts "Oops"; "32"}
|
215
|
+
|
216
|
+
SPEC3 = %q(
|
217
|
+
Description
|
218
|
+
|
219
|
+
-- ARG1 ARG2
|
220
|
+
-- ARG3 ARG4
|
221
|
+
|
222
|
+
OPTIONS
|
223
|
+
Some text
|
224
|
+
|
225
|
+
-a An option and not code
|
226
|
+
|
227
|
+
Some code
|
228
|
+
|
229
|
+
\\-b A code line # Because we escaped it
|
230
|
+
-c # this is not an option
|
231
|
+
this_is_more_code()
|
232
|
+
|
233
|
+
Another option
|
234
|
+
|
235
|
+
-d Another
|
236
|
+
|
237
|
+
More code
|
238
|
+
|
239
|
+
# A command
|
240
|
+
-e # is not an option
|
241
|
+
|
242
|
+
COMMANDS
|
243
|
+
Some commands text
|
244
|
+
|
245
|
+
cmd1! A command
|
246
|
+
cmd1! A dup
|
247
|
+
|
248
|
+
Some code
|
249
|
+
|
250
|
+
\\cmd2! A code line
|
251
|
+
cmd3! # this is not a command
|
252
|
+
|
253
|
+
Final
|
254
|
+
)
|
255
|
+
|
256
|
+
SPEC4 = %(
|
257
|
+
cmd!
|
258
|
+
A command
|
259
|
+
|
260
|
+
cmd.nested!
|
261
|
+
A nested command
|
262
|
+
|
263
|
+
cmd.subcmd!
|
264
|
+
A subcommand
|
265
|
+
)
|
266
|
+
|
267
|
+
SPEC5 = "cmd! cmd!"
|
268
|
+
|
269
|
+
shellopts = ShellOpts::ShellOpts.new(exception: false)
|
270
|
+
#shellopts.compile("cmd! cmd!")
|
271
|
+
shellopts.compile(SPEC)
|
272
|
+
#shellopts.compile(SPEC2)
|
273
|
+
#shellopts.compile(SPEC3)
|
274
|
+
#shellopts.compile(SPEC4)
|
275
|
+
#shellopts.compile(SPEC5)
|
276
|
+
|
277
|
+
#shellopts.tokens.each(&:dump)
|
278
|
+
#exit
|
279
|
+
|
280
|
+
shellopts.usage
|
281
|
+
#shellopts.brief
|
282
|
+
#shellopts.help
|
283
|
+
#shellopts.help("cmd")
|
284
|
+
#shellopts.help(ARGV.first)
|
285
|
+
|
286
|
+
#p shellopts.tokens
|
287
|
+
|
288
|
+
#
|
289
|
+
#shellopts = ShellOpts::ShellOpts.new
|
290
|
+
#shellopts.compile(SPEC4)
|
291
|
+
#p shellopts.file
|
292
|
+
#ShellOpts.process(SPEC4, [])
|
293
|
+
#exit
|
294
|
+
|
295
|
+
|
296
|
+
#argv = ARGV.empty? ? %w(-a cmd -c) : ARGV
|
297
|
+
#prog, args = ShellOpts::ShellOpts.process SPEC2, argv
|
298
|
+
|
299
|
+
#tokens = ShellOpts::Lexer.lex("main", SPEC4)
|
300
|
+
#tokens.each(&:dump)
|
301
|
+
#exit
|
302
|
+
|
303
|
+
#ast = ShellOpts::Parser.parse(tokens)
|
304
|
+
#ast.dump_ast
|
305
|
+
#exit
|
306
|
+
|
307
|
+
#idr = ShellOpts::Analyzer.analyze(ast) # @idr and @ast refer to the same object
|
308
|
+
#idr.dump_idr
|
309
|
+
#exit
|
310
|
+
|
311
|
+
#puts "-" * 80
|
312
|
+
#ShellOpts::Formatter.usage(idr)
|
313
|
+
#puts "-" * 80
|
314
|
+
#ShellOpts::Formatter.brief(idr)
|
315
|
+
#puts "-" * 80
|
316
|
+
#ShellOpts::Formatter.help(idr)
|
317
|
+
#puts "-" * 80
|
318
|
+
|
319
|
+
#ShellOpts.process(SPEC, [])
|
320
|
+
#ShellOpts.error("Hej")
|
321
|
+
|
322
|
+
|
323
|
+
|
324
|
+
|
325
|
+
|
326
|
+
|
327
|
+
|
328
|
+
|
329
|
+
__END__
|
330
|
+
exit
|
331
|
+
|
332
|
+
puts "prog.verbose: #{prog.verbose.inspect}"
|
333
|
+
puts "prog.all?: #{prog.all?}"
|
334
|
+
puts "prog.file: #{prog.file.inspect}"
|
335
|
+
puts "prog.subcommand: #{prog.subcommand.inspect}"
|
336
|
+
puts "prog[:cmd!]: #{prog[:cmd!].__ident__}"
|
337
|
+
puts "prog[\"cmd\"]: #{prog["cmd"].__ident__}"
|
338
|
+
puts "prog.subcommand!.subcommand: #{prog.subcommand!.subcommand.inspect}"
|
339
|
+
#shellopts = ShellOpts::ShellOpts.new ONELINE, ARGV
|
340
|
+
|
341
|
+
|
342
|
+
|
343
|
+
puts "---------------------"
|
344
|
+
puts ShellOpts::Formatter.option_help(prog)
|
345
|
+
|
346
|
+
#spec = %(
|
347
|
+
#hej
|
348
|
+
#med dig
|
349
|
+
#)
|
350
|
+
#shellopts = ShellOpts::ShellOpts.new spec, ARGV
|
351
|
+
|
352
|
+
#shellopts.tokens.each(&:dump)
|
353
|
+
|
354
|
+
|
355
|
+
__END__
|
356
|
+
|
357
|
+
|
358
|
+
shellopts = ShellOpts::ShellOpts.new(SPEC, ARGV)
|
359
|
+
opts, args = shellopts.result
|
360
|
+
|
361
|
+
#opts, args = ShellOpts.make(SPEC, ARGV)
|
362
|
+
|
363
|
+
#opts.help? # True if present
|
364
|
+
#opts.file? # True if present
|
365
|
+
#opts.file # Not nil if argument was given
|
366
|
+
|
367
|
+
shellopts.mesg "This is a message"
|
368
|
+
shellopts.quiet!
|
369
|
+
shellopts.mesg "Not printed"
|
370
|
+
|
371
|
+
shellopts.verb "Not printed"
|
372
|
+
shellopts.verbose!
|
373
|
+
shellopts.verb "Printed"
|
374
|
+
|
375
|
+
|
376
|
+
#ShellOpts.failure "Something went wrong"
|
377
|
+
#
|
378
|
+
#
|
379
|
+
#ShellOpts.mesg "This is a message"
|
380
|
+
#ShellOpts.quiet!
|
381
|
+
#ShellOpts.mesg "Not printed"
|
382
|
+
#
|
383
|
+
#ShellOpts.verb "Not printed"
|
384
|
+
#ShellOpts.verbose!
|
385
|
+
#ShellOpts.verb "Printed"
|
386
|
+
#
|
387
|
+
#
|
388
|
+
#ShellOpts.failure "Something went wrong"
|
389
|
+
|
390
|
+
#include ShellOpts::Include
|
391
|
+
#
|
392
|
+
#mesg "This is a message"
|
393
|
+
#quiet!
|
394
|
+
#mesg "Not printed"
|
395
|
+
#
|
396
|
+
#verb "Not printed"
|
397
|
+
#verbose!
|
398
|
+
#verb "Printed"
|
399
|
+
#
|
400
|
+
#
|
401
|
+
#failure "Something went wrong"
|
402
|
+
|
403
|
+
|
404
|
+
|
405
|
+
__END__
|
406
|
+
|
407
|
+
opts, args = ShellOpts.process(OPTIONS, ARGV, exception: true)
|
408
|
+
|
409
|
+
if opts.version?
|
410
|
+
puts "pg_graph-#{PgGraph::VERSION}"
|
411
|
+
exit
|
412
|
+
end
|
413
|
+
|
414
|
+
if opts.help?
|
415
|
+
puts "Name"
|
416
|
+
puts " #{PROGRAM}"
|
417
|
+
puts
|
418
|
+
puts "Usage"
|
419
|
+
puts " #{PROGRAM} #{USAGE}"
|
420
|
+
puts
|
421
|
+
print "Options"
|
422
|
+
puts OPTIONS
|
423
|
+
exit
|
424
|
+
end
|
425
|
+
|
426
|
+
timing = opts.time?
|
427
|
+
timer = Timer::Timer.new
|
428
|
+
|
429
|
+
# Process options
|
430
|
+
meta = opts.meta
|
431
|
+
reflections = opts.reflections
|
432
|
+
|
433
|
+
!opts.kind? || %w(meta type data).include?(opts.kind) or
|
434
|
+
raise "Unknown argument for --kind option - '#{opts.kind}'"
|
435
|
+
kind = opts.kind? ? opts.kind : "type"
|
436
|
+
|
437
|
+
!opts.format? || %w(sql exec psql yaml).include?(opts.format) or
|
438
|
+
raise "Unknown argument for --format option - '#{opts.format}'"
|
439
|
+
format = opts.format? ? opts.format : "yaml"
|
440
|
+
|
441
|
+
case opts.subcommand || :dump
|
442
|
+
when :load
|
443
|
+
opts = opts.subcommand!
|
444
|
+
!opts.format? || %w(sql exec psql yaml).include?(opts.format) or
|
445
|
+
raise "Unknown argument for --format option - '#{opts.format}'"
|
446
|
+
|
447
|
+
database = args.expect(-1)
|
448
|
+
file = args.expect(0..1) || "/dev/stdin"
|
449
|
+
|
450
|
+
if opts.format?
|
451
|
+
format = opts.format
|
452
|
+
else
|
453
|
+
format =
|
454
|
+
case File.extname(file)
|
455
|
+
when ".sql"; "sql"
|
456
|
+
when ".yaml", ".yml"; "yaml"
|
457
|
+
else
|
458
|
+
"yaml"
|
459
|
+
end
|
460
|
+
end
|
461
|
+
|
462
|
+
case format
|
463
|
+
when "sql", "exec";
|
464
|
+
connection = timer.time("connect") { PgConn.new(database) }
|
465
|
+
timer.time("load file") {
|
466
|
+
connection.exec(IO.read(file))
|
467
|
+
}
|
468
|
+
when "psql"
|
469
|
+
timer.time("psql") {
|
470
|
+
system "psql -d #{database} < #{file} >/dev/null"
|
471
|
+
}
|
472
|
+
when "yaml"
|
473
|
+
connection, type = load_type(timer, opts, database)
|
474
|
+
tg = timer.group("read data")
|
475
|
+
data = tg.time("data") { PgGraph::Data.new(type, YAML.load(IO.read(file))) }
|
476
|
+
tg = timer.group("write data")
|
477
|
+
for label, sql in PgGraph::Data::SqlRender.new(data, :exec).to_h
|
478
|
+
tg.time(label) { connection.exec(sql.join) }
|
479
|
+
end
|
480
|
+
end
|
481
|
+
|
482
|
+
when :dump
|
483
|
+
if opts # When pg_graph is called without a subcommand
|
484
|
+
kind = "type"
|
485
|
+
format = "yaml"
|
486
|
+
else
|
487
|
+
!opts.kind? || %w(meta type data).include?(opts.kind) or
|
488
|
+
raise "Unknown argument for --kind option - '#{opts.kind}'"
|
489
|
+
kind = opts.kind? ? opts.kind : "type"
|
490
|
+
|
491
|
+
!opts.format? || %w(sql exec psql yaml).include?(opts.format) or
|
492
|
+
raise "Unknown argument for --format option - '#{opts.format}'"
|
493
|
+
format = opts.format? ? opts.format : "yaml"
|
494
|
+
end
|
495
|
+
|
496
|
+
database = args.expect(1)
|
497
|
+
|
498
|
+
case kind
|
499
|
+
when "meta"
|
500
|
+
connection = timer.time("connect") { PgConn.new(database) if !opts.meta? }
|
501
|
+
meta = timer.time("meta") { opts.meta? ? PgMeta.load_file(opts.meta) : PgMeta.new(connection) }
|
502
|
+
meta.dump
|
503
|
+
when "type"
|
504
|
+
connection, type = load_type(timer, opts, database)
|
505
|
+
type.dump
|
506
|
+
when "data"
|
507
|
+
connection, type = load_type(timer, opts, database)
|
508
|
+
data = timer.time("instantiate") { type.instantiate(connection) }
|
509
|
+
timer.time("dump") {
|
510
|
+
case format
|
511
|
+
when "sql"; puts data.to_sql
|
512
|
+
when "exec"; puts data.to_exec_sql
|
513
|
+
when "psql"; puts data.to_psql_sql
|
514
|
+
when "yaml"; puts data.to_yaml.to_yaml
|
515
|
+
end
|
516
|
+
}
|
517
|
+
end
|
518
|
+
|
519
|
+
when :clean
|
520
|
+
opts = opts.subcommand!
|
521
|
+
database = args.expect(1)
|
522
|
+
|
523
|
+
tg = timer.group("initialization")
|
524
|
+
connection = tg.time("connect") { PgConn.new(database) }
|
525
|
+
meta = tg.time("meta") { opts.meta? ? PgMeta.new(opts.meta) : PgMeta.new(connection) }
|
526
|
+
type = tg.time("type") { PgGraph::Type.new(meta) }
|
527
|
+
data = tg.time("data") { type.instantiate }
|
528
|
+
|
529
|
+
tg = timer.group("clean data")
|
530
|
+
for label, sql in PgGraph::Data::SqlRender.new(data, :exec).to_h
|
531
|
+
tg.time(label) { connection.exec(sql.join) }
|
532
|
+
end
|
533
|
+
else
|
534
|
+
puts "else"
|
535
|
+
end
|
536
|
+
|
537
|
+
timer.dump($stderr) if timing
|
538
|
+
|
539
|
+
rescue ShellOpts::Error => ex
|
540
|
+
$stderr.puts "#{PROGRAM}: #{ex.message}"
|
541
|
+
$stderr.puts "Usage: #{PROGRAM} #{USAGE}"
|
542
|
+
exit 1
|
543
|
+
end
|
544
|
+
|
545
|
+
include ShellOpts
|
546
|
+
include Prick
|
547
|
+
|
548
|
+
TIME = false
|
549
|
+
|
550
|
+
SPEC = %(
|
551
|
+
-h,help COMMAND...
|
552
|
+
Print this page
|
553
|
+
|
554
|
+
+v,verbose
|
555
|
+
Be verbose. Repeated -v options increase the verbosity level
|
556
|
+
|
557
|
+
-q,quiet
|
558
|
+
Be quiet
|
559
|
+
|
560
|
+
--version
|
561
|
+
Print prick version. Use 'prick version' to get the project version
|
562
|
+
|
563
|
+
-C,directory=EDIR
|
564
|
+
Change to directory DIR before doing anything else
|
565
|
+
|
566
|
+
-d,database=DATABASE
|
567
|
+
Override database name from prick.yml
|
568
|
+
|
569
|
+
-U,username=USERNAME
|
570
|
+
Override username from from prick.yml
|
571
|
+
|
572
|
+
!version
|
573
|
+
Print project version
|
574
|
+
|
575
|
+
!init -n,name=NAME -t,title=TITLE -- [DIRECTORY]
|
576
|
+
Initializes a prick project
|
577
|
+
|
578
|
+
!setup
|
579
|
+
Create the database user (if necessary) and an empty database
|
580
|
+
|
581
|
+
!teardown
|
582
|
+
Drop the database and the database user. TODO: Also run teardown scripts
|
583
|
+
|
584
|
+
!create.data
|
585
|
+
!create.schema
|
586
|
+
!create.database
|
587
|
+
!create.users
|
588
|
+
!create.all
|
589
|
+
Create an object. Fails if migration exist unless the --force flag is given
|
590
|
+
|
591
|
+
!create.migration -f,force -o,file=NFILE -- VERSION
|
592
|
+
Create a migration from VERSION to the current and write it to
|
593
|
+
migration/VERSION. Fails if migration exist unless the --force flag is
|
594
|
+
given. If --file is given, the migration is written to FILE instead of
|
595
|
+
the migration directory. This doesn't require you to be on a release
|
596
|
+
branch and can be used to create ad-hoc migration scripts
|
597
|
+
|
598
|
+
!drop -- [KIND]
|
599
|
+
Kind can be 'users', 'data', 'schema', 'database' (the default), or 'all'. It is
|
600
|
+
not an error if the object doesn't exist. TODO Only 'users' is currently defined
|
601
|
+
|
602
|
+
!build -t,time --dump=KIND? -- [SCHEMA]
|
603
|
+
Build the project. If SCHEMA is defined, later schemas are excluded.
|
604
|
+
KIND can be 'nodes', 'allnodes' or 'batches' (the default)
|
605
|
+
|
606
|
+
!make -t,time --dump=KIND? -- [SCHEMA]
|
607
|
+
Checks file timestamps against the time of the last build and only
|
608
|
+
rebuild affected parts of the project. KIND can be 'nodes', 'allnodes' or
|
609
|
+
'batches'
|
610
|
+
|
611
|
+
!fox -- FILE...
|
612
|
+
Load fox file data. Data are reset to their initial state after build
|
613
|
+
before the fox data are loaded
|
614
|
+
|
615
|
+
!release -- KIND
|
616
|
+
Create a release of the given kind. KIND can be 'major', 'minor', or
|
617
|
+
'patch'. Release checks that the current repo is clean and up to date
|
618
|
+
with the origin
|
619
|
+
|
620
|
+
!migrate -f,file=EFILE
|
621
|
+
Execute a migration
|
622
|
+
|
623
|
+
!dump.type
|
624
|
+
!dump.data
|
625
|
+
!dump.schema
|
626
|
+
!dump.database
|
627
|
+
TODO
|
628
|
+
|
629
|
+
dump.migration! --force VERSION
|
630
|
+
)
|
631
|
+
|
632
|
+
opts, args = ShellOpts.process(SPEC, ARGV)
|
633
|
+
|
634
|
+
# Handle --help
|
635
|
+
if opts.help?
|
636
|
+
puts "Name"
|
637
|
+
puts " prick - Postgres project management tool"
|
638
|
+
puts
|
639
|
+
puts "Usage"
|
640
|
+
puts " prick [GLOBAL-OPTIONS] command [COMMAND-OPTIONS] ARGUMENTS"
|
641
|
+
puts
|
642
|
+
puts "Options and commands"
|
643
|
+
puts SPEC.sub(/^\s*\n/, "")
|
644
|
+
exit
|
645
|
+
end
|
646
|
+
|
647
|
+
# Initial directory. Used to create relative paths in user messages
|
648
|
+
#rundir = Dir.getwd
|
649
|
+
|
650
|
+
begin
|
651
|
+
# Handle --version
|
652
|
+
if opts.version?
|
653
|
+
puts "prick-#{VERSION}"
|
654
|
+
exit
|
655
|
+
end
|
656
|
+
|
657
|
+
# Handle verbose and quiet
|
658
|
+
$verbose = opts.verbose
|
659
|
+
$quiet = opts.quiet?
|
660
|
+
|
661
|
+
# Honor -C option
|
662
|
+
if opts.directory?
|
663
|
+
if File.exist?(opts.directory)
|
664
|
+
begin
|
665
|
+
Dir.chdir(opts.directory)
|
666
|
+
rescue Errno::ENOENT
|
667
|
+
raise Prick::Error, "Can't cd to '#{opts.directory}'"
|
668
|
+
end
|
669
|
+
else
|
670
|
+
raise Prick::Error, "Can't find directory: #{opts.directory}"
|
671
|
+
end
|
672
|
+
end
|
673
|
+
|
674
|
+
# Get subcommand
|
675
|
+
cmd = opts.subcommand!
|
676
|
+
|
677
|
+
# Process init command
|
678
|
+
if opts.subcommand == :init
|
679
|
+
dir, state = Prick::SubCommand.init(args.expect(0..1), cmd.name, cmd.title, opts.database, opts.username)
|
680
|
+
puts "Initialized prick project '#{state.name}' in #{dir}"
|
681
|
+
if opts.database.nil? || opts.username.nil?
|
682
|
+
puts
|
683
|
+
puts "Please check database/username in #{PRICK_CONTEXT_FILE}"
|
684
|
+
end
|
685
|
+
exit
|
686
|
+
end
|
687
|
+
|
688
|
+
# Load state
|
689
|
+
Prick.state = State.load
|
690
|
+
|
691
|
+
# Handle -d and -U options
|
692
|
+
database = opts.database || Prick.state.database
|
693
|
+
username = opts.username || Prick.state.username
|
694
|
+
|
695
|
+
# Expect a sub-command
|
696
|
+
cmd = opts.subcommand! or raise Prick::Error, "Subcomand expected"
|
697
|
+
|
698
|
+
# Process subcommands
|
699
|
+
case opts.subcommand
|
700
|
+
when :version
|
701
|
+
puts "#{Prick.state.name}-#{Prick.state.version}"
|
702
|
+
|
703
|
+
when :setup
|
704
|
+
Prick::SubCommand.setup(database, username)
|
705
|
+
|
706
|
+
when :teardown
|
707
|
+
Prick::SubCommand.teardown(database, username)
|
708
|
+
|
709
|
+
when :create
|
710
|
+
create_command = opts.create!
|
711
|
+
case create_command.subcommand
|
712
|
+
when :migration
|
713
|
+
arg = args.expect(1)
|
714
|
+
version = PrickVersion.try(arg) or raise Prick::Error, "Illegal version: #{arg}"
|
715
|
+
Prick::SubCommand.create_migration(
|
716
|
+
username, version,
|
717
|
+
force: create_command.subcommand!.force?,
|
718
|
+
file: create_command.subcommand!.file)
|
719
|
+
else
|
720
|
+
raise NotImplementedError
|
721
|
+
end
|
722
|
+
|
723
|
+
when :build
|
724
|
+
dump = cmd.dump("batches")&.to_sym
|
725
|
+
Prick::SubCommand.build(database, username, args.expect(0..1), timer: cmd.time?, dump: dump)
|
726
|
+
|
727
|
+
when :make
|
728
|
+
dump = cmd.dump("batches")&.to_sym
|
729
|
+
Prick::SubCommand.make(database, username, args.expect(0..1), timer: cmd.time?, dump: dump)
|
730
|
+
|
731
|
+
when :fox
|
732
|
+
Prick::SubCommand.fox(database, username, args)
|
733
|
+
|
734
|
+
when :drop
|
735
|
+
case subject = args.expect(1).to_sym
|
736
|
+
when :all
|
737
|
+
Prick::SubCommand.drop_all(database)
|
738
|
+
when :users
|
739
|
+
Prick::SubCommand.drop_users(database)
|
740
|
+
when :database
|
741
|
+
Prick::SubCommand.drop_database(database)
|
742
|
+
when :data, :schema, :database, :all
|
743
|
+
raise NotImplementedError
|
744
|
+
else
|
745
|
+
raise Prick::Error, "Unknown subject: #{subject}"
|
746
|
+
end
|
747
|
+
|
748
|
+
when :release
|
749
|
+
kind = args.expect(1).to_sym
|
750
|
+
constrain? kind, :major, :minor, :patch or
|
751
|
+
raise Prick::Fail, "Expected 'major', 'minor', or 'patch' argument, got '#{kind}'"
|
752
|
+
Prick::SubCommand.release(kind)
|
753
|
+
|
754
|
+
when :migrate
|
755
|
+
args.expect(0)
|
756
|
+
Prick::SubCommand.migrate(database, username, file: cmd.file)
|
757
|
+
|
758
|
+
when :dump
|
759
|
+
subject = cmd.subcommand!
|
760
|
+
case cmd.subcommand
|
761
|
+
when :migration
|
762
|
+
arg = args.expect(1)
|
763
|
+
version = PrickVersion.try(arg) or raise "Illegal version number: #{arg}"
|
764
|
+
Prick::SubCommand.create_migration(username, version, force: subject.force?, file: "/dev/stdout")
|
765
|
+
when :data, :schema, :database
|
766
|
+
raise NotImplementedError
|
767
|
+
else
|
768
|
+
raise Prick::Error, "Unknown subject: #{subject}"
|
769
|
+
end
|
770
|
+
|
771
|
+
else
|
772
|
+
raise Prick::Fail, "Internal error: Unhandled command - #{opts.subcommand.inspect}"
|
773
|
+
end
|
774
|
+
|
775
|
+
rescue ShellOpts::Fail, Prick::Fail, Prick::Build::PostgresError => ex
|
776
|
+
ShellOpts.fail(ex.message)
|
777
|
+
|
778
|
+
rescue ShellOpts::Error, Prick::Error => ex
|
779
|
+
ShellOpts.error(ex.message)
|
780
|
+
end
|
781
|
+
|
782
|
+
__END__
|
783
|
+
|
784
|
+
-n,name=NAME
|
785
|
+
Name of project. Defauls to the environment variable `PRICK_PROJECT` if
|
786
|
+
set and else the name of the current directory
|
787
|
+
|
788
|
+
init! -u,user=USER [NAME]
|
789
|
+
Initialize a project in the given directory. The USER is the postgres
|
790
|
+
user and defaults to the project name
|
791
|
+
|
792
|
+
info!
|
793
|
+
Print project information
|
794
|
+
|
795
|
+
list.releases! -m,migrations -c,cancelled
|
796
|
+
List releases. Include migration releases if the --migration option is
|
797
|
+
present and also include cancelled releases if the --cancelled option is
|
798
|
+
present
|
799
|
+
|
800
|
+
list.migrations!
|
801
|
+
List migrations
|
802
|
+
|
803
|
+
list.upgrades! [FROM [TO]]
|
804
|
+
List available upgrades
|
805
|
+
|
806
|
+
list.cache!
|
807
|
+
List cache files
|
808
|
+
|
809
|
+
build! -d,database=DATABASE -s,state=FILE -C,no-cache [TAG]
|
810
|
+
Drop all users associated with the database before building the current
|
811
|
+
database from the content in the schemas/ directory. With a tag the
|
812
|
+
version is built into the associated versioned database and the result is
|
813
|
+
saved to cache unless the -C option is given. The -d option overrides the
|
814
|
+
default database and the -s option overrides the default state file
|
815
|
+
(fox.state)
|
816
|
+
|
817
|
+
make! -d,database=DATABASE -C,no-cache [TAG]
|
818
|
+
Build the current database from the content in the schemas/ directory.
|
819
|
+
With a tag the associated versioned database is loaded from cache if
|
820
|
+
present. The -C option ignores the cache and the -d option overrides
|
821
|
+
the default database
|
822
|
+
|
823
|
+
make.clean! -a,all
|
824
|
+
Drop versioned databases and remove cached and other temporary files.
|
825
|
+
Also drop the project database if the -a option is given
|
826
|
+
|
827
|
+
load! -d,database=DATABASE VERSION|FILE
|
828
|
+
Load the cached version or the file into the associated versioned
|
829
|
+
database. It is an error if the version hasn't been cached. The --database
|
830
|
+
argument overrides the database
|
831
|
+
|
832
|
+
save! VERSION [FILE]
|
833
|
+
Save the versioned database associated with version to the cache or the
|
834
|
+
given file
|
835
|
+
|
836
|
+
drop! -a,all [DATABASE]
|
837
|
+
Drop the given database or all versioned databases. Users with a username
|
838
|
+
on the form <database>__<username> are also dropped. The --all option
|
839
|
+
also drops the project database
|
840
|
+
|
841
|
+
drop.users! [DATABASE]
|
842
|
+
Drop users with a username on the form <database>__<username>
|
843
|
+
|
844
|
+
diff! -m,mark -t,tables -T,notables
|
845
|
+
diff [FROM-DATABASE|FROM-VERSION [TO-DATABASE|TO-VERSION]]
|
846
|
+
Create a schema diff between the given databases or versions. Default
|
847
|
+
to-version is the current schema and default from-version is the base
|
848
|
+
version of this branch/tag
|
849
|
+
|
850
|
+
migrate!
|
851
|
+
Not yet implemented
|
852
|
+
|
853
|
+
prepare!
|
854
|
+
Prepare a release. Just a shorthand for 'prick prepare release'
|
855
|
+
|
856
|
+
prepare.release! [FORK]
|
857
|
+
Populate the current migration directory with migration files
|
858
|
+
|
859
|
+
prepare.feature! NAME
|
860
|
+
Create and populate a feature as a subdirectory of the current directory.
|
861
|
+
Also prepares the current release directory
|
862
|
+
|
863
|
+
prepare.migration! [FROM]
|
864
|
+
Create and populate a migration directory
|
865
|
+
|
866
|
+
prepare.schema! NAME
|
867
|
+
Create and populate a new schema directory. Existing files and
|
868
|
+
directories are kept
|
869
|
+
|
870
|
+
prepare.diff! [VERSION]
|
871
|
+
Not yet implemented
|
872
|
+
|
873
|
+
include.feature! FEATURE
|
874
|
+
Include the given feature in the current pre-release
|
875
|
+
|
876
|
+
check!
|
877
|
+
Check that the current migration applied to the base version yields the
|
878
|
+
same result as loading the current schema
|
879
|
+
|
880
|
+
create.release! [RELEASE]
|
881
|
+
Prepare a release and create release directory and migration file before
|
882
|
+
tagging and branching to a release branch. The RELEASE argument can be
|
883
|
+
left out if the current branch is a prerelease branch
|
884
|
+
|
885
|
+
create.prerelease! RELEASE
|
886
|
+
Prepare a release and create release directory and migration file before
|
887
|
+
branching to a prerelease branch
|
888
|
+
|
889
|
+
create.feature! NAME
|
890
|
+
Prepare a feature before branching to a feature branch
|
891
|
+
|
892
|
+
cancel!
|
893
|
+
Cancel a release. Just a shorthand for 'prick cancel release'
|
894
|
+
|
895
|
+
cancel.release!
|
896
|
+
Cancel a release. Since tags are immutable, the release is cancelled by
|
897
|
+
added a special cancel-tag to the release that makes prick ignore it
|
898
|
+
|
899
|
+
generate.migration!
|
900
|
+
Create a script to migrate the database
|
901
|
+
|
902
|
+
generate.schema!
|
903
|
+
Create a script to create the database
|
904
|
+
|
905
|
+
upgrade!
|
906
|
+
Migrate the database to match the current schema
|
907
|
+
|
908
|
+
backup! [FILE]
|
909
|
+
Saves a backup of the database to the given file or to the var/spool
|
910
|
+
directory
|
911
|
+
|
912
|
+
restore! [FILE]
|
913
|
+
Restore the database from the given backup file or from the latest backup
|
914
|
+
in the var/spool directory
|
915
|
+
)
|
916
|
+
|
917
|
+
__END__
|
918
|
+
|
919
|
+
|
920
|
+
|
921
|
+
|
922
|
+
|
923
|
+
|
924
|
+
DEFAULT_STATE_FILE = "fox.state"
|
925
|
+
|
926
|
+
opts, args = ShellOpts.process(SPEC, ARGV)
|
927
|
+
|
928
|
+
# Handle --help
|
929
|
+
if opts.help?
|
930
|
+
ShellOpts.help
|
931
|
+
exit
|
932
|
+
end
|
933
|
+
|
934
|
+
# Handle --version
|
935
|
+
if opts.version?
|
936
|
+
puts "prick-#{VERSION}"
|
937
|
+
exit
|
938
|
+
end
|
939
|
+
|
940
|
+
begin
|
941
|
+
# Honor -C option
|
942
|
+
if opts.directory?
|
943
|
+
if File.exist?(opts.directory)
|
944
|
+
begin
|
945
|
+
Dir.chdir(opts.directory)
|
946
|
+
rescue Errno::ENOENT
|
947
|
+
raise Prick::Error, "Can't cd to '#{opts.directory}'"
|
948
|
+
end
|
949
|
+
else
|
950
|
+
raise Prick::Error, "Can't find directory: #{opts.directory}"
|
951
|
+
end
|
952
|
+
end
|
953
|
+
|
954
|
+
# Create program object
|
955
|
+
program = Program.new(quiet: opts.quiet?, verbose: opts.verbose?)
|
956
|
+
$verbose = opts.verbose? ? opts.verbose : nil
|
957
|
+
|
958
|
+
# Handle init command
|
959
|
+
if opts.subcommand == :init
|
960
|
+
directory = args.expect(0..1)
|
961
|
+
name = opts.name || (directory && File.basename(directory)) || File.basename(Dir.getwd)
|
962
|
+
user = opts.init!.user || name
|
963
|
+
program.init(name, user, directory || ".")
|
964
|
+
exit 0
|
965
|
+
end
|
966
|
+
|
967
|
+
# Change to parent directory containing the Prick version file if not found
|
968
|
+
# in the current directory
|
969
|
+
program.current_directory = Dir.getwd
|
970
|
+
while Dir.getwd != "/" && !File.exist?(PRICK_VERSION_FILE)
|
971
|
+
Dir.chdir("..")
|
972
|
+
end
|
973
|
+
|
974
|
+
# Check prick version
|
975
|
+
file = PrickVersion.new
|
976
|
+
file.exist? or raise Prick::Error, "Can't find prick version file '#{file.path}'"
|
977
|
+
VERSION == file.read.to_s or
|
978
|
+
raise Prick::Fail, ".prick-version required prick-#{file.read} but you're using prick-#{VERSION}"
|
979
|
+
|
980
|
+
# TODO: Check for dirty detached head
|
981
|
+
|
982
|
+
# Expect a sub-command
|
983
|
+
opts.subcommand or raise Prick::Error, "Subcomand expected"
|
984
|
+
|
985
|
+
case opts.subcommand
|
986
|
+
when :info
|
987
|
+
args.expect(0)
|
988
|
+
program.info
|
989
|
+
|
990
|
+
when :list
|
991
|
+
command = opts.list!
|
992
|
+
case command.subcommand
|
993
|
+
when :releases;
|
994
|
+
obj = command.releases!
|
995
|
+
program.list_releases(migrations: obj.migrations?, cancelled: obj.cancelled?)
|
996
|
+
when :migrations; program.list_migrations
|
997
|
+
when :upgrades; program.list_upgrades(*args.expect(0..2).compact)
|
998
|
+
when :cache;
|
999
|
+
args.expect(0)
|
1000
|
+
program.list_cache
|
1001
|
+
when NilClass; raise Prick::Error, "list requires a releases|migrations|upgrades sub-command"
|
1002
|
+
else
|
1003
|
+
raise Prick::Internal, "Subcommand #{opts.subcommand}.#{command.subcommand} is not matched"
|
1004
|
+
end
|
1005
|
+
|
1006
|
+
when :build
|
1007
|
+
version = args.expect(0..1)
|
1008
|
+
state_file = File.expand_path(opts.build!.state || DEFAULT_STATE_FILE)
|
1009
|
+
FileUtils.rm_f(state_file)
|
1010
|
+
program.build(opts.build!.database, version, state_file, opts.build!.no_cache?)
|
1011
|
+
|
1012
|
+
when :make
|
1013
|
+
command = opts.make!
|
1014
|
+
case command.subcommand
|
1015
|
+
when :clean
|
1016
|
+
args.expect(0)
|
1017
|
+
program.make_clean(command.clean!.all?)
|
1018
|
+
else
|
1019
|
+
version = args.expect(0..1)
|
1020
|
+
program.make(opts.make!.database, version, opts.make!.no_cache?)
|
1021
|
+
end
|
1022
|
+
|
1023
|
+
when :load
|
1024
|
+
version_or_file = args.expect(1)
|
1025
|
+
program.load(opts.load!.database, version_or_file)
|
1026
|
+
|
1027
|
+
when :save
|
1028
|
+
version, file = args.expect(1..2)
|
1029
|
+
program.save(version, file)
|
1030
|
+
|
1031
|
+
when :drop
|
1032
|
+
command = opts.drop!
|
1033
|
+
case command.subcommand
|
1034
|
+
when :users
|
1035
|
+
database = args.extract(0..1) || program.project.database.name
|
1036
|
+
args.expect(0)
|
1037
|
+
program.drop_users(database)
|
1038
|
+
else
|
1039
|
+
program.drop(args.expect(0..1), opts.drop!.all?)
|
1040
|
+
end
|
1041
|
+
|
1042
|
+
when :diff
|
1043
|
+
mark = opts.diff!.mark
|
1044
|
+
tables = opts.diff!.tables
|
1045
|
+
no_tables = opts.diff!.notables
|
1046
|
+
tables.nil? && no_tables.nil? || tables ^ no_tables or
|
1047
|
+
raise Error, "--tables and --no-tables options are exclusive"
|
1048
|
+
select = tables ? :tables : (no_tables ? :no_tables : :all)
|
1049
|
+
from, to = args.expect(0..2)
|
1050
|
+
program.diff(from, to, mark, select)
|
1051
|
+
|
1052
|
+
when :migrate
|
1053
|
+
raise NotYet
|
1054
|
+
|
1055
|
+
when :prepare
|
1056
|
+
cmd = opts.prepare!.subcommand || :release
|
1057
|
+
case cmd
|
1058
|
+
when :release; program.prepare_release(args.expect(0..1))
|
1059
|
+
when :feature; program.prepare_feature(args.expect(1))
|
1060
|
+
when :migration; program.prepare_migration(args.expect(0..1))
|
1061
|
+
when :schema; program.prepare_schema(args.expect(1))
|
1062
|
+
when :diff; program.prepare_diff(args.expect(0..1))
|
1063
|
+
else
|
1064
|
+
raise Prick::Internal, "Subcommand #{opts.subcommand}.#{cmd} is not matched"
|
1065
|
+
end
|
1066
|
+
|
1067
|
+
when :include
|
1068
|
+
cmd = opts.include!.subcommand || :feature
|
1069
|
+
case cmd
|
1070
|
+
when :feature; program.include_feature(args.expect(1))
|
1071
|
+
else
|
1072
|
+
raise Prick::Internal, "Subcommand #{opts.subcommand}.#{cmd} is not matched"
|
1073
|
+
end
|
1074
|
+
|
1075
|
+
when :check
|
1076
|
+
args.expect(0)
|
1077
|
+
program.check
|
1078
|
+
|
1079
|
+
when :create
|
1080
|
+
cmd = opts.create!.subcommand || :release
|
1081
|
+
case cmd
|
1082
|
+
when :release; program.create_release(args.expect(0..1))
|
1083
|
+
when :prerelease; program.create_prerelease(args.expect(0..1))
|
1084
|
+
when :feature; program.create_feature(args.expect(1))
|
1085
|
+
else
|
1086
|
+
raise Prick::Internal, "Subcommand #{opts.subcommand}.#{cmd} is not matched"
|
1087
|
+
end
|
1088
|
+
|
1089
|
+
when :cancel
|
1090
|
+
cmd = opts.cancel!.subcommand
|
1091
|
+
case cmd
|
1092
|
+
when :release; program.cancel_release(args.expect(1))
|
1093
|
+
when nil; raise Prick::Error, "'cancel' subcommand requires a release argument"
|
1094
|
+
else
|
1095
|
+
raise Prick::Internal, "Subcommand #{opts.subcommand}.#{cmd} is not matched"
|
1096
|
+
end
|
1097
|
+
|
1098
|
+
when :generate
|
1099
|
+
cmd = opts.generate!.subcommand
|
1100
|
+
case cmd
|
1101
|
+
when :schema; program.generate_schema
|
1102
|
+
when :migration; program.generate_migration
|
1103
|
+
when nil; raise Prick::Error, "'generate' subcommand requires a 'schema' or 'migration' argument"
|
1104
|
+
else
|
1105
|
+
raise Prick::Internal, "Subcommand #{opts.subcommand}.#{cmd} is not matched"
|
1106
|
+
end
|
1107
|
+
|
1108
|
+
when :upgrade
|
1109
|
+
args.expect(0)
|
1110
|
+
program.upgrade
|
1111
|
+
|
1112
|
+
when :backup
|
1113
|
+
program.backup(args.expect(0..1))
|
1114
|
+
|
1115
|
+
when :restore
|
1116
|
+
program.restore(args.expect(0..1))
|
1117
|
+
else
|
1118
|
+
raise Prick::Internal, "Subcommand #{opts.subcommand} is not matched"
|
1119
|
+
end
|
1120
|
+
|
1121
|
+
rescue Prick::Fail => ex # Handling of Fail has to come first because Fail < Error
|
1122
|
+
ShellOpts.fail(ex.message)
|
1123
|
+
rescue Prick::Error => ex
|
1124
|
+
ShellOpts.error(ex.message)
|
1125
|
+
end
|
1126
|
+
|
1127
|
+
__END__
|
1128
|
+
|
1129
|
+
# Awaits support for sections in ShellOpts
|
1130
|
+
HELP = %(
|
1131
|
+
OPTIONS
|
1132
|
+
-n, --name=NAME
|
1133
|
+
-C, --directory=DIR
|
1134
|
+
-h, --help
|
1135
|
+
-v, --verbose
|
1136
|
+
--version
|
1137
|
+
|
1138
|
+
COMMANDS
|
1139
|
+
INITIALIZATION
|
1140
|
+
init --user=USER [DIR]
|
1141
|
+
|
1142
|
+
INFO COMMANDS
|
1143
|
+
info
|
1144
|
+
list releases --migrations --cancelled
|
1145
|
+
list migrations
|
1146
|
+
list upgrades --all
|
1147
|
+
|
1148
|
+
BUILDING
|
1149
|
+
build -d DATABASE -C --nocache [TAG]
|
1150
|
+
make -d DATABASE -C --nocache [TAG]
|
1151
|
+
make clean -a
|
1152
|
+
load -d DATABASE VERSION|FILE
|
1153
|
+
save VERSION [FILE]
|
1154
|
+
drop --all [DATABASE]
|
1155
|
+
diff [FROM-DATABASE|FROM-VERSION [TO-DATABASE|TO-VERSION]]
|
1156
|
+
migrate
|
1157
|
+
|
1158
|
+
PREPARING RELEASES
|
1159
|
+
prepare release [FORK]
|
1160
|
+
prepare feature NAME
|
1161
|
+
prepare migration FROM
|
1162
|
+
prepare schema NAME
|
1163
|
+
prepare diff [VERSION]
|
1164
|
+
include feature FEATURE
|
1165
|
+
check
|
1166
|
+
|
1167
|
+
CREATING RELEASES
|
1168
|
+
create release [RELEASE]
|
1169
|
+
create prerelease RELEASE
|
1170
|
+
create feature NAME
|
1171
|
+
cancel release RELEASE
|
1172
|
+
|
1173
|
+
DEPLOYING RELEASES
|
1174
|
+
generate migration
|
1175
|
+
generate schema
|
1176
|
+
upgrade
|
1177
|
+
backup [FILE]
|
1178
|
+
restore [FILE]
|
1179
|
+
)
|
1180
|
+
|