maglevrecord 0.1.1 → 0.1.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (109) hide show
  1. data/.gitignore +7 -0
  2. data/.travis.yml +0 -1
  3. data/README.md +22 -7
  4. data/Rakefile +2 -2
  5. data/install.sh +0 -1
  6. data/lib/maglev_record/base.rb +10 -1
  7. data/lib/maglev_record/enumerable.rb +6 -1
  8. data/lib/maglev_record/errors.rb +3 -0
  9. data/lib/maglev_record/maglev_record.rb +1 -1
  10. data/lib/maglev_record/maglev_support/concern.rb +2 -3
  11. data/lib/maglev_record/maglev_support/secure_password.rb +2 -3
  12. data/lib/maglev_record/migration/loader.rb +1 -1
  13. data/lib/maglev_record/migration/migration.rb +14 -0
  14. data/lib/maglev_record/migration/migrator.rb +22 -4
  15. data/lib/maglev_record/migration/operations.rb +58 -0
  16. data/lib/maglev_record/persistence.rb +12 -5
  17. data/lib/maglev_record/read_write.rb +38 -7
  18. data/lib/maglev_record/rooted_enumerable.rb +6 -1
  19. data/lib/maglev_record/rooted_persistence.rb +4 -0
  20. data/lib/maglev_record/sensible.rb +2 -2
  21. data/lib/maglev_record/snapshot/change.rb +54 -15
  22. data/lib/maglev_record/snapshot/class_change.rb +66 -0
  23. data/lib/maglev_record/snapshot/class_snapshot.rb +63 -0
  24. data/lib/maglev_record/snapshot/snapshot.rb +44 -38
  25. data/lib/maglev_record/snapshot/snapshotable.rb +81 -26
  26. data/lib/maglev_record/snapshot/superclass_mismatch_change.rb +143 -0
  27. data/lib/maglev_record/snapshot.rb +3 -2
  28. data/lib/maglev_record/validations.rb +49 -0
  29. data/lib/maglev_record.rb +3 -2
  30. data/lib/tasks/database.rake +61 -17
  31. data/test/automigration/rake_task_base.rb +57 -0
  32. data/test/automigration/test_auto.slow.rb +38 -0
  33. data/test/automigration/test_migration_string.rb +173 -0
  34. data/test/automigration/test_rake_task_preconditions.slow.rb +41 -0
  35. data/test/automigration/test_show.slow.rb +21 -0
  36. data/test/automigration/test_superclass_mismatch.rb +96 -0
  37. data/test/example_model.rb +7 -5
  38. data/test/migration/base_lectures.rb +30 -0
  39. data/test/migration/operation_setup.rb +66 -56
  40. data/test/migration/projects/automigration/.gitignore +15 -0
  41. data/test/migration/projects/automigration/Gemfile +44 -0
  42. data/test/migration/projects/automigration/Gemfile.lock +115 -0
  43. data/test/migration/projects/automigration/README.rdoc +261 -0
  44. data/test/migration/projects/automigration/Rakefile +17 -0
  45. data/test/migration/projects/automigration/app/assets/images/rails.png +0 -0
  46. data/test/migration/projects/automigration/app/assets/javascripts/application.js +15 -0
  47. data/test/migration/projects/automigration/app/assets/stylesheets/application.css +13 -0
  48. data/test/migration/projects/automigration/app/controllers/application_controller.rb +3 -0
  49. data/test/migration/projects/automigration/app/helpers/application_helper.rb +2 -0
  50. data/test/migration/projects/automigration/app/mailers/.gitkeep +0 -0
  51. data/test/migration/projects/automigration/app/models/.gitkeep +0 -0
  52. data/test/migration/projects/automigration/app/models/project_model.rb +8 -0
  53. data/test/migration/projects/automigration/app/views/layouts/application.html.erb +14 -0
  54. data/test/migration/projects/automigration/config/application.rb +62 -0
  55. data/test/migration/projects/automigration/config/boot.rb +6 -0
  56. data/test/migration/projects/automigration/config/database.yml +25 -0
  57. data/test/migration/projects/automigration/config/environment.rb +5 -0
  58. data/test/migration/projects/automigration/config/environments/development.rb +37 -0
  59. data/test/migration/projects/automigration/config/environments/production.rb +67 -0
  60. data/test/migration/projects/automigration/config/initializers/backtrace_silencers.rb +7 -0
  61. data/test/migration/projects/automigration/config/initializers/inflections.rb +15 -0
  62. data/test/migration/projects/automigration/config/initializers/mime_types.rb +5 -0
  63. data/test/migration/projects/automigration/config/initializers/secret_token.rb +7 -0
  64. data/test/migration/projects/automigration/config/initializers/session_store.rb +8 -0
  65. data/test/migration/projects/automigration/config/initializers/wrap_parameters.rb +14 -0
  66. data/test/migration/projects/automigration/config/locales/en.yml +5 -0
  67. data/test/migration/projects/automigration/config/routes.rb +58 -0
  68. data/test/migration/projects/automigration/config.ru +4 -0
  69. data/test/migration/projects/automigration/db/seeds.rb +7 -0
  70. data/test/migration/projects/automigration/lib/assets/.gitkeep +0 -0
  71. data/test/migration/projects/automigration/lib/tasks/.gitkeep +0 -0
  72. data/test/migration/projects/automigration/log/.gitkeep +0 -0
  73. data/test/migration/projects/automigration/public/404.html +26 -0
  74. data/test/migration/projects/automigration/public/422.html +26 -0
  75. data/test/migration/projects/automigration/public/500.html +25 -0
  76. data/test/migration/projects/automigration/public/favicon.ico +0 -0
  77. data/test/migration/projects/automigration/public/index.html +241 -0
  78. data/test/migration/projects/automigration/public/robots.txt +5 -0
  79. data/test/migration/projects/automigration/script/rails +6 -0
  80. data/test/migration/projects/project2/Rakefile +2 -2
  81. data/test/migration/projects/project2/migrations/migration_2013-04-Apr-23_17.31.38.rb +1 -1
  82. data/test/migration/projects/project2/migrations/migration_2013-04-Apr-23_17.31.52.rb +1 -1
  83. data/test/migration/projects/project2/migrations/migration_2013-04-Apr-23_17.32.07.rb +1 -1
  84. data/test/migration/test_change_superclass.rb +111 -0
  85. data/test/migration/test_loader.rb +6 -0
  86. data/test/migration/test_project.rb +1 -29
  87. data/test/migration/test_project1.slow.rb +1 -0
  88. data/test/migration/test_project2.slow.rb +7 -0
  89. data/test/migration/test_remove.rb +123 -2
  90. data/test/migration/todo.txt +1 -1
  91. data/test/more_asserts.rb +1 -1
  92. data/test/snapshot/test_attributes.rb +33 -0
  93. data/test/snapshot/{test_snapshot_attributes.slow.rb → test_attributes.slow.rb} +3 -5
  94. data/test/snapshot/test_classes.rb +32 -0
  95. data/test/snapshot/{test_snapshot_classes.slow.rb → test_classes.slow.rb} +1 -1
  96. data/test/snapshot/test_from_file_system.rb +113 -0
  97. data/test/snapshot/test_methods.rb +136 -0
  98. data/test/snapshot/test_methods.slow.rb +67 -0
  99. data/test/snapshot/test_reset.rb +68 -0
  100. data/test/snapshot/test_snapshot.rb +41 -75
  101. data/test/snapshot/test_snapshot.slow.rb +98 -0
  102. data/test/snapshot/test_snapshotable.rb +0 -1
  103. data/test/temp_dir_test.rb +40 -0
  104. data/test/test_active_model_like_interface.rb +4 -1
  105. data/test/test_model_timestamps.rb +13 -5
  106. data/test/test_query_interface.rb +10 -0
  107. data/test/test_sensibles.rb +18 -10
  108. data/test/test_validation.rb +2 -12
  109. metadata +64 -5
data/.gitignore CHANGED
@@ -33,3 +33,10 @@ gem_*_code.log
33
33
 
34
34
  /caller_content.rb
35
35
  /module_content.rb
36
+
37
+ # ignore files for presentation no 8
38
+ /a.rb
39
+ /b.rb
40
+ /snapshot_test.rb
41
+ /test3.rb
42
+
data/.travis.yml CHANGED
@@ -10,7 +10,6 @@ before_script:
10
10
  - "gem uninstall -x -i /home/travis/.rvm/gems/ruby-1.9.3-p327@global bundler"
11
11
  - "maglev-gem install bundler"
12
12
  - "bundle install"
13
- - "gem install maglevrecord"
14
13
  script:
15
14
  - "bundle exec rake testslow"
16
15
  - "bundle exec rake testslow"
data/README.md CHANGED
@@ -1,13 +1,15 @@
1
- # MagLevRecord - a model interface for Rails
1
+ # MagLevRecord - a model interface for Rails
2
+ [![Build Status master](https://travis-ci.org/knub/maglevrecord.png?branch=master)](https://travis-ci.org/knub/maglevrecord)
3
+ [![Code Climate](https://codeclimate.com/github/knub/maglevrecord.png)](https://codeclimate.com/github/knub/maglevrecord)
2
4
 
3
- master [![Build Status master](https://travis-ci.org/knub/maglevrecord.png?branch=master)](https://travis-ci.org/knub/maglevrecord)
4
- develop [![Build Status develop](https://travis-ci.org/knub/maglevrecord.png?branch=develop)](https://travis-ci.org/knub/maglevrecord)
5
+ ```ActiveRecord``` is the ORM commonly used when working with Rails and relational databases.
6
+ When using MagLev, using an ORM is no longer necessary, **you can just work with your objects as they are**.
7
+ Still, ```ActiveRecord::Base``` offers some nice features for working with your models (*e.g.* like Validations).
5
8
 
6
- This is intended to replace ActiveRecord::Base when using MagLev. It is not ready for production use, but feel free to play around with it.
9
+ This gem offers all those features and bundles them in a comfortable way.
7
10
 
8
- ## Installation
11
+ ### Installation
9
12
 
10
- You can install this gem from rubygems.org:
11
13
  ```gem install maglevrecord```
12
14
 
13
15
 
@@ -15,7 +17,15 @@ You can install this gem from rubygems.org:
15
17
 
16
18
  ### Creating classes
17
19
 
18
- Classes which you wish to persist have to include ```MaglevRecord::Base``` or ```MaglevRecord::RootedBase```. You can still inherit from whatever base class you want.
20
+ Models, which you wish to persist, have to include ```MaglevRecord::Base``` or ```MaglevRecord::RootedBase```.
21
+ You can still inherit from whatever base class you want.
22
+
23
+ The difference between ```RootedBase``` and ```Base``` is, that ```RootedBase``` automatically stores the
24
+ instances in ```Maglev::PERSISTENT_ROOT``` ([persistence by reachability](http://maglevity.wordpress.com/2010/01/17/persistence-by-reachability/)).
25
+ The instances are stored in a hash, which contains all instances of the class.
26
+
27
+ When using ```Base``` you are responsible for making the object reachable from ```PERSISTENT_ROOT```.
28
+
19
29
 
20
30
  ```ruby
21
31
  require 'maglev_record'
@@ -83,6 +93,11 @@ Using ```book.valid?``` you can determine if a model is valid. Models are *not*
83
93
 
84
94
  Use ```MaglevRecord.reset``` the reset all changed made since the last save or reset.
85
95
 
96
+
97
+ ### Migrating objects
98
+
99
+ Have a look at the [demo application for migrations](https://github.com/niccokunzmann/maglevrecord-demo/blob/master/README.md).
100
+
86
101
  ## Further questions, improvements?
87
102
 
88
103
  Feel free to fork, pull-request or ask via email at bp2012h1 [at] hpi.uni-potsdam.de.
data/Rakefile CHANGED
@@ -6,14 +6,14 @@ $LOAD_PATH << './lib'
6
6
  Rake::TestTask.new do |t|
7
7
  t.libs << 'test'
8
8
  t.name = 'testfast'
9
- t.test_files = FileList['test/**/test*.rb'] - FileList['test/**/test*.slow.rb']
9
+ t.test_files = FileList['test/{,**/}test*.rb'] - FileList['test/{,**/}test*.slow.rb']
10
10
  t.ruby_opts << "-W0 -rubygems --stone test"
11
11
  end
12
12
 
13
13
  Rake::TestTask.new do |t|
14
14
  t.libs << 'test'
15
15
  t.name = 'testslow'
16
- t.test_files = FileList['test/**/test*.rb']
16
+ t.test_files = FileList['test/{,**/}test*.rb']
17
17
  t.ruby_opts << "-W0 -rubygems --stone test"
18
18
  end
19
19
 
data/install.sh CHANGED
@@ -121,7 +121,6 @@ case "$PLATFORM" in
121
121
  shmall="`sysctl kern.sysv.shmall | cut -f2 -d' '`"
122
122
  ;;
123
123
  SunOS-i86pc)
124
- # TODO: figure memory needs for Solaris-x86
125
124
  # Investigate project.max-shm-memory
126
125
  totalMemMB="`/usr/sbin/prtconf | grep Memory | cut -f3 -d' '`"
127
126
  totalMem=$(($totalMemMB * 1048576))
@@ -1,14 +1,23 @@
1
1
  module MaglevRecord
2
2
  module Base
3
3
  extend MaglevSupport::Concern
4
+ include MaglevRecord::Snapshotable
4
5
  include MaglevRecord::Integration
5
6
  include MaglevRecord::Persistence
6
7
  include MaglevRecord::ReadWrite
7
8
  include MaglevRecord::MigrationOperations
8
9
  redo_include ActiveModel::Conversion
10
+ redo_include MaglevRecord::Validations
11
+ #module ClassMethods
12
+ # def self.extended(base)
13
+ #base.class_methods_not_to_reset << "_validators"
14
+ #base.class_methods_not_to_reset << "_validators="
15
+ #base.class_methods_not_to_reset << "_validators?"
16
+ #base.class_methods_not_to_reset << "method_missing"
17
+ # end
18
+ #end
9
19
  include MaglevRecord::Sensible
10
20
  include ActiveModel::Conversion
11
21
  include MaglevRecord::SecurePassword
12
- include MaglevRecord::Snapshotable
13
22
  end
14
23
  end
@@ -1,6 +1,11 @@
1
1
  module MaglevRecord
2
2
  module Enumerable
3
3
  module ClassMethods
4
+ def first
5
+ each{ |model|
6
+ return model
7
+ }
8
+ end
4
9
  def all
5
10
  raise "method not available for MaglevRecord::Base"
6
11
  end
@@ -21,4 +26,4 @@ module MaglevRecord
21
26
  end
22
27
  end
23
28
  end
24
- end
29
+ end
@@ -7,4 +7,7 @@ module MaglevRecord
7
7
 
8
8
  class IrreversibleMigration < Error
9
9
  end
10
+
11
+ class NoMigrationReadError < Error
12
+ end
10
13
  end
@@ -1,5 +1,5 @@
1
1
  module MaglevRecord
2
- VERSION = '0.1.1'
2
+ VERSION = '0.1.2'
3
3
  PERSISTENT_ROOT_KEY = :MaglevRecord
4
4
 
5
5
  def self.save
@@ -4,16 +4,15 @@ module MaglevSupport
4
4
  self.included_modules.reverse.each do |mod|
5
5
  base.extend(mod::ClassMethods) if mod.constants.include? 'ClassMethods'
6
6
  end
7
+ base.extend(self::ClassMethods) if self.constants.include? 'ClassMethods'
7
8
  self.reinclude_store.each do |mod|
8
9
  base.__save_for_reinclude(mod)
9
10
  end unless self.reinclude_store.nil?
10
- base.extend(self::ClassMethods) if self.constants.include? 'ClassMethods'
11
11
  if base.is_a? Class
12
- # base.send :redo_include, ActiveModel::Validations
13
12
  base.send :redo_extend, Enumerable
14
13
  base.send :redo_extend, MaglevSupport.constantize("ActiveModel::Naming")
15
14
  end
16
- Maglev.commit_transaction
15
+ #Maglev.commit_transaction
17
16
  end
18
17
  end
19
18
  end
@@ -4,9 +4,8 @@ module MaglevRecord
4
4
  def has_secure_password
5
5
  attr_reader :password
6
6
 
7
- # TODO: No validations so far
8
- # validates_confirmation_of :password
9
- # validates_presence_of :password_digest
7
+ validates_confirmation_of :password
8
+ validates_presence_of :password_digest
10
9
 
11
10
  mark_sensible :password, :password_confirmation
12
11
 
@@ -19,7 +19,7 @@ module MaglevRecord
19
19
 
20
20
  def load_string(source, file = __FILE__)
21
21
  migration = instance_eval source, file
22
- # TODO: Check class of migration is really migration!
22
+ raise MaglevRecord::NoMigrationReadError unless migration.class == MaglevRecord::Migration
23
23
  migration.source = source
24
24
  @migration_list << migration
25
25
  end
@@ -138,5 +138,19 @@ MaglevRecord::Migration.new(Time.parse('#{
138
138
  end
139
139
  eos
140
140
  end
141
+
142
+ def self.write_to_file(folder, description,
143
+ upcode = nil, downcode = nil)
144
+ now = Time.now
145
+ filename = now.strftime("migration_%Y-%m-%b-%d_%H.%M.%S.rb")
146
+ # TODO the arguments point at this to need to be refactored to an own
147
+ # class
148
+ content = file_content(now, description, upcode, downcode)
149
+ filepath = File.join(folder, filename)
150
+ File.open(filepath, 'w') { |file|
151
+ file.write(content)
152
+ }
153
+ filepath
154
+ end
141
155
  end
142
156
  end
@@ -17,16 +17,28 @@ module MaglevRecord
17
17
  ##
18
18
  # Returns all migrations currently in the stone in the correct order.
19
19
  def migration_store
20
- Maglev::PERSISTENT_ROOT[MIGRATION_KEY] ||= SortedSet.new
20
+ Maglev::PERSISTENT_ROOT[MIGRATION_KEY] ||= []
21
+ end
22
+
23
+ def migrations_todo
24
+ @migration_list.reject do |mig|
25
+ migration_store.include?(mig.id)
26
+ end
27
+ end
28
+
29
+ def up?(logger = @non_displaying_logger)
30
+ # todo: test
31
+ migrations_todo.sort.each do |mig|
32
+ logger.info("to do: '" + mig.name + "' from " + mig.timestamp.to_s)
33
+ end
34
+ logger.info("Already applied all migrations.") if migrations_todo.empty?
21
35
  end
22
36
 
23
37
  ##
24
38
  # Applies the desired state of migrations.
25
39
  def up(logger = @non_displaying_logger)
26
40
  Maglev.abort_transaction
27
- to_do = @migration_list.reject do |mig|
28
- migration_store.include?(mig.id)
29
- end
41
+ to_do = migrations_todo
30
42
  logger.info("Already applied all migrations.") if to_do.empty?
31
43
  to_do.sort.each do |mig|
32
44
  mig.logger = logger
@@ -36,5 +48,11 @@ module MaglevRecord
36
48
  end
37
49
  Maglev.commit_transaction
38
50
  end
51
+
52
+ def self.for_directory(directory)
53
+ loader = MigrationLoader.new
54
+ loader.load_directory(directory)
55
+ new(loader.migration_list)
56
+ end
39
57
  end
40
58
  end
@@ -12,6 +12,7 @@ module MaglevRecord
12
12
  end
13
13
 
14
14
  def rename_attribute(old_name, new_name)
15
+ # TODO: test wether attribute is removed from attributes of class
15
16
  attr_accessor new_name
16
17
  each { |model|
17
18
  value = model.attributes[old_name]
@@ -30,10 +31,12 @@ module MaglevRecord
30
31
  end
31
32
 
32
33
  def delete_attribute(name)
34
+ # TODO: test attribute names for string and for symbol
33
35
  each { |model|
34
36
  value = model.attributes.delete(name)
35
37
  yield value if block_given?
36
38
  }
39
+ attributes.delete name.to_s if respond_to? :attributes
37
40
  end
38
41
 
39
42
  def migration_rename_to(new_name)
@@ -70,7 +73,46 @@ module MaglevRecord
70
73
  }
71
74
  list
72
75
  end
76
+
77
+ def remove_instance_method(name)
78
+ begin
79
+ remove_method name
80
+ rescue NameError
81
+ end
82
+ end
83
+
84
+ def remove_class_method(name)
85
+ begin
86
+ singleton_class.remove_method name
87
+ rescue NameError
88
+ end
89
+ end
90
+
91
+ def change_superclass_to(new_superclass)
92
+ # remove the superclass to enable to change the superclass
93
+ return unless new_superclass.is_able_to_become_superclass_of(self)
94
+ _name = name
95
+ Maglev.persistent do
96
+ maglev_redefine {
97
+ new_class = Object.module_eval "
98
+ class #{_name} < #{new_superclass.name}
99
+ self
100
+ end"
101
+ if respond_to? :object_pool_key
102
+ # where should I put it else?
103
+ raise "self should be object_pool_key" unless self == object_pool_key
104
+ pool_pool = Maglev::PERSISTENT_ROOT[MaglevRecord::PERSISTENT_ROOT_KEY]
105
+ unless pool_pool.nil?
106
+ #TODO: test no instances while superclass change
107
+ old_pool = pool_pool.delete(self) # TODO: test removed
108
+ pool_pool[new_class] = old_pool unless old_pool.nil?
109
+ end
110
+ end
111
+ }
112
+ end
113
+ end
73
114
  end
115
+
74
116
  class NullClass
75
117
  include ClassMethods
76
118
  def initialize(name)
@@ -86,7 +128,23 @@ module MaglevRecord
86
128
  end
87
129
  def migration_delete
88
130
  end
131
+ def remove_instance_method(name)
132
+ end
133
+ def remove_class_method(name)
134
+ end
135
+ def is_able_to_become_superclass_of(base_class)
136
+ false
137
+ end
138
+ def change_superclass_to(new_superclass)
139
+ end
89
140
  end
90
141
  end
91
142
  end
92
143
 
144
+ Maglev.persistent do
145
+ class Class
146
+ def is_able_to_become_superclass_of(base_class)
147
+ true
148
+ end
149
+ end
150
+ end
@@ -5,18 +5,25 @@ module MaglevRecord
5
5
  def initialize(*args)
6
6
  if args.size == 1
7
7
  args[0].each do |k, v|
8
- meth_name = "#{k.to_s}=".to_sym
8
+ meth_name = "#{k.to_s}=".to_sym
9
9
  self.send(meth_name, v) if self.respond_to? meth_name
10
10
  end
11
11
  end
12
- @created_at_timestamp = Time.now
12
+ created
13
13
  end
14
14
 
15
15
  def created_at
16
16
  @created_at_timestamp
17
17
  end
18
- def created_at=(timestamp)
19
- @created_at_timestamp = timestamp
18
+ def created
19
+ @created_at_timestamp = Time.now
20
+ end
21
+
22
+ def updated_at
23
+ @updated_at_timestamp
24
+ end
25
+ def updated
26
+ @updated_at_timestamp = Time.now
20
27
  end
21
28
 
22
29
  alias :persisted? :committed?
@@ -38,4 +45,4 @@ module MaglevRecord
38
45
  end
39
46
  end
40
47
  end
41
- end
48
+ end
@@ -4,7 +4,7 @@ module MaglevRecord
4
4
  extend MaglevSupport::Concern
5
5
 
6
6
  def attributes
7
- @maglev_attributes ||= Hash.new
7
+ @maglev_attributes ||= Hash.new
8
8
  end
9
9
 
10
10
  def update_attributes(attribute_hash)
@@ -16,9 +16,8 @@ module MaglevRecord
16
16
 
17
17
  module ClassMethods
18
18
  def attr_reader(*attr_names)
19
- @attr_readers ||= []
20
19
  attr_names.each do |attr_name|
21
- attr_readers << attr_name
20
+ attributes << attr_name.to_s
22
21
  self.module_eval <<-ATTRREADER, __FILE__, __LINE__ + 1
23
22
  def #{attr_name}
24
23
  attributes[:#{attr_name}]
@@ -27,24 +26,56 @@ module MaglevRecord
27
26
  end
28
27
  end
29
28
 
30
- def attr_readers
31
- @attr_readers
32
- end
33
-
34
29
  def attr_writer(*attr_names)
35
30
  attr_names.each do |attr_name|
31
+ attributes << attr_name.to_s
36
32
  self.module_eval <<-ATTRWRITER, __FILE__, __LINE__ + 1
37
33
  def #{attr_name}=(new_value)
34
+ updated
38
35
  attributes[:#{attr_name}] = new_value
39
36
  end
40
37
  ATTRWRITER
41
38
  end
42
39
  end
43
40
 
41
+
44
42
  def attr_accessor(*attr_names)
45
43
  attr_reader *attr_names
46
44
  attr_writer *attr_names
47
45
  end
46
+
47
+ #
48
+ # resets the class to no methods
49
+ # returns a memento proc that can be called to restore the old state
50
+ #
51
+ def attributes
52
+ @attributes ||= []
53
+ raise TypeError, "attributes contain bad elements #{@attributes}" unless @attributes.all?{ |attribute| attribute.is_a? String }
54
+ @attributes.sort!
55
+ @attributes.uniq!
56
+ @attributes
57
+ end
58
+
59
+ def reset
60
+ _attributes = attributes_to_reset.map{ |attribute|
61
+ attributes.delete attribute
62
+ }
63
+ reset_proc = super if defined?(super)
64
+ return Proc.new {
65
+ reset_proc.call unless reset_proc.nil?
66
+ _attributes.each{ |attribute| attributes << attribute}
67
+ attributes
68
+ self
69
+ }
70
+ end
71
+
72
+ def snapshot_attributes
73
+ attributes.reject{|attribute| attribute.include? 'valid' }
74
+ end
75
+
76
+ def attributes_to_reset
77
+ snapshot_attributes
78
+ end
48
79
  end
49
80
  end
50
81
  end
@@ -1,6 +1,11 @@
1
1
  module MaglevRecord
2
2
  module RootedEnumerable
3
3
  module ClassMethods
4
+ def first
5
+ each{ |model|
6
+ return model
7
+ }
8
+ end
4
9
  def all
5
10
  object_pool.values
6
11
  end
@@ -22,4 +27,4 @@ module MaglevRecord
22
27
  end
23
28
  end
24
29
  end
25
- end
30
+ end
@@ -2,6 +2,9 @@ module MaglevRecord
2
2
  module RootedPersistence
3
3
  extend MaglevSupport::Concern
4
4
 
5
+ def destroy
6
+ self.class.object_pool.delete(self.id)
7
+ end
5
8
  module ClassMethods
6
9
  def object_pool_key
7
10
  self
@@ -17,6 +20,7 @@ module MaglevRecord
17
20
  end
18
21
 
19
22
  def new(*args)
23
+ create_validations
20
24
  instance = super(*args)
21
25
  self.object_pool[instance.id] = instance
22
26
  instance
@@ -5,7 +5,7 @@ module MaglevRecord
5
5
  end
6
6
 
7
7
  def clear_sensibles
8
- sensibles.each do |attribute|
8
+ sensibles.each do |attribute|
9
9
  send("#{attribute}=", nil)
10
10
  end
11
11
  end
@@ -17,7 +17,7 @@ module MaglevRecord
17
17
 
18
18
  def mark_sensible(*attr_names)
19
19
  @maglev_sensible_attributes ||= Array.new unless attr_names.empty?
20
- attr_names.each do |attribute|
20
+ attr_names.each do |attribute|
21
21
  @maglev_sensible_attributes << attribute unless @maglev_sensible_attributes.include?(attribute)
22
22
  end
23
23
  end
@@ -1,19 +1,9 @@
1
1
 
2
2
  module MaglevRecord
3
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
-
4
+ #
5
+ # The Change between two snapshots of the image
6
+ #
17
7
  class Change
18
8
 
19
9
  def initialize(old, new)
@@ -34,14 +24,22 @@ module MaglevRecord
34
24
  changes
35
25
  end
36
26
 
27
+ def changed_class_names
28
+ changed_classes.map(&:class_name).sort
29
+ end
30
+
37
31
  def removed_classes
38
- @new.class_snapshots.select{ |new|
39
- not new.exists? and @old.class_snapshots.all?{ |old|
32
+ @old.class_snapshots.select{ |old|
33
+ old.exists? and @new.class_snapshots.all?{ |new|
40
34
  old != new
41
35
  }
42
36
  }
43
37
  end
44
38
 
39
+ def removed_class_names
40
+ removed_classes.map(&:class_name).sort
41
+ end
42
+
45
43
  def new_classes
46
44
  @new.class_snapshots.select{ |new|
47
45
  new.exists? and @old.class_snapshots.all?{ |old|
@@ -49,5 +47,46 @@ module MaglevRecord
49
47
  }
50
48
  }
51
49
  end
50
+
51
+ def new_class_names
52
+ new_classes.map(&:class_name).sort
53
+ end
54
+
55
+ def migration_string_list
56
+ if removed_classes.size == 1 and new_classes.size == 1
57
+ ["rename_class #{removed_class_names.first
58
+ }, :#{new_class_names.first}"]
59
+ else
60
+ removed_class_names.map{ |class_name|
61
+ "delete_class #{class_name}"
62
+ } + new_class_names.map{ |class_name|
63
+ "#new class: #{class_name}"
64
+ }
65
+ end + changed_classes.map(&:migration_string_list).flatten
66
+ end
67
+
68
+ def migration_string(identation = 0)
69
+ " " * identation + migration_string_list.select{ |s| s.strip != ""}.join("\n" + " " * identation)
70
+ end
71
+
72
+ def nothing_changed?
73
+ removed_classes == [] and changed_classes == [] and new_classes == []
74
+ end
75
+
76
+ def [](class_or_class_name)
77
+ changed_classes.each{ |change|
78
+ return change if change.changed_class == class_or_class_name or
79
+ change.class_name == class_or_class_name
80
+ }
81
+ end
82
+
83
+ def superclass_mismatch_classes
84
+ []
85
+ end
86
+
87
+ def reversed
88
+ self.class.new(@new, @old)
89
+ end
90
+
52
91
  end
53
92
  end