migratrix 0.8.3 → 0.8.5
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.
- data/lib/migratrix.rb +8 -0
- data/lib/migratrix/active_record_migration_helpers.rb +54 -47
- data/lib/migratrix/extractions/active_record.rb +45 -5
- data/lib/migratrix/extractions/extraction.rb +19 -15
- data/lib/migratrix/extractions/no_op.rb +11 -0
- data/lib/migratrix/loads/no_op.rb +11 -0
- data/lib/migratrix/migration.rb +14 -11
- data/lib/migratrix/transforms/no_op.rb +11 -0
- data/lib/migratrix/transforms/transform.rb +11 -7
- data/lib/patches/object_ext.rb +17 -1
- data/spec/lib/migratrix/callbacks_spec.rb +0 -5
- data/spec/lib/migratrix/extractions/active_record_spec.rb +17 -8
- data/spec/lib/migratrix/extractions/extraction_spec.rb +22 -13
- data/spec/lib/migratrix/migration_spec.rb +1 -2
- data/spec/spec_helper.rb +2 -3
- metadata +7 -5
- data/spec/fixtures/components/no_op_components.rb +0 -22
data/lib/migratrix.rb
CHANGED
@@ -10,6 +10,7 @@ module Migratrix
|
|
10
10
|
require EXT + 'object_ext'
|
11
11
|
require EXT + 'andand'
|
12
12
|
require APP + 'exceptions'
|
13
|
+
require APP + 'active_record_migration_helpers'
|
13
14
|
|
14
15
|
require APP + 'callbacks'
|
15
16
|
require APP + 'loggable'
|
@@ -20,12 +21,15 @@ module Migratrix
|
|
20
21
|
require APP + 'migratrix'
|
21
22
|
|
22
23
|
require APP + 'extractions/extraction'
|
24
|
+
require APP + 'extractions/no_op'
|
23
25
|
require APP + 'extractions/active_record'
|
24
26
|
|
25
27
|
require APP + 'transforms/transform'
|
28
|
+
require APP + 'transforms/no_op'
|
26
29
|
require APP + 'transforms/map'
|
27
30
|
|
28
31
|
require APP + 'loads/load'
|
32
|
+
require APP + 'loads/no_op'
|
29
33
|
require APP + 'loads/yaml'
|
30
34
|
# require APP + 'loads/csv'
|
31
35
|
# require APP + 'loads/active_record'
|
@@ -73,11 +77,15 @@ module Migratrix
|
|
73
77
|
# Register in-gem Components
|
74
78
|
register_extraction :extraction, Extractions::Extraction
|
75
79
|
register_extraction :active_record, Extractions::ActiveRecord
|
80
|
+
register_extraction :no_op, Extractions::NoOp
|
76
81
|
|
77
82
|
register_transform :transform, Transforms::Transform
|
78
83
|
register_transform :map, Transforms::Map
|
84
|
+
register_transform :no_op, Transforms::NoOp
|
79
85
|
|
80
86
|
register_load :load, Loads::Load
|
87
|
+
register_load :no_op, Loads::NoOp
|
81
88
|
register_load :yaml, Loads::Yaml
|
89
|
+
|
82
90
|
end
|
83
91
|
|
@@ -1,57 +1,64 @@
|
|
1
|
+
require 'active_support/concern'
|
2
|
+
|
1
3
|
module Migratrix
|
2
4
|
module ActiveRecordMigrationHelpers
|
5
|
+
extend ActiveSupport::Concern
|
3
6
|
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
7
|
+
module ClassMethods
|
8
|
+
# Executes a query on this class' connection, and logs the query
|
9
|
+
# or an optional message to Migratrix.logger.
|
10
|
+
def execute(query, msg=nil)
|
11
|
+
Migratrix.logger.info(msg || query) unless msg == false
|
12
|
+
connection.execute query
|
13
|
+
end
|
10
14
|
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
15
|
+
# MySQL ONLY: truncates a table using TRUNCATE, which drops the
|
16
|
+
# data very quickly and resets any autoindexing primary key to 1.
|
17
|
+
def mysql_truncate(table=nil)
|
18
|
+
table ||= table_name
|
19
|
+
execute("TRUNCATE #{table}")
|
20
|
+
end
|
16
21
|
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
22
|
+
# PostGreSQL ONLY: truncates a table by deleting all its rows and
|
23
|
+
# restarting its id sequence at 1.
|
24
|
+
#
|
25
|
+
# Note: TRUNCATE was added to PostGreSQL in version 8.3, which at
|
26
|
+
# the time of this writing is still poorly adopted. This code
|
27
|
+
# works on earlier versions, is MVCC-safe, and will trigger
|
28
|
+
# cascading deletes.
|
29
|
+
#
|
30
|
+
# It does NOT, however, actually look up the table's sequence
|
31
|
+
# definition. It assumes the sequence for a is named a_id_seq and
|
32
|
+
# that it should be reset to 1. (A tiny dash of extra cleverness
|
33
|
+
# is all that would be needed to read start_value from the
|
34
|
+
# sequence, but for now this is a pure-SQL, stateless call.)
|
35
|
+
def psql_truncate(table=nil)
|
36
|
+
table ||= table_name
|
37
|
+
execute("DELETE FROM #{table}; ALTER SEQUENCE #{table}_id_seq RESTART WITH 1")
|
38
|
+
end
|
33
39
|
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
40
|
+
# MySQL ONLY: Disables indexes on a table and locks it for
|
41
|
+
# writing, optionally read-locks another list of tables, then
|
42
|
+
# yields to the given block before unlocking. This prevents MySQL
|
43
|
+
# from indexing the migrated data until the block is complete.
|
44
|
+
# This produces a significant speedup on InnoDB tables with
|
45
|
+
# multiple indexes. (The plural of anecdote is not data, but on
|
46
|
+
# one heavily-indexed table, migrating 10 million records took 38
|
47
|
+
# hours with indexes enabled and under 2 hours with them
|
48
|
+
# disabled.)
|
49
|
+
def with_mysql_indexes_disabled_on(table, *read_locked_tables, &block)
|
50
|
+
info "Locking table '#{table}' and disabling indexes..."
|
51
|
+
lock_cmd = "LOCK TABLES `#{table}` WRITE"
|
52
|
+
if read_locked_tables.andand.size > 0
|
53
|
+
lock_cmd += ', ' + (read_locked_tables.map {|t| "#{t} READ"} * ", ")
|
54
|
+
end
|
55
|
+
execute lock_cmd
|
56
|
+
execute("/*!40000 ALTER TABLE `#{table}` DISABLE KEYS */")
|
57
|
+
yield
|
58
|
+
info "Unlocking table '#{table}' and re-enabling indexes..."
|
59
|
+
execute("/*!40000 ALTER TABLE `#{table}` ENABLE KEYS */")
|
60
|
+
execute("UNLOCK TABLES")
|
48
61
|
end
|
49
|
-
execute lock_cmd
|
50
|
-
execute("/*!40000 ALTER TABLE `#{table}` DISABLE KEYS */")
|
51
|
-
yield
|
52
|
-
log "Unlocking table '#{table}' and re-enabling indexes..."
|
53
|
-
execute("/*!40000 ALTER TABLE `#{table}` ENABLE KEYS */")
|
54
|
-
execute("UNLOCK TABLES")
|
55
62
|
end
|
56
63
|
end
|
57
64
|
end
|
@@ -2,7 +2,7 @@ module Migratrix
|
|
2
2
|
module Extractions
|
3
3
|
# Extraction that expects to be pointed at an ActiveRecord class.
|
4
4
|
class ActiveRecord < Extraction
|
5
|
-
set_valid_options :fetchall
|
5
|
+
set_valid_options :fetchall, :includes, :joins
|
6
6
|
|
7
7
|
def source=(new_source)
|
8
8
|
raise TypeError.new(":source is of type must be an ActiveRecord model class (must inherit from ActiveRecord::Base)") unless is_ar?(new_source)
|
@@ -15,10 +15,25 @@ module Migratrix
|
|
15
15
|
|
16
16
|
def obtain_source(source, options={})
|
17
17
|
raise ExtractionSourceUndefined unless source
|
18
|
-
raise TypeError.new(":source is of type must be an ActiveRecord model class (must inherit from ActiveRecord::Base)") unless is_ar?(source)
|
19
18
|
source
|
20
19
|
end
|
21
20
|
|
21
|
+
def process_source(source, options={})
|
22
|
+
options = @options.merge(options)
|
23
|
+
source = super source, options
|
24
|
+
source = handle_joins(source, options[:joins]) if options[:joins]
|
25
|
+
source = handle_includes(source, options[:includes]) if options[:includes]
|
26
|
+
source
|
27
|
+
end
|
28
|
+
|
29
|
+
def handle_joins(source, clause)
|
30
|
+
source.joins(clause)
|
31
|
+
end
|
32
|
+
|
33
|
+
def handle_includes(source, clause)
|
34
|
+
source.includes(clause)
|
35
|
+
end
|
36
|
+
|
22
37
|
def handle_where(source, clause)
|
23
38
|
source.where(clause)
|
24
39
|
end
|
@@ -36,8 +51,33 @@ module Migratrix
|
|
36
51
|
end
|
37
52
|
|
38
53
|
# Constructs the query
|
39
|
-
|
40
|
-
|
54
|
+
#
|
55
|
+
# TODO: A bit of a POLS violation here. Let's say you define a
|
56
|
+
# migration class that has an extraction, then instantiate that
|
57
|
+
# migration with m = MyMigration.new(where: 'id=42'). You might
|
58
|
+
# expect to be abe to call m.extractions[:default].to_sql and
|
59
|
+
# get the query, but the reality is that the extraction hasn't
|
60
|
+
# actually seen the migration's options yet. They are passed in
|
61
|
+
# to extract, but not to to_sql. Not sure how to resolve this
|
62
|
+
# cleanly. Some options: 1. have Components know who their
|
63
|
+
# Migration is and ask it for its options (blegh); 2. pass in
|
64
|
+
# options here to to_sql (also blegh, because now end-users have
|
65
|
+
# to know they need to pass in the migration's options to
|
66
|
+
# to_sql); 3. have a proxy method on Migration that essentially
|
67
|
+
# works just like extract, but returns the query instead of the
|
68
|
+
# results. I dislike this mechanism the least but it's still
|
69
|
+
# only applicable to certain subclasses of Extraction so I
|
70
|
+
# hesitate to clutter Migration's API; 4. change
|
71
|
+
# Migration#extractions (et al) so it settles its options with
|
72
|
+
# the component before returning it. This seems like it would
|
73
|
+
# appear the cleanest to the end user but also seems like it
|
74
|
+
# would be excessively magical and a weird source of bugs--eg if
|
75
|
+
# you try to access migration.loads[:cheese] and it crashes
|
76
|
+
# because of an invalid option in loads[:wine].
|
77
|
+
def to_sql(source=nil)
|
78
|
+
source ||= @source
|
79
|
+
source = process_source(obtain_source(source))
|
80
|
+
if source.respond_to? :to_sql
|
41
81
|
source.to_sql
|
42
82
|
else
|
43
83
|
handle_where(source, 1).to_sql
|
@@ -46,7 +86,7 @@ module Migratrix
|
|
46
86
|
|
47
87
|
def execute_extract(src, options={})
|
48
88
|
return src.all if options['fetchall']
|
49
|
-
ret = if src.
|
89
|
+
ret = if src.respond_to? :to_sql
|
50
90
|
src
|
51
91
|
else
|
52
92
|
handle_where(src, 1)
|
@@ -15,6 +15,8 @@ module Migratrix
|
|
15
15
|
end
|
16
16
|
|
17
17
|
def extract(options={})
|
18
|
+
options = options.deep_copy
|
19
|
+
options[:where] = Array(options[:where]) + Array(@options[:where])
|
18
20
|
options = @options.merge(options).symbolize_keys
|
19
21
|
|
20
22
|
# TODO: Raise error if self.abstract? DANGER/NOTE that this is
|
@@ -22,15 +24,23 @@ module Migratrix
|
|
22
24
|
# extracted to a strategy object.
|
23
25
|
|
24
26
|
src = obtain_source(self.source, options)
|
25
|
-
src =
|
26
|
-
src = handle_order(src, options[:order]) if options[:order]
|
27
|
-
src = handle_limit(src, options[:limit]) if options[:limit]
|
28
|
-
src = handle_offset(src, options[:offset]) if options[:offset]
|
27
|
+
src = process_source(src, options)
|
29
28
|
execute_extract(src, options)
|
30
29
|
end
|
31
30
|
|
31
|
+
def process_source(source, options)
|
32
|
+
if options[:where]
|
33
|
+
options[:where].each do |where|
|
34
|
+
source = handle_where(source, where)
|
35
|
+
end
|
36
|
+
end
|
37
|
+
source = handle_order(source, options[:order]) if options[:order]
|
38
|
+
source = handle_limit(source, options[:limit]) if options[:limit]
|
39
|
+
source = handle_offset(source, options[:offset]) if options[:offset]
|
40
|
+
source
|
41
|
+
end
|
32
42
|
|
33
|
-
|
43
|
+
# = extraction filter methods
|
34
44
|
#
|
35
45
|
# The handle_* methods receive a source and return a source and
|
36
46
|
# must be chainable. For example, source might come in as an
|
@@ -55,28 +65,22 @@ module Migratrix
|
|
55
65
|
end
|
56
66
|
|
57
67
|
# Apply where clause to source, return new source.
|
58
|
-
def handle_where(source)
|
68
|
+
def handle_where(source, where)
|
59
69
|
raise NotImplementedError
|
60
70
|
end
|
61
71
|
|
62
72
|
# Apply limit clause to source, return new source.
|
63
|
-
def handle_limit(source)
|
73
|
+
def handle_limit(source, limit)
|
64
74
|
raise NotImplementedError
|
65
75
|
end
|
66
76
|
|
67
77
|
# Apply offset clause to source, return new source.
|
68
|
-
def handle_offset(source)
|
78
|
+
def handle_offset(source, offset)
|
69
79
|
raise NotImplementedError
|
70
80
|
end
|
71
81
|
|
72
82
|
# Apply order clause to source, return new source.
|
73
|
-
def handle_order(source)
|
74
|
-
raise NotImplementedError
|
75
|
-
end
|
76
|
-
|
77
|
-
# Constructs the query, if applicable. May not exist or make
|
78
|
-
# sense for non-SQL and/or non-ActiveRecord extractions.
|
79
|
-
def to_query(source)
|
83
|
+
def handle_order(source, order)
|
80
84
|
raise NotImplementedError
|
81
85
|
end
|
82
86
|
|
data/lib/migratrix/migration.rb
CHANGED
@@ -63,7 +63,8 @@ module Migratrix
|
|
63
63
|
extractions[nickname] = Migratrix.extraction(nickname, registered_name, options)
|
64
64
|
end
|
65
65
|
|
66
|
-
def self.extend_extraction(nickname, options=
|
66
|
+
def self.extend_extraction(nickname, options=nil)
|
67
|
+
nickname, options = :default, nickname if options.nil?
|
67
68
|
migration = ancestors.detect {|k| k.respond_to?(:extractions) && k.extractions[nickname]}
|
68
69
|
raise ExtractionNotDefined.new("Could not extend extraction '%s'; no parent Migration defines it" % nickname) unless migration
|
69
70
|
extraction = migration.extractions[nickname]
|
@@ -104,11 +105,12 @@ module Migratrix
|
|
104
105
|
transforms[nickname] = Migratrix.transform(nickname, registered_name, options)
|
105
106
|
end
|
106
107
|
|
107
|
-
def self.extend_transform(
|
108
|
-
|
109
|
-
|
110
|
-
transform
|
111
|
-
|
108
|
+
def self.extend_transform(nickname, options=nil)
|
109
|
+
nickname, options = :default, nickname if options.nil?
|
110
|
+
migration = ancestors.detect {|k| k.respond_to?(:transforms) && k.transforms[nickname]}
|
111
|
+
raise TransformNotDefined.new("Could not extend transform '%s'; no parent Migration defines it" % nickname) unless migration
|
112
|
+
transform = migration.transforms[nickname]
|
113
|
+
transforms[nickname] = transform.class.new(nickname, transform.options.merge(options))
|
112
114
|
end
|
113
115
|
|
114
116
|
def self.transforms
|
@@ -149,11 +151,12 @@ module Migratrix
|
|
149
151
|
# loads[name] = Migratrix.load(name, type, options)
|
150
152
|
# end
|
151
153
|
|
152
|
-
def self.extend_load(
|
153
|
-
|
154
|
-
|
155
|
-
load
|
156
|
-
|
154
|
+
def self.extend_load(nickname, options=nil)
|
155
|
+
nickname, options = :default, nickname if options.nil?
|
156
|
+
migration = ancestors.detect {|k| k.respond_to?(:loads) && k.loads[nickname]}
|
157
|
+
raise LoadNotDefined.new("Could not extend load '%s'; no parent Migration defines it" % nickname) unless migration
|
158
|
+
load = migration.loads[nickname]
|
159
|
+
loads[nickname] = load.class.new(nickname, load.options.merge(options))
|
157
160
|
end
|
158
161
|
|
159
162
|
def self.loads
|
@@ -0,0 +1,11 @@
|
|
1
|
+
module Migratrix
|
2
|
+
module Transforms
|
3
|
+
# A Transform object that does nothing. Useful for plugging into a
|
4
|
+
# Migration when you need to debug other parts of the migration
|
5
|
+
class NoOp < Transform
|
6
|
+
def transform(extracted_items, options={})
|
7
|
+
[]
|
8
|
+
end
|
9
|
+
end
|
10
|
+
end
|
11
|
+
end
|
@@ -226,14 +226,18 @@ module Migratrix
|
|
226
226
|
|
227
227
|
def extract_attribute(object, attribute_or_extract)
|
228
228
|
raise NotImplementedError unless options[:extract_attribute]
|
229
|
-
|
230
|
-
|
231
|
-
when Proc
|
232
|
-
option.call(object, attribute_or_extract)
|
233
|
-
when Symbol
|
234
|
-
object.send(option, attribute_or_extract)
|
229
|
+
if attribute_or_extract.is_a? Proc
|
230
|
+
attribute_or_extract.call(object)
|
235
231
|
else
|
236
|
-
|
232
|
+
option = options[:extract_attribute]
|
233
|
+
case option
|
234
|
+
when Proc
|
235
|
+
option.call(object, attribute_or_extract)
|
236
|
+
when Symbol
|
237
|
+
object.send(option, attribute_or_extract)
|
238
|
+
else
|
239
|
+
raise TypeError
|
240
|
+
end
|
237
241
|
end
|
238
242
|
end
|
239
243
|
|
data/lib/patches/object_ext.rb
CHANGED
@@ -1,6 +1,22 @@
|
|
1
1
|
class Object
|
2
|
+
# Make a deep copy of an object as safely as possible. This was
|
3
|
+
# originally a simple Marshal::load(Marshal::dump(self)) but it
|
4
|
+
# turns out in Migratrix that we frequently need to copy IO streams
|
5
|
+
# and lambdas, neither of which can be marshaled. Then it was a
|
6
|
+
# simple dup, but some singleton types like Fixnum cannot be dup'ed.
|
7
|
+
# So now we have this monstrosity.
|
2
8
|
def deep_copy
|
3
|
-
|
9
|
+
if is_a?(Array)
|
10
|
+
map(&:deep_copy)
|
11
|
+
elsif is_a?(Hash)
|
12
|
+
Hash[to_a.map {|k,v| [k, v.deep_copy]}]
|
13
|
+
else
|
14
|
+
begin
|
15
|
+
Marshal::load(Marshal::dump(self))
|
16
|
+
rescue TypeError
|
17
|
+
dup
|
18
|
+
end
|
19
|
+
end
|
4
20
|
end
|
5
21
|
end
|
6
22
|
|
@@ -1,5 +1,3 @@
|
|
1
|
-
require 'no_op_components'
|
2
|
-
|
3
1
|
class TestCallbackMigration < Migratrix::Migration
|
4
2
|
set_extraction :test, :no_op
|
5
3
|
set_transform :test, :no_op
|
@@ -127,9 +125,6 @@ end
|
|
127
125
|
describe "callbacks" do
|
128
126
|
describe "sanity check cat" do
|
129
127
|
it "is sanity checked" do
|
130
|
-
NoOpExtraction.should_not be_nil
|
131
|
-
NoOpTransform.should_not be_nil
|
132
|
-
NoOpLoad.should_not be_nil
|
133
128
|
TestCallbackMigration.should_not be_nil
|
134
129
|
MethodCallbackMigration.should_not be_nil
|
135
130
|
end
|
@@ -30,13 +30,13 @@ describe Migratrix::Extractions::ActiveRecord do
|
|
30
30
|
|
31
31
|
describe ".local_valid_options" do
|
32
32
|
it "returns the valid set of option keys" do
|
33
|
-
Migratrix::Extractions::ActiveRecord.local_valid_options.should == [:fetchall]
|
33
|
+
Migratrix::Extractions::ActiveRecord.local_valid_options.should == [:fetchall, :includes, :joins]
|
34
34
|
end
|
35
35
|
end
|
36
36
|
|
37
37
|
describe ".valid_options" do
|
38
38
|
it "returns the valid set of option keys" do
|
39
|
-
Migratrix::Extractions::ActiveRecord.valid_options.should == [:fetchall] + Migratrix::Extractions::Extraction.valid_options
|
39
|
+
Migratrix::Extractions::ActiveRecord.valid_options.should == [:fetchall, :includes, :joins] + Migratrix::Extractions::Extraction.valid_options
|
40
40
|
end
|
41
41
|
end
|
42
42
|
|
@@ -57,7 +57,7 @@ describe Migratrix::Extractions::ActiveRecord do
|
|
57
57
|
extraction.source = source
|
58
58
|
end
|
59
59
|
|
60
|
-
[:where, :order, :limit, :offset].each do |handler|
|
60
|
+
[:where, :order, :limit, :offset, :includes, :joins].each do |handler|
|
61
61
|
describe "#handle_#{handler}" do
|
62
62
|
it "calls #{handler} on the source ActiveRelation" do
|
63
63
|
source.should_receive(handler).with(1).and_return(nil)
|
@@ -67,7 +67,7 @@ describe Migratrix::Extractions::ActiveRecord do
|
|
67
67
|
end
|
68
68
|
end
|
69
69
|
|
70
|
-
describe "#
|
70
|
+
describe "#to_sql" do
|
71
71
|
let(:source) { TestModel }
|
72
72
|
let(:relation) { source.where(1) }
|
73
73
|
let(:lolquery) { 'SELECT "HAY GUYZ IM A QUARY LOL"' }
|
@@ -75,18 +75,27 @@ describe Migratrix::Extractions::ActiveRecord do
|
|
75
75
|
extraction.source = source
|
76
76
|
end
|
77
77
|
|
78
|
-
describe "when source is ActiveRecord" do
|
78
|
+
describe "when source does not have to_sql (e.g. is ActiveRecord)" do
|
79
79
|
it "converts it to ActiveRelation with where(1)" do
|
80
80
|
extraction.should_receive(:handle_where).with(source, 1).and_return(relation)
|
81
81
|
relation.should_receive(:to_sql).and_return(lolquery)
|
82
|
-
extraction.
|
82
|
+
extraction.to_sql(source).should == lolquery
|
83
83
|
end
|
84
84
|
end
|
85
85
|
|
86
|
-
describe "When source
|
86
|
+
describe "When source responds to to_sql (e.g. is already an ActiveRelation)" do
|
87
87
|
it "delegates to to_sql on source" do
|
88
|
+
relation.should_receive(:respond_to?).with(:to_sql).and_return(true)
|
88
89
|
relation.should_receive(:to_sql).and_return(lolquery)
|
89
|
-
extraction.
|
90
|
+
extraction.to_sql(relation).should == lolquery
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
describe "with default source" do
|
95
|
+
it "uses @source" do
|
96
|
+
extraction = Migratrix::Extractions::ActiveRecord.new(:default, source: source)
|
97
|
+
source.should_receive(:to_sql).and_return(lolquery)
|
98
|
+
extraction.to_sql.should == lolquery
|
90
99
|
end
|
91
100
|
end
|
92
101
|
end
|
@@ -19,10 +19,11 @@ describe Migratrix::Extractions::Extraction do
|
|
19
19
|
|
20
20
|
describe "unimplemented methods:" do
|
21
21
|
let(:base_extraction) { Migratrix::Extractions::Extraction.new :test }
|
22
|
-
[:obtain_source, :handle_where, :handle_limit, :handle_offset, :handle_order, :
|
22
|
+
[:obtain_source, :handle_where, :handle_limit, :handle_offset, :handle_order, :execute_extract].each do |method|
|
23
23
|
describe "#{method}" do
|
24
24
|
it "raises NotImplementedError" do
|
25
|
-
|
25
|
+
args = [nil, nil]
|
26
|
+
lambda { base_extraction.send(method, *args) }.should raise_error(NotImplementedError)
|
26
27
|
end
|
27
28
|
end
|
28
29
|
end
|
@@ -31,17 +32,17 @@ describe Migratrix::Extractions::Extraction do
|
|
31
32
|
describe "#extract (default strategy)" do
|
32
33
|
describe "with no options" do
|
33
34
|
let(:extraction) { TestExtraction.new :test }
|
34
|
-
it "
|
35
|
-
extraction.should_receive(:obtain_source).with(nil, {}).and_return(13)
|
36
|
-
extraction.should_receive(:execute_extract).with(13, {}).and_return(64)
|
35
|
+
it "calls handle_source and execute_extract only" do
|
36
|
+
extraction.should_receive(:obtain_source).with(nil, {where: []}).and_return(13)
|
37
|
+
extraction.should_receive(:execute_extract).with(13, {where: []}).and_return(64)
|
37
38
|
extraction.extract.should == 64
|
38
39
|
end
|
39
40
|
end
|
40
41
|
|
41
42
|
describe "with all options" do
|
42
|
-
let(:options) { { :where => 1, :order => 2, :limit => 3, :offset => 4 } }
|
43
|
+
let(:options) { { :where => [1], :order => 2, :limit => 3, :offset => 4 } }
|
43
44
|
let(:extraction) { TestExtraction.new :test, options }
|
44
|
-
it "
|
45
|
+
it "calls entire handler chain" do
|
45
46
|
extraction.should_receive(:obtain_source).with(nil, options).and_return("A")
|
46
47
|
extraction.should_receive(:handle_where).with("A", 1).and_return("B")
|
47
48
|
extraction.should_receive(:handle_order).with("B", 2).and_return("C")
|
@@ -53,19 +54,27 @@ describe Migratrix::Extractions::Extraction do
|
|
53
54
|
end
|
54
55
|
|
55
56
|
describe "with overridden options" do
|
56
|
-
let(:options) { { :where => 1, :order => 2, :limit => 3, :offset => 4 } }
|
57
|
+
let(:options) { { :where => [1], :order => 2, :limit => 3, :offset => 4 } }
|
57
58
|
let(:extraction) { TestExtraction.new :test, options }
|
58
|
-
let(:overrides) { {:where => 5, :order => 6, :limit => 7, :offset => 8 } }
|
59
|
-
|
60
|
-
|
61
|
-
extraction.should_receive(:
|
59
|
+
let(:overrides) { {:where => [5], :order => 6, :limit => 7, :offset => 8 } }
|
60
|
+
let(:merged_options) { {:where => [5, 1], :order => 6, :limit => 7, :offset => 8 } }
|
61
|
+
it "calls entire handler chain, merging where clauses" do
|
62
|
+
extraction.should_receive(:obtain_source).with(nil, merged_options).and_return("A")
|
63
|
+
extraction.should_receive(:handle_where).exactly(2).times.and_return("B")
|
62
64
|
extraction.should_receive(:handle_order).with("B", 6).and_return("C")
|
63
65
|
extraction.should_receive(:handle_limit).with("C", 7).and_return("D")
|
64
66
|
extraction.should_receive(:handle_offset).with("D", 8).and_return("E")
|
65
|
-
extraction.should_receive(:execute_extract).with("E",
|
67
|
+
extraction.should_receive(:execute_extract).with("E", merged_options).and_return("BONG")
|
66
68
|
extraction.extract(overrides).should == "BONG"
|
67
69
|
end
|
68
70
|
end
|
71
|
+
|
72
|
+
describe "with where clause" do
|
73
|
+
let(:options) { { :where => 1, :order => 2, :limit => 3, :offset => 4 } }
|
74
|
+
let(:extraction) { TestExtraction.new :test, options }
|
75
|
+
it "promotes where clause to array" do
|
76
|
+
end
|
77
|
+
end
|
69
78
|
end
|
70
79
|
end
|
71
80
|
|
@@ -1,5 +1,4 @@
|
|
1
1
|
require 'spec_helper'
|
2
|
-
require 'no_op_components'
|
3
2
|
require 'test_migration'
|
4
3
|
require 'inherited_migrations'
|
5
4
|
|
@@ -193,7 +192,7 @@ describe Migratrix::Migration do
|
|
193
192
|
describe "#{component}" do
|
194
193
|
it "extends the #{component} to child class" do
|
195
194
|
ChildMigration1.send("extend_#{component}", :cheese, { second_option: 2 })
|
196
|
-
ChildMigration1.new.send("#{component}s")[:cheese].options.should == { second_option: 2, first_option: 'id>100'}
|
195
|
+
ChildMigration1.new.send("#{component}s")[:cheese].options.should == { second_option: 2, first_option: 'id>100' }
|
197
196
|
end
|
198
197
|
|
199
198
|
it "extends the #{component} to the grandchild class" do
|
data/spec/spec_helper.rb
CHANGED
@@ -24,13 +24,12 @@ module Rails
|
|
24
24
|
end
|
25
25
|
end
|
26
26
|
|
27
|
-
Dir[SPEC + "support/**/*.rb"].each {|f| require f}
|
28
|
-
|
29
27
|
require LIB + 'migratrix'
|
30
|
-
|
31
28
|
$:.unshift SPEC + "fixtures/migrations/"
|
32
29
|
$:.unshift SPEC + "fixtures/components/"
|
33
30
|
|
31
|
+
Dir[SPEC + "support/**/*.rb"].each {|f| require f}
|
32
|
+
|
34
33
|
|
35
34
|
# Redirect singleton logger to logger of our choice, then release it
|
36
35
|
# after the spec finishes or crashes.
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: migratrix
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.8.
|
4
|
+
version: 0.8.5
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,11 +9,11 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2011-10-
|
12
|
+
date: 2011-10-31 00:00:00.000000000Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: trollop
|
16
|
-
requirement: &
|
16
|
+
requirement: &2164754380 !ruby/object:Gem::Requirement
|
17
17
|
none: false
|
18
18
|
requirements:
|
19
19
|
- - ! '>='
|
@@ -21,7 +21,7 @@ dependencies:
|
|
21
21
|
version: '0'
|
22
22
|
type: :runtime
|
23
23
|
prerelease: false
|
24
|
-
version_requirements: *
|
24
|
+
version_requirements: *2164754380
|
25
25
|
description: Migratrix, a Rails legacy database migration tool supporting multiple
|
26
26
|
strategies, including arbitrary n-ary migrations (1->n, n->1, n->m), arbitrary inputs
|
27
27
|
and outputs (ActiveRecord, bare SQL, CSV) and migration logging
|
@@ -41,7 +41,9 @@ files:
|
|
41
41
|
- lib/migratrix/exceptions.rb
|
42
42
|
- lib/migratrix/extractions/active_record.rb
|
43
43
|
- lib/migratrix/extractions/extraction.rb
|
44
|
+
- lib/migratrix/extractions/no_op.rb
|
44
45
|
- lib/migratrix/loads/load.rb
|
46
|
+
- lib/migratrix/loads/no_op.rb
|
45
47
|
- lib/migratrix/loads/yaml.rb
|
46
48
|
- lib/migratrix/loggable.rb
|
47
49
|
- lib/migratrix/migration.rb
|
@@ -49,12 +51,12 @@ files:
|
|
49
51
|
- lib/migratrix/migratrix.rb
|
50
52
|
- lib/migratrix/registry.rb
|
51
53
|
- lib/migratrix/transforms/map.rb
|
54
|
+
- lib/migratrix/transforms/no_op.rb
|
52
55
|
- lib/migratrix/transforms/transform.rb
|
53
56
|
- lib/migratrix/valid_options.rb
|
54
57
|
- lib/patches/andand.rb
|
55
58
|
- lib/patches/object_ext.rb
|
56
59
|
- lib/patches/string_ext.rb
|
57
|
-
- spec/fixtures/components/no_op_components.rb
|
58
60
|
- spec/fixtures/migrations/inherited_migrations.rb
|
59
61
|
- spec/fixtures/migrations/test_migration.rb
|
60
62
|
- spec/lib/migratrix/_loggable_spec.rb
|
@@ -1,22 +0,0 @@
|
|
1
|
-
class NoOpExtraction < Migratrix::Extractions::Extraction
|
2
|
-
def extract(options={})
|
3
|
-
[]
|
4
|
-
end
|
5
|
-
end
|
6
|
-
|
7
|
-
class NoOpTransform < Migratrix::Transforms::Transform
|
8
|
-
def transform(exts, options={})
|
9
|
-
[]
|
10
|
-
end
|
11
|
-
end
|
12
|
-
|
13
|
-
class NoOpLoad < Migratrix::Loads::Load
|
14
|
-
def load(trans, options={})
|
15
|
-
[]
|
16
|
-
end
|
17
|
-
end
|
18
|
-
|
19
|
-
Migratrix::Migratrix.register_extraction :no_op, NoOpExtraction
|
20
|
-
Migratrix::Migratrix.register_transform :no_op, NoOpTransform
|
21
|
-
Migratrix::Migratrix.register_load :no_op, NoOpLoad
|
22
|
-
|