relaxdb 0.3.5 → 0.5
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 +21 -23
- data/Rakefile +2 -7
- data/docs/spec_results.html +5 -5
- data/lib/more/grapher.rb +1 -1
- data/lib/relaxdb.rb +3 -5
- data/lib/relaxdb/all_delegator.rb +19 -13
- data/lib/relaxdb/document.rb +150 -218
- data/lib/relaxdb/extlib.rb +7 -1
- data/lib/relaxdb/migration.rb +11 -8
- data/lib/relaxdb/net_http_server.rb +19 -1
- data/lib/relaxdb/paginator.rb +30 -11
- data/lib/relaxdb/query.rb +1 -1
- data/lib/relaxdb/{belongs_to_proxy.rb → references_proxy.rb} +3 -3
- data/lib/relaxdb/relaxdb.rb +87 -7
- data/lib/relaxdb/server.rb +8 -2
- data/lib/relaxdb/taf2_curb_server.rb +2 -1
- data/lib/relaxdb/uuid_generator.rb +38 -2
- data/lib/relaxdb/view_by_delegator.rb +34 -0
- data/lib/relaxdb/view_object.rb +1 -1
- data/lib/relaxdb/view_uploader.rb +16 -2
- data/lib/relaxdb/views.rb +23 -55
- data/readme.rb +3 -3
- data/spec/all_delegator_spec.rb +52 -0
- data/spec/callbacks_spec.rb +4 -4
- data/spec/derived_properties_spec.rb +4 -4
- data/spec/design_doc_spec.rb +2 -2
- data/spec/doc_inheritable_spec.rb +2 -2
- data/spec/document_spec.rb +47 -25
- data/spec/migration_spec.rb +12 -10
- data/spec/qpaginate_spec.rb +88 -0
- data/spec/query_spec.rb +2 -2
- data/spec/references_proxy_spec.rb +94 -0
- data/spec/relaxdb_spec.rb +29 -21
- data/spec/server_spec.rb +4 -3
- data/spec/spec_helper.rb +1 -0
- data/spec/spec_models.rb +48 -57
- data/spec/uuid_generator_spec.rb +34 -0
- data/spec/view_by_spec.rb +62 -54
- data/spec/view_docs_by_spec.rb +85 -0
- metadata +38 -27
- data/lib/more/atomic_bulk_save_support.rb +0 -18
- data/lib/relaxdb/has_many_proxy.rb +0 -101
- data/lib/relaxdb/has_one_proxy.rb +0 -42
- data/lib/relaxdb/references_many_proxy.rb +0 -97
- data/spec/belongs_to_spec.rb +0 -124
- data/spec/has_many_spec.rb +0 -202
- data/spec/has_one_spec.rb +0 -123
- data/spec/references_many_spec.rb +0 -173
- data/spec/view_spec.rb +0 -23
data/lib/relaxdb/view_object.rb
CHANGED
@@ -7,11 +7,17 @@ module RelaxDB
|
|
7
7
|
# Methods must start and finish on different lines
|
8
8
|
# The function declaration must start at the beginning of a line
|
9
9
|
# As '-' is used as a delimiter, the view name may not contain '-'
|
10
|
-
#
|
10
|
+
# Expected function declaration form is
|
11
11
|
# function funcname-functype(doc) {
|
12
12
|
# For example
|
13
13
|
# function Users_followers-map(doc) {
|
14
14
|
#
|
15
|
+
# Builtin Erlang views may be specified by listing a one-line function
|
16
|
+
# that contains only the builtin name. For example:
|
17
|
+
# function Users_followers-reduce() {
|
18
|
+
# _sum
|
19
|
+
# }
|
20
|
+
#
|
15
21
|
def upload(filename)
|
16
22
|
lines = File.readlines(filename)
|
17
23
|
dd = RelaxDB::DesignDocument.get(RelaxDB.dd)
|
@@ -37,7 +43,15 @@ module RelaxDB
|
|
37
43
|
declr =~ /(\w)+-(\w)+/
|
38
44
|
declr.sub!($&, '')
|
39
45
|
view_name, type = $&.split('-')
|
40
|
-
func = lines[m[i]...m[i+1]]
|
46
|
+
func = lines[m[i]...m[i+1]]
|
47
|
+
|
48
|
+
# Cater for erlang view shortcuts e.g. _sum, _count etc.
|
49
|
+
if func[1] =~ /\s*_\w+\s*$/
|
50
|
+
func = func[1].strip
|
51
|
+
else
|
52
|
+
func = func.join
|
53
|
+
end
|
54
|
+
|
41
55
|
yield view_name, type, func
|
42
56
|
end
|
43
57
|
end
|
data/lib/relaxdb/views.rb
CHANGED
@@ -8,15 +8,23 @@ module RelaxDB
|
|
8
8
|
function(doc) {
|
9
9
|
var class_match = #{kls_check kls}
|
10
10
|
if (class_match) {
|
11
|
-
emit(doc._id,
|
11
|
+
emit(doc._id, 1);
|
12
12
|
}
|
13
13
|
}
|
14
14
|
QUERY
|
15
15
|
|
16
|
-
View.new "#{class_name}_all", map,
|
16
|
+
View.new "#{class_name}_all", map, "_sum"
|
17
|
+
end
|
18
|
+
|
19
|
+
def self.docs_by_att_list(kls, *atts)
|
20
|
+
create_by_att_list "doc", "_count", kls, *atts
|
17
21
|
end
|
18
22
|
|
19
23
|
def self.by_att_list(kls, *atts)
|
24
|
+
create_by_att_list 1, "_sum", kls, *atts
|
25
|
+
end
|
26
|
+
|
27
|
+
def self.create_by_att_list emit_val, reduce_func, kls, *atts
|
20
28
|
class_name = kls[0]
|
21
29
|
key = atts.map { |a| "doc.#{a}" }.join(", ")
|
22
30
|
key = atts.size > 1 ? key.sub(/^/, "[").sub(/$/, "]") : key
|
@@ -26,61 +34,20 @@ module RelaxDB
|
|
26
34
|
function(doc) {
|
27
35
|
var class_match = #{kls_check kls}
|
28
36
|
if (class_match && #{prop_check}) {
|
29
|
-
emit(#{key},
|
37
|
+
emit(#{key}, #{emit_val});
|
30
38
|
}
|
31
39
|
}
|
32
40
|
QUERY
|
33
41
|
|
34
42
|
view_name = "#{class_name}_by_" << atts.join("_and_")
|
35
|
-
View.new view_name, map,
|
43
|
+
View.new view_name, map, reduce_func
|
36
44
|
end
|
37
|
-
|
38
|
-
|
39
|
-
def self.has_n(client_class, relationship, target_class, relationship_to_client)
|
40
|
-
map = <<-QUERY
|
41
|
-
function(doc) {
|
42
|
-
if (doc.relaxdb_class == "#{target_class}" && doc.#{relationship_to_client}_id)
|
43
|
-
emit(doc.#{relationship_to_client}_id, doc);
|
44
|
-
}
|
45
|
-
QUERY
|
46
45
|
|
47
|
-
view_name = "#{client_class}_#{relationship}"
|
48
|
-
View.new view_name, map
|
49
|
-
end
|
50
|
-
|
51
|
-
def self.references_many(client_class, relationship, target_class, peers)
|
52
|
-
map = <<-QUERY
|
53
|
-
function(doc) {
|
54
|
-
if (doc.relaxdb_class == "#{target_class}" && doc.#{peers}) {
|
55
|
-
var i;
|
56
|
-
for(i = 0; i < doc.#{peers}.length; i++) {
|
57
|
-
emit(doc.#{peers}[i], doc);
|
58
|
-
}
|
59
|
-
}
|
60
|
-
}
|
61
|
-
QUERY
|
62
|
-
|
63
|
-
view_name = "#{client_class}_#{relationship}"
|
64
|
-
View.new view_name, map
|
65
|
-
end
|
66
|
-
|
67
46
|
def self.kls_check kls
|
68
47
|
kls_names = kls.map{ |k| %Q("#{k}") }.join(",")
|
69
48
|
"[#{kls_names}].indexOf(doc.relaxdb_class) >= 0;"
|
70
49
|
end
|
71
50
|
|
72
|
-
def self.sum_reduce_func
|
73
|
-
<<-QUERY
|
74
|
-
function(keys, values, rereduce) {
|
75
|
-
if (rereduce) {
|
76
|
-
return sum(values);
|
77
|
-
} else {
|
78
|
-
return values.length;
|
79
|
-
}
|
80
|
-
}
|
81
|
-
QUERY
|
82
|
-
end
|
83
|
-
|
84
51
|
end
|
85
52
|
|
86
53
|
class View
|
@@ -93,22 +60,23 @@ module RelaxDB
|
|
93
60
|
@reduce_func = reduce_func
|
94
61
|
end
|
95
62
|
|
96
|
-
def design_doc
|
63
|
+
def self.design_doc
|
97
64
|
@design_doc ||= DesignDocument.get(RelaxDB.dd)
|
98
65
|
end
|
99
66
|
|
100
|
-
|
101
|
-
|
67
|
+
#
|
68
|
+
# A convenience for tests that create their own views
|
69
|
+
#
|
70
|
+
def self.reset
|
71
|
+
@design_doc = nil
|
72
|
+
end
|
73
|
+
|
74
|
+
def add_to_design_doc
|
75
|
+
dd = View.design_doc
|
102
76
|
dd.add_map_view(@view_name, @map_func)
|
103
77
|
dd.add_reduce_view(@view_name, @reduce_func) if @reduce_func
|
104
|
-
dd.save
|
105
|
-
end
|
106
|
-
|
107
|
-
def exists?
|
108
|
-
dd = design_doc
|
109
|
-
dd.data["views"] && dd.data["views"][@view_name]
|
110
78
|
end
|
111
|
-
|
79
|
+
|
112
80
|
end
|
113
81
|
|
114
82
|
end
|
data/readme.rb
CHANGED
@@ -28,9 +28,9 @@ class Invite < RelaxDB::Document
|
|
28
28
|
property :sender_name,
|
29
29
|
:derived => [:sender, lambda { |p, o| o.sender.name } ]
|
30
30
|
|
31
|
-
|
32
|
-
|
33
|
-
|
31
|
+
view_docs_by :sender_name
|
32
|
+
view_docs_by :sender_id
|
33
|
+
view_docs_by :recipient_id, :created_at, :descending => true
|
34
34
|
|
35
35
|
def on_update_conflict
|
36
36
|
puts "conflict!"
|
@@ -0,0 +1,52 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/spec_helper.rb'
|
2
|
+
require File.dirname(__FILE__) + '/spec_models.rb'
|
3
|
+
|
4
|
+
describe RelaxDB::AllDelegator do
|
5
|
+
|
6
|
+
before(:all) do
|
7
|
+
setup_test_db
|
8
|
+
end
|
9
|
+
|
10
|
+
describe "size" do
|
11
|
+
|
12
|
+
it "should return the total count for a given class" do
|
13
|
+
docs = (1..101).map { |i| Primitives.new :num => i }
|
14
|
+
RelaxDB.bulk_save! *docs
|
15
|
+
Primitives.all.size.should == 101
|
16
|
+
end
|
17
|
+
|
18
|
+
end
|
19
|
+
|
20
|
+
describe "all" do
|
21
|
+
|
22
|
+
it "should return the ids for the given class" do
|
23
|
+
docs = (1..3).map { |i| Primitives.new :_id => "p#{i}" }
|
24
|
+
RelaxDB.bulk_save! *docs
|
25
|
+
Primitives.all.should == %w(p1 p2 p3)
|
26
|
+
end
|
27
|
+
|
28
|
+
end
|
29
|
+
|
30
|
+
describe "load" do
|
31
|
+
|
32
|
+
it "should load all docs for the given class" do
|
33
|
+
docs = (1..3).map { |i| Primitives.new :num => i }
|
34
|
+
RelaxDB.bulk_save! *docs
|
35
|
+
pms = Primitives.all.load!
|
36
|
+
pms.map { |p| p.num }.inject(&:+).should == 6
|
37
|
+
end
|
38
|
+
|
39
|
+
end
|
40
|
+
|
41
|
+
describe "destroy" do
|
42
|
+
|
43
|
+
it "should destroy all docs for the given class" do
|
44
|
+
docs = (1..3).map { |i| Primitives.new :num => i }
|
45
|
+
RelaxDB.bulk_save! *docs
|
46
|
+
Primitives.all.destroy!
|
47
|
+
Primitives.all.load!.should == []
|
48
|
+
end
|
49
|
+
|
50
|
+
end
|
51
|
+
|
52
|
+
end
|
data/spec/callbacks_spec.rb
CHANGED
@@ -26,14 +26,14 @@ describe RelaxDB::Document, "callbacks" do
|
|
26
26
|
|
27
27
|
it "should prevent the object from being saved if it returns false" do
|
28
28
|
c = Class.new(RelaxDB::Document) do
|
29
|
-
before_save lambda { false }
|
29
|
+
before_save lambda { |o| false }
|
30
30
|
end
|
31
31
|
c.new.save.should == false
|
32
32
|
end
|
33
33
|
|
34
34
|
it "should add a description to errors when false is returned" do
|
35
35
|
c = Class.new(RelaxDB::Document) do
|
36
|
-
before_save lambda { false }
|
36
|
+
before_save lambda { |o| false }
|
37
37
|
end
|
38
38
|
x = c.new
|
39
39
|
x.save
|
@@ -42,14 +42,14 @@ describe RelaxDB::Document, "callbacks" do
|
|
42
42
|
|
43
43
|
it "should not prevent the object from being saved if it returns nil" do
|
44
44
|
c = Class.new(RelaxDB::Document) do
|
45
|
-
before_save lambda { nil }
|
45
|
+
before_save lambda { |o| nil }
|
46
46
|
end
|
47
47
|
c.new.save!
|
48
48
|
end
|
49
49
|
|
50
50
|
it "may be a proc" do
|
51
51
|
c = Class.new(RelaxDB::Document) do
|
52
|
-
before_save lambda { false }
|
52
|
+
before_save lambda { |o| false }
|
53
53
|
end
|
54
54
|
c.new.save.should == false
|
55
55
|
end
|
@@ -3,7 +3,7 @@ require File.dirname(__FILE__) + '/spec_models.rb'
|
|
3
3
|
|
4
4
|
class DpInvite < RelaxDB::Document
|
5
5
|
property :event_name, :derived => [:event, lambda { |en, i| i.event.name }]
|
6
|
-
|
6
|
+
references :event
|
7
7
|
end
|
8
8
|
|
9
9
|
class DpEvent < RelaxDB::Document
|
@@ -46,7 +46,7 @@ describe RelaxDB::Document, "derived properties" do
|
|
46
46
|
it "should only be updated for registered properties" do
|
47
47
|
invite = Class.new(RelaxDB::Document) do
|
48
48
|
property :event_name, :derived => [:foo, lambda { |en, i| i.event.name }]
|
49
|
-
|
49
|
+
references :event
|
50
50
|
end
|
51
51
|
|
52
52
|
event = Class.new(RelaxDB::Document) do
|
@@ -61,7 +61,7 @@ describe RelaxDB::Document, "derived properties" do
|
|
61
61
|
it "should have the existing value passed to the first lambda param" do
|
62
62
|
invite = Class.new(RelaxDB::Document) do
|
63
63
|
property :event_name, :derived => [:event, lambda { |en, i| en.nil? ? i.event.name : "bar" }]
|
64
|
-
|
64
|
+
references :event
|
65
65
|
end
|
66
66
|
|
67
67
|
event = Class.new(RelaxDB::Document) do
|
@@ -93,7 +93,7 @@ describe RelaxDB::Document, "derived properties" do
|
|
93
93
|
invite = Class.new(RelaxDB::Document) do
|
94
94
|
property :name, :derived => [:event, lambda { |en, i| i.event.name }]
|
95
95
|
property :location, :derived => [:event, lambda { |en, i| i.event.location }]
|
96
|
-
|
96
|
+
references :event
|
97
97
|
end
|
98
98
|
|
99
99
|
event = Class.new(RelaxDB::Document) do
|
data/spec/design_doc_spec.rb
CHANGED
@@ -15,8 +15,8 @@ describe RelaxDB::DesignDocument do
|
|
15
15
|
describe "#save" do
|
16
16
|
|
17
17
|
it "should create a corresponding document in CouchDB" do
|
18
|
-
RelaxDB::DesignDocument.get("foo").save
|
19
|
-
RelaxDB.
|
18
|
+
RelaxDB::DesignDocument.get("foo").save
|
19
|
+
RelaxDB::DesignDocument.get("foo").should_not be_nil
|
20
20
|
end
|
21
21
|
|
22
22
|
end
|
@@ -34,8 +34,8 @@ describe "Inheritance" do
|
|
34
34
|
a = Ancestor.new(:x => 0).save!
|
35
35
|
d = Descendant.new(:x => 1).save!
|
36
36
|
|
37
|
-
Ancestor.all.should
|
38
|
-
Descendant.all.should == [d]
|
37
|
+
Ancestor.all.load!.should include(a, d)
|
38
|
+
Descendant.all.load!.should == [d]
|
39
39
|
end
|
40
40
|
|
41
41
|
it "should function with inheritance trees" do
|
data/spec/document_spec.rb
CHANGED
@@ -25,10 +25,10 @@ describe RelaxDB::Document do
|
|
25
25
|
p.viewed_at.should be_close(now, 1)
|
26
26
|
end
|
27
27
|
|
28
|
-
it "will
|
29
|
-
|
30
|
-
|
31
|
-
|
28
|
+
it "will fail on parameters that don't specify class attributes" do
|
29
|
+
lambda {
|
30
|
+
Post.new :foo => ""
|
31
|
+
}.should raise_error
|
32
32
|
end
|
33
33
|
|
34
34
|
it "should create a document with a non conflicing state" do
|
@@ -60,6 +60,11 @@ describe RelaxDB::Document do
|
|
60
60
|
json = RelaxDB.get(p._id)
|
61
61
|
json["created_at"].should == "1970/01/01 00:00:00 +0000"
|
62
62
|
end
|
63
|
+
|
64
|
+
it "should allow to be called with an options argument, to be compatible with ActiveSupport" do
|
65
|
+
s = Time.at(0)
|
66
|
+
lambda { s.to_json( :active_support => 'love' ) }.should_not raise_error(ArgumentError)
|
67
|
+
end
|
63
68
|
|
64
69
|
end
|
65
70
|
|
@@ -112,12 +117,11 @@ describe RelaxDB::Document do
|
|
112
117
|
p.created_at.should be_close(back_then, 1)
|
113
118
|
end
|
114
119
|
|
115
|
-
it "should set document conflict state on conflicting save" do
|
116
|
-
a1 = Atom.new
|
117
|
-
a2 = a1.dup
|
120
|
+
it "should set document conflict state on conflicting save" do
|
121
|
+
a1, a2 = Atom.new(:_id => "a1"), Atom.new(:_id => "a1")
|
118
122
|
a1.save!
|
119
123
|
a2.save
|
120
|
-
a2.should be_update_conflict
|
124
|
+
a2.should be_update_conflict
|
121
125
|
end
|
122
126
|
|
123
127
|
end
|
@@ -139,13 +143,26 @@ describe RelaxDB::Document do
|
|
139
143
|
end
|
140
144
|
|
141
145
|
it "should raise UpdateConflict on an update conflict" do
|
142
|
-
a1 = Atom.new
|
143
|
-
a2 = a1.dup
|
146
|
+
a1, a2 = Atom.new(:_id => "a1"), Atom.new(:_id => "a1")
|
144
147
|
a1.save!
|
145
148
|
lambda { a2.save! }.should raise_error(RelaxDB::UpdateConflict)
|
146
149
|
end
|
147
150
|
|
148
151
|
end
|
152
|
+
|
153
|
+
describe "property" do
|
154
|
+
|
155
|
+
it "may contain another object" do
|
156
|
+
a = Atom.new.save!
|
157
|
+
p = Primitives.new(:context => a).save!
|
158
|
+
|
159
|
+
p = RelaxDB.reload p
|
160
|
+
RelaxDB.db.reset_req_count
|
161
|
+
p.context.to_obj.should == a
|
162
|
+
RelaxDB.db.req_count.should == 0
|
163
|
+
end
|
164
|
+
|
165
|
+
end
|
149
166
|
|
150
167
|
describe "user defined property reader" do
|
151
168
|
|
@@ -158,27 +175,22 @@ describe RelaxDB::Document do
|
|
158
175
|
it "should not modify internal state" do
|
159
176
|
o = BespokeReader.new(:val => 101).save
|
160
177
|
o = RelaxDB.load o._id
|
161
|
-
o.
|
178
|
+
o.data["val"].should == 101
|
162
179
|
end
|
163
180
|
|
164
181
|
end
|
165
182
|
|
166
183
|
describe "user defined property writer" do
|
167
184
|
|
168
|
-
it
|
185
|
+
# So this works now, but it hasn't in the past - this behaviour has changed
|
186
|
+
# many times. Use with caution i.e. it wouldn't be wise to rely on this
|
187
|
+
# across a large swathe of your codebase.
|
188
|
+
it "should only be used to modify state with caution" do
|
169
189
|
o = BespokeWriter.new(:val => 101).save
|
170
190
|
o = RelaxDB.load o._id
|
171
|
-
o.val.should ==
|
191
|
+
o.val.should == 91
|
172
192
|
end
|
173
|
-
|
174
|
-
it "may be used if effectively idempotent" do
|
175
|
-
o = BespokeWriter.new(:tt => "2009/04/01").save
|
176
|
-
RelaxDB.reload(o).tt.should == Time.parse("2009/04/01")
|
177
|
-
|
178
|
-
o = BespokeWriter.new(:tt => Time.now).save
|
179
|
-
RelaxDB.reload(o).tt.should be_close(Time.now, 2)
|
180
|
-
end
|
181
|
-
|
193
|
+
|
182
194
|
end
|
183
195
|
|
184
196
|
describe "loaded objects" do
|
@@ -190,10 +202,9 @@ describe RelaxDB::Document do
|
|
190
202
|
p.str.should == "foo"
|
191
203
|
p.num.should == 19.30
|
192
204
|
p.true_bool.should be_true
|
193
|
-
|
194
|
-
p.false_bool.should_not be
|
205
|
+
p.false_bool.should be_false
|
195
206
|
p.created_at.should be_close(now, 1)
|
196
|
-
p.
|
207
|
+
p.context.should be_nil
|
197
208
|
end
|
198
209
|
|
199
210
|
it "should be saveable" do
|
@@ -542,4 +553,15 @@ describe RelaxDB::Document do
|
|
542
553
|
|
543
554
|
end
|
544
555
|
|
556
|
+
describe "initialization process" do
|
557
|
+
|
558
|
+
it "should not modify internal state unexpectedly" do
|
559
|
+
a = Atom.new
|
560
|
+
c = Contrived.new :context => a
|
561
|
+
c.context_count.should == 1
|
562
|
+
c.foo.should == 10
|
563
|
+
end
|
564
|
+
|
565
|
+
end
|
566
|
+
|
545
567
|
end
|