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