cuprum-collections 0.1.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.
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