rsolr 0.12.1 → 0.13.0.pre
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/README.rdoc +16 -6
- data/VERSION +1 -1
- data/lib/rsolr.rb +3 -0
- data/lib/rsolr/client.rb +29 -9
- data/lib/rsolr/connection.rb +1 -1
- data/lib/rsolr/connection/httpable.rb +101 -0
- data/lib/rsolr/connection/net_http.rb +12 -30
- data/lib/rsolr/connection/utils.rb +7 -6
- data/spec/api/client_spec.rb +26 -4
- data/spec/api/connection/httpable_spec.rb +157 -0
- data/spec/api/connection/net_http_spec.rb +23 -34
- data/spec/api/connection/utils_spec.rb +3 -3
- data/spec/integration/http_errors_spec.rb +12 -0
- metadata +28 -13
- data/lib/rsolr/connection/requestable.rb +0 -43
- data/spec/api/connection/requestable_spec.rb +0 -92
data/README.rdoc
CHANGED
@@ -9,16 +9,21 @@ A simple, extensible Ruby client for Apache Solr.
|
|
9
9
|
== Example:
|
10
10
|
require 'rubygems'
|
11
11
|
require 'rsolr'
|
12
|
+
|
13
|
+
# Direct connection
|
12
14
|
solr = RSolr.connect :url=>'http://solrserver.com'
|
13
15
|
|
16
|
+
# Connecting over a proxy server
|
17
|
+
solr = RSolr.connect :url=>'http://solrserver.com', :proxy=>'http://user:pass@proxy.example.com:8080'
|
18
|
+
|
14
19
|
# send a request to /select
|
15
|
-
response =
|
20
|
+
response = solr.select :q=>'*:*'
|
16
21
|
|
17
22
|
# send a request to a custom request handler; /catalog
|
18
|
-
response =
|
23
|
+
response = solr.request '/catalog', :q=>'*:*'
|
19
24
|
|
20
25
|
# alternative to above:
|
21
|
-
response =
|
26
|
+
response = solr.catalog :q=>'*:*'
|
22
27
|
|
23
28
|
== Querying
|
24
29
|
Use the #select method to send requests to the /select handler:
|
@@ -107,6 +112,7 @@ The raw is a hash that contains the generated params, url, path, post data, head
|
|
107
112
|
* {RSolr Google Group}[http://groups.google.com/group/rsolr] -- The RSolr discussion group
|
108
113
|
* {rsolr-ext}[http://github.com/mwmitchell/rsolr-ext] -- An extension kit for RSolr
|
109
114
|
* {rsolr-direct}[http://github.com/mwmitchell/rsolr-direct] -- JRuby direct connection for RSolr
|
115
|
+
* {rsolr-nokogiri}[http://github.com/mwmitchell/rsolr-nokogiri] -- Gives RSolr Nokogiri for XML generation.
|
110
116
|
* {SunSpot}[http://github.com/outoftime/sunspot] -- An awesome Solr DSL, built with RSolr
|
111
117
|
* {Blacklight}[http://blacklightopac.org] -- A "next generation" Library OPAC, built with RSolr
|
112
118
|
* {java_bin}[http://github.com/kennyj/java_bin] -- Provides javabin/binary parsing for RSolr
|
@@ -122,8 +128,12 @@ The raw is a hash that contains the generated params, url, path, post data, head
|
|
122
128
|
* Send me a pull request. Bonus points for topic branches.
|
123
129
|
|
124
130
|
==Contributors
|
125
|
-
*
|
131
|
+
* Lorenzo Riccucci
|
132
|
+
* Mike Perham
|
126
133
|
* Mat Brown
|
127
|
-
*
|
134
|
+
* Shairon Toledo
|
128
135
|
* Matthew Rudy
|
129
|
-
* Fouad Mardini
|
136
|
+
* Fouad Mardini
|
137
|
+
* Jeremy Hinegardner
|
138
|
+
* Nathan Witmer
|
139
|
+
* Craig Smith
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.
|
1
|
+
0.13.0.pre
|
data/lib/rsolr.rb
CHANGED
data/lib/rsolr/client.rb
CHANGED
@@ -42,18 +42,38 @@ class RSolr::Client
|
|
42
42
|
# update using an array
|
43
43
|
# solr.update([{:id=>1, :name=>'one'}, {:id=>2, :name=>'two'}])
|
44
44
|
#
|
45
|
-
def add(doc, &block)
|
46
|
-
update message.add(doc, &block)
|
45
|
+
def add(doc, params={}, &block)
|
46
|
+
update message.add(doc, params, &block)
|
47
47
|
end
|
48
48
|
|
49
|
-
# send
|
50
|
-
|
51
|
-
|
49
|
+
# send "commit" message with options
|
50
|
+
#
|
51
|
+
# Options recognized by solr
|
52
|
+
#
|
53
|
+
# :maxSegments => N - optimizes down to at most N number of segments
|
54
|
+
# :waitFlush => true|false - do not return until changes are flushed to disk
|
55
|
+
# :waitSearcher => true|false - do not return until a new searcher is opened and registered
|
56
|
+
# :expungeDeletes => true|false - merge segments with deletes into other segments #NOT
|
57
|
+
#
|
58
|
+
# *NOTE* :expungeDeletes is Solr 1.4 only
|
59
|
+
#
|
60
|
+
def commit( options = {} )
|
61
|
+
update message.commit( options )
|
52
62
|
end
|
53
63
|
|
54
|
-
# send
|
55
|
-
|
56
|
-
|
64
|
+
# send "optimize" message with options.
|
65
|
+
#
|
66
|
+
# Options recognized by solr
|
67
|
+
#
|
68
|
+
# :maxSegments => N - optimizes down to at most N number of segments
|
69
|
+
# :waitFlush => true|false - do not return until changes are flushed to disk
|
70
|
+
# :waitSearcher => true|false - do not return until a new searcher is opened and registered
|
71
|
+
# :expungeDeletes => true|false - merge segments with deletes into other segments
|
72
|
+
#
|
73
|
+
# *NOTE* :expungeDeletes is Solr 1.4 only
|
74
|
+
#
|
75
|
+
def optimize( options = {} )
|
76
|
+
update message.optimize( options )
|
57
77
|
end
|
58
78
|
|
59
79
|
# send </rollback>
|
@@ -77,7 +97,7 @@ class RSolr::Client
|
|
77
97
|
end
|
78
98
|
|
79
99
|
# shortcut to RSolr::Message::Generator
|
80
|
-
def message
|
100
|
+
def message
|
81
101
|
@message ||= RSolr::Message::Generator.new
|
82
102
|
end
|
83
103
|
|
data/lib/rsolr/connection.rb
CHANGED
@@ -0,0 +1,101 @@
|
|
1
|
+
# A module that defines the interface and top-level logic for http based connection classes.
|
2
|
+
# Httpable provides URL parsing and handles proxy logic.
|
3
|
+
|
4
|
+
module RSolr::Connection::Httpable
|
5
|
+
|
6
|
+
include RSolr::Connection::Utils
|
7
|
+
|
8
|
+
attr_reader :opts, :uri, :proxy
|
9
|
+
|
10
|
+
# opts can have:
|
11
|
+
# :url => 'http://localhost:8080/solr'
|
12
|
+
def initialize opts={}
|
13
|
+
opts[:url] ||= 'http://127.0.0.1:8983/solr'
|
14
|
+
@opts = opts
|
15
|
+
@uri = URI.parse opts[:url]
|
16
|
+
@proxy = URI.parse opts[:proxy] if opts[:proxy]
|
17
|
+
end
|
18
|
+
|
19
|
+
# send a request to the connection
|
20
|
+
# request '/select', :q=>'*:*'
|
21
|
+
#
|
22
|
+
# request '/update', {:wt=>:xml}, '</commit>'
|
23
|
+
#
|
24
|
+
# force a post where the post body is the param query
|
25
|
+
# request '/update', "<optimize/>", :method=>:post
|
26
|
+
#
|
27
|
+
def request path, params={}, *extra
|
28
|
+
extra = extra.dup
|
29
|
+
opts = extra[-1].kind_of?(Hash) ? extra.pop : {}
|
30
|
+
data = extra[0]
|
31
|
+
|
32
|
+
context = create_http_context path, params, data, opts
|
33
|
+
|
34
|
+
error = nil
|
35
|
+
|
36
|
+
begin
|
37
|
+
|
38
|
+
# if data is being sent, this is a POST
|
39
|
+
if context[:data]
|
40
|
+
response = self.post context[:path], context[:data], context[:headers]
|
41
|
+
# use POST if :method => :post
|
42
|
+
elsif opts[:method] == :post
|
43
|
+
response = self.post context[:path], context[:query], context[:headers]
|
44
|
+
# GET
|
45
|
+
else
|
46
|
+
response = self.get context[:path]
|
47
|
+
end
|
48
|
+
|
49
|
+
# spray out our response into variables...
|
50
|
+
body, status_code, message = response
|
51
|
+
|
52
|
+
# merge the response into the http context
|
53
|
+
context.merge!(:body => body, :status_code => status_code, :message => message)
|
54
|
+
|
55
|
+
rescue
|
56
|
+
# throw RequestError?
|
57
|
+
context[:message] = $!.to_s
|
58
|
+
end
|
59
|
+
|
60
|
+
# if no :message but a non-200, throw a "SolrRequestError" ?
|
61
|
+
unless context[:status_code] == 200
|
62
|
+
error = context[:message] || "Non-200 Response Status Code"
|
63
|
+
raise RSolr::RequestError.new("#{error} -> #{context.inspect}")
|
64
|
+
end
|
65
|
+
|
66
|
+
context
|
67
|
+
end
|
68
|
+
|
69
|
+
# -> should this stuff be in a "ReqResContext" class? ->
|
70
|
+
|
71
|
+
# creates a Hash based "context"
|
72
|
+
# that contains all of the information sent to Solr
|
73
|
+
# The keys are:
|
74
|
+
# :host, :path, :params, :query, :data, :headers
|
75
|
+
def create_http_context path, params, data=nil, opts={}
|
76
|
+
context = {:host => base_url, :path => build_url(path), :params => params, :query => hash_to_query(params), :data => data}
|
77
|
+
if opts[:method] == :post
|
78
|
+
raise "Don't send POST data when using :method => :post" unless data.to_s.empty?
|
79
|
+
# force a POST, use the query string as the POST body
|
80
|
+
context.merge! :data => hash_to_query(params), :headers => {'Content-Type' => 'application/x-www-form-urlencoded'}
|
81
|
+
elsif data
|
82
|
+
# standard POST, using "data" as the POST body
|
83
|
+
context.merge! :headers => {'Content-Type' => 'text/xml; charset=utf-8'}
|
84
|
+
else
|
85
|
+
context.merge! :path => build_url(path, params)
|
86
|
+
end
|
87
|
+
context
|
88
|
+
end
|
89
|
+
|
90
|
+
# accepts a path/string and optional hash of query params
|
91
|
+
# returns a string that represents the full url
|
92
|
+
def build_url path, params={}
|
93
|
+
full_path = @uri.path + path
|
94
|
+
super full_path, params, @uri.query
|
95
|
+
end
|
96
|
+
|
97
|
+
def base_url
|
98
|
+
"#{@uri.scheme}://#{@uri.host}" + (@uri.port ? ":#{@uri.port}" : "")
|
99
|
+
end
|
100
|
+
|
101
|
+
end
|
@@ -5,44 +5,26 @@ require 'net/http'
|
|
5
5
|
#
|
6
6
|
class RSolr::Connection::NetHttp
|
7
7
|
|
8
|
-
include RSolr::Connection::
|
8
|
+
include RSolr::Connection::Httpable
|
9
9
|
|
10
10
|
def connection
|
11
|
-
|
11
|
+
if @proxy
|
12
|
+
proxy_user, proxy_pass = @proxy.userinfo.split(/:/) if @proxy.userinfo
|
13
|
+
@connection ||= Net::HTTP.Proxy(@proxy.host, @proxy.port, proxy_user, proxy_pass).new(@uri.host, @uri.port)
|
14
|
+
else
|
15
|
+
@connection ||= Net::HTTP.new(@uri.host, @uri.port)
|
16
|
+
end
|
12
17
|
end
|
13
18
|
|
14
|
-
|
15
|
-
|
19
|
+
# maybe follow Rack and do [status, headers, body]
|
20
|
+
def get url
|
16
21
|
net_http_response = self.connection.get url
|
17
|
-
|
22
|
+
[net_http_response.code.to_i, net_http_response.message, net_http_response.body]
|
18
23
|
end
|
19
24
|
|
20
|
-
def post
|
21
|
-
url = self.build_url path, params
|
25
|
+
def post url, data, headers={}
|
22
26
|
net_http_response = self.connection.post url, data, headers
|
23
|
-
|
24
|
-
end
|
25
|
-
|
26
|
-
def create_http_context net_http_response, url, path, params, data=nil, headers={}
|
27
|
-
full_url = "#{@uri.scheme}://#{@uri.host}"
|
28
|
-
full_url += @uri.port ? ":#{@uri.port}" : ''
|
29
|
-
full_url += url
|
30
|
-
{
|
31
|
-
:status_code=>net_http_response.code.to_i,
|
32
|
-
:url=>full_url,
|
33
|
-
:body=> encode_utf8(net_http_response.body),
|
34
|
-
:path=>path,
|
35
|
-
:params=>params,
|
36
|
-
:data=>data,
|
37
|
-
:headers=>headers,
|
38
|
-
:message => net_http_response.message
|
39
|
-
}
|
40
|
-
end
|
41
|
-
|
42
|
-
# accepts a path/string and optional hash of query params
|
43
|
-
def build_url path, params={}
|
44
|
-
full_path = @uri.path + path
|
45
|
-
super full_path, params, @uri.query
|
27
|
+
[net_http_response.code.to_i, net_http_response.message, net_http_response.body]
|
46
28
|
end
|
47
29
|
|
48
30
|
end
|
@@ -1,5 +1,5 @@
|
|
1
1
|
# Helpful utility methods for building queries to a Solr server
|
2
|
-
# This includes helpers that the Direct connection can use
|
2
|
+
# This includes helpers that the Direct connection can use -- not specific to http, for example
|
3
3
|
module RSolr::Connection::Utils
|
4
4
|
|
5
5
|
# Performs URI escaping so that you can construct proper
|
@@ -19,7 +19,7 @@ module RSolr::Connection::Utils
|
|
19
19
|
string.force_encoding(Encoding::UTF_8) : string
|
20
20
|
end
|
21
21
|
|
22
|
-
# Return the bytesize of String; uses String#
|
22
|
+
# Return the bytesize of String; uses String#size under Ruby 1.8 and
|
23
23
|
# String#bytesize under 1.9.
|
24
24
|
if ''.respond_to?(:bytesize)
|
25
25
|
def bytesize(string)
|
@@ -53,11 +53,12 @@ module RSolr::Connection::Utils
|
|
53
53
|
#
|
54
54
|
# converts hash into URL query string, keys get an alpha sort
|
55
55
|
# if a value is an array, the array values get mapped to the same key:
|
56
|
-
# hash_to_query(:q=>'blah', :fq=>['
|
56
|
+
# hash_to_query(:q=>'blah-query', :fq=>['f1', 'f1'], :facet=>true, 'facet.field'=>['location_facet', 'format_facet'])
|
57
57
|
# returns:
|
58
|
-
# ?q=blah&fq=
|
58
|
+
# ?q=blah-query&fq=f1&fq=f2&facet=true&facet.field=location_facet&facet.field=format.facet
|
59
|
+
#
|
60
|
+
# if a value is empty/nil etc., it is tossed out
|
59
61
|
#
|
60
|
-
# if a value is empty/nil etc., it is not added
|
61
62
|
def hash_to_query(params)
|
62
63
|
mapped = params.map do |k, v|
|
63
64
|
next if v.to_s.empty?
|
@@ -69,5 +70,5 @@ module RSolr::Connection::Utils
|
|
69
70
|
end
|
70
71
|
mapped.compact.join("&")
|
71
72
|
end
|
72
|
-
|
73
|
+
|
73
74
|
end
|
data/spec/api/client_spec.rb
CHANGED
@@ -26,18 +26,40 @@ describe RSolr::Client do
|
|
26
26
|
}
|
27
27
|
client.add(:id=>1)
|
28
28
|
end
|
29
|
-
|
29
|
+
|
30
30
|
it 'should forward #commit calls to #update' do
|
31
31
|
client.should_receive(:update).
|
32
32
|
with("<?xml version=\"1.0\" encoding=\"UTF-8\"?><commit/>")
|
33
33
|
client.commit
|
34
34
|
end
|
35
|
+
|
36
|
+
it 'should forward #commit calls with options to #update' do
|
37
|
+
opts = {:waitFlush => false, :waitSearcher => false, :expungeDeletes => true}
|
38
|
+
# when client.commit is called, it eventually calls update
|
39
|
+
client.should_receive(:update).
|
40
|
+
with(opts)
|
41
|
+
# client.message is calls to create the xml
|
42
|
+
client.message.should_receive(:commit).
|
43
|
+
and_return(opts)
|
44
|
+
client.commit(opts)
|
45
|
+
end
|
35
46
|
|
36
47
|
it 'should forward #optimize calls to #update' do
|
37
48
|
client.should_receive(:update).
|
38
49
|
with("<?xml version=\"1.0\" encoding=\"UTF-8\"?><optimize/>")
|
39
50
|
client.optimize
|
40
51
|
end
|
52
|
+
|
53
|
+
it 'should forward #optimize calls with options to #update' do
|
54
|
+
opts = {:maxSegments => 5, :waitFlush => false}
|
55
|
+
# when client.commit is called, it eventually calls update
|
56
|
+
client.should_receive(:update).
|
57
|
+
with(opts)
|
58
|
+
# client.message is calls to create the xml
|
59
|
+
client.message.should_receive(:optimize).
|
60
|
+
and_return(opts)
|
61
|
+
client.optimize(opts)
|
62
|
+
end
|
41
63
|
|
42
64
|
it 'should forward #rollback calls to #update' do
|
43
65
|
client.should_receive(:update).
|
@@ -69,9 +91,9 @@ describe RSolr::Client do
|
|
69
91
|
and_return(:params=>{})
|
70
92
|
client.request '/music', :q=>'Coltrane'
|
71
93
|
end
|
72
|
-
|
94
|
+
|
73
95
|
end
|
74
|
-
|
96
|
+
|
75
97
|
context :adapt_response do
|
76
98
|
|
77
99
|
it 'should not try to evaluate ruby when the :qt is not :ruby' do
|
@@ -90,4 +112,4 @@ describe RSolr::Client do
|
|
90
112
|
|
91
113
|
end
|
92
114
|
|
93
|
-
end
|
115
|
+
end
|
@@ -0,0 +1,157 @@
|
|
1
|
+
describe RSolr::Connection::Httpable do
|
2
|
+
|
3
|
+
# calls #let to set "net_http" as method accessor
|
4
|
+
class R
|
5
|
+
include RSolr::Connection::Httpable
|
6
|
+
end
|
7
|
+
|
8
|
+
module HttpableHelper
|
9
|
+
def self.included base
|
10
|
+
base.let(:httpable){R.new}
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
context 'new' do
|
15
|
+
|
16
|
+
include HttpableHelper
|
17
|
+
|
18
|
+
it 'should define an intialize method that accepts a hash argument' do
|
19
|
+
lambda{R.new({})}.should_not raise_error
|
20
|
+
end
|
21
|
+
|
22
|
+
it 'should have an #opts attribute after creation' do
|
23
|
+
httpable.should respond_to(:opts)
|
24
|
+
end
|
25
|
+
|
26
|
+
it 'should have an #uri attribute after creation' do
|
27
|
+
httpable.should respond_to(:uri)
|
28
|
+
end
|
29
|
+
|
30
|
+
end
|
31
|
+
|
32
|
+
context 'opts and uri' do
|
33
|
+
|
34
|
+
it 'should allow setting the opts' do
|
35
|
+
opts = {:url=>'blah'}
|
36
|
+
r = R.new(opts)
|
37
|
+
r.opts.should == opts
|
38
|
+
end
|
39
|
+
|
40
|
+
it 'should parser the url option into a URI object' do
|
41
|
+
opts = {:url => 'http://xyz:1010/solr'}
|
42
|
+
r = R.new(opts)
|
43
|
+
r.uri.should be_a(URI)
|
44
|
+
r.uri.host.should == 'xyz'
|
45
|
+
end
|
46
|
+
|
47
|
+
end
|
48
|
+
|
49
|
+
context :build_url do
|
50
|
+
|
51
|
+
include HttpableHelper
|
52
|
+
|
53
|
+
it 'should build a full path and create a query string' do
|
54
|
+
r = httpable
|
55
|
+
r.build_url("/select", :q=>1).should == '/solr/select?q=1'
|
56
|
+
end
|
57
|
+
|
58
|
+
it 'should build a full path without a query string' do
|
59
|
+
r = httpable
|
60
|
+
r.build_url("/select").should == '/solr/select'
|
61
|
+
end
|
62
|
+
|
63
|
+
end
|
64
|
+
|
65
|
+
context :create_http_context do
|
66
|
+
|
67
|
+
include HttpableHelper
|
68
|
+
|
69
|
+
it "should build a simple GET context" do
|
70
|
+
r = httpable
|
71
|
+
result = r.create_http_context('/select', :q=>'a', :fq=>'b')
|
72
|
+
expected = {:path=>"/solr/select?q=a&fq=b", :params=>{:q=>"a", :fq=>"b"}, :data=>nil, :query=>"q=a&fq=b", :host=>"http://127.0.0.1:8983"}
|
73
|
+
|
74
|
+
result.keys.all? {|v| expected.keys.include?(v) }
|
75
|
+
result.values.all? {|v| expected.values.include?(v) }
|
76
|
+
end
|
77
|
+
|
78
|
+
it "should build a POST context" do
|
79
|
+
r = httpable
|
80
|
+
result = r.create_http_context('/select', {:wt => :xml}, '<commit/>')
|
81
|
+
expected = {:path=>"/solr/select", :params=>{:wt=>:xml}, :headers=>{"Content-Type"=>"text/xml; charset=utf-8"}, :data=>"<commit/>", :query=>"wt=xml", :host=>"http://127.0.0.1:8983"}
|
82
|
+
result.should == expected
|
83
|
+
end
|
84
|
+
|
85
|
+
it "should raise an exception when trying to use POST data AND :method => :post" do
|
86
|
+
r = httpable
|
87
|
+
lambda{
|
88
|
+
r.create_http_context('/select', {:wt => :xml}, '<commit/>', :method => :post)
|
89
|
+
}.should raise_error("Don't send POST data when using :method => :post")
|
90
|
+
end
|
91
|
+
|
92
|
+
it "should form-encoded POST context" do
|
93
|
+
r = httpable
|
94
|
+
result = r.create_http_context('/select', {:q => 'some gigantic query string that is too big for GET (for example)'}, nil, :method => :post)
|
95
|
+
result.should == {:path=>"/solr/select", :params=>{:q=>"some gigantic query string that is too big for GET (for example)"}, :headers=>{"Content-Type"=>"application/x-www-form-urlencoded"}, :data=>"q=some+gigantic+query+string+that+is+too+big+for+GET+%28for+example%29", :query=>"q=some+gigantic+query+string+that+is+too+big+for+GET+%28for+example%29", :host=>"http://127.0.0.1:8983"}
|
96
|
+
end
|
97
|
+
|
98
|
+
end
|
99
|
+
|
100
|
+
context :request do
|
101
|
+
|
102
|
+
include HttpableHelper
|
103
|
+
|
104
|
+
it "should be able to build a request context, pass the url to #get and return a full context" do
|
105
|
+
httpable.should_receive(:create_http_context).
|
106
|
+
with("/admin/ping", {}, nil, {}).
|
107
|
+
and_return({:path => '/solr/admin/ping'})
|
108
|
+
httpable.should_receive(:get).
|
109
|
+
with('/solr/admin/ping').
|
110
|
+
and_return(["asdfasdf", 200, "OK"])
|
111
|
+
response = httpable.request '/admin/ping'
|
112
|
+
response.should == {:status_code=>200, :message=>"OK", :path=>"/solr/admin/ping", :body=>"asdfasdf"}
|
113
|
+
end
|
114
|
+
|
115
|
+
it 'should send a get to itself with params' do
|
116
|
+
httpable.should_receive(:get).
|
117
|
+
with("/solr/blahasdf?id=1").
|
118
|
+
and_return(["", 200, "OK"])
|
119
|
+
r = httpable.request('/blahasdf', :id=>1)
|
120
|
+
r.should == {:status_code=>200, :path=>"/solr/blahasdf?id=1", :params=>{:id=>1}, :message=>"OK", :data=>nil, :query=>"id=1", :host=>"http://127.0.0.1:8983", :body=>""}
|
121
|
+
end
|
122
|
+
|
123
|
+
it 'should raise an error if the status_code is not 200' do
|
124
|
+
httpable.should_receive(:get).
|
125
|
+
with("/solr/blah?id=1").
|
126
|
+
and_return( ["", 404, "Not Found"] )
|
127
|
+
lambda{
|
128
|
+
httpable.request('/blah', :id=>1).should == true
|
129
|
+
}.should raise_error(/Not Found/)
|
130
|
+
end
|
131
|
+
|
132
|
+
it 'should send a post to itself if data is supplied' do
|
133
|
+
httpable.should_receive(:post).
|
134
|
+
with("/solr/blah", "<commit/>", {"Content-Type"=>"text/xml; charset=utf-8"}).
|
135
|
+
and_return(["", 200, "OK"])
|
136
|
+
httpable.request('/blah', {:id=>1}, "<commit/>")#.should == expected_response
|
137
|
+
end
|
138
|
+
|
139
|
+
it 'should send a post to itself when :method=>:post is set even if no POST data is supplied' do
|
140
|
+
httpable.should_receive(:post).
|
141
|
+
with("/solr/blah", "q=testing", {"Content-Type"=>"application/x-www-form-urlencoded"}).
|
142
|
+
and_return(["", 200, "OK"])
|
143
|
+
response = httpable.request('/blah', {:q => "testing"}, :method => :post)#.should == expected_response
|
144
|
+
response[:body].should == ""
|
145
|
+
response[:path].should == "/solr/blah"
|
146
|
+
response[:message].should == "OK"
|
147
|
+
response[:status_code].should == 200
|
148
|
+
response[:params].should == {:q=>"testing"}
|
149
|
+
response[:headers].should == {"Content-Type"=>"application/x-www-form-urlencoded"}
|
150
|
+
response[:data].should == "q=testing"
|
151
|
+
response[:query].should == "q=testing"
|
152
|
+
response[:host].should == "http://127.0.0.1:8983"
|
153
|
+
end
|
154
|
+
|
155
|
+
end
|
156
|
+
|
157
|
+
end
|
@@ -12,23 +12,26 @@ describe RSolr::Connection::NetHttp do
|
|
12
12
|
include NetHttpHelper
|
13
13
|
|
14
14
|
it 'should forward simple, non-data calls to #get' do
|
15
|
+
net_http.should_receive(:create_http_context).
|
16
|
+
with("/select", {:q=>"a"}, nil, {}).
|
17
|
+
and_return(:path=>"/solr/select?q=a", :params=>{:q=>"a"}, :data=>nil, :query=>"q=a", :host=>"http://127.0.0.1:8983")
|
15
18
|
net_http.should_receive(:get).
|
16
|
-
with(
|
17
|
-
and_return(
|
18
|
-
net_http.request('/select', :q=>'a')
|
19
|
+
with("/solr/select?q=a").
|
20
|
+
and_return(['', 200, 'OK'])
|
21
|
+
net_http.request('/select', :q=>'a')
|
19
22
|
end
|
20
23
|
|
21
24
|
it 'should forward :method=>:post calls to #post with a special header' do
|
22
25
|
net_http.should_receive(:post).
|
23
|
-
with(
|
24
|
-
and_return(
|
26
|
+
with("/solr/select", "q=a", {"Content-Type"=>"application/x-www-form-urlencoded"}).
|
27
|
+
and_return(["", 200, "OK"])
|
25
28
|
net_http.request('/select', {:q=>'a'}, :method=>:post)
|
26
29
|
end
|
27
30
|
|
28
31
|
it 'should forward data calls to #post' do
|
29
32
|
net_http.should_receive(:post).
|
30
|
-
with("/update", "<optimize/>", {
|
31
|
-
and_return(
|
33
|
+
with("/solr/update", "<optimize/>", {"Content-Type"=>"text/xml; charset=utf-8"}).
|
34
|
+
and_return(["", 200, "OK"])
|
32
35
|
net_http.request('/update', {}, '<optimize/>')
|
33
36
|
end
|
34
37
|
|
@@ -61,21 +64,14 @@ describe RSolr::Connection::NetHttp do
|
|
61
64
|
with('/solr/select?q=1').
|
62
65
|
and_return(net_http_response)
|
63
66
|
|
64
|
-
context = net_http.send(:get, '/select
|
65
|
-
context.should be_a(
|
67
|
+
context = net_http.send(:get, '/solr/select?q=1')
|
68
|
+
context.should be_a(Array)
|
66
69
|
|
67
|
-
|
68
|
-
context.keys.size.should == keys.size
|
69
|
-
context.keys.all?{|key| keys.include?(key) }.should == true
|
70
|
+
context.size.should == 3
|
70
71
|
|
71
|
-
context[
|
72
|
-
context[
|
73
|
-
context[
|
74
|
-
context[:path].should == '/select'
|
75
|
-
context[:url].should == 'http://127.0.0.1:8983/solr/select?q=1'
|
76
|
-
context[:headers].should == {}
|
77
|
-
context[:params].should == {:q=>1}
|
78
|
-
context[:message].should == 'OK'
|
72
|
+
context[0].should == 200
|
73
|
+
context[1].should == 'OK'
|
74
|
+
context[2].should == 'The Response'
|
79
75
|
end
|
80
76
|
|
81
77
|
it 'should make a POST request as expected' do
|
@@ -90,21 +86,14 @@ describe RSolr::Connection::NetHttp do
|
|
90
86
|
c.should_receive(:post).
|
91
87
|
with('/solr/update', '<rollback/>', {}).
|
92
88
|
and_return(net_http_response)
|
93
|
-
context = net_http.send(:post, '/update', '<rollback/>')
|
94
|
-
context.should be_a(
|
89
|
+
context = net_http.send(:post, '/solr/update', '<rollback/>')
|
90
|
+
context.should be_a(Array)
|
95
91
|
|
96
|
-
|
97
|
-
context.keys.size.should == keys.size
|
98
|
-
context.keys.all?{|key| keys.include?(key) }.should == true
|
92
|
+
context.size.should == 3
|
99
93
|
|
100
|
-
context[
|
101
|
-
context[
|
102
|
-
context[
|
103
|
-
context[:path].should == '/update'
|
104
|
-
context[:url].should == 'http://127.0.0.1:8983/solr/update'
|
105
|
-
context[:headers].should == {}
|
106
|
-
context[:params].should == {}
|
107
|
-
context[:message].should == 'OK'
|
94
|
+
context[0].should == 200
|
95
|
+
context[1].should == 'OK'
|
96
|
+
context[2].should == 'The Response'
|
108
97
|
end
|
109
98
|
|
110
99
|
end
|
@@ -145,4 +134,4 @@ describe RSolr::Connection::NetHttp do
|
|
145
134
|
|
146
135
|
end
|
147
136
|
|
148
|
-
end
|
137
|
+
end
|
@@ -39,12 +39,12 @@ describe RSolr::Connection::Utils do
|
|
39
39
|
utils.hash_to_query(my_params).should == expected
|
40
40
|
end
|
41
41
|
|
42
|
-
it 'should escape
|
42
|
+
it 'should escape complex queries, part 2' do
|
43
43
|
my_params = {'q' => '+popularity:[10 TO *] +section:0'}
|
44
44
|
expected = 'q=%2Bpopularity%3A%5B10+TO+%2A%5D+%2Bsection%3A0'
|
45
45
|
utils.hash_to_query(my_params).should == expected
|
46
46
|
end
|
47
|
-
|
47
|
+
|
48
48
|
end
|
49
49
|
|
50
50
|
context 'escape method' do
|
@@ -81,4 +81,4 @@ describe RSolr::Connection::Utils do
|
|
81
81
|
|
82
82
|
end
|
83
83
|
|
84
|
-
end
|
84
|
+
end
|
metadata
CHANGED
@@ -1,7 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: rsolr
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
|
4
|
+
prerelease: true
|
5
|
+
segments:
|
6
|
+
- 0
|
7
|
+
- 13
|
8
|
+
- 0
|
9
|
+
- pre
|
10
|
+
version: 0.13.0.pre
|
5
11
|
platform: ruby
|
6
12
|
authors:
|
7
13
|
- Matt Mitchell
|
@@ -9,19 +15,23 @@ autorequire:
|
|
9
15
|
bindir: bin
|
10
16
|
cert_chain: []
|
11
17
|
|
12
|
-
date: 2010-
|
18
|
+
date: 2010-04-04 00:00:00 -04:00
|
13
19
|
default_executable:
|
14
20
|
dependencies:
|
15
21
|
- !ruby/object:Gem::Dependency
|
16
22
|
name: builder
|
17
|
-
|
18
|
-
|
19
|
-
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
prerelease: false
|
24
|
+
requirement: &id001 !ruby/object:Gem::Requirement
|
20
25
|
requirements:
|
21
26
|
- - ">="
|
22
27
|
- !ruby/object:Gem::Version
|
28
|
+
segments:
|
29
|
+
- 2
|
30
|
+
- 1
|
31
|
+
- 2
|
23
32
|
version: 2.1.2
|
24
|
-
|
33
|
+
type: :runtime
|
34
|
+
version_requirements: *id001
|
25
35
|
description: RSolr aims to provide a simple and extensible library for working with Solr
|
26
36
|
email: goodieboy@gmail.com
|
27
37
|
executables: []
|
@@ -38,8 +48,8 @@ files:
|
|
38
48
|
- lib/rsolr.rb
|
39
49
|
- lib/rsolr/client.rb
|
40
50
|
- lib/rsolr/connection.rb
|
51
|
+
- lib/rsolr/connection/httpable.rb
|
41
52
|
- lib/rsolr/connection/net_http.rb
|
42
|
-
- lib/rsolr/connection/requestable.rb
|
43
53
|
- lib/rsolr/connection/utils.rb
|
44
54
|
- lib/rsolr/message.rb
|
45
55
|
- lib/rsolr/message/document.rb
|
@@ -58,28 +68,33 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
58
68
|
requirements:
|
59
69
|
- - ">="
|
60
70
|
- !ruby/object:Gem::Version
|
71
|
+
segments:
|
72
|
+
- 0
|
61
73
|
version: "0"
|
62
|
-
version:
|
63
74
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
64
75
|
requirements:
|
65
|
-
- - "
|
76
|
+
- - ">"
|
66
77
|
- !ruby/object:Gem::Version
|
67
|
-
|
68
|
-
|
78
|
+
segments:
|
79
|
+
- 1
|
80
|
+
- 3
|
81
|
+
- 1
|
82
|
+
version: 1.3.1
|
69
83
|
requirements: []
|
70
84
|
|
71
85
|
rubyforge_project:
|
72
|
-
rubygems_version: 1.3.
|
86
|
+
rubygems_version: 1.3.6
|
73
87
|
signing_key:
|
74
88
|
specification_version: 3
|
75
89
|
summary: A Ruby client for Apache Solr
|
76
90
|
test_files:
|
77
91
|
- spec/api/client_spec.rb
|
92
|
+
- spec/api/connection/httpable_spec.rb
|
78
93
|
- spec/api/connection/net_http_spec.rb
|
79
|
-
- spec/api/connection/requestable_spec.rb
|
80
94
|
- spec/api/connection/utils_spec.rb
|
81
95
|
- spec/api/message_spec.rb
|
82
96
|
- spec/api/rsolr_spec.rb
|
97
|
+
- spec/integration/http_errors_spec.rb
|
83
98
|
- spec/spec_helper.rb
|
84
99
|
- Rakefile
|
85
100
|
- tasks/spec.rake
|
@@ -1,43 +0,0 @@
|
|
1
|
-
# A module that defines the interface and top-level logic for http based connection classes.
|
2
|
-
module RSolr::Connection::Requestable
|
3
|
-
|
4
|
-
include RSolr::Connection::Utils
|
5
|
-
|
6
|
-
attr_reader :opts, :uri
|
7
|
-
|
8
|
-
# opts can have:
|
9
|
-
# :url => 'http://localhost:8080/solr'
|
10
|
-
def initialize opts={}
|
11
|
-
opts[:url] ||= 'http://127.0.0.1:8983/solr'
|
12
|
-
@opts = opts
|
13
|
-
@uri = URI.parse opts[:url]
|
14
|
-
end
|
15
|
-
|
16
|
-
# send a request to the connection
|
17
|
-
# request '/select', :q=>'*:*'
|
18
|
-
#
|
19
|
-
# request '/update', {:wt=>:xml}, '</commit>'
|
20
|
-
#
|
21
|
-
# force a post where the post body is the param query
|
22
|
-
# request '/update', "<optimize/>", :method=>:post
|
23
|
-
#
|
24
|
-
def request path, params={}, *extra
|
25
|
-
opts = extra[-1].kind_of?(Hash) ? extra.pop : {}
|
26
|
-
data = extra[0]
|
27
|
-
# force a POST, use the query string as the POST body
|
28
|
-
if opts[:method] == :post and data.to_s.empty?
|
29
|
-
http_context = self.post(path, hash_to_query(params), {}, {'Content-Type' => 'application/x-www-form-urlencoded'})
|
30
|
-
else
|
31
|
-
if data
|
32
|
-
# standard POST, using "data" as the POST body
|
33
|
-
http_context = self.post(path, data, params, {"Content-Type" => 'text/xml; charset=utf-8'})
|
34
|
-
else
|
35
|
-
# standard GET
|
36
|
-
http_context = self.get(path, params)
|
37
|
-
end
|
38
|
-
end
|
39
|
-
raise RSolr::RequestError.new("Solr Response: #{http_context[:message]}") unless http_context[:status_code] == 200
|
40
|
-
http_context
|
41
|
-
end
|
42
|
-
|
43
|
-
end
|
@@ -1,92 +0,0 @@
|
|
1
|
-
describe RSolr::Connection::Requestable do
|
2
|
-
|
3
|
-
# calls #let to set "net_http" as method accessor
|
4
|
-
class R
|
5
|
-
include RSolr::Connection::Requestable
|
6
|
-
end
|
7
|
-
|
8
|
-
module RequestableHelper
|
9
|
-
def self.included base
|
10
|
-
base.let(:requestable){R.new}
|
11
|
-
end
|
12
|
-
end
|
13
|
-
|
14
|
-
context 'new' do
|
15
|
-
|
16
|
-
include RequestableHelper
|
17
|
-
|
18
|
-
it 'should define an intialize method that accepts a hash argument' do
|
19
|
-
lambda{R.new({})}.should_not raise_error
|
20
|
-
end
|
21
|
-
|
22
|
-
it 'should have an #opts attribute after creation' do
|
23
|
-
requestable.should respond_to(:opts)
|
24
|
-
end
|
25
|
-
|
26
|
-
it 'should have an #uri attribute after creation' do
|
27
|
-
requestable.should respond_to(:uri)
|
28
|
-
end
|
29
|
-
|
30
|
-
end
|
31
|
-
|
32
|
-
context 'opts and uri' do
|
33
|
-
|
34
|
-
it 'should allow setting the opts' do
|
35
|
-
opts = {:url=>'blah'}
|
36
|
-
r = R.new(opts)
|
37
|
-
r.opts.should == opts
|
38
|
-
end
|
39
|
-
|
40
|
-
it 'should parser the url option into a URI object' do
|
41
|
-
opts = {:url => 'http://xyz:1010/solr'}
|
42
|
-
r = R.new(opts)
|
43
|
-
r.uri.should be_a(URI)
|
44
|
-
r.uri.host.should == 'xyz'
|
45
|
-
end
|
46
|
-
|
47
|
-
end
|
48
|
-
|
49
|
-
context :request do
|
50
|
-
|
51
|
-
include RequestableHelper
|
52
|
-
|
53
|
-
it 'should send a get to itself' do
|
54
|
-
expected_response = {:status_code => 200}
|
55
|
-
requestable.should_receive(:get).
|
56
|
-
with('/blah', {:id=>1}).
|
57
|
-
and_return(expected_response)
|
58
|
-
requestable.request('/blah', :id=>1).should == expected_response
|
59
|
-
end
|
60
|
-
|
61
|
-
it 'should raise an error if the status_code is not 200' do
|
62
|
-
expected_response = {:status_code => 503}
|
63
|
-
requestable.should_receive(:get).
|
64
|
-
with('/blah', {:id=>1}).
|
65
|
-
and_return(expected_response)
|
66
|
-
lambda{
|
67
|
-
requestable.request('/blah', :id=>1)
|
68
|
-
}.should raise_error
|
69
|
-
end
|
70
|
-
|
71
|
-
it 'should send a post to itself if data is supplied' do
|
72
|
-
expected_response = {:status_code => 200}
|
73
|
-
my_data = "<commit/>"
|
74
|
-
post_headers = {"Content-Type"=>"text/xml; charset=utf-8"}
|
75
|
-
requestable.should_receive(:post).
|
76
|
-
with('/blah', my_data, {:id=>1}, post_headers).
|
77
|
-
and_return(expected_response)
|
78
|
-
requestable.request('/blah', {:id=>1}, my_data).should == expected_response
|
79
|
-
end
|
80
|
-
|
81
|
-
it 'should send a post to itself when :method=>:post is set even if no POST data is supplied' do
|
82
|
-
expected_response = {:status_code => 200}
|
83
|
-
post_headers = {"Content-Type"=>"application/x-www-form-urlencoded"}
|
84
|
-
requestable.should_receive(:post).
|
85
|
-
with('/blah', "", {}, post_headers).
|
86
|
-
and_return(expected_response)
|
87
|
-
requestable.request('/blah', {}, :method => :post).should == expected_response
|
88
|
-
end
|
89
|
-
|
90
|
-
end
|
91
|
-
|
92
|
-
end
|