couchrest_model 1.2.0.beta → 2.0.0.beta

Sign up to get free protection for your applications and to get access to all the features.
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: