couchrest_extended_document 1.0.0.beta5

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.
Files changed (71) hide show
  1. data/LICENSE +176 -0
  2. data/README.md +68 -0
  3. data/Rakefile +68 -0
  4. data/THANKS.md +19 -0
  5. data/examples/model/example.rb +144 -0
  6. data/history.txt +159 -0
  7. data/lib/couchrest/casted_array.rb +25 -0
  8. data/lib/couchrest/casted_model.rb +55 -0
  9. data/lib/couchrest/extended_document.rb +323 -0
  10. data/lib/couchrest/mixins/attribute_protection.rb +74 -0
  11. data/lib/couchrest/mixins/callbacks.rb +532 -0
  12. data/lib/couchrest/mixins/class_proxy.rb +120 -0
  13. data/lib/couchrest/mixins/collection.rb +260 -0
  14. data/lib/couchrest/mixins/design_doc.rb +127 -0
  15. data/lib/couchrest/mixins/document_queries.rb +82 -0
  16. data/lib/couchrest/mixins/extended_attachments.rb +73 -0
  17. data/lib/couchrest/mixins/properties.rb +162 -0
  18. data/lib/couchrest/mixins/validation.rb +245 -0
  19. data/lib/couchrest/mixins/views.rb +148 -0
  20. data/lib/couchrest/mixins.rb +11 -0
  21. data/lib/couchrest/property.rb +50 -0
  22. data/lib/couchrest/support/couchrest.rb +19 -0
  23. data/lib/couchrest/support/rails.rb +42 -0
  24. data/lib/couchrest/typecast.rb +175 -0
  25. data/lib/couchrest/validation/auto_validate.rb +156 -0
  26. data/lib/couchrest/validation/contextual_validators.rb +78 -0
  27. data/lib/couchrest/validation/validation_errors.rb +125 -0
  28. data/lib/couchrest/validation/validators/absent_field_validator.rb +74 -0
  29. data/lib/couchrest/validation/validators/confirmation_validator.rb +107 -0
  30. data/lib/couchrest/validation/validators/format_validator.rb +122 -0
  31. data/lib/couchrest/validation/validators/formats/email.rb +66 -0
  32. data/lib/couchrest/validation/validators/formats/url.rb +43 -0
  33. data/lib/couchrest/validation/validators/generic_validator.rb +120 -0
  34. data/lib/couchrest/validation/validators/length_validator.rb +139 -0
  35. data/lib/couchrest/validation/validators/method_validator.rb +89 -0
  36. data/lib/couchrest/validation/validators/numeric_validator.rb +109 -0
  37. data/lib/couchrest/validation/validators/required_field_validator.rb +114 -0
  38. data/lib/couchrest/validation.rb +245 -0
  39. data/lib/couchrest_extended_document.rb +21 -0
  40. data/spec/couchrest/attribute_protection_spec.rb +150 -0
  41. data/spec/couchrest/casted_extended_doc_spec.rb +79 -0
  42. data/spec/couchrest/casted_model_spec.rb +406 -0
  43. data/spec/couchrest/extended_doc_attachment_spec.rb +148 -0
  44. data/spec/couchrest/extended_doc_inherited_spec.rb +40 -0
  45. data/spec/couchrest/extended_doc_spec.rb +868 -0
  46. data/spec/couchrest/extended_doc_subclass_spec.rb +99 -0
  47. data/spec/couchrest/extended_doc_view_spec.rb +529 -0
  48. data/spec/couchrest/property_spec.rb +648 -0
  49. data/spec/fixtures/attachments/README +3 -0
  50. data/spec/fixtures/attachments/couchdb.png +0 -0
  51. data/spec/fixtures/attachments/test.html +11 -0
  52. data/spec/fixtures/more/article.rb +35 -0
  53. data/spec/fixtures/more/card.rb +22 -0
  54. data/spec/fixtures/more/cat.rb +22 -0
  55. data/spec/fixtures/more/course.rb +25 -0
  56. data/spec/fixtures/more/event.rb +8 -0
  57. data/spec/fixtures/more/invoice.rb +17 -0
  58. data/spec/fixtures/more/person.rb +9 -0
  59. data/spec/fixtures/more/question.rb +6 -0
  60. data/spec/fixtures/more/service.rb +12 -0
  61. data/spec/fixtures/more/user.rb +22 -0
  62. data/spec/fixtures/views/lib.js +3 -0
  63. data/spec/fixtures/views/test_view/lib.js +3 -0
  64. data/spec/fixtures/views/test_view/only-map.js +4 -0
  65. data/spec/fixtures/views/test_view/test-map.js +3 -0
  66. data/spec/fixtures/views/test_view/test-reduce.js +3 -0
  67. data/spec/spec.opts +5 -0
  68. data/spec/spec_helper.rb +49 -0
  69. data/utils/remap.rb +27 -0
  70. data/utils/subset.rb +30 -0
  71. metadata +200 -0
@@ -0,0 +1,532 @@
1
+ # Copyright (c) 2006-2009 David Heinemeier Hansson
2
+ #
3
+ # Permission is hereby granted, free of charge, to any person obtaining
4
+ # a copy of this software and associated documentation files (the
5
+ # "Software"), to deal in the Software without restriction, including
6
+ # without limitation the rights to use, copy, modify, merge, publish,
7
+ # distribute, sublicense, and/or sell copies of the Software, and to
8
+ # permit persons to whom the Software is furnished to do so, subject to
9
+ # the following conditions:
10
+ #
11
+ # The above copyright notice and this permission notice shall be
12
+ # included in all copies or substantial portions of the Software.
13
+ #
14
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
21
+ #
22
+ # Extracted from ActiveSupport::NewCallbacks written by Yehuda Katz
23
+ # http://github.com/rails/rails/raw/d6e4113c83a9d55be6f2af247da2cecaa855f43b/activesupport/lib/active_support/new_callbacks.rb
24
+ # http://github.com/rails/rails/commit/1126a85aed576402d978e6f76eb393b6baaa9541
25
+
26
+ module CouchRest
27
+ module Mixins
28
+ # Callbacks are hooks into the lifecycle of an object that allow you to trigger logic
29
+ # before or after an alteration of the object state.
30
+ #
31
+ # Mixing in this module allows you to define callbacks in your class.
32
+ #
33
+ # Example:
34
+ # class Storage
35
+ # include ActiveSupport::Callbacks
36
+ #
37
+ # define_callbacks :save
38
+ # end
39
+ #
40
+ # class ConfigStorage < Storage
41
+ # save_callback :before, :saving_message
42
+ # def saving_message
43
+ # puts "saving..."
44
+ # end
45
+ #
46
+ # save_callback :after do |object|
47
+ # puts "saved"
48
+ # end
49
+ #
50
+ # def save
51
+ # _run_save_callbacks do
52
+ # puts "- save"
53
+ # end
54
+ # end
55
+ # end
56
+ #
57
+ # config = ConfigStorage.new
58
+ # config.save
59
+ #
60
+ # Output:
61
+ # saving...
62
+ # - save
63
+ # saved
64
+ #
65
+ # Callbacks from parent classes are inherited.
66
+ #
67
+ # Example:
68
+ # class Storage
69
+ # include ActiveSupport::Callbacks
70
+ #
71
+ # define_callbacks :save
72
+ #
73
+ # save_callback :before, :prepare
74
+ # def prepare
75
+ # puts "preparing save"
76
+ # end
77
+ # end
78
+ #
79
+ # class ConfigStorage < Storage
80
+ # save_callback :before, :saving_message
81
+ # def saving_message
82
+ # puts "saving..."
83
+ # end
84
+ #
85
+ # save_callback :after do |object|
86
+ # puts "saved"
87
+ # end
88
+ #
89
+ # def save
90
+ # _run_save_callbacks do
91
+ # puts "- save"
92
+ # end
93
+ # end
94
+ # end
95
+ #
96
+ # config = ConfigStorage.new
97
+ # config.save
98
+ #
99
+ # Output:
100
+ # preparing save
101
+ # saving...
102
+ # - save
103
+ # saved
104
+ module Callbacks
105
+ def self.included(klass)
106
+ klass.extend ClassMethods
107
+ end
108
+
109
+ def run_callbacks(kind, options = {}, &blk)
110
+ send("_run_#{kind}_callbacks", &blk)
111
+ end
112
+
113
+ class Callback
114
+ @@_callback_sequence = 0
115
+
116
+ attr_accessor :filter, :kind, :name, :options, :per_key, :klass
117
+ def initialize(filter, kind, options, klass)
118
+ @kind, @klass = kind, klass
119
+
120
+ normalize_options!(options)
121
+
122
+ @per_key = options.delete(:per_key)
123
+ @raw_filter, @options = filter, options
124
+ @filter = _compile_filter(filter)
125
+ @compiled_options = _compile_options(options)
126
+ @callback_id = next_id
127
+
128
+ _compile_per_key_options
129
+ end
130
+
131
+ def clone(klass)
132
+ obj = super()
133
+ obj.klass = klass
134
+ obj.per_key = @per_key.dup
135
+ obj.options = @options.dup
136
+ obj.per_key[:if] = @per_key[:if].dup
137
+ obj.per_key[:unless] = @per_key[:unless].dup
138
+ obj.options[:if] = @options[:if].dup
139
+ obj.options[:unless] = @options[:unless].dup
140
+ obj
141
+ end
142
+
143
+ def normalize_options!(options)
144
+ options[:if] = Array.wrap(options[:if])
145
+ options[:unless] = Array.wrap(options[:unless])
146
+
147
+ options[:per_key] ||= {}
148
+ options[:per_key][:if] = Array.wrap(options[:per_key][:if])
149
+ options[:per_key][:unless] = Array.wrap(options[:per_key][:unless])
150
+ end
151
+
152
+ def next_id
153
+ @@_callback_sequence += 1
154
+ end
155
+
156
+ def matches?(_kind, _filter)
157
+ @kind == _kind &&
158
+ @filter == _filter
159
+ end
160
+
161
+ def _update_filter(filter_options, new_options)
162
+ filter_options[:if].push(new_options[:unless]) if new_options.key?(:unless)
163
+ filter_options[:unless].push(new_options[:if]) if new_options.key?(:if)
164
+ end
165
+
166
+ def recompile!(_options, _per_key)
167
+ _update_filter(self.options, _options)
168
+ _update_filter(self.per_key, _per_key)
169
+
170
+ @callback_id = next_id
171
+ @filter = _compile_filter(@raw_filter)
172
+ @compiled_options = _compile_options(@options)
173
+ _compile_per_key_options
174
+ end
175
+
176
+ def _compile_per_key_options
177
+ key_options = _compile_options(@per_key)
178
+
179
+ @klass.class_eval <<-RUBY_EVAL, __FILE__, __LINE__ + 1
180
+ def _one_time_conditions_valid_#{@callback_id}?
181
+ true #{key_options[0]}
182
+ end
183
+ RUBY_EVAL
184
+ end
185
+
186
+ # This will supply contents for before and around filters, and no
187
+ # contents for after filters (for the forward pass).
188
+ def start(key = nil, options = {})
189
+ object, terminator = (options || {}).values_at(:object, :terminator)
190
+
191
+ return if key && !object.send("_one_time_conditions_valid_#{@callback_id}?")
192
+
193
+ terminator ||= false
194
+
195
+ # options[0] is the compiled form of supplied conditions
196
+ # options[1] is the "end" for the conditional
197
+
198
+ if @kind == :before || @kind == :around
199
+ if @kind == :before
200
+ # if condition # before_save :filter_name, :if => :condition
201
+ # filter_name
202
+ # end
203
+ filter = <<-RUBY_EVAL
204
+ unless halted
205
+ result = #{@filter}
206
+ halted = (#{terminator})
207
+ end
208
+ RUBY_EVAL
209
+
210
+ [@compiled_options[0], filter, @compiled_options[1]].compact.join("\n")
211
+ else
212
+ # Compile around filters with conditions into proxy methods
213
+ # that contain the conditions.
214
+ #
215
+ # For `around_save :filter_name, :if => :condition':
216
+ #
217
+ # def _conditional_callback_save_17
218
+ # if condition
219
+ # filter_name do
220
+ # yield self
221
+ # end
222
+ # else
223
+ # yield self
224
+ # end
225
+ # end
226
+
227
+ name = "_conditional_callback_#{@kind}_#{next_id}"
228
+ txt, line = <<-RUBY_EVAL, __LINE__ + 1
229
+ def #{name}(halted)
230
+ #{@compiled_options[0] || "if true"} && !halted
231
+ #{@filter} do
232
+ yield self
233
+ end
234
+ else
235
+ yield self
236
+ end
237
+ end
238
+ RUBY_EVAL
239
+ @klass.class_eval(txt, __FILE__, line)
240
+ "#{name}(halted) do"
241
+ end
242
+ end
243
+ end
244
+
245
+ # This will supply contents for around and after filters, but not
246
+ # before filters (for the backward pass).
247
+ def end(key = nil, options = {})
248
+ object = (options || {})[:object]
249
+
250
+ return if key && !object.send("_one_time_conditions_valid_#{@callback_id}?")
251
+
252
+ if @kind == :around || @kind == :after
253
+ # if condition # after_save :filter_name, :if => :condition
254
+ # filter_name
255
+ # end
256
+ if @kind == :after
257
+ [@compiled_options[0], @filter, @compiled_options[1]].compact.join("\n")
258
+ else
259
+ "end"
260
+ end
261
+ end
262
+ end
263
+
264
+ private
265
+ # Options support the same options as filters themselves (and support
266
+ # symbols, string, procs, and objects), so compile a conditional
267
+ # expression based on the options
268
+ def _compile_options(options)
269
+ return [] if options[:if].empty? && options[:unless].empty?
270
+
271
+ conditions = []
272
+
273
+ unless options[:if].empty?
274
+ conditions << Array.wrap(_compile_filter(options[:if]))
275
+ end
276
+
277
+ unless options[:unless].empty?
278
+ conditions << Array.wrap(_compile_filter(options[:unless])).map {|f| "!#{f}"}
279
+ end
280
+
281
+ ["if #{conditions.flatten.join(" && ")}", "end"]
282
+ end
283
+
284
+ # Filters support:
285
+ # Arrays:: Used in conditions. This is used to specify
286
+ # multiple conditions. Used internally to
287
+ # merge conditions from skip_* filters
288
+ # Symbols:: A method to call
289
+ # Strings:: Some content to evaluate
290
+ # Procs:: A proc to call with the object
291
+ # Objects:: An object with a before_foo method on it to call
292
+ #
293
+ # All of these objects are compiled into methods and handled
294
+ # the same after this point:
295
+ # Arrays:: Merged together into a single filter
296
+ # Symbols:: Already methods
297
+ # Strings:: class_eval'ed into methods
298
+ # Procs:: define_method'ed into methods
299
+ # Objects::
300
+ # a method is created that calls the before_foo method
301
+ # on the object.
302
+ def _compile_filter(filter)
303
+ method_name = "_callback_#{@kind}_#{next_id}"
304
+ case filter
305
+ when Array
306
+ filter.map {|f| _compile_filter(f)}
307
+ when Symbol
308
+ filter
309
+ when String
310
+ "(#{filter})"
311
+ when Proc
312
+ @klass.send(:define_method, method_name, &filter)
313
+ return method_name if filter.arity == 0
314
+
315
+ method_name << (filter.arity == 1 ? "(self)" : " self, Proc.new ")
316
+ else
317
+ @klass.send(:define_method, "#{method_name}_object") { filter }
318
+
319
+ _normalize_legacy_filter(kind, filter)
320
+
321
+ @klass.class_eval <<-RUBY_EVAL, __FILE__, __LINE__ + 1
322
+ def #{method_name}(&blk)
323
+ #{method_name}_object.send(:#{kind}, self, &blk)
324
+ end
325
+ RUBY_EVAL
326
+
327
+ method_name
328
+ end
329
+ end
330
+
331
+ def _normalize_legacy_filter(kind, filter)
332
+ if !filter.respond_to?(kind) && filter.respond_to?(:filter)
333
+ filter.class_eval(
334
+ "def #{kind}(context, &block) filter(context, &block) end",
335
+ __FILE__, __LINE__ - 1)
336
+ elsif filter.respond_to?(:before) && filter.respond_to?(:after) && kind == :around
337
+ def filter.around(context)
338
+ should_continue = before(context)
339
+ yield if should_continue
340
+ after(context)
341
+ end
342
+ end
343
+ end
344
+
345
+ end
346
+
347
+ # An Array with a compile method
348
+ class CallbackChain < Array
349
+ def initialize(symbol)
350
+ @symbol = symbol
351
+ end
352
+
353
+ def compile(key = nil, options = {})
354
+ method = []
355
+ method << "halted = false"
356
+ each do |callback|
357
+ method << callback.start(key, options)
358
+ end
359
+ method << "yield self if block_given? && !halted"
360
+ reverse_each do |callback|
361
+ method << callback.end(key, options)
362
+ end
363
+ method.compact.join("\n")
364
+ end
365
+
366
+ def clone(klass)
367
+ chain = CallbackChain.new(@symbol)
368
+ chain.push(*map {|c| c.clone(klass)})
369
+ end
370
+ end
371
+
372
+ module ClassMethods
373
+ #CHAINS = {:before => :before, :around => :before, :after => :after}
374
+
375
+ # Make the _run_save_callbacks method. The generated method takes
376
+ # a block that it'll yield to. It'll call the before and around filters
377
+ # in order, yield the block, and then run the after filters.
378
+ #
379
+ # _run_save_callbacks do
380
+ # save
381
+ # end
382
+ #
383
+ # The _run_save_callbacks method can optionally take a key, which
384
+ # will be used to compile an optimized callback method for each
385
+ # key. See #define_callbacks for more information.
386
+ def _define_runner(symbol)
387
+ body = send("_#{symbol}_callback").
388
+ compile(nil, :terminator => send("_#{symbol}_terminator"))
389
+
390
+ body, line = <<-RUBY_EVAL, __LINE__ + 1
391
+ def _run_#{symbol}_callbacks(key = nil, &blk)
392
+ if key
393
+ name = "_run__\#{self.class.name.hash.abs}__#{symbol}__\#{key.hash.abs}__callbacks"
394
+
395
+ unless respond_to?(name)
396
+ self.class._create_keyed_callback(name, :#{symbol}, self, &blk)
397
+ end
398
+
399
+ send(name, &blk)
400
+ else
401
+ #{body}
402
+ end
403
+ end
404
+ RUBY_EVAL
405
+
406
+ undef_method "_run_#{symbol}_callbacks" if method_defined?("_run_#{symbol}_callbacks")
407
+ class_eval body, __FILE__, line
408
+ end
409
+
410
+ # This is called the first time a callback is called with a particular
411
+ # key. It creates a new callback method for the key, calculating
412
+ # which callbacks can be omitted because of per_key conditions.
413
+ def _create_keyed_callback(name, kind, obj, &blk)
414
+ @_keyed_callbacks ||= {}
415
+ @_keyed_callbacks[name] ||= begin
416
+ str = send("_#{kind}_callback").
417
+ compile(name, :object => obj, :terminator => send("_#{kind}_terminator"))
418
+
419
+ class_eval "def #{name}() #{str} end", __FILE__, __LINE__
420
+
421
+ true
422
+ end
423
+ end
424
+
425
+ # Define callbacks.
426
+ #
427
+ # Creates a <name>_callback method that you can use to add callbacks.
428
+ #
429
+ # Syntax:
430
+ # save_callback :before, :before_meth
431
+ # save_callback :after, :after_meth, :if => :condition
432
+ # save_callback :around {|r| stuff; yield; stuff }
433
+ #
434
+ # The <name>_callback method also updates the _run_<name>_callbacks
435
+ # method, which is the public API to run the callbacks.
436
+ #
437
+ # Also creates a skip_<name>_callback method that you can use to skip
438
+ # callbacks.
439
+ #
440
+ # When creating or skipping callbacks, you can specify conditions that
441
+ # are always the same for a given key. For instance, in ActionPack,
442
+ # we convert :only and :except conditions into per-key conditions.
443
+ #
444
+ # before_filter :authenticate, :except => "index"
445
+ # becomes
446
+ # dispatch_callback :before, :authenticate, :per_key => {:unless => proc {|c| c.action_name == "index"}}
447
+ #
448
+ # Per-Key conditions are evaluated only once per use of a given key.
449
+ # In the case of the above example, you would do:
450
+ #
451
+ # run_dispatch_callbacks(action_name) { ... dispatch stuff ... }
452
+ #
453
+ # In that case, each action_name would get its own compiled callback
454
+ # method that took into consideration the per_key conditions. This
455
+ # is a speed improvement for ActionPack.
456
+ def _update_callbacks(name, filters = CallbackChain.new(name), block = nil)
457
+ type = [:before, :after, :around].include?(filters.first) ? filters.shift : :before
458
+ options = filters.last.is_a?(Hash) ? filters.pop : {}
459
+ filters.unshift(block) if block
460
+
461
+ callbacks = send("_#{name}_callback")
462
+ yield callbacks, type, filters, options if block_given?
463
+
464
+ _define_runner(name)
465
+ end
466
+
467
+ alias_method :_reset_callbacks, :_update_callbacks
468
+
469
+ def set_callback(name, *filters, &block)
470
+ _update_callbacks(name, filters, block) do |callbacks, type, filters, options|
471
+ filters.map! do |filter|
472
+ # overrides parent class
473
+ callbacks.delete_if {|c| c.matches?(type, filter) }
474
+ Callback.new(filter, type, options.dup, self)
475
+ end
476
+
477
+ options[:prepend] ? callbacks.unshift(*filters) : callbacks.push(*filters)
478
+ end
479
+ end
480
+
481
+ def skip_callback(name, *filters, &block)
482
+ _update_callbacks(name, filters, block) do |callbacks, type, filters, options|
483
+ filters.each do |filter|
484
+ callbacks = send("_#{name}_callback=", callbacks.clone(self))
485
+
486
+ filter = callbacks.find {|c| c.matches?(type, filter) }
487
+
488
+ if filter && options.any?
489
+ filter.recompile!(options, options[:per_key] || {})
490
+ else
491
+ callbacks.delete(filter)
492
+ end
493
+ end
494
+ end
495
+ end
496
+
497
+ def define_callbacks(*symbols)
498
+ terminator = symbols.pop if symbols.last.is_a?(String)
499
+ symbols.each do |symbol|
500
+ extlib_inheritable_accessor("_#{symbol}_terminator") { terminator }
501
+
502
+ extlib_inheritable_accessor("_#{symbol}_callback") do
503
+ CallbackChain.new(symbol)
504
+ end
505
+
506
+ _define_runner(symbol)
507
+
508
+ # Define more convenient callback methods
509
+ # set_callback(:save, :before) becomes before_save
510
+ [:before, :after, :around].each do |filter|
511
+ self.class_eval <<-RUBY_EVAL, __FILE__, __LINE__ + 1
512
+ def self.#{filter}_#{symbol}(*symbols, &blk)
513
+ _alias_callbacks(symbols, blk) do |callback, options|
514
+ set_callback(:#{symbol}, :#{filter}, callback, options)
515
+ end
516
+ end
517
+ RUBY_EVAL
518
+ end
519
+ end
520
+ end
521
+
522
+ def _alias_callbacks(callbacks, block)
523
+ options = callbacks.last.is_a?(Hash) ? callbacks.pop : {}
524
+ callbacks.push(block) if block
525
+ callbacks.each do |callback|
526
+ yield callback, options
527
+ end
528
+ end
529
+ end
530
+ end
531
+ end
532
+ end
@@ -0,0 +1,120 @@
1
+ module CouchRest
2
+ module Mixins
3
+ module ClassProxy
4
+
5
+ def self.included(base)
6
+ base.extend(ClassMethods)
7
+ end
8
+
9
+ module ClassMethods
10
+
11
+ # Return a proxy object which represents a model class on a
12
+ # chosen database instance. This allows you to DRY operations
13
+ # where a database is chosen dynamically.
14
+ #
15
+ # ==== Example:
16
+ #
17
+ # db = CouchRest::Database.new(...)
18
+ # articles = Article.on(db)
19
+ #
20
+ # articles.all { ... }
21
+ # articles.by_title { ... }
22
+ #
23
+ # u = articles.get("someid")
24
+ #
25
+ # u = articles.new(:title => "I like plankton")
26
+ # u.save # saved on the correct database
27
+
28
+ def on(database)
29
+ Proxy.new(self, database)
30
+ end
31
+ end
32
+
33
+ class Proxy #:nodoc:
34
+ def initialize(klass, database)
35
+ @klass = klass
36
+ @database = database
37
+ end
38
+
39
+ # ExtendedDocument
40
+
41
+ def new(*args)
42
+ doc = @klass.new(*args)
43
+ doc.database = @database
44
+ doc
45
+ end
46
+
47
+ def method_missing(m, *args, &block)
48
+ if has_view?(m)
49
+ query = args.shift || {}
50
+ view(m, query, *args, &block)
51
+ else
52
+ super
53
+ end
54
+ end
55
+
56
+ # Mixins::DocumentQueries
57
+
58
+ def all(opts = {}, &block)
59
+ docs = @klass.all({:database => @database}.merge(opts), &block)
60
+ docs.each { |doc| doc.database = @database if doc.respond_to?(:database) } if docs
61
+ docs
62
+ end
63
+
64
+ def count(opts = {}, &block)
65
+ @klass.all({:database => @database, :raw => true, :limit => 0}.merge(opts), &block)['total_rows']
66
+ end
67
+
68
+ def first(opts = {})
69
+ doc = @klass.first({:database => @database}.merge(opts))
70
+ doc.database = @database if doc && doc.respond_to?(:database)
71
+ doc
72
+ end
73
+
74
+ def get(id)
75
+ doc = @klass.get(id, @database)
76
+ doc.database = @database if doc && doc.respond_to?(:database)
77
+ doc
78
+ end
79
+
80
+ # Mixins::Views
81
+
82
+ def has_view?(view)
83
+ @klass.has_view?(view)
84
+ end
85
+
86
+ def view(name, query={}, &block)
87
+ docs = @klass.view(name, {:database => @database}.merge(query), &block)
88
+ docs.each { |doc| doc.database = @database if doc.respond_to?(:database) } if docs
89
+ docs
90
+ end
91
+
92
+
93
+ # Mixins::DesignDoc
94
+
95
+ def design_doc
96
+ @klass.design_doc
97
+ end
98
+
99
+ def refresh_design_doc
100
+ @klass.refresh_design_doc(@database)
101
+ end
102
+
103
+ def save_design_doc
104
+ @klass.save_design_doc(@database)
105
+ end
106
+
107
+ # DEPRICATED
108
+ def all_design_doc_versions
109
+ @klass.all_design_doc_versions(@database)
110
+ end
111
+
112
+ def stored_design_doc
113
+ @klass.stored_design_doc(@database)
114
+ end
115
+ alias :model_design_doc :stored_design_doc
116
+
117
+ end
118
+ end
119
+ end
120
+ end