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