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
@@ -1,42 +0,0 @@
|
|
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
|
-
view_name = "#{@client.class}_#{@relationship}"
|
37
|
-
RelaxDB.view(view_name, :key => @client._id).first
|
38
|
-
end
|
39
|
-
|
40
|
-
end
|
41
|
-
|
42
|
-
end
|
@@ -1,97 +0,0 @@
|
|
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
|
-
|
14
|
-
@peers = resolve
|
15
|
-
end
|
16
|
-
|
17
|
-
def <<(obj, reciprocal_invocation=false)
|
18
|
-
return false if peer_ids.include? obj._id
|
19
|
-
|
20
|
-
@peers << obj if @peers
|
21
|
-
peer_ids << obj._id
|
22
|
-
|
23
|
-
unless reciprocal_invocation
|
24
|
-
# Set the other side of the relationship, ensuring this method isn't called again
|
25
|
-
obj.send(@relationship_as_viewed_by_target).send(:<<, @client, true)
|
26
|
-
|
27
|
-
# Bulk save to ensure relationship is persisted on both sides
|
28
|
-
RelaxDB.bulk_save! @client, obj
|
29
|
-
end
|
30
|
-
|
31
|
-
self
|
32
|
-
end
|
33
|
-
|
34
|
-
def clear
|
35
|
-
@peers.each do |peer|
|
36
|
-
peer.send(@relationship_as_viewed_by_target).send(:delete_from_self, @client)
|
37
|
-
end
|
38
|
-
|
39
|
-
# Important to resolve in the database before in memory, although an examination of the
|
40
|
-
# contents of the bulk_save will look wrong as this object will still list all its peers
|
41
|
-
RelaxDB.bulk_save(@client, *@peers)
|
42
|
-
|
43
|
-
peer_ids.clear
|
44
|
-
@peers.clear
|
45
|
-
end
|
46
|
-
|
47
|
-
def delete(obj)
|
48
|
-
deleted = obj.send(@relationship_as_viewed_by_target).send(:delete_from_self, @client)
|
49
|
-
if deleted
|
50
|
-
delete_from_self(obj)
|
51
|
-
RelaxDB.bulk_save(@client, obj)
|
52
|
-
end
|
53
|
-
deleted
|
54
|
-
end
|
55
|
-
|
56
|
-
def delete_from_self(obj)
|
57
|
-
@peers.delete(obj)
|
58
|
-
peer_ids.delete(obj._id)
|
59
|
-
end
|
60
|
-
|
61
|
-
def empty?
|
62
|
-
peer_ids.empty?
|
63
|
-
end
|
64
|
-
|
65
|
-
def size
|
66
|
-
peer_ids.size
|
67
|
-
end
|
68
|
-
|
69
|
-
def [](*args)
|
70
|
-
@peers[*args]
|
71
|
-
end
|
72
|
-
|
73
|
-
def each(&blk)
|
74
|
-
@peers.each(&blk)
|
75
|
-
end
|
76
|
-
|
77
|
-
def inspect
|
78
|
-
@client.instance_variable_get("@#{@relationship}".to_sym).inspect
|
79
|
-
end
|
80
|
-
|
81
|
-
def peer_ids
|
82
|
-
@client.instance_variable_get("@#{@relationship}".to_sym)
|
83
|
-
end
|
84
|
-
|
85
|
-
alias to_id_a peer_ids
|
86
|
-
|
87
|
-
private
|
88
|
-
|
89
|
-
# Resolves the actual ids into real objects via a single GET to CouchDB
|
90
|
-
def resolve
|
91
|
-
view_name = "#{@client.class}_#{@relationship}"
|
92
|
-
@peers = RelaxDB.view(view_name, :key => @client._id)
|
93
|
-
end
|
94
|
-
|
95
|
-
end
|
96
|
-
|
97
|
-
end
|
data/spec/belongs_to_spec.rb
DELETED
@@ -1,124 +0,0 @@
|
|
1
|
-
require File.dirname(__FILE__) + '/spec_helper.rb'
|
2
|
-
require File.dirname(__FILE__) + '/spec_models.rb'
|
3
|
-
|
4
|
-
describe RelaxDB::BelongsToProxy do
|
5
|
-
|
6
|
-
before(:all) do
|
7
|
-
setup_test_db
|
8
|
-
end
|
9
|
-
|
10
|
-
describe "belongs_to" do
|
11
|
-
|
12
|
-
it "should return nil when accessed before assignment" do
|
13
|
-
r = Rating.new
|
14
|
-
r.photo.should == nil
|
15
|
-
end
|
16
|
-
|
17
|
-
it "should be establishable via constructor attribute" do
|
18
|
-
p = Photo.new
|
19
|
-
r = Rating.new :photo => p
|
20
|
-
r.photo.should == p
|
21
|
-
end
|
22
|
-
|
23
|
-
it "should be establishable via constructor id" do
|
24
|
-
p = Photo.new.save
|
25
|
-
r = Rating.new(:photo_id => p._id).save
|
26
|
-
r.photo.should == p
|
27
|
-
end
|
28
|
-
|
29
|
-
it "should establish the parent relationship when supplied a parent and saved" do
|
30
|
-
p = Photo.new.save
|
31
|
-
r = Rating.new
|
32
|
-
r.photo = p
|
33
|
-
# I'm not saying the following is correct or desired - merely codifying how things stand
|
34
|
-
p.rating.should be_nil
|
35
|
-
r.save
|
36
|
-
p.rating.should == r
|
37
|
-
end
|
38
|
-
|
39
|
-
it "should establish the parent relationship when supplied a parent id and saved" do
|
40
|
-
p = Photo.new.save
|
41
|
-
r = Rating.new(:photo_id => p._id).save
|
42
|
-
p.rating.should == r
|
43
|
-
end
|
44
|
-
|
45
|
-
it "should return the same object on repeated invocations" do
|
46
|
-
p = Photo.new.save
|
47
|
-
r = Rating.new(:photo => p).save
|
48
|
-
r = RelaxDB.load(r._id)
|
49
|
-
r.photo.object_id.should == r.photo.object_id
|
50
|
-
end
|
51
|
-
|
52
|
-
it "should be nullified when the parent is destroyed" do
|
53
|
-
r = Rating.new
|
54
|
-
p = Photo.new(:rating => r).save
|
55
|
-
p.destroy!
|
56
|
-
RelaxDB.load(r._id).photo.should be_nil
|
57
|
-
end
|
58
|
-
|
59
|
-
it "should be preserved across save / load boundary" do
|
60
|
-
r = Rating.new
|
61
|
-
p = Photo.new(:rating => r).save
|
62
|
-
r = RelaxDB.load r._id
|
63
|
-
r.photo.should == p
|
64
|
-
end
|
65
|
-
|
66
|
-
it "should be able to reference itself via its parent" do
|
67
|
-
r = Rating.new
|
68
|
-
p = Photo.new(:rating => r).save
|
69
|
-
r = RelaxDB.load r._id
|
70
|
-
r.photo.rating.should == r
|
71
|
-
end
|
72
|
-
|
73
|
-
it "may be used reciprocally" do
|
74
|
-
C1 = Class.new(RelaxDB::Document) do
|
75
|
-
belongs_to :c2
|
76
|
-
end
|
77
|
-
C2 = Class.new(RelaxDB::Document) do
|
78
|
-
belongs_to :c1
|
79
|
-
end
|
80
|
-
i1, i2 = C1.new, C2.new
|
81
|
-
|
82
|
-
i1.c2 = i2
|
83
|
-
i1.save!
|
84
|
-
i2.c1 = i1
|
85
|
-
i2.save!
|
86
|
-
|
87
|
-
i1 = RelaxDB.load i1._id
|
88
|
-
i1.c2.should == i2
|
89
|
-
|
90
|
-
i2 = RelaxDB.load i2._id
|
91
|
-
i2.c1.should == i1
|
92
|
-
end
|
93
|
-
|
94
|
-
describe "validator" do
|
95
|
-
|
96
|
-
it "should be passed the _id and object" do
|
97
|
-
a = Atom.new(:_id => "atom").save!
|
98
|
-
c = Class.new(RelaxDB::Document) do
|
99
|
-
belongs_to :foo, :validator => lambda { |foo_id, obj| foo_id.reverse == obj._id }
|
100
|
-
end
|
101
|
-
c.new(:_id => "mota", :foo => a).save!
|
102
|
-
end
|
103
|
-
|
104
|
-
it "may be used with a predefined validator" do
|
105
|
-
c = Class.new(RelaxDB::Document) do
|
106
|
-
belongs_to :foo, :validator => :required
|
107
|
-
end
|
108
|
-
c.new.save.should be_false
|
109
|
-
end
|
110
|
-
|
111
|
-
it "should be provided with a default error message when validation fails" do
|
112
|
-
c = Class.new(RelaxDB::Document) do
|
113
|
-
belongs_to :foo, :validator => :required
|
114
|
-
end
|
115
|
-
x = c.new
|
116
|
-
x.save
|
117
|
-
x.errors[:foo].should_not be_blank
|
118
|
-
end
|
119
|
-
|
120
|
-
end
|
121
|
-
|
122
|
-
end
|
123
|
-
|
124
|
-
end
|
data/spec/has_many_spec.rb
DELETED
@@ -1,202 +0,0 @@
|
|
1
|
-
require File.dirname(__FILE__) + '/spec_helper.rb'
|
2
|
-
require File.dirname(__FILE__) + '/spec_models.rb'
|
3
|
-
|
4
|
-
describe RelaxDB::HasManyProxy do
|
5
|
-
|
6
|
-
before(:all) do
|
7
|
-
setup_test_db
|
8
|
-
end
|
9
|
-
|
10
|
-
describe "has_many" do
|
11
|
-
|
12
|
-
|
13
|
-
describe "target_class in the generated view" do
|
14
|
-
it "should infer the class name from the relationship if not supplied" do
|
15
|
-
view = mock(:view).as_null_object
|
16
|
-
RelaxDB::ViewCreator.should_receive(:has_n).with(
|
17
|
-
"", # client_class
|
18
|
-
:foo_bars, # relationship
|
19
|
-
"FooBar", # target_class
|
20
|
-
"" # relationship_to_client
|
21
|
-
).and_return view
|
22
|
-
klass = Class.new(RelaxDB::Document) do
|
23
|
-
has_many :foo_bars
|
24
|
-
end
|
25
|
-
end
|
26
|
-
|
27
|
-
it "should use the class name if supplied" do
|
28
|
-
view = mock(:view).as_null_object
|
29
|
-
RelaxDB::ViewCreator.should_receive(:has_n).with(
|
30
|
-
"", # client_class
|
31
|
-
:foo_bars, # relationship
|
32
|
-
"Bar", # target_class
|
33
|
-
"" # relationship_to_client
|
34
|
-
).and_return view
|
35
|
-
klass = Class.new(RelaxDB::Document) do
|
36
|
-
has_many :foo_bars, :class => "Bar"
|
37
|
-
end
|
38
|
-
end
|
39
|
-
end
|
40
|
-
|
41
|
-
it "should be considered enumerable" do
|
42
|
-
u = User.new.save
|
43
|
-
u.items.should be_a_kind_of(Enumerable)
|
44
|
-
end
|
45
|
-
|
46
|
-
it "should actually be enumerable" do
|
47
|
-
u = User.new.save
|
48
|
-
u.items << Item.new(:name => "a")
|
49
|
-
u.items << Item.new(:name => "b")
|
50
|
-
names = u.items.inject("") { |memo, i| memo << i.name }
|
51
|
-
names.should == "ab"
|
52
|
-
end
|
53
|
-
|
54
|
-
it "should preserve the collection across the load / save boundary" do
|
55
|
-
u = User.new.save
|
56
|
-
u.items << Item.new
|
57
|
-
u = RelaxDB.load u._id
|
58
|
-
u.items.size.should == 1
|
59
|
-
end
|
60
|
-
|
61
|
-
it "should work with MultiWordClassNames" do
|
62
|
-
c = MultiWordChild.new
|
63
|
-
m = MultiWordClass.new.save
|
64
|
-
m.multi_word_children << c
|
65
|
-
m = RelaxDB.load m._id
|
66
|
-
m.multi_word_children[0].should == c
|
67
|
-
end
|
68
|
-
|
69
|
-
describe "#<<" do
|
70
|
-
|
71
|
-
it "should link the added item to the parent" do
|
72
|
-
u = User.new
|
73
|
-
u.items << Item.new
|
74
|
-
u.items[0].user.should == u
|
75
|
-
end
|
76
|
-
|
77
|
-
it "should return self" do
|
78
|
-
u = User.new.save
|
79
|
-
u.items << Item.new << Item.new
|
80
|
-
u.items[0].user.should == u
|
81
|
-
u.items[1].user.should == u
|
82
|
-
end
|
83
|
-
|
84
|
-
it "should not created duplicates when invoked with same object more than once" do
|
85
|
-
u = User.new.save
|
86
|
-
i = Item.new
|
87
|
-
u.items << i << i
|
88
|
-
u.items.size.should == 1
|
89
|
-
end
|
90
|
-
|
91
|
-
it "should return false when the child fails validation" do
|
92
|
-
d = Dysfunctional.new
|
93
|
-
r = (d.failures << Failure.new)
|
94
|
-
r.should be_false
|
95
|
-
d.failures.should be_empty
|
96
|
-
end
|
97
|
-
|
98
|
-
end
|
99
|
-
|
100
|
-
describe "#=" do
|
101
|
-
|
102
|
-
before(:each) do
|
103
|
-
# Create the underlying views
|
104
|
-
User.new(:items => [], :invites_received => [], :invites_sent => [])
|
105
|
-
end
|
106
|
-
|
107
|
-
it "should not attempt to save the child objects when the relationship is established" do
|
108
|
-
RelaxDB.db.put_count = 0
|
109
|
-
i1, i2 = Item.new(:name => "i1"), Item.new(:name => "i2")
|
110
|
-
User.new(:items => [i1, i2])
|
111
|
-
RelaxDB.db.put_count.should == 0
|
112
|
-
end
|
113
|
-
|
114
|
-
it "should preserve given relationships across save/load boundary" do
|
115
|
-
i1, i2 = Item.new(:name => "i1"), Item.new(:name => "i2")
|
116
|
-
u = User.new(:items => [i1, i2])
|
117
|
-
RelaxDB.bulk_save u, *u.items
|
118
|
-
u = RelaxDB.load u._id
|
119
|
-
u.items.map { |i| i.name }.sort.join.should == "i1i2"
|
120
|
-
end
|
121
|
-
|
122
|
-
it "should invoke the derived properties writer" do
|
123
|
-
class HmsdParent < RelaxDB::Document
|
124
|
-
property :foo, :derived => [:zongs, lambda {|f, o| o.zongs.first.z / 2 }]
|
125
|
-
has_many :zongs, :class => "HmsdChild"
|
126
|
-
end
|
127
|
-
class HmsdChild < RelaxDB::Document
|
128
|
-
property :z
|
129
|
-
belongs_to :hmsd_parent
|
130
|
-
end
|
131
|
-
oz = HmsdChild.new(:z => 10)
|
132
|
-
op = HmsdParent.new(:zongs => [oz])
|
133
|
-
op.foo.should == 5
|
134
|
-
end
|
135
|
-
|
136
|
-
end
|
137
|
-
|
138
|
-
describe "#delete" do
|
139
|
-
|
140
|
-
it "should nullify the belongs_to relationship" do
|
141
|
-
u = User.new.save
|
142
|
-
i = Item.new
|
143
|
-
u.items << i
|
144
|
-
u.items.delete i
|
145
|
-
i.user.should be_nil
|
146
|
-
RelaxDB.load(i._id).user.should be_nil
|
147
|
-
end
|
148
|
-
|
149
|
-
end
|
150
|
-
|
151
|
-
describe "#clear" do
|
152
|
-
|
153
|
-
it "should result in an empty collection" do
|
154
|
-
u = User.new.save
|
155
|
-
u.items << Item.new << Item.new
|
156
|
-
u.items.clear
|
157
|
-
u.items.should be_empty
|
158
|
-
end
|
159
|
-
|
160
|
-
it "should nullify all child relationships" do
|
161
|
-
u = User.new.save
|
162
|
-
i1, i2 = Item.new, Item.new
|
163
|
-
u.items << i1
|
164
|
-
u.items << i2
|
165
|
-
u.items.clear
|
166
|
-
|
167
|
-
i1.user.should be_nil
|
168
|
-
i2.user.should be_nil
|
169
|
-
RelaxDB.load(i1._id).user.should be_nil
|
170
|
-
RelaxDB.load(i2._id).user.should be_nil
|
171
|
-
end
|
172
|
-
|
173
|
-
end
|
174
|
-
|
175
|
-
describe "owner" do
|
176
|
-
|
177
|
-
it "should be able to form multiple relationships with the same class of child" do
|
178
|
-
u1, u2 = User.new.save, User.new.save
|
179
|
-
i = Invite.new(:recipient => u2)
|
180
|
-
u1.invites_sent << Invite.new
|
181
|
-
RelaxDB.load(u1._id).invites_sent[0] == i
|
182
|
-
RelaxDB.load(u2._id).invites_received[0] == i
|
183
|
-
end
|
184
|
-
|
185
|
-
describe "#destroy" do
|
186
|
-
|
187
|
-
it "should nullify its child relationships" do
|
188
|
-
Item.view_by :user_id
|
189
|
-
|
190
|
-
u = User.new.save
|
191
|
-
u.items << Item.new << Item.new
|
192
|
-
u.destroy!
|
193
|
-
Item.by_user_id(:key => u._id).should be_empty
|
194
|
-
end
|
195
|
-
|
196
|
-
end
|
197
|
-
|
198
|
-
end
|
199
|
-
|
200
|
-
end
|
201
|
-
|
202
|
-
end
|