#!/usr/bin/env ruby require 'bundler' Bundler.setup require 'shellopts' include ShellOpts VERSION = "1.2.3" SPEC = %( -a @ An option ) opts, args = ShellOpts::process(SPEC, ARGV, version: VERSION) #ShellOpts::ShellOpts.help __END__ #SPEC = %( # -a,alpha @ Brief comment for -a and --alpha options # Longer and more elaborate description of the --alpha option # # -b,beta=ARG # @ Alternative style of brief comment # # Longer and more elaborate description of the --beta option #) # #opts, args = ShellOpts.process(SPEC, ARGV) #puts "opts.alpha?: #{opts.alpha?.inspect}" #puts "opts.alpha: #{opts.alpha.inspect}" #puts "opts.beta?: #{opts.beta?.inspect}" #puts "opts.beta: #{opts.beta.inspect}" #exit #ShellOpts::ShellOpts.brief #exit # Standard options # -h,help # --version # # Message options # -q,quiet # -v,verbose # ShellOpts.parse(spec, argv) # ShellOpts.stdopt(spec, argv) # ShellOpts.msgopt(spec, argv) SPEC = %( # Comment @ Program brief This should end up in the DESCRIPTION section in the @help format. Bla bla bla. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum Here comes some code if this_is_printed_correctly? puts "Success" end Here is a paragraph Here is another paragraph OPTIONS This should be in the OPTIONS section (not supported for now - now it is!) -a,all -b,beta Brief inline comment +v,verbose -h,help -version @ Multi option line. Option group. -c @ Alternative brief. -f,file=FILE Indented comment Some free text not related to a single options. Eg. an introduction to the next set of options and some more text to make this wrap --multiple --options --on --one --line --with --brief @ Brief for multi-option line - aka. option group. Lorem ipsum dolor sit amet, consectetur adipiscing elit Common comment for previous multi-option line. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum Here comes some more code: if something this else that end And some more text # --multiple # --options # --on # --multiple # --lines # Common comment for previous multi-option lines (not doable, but this is how # the previous multi-option line will be rendered in one of the formats). # However, the same is not true for commands that can't have a common comment # A comment that should not be included in the source (useful to out-comment # sections of source) # # The following blank line should be ignored -l=MODE:short,long Another indented comment. The following blank line should be included But not if it is the last blank line. \\hep! initiates a line but is not a command because it is escaped a_code_example() if something == 42 # Something that looks like a command doit! # Something that looks like a option --i_miss_this end -- ARG1 ARG2 cmd! @ Brief description of command Description of command. Another paragraph. Another line in paragraph -c,copt @ Inline comment -d,dopt @ Brief and nested comment Even more nested comment -a,all @ Duplicate Here is some text where the next line is indented (interpreted as code) subcmd! -i,inc ++ SUB ARGS @ Brief description of sub-command A description This text should be included too ++ CMD_ARG1 CMD_ARG2 Longer indented text is not related to the previous description of arguments but is considered code is this code ++ CMD_ARG_A CMD_ARG_B with something that is not a a description # -- GLOBAL ARGS --another-global-option a regular paragraph ) 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" SPEC2 = %( @ Program brief text text text OPTIONS Some option intro text -a Option brief 1. Help text -b Option brief 2 -c @ Option brief 3 -d @ Option brief 4 text text text COMMANDS Some command intro text cmd1! -- ARG1 ARG2 Command brief 1 cmd2! @ Command brief 2 cmd3! First-line option brief. After the first dot ) # #{$stderr.puts "Oops"; "32"} SPEC3 = %q( Description -- ARG1 ARG2 -- ARG3 ARG4 OPTIONS Some text -a An option and not code Some code \\-b A code line # Because we escaped it -c # this is not an option this_is_more_code() Another option -d Another More code # A command -e # is not an option COMMANDS Some commands text cmd1! A command cmd1! A dup Some code \\cmd2! A code line cmd3! # this is not a command Final ) SPEC4 = %( cmd! A command cmd.nested! A nested command cmd.subcmd! A subcommand ) SPEC5 = "cmd! cmd!" shellopts = ShellOpts::ShellOpts.new(exception: true) #shellopts.compile("cmd! cmd!") shellopts.compile(SPEC) #shellopts.compile(SPEC2) #shellopts.compile(SPEC3) #shellopts.compile(SPEC4) #shellopts.compile(SPEC5) #shellopts.tokens.each(&:dump) #exit #shellopts.usage #shellopts.brief shellopts.help #shellopts.help("cmd") #shellopts.help(ARGV.first) #p shellopts.tokens # #shellopts = ShellOpts::ShellOpts.new #shellopts.compile(SPEC4) #p shellopts.file #ShellOpts.process(SPEC4, []) #exit #argv = ARGV.empty? ? %w(-a cmd -c) : ARGV #prog, args = ShellOpts::ShellOpts.process SPEC2, argv #tokens = ShellOpts::Lexer.lex("main", SPEC4) #tokens.each(&:dump) #exit #ast = ShellOpts::Parser.parse(tokens) #ast.dump_ast #exit #idr = ShellOpts::Analyzer.analyze(ast) # @idr and @ast refer to the same object #idr.dump_idr #exit #puts "-" * 80 #ShellOpts::Formatter.usage(idr) #puts "-" * 80 #ShellOpts::Formatter.brief(idr) #puts "-" * 80 #ShellOpts::Formatter.help(idr) #puts "-" * 80 #ShellOpts.process(SPEC, []) #ShellOpts.error("Hej") __END__ exit puts "prog.verbose: #{prog.verbose.inspect}" puts "prog.all?: #{prog.all?}" puts "prog.file: #{prog.file.inspect}" puts "prog.subcommand: #{prog.subcommand.inspect}" puts "prog[:cmd!]: #{prog[:cmd!].__ident__}" puts "prog[\"cmd\"]: #{prog["cmd"].__ident__}" puts "prog.subcommand!.subcommand: #{prog.subcommand!.subcommand.inspect}" #shellopts = ShellOpts::ShellOpts.new ONELINE, ARGV puts "---------------------" puts ShellOpts::Formatter.option_help(prog) #spec = %( #hej #med dig #) #shellopts = ShellOpts::ShellOpts.new spec, ARGV #shellopts.tokens.each(&:dump) __END__ shellopts = ShellOpts::ShellOpts.new(SPEC, ARGV) opts, args = shellopts.result #opts, args = ShellOpts.make(SPEC, ARGV) #opts.help? # True if present #opts.file? # True if present #opts.file # Not nil if argument was given shellopts.mesg "This is a message" shellopts.quiet! shellopts.mesg "Not printed" shellopts.verb "Not printed" shellopts.verbose! shellopts.verb "Printed" #ShellOpts.failure "Something went wrong" # # #ShellOpts.mesg "This is a message" #ShellOpts.quiet! #ShellOpts.mesg "Not printed" # #ShellOpts.verb "Not printed" #ShellOpts.verbose! #ShellOpts.verb "Printed" # # #ShellOpts.failure "Something went wrong" #include ShellOpts::Include # #mesg "This is a message" #quiet! #mesg "Not printed" # #verb "Not printed" #verbose! #verb "Printed" # # #failure "Something went wrong" __END__ opts, args = ShellOpts.process(OPTIONS, ARGV, exception: true) if opts.version? puts "pg_graph-#{PgGraph::VERSION}" exit end if opts.help? puts "Name" puts " #{PROGRAM}" puts puts "Usage" puts " #{PROGRAM} #{USAGE}" puts print "Options" puts OPTIONS exit end timing = opts.time? timer = Timer::Timer.new # Process options meta = opts.meta reflections = opts.reflections !opts.kind? || %w(meta type data).include?(opts.kind) or raise "Unknown argument for --kind option - '#{opts.kind}'" kind = opts.kind? ? opts.kind : "type" !opts.format? || %w(sql exec psql yaml).include?(opts.format) or raise "Unknown argument for --format option - '#{opts.format}'" format = opts.format? ? opts.format : "yaml" case opts.subcommand || :dump when :load opts = opts.subcommand! !opts.format? || %w(sql exec psql yaml).include?(opts.format) or raise "Unknown argument for --format option - '#{opts.format}'" database = args.expect(-1) file = args.expect(0..1) || "/dev/stdin" if opts.format? format = opts.format else format = case File.extname(file) when ".sql"; "sql" when ".yaml", ".yml"; "yaml" else "yaml" end end case format when "sql", "exec"; connection = timer.time("connect") { PgConn.new(database) } timer.time("load file") { connection.exec(IO.read(file)) } when "psql" timer.time("psql") { system "psql -d #{database} < #{file} >/dev/null" } when "yaml" connection, type = load_type(timer, opts, database) tg = timer.group("read data") data = tg.time("data") { PgGraph::Data.new(type, YAML.load(IO.read(file))) } tg = timer.group("write data") for label, sql in PgGraph::Data::SqlRender.new(data, :exec).to_h tg.time(label) { connection.exec(sql.join) } end end when :dump if opts # When pg_graph is called without a subcommand kind = "type" format = "yaml" else !opts.kind? || %w(meta type data).include?(opts.kind) or raise "Unknown argument for --kind option - '#{opts.kind}'" kind = opts.kind? ? opts.kind : "type" !opts.format? || %w(sql exec psql yaml).include?(opts.format) or raise "Unknown argument for --format option - '#{opts.format}'" format = opts.format? ? opts.format : "yaml" end database = args.expect(1) case kind when "meta" connection = timer.time("connect") { PgConn.new(database) if !opts.meta? } meta = timer.time("meta") { opts.meta? ? PgMeta.load_file(opts.meta) : PgMeta.new(connection) } meta.dump when "type" connection, type = load_type(timer, opts, database) type.dump when "data" connection, type = load_type(timer, opts, database) data = timer.time("instantiate") { type.instantiate(connection) } timer.time("dump") { case format when "sql"; puts data.to_sql when "exec"; puts data.to_exec_sql when "psql"; puts data.to_psql_sql when "yaml"; puts data.to_yaml.to_yaml end } end when :clean opts = opts.subcommand! database = args.expect(1) tg = timer.group("initialization") connection = tg.time("connect") { PgConn.new(database) } meta = tg.time("meta") { opts.meta? ? PgMeta.new(opts.meta) : PgMeta.new(connection) } type = tg.time("type") { PgGraph::Type.new(meta) } data = tg.time("data") { type.instantiate } tg = timer.group("clean data") for label, sql in PgGraph::Data::SqlRender.new(data, :exec).to_h tg.time(label) { connection.exec(sql.join) } end else puts "else" end timer.dump($stderr) if timing rescue ShellOpts::Error => ex $stderr.puts "#{PROGRAM}: #{ex.message}" $stderr.puts "Usage: #{PROGRAM} #{USAGE}" exit 1 end include ShellOpts include Prick TIME = false SPEC = %( -h,help COMMAND... Print this page +v,verbose Be verbose. Repeated -v options increase the verbosity level -q,quiet Be quiet --version Print prick version. Use 'prick version' to get the project version -C,directory=EDIR Change to directory DIR before doing anything else -d,database=DATABASE Override database name from prick.yml -U,username=USERNAME Override username from from prick.yml !version Print project version !init -n,name=NAME -t,title=TITLE -- [DIRECTORY] Initializes a prick project !setup Create the database user (if necessary) and an empty database !teardown Drop the database and the database user. TODO: Also run teardown scripts !create.data !create.schema !create.database !create.users !create.all Create an object. Fails if migration exist unless the --force flag is given !create.migration -f,force -o,file=NFILE -- VERSION Create a migration from VERSION to the current and write it to migration/VERSION. Fails if migration exist unless the --force flag is given. If --file is given, the migration is written to FILE instead of the migration directory. This doesn't require you to be on a release branch and can be used to create ad-hoc migration scripts !drop -- [KIND] Kind can be 'users', 'data', 'schema', 'database' (the default), or 'all'. It is not an error if the object doesn't exist. TODO Only 'users' is currently defined !build -t,time --dump=KIND? -- [SCHEMA] Build the project. If SCHEMA is defined, later schemas are excluded. KIND can be 'nodes', 'allnodes' or 'batches' (the default) !make -t,time --dump=KIND? -- [SCHEMA] Checks file timestamps against the time of the last build and only rebuild affected parts of the project. KIND can be 'nodes', 'allnodes' or 'batches' !fox -- FILE... Load fox file data. Data are reset to their initial state after build before the fox data are loaded !release -- KIND Create a release of the given kind. KIND can be 'major', 'minor', or 'patch'. Release checks that the current repo is clean and up to date with the origin !migrate -f,file=EFILE Execute a migration !dump.type !dump.data !dump.schema !dump.database TODO dump.migration! --force VERSION ) opts, args = ShellOpts.process(SPEC, ARGV) # Handle --help if opts.help? puts "Name" puts " prick - Postgres project management tool" puts puts "Usage" puts " prick [GLOBAL-OPTIONS] command [COMMAND-OPTIONS] ARGUMENTS" puts puts "Options and commands" puts SPEC.sub(/^\s*\n/, "") exit end # Initial directory. Used to create relative paths in user messages #rundir = Dir.getwd begin # Handle --version if opts.version? puts "prick-#{VERSION}" exit end # Handle verbose and quiet $verbose = opts.verbose $quiet = opts.quiet? # Honor -C option if opts.directory? if File.exist?(opts.directory) begin Dir.chdir(opts.directory) rescue Errno::ENOENT raise Prick::Error, "Can't cd to '#{opts.directory}'" end else raise Prick::Error, "Can't find directory: #{opts.directory}" end end # Get subcommand cmd = opts.subcommand! # Process init command if opts.subcommand == :init dir, state = Prick::SubCommand.init(args.expect(0..1), cmd.name, cmd.title, opts.database, opts.username) puts "Initialized prick project '#{state.name}' in #{dir}" if opts.database.nil? || opts.username.nil? puts puts "Please check database/username in #{PRICK_CONTEXT_FILE}" end exit end # Load state Prick.state = State.load # Handle -d and -U options database = opts.database || Prick.state.database username = opts.username || Prick.state.username # Expect a sub-command cmd = opts.subcommand! or raise Prick::Error, "Subcomand expected" # Process subcommands case opts.subcommand when :version puts "#{Prick.state.name}-#{Prick.state.version}" when :setup Prick::SubCommand.setup(database, username) when :teardown Prick::SubCommand.teardown(database, username) when :create create_command = opts.create! case create_command.subcommand when :migration arg = args.expect(1) version = PrickVersion.try(arg) or raise Prick::Error, "Illegal version: #{arg}" Prick::SubCommand.create_migration( username, version, force: create_command.subcommand!.force?, file: create_command.subcommand!.file) else raise NotImplementedError end when :build dump = cmd.dump("batches")&.to_sym Prick::SubCommand.build(database, username, args.expect(0..1), timer: cmd.time?, dump: dump) when :make dump = cmd.dump("batches")&.to_sym Prick::SubCommand.make(database, username, args.expect(0..1), timer: cmd.time?, dump: dump) when :fox Prick::SubCommand.fox(database, username, args) when :drop case subject = args.expect(1).to_sym when :all Prick::SubCommand.drop_all(database) when :users Prick::SubCommand.drop_users(database) when :database Prick::SubCommand.drop_database(database) when :data, :schema, :database, :all raise NotImplementedError else raise Prick::Error, "Unknown subject: #{subject}" end when :release kind = args.expect(1).to_sym constrain? kind, :major, :minor, :patch or raise Prick::Fail, "Expected 'major', 'minor', or 'patch' argument, got '#{kind}'" Prick::SubCommand.release(kind) when :migrate args.expect(0) Prick::SubCommand.migrate(database, username, file: cmd.file) when :dump subject = cmd.subcommand! case cmd.subcommand when :migration arg = args.expect(1) version = PrickVersion.try(arg) or raise "Illegal version number: #{arg}" Prick::SubCommand.create_migration(username, version, force: subject.force?, file: "/dev/stdout") when :data, :schema, :database raise NotImplementedError else raise Prick::Error, "Unknown subject: #{subject}" end else raise Prick::Fail, "Internal error: Unhandled command - #{opts.subcommand.inspect}" end rescue ShellOpts::Fail, Prick::Fail, Prick::Build::PostgresError => ex ShellOpts.fail(ex.message) rescue ShellOpts::Error, Prick::Error => ex ShellOpts.error(ex.message) end __END__ -n,name=NAME Name of project. Defauls to the environment variable `PRICK_PROJECT` if set and else the name of the current directory init! -u,user=USER [NAME] Initialize a project in the given directory. The USER is the postgres user and defaults to the project name info! Print project information list.releases! -m,migrations -c,cancelled List releases. Include migration releases if the --migration option is present and also include cancelled releases if the --cancelled option is present list.migrations! List migrations list.upgrades! [FROM [TO]] List available upgrades list.cache! List cache files build! -d,database=DATABASE -s,state=FILE -C,no-cache [TAG] Drop all users associated with the database before building the current database from the content in the schemas/ directory. With a tag the version is built into the associated versioned database and the result is saved to cache unless the -C option is given. The -d option overrides the default database and the -s option overrides the default state file (fox.state) make! -d,database=DATABASE -C,no-cache [TAG] Build the current database from the content in the schemas/ directory. With a tag the associated versioned database is loaded from cache if present. The -C option ignores the cache and the -d option overrides the default database make.clean! -a,all Drop versioned databases and remove cached and other temporary files. Also drop the project database if the -a option is given load! -d,database=DATABASE VERSION|FILE Load the cached version or the file into the associated versioned database. It is an error if the version hasn't been cached. The --database argument overrides the database save! VERSION [FILE] Save the versioned database associated with version to the cache or the given file drop! -a,all [DATABASE] Drop the given database or all versioned databases. Users with a username on the form __ are also dropped. The --all option also drops the project database drop.users! [DATABASE] Drop users with a username on the form __ diff! -m,mark -t,tables -T,notables diff [FROM-DATABASE|FROM-VERSION [TO-DATABASE|TO-VERSION]] Create a schema diff between the given databases or versions. Default to-version is the current schema and default from-version is the base version of this branch/tag migrate! Not yet implemented prepare! Prepare a release. Just a shorthand for 'prick prepare release' prepare.release! [FORK] Populate the current migration directory with migration files prepare.feature! NAME Create and populate a feature as a subdirectory of the current directory. Also prepares the current release directory prepare.migration! [FROM] Create and populate a migration directory prepare.schema! NAME Create and populate a new schema directory. Existing files and directories are kept prepare.diff! [VERSION] Not yet implemented include.feature! FEATURE Include the given feature in the current pre-release check! Check that the current migration applied to the base version yields the same result as loading the current schema create.release! [RELEASE] Prepare a release and create release directory and migration file before tagging and branching to a release branch. The RELEASE argument can be left out if the current branch is a prerelease branch create.prerelease! RELEASE Prepare a release and create release directory and migration file before branching to a prerelease branch create.feature! NAME Prepare a feature before branching to a feature branch cancel! Cancel a release. Just a shorthand for 'prick cancel release' cancel.release! Cancel a release. Since tags are immutable, the release is cancelled by added a special cancel-tag to the release that makes prick ignore it generate.migration! Create a script to migrate the database generate.schema! Create a script to create the database upgrade! Migrate the database to match the current schema backup! [FILE] Saves a backup of the database to the given file or to the var/spool directory restore! [FILE] Restore the database from the given backup file or from the latest backup in the var/spool directory ) __END__ DEFAULT_STATE_FILE = "fox.state" opts, args = ShellOpts.process(SPEC, ARGV) # Handle --help if opts.help? ShellOpts.help exit end # Handle --version if opts.version? puts "prick-#{VERSION}" exit end begin # Honor -C option if opts.directory? if File.exist?(opts.directory) begin Dir.chdir(opts.directory) rescue Errno::ENOENT raise Prick::Error, "Can't cd to '#{opts.directory}'" end else raise Prick::Error, "Can't find directory: #{opts.directory}" end end # Create program object program = Program.new(quiet: opts.quiet?, verbose: opts.verbose?) $verbose = opts.verbose? ? opts.verbose : nil # Handle init command if opts.subcommand == :init directory = args.expect(0..1) name = opts.name || (directory && File.basename(directory)) || File.basename(Dir.getwd) user = opts.init!.user || name program.init(name, user, directory || ".") exit 0 end # Change to parent directory containing the Prick version file if not found # in the current directory program.current_directory = Dir.getwd while Dir.getwd != "/" && !File.exist?(PRICK_VERSION_FILE) Dir.chdir("..") end # Check prick version file = PrickVersion.new file.exist? or raise Prick::Error, "Can't find prick version file '#{file.path}'" VERSION == file.read.to_s or raise Prick::Fail, ".prick-version required prick-#{file.read} but you're using prick-#{VERSION}" # TODO: Check for dirty detached head # Expect a sub-command opts.subcommand or raise Prick::Error, "Subcomand expected" case opts.subcommand when :info args.expect(0) program.info when :list command = opts.list! case command.subcommand when :releases; obj = command.releases! program.list_releases(migrations: obj.migrations?, cancelled: obj.cancelled?) when :migrations; program.list_migrations when :upgrades; program.list_upgrades(*args.expect(0..2).compact) when :cache; args.expect(0) program.list_cache when NilClass; raise Prick::Error, "list requires a releases|migrations|upgrades sub-command" else raise Prick::Internal, "Subcommand #{opts.subcommand}.#{command.subcommand} is not matched" end when :build version = args.expect(0..1) state_file = File.expand_path(opts.build!.state || DEFAULT_STATE_FILE) FileUtils.rm_f(state_file) program.build(opts.build!.database, version, state_file, opts.build!.no_cache?) when :make command = opts.make! case command.subcommand when :clean args.expect(0) program.make_clean(command.clean!.all?) else version = args.expect(0..1) program.make(opts.make!.database, version, opts.make!.no_cache?) end when :load version_or_file = args.expect(1) program.load(opts.load!.database, version_or_file) when :save version, file = args.expect(1..2) program.save(version, file) when :drop command = opts.drop! case command.subcommand when :users database = args.extract(0..1) || program.project.database.name args.expect(0) program.drop_users(database) else program.drop(args.expect(0..1), opts.drop!.all?) end when :diff mark = opts.diff!.mark tables = opts.diff!.tables no_tables = opts.diff!.notables tables.nil? && no_tables.nil? || tables ^ no_tables or raise Error, "--tables and --no-tables options are exclusive" select = tables ? :tables : (no_tables ? :no_tables : :all) from, to = args.expect(0..2) program.diff(from, to, mark, select) when :migrate raise NotYet when :prepare cmd = opts.prepare!.subcommand || :release case cmd when :release; program.prepare_release(args.expect(0..1)) when :feature; program.prepare_feature(args.expect(1)) when :migration; program.prepare_migration(args.expect(0..1)) when :schema; program.prepare_schema(args.expect(1)) when :diff; program.prepare_diff(args.expect(0..1)) else raise Prick::Internal, "Subcommand #{opts.subcommand}.#{cmd} is not matched" end when :include cmd = opts.include!.subcommand || :feature case cmd when :feature; program.include_feature(args.expect(1)) else raise Prick::Internal, "Subcommand #{opts.subcommand}.#{cmd} is not matched" end when :check args.expect(0) program.check when :create cmd = opts.create!.subcommand || :release case cmd when :release; program.create_release(args.expect(0..1)) when :prerelease; program.create_prerelease(args.expect(0..1)) when :feature; program.create_feature(args.expect(1)) else raise Prick::Internal, "Subcommand #{opts.subcommand}.#{cmd} is not matched" end when :cancel cmd = opts.cancel!.subcommand case cmd when :release; program.cancel_release(args.expect(1)) when nil; raise Prick::Error, "'cancel' subcommand requires a release argument" else raise Prick::Internal, "Subcommand #{opts.subcommand}.#{cmd} is not matched" end when :generate cmd = opts.generate!.subcommand case cmd when :schema; program.generate_schema when :migration; program.generate_migration when nil; raise Prick::Error, "'generate' subcommand requires a 'schema' or 'migration' argument" else raise Prick::Internal, "Subcommand #{opts.subcommand}.#{cmd} is not matched" end when :upgrade args.expect(0) program.upgrade when :backup program.backup(args.expect(0..1)) when :restore program.restore(args.expect(0..1)) else raise Prick::Internal, "Subcommand #{opts.subcommand} is not matched" end rescue Prick::Fail => ex # Handling of Fail has to come first because Fail < Error ShellOpts.fail(ex.message) rescue Prick::Error => ex ShellOpts.error(ex.message) end __END__ # Awaits support for sections in ShellOpts HELP = %( OPTIONS -n, --name=NAME -C, --directory=DIR -h, --help -v, --verbose --version COMMANDS INITIALIZATION init --user=USER [DIR] INFO COMMANDS info list releases --migrations --cancelled list migrations list upgrades --all BUILDING build -d DATABASE -C --nocache [TAG] make -d DATABASE -C --nocache [TAG] make clean -a load -d DATABASE VERSION|FILE save VERSION [FILE] drop --all [DATABASE] diff [FROM-DATABASE|FROM-VERSION [TO-DATABASE|TO-VERSION]] migrate PREPARING RELEASES prepare release [FORK] prepare feature NAME prepare migration FROM prepare schema NAME prepare diff [VERSION] include feature FEATURE check CREATING RELEASES create release [RELEASE] create prerelease RELEASE create feature NAME cancel release RELEASE DEPLOYING RELEASES generate migration generate schema upgrade backup [FILE] restore [FILE] )