paulcarey-relaxdb 0.2.1 → 0.2.2

Sign up to get free protection for your applications and to get access to all the features.
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