paulcarey-relaxdb 0.2.4 → 0.2.5

Sign up to get free protection for your applications and to get access to all the features.
data/README.textile CHANGED
@@ -1,16 +1,22 @@
1
1
  h3. What's New?
2
2
 
3
+ * Denormalisation via derived properties. Examples in spec/derived_properties_spec.rb.
4
+
5
+ * Validations may be skipped by passing the attribute symbol(s) to @save@ or @save!@.
6
+
7
+ * Semantic changes for @ has_many#<< @. The parent object is now assigned to the child object *prior* to validation. This potentially breaking change was made to allow child objects to derive properties from a parent object.
8
+
3
9
  * Semantic consistency for load, load!, save and save!. The bang versions raise an exception when their more relaxed siblings would simply return nil.
4
10
 
5
11
  * Minimal support for CouchDB validation
6
12
 
7
- * Time storage changes. All Time objects are now converted to UTC and formatted as %Y/%m/%d %H:%M:%S +0000. Storing all Times as UTC should have been happening anyway. Formatting Times as above (as opposed to ISO 8601 as was done prior to 0.2.3) allows the Time strings to be passed directly to Date.new in a JavaScript interpreter.
13
+ * Time storage changes. All Time objects are now converted to UTC and formatted as @ %Y/%m/%d %H:%M:%S +0000 @. Storing all Times as UTC should have been happening anyway. Formatting Times as above (as opposed to ISO 8601 as was done prior to 0.2.3) allows the Time strings to be passed directly to Date.new in a JavaScript interpreter.
8
14
 
9
15
  * Pagination! CouchDB offers great support for retrieving a subset of data, but the housekeeping is tricky. RelaxDB takes care of it.
10
16
  ** Note that if you invoke paginate_by on an already created view, the necessary reduce function won't be automatically created. Take a look at SortedByView and create the reduce func by hand.
11
17
  * Support for multi key post
12
18
  ** For example, @ Numbers.all.sorted_by(:val) { |q| q.keys([1,2,3,5]) } @
13
- * Works with CouchDB 0.9 trunk as of 2008/10/08. Note that pagination won't work correctly on trunk until issue "COUCHDB-135":http://issues.apache.org/jira/browse/COUCHDB-135 is fixed.
19
+ * Works with CouchDB 0.9 trunk as of 2009/01/02. Note that pagination won't work correctly on trunk until issue "COUCHDB-135":http://issues.apache.org/jira/browse/COUCHDB-135 is fixed.
14
20
 
15
21
  *Note*: 0.2.1 requires CouchDB 0.9 trunk. 0.2.0 works with CouchDB 0.8 onwards.
16
22
 
@@ -154,13 +160,6 @@ RelaxDB::GraphCreator.create
154
160
 
155
161
  Requires graphviz. Useful for visualising relationships between a limited number of document e.g. test fixtures. "Description and example":http://dev.strawberrydiva.com/visually_explore_couchdb/.
156
162
 
157
- h3. Experimental Features
158
-
159
- * Declarative denormalisation
160
- ** Create a partial object graph in JSON with a single call
161
- ** May be used to require fewer GET requests
162
- ** View the denormalisation spec for examples
163
-
164
163
  h2. Incomplete list of limitations
165
164
 
166
165
  * Error handling is not robust
data/Rakefile CHANGED
@@ -4,7 +4,7 @@ require 'spec/rake/spectask'
4
4
 
5
5
  PLUGIN = "relaxdb"
6
6
  NAME = "relaxdb"
7
- GEM_VERSION = "0.2.4"
7
+ GEM_VERSION = "0.2.5"
8
8
  AUTHOR = "Paul Carey"
9
9
  EMAIL = "paul.p.carey@gmail.com"
10
10
  HOMEPAGE = "http://github.com/paulcarey/relaxdb/"
@@ -31,14 +31,17 @@ module RelaxDB
31
31
  end
32
32
  end
33
33
 
34
- create_validator(prop, opts[:validator]) if opts[:validator]
34
+ if opts[:validator]
35
+ create_validator(prop, opts[:validator])
36
+ end
35
37
 
36
38
  if opts[:validation_msg]
37
- define_method("#{prop}_validation_msg") do
38
- opts[:validation_msg]
39
- end
39
+ create_validation_msg(prop, opts[:validation_msg])
40
40
  end
41
41
 
42
+ if opts[:derived]
43
+ add_derived_prop(prop, opts[:derived])
44
+ end
42
45
  end
43
46
 
44
47
  def self.properties
@@ -47,14 +50,47 @@ module RelaxDB
47
50
  end
48
51
 
49
52
  def self.create_validator(att, v)
50
- define_method("validate_#{att}") do |att_val|
51
- if v.is_a? Proc
52
- v.call(att_val)
53
- elsif respond_to? "validator_#{v}"
54
- send("validator_#{v}", att_val)
55
- else
56
- send(v, att_val)
57
- end
53
+ method_name = "validate_#{att}"
54
+ if v.is_a? Proc
55
+ v.arity == 1 ?
56
+ define_method(method_name) { |att_val| v.call(att_val) } :
57
+ define_method(method_name) { |att_val| v.call(att_val, self) }
58
+ elsif instance_methods.include? "validator_#{v}"
59
+ define_method(method_name) { |att_val| send("validator_#{v}", att_val, self) }
60
+ else
61
+ define_method(method_name) { |att_val| send(v, att_val) }
62
+ end
63
+ end
64
+
65
+ def self.create_validation_msg(prop, validation_msg)
66
+ if validation_msg.is_a?(Proc)
67
+ validation_msg.arity == 1 ?
68
+ define_method("#{prop}_validation_msg") { |prop_val| validation_msg.call(prop_val) } :
69
+ define_method("#{prop}_validation_msg") { |prop_val| validation_msg.call(prop_val, self) }
70
+ else
71
+ define_method("#{prop}_validation_msg") { validation_msg }
72
+ end
73
+ end
74
+
75
+ # See derived_properties_spec.rb for usage
76
+ def self.add_derived_prop(prop, deriver)
77
+ source, writer = deriver[0], deriver[1]
78
+ @derived_prop_writers ||= {}
79
+ @derived_prop_writers[source] ||= {}
80
+ @derived_prop_writers[source][prop] = writer
81
+ end
82
+
83
+ def self.derived_prop_writers
84
+ @derived_prop_writers ||= {}
85
+ end
86
+
87
+ def write_derived_props(source)
88
+ writers = self.class.derived_prop_writers[source]
89
+ if writers
90
+ writers.each do |prop, writer|
91
+ current_val = send(prop)
92
+ send("#{prop}=", writer.call(current_val, self))
93
+ end
58
94
  end
59
95
  end
60
96
 
@@ -67,7 +103,9 @@ module RelaxDB
67
103
  property :_id
68
104
  property :_rev
69
105
 
70
- def initialize(hash={})
106
+ def initialize(set_via_writers={}, hash=nil)
107
+ hash, set_via_writers = set_via_writers, true if set_via_writers.is_a?(Hash)
108
+
71
109
  # The default _id will be overwritten if loaded from CouchDB
72
110
  self._id = UuidGenerator.uuid
73
111
 
@@ -82,28 +120,57 @@ module RelaxDB
82
120
  end
83
121
  end
84
122
 
85
- set_attributes(hash)
123
+ # Maybe use the presence of _rev in hash to determine this rather than
124
+ # exposing the implementation detail to clients that choose to override?
125
+ set_via_writers ? set_attributes(hash) : set_raw_attributes(hash)
86
126
  end
87
127
 
88
128
  def set_attributes(data)
89
129
  data.each do |key, val|
90
130
  # Only set instance variables on creation - object references are resolved on demand
91
131
 
92
- # If the variable name ends in _at try to convert it to a Time
93
- if key =~ /_at$/
132
+ # If the variable name ends in _at, _on or _date try to convert it to a Time
133
+ if [/_at$/, /_on$/, /_date$/].inject(nil) { |i, r| i ||= (key =~ r) }
94
134
  val = Time.parse(val).utc rescue val
95
135
  end
96
136
 
97
137
  # Ignore param keys that don't have a corresponding writer
98
138
  # This allows us to comfortably accept a hash containing superflous data
99
139
  # such as a params hash in a controller
140
+ send("#{key}=".to_sym, val) if methods.include? "#{key}="
141
+ end
142
+ end
143
+
144
+ # Set the raw attribute values rather than setting via the associated writers
145
+ # The associated writers may invoke validation functions or set derived values
146
+ # Such behaviour is unwanted when loading from CouchDB and potentially under
147
+ # other circumstances
148
+ def set_raw_attributes(data)
149
+ data.each do |key, val|
150
+ if [/_at$/, /_on$/, /_date$/].inject(nil) { |i, r| i ||= (key =~ r) }
151
+ val = Time.parse(val).utc rescue val
152
+ end
153
+
100
154
  if methods.include? "#{key}="
101
- send("#{key}=".to_sym, val)
155
+ key = key.to_sym
156
+ if properties.include? key
157
+ instance_variable_set("@#{key}".to_sym, val)
158
+ elsif self.class.has_one_rels.include? key
159
+ create_or_get_proxy(HasOneProxy, key).target = val
160
+ else
161
+ # belongs_to
162
+ if key.to_s =~ /_id$/
163
+ instance_variable_set("@#{key}".to_sym, val)
164
+ else
165
+ create_or_get_proxy(BelongsToProxy, key).target = val
166
+ end
167
+ end
102
168
  end
103
169
 
104
170
  end
105
- end
106
-
171
+
172
+ end
173
+
107
174
  def inspect
108
175
  s = "#<#{self.class}:#{self.object_id}"
109
176
  properties.each do |prop|
@@ -122,9 +189,6 @@ module RelaxDB
122
189
  self.class.belongs_to_rels.each do |relationship, opts|
123
190
  id = instance_variable_get("@#{relationship}_id".to_sym)
124
191
  data["#{relationship}_id"] = id if id
125
- if opts[:denormalise]
126
- add_denormalised_data(data, relationship, opts)
127
- end
128
192
  end
129
193
  properties.each do |prop|
130
194
  prop_val = instance_variable_get("@#{prop}".to_sym)
@@ -133,28 +197,16 @@ module RelaxDB
133
197
  data["class"] = self.class.name
134
198
  data.to_json
135
199
  end
136
-
137
- # quick n' dirty denormalisation - explicit denormalisation will probably become a
138
- # permanent fixture of RelaxDB, but quite likely in a different form to this one
139
- def add_denormalised_data(data, relationship, opts)
140
- obj = send(relationship)
141
- if obj
142
- opts[:denormalise].each do |prop_name|
143
- val = obj.send(prop_name)
144
- data["#{relationship}_#{prop_name}"] = val
145
- end
146
- end
147
- end
148
-
200
+
149
201
  # Order changed as of 30/10/2008 to be consistent with ActiveRecord
150
202
  # Not yet sure of final implemention for hooks - may lean more towards DM than AR
151
- def save
152
- return false unless validates?
203
+ def save(*validation_skip_list)
204
+ return false unless validates?(*validation_skip_list)
153
205
  return false unless before_save
154
206
 
155
207
  set_created_at if new_document?
156
208
 
157
- resp = RelaxDB.db.put("#{_id}", to_json)
209
+ resp = RelaxDB.db.put(_id, to_json)
158
210
  self._rev = JSON.parse(resp.body)["rev"]
159
211
 
160
212
  after_save
@@ -162,30 +214,33 @@ module RelaxDB
162
214
  self
163
215
  end
164
216
 
165
- def save!
166
- save || raise(DocumentNotSaved)
217
+ def save!(*validation_skip_list)
218
+ save(*validation_skip_list) || raise(DocumentNotSaved.new(self.errors.to_json))
167
219
  end
168
220
 
169
- def validates?
221
+ def validates?(*skip_list)
170
222
  total_success = true
171
223
  properties.each do |prop|
224
+ next if skip_list.include? prop
172
225
  if methods.include? "validate_#{prop}"
173
226
  prop_val = instance_variable_get("@#{prop}")
174
227
  success = send("validate_#{prop}", prop_val) rescue false
175
228
  unless success
176
229
  if methods.include? "#{prop}_validation_msg"
177
- @errors["#{prop}".to_sym] = send("#{prop}_validation_msg")
230
+ @errors[prop] = send("#{prop}_validation_msg", prop_val) rescue "validation_msg_exception:invalid:#{prop_val}"
231
+ else
232
+ @errors[prop] = "invalid:#{prop}"
178
233
  end
179
234
  end
180
235
  total_success &= success
181
236
  end
182
237
  end
183
238
 
184
- # Unsure whether to pass the id or the doc itself - id is all I need right now
185
239
  self.class.belongs_to_rels.each do |rel, opts|
186
240
  if methods.include? "validate_#{rel}"
187
241
  rel_val = instance_variable_get("@#{rel}_id")
188
242
  success = send("validate_#{rel}", rel_val) rescue false
243
+ @errors[rel] = "invalid:#{rel_val}" unless success
189
244
  total_success &= success
190
245
  end
191
246
  end
@@ -299,11 +354,13 @@ module RelaxDB
299
354
 
300
355
  define_method("#{relationship}=") do |new_target|
301
356
  create_or_get_proxy(BelongsToProxy, relationship).target = new_target
357
+ write_derived_props(relationship)
302
358
  end
303
359
 
304
360
  # Allows all writers to be invoked from the hash passed to initialize
305
361
  define_method("#{relationship}_id=") do |id|
306
362
  instance_variable_set("@#{relationship}_id".to_sym, id)
363
+ write_derived_props(relationship)
307
364
  end
308
365
 
309
366
  # Allows belongs_to relationships to be used by the paginator
@@ -16,13 +16,15 @@ module RelaxDB
16
16
  end
17
17
 
18
18
  def <<(obj)
19
- return false unless obj.validates?
20
19
  return false if @children.include?(obj)
21
-
20
+
22
21
  obj.send("#{@relationship_as_viewed_by_target}=".to_sym, @client)
23
- obj.save
24
- @children << obj
25
- self
22
+ if obj.save
23
+ @children << obj
24
+ self
25
+ else
26
+ false
27
+ end
26
28
  end
27
29
 
28
30
  def clear
@@ -151,7 +151,7 @@ module RelaxDB
151
151
  klass = data.delete("class")
152
152
  if klass
153
153
  k = Module.const_get(klass)
154
- k.new(data)
154
+ k.new(false, data)
155
155
  else
156
156
  # data is not of a known class
157
157
  ViewObject.create(data)
@@ -63,7 +63,12 @@ module RelaxDB
63
63
 
64
64
  class CouchDB
65
65
 
66
+ # Used for test instrumentation only i.e. to assert that
67
+ # an expected number of GET requests have been issued
68
+ attr_accessor :get_count
69
+
66
70
  def initialize(config)
71
+ @get_count = 0
67
72
  @server = RelaxDB::Server.new(config[:host], config[:port])
68
73
  @logger = config[:logger] ? config[:logger] : Logger.new(Tempfile.new('couchdb.log'))
69
74
  end
@@ -96,6 +101,7 @@ module RelaxDB
96
101
 
97
102
  # *ignored allows methods to invoke get or post indifferently
98
103
  def get(path=nil, *ignored)
104
+ @get_count += 1
99
105
  @logger.info("GET /#{@db}/#{unesc(path)}")
100
106
  @server.get("/#{@db}/#{path}")
101
107
  end
@@ -75,13 +75,34 @@ describe RelaxDB::BelongsToProxy do
75
75
  r.photo.rating.should == r
76
76
  end
77
77
 
78
- it "should function with the required validator" do
79
- c = Class.new(RelaxDB::Document) do
80
- belongs_to :foo, :validator => :required
78
+ describe "validator" do
79
+
80
+ it "should be passed the _id and object" do
81
+ a = Atom.new(:_id => "atom").save!
82
+ c = Class.new(RelaxDB::Document) do
83
+ belongs_to :foo, :validator => lambda { |foo_id, obj| foo_id.reverse == obj._id }
84
+ end
85
+ c.new(:_id => "mota", :foo => a).save!
86
+ end
87
+
88
+ it "may be used with a predefined validator" do
89
+ c = Class.new(RelaxDB::Document) do
90
+ belongs_to :foo, :validator => :required
91
+ end
92
+ c.new.save.should be_false
81
93
  end
82
- c.new.save.should be_false
83
- end
84
94
 
95
+ it "should be provided with a default error message when validation fails" do
96
+ c = Class.new(RelaxDB::Document) do
97
+ belongs_to :foo, :validator => :required
98
+ end
99
+ x = c.new
100
+ x.save
101
+ x.errors[:foo].should_not be_blank
102
+ end
103
+
104
+ end
105
+
85
106
  end
86
107
 
87
108
  end
@@ -0,0 +1,109 @@
1
+ require File.dirname(__FILE__) + '/spec_helper.rb'
2
+ require File.dirname(__FILE__) + '/spec_models.rb'
3
+
4
+ # These tests would ideally instrument server.rb, asserting that no
5
+ # HTTP requests are made when retrieving the derived values
6
+
7
+ class DpInvite < RelaxDB::Document
8
+ property :event_name, :derived => [:event, lambda { |en, i| i.event.name }]
9
+ belongs_to :event
10
+ end
11
+
12
+ class DpEvent < RelaxDB::Document
13
+ property :name
14
+ end
15
+
16
+ describe RelaxDB::Document, "derived properties" do
17
+
18
+ before(:all) do
19
+ RelaxDB.configure(:host => "localhost", :port => 5984)
20
+ end
21
+
22
+ before(:each) do
23
+ RelaxDB.delete_db "relaxdb_spec_db" rescue "ok"
24
+ RelaxDB.use_db "relaxdb_spec_db"
25
+ end
26
+
27
+ it "should have its value updated when the source is updated" do
28
+ e = DpEvent.new(:name => "shindig")
29
+ i = DpInvite.new(:event => e)
30
+ i.event_name.should == "shindig"
31
+ end
32
+
33
+ it "should have its value persisted" do
34
+ e = DpEvent.new(:name => "shindig").save!
35
+ i = DpInvite.new(:event => e).save!
36
+
37
+ RelaxDB.db.get_count = 0
38
+ i = RelaxDB.load i._id
39
+ i.event_name.should == "shindig"
40
+ RelaxDB.db.get_count.should == 1
41
+ end
42
+
43
+ it "should have its value updated when the source_id is updated for a saved event" do
44
+ e = DpEvent.new(:name => "shindig").save!
45
+ i = DpInvite.new(:event_id => e._id)
46
+ i.event_name.should == "shindig"
47
+ end
48
+
49
+ it "will fail when the source_id is updated for a unsaved event" do
50
+ # Almost certainly not desired - merely codifying current behaviour
51
+ e = DpEvent.new(:name => "shindig")
52
+ lambda { DpInvite.new(:event_id => e._id) }.should raise_error
53
+ end
54
+
55
+ it "should only be updated for registered properties" do
56
+ invite = Class.new(RelaxDB::Document) do
57
+ property :event_name, :derived => [:foo, lambda { |en, i| i.event.name }]
58
+ belongs_to :event
59
+ end
60
+
61
+ event = Class.new(RelaxDB::Document) do
62
+ property :name
63
+ end
64
+
65
+ e = event.new(:name => "shindig")
66
+ i = invite.new(:event => e)
67
+ i.event_name.should be_nil
68
+ end
69
+
70
+ it "should have the existing value passed to the first lambda param" do
71
+ invite = Class.new(RelaxDB::Document) do
72
+ property :event_name, :derived => [:event, lambda { |en, i| en.nil? ? i.event.name : "bar" }]
73
+ belongs_to :event
74
+ end
75
+
76
+ event = Class.new(RelaxDB::Document) do
77
+ property :name
78
+ end
79
+
80
+ e1 = event.new(:name => "shindig")
81
+ e2 = event.new(:name => "shindig2")
82
+ i = invite.new(:event => e1)
83
+ i.event = e2
84
+ i.event_name.should == "bar"
85
+ end
86
+
87
+ describe "multiple properties" do
88
+
89
+ it "should be derivable from the same source" do
90
+ invite = Class.new(RelaxDB::Document) do
91
+ property :name, :derived => [:event, lambda { |en, i| i.event.name }]
92
+ property :location, :derived => [:event, lambda { |en, i| i.event.location }]
93
+ belongs_to :event
94
+ end
95
+
96
+ event = Class.new(RelaxDB::Document) do
97
+ property :name
98
+ property :location
99
+ end
100
+
101
+ e = event.new(:name => "shindig", :location => "city17")
102
+ i = invite.new(:event => e)
103
+ i.name.should == "shindig"
104
+ i.location.should == "city17"
105
+ end
106
+
107
+ end
108
+
109
+ end
@@ -131,10 +131,10 @@ describe RelaxDB::Document do
131
131
 
132
132
  describe "user defined property writer" do
133
133
 
134
- it "should not be used" do
134
+ it "may be used with caution" do
135
135
  o = BespokeWriter.new(:val => 101).save
136
136
  o = RelaxDB.load o._id
137
- o.val.should == 81
137
+ o.val.should == 91
138
138
  end
139
139
 
140
140
  end
@@ -376,13 +376,12 @@ describe RelaxDB::Document do
376
376
  r.new(:thumbs_up => 3).save.should be_false
377
377
  end
378
378
 
379
- it "should add the validation error message if supplied, on failure" do
379
+ it "should pass the property value and object to the validator" do
380
380
  r = Class.new(RelaxDB::Document) do
381
- property :thumbs_up, :validator => lambda { false }, :validation_msg => "Too many thumbs"
381
+ property :thumbs_up, :validator => lambda { |tu, o| tu >=0 && o.thumbs_up < 3 }
382
382
  end
383
- x = r.new
384
- x.save
385
- x.errors[:thumbs_up].should == "Too many thumbs"
383
+ r.new(:thumbs_up => 2).save.should be
384
+ r.new(:thumbs_up => 3).save.should be_false
386
385
  end
387
386
 
388
387
  it "should perform all validations" do
@@ -405,6 +404,15 @@ describe RelaxDB::Document do
405
404
  x.save.should == false
406
405
  end
407
406
 
407
+ it "should add a default error message if none is specified" do
408
+ r = Class.new(RelaxDB::Document) do
409
+ property :foo, :validator => lambda { raise }
410
+ end
411
+ x = r.new
412
+ x.save
413
+ x.errors[:foo].should_not be_blank
414
+ end
415
+
408
416
  it "may be a proc" do
409
417
  r = Class.new(RelaxDB::Document) do
410
418
  property :thumbs_up, :validator => lambda { false }
@@ -422,6 +430,47 @@ describe RelaxDB::Document do
422
430
  r.new(:thumbs_up => 1).save.should be
423
431
  end
424
432
 
433
+ it "may be skipped by passing the property symbol to save" do
434
+ r = Class.new(RelaxDB::Document) do
435
+ property :thumbs_up, :validator => lambda { raise }
436
+ end
437
+ r.new.save!(:thumbs_up)
438
+ end
439
+
440
+ end
441
+
442
+ describe "validation message" do
443
+
444
+ it "should be set on failure" do
445
+ r = Class.new(RelaxDB::Document) do
446
+ property :thumbs_up, :validator => lambda { false }, :validation_msg => "Too many thumbs"
447
+ end
448
+ x = r.new
449
+ x.save
450
+ x.errors[:thumbs_up].should == "Too many thumbs"
451
+ end
452
+
453
+ it "may be a proc accepting the prop only" do
454
+ r = Class.new(RelaxDB::Document) do
455
+ property :thumbs_up, :validator => lambda { false },
456
+ :validation_msg => lambda { |tu| "#{tu}" }
457
+ end
458
+ x = r.new(:thumbs_up => 13)
459
+ x.save
460
+ x.errors[:thumbs_up].should == "13"
461
+ end
462
+
463
+
464
+ it "may be a proc accepting the prop and object" do
465
+ r = Class.new(RelaxDB::Document) do
466
+ property :thumbs_up, :validator => lambda { false },
467
+ :validation_msg => lambda { |tu, o| "#{tu} #{o.thumbs_up}" }
468
+ end
469
+ x = r.new(:thumbs_up => 13)
470
+ x.save
471
+ x.errors[:thumbs_up].should == "13 13"
472
+ end
473
+
425
474
  end
426
475
 
427
476
  describe "predefined validator" do
@@ -431,7 +480,7 @@ describe RelaxDB::Document do
431
480
  property :foo, :validator => :required
432
481
  def required; raise; end;
433
482
  end
434
- c.new(:foo => :bar).save.should be
483
+ c.new(:foo => :bar).save!.should be
435
484
  end
436
485
 
437
486
  it "should prevent an object from being saved if validation fails" do
data/spec/spec_models.rb CHANGED
@@ -4,7 +4,7 @@ end
4
4
  class Initiative < RelaxDB::Document
5
5
  property :x
6
6
  attr_reader :foo
7
- def initialize(hash={})
7
+ def initialize(svw=true, hash={})
8
8
  super
9
9
  @foo = :bar
10
10
  end
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.2.4
4
+ version: 0.2.5
5
5
  platform: ruby
6
6
  authors:
7
7
  - Paul Carey
@@ -9,7 +9,7 @@ autorequire: relaxdb
9
9
  bindir: bin
10
10
  cert_chain: []
11
11
 
12
- date: 2008-12-26 00:00:00 -08:00
12
+ date: 2009-01-07 00:00:00 -08:00
13
13
  default_executable:
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
@@ -77,8 +77,8 @@ files:
77
77
  - lib/relaxdb.rb
78
78
  - spec/belongs_to_spec.rb
79
79
  - spec/callbacks_spec.rb
80
- - spec/denormalisation_spec.rb
81
80
  - spec/design_doc_spec.rb
81
+ - spec/derived_properties_spec.rb
82
82
  - spec/document_spec.rb
83
83
  - spec/has_many_spec.rb
84
84
  - spec/has_one_spec.rb
@@ -1,49 +0,0 @@
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