wyrm 0.4.1 → 0.4.2

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.
@@ -1,32 +1,33 @@
1
1
  require 'wyrm/pump'
2
2
  require 'wyrm/module'
3
3
 
4
- module Wyrm::PumpMaker
5
- def call_or_self( maybe_callable )
6
- if maybe_callable.respond_to? :call
7
- maybe_callable.call( self )
8
- else
9
- maybe_callable
4
+ module Wyrm
5
+ module PumpMaker
6
+ def call_or_self( maybe_callable )
7
+ if maybe_callable.respond_to? :call
8
+ maybe_callable.call( self )
9
+ else
10
+ maybe_callable
11
+ end
10
12
  end
11
- end
12
13
 
13
- def make_pump( db, pump_thing )
14
- call_or_self(pump_thing) || Pump.new( db: db )
15
- end
14
+ def make_pump( db, pump_thing )
15
+ call_or_self(pump_thing) || Wyrm::Pump.new( db: db )
16
+ end
16
17
 
17
- def maybe_deebe( db_or_string )
18
- case db_or_string
19
- when String
20
- begin
21
- Sequel.connect db_or_string
22
- rescue Sequel::AdapterNotFound
23
- puts "\nCan't find db driver for #{db_or_string}. It might work to do\n\n gem install #{db_or_string.split(?:).first}\n\n"
24
- exit(1)
18
+ def maybe_deebe( db_or_string )
19
+ case db_or_string
20
+ when String
21
+ begin
22
+ Sequel.connect db_or_string
23
+ rescue Sequel::AdapterNotFound
24
+ raise "\nCan't find db driver for #{db_or_string}. It might work to do\n\n gem install #{db_or_string.split(?:).first}\n\n"
25
+ end
26
+ when Sequel::Database
27
+ db_or_string
28
+ else
29
+ raise "Don't know how to db-ify #{db_or_string.inspect}"
25
30
  end
26
- when Sequel::Database
27
- db_or_string
28
- else
29
- raise "Don't know how to db-ify #{db_or_string.inspect}"
30
31
  end
31
32
  end
32
33
  end
@@ -8,105 +8,119 @@ require 'wyrm/schema_tools'
8
8
 
9
9
  # Load a schema from a set of dump files (from DumpSchema)
10
10
  # and restore the table data.
11
- # dst_db = Sequel.connect "postgres://localhost:5454/lots"
12
- # rs = RestoreSchema.new dst_db, '/var/data/lots'
13
- # rs.call
11
+ #
12
+ # Restore["postgres://localhost:5454/lots", '/var/data/lots']
13
+ #
14
14
  # TODO the problem with lazy loading the schema files is that
15
15
  # errors in indexes and foreign keys will only be picked up at the
16
16
  # end of they probably lengthy table restore process.
17
17
  # TODO check if table has been restored already, and has the correct rows,
18
- class Wyrm::Restore
19
- include Wyrm::PumpMaker
20
- include Wyrm::SchemaTools
21
- include Wyrm::Logger
18
+ module Wyrm
19
+ class Restore
20
+ include PumpMaker
21
+ include SchemaTools
22
+ include Logger
23
+
24
+ def self.[]( *args )
25
+ new(*args).call
26
+ end
22
27
 
23
- def initialize( container, dst_db, pump: nil, drop_tables: false )
24
- @container = Pathname.new container
25
- fail "#{@container} does not exist" unless @container.exist?
28
+ def call
29
+ drop_tables(table_names) if options.drop_tables
30
+ create_tables
31
+ restore_tables
32
+ create_indexes
33
+ end
26
34
 
27
- @dst_db = maybe_deebe dst_db
28
- @pump = make_pump( @dst_db, pump )
35
+ def initialize( container, dst_db, pump: nil, drop_tables: false )
36
+ @container = Pathname.new container
37
+ fail "#{@container} does not exist" unless @container.exist?
29
38
 
30
- options.drop_tables = drop_tables
31
- end
39
+ @dst_db = maybe_deebe dst_db
40
+ @pump = make_pump( @dst_db, pump )
32
41
 
33
- attr_reader :pump
34
- attr_reader :dst_db
35
- attr_reader :container
42
+ options.drop_tables = drop_tables
43
+ end
36
44
 
37
- def options
38
- @options ||= OpenStruct.new
39
- end
45
+ attr_reader :pump
46
+ attr_reader :dst_db
47
+ attr_reader :container
40
48
 
41
- # sequel wants migrations numbered, but it's a bit of an annoyance for this.
42
- def find_single( glob )
43
- candidates = Pathname.glob container + glob
44
- raise "no candidates for #{glob}. Probably #{container} does not have wyrm files." unless candidates.size == 1
45
- raise "too many #{candidates.inspect} for #{glob}" unless candidates.size == 1
46
- candidates.first
47
- end
49
+ def options
50
+ @options ||= OpenStruct.new
51
+ end
48
52
 
49
- def schema_migration
50
- @schema_migration ||= find_single( '*schema.rb' ).read
51
- end
53
+ class None < RuntimeError; end
52
54
 
53
- def index_migration
54
- @index_migration ||= find_single( '*indexes.rb' ).read
55
- end
55
+ # sequel wants migrations numbered, but it's a bit of an annoyance for this.
56
+ def find_single( glob )
57
+ candidates = Pathname.glob container + glob
58
+ raise None, "No restore files found for #{glob}" if candidates.size == 0
59
+ raise "too many #{candidates.inspect} for #{glob}" if candidates.size > 1
60
+ candidates.first
61
+ end
56
62
 
57
- def fk_migration
58
- @fk_migration ||= find_single( '*foreign_keys.rb' ).read
59
- end
63
+ def schema_migration
64
+ @schema_migration ||= find_single( '*schema.rb' ).read
65
+ rescue None
66
+ ''
67
+ end
60
68
 
61
- def reload_migrations
62
- @fk_migration = nil
63
- @index_migration = nil
64
- @schema_migration = nil
65
- end
69
+ def index_migration
70
+ @index_migration ||= find_single( '*indexes.rb' ).read
71
+ rescue None
72
+ ''
73
+ end
66
74
 
67
- # assume the table name is the base name of table_file pathname
68
- def restore_table( table_file )
69
- logger.info "restoring from #{table_file}"
70
- pump.table_name = table_file.basename.sub_ext('').sub_ext('').to_s.to_sym
71
- open_bz2 table_file do |io|
72
- pump.io = io
73
- pump.restore filename: table_file
75
+ def fk_migration
76
+ @fk_migration ||= find_single( '*foreign_keys.rb' ).read
77
+ rescue None
78
+ ''
74
79
  end
75
- end
76
80
 
77
- # open a dbp.bz2 file and either yield or return an io of the uncompressed contents
78
- def open_bz2( table_name, &block )
79
- table_file =
80
- case table_name
81
- when Symbol
82
- container + "#{table_name}.dbp.bz2"
83
- when Pathname
84
- table_name
85
- else
86
- raise "Don't know what to do with #{table_name.inspect}"
81
+ def reload_migrations
82
+ @fk_migration = nil
83
+ @index_migration = nil
84
+ @schema_migration = nil
87
85
  end
88
86
 
89
- IO.popen "#{STREAM_DCMP} #{table_file}", &block
90
- end
87
+ # assume the table name is the base name of table_file pathname
88
+ def restore_table( table_file )
89
+ logger.info "restoring from #{table_file}"
90
+ pump.table_name = table_file.basename.sub_ext('').sub_ext('').to_s.to_sym
91
+ open_bz2 table_file do |io|
92
+ pump.io = io
93
+ pump.restore filename: table_file
94
+ end
95
+ end
91
96
 
92
- def table_files
93
- Pathname.glob container + '*.dbp.bz2'
94
- end
97
+ # open a dbp.bz2 file and either yield or return an io of the uncompressed contents
98
+ def open_bz2( table_name, &block )
99
+ table_file =
100
+ case table_name
101
+ when Symbol
102
+ container + "#{table_name}.dbp.bz2"
103
+ when Pathname
104
+ table_name
105
+ else
106
+ raise "Don't know what to do with #{table_name.inspect}"
107
+ end
108
+
109
+ IO.popen "#{STREAM_DCMP} #{table_file}", &block
110
+ end
95
111
 
96
- def restore_tables
97
- table_files.sort_by{|tf| tf.stat.size}.each{|table_file| restore_table table_file}
98
- end
112
+ def table_files
113
+ Pathname.glob container + '*.dbp.bz2'
114
+ end
99
115
 
100
- def table_names
101
- table_files.map do |path|
102
- path.basename.to_s.split(?.)[0...-2].last.to_sym
116
+ def restore_tables
117
+ table_files.sort_by{|tf| tf.stat.size}.each{|table_file| restore_table table_file}
103
118
  end
104
- end
105
119
 
106
- def call
107
- drop_tables(table_names) if options.drop_tables
108
- create_tables
109
- restore_tables
110
- create_indexes
120
+ def table_names
121
+ table_files.map do |path|
122
+ path.basename.to_s.split(?.)[0...-2].last.to_sym
123
+ end
124
+ end
111
125
  end
112
126
  end
@@ -4,86 +4,93 @@ require 'wyrm/module'
4
4
  # needs dst_db for mutate operations
5
5
  # and src_db for fetch operations
6
6
  # src_db must have extension(:schema_dumper)
7
- module Wyrm::SchemaTools
8
- # some includers will need to provide a different implementation for this.
9
- def same_db
10
- respond_to?( :dst_db ) && respond_to?( :src_db ) && dst_db&.database_type == src_db&.database_type
11
- end
7
+ module Wyrm
8
+ module SchemaTools
9
+ # some includers will need to provide a different implementation for this.
10
+ def same_db
11
+ respond_to?( :dst_db ) && respond_to?( :src_db ) && dst_db&.database_type == src_db&.database_type
12
+ end
12
13
 
13
- def schema_migration
14
- @schema_migration ||= src_db.dump_schema_migration(:indexes=>false, :same_db => same_db)
15
- end
14
+ def schema_migration
15
+ @schema_migration ||= src_db.dump_schema_migration indexes: false, :same_db => same_db
16
+ end
16
17
 
17
- def index_migration
18
- @index_migration ||= src_db.dump_indexes_migration(:same_db => same_db)
19
- end
18
+ # dump single table including indexes, but ignore foreign keys
19
+ def table_migration( table )
20
+ src_db.dump_table_schema table, indexes: true, :same_db => same_db
21
+ end
20
22
 
21
- def fk_migration
22
- @fk_migration ||= src_db.dump_foreign_key_migration(:same_db => same_db)
23
- end
23
+ def index_migration
24
+ @index_migration ||= src_db.dump_indexes_migration :same_db => same_db
25
+ end
24
26
 
25
- def drop_table_options
26
- @drop_table_options ||=
27
- begin
28
- if dst_db.database_type == :postgres
29
- {cascade: true}
30
- else
31
- {}
32
- end
27
+ def fk_migration
28
+ @fk_migration ||= src_db.dump_foreign_key_migration :same_db => same_db
33
29
  end
34
- end
35
30
 
36
- # Delete given tables.
37
- # Recurse if there are foreign keys preventing table deletion.
38
- # This implementation will fail for tables with mutual foreign keys.
39
- # TODO maybe this should use the schema down migration?
40
- def drop_tables( tables )
41
- foreign_keyed_tables = []
42
- tables.each do |table_name|
31
+ def drop_table_options
32
+ @drop_table_options ||=
43
33
  begin
44
- logger.debug "dropping #{table_name}"
45
- dst_db.drop_table? table_name, drop_table_options
34
+ if dst_db.database_type == :postgres
35
+ {cascade: true}
36
+ else
37
+ {}
38
+ end
39
+ end
40
+ end
46
41
 
47
- rescue Sequel::ForeignKeyConstraintViolation => ex
48
- foreign_keyed_tables << table_name
42
+ # Delete given tables.
43
+ # Recurse if there are foreign keys preventing table deletion.
44
+ # This implementation will fail for tables with mutual foreign keys.
45
+ # TODO maybe this should use the schema down migration?
46
+ def drop_tables( tables )
47
+ foreign_keyed_tables = []
48
+ tables.each do |table_name|
49
+ begin
50
+ logger.debug "dropping #{table_name}"
51
+ dst_db.drop_table? table_name, drop_table_options
49
52
 
50
- rescue Sequel::DatabaseError => ex
51
- # Mysql2::Error: Cannot delete or update a parent row: a foreign key constraint fails
52
- # SQLite3::ConstraintException: FOREIGN KEY constraint failed==
53
- if ex.message =~ /foreign key constraint fail/i
53
+ rescue Sequel::ForeignKeyConstraintViolation => ex
54
54
  foreign_keyed_tables << table_name
55
- else
56
- raise
55
+
56
+ rescue Sequel::DatabaseError => ex
57
+ # Mysql2::Error: Cannot delete or update a parent row: a foreign key constraint fails
58
+ # SQLite3::ConstraintException: FOREIGN KEY constraint failed==
59
+ if ex.message =~ /foreign key constraint fail/i
60
+ foreign_keyed_tables << table_name
61
+ else
62
+ raise
63
+ end
57
64
  end
58
65
  end
59
- end
60
66
 
61
- # this should be temporary
62
- if tables.any? && tables.sort == foreign_keyed_tables.sort
63
- raise "can't remove #{tables.inspect} because they have mutual foreign keys"
64
- end
67
+ # this should be temporary
68
+ if tables.any? && tables.sort == foreign_keyed_tables.sort
69
+ raise "can't remove #{tables.inspect} because they have mutual foreign keys"
70
+ end
65
71
 
66
- # recursively delete tables. Shuffle as kak workaround for dependency loops.
67
- drop_tables foreign_keyed_tables.shuffle unless foreign_keyed_tables.empty?
68
- end
72
+ # recursively delete tables. Shuffle as kak workaround for dependency loops.
73
+ drop_tables foreign_keyed_tables.shuffle unless foreign_keyed_tables.empty?
74
+ end
69
75
 
70
- def create_tables
71
- logger.info "creating tables"
72
- eval( schema_migration ).apply dst_db, :up
73
- end
76
+ def create_tables
77
+ logger.info "creating tables"
78
+ eval( schema_migration ).apply dst_db, :up
79
+ end
74
80
 
75
- def create_indexes
76
- # create indexes and foreign keys, and reset sequences
77
- logger.info "creating indexes"
78
- eval( index_migration ).apply dst_db, :up
81
+ def create_indexes
82
+ # create indexes and foreign keys, and reset sequences
83
+ logger.info "creating indexes"
84
+ eval( index_migration ).apply dst_db, :up
79
85
 
80
- logger.info "creating foreign keys"
81
- eval( fk_migration ).apply dst_db, :up
86
+ logger.info "creating foreign keys"
87
+ eval( fk_migration ).apply dst_db, :up
82
88
 
83
- if dst_db.database_type == :postgres
84
- logger.info "reset primary key sequences"
85
- dst_db.tables.each{|t| dst_db.reset_primary_key_sequence(t)}
86
- logger.info "Primary key sequences reset successfully"
89
+ if dst_db.database_type == :postgres
90
+ logger.info "reset primary key sequences"
91
+ dst_db.tables.each{|t| dst_db.reset_primary_key_sequence(t)}
92
+ logger.info "Primary key sequences reset successfully"
93
+ end
87
94
  end
88
95
  end
89
96
  end
@@ -1,3 +1,3 @@
1
1
  module Wyrm
2
- VERSION = '0.4.1'
2
+ VERSION = '0.4.2'
3
3
  end
@@ -5,6 +5,8 @@ require Pathname(__dir__) + '../lib/wyrm/pump.rb'
5
5
  include Wyrm
6
6
 
7
7
  describe Pump do
8
+ include DbConnections
9
+
8
10
  describe '.quacks_like' do
9
11
  it 'recognises method' do
10
12
  threequal = Pump.quacks_like( :tap )
@@ -27,7 +29,7 @@ describe Pump do
27
29
  describe '#db=' do
28
30
  it 'invalidates caches' do
29
31
  subject.should_receive(:invalidate_cached_members)
30
- subject.db = Sequel.sqlite
32
+ subject.db = sequel_sqlite_db
31
33
  end
32
34
 
33
35
  it 'handles nil db' do
@@ -35,20 +37,21 @@ describe Pump do
35
37
  end
36
38
 
37
39
  it 'adds pagination extension' do
38
- db = Sequel.sqlite
40
+ db = sequel_sqlite_db
39
41
  db.should_receive(:extension).with(:pagination)
40
42
  subject.db = db
41
43
  end
42
44
 
43
45
  it 'turns on streaming for postgres' do
44
- db = Sequel.postgres
46
+ db = sequel_postgres_db
47
+ pending "Sequel::Postgres::Database not defined" unless defined?(Sequel::Postgres::Database)
45
48
  db.should_receive(:extension).with(:pagination)
46
49
  db.should_receive(:extension).with(:pg_streaming)
47
50
  subject.db = db
48
51
  end
49
52
 
50
53
  it 'no streaming for non-postgres' do
51
- db = Sequel.sqlite
54
+ db = sequel_sqlite_db
52
55
  db.should_receive(:extension).with(:pagination)
53
56
  db.should_not_receive(:extension).with(:pg_streaming)
54
57
  subject.db = db