storey 0.4.2 → 0.5.0

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: ae5569721f64878b097e1b98b07c035fb36a9d71
4
- data.tar.gz: 4dca637ae254650c11a1140143b3242923eb015c
3
+ metadata.gz: f43d4bfca6dbcd859c187fb920e7e296d6c1809b
4
+ data.tar.gz: 322eb8906efac105f0a0c153bc4d8c47209eea28
5
5
  SHA512:
6
- metadata.gz: e62da00edaffe209439f36791dca9709cd9442b3c44a4046071cb7331ed6d0cf2bd3d4cf859da83b6c367673141dd635f558f36eb47ad5f2edb1f8384015a613
7
- data.tar.gz: 2e5ae11654ed101a414c955e00d71cfd3e79c3341509c6a9b1b17c536d79da84c4cdfcb5f494a1161b8bb20827eb31c659f53822296e00c5b94d60031d8c6d14
6
+ metadata.gz: 27fb55a9705fe0a214855fb5c9a6a08f8a0fc4983cb877daff4daf2c2e0bdceab06ed6cdb006e95e72e4c200dc706198ea872f56f78524198aafcf87b02157d3
7
+ data.tar.gz: e29bed608be63691d0d7a62efa278b8b57eb7ea82d0154665522a479cd74c14fc61e40eac2e7712b4407ef74806c60a426345b9385a0d512aaa3780b55987171
data/CHANGELOG.md CHANGED
@@ -1,3 +1,9 @@
1
+ # v0.5.0
2
+
3
+ - Removed attr_accessors from Storey::Duplicator (they were never meant to be part of the public API anyway)
4
+ - Validate the schema name when creating schemas
5
+ - Clean source and target files after duplicating [#18](https://github.com/ramontayag/storey/issues/18)
6
+
1
7
  # v0.4.2
2
8
 
3
9
  - `rake storey:migrate VERSION=xxxx` now works and uses the `VERSION` environment variable
@@ -0,0 +1,36 @@
1
+ module Storey
2
+ class BuildsDumpCommand
3
+
4
+ easy_class_to_instance
5
+
6
+ def initialize(options={})
7
+ @options = options
8
+ if @options[:database].blank?
9
+ raise ArgumentError, 'database must be supplied'
10
+ end
11
+ end
12
+
13
+ def execute
14
+
15
+ switches = {}
16
+ switches['schema-only'] = nil if @options[:structure_only]
17
+ switches['no-privileges'] = nil
18
+ switches['no-owner'] = nil
19
+ switches[:file] = Shellwords.escape(@options[:file])
20
+
21
+ if @options[:schemas]
22
+ schemas = @options[:schemas].split(',')
23
+ schemas_switches = schemas.map do |part|
24
+ Utils.command_line_switches_from({schema: Shellwords.escape(part) })
25
+ end
26
+ end
27
+
28
+ command_parts = ['pg_dump',
29
+ Utils.command_line_switches_from(switches),
30
+ schemas_switches,
31
+ @options[:database]]
32
+ command_parts.compact.join(' ')
33
+ end
34
+
35
+ end
36
+ end
@@ -0,0 +1,31 @@
1
+ module Storey
2
+ class BuildsLoadCommand
3
+
4
+ easy_class_to_instance
5
+
6
+ def initialize(options={})
7
+ @options = options
8
+ end
9
+
10
+ def execute
11
+ switches = {}
12
+ if @options[:file].present?
13
+ switches[:file] = Shellwords.escape(@options[:file])
14
+ end
15
+ switches[:dbname] = @options[:database]
16
+ switches[:username] = @options[:username] if @options[:username].present?
17
+ switches[:host] = @options[:host] if @options[:host].present?
18
+ switches[:port] = @options[:port] if @options[:port].present?
19
+ if @options[:password].present?
20
+ switches[:password] = @options[:password]
21
+ else
22
+ switches['no-password'] = nil
23
+ end
24
+ switches[:command] = %Q("#{@options[:command]}") if @options[:command].present?
25
+ command_parts = ['psql',
26
+ Utils.command_line_switches_from(switches)]
27
+ command_parts.join(' ')
28
+ end
29
+
30
+ end
31
+ end
data/lib/storey/dumper.rb CHANGED
@@ -1,12 +1,9 @@
1
1
  module Storey
2
- class Dumper
2
+ class Dumper
3
3
 
4
+ easy_class_to_instance
4
5
  delegate :dump, to: :dumper
5
6
 
6
- def self.dump(*args)
7
- self.new(*args).dump
8
- end
9
-
10
7
  def initialize(options={})
11
8
  @options = options
12
9
  end
@@ -17,10 +14,9 @@ module Storey
17
14
 
18
15
  def dumper_class
19
16
  schema_format = Rails.configuration.active_record.schema_format || :ruby
20
- klass = case schema_format
21
- when :sql; SqlDumper
22
- when :ruby; RubyDumper
23
- end
17
+ class_name = "#{schema_format.to_s.classify}Dumper"
18
+ namespace = self.class.name.deconstantize.constantize
19
+ namespace.const_get class_name
24
20
  end
25
21
 
26
22
  end
@@ -1,86 +1,84 @@
1
1
  module Storey
2
2
  class Duplicator
3
3
 
4
- attr_accessor(:source_schema,
5
- :target_schema,
6
- :source_file,
7
- :target_file,
8
- :structure_only,
9
- :file_prefix,
10
- :dump_path,
11
- :source_dump_path,
12
- :target_dump_path)
13
-
14
4
  def initialize(from_schema, to_schema, options={})
15
5
  unless from_schema
16
6
  fail SchemaNotFound, "cannot duplicate from nil schema"
17
7
  end
18
8
 
19
- self.dump_path = File.join Rails.root, 'tmp', 'schema_dumps'
20
- self.source_dump_path = File.join self.dump_path, 'source'
21
- self.target_dump_path = File.join self.dump_path, 'target'
22
- self.structure_only = options[:structure_only] || false
23
-
24
- self.source_schema = suffixify(from_schema)
25
- self.target_schema = suffixify(to_schema)
26
- self.file_prefix = "#{Time.now.to_i}_#{rand(100000)}"
27
- self.source_file = File.join(
28
- self.source_dump_path,
29
- "#{self.file_prefix}_#{self.source_schema}.sql"
30
- )
31
- self.target_file = File.join(
32
- self.target_dump_path,
33
- "#{self.file_prefix}_#{self.target_schema}.sql"
34
- )
9
+ @dump_dir = File.join(Rails.root, 'tmp', 'schema_dumps')
10
+ @source_dump_dir = File.join(@dump_dir, 'source')
11
+ @target_dump_dir = File.join(@dump_dir, 'target')
12
+ @structure_only = options[:structure_only] || false
13
+
14
+ @source_schema = suffixify(from_schema)
15
+ @target_schema = suffixify(to_schema)
16
+ @file_prefix = "#{Time.now.to_i}_#{rand(100000)}"
17
+ @source_file = File.join(@source_dump_dir,
18
+ "#{@file_prefix}_#{@source_schema}.sql")
19
+ @target_file = File.join(@target_dump_dir,
20
+ "#{@file_prefix}_#{@target_schema}.sql")
35
21
  end
36
22
 
37
23
  def perform!
38
24
  dump_schema
39
25
  replace_occurrences
40
26
  load_schema
27
+ clean_source
28
+ clean_target
41
29
  end
42
30
 
43
31
  private
44
32
 
33
+ def clean_source
34
+ FileUtils.rm(@source_file)
35
+ end
36
+
37
+ def clean_target
38
+ FileUtils.rm(@target_file)
39
+ end
40
+
45
41
  def dump_schema(options={})
46
42
  ENV['PGPASSWORD'] = Storey.database_config[:password]
47
43
  prepare_schema_dump_directories
48
44
 
49
- unless Storey.database_config[:host].blank?
45
+ if Storey.database_config[:host].present?
50
46
  options[:host] ||= Storey.database_config[:host]
51
47
  end
52
- options[:username] ||= Storey.database_config[:username]
53
- options[:file] ||= self.source_file
54
- options[:schema] ||= self.source_schema
55
48
 
56
- switches = options.map { |k, v| "--#{k}=#{v}" }
57
- switches << '--schema-only' if self.structure_only
58
- switches = switches.join(" ")
49
+ arg_options = options.dup
50
+ arg_options[:username] ||= Storey.database_config[:username]
51
+ arg_options[:file] ||= @source_file
52
+ arg_options[:schema] ||= @source_schema
53
+ arg_options['schema-only'] = nil if @structure_only
54
+
55
+ switches = Utils.command_line_switches_from(arg_options)
59
56
 
60
57
  success = system("pg_dump #{switches} #{Storey.database_config[:database]}")
61
58
  unless success
62
- raise StoreyError, "There seems to have been a problem dumping `#{self.source_schema}` to make a copy of it into `#{self.target_schema}`"
59
+ raise StoreyError, "There seems to have been a problem dumping `#{@source_schema}` to make a copy of it into `#{@target_schema}`"
63
60
  end
64
61
  end
65
62
 
66
63
  def prepare_schema_dump_directories
67
- [self.source_dump_path, self.target_dump_path].each do |d|
64
+ [@source_dump_dir, @target_dump_dir].each do |d|
68
65
  FileUtils.mkdir_p(d)
69
66
  end
70
67
  end
71
68
 
72
69
  def load_schema(options={})
73
- options[:file] ||= self.target_file
74
- switches = Storey.command_line_switches(options)
70
+ options[:file] ||= @target_file
71
+ psql_options = Storey.database_config.merge(options)
75
72
 
76
73
  if duplicating_from_default?
77
74
  # Since we are copying the source schema and we're after structure only,
78
75
  # the dump_schema ended up creating a SQL file without the "CREATE SCHEMA" command
79
76
  # thus we have to create it manually
80
- ::Storey.create_plain_schema self.target_schema
77
+ ::Storey.create_plain_schema @target_schema
81
78
  end
82
79
 
83
- `psql #{switches}`
80
+ psql_load_command = BuildsLoadCommand.execute(psql_options)
81
+ system psql_load_command
84
82
 
85
83
  copy_source_schema_migrations
86
84
 
@@ -88,7 +86,7 @@ module Storey
88
86
  end
89
87
 
90
88
  def copy_source_schema_migrations
91
- ::Storey.switch self.target_schema do
89
+ ::Storey.switch @target_schema do
92
90
  source_schema_migrations.each do |version|
93
91
  unless target_schema_migrations.include?(version)
94
92
  command = "INSERT INTO schema_migrations (version) VALUES ('#{version}');"
@@ -99,28 +97,28 @@ module Storey
99
97
  end
100
98
 
101
99
  def source_schema_migrations
102
- ::Storey.switch(self.source_schema) do
100
+ ::Storey.switch(@source_schema) do
103
101
  ::ActiveRecord::Migrator.get_all_versions
104
102
  end
105
103
  end
106
104
 
107
105
  def target_schema_migrations
108
- ::Storey.switch(self.target_schema) do
106
+ ::Storey.switch(@target_schema) do
109
107
  ::ActiveRecord::Migrator.get_all_versions
110
108
  end
111
109
  end
112
110
 
113
111
  def replace_occurrences
114
- File.open(self.source_file, 'r') do |file|
112
+ File.open(@source_file, 'r') do |file|
115
113
  file.each_line do |line|
116
- new_line = line.gsub(/#{self.source_schema}/, self.target_schema)
117
- File.open(self.target_file, 'a') {|tf| tf.puts new_line}
114
+ new_line = line.gsub(/#{@source_schema}/, @target_schema)
115
+ File.open(@target_file, 'a') {|tf| tf.puts new_line}
118
116
  end
119
117
  end
120
118
  end
121
119
 
122
120
  def duplicating_from_default?
123
- ::Storey.matches_default_search_path?(self.source_schema) && self.structure_only
121
+ ::Storey.matches_default_search_path?(@source_schema) && @structure_only
124
122
  end
125
123
 
126
124
  def suffixify(schema_name)
@@ -1,6 +1,8 @@
1
1
  module Storey
2
2
  class StoreyError < StandardError; end
3
3
  class SchemaExists < StoreyError; end
4
+ class SchemaReserved < StoreyError; end
5
+ class SchemaInvalid < StoreyError; end
4
6
  class SchemaNotFound < StoreyError; end
5
7
  class TableNotFOund < StoreyError; end
6
8
  class WithinTransaction < StoreyError; end
@@ -1,11 +1,7 @@
1
- require 'active_record/schema_dumper'
2
-
3
1
  module Storey
4
2
  class RubyDumper
5
3
 
6
- def self.dump(*args)
7
- self.new(*args).dump
8
- end
4
+ easy_class_to_instance
9
5
 
10
6
  def initialize(options={})
11
7
  default_file_path = File.join(Rails.root, 'db', 'schema.rb')
@@ -0,0 +1,38 @@
1
+ module Storey
2
+ class SchemaName < String
3
+ RESERVED_SCHEMAS = %w(hstore)
4
+
5
+ easy_class_to_instance
6
+
7
+ def initialize(name)
8
+ @name = if @name.respond_to?(:to_s)
9
+ name.to_s
10
+ else
11
+ name
12
+ end
13
+ super @name
14
+ end
15
+
16
+ def valid?
17
+ (@name =~ /^[^0-9][\w]*$/ || @name == '"$user"') &&
18
+ @name !~ /^pg_/
19
+ end
20
+
21
+ def reserved?
22
+ RESERVED_SCHEMAS.include?(@name)
23
+ end
24
+
25
+ def validate_format!
26
+ unless self.valid?
27
+ raise SchemaInvalid, "`#{@name}` is not a valid schema name"
28
+ end
29
+ end
30
+
31
+ def validate_reserved!
32
+ if self.reserved?
33
+ raise SchemaReserved, "`#{@name}` is a reserved schema name"
34
+ end
35
+ end
36
+
37
+ end
38
+ end
@@ -1,24 +1,36 @@
1
1
  module Storey
2
2
  class SqlDumper
3
3
 
4
- def self.dump(*args)
5
- self.new(*args).dump
6
- end
4
+ easy_class_to_instance
7
5
 
8
6
  def initialize(options={})
9
7
  @file = options[:file] || File.join(Rails.root, "db", "structure.sql")
10
8
  end
11
9
 
12
10
  def dump
13
- abcs = ::ActiveRecord::Base.configurations
14
- set_psql_env(abcs[Rails.env])
15
- search_path = abcs[Rails.env]['schema_search_path']
16
- unless search_path.blank?
17
- search_path = search_path.split(",").map{|search_path_part| "--schema=#{Shellwords.escape(search_path_part.strip)}" }.join(" ")
18
- end
19
- `pg_dump -i -s -x -O -f #{Shellwords.escape(@file)} #{search_path} #{Shellwords.escape(abcs[Rails.env]['database'])}`
11
+ `#{command}`
20
12
  raise 'Error dumping database' if $?.exitstatus == 1
21
- File.open(@file, "a") { |f| f << "SET search_path TO #{::ActiveRecord::Base.connection.schema_search_path};\n\n" }
13
+ end
14
+
15
+ private
16
+
17
+ def abcs
18
+ @abcs ||= ::ActiveRecord::Base.configurations.with_indifferent_access[Rails.env]
19
+ end
20
+
21
+ def search_path
22
+ @search_path ||= abcs[:schema_search_path]
23
+ end
24
+
25
+ def database_name
26
+ @database_name ||= Shellwords.escape(abcs[:database])
27
+ end
28
+
29
+ def command
30
+ @command ||= BuildsDumpCommand.execute(structure_only: true,
31
+ file: @file,
32
+ schemas: search_path,
33
+ database: database_name)
22
34
  end
23
35
 
24
36
  end
@@ -0,0 +1,23 @@
1
+ module Storey
2
+ class Utils
3
+
4
+ def self.db_command_line_switches_from(db_config={}, extra_config={})
5
+ switches = {}
6
+ if db_config.has_key?(:host)
7
+ switches[:host] = db_config[:host]
8
+ end
9
+ switches[:dbname] = db_config[:database]
10
+ switches[:username] = db_config[:username]
11
+ command_line_switches_from switches.merge(extra_config)
12
+ end
13
+
14
+ def self.command_line_switches_from(hash={})
15
+ hash.map do |k, v|
16
+ arg = "--#{k}"
17
+ arg << "=#{v}" if v
18
+ arg
19
+ end.join(' ')
20
+ end
21
+
22
+ end
23
+ end
@@ -1,3 +1,3 @@
1
1
  module Storey
2
- VERSION = "0.4.2"
2
+ VERSION = "0.5.0"
3
3
  end
data/lib/storey.rb CHANGED
@@ -1,22 +1,26 @@
1
+ require 'easy_class_to_instance_method'
2
+ require "active_support/core_ext/module" # so we can use mattr_accessor
3
+
1
4
  require "storey/version"
2
5
  require "rails/all"
3
- require "active_support/core_ext/module" # so we can use mattr_accessor
4
- require 'easy_class_to_instance_method'
5
6
  require 'storey/railtie' if defined?(Rails)
6
7
  require 'storey/exceptions'
7
8
  require 'storey/migrator'
8
9
  require 'storey/duplicator'
9
10
  require 'storey/hstore'
10
- require 'storey/dumper'
11
11
  require 'storey/ruby_dumper'
12
12
  require 'storey/sql_dumper'
13
+ require 'storey/dumper'
13
14
  require 'storey/native_schema_matcher'
14
15
  require 'storey/suffixifier'
15
16
  require 'storey/unsuffixifier'
16
17
  require 'storey/resets_column_info'
18
+ require 'storey/utils'
19
+ require 'storey/builds_dump_command'
20
+ require 'storey/builds_load_command'
21
+ require 'storey/schema_name'
17
22
 
18
23
  module Storey
19
- RESERVED_SCHEMAS = %w(hstore)
20
24
 
21
25
  mattr_accessor :suffix, :persistent_schemas
22
26
  mattr_writer :default_search_path
@@ -60,17 +64,12 @@ module Storey
60
64
  end
61
65
 
62
66
  def create(name, options={}, &block)
63
- if name.blank?
64
- fail ArgumentError, "Must pass in a valid schema name"
65
- end
66
-
67
- if RESERVED_SCHEMAS.include?(name) && !options[:force]
68
- fail ArgumentError, "'#{name}' is a reserved schema name"
69
- end
67
+ name = SchemaName.new(name)
68
+ name.validate_format!
69
+ name.validate_reserved! unless options[:force]
70
70
 
71
71
  if self.schemas.include?(name)
72
- fail(Storey::SchemaExists,
73
- %{The schema "#{name}" already exists.})
72
+ fail(Storey::SchemaExists, %{The schema "#{name}" already exists.})
74
73
  end
75
74
 
76
75
  if options[:load_database_structure].nil?
@@ -93,20 +92,8 @@ module Storey
93
92
 
94
93
  def create_plain_schema(schema_name)
95
94
  name = suffixify schema_name
96
- command = %{"CREATE SCHEMA #{name}"}
97
- switches = self.command_line_switches(command: command)
98
- `psql #{switches}`
99
- end
100
-
101
- def command_line_switches(options={})
102
- switches = {}
103
- if self.database_config.has_key?(:host)
104
- switches[:host] = self.database_config[:host]
105
- end
106
- switches[:dbname] = self.database_config[:database]
107
- switches[:username] = self.database_config[:username]
108
- switches = switches.merge(options)
109
- switches.map {|k, v| "--#{k}=#{v}"}.join(' ')
95
+ command = "CREATE SCHEMA #{name}"
96
+ system psql_load_command(command: command)
110
97
  end
111
98
 
112
99
  def schemas(options={})
@@ -208,6 +195,10 @@ module Storey
208
195
  self.default_search_path == schema_name
209
196
  end
210
197
 
198
+ def db_command_line_switches_from(extra_config={})
199
+ Utils.db_command_line_switches_from(self.database_config, extra_config)
200
+ end
201
+
211
202
  protected
212
203
 
213
204
  def schema_migrations
@@ -248,4 +239,8 @@ module Storey
248
239
  end
249
240
  end
250
241
 
242
+ def psql_load_command(options={})
243
+ BuildsLoadCommand.execute(self.database_config.merge(options))
244
+ end
245
+
251
246
  end
data/spec/spec_helper.rb CHANGED
@@ -18,6 +18,8 @@ RSpec.configure do |config|
18
18
  # We don't want configuration to leak into other tests
19
19
  Storey.reload_config!
20
20
 
21
+ FileUtils.rm_rf File.join(Rails.root, 'tmp', 'schema_dumps')
22
+
21
23
  # Clean the public schema
22
24
  Storey.switch do
23
25
  tables = ::ActiveRecord::Base.connection.tables
@@ -0,0 +1,87 @@
1
+ require 'spec_helper'
2
+
3
+ describe Storey::BuildsDumpCommand do
4
+
5
+ describe '.execute' do
6
+ subject do
7
+ described_class.execute(options)
8
+ end
9
+
10
+ let(:options) do
11
+ {
12
+ structure_only: true,
13
+ file: 'myfile.sql',
14
+ schemas: 'public',
15
+ database: 'mydb'
16
+ }
17
+ end
18
+
19
+ context 'when structure_only: true' do
20
+ let(:options) do
21
+ {
22
+ database: 'mydb',
23
+ structure_only: true
24
+ }
25
+ end
26
+ it { should include('--schema-only') }
27
+ end
28
+
29
+ context 'when structure_only: false' do
30
+ let(:options) do
31
+ {
32
+ database: 'mydb',
33
+ structure_only: false
34
+ }
35
+ end
36
+ it { should_not include('--schema-only') }
37
+ end
38
+
39
+ it { should include('--no-privileges') }
40
+ it { should include('--no-owner') }
41
+
42
+ context 'file: is "/path/to/file name.sql"' do
43
+ let(:options) do
44
+ {
45
+ database: 'mydb',
46
+ file: '/path/to/file name.sql'
47
+ }
48
+ end
49
+ it { should include('/path/to/file\ name.sql') }
50
+ end
51
+
52
+ context "schemas: 'public'" do
53
+ let(:options) do
54
+ {
55
+ database: 'mydb',
56
+ schemas: %q(public)
57
+ }
58
+ end
59
+ it { should include('--schema=public') }
60
+ end
61
+
62
+ context "schemas: '$user','public'" do
63
+ let(:options) do
64
+ {
65
+ database: 'mydb',
66
+ schemas: %q($user,public)
67
+ }
68
+ end
69
+ it { should include('--schema=\$user --schema=public') }
70
+ end
71
+
72
+ it { should match(/mydb$/)}
73
+
74
+ context 'no database is given' do
75
+ let(:options) { {} }
76
+ it 'should fail' do
77
+ expect{subject}.to raise_error(ArgumentError, 'database must be supplied')
78
+ end
79
+ end
80
+
81
+ it 'should build a valid full executable command' do
82
+ expect(subject).
83
+ to eq("pg_dump --schema-only --no-privileges --no-owner --file=myfile.sql --schema=public mydb")
84
+ end
85
+ end
86
+
87
+ end
@@ -0,0 +1,56 @@
1
+ require 'spec_helper'
2
+
3
+ describe Storey::BuildsLoadCommand do
4
+
5
+ describe '.execute' do
6
+ let(:options) do
7
+ {
8
+ file: '/path/file dump.sql',
9
+ database: 'mydb',
10
+ username: 'myuser',
11
+ host: 'localhost',
12
+ port: '5467',
13
+ password: '12345'
14
+ }
15
+ end
16
+
17
+ subject { described_class.execute(options) }
18
+
19
+ it { should include('--file=/path/file\ dump.sql') }
20
+ it { should include('--dbname=mydb') }
21
+ it { should include('--username=myuser') }
22
+ it { should include('--host=localhost') }
23
+ it { should include('--password=12345')}
24
+
25
+ context 'when host is not set' do
26
+ before { options[:host] = nil }
27
+ it { should_not include('--host=') }
28
+ end
29
+
30
+ context 'when username is not set' do
31
+ before { options[:username] = nil }
32
+ it { should_not include('--username') }
33
+ end
34
+
35
+ context 'when no password is given' do
36
+ before { options[:password] = nil }
37
+ it { should_not include('--password') }
38
+ it { should include('--no-password') }
39
+ end
40
+
41
+ context 'when no file is given' do
42
+ before { options[:file] = nil }
43
+ it { should_not include('--file=')}
44
+ end
45
+
46
+ context 'command is given' do
47
+ before { options[:command] = "EXECUTE THIS!" }
48
+ it { should include('--command="EXECUTE THIS!"')}
49
+ end
50
+
51
+ it 'should generate a valid command' do
52
+ expect(subject).to eq('psql --file=/path/file\ dump.sql --dbname=mydb --username=myuser --host=localhost --port=5467 --password=12345')
53
+ end
54
+ end
55
+
56
+ end
@@ -1,6 +1,32 @@
1
1
  require 'spec_helper'
2
2
 
3
3
  describe Storey, "#create" do
4
+
5
+ context 'given an invalid schema' do
6
+ it 'should fail' do
7
+ expect { Storey.create('a a') }.to raise_error(Storey::SchemaInvalid)
8
+ end
9
+ end
10
+
11
+ context 'given a reserved schema' do
12
+ context 'force is true' do
13
+ it 'should create the schema' do
14
+ reserved_schema = Storey::SchemaName::RESERVED_SCHEMAS.sample
15
+ expect { Storey.create(reserved_schema, force: true) }.
16
+ to_not raise_error
17
+ expect(Storey.schemas).to include(reserved_schema)
18
+ end
19
+ end
20
+
21
+ context 'force is not true' do
22
+ it 'should fail' do
23
+ reserved_schema = Storey::SchemaName::RESERVED_SCHEMAS.sample
24
+ expect { Storey.create(reserved_schema) }.
25
+ to raise_error(Storey::SchemaReserved)
26
+ end
27
+ end
28
+ end
29
+
4
30
  it "should load the database structure into the new schema" do
5
31
  public_tables = Storey.switch { ::ActiveRecord::Base.connection.tables }.sort
6
32
  Storey.create "foobar" do
@@ -44,7 +70,7 @@ describe Storey, "#create" do
44
70
 
45
71
  context "when a blank string is passed" do
46
72
  it "should raise an argument error about an invalid schema name" do
47
- expect {Storey.create ""}.to raise_error(ArgumentError, "Must pass in a valid schema name")
73
+ expect {Storey.create ""}.to raise_error
48
74
  end
49
75
  end
50
76
 
@@ -97,19 +123,4 @@ describe Storey, "#create" do
97
123
  end
98
124
  end
99
125
 
100
- context 'when creating a reserved schema' do
101
- it 'should fail' do
102
- expect {Storey.create('hstore')}.to raise_error(ArgumentError, "'hstore' is a reserved schema name")
103
- end
104
-
105
- context 'when force: true is passed in' do
106
- it 'should create the schema' do
107
- expect {
108
- Storey.create 'hstore', force: true
109
- }.to_not raise_error(ArgumentError)
110
- Storey.schemas.should include('hstore')
111
- end
112
- end
113
- end
114
-
115
126
  end
@@ -14,6 +14,18 @@ describe Storey::Duplicator do
14
14
  )
15
15
  end
16
16
  end
17
+
18
+ it 'should remove the target and source sql files after work' do
19
+ Storey.create 'boo'
20
+ duplicator = described_class.new('boo', 'ya')
21
+ duplicator.perform!
22
+ source_dump_dir =
23
+ File.join(Rails.root, 'tmp', 'schema_dumps', 'source', '*.*')
24
+ target_dump_dir =
25
+ File.join(Rails.root, 'tmp', 'schema_dumps', 'target', '*.*')
26
+ expect(Dir[source_dump_dir]).to be_empty
27
+ expect(Dir[target_dump_dir]).to be_empty
28
+ end
17
29
  end
18
30
 
19
31
  end
@@ -0,0 +1,92 @@
1
+ require 'spec_helper'
2
+
3
+ describe Storey::SchemaName do
4
+
5
+ it 'should behave just like a string' do
6
+ expect(described_class.new('hi')).to eq('hi')
7
+ end
8
+
9
+ describe '#valid?' do
10
+ subject { described_class.new(schema_name) }
11
+
12
+ context 'when the name starts with `pg_`' do
13
+ let(:schema_name) { 'pg_shouldbereserved' }
14
+ it { should_not be_valid }
15
+ end
16
+
17
+ context 'when the name starts with a number' do
18
+ let(:schema_name) { '8isthemagicnumber' }
19
+ it { should_not be_valid }
20
+ end
21
+
22
+ context 'when the name has a space' do
23
+ let(:schema_name) { 'almost valid' }
24
+ it { should_not be_valid }
25
+ end
26
+
27
+ context 'when the name is valid' do
28
+ let(:schema_name) { 'a_5' }
29
+ it { should be_valid }
30
+ end
31
+
32
+ context 'when the name is valid with $ and "' do
33
+ let(:schema_name) { '"$user"' }
34
+ it { should be_valid }
35
+ end
36
+
37
+ context 'when the string is blank' do
38
+ let(:schema_name) { '' }
39
+ it { should_not be_valid }
40
+ end
41
+
42
+ context 'when the string is nil' do
43
+ let(:schema_name) { nil }
44
+ it { should_not be_valid }
45
+ end
46
+ end
47
+
48
+ describe '#reserved?' do
49
+ subject { described_class.new(schema_name) }
50
+
51
+ context 'when the schema name is reserved' do
52
+ let(:schema_name) { described_class::RESERVED_SCHEMAS.sample }
53
+ it { should be_reserved }
54
+ end
55
+
56
+ context 'when the schema name is not reserved' do
57
+ let(:schema_name) { 'available' }
58
+ it { should_not be_reserved }
59
+ end
60
+ end
61
+
62
+ describe '#validate_format!' do
63
+ subject { described_class.validate_format!(schema_name) }
64
+
65
+ context 'an invalid name is given' do
66
+ let(:schema_name) { 'a a' }
67
+ it 'should fail' do
68
+ expect { subject }.
69
+ to raise_error(Storey::SchemaInvalid, '`a a` is not a valid schema name')
70
+ end
71
+ end
72
+
73
+ context 'a valid name is given' do
74
+ let(:schema_name) { 'a_5' }
75
+ it 'should initialize without exceptions' do
76
+ expect { subject }.to_not raise_error
77
+ end
78
+ end
79
+ end
80
+
81
+ describe '#validate_reserved!' do
82
+ context 'when the name is a reserved schema name' do
83
+ let(:schema_name) { described_class::RESERVED_SCHEMAS.sample }
84
+
85
+ it 'should fail with reserved schema argument error' do
86
+ expect { described_class.validate_reserved!(schema_name) }.
87
+ to raise_error(Storey::SchemaReserved, "`#{schema_name}` is a reserved schema name")
88
+ end
89
+ end
90
+ end
91
+
92
+ end
@@ -0,0 +1,60 @@
1
+ require 'spec_helper'
2
+
3
+ describe Storey::Utils do
4
+
5
+ describe '.command_line_switches_from(hash)' do
6
+ it 'should build command line switches from the hash' do
7
+ hash = {some: 'key',
8
+ pretty: 'cool'}
9
+ expect(described_class.command_line_switches_from(hash)).
10
+ to eq("--some=key --pretty=cool")
11
+ end
12
+ end
13
+
14
+ describe '.db_command_line_switches_from(db_config)' do
15
+ subject do
16
+ described_class.db_command_line_switches_from(db_config, extra_config)
17
+ end
18
+
19
+ context 'db_config does not have :host' do
20
+ let(:db_config) { {} }
21
+ it { should_not include('--host=') }
22
+ end
23
+
24
+ context 'db_config has :host' do
25
+ let(:db_config) { {host: 'localhost'} }
26
+ it { should include('--host=localhost') }
27
+ end
28
+
29
+ let(:db_config) do
30
+ {
31
+ database: 'mydb',
32
+ username: 'uname'
33
+ }
34
+ end
35
+
36
+ let(:extra_config) do
37
+ {
38
+ extra: 'config',
39
+ 'without-arg' => nil
40
+ }
41
+ end
42
+
43
+ it 'should set the database' do
44
+ expect(subject).to include('--dbname=mydb')
45
+ end
46
+
47
+ it 'should set the username' do
48
+ expect(subject).to include('--username=uname')
49
+ end
50
+
51
+ it 'should include extra config' do
52
+ expect(subject).to include('--extra=config')
53
+ end
54
+
55
+ it 'should set flag arguments' do
56
+ expect(subject).to match(/--without-arg$/)
57
+ end
58
+ end
59
+
60
+ end
data/storey.gemspec CHANGED
@@ -23,6 +23,6 @@ Gem::Specification.new do |s|
23
23
  s.add_development_dependency "pg", "~> 0.12.2"
24
24
  s.add_development_dependency "database_cleaner"
25
25
  s.add_development_dependency "pry"
26
- s.add_runtime_dependency 'easy_class_to_instance_method', '~> 0.0.1'
26
+ s.add_runtime_dependency 'easy_class_to_instance_method', '~> 0.0.2'
27
27
  s.add_runtime_dependency "rails", "~> 3.2.2"
28
28
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: storey
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.4.2
4
+ version: 0.5.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Ramon Tayag
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2013-06-12 00:00:00.000000000 Z
11
+ date: 2013-07-14 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rspec-rails
@@ -72,14 +72,14 @@ dependencies:
72
72
  requirements:
73
73
  - - ~>
74
74
  - !ruby/object:Gem::Version
75
- version: 0.0.1
75
+ version: 0.0.2
76
76
  type: :runtime
77
77
  prerelease: false
78
78
  version_requirements: !ruby/object:Gem::Requirement
79
79
  requirements:
80
80
  - - ~>
81
81
  - !ruby/object:Gem::Version
82
- version: 0.0.1
82
+ version: 0.0.2
83
83
  - !ruby/object:Gem::Dependency
84
84
  name: rails
85
85
  requirement: !ruby/object:Gem::Requirement
@@ -109,6 +109,8 @@ files:
109
109
  - README.md
110
110
  - Rakefile
111
111
  - lib/storey.rb
112
+ - lib/storey/builds_dump_command.rb
113
+ - lib/storey/builds_load_command.rb
112
114
  - lib/storey/dumper.rb
113
115
  - lib/storey/duplicator.rb
114
116
  - lib/storey/exceptions.rb
@@ -118,9 +120,11 @@ files:
118
120
  - lib/storey/railtie.rb
119
121
  - lib/storey/resets_column_info.rb
120
122
  - lib/storey/ruby_dumper.rb
123
+ - lib/storey/schema_name.rb
121
124
  - lib/storey/sql_dumper.rb
122
125
  - lib/storey/suffixifier.rb
123
126
  - lib/storey/unsuffixifier.rb
127
+ - lib/storey/utils.rb
124
128
  - lib/storey/version.rb
125
129
  - lib/tasks/storey.rake
126
130
  - rvmrc.sample
@@ -166,7 +170,8 @@ files:
166
170
  - spec/fixtures/.gitkeep
167
171
  - spec/migrator_spec.rb
168
172
  - spec/spec_helper.rb
169
- - spec/storey/command_line_switches_spec.rb
173
+ - spec/storey/builds_dump_command_spec.rb
174
+ - spec/storey/builds_load_command_spec.rb
170
175
  - spec/storey/configuration_spec.rb
171
176
  - spec/storey/create_plain_schema_spec.rb
172
177
  - spec/storey/create_spec.rb
@@ -182,6 +187,7 @@ files:
182
187
  - spec/storey/persistent_schemas_spec.rb
183
188
  - spec/storey/ruby_dumper_spec.rb
184
189
  - spec/storey/schema_exists_spec.rb
190
+ - spec/storey/schema_name_spec.rb
185
191
  - spec/storey/schema_search_path_for_spec.rb
186
192
  - spec/storey/schema_spec.rb
187
193
  - spec/storey/schemas_spec.rb
@@ -189,6 +195,7 @@ files:
189
195
  - spec/storey/suffixifier_spec.rb
190
196
  - spec/storey/switch_spec.rb
191
197
  - spec/storey/unsuffixifier_spec.rb
198
+ - spec/storey/utils_spec.rb
192
199
  - spec/tasks/storey_rake_spec.rb
193
200
  - storey.gemspec
194
201
  homepage: https://github.com/ramontayag/storey
@@ -257,7 +264,8 @@ test_files:
257
264
  - spec/fixtures/.gitkeep
258
265
  - spec/migrator_spec.rb
259
266
  - spec/spec_helper.rb
260
- - spec/storey/command_line_switches_spec.rb
267
+ - spec/storey/builds_dump_command_spec.rb
268
+ - spec/storey/builds_load_command_spec.rb
261
269
  - spec/storey/configuration_spec.rb
262
270
  - spec/storey/create_plain_schema_spec.rb
263
271
  - spec/storey/create_spec.rb
@@ -273,6 +281,7 @@ test_files:
273
281
  - spec/storey/persistent_schemas_spec.rb
274
282
  - spec/storey/ruby_dumper_spec.rb
275
283
  - spec/storey/schema_exists_spec.rb
284
+ - spec/storey/schema_name_spec.rb
276
285
  - spec/storey/schema_search_path_for_spec.rb
277
286
  - spec/storey/schema_spec.rb
278
287
  - spec/storey/schemas_spec.rb
@@ -280,4 +289,5 @@ test_files:
280
289
  - spec/storey/suffixifier_spec.rb
281
290
  - spec/storey/switch_spec.rb
282
291
  - spec/storey/unsuffixifier_spec.rb
292
+ - spec/storey/utils_spec.rb
283
293
  - spec/tasks/storey_rake_spec.rb
@@ -1,22 +0,0 @@
1
- require 'spec_helper'
2
-
3
- describe Storey, '.command_line_switches' do
4
- it 'should build default command line switches for calling the psql command' do
5
- described_class.stub(:database_config).and_return(:host => 'hoop',
6
- :database => 'db',
7
- :username => 'jj')
8
- described_class.command_line_switches.
9
- should == "--host=hoop --dbname=db --username=jj"
10
- end
11
-
12
- context 'given options' do
13
- it 'should add the options to the switches, overriding any default' do
14
- described_class.stub(:database_config).and_return(:host => 'hoop',
15
- :database => 'db',
16
- :username => 'jj')
17
-
18
- described_class.command_line_switches(:a => 'boo', :host => 'loop').
19
- should == "--host=loop --dbname=db --username=jj --a=boo"
20
- end
21
- end
22
- end