paulcarey-relaxdb 0.2.1 → 0.2.2

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 CHANGED
@@ -2,6 +2,8 @@ h3. What's New?
2
2
 
3
3
  * Pagination! CouchDB offers great support for retrieving a subset of data, but the housekeeping is tricky. RelaxDB takes care of it.
4
4
  ** 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.
5
+ * Support for multi key post
6
+ ** For example, @ Numbers.all.sorted_by(:val) { |q| q.keys([1,2,3,5]) } @
5
7
  * 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.
6
8
 
7
9
  *Note*: 0.2.1 requires CouchDB 0.9 trunk. 0.2.0 works with CouchDB 0.8 onwards.
@@ -78,10 +80,10 @@ h3. Paginating models
78
80
  <code>
79
81
  # Controller (merb-action-args used for extracting view_params)
80
82
 
81
- def action(view_params={})
83
+ def action(page_params={})
82
84
  u_id = @user._id
83
85
 
84
- @posts = Post.paginate_by(view_params, :writer_id, :created_at) do |p|
86
+ @posts = Post.paginate_by(page_params, :writer_id, :created_at) do |p|
85
87
  p.startkey([u_id, {}]).endkey([u_id]).descending(true).count(5)
86
88
  end
87
89
  render
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.1"
7
+ GEM_VERSION = "0.2.2"
8
8
  AUTHOR = "Paul Carey"
9
9
  EMAIL = "paul.p.carey@gmail.com"
10
10
  HOMEPAGE = "http://github.com/paulcarey/relaxdb/"
@@ -136,9 +136,11 @@ module RelaxDB
136
136
  end
137
137
  end
138
138
 
139
+ # Order changed as of 30/10/2008 to be consistent with ActiveRecord
140
+ # Not yet sure of final implemention for hooks - may lean more towards DM than AR
139
141
  def save
140
- return false unless before_save
141
142
  return false unless validates?
143
+ return false unless before_save
142
144
 
143
145
  set_created_at if unsaved?
144
146
 
@@ -151,7 +153,7 @@ module RelaxDB
151
153
  end
152
154
 
153
155
  def validates?
154
- success = true
156
+ total_success = true
155
157
  properties.each do |prop|
156
158
  if methods.include? "validate_#{prop}"
157
159
  prop_val = instance_variable_get("@#{prop}")
@@ -161,9 +163,15 @@ module RelaxDB
161
163
  @errors["#{prop}".to_sym] = send("#{prop}_validation_msg")
162
164
  end
163
165
  end
166
+ total_success &= success
164
167
  end
165
168
  end
166
- success
169
+ total_success &= validate
170
+ total_success
171
+ end
172
+
173
+ def validate
174
+ true
167
175
  end
168
176
 
169
177
  # Hmm. Rename... never_saved? newnew?
@@ -171,6 +179,7 @@ module RelaxDB
171
179
  @_rev.nil?
172
180
  end
173
181
  alias_method :new_record?, :unsaved?
182
+ alias_method :new_document?, :unsaved?
174
183
 
175
184
  def to_param
176
185
  self._id
@@ -352,16 +361,6 @@ module RelaxDB
352
361
  end
353
362
  end
354
363
 
355
- #
356
- # Document views are generated on demand if they don't exist when queried.
357
- # The paginator is used with both document generated views, and user defined views.
358
- # Auto generating document views on demand in the paginator makes it significantly
359
- # more complex and less amendable to use by user defined views. For these reasons,
360
- # the first call to paginate_by for a particular list of attributes for a particular
361
- # Document class will update the corresponding design doc, even if it already contains
362
- # the required view. This should not incur a penalty with CouchDB as it uses the
363
- # same index for byte identical views.
364
- #
365
364
  def self.paginate_by(page_params, *view_keys)
366
365
  paginate_params = PaginateParams.new
367
366
  yield paginate_params
@@ -37,7 +37,7 @@ module RelaxDB
37
37
  view_name = @relationship
38
38
  view_path = "_view/#{design_doc}/#{view_name}?key=\"#{@client._id}\""
39
39
  map_function = ViewCreator.has_n(@target_class, @relationship_as_viewed_by_target)
40
- RelaxDB.retrieve(view_path, design_doc, view_name, map_function)[0]
40
+ RelaxDB.retrieve(view_path, design_doc, view_name, map_function).first
41
41
  end
42
42
 
43
43
  end
@@ -2,7 +2,7 @@ module RelaxDB
2
2
 
3
3
  class PaginateParams
4
4
 
5
- @@params = %w(key startkey startkey_docid endkey endkey_docid count update descending group reduce)
5
+ @@params = %w(key startkey startkey_docid endkey endkey_docid count update descending group reduce include_docs)
6
6
 
7
7
  @@params.each do |param|
8
8
  define_method(param.to_sym) do |*val|
@@ -36,7 +36,7 @@ module RelaxDB
36
36
  (offset - orig_offset == 0 ? false : true)
37
37
  prev_params = create_prev(docs.first, view_keys) if prev_exists
38
38
  else
39
- next_exists, prev_exists = false
39
+ next_exists = prev_exists = false
40
40
  end
41
41
 
42
42
  docs.meta_class.instance_eval do
data/lib/relaxdb/query.rb CHANGED
@@ -16,7 +16,8 @@ module RelaxDB
16
16
  #
17
17
  class Query
18
18
 
19
- @@params = %w(key startkey startkey_docid endkey endkey_docid count update descending skip group group_level reduce)
19
+ # keys is not included in the standard param as it is significantly different from the others
20
+ @@params = %w(key startkey startkey_docid endkey endkey_docid count update descending skip group group_level reduce include_docs)
20
21
 
21
22
  @@params.each do |param|
22
23
  define_method(param.to_sym) do |*val|
@@ -36,6 +37,14 @@ module RelaxDB
36
37
  @view_name = view_name
37
38
  end
38
39
 
40
+ def keys(keys=nil)
41
+ if keys.nil?
42
+ @keys
43
+ else
44
+ @keys = { :keys => keys }.to_json
45
+ end
46
+ end
47
+
39
48
  def view_path
40
49
  uri = "_view/#{@design_doc}/#{@view_name}"
41
50
 
@@ -47,10 +47,16 @@ module RelaxDB
47
47
  data["ok"]
48
48
  end
49
49
 
50
- def load(id)
51
- resp = db.get("#{id}")
52
- data = JSON.parse(resp.body)
53
- create_object(data)
50
+ def load(*ids)
51
+ if ids.size == 1
52
+ resp = db.get(ids[0])
53
+ data = JSON.parse(resp.body)
54
+ create_object(data)
55
+ else
56
+ resp = db.post("_all_docs?include_docs=true", {:keys => ids}.to_json)
57
+ data = JSON.parse(resp.body)
58
+ data["rows"].map { |row| create_object(row["doc"]) }
59
+ end
54
60
  end
55
61
 
56
62
  # Used internally by RelaxDB
@@ -72,7 +78,7 @@ module RelaxDB
72
78
  q = Query.new(design_doc, view_name)
73
79
  yield q if block_given?
74
80
 
75
- resp = db.get(q.view_path)
81
+ resp = q.keys ? db.post(q.view_path, q.keys) : db.get(q.view_path)
76
82
  JSON.parse(resp.body)
77
83
  end
78
84
 
@@ -136,10 +142,19 @@ module RelaxDB
136
142
 
137
143
  # Convenience methods - should be in a diffent module?
138
144
 
145
+ def get(uri=nil)
146
+ JSON.parse(db.get(uri).body)
147
+ end
148
+
139
149
  def pp_get(uri=nil)
140
150
  resp = db.get(uri)
141
151
  pp(JSON.parse(resp.body))
142
152
  end
153
+
154
+ def pp_post(uri=nil, json=nil)
155
+ resp = db.post(uri, json)
156
+ pp(JSON.parse(resp.body))
157
+ end
143
158
 
144
159
  end
145
160
 
@@ -84,7 +84,8 @@ module RelaxDB
84
84
  @server.delete("/#{@db}/#{path}")
85
85
  end
86
86
 
87
- def get(path=nil)
87
+ # *ignored allows methods to invoke get or post indifferently
88
+ def get(path=nil, *ignored)
88
89
  @logger.info("GET /#{@db}/#{unesc(path)}")
89
90
  @server.get("/#{@db}/#{path}")
90
91
  end
@@ -31,25 +31,26 @@ module RelaxDB
31
31
  end
32
32
 
33
33
  def view_name
34
- s = @atts.inject("all_sorted_by") do |s, att|
35
- s << "_#{att}_and"
36
- end
37
- s[0, s.size-4]
34
+ "all_sorted_by_" << @atts.join("_and_")
38
35
  end
39
36
 
40
37
  def query(query)
41
38
  # If a view contains both a map and reduce function, CouchDB will by default return
42
39
  # the result of the reduce function when queried.
43
40
  # This class automatically creates both map and reduce functions so it can be used by the paginator.
44
- # In normal usage, this class will be used with map functions, hence reduce is explicitly set to false.
41
+ # In normal usage, this class will be used with map functions, hence reduce is explicitly set
42
+ # to false if it hasn't already been set.
45
43
  query.reduce(false) if query.reduce.nil?
46
44
 
45
+ # I hope the query.group(true) should be temporary only (given that reduce has been set to false)
46
+ method = query.keys ? (query.group(true) && :post) : :get
47
+
47
48
  begin
48
- resp = RelaxDB.db.get(query.view_path)
49
+ resp = RelaxDB.db.send(method, query.view_path, query.keys)
49
50
  rescue => e
50
51
  design_doc = DesignDocument.get(@class_name)
51
52
  design_doc.add_map_view(view_name, map_function).add_reduce_view(view_name, reduce_function).save
52
- resp = RelaxDB.db.get(query.view_path)
53
+ resp = RelaxDB.db.send(method, query.view_path, query.keys)
53
54
  end
54
55
 
55
56
  data = JSON.parse(resp.body)
@@ -0,0 +1,18 @@
1
+ module RelaxDB
2
+
3
+ class ViewResult < DelegateClass(Array)
4
+
5
+ attr_reader :offset, :total_rows
6
+
7
+ def initialize(result_hash)
8
+ objs = RelaxDB.create_from_hash(result_hash)
9
+
10
+ @offset = result_hash["offset"]
11
+ @total_rows = result_hash["total_rows"]
12
+
13
+ super(objs)
14
+ end
15
+
16
+ end
17
+
18
+ end
@@ -15,8 +15,7 @@ describe RelaxDB::Document do
15
15
  describe ".new" do
16
16
 
17
17
  it "should create an object with an id" do
18
- p = Atom.new
19
- p._id.should_not be_nil
18
+ Atom.new._id.should_not be_nil
20
19
  end
21
20
 
22
21
  it "should create an object with a nil revision" do
@@ -244,6 +243,12 @@ describe RelaxDB::Document do
244
243
  User.new(:name => "atlas").save
245
244
  User.all.sorted_by(:name, :age) { |q| q.startkey(["paul",0 ]).endkey(["paul", 50]) }.size.should == 1
246
245
  end
246
+
247
+ it "should be retrievable by a multi key post" do
248
+ 5.times { |i| Primitives.new(:num => i).save }
249
+ ps = Primitives.all.sorted_by(:num) { |q| q.keys([0, 4]) }
250
+ ps.map { |p| p.num }.should == [0, 4]
251
+ end
247
252
 
248
253
  end
249
254
 
@@ -304,7 +309,7 @@ describe RelaxDB::Document do
304
309
  r = Class.new(RelaxDB::Document) do
305
310
  property :thumbs_up, :validator => lambda { |tu| tu >=0 && tu < 3 }
306
311
  end
307
- r.new(:thumbs_up => 2).save.should_not be_false
312
+ r.new(:thumbs_up => 2).save.should be
308
313
  r.new(:thumbs_up => 3).save.should be_false
309
314
  end
310
315
 
@@ -328,6 +333,15 @@ describe RelaxDB::Document do
328
333
  x.errors[:bar].should == "rab"
329
334
  end
330
335
 
336
+ it "should prevent saving unless all validations pass" do
337
+ r = Class.new(RelaxDB::Document) do
338
+ property :foo, :validator => lambda { false }
339
+ property :bar, :validator => lambda { true }
340
+ end
341
+ x = r.new
342
+ x.save.should == false
343
+ end
344
+
331
345
  it "may be a proc" do
332
346
  r = Class.new(RelaxDB::Document) do
333
347
  property :thumbs_up, :validator => lambda { false }
data/spec/query_spec.rb CHANGED
@@ -66,5 +66,15 @@ describe RelaxDB::Query do
66
66
  end
67
67
 
68
68
  end
69
+
70
+ describe "#keys" do
71
+
72
+ it "should return a JSON encoded hash" do
73
+ q = RelaxDB::Query.new("", "")
74
+ q.keys(["a", "b"])
75
+ q.keys.should == '{"keys":["a","b"]}'
76
+ end
77
+
78
+ end
69
79
 
70
80
  end
data/spec/relaxdb_spec.rb CHANGED
@@ -58,6 +58,23 @@ describe RelaxDB do
58
58
 
59
59
  end
60
60
 
61
+ describe ".load" do
62
+
63
+ it "should load a single document" do
64
+ a = Atom.new.save
65
+ ar = RelaxDB.load a._id
66
+ ar.should == a
67
+ end
68
+
69
+ it "should load an arbitrary number of documents" do
70
+ a1, a2 = Atom.new.save, Atom.new.save
71
+ ar1, ar2 = RelaxDB.load a1._id, a2._id
72
+ ar1.should == a1
73
+ ar2.should == a2
74
+ end
75
+
76
+ end
77
+
61
78
  describe ".view" do
62
79
 
63
80
  map_func = %Q<
@@ -80,6 +97,17 @@ describe RelaxDB do
80
97
  data["rows"].size.should == 1
81
98
  end
82
99
 
100
+ it "should be queryable with a multi key post" do
101
+ 5.times { |i| Primitives.new(:num => i).save }
102
+ # Create the view
103
+ Primitives.all.sorted_by(:num)
104
+ resp = RelaxDB.view("Primitives", "all_sorted_by_num") do |q|
105
+ q.keys([0, 4])
106
+ q.reduce(false).group(true) # group invocation should hopefully be temporary
107
+ end
108
+ RelaxDB.instantiate(resp).map{ |p| p.num }.should == [0, 4]
109
+ end
110
+
83
111
  end
84
112
 
85
113
  describe ".merge" do
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.1
4
+ version: 0.2.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Paul Carey
@@ -69,6 +69,7 @@ files:
69
69
  - lib/relaxdb/sorted_by_view.rb
70
70
  - lib/relaxdb/uuid_generator.rb
71
71
  - lib/relaxdb/view_object.rb
72
+ - lib/relaxdb/view_result.rb
72
73
  - lib/relaxdb/view_uploader.rb
73
74
  - lib/relaxdb/views.rb
74
75
  - lib/more/grapher.rb