CloudSesame 0.6.4 → 0.6.5
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/.gitignore +5 -3
- data/.rubocop.yml +1158 -0
- data/.travis.yml +11 -0
- data/Gemfile.lock +28 -1
- data/Guardfile +6 -1
- data/README.md +6 -1
- data/cloud_sesame.gemspec +4 -2
- data/coverage/.last_run.json +5 -0
- data/coverage/.resultset.json +2423 -0
- data/coverage/.resultset.json.lock +0 -0
- data/lib/cloud_sesame.rb +4 -5
- data/lib/cloud_sesame/domain/base.rb +46 -33
- data/lib/cloud_sesame/domain/client_module/caching/base.rb +1 -1
- data/lib/cloud_sesame/query/ast/near.rb +1 -1
- data/lib/cloud_sesame/query/ast/operator.rb +1 -1
- data/lib/cloud_sesame/query/ast/range_value.rb +23 -30
- data/lib/cloud_sesame/query/ast/single_expression_operator.rb +1 -1
- data/lib/cloud_sesame/query/ast/value.rb +18 -4
- data/lib/cloud_sesame/query/domain/block.rb +2 -2
- data/lib/cloud_sesame/query/domain/literal.rb +1 -1
- data/lib/cloud_sesame/query/dsl/applied_filter_query.rb +21 -23
- data/lib/cloud_sesame/query/dsl/field_accessors.rb +8 -3
- data/lib/cloud_sesame/query/dsl/response_methods.rb +2 -2
- data/lib/cloud_sesame/query/dsl/scope_accessors.rb +1 -1
- data/lib/cloud_sesame/query/node/fuzziness.rb +11 -13
- data/lib/cloud_sesame/query/node/query.rb +9 -10
- data/lib/cloud_sesame/query/node/request.rb +2 -2
- data/lib/cloud_sesame/query/node/sloppiness.rb +23 -0
- data/spec/cloud_sesame/config/credential_spec.rb +76 -0
- data/spec/cloud_sesame/domain/base_spec.rb +274 -9
- data/spec/cloud_sesame/domain/client_spec.rb +0 -1
- data/spec/cloud_sesame/query/domain/block_spec.rb +0 -1
- data/spec/cloud_sesame/query/node/query_spec.rb +18 -7
- data/spec/cloud_sesame_spec.rb +10 -10
- data/spec/spec_helper.rb +3 -0
- metadata +38 -3
- data/lib/cloud_sesame/domain/context.rb +0 -39
@@ -3,6 +3,8 @@ module CloudSesame
|
|
3
3
|
module Node
|
4
4
|
class Fuzziness
|
5
5
|
|
6
|
+
EXCLUDING_TERMS = /^\-/
|
7
|
+
|
6
8
|
def initialize(&block)
|
7
9
|
|
8
10
|
# default fuzziness
|
@@ -10,7 +12,7 @@ module CloudSesame
|
|
10
12
|
@min_char_size = 6
|
11
13
|
@fuzzy_percent = 0.17
|
12
14
|
|
13
|
-
instance_eval
|
15
|
+
instance_eval(&block) if block_given?
|
14
16
|
end
|
15
17
|
|
16
18
|
def max_fuzziness(int)
|
@@ -25,32 +27,28 @@ module CloudSesame
|
|
25
27
|
@fuzzy_percent = float.to_f
|
26
28
|
end
|
27
29
|
|
28
|
-
def
|
29
|
-
|
30
|
+
def compile(string)
|
31
|
+
"(#{ each_word_in(string) { |word| fuzziness(word) }.compact.join('+') })"
|
30
32
|
end
|
31
33
|
|
32
34
|
private
|
33
35
|
|
34
|
-
def
|
35
|
-
string.split(' ').map
|
36
|
+
def each_word_in(string, &block)
|
37
|
+
string.split(' ').map(&block)
|
36
38
|
end
|
37
39
|
|
38
40
|
def fuzziness(word)
|
39
|
-
if word.length >= @min_char_size && !excluding_term?(word)
|
40
|
-
fuzziness = (
|
41
|
+
if (length = word.length) >= @min_char_size && !excluding_term?(word)
|
42
|
+
fuzziness = (length * @fuzzy_percent).round
|
41
43
|
fuzziness = [fuzziness, @max_fuzziness].min
|
42
|
-
"#{word}~#{fuzziness}"
|
44
|
+
"#{ word }~#{ fuzziness }"
|
43
45
|
else
|
44
46
|
word
|
45
47
|
end
|
46
48
|
end
|
47
49
|
|
48
|
-
def join_by_and(args = [])
|
49
|
-
(args = args.compact).size > 1 ? "(#{ args.join('+') })" : args[0]
|
50
|
-
end
|
51
|
-
|
52
50
|
def excluding_term?(word)
|
53
|
-
!!word
|
51
|
+
!!(EXCLUDING_TERMS =~ word)
|
54
52
|
end
|
55
53
|
|
56
54
|
end
|
@@ -10,24 +10,23 @@ module CloudSesame
|
|
10
10
|
end
|
11
11
|
|
12
12
|
def compile
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
13
|
+
{ query: "(#{
|
14
|
+
query
|
15
|
+
})#{
|
16
|
+
'|' << fuzziness.compile(query) if fuzziness
|
17
|
+
}#{
|
18
|
+
'|' << sloppiness.compile(query) if sloppiness
|
19
|
+
}" }
|
17
20
|
end
|
18
21
|
|
19
22
|
private
|
20
23
|
|
21
24
|
def fuzziness
|
22
|
-
context[:fuzziness]
|
25
|
+
context[:fuzziness]
|
23
26
|
end
|
24
27
|
|
25
28
|
def sloppiness
|
26
|
-
context[:sloppiness]
|
27
|
-
end
|
28
|
-
|
29
|
-
def join_by_or(args = [])
|
30
|
-
(args = args.compact).size > 1 ? "(#{ args.join('|') })" : args[0]
|
29
|
+
context[:sloppiness]
|
31
30
|
end
|
32
31
|
|
33
32
|
end
|
@@ -51,8 +51,8 @@ module CloudSesame
|
|
51
51
|
page,
|
52
52
|
sort,
|
53
53
|
return_field
|
54
|
-
].each_with_object({}) do |node,
|
55
|
-
|
54
|
+
].each_with_object({}) do |node, object|
|
55
|
+
object.merge!(node.compile || {})
|
56
56
|
end
|
57
57
|
|
58
58
|
if compiled[:filter_query].empty?
|
@@ -0,0 +1,23 @@
|
|
1
|
+
module CloudSesame
|
2
|
+
module Query
|
3
|
+
module Node
|
4
|
+
class Sloppiness
|
5
|
+
|
6
|
+
def initialize(value)
|
7
|
+
@value = value.to_i
|
8
|
+
end
|
9
|
+
|
10
|
+
def compile(string)
|
11
|
+
"\"#{ string }\"~#{ @value }" if more_than_one_word?(string)
|
12
|
+
end
|
13
|
+
|
14
|
+
private
|
15
|
+
|
16
|
+
def more_than_one_word?(string)
|
17
|
+
string.include?(' ')
|
18
|
+
end
|
19
|
+
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,76 @@
|
|
1
|
+
module CloudSesame
|
2
|
+
module Config
|
3
|
+
describe Credential do
|
4
|
+
|
5
|
+
describe '#initialize' do
|
6
|
+
it 'should accept access_key_id' do
|
7
|
+
credential = Credential.new(access_key_id: 123)
|
8
|
+
expect(credential.to_hash[:access_key_id]).to eq 123
|
9
|
+
end
|
10
|
+
it 'should accept access_key as an alias for access_key_id' do
|
11
|
+
credential = Credential.new(access_key: 123)
|
12
|
+
expect(credential.to_hash[:access_key_id]).to eq 123
|
13
|
+
end
|
14
|
+
it 'should accept secret_access_key' do
|
15
|
+
credential = Credential.new(secret_access_key: 'secret')
|
16
|
+
expect(credential.to_hash[:secret_access_key]).to eq 'secret'
|
17
|
+
end
|
18
|
+
it 'should accept secret_key as an alias for secret_access_key' do
|
19
|
+
credential = Credential.new(secret_key: 'secret')
|
20
|
+
expect(credential.to_hash[:secret_access_key]).to eq 'secret'
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
describe 'access_key_id' do
|
25
|
+
context 'getter' do
|
26
|
+
subject { Credential.new(access_key: 123) }
|
27
|
+
it 'should be defined' do
|
28
|
+
expect(subject).to respond_to(:access_key_id)
|
29
|
+
expect(subject.access_key_id).to eq 123
|
30
|
+
end
|
31
|
+
it 'should have an alias getter defined' do
|
32
|
+
expect(subject).to respond_to(:access_key)
|
33
|
+
expect(subject.access_key).to eq 123
|
34
|
+
end
|
35
|
+
end
|
36
|
+
context 'writer' do
|
37
|
+
let(:attributes) { subject.instance_variable_get(:@attributes) }
|
38
|
+
it 'should be defined' do
|
39
|
+
expect(subject).to respond_to(:access_key_id=)
|
40
|
+
expect{ subject.access_key_id = 123 }.to change{ attributes[:access_key_id] }.from(nil).to(123)
|
41
|
+
end
|
42
|
+
it 'should have an alias writer defined' do
|
43
|
+
expect(subject).to respond_to(:access_key=)
|
44
|
+
expect{ subject.access_key = 123 }.to change{ attributes[:access_key_id] }.from(nil).to(123)
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
describe 'secret_access_key' do
|
50
|
+
context 'getter' do
|
51
|
+
subject { Credential.new(secret_key: 123) }
|
52
|
+
it 'should be defined' do
|
53
|
+
expect(subject).to respond_to(:secret_access_key)
|
54
|
+
expect(subject.secret_access_key).to eq 123
|
55
|
+
end
|
56
|
+
it 'should have an alias getter defined' do
|
57
|
+
expect(subject).to respond_to(:secret_key)
|
58
|
+
expect(subject.secret_key).to eq 123
|
59
|
+
end
|
60
|
+
end
|
61
|
+
context 'writer' do
|
62
|
+
let(:attributes) { subject.instance_variable_get(:@attributes) }
|
63
|
+
it 'should be defined' do
|
64
|
+
expect(subject).to respond_to(:secret_access_key=)
|
65
|
+
expect{ subject.secret_access_key = 123 }.to change{ attributes[:secret_access_key] }.from(nil).to(123)
|
66
|
+
end
|
67
|
+
it 'should have an alias writer defined' do
|
68
|
+
expect(subject).to respond_to(:secret_key=)
|
69
|
+
expect{ subject.secret_key = 123 }.to change{ attributes[:secret_access_key] }.from(nil).to(123)
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
@@ -1,25 +1,290 @@
|
|
1
|
-
require 'spec_helper'
|
2
|
-
|
3
1
|
module CloudSesame
|
4
2
|
module Domain
|
5
3
|
describe Base do
|
6
4
|
|
7
|
-
|
8
|
-
|
5
|
+
class BaseSearchable; end
|
6
|
+
|
7
|
+
let(:searchable) { BaseSearchable }
|
8
|
+
subject { Base.new(searchable) }
|
9
9
|
|
10
|
-
|
11
|
-
it 'should
|
12
|
-
expect(subject.
|
10
|
+
shared_examples 'delegation to client' do |method|
|
11
|
+
it 'should be delegated to #client' do
|
12
|
+
expect(subject.client).to receive(method)
|
13
|
+
subject.send(method)
|
13
14
|
end
|
14
15
|
end
|
15
16
|
|
16
|
-
describe '#
|
17
|
+
describe '#config' do
|
18
|
+
it_behaves_like 'delegation to client', :config
|
19
|
+
end
|
20
|
+
|
21
|
+
describe '#caching_with' do
|
22
|
+
it_behaves_like 'delegation to client', :caching_with
|
23
|
+
end
|
24
|
+
|
25
|
+
describe '#builder' do
|
26
|
+
it 'should initialize an new builder each time' do
|
27
|
+
number = 2
|
28
|
+
expect(Query::Builder).to receive(:new).exactly(number).times.and_call_original
|
29
|
+
number.times { subject.builder }
|
30
|
+
end
|
31
|
+
it 'should receive the context from base when called' do
|
32
|
+
c = Context.new
|
33
|
+
expect(subject).to receive(:context).and_return(c)
|
34
|
+
subject.builder
|
35
|
+
end
|
36
|
+
it 'should receive the searchable form base when called' do
|
37
|
+
expect(subject).to receive(:searchable).and_return(searchable)
|
38
|
+
subject.builder
|
39
|
+
end
|
17
40
|
end
|
18
41
|
|
19
42
|
describe '#client' do
|
43
|
+
it 'should initialize and return a domain client with searchable' do
|
44
|
+
expect(Client).to receive(:new).with(searchable)
|
45
|
+
subject.client
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
describe '#context' do
|
50
|
+
it 'should initialize and return a context' do
|
51
|
+
expect(Context).to receive(:new)
|
52
|
+
subject.context
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
describe '#default_size' do
|
57
|
+
it 'should store the value to context page size' do
|
58
|
+
size = 99
|
59
|
+
subject.default_size size
|
60
|
+
expect(subject.context[:page][:size]).to eq(size)
|
61
|
+
end
|
62
|
+
it 'should convert value to integer' do
|
63
|
+
size = "99"
|
64
|
+
subject.default_size size
|
65
|
+
expect(subject.context[:page][:size]).to eq(size.to_i)
|
66
|
+
end
|
67
|
+
it 'should add page size to context' do
|
68
|
+
expect{ subject.default_size 99 }.to change{ subject.context[:page] }.from(nil).to(hash_including(size: 99))
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
describe '#define_sloppiness' do
|
73
|
+
it 'should create a sloppiness node' do
|
74
|
+
value = 3
|
75
|
+
expect(Query::Node::Sloppiness).to receive(:new).with(value)
|
76
|
+
subject.define_sloppiness(value)
|
77
|
+
end
|
78
|
+
it 'should add query sloppiness to context' do
|
79
|
+
expect{ subject.define_sloppiness 3 }.to change{ subject.context[:query] }.from(nil).to(hash_including(sloppiness: Query::Node::Sloppiness))
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
describe '#define_fuzziness' do
|
84
|
+
let(:block) { Proc.new {} }
|
85
|
+
it 'should create a fuzziness node' do
|
86
|
+
expect(Query::Node::Fuzziness).to receive(:new) { |&b|
|
87
|
+
expect(b).to eq block
|
88
|
+
}
|
89
|
+
subject.define_fuzziness(&block)
|
90
|
+
end
|
91
|
+
it 'should add query fuzziness to context' do
|
92
|
+
expect{ subject.define_fuzziness {} }.to change{ subject.context[:query] }.from(nil).to(hash_including(fuzziness: Query::Node::Fuzziness))
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
describe '#field' do
|
97
|
+
let(:field_name) { :name }
|
98
|
+
|
99
|
+
shared_examples 'and options :as is also passed' do
|
100
|
+
let(:real_name) { :text1 }
|
101
|
+
before { options[:as] = real_name }
|
102
|
+
it 'should use the real name as field name' do
|
103
|
+
subject.field field_name, options
|
104
|
+
expect(custom_options).to include(real_name)
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
108
|
+
context 'when options :query is passed in' do
|
109
|
+
let(:options) { { query: true } }
|
110
|
+
|
111
|
+
context 'and query is set to true' do
|
112
|
+
it 'should add query options for field' do
|
113
|
+
subject.field(field_name, options)
|
114
|
+
expect(subject.context[:query_options][:fields][field_name]).to eq({})
|
115
|
+
end
|
116
|
+
end
|
117
|
+
context 'and query has options' do
|
118
|
+
it 'should use the query options for field' do
|
119
|
+
query_options = { weight: 2 }
|
120
|
+
options[:query] = query_options
|
121
|
+
subject.field(field_name, options)
|
122
|
+
expect(subject.context[:query_options][:fields][field_name]).to eq(query_options)
|
123
|
+
end
|
124
|
+
end
|
125
|
+
|
126
|
+
it_behaves_like 'and options :as is also passed' do
|
127
|
+
let(:options) { { query: true } }
|
128
|
+
let(:custom_options) { subject.context[:query_options][:fields] }
|
129
|
+
end
|
130
|
+
end
|
131
|
+
|
132
|
+
context 'when options :facet is passed in' do
|
133
|
+
let(:options) {{ facet: true }}
|
134
|
+
context 'and facet is set to true' do
|
135
|
+
it 'should add facet options for field' do
|
136
|
+
subject.field(field_name, options)
|
137
|
+
expect(subject.context[:facet][field_name]).to eq({})
|
138
|
+
end
|
139
|
+
end
|
140
|
+
context 'and facet has options' do
|
141
|
+
it 'should use the facet options defined' do
|
142
|
+
facet_options = { size: 2 }
|
143
|
+
options[:facet] = facet_options
|
144
|
+
subject.field(field_name, options)
|
145
|
+
expect(subject.context[:facet][field_name]).to eq(facet_options)
|
146
|
+
end
|
147
|
+
end
|
148
|
+
|
149
|
+
it_behaves_like 'and options :as is also passed' do
|
150
|
+
let(:options) { { facet: true } }
|
151
|
+
let(:custom_options) { subject.context[:facet] }
|
152
|
+
end
|
153
|
+
end
|
154
|
+
|
155
|
+
context 'when options :as is passed in' do
|
156
|
+
let(:options) {{ override: false }}
|
157
|
+
context 'and filter_query fields contains an options for field :as' do
|
158
|
+
let(:original_option) {{ hello: 'world', override: true }}
|
159
|
+
let(:real_name) { :text1 }
|
160
|
+
before {
|
161
|
+
options[:as] = real_name
|
162
|
+
((subject.context[:filter_query] ||= {})[:fields] ||= {})[:text1] = original_option
|
163
|
+
}
|
164
|
+
it 'should delete the filter_query fields options for :as field' do
|
165
|
+
expect(subject.context[:filter_query][:fields]).to receive(:delete).with(real_name)
|
166
|
+
subject.field field_name, options
|
167
|
+
end
|
168
|
+
it 'should merge the original options into the new options' do
|
169
|
+
subject.field field_name, options
|
170
|
+
expect(subject.context[:filter_query][:fields][field_name]).to include({hello: 'world' })
|
171
|
+
end
|
172
|
+
it 'should not override the new options' do
|
173
|
+
subject.field field_name, options
|
174
|
+
expect(subject.context[:filter_query][:fields][field_name]).to include(override: false)
|
175
|
+
end
|
176
|
+
end
|
177
|
+
end
|
178
|
+
|
179
|
+
context 'when options :default is passed in' do
|
180
|
+
let(:proc) { Proc.new { } }
|
181
|
+
let(:options) {{ default: proc }}
|
182
|
+
it 'should remove the default lambda or proc from the options' do
|
183
|
+
expect(options).to receive(:delete).with(:default)
|
184
|
+
subject.field field_name, options
|
185
|
+
end
|
186
|
+
it 'should create a literal node using Query::Domain::Literal' do
|
187
|
+
domain = Query::Domain::Literal.new(field_name, {}, self)
|
188
|
+
expect(Query::Domain::Literal).to receive(:new).with(field_name, {}, self).and_return(domain)
|
189
|
+
expect(domain).to receive(:_eval) { |&block| expect(block).to eq proc }
|
190
|
+
subject.field field_name, options
|
191
|
+
end
|
192
|
+
|
193
|
+
context 'when default proc/lambda returns value' do
|
194
|
+
let(:proc) { Proc.new { "name" } }
|
195
|
+
it 'should store the literal node in filter query defaults of context' do
|
196
|
+
node = Query::Domain::Literal.new(field_name, {}, self)._eval(&proc)
|
197
|
+
allow_any_instance_of(Query::Domain::Literal).to receive(:_eval).and_return(node)
|
198
|
+
expect{ subject.field field_name, options }.to change{ subject.context[:filter_query] }.from(nil).to(hash_including(defaults: include(node)))
|
199
|
+
end
|
200
|
+
end
|
201
|
+
|
202
|
+
context 'when default proc/lambda returns nothing' do
|
203
|
+
it 'should not create any literal node in filter query defaults of context' do
|
204
|
+
node = Query::Domain::Literal.new(field_name, {}, self)._eval(&proc)
|
205
|
+
allow_any_instance_of(Query::Domain::Literal).to receive(:_eval).and_return(node)
|
206
|
+
expect{ subject.field field_name, options }.to_not change{ subject.send(:filter_query_defaults) }.from([])
|
207
|
+
end
|
208
|
+
end
|
209
|
+
end
|
210
|
+
|
211
|
+
it 'should create an field accessor' do
|
212
|
+
field_name = :an_indexed_field
|
213
|
+
expect{ subject.field field_name, {} }.to change{ Query::DSL::FieldAccessors.instance_methods }.by([field_name])
|
214
|
+
end
|
215
|
+
|
216
|
+
context 'when options is passed in' do
|
217
|
+
let(:options) {{ hello: "world" }}
|
218
|
+
it 'should store the options in filter query fields' do
|
219
|
+
subject.field field_name, options
|
220
|
+
expect(subject.context[:filter_query][:fields][field_name]).to eq options
|
221
|
+
end
|
222
|
+
end
|
223
|
+
|
224
|
+
context 'when no options is passed in' do
|
225
|
+
it 'should create a options in filter query fields' do
|
226
|
+
subject.field field_name
|
227
|
+
expect(subject.context[:filter_query][:fields][field_name]).to eq({})
|
228
|
+
end
|
229
|
+
end
|
20
230
|
end
|
21
231
|
|
22
|
-
describe '#
|
232
|
+
describe '#scope' do
|
233
|
+
let(:scope_name) { :test_scope }
|
234
|
+
context 'when no proc or block is given' do
|
235
|
+
it 'should not add anything to filter query scopes' do
|
236
|
+
expect{ subject.scope(scope_name) }.to_not change{ subject.send(:filter_query_scopes) }
|
237
|
+
end
|
238
|
+
end
|
239
|
+
context 'when proc is given' do
|
240
|
+
let(:proc) { Proc.new {} }
|
241
|
+
it 'should add the proc to the filter query scopes' do
|
242
|
+
expect{ subject.scope(scope_name, proc) }.to change{ subject.send(:filter_query_scopes) }.from({}).to(hash_including( scope_name => proc))
|
243
|
+
end
|
244
|
+
end
|
245
|
+
context 'when block is given' do
|
246
|
+
let(:block) { -> { "block" } }
|
247
|
+
it 'should add the block to the filter query scopes' do
|
248
|
+
expect{ subject.scope(scope_name, &block) }.to change{ subject.send(:filter_query_scopes) }.from({}).to(scope_name => eq(block))
|
249
|
+
end
|
250
|
+
end
|
251
|
+
end
|
252
|
+
|
253
|
+
context 'when method missing' do
|
254
|
+
let(:undefined_method) { :undefined_method }
|
255
|
+
let(:args) { [1,2,3] }
|
256
|
+
context 'and builder resposnds to the method' do
|
257
|
+
let(:builder) { subject.builder }
|
258
|
+
before { allow(subject).to receive(:builder).and_return(builder) }
|
259
|
+
it 'should check if builder responds to the method' do
|
260
|
+
expect(builder).to receive(:respond_to?).with(undefined_method)
|
261
|
+
subject.undefined_method rescue nil
|
262
|
+
end
|
263
|
+
it 'should call method on builder with parameters passed in' do
|
264
|
+
allow(builder).to receive(:respond_to?).and_return(true)
|
265
|
+
expect(builder).to receive(undefined_method).with(*args)
|
266
|
+
subject.undefined_method(*args)
|
267
|
+
end
|
268
|
+
end
|
269
|
+
|
270
|
+
context 'and builder not responds to the methods but searchable do' do
|
271
|
+
it 'should check if callee existing and responds to the method' do
|
272
|
+
expect(searchable).to receive(:respond_to?).with(undefined_method)
|
273
|
+
subject.undefined_method rescue nil
|
274
|
+
end
|
275
|
+
it 'should call method on searchable with parameters passed in' do
|
276
|
+
allow(searchable).to receive(:respond_to?).and_return(true)
|
277
|
+
expect(searchable).to receive(undefined_method).with(*args)
|
278
|
+
subject.undefined_method(*args)
|
279
|
+
end
|
280
|
+
end
|
281
|
+
|
282
|
+
context 'both builder and searchable do not respond to the method' do
|
283
|
+
it 'should raise NoMethodError on subject' do
|
284
|
+
expect{ subject.undefined_method }.to raise_error(NoMethodError)
|
285
|
+
end
|
286
|
+
end
|
287
|
+
|
23
288
|
end
|
24
289
|
|
25
290
|
end
|