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.
Files changed (49) hide show
  1. data/README.textile +21 -23
  2. data/Rakefile +2 -7
  3. data/docs/spec_results.html +5 -5
  4. data/lib/more/grapher.rb +1 -1
  5. data/lib/relaxdb.rb +3 -5
  6. data/lib/relaxdb/all_delegator.rb +19 -13
  7. data/lib/relaxdb/document.rb +150 -218
  8. data/lib/relaxdb/extlib.rb +7 -1
  9. data/lib/relaxdb/migration.rb +11 -8
  10. data/lib/relaxdb/net_http_server.rb +19 -1
  11. data/lib/relaxdb/paginator.rb +30 -11
  12. data/lib/relaxdb/query.rb +1 -1
  13. data/lib/relaxdb/{belongs_to_proxy.rb → references_proxy.rb} +3 -3
  14. data/lib/relaxdb/relaxdb.rb +87 -7
  15. data/lib/relaxdb/server.rb +8 -2
  16. data/lib/relaxdb/taf2_curb_server.rb +2 -1
  17. data/lib/relaxdb/uuid_generator.rb +38 -2
  18. data/lib/relaxdb/view_by_delegator.rb +34 -0
  19. data/lib/relaxdb/view_object.rb +1 -1
  20. data/lib/relaxdb/view_uploader.rb +16 -2
  21. data/lib/relaxdb/views.rb +23 -55
  22. data/readme.rb +3 -3
  23. data/spec/all_delegator_spec.rb +52 -0
  24. data/spec/callbacks_spec.rb +4 -4
  25. data/spec/derived_properties_spec.rb +4 -4
  26. data/spec/design_doc_spec.rb +2 -2
  27. data/spec/doc_inheritable_spec.rb +2 -2
  28. data/spec/document_spec.rb +47 -25
  29. data/spec/migration_spec.rb +12 -10
  30. data/spec/qpaginate_spec.rb +88 -0
  31. data/spec/query_spec.rb +2 -2
  32. data/spec/references_proxy_spec.rb +94 -0
  33. data/spec/relaxdb_spec.rb +29 -21
  34. data/spec/server_spec.rb +4 -3
  35. data/spec/spec_helper.rb +1 -0
  36. data/spec/spec_models.rb +48 -57
  37. data/spec/uuid_generator_spec.rb +34 -0
  38. data/spec/view_by_spec.rb +62 -54
  39. data/spec/view_docs_by_spec.rb +85 -0
  40. metadata +38 -27
  41. data/lib/more/atomic_bulk_save_support.rb +0 -18
  42. data/lib/relaxdb/has_many_proxy.rb +0 -101
  43. data/lib/relaxdb/has_one_proxy.rb +0 -42
  44. data/lib/relaxdb/references_many_proxy.rb +0 -97
  45. data/spec/belongs_to_spec.rb +0 -124
  46. data/spec/has_many_spec.rb +0 -202
  47. data/spec/has_one_spec.rb +0 -123
  48. data/spec/references_many_spec.rb +0 -173
  49. data/spec/view_spec.rb +0 -23
@@ -7,7 +7,7 @@ module RelaxDB
7
7
  hash.each do |k, v|
8
8
 
9
9
  if k.to_s =~ /_at$/
10
- v = Time.local(*ParseDate.parsedate(v)) rescue v
10
+ v = Time.parse(v).utc rescue v
11
11
  end
12
12
 
13
13
  instance_variable_set("@#{k}", v)
@@ -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
- # Exepcted function declaration form is
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]].join
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, doc);
11
+ emit(doc._id, 1);
12
12
  }
13
13
  }
14
14
  QUERY
15
15
 
16
- View.new "#{class_name}_all", map, sum_reduce_func
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}, doc);
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, sum_reduce_func
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
- def save
101
- dd = design_doc
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
- view_by :sender_name
32
- view_by :sender_id
33
- view_by :recipient_id, :created_at, :descending => true
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
@@ -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
- belongs_to :event
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
- belongs_to :event
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
- belongs_to :event
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
- belongs_to :event
96
+ references :event
97
97
  end
98
98
 
99
99
  event = Class.new(RelaxDB::Document) do
@@ -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.load("_design/foo").should_not be_nil
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 == [a, d]
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
@@ -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 silently ignore parameters that don't specify class attributes" do
29
- # Consider this a feature or bug. It allows an object containing both request params
30
- # and superflous data to be passed directly to a constructor.
31
- Post.new(:foo => "").save
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.instance_variable_get(:@val).should == 101
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 "should not be used to modify state" do
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 == 81
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
- # p.false_bool.should be_false
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.empty.should be_nil
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