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