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 +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:
|