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 +1 -1
- data/VERSION +1 -1
- data/history.md +8 -1
- data/lib/couchrest/model/designs/design.rb +104 -2
- data/lib/couchrest/model/designs/view.rb +31 -35
- data/lib/couchrest/model/migrate.rb +92 -0
- data/lib/couchrest/model/proxyable.rb +6 -1
- data/lib/couchrest/railtie.rb +8 -0
- data/lib/couchrest_model.rb +3 -0
- data/lib/tasks/migrations.rake +21 -0
- data/spec/unit/designs/design_spec.rb +87 -5
- data/spec/unit/designs/view_spec.rb +7 -22
- data/spec/unit/proxyable_spec.rb +25 -1
- metadata +4 -5
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
|
|
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.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
|
-
|
|
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
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
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
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
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
|
-
|
|
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
|
data/lib/couchrest/railtie.rb
CHANGED
|
@@ -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
|
data/lib/couchrest_model.rb
CHANGED
|
@@ -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
|
|
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 "
|
|
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(:
|
|
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('
|
|
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('
|
|
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 "
|
|
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']
|
data/spec/unit/proxyable_spec.rb
CHANGED
|
@@ -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:
|
|
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-
|
|
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:
|