jchris-couchrest 0.2 → 0.2.1

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