mattetti-couchrest 0.17 → 0.20
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/lib/couchrest.rb +17 -4
- data/lib/couchrest/core/database.rb +1 -1
- data/lib/couchrest/mixins/callbacks.rb +73 -32
- data/lib/couchrest/mixins/design_doc.rb +1 -0
- data/lib/couchrest/mixins/views.rb +2 -4
- data/lib/couchrest/monkeypatches.rb +12 -6
- data/lib/couchrest/support/class.rb +148 -132
- data/spec/couchrest/core/database_spec.rb +2 -1
- data/spec/couchrest/more/extended_doc_view_spec.rb +14 -12
- data/spec/couchrest/support/class_spec.rb +59 -0
- metadata +3 -1
data/lib/couchrest.rb
CHANGED
|
@@ -22,12 +22,13 @@ $:.unshift File.dirname(__FILE__) unless
|
|
|
22
22
|
$:.include?(File.dirname(__FILE__)) ||
|
|
23
23
|
$:.include?(File.expand_path(File.dirname(__FILE__)))
|
|
24
24
|
|
|
25
|
+
$COUCHREST_DEBUG ||= false
|
|
25
26
|
|
|
26
27
|
require 'couchrest/monkeypatches'
|
|
27
28
|
|
|
28
29
|
# = CouchDB, close to the metal
|
|
29
30
|
module CouchRest
|
|
30
|
-
VERSION
|
|
31
|
+
VERSION = '0.20' unless self.const_defined?("VERSION")
|
|
31
32
|
|
|
32
33
|
autoload :Server, 'couchrest/core/server'
|
|
33
34
|
autoload :Database, 'couchrest/core/database'
|
|
@@ -129,7 +130,11 @@ module CouchRest
|
|
|
129
130
|
begin
|
|
130
131
|
JSON.parse(RestClient.put(uri, payload))
|
|
131
132
|
rescue Exception => e
|
|
132
|
-
|
|
133
|
+
if $COUCHREST_DEBUG == true
|
|
134
|
+
raise "Error while sending a PUT request #{uri}\npayload: #{payload.inspect}\n#{e}"
|
|
135
|
+
else
|
|
136
|
+
raise e
|
|
137
|
+
end
|
|
133
138
|
end
|
|
134
139
|
end
|
|
135
140
|
|
|
@@ -137,7 +142,11 @@ module CouchRest
|
|
|
137
142
|
begin
|
|
138
143
|
JSON.parse(RestClient.get(uri), :max_nesting => false)
|
|
139
144
|
rescue => e
|
|
140
|
-
|
|
145
|
+
if $COUCHREST_DEBUG == true
|
|
146
|
+
raise "Error while sending a GET request #{uri}\n: #{e}"
|
|
147
|
+
else
|
|
148
|
+
raise e
|
|
149
|
+
end
|
|
141
150
|
end
|
|
142
151
|
end
|
|
143
152
|
|
|
@@ -146,7 +155,11 @@ module CouchRest
|
|
|
146
155
|
begin
|
|
147
156
|
JSON.parse(RestClient.post(uri, payload))
|
|
148
157
|
rescue Exception => e
|
|
149
|
-
|
|
158
|
+
if $COUCHREST_DEBUG == true
|
|
159
|
+
raise "Error while sending a POST request #{uri}\npayload: #{payload.inspect}\n#{e}"
|
|
160
|
+
else
|
|
161
|
+
raise e
|
|
162
|
+
end
|
|
150
163
|
end
|
|
151
164
|
end
|
|
152
165
|
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
require File.join(File.dirname(__FILE__), '..', 'support', 'class')
|
|
2
2
|
|
|
3
3
|
# Extracted from ActiveSupport::Callbacks written by Yehuda Katz
|
|
4
|
+
# http://github.com/wycats/rails/raw/abstract_controller/activesupport/lib/active_support/new_callbacks.rb
|
|
4
5
|
# http://github.com/wycats/rails/raw/18b405f154868204a8f332888871041a7bad95e1/activesupport/lib/active_support/callbacks.rb
|
|
5
6
|
|
|
6
7
|
module CouchRest
|
|
@@ -11,7 +12,7 @@ module CouchRest
|
|
|
11
12
|
#
|
|
12
13
|
# Example:
|
|
13
14
|
# class Storage
|
|
14
|
-
# include
|
|
15
|
+
# include ActiveSupport::Callbacks
|
|
15
16
|
#
|
|
16
17
|
# define_callbacks :save
|
|
17
18
|
# end
|
|
@@ -45,7 +46,7 @@ module CouchRest
|
|
|
45
46
|
#
|
|
46
47
|
# Example:
|
|
47
48
|
# class Storage
|
|
48
|
-
# include
|
|
49
|
+
# include ActiveSupport::Callbacks
|
|
49
50
|
#
|
|
50
51
|
# define_callbacks :save
|
|
51
52
|
#
|
|
@@ -85,8 +86,8 @@ module CouchRest
|
|
|
85
86
|
klass.extend ClassMethods
|
|
86
87
|
end
|
|
87
88
|
|
|
88
|
-
def run_callbacks(kind, options = {})
|
|
89
|
-
send("_run_#{kind}_callbacks")
|
|
89
|
+
def run_callbacks(kind, options = {}, &blk)
|
|
90
|
+
send("_run_#{kind}_callbacks", &blk)
|
|
90
91
|
end
|
|
91
92
|
|
|
92
93
|
class Callback
|
|
@@ -166,9 +167,13 @@ module CouchRest
|
|
|
166
167
|
|
|
167
168
|
# This will supply contents for before and around filters, and no
|
|
168
169
|
# contents for after filters (for the forward pass).
|
|
169
|
-
def start(key = nil,
|
|
170
|
+
def start(key = nil, options = {})
|
|
171
|
+
object, terminator = (options || {}).values_at(:object, :terminator)
|
|
172
|
+
|
|
170
173
|
return if key && !object.send("_one_time_conditions_valid_#{@callback_id}?")
|
|
171
174
|
|
|
175
|
+
terminator ||= false
|
|
176
|
+
|
|
172
177
|
# options[0] is the compiled form of supplied conditions
|
|
173
178
|
# options[1] is the "end" for the conditional
|
|
174
179
|
|
|
@@ -177,8 +182,14 @@ module CouchRest
|
|
|
177
182
|
# if condition # before_save :filter_name, :if => :condition
|
|
178
183
|
# filter_name
|
|
179
184
|
# end
|
|
180
|
-
|
|
181
|
-
|
|
185
|
+
filter = <<-RUBY_EVAL
|
|
186
|
+
unless halted
|
|
187
|
+
result = #{@filter}
|
|
188
|
+
halted ||= (#{terminator})
|
|
189
|
+
end
|
|
190
|
+
RUBY_EVAL
|
|
191
|
+
[@compiled_options[0], filter, @compiled_options[1]].compact.join("\n")
|
|
192
|
+
else
|
|
182
193
|
# Compile around filters with conditions into proxy methods
|
|
183
194
|
# that contain the conditions.
|
|
184
195
|
#
|
|
@@ -196,8 +207,8 @@ module CouchRest
|
|
|
196
207
|
|
|
197
208
|
name = "_conditional_callback_#{@kind}_#{next_id}"
|
|
198
209
|
txt = <<-RUBY_EVAL
|
|
199
|
-
def #{name}
|
|
200
|
-
#{@compiled_options[0]}
|
|
210
|
+
def #{name}(halted)
|
|
211
|
+
#{@compiled_options[0] || "if true"} && !halted
|
|
201
212
|
#{@filter} do
|
|
202
213
|
yield self
|
|
203
214
|
end
|
|
@@ -207,16 +218,16 @@ module CouchRest
|
|
|
207
218
|
end
|
|
208
219
|
RUBY_EVAL
|
|
209
220
|
@klass.class_eval(txt)
|
|
210
|
-
"#{name} do"
|
|
211
|
-
else
|
|
212
|
-
"#{@filter} do"
|
|
221
|
+
"#{name}(halted) do"
|
|
213
222
|
end
|
|
214
223
|
end
|
|
215
224
|
end
|
|
216
225
|
|
|
217
226
|
# This will supply contents for around and after filters, but not
|
|
218
227
|
# before filters (for the backward pass).
|
|
219
|
-
def end(key = nil,
|
|
228
|
+
def end(key = nil, options = {})
|
|
229
|
+
object = (options || {})[:object]
|
|
230
|
+
|
|
220
231
|
return if key && !object.send("_one_time_conditions_valid_#{@callback_id}?")
|
|
221
232
|
|
|
222
233
|
if @kind == :around || @kind == :after
|
|
@@ -299,7 +310,7 @@ module CouchRest
|
|
|
299
310
|
# This method_missing is supplied to catch callbacks with keys and create
|
|
300
311
|
# the appropriate callback for future use.
|
|
301
312
|
def method_missing(meth, *args, &blk)
|
|
302
|
-
if meth.to_s =~ /
|
|
313
|
+
if meth.to_s =~ /_run__([\w:]+)__(\w+)__(\w+)__callbacks/
|
|
303
314
|
return self.class._create_and_run_keyed_callback($1, $2.to_sym, $3.to_sym, self, &blk)
|
|
304
315
|
end
|
|
305
316
|
super
|
|
@@ -307,20 +318,26 @@ module CouchRest
|
|
|
307
318
|
|
|
308
319
|
# An Array with a compile method
|
|
309
320
|
class CallbackChain < Array
|
|
310
|
-
def
|
|
321
|
+
def initialize(symbol)
|
|
322
|
+
@symbol = symbol
|
|
323
|
+
end
|
|
324
|
+
|
|
325
|
+
def compile(key = nil, options = {})
|
|
311
326
|
method = []
|
|
327
|
+
method << "halted = false"
|
|
312
328
|
each do |callback|
|
|
313
|
-
method << callback.start(key,
|
|
329
|
+
method << callback.start(key, options)
|
|
314
330
|
end
|
|
315
|
-
method << "yield self"
|
|
331
|
+
method << "yield self if block_given?"
|
|
316
332
|
reverse_each do |callback|
|
|
317
|
-
method << callback.end(key,
|
|
333
|
+
method << callback.end(key, options)
|
|
318
334
|
end
|
|
319
335
|
method.compact.join("\n")
|
|
320
336
|
end
|
|
321
337
|
|
|
322
338
|
def clone(klass)
|
|
323
|
-
CallbackChain.new(
|
|
339
|
+
chain = CallbackChain.new(@symbol)
|
|
340
|
+
chain.push(*map {|c| c.clone(klass)})
|
|
324
341
|
end
|
|
325
342
|
end
|
|
326
343
|
|
|
@@ -338,16 +355,18 @@ module CouchRest
|
|
|
338
355
|
# The _run_save_callbacks method can optionally take a key, which
|
|
339
356
|
# will be used to compile an optimized callback method for each
|
|
340
357
|
# key. See #define_callbacks for more information.
|
|
341
|
-
def _define_runner(symbol, str, options)
|
|
342
|
-
|
|
358
|
+
def _define_runner(symbol, str, options)
|
|
359
|
+
str = <<-RUBY_EVAL
|
|
343
360
|
def _run_#{symbol}_callbacks(key = nil)
|
|
344
361
|
if key
|
|
345
|
-
send("
|
|
362
|
+
send("_run__\#{self.class.name.split("::").last}__#{symbol}__\#{key}__callbacks") { yield if block_given? }
|
|
346
363
|
else
|
|
347
364
|
#{str}
|
|
348
365
|
end
|
|
349
366
|
end
|
|
350
367
|
RUBY_EVAL
|
|
368
|
+
|
|
369
|
+
class_eval str, __FILE__, __LINE__ + 1
|
|
351
370
|
|
|
352
371
|
before_name, around_name, after_name =
|
|
353
372
|
options.values_at(:before, :after, :around)
|
|
@@ -359,15 +378,18 @@ module CouchRest
|
|
|
359
378
|
def _create_and_run_keyed_callback(klass, kind, key, obj, &blk)
|
|
360
379
|
@_keyed_callbacks ||= {}
|
|
361
380
|
@_keyed_callbacks[[kind, key]] ||= begin
|
|
362
|
-
str = self.send("_#{kind}_callbacks").compile(key, obj)
|
|
381
|
+
str = self.send("_#{kind}_callbacks").compile(key, :object => obj, :terminator => self.send("_#{kind}_terminator"))
|
|
382
|
+
|
|
363
383
|
self.class_eval <<-RUBY_EVAL, __FILE__, __LINE__ + 1
|
|
364
|
-
def
|
|
384
|
+
def _run__#{klass.split("::").last}__#{kind}__#{key}__callbacks
|
|
365
385
|
#{str}
|
|
366
386
|
end
|
|
367
387
|
RUBY_EVAL
|
|
388
|
+
|
|
368
389
|
true
|
|
369
390
|
end
|
|
370
|
-
|
|
391
|
+
|
|
392
|
+
obj.send("_run__#{klass.split("::").last}__#{kind}__#{key}__callbacks", &blk)
|
|
371
393
|
end
|
|
372
394
|
|
|
373
395
|
# Define callbacks.
|
|
@@ -402,20 +424,32 @@ module CouchRest
|
|
|
402
424
|
# method that took into consideration the per_key conditions. This
|
|
403
425
|
# is a speed improvement for ActionPack.
|
|
404
426
|
def define_callbacks(*symbols)
|
|
427
|
+
terminator = symbols.pop if symbols.last.is_a?(String)
|
|
405
428
|
symbols.each do |symbol|
|
|
429
|
+
self.class_inheritable_accessor("_#{symbol}_terminator")
|
|
430
|
+
self.send("_#{symbol}_terminator=", terminator)
|
|
406
431
|
self.class_eval <<-RUBY_EVAL, __FILE__, __LINE__ + 1
|
|
407
432
|
class_inheritable_accessor :_#{symbol}_callbacks
|
|
408
|
-
self._#{symbol}_callbacks = CallbackChain.new
|
|
433
|
+
self._#{symbol}_callbacks = CallbackChain.new(:#{symbol})
|
|
409
434
|
|
|
410
|
-
def self.#{symbol}_callback(
|
|
435
|
+
def self.#{symbol}_callback(*filters, &blk)
|
|
436
|
+
type = [:before, :after, :around].include?(filters.first) ? filters.shift : :before
|
|
411
437
|
options = filters.last.is_a?(Hash) ? filters.pop : {}
|
|
412
438
|
filters.unshift(blk) if block_given?
|
|
413
|
-
|
|
439
|
+
|
|
440
|
+
filters.map! do |filter|
|
|
441
|
+
# overrides parent class
|
|
442
|
+
self._#{symbol}_callbacks.delete_if {|c| c.matches?(type, :#{symbol}, filter)}
|
|
443
|
+
Callback.new(filter, type, options.dup, self, :#{symbol})
|
|
444
|
+
end
|
|
414
445
|
self._#{symbol}_callbacks.push(*filters)
|
|
415
|
-
_define_runner(:#{symbol},
|
|
446
|
+
_define_runner(:#{symbol},
|
|
447
|
+
self._#{symbol}_callbacks.compile(nil, :terminator => _#{symbol}_terminator),
|
|
448
|
+
options)
|
|
416
449
|
end
|
|
417
450
|
|
|
418
|
-
def self.skip_#{symbol}_callback(
|
|
451
|
+
def self.skip_#{symbol}_callback(*filters, &blk)
|
|
452
|
+
type = [:before, :after, :around].include?(filters.first) ? filters.shift : :before
|
|
419
453
|
options = filters.last.is_a?(Hash) ? filters.pop : {}
|
|
420
454
|
filters.unshift(blk) if block_given?
|
|
421
455
|
filters.each do |filter|
|
|
@@ -428,15 +462,22 @@ module CouchRest
|
|
|
428
462
|
else
|
|
429
463
|
self._#{symbol}_callbacks.delete(filter)
|
|
430
464
|
end
|
|
431
|
-
_define_runner(:#{symbol},
|
|
465
|
+
_define_runner(:#{symbol},
|
|
466
|
+
self._#{symbol}_callbacks.compile(nil, :terminator => _#{symbol}_terminator),
|
|
467
|
+
options)
|
|
432
468
|
end
|
|
433
469
|
|
|
434
470
|
end
|
|
435
471
|
|
|
472
|
+
def self.reset_#{symbol}_callbacks
|
|
473
|
+
self._#{symbol}_callbacks = CallbackChain.new(:#{symbol})
|
|
474
|
+
_define_runner(:#{symbol}, self._#{symbol}_callbacks.compile, {})
|
|
475
|
+
end
|
|
476
|
+
|
|
436
477
|
self.#{symbol}_callback(:before)
|
|
437
478
|
RUBY_EVAL
|
|
438
479
|
end
|
|
439
480
|
end
|
|
440
481
|
end
|
|
441
482
|
end
|
|
442
|
-
end
|
|
483
|
+
end
|
|
@@ -16,6 +16,7 @@ module CouchRest
|
|
|
16
16
|
def design_doc_slug
|
|
17
17
|
return design_doc_slug_cache if (design_doc_slug_cache && design_doc_fresh)
|
|
18
18
|
funcs = []
|
|
19
|
+
design_doc ||= Design.new(default_design_doc)
|
|
19
20
|
design_doc['views'].each do |name, view|
|
|
20
21
|
funcs << "#{name}/#{view['map']}#{view['reduce']}"
|
|
21
22
|
end
|
|
@@ -129,13 +129,11 @@ module CouchRest
|
|
|
129
129
|
private
|
|
130
130
|
|
|
131
131
|
def fetch_view_with_docs(name, opts, raw=false, &block)
|
|
132
|
-
if raw
|
|
132
|
+
if raw || (opts.has_key?(:include_docs) && opts[:include_docs] == false)
|
|
133
133
|
fetch_view(name, opts, &block)
|
|
134
134
|
else
|
|
135
135
|
begin
|
|
136
|
-
|
|
137
|
-
opts.merge({:include_docs => true}) unless opts.has_key?(:include_docs)
|
|
138
|
-
view = fetch_view name, opts, &block
|
|
136
|
+
view = fetch_view name, opts.merge({:include_docs => true}), &block
|
|
139
137
|
view['rows'].collect{|r|new(r['doc'])} if view['rows']
|
|
140
138
|
rescue
|
|
141
139
|
# fallback for old versions of couchdb that don't
|
|
@@ -31,13 +31,19 @@ if RUBY_VERSION.to_f < 1.9
|
|
|
31
31
|
class Net::BufferedIO #:nodoc:
|
|
32
32
|
alias :old_rbuf_fill :rbuf_fill
|
|
33
33
|
def rbuf_fill
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
rescue Errno::EWOULDBLOCK
|
|
37
|
-
if IO.select([@io], nil, nil, @read_timeout)
|
|
34
|
+
if @io.respond_to?(:read_nonblock)
|
|
35
|
+
begin
|
|
38
36
|
@rbuf << @io.read_nonblock(65536)
|
|
39
|
-
|
|
40
|
-
|
|
37
|
+
rescue Errno::EWOULDBLOCK
|
|
38
|
+
if IO.select([@io], nil, nil, @read_timeout)
|
|
39
|
+
retry
|
|
40
|
+
else
|
|
41
|
+
raise Timeout::TimeoutError
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
else
|
|
45
|
+
timeout(@read_timeout) do
|
|
46
|
+
@rbuf << @io.sysread(65536)
|
|
41
47
|
end
|
|
42
48
|
end
|
|
43
49
|
end
|
|
@@ -25,151 +25,167 @@
|
|
|
25
25
|
# example, an array without those additions being shared with either their
|
|
26
26
|
# parent, siblings, or children, which is unlike the regular class-level
|
|
27
27
|
# attributes that are shared across the entire hierarchy.
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
28
|
+
module CouchRest
|
|
29
|
+
module ClassExtension
|
|
30
|
+
def self.included(base)
|
|
31
|
+
if CouchRest::ClassExtension::InstanceMethods.instance_methods.all? {|m| base.respond_to?(m)}
|
|
32
|
+
# do nothing
|
|
33
|
+
elsif CouchRest::ClassExtension::InstanceMethods.instance_methods.any? {|m| base.respond_to?(m)}
|
|
34
|
+
raise RuntimeError, "Conflicting extentions to Class, work it out"
|
|
35
|
+
else
|
|
36
|
+
base.send(:include, CouchRest::ClassExtension::InstanceMethods)
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
module InstanceMethods
|
|
41
|
+
# Defines class-level and instance-level attribute reader.
|
|
42
|
+
#
|
|
43
|
+
# @param *syms<Array> Array of attributes to define reader for.
|
|
44
|
+
# @return <Array[#to_s]> List of attributes that were made into cattr_readers
|
|
45
|
+
#
|
|
46
|
+
# @api public
|
|
47
|
+
#
|
|
48
|
+
# @todo Is this inconsistent in that it does not allow you to prevent
|
|
49
|
+
# an instance_reader via :instance_reader => false
|
|
50
|
+
def cattr_reader(*syms)
|
|
51
|
+
syms.flatten.each do |sym|
|
|
52
|
+
next if sym.is_a?(Hash)
|
|
53
|
+
class_eval(<<-RUBY, __FILE__, __LINE__ + 1)
|
|
54
|
+
unless defined? @@#{sym}
|
|
55
|
+
@@#{sym} = nil
|
|
56
|
+
end
|
|
45
57
|
|
|
46
|
-
def self.#{sym}
|
|
47
|
-
@@#{sym}
|
|
48
|
-
end
|
|
58
|
+
def self.#{sym}
|
|
59
|
+
@@#{sym}
|
|
60
|
+
end
|
|
49
61
|
|
|
50
|
-
def #{sym}
|
|
51
|
-
@@#{sym}
|
|
52
|
-
end
|
|
53
|
-
RUBY
|
|
62
|
+
def #{sym}
|
|
63
|
+
@@#{sym}
|
|
54
64
|
end
|
|
55
|
-
|
|
65
|
+
RUBY
|
|
66
|
+
end
|
|
67
|
+
end
|
|
56
68
|
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
unless defined? @@#{sym}
|
|
69
|
-
@@#{sym} = nil
|
|
70
|
-
end
|
|
69
|
+
# Defines class-level (and optionally instance-level) attribute writer.
|
|
70
|
+
#
|
|
71
|
+
# @param <Array[*#to_s, Hash{:instance_writer => Boolean}]> Array of attributes to define writer for.
|
|
72
|
+
# @option syms :instance_writer<Boolean> if true, instance-level attribute writer is defined.
|
|
73
|
+
# @return <Array[#to_s]> List of attributes that were made into cattr_writers
|
|
74
|
+
#
|
|
75
|
+
# @api public
|
|
76
|
+
def cattr_writer(*syms)
|
|
77
|
+
options = syms.last.is_a?(Hash) ? syms.pop : {}
|
|
78
|
+
syms.flatten.each do |sym|
|
|
79
|
+
class_eval(<<-RUBY, __FILE__, __LINE__ + 1)
|
|
80
|
+
unless defined? @@#{sym}
|
|
81
|
+
@@#{sym} = nil
|
|
82
|
+
end
|
|
71
83
|
|
|
72
|
-
def self.#{sym}=(obj)
|
|
73
|
-
@@#{sym} = obj
|
|
74
|
-
end
|
|
75
|
-
RUBY
|
|
84
|
+
def self.#{sym}=(obj)
|
|
85
|
+
@@#{sym} = obj
|
|
86
|
+
end
|
|
87
|
+
RUBY
|
|
76
88
|
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
def #{sym}=(obj)
|
|
80
|
-
@@#{sym} = obj
|
|
81
|
-
end
|
|
82
|
-
RUBY
|
|
83
|
-
end
|
|
89
|
+
unless options[:instance_writer] == false
|
|
90
|
+
class_eval(<<-RUBY, __FILE__, __LINE__ + 1)
|
|
91
|
+
def #{sym}=(obj)
|
|
92
|
+
@@#{sym} = obj
|
|
84
93
|
end
|
|
85
|
-
|
|
94
|
+
RUBY
|
|
95
|
+
end
|
|
96
|
+
end
|
|
97
|
+
end
|
|
86
98
|
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
99
|
+
# Defines class-level (and optionally instance-level) attribute accessor.
|
|
100
|
+
#
|
|
101
|
+
# @param *syms<Array[*#to_s, Hash{:instance_writer => Boolean}]> Array of attributes to define accessor for.
|
|
102
|
+
# @option syms :instance_writer<Boolean> if true, instance-level attribute writer is defined.
|
|
103
|
+
# @return <Array[#to_s]> List of attributes that were made into accessors
|
|
104
|
+
#
|
|
105
|
+
# @api public
|
|
106
|
+
def cattr_accessor(*syms)
|
|
107
|
+
cattr_reader(*syms)
|
|
108
|
+
cattr_writer(*syms)
|
|
109
|
+
end
|
|
98
110
|
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
111
|
+
# Defines class-level inheritable attribute reader. Attributes are available to subclasses,
|
|
112
|
+
# each subclass has a copy of parent's attribute.
|
|
113
|
+
#
|
|
114
|
+
# @param *syms<Array[#to_s]> Array of attributes to define inheritable reader for.
|
|
115
|
+
# @return <Array[#to_s]> Array of attributes converted into inheritable_readers.
|
|
116
|
+
#
|
|
117
|
+
# @api public
|
|
118
|
+
#
|
|
119
|
+
# @todo Do we want to block instance_reader via :instance_reader => false
|
|
120
|
+
# @todo It would be preferable that we do something with a Hash passed in
|
|
121
|
+
# (error out or do the same as other methods above) instead of silently
|
|
122
|
+
# moving on). In particular, this makes the return value of this function
|
|
123
|
+
# less useful.
|
|
124
|
+
def class_inheritable_reader(*ivars)
|
|
125
|
+
instance_reader = ivars.pop[:reader] if ivars.last.is_a?(Hash)
|
|
114
126
|
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
def self.#{ivar}
|
|
118
|
-
return @#{ivar} if self.object_id == #{self.object_id} || defined?(@#{ivar})
|
|
119
|
-
ivar = superclass.#{ivar}
|
|
120
|
-
return nil if ivar.nil? && !#{self}.instance_variable_defined?("@#{ivar}")
|
|
121
|
-
@#{ivar} = ivar && !ivar.is_a?(Module) && !ivar.is_a?(Numeric) && !ivar.is_a?(TrueClass) && !ivar.is_a?(FalseClass) && !ivar.is_a?(Symbol) ? ivar.dup : ivar
|
|
122
|
-
end
|
|
123
|
-
RUBY
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
def #{ivar}
|
|
127
|
-
self.class.#{ivar}
|
|
128
|
-
end
|
|
129
|
-
RUBY
|
|
127
|
+
ivars.each do |ivar|
|
|
128
|
+
self.class_eval <<-RUBY, __FILE__, __LINE__ + 1
|
|
129
|
+
def self.#{ivar}
|
|
130
|
+
return @#{ivar} if self.object_id == #{self.object_id} || defined?(@#{ivar})
|
|
131
|
+
ivar = superclass.#{ivar}
|
|
132
|
+
return nil if ivar.nil? && !#{self}.instance_variable_defined?("@#{ivar}")
|
|
133
|
+
@#{ivar} = ivar && !ivar.is_a?(Module) && !ivar.is_a?(Numeric) && !ivar.is_a?(TrueClass) && !ivar.is_a?(FalseClass) && !ivar.is_a?(Symbol) ? ivar.dup : ivar
|
|
134
|
+
end
|
|
135
|
+
RUBY
|
|
136
|
+
unless instance_reader == false
|
|
137
|
+
self.class_eval <<-RUBY, __FILE__, __LINE__ + 1
|
|
138
|
+
def #{ivar}
|
|
139
|
+
self.class.#{ivar}
|
|
140
|
+
end
|
|
141
|
+
RUBY
|
|
142
|
+
end
|
|
143
|
+
end
|
|
130
144
|
end
|
|
145
|
+
|
|
146
|
+
# Defines class-level inheritable attribute writer. Attributes are available to subclasses,
|
|
147
|
+
# each subclass has a copy of parent's attribute.
|
|
148
|
+
#
|
|
149
|
+
# @param *syms<Array[*#to_s, Hash{:instance_writer => Boolean}]> Array of attributes to
|
|
150
|
+
# define inheritable writer for.
|
|
151
|
+
# @option syms :instance_writer<Boolean> if true, instance-level inheritable attribute writer is defined.
|
|
152
|
+
# @return <Array[#to_s]> An Array of the attributes that were made into inheritable writers.
|
|
153
|
+
#
|
|
154
|
+
# @api public
|
|
155
|
+
#
|
|
156
|
+
# @todo We need a style for class_eval <<-HEREDOC. I'd like to make it
|
|
157
|
+
# class_eval(<<-RUBY, __FILE__, __LINE__), but we should codify it somewhere.
|
|
158
|
+
def class_inheritable_writer(*ivars)
|
|
159
|
+
instance_writer = ivars.pop[:instance_writer] if ivars.last.is_a?(Hash)
|
|
160
|
+
ivars.each do |ivar|
|
|
161
|
+
self.class_eval <<-RUBY, __FILE__, __LINE__ + 1
|
|
162
|
+
def self.#{ivar}=(obj)
|
|
163
|
+
@#{ivar} = obj
|
|
131
164
|
end
|
|
132
|
-
|
|
165
|
+
RUBY
|
|
166
|
+
unless instance_writer == false
|
|
167
|
+
self.class_eval <<-RUBY, __FILE__, __LINE__ + 1
|
|
168
|
+
def #{ivar}=(obj) self.class.#{ivar} = obj end
|
|
169
|
+
RUBY
|
|
170
|
+
end
|
|
171
|
+
end
|
|
172
|
+
end
|
|
133
173
|
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
def class_inheritable_writer(*ivars)
|
|
147
|
-
instance_writer = ivars.pop[:instance_writer] if ivars.last.is_a?(Hash)
|
|
148
|
-
ivars.each do |ivar|
|
|
149
|
-
self.class_eval <<-RUBY, __FILE__, __LINE__ + 1
|
|
150
|
-
def self.#{ivar}=(obj)
|
|
151
|
-
@#{ivar} = obj
|
|
152
|
-
end
|
|
153
|
-
RUBY
|
|
154
|
-
unless instance_writer == false
|
|
155
|
-
self.class_eval <<-RUBY, __FILE__, __LINE__ + 1
|
|
156
|
-
def #{ivar}=(obj) self.class.#{ivar} = obj end
|
|
157
|
-
RUBY
|
|
174
|
+
# Defines class-level inheritable attribute accessor. Attributes are available to subclasses,
|
|
175
|
+
# each subclass has a copy of parent's attribute.
|
|
176
|
+
#
|
|
177
|
+
# @param *syms<Array[*#to_s, Hash{:instance_writer => Boolean}]> Array of attributes to
|
|
178
|
+
# define inheritable accessor for.
|
|
179
|
+
# @option syms :instance_writer<Boolean> if true, instance-level inheritable attribute writer is defined.
|
|
180
|
+
# @return <Array[#to_s]> An Array of attributes turned into inheritable accessors.
|
|
181
|
+
#
|
|
182
|
+
# @api public
|
|
183
|
+
def class_inheritable_accessor(*syms)
|
|
184
|
+
class_inheritable_reader(*syms)
|
|
185
|
+
class_inheritable_writer(*syms)
|
|
158
186
|
end
|
|
159
187
|
end
|
|
160
188
|
end
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
#
|
|
165
|
-
# @param *syms<Array[*#to_s, Hash{:instance_writer => Boolean}]> Array of attributes to
|
|
166
|
-
# define inheritable accessor for.
|
|
167
|
-
# @option syms :instance_writer<Boolean> if true, instance-level inheritable attribute writer is defined.
|
|
168
|
-
# @return <Array[#to_s]> An Array of attributes turned into inheritable accessors.
|
|
169
|
-
#
|
|
170
|
-
# @api public
|
|
171
|
-
def class_inheritable_accessor(*syms)
|
|
172
|
-
class_inheritable_reader(*syms)
|
|
173
|
-
class_inheritable_writer(*syms)
|
|
174
|
-
end
|
|
175
|
-
end
|
|
189
|
+
end
|
|
190
|
+
|
|
191
|
+
Class.send(:include, CouchRest::ClassExtension)
|
|
@@ -312,8 +312,9 @@ describe CouchRest::Database do
|
|
|
312
312
|
}
|
|
313
313
|
}
|
|
314
314
|
@db.save_doc(doc)
|
|
315
|
+
doc['_rev'].should_not be_nil
|
|
315
316
|
doc['field'] << 'another value'
|
|
316
|
-
@db.save_doc(doc).should be_true
|
|
317
|
+
@db.save_doc(doc)["ok"].should be_true
|
|
317
318
|
end
|
|
318
319
|
|
|
319
320
|
it 'should be there' do
|
|
@@ -105,6 +105,9 @@ describe "ExtendedDocument views" do
|
|
|
105
105
|
|
|
106
106
|
describe "a model with a compound key view" do
|
|
107
107
|
before(:all) do
|
|
108
|
+
Article.design_doc_fresh = false
|
|
109
|
+
Article.by_user_id_and_date.each{|a| a.destroy(true)}
|
|
110
|
+
Article.database.bulk_delete
|
|
108
111
|
written_at = Time.now - 24 * 3600 * 7
|
|
109
112
|
@titles = ["uniq one", "even more interesting", "less fun", "not junk"]
|
|
110
113
|
@user_ids = ["quentin", "aaron"]
|
|
@@ -164,24 +167,24 @@ describe "ExtendedDocument views" do
|
|
|
164
167
|
# TODO: moved to Design, delete
|
|
165
168
|
describe "adding a view" do
|
|
166
169
|
before(:each) do
|
|
170
|
+
reset_test_db!
|
|
167
171
|
Article.by_date
|
|
168
|
-
@design_docs = Article.database.documents :startkey => "_design/",
|
|
169
|
-
:endkey => "_design/\u9999"
|
|
172
|
+
@design_docs = Article.database.documents :startkey => "_design/", :endkey => "_design/\u9999"
|
|
170
173
|
end
|
|
171
174
|
it "should not create a design doc on view definition" do
|
|
172
175
|
Article.view_by :created_at
|
|
173
|
-
newdocs = Article.database.documents :startkey => "_design/",
|
|
174
|
-
:endkey => "_design/\u9999"
|
|
176
|
+
newdocs = Article.database.documents :startkey => "_design/", :endkey => "_design/\u9999"
|
|
175
177
|
newdocs["rows"].length.should == @design_docs["rows"].length
|
|
176
178
|
end
|
|
177
|
-
it "should create a new design document on view access" do
|
|
179
|
+
it "should create a new version of the design document on view access" do
|
|
180
|
+
old_design_doc = Article.database.documents(:key => @design_docs["rows"].first["key"], :include_docs => true)["rows"][0]["doc"]
|
|
178
181
|
Article.view_by :updated_at
|
|
179
182
|
Article.by_updated_at
|
|
180
|
-
newdocs = Article.database.documents
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
183
|
+
newdocs = Article.database.documents({:startkey => "_design/", :endkey => "_design/\u9999"})
|
|
184
|
+
|
|
185
|
+
doc = Article.database.documents(:key => @design_docs["rows"].first["key"], :include_docs => true)["rows"][0]["doc"]
|
|
186
|
+
doc["_rev"].should_not == old_design_doc["_rev"]
|
|
187
|
+
doc["views"].keys.should include("by_updated_at")
|
|
185
188
|
end
|
|
186
189
|
end
|
|
187
190
|
|
|
@@ -192,10 +195,9 @@ describe "ExtendedDocument views" do
|
|
|
192
195
|
Article.by_field
|
|
193
196
|
end
|
|
194
197
|
it "should clean them up" do
|
|
198
|
+
ddocs = Article.all_design_doc_versions
|
|
195
199
|
Article.view_by :stream
|
|
196
200
|
Article.by_stream
|
|
197
|
-
ddocs = Article.all_design_doc_versions
|
|
198
|
-
ddocs["rows"].length.should > 1
|
|
199
201
|
Article.cleanup_design_docs!
|
|
200
202
|
ddocs = Article.all_design_doc_versions
|
|
201
203
|
ddocs["rows"].length.should == 1
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
require File.join(File.dirname(__FILE__), '..', '..', 'spec_helper')
|
|
2
|
+
require File.join(File.dirname(__FILE__), '..', '..', '..', 'lib', 'couchrest', 'support', 'class')
|
|
3
|
+
|
|
4
|
+
describe CouchRest::ClassExtension do
|
|
5
|
+
|
|
6
|
+
before :all do
|
|
7
|
+
class FullyDefinedClassExtensions
|
|
8
|
+
def self.respond_to?(method)
|
|
9
|
+
if CouchRest::ClassExtension::InstanceMethods.instance_methods.include?(method)
|
|
10
|
+
true
|
|
11
|
+
else
|
|
12
|
+
super
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
class PartDefinedClassExtensions
|
|
18
|
+
def self.respond_to?(method)
|
|
19
|
+
methods = CouchRest::ClassExtension::InstanceMethods.instance_methods
|
|
20
|
+
methods.delete('cattr_reader')
|
|
21
|
+
|
|
22
|
+
if methods.include?(method)
|
|
23
|
+
false
|
|
24
|
+
else
|
|
25
|
+
super
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
class NoClassExtensions
|
|
31
|
+
def self.respond_to?(method)
|
|
32
|
+
if CouchRest::ClassExtension::InstanceMethods.instance_methods.include?(method)
|
|
33
|
+
false
|
|
34
|
+
else
|
|
35
|
+
super
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
it "should not include InstanceMethods if the class extensions are already defined" do
|
|
44
|
+
FullyDefinedClassExtensions.send(:include, CouchRest::ClassExtension)
|
|
45
|
+
FullyDefinedClassExtensions.ancestors.should_not include(CouchRest::ClassExtension::InstanceMethods)
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
it "should raise RuntimeError if the class extensions are only partially defined" do
|
|
49
|
+
lambda {
|
|
50
|
+
PartDefinedClassExtensions.send(:include, CouchRest::ClassExtension)
|
|
51
|
+
}.should raise_error(RuntimeError)
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
it "should include class extensions if they are not already defined" do
|
|
55
|
+
NoClassExtensions.send(:include, CouchRest::ClassExtension)
|
|
56
|
+
NoClassExtensions.ancestors.should include(CouchRest::ClassExtension::InstanceMethods)
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
end
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: mattetti-couchrest
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: "0.
|
|
4
|
+
version: "0.20"
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- J. Chris Anderson
|
|
@@ -142,6 +142,8 @@ files:
|
|
|
142
142
|
- spec/couchrest/more/extended_doc_spec.rb
|
|
143
143
|
- spec/couchrest/more/extended_doc_view_spec.rb
|
|
144
144
|
- spec/couchrest/more/property_spec.rb
|
|
145
|
+
- spec/couchrest/support
|
|
146
|
+
- spec/couchrest/support/class_spec.rb
|
|
145
147
|
- spec/fixtures
|
|
146
148
|
- spec/fixtures/attachments
|
|
147
149
|
- spec/fixtures/attachments/couchdb.png
|