couch_potato 1.4.0 → 1.6.3
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 +4 -4
- data/.ruby-version +1 -0
- data/.travis.yml +12 -8
- data/CHANGES.md +4 -0
- data/Gemfile +1 -1
- data/README.md +396 -276
- data/Rakefile +9 -9
- data/couch_potato-rspec.gemspec +20 -0
- data/couch_potato.gemspec +15 -16
- data/{active_support_4_0 → gemfiles/active_support_4_0} +3 -3
- data/{active_support_3_2 → gemfiles/active_support_4_1} +3 -2
- data/gemfiles/active_support_4_2 +11 -0
- data/lib/couch_potato-rspec.rb +3 -0
- data/lib/couch_potato.rb +3 -1
- data/lib/couch_potato/database.rb +42 -39
- data/lib/couch_potato/persistence/magic_timestamps.rb +5 -5
- data/lib/couch_potato/persistence/properties.rb +8 -2
- data/lib/couch_potato/persistence/simple_property.rb +11 -9
- data/lib/couch_potato/persistence/type_caster.rb +1 -1
- data/lib/couch_potato/railtie.rb +2 -0
- data/lib/couch_potato/version.rb +2 -1
- data/lib/couch_potato/view/base_view_spec.rb +18 -8
- data/lib/couch_potato/view/view_query.rb +2 -3
- data/spec/attachments_spec.rb +3 -3
- data/spec/callbacks_spec.rb +193 -113
- data/spec/conflict_handling_spec.rb +4 -4
- data/spec/create_spec.rb +5 -5
- data/spec/default_property_spec.rb +6 -6
- data/spec/destroy_spec.rb +5 -5
- data/spec/property_spec.rb +71 -61
- data/spec/rails_spec.rb +3 -3
- data/spec/railtie_spec.rb +12 -13
- data/spec/spec_helper.rb +3 -3
- data/spec/unit/active_model_compliance_spec.rb +16 -16
- data/spec/unit/attributes_spec.rb +36 -34
- data/spec/unit/base_view_spec_spec.rb +82 -35
- data/spec/unit/callbacks_spec.rb +2 -2
- data/spec/unit/couch_potato_spec.rb +3 -3
- data/spec/unit/create_spec.rb +12 -12
- data/spec/unit/custom_views_spec.rb +1 -1
- data/spec/unit/database_spec.rb +95 -95
- data/spec/unit/date_spec.rb +3 -3
- data/spec/unit/deep_dirty_attributes_spec.rb +104 -104
- data/spec/unit/dirty_attributes_spec.rb +19 -19
- data/spec/unit/forbidden_attributes_protection_spec.rb +4 -4
- data/spec/unit/initialize_spec.rb +37 -19
- data/spec/unit/json_spec.rb +4 -4
- data/spec/unit/lists_spec.rb +8 -8
- data/spec/unit/model_view_spec_spec.rb +14 -14
- data/spec/unit/persistence_spec.rb +6 -6
- data/spec/unit/properties_view_spec_spec.rb +4 -4
- data/spec/unit/rspec_matchers_spec.rb +73 -73
- data/spec/unit/rspec_stub_db_spec.rb +43 -42
- data/spec/unit/string_spec.rb +1 -1
- data/spec/unit/time_spec.rb +2 -2
- data/spec/unit/validation_spec.rb +1 -1
- data/spec/unit/view_query_spec.rb +54 -59
- data/spec/update_spec.rb +5 -5
- data/spec/view_updates_spec.rb +4 -4
- data/spec/views_spec.rb +43 -43
- metadata +18 -22
- data/lib/couch_potato/rspec.rb +0 -2
- data/lib/couch_potato/rspec/matchers.rb +0 -56
- data/lib/couch_potato/rspec/matchers/json2.js +0 -482
- data/lib/couch_potato/rspec/matchers/list_as_matcher.rb +0 -53
- data/lib/couch_potato/rspec/matchers/map_reduce_to_matcher.rb +0 -166
- data/lib/couch_potato/rspec/matchers/map_to_matcher.rb +0 -61
- data/lib/couch_potato/rspec/matchers/reduce_to_matcher.rb +0 -48
- data/lib/couch_potato/rspec/stub_db.rb +0 -57
@@ -1,53 +0,0 @@
|
|
1
|
-
module CouchPotato
|
2
|
-
module RSpec
|
3
|
-
class ListAsProxy
|
4
|
-
def initialize(results_ruby)
|
5
|
-
@results_ruby = results_ruby
|
6
|
-
end
|
7
|
-
|
8
|
-
def as(expected_ruby)
|
9
|
-
ListAsMatcher.new(expected_ruby, @results_ruby)
|
10
|
-
end
|
11
|
-
end
|
12
|
-
|
13
|
-
class ListAsMatcher
|
14
|
-
include RunJS
|
15
|
-
|
16
|
-
def initialize(expected_ruby, results_ruby)
|
17
|
-
@expected_ruby = expected_ruby
|
18
|
-
@results_ruby = results_ruby
|
19
|
-
end
|
20
|
-
|
21
|
-
def matches?(view_spec)
|
22
|
-
js = <<-JS
|
23
|
-
#{File.read(File.dirname(__FILE__) + '/json2.js')}
|
24
|
-
var results = #{@results_ruby.to_json};
|
25
|
-
var listed = '';
|
26
|
-
var list = #{view_spec.list_function};
|
27
|
-
|
28
|
-
var getRow = function() {
|
29
|
-
return results.rows.shift();
|
30
|
-
};
|
31
|
-
var send = function(text) {
|
32
|
-
listed = listed + text;
|
33
|
-
};
|
34
|
-
list();
|
35
|
-
JSON.stringify(JSON.parse(listed));
|
36
|
-
JS
|
37
|
-
|
38
|
-
@actual_ruby = JSON.parse(run_js(js))
|
39
|
-
|
40
|
-
@expected_ruby == @actual_ruby
|
41
|
-
end
|
42
|
-
|
43
|
-
def failure_message_for_should
|
44
|
-
"Expected to list as #{@expected_ruby.inspect} but got #{@actual_ruby.inspect}."
|
45
|
-
end
|
46
|
-
|
47
|
-
def failure_message_for_should_not
|
48
|
-
"Expected to not list as #{@expected_ruby.inspect} but did."
|
49
|
-
end
|
50
|
-
|
51
|
-
end
|
52
|
-
end
|
53
|
-
end
|
@@ -1,166 +0,0 @@
|
|
1
|
-
require 'json'
|
2
|
-
|
3
|
-
module CouchPotato
|
4
|
-
module RSpec
|
5
|
-
class MapReduceToProxy
|
6
|
-
def initialize(*input_ruby)
|
7
|
-
@input_ruby, @options = input_ruby.flatten, {}
|
8
|
-
end
|
9
|
-
|
10
|
-
def with_options(options)
|
11
|
-
@options = options
|
12
|
-
self
|
13
|
-
end
|
14
|
-
|
15
|
-
def to(*expected_ruby)
|
16
|
-
MapReduceToMatcher.new(expected_ruby, @input_ruby, @options)
|
17
|
-
end
|
18
|
-
end
|
19
|
-
|
20
|
-
class MapReduceToMatcher
|
21
|
-
include RunJS
|
22
|
-
|
23
|
-
def initialize(expected_ruby, input_ruby, options)
|
24
|
-
@expected_ruby = expected_ruby
|
25
|
-
@input_ruby = input_ruby
|
26
|
-
@options = options
|
27
|
-
end
|
28
|
-
|
29
|
-
def matches?(view_spec)
|
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
|
-
|
70
|
-
var docs = #{@input_ruby.to_json};
|
71
|
-
var options = #{@options.to_json};
|
72
|
-
var map = #{view_spec.map_function};
|
73
|
-
var reduce = #{view_spec.reduce_function};
|
74
|
-
var lib = #{view_spec.respond_to?(:lib) && view_spec.lib.to_json};
|
75
|
-
|
76
|
-
// Map the input docs
|
77
|
-
var require = function(modulePath) {
|
78
|
-
var module = {exports: {}};
|
79
|
-
var exports = module.exports;
|
80
|
-
var pathArray = modulePath.split("/").slice(2);
|
81
|
-
var result = lib;
|
82
|
-
for (var i in pathArray) {
|
83
|
-
result = result[pathArray[i]];
|
84
|
-
}
|
85
|
-
eval(result);
|
86
|
-
return module.exports;
|
87
|
-
}
|
88
|
-
|
89
|
-
var mapResults = [];
|
90
|
-
var emit = function(key, value) {
|
91
|
-
mapResults.push({key: key, value: value});
|
92
|
-
};
|
93
|
-
for (var i in docs) {
|
94
|
-
map(docs[i]);
|
95
|
-
}
|
96
|
-
|
97
|
-
// Group the map results, honoring the group and group_level options
|
98
|
-
var grouped = [];
|
99
|
-
if (options.group || options.group_level) {
|
100
|
-
var groupLevel = options.group_level;
|
101
|
-
if (groupLevel == "exact" || options.group == true)
|
102
|
-
groupLevel = 9999;
|
103
|
-
var keysEqual = function(a, b) { return !(a < b || b < a); }
|
104
|
-
|
105
|
-
for (var mr in mapResults) {
|
106
|
-
var mapResult = mapResults[mr];
|
107
|
-
var groupedKey = mapResult.key.slice(0, groupLevel);
|
108
|
-
var groupFound = false;
|
109
|
-
for (var g in grouped) {
|
110
|
-
var group = grouped[g];
|
111
|
-
if (keysEqual(groupedKey, group.groupedKey)) {
|
112
|
-
group.keys.push(mapResult.key);
|
113
|
-
group.values.push(mapResult.value);
|
114
|
-
groupFound = true;
|
115
|
-
break;
|
116
|
-
}
|
117
|
-
}
|
118
|
-
|
119
|
-
if (!groupFound)
|
120
|
-
grouped.push({keys: [mapResult.key], groupedKey: groupedKey, values: [mapResult.value]});
|
121
|
-
}
|
122
|
-
} else {
|
123
|
-
var group = {keys: null, groupedKey: null, values: []};
|
124
|
-
for (var mr in mapResults)
|
125
|
-
group.values.push(mapResults[mr].value);
|
126
|
-
grouped.push(group);
|
127
|
-
}
|
128
|
-
|
129
|
-
// Reduce the grouped map results
|
130
|
-
var results = [];
|
131
|
-
for (var g in grouped) {
|
132
|
-
var group = grouped[g], reduced = null;
|
133
|
-
if (group.values.length >= 2) {
|
134
|
-
// Split the values into two parts, reduce each part, then rereduce those results
|
135
|
-
var mid = parseInt(group.values.length / 2);
|
136
|
-
var keys1 = (group.keys || []).slice(0, mid),
|
137
|
-
values1 = group.values.slice(0, mid);
|
138
|
-
var reduced1 = reduce(keys1, values1, false);
|
139
|
-
var keys2 = (group.keys || []).slice(mid, group.values.length),
|
140
|
-
values2 = group.values.slice(mid, group.values.length);
|
141
|
-
var reduced2 = reduce(keys2, values2, false);
|
142
|
-
reduced = reduce(null, [reduced1, reduced2], true);
|
143
|
-
} else {
|
144
|
-
// Not enough values to split, so just reduce, and then rereduce the single result
|
145
|
-
reduced = reduce(group.keys, group.values, false);
|
146
|
-
reduced = reduce(null, [reduced], true);
|
147
|
-
}
|
148
|
-
results.push({key: group.groupedKey, value: reduced});
|
149
|
-
}
|
150
|
-
|
151
|
-
JSON.stringify(results);
|
152
|
-
JS
|
153
|
-
@actual_ruby = JSON.parse(run_js(js))
|
154
|
-
@expected_ruby == @actual_ruby
|
155
|
-
end
|
156
|
-
|
157
|
-
def failure_message_for_should
|
158
|
-
"Expected to map/reduce to #{@expected_ruby.inspect} but got #{@actual_ruby.inspect}."
|
159
|
-
end
|
160
|
-
|
161
|
-
def failure_message_for_should_not
|
162
|
-
"Expected not to map/reduce to #{@actual_ruby.inspect} but did."
|
163
|
-
end
|
164
|
-
end
|
165
|
-
end
|
166
|
-
end
|
@@ -1,61 +0,0 @@
|
|
1
|
-
require 'json'
|
2
|
-
|
3
|
-
|
4
|
-
module CouchPotato
|
5
|
-
module RSpec
|
6
|
-
class MapToProxy
|
7
|
-
def initialize(input_ruby)
|
8
|
-
@input_ruby = input_ruby
|
9
|
-
end
|
10
|
-
|
11
|
-
def to(*expected_ruby)
|
12
|
-
MapToMatcher.new(expected_ruby, @input_ruby)
|
13
|
-
end
|
14
|
-
end
|
15
|
-
|
16
|
-
class MapToMatcher
|
17
|
-
include RunJS
|
18
|
-
|
19
|
-
def initialize(expected_ruby, input_ruby)
|
20
|
-
@expected_ruby = expected_ruby
|
21
|
-
@input_ruby = input_ruby
|
22
|
-
end
|
23
|
-
|
24
|
-
def matches?(view_spec)
|
25
|
-
js = <<-JS
|
26
|
-
var doc = #{@input_ruby.to_json};
|
27
|
-
var map = #{view_spec.map_function};
|
28
|
-
var lib = #{view_spec.respond_to?(:lib) && view_spec.lib.to_json};
|
29
|
-
var result = [];
|
30
|
-
var require = function(modulePath) {
|
31
|
-
var module = {exports: {}};
|
32
|
-
var exports = module.exports;
|
33
|
-
var pathArray = modulePath.split("/").slice(2);
|
34
|
-
var result = lib;
|
35
|
-
for (var i in pathArray) {
|
36
|
-
result = result[pathArray[i]];
|
37
|
-
}
|
38
|
-
eval(result);
|
39
|
-
return module.exports;
|
40
|
-
}
|
41
|
-
|
42
|
-
var emit = function(key, value) {
|
43
|
-
result.push([key, value]);
|
44
|
-
};
|
45
|
-
map(doc);
|
46
|
-
JSON.stringify(result);
|
47
|
-
JS
|
48
|
-
@actual_ruby = JSON.parse(run_js(js))
|
49
|
-
@expected_ruby == @actual_ruby
|
50
|
-
end
|
51
|
-
|
52
|
-
def failure_message_for_should
|
53
|
-
"Expected to map to #{@expected_ruby.inspect} but got #{@actual_ruby.inspect}."
|
54
|
-
end
|
55
|
-
|
56
|
-
def failure_message_for_should_not
|
57
|
-
"Expected not to map to #{@actual_ruby.inspect} but did."
|
58
|
-
end
|
59
|
-
end
|
60
|
-
end
|
61
|
-
end
|
@@ -1,48 +0,0 @@
|
|
1
|
-
module CouchPotato
|
2
|
-
module RSpec
|
3
|
-
class ReduceToProxy
|
4
|
-
def initialize(keys, values, rereduce = false)
|
5
|
-
@keys, @values, @rereduce = keys, values, rereduce
|
6
|
-
end
|
7
|
-
|
8
|
-
def to(expected_ruby)
|
9
|
-
ReduceToMatcher.new(expected_ruby, @keys, @values, @rereduce)
|
10
|
-
end
|
11
|
-
end
|
12
|
-
|
13
|
-
class ReduceToMatcher
|
14
|
-
include RunJS
|
15
|
-
|
16
|
-
def initialize(expected_ruby, keys, values, rereduce = false)
|
17
|
-
@expected_ruby, @keys, @values, @rereduce = expected_ruby, keys, values, rereduce
|
18
|
-
end
|
19
|
-
|
20
|
-
def matches?(view_spec)
|
21
|
-
js = <<-JS
|
22
|
-
sum = function(values) {
|
23
|
-
var rv = 0;
|
24
|
-
for (var i in values) {
|
25
|
-
rv += values[i];
|
26
|
-
}
|
27
|
-
return rv;
|
28
|
-
};
|
29
|
-
|
30
|
-
var keys = #{@keys.to_json};
|
31
|
-
var values = #{@values.to_json};
|
32
|
-
var reduce = #{view_spec.reduce_function};
|
33
|
-
JSON.stringify({result: reduce(keys, values, #{@rereduce})});
|
34
|
-
JS
|
35
|
-
@actual_ruby = JSON.parse(run_js(js))['result']
|
36
|
-
@expected_ruby == @actual_ruby
|
37
|
-
end
|
38
|
-
|
39
|
-
def failure_message_for_should
|
40
|
-
"Expected to reduce to #{@expected_ruby.inspect} but got #{@actual_ruby.inspect}."
|
41
|
-
end
|
42
|
-
|
43
|
-
def failure_message_for_should_not
|
44
|
-
"Expected not to reduce to #{@actual_ruby.inspect} but did."
|
45
|
-
end
|
46
|
-
end
|
47
|
-
end
|
48
|
-
end
|
@@ -1,57 +0,0 @@
|
|
1
|
-
module CouchPotato::RSpec
|
2
|
-
|
3
|
-
module StubView
|
4
|
-
class ViewStub
|
5
|
-
def initialize(clazz, view, db)
|
6
|
-
@clazz = clazz
|
7
|
-
@view = view
|
8
|
-
@db = db
|
9
|
-
end
|
10
|
-
|
11
|
-
def with(*args, &block)
|
12
|
-
@args = args
|
13
|
-
and_return(block.call) if block
|
14
|
-
self
|
15
|
-
end
|
16
|
-
|
17
|
-
def and_return(return_value)
|
18
|
-
view_stub = RSpec::Mocks::Mock.new("#{@clazz}.#{@view}(#{@args.try(:join, ', ')}) view")
|
19
|
-
_stub = @clazz.stub(@view)
|
20
|
-
_stub.with(*@args) if @args
|
21
|
-
_stub.and_return(view_stub)
|
22
|
-
@db.stub(:view).with(view_stub).and_return(return_value)
|
23
|
-
if return_value.respond_to?(:first)
|
24
|
-
@db.stub(:first).with(view_stub).and_return(return_value.first)
|
25
|
-
if return_value.first
|
26
|
-
@db.stub(:first!).with(view_stub).and_return(return_value.first)
|
27
|
-
else
|
28
|
-
@db.stub(:first!).with(view_stub).and_raise(CouchPotato::NotFound)
|
29
|
-
end
|
30
|
-
end
|
31
|
-
end
|
32
|
-
end
|
33
|
-
|
34
|
-
def stub_view(clazz, view, &block)
|
35
|
-
stub = ViewStub.new clazz, view, self
|
36
|
-
stub.and_return(block.call) if block
|
37
|
-
stub
|
38
|
-
end
|
39
|
-
end
|
40
|
-
|
41
|
-
module StubDb
|
42
|
-
def stub_db(options = {})
|
43
|
-
db = double('db', options)
|
44
|
-
db.extend CouchPotato::RSpec::StubView
|
45
|
-
CouchPotato.stub(:database => db)
|
46
|
-
db
|
47
|
-
end
|
48
|
-
end
|
49
|
-
end
|
50
|
-
|
51
|
-
module RSpec
|
52
|
-
module Mocks
|
53
|
-
module ExampleMethods
|
54
|
-
include CouchPotato::RSpec::StubDb
|
55
|
-
end
|
56
|
-
end
|
57
|
-
end
|