cuprum-collections 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (72) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +59 -0
  3. data/CODE_OF_CONDUCT.md +132 -0
  4. data/DEVELOPMENT.md +25 -0
  5. data/LICENSE +22 -0
  6. data/README.md +950 -0
  7. data/lib/cuprum/collections/base.rb +11 -0
  8. data/lib/cuprum/collections/basic/collection.rb +135 -0
  9. data/lib/cuprum/collections/basic/command.rb +112 -0
  10. data/lib/cuprum/collections/basic/commands/assign_one.rb +54 -0
  11. data/lib/cuprum/collections/basic/commands/build_one.rb +45 -0
  12. data/lib/cuprum/collections/basic/commands/destroy_one.rb +48 -0
  13. data/lib/cuprum/collections/basic/commands/find_many.rb +65 -0
  14. data/lib/cuprum/collections/basic/commands/find_matching.rb +126 -0
  15. data/lib/cuprum/collections/basic/commands/find_one.rb +49 -0
  16. data/lib/cuprum/collections/basic/commands/insert_one.rb +50 -0
  17. data/lib/cuprum/collections/basic/commands/update_one.rb +52 -0
  18. data/lib/cuprum/collections/basic/commands/validate_one.rb +69 -0
  19. data/lib/cuprum/collections/basic/commands.rb +18 -0
  20. data/lib/cuprum/collections/basic/query.rb +160 -0
  21. data/lib/cuprum/collections/basic/query_builder.rb +69 -0
  22. data/lib/cuprum/collections/basic/rspec/command_contract.rb +392 -0
  23. data/lib/cuprum/collections/basic/rspec.rb +8 -0
  24. data/lib/cuprum/collections/basic.rb +22 -0
  25. data/lib/cuprum/collections/command.rb +26 -0
  26. data/lib/cuprum/collections/commands/abstract_find_many.rb +77 -0
  27. data/lib/cuprum/collections/commands/abstract_find_matching.rb +64 -0
  28. data/lib/cuprum/collections/commands/abstract_find_one.rb +44 -0
  29. data/lib/cuprum/collections/commands.rb +8 -0
  30. data/lib/cuprum/collections/constraints/attribute_name.rb +22 -0
  31. data/lib/cuprum/collections/constraints/order/attributes_array.rb +26 -0
  32. data/lib/cuprum/collections/constraints/order/attributes_hash.rb +27 -0
  33. data/lib/cuprum/collections/constraints/order/complex_ordering.rb +46 -0
  34. data/lib/cuprum/collections/constraints/order/sort_direction.rb +32 -0
  35. data/lib/cuprum/collections/constraints/order.rb +8 -0
  36. data/lib/cuprum/collections/constraints/ordering.rb +114 -0
  37. data/lib/cuprum/collections/constraints/query_hash.rb +25 -0
  38. data/lib/cuprum/collections/constraints.rb +8 -0
  39. data/lib/cuprum/collections/errors/already_exists.rb +86 -0
  40. data/lib/cuprum/collections/errors/extra_attributes.rb +66 -0
  41. data/lib/cuprum/collections/errors/failed_validation.rb +66 -0
  42. data/lib/cuprum/collections/errors/invalid_parameters.rb +50 -0
  43. data/lib/cuprum/collections/errors/invalid_query.rb +55 -0
  44. data/lib/cuprum/collections/errors/missing_default_contract.rb +49 -0
  45. data/lib/cuprum/collections/errors/not_found.rb +81 -0
  46. data/lib/cuprum/collections/errors/unknown_operator.rb +71 -0
  47. data/lib/cuprum/collections/errors.rb +8 -0
  48. data/lib/cuprum/collections/queries/ordering.rb +74 -0
  49. data/lib/cuprum/collections/queries/parse.rb +22 -0
  50. data/lib/cuprum/collections/queries/parse_block.rb +206 -0
  51. data/lib/cuprum/collections/queries/parse_strategy.rb +91 -0
  52. data/lib/cuprum/collections/queries.rb +25 -0
  53. data/lib/cuprum/collections/query.rb +247 -0
  54. data/lib/cuprum/collections/query_builder.rb +61 -0
  55. data/lib/cuprum/collections/rspec/assign_one_command_contract.rb +168 -0
  56. data/lib/cuprum/collections/rspec/build_one_command_contract.rb +93 -0
  57. data/lib/cuprum/collections/rspec/collection_contract.rb +153 -0
  58. data/lib/cuprum/collections/rspec/destroy_one_command_contract.rb +106 -0
  59. data/lib/cuprum/collections/rspec/find_many_command_contract.rb +327 -0
  60. data/lib/cuprum/collections/rspec/find_matching_command_contract.rb +194 -0
  61. data/lib/cuprum/collections/rspec/find_one_command_contract.rb +154 -0
  62. data/lib/cuprum/collections/rspec/fixtures.rb +89 -0
  63. data/lib/cuprum/collections/rspec/insert_one_command_contract.rb +83 -0
  64. data/lib/cuprum/collections/rspec/query_builder_contract.rb +92 -0
  65. data/lib/cuprum/collections/rspec/query_contract.rb +650 -0
  66. data/lib/cuprum/collections/rspec/querying_contract.rb +298 -0
  67. data/lib/cuprum/collections/rspec/update_one_command_contract.rb +79 -0
  68. data/lib/cuprum/collections/rspec/validate_one_command_contract.rb +96 -0
  69. data/lib/cuprum/collections/rspec.rb +8 -0
  70. data/lib/cuprum/collections/version.rb +59 -0
  71. data/lib/cuprum/collections.rb +26 -0
  72. metadata +219 -0
@@ -0,0 +1,194 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'stannum/rspec/validate_parameter'
4
+
5
+ require 'cuprum/collections/constraints/ordering'
6
+ require 'cuprum/collections/rspec'
7
+ require 'cuprum/collections/rspec/querying_contract'
8
+
9
+ module Cuprum::Collections::RSpec
10
+ # Contract validating the behavior of a FindMatching command implementation.
11
+ FIND_MATCHING_COMMAND_CONTRACT = lambda do
12
+ include Stannum::RSpec::Matchers
13
+
14
+ describe '#call' do
15
+ shared_examples 'should return the matching items' do
16
+ it { expect(result).to be_a_passing_result }
17
+
18
+ it { expect(result.value).to be_a Enumerator }
19
+
20
+ it { expect(result.value.to_a).to be == expected_data }
21
+ end
22
+
23
+ shared_examples 'should return the wrapped items' do
24
+ it { expect(result).to be_a_passing_result }
25
+
26
+ it { expect(result.value).to be_a Hash }
27
+
28
+ it { expect(result.value.keys).to be == [collection_name] }
29
+
30
+ it { expect(result.value[collection_name]).to be == expected_data }
31
+ end
32
+
33
+ include_contract Cuprum::Collections::RSpec::QUERYING_CONTEXTS
34
+
35
+ let(:options) do
36
+ opts = {}
37
+
38
+ opts[:limit] = limit if limit
39
+ opts[:offset] = offset if offset
40
+ opts[:order] = order if order
41
+ opts[:where] = filter unless filter.nil? || filter.is_a?(Proc)
42
+
43
+ opts
44
+ end
45
+ let(:block) { filter.is_a?(Proc) ? filter : nil }
46
+ let(:result) { command.call(**options, &block) }
47
+ let(:data) { [] }
48
+ let(:matching_data) { data }
49
+ let(:expected_data) do
50
+ defined?(super()) ? super() : matching_data
51
+ end
52
+
53
+ it 'should validate the :envelope keyword' do
54
+ expect(command)
55
+ .to validate_parameter(:call, :envelope)
56
+ .using_constraint(Stannum::Constraints::Boolean.new)
57
+ end
58
+
59
+ it 'should validate the :limit keyword' do
60
+ expect(command)
61
+ .to validate_parameter(:call, :limit)
62
+ .with_value(Object.new)
63
+ .using_constraint(Integer, required: false)
64
+ end
65
+
66
+ it 'should validate the :offset keyword' do
67
+ expect(command)
68
+ .to validate_parameter(:call, :offset)
69
+ .with_value(Object.new)
70
+ .using_constraint(Integer, required: false)
71
+ end
72
+
73
+ it 'should validate the :order keyword' do
74
+ constraint = Cuprum::Collections::Constraints::Ordering.new
75
+
76
+ expect(command)
77
+ .to validate_parameter(:call, :order)
78
+ .with_value(Object.new)
79
+ .using_constraint(constraint, required: false)
80
+ end
81
+
82
+ it 'should validate the :scope keyword' do
83
+ expect(command)
84
+ .to validate_parameter(:call, :scope)
85
+ .using_constraint(
86
+ Stannum::Constraints::Type.new(query.class, optional: true)
87
+ )
88
+ .with_value(Object.new.freeze)
89
+ end
90
+
91
+ it 'should validate the :where keyword' do
92
+ expect(command).to validate_parameter(:call, :where)
93
+ end
94
+
95
+ include_examples 'should return the matching items'
96
+
97
+ include_contract Cuprum::Collections::RSpec::QUERYING_CONTRACT,
98
+ block: lambda {
99
+ include_examples 'should return the matching items'
100
+ }
101
+
102
+ describe 'with an invalid filter block' do
103
+ let(:block) { -> {} }
104
+ let(:expected_error) do
105
+ an_instance_of(Cuprum::Collections::Errors::InvalidQuery)
106
+ end
107
+
108
+ it 'should return a failing result' do
109
+ expect(result).to be_a_failing_result.with_error(expected_error)
110
+ end
111
+ end
112
+
113
+ describe 'with envelope: true' do
114
+ let(:options) { super().merge(envelope: true) }
115
+
116
+ include_examples 'should return the wrapped items'
117
+
118
+ include_contract Cuprum::Collections::RSpec::QUERYING_CONTRACT,
119
+ block: lambda {
120
+ include_examples 'should return the wrapped items'
121
+ }
122
+ end
123
+
124
+ context 'when the collection has many items' do
125
+ let(:data) { fixtures_data }
126
+
127
+ include_examples 'should return the matching items'
128
+
129
+ include_contract Cuprum::Collections::RSpec::QUERYING_CONTRACT,
130
+ block: lambda {
131
+ include_examples 'should return the matching items'
132
+ }
133
+
134
+ describe 'with envelope: true' do
135
+ let(:options) { super().merge(envelope: true) }
136
+
137
+ include_examples 'should return the wrapped items'
138
+
139
+ include_contract Cuprum::Collections::RSpec::QUERYING_CONTRACT,
140
+ block: lambda {
141
+ include_examples 'should return the wrapped items'
142
+ }
143
+ end
144
+
145
+ describe 'with scope: query' do
146
+ let(:scope_filter) { -> { {} } }
147
+ let(:options) { super().merge(scope: scope) }
148
+
149
+ describe 'with a scope that does not match any values' do
150
+ let(:scope_filter) { -> { { series: 'Mistborn' } } }
151
+ let(:matching_data) { [] }
152
+
153
+ include_examples 'should return the matching items'
154
+ end
155
+
156
+ describe 'with a scope that matches some values' do
157
+ let(:scope_filter) { -> { { series: nil } } }
158
+ let(:matching_data) do
159
+ super().select { |item| item['series'].nil? }
160
+ end
161
+
162
+ include_examples 'should return the matching items'
163
+
164
+ describe 'with a where filter' do
165
+ let(:filter) { -> { { author: 'Ursula K. LeGuin' } } }
166
+ let(:options) { super().merge(where: filter) }
167
+ let(:matching_data) do
168
+ super().select { |item| item['author'] == 'Ursula K. LeGuin' }
169
+ end
170
+
171
+ include_examples 'should return the matching items'
172
+ end
173
+ end
174
+
175
+ describe 'with a scope that matches all values' do
176
+ let(:scope_filter) { -> { { id: not_equal(nil) } } }
177
+
178
+ include_examples 'should return the matching items'
179
+
180
+ describe 'with a where filter' do
181
+ let(:filter) { -> { { author: 'Ursula K. LeGuin' } } }
182
+ let(:options) { super().merge(where: filter) }
183
+ let(:matching_data) do
184
+ super().select { |item| item['author'] == 'Ursula K. LeGuin' }
185
+ end
186
+
187
+ include_examples 'should return the matching items'
188
+ end
189
+ end
190
+ end
191
+ end
192
+ end
193
+ end
194
+ end
@@ -0,0 +1,154 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'cuprum/collections/rspec'
4
+
5
+ module Cuprum::Collections::RSpec
6
+ # Contract validating the behavior of a FindOne command implementation.
7
+ FIND_ONE_COMMAND_CONTRACT = lambda do
8
+ describe '#call' do
9
+ let(:mapped_data) do
10
+ defined?(super()) ? super() : data
11
+ end
12
+ let(:primary_key_name) { defined?(super()) ? super() : :id }
13
+ let(:primary_key_type) { defined?(super()) ? super() : Integer }
14
+ let(:invalid_primary_key_value) do
15
+ defined?(super()) ? super() : 100
16
+ end
17
+ let(:valid_primary_key_value) do
18
+ defined?(super()) ? super() : 0
19
+ end
20
+
21
+ def tools
22
+ SleepingKingStudios::Tools::Toolbelt.instance
23
+ end
24
+
25
+ it 'should validate the :envelope keyword' do
26
+ expect(command)
27
+ .to validate_parameter(:call, :envelope)
28
+ .using_constraint(Stannum::Constraints::Boolean.new)
29
+ end
30
+
31
+ it 'should validate the :primary_key keyword' do
32
+ expect(command)
33
+ .to validate_parameter(:call, :primary_key)
34
+ .using_constraint(primary_key_type)
35
+ end
36
+
37
+ it 'should validate the :scope keyword' do
38
+ expect(command)
39
+ .to validate_parameter(:call, :scope)
40
+ .using_constraint(
41
+ Stannum::Constraints::Type.new(query.class, optional: true)
42
+ )
43
+ .with_value(Object.new.freeze)
44
+ end
45
+
46
+ describe 'with an invalid primary key' do
47
+ let(:primary_key) { invalid_primary_key_value }
48
+ let(:expected_error) do
49
+ Cuprum::Collections::Errors::NotFound.new(
50
+ collection_name: command.collection_name,
51
+ primary_key_name: primary_key_name,
52
+ primary_key_values: primary_key
53
+ )
54
+ end
55
+
56
+ it 'should return a failing result' do
57
+ expect(command.call(primary_key: primary_key))
58
+ .to be_a_failing_result
59
+ .with_error(expected_error)
60
+ end
61
+ end
62
+
63
+ context 'when the collection has many items' do
64
+ let(:data) { fixtures_data }
65
+ let(:matching_data) do
66
+ mapped_data.find { |item| item[primary_key_name.to_s] == primary_key }
67
+ end
68
+ let(:expected_data) do
69
+ defined?(super()) ? super() : matching_data
70
+ end
71
+
72
+ describe 'with an invalid primary key' do
73
+ let(:primary_key) { invalid_primary_key_value }
74
+ let(:expected_error) do
75
+ Cuprum::Collections::Errors::NotFound.new(
76
+ collection_name: command.collection_name,
77
+ primary_key_name: primary_key_name,
78
+ primary_key_values: primary_key
79
+ )
80
+ end
81
+
82
+ it 'should return a failing result' do
83
+ expect(command.call(primary_key: primary_key))
84
+ .to be_a_failing_result
85
+ .with_error(expected_error)
86
+ end
87
+ end
88
+
89
+ describe 'with a valid primary key' do
90
+ let(:primary_key) { valid_primary_key_value }
91
+
92
+ it 'should return a passing result' do
93
+ expect(command.call(primary_key: primary_key))
94
+ .to be_a_passing_result
95
+ .with_value(expected_data)
96
+ end
97
+ end
98
+
99
+ describe 'with envelope: true' do
100
+ let(:member_name) { tools.str.singularize(collection_name) }
101
+
102
+ describe 'with a valid primary key' do
103
+ let(:primary_key) { valid_primary_key_value }
104
+
105
+ it 'should return a passing result' do
106
+ expect(command.call(primary_key: primary_key, envelope: true))
107
+ .to be_a_passing_result
108
+ .with_value({ member_name => expected_data })
109
+ end
110
+ end
111
+ end
112
+
113
+ describe 'with scope: query' do
114
+ let(:scope_filter) { -> { {} } }
115
+
116
+ describe 'with a scope that does not match the key' do
117
+ let(:scope_filter) { -> { { author: 'Ursula K. LeGuin' } } }
118
+
119
+ describe 'with an valid primary key' do
120
+ let(:primary_key) { valid_primary_key_value }
121
+ let(:expected_error) do
122
+ Cuprum::Collections::Errors::NotFound.new(
123
+ collection_name: command.collection_name,
124
+ primary_key_name: primary_key_name,
125
+ primary_key_values: primary_key
126
+ )
127
+ end
128
+
129
+ it 'should return a failing result' do
130
+ expect(command.call(primary_key: primary_key, scope: scope))
131
+ .to be_a_failing_result
132
+ .with_error(expected_error)
133
+ end
134
+ end
135
+ end
136
+
137
+ describe 'with a scope that matches the key' do
138
+ let(:scope_filter) { -> { { author: 'J.R.R. Tolkien' } } }
139
+
140
+ describe 'with a valid primary key' do
141
+ let(:primary_key) { valid_primary_key_value }
142
+
143
+ it 'should return a passing result' do
144
+ expect(command.call(primary_key: primary_key))
145
+ .to be_a_passing_result
146
+ .with_value(expected_data)
147
+ end
148
+ end
149
+ end
150
+ end
151
+ end
152
+ end
153
+ end
154
+ end
@@ -0,0 +1,89 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'cuprum/collections/rspec'
4
+
5
+ module Cuprum::Collections::RSpec
6
+ # Sample data for validating collection implementations.
7
+ BOOKS_FIXTURES = [
8
+ {
9
+ 'id' => 0,
10
+ 'title' => 'The Hobbit',
11
+ 'author' => 'J.R.R. Tolkien',
12
+ 'series' => nil,
13
+ 'category' => 'Science Fiction and Fantasy',
14
+ 'published_at' => '1937-09-21'
15
+ },
16
+ {
17
+ 'id' => 1,
18
+ 'title' => 'The Silmarillion',
19
+ 'author' => 'J.R.R. Tolkien',
20
+ 'series' => nil,
21
+ 'category' => 'Science Fiction and Fantasy',
22
+ 'published_at' => '1977-09-15'
23
+ },
24
+ {
25
+ 'id' => 2,
26
+ 'title' => 'The Fellowship of the Ring',
27
+ 'author' => 'J.R.R. Tolkien',
28
+ 'series' => 'The Lord of the Rings',
29
+ 'category' => 'Science Fiction and Fantasy',
30
+ 'published_at' => '1954-07-29'
31
+ },
32
+ {
33
+ 'id' => 3,
34
+ 'title' => 'The Two Towers',
35
+ 'author' => 'J.R.R. Tolkien',
36
+ 'series' => 'The Lord of the Rings',
37
+ 'category' => 'Science Fiction and Fantasy',
38
+ 'published_at' => '1954-11-11'
39
+ },
40
+ {
41
+ 'id' => 4,
42
+ 'title' => 'The Return of the King',
43
+ 'author' => 'J.R.R. Tolkien',
44
+ 'series' => 'The Lord of the Rings',
45
+ 'category' => 'Science Fiction and Fantasy',
46
+ 'published_at' => '1955-10-20'
47
+ },
48
+ {
49
+ 'id' => 5,
50
+ 'title' => 'The Word for World is Forest',
51
+ 'author' => 'Ursula K. LeGuin',
52
+ 'series' => nil,
53
+ 'category' => 'Science Fiction and Fantasy',
54
+ 'published_at' => '1972-03-13'
55
+ },
56
+ {
57
+ 'id' => 6,
58
+ 'title' => 'The Ones Who Walk Away From Omelas',
59
+ 'author' => 'Ursula K. LeGuin',
60
+ 'series' => nil,
61
+ 'category' => 'Science Fiction and Fantasy',
62
+ 'published_at' => '1973-10-01'
63
+ },
64
+ {
65
+ 'id' => 7,
66
+ 'title' => 'A Wizard of Earthsea',
67
+ 'author' => 'Ursula K. LeGuin',
68
+ 'series' => 'Earthsea',
69
+ 'category' => 'Science Fiction and Fantasy',
70
+ 'published_at' => '1968-11-01'
71
+ },
72
+ {
73
+ 'id' => 8,
74
+ 'title' => 'The Tombs of Atuan',
75
+ 'author' => 'Ursula K. LeGuin',
76
+ 'series' => 'Earthsea',
77
+ 'category' => 'Science Fiction and Fantasy',
78
+ 'published_at' => '1970-12-01'
79
+ },
80
+ {
81
+ 'id' => 9,
82
+ 'title' => 'The Farthest Shore',
83
+ 'author' => 'Ursula K. LeGuin',
84
+ 'series' => 'Earthsea',
85
+ 'category' => 'Science Fiction and Fantasy',
86
+ 'published_at' => '1972-09-01'
87
+ }
88
+ ].map(&:freeze).freeze
89
+ end
@@ -0,0 +1,83 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'cuprum/collections/rspec'
4
+
5
+ module Cuprum::Collections::RSpec
6
+ # Contract validating the behavior of an InsertOne command implementation.
7
+ INSERT_ONE_COMMAND_CONTRACT = lambda do
8
+ describe '#call' do
9
+ let(:matching_data) { attributes }
10
+ let(:expected_data) do
11
+ defined?(super()) ? super() : matching_data
12
+ end
13
+ let(:primary_key_name) do
14
+ defined?(super()) ? super() : :id
15
+ end
16
+ let(:primary_key_type) do
17
+ defined?(super()) ? super() : Integer
18
+ end
19
+ let(:scoped) do
20
+ key = primary_key_name
21
+ value = entity[primary_key_name.to_s]
22
+
23
+ query.where { { key => value } }
24
+ end
25
+
26
+ it 'should validate the :entity keyword' do
27
+ expect(command)
28
+ .to validate_parameter(:call, :entity)
29
+ .using_constraint(entity_type)
30
+ end
31
+
32
+ context 'when the item does not exist in the collection' do
33
+ it 'should return a passing result' do
34
+ expect(command.call(entity: entity))
35
+ .to be_a_passing_result
36
+ .with_value(be == expected_data)
37
+ end
38
+
39
+ it 'should append an item to the collection' do
40
+ expect { command.call(entity: entity) }
41
+ .to(
42
+ change { query.reset.count }
43
+ .by(1)
44
+ )
45
+ end
46
+
47
+ it 'should add the entity to the collection' do
48
+ expect { command.call(entity: entity) }
49
+ .to change(scoped, :exists?)
50
+ .to be true
51
+ end
52
+
53
+ it 'should set the attributes' do
54
+ command.call(entity: entity)
55
+
56
+ expect(scoped.to_a.first).to be == expected_data
57
+ end
58
+ end
59
+
60
+ context 'when the item exists in the collection' do
61
+ let(:data) { fixtures_data }
62
+ let(:expected_error) do
63
+ Cuprum::Collections::Errors::AlreadyExists.new(
64
+ collection_name: collection_name,
65
+ primary_key_name: primary_key_name,
66
+ primary_key_values: attributes[primary_key_name]
67
+ )
68
+ end
69
+
70
+ it 'should return a failing result' do
71
+ expect(command.call(entity: entity))
72
+ .to be_a_failing_result
73
+ .with_error(expected_error)
74
+ end
75
+
76
+ it 'should not append an item to the collection' do
77
+ expect { command.call(entity: entity) }
78
+ .not_to(change { query.reset.count })
79
+ end
80
+ end
81
+ end
82
+ end
83
+ end
@@ -0,0 +1,92 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'cuprum/collections/rspec'
4
+
5
+ module Cuprum::Collections::RSpec
6
+ # Contract validating the behavior of a QueryBuilder implementation.
7
+ QUERY_BUILDER_CONTRACT = lambda do
8
+ describe '#base_query' do
9
+ include_examples 'should define reader', :base_query, -> { base_query }
10
+ end
11
+
12
+ describe '#call' do
13
+ let(:criteria) { [['title', :equal, 'The Naked Sun']] }
14
+ let(:expected) { criteria }
15
+ let(:filter) { { title: 'The Naked Sun' } }
16
+ let(:strategy) { :custom }
17
+ let(:parser) do
18
+ instance_double(
19
+ Cuprum::Collections::Queries::Parse,
20
+ call: Cuprum::Result.new(value: criteria)
21
+ )
22
+ end
23
+ let(:query) do
24
+ builder.call(strategy: strategy, where: filter)
25
+ end
26
+
27
+ before(:example) do
28
+ allow(Cuprum::Collections::Queries::Parse)
29
+ .to receive(:new)
30
+ .and_return(parser)
31
+ end
32
+
33
+ it 'should define the method' do
34
+ expect(builder).to respond_to(:call)
35
+ .with(0).arguments
36
+ .and_keywords(:strategy, :where)
37
+ end
38
+
39
+ it 'should parse the criteria' do
40
+ builder.call(strategy: strategy, where: filter)
41
+
42
+ expect(parser)
43
+ .to have_received(:call)
44
+ .with(strategy: strategy, where: filter)
45
+ end
46
+
47
+ it { expect(query).to be_a base_query.class }
48
+
49
+ it { expect(query).not_to be base_query }
50
+
51
+ it { expect(query.criteria).to be == expected }
52
+
53
+ describe 'with strategy: :unsafe' do
54
+ let(:strategy) { :unsafe }
55
+ let(:filter) { criteria }
56
+
57
+ it 'should not parse the criteria' do
58
+ builder.call(strategy: strategy, where: filter)
59
+
60
+ expect(parser).not_to have_received(:call)
61
+ end
62
+
63
+ it { expect(query.criteria).to be == expected }
64
+ end
65
+
66
+ context 'when the query has existing criteria' do
67
+ let(:old_criteria) { [['genre', :eq, 'Science Fiction']] }
68
+ let(:expected) { old_criteria + criteria }
69
+ let(:base_query) { super().send(:with_criteria, old_criteria) }
70
+
71
+ it { expect(query.criteria).to be == expected }
72
+ end
73
+
74
+ context 'when the parser is unable to parse the query' do
75
+ let(:error) { Cuprum::Error.new(message: 'Something went wrong.') }
76
+ let(:result) { Cuprum::Result.new(error: error) }
77
+
78
+ before(:example) do
79
+ allow(parser).to receive(:call).and_return(result)
80
+ end
81
+
82
+ it 'should raise an exception' do
83
+ expect do
84
+ builder.call(strategy: strategy, where: filter)
85
+ end
86
+ .to raise_error Cuprum::Collections::QueryBuilder::ParseError,
87
+ error.message
88
+ end
89
+ end
90
+ end
91
+ end
92
+ end