couch_potato 1.2.0 → 1.3.0

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 2771fb2d4e5b08e8ce5a50afa1d6747a54682807
4
- data.tar.gz: 1009498ac99d3d0b5e2bba6a7a5a4624ff5759be
3
+ metadata.gz: 6f37912f0e7ed48878e92e5ad8ef34d106359f15
4
+ data.tar.gz: 020faa774590847e21f942504cf9fb02df3683e7
5
5
  SHA512:
6
- metadata.gz: 665f44a87cc9d5f96b1c0ba3dc416849e6fc1b02888a96658e94e7a295601aa4e81d81423df6a8362e87c4feb41f1a738fa1bd08d18fe716a7d655b03714eff7
7
- data.tar.gz: c53f3dba7f3fd8208182ecfc54a8e6d747af55d316ea2097df26762ab70e3544676575e36594922caf1b85033c9b6631e79d2876105b37c901a100f3ce6608f2
6
+ metadata.gz: 6a6fce1ef1c76b01c073ac3706d894d5c567d05afe20ef4177672950f96db1011c9f6aee607852a7d0c605e303250ecec4ad0acbbc9494b30b603a25690924cd
7
+ data.tar.gz: 240d66a1fcb88a3214c946fd51e924abf314410c8b14a203423626d6d777633ee0764ebffbfb2e62b707539c218da0b01da29068f717fe2057b69f1f6d9a2921
data/.travis.yml CHANGED
@@ -3,7 +3,7 @@ rvm:
3
3
  - 2.0.0
4
4
  - 2.1.2
5
5
  - jruby-19mode
6
- - rbx-2.1.1
6
+ - rbx-2.2.10
7
7
  gemfile:
8
8
  - active_support_3_2
9
9
  - active_support_4_0
data/CHANGES.md CHANGED
@@ -1,5 +1,11 @@
1
1
  ## Changes
2
2
 
3
+ ### 1.3.0
4
+
5
+ * Add support for built-in couchdb reduce functions in map reduce specs (Andy Morris)
6
+ * Make specs handle CommonJS modules via module.exports as well as exports (Andy Morris)
7
+ * Changed #attributes to return a HashWithIndifferentAccess (Owen Davies)
8
+
3
9
  ### 1.2.0
4
10
 
5
11
  * adds optional deep dirty tracking (andymorris)
data/README.md CHANGED
@@ -360,7 +360,7 @@ You can also pass in custom map/reduce functions with the custom view spec:
360
360
  commonJS modules can also be used in custom views:
361
361
 
362
362
  class User
363
- view :all, :map => "function(doc) { emit(null, require("lib/test").test)}", :lib => {:test => "exports.test = 'test'"}, :include_docs => true, :type => :custom
363
+ view :all, :map => "function(doc) { emit(null, require("views/lib/test").test)}", :lib => {:test => "exports.test = 'test'"}, :include_docs => true, :type => :custom
364
364
  end
365
365
 
366
366
  If you don't want the results to be converted into models the raw view is your friend:
@@ -75,7 +75,7 @@ module CouchPotato
75
75
  # property :year
76
76
  # end
77
77
  # book = Book.new
78
- # book.attributes = {:title => 'Time to Relax', :year => 2009}
78
+ # book.attributes = {'title' => 'Time to Relax', 'year' => 2009}
79
79
  # book.title # => 'Time to Relax'
80
80
  # book.year # => 2009
81
81
  def attributes=(hash)
@@ -93,9 +93,9 @@ module CouchPotato
93
93
  # property :year
94
94
  # end
95
95
  # book = Book.new :year => 2009
96
- # book.attributes # => {:title => nil, :year => 2009}
96
+ # book.attributes # => {'title' => nil, 'year' => 2009}
97
97
  def attributes
98
- self.class.properties.inject({}) do |res, property|
98
+ self.class.properties.inject(ActiveSupport::HashWithIndifferentAccess.new) do |res, property|
99
99
  property.value(res, self)
100
100
  res
101
101
  end
@@ -28,6 +28,45 @@ module CouchPotato
28
28
 
29
29
  def matches?(view_spec)
30
30
  js = <<-JS
31
+ var sum = function(values) {
32
+ return values.reduce(function(memo, value) { return memo + value; });
33
+ };
34
+ // Equivalents of couchdb built-in reduce functions whose names can be
35
+ // given as the reduce function in the view_spec:
36
+ var _sum = function(keys, values, rereduce) {
37
+ return sum(values);
38
+ }
39
+ var _count = function(keys, values, rereduce) {
40
+ if (rereduce) {
41
+ return sum(values);
42
+ } else {
43
+ return values.length;
44
+ }
45
+ }
46
+ var _stats = function(keys, values, rereduce) {
47
+ var result = {sum: 0, count: 0, min: Number.MAX_VALUE, max: Number.MIN_VALUE, sumsqr: 0};
48
+ if (rereduce) {
49
+ for (var i in values) {
50
+ var value = values[i];
51
+ result.sum += value.sum;
52
+ result.count += value.count;
53
+ result.min = Math.min(result.min, value.min);
54
+ result.max = Math.max(result.max, value.max);
55
+ result.sumsqr += value.sumsqr;
56
+ }
57
+ } else {
58
+ for (var i in values) {
59
+ var value = values[i];
60
+ result.sum += value;
61
+ result.count += 1;
62
+ result.min = Math.min(result.min, value);
63
+ result.max = Math.max(result.max, value);
64
+ result.sumsqr += Math.pow(value, 2);
65
+ }
66
+ }
67
+ return result;
68
+ }
69
+
31
70
  var docs = #{@input_ruby.to_json};
32
71
  var options = #{@options.to_json};
33
72
  var map = #{view_spec.map_function};
@@ -36,23 +75,21 @@ module CouchPotato
36
75
 
37
76
  // Map the input docs
38
77
  var require = function(modulePath) {
39
- var exports = {};
78
+ var module = {exports: {}};
79
+ var exports = module.exports;
40
80
  var pathArray = modulePath.split("/").slice(2);
41
81
  var result = lib;
42
82
  for (var i in pathArray) {
43
- result = result[pathArray[i]]
83
+ result = result[pathArray[i]];
44
84
  }
45
85
  eval(result);
46
- return exports;
86
+ return module.exports;
47
87
  }
48
88
 
49
89
  var mapResults = [];
50
90
  var emit = function(key, value) {
51
91
  mapResults.push({key: key, value: value});
52
92
  };
53
- var sum = function(values) {
54
- return values.reduce(function(memo, value) { return memo + value; });
55
- };
56
93
  for (var i in docs) {
57
94
  map(docs[i]);
58
95
  }
@@ -28,14 +28,15 @@ module CouchPotato
28
28
  var lib = #{view_spec.respond_to?(:lib) && view_spec.lib.to_json};
29
29
  var result = [];
30
30
  var require = function(modulePath) {
31
- var exports = {};
31
+ var module = {exports: {}};
32
+ var exports = module.exports;
32
33
  var pathArray = modulePath.split("/").slice(2);
33
34
  var result = lib;
34
35
  for (var i in pathArray) {
35
- result = result[pathArray[i]]
36
+ result = result[pathArray[i]];
36
37
  }
37
38
  eval(result);
38
- return exports;
39
+ return module.exports;
39
40
  }
40
41
 
41
42
  var emit = function(key, value) {
@@ -1,3 +1,3 @@
1
1
  module CouchPotato
2
- VERSION = "1.2.0"
2
+ VERSION = "1.3.0"
3
3
  end
@@ -26,6 +26,17 @@ module CouchPotato
26
26
  end
27
27
  end
28
28
 
29
+ # mainly useful for testing where you drop the database between tests.
30
+ # only after clearing the cache design docs will be updated/re-created.
31
+ def self.clear_cache
32
+ __updated_views.clear
33
+ end
34
+
35
+ def self.__updated_views
36
+ @updated_views ||= {}
37
+ @updated_views
38
+ end
39
+
29
40
  private
30
41
 
31
42
  def update_view
@@ -66,8 +77,7 @@ module CouchPotato
66
77
  end
67
78
 
68
79
  def updated_views
69
- @@updated_views ||= {}
70
- @@updated_views
80
+ self.class.__updated_views
71
81
  end
72
82
 
73
83
  def query_view(parameters)
@@ -34,15 +34,21 @@ describe "attributes" do
34
34
  context "attributes" do
35
35
  it "should return the attributes" do
36
36
  plant = Plant.new(:leaf_count => 1)
37
- plant.attributes.should == {:leaf_count => 1, :created_at => nil, :updated_at => nil,
38
- :typed_leaf_count => nil, :typed_leaf_size => nil, :branch => nil}
37
+ plant.attributes.should == {'leaf_count' => 1, 'created_at' => nil, 'updated_at' => nil,
38
+ 'typed_leaf_count' => nil, 'typed_leaf_size' => nil, 'branch' => nil}
39
39
  end
40
40
 
41
- it "should return the attributes via []" do
41
+ it "should return the attributes via [symbol]" do
42
42
  plant = Plant.new(:leaf_count => 1)
43
43
  plant.attributes[:leaf_count].should eql(plant[:leaf_count])
44
44
  plant.attributes[:leaf_count].should eql(1)
45
45
  end
46
+
47
+ it "should return the attributes via [string]" do
48
+ plant = Plant.new(:leaf_count => 1)
49
+ plant.attributes["leaf_count"].should eql(plant[:leaf_count])
50
+ plant.attributes["leaf_count"].should eql(1)
51
+ end
46
52
  end
47
53
 
48
54
  context "has_key?" do
@@ -36,7 +36,7 @@ describe CouchPotato::RSpec::MapToMatcher do
36
36
  spec.should map({}).to([nil, "2013-05-17T15:00:00.000Z"])
37
37
  end
38
38
 
39
- it "should work with commonJS modules" do
39
+ it "should work with commonJS modules that use 'exports'" do
40
40
  spec = stub(
41
41
  :map_function => "function(doc) { var test = require('views/lib/test'); emit(null, test.test); }",
42
42
  :lib => {:test => "exports.test = 'test';"}
@@ -44,6 +44,14 @@ describe CouchPotato::RSpec::MapToMatcher do
44
44
  spec.should map({}).to([nil, "test"])
45
45
  end
46
46
 
47
+ it "should work with commonJS modules that use 'module.exports'" do
48
+ spec = stub(
49
+ :map_function => "function(doc) { var test = require('views/lib/test'); emit(null, test.test); }",
50
+ :lib => {:test => "module.exports.test = 'test';"}
51
+ )
52
+ spec.should map({}).to([nil, "test"])
53
+ end
54
+
47
55
  describe "failing specs" do
48
56
  before(:each) do
49
57
  @view_spec = stub(:map_function => "function(doc) {emit(doc.name, null)}")
@@ -137,7 +145,7 @@ describe CouchPotato::RSpec::MapReduceToMatcher do
137
145
  spec.should map_reduce({}).to({"key" => nil, "value" => "2013-05-17T15:00:00.000Z"})
138
146
  end
139
147
 
140
- it "should handle date return values" do
148
+ it "should handle CommonJS requires for modules that use 'exports'" do
141
149
  spec = stub(
142
150
  :map_function => "function() { var test = require('views/lib/test'); emit(null, test.test); }",
143
151
  :reduce_function => "function(keys, values) { return 'test' }",
@@ -145,6 +153,14 @@ describe CouchPotato::RSpec::MapReduceToMatcher do
145
153
  spec.should map_reduce({}).to({"key" => nil, "value" => "test"})
146
154
  end
147
155
 
156
+ it "should handle CommonJS requires for modules that use 'module.exports'" do
157
+ spec = stub(
158
+ :map_function => "function() { var test = require('views/lib/test'); emit(null, test.test); }",
159
+ :reduce_function => "function(keys, values) { return 'test' }",
160
+ :lib => {:test => "module.exports.test = 'test'"})
161
+ spec.should map_reduce({}).to({"key" => nil, "value" => "test"})
162
+ end
163
+
148
164
  it "should handle sum function" do
149
165
  spec = stub(
150
166
  :map_function => "function(doc) { emit(null, doc.age); }",
@@ -233,6 +249,38 @@ describe CouchPotato::RSpec::MapReduceToMatcher do
233
249
  }.should raise_error('Expected not to map/reduce to [{"key"=>nil, "value"=>8}] but did.')
234
250
  end
235
251
  end
252
+
253
+ describe "couchdb built-in reduce functions" do
254
+ describe "_sum" do
255
+ it "should return the sum of emitted values" do
256
+ spec = stub(:map_function => @view_spec.map_function, :reduce_function => "_sum")
257
+ spec.should map_reduce(@docs).to({"key" => nil, "value" => 36})
258
+ end
259
+ end
260
+
261
+ describe "_count" do
262
+ it "should return the count of emitted values" do
263
+ spec = stub(:map_function => @view_spec.map_function, :reduce_function => "_count")
264
+ spec.should map_reduce(@docs).to({"key" => nil, "value" => 8})
265
+ end
266
+ end
267
+
268
+ describe "_stats" do
269
+ it "should return the numerical statistics of emitted values" do
270
+ spec = stub(:map_function => @view_spec.map_function, :reduce_function => "_stats")
271
+ spec.should map_reduce(@docs).to({
272
+ "key" => nil,
273
+ "value" => {
274
+ "sum" => 36,
275
+ "count" => 8,
276
+ "min" => 1,
277
+ "max" => 8,
278
+ "sumsqr" => 204
279
+ }
280
+ })
281
+ end
282
+ end
283
+ end
236
284
  end
237
285
 
238
286
  describe CouchPotato::RSpec::ListAsMatcher do
@@ -3,6 +3,7 @@ require 'spec_helper'
3
3
  describe CouchPotato::View::ViewQuery, 'query_view!' do
4
4
  before(:each) do
5
5
  CouchRest.stub(:get => nil)
6
+ CouchPotato::View::ViewQuery.clear_cache
6
7
  end
7
8
 
8
9
  it "does not pass a key if conditions are empty" do
@@ -24,6 +25,28 @@ describe CouchPotato::View::ViewQuery, 'query_view!' do
24
25
  CouchPotato::View::ViewQuery.new(db, 'design', {:view => {:map => '<map_code>', :reduce => '<reduce_code>'}}, nil, {'test' => "<lib_code>"}).query_view!
25
26
  end
26
27
 
28
+ it 'only updates a view once' do
29
+ db = double :db, view: nil
30
+ db.stub(:get).and_return({'views' => {}}, {'views' => {}, x: 1}) # return something different on the second call otherwise it would never try to update the views twice
31
+ query = CouchPotato::View::ViewQuery.new(db, 'design', {:view => {:map => '<map_code>', :reduce => '<reduce_code>'}})
32
+
33
+ db.should_receive(:save_doc).once
34
+
35
+ 2.times { query.query_view! }
36
+ end
37
+
38
+ it 'updates a view again after clearing the view cache' do
39
+ db = double :db, view: nil
40
+ db.stub(:get).and_return({'views' => {}}, {'views' => {}, x: 1}) # return something different on the second call otherwise it would never try to update the views twice
41
+ query = CouchPotato::View::ViewQuery.new(db, 'design', {:view => {:map => '<map_code>', :reduce => '<reduce_code>'}})
42
+
43
+ db.should_receive(:save_doc).twice
44
+
45
+ query.query_view!
46
+ CouchPotato::View::ViewQuery.clear_cache
47
+ query.query_view!
48
+ end
49
+
27
50
  it 'updates a view in erlang if it does not exist' do
28
51
  db = mock 'db', :get => nil, :view => nil
29
52
 
@@ -49,8 +72,10 @@ describe CouchPotato::View::ViewQuery, 'query_view!' do
49
72
  end
50
73
 
51
74
  it "does not update a view when the lib function hasn't changed" do
52
- db = mock 'db', :get => {'views' => {'view' => {'map' => '<map_code>', 'reduce' => '<reduce_code>'}}, 'lib' => {'test' => '<lib_code>'}}, :view => nil
75
+ db = mock 'db', :get => {'views' => {'view' => {'map' => '<map_code>', 'reduce' => '<reduce_code>'}, 'lib' => {'test' => '<lib_code>'}}}, :view => nil
76
+
53
77
  db.should_not_receive(:save_doc)
78
+
54
79
  CouchPotato::View::ViewQuery.new(db, 'design', {:view => {:map => '<map_code>', :reduce => '<reduce_code>'}}, nil, {'test' => "<lib_code>"}).query_view!
55
80
  end
56
81
 
@@ -67,8 +92,10 @@ describe CouchPotato::View::ViewQuery, 'query_view!' do
67
92
  end
68
93
 
69
94
  it "updates a view when the lib hash has changed" do
70
- db = mock 'db', :get => {'views' => {'view4' => {'map' => '<map_code>'}}, 'lib' => {'test' => "<test_lib>"}}, :view => nil
95
+ db = mock 'db', :get => {'views' => {'view4' => {'map' => '<map_code>'}}}, 'lib' => {'test' => "<test_lib>"}, :view => nil
96
+
71
97
  db.should_receive(:save_doc)
98
+
72
99
  CouchPotato::View::ViewQuery.new(db, 'design', {:view4 => {:map => '<map_code>'}}, nil, {:test => "<test_lib>"}).query_view!
73
100
  end
74
101
 
@@ -76,8 +103,8 @@ describe CouchPotato::View::ViewQuery, 'query_view!' do
76
103
  db = mock 'db', :get => {'views' => {'view5' => {'map' => '<map_code>'}, 'lib' => {'test' => "<test_lib>"}}}, :view => nil
77
104
  db.should_receive(:save_doc).with({
78
105
  'views' => {
79
- 'view5' => {'map' => '<map_code>'},
80
- 'lib' => {'test' => '<test_lib>', 'test1' => '<test1_lib>'}
106
+ 'view5' => {'map' => '<map_code>'},
107
+ 'lib' => {'test' => '<test_lib>', 'test1' => '<test1_lib>'}
81
108
  }
82
109
  })
83
110
  CouchPotato::View::ViewQuery.new(db, 'design', {:view5 => {:map => '<map_code>'}}, nil, {'test1' => '<test1_lib>'}).query_view!
@@ -85,12 +112,14 @@ describe CouchPotato::View::ViewQuery, 'query_view!' do
85
112
 
86
113
  it "overrides libs with the same name" do
87
114
  db = mock 'db', :get => {'views' => {'view6' => {'map' => '<map_code>'}, 'lib' => {'test' => "<test_lib>"}}}, :view => nil
115
+
88
116
  db.should_receive(:save_doc).with({
89
117
  'views' => {
90
- 'view6' => {'map' => '<map_code>'},
91
- 'lib' => {'test' => '<test1_lib>'}
92
- }
118
+ 'view6' => {'map' => '<map_code>'},
119
+ 'lib' => {'test' => '<test1_lib>'}
120
+ },
93
121
  })
122
+
94
123
  CouchPotato::View::ViewQuery.new(db, 'design', {:view6 => {:map => '<map_code>'}}, nil, {'test' => '<test1_lib>'}).query_view!
95
124
  end
96
125
 
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: couch_potato
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.2.0
4
+ version: 1.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Alexander Lang
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2014-05-12 00:00:00.000000000 Z
11
+ date: 2014-08-26 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: json