paulcarey-relaxdb 0.2.8 → 0.3.0
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/README.textile +100 -108
- data/Rakefile +13 -2
- data/docs/spec_results.html +609 -154
- data/lib/more/atomic_bulk_save_support.rb +18 -0
- data/lib/relaxdb/all_delegator.rb +15 -39
- data/lib/relaxdb/design_doc.rb +8 -6
- data/lib/relaxdb/document.rb +140 -74
- data/lib/relaxdb/has_many_proxy.rb +6 -5
- data/lib/relaxdb/has_one_proxy.rb +2 -5
- data/lib/relaxdb/paginate_params.rb +3 -4
- data/lib/relaxdb/paginator.rb +12 -16
- data/lib/relaxdb/query.rb +17 -13
- data/lib/relaxdb/references_many_proxy.rb +3 -5
- data/lib/relaxdb/relaxdb.rb +51 -45
- data/lib/relaxdb/server.rb +4 -4
- data/lib/relaxdb/view_uploader.rb +10 -8
- data/lib/relaxdb/views.rb +88 -28
- data/lib/relaxdb.rb +0 -1
- data/readme.rb +79 -0
- data/spec/belongs_to_spec.rb +1 -6
- data/spec/callbacks_spec.rb +19 -3
- data/spec/derived_properties_spec.rb +2 -7
- data/spec/design_doc_spec.rb +3 -3
- data/spec/document_spec.rb +19 -57
- data/spec/has_many_spec.rb +11 -14
- data/spec/has_one_spec.rb +1 -6
- data/spec/query_spec.rb +26 -22
- data/spec/references_many_spec.rb +1 -6
- data/spec/relaxdb_spec.rb +88 -29
- data/spec/spec_helper.rb +34 -0
- data/spec/spec_models.rb +163 -118
- metadata +7 -3
- data/lib/relaxdb/sorted_by_view.rb +0 -65
@@ -0,0 +1,18 @@
|
|
1
|
+
$:.unshift(File.dirname(__FILE__) + '/../lib')
|
2
|
+
require 'relaxdb'
|
3
|
+
require File.dirname(__FILE__) + '/../../spec/spec_models.rb'
|
4
|
+
|
5
|
+
RelaxDB.configure :host => "localhost", :port => 5984
|
6
|
+
RelaxDB.delete_db "relaxdb_spec" rescue :ok
|
7
|
+
RelaxDB.use_db "relaxdb_spec"
|
8
|
+
|
9
|
+
a1 = Atom.new.save!
|
10
|
+
a1_dup = a1.dup
|
11
|
+
a1.save!
|
12
|
+
begin
|
13
|
+
RelaxDB.bulk_save! a1_dup
|
14
|
+
puts "Atomic bulk_save _not_ supported"
|
15
|
+
rescue RelaxDB::UpdateConflict
|
16
|
+
puts "Atomic bulk_save supported"
|
17
|
+
end
|
18
|
+
|
@@ -3,65 +3,41 @@ module RelaxDB
|
|
3
3
|
#
|
4
4
|
# The AllDelegator allows clients to query CouchDB in a natural way
|
5
5
|
# FooDoc.all - returns all docs in CouchDB of type FooDoc
|
6
|
-
# FooDoc.all.sorted_by(:att1, :att2) - returns all docs in CouchDB of type FooDoc sorted by att1, then att2
|
7
|
-
# FooDoc.all.sorted_by(:att1) { |q| q.key("bar") } - returns all docs of type FooDoc where att1 equals "bar"
|
8
|
-
# FooDoc.all.destroy! - does what it says on the tin
|
9
6
|
# FooDoc.all.size - issues a query to a reduce function that returns the total number of docs for that class
|
7
|
+
# FooDoc.all.destroy! - TODO - better description
|
10
8
|
#
|
11
9
|
class AllDelegator < Delegator
|
12
10
|
|
13
|
-
def initialize(class_name)
|
11
|
+
def initialize(class_name, params)
|
14
12
|
super([])
|
15
13
|
@class_name = class_name
|
14
|
+
@params = params
|
16
15
|
end
|
17
16
|
|
18
17
|
def __getobj__
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
18
|
+
unless @objs
|
19
|
+
@objs = RelaxDB.view "#{@class_name}_all", @params
|
20
|
+
end
|
21
|
+
@objs
|
23
22
|
end
|
24
23
|
|
25
|
-
def
|
26
|
-
|
27
|
-
|
28
|
-
query = Query.new(@class_name, view.view_name)
|
29
|
-
yield query if block_given?
|
30
|
-
|
31
|
-
view.query(query)
|
24
|
+
def size
|
25
|
+
size = RelaxDB.view "#{@class_name}_all", :reduce => true
|
26
|
+
size || 0
|
32
27
|
end
|
33
28
|
|
34
|
-
#
|
29
|
+
# TODO: destroy in a bulk_save if feasible
|
35
30
|
def destroy!
|
36
|
-
|
31
|
+
__getobj__
|
32
|
+
@objs.each do |o|
|
37
33
|
# A reload is required for deleting objects with a self referential references_many relationship
|
38
|
-
# This makes all.destroy! very slow.
|
39
|
-
# soon be removed, the required reload is no longer performed.
|
34
|
+
# This makes all.destroy! very slow. Change if needed
|
40
35
|
# obj = RelaxDB.load(o._id)
|
41
36
|
# obj.destroy!
|
42
37
|
|
43
38
|
o.destroy!
|
44
39
|
end
|
45
|
-
end
|
46
|
-
|
47
|
-
# This is pretty ugly - this pattern is now spread over three
|
48
|
-
# places (sorted_by_view, relaxdb and here)
|
49
|
-
# Consolidation needed
|
50
|
-
def size
|
51
|
-
view_path = "_view/#{@class_name}/all"
|
52
|
-
map, reduce = ViewCreator.all(@class_name)
|
53
|
-
|
54
|
-
begin
|
55
|
-
resp = RelaxDB.db.get(view_path)
|
56
|
-
rescue => e
|
57
|
-
DesignDocument.get(@class_name).add_map_view("all", map).
|
58
|
-
add_reduce_view("all", reduce).save
|
59
|
-
resp = RelaxDB.db.get(view_path)
|
60
|
-
end
|
61
|
-
|
62
|
-
data = JSON.parse(resp.body)
|
63
|
-
data["rows"][0] ? data["rows"][0]["value"] : 0
|
64
|
-
end
|
40
|
+
end
|
65
41
|
|
66
42
|
end
|
67
43
|
|
data/lib/relaxdb/design_doc.rb
CHANGED
@@ -1,9 +1,11 @@
|
|
1
1
|
module RelaxDB
|
2
2
|
|
3
3
|
class DesignDocument
|
4
|
+
|
5
|
+
attr_reader :data
|
4
6
|
|
5
|
-
def initialize(
|
6
|
-
@
|
7
|
+
def initialize(design_doc_name, data)
|
8
|
+
@design_doc_name = design_doc_name
|
7
9
|
@data = data
|
8
10
|
end
|
9
11
|
|
@@ -34,13 +36,13 @@ module RelaxDB
|
|
34
36
|
self
|
35
37
|
end
|
36
38
|
|
37
|
-
def self.get(
|
39
|
+
def self.get(design_doc_name)
|
38
40
|
begin
|
39
41
|
database = RelaxDB.db
|
40
|
-
resp = database.get("_design/#{
|
41
|
-
DesignDocument.new(
|
42
|
+
resp = database.get("_design/#{design_doc_name}")
|
43
|
+
DesignDocument.new(design_doc_name, JSON.parse(resp.body))
|
42
44
|
rescue HTTP_404
|
43
|
-
DesignDocument.new(
|
45
|
+
DesignDocument.new(design_doc_name, {"_id" => "_design/#{design_doc_name}"} )
|
44
46
|
end
|
45
47
|
end
|
46
48
|
|
data/lib/relaxdb/document.rb
CHANGED
@@ -7,16 +7,28 @@ module RelaxDB
|
|
7
7
|
# Used to store validation messages
|
8
8
|
attr_accessor :errors
|
9
9
|
|
10
|
+
# A call issued to save_all will save this object and the
|
11
|
+
# contents of the save_list. This allows secondary object to
|
12
|
+
# be saved at the same time as this object.
|
13
|
+
attr_accessor :save_list
|
14
|
+
|
10
15
|
# Attribute symbols added to this list won't be validated on save
|
11
16
|
attr_accessor :validation_skip_list
|
12
17
|
|
13
|
-
|
18
|
+
class_inheritable_accessor :properties, :reader => true
|
19
|
+
self.properties = []
|
20
|
+
|
21
|
+
class_inheritable_accessor :derived_prop_writers
|
22
|
+
self.derived_prop_writers = {}
|
23
|
+
|
24
|
+
class_inheritable_accessor :__view_by_list__
|
25
|
+
self.__view_by_list__ = []
|
14
26
|
|
27
|
+
class_inheritable_accessor :belongs_to_rels, :reder => true
|
28
|
+
self.belongs_to_rels = {}
|
29
|
+
|
15
30
|
def self.property(prop, opts={})
|
16
|
-
|
17
|
-
# Perhaps a better solution exists. Revise. I think extlib contains a solution for this...
|
18
|
-
@properties ||= [:_id, :_rev]
|
19
|
-
@properties << prop
|
31
|
+
properties << prop
|
20
32
|
|
21
33
|
define_method(prop) do
|
22
34
|
instance_variable_get("@#{prop}".to_sym)
|
@@ -45,12 +57,10 @@ module RelaxDB
|
|
45
57
|
if opts[:derived]
|
46
58
|
add_derived_prop(prop, opts[:derived])
|
47
59
|
end
|
48
|
-
end
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
@properties ||= [:_id, :_rev]
|
53
|
-
end
|
60
|
+
end
|
61
|
+
|
62
|
+
property :_id
|
63
|
+
property :_rev
|
54
64
|
|
55
65
|
def self.create_validator(att, v)
|
56
66
|
method_name = "validate_#{att}"
|
@@ -78,15 +88,10 @@ module RelaxDB
|
|
78
88
|
# See derived_properties_spec.rb for usage
|
79
89
|
def self.add_derived_prop(prop, deriver)
|
80
90
|
source, writer = deriver[0], deriver[1]
|
81
|
-
|
82
|
-
|
83
|
-
@derived_prop_writers[source][prop] = writer
|
84
|
-
end
|
85
|
-
|
86
|
-
def self.derived_prop_writers
|
87
|
-
@derived_prop_writers ||= {}
|
91
|
+
derived_prop_writers[source] ||= {}
|
92
|
+
derived_prop_writers[source][prop] = writer
|
88
93
|
end
|
89
|
-
|
94
|
+
|
90
95
|
#
|
91
96
|
# The rationale for rescuing the send below is that the lambda for a derived
|
92
97
|
# property shouldn't need to concern itself with checking the validity of
|
@@ -94,27 +99,19 @@ module RelaxDB
|
|
94
99
|
# possibility of a writer raising an exception.
|
95
100
|
#
|
96
101
|
def write_derived_props(source)
|
97
|
-
writers = self.class.derived_prop_writers
|
102
|
+
writers = self.class.derived_prop_writers
|
103
|
+
writers = writers && writers[source]
|
98
104
|
if writers
|
99
105
|
writers.each do |prop, writer|
|
100
106
|
current_val = send(prop)
|
101
107
|
begin
|
102
108
|
send("#{prop}=", writer.call(current_val, self))
|
103
109
|
rescue => e
|
104
|
-
RelaxDB.logger.
|
110
|
+
RelaxDB.logger.error "Deriving #{prop} from #{source} raised #{e}"
|
105
111
|
end
|
106
112
|
end
|
107
113
|
end
|
108
114
|
end
|
109
|
-
|
110
|
-
def properties
|
111
|
-
self.class.properties
|
112
|
-
end
|
113
|
-
|
114
|
-
# Specifying these properties here (after property method has been defined)
|
115
|
-
# is kinda ugly. Consider a better solution.
|
116
|
-
property :_id
|
117
|
-
property :_rev
|
118
115
|
|
119
116
|
def initialize(hash={})
|
120
117
|
unless hash["_id"]
|
@@ -122,6 +119,7 @@ module RelaxDB
|
|
122
119
|
end
|
123
120
|
|
124
121
|
@errors = Errors.new
|
122
|
+
@save_list = []
|
125
123
|
@validation_skip_list = []
|
126
124
|
|
127
125
|
# Set default properties if this object isn't being loaded from CouchDB
|
@@ -165,6 +163,7 @@ module RelaxDB
|
|
165
163
|
s << ", #{relationship}_id: #{id}" if id
|
166
164
|
end
|
167
165
|
s << ", errors: #{errors.inspect}" unless errors.empty?
|
166
|
+
s << ", save_list: #{save_list.map { |o| o.inspect }.join ", " }" unless save_list.empty?
|
168
167
|
s << ">"
|
169
168
|
end
|
170
169
|
|
@@ -180,7 +179,7 @@ module RelaxDB
|
|
180
179
|
prop_val = instance_variable_get("@#{prop}".to_sym)
|
181
180
|
data["#{prop}"] = prop_val if prop_val
|
182
181
|
end
|
183
|
-
data["
|
182
|
+
data["relaxdb_class"] = self.class.name
|
184
183
|
data.to_json
|
185
184
|
end
|
186
185
|
|
@@ -211,9 +210,9 @@ module RelaxDB
|
|
211
210
|
end
|
212
211
|
|
213
212
|
def pre_save
|
213
|
+
set_created_at if new_document?
|
214
214
|
return false unless validates?
|
215
215
|
return false unless before_save
|
216
|
-
set_created_at if new_document?
|
217
216
|
true
|
218
217
|
end
|
219
218
|
|
@@ -221,6 +220,15 @@ module RelaxDB
|
|
221
220
|
after_save
|
222
221
|
end
|
223
222
|
|
223
|
+
# save_all and save_all! are untested
|
224
|
+
def save_all
|
225
|
+
RelaxDB.bulk_save self, *save_list
|
226
|
+
end
|
227
|
+
|
228
|
+
def save_all!
|
229
|
+
RelaxDB.bulk_save! self, *save_list
|
230
|
+
end
|
231
|
+
|
224
232
|
def save!
|
225
233
|
if save
|
226
234
|
self
|
@@ -230,21 +238,7 @@ module RelaxDB
|
|
230
238
|
raise ValidationFailure, self.errors.to_json
|
231
239
|
end
|
232
240
|
end
|
233
|
-
|
234
|
-
def save_all
|
235
|
-
RelaxDB.bulk_save(self, *all_children)
|
236
|
-
end
|
237
|
-
|
238
|
-
def save_all!
|
239
|
-
RelaxDB.bulk_save!(self, *all_children)
|
240
|
-
end
|
241
|
-
|
242
|
-
def all_children
|
243
|
-
ho = self.class.has_one_rels.map { |r| send(r) }
|
244
|
-
hm = self.class.has_many_rels.inject([]) { |m,r| m += send(r).children }
|
245
|
-
ho + hm
|
246
|
-
end
|
247
|
-
|
241
|
+
|
248
242
|
def update_conflict?
|
249
243
|
@update_conflict
|
250
244
|
end
|
@@ -269,6 +263,7 @@ module RelaxDB
|
|
269
263
|
|
270
264
|
total_success
|
271
265
|
end
|
266
|
+
alias_method :validate, :validates?
|
272
267
|
|
273
268
|
def validate_att(att_name, att_val)
|
274
269
|
begin
|
@@ -286,7 +281,8 @@ module RelaxDB
|
|
286
281
|
RelaxDB.logger.warn "Validation_msg for #{att_name} with #{att_val} raised #{e}"
|
287
282
|
@errors[att_name] = "validation_msg_exception:invalid:#{att_val}"
|
288
283
|
end
|
289
|
-
|
284
|
+
elsif @errors[att_name].nil?
|
285
|
+
# Only set a validation message if a validator hasn't already set one
|
290
286
|
@errors[att_name] = "invalid:#{att_val}"
|
291
287
|
end
|
292
288
|
end
|
@@ -337,6 +333,12 @@ module RelaxDB
|
|
337
333
|
@references_many_rels << relationship
|
338
334
|
|
339
335
|
id_arr_sym = "@#{relationship}".to_sym
|
336
|
+
|
337
|
+
if RelaxDB.create_views?
|
338
|
+
target_class = opts[:class]
|
339
|
+
relationship_as_viewed_by_target = opts[:known_as].to_s
|
340
|
+
ViewCreator.references_many(self.name, relationship, target_class, relationship_as_viewed_by_target).save
|
341
|
+
end
|
340
342
|
|
341
343
|
define_method(relationship) do
|
342
344
|
instance_variable_set(id_arr_sym, []) unless instance_variable_defined? id_arr_sym
|
@@ -362,6 +364,12 @@ module RelaxDB
|
|
362
364
|
@has_many_rels ||= []
|
363
365
|
@has_many_rels << relationship
|
364
366
|
|
367
|
+
if RelaxDB.create_views?
|
368
|
+
target_class = opts[:class]
|
369
|
+
relationship_as_viewed_by_target = (opts[:known_as] || self.name.snake_case).to_s
|
370
|
+
ViewCreator.has_n(self.name, relationship, target_class, relationship_as_viewed_by_target).save
|
371
|
+
end
|
372
|
+
|
365
373
|
define_method(relationship) do
|
366
374
|
create_or_get_proxy(HasManyProxy, relationship, opts)
|
367
375
|
end
|
@@ -382,6 +390,12 @@ module RelaxDB
|
|
382
390
|
@has_one_rels ||= []
|
383
391
|
@has_one_rels << relationship
|
384
392
|
|
393
|
+
if RelaxDB.create_views?
|
394
|
+
target_class = relationship.to_s.camel_case
|
395
|
+
relationship_as_viewed_by_target = self.name.snake_case
|
396
|
+
ViewCreator.has_n(self.name, relationship, target_class, relationship_as_viewed_by_target).save
|
397
|
+
end
|
398
|
+
|
385
399
|
define_method(relationship) do
|
386
400
|
create_or_get_proxy(HasOneProxy, relationship).target
|
387
401
|
end
|
@@ -398,8 +412,7 @@ module RelaxDB
|
|
398
412
|
end
|
399
413
|
|
400
414
|
def self.belongs_to(relationship, opts={})
|
401
|
-
|
402
|
-
@belongs_to_rels[relationship] = opts
|
415
|
+
belongs_to_rels[relationship] = opts
|
403
416
|
|
404
417
|
define_method(relationship) do
|
405
418
|
create_or_get_proxy(BelongsToProxy, relationship).target
|
@@ -417,7 +430,6 @@ module RelaxDB
|
|
417
430
|
id
|
418
431
|
end
|
419
432
|
|
420
|
-
# Allows belongs_to relationships to be used by the paginator
|
421
433
|
define_method("#{relationship}_id") do
|
422
434
|
instance_variable_get("@#{relationship}_id")
|
423
435
|
end
|
@@ -432,16 +444,14 @@ module RelaxDB
|
|
432
444
|
alias_method :references, :belongs_to
|
433
445
|
end
|
434
446
|
|
435
|
-
|
436
|
-
@belongs_to_rels ||= {}
|
437
|
-
end
|
447
|
+
self.belongs_to_rels = {}
|
438
448
|
|
439
449
|
def self.all_relationships
|
440
450
|
belongs_to_rels + has_one_rels + has_many_rels + references_many_rels
|
441
451
|
end
|
442
452
|
|
443
|
-
def self.all
|
444
|
-
|
453
|
+
def self.all params = {}
|
454
|
+
AllDelegator.new self.name, params
|
445
455
|
end
|
446
456
|
|
447
457
|
# destroy! nullifies all relationships with peers and children before deleting
|
@@ -482,7 +492,10 @@ module RelaxDB
|
|
482
492
|
def before_save
|
483
493
|
self.class.before_save_callbacks.each do |callback|
|
484
494
|
resp = callback.is_a?(Proc) ? callback.call(self) : send(callback)
|
485
|
-
|
495
|
+
if resp == false
|
496
|
+
errors[:before_save] = :failed
|
497
|
+
return false
|
498
|
+
end
|
486
499
|
end
|
487
500
|
end
|
488
501
|
|
@@ -499,27 +512,80 @@ module RelaxDB
|
|
499
512
|
callback.is_a?(Proc) ? callback.call(self) : send(callback)
|
500
513
|
end
|
501
514
|
end
|
502
|
-
|
503
|
-
|
504
|
-
|
505
|
-
|
506
|
-
|
507
|
-
|
508
|
-
|
509
|
-
|
510
|
-
design_doc_name = self.name
|
511
|
-
view = SortedByView.new(design_doc_name, *view_keys)
|
512
|
-
query = Query.new(design_doc_name, view.view_name)
|
513
|
-
query.merge(paginate_params)
|
515
|
+
|
516
|
+
#
|
517
|
+
# Creates the corresponding view and stores it in CouchDB
|
518
|
+
# Adds by_ and paginate_by_ methods to the class
|
519
|
+
#
|
520
|
+
def self.view_by *atts
|
521
|
+
opts = atts.last.is_a?(Hash) ? atts.pop : {}
|
522
|
+
__view_by_list__ << atts
|
514
523
|
|
515
|
-
|
516
|
-
|
524
|
+
if RelaxDB.create_views?
|
525
|
+
ViewCreator.by_att_list([self.name], *atts).save
|
526
|
+
end
|
517
527
|
|
518
|
-
|
528
|
+
by_name = "by_#{atts.join "_and_"}"
|
529
|
+
meta_class.instance_eval do
|
530
|
+
define_method by_name do |*params|
|
531
|
+
view_name = "#{self.name}_#{by_name}"
|
532
|
+
if params.empty?
|
533
|
+
res = RelaxDB.view view_name, opts
|
534
|
+
elsif params[0].is_a? Hash
|
535
|
+
res = RelaxDB.view view_name, opts.merge(params[0])
|
536
|
+
else
|
537
|
+
res = RelaxDB.view(view_name, :key => params[0]).first
|
538
|
+
end
|
539
|
+
end
|
540
|
+
end
|
519
541
|
|
520
|
-
|
542
|
+
paginate_by_name = "paginate_by_#{atts.join "_and_"}"
|
543
|
+
meta_class.instance_eval do
|
544
|
+
define_method paginate_by_name do |params|
|
545
|
+
view_name = "#{self.name}_#{by_name}"
|
546
|
+
params[:attributes] = atts
|
547
|
+
params = opts.merge params
|
548
|
+
RelaxDB.paginate_view view_name, params
|
549
|
+
end
|
550
|
+
end
|
551
|
+
end
|
552
|
+
|
553
|
+
# Create a view allowing all instances of a particular class to be retreived
|
554
|
+
def self.create_all_by_class_view
|
555
|
+
if RelaxDB.create_views?
|
556
|
+
view = ViewCreator.all
|
557
|
+
view.save unless view.exists?
|
558
|
+
end
|
559
|
+
end
|
560
|
+
|
561
|
+
def self.inherited subclass
|
562
|
+
chain = subclass.up_chain
|
563
|
+
while k = chain.pop
|
564
|
+
k.create_views chain
|
565
|
+
end
|
566
|
+
end
|
567
|
+
|
568
|
+
def self.up_chain
|
569
|
+
k = self
|
570
|
+
kls = [k]
|
571
|
+
kls << k while ((k = k.superclass) != RelaxDB::Document)
|
572
|
+
kls
|
573
|
+
end
|
574
|
+
|
575
|
+
def self.create_views chain
|
576
|
+
# Capture the inheritance hierarchy of this class
|
577
|
+
@hierarchy ||= [self]
|
578
|
+
@hierarchy += chain
|
579
|
+
@hierarchy.uniq!
|
580
|
+
|
581
|
+
if RelaxDB.create_views?
|
582
|
+
ViewCreator.all(@hierarchy).save
|
583
|
+
__view_by_list__.each do |atts|
|
584
|
+
ViewCreator.by_att_list(@hierarchy, *atts).save
|
585
|
+
end
|
586
|
+
end
|
521
587
|
end
|
522
|
-
|
588
|
+
|
523
589
|
end
|
524
590
|
|
525
591
|
end
|
@@ -77,11 +77,8 @@ module RelaxDB
|
|
77
77
|
end
|
78
78
|
|
79
79
|
def load_children
|
80
|
-
|
81
|
-
|
82
|
-
view_name = @relationship
|
83
|
-
map_function = ViewCreator.has_n(@target_class, @relationship_as_viewed_by_target)
|
84
|
-
@children = RelaxDB.retrieve(view_path, design_doc, view_name, map_function)
|
80
|
+
view_name = "#{@client.class}_#{@relationship}"
|
81
|
+
@children = RelaxDB.view(view_name, :key => @client._id)
|
85
82
|
end
|
86
83
|
|
87
84
|
def children=(children)
|
@@ -94,6 +91,10 @@ module RelaxDB
|
|
94
91
|
def inspect
|
95
92
|
@children.inspect
|
96
93
|
end
|
94
|
+
|
95
|
+
# Play nice with Merb partials - [ obj ].flatten invokes
|
96
|
+
# obj.to_ary if it responds to to_ary
|
97
|
+
alias_method :to_ary, :to_a
|
97
98
|
|
98
99
|
end
|
99
100
|
|
@@ -33,11 +33,8 @@ module RelaxDB
|
|
33
33
|
end
|
34
34
|
|
35
35
|
def load_target
|
36
|
-
|
37
|
-
view_name
|
38
|
-
view_path = "_view/#{design_doc}/#{view_name}?key=\"#{@client._id}\""
|
39
|
-
map_function = ViewCreator.has_n(@target_class, @relationship_as_viewed_by_target)
|
40
|
-
RelaxDB.retrieve(view_path, design_doc, view_name, map_function).first
|
36
|
+
view_name = "#{@client.class}_#{@relationship}"
|
37
|
+
RelaxDB.view(view_name, :key => @client._id).first
|
41
38
|
end
|
42
39
|
|
43
40
|
end
|
@@ -17,11 +17,11 @@ module RelaxDB
|
|
17
17
|
end
|
18
18
|
end
|
19
19
|
|
20
|
-
def initialize
|
20
|
+
def initialize(params)
|
21
|
+
params.each { |k, v| send(k, v) }
|
22
|
+
|
21
23
|
# If a client hasn't explicitly set descending, set it to the CouchDB default
|
22
24
|
@descending = false if @descending.nil?
|
23
|
-
# CouchDB defaults reduce to true when a reduce func is present
|
24
|
-
@reduce = false
|
25
25
|
end
|
26
26
|
|
27
27
|
def update(params)
|
@@ -44,7 +44,6 @@ module RelaxDB
|
|
44
44
|
|
45
45
|
def invalid?
|
46
46
|
# Simply because allowing either to be omitted increases the complexity of the paginator
|
47
|
-
# This constraint may be removed in future, but don't hold your breath
|
48
47
|
@startkey_set && @endkey_set ? nil : "Both startkey and endkey must be set"
|
49
48
|
end
|
50
49
|
alias error_msg invalid?
|
data/lib/relaxdb/paginator.rb
CHANGED
@@ -13,21 +13,17 @@ module RelaxDB
|
|
13
13
|
@paginate_params.update(page_params)
|
14
14
|
end
|
15
15
|
|
16
|
-
def total_doc_count(
|
17
|
-
|
18
|
-
|
19
|
-
q.startkey(@orig_paginate_params.startkey).endkey(@orig_paginate_params.endkey).descending(@orig_paginate_params.descending)
|
20
|
-
end
|
21
|
-
|
22
|
-
total_docs = RelaxDB.reduce_result(result)
|
16
|
+
def total_doc_count(view_name)
|
17
|
+
RelaxDB.view view_name, :startkey => @orig_paginate_params.startkey, :endkey => @orig_paginate_params.endkey,
|
18
|
+
:descending => @orig_paginate_params.descending, :reduce => true
|
23
19
|
end
|
24
20
|
|
25
|
-
def add_next_and_prev(docs,
|
21
|
+
def add_next_and_prev(docs, view_name, view_keys)
|
26
22
|
unless docs.empty?
|
27
23
|
no_docs = docs.size
|
28
24
|
offset = docs.offset
|
29
|
-
orig_offset = orig_offset(
|
30
|
-
total_doc_count = total_doc_count(
|
25
|
+
orig_offset = orig_offset(view_name)
|
26
|
+
total_doc_count = total_doc_count(view_name)
|
31
27
|
|
32
28
|
next_exists = !@paginate_params.order_inverted? ? (offset - orig_offset + no_docs < total_doc_count) : true
|
33
29
|
next_params = create_next(docs.last, view_keys) if next_exists
|
@@ -62,15 +58,15 @@ module RelaxDB
|
|
62
58
|
prev_params = { :startkey => prev_key, :startkey_docid => prev_key_docid, :descending => !@orig_paginate_params.descending }
|
63
59
|
end
|
64
60
|
|
65
|
-
def orig_offset(
|
66
|
-
query = Query.new(design_doc, view_name)
|
61
|
+
def orig_offset(view_name)
|
67
62
|
if @paginate_params.order_inverted?
|
68
|
-
|
63
|
+
params = {:startkey => @orig_paginate_params.endkey, :descending => !@orig_paginate_params.descending}
|
69
64
|
else
|
70
|
-
|
65
|
+
params = {:startkey => @orig_paginate_params.startkey, :descending => @orig_paginate_params.descending}
|
71
66
|
end
|
72
|
-
|
73
|
-
|
67
|
+
params[:limit] = 1
|
68
|
+
|
69
|
+
RelaxDB.view(view_name, params).offset
|
74
70
|
end
|
75
71
|
|
76
72
|
end
|
data/lib/relaxdb/query.rb
CHANGED
@@ -3,17 +3,7 @@ module RelaxDB
|
|
3
3
|
# A Query is used to build the query string made against a view
|
4
4
|
# All parameter values are first JSON encoded and then URL encoded
|
5
5
|
# Nil values are set to the empty string
|
6
|
-
# All parameter calls return self so calls may be chained => q.startkey("foo").endkey("bar").limit(2)
|
7
6
|
|
8
|
-
#
|
9
|
-
# The query object is currently inconsistent with the RelaxDB object idiom. Consider
|
10
|
-
# paul = User.new(:name => "paul").save; Event.new(:host=>paul).save
|
11
|
-
# but an event query requires
|
12
|
-
# Event.all.sorted_by(:host_id) { |q| q.key(paul._id) }
|
13
|
-
# rather than
|
14
|
-
# Event.all.sorted_by(:host) { |q| q.key(paul) }
|
15
|
-
# I feel that both forms should be supported
|
16
|
-
#
|
17
7
|
class Query
|
18
8
|
|
19
9
|
# keys is not included in the standard param as it is significantly different from the others
|
@@ -32,9 +22,13 @@ module RelaxDB
|
|
32
22
|
end
|
33
23
|
end
|
34
24
|
|
35
|
-
def initialize(
|
36
|
-
|
25
|
+
def initialize(view_name, params = {})
|
26
|
+
# CouchDB defaults reduce to true when a reduce func is present
|
27
|
+
# but returning the map view is typically more useful
|
28
|
+
reduce(false)
|
29
|
+
|
37
30
|
@view_name = view_name
|
31
|
+
params.each { |k, v| send(k, v) }
|
38
32
|
end
|
39
33
|
|
40
34
|
def keys(keys=nil)
|
@@ -46,8 +40,18 @@ module RelaxDB
|
|
46
40
|
end
|
47
41
|
end
|
48
42
|
|
43
|
+
# If set to true RelaxDB.view will return unprocessed data
|
44
|
+
def raw(val = nil)
|
45
|
+
if val.nil?
|
46
|
+
@raw
|
47
|
+
else
|
48
|
+
@raw = val
|
49
|
+
self
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
49
53
|
def view_path
|
50
|
-
uri = "
|
54
|
+
uri = "_design/#{RelaxDB.dd}/_view/#{@view_name}"
|
51
55
|
|
52
56
|
query = ""
|
53
57
|
@@params.each do |param|
|