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 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
- # Executes a query on this class' connection, and logs the query
5
- # or an optional message to Migratrix.log.
6
- def execute(query, msg=nil)
7
- Migratrix.log(msg || query) unless msg == false
8
- connection.execute query
9
- end
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
- # MySQL ONLY: truncates a table using TRUNCATE, which drops the
12
- # data very quickly and resets any autoindexing primary key to 1.
13
- def mysql_truncate(table)
14
- execute("TRUNCATE #{table}")
15
- end
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
- # PostGreSQL ONLY: truncates a table by deleting all its rows and
18
- # restarting its id sequence at 1.
19
- #
20
- # Note: TRUNCATE was added to PostGreSQL in version 8.3, which at
21
- # the time of this writing is still poorly adopted. This code
22
- # works on earlier versions, is MVCC-safe, and will trigger
23
- # cascading deletes.
24
- #
25
- # It does NOT, however, actually look up the table's sequence
26
- # definition. It assumes the sequence for a is named a_id_seq and
27
- # that it should be reset to 1. (A tiny dash of extra cleverness
28
- # is all that would be needed to read start_value from the
29
- # sequence, but for now this is a pure-SQL, stateless call.)
30
- def psql_truncate(table)
31
- execute("DELETE FROM #{table}; ALTER SEQUENCE #{table}_id_seq RESTART WITH 1")
32
- end
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
- # MySQL ONLY: Disables indexes on a table and locks it for
35
- # writing, optionally read-locks another list of tables, then
36
- # yields to the given block before unlocking. This prevents MySQL
37
- # from indexing the migrated data until the block is complete.
38
- # This produces a significant speedup on InnoDB tables with
39
- # multiple indexes. (The plural of anecdote is not data, but on
40
- # one heavily-indexed table, migrating 10 million records took 38
41
- # hours with indexes enabled and under 2 hours with them
42
- # disabled.)
43
- def with_mysql_indexes_disabled_on(table, *read_locked_tables, &block)
44
- log "Locking table '#{table}' and disabling indexes..."
45
- lock_cmd = "LOCK TABLES `#{table}` WRITE"
46
- if read_locked_tables.andand.size > 0
47
- lock_cmd += ', ' + (read_locked_tables.map {|t| "#{t} READ"} * ", ")
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
- def to_query(source)
40
- if source.is_a?(::ActiveRecord::Relation)
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.is_a?(::ActiveRecord::Relation)
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 = handle_where(src, options[:where]) if options[:where]
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
- # = extraction filter methods
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
 
@@ -0,0 +1,11 @@
1
+ module Migratrix
2
+ module Extractions
3
+ # An Extraction object that does nothing. Useful for plugging into
4
+ # a Migration when you need to debug other parts of the migration
5
+ class NoOp < Extraction
6
+ def extract(options={})
7
+ []
8
+ end
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,11 @@
1
+ module Migratrix
2
+ module Loads
3
+ # A Load object that does nothing. Useful for plugging into a
4
+ # Migration when you need to debug other parts of the migration
5
+ class NoOp < Load
6
+ def load(transformed_items, options={})
7
+ []
8
+ end
9
+ end
10
+ end
11
+ end
@@ -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(transform_name, options={})
108
- migration = ancestors.detect {|k| k.respond_to?(:transforms) && k.transforms[transform_name]}
109
- raise TransformNotDefined.new("Could not extend extractar '%s'; no parent Migration defines it" % transform_name) unless migration
110
- transform = migration.transforms[transform_name]
111
- transforms[transform_name] = transform.class.new(transform_name, transform.options.merge(options))
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(load_name, options={})
153
- migration = ancestors.detect {|k| k.respond_to?(:loads) && k.loads[load_name]}
154
- raise LoadNotDefined.new("Could not extend extractar '%s'; no parent Migration defines it" % load_name) unless migration
155
- load = migration.loads[load_name]
156
- loads[load_name] = load.class.new(load_name, load.options.merge(options))
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
- option = options[:extract_attribute]
230
- case option
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
- raise TypeError
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
 
@@ -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
- Marshal::load(Marshal::dump(self))
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 "#to_query" do
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.to_query(source).should == lolquery
82
+ extraction.to_sql(source).should == lolquery
83
83
  end
84
84
  end
85
85
 
86
- describe "When source has already been converted to ActiveRelation" do
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.to_query(relation).should == lolquery
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, :to_query, :execute_extract].each do |method|
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
- lambda { base_extraction.send(method, nil) }.should raise_error(NotImplementedError)
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 "should call handle_source and execute_extract only" do
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 "should call entire handler chain" do
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
- it "should call entire handler chain" do
60
- extraction.should_receive(:obtain_source).with(nil, overrides).and_return("A")
61
- extraction.should_receive(:handle_where).with("A", 5).and_return("B")
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", overrides).and_return("BONG")
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.3
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-26 00:00:00.000000000Z
12
+ date: 2011-10-31 00:00:00.000000000Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: trollop
16
- requirement: &2161676040 !ruby/object:Gem::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: *2161676040
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
-