cohitre-relaxdb 0.2.2
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/LICENSE +20 -0
- data/README.textile +164 -0
- data/Rakefile +52 -0
- data/docs/spec_results.html +604 -0
- data/lib/more/grapher.rb +48 -0
- data/lib/relaxdb.rb +38 -0
- data/lib/relaxdb/all_delegator.rb +48 -0
- data/lib/relaxdb/belongs_to_proxy.rb +29 -0
- data/lib/relaxdb/design_doc.rb +50 -0
- data/lib/relaxdb/document.rb +386 -0
- data/lib/relaxdb/extlib.rb +3 -0
- data/lib/relaxdb/has_many_proxy.rb +81 -0
- data/lib/relaxdb/has_one_proxy.rb +45 -0
- data/lib/relaxdb/paginate_params.rb +54 -0
- data/lib/relaxdb/paginator.rb +78 -0
- data/lib/relaxdb/query.rb +74 -0
- data/lib/relaxdb/references_many_proxy.rb +99 -0
- data/lib/relaxdb/relaxdb.rb +157 -0
- data/lib/relaxdb/server.rb +132 -0
- data/lib/relaxdb/sorted_by_view.rb +62 -0
- data/lib/relaxdb/uuid_generator.rb +21 -0
- data/lib/relaxdb/view_object.rb +34 -0
- data/lib/relaxdb/view_result.rb +18 -0
- data/lib/relaxdb/view_uploader.rb +47 -0
- data/lib/relaxdb/views.rb +42 -0
- data/spec/belongs_to_spec.rb +80 -0
- data/spec/callbacks_spec.rb +64 -0
- data/spec/denormalisation_spec.rb +49 -0
- data/spec/design_doc_spec.rb +34 -0
- data/spec/document_spec.rb +364 -0
- data/spec/has_many_spec.rb +147 -0
- data/spec/has_one_spec.rb +128 -0
- data/spec/query_spec.rb +80 -0
- data/spec/references_many_spec.rb +141 -0
- data/spec/relaxdb_spec.rb +137 -0
- data/spec/spec.opts +1 -0
- data/spec/spec_helper.rb +10 -0
- data/spec/spec_models.rb +130 -0
- data/spec/view_object_spec.rb +47 -0
- metadata +119 -0
@@ -0,0 +1,81 @@
|
|
1
|
+
module RelaxDB
|
2
|
+
|
3
|
+
class HasManyProxy
|
4
|
+
|
5
|
+
include Enumerable
|
6
|
+
|
7
|
+
def initialize(client, relationship, opts)
|
8
|
+
@client = client
|
9
|
+
@relationship = relationship
|
10
|
+
@opts = opts
|
11
|
+
|
12
|
+
@target_class = opts[:class]
|
13
|
+
@relationship_as_viewed_by_target = (opts[:known_as] || client.class.name.snake_case).to_s
|
14
|
+
|
15
|
+
@children = load_children
|
16
|
+
end
|
17
|
+
|
18
|
+
def <<(obj)
|
19
|
+
return false unless obj.validates?
|
20
|
+
return false if @children.include?(obj)
|
21
|
+
|
22
|
+
obj.send("#{@relationship_as_viewed_by_target}=".to_sym, @client)
|
23
|
+
obj.save
|
24
|
+
@children << obj
|
25
|
+
self
|
26
|
+
end
|
27
|
+
|
28
|
+
def clear
|
29
|
+
@children.each do |c|
|
30
|
+
break_back_link c
|
31
|
+
end
|
32
|
+
@children.clear
|
33
|
+
end
|
34
|
+
|
35
|
+
def delete(obj)
|
36
|
+
obj = @children.delete(obj)
|
37
|
+
break_back_link(obj) if obj
|
38
|
+
end
|
39
|
+
|
40
|
+
def break_back_link(obj)
|
41
|
+
if obj
|
42
|
+
obj.send("#{@relationship_as_viewed_by_target}=".to_sym, nil)
|
43
|
+
obj.save
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
def empty?
|
48
|
+
@children.empty?
|
49
|
+
end
|
50
|
+
|
51
|
+
def size
|
52
|
+
@children.size
|
53
|
+
end
|
54
|
+
|
55
|
+
def [](*args)
|
56
|
+
@children[*args]
|
57
|
+
end
|
58
|
+
|
59
|
+
def each(&blk)
|
60
|
+
@children.each(&blk)
|
61
|
+
end
|
62
|
+
|
63
|
+
def reload
|
64
|
+
@children = load_children
|
65
|
+
end
|
66
|
+
|
67
|
+
def load_children
|
68
|
+
view_path = "_view/#{@client.class}/#{@relationship}?key=\"#{@client._id}\""
|
69
|
+
design_doc = @client.class
|
70
|
+
view_name = @relationship
|
71
|
+
map_function = ViewCreator.has_n(@target_class, @relationship_as_viewed_by_target)
|
72
|
+
@children = RelaxDB.retrieve(view_path, design_doc, view_name, map_function)
|
73
|
+
end
|
74
|
+
|
75
|
+
def inspect
|
76
|
+
@children.inspect
|
77
|
+
end
|
78
|
+
|
79
|
+
end
|
80
|
+
|
81
|
+
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
module RelaxDB
|
2
|
+
|
3
|
+
class HasOneProxy
|
4
|
+
|
5
|
+
def initialize(client, relationship)
|
6
|
+
@client = client
|
7
|
+
@relationship = relationship
|
8
|
+
@target_class = @relationship.to_s.camel_case
|
9
|
+
@relationship_as_viewed_by_target = client.class.name.snake_case
|
10
|
+
|
11
|
+
@target = nil
|
12
|
+
end
|
13
|
+
|
14
|
+
def target
|
15
|
+
return @target if @target
|
16
|
+
@target = load_target
|
17
|
+
end
|
18
|
+
|
19
|
+
# All database changes performed by this method would ideally be done in a transaction
|
20
|
+
def target=(new_target)
|
21
|
+
# Nullify any existing relationship on assignment
|
22
|
+
old_target = target
|
23
|
+
if old_target
|
24
|
+
old_target.send("#{@relationship_as_viewed_by_target}=".to_sym, nil)
|
25
|
+
old_target.save
|
26
|
+
end
|
27
|
+
|
28
|
+
@target = new_target
|
29
|
+
unless @target.nil?
|
30
|
+
@target.send("#{@relationship_as_viewed_by_target}=".to_sym, @client)
|
31
|
+
@target.save
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
def load_target
|
36
|
+
design_doc = @client.class
|
37
|
+
view_name = @relationship
|
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
|
41
|
+
end
|
42
|
+
|
43
|
+
end
|
44
|
+
|
45
|
+
end
|
@@ -0,0 +1,54 @@
|
|
1
|
+
module RelaxDB
|
2
|
+
|
3
|
+
class PaginateParams
|
4
|
+
|
5
|
+
@@params = %w(key startkey startkey_docid endkey endkey_docid count update descending group reduce include_docs)
|
6
|
+
|
7
|
+
@@params.each do |param|
|
8
|
+
define_method(param.to_sym) do |*val|
|
9
|
+
if val.empty?
|
10
|
+
instance_variable_get("@#{param}")
|
11
|
+
else
|
12
|
+
instance_variable_set("@#{param}", val[0])
|
13
|
+
# null is meaningful to CouchDB. _set allows us to know that a param has been set, even to nil
|
14
|
+
instance_variable_set("@#{param}_set", true)
|
15
|
+
self
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
def initialize
|
21
|
+
# If a client hasn't explicitly set descending, set it to the CouchDB default
|
22
|
+
@descending = false if @descending.nil?
|
23
|
+
# CouchDB defaults reduce to true when a reduce func is present
|
24
|
+
@reduce = false
|
25
|
+
end
|
26
|
+
|
27
|
+
def update(params)
|
28
|
+
@order_inverted = params[:descending].nil? ? false : @descending ^ params[:descending]
|
29
|
+
@descending = !@descending if @order_inverted
|
30
|
+
|
31
|
+
@endkey = @startkey if @order_inverted
|
32
|
+
|
33
|
+
@startkey = params[:startkey] || @startkey
|
34
|
+
|
35
|
+
@skip = 1 if params[:startkey]
|
36
|
+
|
37
|
+
@startkey_docid = params[:startkey_docid] if params[:startkey_docid]
|
38
|
+
@endkey_docid = params[:endkey_docid] if params[:endkey_docid]
|
39
|
+
end
|
40
|
+
|
41
|
+
def order_inverted?
|
42
|
+
@order_inverted
|
43
|
+
end
|
44
|
+
|
45
|
+
def invalid?
|
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
|
+
@startkey_set && @endkey_set ? nil : "Both startkey and endkey must be set"
|
49
|
+
end
|
50
|
+
alias error_msg invalid?
|
51
|
+
|
52
|
+
end
|
53
|
+
|
54
|
+
end
|
@@ -0,0 +1,78 @@
|
|
1
|
+
module RelaxDB
|
2
|
+
|
3
|
+
class Paginator
|
4
|
+
|
5
|
+
attr_reader :paginate_params
|
6
|
+
|
7
|
+
def initialize(paginate_params, page_params)
|
8
|
+
@paginate_params = paginate_params
|
9
|
+
@orig_paginate_params = @paginate_params.clone
|
10
|
+
|
11
|
+
page_params = page_params.is_a?(String) ? JSON.parse(page_params).to_mash : page_params
|
12
|
+
# Where the magic happens - the original params are updated with the page specific params
|
13
|
+
@paginate_params.update(page_params)
|
14
|
+
end
|
15
|
+
|
16
|
+
def total_doc_count(design_doc, view_name)
|
17
|
+
result = RelaxDB.view(design_doc, view_name) do |q|
|
18
|
+
q.group(true).group_level(0).reduce(true)
|
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)
|
23
|
+
end
|
24
|
+
|
25
|
+
def add_next_and_prev(docs, design_doc, view_name, view_keys)
|
26
|
+
unless docs.empty?
|
27
|
+
no_docs = docs.size
|
28
|
+
offset = docs.offset
|
29
|
+
orig_offset = orig_offset(design_doc, view_name)
|
30
|
+
total_doc_count = total_doc_count(design_doc, view_name)
|
31
|
+
|
32
|
+
next_exists = !@paginate_params.order_inverted? ? (offset - orig_offset + no_docs < total_doc_count) : true
|
33
|
+
next_params = create_next(docs.last, view_keys) if next_exists
|
34
|
+
|
35
|
+
prev_exists = @paginate_params.order_inverted? ? (offset - orig_offset + no_docs < total_doc_count) :
|
36
|
+
(offset - orig_offset == 0 ? false : true)
|
37
|
+
prev_params = create_prev(docs.first, view_keys) if prev_exists
|
38
|
+
else
|
39
|
+
next_exists = prev_exists = false
|
40
|
+
end
|
41
|
+
|
42
|
+
docs.meta_class.instance_eval do
|
43
|
+
define_method(:next_params) { next_exists ? next_params : false }
|
44
|
+
define_method(:next_query) { next_exists ? "page_params=#{::CGI::escape(next_params.to_json)}" : false }
|
45
|
+
|
46
|
+
define_method(:prev_params) { prev_exists ? prev_params : false }
|
47
|
+
define_method(:prev_query) { prev_exists ? "page_params=#{::CGI::escape(prev_params.to_json)}" : false }
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
def create_next(doc, view_keys)
|
52
|
+
next_key = view_keys.map { |a| doc.send(a) }
|
53
|
+
next_key = next_key.length == 1 ? next_key[0] : next_key
|
54
|
+
next_key_docid = doc._id
|
55
|
+
{ :startkey => next_key, :startkey_docid => next_key_docid, :descending => @orig_paginate_params.descending }
|
56
|
+
end
|
57
|
+
|
58
|
+
def create_prev(doc, view_keys)
|
59
|
+
prev_key = view_keys.map { |a| doc.send(a) }
|
60
|
+
prev_key = prev_key.length == 1 ? prev_key[0] : prev_key
|
61
|
+
prev_key_docid = doc._id
|
62
|
+
prev_params = { :startkey => prev_key, :startkey_docid => prev_key_docid, :descending => !@orig_paginate_params.descending }
|
63
|
+
end
|
64
|
+
|
65
|
+
def orig_offset(design_doc, view_name)
|
66
|
+
query = Query.new(design_doc, view_name)
|
67
|
+
if @paginate_params.order_inverted?
|
68
|
+
query.startkey(@orig_paginate_params.endkey).descending(!@orig_paginate_params.descending)
|
69
|
+
else
|
70
|
+
query.startkey(@orig_paginate_params.startkey).descending(@orig_paginate_params.descending)
|
71
|
+
end
|
72
|
+
query.reduce(false).count(1)
|
73
|
+
RelaxDB.retrieve(query.view_path).offset
|
74
|
+
end
|
75
|
+
|
76
|
+
end
|
77
|
+
|
78
|
+
end
|
@@ -0,0 +1,74 @@
|
|
1
|
+
module RelaxDB
|
2
|
+
|
3
|
+
# A Query is used to build the query string made against a view
|
4
|
+
# All parameter values are first JSON encoded and then URL encoded
|
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").count(2)
|
7
|
+
|
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
|
+
class Query
|
18
|
+
|
19
|
+
# keys is not included in the standard param as it is significantly different from the others
|
20
|
+
@@params = %w(key startkey startkey_docid endkey endkey_docid count update descending skip group group_level reduce include_docs)
|
21
|
+
|
22
|
+
@@params.each do |param|
|
23
|
+
define_method(param.to_sym) do |*val|
|
24
|
+
if val.empty?
|
25
|
+
instance_variable_get("@#{param}")
|
26
|
+
else
|
27
|
+
instance_variable_set("@#{param}", val[0])
|
28
|
+
# null is meaningful to CouchDB. _set allows us to know that a param has been set, even to nil
|
29
|
+
instance_variable_set("@#{param}_set", true)
|
30
|
+
self
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
def initialize(design_doc, view_name)
|
36
|
+
@design_doc = design_doc
|
37
|
+
@view_name = view_name
|
38
|
+
end
|
39
|
+
|
40
|
+
def keys(keys=nil)
|
41
|
+
if keys.nil?
|
42
|
+
@keys
|
43
|
+
else
|
44
|
+
@keys = { :keys => keys }.to_json
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
def view_path
|
49
|
+
uri = "_view/#{@design_doc}/#{@view_name}"
|
50
|
+
|
51
|
+
query = ""
|
52
|
+
@@params.each do |param|
|
53
|
+
val_set = instance_variable_get("@#{param}_set")
|
54
|
+
if val_set
|
55
|
+
val = instance_variable_get("@#{param}")
|
56
|
+
val = val.to_json unless ["startkey_docid", "endkey_docid"].include?(param)
|
57
|
+
query << "&#{param}=#{::CGI::escape(val)}"
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
uri << query.sub(/^&/, "?")
|
62
|
+
end
|
63
|
+
|
64
|
+
def merge(paginate_params)
|
65
|
+
paginate_params.instance_variables.each do |pp|
|
66
|
+
val = paginate_params.instance_variable_get(pp)
|
67
|
+
method_name = pp[1, pp.length]
|
68
|
+
send(method_name, val) if methods.include? method_name
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
end
|
73
|
+
|
74
|
+
end
|
@@ -0,0 +1,99 @@
|
|
1
|
+
module RelaxDB
|
2
|
+
|
3
|
+
class ReferencesManyProxy
|
4
|
+
|
5
|
+
include Enumerable
|
6
|
+
|
7
|
+
def initialize(client, relationship, opts)
|
8
|
+
@client = client
|
9
|
+
@relationship = relationship
|
10
|
+
|
11
|
+
@target_class = opts[:class]
|
12
|
+
@relationship_as_viewed_by_target = opts[:known_as].to_s
|
13
|
+
end
|
14
|
+
|
15
|
+
def <<(obj, reciprocal_invocation=false)
|
16
|
+
return false if peer_ids.include? obj._id
|
17
|
+
|
18
|
+
@peers << obj if @peers
|
19
|
+
peer_ids << obj._id
|
20
|
+
|
21
|
+
unless reciprocal_invocation
|
22
|
+
# Set the other side of the relationship, ensuring this method isn't called again
|
23
|
+
obj.send(@relationship_as_viewed_by_target).send(:<<, @client, true)
|
24
|
+
|
25
|
+
# Bulk save to ensure relationship is persisted on both sides
|
26
|
+
RelaxDB.bulk_save(@client, obj)
|
27
|
+
end
|
28
|
+
|
29
|
+
self
|
30
|
+
end
|
31
|
+
|
32
|
+
def clear
|
33
|
+
resolve
|
34
|
+
@peers.each do |peer|
|
35
|
+
peer.send(@relationship_as_viewed_by_target).send(:delete_from_self, @client)
|
36
|
+
end
|
37
|
+
|
38
|
+
# Important to resolve in the database before in memory, although an examination of the
|
39
|
+
# contents of the bulk_save will look wrong as this object will still list all its peers
|
40
|
+
RelaxDB.bulk_save(@client, *@peers)
|
41
|
+
|
42
|
+
peer_ids.clear
|
43
|
+
@peers.clear
|
44
|
+
end
|
45
|
+
|
46
|
+
def delete(obj)
|
47
|
+
deleted = obj.send(@relationship_as_viewed_by_target).send(:delete_from_self, @client)
|
48
|
+
if deleted
|
49
|
+
delete_from_self(obj)
|
50
|
+
RelaxDB.bulk_save(@client, obj)
|
51
|
+
end
|
52
|
+
deleted
|
53
|
+
end
|
54
|
+
|
55
|
+
def delete_from_self(obj)
|
56
|
+
@peers.delete(obj) if @peers
|
57
|
+
peer_ids.delete(obj._id)
|
58
|
+
end
|
59
|
+
|
60
|
+
def empty?
|
61
|
+
peer_ids.empty?
|
62
|
+
end
|
63
|
+
|
64
|
+
def size
|
65
|
+
peer_ids.size
|
66
|
+
end
|
67
|
+
|
68
|
+
def [](*args)
|
69
|
+
resolve
|
70
|
+
@peers[*args]
|
71
|
+
end
|
72
|
+
|
73
|
+
def each(&blk)
|
74
|
+
resolve
|
75
|
+
@peers.each(&blk)
|
76
|
+
end
|
77
|
+
|
78
|
+
def inspect
|
79
|
+
@client.instance_variable_get("@#{@relationship}".to_sym).inspect
|
80
|
+
end
|
81
|
+
|
82
|
+
private
|
83
|
+
|
84
|
+
def peer_ids
|
85
|
+
@client.instance_variable_get("@#{@relationship}".to_sym)
|
86
|
+
end
|
87
|
+
|
88
|
+
# Resolves the actual ids into real objects via a single GET to CouchDB. Called internally by each
|
89
|
+
def resolve
|
90
|
+
design_doc = @client.class
|
91
|
+
view_name = @relationship
|
92
|
+
view_path = "_view/#{design_doc}/#{view_name}?key=\"#{@client._id}\""
|
93
|
+
map_function = ViewCreator.has_many_through(@target_class, @relationship_as_viewed_by_target)
|
94
|
+
@peers = RelaxDB.retrieve(view_path, design_doc, view_name, map_function)
|
95
|
+
end
|
96
|
+
|
97
|
+
end
|
98
|
+
|
99
|
+
end
|
@@ -0,0 +1,157 @@
|
|
1
|
+
module RelaxDB
|
2
|
+
|
3
|
+
@@db = nil
|
4
|
+
|
5
|
+
class <<self
|
6
|
+
|
7
|
+
def configure(config)
|
8
|
+
@@db = CouchDB.new(config)
|
9
|
+
end
|
10
|
+
|
11
|
+
def db
|
12
|
+
@@db
|
13
|
+
end
|
14
|
+
|
15
|
+
def logger
|
16
|
+
@@db.logger
|
17
|
+
end
|
18
|
+
|
19
|
+
# Creates the named database if it doesn't already exist
|
20
|
+
def use_db(name)
|
21
|
+
db.use_db(name)
|
22
|
+
end
|
23
|
+
|
24
|
+
def delete_db(name)
|
25
|
+
db.delete_db(name)
|
26
|
+
end
|
27
|
+
|
28
|
+
def list_dbs
|
29
|
+
db.list_dbs
|
30
|
+
end
|
31
|
+
|
32
|
+
def replicate_db(source, target)
|
33
|
+
db.replicate_db source, target
|
34
|
+
end
|
35
|
+
|
36
|
+
def bulk_save(*objs)
|
37
|
+
docs = {}
|
38
|
+
objs.each { |o| docs[o._id] = o }
|
39
|
+
|
40
|
+
resp = db.post("_bulk_docs", { "docs" => objs }.to_json )
|
41
|
+
data = JSON.parse(resp.body)
|
42
|
+
|
43
|
+
data["new_revs"].each do |new_rev|
|
44
|
+
docs[ new_rev["id"] ]._rev = new_rev["rev"]
|
45
|
+
end
|
46
|
+
|
47
|
+
data["ok"]
|
48
|
+
end
|
49
|
+
|
50
|
+
def load(*ids)
|
51
|
+
if ids.size == 1
|
52
|
+
resp = db.get(ids[0])
|
53
|
+
data = JSON.parse(resp.body)
|
54
|
+
create_object(data)
|
55
|
+
else
|
56
|
+
resp = db.post("_all_docs?include_docs=true", {:keys => ids}.to_json)
|
57
|
+
data = JSON.parse(resp.body)
|
58
|
+
data["rows"].map { |row| create_object(row["doc"]) }
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
# Used internally by RelaxDB
|
63
|
+
def retrieve(view_path, design_doc=nil, view_name=nil, map_function=nil)
|
64
|
+
begin
|
65
|
+
resp = db.get(view_path)
|
66
|
+
rescue => e
|
67
|
+
DesignDocument.get(design_doc).add_map_view(view_name, map_function).save
|
68
|
+
resp = db.get(view_path)
|
69
|
+
end
|
70
|
+
|
71
|
+
data = JSON.parse(resp.body)
|
72
|
+
ViewResult.new(data)
|
73
|
+
end
|
74
|
+
|
75
|
+
# Requests the given view from CouchDB and returns a hash.
|
76
|
+
# This method should typically be wrapped in one of merge, instantiate, or reduce_result.
|
77
|
+
def view(design_doc, view_name)
|
78
|
+
q = Query.new(design_doc, view_name)
|
79
|
+
yield q if block_given?
|
80
|
+
|
81
|
+
resp = q.keys ? db.post(q.view_path, q.keys) : db.get(q.view_path)
|
82
|
+
JSON.parse(resp.body)
|
83
|
+
end
|
84
|
+
|
85
|
+
# Should be invoked on the result of a join view
|
86
|
+
# Merges all rows based on merge_key and returns an array of ViewOject
|
87
|
+
def merge(data, merge_key)
|
88
|
+
merged = {}
|
89
|
+
data["rows"].each do |row|
|
90
|
+
value = row["value"]
|
91
|
+
merged[value[merge_key]] ||= {}
|
92
|
+
merged[value[merge_key]].merge!(value)
|
93
|
+
end
|
94
|
+
|
95
|
+
merged.values.map { |v| ViewObject.create(v) }
|
96
|
+
end
|
97
|
+
|
98
|
+
# Creates RelaxDB::Document objects from the result
|
99
|
+
def instantiate(data)
|
100
|
+
create_from_hash(data)
|
101
|
+
end
|
102
|
+
|
103
|
+
# Returns a scalar, an object, or an Array of objects
|
104
|
+
def reduce_result(data)
|
105
|
+
obj = data["rows"][0] && data["rows"][0]["value"]
|
106
|
+
ViewObject.create(obj)
|
107
|
+
end
|
108
|
+
|
109
|
+
def paginate_view(page_params, design_doc, view_name, *view_keys)
|
110
|
+
paginate_params = PaginateParams.new
|
111
|
+
yield paginate_params
|
112
|
+
raise paginate_params.error_msg if paginate_params.invalid?
|
113
|
+
|
114
|
+
paginator = Paginator.new(paginate_params, page_params)
|
115
|
+
|
116
|
+
query = Query.new(design_doc, view_name)
|
117
|
+
query.merge(paginate_params)
|
118
|
+
|
119
|
+
docs = ViewResult.new(JSON.parse(db.get(query.view_path).body))
|
120
|
+
docs.reverse! if paginate_params.order_inverted?
|
121
|
+
|
122
|
+
paginator.add_next_and_prev(docs, design_doc, view_name, view_keys)
|
123
|
+
|
124
|
+
docs
|
125
|
+
end
|
126
|
+
|
127
|
+
def create_from_hash(data)
|
128
|
+
data["rows"].map { |row| create_object(row["value"]) }
|
129
|
+
end
|
130
|
+
|
131
|
+
def create_object(data)
|
132
|
+
# revise use of string 'class' - it's a reserved word in JavaScript
|
133
|
+
klass = data.delete("class")
|
134
|
+
if klass
|
135
|
+
k = Module.const_get(klass)
|
136
|
+
k.new(data)
|
137
|
+
else
|
138
|
+
# data is not of a known class
|
139
|
+
ViewObject.create(data)
|
140
|
+
end
|
141
|
+
end
|
142
|
+
|
143
|
+
# Convenience methods - should be in a diffent module?
|
144
|
+
|
145
|
+
def pp_get(uri=nil)
|
146
|
+
resp = db.get(uri)
|
147
|
+
pp(JSON.parse(resp.body))
|
148
|
+
end
|
149
|
+
|
150
|
+
def pp_post(uri=nil, json=nil)
|
151
|
+
resp = db.post(uri, json)
|
152
|
+
pp(JSON.parse(resp.body))
|
153
|
+
end
|
154
|
+
|
155
|
+
end
|
156
|
+
|
157
|
+
end
|