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 +4 -4
- data/.travis.yml +1 -1
- data/CHANGES.md +6 -0
- data/README.md +1 -1
- data/lib/couch_potato/persistence.rb +3 -3
- data/lib/couch_potato/rspec/matchers/map_reduce_to_matcher.rb +43 -6
- data/lib/couch_potato/rspec/matchers/map_to_matcher.rb +4 -3
- data/lib/couch_potato/version.rb +1 -1
- data/lib/couch_potato/view/view_query.rb +12 -2
- data/spec/unit/attributes_spec.rb +9 -3
- data/spec/unit/rspec_matchers_spec.rb +50 -2
- data/spec/unit/view_query_spec.rb +36 -7
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 6f37912f0e7ed48878e92e5ad8ef34d106359f15
|
4
|
+
data.tar.gz: 020faa774590847e21f942504cf9fb02df3683e7
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 6a6fce1ef1c76b01c073ac3706d894d5c567d05afe20ef4177672950f96db1011c9f6aee607852a7d0c605e303250ecec4ad0acbbc9494b30b603a25690924cd
|
7
|
+
data.tar.gz: 240d66a1fcb88a3214c946fd51e924abf314410c8b14a203423626d6d777633ee0764ebffbfb2e62b707539c218da0b01da29068f717fe2057b69f1f6d9a2921
|
data/.travis.yml
CHANGED
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 = {
|
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 # => {
|
96
|
+
# book.attributes # => {'title' => nil, 'year' => 2009}
|
97
97
|
def attributes
|
98
|
-
self.class.properties.inject(
|
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
|
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
|
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) {
|
data/lib/couch_potato/version.rb
CHANGED
@@ -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
|
-
|
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 == {
|
38
|
-
|
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
|
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>'}
|
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>"}
|
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
|
-
|
80
|
-
|
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
|
-
|
91
|
-
|
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.
|
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-
|
11
|
+
date: 2014-08-26 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: json
|