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 +4 -4
- data/CHANGELOG.md +6 -0
- data/lib/storey/builds_dump_command.rb +36 -0
- data/lib/storey/builds_load_command.rb +31 -0
- data/lib/storey/dumper.rb +5 -9
- data/lib/storey/duplicator.rb +44 -46
- data/lib/storey/exceptions.rb +2 -0
- data/lib/storey/ruby_dumper.rb +1 -5
- data/lib/storey/schema_name.rb +38 -0
- data/lib/storey/sql_dumper.rb +23 -11
- data/lib/storey/utils.rb +23 -0
- data/lib/storey/version.rb +1 -1
- data/lib/storey.rb +22 -27
- data/spec/spec_helper.rb +2 -0
- data/spec/storey/builds_dump_command_spec.rb +87 -0
- data/spec/storey/builds_load_command_spec.rb +56 -0
- data/spec/storey/create_spec.rb +27 -16
- data/spec/storey/duplicator_spec.rb +12 -0
- data/spec/storey/schema_name_spec.rb +92 -0
- data/spec/storey/utils_spec.rb +60 -0
- data/storey.gemspec +1 -1
- metadata +16 -6
- data/spec/storey/command_line_switches_spec.rb +0 -22
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: f43d4bfca6dbcd859c187fb920e7e296d6c1809b
|
4
|
+
data.tar.gz: 322eb8906efac105f0a0c153bc4d8c47209eea28
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
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
|
-
|
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
|
-
|
21
|
-
|
22
|
-
|
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
|
data/lib/storey/duplicator.rb
CHANGED
@@ -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
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
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
|
-
|
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
|
-
|
57
|
-
|
58
|
-
|
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 `#{
|
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
|
-
[
|
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] ||=
|
74
|
-
|
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
|
77
|
+
::Storey.create_plain_schema @target_schema
|
81
78
|
end
|
82
79
|
|
83
|
-
|
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
|
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(
|
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(
|
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(
|
112
|
+
File.open(@source_file, 'r') do |file|
|
115
113
|
file.each_line do |line|
|
116
|
-
new_line = line.gsub(/#{
|
117
|
-
File.open(
|
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?(
|
121
|
+
::Storey.matches_default_search_path?(@source_schema) && @structure_only
|
124
122
|
end
|
125
123
|
|
126
124
|
def suffixify(schema_name)
|
data/lib/storey/exceptions.rb
CHANGED
@@ -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
|
data/lib/storey/ruby_dumper.rb
CHANGED
@@ -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
|
data/lib/storey/sql_dumper.rb
CHANGED
@@ -1,24 +1,36 @@
|
|
1
1
|
module Storey
|
2
2
|
class SqlDumper
|
3
3
|
|
4
|
-
|
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
|
-
|
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
|
-
|
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
|
data/lib/storey/utils.rb
ADDED
@@ -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
|
data/lib/storey/version.rb
CHANGED
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
|
-
|
64
|
-
|
65
|
-
|
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 =
|
97
|
-
|
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
|
data/spec/storey/create_spec.rb
CHANGED
@@ -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
|
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.
|
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
|
+
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-
|
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.
|
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.
|
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/
|
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/
|
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
|