maglevrecord 0.1.1 → 0.1.2
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/.gitignore +7 -0
- data/.travis.yml +0 -1
- data/README.md +22 -7
- data/Rakefile +2 -2
- data/install.sh +0 -1
- data/lib/maglev_record/base.rb +10 -1
- data/lib/maglev_record/enumerable.rb +6 -1
- data/lib/maglev_record/errors.rb +3 -0
- data/lib/maglev_record/maglev_record.rb +1 -1
- data/lib/maglev_record/maglev_support/concern.rb +2 -3
- data/lib/maglev_record/maglev_support/secure_password.rb +2 -3
- data/lib/maglev_record/migration/loader.rb +1 -1
- data/lib/maglev_record/migration/migration.rb +14 -0
- data/lib/maglev_record/migration/migrator.rb +22 -4
- data/lib/maglev_record/migration/operations.rb +58 -0
- data/lib/maglev_record/persistence.rb +12 -5
- data/lib/maglev_record/read_write.rb +38 -7
- data/lib/maglev_record/rooted_enumerable.rb +6 -1
- data/lib/maglev_record/rooted_persistence.rb +4 -0
- data/lib/maglev_record/sensible.rb +2 -2
- data/lib/maglev_record/snapshot/change.rb +54 -15
- data/lib/maglev_record/snapshot/class_change.rb +66 -0
- data/lib/maglev_record/snapshot/class_snapshot.rb +63 -0
- data/lib/maglev_record/snapshot/snapshot.rb +44 -38
- data/lib/maglev_record/snapshot/snapshotable.rb +81 -26
- data/lib/maglev_record/snapshot/superclass_mismatch_change.rb +143 -0
- data/lib/maglev_record/snapshot.rb +3 -2
- data/lib/maglev_record/validations.rb +49 -0
- data/lib/maglev_record.rb +3 -2
- data/lib/tasks/database.rake +61 -17
- data/test/automigration/rake_task_base.rb +57 -0
- data/test/automigration/test_auto.slow.rb +38 -0
- data/test/automigration/test_migration_string.rb +173 -0
- data/test/automigration/test_rake_task_preconditions.slow.rb +41 -0
- data/test/automigration/test_show.slow.rb +21 -0
- data/test/automigration/test_superclass_mismatch.rb +96 -0
- data/test/example_model.rb +7 -5
- data/test/migration/base_lectures.rb +30 -0
- data/test/migration/operation_setup.rb +66 -56
- data/test/migration/projects/automigration/.gitignore +15 -0
- data/test/migration/projects/automigration/Gemfile +44 -0
- data/test/migration/projects/automigration/Gemfile.lock +115 -0
- data/test/migration/projects/automigration/README.rdoc +261 -0
- data/test/migration/projects/automigration/Rakefile +17 -0
- data/test/migration/projects/automigration/app/assets/images/rails.png +0 -0
- data/test/migration/projects/automigration/app/assets/javascripts/application.js +15 -0
- data/test/migration/projects/automigration/app/assets/stylesheets/application.css +13 -0
- data/test/migration/projects/automigration/app/controllers/application_controller.rb +3 -0
- data/test/migration/projects/automigration/app/helpers/application_helper.rb +2 -0
- data/test/migration/projects/automigration/app/mailers/.gitkeep +0 -0
- data/test/migration/projects/automigration/app/models/.gitkeep +0 -0
- data/test/migration/projects/automigration/app/models/project_model.rb +8 -0
- data/test/migration/projects/automigration/app/views/layouts/application.html.erb +14 -0
- data/test/migration/projects/automigration/config/application.rb +62 -0
- data/test/migration/projects/automigration/config/boot.rb +6 -0
- data/test/migration/projects/automigration/config/database.yml +25 -0
- data/test/migration/projects/automigration/config/environment.rb +5 -0
- data/test/migration/projects/automigration/config/environments/development.rb +37 -0
- data/test/migration/projects/automigration/config/environments/production.rb +67 -0
- data/test/migration/projects/automigration/config/initializers/backtrace_silencers.rb +7 -0
- data/test/migration/projects/automigration/config/initializers/inflections.rb +15 -0
- data/test/migration/projects/automigration/config/initializers/mime_types.rb +5 -0
- data/test/migration/projects/automigration/config/initializers/secret_token.rb +7 -0
- data/test/migration/projects/automigration/config/initializers/session_store.rb +8 -0
- data/test/migration/projects/automigration/config/initializers/wrap_parameters.rb +14 -0
- data/test/migration/projects/automigration/config/locales/en.yml +5 -0
- data/test/migration/projects/automigration/config/routes.rb +58 -0
- data/test/migration/projects/automigration/config.ru +4 -0
- data/test/migration/projects/automigration/db/seeds.rb +7 -0
- data/test/migration/projects/automigration/lib/assets/.gitkeep +0 -0
- data/test/migration/projects/automigration/lib/tasks/.gitkeep +0 -0
- data/test/migration/projects/automigration/log/.gitkeep +0 -0
- data/test/migration/projects/automigration/public/404.html +26 -0
- data/test/migration/projects/automigration/public/422.html +26 -0
- data/test/migration/projects/automigration/public/500.html +25 -0
- data/test/migration/projects/automigration/public/favicon.ico +0 -0
- data/test/migration/projects/automigration/public/index.html +241 -0
- data/test/migration/projects/automigration/public/robots.txt +5 -0
- data/test/migration/projects/automigration/script/rails +6 -0
- data/test/migration/projects/project2/Rakefile +2 -2
- data/test/migration/projects/project2/migrations/migration_2013-04-Apr-23_17.31.38.rb +1 -1
- data/test/migration/projects/project2/migrations/migration_2013-04-Apr-23_17.31.52.rb +1 -1
- data/test/migration/projects/project2/migrations/migration_2013-04-Apr-23_17.32.07.rb +1 -1
- data/test/migration/test_change_superclass.rb +111 -0
- data/test/migration/test_loader.rb +6 -0
- data/test/migration/test_project.rb +1 -29
- data/test/migration/test_project1.slow.rb +1 -0
- data/test/migration/test_project2.slow.rb +7 -0
- data/test/migration/test_remove.rb +123 -2
- data/test/migration/todo.txt +1 -1
- data/test/more_asserts.rb +1 -1
- data/test/snapshot/test_attributes.rb +33 -0
- data/test/snapshot/{test_snapshot_attributes.slow.rb → test_attributes.slow.rb} +3 -5
- data/test/snapshot/test_classes.rb +32 -0
- data/test/snapshot/{test_snapshot_classes.slow.rb → test_classes.slow.rb} +1 -1
- data/test/snapshot/test_from_file_system.rb +113 -0
- data/test/snapshot/test_methods.rb +136 -0
- data/test/snapshot/test_methods.slow.rb +67 -0
- data/test/snapshot/test_reset.rb +68 -0
- data/test/snapshot/test_snapshot.rb +41 -75
- data/test/snapshot/test_snapshot.slow.rb +98 -0
- data/test/snapshot/test_snapshotable.rb +0 -1
- data/test/temp_dir_test.rb +40 -0
- data/test/test_active_model_like_interface.rb +4 -1
- data/test/test_model_timestamps.rb +13 -5
- data/test/test_query_interface.rb +10 -0
- data/test/test_sensibles.rb +18 -10
- data/test/test_validation.rb +2 -12
- metadata +64 -5
@@ -0,0 +1,66 @@
|
|
1
|
+
module MaglevRecord
|
2
|
+
|
3
|
+
#
|
4
|
+
# Change of a class between two class snapshots
|
5
|
+
#
|
6
|
+
class ClassChange
|
7
|
+
def initialize(old, new)
|
8
|
+
@old = old
|
9
|
+
@new = new
|
10
|
+
end
|
11
|
+
|
12
|
+
def new_attributes
|
13
|
+
(@new.attributes - @old.attributes).sort
|
14
|
+
end
|
15
|
+
|
16
|
+
def removed_attributes
|
17
|
+
(@old.attributes - @new.attributes).sort
|
18
|
+
end
|
19
|
+
|
20
|
+
def class_name
|
21
|
+
@old.class_name
|
22
|
+
end
|
23
|
+
|
24
|
+
def changed_class
|
25
|
+
@old.snapshot_class
|
26
|
+
end
|
27
|
+
|
28
|
+
def migration_string_list
|
29
|
+
if removed_attributes.size == 1 and new_attributes.size == 1
|
30
|
+
from_attr = removed_attributes.first
|
31
|
+
to_attr = new_attributes.first
|
32
|
+
["#{class_name}.rename_attribute(:#{from_attr}, :#{to_attr})"]
|
33
|
+
else
|
34
|
+
removed_attributes.map{ |attr|
|
35
|
+
"#{class_name}.delete_attribute(:#{attr})"
|
36
|
+
} + new_attributes.map{ |attr|
|
37
|
+
"#new accessor :#{attr} of #{class_name}"
|
38
|
+
}
|
39
|
+
end + new_class_methods.map{ |cm|
|
40
|
+
"#new class method: #{class_name}.#{cm.to_s}"
|
41
|
+
} + new_instance_methods.map{ |im|
|
42
|
+
"#new instance method: #{class_name}.new.#{im.to_s}"
|
43
|
+
} + removed_class_methods.map{ |cm|
|
44
|
+
"#{class_name}.remove_class_method :#{cm}"
|
45
|
+
} + removed_instance_methods.map{ |im|
|
46
|
+
"#{class_name}.remove_instance_method :#{im}"
|
47
|
+
}
|
48
|
+
end
|
49
|
+
|
50
|
+
def new_instance_methods
|
51
|
+
(@new.instance_methods - @old.instance_methods).sort
|
52
|
+
end
|
53
|
+
|
54
|
+
def removed_instance_methods
|
55
|
+
(@old.instance_methods - @new.instance_methods).sort
|
56
|
+
end
|
57
|
+
|
58
|
+
def new_class_methods
|
59
|
+
(@new.class_methods - @old.class_methods).sort
|
60
|
+
end
|
61
|
+
|
62
|
+
def removed_class_methods
|
63
|
+
(@old.class_methods - @new.class_methods).sort
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
@@ -0,0 +1,63 @@
|
|
1
|
+
require "maglev_record/snapshot/class_change.rb"
|
2
|
+
|
3
|
+
module MaglevRecord
|
4
|
+
|
5
|
+
class ClassSnapshot
|
6
|
+
def initialize(cls)
|
7
|
+
cls.redo_include_and_extend
|
8
|
+
@snapshot_class = cls
|
9
|
+
@attributes = cls.snapshot_attributes if cls.respond_to? :snapshot_attributes
|
10
|
+
@files = cls.file_paths
|
11
|
+
@exists = cls.has_definitions?
|
12
|
+
@class_methods = cls.snapshot_class_methods
|
13
|
+
@instance_methods = cls.snapshot_instance_methods
|
14
|
+
end
|
15
|
+
|
16
|
+
def exists?
|
17
|
+
@exists
|
18
|
+
end
|
19
|
+
|
20
|
+
def ==(other)
|
21
|
+
self.snapshot_class == other.snapshot_class \
|
22
|
+
and self.exists? == other.exists?
|
23
|
+
end
|
24
|
+
|
25
|
+
alias :eql? :==
|
26
|
+
|
27
|
+
def <=>(other)
|
28
|
+
class_name <=> other.class_name
|
29
|
+
end
|
30
|
+
|
31
|
+
def snapshot_class
|
32
|
+
@snapshot_class
|
33
|
+
end
|
34
|
+
|
35
|
+
def changes_since(older)
|
36
|
+
return nil unless changed_since?(older)
|
37
|
+
ClassChange.new(older, self)
|
38
|
+
end
|
39
|
+
|
40
|
+
def changed_since?(older)
|
41
|
+
attributes != older.attributes or
|
42
|
+
instance_methods != older.instance_methods or
|
43
|
+
class_methods != older.class_methods
|
44
|
+
end
|
45
|
+
|
46
|
+
def attributes
|
47
|
+
Array.new(@attributes || []).sort.uniq
|
48
|
+
end
|
49
|
+
|
50
|
+
def class_name
|
51
|
+
@snapshot_class.name
|
52
|
+
end
|
53
|
+
|
54
|
+
def class_methods
|
55
|
+
Array.new(@class_methods)
|
56
|
+
end
|
57
|
+
|
58
|
+
def instance_methods
|
59
|
+
@instance_methods
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
@@ -1,67 +1,73 @@
|
|
1
|
+
require "maglev_record/snapshot/snapshotable"
|
2
|
+
require "maglev_record/snapshot/class_snapshot"
|
1
3
|
require "maglev_record/snapshot/change"
|
4
|
+
require "maglev_record/snapshot/superclass_mismatch_change"
|
2
5
|
|
3
6
|
module MaglevRecord
|
4
|
-
class ClassSnapshot
|
5
|
-
def initialize(cls)
|
6
|
-
@snapshot_class = cls
|
7
|
-
@attr_readers = cls.attr_readers
|
8
|
-
@files = cls.file_paths
|
9
|
-
@exists = cls.has_definitions?
|
10
|
-
end
|
11
7
|
|
12
|
-
|
13
|
-
@exists
|
14
|
-
end
|
8
|
+
class Snapshot
|
15
9
|
|
16
|
-
def
|
17
|
-
|
18
|
-
and self.exists? == other.exists?
|
10
|
+
def for_class(cls)
|
11
|
+
ClassSnapshot.new(cls)
|
19
12
|
end
|
20
13
|
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
14
|
+
def initialize(classes = Snapshotable.snapshotable_classes)
|
15
|
+
@class_snapshots = classes.map{ |cls|
|
16
|
+
for_class cls
|
17
|
+
}
|
25
18
|
end
|
26
19
|
|
27
20
|
def changes_since(older)
|
28
|
-
|
29
|
-
ClassChange.new(older, self)
|
21
|
+
Change.new(older, self)
|
30
22
|
end
|
31
23
|
|
32
|
-
def
|
33
|
-
|
24
|
+
def class_snapshots
|
25
|
+
Array.new(@class_snapshots)
|
34
26
|
end
|
35
27
|
|
36
|
-
def
|
37
|
-
|
28
|
+
def snapshot_classes
|
29
|
+
class_snapshots.map(&:snapshot_class)
|
38
30
|
end
|
39
31
|
|
40
|
-
def
|
41
|
-
|
32
|
+
def class_names
|
33
|
+
class_snapshots.map(&:class_name).sort
|
42
34
|
end
|
43
|
-
end
|
44
35
|
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
36
|
+
def [](cls)
|
37
|
+
class_snapshots.each{ |snap|
|
38
|
+
return snap if snap.snapshot_class == cls
|
39
|
+
}
|
49
40
|
end
|
50
41
|
|
51
|
-
def
|
52
|
-
|
53
|
-
|
42
|
+
def self.with_files(file_paths = [], classes = Snapshotable.snapshotable_classes)
|
43
|
+
class_files = classes.map(&:file_paths).flatten.uniq
|
44
|
+
restore_state = classes.map(&:reset)
|
45
|
+
begin
|
46
|
+
(file_paths + class_files).uniq.each{ |file_path|
|
47
|
+
begin
|
48
|
+
Kernel.load file_path if File.exist? file_path
|
49
|
+
rescue TypeError => e
|
50
|
+
class_name = e.message[/superclass mismatch for( class)? (\S*)$/, 2]
|
51
|
+
raise e if class_name.nil? # TODO: test
|
52
|
+
raise e unless Object.const_defined? class_name # TODO: test
|
53
|
+
cls = Object.const_get class_name # TODO: rescue exceptions
|
54
|
+
return SuperclassMismatchChange.new(cls, file_path)
|
55
|
+
end
|
54
56
|
}
|
57
|
+
else
|
58
|
+
return new
|
59
|
+
ensure
|
60
|
+
restore_state.map(&:call)
|
61
|
+
end
|
55
62
|
end
|
56
63
|
|
57
|
-
def
|
58
|
-
|
64
|
+
def new_with_files(file_paths = [])
|
65
|
+
self.class.with_files(file_paths)
|
59
66
|
end
|
60
67
|
|
61
|
-
def
|
62
|
-
|
68
|
+
def changes_in_files(file_paths = [])
|
69
|
+
return new_with_files(file_paths).changes_since(self)
|
63
70
|
end
|
64
|
-
|
65
71
|
end
|
66
72
|
end
|
67
73
|
|
@@ -1,22 +1,9 @@
|
|
1
|
-
|
2
|
-
class ::Class
|
3
|
-
def exist!
|
4
|
-
@exists = true
|
5
|
-
end
|
6
|
-
|
7
|
-
def start_existence_test!
|
8
|
-
@exists = false
|
9
|
-
end
|
10
|
-
|
11
|
-
def exists?
|
12
|
-
return true if @exists
|
13
|
-
false
|
14
|
-
end
|
15
|
-
end
|
16
|
-
end
|
1
|
+
require "maglev_record/maglev_support/concern"
|
17
2
|
|
18
3
|
module MaglevRecord
|
19
4
|
module Snapshotable
|
5
|
+
extend MaglevSupport::Concern
|
6
|
+
|
20
7
|
def self.snapshotable_classes
|
21
8
|
classes = []
|
22
9
|
Object.constants.each { |constant|
|
@@ -32,10 +19,6 @@ module MaglevRecord
|
|
32
19
|
|
33
20
|
module ClassMethods
|
34
21
|
|
35
|
-
def self.extended(base)
|
36
|
-
base.exist! if base.respond_to? :exist!
|
37
|
-
end
|
38
|
-
|
39
22
|
def file_paths
|
40
23
|
(self.instance_methods(false).map { |m|
|
41
24
|
self.instance_method(m).source_location.first
|
@@ -45,13 +28,85 @@ module MaglevRecord
|
|
45
28
|
end
|
46
29
|
|
47
30
|
def has_definitions?
|
48
|
-
#
|
49
|
-
|
50
|
-
file_paths
|
51
|
-
|
52
|
-
|
31
|
+
# In nested transaction this error occurs:
|
32
|
+
# Error 2101, objId i52279553 does not exist, during transaction boundary
|
33
|
+
fp = file_paths
|
34
|
+
without_methods do
|
35
|
+
fp.each{ |file_path|
|
36
|
+
Kernel.load file_path if File.file? file_path
|
37
|
+
}
|
38
|
+
return !file_paths.empty?
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
def without_methods
|
43
|
+
return unless block_given?
|
44
|
+
memento = reset
|
45
|
+
begin
|
46
|
+
yield
|
47
|
+
ensure
|
48
|
+
memento.call
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
def class_methods_not_to_snapshot
|
53
|
+
@class_methods_not_to_reset ||= []
|
54
|
+
# hackady hack!
|
55
|
+
# welcome to the big ball of mud and the
|
56
|
+
# "I do not know what goes on after hours trying"-architecture
|
57
|
+
@class_methods_not_to_reset << "_validators"
|
58
|
+
@class_methods_not_to_reset << "_validators="
|
59
|
+
@class_methods_not_to_reset << "_validators?"
|
60
|
+
#@class_methods_not_to_reset << "method_missing"
|
61
|
+
@class_methods_not_to_reset << methods(false).select{|m| m.include? "callback" }
|
62
|
+
@class_methods_not_to_reset.flatten!
|
63
|
+
@class_methods_not_to_reset.map!(&:to_s)
|
64
|
+
@class_methods_not_to_reset.uniq!
|
65
|
+
@class_methods_not_to_reset
|
66
|
+
end
|
67
|
+
|
68
|
+
def snapshot_class_methods
|
69
|
+
methods(false) - class_methods_not_to_snapshot
|
70
|
+
end
|
71
|
+
|
72
|
+
def snapshot_instance_methods
|
73
|
+
instance_methods(false).reject{|m| m.include? 'callback' or m.include? 'valid'}
|
74
|
+
end
|
75
|
+
|
76
|
+
def class_methods_to_reset
|
77
|
+
snapshot_class_methods
|
78
|
+
end
|
79
|
+
|
80
|
+
def instance_methods_to_reset
|
81
|
+
snapshot_instance_methods
|
82
|
+
end
|
83
|
+
|
84
|
+
#
|
85
|
+
# resets the class to no methods
|
86
|
+
# returns a memento proc that can be called to restore the old state
|
87
|
+
#
|
88
|
+
def reset
|
89
|
+
_instance_methods = instance_methods_to_reset.map { |m|
|
90
|
+
meth = instance_method m
|
91
|
+
remove_method m
|
92
|
+
meth
|
93
|
+
}
|
94
|
+
_class_methods = class_methods_to_reset.map { |m|
|
95
|
+
meth = method m
|
96
|
+
singleton_class.remove_method m
|
97
|
+
meth
|
98
|
+
}
|
99
|
+
return Proc.new {
|
100
|
+
instance_methods_to_reset.each { |m| remove_method m }
|
101
|
+
class_methods_to_reset.each{ |m| singleton_class.remove_method m }
|
102
|
+
_instance_methods.each{|m|
|
103
|
+
define_method m.name, m
|
104
|
+
}
|
105
|
+
_class_methods.each{|m|
|
106
|
+
singleton_class.define_method m.name, m
|
107
|
+
}
|
108
|
+
self
|
53
109
|
}
|
54
|
-
exists?
|
55
110
|
end
|
56
111
|
end
|
57
112
|
end
|
@@ -0,0 +1,143 @@
|
|
1
|
+
|
2
|
+
class ClassWithMismatchNotFound
|
3
|
+
def name
|
4
|
+
"<fill in the name of the new superclass here>"
|
5
|
+
end
|
6
|
+
end
|
7
|
+
|
8
|
+
module MaglevRecord
|
9
|
+
class SuperclassMismatchChangeBase
|
10
|
+
"new_class_names new_classes
|
11
|
+
removed_class_names removed_classes changed_classes changed_class_names
|
12
|
+
".split.each do |name|
|
13
|
+
define_method(name){ [] }
|
14
|
+
end
|
15
|
+
|
16
|
+
def nothing_changed?
|
17
|
+
false
|
18
|
+
end
|
19
|
+
|
20
|
+
def superclass_mismatch_classes
|
21
|
+
[self]
|
22
|
+
end
|
23
|
+
|
24
|
+
def superclass_mismatch_class_names
|
25
|
+
superclass_mismatch_classes.map(&:class_name)
|
26
|
+
end
|
27
|
+
|
28
|
+
def changes_since(snapshot)
|
29
|
+
self
|
30
|
+
end
|
31
|
+
|
32
|
+
end
|
33
|
+
|
34
|
+
class SuperclassMismatchChange < SuperclassMismatchChangeBase
|
35
|
+
def initialize(cls, file_path)
|
36
|
+
@cls = cls
|
37
|
+
@file_path = file_path
|
38
|
+
end
|
39
|
+
|
40
|
+
def file_path
|
41
|
+
@file_path
|
42
|
+
end
|
43
|
+
|
44
|
+
def migration_string(identation = 0)
|
45
|
+
" " * identation + [
|
46
|
+
"# TypeError: superclass mismatch for #{class_name}",
|
47
|
+
"# in #{file_path}",
|
48
|
+
"#{class_name}.change_superclass_to #{new_superclass.name}"
|
49
|
+
].join("\n" + " " * identation)
|
50
|
+
end
|
51
|
+
|
52
|
+
def determine_new_superclass
|
53
|
+
# 1. replace the actual class by a new one
|
54
|
+
# 2. inherit
|
55
|
+
# 3. get the superclass
|
56
|
+
# 4. restore the class
|
57
|
+
constant_name = mismatching_class.name
|
58
|
+
class_to_replace = nil # change the scope
|
59
|
+
Maglev.persistent do
|
60
|
+
begin
|
61
|
+
class_to_replace = Object.remove_const constant_name
|
62
|
+
rescue NameError
|
63
|
+
# negative path 1 TODO: test
|
64
|
+
return ClassWithMismatchNotFound.new
|
65
|
+
else
|
66
|
+
if class_to_replace != mismatching_class
|
67
|
+
# negative path 2 TODO: test
|
68
|
+
Object.const_set constant_name, class_to_replace
|
69
|
+
return ClassWithMismatchNotFound.new
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
73
|
+
begin
|
74
|
+
Kernel.load file_path
|
75
|
+
cls = Object.const_get constant_name
|
76
|
+
return cls.superclass
|
77
|
+
ensure
|
78
|
+
Object.const_set constant_name, class_to_replace
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
def new_superclass
|
83
|
+
@new_super_class = determine_new_superclass if @new_super_class.nil?
|
84
|
+
@new_super_class
|
85
|
+
end
|
86
|
+
|
87
|
+
# methods for the single change
|
88
|
+
|
89
|
+
def class_name
|
90
|
+
mismatching_class.name
|
91
|
+
end
|
92
|
+
|
93
|
+
def mismatching_class
|
94
|
+
@cls
|
95
|
+
end
|
96
|
+
|
97
|
+
# reverse
|
98
|
+
|
99
|
+
def reversed
|
100
|
+
ReversedSuperclassMismatchChange.new(self)
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
class ReversedSuperclassMismatchChange < SuperclassMismatchChangeBase
|
105
|
+
# TODO: test
|
106
|
+
def initialize(superclass_mismatch_change)
|
107
|
+
@superclass_mismatch_change = superclass_mismatch_change
|
108
|
+
end
|
109
|
+
|
110
|
+
def superclass_mismatch_change
|
111
|
+
@superclass_mismatch_change
|
112
|
+
end
|
113
|
+
|
114
|
+
def reversed
|
115
|
+
superclass_mismatch_change
|
116
|
+
end
|
117
|
+
|
118
|
+
def mismatching_class
|
119
|
+
superclass_mismatch_change.mismatching_class
|
120
|
+
end
|
121
|
+
|
122
|
+
def class_name
|
123
|
+
superclass_mismatch_change.mismatching_class
|
124
|
+
end
|
125
|
+
|
126
|
+
def file_path
|
127
|
+
superclass_mismatch_change.file_path
|
128
|
+
end
|
129
|
+
|
130
|
+
def old_superclass
|
131
|
+
mismatching_class.superclass
|
132
|
+
end
|
133
|
+
|
134
|
+
def migration_string(identation = 0)
|
135
|
+
" " * identation + [
|
136
|
+
"# TypeError: superclass mismatch for #{class_name}",
|
137
|
+
"# in #{file_path}",
|
138
|
+
"#{class_name}.change_superclass_to #{old_superclass.name}"
|
139
|
+
].join("\n" + " " * identation)
|
140
|
+
end
|
141
|
+
|
142
|
+
end
|
143
|
+
end
|
@@ -1,5 +1,6 @@
|
|
1
1
|
require "maglev_record/snapshot/snapshotable"
|
2
2
|
require "maglev_record/snapshot/change"
|
3
|
+
require "maglev_record/snapshot/class_change"
|
4
|
+
require "maglev_record/snapshot/class_snapshot"
|
3
5
|
require "maglev_record/snapshot/snapshot"
|
4
|
-
|
5
|
-
|
6
|
+
require "maglev_record/snapshot/superclass_mismatch_change"
|
@@ -0,0 +1,49 @@
|
|
1
|
+
module MaglevRecord
|
2
|
+
module Validations
|
3
|
+
def self.included(base)
|
4
|
+
base.extend(self::ClassMethods) if self.constants.include? 'ClassMethods'
|
5
|
+
end
|
6
|
+
|
7
|
+
def method_missing(symbol, *args)
|
8
|
+
self.class.create_validations
|
9
|
+
if self.respond_to?(symbol)
|
10
|
+
send(symbol, *args)
|
11
|
+
else
|
12
|
+
super
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
module ClassMethods
|
17
|
+
|
18
|
+
def method_missing(symbol, *args)
|
19
|
+
super unless symbol.to_s.include? "valid"
|
20
|
+
|
21
|
+
@validates_options ||= Hash.new
|
22
|
+
@validates_options[symbol] ||= []
|
23
|
+
@validates_options[symbol] << args
|
24
|
+
end
|
25
|
+
|
26
|
+
def create_validations
|
27
|
+
return if not self.maglev_persistable? or @validates_options.nil?
|
28
|
+
|
29
|
+
self.include MaglevSupport.constantize("ActiveModel::Validations")
|
30
|
+
@validates_options.each do |symbol, args_list|
|
31
|
+
args_list.each do |args|
|
32
|
+
self.send(symbol, *args)
|
33
|
+
end
|
34
|
+
end
|
35
|
+
@validates_options = nil
|
36
|
+
end
|
37
|
+
|
38
|
+
def reset
|
39
|
+
_validates_options = @validates_options
|
40
|
+
@validates_options = nil
|
41
|
+
reset_proc = super
|
42
|
+
return Proc.new {
|
43
|
+
reset_proc.call
|
44
|
+
@validates_options = _validates_options
|
45
|
+
}
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
data/lib/maglev_record.rb
CHANGED
@@ -17,7 +17,6 @@ if defined? MaglevRecord::VERSION
|
|
17
17
|
end
|
18
18
|
MaglevRecord::Migration.redo_include_and_extend
|
19
19
|
else
|
20
|
-
puts "IT IS NOT DEFINED"
|
21
20
|
# require "maglev_record/tools"
|
22
21
|
require "maglev_record/maglev_record"
|
23
22
|
|
@@ -29,6 +28,7 @@ else
|
|
29
28
|
require "maglev_record/errors"
|
30
29
|
require "maglev_record/integration"
|
31
30
|
require "maglev_record/persistence"
|
31
|
+
require "maglev_record/validations"
|
32
32
|
require "maglev_record/read_write"
|
33
33
|
require "maglev_record/rooted_persistence"
|
34
34
|
require "maglev_record/migration"
|
@@ -54,7 +54,8 @@ else
|
|
54
54
|
mod.maglev_record_persistable
|
55
55
|
end
|
56
56
|
end
|
57
|
+
ActiveModel::Errors.maglev_nil_references
|
58
|
+
Maglev.commit_transaction
|
57
59
|
end
|
58
60
|
|
59
61
|
ActiveModel::Errors.maglev_nil_references
|
60
|
-
Maglev.commit_transaction
|
data/lib/tasks/database.rake
CHANGED
@@ -1,9 +1,9 @@
|
|
1
|
-
require "maglev_record
|
2
|
-
require "time"
|
1
|
+
require "maglev_record"
|
3
2
|
require "logger"
|
4
3
|
require "fileutils"
|
5
4
|
|
6
|
-
MIGRATION_FOLDER = "migrations"
|
5
|
+
MIGRATION_FOLDER = "./migrations"
|
6
|
+
MODEL_FILES = Array.new(Dir.glob("./app/models/*.rb"))
|
7
7
|
|
8
8
|
desc "transform the maglev database"
|
9
9
|
namespace :migrate do
|
@@ -15,24 +15,68 @@ namespace :migrate do
|
|
15
15
|
end
|
16
16
|
|
17
17
|
desc "migrate all the migrations in the migration folder"
|
18
|
-
task :up => :setup do
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
18
|
+
task :up => [:setup] do
|
19
|
+
migrator = MaglevRecord::Migrator.for_directory(MIGRATION_FOLDER)
|
20
|
+
migrator.up(Logger.new(STDOUT))
|
21
|
+
Rake::Task['migrate:load_all_models'].execute
|
22
|
+
Maglev::PERSISTENT_ROOT[:last_snapshot] = MaglevRecord::Snapshot.new
|
23
|
+
Maglev.commit_transaction
|
24
|
+
end
|
25
|
+
|
26
|
+
desc "show which migrations would be done by up"
|
27
|
+
task :up? => :setup do
|
28
|
+
# TODO: test
|
29
|
+
migrator = MaglevRecord::Migrator.for_directory(MIGRATION_FOLDER)
|
30
|
+
migrator.up?(Logger.new(STDOUT))
|
24
31
|
end
|
25
32
|
|
26
33
|
desc "create a new migration in the migration folder"
|
27
34
|
task :new => :setup do
|
28
|
-
|
29
|
-
|
30
|
-
content = MaglevRecord::Migration.file_content(now, 'fill in description here')
|
31
|
-
filepath = File.join(MIGRATION_FOLDER, filename)
|
32
|
-
File.open(filepath, 'w') { |file|
|
33
|
-
file.write(content)
|
34
|
-
}
|
35
|
-
puts "created migration #{filepath}"
|
35
|
+
MaglevRecord::Migration.write_to_file(MIGRATION_FOLDER,
|
36
|
+
'fill in description here')
|
36
37
|
end
|
38
|
+
|
39
|
+
desc "create a migration file for the changes shown by migrate:auto?"
|
40
|
+
task :auto => :setup do
|
41
|
+
last_snapshot = Maglev::PERSISTENT_ROOT[:last_snapshot]
|
42
|
+
if last_snapshot.nil?
|
43
|
+
puts "rake migrate:up has to be done first"
|
44
|
+
break
|
45
|
+
end
|
46
|
+
changes = last_snapshot.changes_in_files(MODEL_FILES)
|
47
|
+
if changes.nothing_changed?
|
48
|
+
puts "# no changes"
|
49
|
+
break
|
50
|
+
end
|
51
|
+
upcode = changes.migration_string(4)
|
52
|
+
downcode = changes.reversed.migration_string(4)
|
53
|
+
file_name = MaglevRecord::Migration.write_to_file(MIGRATION_FOLDER,
|
54
|
+
'fill in description here',
|
55
|
+
upcode,
|
56
|
+
downcode)
|
57
|
+
puts file_name
|
58
|
+
end
|
59
|
+
|
60
|
+
desc "show the changes since the last migrate:auto or migrate:up"
|
61
|
+
task :auto? do
|
62
|
+
last_snapshot = Maglev::PERSISTENT_ROOT[:last_snapshot]
|
63
|
+
if last_snapshot.nil?
|
64
|
+
puts "rake migrate:up has to be done first"
|
65
|
+
break
|
66
|
+
end
|
67
|
+
changes = last_snapshot.changes_in_files(MODEL_FILES)
|
68
|
+
if changes.nothing_changed?
|
69
|
+
puts "# no changes"
|
70
|
+
else
|
71
|
+
puts changes.migration_string
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
task :load_all_models do
|
76
|
+
MODEL_FILES.each do |model_file_path|
|
77
|
+
load model_file_path
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
37
81
|
end
|
38
82
|
|