rsolr 1.0.8 → 1.1.2

Sign up to get free protection for your applications and to get access to all the features.
data/rsolr.gemspec CHANGED
@@ -1,13 +1,13 @@
1
1
  # -*- encoding: utf-8 -*-
2
2
  $:.push File.expand_path("../lib", __FILE__)
3
3
 
4
- require "rsolr"
4
+ require "rsolr/version"
5
5
 
6
6
  Gem::Specification.new do |s|
7
7
  s.name = "rsolr"
8
8
  s.summary = "A Ruby client for Apache Solr"
9
9
  s.description = %q{RSolr aims to provide a simple and extensible library for working with Solr}
10
- s.version = RSolr.version
10
+ s.version = RSolr::VERSION
11
11
  s.authors = ["Antoine Latter", "Dmitry Lihachev",
12
12
  "Lucas Souza", "Peter Kieltyka",
13
13
  "Rob Di Marco", "Magnus Bergmark",
@@ -18,17 +18,24 @@ Gem::Specification.new do |s|
18
18
  "Mat Brown", "Shairon Toledo",
19
19
  "Matthew Rudy", "Fouad Mardini",
20
20
  "Jeremy Hinegardner", "Nathan Witmer",
21
+ "Naomi Dushay",
21
22
  "\"shima\""]
22
23
  s.email = ["goodieboy@gmail.com"]
23
- s.homepage = "https://github.com/mwmitchell/rsolr"
24
+ s.license = 'Apache-2.0'
25
+ s.homepage = "https://github.com/rsolr/rsolr"
24
26
  s.rubyforge_project = "rsolr"
25
27
  s.files = `git ls-files`.split("\n")
26
28
  s.test_files = `git ls-files -- {spec}/*`.split("\n")
27
29
  s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
28
30
  s.require_paths = ["lib"]
29
31
 
32
+ s.required_ruby_version = '>= 1.9.3'
33
+
30
34
  s.add_dependency 'builder', '>= 2.1.2'
31
- s.add_development_dependency 'rake', '~> 0.9.2'
32
- s.add_development_dependency 'rdoc', '~> 3.9.4'
33
- s.add_development_dependency 'rspec', '~> 2.6.0'
34
- end
35
+ s.add_development_dependency 'activesupport'
36
+ s.add_development_dependency 'nokogiri', '>= 1.4.0'
37
+ s.add_development_dependency 'rake', '~> 10.0'
38
+ s.add_development_dependency 'rdoc', '~> 4.0'
39
+ s.add_development_dependency 'rspec', '~> 3.0'
40
+ s.add_development_dependency 'solr_wrapper'
41
+ end
@@ -1,18 +1,23 @@
1
1
  require 'spec_helper'
2
+ # @deprecated remove this module's specs when we remove the method (duh)
2
3
  describe "RSolr::Char" do
3
4
 
4
5
  let(:char){Object.new.extend RSolr::Char}
5
6
 
7
+ # deprecated as of 2015-02, as it is incorrect Solr escaping.
8
+ # instead, use RSolr.solr_escape
9
+ # commented out as it gives a mess of deprecation warnings
10
+ =begin
6
11
  it 'should escape everything that is not a word with \\' do
7
12
  (0..255).each do |ascii|
8
13
  chr = ascii.chr
9
14
  esc = char.escape(chr)
10
15
  if chr =~ /\W/
11
- esc.to_s.should == "\\#{chr}"
16
+ expect(esc.to_s).to eq("\\#{chr}")
12
17
  else
13
- esc.to_s.should == chr
18
+ expect(esc.to_s).to eq(chr)
14
19
  end
15
20
  end
16
21
  end
17
-
22
+ =end
18
23
  end
@@ -1,6 +1,6 @@
1
1
  require 'spec_helper'
2
2
  describe "RSolr::Client" do
3
-
3
+
4
4
  module ClientHelper
5
5
  def client
6
6
  @client ||= (
@@ -8,58 +8,122 @@ describe "RSolr::Client" do
8
8
  RSolr::Client.new connection, :url => "http://localhost:9999/solr", :read_timeout => 42, :open_timeout=>43
9
9
  )
10
10
  end
11
+
12
+ def client_with_proxy
13
+ @client_with_proxy ||= (
14
+ connection = RSolr::Connection.new
15
+ RSolr::Client.new connection, :url => "http://localhost:9999/solr", :proxy => 'http://localhost:8080', :read_timeout => 42, :open_timeout=>43
16
+ )
17
+ end
11
18
  end
12
-
19
+
13
20
  context "initialize" do
14
21
  it "should accept whatevs and set it as the @connection" do
15
- RSolr::Client.new(:whatevs).connection.should == :whatevs
22
+ expect(RSolr::Client.new(:whatevs).connection).to eq(:whatevs)
23
+ end
24
+
25
+ it "should use :update_path from options" do
26
+ client = RSolr::Client.new(:whatevs, { update_path: 'update_test' })
27
+ expect(client.update_path).to eql('update_test')
28
+ end
29
+
30
+ it "should use 'update' for update_path by default" do
31
+ client = RSolr::Client.new(:whatevs)
32
+ expect(client.update_path).to eql('update')
33
+ end
34
+
35
+ it "should use :proxy from options" do
36
+ client = RSolr::Client.new(:whatevs, { proxy: 'http://my.proxy/' })
37
+ expect(client.proxy.to_s).to eql('http://my.proxy/')
38
+ end
39
+
40
+ it "should use 'nil' for proxy by default" do
41
+ client = RSolr::Client.new(:whatevs)
42
+ expect(client.proxy).to be_nil
43
+ end
44
+
45
+ it "should use 'false' for proxy if passed 'false'" do
46
+ client = RSolr::Client.new(:whatevs, { proxy: false })
47
+ expect(client.proxy).to eq(false)
16
48
  end
17
49
  end
18
-
50
+
19
51
  context "send_and_receive" do
20
52
  include ClientHelper
21
53
  it "should forward these method calls the #connection object" do
22
54
  [:get, :post, :head].each do |meth|
23
- client.connection.should_receive(:execute).
24
- and_return({:status => 200, :body => "{}", :headers => {}})
55
+ expect(client.connection).to receive(:execute).
56
+ and_return({:status => 200, :body => "{}", :headers => {}})
25
57
  client.send_and_receive '', :method => meth, :params => {}, :data => nil, :headers => {}
26
58
  end
27
59
  end
28
60
 
29
61
  it "should be timeout aware" do
30
62
  [:get, :post, :head].each do |meth|
31
- client.connection.should_receive(:execute).with(client, hash_including(:read_timeout => 42, :open_timeout=>43))
63
+ expect(client.connection).to receive(:execute).with(client, hash_including(:read_timeout => 42, :open_timeout=>43))
32
64
  client.send_and_receive '', :method => meth, :params => {}, :data => nil, :headers => {}
33
65
  end
34
66
  end
35
67
  end
36
68
 
69
+ context "execute" do
70
+ include ClientHelper
71
+ let :request_context do
72
+ {
73
+ :method => :post,
74
+ :params => {},
75
+ :data => nil,
76
+ :headers => {},
77
+ :path => '',
78
+ :uri => client.base_uri,
79
+ :retry_503 => 1
80
+ }
81
+ end
82
+ it "should retry 503s if requested" do
83
+ expect(client.connection).to receive(:execute).exactly(2).times.and_return(
84
+ {:status => 503, :body => "{}", :headers => {'Retry-After' => 0}},
85
+ {:status => 200, :body => "{}", :headers => {}}
86
+ )
87
+ client.execute request_context
88
+ end
89
+ it "should not retry a 503 if the retry-after is too large" do
90
+ expect(client.connection).to receive(:execute).exactly(1).times.and_return(
91
+ {:status => 503, :body => "{}", :headers => {'Retry-After' => 10}}
92
+ )
93
+ expect {
94
+ Timeout.timeout(0.5) do
95
+ client.execute({:retry_after_limit => 0}.merge(request_context))
96
+ end
97
+ }.to raise_error(RSolr::Error::Http)
98
+ end
99
+ end
100
+
37
101
  context "post" do
38
102
  include ClientHelper
39
103
  it "should pass the expected params to the connection's #execute method" do
40
104
  request_opts = {:data => "the data", :method=>:post, :headers => {"Content-Type" => "text/plain"}}
41
- client.connection.should_receive(:execute).
105
+ expect(client.connection).to receive(:execute).
42
106
  with(client, hash_including(request_opts)).
43
107
  and_return(
44
108
  :body => "",
45
109
  :status => 200,
46
110
  :headers => {"Content-Type"=>"text/plain"}
47
111
  )
48
- client.post "update", request_opts
112
+ client.post "update", request_opts
49
113
  end
50
114
  end
51
-
115
+
52
116
  context "xml" do
53
117
  include ClientHelper
54
118
  it "should return an instance of RSolr::Xml::Generator" do
55
- client.xml.should be_a RSolr::Xml::Generator
119
+ expect(client.xml).to be_a RSolr::Xml::Generator
56
120
  end
57
121
  end
58
-
122
+
59
123
  context "add" do
60
124
  include ClientHelper
61
125
  it "should send xml to the connection's #post method" do
62
- client.connection.should_receive(:execute).
126
+ expect(client.connection).to receive(:execute).
63
127
  with(
64
128
  client, hash_including({
65
129
  :path => "update",
@@ -67,23 +131,23 @@ describe "RSolr::Client" do
67
131
  :method => :post,
68
132
  :data => "<xml/>"
69
133
  })
70
- ).
71
- and_return(
72
- :body => "",
73
- :status => 200,
74
- :headers => {"Content-Type"=>"text/xml"}
75
- )
76
- client.xml.should_receive(:add).
134
+ ).
135
+ and_return(
136
+ :body => "",
137
+ :status => 200,
138
+ :headers => {"Content-Type"=>"text/xml"}
139
+ )
140
+ expect(client.xml).to receive(:add).
77
141
  with({:id=>1}, {:commitWith=>10}).
78
- and_return("<xml/>")
142
+ and_return("<xml/>")
79
143
  client.add({:id=>1}, :add_attributes => {:commitWith=>10})
80
144
  end
81
145
  end
82
-
146
+
83
147
  context "update" do
84
148
  include ClientHelper
85
149
  it "should send data to the connection's #post method" do
86
- client.connection.should_receive(:execute).
150
+ expect(client.connection).to receive(:execute).
87
151
  with(
88
152
  client, hash_including({
89
153
  :path => "update",
@@ -91,21 +155,33 @@ describe "RSolr::Client" do
91
155
  :method => :post,
92
156
  :data => "<optimize/>"
93
157
  })
94
- ).
95
- and_return(
96
- :body => "",
97
- :status => 200,
98
- :headers => {"Content-Type"=>"text/xml"}
99
- )
158
+ ).
159
+ and_return(
160
+ :body => "",
161
+ :status => 200,
162
+ :headers => {"Content-Type"=>"text/xml"}
163
+ )
100
164
  client.update(:data => "<optimize/>")
101
165
  end
166
+
167
+ it "should use #update_path" do
168
+ expect(client).to receive(:post).with('update_test', any_args)
169
+ expect(client).to receive(:update_path).and_return('update_test')
170
+ client.update({})
171
+ end
172
+
173
+ it "should use path from opts" do
174
+ expect(client).to receive(:post).with('update_opts', any_args)
175
+ allow(client).to receive(:update_path).and_return('update_test')
176
+ client.update({path: 'update_opts'})
177
+ end
102
178
  end
103
-
179
+
104
180
  context "post based helper methods:" do
105
181
  include ClientHelper
106
182
  [:commit, :optimize, :rollback].each do |meth|
107
183
  it "should send a #{meth} message to the connection's #post method" do
108
- client.connection.should_receive(:execute).
184
+ expect(client.connection).to receive(:execute).
109
185
  with(
110
186
  client, hash_including({
111
187
  :path => "update",
@@ -113,21 +189,21 @@ describe "RSolr::Client" do
113
189
  :method => :post,
114
190
  :data => "<?xml version=\"1.0\" encoding=\"UTF-8\"?><#{meth}/>"
115
191
  })
116
- ).
117
- and_return(
118
- :body => "",
119
- :status => 200,
120
- :headers => {"Content-Type"=>"text/xml"}
121
- )
192
+ ).
193
+ and_return(
194
+ :body => "",
195
+ :status => 200,
196
+ :headers => {"Content-Type"=>"text/xml"}
197
+ )
122
198
  client.send meth
123
199
  end
124
200
  end
125
201
  end
126
-
202
+
127
203
  context "delete_by_id" do
128
204
  include ClientHelper
129
205
  it "should send data to the connection's #post method" do
130
- client.connection.should_receive(:execute).
206
+ expect(client.connection).to receive(:execute).
131
207
  with(
132
208
  client, hash_including({
133
209
  :path => "update",
@@ -135,20 +211,20 @@ describe "RSolr::Client" do
135
211
  :method => :post,
136
212
  :data => "<?xml version=\"1.0\" encoding=\"UTF-8\"?><delete><id>1</id></delete>"
137
213
  })
138
- ).
139
- and_return(
140
- :body => "",
141
- :status => 200,
142
- :headers => {"Content-Type"=>"text/xml"}
143
- )
214
+ ).
215
+ and_return(
216
+ :body => "",
217
+ :status => 200,
218
+ :headers => {"Content-Type"=>"text/xml"}
219
+ )
144
220
  client.delete_by_id 1
145
221
  end
146
222
  end
147
-
223
+
148
224
  context "delete_by_query" do
149
225
  include ClientHelper
150
226
  it "should send data to the connection's #post method" do
151
- client.connection.should_receive(:execute).
227
+ expect(client.connection).to receive(:execute).
152
228
  with(
153
229
  client, hash_including({
154
230
  :path => "update",
@@ -156,68 +232,126 @@ describe "RSolr::Client" do
156
232
  :method => :post,
157
233
  :data => "<?xml version=\"1.0\" encoding=\"UTF-8\"?><delete><query fq=\"category:&quot;trash&quot;\"/></delete>"
158
234
  })
159
- ).
160
- and_return(
161
- :body => "",
162
- :status => 200,
163
- :headers => {"Content-Type"=>"text/xml"}
164
- )
235
+ ).
236
+ and_return(
237
+ :body => "",
238
+ :status => 200,
239
+ :headers => {"Content-Type"=>"text/xml"}
240
+ )
165
241
  client.delete_by_query :fq => "category:\"trash\""
166
242
  end
167
243
  end
168
-
244
+
169
245
  context "adapt_response" do
170
246
  include ClientHelper
171
247
  it 'should not try to evaluate ruby when the :qt is not :ruby' do
172
- body = '{:time=>"NOW"}'
248
+ body = '{"time"=>"NOW"}'
173
249
  result = client.adapt_response({:params=>{}}, {:status => 200, :body => body, :headers => {}})
174
- result.should == body
250
+ expect(result).to eq(body)
175
251
  end
176
-
252
+
177
253
  it 'should evaluate ruby responses when the :wt is :ruby' do
178
- body = '{:time=>"NOW"}'
254
+ body = '{"time"=>"NOW"}'
179
255
  result = client.adapt_response({:params=>{:wt=>:ruby}}, {:status => 200, :body => body, :headers => {}})
180
- result.should == {:time=>"NOW"}
256
+ expect(result).to eq({"time"=>"NOW"})
181
257
  end
182
-
258
+
259
+ it 'should evaluate json responses when the :wt is :json' do
260
+ body = '{"time": "NOW"}'
261
+ result = client.adapt_response({:params=>{:wt=>:json}}, {:status => 200, :body => body, :headers => {}})
262
+ if defined? JSON
263
+ expect(result).to eq({"time"=>"NOW"})
264
+ else
265
+ # ruby 1.8 without the JSON gem
266
+ expect(result).to eq('{"time": "NOW"}')
267
+ end
268
+ end
269
+
270
+ it 'should return a response for a head request' do
271
+ result = client.adapt_response({:method=>:head,:params=>{}}, {:status => 200, :body => nil, :headers => {}})
272
+ expect(result.response[:status]).to eq 200
273
+ end
274
+
183
275
  it "ought raise a RSolr::Error::InvalidRubyResponse when the ruby is indeed frugged, or even fruggified" do
184
- lambda {
276
+ expect {
185
277
  client.adapt_response({:params=>{:wt => :ruby}}, {:status => 200, :body => "<woops/>", :headers => {}})
186
- }.should raise_error RSolr::Error::InvalidRubyResponse
278
+ }.to raise_error RSolr::Error::InvalidRubyResponse
187
279
  end
188
-
280
+
189
281
  end
190
-
282
+
283
+ context "indifferent access" do
284
+ include ClientHelper
285
+ it "should raise a RuntimeError if the #with_indifferent_access extension isn't loaded" do
286
+ hide_const("HashWithIndifferentAccess")
287
+ body = "{'foo'=>'bar'}"
288
+ result = client.adapt_response({:params=>{:wt=>:ruby}}, {:status => 200, :body => body, :headers => {}})
289
+ expect { result.with_indifferent_access }.to raise_error RuntimeError
290
+ end
291
+
292
+ it "should provide indifferent access" do
293
+ require 'active_support/core_ext/hash/indifferent_access'
294
+ body = "{'foo'=>'bar'}"
295
+ result = client.adapt_response({:params=>{:wt=>:ruby}}, {:status => 200, :body => body, :headers => {}})
296
+ indifferent_result = result.with_indifferent_access
297
+
298
+ expect(result).to be_a(RSolr::Response)
299
+ expect(result['foo']).to eq('bar')
300
+ expect(result[:foo]).to be_nil
301
+
302
+ expect(indifferent_result).to be_a(RSolr::Response)
303
+ expect(indifferent_result['foo']).to eq('bar')
304
+ expect(indifferent_result[:foo]).to eq('bar')
305
+ end
306
+ end
307
+
191
308
  context "build_request" do
192
309
  include ClientHelper
193
- it 'should return a request context array' do
194
- result = client.build_request('select',
195
- :method => :post,
196
- :params => {:q=>'test', :fq=>[0,1]},
197
- :data => "data",
198
- :headers => {}
199
- )
200
- [/fq=0/, /fq=1/, /q=test/, /wt=ruby/].each do |pattern|
201
- result[:query].should match pattern
310
+ let(:data) { 'data' }
311
+ let(:params) { { q: 'test', fq: [0,1] } }
312
+ let(:options) { { method: :post, params: params, data: data, headers: {} } }
313
+ subject { client.build_request('select', options) }
314
+
315
+ context "when params are symbols" do
316
+ it 'should return a request context array' do
317
+ [/fq=0/, /fq=1/, /q=test/, /wt=ruby/].each do |pattern|
318
+ expect(subject[:query]).to match pattern
319
+ end
320
+ expect(subject[:data]).to eq("data")
321
+ expect(subject[:headers]).to eq({})
322
+ end
323
+ end
324
+
325
+ context "when params are strings" do
326
+ let(:params) { { 'q' => 'test', 'wt' => 'json' } }
327
+ it 'should return a request context array' do
328
+ expect(subject[:query]).to eq 'q=test&wt=json'
329
+ expect(subject[:data]).to eq("data")
330
+ expect(subject[:headers]).to eq({})
331
+ end
332
+ end
333
+
334
+ context "when a Hash is passed in as data" do
335
+ let(:data) { { q: 'test', fq: [0,1] } }
336
+ let(:options) { { method: :post, data: data, headers: {} } }
337
+
338
+ it "sets the Content-Type header to application/x-www-form-urlencoded; charset=UTF-8" do
339
+ expect(subject[:query]).to eq("wt=ruby")
340
+ [/fq=0/, /fq=1/, /q=test/].each do |pattern|
341
+ expect(subject[:data]).to match pattern
342
+ end
343
+ expect(subject[:data]).not_to match /wt=ruby/
344
+ expect(subject[:headers]).to eq({"Content-Type" => "application/x-www-form-urlencoded; charset=UTF-8"})
202
345
  end
203
- result[:data].should == "data"
204
- result[:headers].should == {}
205
346
  end
206
-
207
- it "should set the Content-Type header to application/x-www-form-urlencoded; charset=UTF-8 if a hash is passed in to the data arg" do
208
- result = client.build_request('select',
347
+
348
+ it "should properly handle proxy configuration" do
349
+ result = client_with_proxy.build_request('select',
209
350
  :method => :post,
210
351
  :data => {:q=>'test', :fq=>[0,1]},
211
352
  :headers => {}
212
353
  )
213
- result[:query].should == "wt=ruby"
214
- [/fq=0/, /fq=1/, /q=test/].each do |pattern|
215
- result[:data].should match pattern
216
- end
217
- result[:data].should_not match /wt=ruby/
218
- result[:headers].should == {"Content-Type" => "application/x-www-form-urlencoded; charset=UTF-8"}
219
- end
220
-
354
+ expect(result[:uri].to_s).to match /^http:\/\/localhost:9999\/solr\//
355
+ end
221
356
  end
222
-
223
357
  end