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.
- data/lib/migratrix/exceptions.rb +1 -0
- data/lib/migratrix/extractors/active_record.rb +54 -0
- data/lib/migratrix/extractors/extractor.rb +88 -0
- data/lib/migratrix/loggable.rb +33 -0
- data/lib/migratrix/migration.rb +32 -4
- data/lib/migratrix/migratrix.rb +21 -5
- data/lib/migratrix.rb +4 -1
- data/spec/lib/migratrix/extractors/active_record_spec.rb +43 -0
- data/spec/lib/migratrix/extractors/extractor_spec.rb +63 -0
- data/spec/lib/migratrix/loggable_spec.rb +90 -0
- data/spec/lib/{migration_spec.rb → migratrix/migration_spec.rb} +25 -3
- data/spec/lib/{migrator_spec.rb → migratrix/migrator_spec.rb} +0 -0
- data/spec/lib/{migratrix_spec.rb → migratrix/migratrix_spec.rb} +30 -13
- data/spec/lib/migratrix_module_spec.rb +63 -0
- data/spec/{patches/string_ext_spec.rb → lib/patches/object_ext_spec.rb} +0 -3
- data/spec/{patches/object_ext_spec.rb → lib/patches/string_ext_spec.rb} +2 -0
- metadata +15 -11
- data/spec/lib/loggable_spec.rb +0 -32
- data/spec/migratrix_module_spec.rb +0 -41
data/lib/migratrix/exceptions.rb
CHANGED
@@ -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
|
data/lib/migratrix/loggable.rb
CHANGED
@@ -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
|
data/lib/migratrix/migration.rb
CHANGED
@@ -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
|
-
#
|
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
|
-
|
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
|
data/lib/migratrix/migratrix.rb
CHANGED
@@ -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
|
-
|
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
|
-
|
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 + '
|
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
|
|
File without changes
|
@@ -34,8 +34,8 @@ describe Migratrix::Migratrix do
|
|
34
34
|
end
|
35
35
|
|
36
36
|
describe ".migrations_path" do
|
37
|
-
it "uses ./
|
38
|
-
migratrix.migrations_path.should == ROOT + "
|
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 "
|
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
|
-
|
112
|
-
logger
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
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
|
+
|
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.
|
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-
|
12
|
+
date: 2011-10-16 00:00:00.000000000Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: trollop
|
16
|
-
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: *
|
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/
|
49
|
-
- spec/lib/
|
50
|
-
- spec/lib/
|
51
|
-
- spec/lib/
|
52
|
-
- spec/
|
53
|
-
- spec/
|
54
|
-
- spec/
|
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/
|
data/spec/lib/loggable_spec.rb
DELETED
@@ -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
|
-
|