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,39 @@
|
|
|
1
|
+
module MaglevRecord
|
|
2
|
+
module SecurePassword
|
|
3
|
+
module ClassMethods
|
|
4
|
+
def has_secure_password
|
|
5
|
+
attr_reader :password
|
|
6
|
+
|
|
7
|
+
# TODO: No validations so far
|
|
8
|
+
# validates_confirmation_of :password
|
|
9
|
+
# validates_presence_of :password_digest
|
|
10
|
+
|
|
11
|
+
mark_sensible :password, :password_confirmation
|
|
12
|
+
|
|
13
|
+
include InstanceMethodsOnActivation
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def encrypt_password(raw_password)
|
|
17
|
+
Digest::SHA256.new.update(raw_password).to_s
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
module InstanceMethodsOnActivation
|
|
22
|
+
def authenticate(raw_password)
|
|
23
|
+
if password_digest == self.class.encrypt_password(raw_password)
|
|
24
|
+
self
|
|
25
|
+
else
|
|
26
|
+
false
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def password=(raw_password)
|
|
31
|
+
require 'digest'
|
|
32
|
+
attributes[:password] = raw_password
|
|
33
|
+
unless raw_password.blank?
|
|
34
|
+
self.password_digest = self.class.encrypt_password(raw_password)
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
end
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
require "maglev_record/migration"
|
|
2
|
+
|
|
3
|
+
##
|
|
4
|
+
# Offers methods for reading migrations from strings, files and directories.
|
|
5
|
+
# Migrations are sorted and dou
|
|
6
|
+
# All read in migrations can be accessed after the reading.
|
|
7
|
+
module MaglevRecord
|
|
8
|
+
class MigrationLoader
|
|
9
|
+
|
|
10
|
+
def initialize
|
|
11
|
+
@migration_list = []
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def migration_list
|
|
15
|
+
# TODO
|
|
16
|
+
# Print a warning, if there are two or more migrations which are equal.
|
|
17
|
+
@migration_list.sort.uniq
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def load_string(source, file = __FILE__)
|
|
21
|
+
migration = instance_eval source, file
|
|
22
|
+
# TODO: Check class of migration is really migration!
|
|
23
|
+
migration.source = source
|
|
24
|
+
@migration_list << migration
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def load_file(file_path)
|
|
28
|
+
raise ArgumentError, "file #{file_path.inspect} not found" unless File.file?(file_path)
|
|
29
|
+
load_string(File.open(file_path).read, file_path)
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def load_directory(directory_path)
|
|
33
|
+
Dir.foreach(directory_path) { |file_name|
|
|
34
|
+
load_file(directory_path + '/' + file_name) if file_name.end_with? '.rb'
|
|
35
|
+
}
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
end
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
require "time"
|
|
2
|
+
|
|
3
|
+
Maglev.persistent do
|
|
4
|
+
class ::String
|
|
5
|
+
def escape_single_quotes
|
|
6
|
+
self.gsub(/[']/, '\\\\\'')
|
|
7
|
+
end
|
|
8
|
+
end
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
module MaglevRecord
|
|
12
|
+
##
|
|
13
|
+
# This class represents a migration which transfers the data set from one
|
|
14
|
+
# state to another.
|
|
15
|
+
# Each migrations is identified by its name and its timestamp. Creating a new
|
|
16
|
+
# migration is as easy as:
|
|
17
|
+
|
|
18
|
+
# Migration.new("2013-02-02 1, 10, 0, 0, 0), "Change book title") do
|
|
19
|
+
# def up
|
|
20
|
+
# Book.each do |book|
|
|
21
|
+
# book.title = "A new book title"
|
|
22
|
+
# end
|
|
23
|
+
# end
|
|
24
|
+
# def down
|
|
25
|
+
# Book.each do |book|
|
|
26
|
+
# book.title = "Back to old title"
|
|
27
|
+
# end
|
|
28
|
+
# end
|
|
29
|
+
# end
|
|
30
|
+
# Furthermore, this class offers methods to actually do data migration.
|
|
31
|
+
class Migration
|
|
32
|
+
redo_include ::Comparable
|
|
33
|
+
|
|
34
|
+
attr_accessor :source, :logger
|
|
35
|
+
attr_reader :timestamp
|
|
36
|
+
attr_reader :name
|
|
37
|
+
|
|
38
|
+
def initialize(timestamp, name, &block)
|
|
39
|
+
@timestamp = timestamp
|
|
40
|
+
@name = name
|
|
41
|
+
@done = false
|
|
42
|
+
instance_eval &block unless block.nil?
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def id
|
|
46
|
+
self.class.id_for(timestamp, name)
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
def hash
|
|
50
|
+
id.hash
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
def self.id_for(timestamp, name)
|
|
54
|
+
[timestamp.month, timestamp.day, timestamp.hour, timestamp.min, timestamp.sec].reduce(timestamp.year.to_s) do |sum, s|
|
|
55
|
+
sum + s.to_s.rjust(2, '0')
|
|
56
|
+
end + name.to_s
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
def to_s
|
|
60
|
+
self.class.name + "<\"#{timestamp.year}-#{timestamp.month.to_s.rjust(2, '0')}-#{timestamp.day.to_s.rjust(2, '0')} #{timestamp.hour.to_s.rjust(2, '0')}:#{timestamp.min.to_s.rjust(2, '0')}:#{timestamp.sec.to_s.rjust(2, '0')}\", \"#{name}\">"
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
def inspect
|
|
64
|
+
return source unless source.nil?
|
|
65
|
+
to_s
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
def self.new(timestamp, name)
|
|
69
|
+
timestamp = Time.parse(timestamp) if timestamp.kind_of? String
|
|
70
|
+
id = id_for(timestamp, name)
|
|
71
|
+
migration = super(timestamp, name)
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
def done?
|
|
75
|
+
@done
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
def do
|
|
79
|
+
up if respond_to?(:up) && !done?
|
|
80
|
+
@done = true
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
def undo
|
|
84
|
+
down if done?
|
|
85
|
+
@done = false
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
def down
|
|
89
|
+
raise IrreversibleMigration, "The migration has no down code specified. Do something about it man. Either give it up or write a down method into the migration definition."
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
def <=>(other)
|
|
93
|
+
compare = timestamp <=> other.timestamp
|
|
94
|
+
return compare if compare != 0
|
|
95
|
+
name <=> other.name
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
def rename_class(old_class, new_name)
|
|
99
|
+
old_class.migration_rename_to(new_name)
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
def delete_class(cls)
|
|
103
|
+
cls.migration_delete
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
def self.const_missing(name)
|
|
107
|
+
#logger.warn("class #{name} was not created but migration by migration #{self.id}") if logger
|
|
108
|
+
MigrationOperations::NullClass.new(name)
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
def self.file_content(time, description, upcode = nil,
|
|
112
|
+
downcode = nil)
|
|
113
|
+
timestamp = time.to_s
|
|
114
|
+
Time.parse(timestamp) # make sure there is no error in the string
|
|
115
|
+
upcode = " # put your transformation code here" if upcode.nil?
|
|
116
|
+
if downcode.nil?
|
|
117
|
+
downcode = " # put the code that reverses the code in up here \n" +
|
|
118
|
+
" # remove the next line that throws he error \n" +
|
|
119
|
+
" raise IrreversibleMigration, " +
|
|
120
|
+
"'The migration has no downcode'"
|
|
121
|
+
end
|
|
122
|
+
<<-eos
|
|
123
|
+
require "maglev_record/migration"
|
|
124
|
+
require "time"
|
|
125
|
+
|
|
126
|
+
MaglevRecord::Migration.new(Time.parse('#{
|
|
127
|
+
timestamp.escape_single_quotes}'), '#{
|
|
128
|
+
description.escape_single_quotes}') do
|
|
129
|
+
|
|
130
|
+
def up
|
|
131
|
+
#{upcode}
|
|
132
|
+
end
|
|
133
|
+
|
|
134
|
+
def down
|
|
135
|
+
#{downcode}
|
|
136
|
+
end
|
|
137
|
+
|
|
138
|
+
end
|
|
139
|
+
eos
|
|
140
|
+
end
|
|
141
|
+
end
|
|
142
|
+
end
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
module MaglevRecord
|
|
2
|
+
##
|
|
3
|
+
# Given a migration list (the desired applied migrations)
|
|
4
|
+
# this class migrates to these state.
|
|
5
|
+
# Therefore, it decides which migrations must be undone, and
|
|
6
|
+
# which must be done and leaves the stone in the desired state.
|
|
7
|
+
class Migrator
|
|
8
|
+
|
|
9
|
+
MIGRATION_KEY = :__migrations__
|
|
10
|
+
|
|
11
|
+
def initialize(migration_list)
|
|
12
|
+
@migration_list = migration_list
|
|
13
|
+
@non_displaying_logger = Logger.new(STDOUT)
|
|
14
|
+
@non_displaying_logger.level = Logger::FATAL
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
##
|
|
18
|
+
# Returns all migrations currently in the stone in the correct order.
|
|
19
|
+
def migration_store
|
|
20
|
+
Maglev::PERSISTENT_ROOT[MIGRATION_KEY] ||= SortedSet.new
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
##
|
|
24
|
+
# Applies the desired state of migrations.
|
|
25
|
+
def up(logger = @non_displaying_logger)
|
|
26
|
+
Maglev.abort_transaction
|
|
27
|
+
to_do = @migration_list.reject do |mig|
|
|
28
|
+
migration_store.include?(mig.id)
|
|
29
|
+
end
|
|
30
|
+
logger.info("Already applied all migrations.") if to_do.empty?
|
|
31
|
+
to_do.sort.each do |mig|
|
|
32
|
+
mig.logger = logger
|
|
33
|
+
logger.info("Doing '" + mig.name + "' from " + mig.timestamp.to_s)
|
|
34
|
+
mig.do
|
|
35
|
+
migration_store << mig.id
|
|
36
|
+
end
|
|
37
|
+
Maglev.commit_transaction
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
end
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
module MaglevRecord
|
|
2
|
+
module MigrationOperations
|
|
3
|
+
module ClassMethods
|
|
4
|
+
|
|
5
|
+
def rename_instance_variable(old_name, new_name)
|
|
6
|
+
each { |model|
|
|
7
|
+
value = model.instance_variable_get(old_name)
|
|
8
|
+
value = yield value if block_given?
|
|
9
|
+
model.remove_instance_variable old_name.to_s
|
|
10
|
+
model.instance_variable_set(new_name, value)
|
|
11
|
+
}
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def rename_attribute(old_name, new_name)
|
|
15
|
+
attr_accessor new_name
|
|
16
|
+
each { |model|
|
|
17
|
+
value = model.attributes[old_name]
|
|
18
|
+
value = yield value if block_given?
|
|
19
|
+
model.attributes.delete old_name
|
|
20
|
+
model.attributes[new_name] = value
|
|
21
|
+
}
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def delete_instance_variable(name)
|
|
25
|
+
each { |model|
|
|
26
|
+
value = model.instance_variable_get(name)
|
|
27
|
+
yield value if block_given?
|
|
28
|
+
model.remove_instance_variable name.to_s
|
|
29
|
+
}
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def delete_attribute(name)
|
|
33
|
+
each { |model|
|
|
34
|
+
value = model.attributes.delete(name)
|
|
35
|
+
yield value if block_given?
|
|
36
|
+
}
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def migration_rename_to(new_name)
|
|
40
|
+
old_name = name
|
|
41
|
+
old_class = self
|
|
42
|
+
nested_class = old_class.nesting_list[-2]
|
|
43
|
+
Maglev.persistent do
|
|
44
|
+
cls = nested_class.remove_const nesting_name_list[-1]
|
|
45
|
+
end
|
|
46
|
+
old_class.instance_eval "
|
|
47
|
+
def name
|
|
48
|
+
'#{(nesting_name_list[0...-1] + [new_name.to_s]).join('::')}'
|
|
49
|
+
end
|
|
50
|
+
"
|
|
51
|
+
nested_class.const_set new_name, old_class
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
def migration_delete
|
|
55
|
+
Maglev.persistent do
|
|
56
|
+
nesting_list[-2].remove_const(nesting_name_list[-1])
|
|
57
|
+
end
|
|
58
|
+
delete_object_pool
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
def nesting_name_list
|
|
62
|
+
self.name.split('::')
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
def nesting_list
|
|
66
|
+
names = nesting_name_list
|
|
67
|
+
list = [Object]
|
|
68
|
+
names.each { |name|
|
|
69
|
+
list << list[-1].const_get(name)
|
|
70
|
+
}
|
|
71
|
+
list
|
|
72
|
+
end
|
|
73
|
+
end
|
|
74
|
+
class NullClass
|
|
75
|
+
include ClassMethods
|
|
76
|
+
def initialize(name)
|
|
77
|
+
@name = name
|
|
78
|
+
end
|
|
79
|
+
def new(*args)
|
|
80
|
+
# TODO: test
|
|
81
|
+
raise "This class #{@name} has not been created"
|
|
82
|
+
end
|
|
83
|
+
def each
|
|
84
|
+
end
|
|
85
|
+
def migration_rename_to(new_name)
|
|
86
|
+
end
|
|
87
|
+
def migration_delete
|
|
88
|
+
end
|
|
89
|
+
end
|
|
90
|
+
end
|
|
91
|
+
end
|
|
92
|
+
|
|
@@ -1,66 +1,41 @@
|
|
|
1
|
-
require "active_support"
|
|
2
|
-
|
|
3
1
|
module MaglevRecord
|
|
4
2
|
module Persistence
|
|
5
|
-
extend
|
|
6
|
-
|
|
7
|
-
def delete
|
|
8
|
-
self.class.delete(self)
|
|
9
|
-
end
|
|
3
|
+
extend MaglevSupport::Concern
|
|
10
4
|
|
|
11
|
-
def
|
|
12
|
-
if
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
@dirty = nil
|
|
18
|
-
true
|
|
19
|
-
else
|
|
20
|
-
raise StandardError, "Model validation failed"
|
|
5
|
+
def initialize(*args)
|
|
6
|
+
if args.size == 1
|
|
7
|
+
args[0].each do |k, v|
|
|
8
|
+
meth_name = "#{k.to_s}=".to_sym
|
|
9
|
+
self.send(meth_name, v) if self.respond_to? meth_name
|
|
10
|
+
end
|
|
21
11
|
end
|
|
12
|
+
@created_at_timestamp = Time.now
|
|
22
13
|
end
|
|
23
14
|
|
|
24
|
-
def
|
|
25
|
-
|
|
26
|
-
self.save!(options)
|
|
27
|
-
rescue StandardError
|
|
28
|
-
false
|
|
29
|
-
end
|
|
15
|
+
def created_at
|
|
16
|
+
@created_at_timestamp
|
|
30
17
|
end
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
!new_record?
|
|
18
|
+
def created_at=(timestamp)
|
|
19
|
+
@created_at_timestamp = timestamp
|
|
34
20
|
end
|
|
35
21
|
|
|
22
|
+
alias :persisted? :committed?
|
|
36
23
|
def new_record?
|
|
37
|
-
!
|
|
24
|
+
!persisted?
|
|
38
25
|
end
|
|
39
26
|
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
end
|
|
27
|
+
def id
|
|
28
|
+
object_id
|
|
29
|
+
end
|
|
44
30
|
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
self.object_pool.delete(m.object_id) if yield(m)
|
|
49
|
-
end
|
|
50
|
-
elsif !block_given? and args.size > 0
|
|
51
|
-
args.each do |m|
|
|
52
|
-
self.object_pool.delete(m.object_id)
|
|
53
|
-
end
|
|
54
|
-
else
|
|
55
|
-
raise ArgumentError, "only block or arguments allowed"
|
|
56
|
-
end
|
|
31
|
+
module ClassMethods
|
|
32
|
+
def clear
|
|
33
|
+
raise MaglevRecord::InvalidOperationError, "Do not use clear without including MaglevRecord::RootedBase."
|
|
57
34
|
end
|
|
58
35
|
|
|
59
|
-
def
|
|
60
|
-
|
|
36
|
+
def create
|
|
37
|
+
raise MaglevRecord::InvalidOperationError, "Do not use create without including MaglevRecord::RootedBase."
|
|
61
38
|
end
|
|
62
39
|
end
|
|
63
|
-
|
|
64
40
|
end
|
|
65
|
-
end
|
|
66
|
-
|
|
41
|
+
end
|
|
@@ -1,15 +1,25 @@
|
|
|
1
|
-
require "active_support"
|
|
2
1
|
|
|
3
2
|
module MaglevRecord
|
|
4
3
|
module ReadWrite
|
|
5
|
-
extend
|
|
4
|
+
extend MaglevSupport::Concern
|
|
5
|
+
|
|
6
|
+
def attributes
|
|
7
|
+
@maglev_attributes ||= Hash.new
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
def update_attributes(attribute_hash)
|
|
11
|
+
attribute_hash.each_pair do |k,v|
|
|
12
|
+
attributes[k.to_sym] = v
|
|
13
|
+
end
|
|
14
|
+
self
|
|
15
|
+
end
|
|
6
16
|
|
|
7
17
|
module ClassMethods
|
|
8
18
|
def attr_reader(*attr_names)
|
|
19
|
+
@attr_readers ||= []
|
|
9
20
|
attr_names.each do |attr_name|
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
generated_attribute_methods.module_eval <<-ATTRREADER, __FILE__, __LINE__ + 1
|
|
21
|
+
attr_readers << attr_name
|
|
22
|
+
self.module_eval <<-ATTRREADER, __FILE__, __LINE__ + 1
|
|
13
23
|
def #{attr_name}
|
|
14
24
|
attributes[:#{attr_name}]
|
|
15
25
|
end
|
|
@@ -17,14 +27,14 @@ module MaglevRecord
|
|
|
17
27
|
end
|
|
18
28
|
end
|
|
19
29
|
|
|
30
|
+
def attr_readers
|
|
31
|
+
@attr_readers
|
|
32
|
+
end
|
|
33
|
+
|
|
20
34
|
def attr_writer(*attr_names)
|
|
21
35
|
attr_names.each do |attr_name|
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
generated_attribute_methods.module_eval <<-ATTRWRITER, __FILE__, __LINE__ + 1
|
|
36
|
+
self.module_eval <<-ATTRWRITER, __FILE__, __LINE__ + 1
|
|
25
37
|
def #{attr_name}=(new_value)
|
|
26
|
-
@dirty = ModelNotSavedOrReset.new
|
|
27
|
-
#{attr_name}_will_change! unless new_value == attributes[:#{attr_name}]
|
|
28
38
|
attributes[:#{attr_name}] = new_value
|
|
29
39
|
end
|
|
30
40
|
ATTRWRITER
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
module MaglevRecord
|
|
2
|
+
module RootedEnumerable
|
|
3
|
+
module ClassMethods
|
|
4
|
+
def all
|
|
5
|
+
object_pool.values
|
|
6
|
+
end
|
|
7
|
+
def each
|
|
8
|
+
object_pool.each_value do |model|
|
|
9
|
+
yield model
|
|
10
|
+
end
|
|
11
|
+
end
|
|
12
|
+
def size
|
|
13
|
+
object_pool.size
|
|
14
|
+
end
|
|
15
|
+
def find_by_objectid(id)
|
|
16
|
+
if id.respond_to? :to_i
|
|
17
|
+
id = id.to_i
|
|
18
|
+
else
|
|
19
|
+
raise "#{id} do not respond to :to_i!"
|
|
20
|
+
end
|
|
21
|
+
object_pool[id]
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
end
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
module MaglevRecord
|
|
2
|
+
module RootedPersistence
|
|
3
|
+
extend MaglevSupport::Concern
|
|
4
|
+
|
|
5
|
+
module ClassMethods
|
|
6
|
+
def object_pool_key
|
|
7
|
+
self
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
def object_pool
|
|
11
|
+
Maglev::PERSISTENT_ROOT[MaglevRecord::PERSISTENT_ROOT_KEY] ||= {}
|
|
12
|
+
Maglev::PERSISTENT_ROOT[MaglevRecord::PERSISTENT_ROOT_KEY][object_pool_key] ||= {}
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def delete_object_pool
|
|
16
|
+
Maglev::PERSISTENT_ROOT[MaglevRecord::PERSISTENT_ROOT_KEY].delete(object_pool_key)
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def new(*args)
|
|
20
|
+
instance = super(*args)
|
|
21
|
+
self.object_pool[instance.id] = instance
|
|
22
|
+
instance
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def clear
|
|
26
|
+
self.object_pool.clear
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def create(*args)
|
|
30
|
+
instance = new(*args)
|
|
31
|
+
MaglevRecord.save
|
|
32
|
+
instance
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
end
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
module MaglevRecord
|
|
2
|
+
module Sensible
|
|
3
|
+
def sensibles
|
|
4
|
+
self.class.sensibles
|
|
5
|
+
end
|
|
6
|
+
|
|
7
|
+
def clear_sensibles
|
|
8
|
+
sensibles.each do |attribute|
|
|
9
|
+
send("#{attribute}=", nil)
|
|
10
|
+
end
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
module ClassMethods
|
|
14
|
+
def sensibles
|
|
15
|
+
@maglev_sensible_attributes ||= Array.new
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def mark_sensible(*attr_names)
|
|
19
|
+
@maglev_sensible_attributes ||= Array.new unless attr_names.empty?
|
|
20
|
+
attr_names.each do |attribute|
|
|
21
|
+
@maglev_sensible_attributes << attribute unless @maglev_sensible_attributes.include?(attribute)
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
end
|
|
28
|
+
end
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
|
|
2
|
+
module MaglevRecord
|
|
3
|
+
|
|
4
|
+
class ClassChange
|
|
5
|
+
def initialize(old, new)
|
|
6
|
+
@old = old
|
|
7
|
+
@new = new
|
|
8
|
+
end
|
|
9
|
+
def new_attr_accessors
|
|
10
|
+
@new.attr_readers - @old.attr_readers
|
|
11
|
+
end
|
|
12
|
+
def removed_attr_accessors
|
|
13
|
+
@old.attr_readers - @new.attr_readers
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
class Change
|
|
18
|
+
|
|
19
|
+
def initialize(old, new)
|
|
20
|
+
@old = old
|
|
21
|
+
@new = new
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def changed_classes
|
|
25
|
+
changes = []
|
|
26
|
+
@new.class_snapshots.each{ |new|
|
|
27
|
+
@old.class_snapshots.each { |old|
|
|
28
|
+
if old == new and
|
|
29
|
+
new.changed_since? old
|
|
30
|
+
changes << new.changes_since(old)
|
|
31
|
+
end
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
changes
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def removed_classes
|
|
38
|
+
@new.class_snapshots.select{ |new|
|
|
39
|
+
not new.exists? and @old.class_snapshots.all?{ |old|
|
|
40
|
+
old != new
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def new_classes
|
|
46
|
+
@new.class_snapshots.select{ |new|
|
|
47
|
+
new.exists? and @old.class_snapshots.all?{ |old|
|
|
48
|
+
old != new
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
end
|