jchris-couchrest 0.2 → 0.2.1

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 CHANGED
@@ -28,7 +28,7 @@ require 'couchrest/monkeypatches'
28
28
 
29
29
  # = CouchDB, close to the metal
30
30
  module CouchRest
31
- VERSION = '0.2' unless self.const_defined?("VERSION")
31
+ VERSION = '0.2.1' unless self.const_defined?("VERSION")
32
32
 
33
33
  autoload :Server, 'couchrest/core/server'
34
34
  autoload :Database, 'couchrest/core/database'
@@ -20,7 +20,7 @@ module CouchRest
20
20
  @uri = @root = "#{host}/#{name}"
21
21
  @streamer = Streamer.new(self)
22
22
  @bulk_save_cache = []
23
- @bulk_save_cache_limit = 50
23
+ @bulk_save_cache_limit = 500 # must be smaller than the uuid count
24
24
  end
25
25
 
26
26
  # returns the database's uri
@@ -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 CouchRest::Callbacks
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 CouchRest::Callbacks
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, object = 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
- [@compiled_options[0], @filter, @compiled_options[1]].compact.join("\n")
181
- elsif @compiled_options[0]
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, object = 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 =~ /_run_(\w+)_(\w+)_(\w+)_callbacks/
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 compile(key = nil, object = nil)
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, object)
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, object)
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(map {|c| c.clone(klass)})
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
- self.class_eval <<-RUBY_EVAL, __FILE__, __LINE__ + 1
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("_run_\#{self.class}_#{symbol}_\#{key}_callbacks") { yield }
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 _run_#{klass}_#{kind}_#{key}_callbacks
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
- obj.send("_run_#{klass}_#{kind}_#{key}_callbacks", &blk)
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(type, *filters, &blk)
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
- filters.map! {|f| Callback.new(f, type, options.dup, self, :#{symbol})}
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}, self._#{symbol}_callbacks.compile, options)
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(type, *filters, &blk)
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}, self._#{symbol}_callbacks.compile, options)
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,12 +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
- # auto load mentioned documents unless asked differently (didn't use merge! on a previous line to avoid duping the object)
137
- view = fetch_view name, (opts.has_key?(:include_docs) ? opts : opts.merge({:include_docs => true})), &block
136
+ view = fetch_view name, opts.merge({:include_docs => true}), &block
138
137
  view['rows'].collect{|r|new(r['doc'])} if view['rows']
139
138
  rescue
140
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
- begin
35
- @rbuf << @io.read_nonblock(65536)
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
- else
40
- raise Timeout::Error
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
- class Class
29
- # Defines class-level and instance-level attribute reader.
30
- #
31
- # @param *syms<Array> Array of attributes to define reader for.
32
- # @return <Array[#to_s]> List of attributes that were made into cattr_readers
33
- #
34
- # @api public
35
- #
36
- # @todo Is this inconsistent in that it does not allow you to prevent
37
- # an instance_reader via :instance_reader => false
38
- def cattr_reader(*syms)
39
- syms.flatten.each do |sym|
40
- next if sym.is_a?(Hash)
41
- class_eval(<<-RUBY, __FILE__, __LINE__ + 1)
42
- unless defined? @@#{sym}
43
- @@#{sym} = nil
44
- end
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
- end
65
+ RUBY
66
+ end
67
+ end
56
68
 
57
- # Defines class-level (and optionally instance-level) attribute writer.
58
- #
59
- # @param <Array[*#to_s, Hash{:instance_writer => Boolean}]> Array of attributes to define writer for.
60
- # @option syms :instance_writer<Boolean> if true, instance-level attribute writer is defined.
61
- # @return <Array[#to_s]> List of attributes that were made into cattr_writers
62
- #
63
- # @api public
64
- def cattr_writer(*syms)
65
- options = syms.last.is_a?(Hash) ? syms.pop : {}
66
- syms.flatten.each do |sym|
67
- class_eval(<<-RUBY, __FILE__, __LINE__ + 1)
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
- unless options[:instance_writer] == false
78
- class_eval(<<-RUBY, __FILE__, __LINE__ + 1)
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
- end
94
+ RUBY
95
+ end
96
+ end
97
+ end
86
98
 
87
- # Defines class-level (and optionally instance-level) attribute accessor.
88
- #
89
- # @param *syms<Array[*#to_s, Hash{:instance_writer => Boolean}]> Array of attributes to define accessor for.
90
- # @option syms :instance_writer<Boolean> if true, instance-level attribute writer is defined.
91
- # @return <Array[#to_s]> List of attributes that were made into accessors
92
- #
93
- # @api public
94
- def cattr_accessor(*syms)
95
- cattr_reader(*syms)
96
- cattr_writer(*syms)
97
- end
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
- # Defines class-level inheritable attribute reader. Attributes are available to subclasses,
100
- # each subclass has a copy of parent's attribute.
101
- #
102
- # @param *syms<Array[#to_s]> Array of attributes to define inheritable reader for.
103
- # @return <Array[#to_s]> Array of attributes converted into inheritable_readers.
104
- #
105
- # @api public
106
- #
107
- # @todo Do we want to block instance_reader via :instance_reader => false
108
- # @todo It would be preferable that we do something with a Hash passed in
109
- # (error out or do the same as other methods above) instead of silently
110
- # moving on). In particular, this makes the return value of this function
111
- # less useful.
112
- def class_inheritable_reader(*ivars)
113
- instance_reader = ivars.pop[:reader] if ivars.last.is_a?(Hash)
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
- ivars.each do |ivar|
116
- self.class_eval <<-RUBY, __FILE__, __LINE__ + 1
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
- unless instance_reader == false
125
- self.class_eval <<-RUBY, __FILE__, __LINE__ + 1
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
- end
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
- # Defines class-level inheritable attribute writer. Attributes are available to subclasses,
135
- # each subclass has a copy of parent's attribute.
136
- #
137
- # @param *syms<Array[*#to_s, Hash{:instance_writer => Boolean}]> Array of attributes to
138
- # define inheritable writer for.
139
- # @option syms :instance_writer<Boolean> if true, instance-level inheritable attribute writer is defined.
140
- # @return <Array[#to_s]> An Array of the attributes that were made into inheritable writers.
141
- #
142
- # @api public
143
- #
144
- # @todo We need a style for class_eval <<-HEREDOC. I'd like to make it
145
- # class_eval(<<-RUBY, __FILE__, __LINE__), but we should codify it somewhere.
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
- # Defines class-level inheritable attribute accessor. Attributes are available to subclasses,
163
- # each subclass has a copy of parent's attribute.
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)
@@ -167,24 +167,24 @@ describe "ExtendedDocument views" do
167
167
  # TODO: moved to Design, delete
168
168
  describe "adding a view" do
169
169
  before(:each) do
170
+ reset_test_db!
170
171
  Article.by_date
171
- @design_docs = Article.database.documents :startkey => "_design/",
172
- :endkey => "_design/\u9999"
172
+ @design_docs = Article.database.documents :startkey => "_design/", :endkey => "_design/\u9999"
173
173
  end
174
174
  it "should not create a design doc on view definition" do
175
175
  Article.view_by :created_at
176
- newdocs = Article.database.documents :startkey => "_design/",
177
- :endkey => "_design/\u9999"
176
+ newdocs = Article.database.documents :startkey => "_design/", :endkey => "_design/\u9999"
178
177
  newdocs["rows"].length.should == @design_docs["rows"].length
179
178
  end
180
- 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"]
181
181
  Article.view_by :updated_at
182
182
  Article.by_updated_at
183
- newdocs = Article.database.documents :startkey => "_design/",
184
- :endkey => "_design/\u9999"
185
- # puts @design_docs.inspect
186
- # puts newdocs.inspect
187
- newdocs["rows"].length.should == @design_docs["rows"].length + 1
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")
188
188
  end
189
189
  end
190
190
 
@@ -195,10 +195,9 @@ describe "ExtendedDocument views" do
195
195
  Article.by_field
196
196
  end
197
197
  it "should clean them up" do
198
+ ddocs = Article.all_design_doc_versions
198
199
  Article.view_by :stream
199
200
  Article.by_stream
200
- ddocs = Article.all_design_doc_versions
201
- ddocs["rows"].length.should > 1
202
201
  Article.cleanup_design_docs!
203
202
  ddocs = Article.all_design_doc_versions
204
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: jchris-couchrest
3
3
  version: !ruby/object:Gem::Version
4
- version: "0.2"
4
+ version: 0.2.1
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