paulcarey-relaxdb 0.1.2 → 0.1.3
Sign up to get free protection for your applications and to get access to all the features.
- data/README.textile +0 -2
- data/Rakefile +1 -1
- data/lib/more/grapher.rb +42 -0
- data/lib/relaxdb/all_delegator.rb +2 -2
- data/lib/relaxdb/document.rb +75 -21
- data/lib/relaxdb/extlib.rb +3 -0
- data/lib/relaxdb/has_many_proxy.rb +1 -1
- data/lib/relaxdb/has_one_proxy.rb +2 -2
- data/lib/relaxdb/relaxdb.rb +33 -15
- data/lib/relaxdb/server.rb +19 -5
- data/lib/relaxdb.rb +2 -0
- data/spec/callbacks_spec.rb +64 -0
- data/spec/denormalisation_spec.rb +49 -0
- data/spec/document_spec.rb +20 -3
- data/spec/has_many_spec.rb +8 -0
- data/spec/has_one_spec.rb +7 -0
- data/spec/relaxdb_spec.rb +60 -1
- data/spec/spec_models.rb +9 -0
- metadata +5 -1
data/README.textile
CHANGED
@@ -1,5 +1,3 @@
|
|
1
|
-
*Note:* For a correctly formatted version of this page, take a look at the "wiki":http://github.com/paulcarey/relaxdb/wikis
|
2
|
-
|
3
1
|
h2. Overview
|
4
2
|
|
5
3
|
RelaxDB provides a Ruby interface to CouchDB. It offers a simple idiom for specifying object relationships. The underlying objects are persisted to the mighty CouchDB. Combined with the schema free nature of CouchDB, RelaxDB's current strength lies in quick prototyping of object models.
|
data/Rakefile
CHANGED
data/lib/more/grapher.rb
ADDED
@@ -0,0 +1,42 @@
|
|
1
|
+
module RelaxDB
|
2
|
+
|
3
|
+
class GraphCreator
|
4
|
+
|
5
|
+
def self.create
|
6
|
+
system "mkdir -p graphs"
|
7
|
+
|
8
|
+
data = JSON.parse(RelaxDB.db.get("_all_docs").body)
|
9
|
+
all_ids = data["rows"].map { |r| r["id"] }
|
10
|
+
all_ids = all_ids.reject { |id| id =~ /_/ }
|
11
|
+
|
12
|
+
dot = "digraph G { \nrankdir=LR;\nnode [shape=record];\n"
|
13
|
+
all_ids.each do |id|
|
14
|
+
doc = RelaxDB.load(id)
|
15
|
+
atts = "#{doc.class}\\l|"
|
16
|
+
doc.properties.each do |prop|
|
17
|
+
# we don't care about the revision
|
18
|
+
next if prop == :_rev
|
19
|
+
|
20
|
+
prop_val = doc.instance_variable_get("@#{prop}".to_sym)
|
21
|
+
atts << "#{prop}\\l#{prop_val}|" if prop_val
|
22
|
+
end
|
23
|
+
atts = atts[0, atts.length-1]
|
24
|
+
|
25
|
+
dot << %Q%#{doc._id} [ label ="#{atts}"];\n%
|
26
|
+
|
27
|
+
doc.class.belongs_to_rels.each do |relationship, opts|
|
28
|
+
id = doc.instance_variable_get("@#{relationship}_id".to_sym)
|
29
|
+
dot << %Q%#{id} -> #{doc._id} [ label = "#{relationship}"];\n% if id
|
30
|
+
end
|
31
|
+
|
32
|
+
end
|
33
|
+
dot << "}"
|
34
|
+
|
35
|
+
File.open("graphs/data.dot", "w") { |f| f.write(dot) }
|
36
|
+
|
37
|
+
system "dot -Tpng -o graphs/all_docs.png graphs/data.dot"
|
38
|
+
end
|
39
|
+
|
40
|
+
end
|
41
|
+
|
42
|
+
end
|
@@ -18,7 +18,7 @@ module RelaxDB
|
|
18
18
|
view_path = "_view/#{@klass}/all"
|
19
19
|
map_function = ViewCreator.all(@klass)
|
20
20
|
|
21
|
-
@all = RelaxDB
|
21
|
+
@all = RelaxDB.retrieve(view_path, @klass, "all", map_function)
|
22
22
|
end
|
23
23
|
|
24
24
|
def sorted_by(*atts)
|
@@ -27,7 +27,7 @@ module RelaxDB
|
|
27
27
|
q = Query.new(@klass.name, v.view_name)
|
28
28
|
yield q if block_given?
|
29
29
|
|
30
|
-
RelaxDB
|
30
|
+
RelaxDB.retrieve(q.view_path, @klass, v.view_name, v.map_function)
|
31
31
|
end
|
32
32
|
|
33
33
|
# Note that this method leaves the corresponding DesignDoc for the associated class intact
|
data/lib/relaxdb/document.rb
CHANGED
@@ -61,7 +61,7 @@ module RelaxDB
|
|
61
61
|
# The default _id will be overwritten if loaded from CouchDB
|
62
62
|
self._id = UuidGenerator.uuid
|
63
63
|
|
64
|
-
@errors =
|
64
|
+
@errors = Errors.new
|
65
65
|
|
66
66
|
# Set default properties if this object has not known CouchDB
|
67
67
|
unless hash["_rev"]
|
@@ -109,9 +109,12 @@ module RelaxDB
|
|
109
109
|
|
110
110
|
def to_json
|
111
111
|
data = {}
|
112
|
-
self.class.belongs_to_rels.each do |relationship|
|
112
|
+
self.class.belongs_to_rels.each do |relationship, opts|
|
113
113
|
id = instance_variable_get("@#{relationship}_id".to_sym)
|
114
114
|
data["#{relationship}_id"] = id if id
|
115
|
+
if opts[:denormalise]
|
116
|
+
add_denormalised_data(data, relationship, opts)
|
117
|
+
end
|
115
118
|
end
|
116
119
|
properties.each do |prop|
|
117
120
|
prop_val = instance_variable_get("@#{prop}".to_sym)
|
@@ -121,16 +124,30 @@ module RelaxDB
|
|
121
124
|
data.to_json
|
122
125
|
end
|
123
126
|
|
127
|
+
# quick n' dirty denormalisation - explicit denormalisation will probably become a
|
128
|
+
# permanent fixture of RelaxDB, but quite likely in a different form to this one
|
129
|
+
def add_denormalised_data(data, relationship, opts)
|
130
|
+
obj = send(relationship)
|
131
|
+
if obj
|
132
|
+
opts[:denormalise].each do |prop_name|
|
133
|
+
val = obj.send(prop_name)
|
134
|
+
data["#{relationship}_#{prop_name}"] = val
|
135
|
+
end
|
136
|
+
end
|
137
|
+
end
|
138
|
+
|
124
139
|
def save
|
125
|
-
|
140
|
+
return false unless before_save
|
141
|
+
return false unless validates?
|
142
|
+
|
143
|
+
set_created_at if unsaved?
|
126
144
|
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
end
|
145
|
+
resp = RelaxDB.db.put("#{_id}", to_json)
|
146
|
+
self._rev = JSON.parse(resp.body)["rev"]
|
147
|
+
|
148
|
+
after_save
|
149
|
+
|
150
|
+
self
|
134
151
|
end
|
135
152
|
|
136
153
|
def validates?
|
@@ -149,16 +166,21 @@ module RelaxDB
|
|
149
166
|
success
|
150
167
|
end
|
151
168
|
|
169
|
+
# Hmm. Rename... never_saved? newnew?
|
152
170
|
def unsaved?
|
153
|
-
|
171
|
+
@_rev.nil?
|
154
172
|
end
|
173
|
+
alias_method :new_record?, :unsaved?
|
155
174
|
|
156
|
-
def
|
157
|
-
|
175
|
+
def to_param
|
176
|
+
self._id
|
177
|
+
end
|
178
|
+
alias_method :id, :to_param
|
179
|
+
|
180
|
+
def set_created_at
|
181
|
+
if methods.include? "created_at"
|
158
182
|
# Don't override it if it's already been set
|
159
|
-
|
160
|
-
instance_variable_set(:@created_at, Time.now)
|
161
|
-
end
|
183
|
+
@created_at = Time.now if @created_at.nil?
|
162
184
|
end
|
163
185
|
end
|
164
186
|
|
@@ -240,9 +262,9 @@ module RelaxDB
|
|
240
262
|
@has_one_rels ||= []
|
241
263
|
end
|
242
264
|
|
243
|
-
def self.belongs_to(relationship)
|
244
|
-
@belongs_to_rels ||=
|
245
|
-
@belongs_to_rels
|
265
|
+
def self.belongs_to(relationship, opts={})
|
266
|
+
@belongs_to_rels ||= {}
|
267
|
+
@belongs_to_rels[relationship] = opts
|
246
268
|
|
247
269
|
define_method(relationship) do
|
248
270
|
create_or_get_proxy(BelongsToProxy, relationship).target
|
@@ -261,7 +283,7 @@ module RelaxDB
|
|
261
283
|
|
262
284
|
def self.belongs_to_rels
|
263
285
|
# Don't force clients to check that it's instantiated
|
264
|
-
@belongs_to_rels ||=
|
286
|
+
@belongs_to_rels ||= {}
|
265
287
|
end
|
266
288
|
|
267
289
|
def self.all_relationships
|
@@ -271,7 +293,7 @@ module RelaxDB
|
|
271
293
|
def self.all
|
272
294
|
@all_delegator ||= AllDelegator.new(self)
|
273
295
|
end
|
274
|
-
|
296
|
+
|
275
297
|
# destroy! nullifies all relationships with peers and children before deleting
|
276
298
|
# itself in CouchDB
|
277
299
|
# The nullification and deletion are not performed in a transaction
|
@@ -292,6 +314,38 @@ module RelaxDB
|
|
292
314
|
RelaxDB.db.delete("#{_id}?rev=#{_rev}")
|
293
315
|
self
|
294
316
|
end
|
317
|
+
|
318
|
+
#
|
319
|
+
# Callbacks - define these in a module and mix'em'in ?
|
320
|
+
#
|
321
|
+
def self.before_save(callback)
|
322
|
+
before_save_callbacks << callback
|
323
|
+
end
|
324
|
+
|
325
|
+
def self.before_save_callbacks
|
326
|
+
@before_save ||= []
|
327
|
+
end
|
328
|
+
|
329
|
+
def before_save
|
330
|
+
self.class.before_save_callbacks.each do |callback|
|
331
|
+
resp = callback.is_a?(Proc) ? callback.call(self) : send(callback)
|
332
|
+
return false unless resp
|
333
|
+
end
|
334
|
+
end
|
335
|
+
|
336
|
+
def self.after_save(callback)
|
337
|
+
after_save_callbacks << callback
|
338
|
+
end
|
339
|
+
|
340
|
+
def self.after_save_callbacks
|
341
|
+
@after_save_callbacks ||= []
|
342
|
+
end
|
343
|
+
|
344
|
+
def after_save
|
345
|
+
self.class.after_save_callbacks.each do |callback|
|
346
|
+
callback.is_a?(Proc) ? callback.call(self) : send(callback)
|
347
|
+
end
|
348
|
+
end
|
295
349
|
|
296
350
|
end
|
297
351
|
|
@@ -10,7 +10,7 @@ module RelaxDB
|
|
10
10
|
@opts = opts
|
11
11
|
|
12
12
|
@target_class = opts[:class]
|
13
|
-
@relationship_as_viewed_by_target = (opts[:known_as] || client.class.name.
|
13
|
+
@relationship_as_viewed_by_target = (opts[:known_as] || client.class.name.snake_case).to_s
|
14
14
|
|
15
15
|
@children = load_children
|
16
16
|
end
|
@@ -5,8 +5,8 @@ module RelaxDB
|
|
5
5
|
def initialize(client, relationship)
|
6
6
|
@client = client
|
7
7
|
@relationship = relationship
|
8
|
-
@target_class = @relationship.to_s.
|
9
|
-
@relationship_as_viewed_by_target = client.class.
|
8
|
+
@target_class = @relationship.to_s.camel_case
|
9
|
+
@relationship_as_viewed_by_target = client.class.name.snake_case
|
10
10
|
|
11
11
|
@target = nil
|
12
12
|
end
|
data/lib/relaxdb/relaxdb.rb
CHANGED
@@ -25,6 +25,10 @@ module RelaxDB
|
|
25
25
|
db.list_dbs
|
26
26
|
end
|
27
27
|
|
28
|
+
def replicate_db(source, target)
|
29
|
+
db.replicate_db source, target
|
30
|
+
end
|
31
|
+
|
28
32
|
def bulk_save(*objs)
|
29
33
|
docs = {}
|
30
34
|
objs.each { |o| docs[o._id] = o }
|
@@ -45,6 +49,7 @@ module RelaxDB
|
|
45
49
|
create_object(data)
|
46
50
|
end
|
47
51
|
|
52
|
+
# Used internally by RelaxDB
|
48
53
|
def retrieve(view_path, design_doc, view_name, map_function)
|
49
54
|
begin
|
50
55
|
resp = db.get(view_path)
|
@@ -57,29 +62,42 @@ module RelaxDB
|
|
57
62
|
create_from_hash(data)
|
58
63
|
end
|
59
64
|
|
60
|
-
|
65
|
+
# Requests the given view from CouchDB and returns a hash.
|
66
|
+
# This method should typically be wrapped in one of merge, instantiate, or reduce_result.
|
67
|
+
def view(design_doc, view_name)
|
61
68
|
q = Query.new(design_doc, view_name)
|
62
69
|
yield q if block_given?
|
63
70
|
|
64
71
|
resp = db.get(q.view_path)
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
72
|
+
JSON.parse(resp.body)
|
73
|
+
end
|
74
|
+
|
75
|
+
# Should be invoked on the result of a join view
|
76
|
+
# Merges all rows based on merge_key and returns an array of ViewOject
|
77
|
+
def merge(data, merge_key)
|
78
|
+
merged = {}
|
79
|
+
data["rows"].each do |row|
|
80
|
+
value = row["value"]
|
81
|
+
merged[value[merge_key]] ||= {}
|
82
|
+
merged[value[merge_key]].merge!(value)
|
74
83
|
end
|
84
|
+
|
85
|
+
merged.values.map { |v| ViewObject.create(v) }
|
86
|
+
end
|
87
|
+
|
88
|
+
# Creates RelaxDB::Document objects from the result
|
89
|
+
def instantiate(data)
|
90
|
+
create_from_hash(data)
|
91
|
+
end
|
92
|
+
|
93
|
+
# Returns a scalar, an object, or an Array of objects
|
94
|
+
def reduce_result(data)
|
95
|
+
obj = data["rows"][0] && data["rows"][0]["value"]
|
96
|
+
ViewObject.create(obj)
|
75
97
|
end
|
76
98
|
|
77
99
|
def create_from_hash(data)
|
78
|
-
|
79
|
-
data["rows"].each do |row|
|
80
|
-
@objects << create_object(row["value"])
|
81
|
-
end
|
82
|
-
@objects
|
100
|
+
data["rows"].map { |row| create_object(row["value"]) }
|
83
101
|
end
|
84
102
|
|
85
103
|
def create_object(data)
|
data/lib/relaxdb/server.rb
CHANGED
@@ -59,15 +59,12 @@ module RelaxDB
|
|
59
59
|
end
|
60
60
|
|
61
61
|
def use_db(name)
|
62
|
-
|
63
|
-
@server.get("/#{name}")
|
64
|
-
rescue
|
65
|
-
@server.put("/#{name}", "")
|
66
|
-
end
|
62
|
+
create_db_if_non_existant(name)
|
67
63
|
@db = name
|
68
64
|
end
|
69
65
|
|
70
66
|
def delete_db(name)
|
67
|
+
@logger.info("Deleting database #{name}")
|
71
68
|
@server.delete("/#{name}")
|
72
69
|
end
|
73
70
|
|
@@ -75,6 +72,13 @@ module RelaxDB
|
|
75
72
|
JSON.parse(@server.get("/_all_dbs").body)
|
76
73
|
end
|
77
74
|
|
75
|
+
def replicate_db(source, target)
|
76
|
+
@logger.info("Replicating from #{source} to #{target}")
|
77
|
+
create_db_if_non_existant target
|
78
|
+
data = { "source" => source, "target" => target}
|
79
|
+
@server.post("/_replicate", data.to_json)
|
80
|
+
end
|
81
|
+
|
78
82
|
def delete(path=nil)
|
79
83
|
@logger.info("DELETE /#{@db}/#{unesc(path)}")
|
80
84
|
@server.delete("/#{@db}/#{path}")
|
@@ -107,6 +111,16 @@ module RelaxDB
|
|
107
111
|
@db
|
108
112
|
end
|
109
113
|
|
114
|
+
private
|
115
|
+
|
116
|
+
def create_db_if_non_existant(name)
|
117
|
+
begin
|
118
|
+
@server.get("/#{name}")
|
119
|
+
rescue
|
120
|
+
@server.put("/#{name}", "")
|
121
|
+
end
|
122
|
+
end
|
123
|
+
|
110
124
|
end
|
111
125
|
|
112
126
|
end
|
data/lib/relaxdb.rb
CHANGED
@@ -17,6 +17,7 @@ require 'relaxdb/all_delegator'
|
|
17
17
|
require 'relaxdb/belongs_to_proxy'
|
18
18
|
require 'relaxdb/design_doc'
|
19
19
|
require 'relaxdb/document'
|
20
|
+
require 'relaxdb/extlib'
|
20
21
|
require 'relaxdb/has_many_proxy'
|
21
22
|
require 'relaxdb/has_one_proxy'
|
22
23
|
require 'relaxdb/query'
|
@@ -28,6 +29,7 @@ require 'relaxdb/uuid_generator'
|
|
28
29
|
require 'relaxdb/view_object'
|
29
30
|
require 'relaxdb/view_uploader'
|
30
31
|
require 'relaxdb/views'
|
32
|
+
require 'more/grapher.rb'
|
31
33
|
|
32
34
|
module RelaxDB
|
33
35
|
end
|
@@ -0,0 +1,64 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/spec_helper.rb'
|
2
|
+
|
3
|
+
# A little naiive, needs quite a bit more thought and work
|
4
|
+
|
5
|
+
describe RelaxDB::Document, "callbacks" do
|
6
|
+
|
7
|
+
before(:all) do
|
8
|
+
RelaxDB.configure(:host => "localhost", :port => 5984)
|
9
|
+
end
|
10
|
+
|
11
|
+
before(:each) do
|
12
|
+
RelaxDB.delete_db "relaxdb_spec_db" rescue "ok"
|
13
|
+
RelaxDB.use_db "relaxdb_spec_db"
|
14
|
+
end
|
15
|
+
|
16
|
+
describe "before_save" do
|
17
|
+
|
18
|
+
it "should be run before the object is saved" do
|
19
|
+
c = Class.new(RelaxDB::Document) do
|
20
|
+
before_save lambda { |s| s.gem += 1 if s.unsaved? }
|
21
|
+
property :gem
|
22
|
+
end
|
23
|
+
p = c.new(:gem => 5).save
|
24
|
+
p.gem.should == 6
|
25
|
+
end
|
26
|
+
|
27
|
+
it "should prevent the object from being saved if it returns false" do
|
28
|
+
c = Class.new(RelaxDB::Document) do
|
29
|
+
before_save lambda { false }
|
30
|
+
end
|
31
|
+
c.new.save.should == false
|
32
|
+
end
|
33
|
+
|
34
|
+
it "may be a proc" do
|
35
|
+
c = Class.new(RelaxDB::Document) do
|
36
|
+
before_save lambda { false }
|
37
|
+
end
|
38
|
+
c.new.save.should == false
|
39
|
+
end
|
40
|
+
|
41
|
+
it "may be a method" do
|
42
|
+
c = Class.new(RelaxDB::Document) do
|
43
|
+
before_save :never
|
44
|
+
def never; false; end
|
45
|
+
end
|
46
|
+
c.new.save.should == false
|
47
|
+
end
|
48
|
+
|
49
|
+
end
|
50
|
+
|
51
|
+
describe "after_save" do
|
52
|
+
|
53
|
+
it "should be run after the object is saved" do
|
54
|
+
c = Class.new(RelaxDB::Document) do
|
55
|
+
after_save lambda { |s| s.gem +=1 unless s.unsaved? }
|
56
|
+
property :gem
|
57
|
+
end
|
58
|
+
p = c.new(:gem => 5).save
|
59
|
+
p.gem.should == 6
|
60
|
+
end
|
61
|
+
|
62
|
+
end
|
63
|
+
|
64
|
+
end
|
@@ -0,0 +1,49 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/spec_helper.rb'
|
2
|
+
|
3
|
+
# Experimental only for now
|
4
|
+
|
5
|
+
class Tree < RelaxDB::Document
|
6
|
+
property :name
|
7
|
+
property :climate
|
8
|
+
has_one :leaf
|
9
|
+
end
|
10
|
+
|
11
|
+
class Leaf < RelaxDB::Document
|
12
|
+
belongs_to :tree, :denormalise => [:name]
|
13
|
+
end
|
14
|
+
|
15
|
+
describe RelaxDB::Document, "denormalisation" do
|
16
|
+
|
17
|
+
before(:all) do
|
18
|
+
RelaxDB.configure(:host => "localhost", :port => 5984)
|
19
|
+
end
|
20
|
+
|
21
|
+
before(:each) do
|
22
|
+
RelaxDB.delete_db "relaxdb_spec_db" rescue "ok"
|
23
|
+
RelaxDB.use_db "relaxdb_spec_db"
|
24
|
+
end
|
25
|
+
|
26
|
+
describe "belongs_to" do
|
27
|
+
|
28
|
+
it "should store denormalised options in its json representation" do
|
29
|
+
tree = Tree.new(:name => "sapling").save
|
30
|
+
leaf = Leaf.new(:tree => tree)
|
31
|
+
obj = JSON.parse(leaf.to_json)
|
32
|
+
obj["tree_name"].should == "sapling"
|
33
|
+
end
|
34
|
+
|
35
|
+
it "should ignore denormalised options for nil properties" do
|
36
|
+
Leaf.new.to_json
|
37
|
+
end
|
38
|
+
|
39
|
+
it "should not interfere with normal belongs_to behaviour" do
|
40
|
+
tree = Tree.new(:name => "sapling", :climate => "tropical").save
|
41
|
+
leaf = Leaf.new(:tree => tree).save
|
42
|
+
leaf = RelaxDB.load(leaf._id)
|
43
|
+
leaf.tree.name.should == "sapling"
|
44
|
+
leaf.tree.climate.should == "tropical"
|
45
|
+
end
|
46
|
+
|
47
|
+
end
|
48
|
+
|
49
|
+
end
|
data/spec/document_spec.rb
CHANGED
@@ -148,7 +148,7 @@ describe RelaxDB::Document do
|
|
148
148
|
|
149
149
|
end
|
150
150
|
|
151
|
-
describe "
|
151
|
+
describe ".all" do
|
152
152
|
|
153
153
|
it "should return all instances of that class" do
|
154
154
|
Photo.new.save
|
@@ -164,7 +164,7 @@ describe RelaxDB::Document do
|
|
164
164
|
|
165
165
|
end
|
166
166
|
|
167
|
-
describe "
|
167
|
+
describe ".all.sorted_by" do
|
168
168
|
|
169
169
|
it "should sort ascending by default" do
|
170
170
|
Post.new(:content => "b").save
|
@@ -267,7 +267,7 @@ describe RelaxDB::Document do
|
|
267
267
|
end
|
268
268
|
r.new.save.should be_false
|
269
269
|
end
|
270
|
-
|
270
|
+
|
271
271
|
it "should prevent an object from being saved if it throws an exception" do
|
272
272
|
r = Class.new(RelaxDB::Document) do
|
273
273
|
property :thumbs_up, :validator => lambda { raise "foo" }
|
@@ -303,6 +303,23 @@ describe RelaxDB::Document do
|
|
303
303
|
x.errors[:bar].should == "rab"
|
304
304
|
end
|
305
305
|
|
306
|
+
it "may be a proc" do
|
307
|
+
r = Class.new(RelaxDB::Document) do
|
308
|
+
property :thumbs_up, :validator => lambda { false }
|
309
|
+
end
|
310
|
+
r.new.save.should be_false
|
311
|
+
end
|
312
|
+
|
313
|
+
it "may be a method" do
|
314
|
+
r = Class.new(RelaxDB::Document) do
|
315
|
+
property :thumbs_up, :validator => :count_em
|
316
|
+
def count_em
|
317
|
+
false
|
318
|
+
end
|
319
|
+
end
|
320
|
+
r.new.save.should be_false
|
321
|
+
end
|
322
|
+
|
306
323
|
end
|
307
324
|
|
308
325
|
end
|
data/spec/has_many_spec.rb
CHANGED
@@ -33,6 +33,14 @@ describe RelaxDB::HasManyProxy do
|
|
33
33
|
u = RelaxDB.load u._id
|
34
34
|
u.items.size.should == 1
|
35
35
|
end
|
36
|
+
|
37
|
+
it "should work with MultiWordClassNames" do
|
38
|
+
c = MultiWordChild.new
|
39
|
+
m = MultiWordClass.new.save
|
40
|
+
m.multi_word_children << c
|
41
|
+
m = RelaxDB.load m._id
|
42
|
+
m.multi_word_children[0].should == c
|
43
|
+
end
|
36
44
|
|
37
45
|
describe "#<<" do
|
38
46
|
|
data/spec/has_one_spec.rb
CHANGED
@@ -52,6 +52,13 @@ describe RelaxDB::HasOneProxy do
|
|
52
52
|
p = RelaxDB.load p._id
|
53
53
|
p.rating.photo.should == p
|
54
54
|
end
|
55
|
+
|
56
|
+
it "should work with MultiWordClassNames" do
|
57
|
+
c = MultiWordChild.new
|
58
|
+
m = MultiWordClass.new(:multi_word_child => c).save
|
59
|
+
m = RelaxDB.load m._id
|
60
|
+
m.multi_word_child.should == c
|
61
|
+
end
|
55
62
|
|
56
63
|
describe "#=" do
|
57
64
|
|
data/spec/relaxdb_spec.rb
CHANGED
@@ -44,7 +44,66 @@ describe RelaxDB do
|
|
44
44
|
|
45
45
|
end
|
46
46
|
|
47
|
-
|
47
|
+
describe ".replicate_db" do
|
48
|
+
|
49
|
+
it "should replicate the named database" do
|
50
|
+
orig = "relaxdb_spec_db"
|
51
|
+
replica = "relaxdb_spec_db_replica"
|
52
|
+
RelaxDB.delete_db replica rescue "ok"
|
53
|
+
Atom.new.save # implicitly saved to orig
|
54
|
+
RelaxDB.replicate_db orig, replica
|
55
|
+
RelaxDB.use_db replica
|
56
|
+
Atom.all.size.should == 1
|
57
|
+
end
|
58
|
+
|
59
|
+
end
|
60
|
+
|
61
|
+
describe ".view" do
|
62
|
+
|
63
|
+
map_func = %Q<
|
64
|
+
function (doc) {
|
65
|
+
emit(doc._id, doc);
|
66
|
+
}
|
67
|
+
>
|
68
|
+
|
69
|
+
it "should request a view and return a hash" do
|
70
|
+
RelaxDB::DesignDocument.get("viewtest").add_view("simple", "map", map_func).save
|
71
|
+
data = RelaxDB.view("viewtest", "simple")
|
72
|
+
data.should be_instance_of(Hash)
|
73
|
+
end
|
74
|
+
|
75
|
+
it "may accept a block" do
|
76
|
+
RelaxDB::DesignDocument.get("viewtest").add_view("simple", "map", map_func).save
|
77
|
+
RelaxDB.db.put("x", {}.to_json)
|
78
|
+
RelaxDB.db.put("y", {}.to_json)
|
79
|
+
data = RelaxDB.view("viewtest", "simple") { |q| q.key("x") }
|
80
|
+
data["rows"].size.should == 1
|
81
|
+
end
|
82
|
+
|
48
83
|
end
|
84
|
+
|
85
|
+
describe ".merge" do
|
86
|
+
|
87
|
+
it "should merge rows sharing a common merge key into a single ViewObject" do
|
88
|
+
rows = [
|
89
|
+
{"value" => {"sculptor_id" => 1, "sculpture_name" => "strandbeesten"} },
|
90
|
+
{"value" => {"sculptor_id" => 1, "sculptor_name" => "hans"} },
|
91
|
+
{"value" => {"sculptor_id" => 2, "sculpture_name" => "parading dogs"} },
|
92
|
+
{"value" => {"sculptor_id" => 2, "sculptor_name" => "holmes"} }
|
93
|
+
]
|
94
|
+
data = {"rows" => rows}
|
95
|
+
result = RelaxDB.merge(data, "sculptor_id")
|
96
|
+
result = result.sort { |a, b| a.sculptor_name <=> b.sculptor_name }
|
97
|
+
|
98
|
+
result[0].sculptor_name.should == "hans"
|
99
|
+
result[0].sculpture_name.should == "strandbeesten"
|
100
|
+
result[1].sculptor_name.should == "holmes"
|
101
|
+
result[1].sculpture_name.should == "parading dogs"
|
102
|
+
end
|
103
|
+
|
104
|
+
end
|
105
|
+
|
106
|
+
# if caching is added
|
107
|
+
# it "should offer an example where behaviour is different with caching enabled and caching disabled"
|
49
108
|
|
50
109
|
end
|
data/spec/spec_models.rb
CHANGED
@@ -85,6 +85,15 @@ class Tagging < RelaxDB::Document
|
|
85
85
|
|
86
86
|
end
|
87
87
|
|
88
|
+
class MultiWordClass < RelaxDB::Document
|
89
|
+
has_one :multi_word_child
|
90
|
+
has_many :multi_word_children, :class => "MultiWordChild"
|
91
|
+
end
|
92
|
+
|
93
|
+
class MultiWordChild < RelaxDB::Document
|
94
|
+
belongs_to :multi_word_class
|
95
|
+
end
|
96
|
+
|
88
97
|
class TwitterUser < RelaxDB::Document
|
89
98
|
|
90
99
|
property :name
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: paulcarey-relaxdb
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1.
|
4
|
+
version: 0.1.3
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Paul Carey
|
@@ -57,6 +57,7 @@ files:
|
|
57
57
|
- lib/relaxdb/belongs_to_proxy.rb
|
58
58
|
- lib/relaxdb/design_doc.rb
|
59
59
|
- lib/relaxdb/document.rb
|
60
|
+
- lib/relaxdb/extlib.rb
|
60
61
|
- lib/relaxdb/has_many_proxy.rb
|
61
62
|
- lib/relaxdb/has_one_proxy.rb
|
62
63
|
- lib/relaxdb/query.rb
|
@@ -68,8 +69,11 @@ files:
|
|
68
69
|
- lib/relaxdb/view_object.rb
|
69
70
|
- lib/relaxdb/view_uploader.rb
|
70
71
|
- lib/relaxdb/views.rb
|
72
|
+
- lib/more/grapher.rb
|
71
73
|
- lib/relaxdb.rb
|
72
74
|
- spec/belongs_to_spec.rb
|
75
|
+
- spec/callbacks_spec.rb
|
76
|
+
- spec/denormalisation_spec.rb
|
73
77
|
- spec/design_doc_spec.rb
|
74
78
|
- spec/document_spec.rb
|
75
79
|
- spec/has_many_spec.rb
|