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 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
@@ -4,7 +4,7 @@ require 'spec/rake/spectask'
4
4
 
5
5
  PLUGIN = "relaxdb"
6
6
  NAME = "relaxdb"
7
- GEM_VERSION = "0.1.2"
7
+ GEM_VERSION = "0.1.3"
8
8
  AUTHOR = "Paul Carey"
9
9
  EMAIL = "paul.p.carey@gmail.com"
10
10
  HOMEPAGE = "http://github.com/paulcarey/relaxdb/"
@@ -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::retrieve(view_path, @klass, "all", map_function)
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::retrieve(q.view_path, @klass, v.view_name, v.map_function)
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
@@ -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
- set_created_at_if_new
140
+ return false unless before_save
141
+ return false unless validates?
142
+
143
+ set_created_at if unsaved?
126
144
 
127
- if validates?
128
- resp = RelaxDB.db.put("#{_id}", to_json)
129
- self._rev = JSON.parse(resp.body)["rev"]
130
- self
131
- else
132
- false
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
- instance_variable_get(:@_rev).nil?
171
+ @_rev.nil?
154
172
  end
173
+ alias_method :new_record?, :unsaved?
155
174
 
156
- def set_created_at_if_new
157
- if unsaved? and methods.include? "created_at"
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
- unless instance_variable_get(:@created_at)
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 << relationship
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
 
@@ -0,0 +1,3 @@
1
+ class Errors < Hash
2
+ alias_method :on, :[]
3
+ end
@@ -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.downcase).to_s
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.capitalize
9
- @relationship_as_viewed_by_target = client.class.to_s.downcase
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
@@ -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
- def view(design_doc, view_name, default_ret_val=[])
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
- data = JSON.parse(resp.body)
66
-
67
- # presence of total_rows tells us a map function was invoked
68
- # if it's absent a map reduce invocation occured
69
- if data["total_rows"]
70
- create_from_hash(data)
71
- else
72
- obj = data["rows"][0] && data["rows"][0]["value"]
73
- obj ? ViewObject.create(obj) : default_ret_val
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
- @objects = []
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)
@@ -59,15 +59,12 @@ module RelaxDB
59
59
  end
60
60
 
61
61
  def use_db(name)
62
- begin
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
@@ -148,7 +148,7 @@ describe RelaxDB::Document do
148
148
 
149
149
  end
150
150
 
151
- describe "#all" do
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 "#all.sorted_by" do
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
@@ -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
- it "should offer an example where behaviour is different with caching enabled and caching disabled" do
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.2
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