couchrest_model 1.2.0.beta → 2.0.0.beta

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/README.md CHANGED
@@ -23,7 +23,7 @@ CouchRest Model is only properly tested on CouchDB version 1.0 or newer.
23
23
 
24
24
  ### Upgrading from an earlier version?
25
25
 
26
- *Pre 1.2:* As of June 2012, couchrest model no longer supports the `view_by` and `view` calls from the model. Views are no only accessed via a design document. If you have older code and wish to upgrade, please ensure you move to the new syntax for using views.
26
+ *Pre 2.0:* As of June 2012, couchrest model no longer supports the `view_by` and `view` calls from the model. Views are no only accessed via a design document. If you have older code and wish to upgrade, please ensure you move to the new syntax for using views.
27
27
 
28
28
  *Pre 1.1:* As of April 2011 and the release of version 1.1.0, the default model type key is 'type' instead of 'couchrest-type'. Simply updating your project will not work unless you migrate your data or set the configuration option in your initializers:
29
29
 
data/VERSION CHANGED
@@ -1 +1 @@
1
- 1.2.0.beta
1
+ 2.0.0.beta
data/history.md CHANGED
@@ -1,10 +1,17 @@
1
1
  # CouchRest Model Change History
2
2
 
3
+ ## 2.0.0.beta - 2012-06-14
4
+
5
+ * Added design doc migration support, including for proxied models
6
+ * Rake tasks available for migrations
7
+ * Rails config option now available: `config.couchrest_model.auto_update_design_docs = false`
8
+ * Skipping 1.2 version due to design doc API changes
9
+
3
10
  ## 1.2.0.beta - 2012-06-08
4
11
 
5
12
  * Completely refactored Design Document handling.
6
13
  * Removed old `view` and `view_by` methods.
7
- * CouchRest::Model::Base.respond_to_missing? and respond_to? (Kim Burgestrand)
14
+ * CouchRest::Model::Base.respond_to_missing? and respond_to? (Kim Burgestrand) (later removed)
8
15
  * Time#as_json now insists on using xmlschema with 3 fraction digits by default.
9
16
  * Added time_fraction_digits configuration object
10
17
 
@@ -57,6 +57,107 @@ module CouchRest
57
57
  self
58
58
  end
59
59
 
60
+ # Migrate the design document preventing downtime on a production
61
+ # system. Typically this will be used when auto updates are disabled.
62
+ #
63
+ # Steps taken are:
64
+ #
65
+ # 1. Compare the checksum with the current version
66
+ # 2. If different, create a new design doc with timestamp
67
+ # 3. Wait until the view returns a result
68
+ # 4. Copy over the original design doc
69
+ #
70
+ # If a block is provided, it will be called with the result of the migration:
71
+ #
72
+ # * :no_change - Nothing performed as there are no changes.
73
+ # * :created - Add a new design doc as non existed
74
+ # * :migrated - Migrated the existing design doc.
75
+ #
76
+ # This can be used for progressivly printing the results of the migration.
77
+ #
78
+ # After completion, either a "cleanup" Proc object will be provided to finalize
79
+ # the process and copy the document into place, or simply nil if no cleanup is
80
+ # required. For example:
81
+ #
82
+ # print "Synchronising Cat model designs: "
83
+ # callback = Cat.design_doc.migrate do |res|
84
+ # puts res.to_s
85
+ # end
86
+ # if callback
87
+ # puts "Cleaning up."
88
+ # callback.call
89
+ # end
90
+ #
91
+ def migrate(db = nil, &block)
92
+ db ||= database
93
+ doc = load_from_database(db)
94
+ cleanup = nil
95
+ id = self['_id']
96
+
97
+ if !doc
98
+ # no need to migrate, just save it
99
+ new_doc = to_hash.dup
100
+ db.save_doc(new_doc)
101
+
102
+ result = :created
103
+ elsif doc['couchrest-hash'] != checksum
104
+ id += "_migration"
105
+
106
+ # Delete current migration if there is one
107
+ old_migration = load_from_database(db, id)
108
+ db.delete_doc(old_migration) if old_migration
109
+
110
+ # Save new design doc
111
+ new_doc = doc.merge(to_hash)
112
+ new_doc['_id'] = id
113
+ new_doc.delete('_rev')
114
+ db.save_doc(new_doc)
115
+
116
+ # Proc definition to copy the migration doc over the original
117
+ cleanup = Proc.new do
118
+ db.copy_doc(new_doc, doc)
119
+ db.delete_doc(new_doc)
120
+ self
121
+ end
122
+
123
+ result = :migrated
124
+ else
125
+ # Already up to date
126
+ result = :no_change
127
+ end
128
+
129
+ if new_doc && !new_doc['views'].empty?
130
+ # Create a view query and send
131
+ name = new_doc['views'].keys.first
132
+ view = new_doc['views'][name]
133
+ params = {:limit => 1}
134
+ params[:reduce] = false if view['reduce']
135
+ db.view("#{id}/_view/#{name}", params) do |res|
136
+ # Block to use streamer!
137
+ end
138
+ end
139
+
140
+ # Provide the result in block
141
+ yield result if block_given?
142
+
143
+ cleanup
144
+ end
145
+
146
+ # Perform a single migration and inmediatly request a cleanup operation:
147
+ #
148
+ # print "Synchronising Cat model designs: "
149
+ # Cat.design_doc.migrate! do |res|
150
+ # puts res.to_s
151
+ # end
152
+ #
153
+ def migrate!(db = nil, &block)
154
+ callback = migrate(db, &block)
155
+ if callback.is_a?(Proc)
156
+ callback.call
157
+ else
158
+ callback
159
+ end
160
+ end
60
161
 
61
162
  def checksum
62
163
  sum = self['couchrest-hash']
@@ -111,8 +212,9 @@ module CouchRest
111
212
 
112
213
  protected
113
214
 
114
- def load_from_database(db = database)
115
- db.get(self['_id'])
215
+ def load_from_database(db = database, id = nil)
216
+ id ||= self['_id']
217
+ db.get(id)
116
218
  rescue RestClient::ResourceNotFound
117
219
  nil
118
220
  end
@@ -453,49 +453,45 @@ module CouchRest
453
453
  # is true, empty strings are permited in the indexes.
454
454
  #
455
455
  def define(design_doc, name, opts = {})
456
- # Don't create the map or reduce method if auto updates are disabled
457
- if design_doc.auto_update
458
- model = design_doc.model
459
- # Is this an all view?
460
- if name.to_s == 'all'
461
- opts[:map] = <<-EOF
462
- function(doc) {
463
- if (doc['#{model.model_type_key}'] == '#{model.to_s}') {
464
- emit(doc._id, null);
465
- }
456
+ model = design_doc.model
457
+
458
+ # Is this an all view?
459
+ if name.to_s == 'all'
460
+ opts[:map] = <<-EOF
461
+ function(doc) {
462
+ if (doc['#{model.model_type_key}'] == '#{model.to_s}') {
463
+ emit(doc._id, null);
466
464
  }
467
- EOF
468
- elsif !opts[:map]
469
- if opts[:by].nil? && name.to_s =~ /^by_(.+)/
470
- opts[:by] = $1.split(/_and_/)
471
- end
472
-
473
- raise "View cannot be created without recognised name, :map or :by options" if opts[:by].nil?
474
-
475
- opts[:allow_blank] = opts[:allow_blank].nil? ? true : opts[:allow_blank]
476
- opts[:guards] ||= []
477
- opts[:guards].push "(doc['#{model.model_type_key}'] == '#{model.to_s}')"
478
-
479
- keys = opts[:by].map{|o| "doc['#{o}']"}
480
- emit = keys.length == 1 ? keys.first : "[#{keys.join(', ')}]"
481
- opts[:guards] += keys.map{|k| "(#{k} != null)"} unless opts[:allow_nil]
482
- opts[:guards] += keys.map{|k| "(#{k} != '')"} unless opts[:allow_blank]
483
- opts[:map] = <<-EOF
484
- function(doc) {
485
- if (#{opts[:guards].join(' && ')}) {
486
- emit(#{emit}, 1);
487
- }
465
+ }
466
+ EOF
467
+ elsif !opts[:map]
468
+ if opts[:by].nil? && name.to_s =~ /^by_(.+)/
469
+ opts[:by] = $1.split(/_and_/)
470
+ end
471
+ raise "View cannot be created without recognised name, :map or :by options" if opts[:by].nil?
472
+
473
+ opts[:allow_blank] = opts[:allow_blank].nil? ? true : opts[:allow_blank]
474
+ opts[:guards] ||= []
475
+ opts[:guards].push "(doc['#{model.model_type_key}'] == '#{model.to_s}')"
476
+
477
+ keys = opts[:by].map{|o| "doc['#{o}']"}
478
+ emit = keys.length == 1 ? keys.first : "[#{keys.join(', ')}]"
479
+ opts[:guards] += keys.map{|k| "(#{k} != null)"} unless opts[:allow_nil]
480
+ opts[:guards] += keys.map{|k| "(#{k} != '')"} unless opts[:allow_blank]
481
+ opts[:map] = <<-EOF
482
+ function(doc) {
483
+ if (#{opts[:guards].join(' && ')}) {
484
+ emit(#{emit}, 1);
488
485
  }
489
- EOF
486
+ }
487
+ EOF
488
+ if opts[:reduce].nil?
490
489
  opts[:reduce] = <<-EOF
491
490
  function(key, values, rereduce) {
492
491
  return sum(values);
493
492
  }
494
493
  EOF
495
494
  end
496
- else
497
- # Assume there is always a map method
498
- opts[:map] ||= true
499
495
  end
500
496
 
501
497
  design_doc['views'] ||= {}
@@ -0,0 +1,92 @@
1
+ module CouchRest
2
+ module Model
3
+
4
+ # Handle CouchDB migrations.
5
+ #
6
+ # Actual migrations are handled by the Design document, this serves as a utility
7
+ # to find all the CouchRest Model submodels and perform the migration on them.
8
+ #
9
+ # Also contains some more advanced support for handling proxied models.
10
+ #
11
+ # Examples of usage:
12
+ #
13
+ # # Ensure all models have been loaded (only Rails)
14
+ # CouchRest::Model::Migrate.load_all_models
15
+ #
16
+ # # Migrate all regular models (not proxied)
17
+ # CouchRest::Model::Migrate.all_models
18
+ #
19
+ # # Migrate all models and submodels of proxies
20
+ # CouchRest::Model::Migrate.all_models_and_proxies
21
+ #
22
+ class Migrate
23
+
24
+ def self.all_models
25
+ callbacks = migrate_each_model(find_models)
26
+ cleanup(callbacks)
27
+ end
28
+
29
+ def self.all_models_and_proxies
30
+ callbacks = migrate_each_model(find_models)
31
+ callbacks += migrate_each_proxying_model(find_proxying_models)
32
+ cleanup(callbacks)
33
+ end
34
+
35
+ def self.load_all_models
36
+ # Make a reasonable effort to load all models
37
+ return unless defined?(Rails)
38
+ Dir[Rails.root + 'app/models/**/*.rb'].each do |path|
39
+ require path
40
+ end
41
+ end
42
+
43
+ def self.find_models
44
+ CouchRest::Model::Base.subclasses.reject{|m| m.proxy_owner_method.present?}
45
+ end
46
+
47
+ def self.find_proxying_models
48
+ CouchRest::Model::Base.subclasses.reject{|m| m.proxy_database_method.blank?}
49
+ end
50
+
51
+ def self.migrate_each_model(models, db = nil)
52
+ callbacks = [ ]
53
+ models.each do |model|
54
+ model.design_docs.each do |design|
55
+ callbacks << migrate_design(model, design, db)
56
+ end
57
+ end
58
+ callbacks
59
+ end
60
+
61
+ def self.migrate_each_proxying_model(models)
62
+ callbacks = [ ]
63
+ models.each do |model|
64
+ submodels = model.proxied_model_names.map{|n| n.constantize}
65
+ model.all.each do |base|
66
+ puts "Finding proxied models for #{model}: \"#{base.send(model.proxy_database_method)}\""
67
+ callbacks += migrate_each_model(submodels, base.proxy_database)
68
+ end
69
+ end
70
+ callbacks
71
+ end
72
+
73
+ def self.migrate_design(model, design, db = nil)
74
+ print "Migrating #{model.to_s}##{design.method_name}... "
75
+ callback = design.migrate(db) do |result|
76
+ puts "#{result.to_s.gsub(/_/, ' ')}"
77
+ end
78
+ # Return the callback hash if there is one
79
+ callback ? {:design => design, :proc => callback, :db => db || model.database} : nil
80
+ end
81
+
82
+ def self.cleanup(methods)
83
+ methods.compact.each do |cb|
84
+ name = "/#{cb[:db].name}/#{cb[:design]['_id']}"
85
+ puts "Activating new design: #{name}"
86
+ cb[:proc].call
87
+ end
88
+ end
89
+
90
+ end
91
+ end
92
+ end
@@ -17,6 +17,7 @@ module CouchRest
17
17
  def proxy_for(assoc_name, options = {})
18
18
  db_method = options[:database_method] || "proxy_database"
19
19
  options[:class_name] ||= assoc_name.to_s.singularize.camelize
20
+ proxied_model_names << options[:class_name] unless proxied_model_names.include?(options[:class_name])
20
21
  class_eval <<-EOS, __FILE__, __LINE__ + 1
21
22
  def #{assoc_name}
22
23
  @#{assoc_name} ||= CouchRest::Model::Proxyable::ModelProxy.new(::#{options[:class_name]}, self, self.class.to_s.underscore, #{db_method})
@@ -24,7 +25,7 @@ module CouchRest
24
25
  EOS
25
26
  end
26
27
 
27
- # Tell this model which other model to use a base for the database
28
+ # Tell this model which other model to use as a base for the database
28
29
  # connection to use.
29
30
  def proxied_by(model_name, options = {})
30
31
  raise "Model can only be proxied once or ##{model_name} already defined" if method_defined?(model_name) || !proxy_owner_method.nil?
@@ -47,6 +48,10 @@ module CouchRest
47
48
  @proxy_database_method
48
49
  end
49
50
 
51
+ def proxied_model_names
52
+ @proxied_model_names ||= []
53
+ end
54
+
50
55
  private
51
56
 
52
57
  # Ensure that no attempt is made to autoload a database connection
@@ -18,6 +18,14 @@ module CouchRest
18
18
  Rails.application.class.to_s.underscore.gsub(/\/.*/, '')
19
19
  end
20
20
  end
21
+
22
+ config.before_configuration do
23
+ config.couchrest_model = CouchRest::Model::Base
24
+ end
25
+
26
+ rake_tasks do
27
+ Dir[File.join(File.dirname(__FILE__),'../tasks/*.rake')].each { |f| load f }
28
+ end
21
29
  end
22
30
 
23
31
  end
@@ -56,6 +56,9 @@ require "couchrest/model/core_extensions/time_parsing"
56
56
  require "couchrest/model/embeddable"
57
57
  require "couchrest/model/base"
58
58
 
59
+ # Design Migration support
60
+ require "couchrest/model/migrate.rb"
61
+
59
62
  # Add rails support *after* everything has loaded
60
63
  if defined?(Rails)
61
64
  require "couchrest/railtie"
@@ -0,0 +1,21 @@
1
+ #
2
+ # CouchRest Migration Rake Tasks
3
+ #
4
+ # Use at own risk! These are not tested yet!
5
+ #
6
+ namespace :couchrest do
7
+
8
+ desc "Migrate all the design docs found in each model"
9
+ task :migrate => :environment do
10
+ CouchRest::Model::Migrate.load_all_models
11
+ CouchRest::Model::Migrate.all_models
12
+ end
13
+
14
+ desc "Migrate all the design docs "
15
+ task :migrate_with_proxies => :environment do
16
+ CouchRest::Model::Migrate.load_all_models
17
+ CouchRest::Model::Migrate.all_models_and_proxies
18
+ end
19
+
20
+
21
+ end
@@ -8,7 +8,7 @@ describe CouchRest::Model::Designs::Design do
8
8
  reset_test_db!
9
9
  end
10
10
 
11
- class DesignSampleModel < CouchRest::Model::Base
11
+ class DesignSampleModelBase < CouchRest::Model::Base
12
12
  use_database DB
13
13
  property :name
14
14
  property :surname
@@ -20,6 +20,9 @@ describe CouchRest::Model::Designs::Design do
20
20
  end
21
21
  end
22
22
 
23
+ class DesignSampleModel < DesignSampleModelBase
24
+ end
25
+
23
26
  describe "class methods" do
24
27
 
25
28
  before :all do
@@ -160,7 +163,86 @@ describe CouchRest::Model::Designs::Design do
160
163
  end
161
164
 
162
165
 
163
- describe "checksum" do
166
+ describe "#migrate" do
167
+ # WARNING! ORDER IS IMPORTANT!
168
+
169
+ describe "with limited changes" do
170
+
171
+ class DesignSampleModelMigrate < DesignSampleModelBase
172
+ end
173
+
174
+ before :all do
175
+ reset_test_db!
176
+ @mod = DesignSampleModelMigrate
177
+ @doc = @mod.design_doc
178
+ @db = @mod.database
179
+ end
180
+
181
+ it "should create new design if non exists" do
182
+ @db.should_receive(:view).with("#{@doc['_id']}/_view/#{@doc['views'].keys.first}", {:limit => 1, :reduce => false})
183
+ callback = @doc.migrate do |res|
184
+ res.should eql(:created)
185
+ end
186
+ doc = @db.get(@doc['_id'])
187
+ doc['views']['all'].should eql(@doc['views']['all'])
188
+ callback.should be_nil
189
+ end
190
+
191
+ it "should not change anything if design is up to date" do
192
+ @doc.sync
193
+ @db.should_not_receive(:view)
194
+ callback = @doc.migrate do |res|
195
+ res.should eql(:no_change)
196
+ end
197
+ callback.should be_nil
198
+ end
199
+
200
+ end
201
+
202
+ describe "migrating a document if there are changes" do
203
+
204
+ class DesignSampleModelMigrate2 < DesignSampleModelBase
205
+ end
206
+
207
+ before :all do
208
+ reset_test_db!
209
+ @mod = DesignSampleModelMigrate2
210
+ @doc = @mod.design_doc
211
+ @db = @mod.database
212
+ @doc.sync!
213
+ @doc.create_view(:by_name_and_surname)
214
+ @doc_id = @doc['_id'] + '_migration'
215
+ end
216
+
217
+ it "should save new migration design doc" do
218
+ @db.should_receive(:view).with("#{@doc_id}/_view/by_name", {:limit => 1, :reduce => false})
219
+ @callback = @doc.migrate do |res|
220
+ res.should eql(:migrated)
221
+ end
222
+ @callback.should_not be_nil
223
+
224
+ # should not have updated original view until cleanup
225
+ doc = @db.get(@doc['_id'])
226
+ doc['views'].should_not have_key('by_name_and_surname')
227
+
228
+ # Should have created the migration
229
+ new_doc = @db.get(@doc_id)
230
+ new_doc.should_not be_nil
231
+
232
+ # should be possible to perform cleanup
233
+ @callback.call
234
+ lambda { new_doc = @db.get(@doc_id) }.should raise_error RestClient::ResourceNotFound
235
+
236
+ doc = @db.get(@doc['_id'])
237
+ doc['views'].should have_key('by_name_and_surname')
238
+ end
239
+
240
+ end
241
+
242
+ end
243
+
244
+
245
+ describe "#checksum" do
164
246
 
165
247
  before :all do
166
248
  @mod = DesignSampleModel
@@ -173,7 +255,7 @@ describe CouchRest::Model::Designs::Design do
173
255
 
174
256
  it "should provide same checksum without refresh on re-request" do
175
257
  chk = @doc.checksum
176
- @doc.should_not_receive(:chaecksum!)
258
+ @doc.should_not_receive(:checksum!)
177
259
  @doc.checksum.should eql(chk)
178
260
  end
179
261
 
@@ -271,12 +353,12 @@ describe CouchRest::Model::Designs::Design do
271
353
 
272
354
  it "should calculate a consistent checksum for model" do
273
355
  #WithTemplateAndUniqueID.design_doc.checksum.should eql('caa2b4c27abb82b4e37421de76d96ffc')
274
- WithTemplateAndUniqueID.design_doc.checksum.should eql('f0973aaa72e4db0efeb2a281ea297cec')
356
+ WithTemplateAndUniqueID.design_doc.checksum.should eql('7f44e88afbce06204010c49b76f31bcf')
275
357
  end
276
358
 
277
359
  it "should calculate checksum for complex model" do
278
360
  #Article.design_doc.checksum.should eql('70dff8caea143bf40fad09adf0701104')
279
- Article.design_doc.checksum.should eql('7ef39bffdf5837e8b078411ac417d860')
361
+ Article.design_doc.checksum.should eql('0f25a2d9f86e31ebd61b29863a41d5ed')
280
362
  end
281
363
 
282
364
  it "should cache the generated checksum value" do
@@ -100,32 +100,11 @@ describe "Design View" do
100
100
 
101
101
  describe ".define" do
102
102
 
103
- describe "with no auto update" do
104
- before :each do
105
- @design_doc = { }
106
- @design_doc.stub!(:model).and_return(DesignViewModel)
107
- @design_doc.stub!(:auto_update).and_return(false)
108
- end
109
-
110
- it "should set map view to true" do
111
- @klass.define(@design_doc, 'test_view')
112
- @design_doc['views']['test_view']['map'].should eql(true)
113
- @design_doc['views']['test_view']['reduce'].should be_false
114
- end
115
-
116
- it "should set reduce to true if set" do
117
- @klass.define(@design_doc, 'test_view', :reduce => true)
118
- @design_doc['views']['test_view']['map'].should eql(true)
119
- @design_doc['views']['test_view']['reduce'].should eql(true)
120
- end
121
- end
122
-
123
- describe "with auto update" do
103
+ describe "under normal circumstances" do
124
104
 
125
105
  before :each do
126
106
  @design_doc = { }
127
107
  @design_doc.stub!(:model).and_return(DesignViewModel)
128
- @design_doc.stub!(:auto_update).and_return(true)
129
108
  end
130
109
 
131
110
  it "should add a basic view" do
@@ -133,6 +112,12 @@ describe "Design View" do
133
112
  @design_doc['views']['test_view'].should_not be_nil
134
113
  end
135
114
 
115
+ it "should not overwrite reduce if set" do
116
+ @klass.define(@design_doc, 'by_title', :reduce => true)
117
+ @design_doc['views']['by_title']['map'].should_not be_blank
118
+ @design_doc['views']['by_title']['reduce'].should eql(true)
119
+ end
120
+
136
121
  it "should auto generate mapping from name" do
137
122
  lambda { @klass.define(@design_doc, 'by_title') }.should_not raise_error
138
123
  str = @design_doc['views']['by_title']['map']
@@ -35,7 +35,6 @@ describe CouchRest::Model::Proxyable do
35
35
  it "should raise an error if called and no proxy_database_method set" do
36
36
  lambda { @obj.proxy_database }.should raise_error(StandardError, /Please set/)
37
37
  end
38
-
39
38
  end
40
39
 
41
40
  describe "class methods" do
@@ -74,6 +73,11 @@ describe CouchRest::Model::Proxyable do
74
73
  @class.should respond_to(:proxy_for)
75
74
  end
76
75
 
76
+ it "should add model name to proxied model name array" do
77
+ @class.proxy_for(:cats)
78
+ @class.proxied_model_names.should eql(['Cat'])
79
+ end
80
+
77
81
  it "should create a new method" do
78
82
  DummyProxyable.stub!(:method_defined?).and_return(true)
79
83
  DummyProxyable.proxy_for(:cats)
@@ -140,6 +144,26 @@ describe CouchRest::Model::Proxyable do
140
144
  lambda { @class.database }.should raise_error(StandardError, /database must be accessed via/)
141
145
  end
142
146
  end
147
+
148
+ describe ".proxied_model_names" do
149
+ before do
150
+ @class = Class.new(CouchRest::Model::Base)
151
+ end
152
+
153
+ it "should respond to proxied_model_names" do
154
+ @class.should respond_to(:proxied_model_names)
155
+ end
156
+
157
+ it "should provide an empty array" do
158
+ @class.proxied_model_names.should be_empty
159
+ end
160
+
161
+ it "should accept new entries" do
162
+ @class.proxied_model_names << 'Cat'
163
+ @class.proxied_model_names.first.should eql('Cat')
164
+ end
165
+ end
166
+
143
167
  end
144
168
 
145
169
  describe "ModelProxy" do
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: couchrest_model
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.2.0.beta
4
+ version: 2.0.0.beta
5
5
  prerelease: 6
6
6
  platform: ruby
7
7
  authors:
@@ -13,7 +13,7 @@ authors:
13
13
  autorequire:
14
14
  bindir: bin
15
15
  cert_chain: []
16
- date: 2012-06-08 00:00:00.000000000 Z
16
+ date: 2012-06-14 00:00:00.000000000 Z
17
17
  dependencies:
18
18
  - !ruby/object:Gem::Dependency
19
19
  name: couchrest
@@ -185,6 +185,7 @@ files:
185
185
  - lib/couchrest/model/embeddable.rb
186
186
  - lib/couchrest/model/errors.rb
187
187
  - lib/couchrest/model/extended_attachments.rb
188
+ - lib/couchrest/model/migrate.rb
188
189
  - lib/couchrest/model/persistence.rb
189
190
  - lib/couchrest/model/properties.rb
190
191
  - lib/couchrest/model/property.rb
@@ -203,6 +204,7 @@ files:
203
204
  - lib/rails/generators/couchrest_model/config/templates/couchdb.yml
204
205
  - lib/rails/generators/couchrest_model/model/model_generator.rb
205
206
  - lib/rails/generators/couchrest_model/model/templates/model.rb
207
+ - lib/tasks/migrations.rake
206
208
  - spec/.gitignore
207
209
  - spec/fixtures/attachments/README
208
210
  - spec/fixtures/attachments/couchdb.png
@@ -265,9 +267,6 @@ required_ruby_version: !ruby/object:Gem::Requirement
265
267
  - - ! '>='
266
268
  - !ruby/object:Gem::Version
267
269
  version: '0'
268
- segments:
269
- - 0
270
- hash: -3604299647194247287
271
270
  required_rubygems_version: !ruby/object:Gem::Requirement
272
271
  none: false
273
272
  requirements: