maglevrecord 0.0.3 → 0.1.1
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 +35 -0
- data/.travis.yml +19 -0
- data/Gemfile +13 -0
- data/README.md +89 -0
- data/Rakefile +22 -0
- data/install.sh +238 -0
- data/lib/maglev_record/base.rb +10 -62
- data/lib/maglev_record/enumerable.rb +13 -24
- data/lib/maglev_record/errors.rb +10 -0
- data/lib/maglev_record/integration.rb +7 -0
- data/lib/maglev_record/maglev_record.rb +13 -0
- data/lib/maglev_record/maglev_support/active_support_patch.rb +31 -0
- data/lib/maglev_record/maglev_support/concern.rb +19 -0
- data/lib/maglev_record/maglev_support/maglev_support.rb +15 -0
- data/lib/maglev_record/maglev_support/secure_password.rb +39 -0
- data/lib/maglev_record/migration/loader.rb +38 -0
- data/lib/maglev_record/migration/migration.rb +142 -0
- data/lib/maglev_record/migration/migrator.rb +40 -0
- data/lib/maglev_record/migration/operations.rb +92 -0
- data/lib/maglev_record/migration.rb +5 -0
- data/lib/maglev_record/persistence.rb +23 -48
- data/lib/maglev_record/raketasks.rb +9 -0
- data/lib/maglev_record/read_write.rb +20 -10
- data/lib/maglev_record/rooted_base.rb +8 -0
- data/lib/maglev_record/rooted_enumerable.rb +25 -0
- data/lib/maglev_record/rooted_persistence.rb +36 -0
- data/lib/maglev_record/sensible.rb +28 -0
- data/lib/maglev_record/snapshot/change.rb +53 -0
- data/lib/maglev_record/snapshot/snapshot.rb +67 -0
- data/lib/maglev_record/snapshot/snapshotable.rb +58 -0
- data/lib/maglev_record/snapshot.rb +5 -0
- data/lib/maglev_record/tools/object_reference.rb +189 -0
- data/lib/maglev_record/tools/submodule_finder.rb +42 -0
- data/lib/maglev_record/tools.rb +2 -0
- data/lib/maglev_record.rb +60 -4
- data/lib/tasks/database.rake +38 -0
- data/maglevrecord.gemspec +17 -0
- data/rails/init.rb +1 -0
- data/test/_test_object_reference.rb +70 -0
- data/test/example_model.rb +26 -13
- data/test/migration/dummy_migrations/migration_1.rb +8 -0
- data/test/migration/dummy_migrations/migration_2.rb +8 -0
- data/test/migration/dummy_migrations/migration_3.rb +8 -0
- data/test/migration/migrations/migration_1.rb +12 -0
- data/test/migration/migrations/migration_2.rb +12 -0
- data/test/migration/migrations/migration_3.rb +12 -0
- data/test/migration/operation_setup.rb +91 -0
- data/test/migration/projects/project1/.gitignore +15 -0
- data/test/migration/projects/project1/Gemfile +43 -0
- data/test/migration/projects/project1/Gemfile.lock +114 -0
- data/test/migration/projects/project1/README.rdoc +261 -0
- data/test/migration/projects/project1/Rakefile +17 -0
- data/test/migration/projects/project1/app/assets/images/rails.png +0 -0
- data/test/migration/projects/project1/app/assets/javascripts/application.js +15 -0
- data/test/migration/projects/project1/app/assets/stylesheets/application.css +13 -0
- data/test/migration/projects/project1/app/controllers/application_controller.rb +3 -0
- data/test/migration/projects/project1/app/helpers/application_helper.rb +2 -0
- data/test/migration/projects/project1/app/mailers/.gitkeep +0 -0
- data/test/migration/projects/project1/app/models/.gitkeep +0 -0
- data/test/migration/projects/project1/app/views/layouts/application.html.erb +14 -0
- data/test/migration/projects/project1/config/application.rb +62 -0
- data/test/migration/projects/project1/config/boot.rb +6 -0
- data/test/migration/projects/project1/config/database.yml +25 -0
- data/test/migration/projects/project1/config/environment.rb +5 -0
- data/test/migration/projects/project1/config/environments/development.rb +37 -0
- data/test/migration/projects/project1/config/environments/production.rb +67 -0
- data/test/migration/projects/project1/config/initializers/backtrace_silencers.rb +7 -0
- data/test/migration/projects/project1/config/initializers/inflections.rb +15 -0
- data/test/migration/projects/project1/config/initializers/mime_types.rb +5 -0
- data/test/migration/projects/project1/config/initializers/secret_token.rb +7 -0
- data/test/migration/projects/project1/config/initializers/session_store.rb +8 -0
- data/test/migration/projects/project1/config/initializers/wrap_parameters.rb +14 -0
- data/test/migration/projects/project1/config/locales/en.yml +5 -0
- data/test/migration/projects/project1/config/routes.rb +58 -0
- data/test/migration/projects/project1/config.ru +4 -0
- data/test/migration/projects/project1/db/seeds.rb +7 -0
- data/test/migration/projects/project1/lib/assets/.gitkeep +0 -0
- data/test/migration/projects/project1/lib/tasks/.gitkeep +0 -0
- data/test/migration/projects/project1/log/.gitkeep +0 -0
- data/test/migration/projects/project1/public/404.html +26 -0
- data/test/migration/projects/project1/public/422.html +26 -0
- data/test/migration/projects/project1/public/500.html +25 -0
- data/test/migration/projects/project1/public/favicon.ico +0 -0
- data/test/migration/projects/project1/public/index.html +241 -0
- data/test/migration/projects/project1/public/robots.txt +5 -0
- data/test/migration/projects/project1/script/rails +6 -0
- data/test/migration/projects/project2/.gitignore +15 -0
- data/test/migration/projects/project2/Gemfile +44 -0
- data/test/migration/projects/project2/Gemfile.lock +115 -0
- data/test/migration/projects/project2/README.rdoc +261 -0
- data/test/migration/projects/project2/Rakefile +17 -0
- data/test/migration/projects/project2/app/assets/images/rails.png +0 -0
- data/test/migration/projects/project2/app/assets/javascripts/application.js +15 -0
- data/test/migration/projects/project2/app/assets/stylesheets/application.css +13 -0
- data/test/migration/projects/project2/app/controllers/application_controller.rb +3 -0
- data/test/migration/projects/project2/app/helpers/application_helper.rb +2 -0
- data/test/migration/projects/project2/app/mailers/.gitkeep +0 -0
- data/test/migration/projects/project2/app/models/.gitkeep +0 -0
- data/test/migration/projects/project2/app/models/project_model.rb +8 -0
- data/test/migration/projects/project2/app/views/layouts/application.html.erb +14 -0
- data/test/migration/projects/project2/config/application.rb +62 -0
- data/test/migration/projects/project2/config/boot.rb +6 -0
- data/test/migration/projects/project2/config/database.yml +25 -0
- data/test/migration/projects/project2/config/environment.rb +5 -0
- data/test/migration/projects/project2/config/environments/development.rb +37 -0
- data/test/migration/projects/project2/config/environments/production.rb +67 -0
- data/test/migration/projects/project2/config/initializers/backtrace_silencers.rb +7 -0
- data/test/migration/projects/project2/config/initializers/inflections.rb +15 -0
- data/test/migration/projects/project2/config/initializers/mime_types.rb +5 -0
- data/test/migration/projects/project2/config/initializers/secret_token.rb +7 -0
- data/test/migration/projects/project2/config/initializers/session_store.rb +8 -0
- data/test/migration/projects/project2/config/initializers/wrap_parameters.rb +14 -0
- data/test/migration/projects/project2/config/locales/en.yml +5 -0
- data/test/migration/projects/project2/config/routes.rb +58 -0
- data/test/migration/projects/project2/config.ru +4 -0
- data/test/migration/projects/project2/db/seeds.rb +7 -0
- data/test/migration/projects/project2/lib/assets/.gitkeep +0 -0
- data/test/migration/projects/project2/lib/tasks/.gitkeep +0 -0
- data/test/migration/projects/project2/log/.gitkeep +0 -0
- data/test/migration/projects/project2/migrations/migration_2013-04-Apr-23_17.31.38.rb +16 -0
- data/test/migration/projects/project2/migrations/migration_2013-04-Apr-23_17.31.52.rb +16 -0
- data/test/migration/projects/project2/migrations/migration_2013-04-Apr-23_17.32.07.rb +16 -0
- data/test/migration/projects/project2/public/404.html +26 -0
- data/test/migration/projects/project2/public/422.html +26 -0
- data/test/migration/projects/project2/public/500.html +25 -0
- data/test/migration/projects/project2/public/favicon.ico +0 -0
- data/test/migration/projects/project2/public/index.html +241 -0
- data/test/migration/projects/project2/public/robots.txt +5 -0
- data/test/migration/projects/project2/script/rails +6 -0
- data/test/migration/test_loader.rb +78 -0
- data/test/migration/test_migration.rb +127 -0
- data/test/migration/test_nonexistent_classes.rb +120 -0
- data/test/migration/test_operations_fit_together.rb +68 -0
- data/test/migration/test_project.rb +105 -0
- data/test/migration/test_project1.slow.rb +58 -0
- data/test/migration/test_project2.slow.rb +34 -0
- data/test/migration/test_remove.rb +219 -0
- data/test/migration/test_rename.rb +238 -0
- data/test/migration/test_scenario.rb +37 -0
- data/test/migration/todo.txt +17 -0
- data/test/more_asserts.rb +63 -0
- data/test/snapshot/test_snapshot.rb +99 -0
- data/test/snapshot/test_snapshot_attributes.slow.rb +57 -0
- data/test/snapshot/test_snapshot_classes.slow.rb +32 -0
- data/test/snapshot/test_snapshotable.rb +53 -0
- data/test/test_accessors.rb +46 -0
- data/test/test_active_model_like_interface.rb +1 -1
- data/test/test_model_timestamps.rb +22 -0
- data/test/test_more_asserts.rb +68 -0
- data/test/test_naming.rb +22 -0
- data/test/test_persistence.rb +127 -0
- data/test/test_query_interface.rb +20 -25
- data/test/test_sensibles.rb +38 -0
- data/test/test_validation.rb +20 -57
- data/todo.txt +1 -0
- data/update.sh +180 -0
- metadata +156 -12
- data/lib/maglev_record/model_not_saved_or_reset.rb +0 -15
- data/test/test_dirty_object.rb +0 -50
- data/test/test_maglev_record_base.rb +0 -22
- data/test/test_maglev_simple_persistance.rb +0 -45
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
require "maglev_record/snapshot/change"
|
|
2
|
+
|
|
3
|
+
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
|
+
|
|
12
|
+
def exists?
|
|
13
|
+
@exists
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def ==(other)
|
|
17
|
+
self.snapshot_class == other.snapshot_class \
|
|
18
|
+
and self.exists? == other.exists?
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
alias :eql? :==
|
|
22
|
+
|
|
23
|
+
def snapshot_class
|
|
24
|
+
@snapshot_class
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def changes_since(older)
|
|
28
|
+
return nil unless changed_since?(older)
|
|
29
|
+
ClassChange.new(older, self)
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def changed_since?(older)
|
|
33
|
+
attr_readers != older.attr_readers
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def attr_readers
|
|
37
|
+
@attr_readers
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def class_name
|
|
41
|
+
@snapshot_class.name
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
class Snapshot
|
|
46
|
+
|
|
47
|
+
def for_class(cls)
|
|
48
|
+
ClassSnapshot.new(cls)
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
def initialize
|
|
52
|
+
@class_snapshots = Snapshotable.snapshotable_classes.map{ |cls|
|
|
53
|
+
for_class cls
|
|
54
|
+
}
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
def changes_since(older)
|
|
58
|
+
Change.new(older, self)
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
def class_snapshots
|
|
62
|
+
Array.new(@class_snapshots)
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
end
|
|
66
|
+
end
|
|
67
|
+
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
Maglev.persistent do
|
|
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
|
|
17
|
+
|
|
18
|
+
module MaglevRecord
|
|
19
|
+
module Snapshotable
|
|
20
|
+
def self.snapshotable_classes
|
|
21
|
+
classes = []
|
|
22
|
+
Object.constants.each { |constant|
|
|
23
|
+
begin
|
|
24
|
+
cls = Object.const_get constant
|
|
25
|
+
rescue Exception
|
|
26
|
+
else
|
|
27
|
+
classes << cls if cls.is_a? Class and cls.ancestors.include? self
|
|
28
|
+
end
|
|
29
|
+
}
|
|
30
|
+
classes
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
module ClassMethods
|
|
34
|
+
|
|
35
|
+
def self.extended(base)
|
|
36
|
+
base.exist! if base.respond_to? :exist!
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def file_paths
|
|
40
|
+
(self.instance_methods(false).map { |m|
|
|
41
|
+
self.instance_method(m).source_location.first
|
|
42
|
+
} + self.methods(false).map { |m|
|
|
43
|
+
self.method(m).source_location.first
|
|
44
|
+
}).uniq
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
def has_definitions?
|
|
48
|
+
# TODO: maybe in a nested transaction?
|
|
49
|
+
start_existence_test!
|
|
50
|
+
file_paths.each{ |file_path|
|
|
51
|
+
Kernel.load file_path if File.file? file_path
|
|
52
|
+
return true if exists?
|
|
53
|
+
}
|
|
54
|
+
exists?
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
end
|
|
58
|
+
end
|
|
@@ -0,0 +1,189 @@
|
|
|
1
|
+
|
|
2
|
+
|
|
3
|
+
class Object
|
|
4
|
+
#
|
|
5
|
+
# breadth first search for references from the given object to self
|
|
6
|
+
#
|
|
7
|
+
def reference_path_to(to_object, length, trace = false)
|
|
8
|
+
paths = [[to_object]]
|
|
9
|
+
traversed = IdentitySet.new
|
|
10
|
+
traversed.add(to_object)
|
|
11
|
+
start_size = 1 if trace
|
|
12
|
+
while not paths.empty? and paths.first.size <= length
|
|
13
|
+
references = paths[0][0].find_references_in_memory
|
|
14
|
+
# if we print here a SecurityError mey occur
|
|
15
|
+
references.each{ |reference|
|
|
16
|
+
return [reference] + paths[0] if reference.equal?(self)
|
|
17
|
+
unless traversed.include?(reference) or paths.any?{ |path| reference.equal?(path)}
|
|
18
|
+
paths.push([reference] + paths[0])
|
|
19
|
+
traversed.add(reference)
|
|
20
|
+
end
|
|
21
|
+
}
|
|
22
|
+
if trace and start_size != paths[0].size
|
|
23
|
+
puts "reference_path_length: #{paths[0].size}"
|
|
24
|
+
start_size = paths[0].size
|
|
25
|
+
end
|
|
26
|
+
paths.delete_at(0)
|
|
27
|
+
end
|
|
28
|
+
return nil
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def __hash_key_value
|
|
32
|
+
begin
|
|
33
|
+
assoc_value = instance_variable_defined?(:@_st_value)
|
|
34
|
+
assoc_key = instance_variable_defined?(:@_st_key)
|
|
35
|
+
rescue NameError
|
|
36
|
+
return nil
|
|
37
|
+
end
|
|
38
|
+
return nil unless assoc_key and assoc_value
|
|
39
|
+
return [assoc_key, assoc_value]
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def __hash_key_value_equal_to(anObject)
|
|
43
|
+
value = __hash_key_value
|
|
44
|
+
return nil if value.nil?
|
|
45
|
+
assoc_key, assoc_value = value
|
|
46
|
+
return nil unless assoc_key.equal?(anObject) or assoc_value.equal?(anObject)
|
|
47
|
+
return [assoc_key, assoc_value]
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
def __instance_variable_equal_to(anObject)
|
|
51
|
+
return nil unless self.respond_to?(:instance_variables) and self.respond_to?(:instance_variable_get)
|
|
52
|
+
self.instance_variables.each{ |v|
|
|
53
|
+
return v if self.instance_variable_get(v).equal?(anObject)
|
|
54
|
+
}
|
|
55
|
+
return nil
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
def __eigenclass_equal_to(anObject)
|
|
59
|
+
# breaks maglev if this is used by
|
|
60
|
+
# maglev-ruby -e "require 'object_reference'; o = Object.new; start = Time.now; f = o.string_reference_path_to(true, 20); p start - Time.now; puts f"
|
|
61
|
+
return false if self.class.equal?(Array)
|
|
62
|
+
eigenclass = class << self
|
|
63
|
+
self
|
|
64
|
+
end
|
|
65
|
+
eigenclass.equal? anObject
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
def __incuded_module_equal_to(anObject)
|
|
69
|
+
included_modules.any?{ |m| m.equal?(anObject)} if respond_to? :included_modules
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
# format the reference path
|
|
73
|
+
# returns a human readable string
|
|
74
|
+
#
|
|
75
|
+
# length
|
|
76
|
+
# the maximum length of the path
|
|
77
|
+
#
|
|
78
|
+
# width_of_line
|
|
79
|
+
# the maximum size of a line
|
|
80
|
+
#
|
|
81
|
+
# trace
|
|
82
|
+
# true if reference_path_length shall be printed to stdout to trace the progress
|
|
83
|
+
# false otherwise
|
|
84
|
+
#
|
|
85
|
+
# pretty
|
|
86
|
+
# true if module includes shall be detected
|
|
87
|
+
# false if maglev internals shall be revealed
|
|
88
|
+
#
|
|
89
|
+
def string_reference_path_to(to_object, length = 10, width_of_line = 80, trace = true, pretty = true)
|
|
90
|
+
reference_path = reference_path_to(to_object, length, trace)
|
|
91
|
+
return "" if reference_path.nil?
|
|
92
|
+
lines = []
|
|
93
|
+
left_column_size = 0
|
|
94
|
+
referenced_index = 0
|
|
95
|
+
while referenced_index < reference_path.size - 1
|
|
96
|
+
# prepare loop
|
|
97
|
+
object = reference_path[referenced_index]
|
|
98
|
+
referenced = reference_path[referenced_index + 1]
|
|
99
|
+
referenced_index += 1
|
|
100
|
+
name_of_referenced = '?? of'
|
|
101
|
+
inspect_string = nil
|
|
102
|
+
# switch case on displaying references
|
|
103
|
+
begin
|
|
104
|
+
# puts "#obj: #{object.inspect} #{referenced.inspect} at: #{referenced_index}\n\n"
|
|
105
|
+
# object --> class
|
|
106
|
+
if object.class.equal?(referenced)
|
|
107
|
+
name_of_referenced = 'class of'
|
|
108
|
+
# class --> superclass
|
|
109
|
+
elsif object.respond_to? :superclass and object.superclass.equal?(referenced)
|
|
110
|
+
name_of_referenced = 'superclass of'
|
|
111
|
+
# included
|
|
112
|
+
elsif pretty and object.__incuded_module_equal_to(reference_path[referenced_index + 2])
|
|
113
|
+
name_of_referenced = 'included by'
|
|
114
|
+
referenced = reference_path[referenced_index + 2]
|
|
115
|
+
referenced_index += 2
|
|
116
|
+
# OrderPreservingHashAssociation --> ...
|
|
117
|
+
elsif not object.__hash_key_value_equal_to(referenced).nil?
|
|
118
|
+
assoc_key, assoc_value = object.__hash_key_value_equal_to(referenced)
|
|
119
|
+
if assoc_value.equal?(referenced)
|
|
120
|
+
name_of_referenced = 'value of'
|
|
121
|
+
elsif assoc_key.equal?(referenced)
|
|
122
|
+
name_of_referenced = 'key of'
|
|
123
|
+
end
|
|
124
|
+
# ... --> OrderPreservingHashAssociation
|
|
125
|
+
elsif not referenced.__hash_key_value.nil?
|
|
126
|
+
assoc_key, assoc_value = referenced.__hash_key_value
|
|
127
|
+
inspect_string = "#{assoc_key.inspect} => #{assoc_value.inspect}"
|
|
128
|
+
name_of_referenced = 'association in'
|
|
129
|
+
# ... --> attribute
|
|
130
|
+
elsif not (variable = object.__instance_variable_equal_to(referenced)).nil?
|
|
131
|
+
name_of_referenced = variable.to_s + ' in'
|
|
132
|
+
# [...] -> object at index
|
|
133
|
+
elsif object.respond_to?(:find_index) and (index = object.find_index{ |element| element.equal?(referenced)})
|
|
134
|
+
name_of_referenced = "at #{index.inspect} of"
|
|
135
|
+
# eigenclass
|
|
136
|
+
elsif object.__eigenclass_equal_to(referenced)
|
|
137
|
+
name_of_referenced = "eigenclass of"
|
|
138
|
+
elsif referenced.__eigenclass_equal_to(object)
|
|
139
|
+
name_of_referenced = "eigenbase of"
|
|
140
|
+
end
|
|
141
|
+
rescue Exception => e
|
|
142
|
+
name_of_referenced = 'error'
|
|
143
|
+
referenced = e
|
|
144
|
+
end
|
|
145
|
+
begin
|
|
146
|
+
inspect_string = referenced.inspect if inspect_string.nil?
|
|
147
|
+
rescue
|
|
148
|
+
inspect_string = 'can not inspect this object'
|
|
149
|
+
end
|
|
150
|
+
lines << [name_of_referenced, inspect_string]
|
|
151
|
+
left_column_size = name_of_referenced.size if name_of_referenced.size > left_column_size
|
|
152
|
+
end
|
|
153
|
+
width_of_line -= 8 # subtract additional characters like ' is ' ' of '
|
|
154
|
+
right_column_size = width_of_line - left_column_size
|
|
155
|
+
right_column_size = 10 if right_column_size < 0
|
|
156
|
+
s = ""
|
|
157
|
+
lines.reverse.each{ |line|
|
|
158
|
+
s += "#{line[1][0..right_column_size]} is \n#{line[0].rjust(left_column_size)} "
|
|
159
|
+
}
|
|
160
|
+
return s + "#{reference_path[0].inspect}"
|
|
161
|
+
end
|
|
162
|
+
end
|
|
163
|
+
|
|
164
|
+
# TestCases
|
|
165
|
+
|
|
166
|
+
## Hash
|
|
167
|
+
# maglev-ruby -e "require 'object_reference'; d = {a:'2', b: 34}; f = d.string_reference_path_to(d[:a], 3); puts f"
|
|
168
|
+
|
|
169
|
+
## superclass
|
|
170
|
+
# maglev-ruby -e "require 'object_reference'; class AA;end; class BB < AA;end; class CC < BB; end; class DD < CC; end; class EE < DD; end; puts EE.string_reference_path_to(AA, 5)"
|
|
171
|
+
|
|
172
|
+
## instance var
|
|
173
|
+
# maglev-ruby -e "require 'object_reference'; class AA; attr_accessor :x end; o = Object.new; aa = AA.new; aa.x = o; puts aa.string_reference_path_to(o, 3)"
|
|
174
|
+
|
|
175
|
+
## class and instvar
|
|
176
|
+
# maglev-ruby -e "require 'object_reference'; class AA; attr_accessor :x end; class OO; end;o = OO.new; aa = AA.new; aa.x = o; bb = AA.new; bb.x = aa; cc = AA.new; cc.x = bb; puts cc.string_reference_path_to(OO, 5)"
|
|
177
|
+
|
|
178
|
+
## Object.new references true
|
|
179
|
+
# maglev-ruby -e "require 'object_reference'; o = Object.new; start = Time.now; f = o.string_reference_path_to(true, 20); p start - Time.now; puts f"
|
|
180
|
+
|
|
181
|
+
## Array
|
|
182
|
+
# maglev-ruby -e "require 'object_reference'; a = b = []; 20.times{a = [a]}; start = Time.now; f = a.string_reference_path_to(b, 20); p start - Time.now; puts f"
|
|
183
|
+
|
|
184
|
+
## Eigenclass (x is eigenclass of y)
|
|
185
|
+
# maglev-ruby -e "require 'object_reference'; class X;end; x = class <<X;self;end; puts X.string_reference_path_to(x, 5)"
|
|
186
|
+
|
|
187
|
+
## Eigenbase ( reverse of eigenclass)
|
|
188
|
+
# maglev-ruby -e "require 'object_reference'; class X;end; x = class <<X;self;end; puts x.string_reference_path_to(X, 5)"
|
|
189
|
+
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
class MaglevSupport::SubmoduleFinder
|
|
2
|
+
def submodules_for(*constants)
|
|
3
|
+
whole_set = Set.new
|
|
4
|
+
constants.each do |constant|
|
|
5
|
+
@referenced_modules = Set.new
|
|
6
|
+
reference(constant)
|
|
7
|
+
whole_set = whole_set.union(@referenced_modules.select do |mod|
|
|
8
|
+
mod.name.to_s.include?(constant.to_s)
|
|
9
|
+
end)
|
|
10
|
+
end
|
|
11
|
+
constants + whole_set.to_a.sort_by do |mod| mod.name end
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def reference(constant)
|
|
15
|
+
return if @referenced_modules.include?(constant)
|
|
16
|
+
@referenced_modules.add(constant)
|
|
17
|
+
|
|
18
|
+
submodules = constant.constants.map do |const|
|
|
19
|
+
begin
|
|
20
|
+
constant.const_get(const)
|
|
21
|
+
rescue Exception => e; end
|
|
22
|
+
end.select do |mod|
|
|
23
|
+
# We only want constants which are classes or modules, e.g. no Fixnums, Strings, ...
|
|
24
|
+
[Module, Class].include?(mod.class)
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
submodules.each do |mod|
|
|
28
|
+
reference(mod)
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
private :reference
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
if __FILE__ == $0
|
|
35
|
+
# EXAMPLE USAGE
|
|
36
|
+
require "rubygems"
|
|
37
|
+
require "rake"
|
|
38
|
+
puts "Finding all submodules of Rake."
|
|
39
|
+
ref_finder = SubmoduleFinder.new
|
|
40
|
+
referenced_modules = ref_finder.submodules_for(Rake)
|
|
41
|
+
puts referenced_modules.to_a.map { |mod| mod.name }.sort.inspect
|
|
42
|
+
end
|
data/lib/maglev_record.rb
CHANGED
|
@@ -1,4 +1,60 @@
|
|
|
1
|
-
require "
|
|
2
|
-
require "
|
|
3
|
-
require "
|
|
4
|
-
require "maglev_record/
|
|
1
|
+
require "active_model"
|
|
2
|
+
require "active_support"
|
|
3
|
+
require "active_support/core_ext/class/attribute"
|
|
4
|
+
require "maglev_record/maglev_support/maglev_support"
|
|
5
|
+
require "maglev_record/tools"
|
|
6
|
+
require "bundler/setup"
|
|
7
|
+
require "maglev_record/maglev_support/concern"
|
|
8
|
+
require "set"
|
|
9
|
+
require "logger"
|
|
10
|
+
|
|
11
|
+
if defined? MaglevRecord::VERSION
|
|
12
|
+
if defined? RootedBook
|
|
13
|
+
RootedBook.redo_include_and_extend
|
|
14
|
+
end
|
|
15
|
+
if defined? UnrootedBook
|
|
16
|
+
UnrootedBook.redo_include_and_extend
|
|
17
|
+
end
|
|
18
|
+
MaglevRecord::Migration.redo_include_and_extend
|
|
19
|
+
else
|
|
20
|
+
puts "IT IS NOT DEFINED"
|
|
21
|
+
# require "maglev_record/tools"
|
|
22
|
+
require "maglev_record/maglev_record"
|
|
23
|
+
|
|
24
|
+
require "maglev_record/snapshot"
|
|
25
|
+
require "maglev_record/maglev_support/secure_password"
|
|
26
|
+
require "maglev_record/sensible"
|
|
27
|
+
require "maglev_record/rooted_enumerable"
|
|
28
|
+
require "maglev_record/enumerable"
|
|
29
|
+
require "maglev_record/errors"
|
|
30
|
+
require "maglev_record/integration"
|
|
31
|
+
require "maglev_record/persistence"
|
|
32
|
+
require "maglev_record/read_write"
|
|
33
|
+
require "maglev_record/rooted_persistence"
|
|
34
|
+
require "maglev_record/migration"
|
|
35
|
+
require "maglev_record/base"
|
|
36
|
+
require "maglev_record/rooted_base"
|
|
37
|
+
|
|
38
|
+
module MaglevRecord
|
|
39
|
+
ref_finder = MaglevSupport::SubmoduleFinder.new
|
|
40
|
+
referenced_modules = ref_finder.submodules_for(MaglevRecord, MaglevSupport, Set)
|
|
41
|
+
MAGLEV_RECORD_PROC = Proc.new do |superklass_module|
|
|
42
|
+
referenced_modules.include?(superklass_module)
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
Maglev.persistent do
|
|
46
|
+
class ::Module
|
|
47
|
+
def maglev_record_persistable
|
|
48
|
+
self.maglev_persistable(true, &MAGLEV_RECORD_PROC)
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
referenced_modules.each do |mod|
|
|
54
|
+
mod.maglev_record_persistable
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
ActiveModel::Errors.maglev_nil_references
|
|
60
|
+
Maglev.commit_transaction
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
require "maglev_record/migration"
|
|
2
|
+
require "time"
|
|
3
|
+
require "logger"
|
|
4
|
+
require "fileutils"
|
|
5
|
+
|
|
6
|
+
MIGRATION_FOLDER = "migrations"
|
|
7
|
+
|
|
8
|
+
desc "transform the maglev database"
|
|
9
|
+
namespace :migrate do
|
|
10
|
+
|
|
11
|
+
desc "set up the project for migrations"
|
|
12
|
+
task :setup do
|
|
13
|
+
# puts "PID: #{Process.pid} stone: #{Maglev::System.stone_name}"
|
|
14
|
+
FileUtils.mkpath(MIGRATION_FOLDER) unless File.directory?(MIGRATION_FOLDER)
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
desc "migrate all the migrations in the migration folder"
|
|
18
|
+
task :up => :setup do
|
|
19
|
+
loader = MaglevRecord::MigrationLoader.new
|
|
20
|
+
loader.load_directory(MIGRATION_FOLDER)
|
|
21
|
+
migrator = MaglevRecord::Migrator.new(loader.migration_list)
|
|
22
|
+
logger = Logger.new(STDOUT)
|
|
23
|
+
migrator.up(logger)
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
desc "create a new migration in the migration folder"
|
|
27
|
+
task :new => :setup do
|
|
28
|
+
now = Time.now
|
|
29
|
+
filename = now.strftime("migration_%Y-%m-%b-%d_%H.%M.%S.rb")
|
|
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}"
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
require File.expand_path "lib/maglev_record/maglev_record"
|
|
2
|
+
|
|
3
|
+
Gem::Specification.new do |s|
|
|
4
|
+
s.name = 'maglevrecord'
|
|
5
|
+
s.version = MaglevRecord::VERSION
|
|
6
|
+
s.date = '2013-05-06'
|
|
7
|
+
s.summary = "MagLev persistence with an ActiveRecord-like interface!"
|
|
8
|
+
s.description = "Library for using to MagLev to persist your data in a Rails app."
|
|
9
|
+
s.authors = ["Dimitri Korsch", "Kirstin Heidler", "Nicco Kunzmann", "Matthias Springer", "Stefan Bunk"]
|
|
10
|
+
s.files = `git ls-files`.split("\n")
|
|
11
|
+
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
|
12
|
+
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
|
13
|
+
s.email = "bp2012h1@hpi.uni-potsdam.de"
|
|
14
|
+
s.homepage = 'https://github.com/knub/maglevrecord'
|
|
15
|
+
s.require_path = 'lib'
|
|
16
|
+
|
|
17
|
+
end
|
data/rails/init.rb
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
Kernel.load File.join(File.dirname(__FILE__), '..', 'init.rb')
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
require "maglev_record"
|
|
2
|
+
|
|
3
|
+
# use
|
|
4
|
+
# bundle exec rake test TESTOPTS="-v" TEST=test/_test_object_reference.rb
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
module Mod
|
|
8
|
+
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
class AA
|
|
12
|
+
include Mod
|
|
13
|
+
attr_accessor :x
|
|
14
|
+
end
|
|
15
|
+
class BB < AA
|
|
16
|
+
end
|
|
17
|
+
class CC < BB
|
|
18
|
+
end
|
|
19
|
+
class DD < CC
|
|
20
|
+
end
|
|
21
|
+
class EE < DD
|
|
22
|
+
end
|
|
23
|
+
class OO
|
|
24
|
+
end
|
|
25
|
+
class X
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
o = OO.new
|
|
29
|
+
d = {a:'2', b: 34}
|
|
30
|
+
aa = AA.new
|
|
31
|
+
aa.x = o;
|
|
32
|
+
bb = AA.new
|
|
33
|
+
bb.x = aa
|
|
34
|
+
cc = AA.new
|
|
35
|
+
cc.x = bb
|
|
36
|
+
a = b = []
|
|
37
|
+
20.times{ a = [a] }
|
|
38
|
+
x = class << X
|
|
39
|
+
self
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def self.assert_equal(v1, v2)
|
|
43
|
+
raise "expected \n#{v1.inspect}\n == \n#{v2.inspect}" unless v1 == v2
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
puts 'test_hash'
|
|
47
|
+
assert_equal d.string_reference_path_to(d[:a], 3), "\"2\" is \n value of :a => \"2\" is \nassociation in {:a=>\"2\", :b=>34}"
|
|
48
|
+
|
|
49
|
+
puts 'test_superclass'
|
|
50
|
+
assert_equal EE.string_reference_path_to(AA, 5), "AA is \nsuperclass of BB is \nsuperclass of CC is \nsuperclass of DD is \nsuperclass of EE"
|
|
51
|
+
|
|
52
|
+
puts 'test_instance_variable'
|
|
53
|
+
assert_equal aa.string_reference_path_to(o, 3), "#{o.inspect} is \n@x in #{aa.inspect}"
|
|
54
|
+
|
|
55
|
+
puts 'test_class_to_inst_var'
|
|
56
|
+
assert_equal cc.string_reference_path_to(OO, 5), "OO is \nclass of #{o.inspect} is \n @x in #{aa.inspect} is \n @x in #{bb.inspect} is \n @x in #{cc.inspect}"
|
|
57
|
+
|
|
58
|
+
puts 'test_array'
|
|
59
|
+
assert_equal a.string_reference_path_to(b, 20), "[] is \nat 0 of [[]] is \nat 0 of [[[]]] is \nat 0 of [[[[]]]] is \nat 0 of [[[[[]]]]] is \nat 0 of [[[[[[]]]]]] is \nat 0 of [[[[[[[]]]]]]] is \nat 0 of [[[[[[[[]]]]]]]] is \nat 0 of [[[[[[[[[]]]]]]]]] is \nat 0 of [[[[[[[[[[]]]]]]]]]] is \nat 0 of [[[[[[[[[[[]]]]]]]]]]] is \nat 0 of [[[[[[[[[[[[]]]]]]]]]]]] is \nat 0 of [[[[[[[[[[[[[]]]]]]]]]]]]] is \nat 0 of [[[[[[[[[[[[[[]]]]]]]]]]]]]] is \nat 0 of [[[[[[[[[[[[[[[]]]]]]]]]]]]]]] is \nat 0 of [[[[[[[[[[[[[[[[]]]]]]]]]]]]]]]] is \nat 0 of [[[[[[[[[[[[[[[[[]]]]]]]]]]]]]]]]] is \nat 0 of [[[[[[[[[[[[[[[[[[]]]]]]]]]]]]]]]]]] is \nat 0 of [[[[[[[[[[[[[[[[[[[]]]]]]]]]]]]]]]]]]] is \nat 0 of [[[[[[[[[[[[[[[[[[[[]]]]]]]]]]]]]]]]]]]] is \nat 0 of [[[[[[[[[[[[[[[[[[[[[]]]]]]]]]]]]]]]]]]]]]"
|
|
60
|
+
|
|
61
|
+
puts 'test_eigenclass'
|
|
62
|
+
assert_equal X.string_reference_path_to(x, 5), "#{x.inspect} is \neigenclass of X"
|
|
63
|
+
|
|
64
|
+
puts 'test_eigenclass_base'
|
|
65
|
+
assert_equal x.string_reference_path_to(X, 5), "X is \neigenbase of #{x.inspect}"
|
|
66
|
+
|
|
67
|
+
puts 'test_include'
|
|
68
|
+
assert_equal AA.string_reference_path_to(Mod, 5), "Mod is \nincluded by AA"
|
|
69
|
+
|
|
70
|
+
puts 'done!'
|
data/test/example_model.rb
CHANGED
|
@@ -1,19 +1,32 @@
|
|
|
1
|
-
require "maglev_record"
|
|
2
|
-
|
|
3
1
|
class Book
|
|
4
|
-
include MaglevRecord::
|
|
5
|
-
|
|
2
|
+
include MaglevRecord::ReadWrite
|
|
6
3
|
attr_accessor :author, :title, :comments
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
def to_s
|
|
13
|
-
title
|
|
4
|
+
def self.example_params
|
|
5
|
+
{ :author => "Author", :title => "Title" }
|
|
6
|
+
end
|
|
7
|
+
def self.example
|
|
8
|
+
self.new(example_params)
|
|
14
9
|
end
|
|
10
|
+
end
|
|
15
11
|
|
|
16
|
-
|
|
17
|
-
|
|
12
|
+
class RootedBook < Book
|
|
13
|
+
include MaglevRecord::RootedBase
|
|
14
|
+
def book
|
|
15
|
+
puts "I am a RootedBook"
|
|
18
16
|
end
|
|
19
17
|
end
|
|
18
|
+
|
|
19
|
+
class UnrootedBook < Book
|
|
20
|
+
include MaglevRecord::Base
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
Book.maglev_record_persistable
|
|
24
|
+
RootedBook.maglev_record_persistable
|
|
25
|
+
UnrootedBook.maglev_record_persistable
|
|
26
|
+
Maglev.commit_transaction
|
|
27
|
+
|
|
28
|
+
class RootedBook
|
|
29
|
+
include ActiveModel::Validations
|
|
30
|
+
validates :author, :presence => true,
|
|
31
|
+
:length => { :minimum => 4 }
|
|
32
|
+
end
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
Migration.new(Time.utc(2014, 2, 1, 10, 0, 0, 0), "Change book title again") do
|
|
2
|
+
def up
|
|
3
|
+
RootedBook.each do |book|
|
|
4
|
+
book.title = "A even newer book title"
|
|
5
|
+
end
|
|
6
|
+
end
|
|
7
|
+
def down
|
|
8
|
+
RootedBook.each do |book|
|
|
9
|
+
book.title = "A new book title"
|
|
10
|
+
end
|
|
11
|
+
end
|
|
12
|
+
end
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
Migration.new(Time.utc(2015, 2, 1, 10, 0, 0, 0), "Change book title to most recent one") do
|
|
2
|
+
def up
|
|
3
|
+
RootedBook.each do |book|
|
|
4
|
+
book.title = "The most recent book title"
|
|
5
|
+
end
|
|
6
|
+
end
|
|
7
|
+
def down
|
|
8
|
+
RootedBook.each do |book|
|
|
9
|
+
book.title = "A even newer book title"
|
|
10
|
+
end
|
|
11
|
+
end
|
|
12
|
+
end
|