mongoid_migration 0.0.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 (51) hide show
  1. data/MIT-LICENSE +20 -0
  2. data/README.rdoc +26 -0
  3. data/Rakefile +37 -0
  4. data/lib/generators/mongoid_migration/USAGE +8 -0
  5. data/lib/generators/mongoid_migration/migration.rb +11 -0
  6. data/lib/generators/mongoid_migration/mongoid_migration_generator.rb +14 -0
  7. data/lib/generators/mongoid_migration/templates/mongoid_migration.rb +9 -0
  8. data/lib/mongoid_migration.rb +1 -0
  9. data/lib/mongoid_migration/migration.rb +332 -0
  10. data/lib/mongoid_migration/version.rb +3 -0
  11. data/lib/tasks/mongoid_migration_tasks.rake +68 -0
  12. data/test/dummy/Rakefile +7 -0
  13. data/test/dummy/app/assets/javascripts/application.js +7 -0
  14. data/test/dummy/app/assets/stylesheets/application.css +7 -0
  15. data/test/dummy/app/controllers/application_controller.rb +3 -0
  16. data/test/dummy/app/helpers/application_helper.rb +2 -0
  17. data/test/dummy/app/models/product.rb +5 -0
  18. data/test/dummy/app/views/layouts/application.html.erb +14 -0
  19. data/test/dummy/config.ru +4 -0
  20. data/test/dummy/config/application.rb +51 -0
  21. data/test/dummy/config/boot.rb +10 -0
  22. data/test/dummy/config/environment.rb +5 -0
  23. data/test/dummy/config/environments/development.rb +30 -0
  24. data/test/dummy/config/environments/production.rb +60 -0
  25. data/test/dummy/config/environments/test.rb +39 -0
  26. data/test/dummy/config/initializers/backtrace_silencers.rb +7 -0
  27. data/test/dummy/config/initializers/inflections.rb +10 -0
  28. data/test/dummy/config/initializers/mime_types.rb +5 -0
  29. data/test/dummy/config/initializers/secret_token.rb +7 -0
  30. data/test/dummy/config/initializers/session_store.rb +8 -0
  31. data/test/dummy/config/initializers/wrap_parameters.rb +10 -0
  32. data/test/dummy/config/locales/en.yml +5 -0
  33. data/test/dummy/config/mongoid.yml +20 -0
  34. data/test/dummy/config/routes.rb +58 -0
  35. data/test/dummy/lib/generators/mongoid_migration/USAGE +8 -0
  36. data/test/dummy/lib/generators/mongoid_migration/migration.rb +11 -0
  37. data/test/dummy/lib/generators/mongoid_migration/mongoid_migration_generator.rb +14 -0
  38. data/test/dummy/lib/generators/mongoid_migration/templates/mongoid_migration.rb +9 -0
  39. data/test/dummy/lib/tasks/mongoid_migration_tasks.rake +68 -0
  40. data/test/dummy/log/development.log +233 -0
  41. data/test/dummy/log/test.log +4 -0
  42. data/test/dummy/mongodb/migrate/20111114234935_yo.rb +9 -0
  43. data/test/dummy/mongodb/migrate/20111116172729_foo.rb +9 -0
  44. data/test/dummy/public/404.html +26 -0
  45. data/test/dummy/public/422.html +26 -0
  46. data/test/dummy/public/500.html +26 -0
  47. data/test/dummy/public/favicon.ico +0 -0
  48. data/test/dummy/script/rails +6 -0
  49. data/test/mongoid_migration_test.rb +7 -0
  50. data/test/test_helper.rb +10 -0
  51. metadata +184 -0
data/MIT-LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright 2011 YOURNAME
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.rdoc ADDED
@@ -0,0 +1,26 @@
1
+ = MongoidMigration
2
+
3
+ ActiveRecord::Migration ported to Mongoid to run incremental/reversible migrations to manipulate/populate data in the database.
4
+
5
+ == Rails Generator
6
+
7
+ Run the generator to generate timestamped migration files:
8
+
9
+ rails generate mongoid_migration Thing
10
+
11
+ which generates a timestamped migration file: mongodb/migrate/20111114234935_thing.rb similar to AR.
12
+
13
+ The migration has 2 class methods defined, self.up - executed when migrating up and self.down - executed when migrating down.
14
+ Obviously ActiveRecord::ConnectionAdapters::SchemaStatements methods have not been ported over, so don't attempt to use them. (they make no sense for mongodb anyways)
15
+
16
+ == The following rake tasks are available:
17
+
18
+ * rake db:mongoid:migrate # Migrate the database (options: VERSION=x, VERBOSE=false).
19
+ * rake db:mongoid:migration:down # Runs the "down" for a given migration VERSION.
20
+ * rake db:mongoid:migration:status # Display status of migrations
21
+ * rake db:mongoid:migration:up # Runs the "up" for a given migration VERSION.
22
+ * rake db:mongoid:rollback # Rolls migrations back to the previous version (specify steps w/ STEP=n).
23
+ * rake db:mongoid:version # Retrieves the current schema version number
24
+
25
+ This is the fork of ActiveRecord::Migrations ported to work with mongoid.
26
+ All thanks should go to the Rails team.
data/Rakefile ADDED
@@ -0,0 +1,37 @@
1
+ #!/usr/bin/env rake
2
+ begin
3
+ require 'bundler/setup'
4
+ rescue LoadError
5
+ puts 'You must `gem install bundler` and `bundle install` to run rake tasks'
6
+ end
7
+ begin
8
+ require 'rdoc/task'
9
+ rescue LoadError
10
+ require 'rdoc/rdoc'
11
+ require 'rake/rdoctask'
12
+ RDoc::Task = Rake::RDocTask
13
+ end
14
+
15
+ RDoc::Task.new(:rdoc) do |rdoc|
16
+ rdoc.rdoc_dir = 'rdoc'
17
+ rdoc.title = 'MongoidMigration'
18
+ rdoc.options << '--line-numbers'
19
+ rdoc.rdoc_files.include('README.rdoc')
20
+ rdoc.rdoc_files.include('lib/**/*.rb')
21
+ end
22
+
23
+
24
+
25
+ Bundler::GemHelper.install_tasks
26
+
27
+ require 'rake/testtask'
28
+
29
+ Rake::TestTask.new(:test) do |t|
30
+ t.libs << 'lib'
31
+ t.libs << 'test'
32
+ t.pattern = 'test/**/*_test.rb'
33
+ t.verbose = false
34
+ end
35
+
36
+
37
+ task :default => :test
@@ -0,0 +1,8 @@
1
+ Description:
2
+ Mongoid migration generator generates 'migration' files for mongoid.
3
+
4
+ Example:
5
+ rails generate mongoid_migration Thing
6
+
7
+ This will create migration file:
8
+ mongodb/migrate/20111114234935_thing.rb
@@ -0,0 +1,11 @@
1
+ module MongoidMigration
2
+ module Generators
3
+ module Migration
4
+ # Implement the required interface for Rails::Generators::Migration.
5
+ def next_migration_number(dirname) #:nodoc:
6
+ next_migration_number = current_migration_number(dirname) + 1
7
+ [Time.now.utc.strftime("%Y%m%d%H%M%S"), "%.14d" % next_migration_number].max
8
+ end
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,14 @@
1
+ require 'generators/mongoid_migration/migration'
2
+
3
+ class MongoidMigrationGenerator < Rails::Generators::NamedBase
4
+
5
+ include Rails::Generators::Migration
6
+ extend MongoidMigration::Generators::Migration
7
+
8
+ source_root File.expand_path("../templates", __FILE__)
9
+
10
+ def create_migration_file
11
+ migration_template "mongoid_migration.rb", "mongodb/migrate/#{file_name}.rb"
12
+ end
13
+
14
+ end
@@ -0,0 +1,9 @@
1
+ class <%= migration_class_name %> < MongoidMigration::Migration
2
+ def self.up
3
+
4
+ end
5
+
6
+ def self.down
7
+
8
+ end
9
+ end
@@ -0,0 +1 @@
1
+ require 'mongoid_migration/migration'
@@ -0,0 +1,332 @@
1
+ require 'active_support/core_ext/kernel/singleton_class'
2
+ require 'active_support/core_ext/module/aliasing'
3
+ require 'active_support/core_ext/module/delegation'
4
+ require 'mongoid'
5
+
6
+ module MongoidMigration
7
+ class MongoidMigrationError < StandardError
8
+ end
9
+
10
+ # Exception that can be raised to stop migrations from going backwards.
11
+ class IrreversibleMigration < MongoidMigrationError
12
+ end
13
+
14
+ class DuplicateMigrationVersionError < MongoidMigrationError#:nodoc:
15
+ def initialize(version)
16
+ super("Multiple migrations have the version number #{version}")
17
+ end
18
+ end
19
+
20
+ class DuplicateMigrationNameError < MongoidMigrationError#:nodoc:
21
+ def initialize(name)
22
+ super("Multiple migrations have the name #{name}")
23
+ end
24
+ end
25
+
26
+ class UnknownMigrationVersionError < MongoidMigrationError #:nodoc:
27
+ def initialize(version)
28
+ super("No migration with version number #{version}")
29
+ end
30
+ end
31
+
32
+ class IllegalMigrationNameError < MongoidMigrationError#:nodoc:
33
+ def initialize(name)
34
+ super("Illegal name for migration file: #{name}\n\t(only lower case letters, numbers, and '_' allowed)")
35
+ end
36
+ end
37
+
38
+ # MigrationProxy is used to defer loading of the actual migration classes
39
+ # until they are needed
40
+ class MigrationProxy
41
+
42
+ attr_accessor :name, :version, :filename
43
+
44
+ delegate :migrate, :announce, :write, :to=>:migration
45
+
46
+ private
47
+
48
+ def migration
49
+ @migration ||= load_migration
50
+ end
51
+
52
+ def load_migration
53
+ require(File.expand_path(filename))
54
+ name.constantize
55
+ end
56
+ end
57
+
58
+ class Migration
59
+
60
+ include Mongoid::Document
61
+ include Mongoid::Timestamps
62
+
63
+ validates_presence_of :version
64
+ validates_uniqueness_of :version
65
+
66
+ field :version
67
+
68
+ index :version
69
+
70
+ @@verbose = true
71
+ cattr_accessor :verbose
72
+
73
+ class << self
74
+ def up_with_benchmarks #:nodoc:
75
+ migrate(:up)
76
+ end
77
+
78
+ def down_with_benchmarks #:nodoc:
79
+ migrate(:down)
80
+ end
81
+
82
+ # Execute this migration in the named direction
83
+ def migrate(direction)
84
+ return unless respond_to?(direction)
85
+
86
+ case direction
87
+ when :up then announce "migrating"
88
+ when :down then announce "reverting"
89
+ end
90
+
91
+ result = nil
92
+ time = Benchmark.measure { result = send("#{direction}_without_benchmarks") }
93
+
94
+ case direction
95
+ when :up then announce "migrated (%.4fs)" % time.real; write
96
+ when :down then announce "reverted (%.4fs)" % time.real; write
97
+ end
98
+
99
+ result
100
+ end
101
+
102
+ # Because the method added may do an alias_method, it can be invoked
103
+ # recursively. We use @ignore_new_methods as a guard to indicate whether
104
+ # it is safe for the call to proceed.
105
+ def singleton_method_added(sym) #:nodoc:
106
+ return if defined?(@ignore_new_methods) && @ignore_new_methods
107
+
108
+ begin
109
+ @ignore_new_methods = true
110
+
111
+ case sym
112
+ when :up, :down
113
+ singleton_class.send(:alias_method_chain, sym, "benchmarks")
114
+ end
115
+ ensure
116
+ @ignore_new_methods = false
117
+ end
118
+ end
119
+
120
+ def write(text="")
121
+ puts(text) if verbose
122
+ end
123
+
124
+ def announce(message)
125
+ version = defined?(@version) ? @version : nil
126
+
127
+ text = "#{version} #{name}: #{message}"
128
+ length = [0, 75 - text.length].max
129
+ write "== %s %s" % [text, "=" * length]
130
+ end
131
+
132
+ def say(message, subitem=false)
133
+ write "#{subitem ? " ->" : "--"} #{message}"
134
+ end
135
+
136
+ def say_with_time(message)
137
+ say(message)
138
+ result = nil
139
+ time = Benchmark.measure { result = yield }
140
+ say "%.4fs" % time.real, :subitem
141
+ say("#{result} rows", :subitem) if result.is_a?(Integer)
142
+ result
143
+ end
144
+
145
+ def suppress_messages
146
+ save, self.verbose = verbose, false
147
+ yield
148
+ ensure
149
+ self.verbose = save
150
+ end
151
+ end
152
+
153
+ end
154
+
155
+ class Migrator#:nodoc:
156
+ class << self
157
+ def migrate(migrations_path, target_version = nil)
158
+ case
159
+ when target_version.nil?
160
+ up(migrations_path, target_version)
161
+ when current_version == 0 && target_version == 0
162
+ when current_version > target_version
163
+ down(migrations_path, target_version)
164
+ else
165
+ up(migrations_path, target_version)
166
+ end
167
+ end
168
+
169
+ def rollback(migrations_path, steps=1)
170
+ move(:down, migrations_path, steps)
171
+ end
172
+
173
+ def forward(migrations_path, steps=1)
174
+ move(:up, migrations_path, steps)
175
+ end
176
+
177
+ def up(migrations_path, target_version = nil)
178
+ self.new(:up, migrations_path, target_version).migrate
179
+ end
180
+
181
+ def down(migrations_path, target_version = nil)
182
+ self.new(:down, migrations_path, target_version).migrate
183
+ end
184
+
185
+ def run(direction, migrations_path, target_version)
186
+ self.new(direction, migrations_path, target_version).run
187
+ end
188
+
189
+ def migrations_path
190
+ 'mongodb/migrate'
191
+ end
192
+
193
+ def get_all_versions
194
+ Migration.all.map {|e| e.version.to_i}.sort
195
+ end
196
+
197
+ def current_version
198
+ get_all_versions.max || 0
199
+ end
200
+
201
+ private
202
+
203
+ def move(direction, migrations_path, steps)
204
+ migrator = self.new(direction, migrations_path)
205
+ start_index = migrator.migrations.index(migrator.current_migration)
206
+
207
+ if start_index
208
+ finish = migrator.migrations[start_index + steps]
209
+ version = finish ? finish.version : 0
210
+ send(direction, migrations_path, version)
211
+ end
212
+ end
213
+ end
214
+
215
+ def initialize(direction, migrations_path, target_version = nil)
216
+ @direction, @migrations_path, @target_version = direction, migrations_path, target_version
217
+ end
218
+
219
+ def current_version
220
+ migrated.last || 0
221
+ end
222
+
223
+ def current_migration
224
+ migrations.detect { |m| m.version == current_version }
225
+ end
226
+
227
+ def run
228
+ target = migrations.detect { |m| m.version == @target_version }
229
+ raise UnknownMigrationVersionError.new(@target_version) if target.nil?
230
+ unless (up? && migrated.include?(target.version.to_i)) || (down? && !migrated.include?(target.version.to_i))
231
+ target.migrate(@direction)
232
+ record_version_state_after_migrating(target.version)
233
+ end
234
+ end
235
+
236
+ def migrate
237
+ current = migrations.detect { |m| m.version == current_version }
238
+ target = migrations.detect { |m| m.version == @target_version }
239
+
240
+ if target.nil? && !@target_version.nil? && @target_version > 0
241
+ raise UnknownMigrationVersionError.new(@target_version)
242
+ end
243
+
244
+ start = up? ? 0 : (migrations.index(current) || 0)
245
+ finish = migrations.index(target) || migrations.size - 1
246
+ runnable = migrations[start..finish]
247
+
248
+ # skip the last migration if we're headed down, but not ALL the way down
249
+ runnable.pop if down? && !target.nil?
250
+
251
+ runnable.each do |migration|
252
+ Rails.logger.info "Migrating to #{migration.name} (#{migration.version})" if Rails.logger
253
+
254
+ # On our way up, we skip migrating the ones we've already migrated
255
+ next if up? && migrated.include?(migration.version.to_i)
256
+
257
+ # On our way down, we skip reverting the ones we've never migrated
258
+ if down? && !migrated.include?(migration.version.to_i)
259
+ migration.announce 'never migrated, skipping'; migration.write
260
+ next
261
+ end
262
+
263
+ begin
264
+ migration.migrate(@direction)
265
+ record_version_state_after_migrating(migration.version)
266
+ rescue => e
267
+ raise StandardError, "An error has occurred, all later migrations canceled:\n\n#{e}", e.backtrace
268
+ end
269
+ end
270
+ end
271
+
272
+ def migrations
273
+ @migrations ||= begin
274
+ files = Dir["#{@migrations_path}/[0-9]*_*.rb"]
275
+
276
+ migrations = files.inject([]) do |klasses, file|
277
+ version, name = file.scan(/([0-9]+)_([_a-z0-9]*).rb/).first
278
+
279
+ raise IllegalMigrationNameError.new(file) unless version
280
+ version = version.to_i
281
+
282
+ if klasses.detect { |m| m.version == version }
283
+ raise DuplicateMigrationVersionError.new(version)
284
+ end
285
+
286
+ if klasses.detect { |m| m.name == name.camelize }
287
+ raise DuplicateMigrationNameError.new(name.camelize)
288
+ end
289
+
290
+ migration = MigrationProxy.new
291
+ migration.name = name.camelize
292
+ migration.version = version
293
+ migration.filename = file
294
+ klasses << migration
295
+ end
296
+
297
+ migrations = migrations.sort_by { |m| m.version }
298
+ down? ? migrations.reverse : migrations
299
+ end
300
+ end
301
+
302
+ def pending_migrations
303
+ already_migrated = migrated
304
+ migrations.reject { |m| already_migrated.include?(m.version.to_i) }
305
+ end
306
+
307
+ def migrated
308
+ @migrated_versions ||= self.class.get_all_versions
309
+ end
310
+
311
+ private
312
+ def record_version_state_after_migrating(version)
313
+ @migrated_versions ||= []
314
+ if down?
315
+ @migrated_versions.delete(version)
316
+ Migration.where(version: version.to_s).delete
317
+ else
318
+ @migrated_versions.push(version).sort!
319
+ Migration.create "version" => version.to_s
320
+ end
321
+ end
322
+
323
+ def up?
324
+ @direction == :up
325
+ end
326
+
327
+ def down?
328
+ @direction == :down
329
+ end
330
+
331
+ end
332
+ end