ardb 0.28.0 → 0.29.1

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.
Files changed (53) hide show
  1. checksums.yaml +7 -7
  2. data/Gemfile +19 -8
  3. data/README.md +252 -3
  4. data/ardb.gemspec +8 -7
  5. data/bin/ardb +1 -1
  6. data/lib/ardb.rb +55 -39
  7. data/lib/ardb/adapter/base.rb +35 -30
  8. data/lib/ardb/adapter/mysql.rb +2 -17
  9. data/lib/ardb/adapter/postgresql.rb +24 -30
  10. data/lib/ardb/adapter/sqlite.rb +4 -8
  11. data/lib/ardb/adapter_spy.rb +2 -16
  12. data/lib/ardb/cli.rb +15 -15
  13. data/lib/ardb/cli/clirb.rb +14 -17
  14. data/lib/ardb/cli/commands.rb +204 -114
  15. data/lib/ardb/db_tests.rb +2 -4
  16. data/lib/ardb/default_order_by.rb +3 -13
  17. data/lib/ardb/migration.rb +9 -13
  18. data/lib/ardb/record_spy.rb +7 -26
  19. data/lib/ardb/relation_spy.rb +0 -6
  20. data/lib/ardb/require_autoloaded_active_record_files.rb +172 -58
  21. data/lib/ardb/test_helpers.rb +2 -5
  22. data/lib/ardb/use_db_default.rb +4 -15
  23. data/lib/ardb/version.rb +1 -1
  24. data/script/determine_autoloaded_active_record_files.rb +14 -9
  25. data/test/helper.rb +4 -13
  26. data/test/support/factory.rb +2 -2
  27. data/test/support/postgresql/migrations/{.gitkeep → .keep} +0 -0
  28. data/test/support/postgresql/schema.rb +0 -1
  29. data/test/support/postgresql/setup_test_db.rb +15 -15
  30. data/test/system/.keep +0 -0
  31. data/test/unit/adapter/base_tests.rb +76 -52
  32. data/test/unit/adapter/mysql_tests.rb +2 -19
  33. data/test/unit/adapter/postgresql_tests.rb +14 -23
  34. data/test/unit/adapter/sqlite_tests.rb +3 -11
  35. data/test/unit/adapter_spy_tests.rb +2 -16
  36. data/test/unit/ardb_tests.rb +57 -44
  37. data/test/unit/cli_tests.rb +220 -158
  38. data/test/unit/db_tests_tests.rb +3 -6
  39. data/test/unit/default_order_by_tests.rb +4 -8
  40. data/test/unit/migration_tests.rb +11 -15
  41. data/test/unit/record_spy_tests.rb +17 -22
  42. data/test/unit/relation_spy_tests.rb +17 -46
  43. data/test/unit/test_helpers_tests.rb +3 -14
  44. data/test/unit/use_db_default_tests.rb +7 -11
  45. metadata +100 -83
  46. data/lib/ardb/has_slug.rb +0 -107
  47. data/lib/ardb/migration_helpers.rb +0 -77
  48. data/lib/ardb/pg_json.rb +0 -90
  49. data/test/support/postgresql/pg_json_migrations/20160519133432_create_pg_json_migrate_test.rb +0 -13
  50. data/test/system/pg_json_tests.rb +0 -85
  51. data/test/unit/has_slug_tests.rb +0 -341
  52. data/test/unit/migration_helpers_tests.rb +0 -65
  53. data/test/unit/pg_json_tests.rb +0 -39
@@ -1,14 +1,13 @@
1
- require 'ardb'
1
+ require "ardb"
2
2
 
3
3
  module Ardb; end
4
4
  module Ardb::Adapter
5
-
6
5
  class Base
7
-
8
6
  attr_reader :config
9
7
 
10
8
  def initialize(config)
11
9
  @config = config
10
+ validate!
12
11
  end
13
12
 
14
13
  def connect_hash; self.config.activerecord_connect_hash; end
@@ -30,11 +29,9 @@ module Ardb::Adapter
30
29
  pattern.gsub!(escape_char){ escape_char * 2 }
31
30
  # don't allow custom wildcards
32
31
  pattern.gsub!(/%|_/){ |wildcard_char| "#{escape_char}#{wildcard_char}" }
32
+ pattern
33
33
  end
34
34
 
35
- def foreign_key_add_sql(*args); raise NotImplementedError; end
36
- def foreign_key_drop_sql(*args); raise NotImplementedError; end
37
-
38
35
  def create_db(*args); raise NotImplementedError; end
39
36
  def drop_db(*args); raise NotImplementedError; end
40
37
 
@@ -42,33 +39,34 @@ module Ardb::Adapter
42
39
 
43
40
  def connect_db
44
41
  ActiveRecord::Base.establish_connection(self.connect_hash)
45
- # checkout a connection to ensure we can connect to the DB, we don't do
42
+ # checkout a connection to ensure we can connect to the DB, we don"t do
46
43
  # anything with the connection and immediately check it back in
47
44
  ActiveRecord::Base.connection_pool.with_connection{ }
48
45
  end
49
46
 
50
47
  def migrate_db
51
- verbose = ENV["MIGRATE_QUIET"].nil?
52
- version = ENV["MIGRATE_VERSION"] ? ENV["MIGRATE_VERSION"].to_i : nil
53
-
54
- if defined?(ActiveRecord::Migration::CommandRecorder)
55
- require 'ardb/migration_helpers'
56
- ActiveRecord::Migration::CommandRecorder.class_eval do
57
- include Ardb::MigrationHelpers::RecorderMixin
58
- end
59
- end
48
+ migrate_db_up
49
+ end
60
50
 
61
- ActiveRecord::Migrator.migrations_path = self.migrations_path
62
- ActiveRecord::Migration.verbose = verbose
63
- ActiveRecord::Migrator.migrate(self.migrations_path, version) do |migration|
64
- ENV["MIGRATE_SCOPE"].blank? || (ENV["MIGRATE_SCOPE"] == migration.scope)
65
- end
51
+ def migrate_db_up(target_version = nil)
52
+ migration_context.up(target_version)
53
+ end
54
+
55
+ def migrate_db_down(target_version = nil)
56
+ migration_context.down(target_version)
57
+ end
58
+
59
+ def migrate_db_forward(steps = 1)
60
+ migration_context.forward(steps)
61
+ end
62
+
63
+ def migrate_db_backward(steps = 1)
64
+ migration_context.rollback(steps)
66
65
  end
67
66
 
68
67
  def load_schema
69
- # silence STDOUT
70
- current_stdout = $stdout.dup
71
- $stdout = File.new('/dev/null', 'w')
68
+ current_stdout = $stdout.dup # silence STDOUT
69
+ $stdout = File.new("/dev/null", "w")
72
70
  load_ruby_schema if self.schema_format == Ardb::Config::RUBY_SCHEMA_FORMAT
73
71
  load_sql_schema if self.schema_format == Ardb::Config::SQL_SCHEMA_FORMAT
74
72
  $stdout = current_stdout
@@ -83,18 +81,17 @@ module Ardb::Adapter
83
81
  end
84
82
 
85
83
  def dump_schema
86
- # silence STDOUT
87
- current_stdout = $stdout.dup
88
- $stdout = File.new('/dev/null', 'w')
84
+ current_stdout = $stdout.dup # silence STDOUT
85
+ $stdout = File.new("/dev/null", "w")
89
86
  dump_ruby_schema
90
87
  dump_sql_schema if self.schema_format == Ardb::Config::SQL_SCHEMA_FORMAT
91
88
  $stdout = current_stdout
92
89
  end
93
90
 
94
91
  def dump_ruby_schema
95
- require 'active_record/schema_dumper'
92
+ require "active_record/schema_dumper"
96
93
  FileUtils.mkdir_p File.dirname(self.ruby_schema_path)
97
- File.open(self.ruby_schema_path, 'w:utf-8') do |file|
94
+ File.open(self.ruby_schema_path, "w:utf-8") do |file|
98
95
  ActiveRecord::SchemaDumper.dump(ActiveRecord::Base.connection, file)
99
96
  end
100
97
  end
@@ -111,6 +108,14 @@ module Ardb::Adapter
111
108
  end
112
109
  end
113
110
 
114
- end
111
+ private
115
112
 
113
+ def validate!
114
+ # override as needed
115
+ end
116
+
117
+ def migration_context
118
+ ActiveRecord::MigrationContext.new(migrations_path)
119
+ end
120
+ end
116
121
  end
@@ -1,21 +1,6 @@
1
- require 'ardb/adapter/base'
1
+ require "ardb/adapter/base"
2
2
 
3
3
  module Ardb::Adapter
4
-
5
- class Mysql < Base
6
-
7
- def foreign_key_add_sql
8
- "ALTER TABLE :from_table"\
9
- " ADD CONSTRAINT :name"\
10
- " FOREIGN KEY (:from_column)"\
11
- " REFERENCES :to_table (:to_column)"
12
- end
13
-
14
- def foreign_key_drop_sql
15
- "ALTER TABLE :from_table"\
16
- " DROP FOREIGN KEY :name"
17
- end
18
-
4
+ class Mysql < Ardb::Adapter::Base
19
5
  end
20
-
21
6
  end
@@ -1,16 +1,14 @@
1
- require 'ardb/adapter/base'
1
+ require "ardb/adapter/base"
2
2
 
3
3
  module Ardb::Adapter
4
-
5
- class Postgresql < Base
6
-
7
- # the 'postgres' db is a "public" (doesn't typically require auth/grants to
4
+ class Postgresql < Ardb::Adapter::Base
5
+ # the "postgres" db is a "public" (doesn"t typically require auth/grants to
8
6
  # connect to) db that typically exists for all postgres installations; the
9
7
  # adapter uses it to create/drop other databases
10
8
  def public_connect_hash
11
9
  @public_connect_hash ||= self.connect_hash.merge({
12
- 'database' => 'postgres',
13
- 'schema_search_path' => 'public'
10
+ "database" => "postgres",
11
+ "schema_search_path" => "public"
14
12
  })
15
13
  end
16
14
 
@@ -42,47 +40,43 @@ module Ardb::Adapter
42
40
  tables = conn.execute "SELECT table_name"\
43
41
  " FROM information_schema.tables"\
44
42
  " WHERE table_schema = 'public';"
45
- tables.each{ |row| conn.execute "DROP TABLE #{row['table_name']} CASCADE" }
43
+ tables.each{ |row| conn.execute "DROP TABLE #{row["table_name"]} CASCADE" }
46
44
  end
47
45
  end
48
46
 
49
- def foreign_key_add_sql
50
- "ALTER TABLE :from_table"\
51
- " ADD CONSTRAINT :name"\
52
- " FOREIGN KEY (:from_column)"\
53
- " REFERENCES :to_table (:to_column)"
54
- end
55
-
56
- def foreign_key_drop_sql
57
- "ALTER TABLE :from_table"\
58
- " DROP CONSTRAINT :name"
59
- end
60
-
61
47
  def load_sql_schema
62
- require 'scmd'
48
+ require "scmd"
63
49
  cmd_str = "psql -f \"#{self.sql_schema_path}\" #{self.database}"
64
50
  cmd = Scmd.new(cmd_str, :env => env_var_hash).tap(&:run)
65
- raise 'Error loading database' unless cmd.success?
51
+ raise "Error loading database" unless cmd.success?
66
52
  end
67
53
 
68
54
  def dump_sql_schema
69
- require 'scmd'
55
+ require "scmd"
70
56
  cmd_str = "pg_dump -i -s -x -O -f \"#{self.sql_schema_path}\" #{self.database}"
71
57
  cmd = Scmd.new(cmd_str, :env => env_var_hash).tap(&:run)
72
- raise 'Error dumping database' unless cmd.success?
58
+ raise "Error dumping database" unless cmd.success?
73
59
  end
74
60
 
75
61
  private
76
62
 
63
+ def validate!
64
+ if self.database =~ /\W/
65
+ raise(
66
+ Ardb::ConfigurationError,
67
+ "database value must not contain non-word characters. "\
68
+ "Given: #{self.database.inspect}."
69
+ )
70
+ end
71
+ end
72
+
77
73
  def env_var_hash
78
74
  @env_var_hash ||= {
79
- 'PGHOST' => self.connect_hash['host'],
80
- 'PGPORT' => self.connect_hash['port'],
81
- 'PGUSER' => self.connect_hash['username'],
82
- 'PGPASSWORD' => self.connect_hash['password']
75
+ "PGHOST" => self.connect_hash["host"],
76
+ "PGPORT" => self.connect_hash["port"],
77
+ "PGUSER" => self.connect_hash["username"],
78
+ "PGPASSWORD" => self.connect_hash["password"]
83
79
  }
84
80
  end
85
-
86
81
  end
87
-
88
82
  end
@@ -1,11 +1,9 @@
1
- require 'fileutils'
2
- require 'ardb'
3
- require 'ardb/adapter/base'
1
+ require "fileutils"
2
+ require "ardb"
3
+ require "ardb/adapter/base"
4
4
 
5
5
  module Ardb::Adapter
6
-
7
- class Sqlite < Base
8
-
6
+ class Sqlite < Ardb::Adapter::Base
9
7
  def db_file_path
10
8
  File.expand_path(self.database, self.config.root_path)
11
9
  end
@@ -25,7 +23,5 @@ module Ardb::Adapter
25
23
  def drop_db
26
24
  FileUtils.rm(self.db_file_path) if File.exist?(self.db_file_path)
27
25
  end
28
-
29
26
  end
30
-
31
27
  end
@@ -1,10 +1,8 @@
1
- require 'ardb'
2
- require 'ardb/adapter/base'
1
+ require "ardb"
2
+ require "ardb/adapter/base"
3
3
 
4
4
  module Ardb
5
-
6
5
  class AdapterSpy < Ardb::Adapter::Base
7
-
8
6
  attr_accessor :drop_tables_called_count
9
7
  attr_accessor :dump_schema_called_count, :load_schema_called_count
10
8
  attr_accessor :drop_db_called_count, :create_db_called_count
@@ -51,16 +49,6 @@ module Ardb
51
49
 
52
50
  # Overwritten `Adapter::Base` methods
53
51
 
54
- def foreign_key_add_sql
55
- "FAKE ADD FOREIGN KEY SQL :from_table :from_column " \
56
- ":to_table :to_column :name"
57
- end
58
-
59
- def foreign_key_drop_sql
60
- "FAKE DROP FOREIGN KEY SQL :from_table :from_column " \
61
- ":to_table :to_column :name"
62
- end
63
-
64
52
  def create_db(*args, &block)
65
53
  self.create_db_called_count += 1
66
54
  end
@@ -88,7 +76,5 @@ module Ardb
88
76
  def dump_schema(*args, &block)
89
77
  self.dump_schema_called_count += 1
90
78
  end
91
-
92
79
  end
93
-
94
80
  end
@@ -1,17 +1,19 @@
1
- require 'ardb/cli/clirb'
2
- require 'ardb/cli/commands'
3
- require 'ardb/version'
1
+ require "ardb/cli/clirb"
2
+ require "ardb/cli/commands"
3
+ require "ardb/version"
4
4
 
5
5
  module Ardb
6
-
7
6
  class CLI
8
-
9
7
  COMMANDS = CommandSet.new{ |unknown| InvalidCommand.new(unknown) }.tap do |c|
10
- c.add(ConnectCommand, 'connect')
11
- c.add(CreateCommand, 'create')
12
- c.add(DropCommand, 'drop')
13
- c.add(MigrateCommand, 'migrate')
14
- c.add(GenerateMigrationCommand, 'generate-migration')
8
+ c.add(ConnectCommand)
9
+ c.add(CreateCommand)
10
+ c.add(DropCommand)
11
+ c.add(GenerateMigrationCommand)
12
+ c.add(MigrateCommand)
13
+ c.add(MigrateUpCommand)
14
+ c.add(MigrateDownCommand)
15
+ c.add(MigrateForwardCommand)
16
+ c.add(MigrateBackwardCommand)
15
17
  end
16
18
 
17
19
  def self.run(args)
@@ -32,13 +34,13 @@ module Ardb
32
34
  cmd = COMMANDS[cmd_name]
33
35
  cmd.run(args)
34
36
  rescue CLIRB::HelpExit
35
- @stdout.puts cmd.help
37
+ @stdout.puts cmd.command_help
36
38
  rescue CLIRB::VersionExit
37
39
  @stdout.puts Ardb::VERSION
38
40
  rescue CLIRB::Error, ArgumentError, InvalidCommandError => exception
39
41
  display_debug(exception)
40
42
  @stderr.puts "#{exception.message}\n\n"
41
- @stdout.puts cmd.help
43
+ @stdout.puts cmd.command_help
42
44
  @kernel.exit 1
43
45
  rescue CommandExitError
44
46
  @kernel.exit 1
@@ -53,12 +55,10 @@ module Ardb
53
55
  private
54
56
 
55
57
  def display_debug(exception)
56
- if ENV['DEBUG']
58
+ if ENV["DEBUG"]
57
59
  @stderr.puts "#{exception.class}: #{exception.message}"
58
60
  @stderr.puts exception.backtrace.join("\n")
59
61
  end
60
62
  end
61
-
62
63
  end
63
-
64
64
  end
@@ -1,20 +1,19 @@
1
1
  module Ardb; end
2
2
  class Ardb::CLI
3
-
4
- class CLIRB # Version 1.0.0, https://github.com/redding/cli.rb
3
+ class CLIRB # Version 1.1.0, https://github.com/redding/cli.rb
5
4
  Error = Class.new(RuntimeError);
6
5
  HelpExit = Class.new(RuntimeError); VersionExit = Class.new(RuntimeError)
7
6
  attr_reader :argv, :args, :opts, :data
8
7
 
9
8
  def initialize(&block)
10
9
  @options = []; instance_eval(&block) if block
11
- require 'optparse'
10
+ require "optparse"
12
11
  @data, @args, @opts = [], [], {}; @parser = OptionParser.new do |p|
13
- p.banner = ''; @options.each do |o|
12
+ p.banner = ""; @options.each do |o|
14
13
  @opts[o.name] = o.value; p.on(*o.parser_args){ |v| @opts[o.name] = v }
15
14
  end
16
- p.on_tail('--version', ''){ |v| raise VersionExit, v.to_s }
17
- p.on_tail('--help', ''){ |v| raise HelpExit, v.to_s }
15
+ p.on_tail("--version", ""){ |v| raise VersionExit, v.to_s }
16
+ p.on_tail("--help", ""){ |v| raise HelpExit, v.to_s }
18
17
  end
19
18
  end
20
19
 
@@ -27,33 +26,31 @@ class Ardb::CLI
27
26
  end
28
27
  def to_s; @parser.to_s; end
29
28
  def inspect
30
- "#<#{self.class}:#{'0x0%x' % (object_id << 1)} @data=#{@data.inspect}>"
29
+ "#<#{self.class}:#{"0x0%x" % (object_id << 1)} @data=#{@data.inspect}>"
31
30
  end
32
31
 
33
32
  class Option
34
33
  attr_reader :name, :opt_name, :desc, :abbrev, :value, :klass, :parser_args
35
34
 
36
- def initialize(name, *args)
37
- settings, @desc = args.last.kind_of?(::Hash) ? args.pop : {}, args.pop || ''
38
- @name, @opt_name, @abbrev = parse_name_values(name, settings[:abbrev])
39
- @value, @klass = gvalinfo(settings[:value])
35
+ def initialize(name, desc = nil, abbrev: nil, value: nil)
36
+ @name, @desc = name, desc || ""
37
+ @opt_name, @abbrev = parse_name_values(name, abbrev)
38
+ @value, @klass = gvalinfo(value)
40
39
  @parser_args = if [TrueClass, FalseClass, NilClass].include?(@klass)
41
40
  ["-#{@abbrev}", "--[no-]#{@opt_name}", @desc]
42
41
  else
43
- ["-#{@abbrev}", "--#{@opt_name} #{@opt_name.upcase}", @klass, @desc]
42
+ ["-#{@abbrev}", "--#{@opt_name} VALUE", @klass, @desc]
44
43
  end
45
44
  end
46
45
 
47
46
  private
48
47
 
49
48
  def parse_name_values(name, custom_abbrev)
50
- [ (processed_name = name.to_s.strip.downcase), processed_name.gsub('_', '-'),
51
- custom_abbrev || processed_name.gsub(/[^a-z]/, '').chars.first || 'a'
49
+ [ (processed_name = name.to_s.strip.downcase).gsub("_", "-"),
50
+ custom_abbrev || processed_name.gsub(/[^a-z]/, "").chars.first || "a"
52
51
  ]
53
52
  end
54
- def gvalinfo(v); v.kind_of?(Class) ? [nil,gklass(v)] : [v,gklass(v.class)]; end
55
- def gklass(k); k == Fixnum ? Integer : k; end
53
+ def gvalinfo(v); v.kind_of?(Class) ? [nil,v] : [v,v.class]; end
56
54
  end
57
55
  end
58
-
59
56
  end
@@ -1,15 +1,13 @@
1
- require 'ardb'
2
- require 'ardb/cli/clirb'
3
- require 'much-plugin'
1
+ require "ardb"
2
+ require "ardb/cli/clirb"
3
+ require "much-plugin"
4
4
 
5
5
  module Ardb; end
6
6
  class Ardb::CLI
7
-
8
7
  InvalidCommandError = Class.new(ArgumentError)
9
8
  CommandExitError = Class.new(RuntimeError)
10
9
 
11
10
  class InvalidCommand
12
-
13
11
  attr_reader :name, :clirb
14
12
 
15
13
  def initialize(name)
@@ -22,27 +20,26 @@ class Ardb::CLI
22
20
  def run(argv)
23
21
  @clirb.parse!([@name, argv].flatten.compact)
24
22
  raise CLIRB::HelpExit if @name.to_s.empty?
25
- raise InvalidCommandError, "'#{self.name}' is not a command."
23
+ raise InvalidCommandError, "\"#{self.name}\" is not a command."
26
24
  end
27
25
 
28
- def help
26
+ def command_help
29
27
  "Usage: ardb [COMMAND] [options]\n\n" \
30
28
  "Options: #{@clirb}\n" \
31
29
  "Commands:\n" \
32
30
  "#{COMMANDS.to_s.split("\n").map{ |l| " #{l}" }.join("\n")}\n"
33
31
  end
34
-
35
32
  end
36
33
 
37
34
  module ValidCommand
38
35
  include MuchPlugin
39
36
 
40
- plugin_included do
41
- include InstanceMethods
37
+ plugin_class_methods do
38
+ def command_name; raise NotImplementedError; end
39
+ def command_summary; ""; end
42
40
  end
43
41
 
44
- module InstanceMethods
45
-
42
+ plugin_instance_methods do
46
43
  def initialize(&clirb_build)
47
44
  @clirb = CLIRB.new(&clirb_build)
48
45
  end
@@ -55,24 +52,34 @@ class Ardb::CLI
55
52
  @stderr = stderr || $stderr
56
53
  end
57
54
 
58
- def summary
59
- ''
60
- end
55
+ def command_name; self.class.command_name; end
56
+ def command_summary; self.class.command_summary; end
61
57
 
58
+ def command_help
59
+ "Usage: ardb #{self.command_name} [options]\n\n" \
60
+ "Options: #{self.clirb}\n" \
61
+ "Description:\n" \
62
+ " #{self.command_summary}"
63
+ end
62
64
  end
63
-
64
65
  end
65
66
 
66
67
  class ConnectCommand
67
68
  include ValidCommand
68
69
 
70
+ def self.command_name; "connect"; end
71
+ def self.command_summary; "Connect to the configured DB"; end
72
+
69
73
  def run(argv, *args)
70
74
  super
71
75
 
72
- Ardb.init(false)
73
76
  begin
77
+ Ardb.init(false)
74
78
  Ardb.adapter.connect_db
75
- @stdout.puts "connected to #{Ardb.config.adapter} db `#{Ardb.config.database}`"
79
+ @stdout.puts "connected to #{Ardb.config.adapter} db #{Ardb.config.database.inspect}"
80
+ rescue ActiveRecord::NoDatabaseError => e
81
+ @stderr.puts "error: database #{Ardb.config.database.inspect} "\
82
+ "does not exist"
76
83
  rescue StandardError => e
77
84
  @stderr.puts e
78
85
  @stderr.puts e.backtrace.join("\n")
@@ -81,150 +88,237 @@ class Ardb::CLI
81
88
  raise CommandExitError
82
89
  end
83
90
  end
84
-
85
- def summary
86
- "Connect to the configured DB"
87
- end
88
-
89
- def help
90
- "Usage: ardb connect [options]\n\n" \
91
- "Options: #{@clirb}\n" \
92
- "Description:\n" \
93
- " #{self.summary}"
94
- end
95
-
96
91
  end
97
92
 
98
93
  class CreateCommand
99
94
  include ValidCommand
100
95
 
96
+ def self.command_name; "create"; end
97
+ def self.command_summary; "Create the configured DB"; end
98
+
101
99
  def run(argv, *args)
102
100
  super
103
101
 
104
- Ardb.init(false)
105
102
  begin
103
+ Ardb.init(false)
106
104
  Ardb.adapter.create_db
107
- @stdout.puts "created #{Ardb.config.adapter} db `#{Ardb.config.database}`"
105
+ @stdout.puts "created #{Ardb.config.adapter} db #{Ardb.config.database.inspect}"
106
+ rescue ActiveRecord::StatementInvalid => e
107
+ @stderr.puts "error: database #{Ardb.config.database.inspect} "\
108
+ "already exists"
108
109
  rescue StandardError => e
109
110
  @stderr.puts e
110
111
  @stderr.puts "error creating #{Ardb.config.database.inspect} database"
111
112
  raise CommandExitError
112
113
  end
113
114
  end
114
-
115
- def summary
116
- "Create the configured DB"
117
- end
118
-
119
- def help
120
- "Usage: ardb create [options]\n\n" \
121
- "Options: #{@clirb}\n" \
122
- "Description:\n" \
123
- " #{self.summary}"
124
- end
125
-
126
115
  end
127
116
 
128
117
  class DropCommand
129
118
  include ValidCommand
130
119
 
120
+ def self.command_name; "drop"; end
121
+ def self.command_summary; "Drop the configured DB"; end
122
+
131
123
  def run(argv, *args)
132
124
  super
133
125
 
134
- Ardb.init(true)
135
126
  begin
127
+ Ardb.init(true)
136
128
  Ardb.adapter.drop_db
137
- @stdout.puts "dropped #{Ardb.config.adapter} db `#{Ardb.config.database}`"
129
+ @stdout.puts "dropped #{Ardb.config.adapter} db #{Ardb.config.database.inspect}"
130
+ rescue ActiveRecord::NoDatabaseError => e
131
+ @stderr.puts "error: database #{Ardb.config.database.inspect} "\
132
+ "does not exist"
138
133
  rescue StandardError => e
139
134
  @stderr.puts e
140
135
  @stderr.puts "error dropping #{Ardb.config.database.inspect} database"
141
136
  raise CommandExitError
142
137
  end
143
138
  end
144
-
145
- def summary
146
- "Drop the configured DB"
147
- end
148
-
149
- def help
150
- "Usage: ardb drop [options]\n\n" \
151
- "Options: #{@clirb}\n" \
152
- "Description:\n" \
153
- " #{self.summary}"
154
- end
155
-
156
139
  end
157
140
 
158
- class MigrateCommand
141
+ class GenerateMigrationCommand
159
142
  include ValidCommand
160
143
 
144
+ def self.command_name; "generate-migration"; end
145
+ def self.command_summary; "Generate a MIGRATION-NAME migration file"; end
146
+
161
147
  def run(argv, *args)
162
148
  super
163
149
 
164
- Ardb.init(true)
165
150
  begin
166
- Ardb.adapter.migrate_db
167
- Ardb.adapter.dump_schema unless ENV['ARDB_MIGRATE_NO_SCHEMA']
151
+ Ardb.init(false)
152
+
153
+ require "ardb/migration"
154
+ migration = Ardb::Migration.new(Ardb.config, @clirb.args.first)
155
+ migration.save!
156
+ @stdout.puts "generated #{migration.file_path}"
157
+ rescue Ardb::Migration::NoIdentifierError => exception
158
+ error = ArgumentError.new("MIGRATION-NAME must be provided")
159
+ error.set_backtrace(exception.backtrace)
160
+ raise error
168
161
  rescue StandardError => e
169
162
  @stderr.puts e
170
163
  @stderr.puts e.backtrace.join("\n")
171
- @stderr.puts "error migrating #{Ardb.config.database.inspect} database"
164
+ @stderr.puts "error generating migration"
172
165
  raise CommandExitError
173
166
  end
174
167
  end
175
168
 
176
- def summary
177
- "Migrate the configured DB"
169
+ def command_help
170
+ "Usage: ardb #{self.command_name} MIGRATION-NAME [options]\n\n" \
171
+ "Options: #{self.clirb}\n" \
172
+ "Description:\n" \
173
+ " #{self.command_summary}"
178
174
  end
175
+ end
179
176
 
180
- def help
181
- "Usage: ardb migrate [options]\n\n" \
182
- "Options: #{@clirb}\n" \
183
- "Description:\n" \
184
- " #{self.summary}"
177
+ module MigrateCommandBehaviors
178
+ include MuchPlugin
179
+
180
+ plugin_included do
181
+ include ValidCommand
185
182
  end
186
183
 
184
+ plugin_instance_methods do
185
+ def migrate; raise NotImplementedError; end
186
+
187
+ def run(argv, *args)
188
+ super
189
+
190
+ begin
191
+ Ardb.init(true)
192
+ self.migrate
193
+ Ardb.adapter.dump_schema unless ENV["ARDB_MIGRATE_NO_SCHEMA"]
194
+ rescue ActiveRecord::NoDatabaseError => e
195
+ @stderr.puts "error: database #{Ardb.config.database.inspect} "\
196
+ "does not exist"
197
+ rescue StandardError => e
198
+ @stderr.puts e
199
+ @stderr.puts e.backtrace.join("\n")
200
+ @stderr.puts "error migrating #{Ardb.config.database.inspect} database"
201
+ raise CommandExitError
202
+ end
203
+ end
204
+ end
187
205
  end
188
206
 
189
- class GenerateMigrationCommand
190
- include ValidCommand
207
+ class MigrateCommand
208
+ include MigrateCommandBehaviors
191
209
 
192
- def run(argv, *args)
193
- super
210
+ def self.command_name; "migrate"; end
211
+ def self.command_summary; "Migrate the configured DB"; end
194
212
 
195
- Ardb.init(false)
196
- begin
197
- require 'ardb/migration'
198
- migration = Ardb::Migration.new(Ardb.config, @clirb.args.first)
199
- migration.save!
200
- @stdout.puts "generated #{migration.file_path}"
201
- rescue Ardb::Migration::NoIdentifierError => exception
202
- error = ArgumentError.new("MIGRATION-NAME must be provided")
203
- error.set_backtrace(exception.backtrace)
204
- raise error
205
- rescue StandardError => e
206
- @stderr.puts e
207
- @stderr.puts e.backtrace.join("\n")
208
- @stderr.puts "error generating migration"
209
- raise CommandExitError
213
+ def migrate
214
+ Ardb.adapter.migrate_db
215
+ end
216
+ end
217
+
218
+ module MigrateStyleBehaviors
219
+ include MuchPlugin
220
+
221
+ plugin_included do
222
+ include MigrateCommandBehaviors
223
+ end
224
+
225
+ plugin_class_methods do
226
+ def command_style; raise NotImplementedError; end
227
+
228
+ def command_name; "migrate-#{self.command_style}"; end
229
+ def command_summary; "Migrate the configured DB #{self.command_style}"; end
230
+ end
231
+
232
+ plugin_instance_methods do
233
+ def migrate
234
+ Ardb.adapter.send("migrate_db_#{self.class.command_style}", *migrate_args)
210
235
  end
236
+
237
+ private
238
+
239
+ def migrate_args; raise NotImplementedError; end
211
240
  end
241
+ end
212
242
 
213
- def summary
214
- "Generate a migration file given a MIGRATION-NAME"
243
+ module MigrateDirectionBehaviors
244
+ include MuchPlugin
245
+
246
+ plugin_included do
247
+ include MigrateStyleBehaviors
215
248
  end
216
249
 
217
- def help
218
- "Usage: ardb generate-migration [options] MIGRATION-NAME\n\n" \
219
- "Options: #{@clirb}\n" \
220
- "Description:\n" \
221
- " #{self.summary}"
250
+ plugin_class_methods do
251
+ def command_style; self.command_direction; end
252
+ def command_direction; raise NotImplementedError; end
253
+ end
254
+
255
+ plugin_instance_methods do
256
+ def initialize
257
+ super do
258
+ option(:target_version, "version to migrate to", value: String)
259
+ end
260
+ end
261
+
262
+ private
263
+
264
+ def migrate_args
265
+ [@clirb.opts[:target_version]]
266
+ end
267
+ end
268
+ end
269
+
270
+ module MigrateStepDirectionBehaviors
271
+ include MuchPlugin
272
+
273
+ plugin_included do
274
+ include MigrateStyleBehaviors
275
+ end
276
+
277
+ plugin_class_methods do
278
+ def command_style; self.command_direction; end
279
+ def command_direction; raise NotImplementedError; end
222
280
  end
223
281
 
282
+ plugin_instance_methods do
283
+ def initialize
284
+ super do
285
+ option(:steps, "number of migrations to migrate", value: 1)
286
+ end
287
+ end
288
+
289
+ private
290
+
291
+ def migrate_args
292
+ [@clirb.opts[:steps]]
293
+ end
294
+ end
224
295
  end
225
296
 
226
- class CommandSet
297
+ class MigrateUpCommand
298
+ include MigrateDirectionBehaviors
299
+
300
+ def self.command_direction; "up"; end
301
+ end
302
+
303
+ class MigrateDownCommand
304
+ include MigrateDirectionBehaviors
227
305
 
306
+ def self.command_direction; "down"; end
307
+ end
308
+
309
+ class MigrateForwardCommand
310
+ include MigrateStepDirectionBehaviors
311
+
312
+ def self.command_direction; "forward"; end
313
+ end
314
+
315
+ class MigrateBackwardCommand
316
+ include MigrateStepDirectionBehaviors
317
+
318
+ def self.command_direction; "backward"; end
319
+ end
320
+
321
+ class CommandSet
228
322
  def initialize(&unknown_cmd_block)
229
323
  @lookup = Hash.new{ |h,k| unknown_cmd_block.call(k) }
230
324
  @names = []
@@ -232,29 +326,28 @@ class Ardb::CLI
232
326
  @summaries = {}
233
327
  end
234
328
 
235
- def add(klass, name, *aliases)
329
+ def add(klass)
236
330
  begin
237
331
  cmd = klass.new
238
- rescue StandardError => err
239
- # don't add any commands you can't init
332
+ rescue StandardError
333
+ # don"t add any commands you can"t initialize
240
334
  else
241
- ([name] + aliases).each{ |n| @lookup[n] = cmd }
335
+ @lookup[cmd.command_name] = cmd
242
336
  @to_s = nil
243
- @names << name
244
- @aliases[name] = aliases.empty? ? '' : "(#{aliases.join(', ')})"
245
- @summaries[name] = cmd.summary.to_s.empty? ? '' : "# #{cmd.summary}"
337
+ @names << cmd.command_name
338
+ @summaries[cmd.command_name] = cmd.command_summary.to_s
246
339
  end
247
340
  end
248
341
 
249
- def remove(name)
250
- @lookup.delete(name)
251
- @names.delete(name)
252
- @aliases.delete(name)
342
+ def remove(klass)
343
+ @lookup.delete(klass.command_name)
344
+ @names.delete(klass.command_name)
345
+ @summaries.delete(klass.command_name)
253
346
  @to_s = nil
254
347
  end
255
348
 
256
- def [](name)
257
- @lookup[name]
349
+ def [](cmd_name)
350
+ @lookup[cmd_name]
258
351
  end
259
352
 
260
353
  def size
@@ -263,13 +356,10 @@ class Ardb::CLI
263
356
 
264
357
  def to_s
265
358
  max_name_size = @names.map{ |n| n.size }.max || 0
266
- max_alias_size = @aliases.values.map{ |v| v.size }.max || 0
267
359
 
268
360
  @to_s ||= @names.map do |n|
269
- "#{n.ljust(max_name_size)} #{@aliases[n].ljust(max_alias_size)} #{@summaries[n]}"
361
+ "#{n.ljust(max_name_size)} #{@summaries[n]}"
270
362
  end.join("\n")
271
363
  end
272
-
273
364
  end
274
-
275
365
  end