migratrix 0.0.7 → 0.0.9

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