couch_potato 1.2.0 → 1.3.0

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