ardb 0.27.3 → 0.28.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (43) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile +5 -1
  3. data/ardb.gemspec +3 -4
  4. data/lib/ardb.rb +135 -68
  5. data/lib/ardb/adapter/base.rb +39 -24
  6. data/lib/ardb/adapter/mysql.rb +1 -2
  7. data/lib/ardb/adapter/postgresql.rb +14 -12
  8. data/lib/ardb/adapter/sqlite.rb +3 -8
  9. data/lib/ardb/adapter_spy.rb +67 -87
  10. data/lib/ardb/cli.rb +11 -219
  11. data/lib/ardb/{clirb.rb → cli/clirb.rb} +2 -1
  12. data/lib/ardb/cli/commands.rb +275 -0
  13. data/lib/ardb/migration.rb +8 -6
  14. data/lib/ardb/migration_helpers.rb +1 -1
  15. data/lib/ardb/pg_json.rb +90 -0
  16. data/lib/ardb/version.rb +1 -1
  17. data/test/helper.rb +15 -3
  18. data/test/support/factory.rb +15 -0
  19. data/test/support/fake_schema.rb +5 -0
  20. data/test/support/postgresql/migrations/.gitkeep +0 -0
  21. data/test/support/postgresql/pg_json_migrations/20160519133432_create_pg_json_migrate_test.rb +13 -0
  22. data/test/support/postgresql/schema.rb +3 -0
  23. data/test/support/postgresql/setup_test_db.rb +51 -0
  24. data/test/support/relative_require_test_db_file.rb +2 -0
  25. data/test/support/require_test_db_file.rb +1 -0
  26. data/test/system/pg_json_tests.rb +85 -0
  27. data/test/unit/adapter/base_tests.rb +104 -39
  28. data/test/unit/adapter/mysql_tests.rb +2 -1
  29. data/test/unit/adapter/postgresql_tests.rb +10 -9
  30. data/test/unit/adapter/sqlite_tests.rb +8 -3
  31. data/test/unit/adapter_spy_tests.rb +57 -66
  32. data/test/unit/ardb_tests.rb +323 -36
  33. data/test/unit/cli_tests.rb +193 -146
  34. data/test/unit/has_slug_tests.rb +9 -9
  35. data/test/unit/migration_helpers_tests.rb +18 -12
  36. data/test/unit/migration_tests.rb +18 -11
  37. data/test/unit/pg_json_tests.rb +39 -0
  38. data/test/unit/record_spy_tests.rb +1 -1
  39. data/test/unit/test_helpers_tests.rb +2 -6
  40. data/test/unit/use_db_default_tests.rb +2 -2
  41. metadata +29 -34
  42. data/lib/ardb/root_path.rb +0 -15
  43. data/test/unit/config_tests.rb +0 -58
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: efed4edb38eade9eaa2de6263e30f163b4ffa3db
4
- data.tar.gz: 41bb43c7936a806124cdeb747d78c6a4de2393e4
3
+ data.tar.gz: 33194d527399daa093277e134bbcda8dee5c6069
4
+ metadata.gz: 7718eb32a25016d2d620a820b5a2b04244445b6a
5
5
  SHA512:
6
- metadata.gz: ba2ed871805feac6de258476b78dc906c63b6a4491b8ab24925dc87447e19f4e89f073b6642729d8ec863a3b760257293da95ed4aa7e8d2b3f7d09aedc86cefe
7
- data.tar.gz: f521735072f0ce180c0073593af0aefc5ae16cc3bcab8ed5c0dcf7677f1da6d818fa8b051c1f91fa07b18626582b4beeec021810a51223f6ca538d503c9fb7bb
6
+ data.tar.gz: 1597717aba91e83109628879b8ab51c52a81fe47c9f8182bf8bff32e91b7fe30f72a0543d97e1200b77926824b1d669790144fc3bf39bb9e9a30a95110192a69
7
+ metadata.gz: 02366a02d6aff1a513ea34969017cc07b56be184e906a7cb34b446811c6d4ebaa6fb97f6eddd04344b3f4d2dbca51624c4c57316f996e1b6b98809826e5d03f4
data/Gemfile CHANGED
@@ -2,8 +2,12 @@ source "https://rubygems.org"
2
2
 
3
3
  gemspec
4
4
 
5
- gem 'pry', "~> 0.9.0"
5
+ gem 'pg', "~> 0.17.1"
6
+ gem 'pry', "~> 0.9.0"
6
7
 
7
8
  # Lock down gem versions because they require a newer version of ruby
8
9
  gem 'i18n', "< 0.7"
9
10
 
11
+ platform :ruby_18 do
12
+ gem 'json', '~> 1.8'
13
+ end
@@ -18,12 +18,11 @@ Gem::Specification.new do |gem|
18
18
  gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
19
19
  gem.require_paths = ["lib"]
20
20
 
21
- gem.add_development_dependency("assert", ["~> 2.15.1"])
21
+ gem.add_development_dependency("assert", ["~> 2.16.1"])
22
22
 
23
23
  gem.add_dependency('activerecord', ["~> 3.2"])
24
24
  gem.add_dependency('activesupport', ["~> 3.2"])
25
- gem.add_dependency('much-plugin', ["~> 0.1.0"])
26
- gem.add_dependency('ns-options', ["~> 1.1.6"])
27
- gem.add_dependency('scmd', ["~> 3.0.1"])
25
+ gem.add_dependency('much-plugin', ["~> 0.1.1"])
26
+ gem.add_dependency('scmd', ["~> 3.0.2"])
28
27
 
29
28
  end
@@ -1,115 +1,182 @@
1
- require 'pathname'
2
- require 'singleton'
3
1
  require 'active_record'
4
- require 'ns-options'
2
+ require 'logger'
5
3
 
6
4
  require 'ardb/version'
7
- require 'ardb/root_path'
8
5
 
9
6
  ENV['ARDB_DB_FILE'] ||= 'config/db'
10
7
 
11
8
  module Ardb
12
- NotConfiguredError = Class.new(RuntimeError)
13
9
 
14
- def self.config; Config; end
15
- def self.configure(&block); Config.define(&block); end
16
-
17
- def self.adapter; Adapter.current; end
10
+ def self.config
11
+ @config ||= Config.new
12
+ end
18
13
 
19
- def self.validate!
20
- if !self.config.required_set?
21
- raise NotConfiguredError, "missing required configs"
22
- end
14
+ def self.configure(&block)
15
+ self.config.tap(&block)
23
16
  end
24
17
 
18
+ def self.adapter; @adapter; end
19
+
25
20
  def self.init(establish_connection = true)
26
21
  require 'ardb/require_autoloaded_active_record_files'
27
- require self.config.db_file
28
- validate!
29
- Adapter.init
22
+ begin
23
+ require_db_file
24
+ rescue InvalidDBFileError => exception
25
+ raise exception.tap{ |e| e.set_backtrace(caller) }
26
+ end
27
+
28
+ self.config.validate!
29
+ @adapter = Adapter.new(self.config)
30
30
 
31
31
  # setup AR
32
32
  ActiveRecord::Base.logger = self.config.logger
33
- if establish_connection
34
- ActiveRecord::Base.establish_connection(self.config.db_settings)
35
- end
33
+ self.adapter.connect_db if establish_connection
36
34
  end
37
35
 
38
36
  def self.escape_like_pattern(pattern, escape_char = nil)
39
37
  self.adapter.escape_like_pattern(pattern, escape_char)
40
38
  end
41
39
 
42
- class Config
43
- include NsOptions::Proxy
44
-
45
- namespace :db do
46
- option :adapter, String, :required => true
47
- option :database, String, :required => true
48
- option :encoding, String, :required => false
49
- option :host, String, :required => false
50
- option :port, Integer, :required => false
51
- option :username, String, :required => false
52
- option :password, String, :required => false
53
- option :pool, Integer, :required => false
54
- option :checkout_timeout, Integer, :required => false
40
+ private
41
+
42
+ # try requiring the db file via the load path or as an absolute path, if
43
+ # that fails it tries requiring relative to the current working directory
44
+ def self.require_db_file
45
+ begin
46
+ require ENV['ARDB_DB_FILE']
47
+ rescue LoadError
48
+ require File.expand_path(ENV['ARDB_DB_FILE'], ENV['PWD'])
55
49
  end
50
+ rescue LoadError
51
+ raise InvalidDBFileError, "can't require `#{ENV['ARDB_DB_FILE']}`, " \
52
+ "check that the ARDB_DB_FILE env var is set to " \
53
+ "the file path of your db file"
54
+ end
56
55
 
57
- option :db_file, Pathname, :default => ENV['ARDB_DB_FILE']
58
- option :root_path, Pathname, :required => true
59
- option :logger, :required => true
60
- option :migrations_path, RootPath, :default => proc{ "db/migrations" }
61
- option :schema_path, RootPath, :default => proc{ "db/schema" }
62
- option :schema_format, Symbol, :default => :ruby
63
-
64
- def self.db_settings
65
- db.to_hash.inject({}) do |settings, (k, v)|
66
- settings[k.to_s] = v if !v.nil?
67
- settings
68
- end
56
+ class Config
57
+
58
+ ACTIVERECORD_ATTRS = [
59
+ :adapter,
60
+ :database,
61
+ :encoding,
62
+ :host,
63
+ :port,
64
+ :username,
65
+ :password,
66
+ :pool,
67
+ :checkout_timeout,
68
+ :min_messages
69
+ ].freeze
70
+ DEFAULT_MIGRATIONS_PATH = 'db/migrations'.freeze
71
+ DEFAULT_SCHEMA_PATH = 'db/schema'.freeze
72
+ RUBY_SCHEMA_FORMAT = :ruby.freeze
73
+ SQL_SCHEMA_FORMAT = :sql.freeze
74
+ VALID_SCHEMA_FORMATS = [RUBY_SCHEMA_FORMAT, SQL_SCHEMA_FORMAT].freeze
75
+
76
+ attr_accessor *ACTIVERECORD_ATTRS
77
+ attr_accessor :logger, :root_path
78
+ attr_reader :schema_format
79
+ attr_writer :migrations_path, :schema_path
80
+
81
+ def initialize
82
+ @logger = Logger.new(STDOUT)
83
+ @root_path = ENV['PWD']
84
+ @migrations_path = DEFAULT_MIGRATIONS_PATH
85
+ @schema_path = DEFAULT_SCHEMA_PATH
86
+ @schema_format = RUBY_SCHEMA_FORMAT
69
87
  end
70
88
 
71
- end
89
+ def migrations_path
90
+ File.expand_path(@migrations_path.to_s, @root_path.to_s)
91
+ end
72
92
 
73
- class Adapter
74
- include Singleton
93
+ def schema_path
94
+ File.expand_path(@schema_path.to_s, @root_path.to_s)
95
+ end
75
96
 
76
- attr_accessor :current
97
+ def schema_format=(new_value)
98
+ @schema_format = begin
99
+ new_value.to_sym
100
+ rescue NoMethodError
101
+ raise ArgumentError, "schema format must be a `Symbol`", caller
102
+ end
103
+ end
77
104
 
78
- def init
79
- @current = Adapter.send(Ardb.config.db.adapter)
105
+ def activerecord_connect_hash
106
+ ACTIVERECORD_ATTRS.inject({}) do |h, attr_name|
107
+ value = self.send(attr_name)
108
+ !value.nil? ? h.merge!(attr_name.to_s => value) : h
109
+ end
80
110
  end
81
111
 
82
- def reset
83
- @current = nil
112
+ def validate!
113
+ if self.adapter.to_s.empty? || self.database.to_s.empty?
114
+ raise ConfigurationError, "an adapter and database must be provided"
115
+ elsif !VALID_SCHEMA_FORMATS.include?(self.schema_format)
116
+ raise ConfigurationError, "schema format must be one of: " \
117
+ "#{VALID_SCHEMA_FORMATS.join(', ')}"
118
+ end
119
+ true
84
120
  end
85
121
 
86
- def sqlite
87
- require 'ardb/adapter/sqlite'
88
- Adapter::Sqlite.new
122
+ def ==(other)
123
+ if other.kind_of?(self.class)
124
+ self.activerecord_connect_hash == other.activerecord_connect_hash &&
125
+ self.logger == other.logger &&
126
+ self.root_path == other.root_path &&
127
+ self.schema_format == other.schema_format &&
128
+ self.migrations_path == other.migrations_path &&
129
+ self.schema_path == other.schema_path
130
+ else
131
+ super
132
+ end
89
133
  end
90
- alias_method :sqlite3, :sqlite
91
134
 
92
- def postgresql
93
- require 'ardb/adapter/postgresql'
94
- Adapter::Postgresql.new
135
+ end
136
+
137
+ module Adapter
138
+
139
+ VALID_ADAPTERS = [
140
+ 'sqlite',
141
+ 'sqlite3',
142
+ 'postgresql',
143
+ 'postgres',
144
+ 'mysql',
145
+ 'mysql2'
146
+ ].freeze
147
+
148
+ def self.new(config)
149
+ if !VALID_ADAPTERS.include?(config.adapter)
150
+ raise InvalidAdapterError, "invalid adapter: `#{config.adapter}`"
151
+ end
152
+ self.send(config.adapter, config)
95
153
  end
96
154
 
97
- def mysql
98
- require 'ardb/adapter/mysql'
99
- Adapter::Mysql.new
155
+ def self.sqlite(config)
156
+ require 'ardb/adapter/sqlite'
157
+ Adapter::Sqlite.new(config)
100
158
  end
101
- alias_method :mysql2, :mysql
102
159
 
103
- # nice singleton api
160
+ def self.sqlite3(config); self.sqlite(config); end
104
161
 
105
- def self.method_missing(method, *args, &block)
106
- self.instance.send(method, *args, &block)
162
+ def self.postgresql(config)
163
+ require 'ardb/adapter/postgresql'
164
+ Adapter::Postgresql.new(config)
107
165
  end
108
166
 
109
- def self.respond_to?(method)
110
- super || self.instance.respond_to?(method)
167
+ def self.postgres(config); self.postgresql(config); end
168
+
169
+ def self.mysql(config)
170
+ require 'ardb/adapter/mysql'
171
+ Adapter::Mysql.new(config)
111
172
  end
112
173
 
174
+ def self.mysql2(config); self.mysql(config); end
175
+
113
176
  end
114
177
 
178
+ InvalidDBFileError = Class.new(ArgumentError)
179
+ ConfigurationError = Class.new(ArgumentError)
180
+ InvalidAdapterError = Class.new(RuntimeError)
181
+
115
182
  end
@@ -1,18 +1,27 @@
1
+ require 'ardb'
2
+
1
3
  module Ardb; end
2
- class Ardb::Adapter
4
+ module Ardb::Adapter
3
5
 
4
6
  class Base
5
7
 
6
- attr_reader :config_settings, :database
7
- attr_reader :schema_format, :ruby_schema_path, :sql_schema_path
8
+ attr_reader :config
9
+
10
+ def initialize(config)
11
+ @config = config
12
+ end
13
+
14
+ def connect_hash; self.config.activerecord_connect_hash; end
15
+ def database; self.config.database; end
16
+ def migrations_path; self.config.migrations_path; end
17
+ def schema_format; self.config.schema_format; end
18
+
19
+ def ruby_schema_path
20
+ @ruby_schema_path ||= "#{self.config.schema_path}.rb"
21
+ end
8
22
 
9
- def initialize
10
- @config_settings = Ardb.config.db_settings
11
- @database = Ardb.config.db.database
12
- @schema_format = Ardb.config.schema_format
13
- schema_path = Ardb.config.schema_path
14
- @ruby_schema_path = "#{schema_path}.rb"
15
- @sql_schema_path = "#{schema_path}.sql"
23
+ def sql_schema_path
24
+ @sql_schema_path ||= "#{self.config.schema_path}.sql"
16
25
  end
17
26
 
18
27
  def escape_like_pattern(pattern, escape_char = nil)
@@ -29,14 +38,18 @@ class Ardb::Adapter
29
38
  def create_db(*args); raise NotImplementedError; end
30
39
  def drop_db(*args); raise NotImplementedError; end
31
40
 
41
+ def drop_tables(*args); raise NotImplementedError; end
42
+
32
43
  def connect_db
33
- ActiveRecord::Base.connection
44
+ ActiveRecord::Base.establish_connection(self.connect_hash)
45
+ # checkout a connection to ensure we can connect to the DB, we don't do
46
+ # anything with the connection and immediately check it back in
47
+ ActiveRecord::Base.connection_pool.with_connection{ }
34
48
  end
35
49
 
36
50
  def migrate_db
37
51
  verbose = ENV["MIGRATE_QUIET"].nil?
38
52
  version = ENV["MIGRATE_VERSION"] ? ENV["MIGRATE_VERSION"].to_i : nil
39
- migrations_path = Ardb.config.migrations_path
40
53
 
41
54
  if defined?(ActiveRecord::Migration::CommandRecorder)
42
55
  require 'ardb/migration_helpers'
@@ -45,26 +58,24 @@ class Ardb::Adapter
45
58
  end
46
59
  end
47
60
 
48
- ActiveRecord::Migrator.migrations_path = migrations_path
61
+ ActiveRecord::Migrator.migrations_path = self.migrations_path
49
62
  ActiveRecord::Migration.verbose = verbose
50
- ActiveRecord::Migrator.migrate(migrations_path, version) do |migration|
63
+ ActiveRecord::Migrator.migrate(self.migrations_path, version) do |migration|
51
64
  ENV["MIGRATE_SCOPE"].blank? || (ENV["MIGRATE_SCOPE"] == migration.scope)
52
65
  end
53
66
  end
54
67
 
55
- def drop_tables(*args); raise NotImplementedError; end
56
-
57
68
  def load_schema
58
69
  # silence STDOUT
59
70
  current_stdout = $stdout.dup
60
71
  $stdout = File.new('/dev/null', 'w')
61
- load_ruby_schema if @schema_format == :ruby
62
- load_sql_schema if @schema_format == :sql
72
+ load_ruby_schema if self.schema_format == Ardb::Config::RUBY_SCHEMA_FORMAT
73
+ load_sql_schema if self.schema_format == Ardb::Config::SQL_SCHEMA_FORMAT
63
74
  $stdout = current_stdout
64
75
  end
65
76
 
66
77
  def load_ruby_schema
67
- load @ruby_schema_path
78
+ load self.ruby_schema_path
68
79
  end
69
80
 
70
81
  def load_sql_schema
@@ -76,14 +87,14 @@ class Ardb::Adapter
76
87
  current_stdout = $stdout.dup
77
88
  $stdout = File.new('/dev/null', 'w')
78
89
  dump_ruby_schema
79
- dump_sql_schema if @schema_format == :sql
90
+ dump_sql_schema if self.schema_format == Ardb::Config::SQL_SCHEMA_FORMAT
80
91
  $stdout = current_stdout
81
92
  end
82
93
 
83
94
  def dump_ruby_schema
84
95
  require 'active_record/schema_dumper'
85
- FileUtils.mkdir_p File.dirname(@ruby_schema_path)
86
- File.open(@ruby_schema_path, 'w:utf-8') do |file|
96
+ FileUtils.mkdir_p File.dirname(self.ruby_schema_path)
97
+ File.open(self.ruby_schema_path, 'w:utf-8') do |file|
87
98
  ActiveRecord::SchemaDumper.dump(ActiveRecord::Base.connection, file)
88
99
  end
89
100
  end
@@ -92,8 +103,12 @@ class Ardb::Adapter
92
103
  raise NotImplementedError
93
104
  end
94
105
 
95
- def ==(other_adapter)
96
- self.class == other_adapter.class
106
+ def ==(other)
107
+ if other.kind_of?(self.class)
108
+ self.config == other.config
109
+ else
110
+ super
111
+ end
97
112
  end
98
113
 
99
114
  end
@@ -1,7 +1,6 @@
1
- require 'ardb'
2
1
  require 'ardb/adapter/base'
3
2
 
4
- class Ardb::Adapter
3
+ module Ardb::Adapter
5
4
 
6
5
  class Mysql < Base
7
6
 
@@ -1,26 +1,28 @@
1
- require 'ardb'
2
1
  require 'ardb/adapter/base'
3
2
 
4
- class Ardb::Adapter
3
+ module Ardb::Adapter
5
4
 
6
5
  class Postgresql < Base
7
6
 
8
- def public_schema_settings
9
- self.config_settings.merge({
7
+ # the 'postgres' db is a "public" (doesn't typically require auth/grants to
8
+ # connect to) db that typically exists for all postgres installations; the
9
+ # adapter uses it to create/drop other databases
10
+ def public_connect_hash
11
+ @public_connect_hash ||= self.connect_hash.merge({
10
12
  'database' => 'postgres',
11
13
  'schema_search_path' => 'public'
12
14
  })
13
15
  end
14
16
 
15
17
  def create_db
16
- ActiveRecord::Base.establish_connection(self.public_schema_settings)
17
- ActiveRecord::Base.connection.create_database(self.database, self.config_settings)
18
- ActiveRecord::Base.establish_connection(self.config_settings)
18
+ ActiveRecord::Base.establish_connection(self.public_connect_hash)
19
+ ActiveRecord::Base.connection.create_database(self.database, self.connect_hash)
20
+ ActiveRecord::Base.establish_connection(self.connect_hash)
19
21
  end
20
22
 
21
23
  def drop_db
22
24
  begin
23
- ActiveRecord::Base.establish_connection(self.public_schema_settings)
25
+ ActiveRecord::Base.establish_connection(self.public_connect_hash)
24
26
  ActiveRecord::Base.connection.tap do |conn|
25
27
  conn.execute "UPDATE pg_catalog.pg_database"\
26
28
  " SET datallowconn=false WHERE datname='#{self.database}'"
@@ -74,10 +76,10 @@ class Ardb::Adapter
74
76
 
75
77
  def env_var_hash
76
78
  @env_var_hash ||= {
77
- 'PGHOST' => self.config_settings['host'],
78
- 'PGPORT' => self.config_settings['port'],
79
- 'PGUSER' => self.config_settings['username'],
80
- 'PGPASSWORD' => self.config_settings['password']
79
+ 'PGHOST' => self.connect_hash['host'],
80
+ 'PGPORT' => self.connect_hash['port'],
81
+ 'PGUSER' => self.connect_hash['username'],
82
+ 'PGPASSWORD' => self.connect_hash['password']
81
83
  }
82
84
  end
83
85