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.
Files changed (161) hide show
  1. data/.gitignore +35 -0
  2. data/.travis.yml +19 -0
  3. data/Gemfile +13 -0
  4. data/README.md +89 -0
  5. data/Rakefile +22 -0
  6. data/install.sh +238 -0
  7. data/lib/maglev_record/base.rb +10 -62
  8. data/lib/maglev_record/enumerable.rb +13 -24
  9. data/lib/maglev_record/errors.rb +10 -0
  10. data/lib/maglev_record/integration.rb +7 -0
  11. data/lib/maglev_record/maglev_record.rb +13 -0
  12. data/lib/maglev_record/maglev_support/active_support_patch.rb +31 -0
  13. data/lib/maglev_record/maglev_support/concern.rb +19 -0
  14. data/lib/maglev_record/maglev_support/maglev_support.rb +15 -0
  15. data/lib/maglev_record/maglev_support/secure_password.rb +39 -0
  16. data/lib/maglev_record/migration/loader.rb +38 -0
  17. data/lib/maglev_record/migration/migration.rb +142 -0
  18. data/lib/maglev_record/migration/migrator.rb +40 -0
  19. data/lib/maglev_record/migration/operations.rb +92 -0
  20. data/lib/maglev_record/migration.rb +5 -0
  21. data/lib/maglev_record/persistence.rb +23 -48
  22. data/lib/maglev_record/raketasks.rb +9 -0
  23. data/lib/maglev_record/read_write.rb +20 -10
  24. data/lib/maglev_record/rooted_base.rb +8 -0
  25. data/lib/maglev_record/rooted_enumerable.rb +25 -0
  26. data/lib/maglev_record/rooted_persistence.rb +36 -0
  27. data/lib/maglev_record/sensible.rb +28 -0
  28. data/lib/maglev_record/snapshot/change.rb +53 -0
  29. data/lib/maglev_record/snapshot/snapshot.rb +67 -0
  30. data/lib/maglev_record/snapshot/snapshotable.rb +58 -0
  31. data/lib/maglev_record/snapshot.rb +5 -0
  32. data/lib/maglev_record/tools/object_reference.rb +189 -0
  33. data/lib/maglev_record/tools/submodule_finder.rb +42 -0
  34. data/lib/maglev_record/tools.rb +2 -0
  35. data/lib/maglev_record.rb +60 -4
  36. data/lib/tasks/database.rake +38 -0
  37. data/maglevrecord.gemspec +17 -0
  38. data/rails/init.rb +1 -0
  39. data/test/_test_object_reference.rb +70 -0
  40. data/test/example_model.rb +26 -13
  41. data/test/migration/dummy_migrations/migration_1.rb +8 -0
  42. data/test/migration/dummy_migrations/migration_2.rb +8 -0
  43. data/test/migration/dummy_migrations/migration_3.rb +8 -0
  44. data/test/migration/migrations/migration_1.rb +12 -0
  45. data/test/migration/migrations/migration_2.rb +12 -0
  46. data/test/migration/migrations/migration_3.rb +12 -0
  47. data/test/migration/operation_setup.rb +91 -0
  48. data/test/migration/projects/project1/.gitignore +15 -0
  49. data/test/migration/projects/project1/Gemfile +43 -0
  50. data/test/migration/projects/project1/Gemfile.lock +114 -0
  51. data/test/migration/projects/project1/README.rdoc +261 -0
  52. data/test/migration/projects/project1/Rakefile +17 -0
  53. data/test/migration/projects/project1/app/assets/images/rails.png +0 -0
  54. data/test/migration/projects/project1/app/assets/javascripts/application.js +15 -0
  55. data/test/migration/projects/project1/app/assets/stylesheets/application.css +13 -0
  56. data/test/migration/projects/project1/app/controllers/application_controller.rb +3 -0
  57. data/test/migration/projects/project1/app/helpers/application_helper.rb +2 -0
  58. data/test/migration/projects/project1/app/mailers/.gitkeep +0 -0
  59. data/test/migration/projects/project1/app/models/.gitkeep +0 -0
  60. data/test/migration/projects/project1/app/views/layouts/application.html.erb +14 -0
  61. data/test/migration/projects/project1/config/application.rb +62 -0
  62. data/test/migration/projects/project1/config/boot.rb +6 -0
  63. data/test/migration/projects/project1/config/database.yml +25 -0
  64. data/test/migration/projects/project1/config/environment.rb +5 -0
  65. data/test/migration/projects/project1/config/environments/development.rb +37 -0
  66. data/test/migration/projects/project1/config/environments/production.rb +67 -0
  67. data/test/migration/projects/project1/config/initializers/backtrace_silencers.rb +7 -0
  68. data/test/migration/projects/project1/config/initializers/inflections.rb +15 -0
  69. data/test/migration/projects/project1/config/initializers/mime_types.rb +5 -0
  70. data/test/migration/projects/project1/config/initializers/secret_token.rb +7 -0
  71. data/test/migration/projects/project1/config/initializers/session_store.rb +8 -0
  72. data/test/migration/projects/project1/config/initializers/wrap_parameters.rb +14 -0
  73. data/test/migration/projects/project1/config/locales/en.yml +5 -0
  74. data/test/migration/projects/project1/config/routes.rb +58 -0
  75. data/test/migration/projects/project1/config.ru +4 -0
  76. data/test/migration/projects/project1/db/seeds.rb +7 -0
  77. data/test/migration/projects/project1/lib/assets/.gitkeep +0 -0
  78. data/test/migration/projects/project1/lib/tasks/.gitkeep +0 -0
  79. data/test/migration/projects/project1/log/.gitkeep +0 -0
  80. data/test/migration/projects/project1/public/404.html +26 -0
  81. data/test/migration/projects/project1/public/422.html +26 -0
  82. data/test/migration/projects/project1/public/500.html +25 -0
  83. data/test/migration/projects/project1/public/favicon.ico +0 -0
  84. data/test/migration/projects/project1/public/index.html +241 -0
  85. data/test/migration/projects/project1/public/robots.txt +5 -0
  86. data/test/migration/projects/project1/script/rails +6 -0
  87. data/test/migration/projects/project2/.gitignore +15 -0
  88. data/test/migration/projects/project2/Gemfile +44 -0
  89. data/test/migration/projects/project2/Gemfile.lock +115 -0
  90. data/test/migration/projects/project2/README.rdoc +261 -0
  91. data/test/migration/projects/project2/Rakefile +17 -0
  92. data/test/migration/projects/project2/app/assets/images/rails.png +0 -0
  93. data/test/migration/projects/project2/app/assets/javascripts/application.js +15 -0
  94. data/test/migration/projects/project2/app/assets/stylesheets/application.css +13 -0
  95. data/test/migration/projects/project2/app/controllers/application_controller.rb +3 -0
  96. data/test/migration/projects/project2/app/helpers/application_helper.rb +2 -0
  97. data/test/migration/projects/project2/app/mailers/.gitkeep +0 -0
  98. data/test/migration/projects/project2/app/models/.gitkeep +0 -0
  99. data/test/migration/projects/project2/app/models/project_model.rb +8 -0
  100. data/test/migration/projects/project2/app/views/layouts/application.html.erb +14 -0
  101. data/test/migration/projects/project2/config/application.rb +62 -0
  102. data/test/migration/projects/project2/config/boot.rb +6 -0
  103. data/test/migration/projects/project2/config/database.yml +25 -0
  104. data/test/migration/projects/project2/config/environment.rb +5 -0
  105. data/test/migration/projects/project2/config/environments/development.rb +37 -0
  106. data/test/migration/projects/project2/config/environments/production.rb +67 -0
  107. data/test/migration/projects/project2/config/initializers/backtrace_silencers.rb +7 -0
  108. data/test/migration/projects/project2/config/initializers/inflections.rb +15 -0
  109. data/test/migration/projects/project2/config/initializers/mime_types.rb +5 -0
  110. data/test/migration/projects/project2/config/initializers/secret_token.rb +7 -0
  111. data/test/migration/projects/project2/config/initializers/session_store.rb +8 -0
  112. data/test/migration/projects/project2/config/initializers/wrap_parameters.rb +14 -0
  113. data/test/migration/projects/project2/config/locales/en.yml +5 -0
  114. data/test/migration/projects/project2/config/routes.rb +58 -0
  115. data/test/migration/projects/project2/config.ru +4 -0
  116. data/test/migration/projects/project2/db/seeds.rb +7 -0
  117. data/test/migration/projects/project2/lib/assets/.gitkeep +0 -0
  118. data/test/migration/projects/project2/lib/tasks/.gitkeep +0 -0
  119. data/test/migration/projects/project2/log/.gitkeep +0 -0
  120. data/test/migration/projects/project2/migrations/migration_2013-04-Apr-23_17.31.38.rb +16 -0
  121. data/test/migration/projects/project2/migrations/migration_2013-04-Apr-23_17.31.52.rb +16 -0
  122. data/test/migration/projects/project2/migrations/migration_2013-04-Apr-23_17.32.07.rb +16 -0
  123. data/test/migration/projects/project2/public/404.html +26 -0
  124. data/test/migration/projects/project2/public/422.html +26 -0
  125. data/test/migration/projects/project2/public/500.html +25 -0
  126. data/test/migration/projects/project2/public/favicon.ico +0 -0
  127. data/test/migration/projects/project2/public/index.html +241 -0
  128. data/test/migration/projects/project2/public/robots.txt +5 -0
  129. data/test/migration/projects/project2/script/rails +6 -0
  130. data/test/migration/test_loader.rb +78 -0
  131. data/test/migration/test_migration.rb +127 -0
  132. data/test/migration/test_nonexistent_classes.rb +120 -0
  133. data/test/migration/test_operations_fit_together.rb +68 -0
  134. data/test/migration/test_project.rb +105 -0
  135. data/test/migration/test_project1.slow.rb +58 -0
  136. data/test/migration/test_project2.slow.rb +34 -0
  137. data/test/migration/test_remove.rb +219 -0
  138. data/test/migration/test_rename.rb +238 -0
  139. data/test/migration/test_scenario.rb +37 -0
  140. data/test/migration/todo.txt +17 -0
  141. data/test/more_asserts.rb +63 -0
  142. data/test/snapshot/test_snapshot.rb +99 -0
  143. data/test/snapshot/test_snapshot_attributes.slow.rb +57 -0
  144. data/test/snapshot/test_snapshot_classes.slow.rb +32 -0
  145. data/test/snapshot/test_snapshotable.rb +53 -0
  146. data/test/test_accessors.rb +46 -0
  147. data/test/test_active_model_like_interface.rb +1 -1
  148. data/test/test_model_timestamps.rb +22 -0
  149. data/test/test_more_asserts.rb +68 -0
  150. data/test/test_naming.rb +22 -0
  151. data/test/test_persistence.rb +127 -0
  152. data/test/test_query_interface.rb +20 -25
  153. data/test/test_sensibles.rb +38 -0
  154. data/test/test_validation.rb +20 -57
  155. data/todo.txt +1 -0
  156. data/update.sh +180 -0
  157. metadata +156 -12
  158. data/lib/maglev_record/model_not_saved_or_reset.rb +0 -15
  159. data/test/test_dirty_object.rb +0 -50
  160. data/test/test_maglev_record_base.rb +0 -22
  161. 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
+
@@ -0,0 +1,5 @@
1
+ require "maglev_record/migration/migration"
2
+ require "maglev_record/migration/loader"
3
+ require "maglev_record/migration/migrator"
4
+ require "maglev_record/migration/operations"
5
+
@@ -1,66 +1,41 @@
1
- require "active_support"
2
-
3
1
  module MaglevRecord
4
2
  module Persistence
5
- extend ActiveSupport::Concern
6
-
7
- def delete
8
- self.class.delete(self)
9
- end
3
+ extend MaglevSupport::Concern
10
4
 
11
- def save!(options = {})
12
- if options[:validate] == false or self.valid?
13
- @previously_changed = changes
14
- @changed_attributes.clear
15
- self.instance_variable_set(:@errors, nil)
16
- self.class.object_pool[self.object_id] = self
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 save(options = {})
25
- begin
26
- self.save!(options)
27
- rescue StandardError
28
- false
29
- end
15
+ def created_at
16
+ @created_at_timestamp
30
17
  end
31
-
32
- def persisted?
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
- !committed?
24
+ !persisted?
38
25
  end
39
26
 
40
- module ClassMethods
41
- def object_pool
42
- Maglev::PERSISTENT_ROOT[self.name.to_sym] ||= {}
43
- end
27
+ def id
28
+ object_id
29
+ end
44
30
 
45
- def delete(*args)
46
- if block_given? and args.size == 0
47
- self.all.each do |m|
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 clear
60
- self.object_pool.clear
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
@@ -0,0 +1,9 @@
1
+ require 'maglev_record/migration'
2
+ require 'rails'
3
+
4
+ class Railtie < Rails::Railtie
5
+ railtie_name :maglev_record
6
+ rake_tasks do
7
+ load "tasks/database.rake"
8
+ end
9
+ end
@@ -1,15 +1,25 @@
1
- require "active_support"
2
1
 
3
2
  module MaglevRecord
4
3
  module ReadWrite
5
- extend ActiveSupport::Concern
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
- define_attribute_method(attr_name)
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
- define_attribute_method(attr_name)
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,8 @@
1
+ module MaglevRecord
2
+ module RootedBase
3
+ extend MaglevSupport::Concern
4
+ include MaglevRecord::Base
5
+ include MaglevRecord::RootedPersistence
6
+ include MaglevRecord::RootedEnumerable
7
+ end
8
+ end
@@ -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