rsolr 1.0.8 → 1.1.2

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.
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