migratrix 0.0.7 → 0.0.9

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.
@@ -4,5 +4,6 @@ module Migratrix
4
4
  class MigrationAlreadyExists < Exception; end
5
5
  class MigrationFileNotFound < Exception; end
6
6
  class MigrationNotDefined < Exception; end
7
+ class ExtractorSourceUndefined < Exception; end
7
8
  end
8
9
 
@@ -0,0 +1,54 @@
1
+ module Migratrix
2
+ module Extractors
3
+ # Extractor that expects to be pointed at an ActiveRecord class.
4
+ class ActiveRecord < Extractor
5
+ # TODO: Raise TypeError in initialize unless Extractor source is
6
+ # an ActiveRecord model
7
+
8
+ def source=(new_source)
9
+ raise TypeError.new(":source is of type must be an ActiveRecord model class (must inherit from ActiveRecord::Base)") unless is_ar?(new_source)
10
+ @source = new_source
11
+ end
12
+
13
+ def is_ar?(source)
14
+ source.is_a?(Class) && source.ancestors.include?(::ActiveRecord::Base)
15
+ end
16
+
17
+ def obtain_source(source)
18
+ raise ExtractorSourceUndefined unless source
19
+ raise TypeError.new(":source is of type must be an ActiveRecord model class (must inherit from ActiveRecord::Base)") unless is_ar?(source)
20
+ source
21
+ end
22
+
23
+ def handle_where(source, clause)
24
+ source.where(clause)
25
+ end
26
+
27
+ def handle_limit(source, clause)
28
+ source.limit(clause.to_i)
29
+ end
30
+
31
+ def handle_offset(source, clause)
32
+ source.offset(clause.to_i)
33
+ end
34
+
35
+ def handle_order(source, clause)
36
+ source.order(clause)
37
+ end
38
+
39
+ # Constructs the query
40
+ def to_query(source)
41
+ if source.is_a?(::ActiveRecord::Relation)
42
+ source.to_sql
43
+ else
44
+ source.handle_where(1).to_sql
45
+ end
46
+ end
47
+
48
+ def execute_extract(source)
49
+ source.all
50
+ end
51
+ end
52
+ end
53
+ end
54
+
@@ -0,0 +1,88 @@
1
+ #require 'active_model/attribute_methods'
2
+
3
+ module Migratrix
4
+ module Extractors
5
+ # base class for extraction
6
+ class Extractor
7
+ include ::Migratrix::Loggable
8
+ # include ActiveModel::AttributeMethods
9
+
10
+ attr_accessor :source, :options
11
+
12
+ def initialize(options={})
13
+ @options = options.deep_copy
14
+ self.source = options[:source] if options[:source]
15
+ end
16
+
17
+ def extract(options={})
18
+ options = @options.merge(options)
19
+
20
+ # TODO: Raise error if self.abstract? DANGER/NOTE that this is
21
+ # the "default strategy" for extraction, and may need to be
22
+ # extracted to a strategy object.
23
+
24
+ src = obtain_source(self.source)
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"]
29
+ execute_extract(src)
30
+ end
31
+
32
+ # = extraction filter methods
33
+ #
34
+ # The handle_* methods receive a source and return a source and
35
+ # must be chainable. For example, source might come in as an
36
+ # ActiveRecord::Base class (or as an ActiveRecord::Relation),
37
+ # and it will be returned as an ActiveRecord::Relation, which
38
+ # will not be expanded into an actual resultset until the
39
+ # execute_extract step is called. On the other hand, for a csv
40
+ # reader, the first self.source call might read all the rows of
41
+ # the table and hold them in memory, the where clause might
42
+ # filter it, the order clause might sort it, and the
43
+ # execute_extract might simply be a no-op returning the
44
+ # processed results.
45
+
46
+ # First step in extraction is to take the given source and turn
47
+ # it into something that the filter chain can used. The
48
+ # ActiveRecord extractor uses a legacy model class as its source
49
+ # so it can simply return its source. A CSV or Yaml extractor
50
+ # here might need to read the entire file contents and returns
51
+ # the full, unfiltered data source.
52
+ def obtain_source(source)
53
+ raise NotImplementedError
54
+ end
55
+
56
+ # Apply where clause to source, return new source.
57
+ def handle_where(source)
58
+ raise NotImplementedError
59
+ end
60
+
61
+ # Apply limit clause to source, return new source.
62
+ def handle_limit(source)
63
+ raise NotImplementedError
64
+ end
65
+
66
+ # Apply offset clause to source, return new source.
67
+ def handle_offset(source)
68
+ raise NotImplementedError
69
+ end
70
+
71
+ # Apply order clause to source, return new source.
72
+ def handle_order(source)
73
+ raise NotImplementedError
74
+ end
75
+
76
+ # Constructs the query, if applicable. May not exist or make
77
+ # sense for non-SQL and/or non-ActiveRecord extractors.
78
+ def to_query(source)
79
+ raise NotImplementedError
80
+ end
81
+
82
+ # Execute the extraction and return the result set.
83
+ def execute_extract(source)
84
+ raise NotImplementedError
85
+ end
86
+ end
87
+ end
88
+ end
@@ -7,12 +7,45 @@ module Migratrix
7
7
  def logger
8
8
  ::Migratrix::Migratrix.logger
9
9
  end
10
+ # If you skip the logger object, your class name will be prepended to the message.
11
+ def info(msg)
12
+ logger.info("#{self}: #{msg}")
13
+ end
14
+ def debug(msg)
15
+ logger.debug("#{self}: #{msg}")
16
+ end
17
+ def warn(msg)
18
+ logger.warn("#{self}: #{msg}")
19
+ end
20
+ def error(msg)
21
+ logger.error("#{self}: #{msg}")
22
+ end
23
+ def fatal(msg)
24
+ logger.fatal("#{self}: #{msg}")
25
+ end
10
26
  end
11
27
 
12
28
  module InstanceMethods
13
29
  def logger
14
30
  ::Migratrix::Migratrix.logger
15
31
  end
32
+
33
+ # If you skip the logger object, your class name will be prepended to the message.
34
+ def info(msg)
35
+ logger.info("#{self.class}: #{msg}")
36
+ end
37
+ def debug(msg)
38
+ logger.debug("#{self.class}: #{msg}")
39
+ end
40
+ def warn(msg)
41
+ logger.warn("#{self.class}: #{msg}")
42
+ end
43
+ def error(msg)
44
+ logger.error("#{self.class}: #{msg}")
45
+ end
46
+ def fatal(msg)
47
+ logger.fatal("#{self.class}: #{msg}")
48
+ end
16
49
  end
17
50
  end
18
51
  end
@@ -1,19 +1,40 @@
1
+ require 'active_model/attribute_methods'
2
+
1
3
  module Migratrix
2
4
  # Superclass for all migrations. Migratrix COULD check to see that a
3
5
  # loaded migration inherits from this class, but hey, duck typing.
4
6
  class Migration
5
7
  include ::Migratrix::Loggable
8
+ include ActiveModel::AttributeMethods
6
9
 
10
+ cattr_accessor :extractor
7
11
  attr_accessor :options
8
12
 
9
13
  def initialize(options={})
10
- # cannot make a deep copy of an IO stream (e.g. logger) so make a shallow copy of it and move it out of the way
11
14
  @options = options.deep_copy
12
15
  end
13
16
 
14
- # Load this data from source
17
+ # Adds an extractor to the extractors chain.
18
+ def self.set_extractor(name, options={})
19
+ # klassify this name
20
+ raise NotImplementedError.new("Migratrix currently only supports ActiveRecord extractor.") unless name == :active_record
21
+ @@extractor = ::Migratrix::Extractors::ActiveRecord.new(options)
22
+ end
23
+
24
+ def extractor
25
+ @@extractor
26
+ end
27
+
28
+ # OKAY, NEW RULE: You get ONE Extractor per Migration. You're
29
+ # allowed to have multiple transform/load chains to the
30
+ # extraction, but extractors? ONE.
31
+
32
+ # default extraction method; simply assigns @extractor.extract to
33
+ # @extracted_items. If you override this method, you should
34
+ # populate @extracted_items if you want the default transform
35
+ # and/or load to work correctly.
15
36
  def extract
16
- # run the chain of extractions
37
+ extractor.extract(options)
17
38
  end
18
39
 
19
40
  # Transforms source data into outputs
@@ -28,8 +49,15 @@ module Migratrix
28
49
  end
29
50
 
30
51
  # Perform the migration
52
+ # TODO: turn this into a strategy object. This pattern migrates
53
+ # everything in all one go, while the user may want to do a batch
54
+ # strategy. YAGNI: Rails 3 lets us defer the querying until we get
55
+ # to the transform step, and then it's batched for us under the
56
+ # hood. ...assuming, of course, we change the ActiveRecord
57
+ # extractor's execute_extract method to return source instead of
58
+ # all, but now the
31
59
  def migrate
32
- extract
60
+ @extracted_items = extract
33
61
  transform
34
62
  load
35
63
  end
@@ -7,6 +7,10 @@ module Migratrix
7
7
  ::Migratrix::Migratrix.migrate(name, options)
8
8
  end
9
9
 
10
+ def self.reload_migration(name)
11
+ ::Migratrix::Migratrix.reload_migration(name)
12
+ end
13
+
10
14
  def self.logger
11
15
  ::Migratrix::Migratrix.logger
12
16
  end
@@ -15,6 +19,10 @@ module Migratrix
15
19
  ::Migratrix::Migratrix.logger = new_logger
16
20
  end
17
21
 
22
+ def self.log_to(stream)
23
+ ::Migratrix::Migratrix.log_to(stream)
24
+ end
25
+
18
26
  class Migratrix
19
27
  include ::Migratrix::Loggable
20
28
 
@@ -35,9 +43,7 @@ module Migratrix
35
43
  klass_name = migration_name(name)
36
44
  unless loaded?(klass_name)
37
45
  raise MigrationAlreadyExists.new("Migratrix cannot instantiate class Migratrix::#{klass_name} because it already exists") if ::Migratrix.const_defined?(klass_name)
38
- filename = migrations_path + "#{name}_migration.rb"
39
- raise MigrationFileNotFound.new("Migratrix cannot find migration file #{filename}") unless File.exists?(filename)
40
- load filename
46
+ reload_migration name
41
47
  raise MigrationNotDefined.new("Expected migration file #{filename} to define Migratrix::#{klass_name} but it did not") unless ::Migratrix.const_defined?(klass_name)
42
48
  register_migration(klass_name, "Migratrix::#{klass_name}".constantize)
43
49
  end
@@ -59,7 +65,13 @@ module Migratrix
59
65
  end
60
66
 
61
67
  def valid_options
62
- %w(limit where)
68
+ %w(limit offset order where)
69
+ end
70
+
71
+ def reload_migration(name)
72
+ filename = migrations_path + "#{name}_migration.rb"
73
+ raise MigrationFileNotFound.new("Migratrix cannot find migration file #{filename}") unless File.exists?(filename)
74
+ load filename
63
75
  end
64
76
 
65
77
  # ----------------------------------------------------------------------
@@ -75,6 +87,10 @@ module Migratrix
75
87
  logger
76
88
  end
77
89
 
90
+ def self.log_to(stream)
91
+ self.logger = self.create_logger(stream)
92
+ end
93
+
78
94
  def self.init_logger
79
95
  return Rails.logger if Rails.logger
80
96
  @@logger = create_logger($stdout)
@@ -106,7 +122,7 @@ module Migratrix
106
122
  end
107
123
 
108
124
  def registered_migrations
109
- @registered_migrations ||= {}
125
+ @@registered_migrations ||= {}
110
126
  end
111
127
  # End MigrationRegistry
112
128
  # ----------------------------------------------------------------------
data/lib/migratrix.rb CHANGED
@@ -7,7 +7,7 @@ module Migratrix
7
7
  EXT=Pathname.new(__FILE__).dirname + "patches"
8
8
 
9
9
  def self.default_migrations_path
10
- Rails.root + 'lib/migrations'
10
+ Rails.root + 'db/legacy'
11
11
  end
12
12
 
13
13
  require EXT + 'string_ext'
@@ -18,4 +18,7 @@ module Migratrix
18
18
  require APP + 'migration'
19
19
  require APP + 'migratrix'
20
20
 
21
+ require APP + 'extractors/extractor'
22
+ require APP + 'extractors/active_record'
21
23
  end
24
+
@@ -0,0 +1,43 @@
1
+ require 'spec_helper'
2
+ require 'active_record'
3
+
4
+ class TestModel < ::ActiveRecord::Base
5
+ # self.abstract_class = true
6
+ end
7
+
8
+ class TestActiveRecordExtractor < Migratrix::Extractors::ActiveRecord
9
+ end
10
+
11
+ describe Migratrix::Extractors::ActiveRecord do
12
+ let(:extractor) { TestActiveRecordExtractor.new }
13
+ describe "sanity check cat" do
14
+ it "is sanity checked" do
15
+ Migratrix::Extractors::Extractor.should_not be_nil
16
+ TestActiveRecordExtractor.should_not be_nil
17
+ end
18
+ end
19
+
20
+ describe ".new" do
21
+ it "raises TypeError unless source is Active" do
22
+ lambda { TestActiveRecordExtractor.new :source => Object }.should raise_error(TypeError)
23
+ end
24
+ end
25
+
26
+ describe "#source=" do
27
+ it "raises TypeError unless source is Active" do
28
+ lambda { extractor.source = Object }.should raise_error(TypeError)
29
+ end
30
+ end
31
+
32
+ describe "#obtain_source" do
33
+ it "raises ExtractorSourceUndefined unless source is defined" do
34
+ lambda { extractor.extract }.should raise_error(Migratrix::ExtractorSourceUndefined)
35
+ end
36
+
37
+ it "returns the legacy ActiveRecord class" do
38
+ extractor.source = TestModel
39
+ extractor.obtain_source(TestModel).should == TestModel
40
+ end
41
+ end
42
+ end
43
+
@@ -0,0 +1,63 @@
1
+ require 'spec_helper'
2
+
3
+ class TestExtractor < Migratrix::Extractors::Extractor
4
+ end
5
+
6
+ describe Migratrix::Extractors::Extractor do
7
+ describe "sanity check cat" do
8
+ it "is sanity checked" do
9
+ Migratrix::Extractors::Extractor.should_not be_nil
10
+ TestExtractor.should_not be_nil
11
+ end
12
+ end
13
+
14
+ describe "unimplemented method" do
15
+ let(:base_extractor) { Migratrix::Extractors::Extractor.new }
16
+ [:obtain_source, :handle_where, :handle_limit, :handle_offset, :handle_order, :to_query, :execute_extract].each do |method|
17
+ describe "#{method}" do
18
+ it "raises NotImplementedError" do
19
+ lambda { base_extractor.send(method, nil) }.should raise_error(NotImplementedError)
20
+ end
21
+ end
22
+ end
23
+ end
24
+
25
+ describe "#extract (default strategy)" do
26
+ describe "with no options" do
27
+ let(:extractor) { TestExtractor.new }
28
+ it "should call handle_source and execute_extract only" do
29
+ extractor.should_receive(:obtain_source).with(nil).and_return(13)
30
+ extractor.should_receive(:execute_extract).with(13).and_return(64)
31
+ extractor.extract.should == 64
32
+ end
33
+ end
34
+
35
+ describe "with all options" do
36
+ let(:extractor) { TestExtractor.new "where" => 1, "order" => 2, "limit" => 3, "offset" => 4 }
37
+ it "should call entire handler chain" do
38
+ extractor.should_receive(:obtain_source).with(nil).and_return("A")
39
+ extractor.should_receive(:handle_where).with("A", 1).and_return("B")
40
+ extractor.should_receive(:handle_order).with("B", 2).and_return("C")
41
+ extractor.should_receive(:handle_limit).with("C", 3).and_return("D")
42
+ extractor.should_receive(:handle_offset).with("D", 4).and_return("E")
43
+ extractor.should_receive(:execute_extract).with("E").and_return("BONG")
44
+ extractor.extract.should == "BONG"
45
+ end
46
+ end
47
+
48
+ describe "with overridden options" do
49
+ let(:extractor) { TestExtractor.new }
50
+ let(:overrides) { {"where" => 5, "order" => 6, "limit" => 7, "offset" => 8 } }
51
+ it "should call entire handler chain" do
52
+ extractor.should_receive(:obtain_source).with(nil).and_return("A")
53
+ extractor.should_receive(:handle_where).with("A", 5).and_return("B")
54
+ extractor.should_receive(:handle_order).with("B", 6).and_return("C")
55
+ extractor.should_receive(:handle_limit).with("C", 7).and_return("D")
56
+ extractor.should_receive(:handle_offset).with("D", 8).and_return("E")
57
+ extractor.should_receive(:execute_extract).with("E").and_return("BONG")
58
+ extractor.extract(overrides).should == "BONG"
59
+ end
60
+ end
61
+ end
62
+ end
63
+
@@ -0,0 +1,90 @@
1
+ require 'spec_helper'
2
+
3
+ # class SomeLoggableThing
4
+ # include Migratrix::Loggable
5
+ # end
6
+
7
+ shared_examples_for "loggable" do
8
+ # Your spec must define loggable and loggable_name!
9
+ # let(:loggable) { SomeLoggableThing.new }
10
+ let(:loggable_name) { loggable.class }
11
+ let(:buffer) { StringIO.new }
12
+ let(:logger) { Migratrix::Migratrix.create_logger(buffer) }
13
+
14
+ before do
15
+ Timecop.freeze(Time.local(2011, 6, 28, 3, 14, 15))
16
+ end
17
+
18
+ after do
19
+ Timecop.return
20
+ end
21
+
22
+ describe "your shared loggable specs" do
23
+ it "must define let(:loggable) to somehting that includes Migratrix::Loggable" do
24
+ loggable.class.ancestors.should include(Migratrix::Loggable)
25
+ end
26
+ it "must define let(:loggable_name) to the name of the class that will use the logger methods" do
27
+ loggable_name.should_not be_nil
28
+ end
29
+ end
30
+
31
+ describe "instance" do
32
+ def spec_correct_instance_log_message(method)
33
+ token = method.to_s.upcase[0]
34
+ with_logger(logger) do
35
+ loggable.send(method, "This is a test #{method} message")
36
+ end
37
+ buffer.string.should == "#{token} 2011-06-28 03:14:15: #{loggable_name}: This is a test #{method} message\n"
38
+ end
39
+
40
+ it "#logger can log" do
41
+ with_logger(logger) do
42
+ loggable.logger.info("This is a test")
43
+ end
44
+ buffer.string.should == "I 2011-06-28 03:14:15: This is a test\n"
45
+ end
46
+
47
+ describe "#info" do
48
+ it "logs with class name" do
49
+ with_logger(logger) do
50
+ loggable.info("This is a test")
51
+ end
52
+ buffer.string.should == "I 2011-06-28 03:14:15: #{loggable_name}: This is a test\n"
53
+ end
54
+ end
55
+
56
+ [:info, :debug, :warn, :error, :fatal].each do |log_level|
57
+ describe ".#{log_level}" do
58
+ it "logs #{log_level} message with class name" do
59
+ spec_correct_instance_log_message log_level
60
+ end
61
+ end
62
+ end
63
+ end
64
+
65
+ describe "class" do
66
+ def spec_correct_class_log_message(method)
67
+ token = method.to_s.upcase[0]
68
+ with_logger(logger) do
69
+ loggable.class.send(method, "This is a test #{method} message")
70
+ end
71
+ buffer.string.should == "#{token} 2011-06-28 03:14:15: #{loggable_name}: This is a test #{method} message\n"
72
+ end
73
+
74
+ it ".logger can log" do
75
+ with_logger(logger) do
76
+ loggable.class.logger.info("This is a test")
77
+ end
78
+ buffer.string.should == "I 2011-06-28 03:14:15: This is a test\n"
79
+ end
80
+
81
+ [:info, :debug, :warn, :error, :fatal].each do |log_level|
82
+ describe ".#{log_level}" do
83
+ it "logs #{log_level} message with class name" do
84
+ spec_correct_class_log_message log_level
85
+ end
86
+ end
87
+ end
88
+ end
89
+
90
+ end
@@ -6,6 +6,7 @@ class Migratrix::TestMigration < Migratrix::Migration
6
6
  end
7
7
 
8
8
  describe Migratrix::Migration do
9
+ let(:migration) { Migratrix::TestMigration.new }
9
10
  let(:loggable) { Migratrix::TestMigration.new }
10
11
  it_should_behave_like "loggable"
11
12
 
@@ -22,8 +23,6 @@ describe Migratrix::Migration do
22
23
  end
23
24
 
24
25
  describe "#migrate" do
25
- let(:migration) { Migratrix::TestMigration.new }
26
-
27
26
  it "delegates to extract, transform, and load" do
28
27
  migration.should_receive(:extract).once
29
28
  migration.should_receive(:transform).once
@@ -31,7 +30,30 @@ describe Migratrix::Migration do
31
30
  migration.migrate
32
31
  end
33
32
  end
34
- end
35
33
 
34
+ describe "with mock active_record extractor" do
35
+ let(:mock_extractor) { mock("Extractor", :extract => 43)}
36
+ before do
37
+ Migratrix::Extractors::ActiveRecord.should_receive(:new).with({ :source => Object}).and_return(mock_extractor)
38
+ Migratrix::TestMigration.class_eval "set_extractor :active_record, :source => Object"
39
+ end
40
+
41
+ describe ".set_extractor" do
42
+ it "sets the class accessor for extractor" do
43
+ Migratrix::TestMigration.extractor.should == mock_extractor
44
+ end
45
+
46
+ it "also sets convenience instance method for extractor" do
47
+ Migratrix::TestMigration.new.extractor.should == mock_extractor
48
+ end
49
+ end
50
+
51
+ describe "#extract" do
52
+ it "delegates to extractor" do
53
+ migration.extract.should == 43
54
+ end
55
+ end
56
+ end
57
+ end
36
58
 
37
59
 
@@ -34,8 +34,8 @@ describe Migratrix::Migratrix do
34
34
  end
35
35
 
36
36
  describe ".migrations_path" do
37
- it "uses ./lib/migrations by default" do
38
- migratrix.migrations_path.should == ROOT + "lib/migrations"
37
+ it "uses ./db/legacy by default" do
38
+ migratrix.migrations_path.should == ROOT + "db/legacy"
39
39
  end
40
40
 
41
41
  it "can be overridden" do
@@ -46,7 +46,7 @@ describe Migratrix::Migratrix do
46
46
 
47
47
  describe "#valid_options" do
48
48
  it "returns the valid set of option keys" do
49
- migratrix.valid_options.should == ["limit", "where"]
49
+ migratrix.valid_options.should == ["limit", "offset", "order", "where"]
50
50
  end
51
51
  end
52
52
 
@@ -99,26 +99,43 @@ describe Migratrix::Migratrix do
99
99
  end
100
100
  end
101
101
 
102
- describe ".logger=" do
102
+ describe "with logger as a singleton" do
103
103
  let (:migration) { migratrix.create_migration :marbles }
104
104
  let (:buffer) { StringIO.new }
105
105
 
106
+ def spec_all_loggers_are(this_logger)
107
+ Migratrix.logger.should == this_logger
108
+ Migratrix::Migratrix.logger.should == this_logger
109
+ migratrix.logger.should == this_logger
110
+ migration.logger.should == this_logger
111
+ Migratrix::MarblesMigration.logger.should == this_logger
112
+ end
113
+
106
114
  before do
107
115
  reset_migratrix! migratrix
108
116
  migratrix.migrations_path = SPEC + "fixtures/migrations"
109
117
  end
110
118
 
111
- it "sets logger globally across all Migratrices, the Migratrix module, Migrators and Models" do
112
- logger = Migratrix::Migratrix.create_logger(buffer)
113
- Migratrix::Migratrix.logger = logger
114
- with_logger(logger) do
115
- Migratrix.logger.should == logger
116
- Migratrix::Migratrix.logger.should == logger
117
- migratrix.logger.should == logger
118
- migration.logger.should == logger
119
- Migratrix::MarblesMigration.logger.should == logger
119
+ describe ".logger=" do
120
+ it "sets logger globally across all Migratrices, the Migratrix module, Migrators and Models" do
121
+ logger = Migratrix::Migratrix.create_logger(buffer)
122
+ with_logger(logger) do
123
+ Migratrix::Migratrix.logger = logger
124
+ spec_all_loggers_are logger
125
+ end
126
+ end
127
+ end
128
+ describe ".log_to" do
129
+ it "sets logger globally across all Migratrices, the Migratrix module, Migrators and Models" do
130
+ logger = Migratrix::Migratrix.create_logger(buffer)
131
+ with_logger(logger) do
132
+ Migratrix::Migratrix.should_receive(:create_logger).with(buffer).once.and_return(logger)
133
+ Migratrix.log_to buffer
134
+ spec_all_loggers_are logger
135
+ end
120
136
  end
121
137
  end
122
138
  end
139
+
123
140
  end
124
141
 
@@ -0,0 +1,63 @@
1
+ require 'spec_helper'
2
+
3
+ describe Migratrix do
4
+ describe "sanity check kitty" do
5
+ it "is sanity checked" do
6
+ Migratrix.should_not be_nil
7
+ Migratrix.class.should == Module
8
+ end
9
+ end
10
+
11
+ describe "convenience delegator methods" do
12
+ def spec_delegates_to_migratrix(method, *args)
13
+ if args.size > 0
14
+ Migratrix::Migratrix.should_receive(method).with(*args).once
15
+ else
16
+ Migratrix::Migratrix.should_receive(method).once
17
+ end
18
+ Migratrix.send(method, *args)
19
+ end
20
+
21
+ describe ".migrate!" do
22
+ let (:migratrix) { Migratrix::Migratrix.new }
23
+
24
+ before do
25
+ reset_migratrix! migratrix
26
+ Migratrix::Migratrix.stub!(:new).and_return(migratrix)
27
+ migratrix.migrations_path = SPEC + "fixtures/migrations"
28
+ end
29
+
30
+ it "delegates to Migratrix::Migratrix" do
31
+ Migratrix.migrate! :marbles
32
+ Migratrix::MarblesMigration.should be_migrated
33
+ end
34
+ end
35
+
36
+ describe ".logger" do
37
+ it "delegates to Migratrix::Migratrix" do
38
+ spec_delegates_to_migratrix :logger
39
+ end
40
+ end
41
+
42
+ describe ".logger=" do
43
+ let (:logger) { Logger.new(StringIO.new) }
44
+ it "delegates to Migratrix::Migratrix" do
45
+ spec_delegates_to_migratrix :logger=, logger
46
+ end
47
+ end
48
+
49
+ describe ".log_to" do
50
+ let (:buffer) { StringIO.new }
51
+ it "delegates to Migratrix::Migratrix" do
52
+ spec_delegates_to_migratrix :log_to, buffer
53
+ end
54
+ end
55
+
56
+ describe ".reload_migration" do
57
+ it "delegates to Migratrix::Migratrix" do
58
+ spec_delegates_to_migratrix :reload_migration, :marbles
59
+ end
60
+ end
61
+ end
62
+ end
63
+
@@ -19,8 +19,5 @@ describe Object do
19
19
  big_hash[:struct1].object_id.should == big_hash[:struct2].object_id
20
20
  end
21
21
  end
22
-
23
- # it "returns a completely cloned, fully deep copy of the object" do
24
- # end
25
22
  end
26
23
  end
@@ -28,4 +28,6 @@ describe String do
28
28
  "socks".should_not be_singular
29
29
  end
30
30
  end
31
+
32
+
31
33
  end
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.0.7
4
+ version: 0.0.9
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-15 00:00:00.000000000Z
12
+ date: 2011-10-16 00:00:00.000000000Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: trollop
16
- requirement: &2157814860 !ruby/object:Gem::Requirement
16
+ requirement: &2153508480 !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: *2157814860
24
+ version_requirements: *2153508480
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
@@ -38,6 +38,8 @@ files:
38
38
  - lib/migratrix.rb
39
39
  - lib/migratrix/active_record_migration_helpers.rb
40
40
  - lib/migratrix/exceptions.rb
41
+ - lib/migratrix/extractors/active_record.rb
42
+ - lib/migratrix/extractors/extractor.rb
41
43
  - lib/migratrix/loggable.rb
42
44
  - lib/migratrix/migration.rb
43
45
  - lib/migratrix/migratrix.rb
@@ -45,13 +47,15 @@ files:
45
47
  - lib/patches/object_ext.rb
46
48
  - lib/patches/string_ext.rb
47
49
  - spec/fixtures/migrations/marbles_migration.rb
48
- - spec/lib/loggable_spec.rb
49
- - spec/lib/migration_spec.rb
50
- - spec/lib/migrator_spec.rb
51
- - spec/lib/migratrix_spec.rb
52
- - spec/migratrix_module_spec.rb
53
- - spec/patches/object_ext_spec.rb
54
- - spec/patches/string_ext_spec.rb
50
+ - spec/lib/migratrix/extractors/active_record_spec.rb
51
+ - spec/lib/migratrix/extractors/extractor_spec.rb
52
+ - spec/lib/migratrix/loggable_spec.rb
53
+ - spec/lib/migratrix/migration_spec.rb
54
+ - spec/lib/migratrix/migrator_spec.rb
55
+ - spec/lib/migratrix/migratrix_spec.rb
56
+ - spec/lib/migratrix_module_spec.rb
57
+ - spec/lib/patches/object_ext_spec.rb
58
+ - spec/lib/patches/string_ext_spec.rb
55
59
  - spec/spec_helper.rb
56
60
  - MIT-LICENSE
57
61
  homepage: http://github.com/dbrady/migratrix/
@@ -1,32 +0,0 @@
1
- require 'spec_helper'
2
-
3
- # class SomeLoggableThing
4
- # include Migratrix::Loggable
5
- # end
6
-
7
- shared_examples_for "loggable" do
8
- # let(:loggable) { SomeLoggableThing.new }
9
- let(:buffer) { StringIO.new }
10
- let(:logger) { Migratrix::Migratrix.create_logger(buffer) }
11
-
12
- before do
13
- Timecop.freeze(Time.local(2011, 6, 28, 3, 14, 15))
14
- end
15
-
16
- after do
17
- Timecop.return
18
- end
19
-
20
- it "is loggable" do
21
- loggable.class.ancestors.should include(Migratrix::Loggable)
22
- end
23
-
24
- describe "instance" do
25
- it "can log" do
26
- with_logger(logger) do
27
- loggable.logger.info("This is a test")
28
- end
29
- buffer.string.should == "I 2011-06-28 03:14:15: This is a test\n"
30
- end
31
- end
32
- end
@@ -1,41 +0,0 @@
1
- require 'spec_helper'
2
-
3
- describe Migratrix do
4
- describe "sanity check kitty" do
5
- it "is sanity checked" do
6
- Migratrix.should_not be_nil
7
- Migratrix.class.should == Module
8
- end
9
- end
10
-
11
- describe ".migrate!" do
12
- let (:migratrix) { Migratrix::Migratrix.new }
13
-
14
- before do
15
- reset_migratrix! migratrix
16
- Migratrix::Migratrix.stub!(:new).and_return(migratrix)
17
- migratrix.migrations_path = SPEC + "fixtures/migrations"
18
- end
19
-
20
- it "delegates to Migratrix::Migratrix" do
21
- Migratrix.migrate! :marbles
22
- Migratrix::MarblesMigration.should be_migrated
23
- end
24
- end
25
-
26
- describe ".logger" do
27
- it "delegates to Migratrix::Migratrix" do
28
- Migratrix::Migratrix.should_receive(:logger).and_return nil
29
- Migratrix.logger
30
- end
31
- end
32
-
33
- describe ".logger=" do
34
- let (:logger) { Logger.new(StringIO.new) }
35
- it "delegates to Migratrix::Migratrix" do
36
- Migratrix::Migratrix.should_receive(:logger=).with(logger).and_return nil
37
- Migratrix.logger = logger
38
- end
39
- end
40
- end
41
-