storey 0.4.2 → 0.5.0

Sign up to get free protection for your applications and to get access to all the features.
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