rsolr 0.12.1 → 0.13.0.pre

Sign up to get free protection for your applications and to get access to all the features.
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 = rsolr.select :q=>'*:*'
20
+ response = solr.select :q=>'*:*'
16
21
 
17
22
  # send a request to a custom request handler; /catalog
18
- response = rsolr.request '/catalog', :q=>'*:*'
23
+ response = solr.request '/catalog', :q=>'*:*'
19
24
 
20
25
  # alternative to above:
21
- response = rsolr.catalog :q=>'*:*'
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
- * mperham
131
+ * Lorenzo Riccucci
132
+ * Mike Perham
126
133
  * Mat Brown
127
- * shairontoledo
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.12.1
1
+ 0.13.0.pre
data/lib/rsolr.rb CHANGED
@@ -47,4 +47,7 @@ module RSolr
47
47
  # RequestError is a common/generic exception class used by the adapters
48
48
  class RequestError < RuntimeError; end
49
49
 
50
+ # TODO: The connection drivers need to start raising this...
51
+ class ConnectionError < RuntimeError; end
52
+
50
53
  end
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 </commit>
50
- def commit
51
- update message.commit
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 </optimize>
55
- def optimize
56
- update message.optimize
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 *opts
100
+ def message
81
101
  @message ||= RSolr::Message::Generator.new
82
102
  end
83
103
 
@@ -4,6 +4,6 @@ module RSolr::Connection
4
4
 
5
5
  autoload :NetHttp, 'rsolr/connection/net_http'
6
6
  autoload :Utils, 'rsolr/connection/utils'
7
- autoload :Requestable, 'rsolr/connection/requestable'
7
+ autoload :Httpable, 'rsolr/connection/httpable'
8
8
 
9
9
  end
@@ -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::Requestable
8
+ include RSolr::Connection::Httpable
9
9
 
10
10
  def connection
11
- @connection ||= Net::HTTP.new(@uri.host, @uri.port)
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
- def get path, params={}
15
- url = self.build_url path, params
19
+ # maybe follow Rack and do [status, headers, body]
20
+ def get url
16
21
  net_http_response = self.connection.get url
17
- create_http_context net_http_response, url, path, params
22
+ [net_http_response.code.to_i, net_http_response.message, net_http_response.body]
18
23
  end
19
24
 
20
- def post path, data, params={}, headers={}
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
- create_http_context net_http_response, url, path, params, data, headers
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#length under Ruby 1.8 and
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=>['blah', 'blah'], :facet=>{:field=>['location_facet', 'format_facet']})
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=blah&fq=blah&facet.field=location_facet&facet.field=format.facet
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
@@ -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('/select', :q=>'a').
17
- and_return({:status_code=>200})
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('/select', 'q=a', {}, {"Content-Type"=>"application/x-www-form-urlencoded"}).
24
- and_return({:status_code=>200})
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/>", {}, {"Content-Type"=>"text/xml; charset=utf-8"}).
31
- and_return({:status_code=>200})
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', :q=>1)
65
- context.should be_a(Hash)
67
+ context = net_http.send(:get, '/solr/select?q=1')
68
+ context.should be_a(Array)
66
69
 
67
- keys = [:data, :body, :status_code, :path, :url, :headers, :params, :message]
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[:data].should == nil
72
- context[:body].should == 'The Response'
73
- context[:status_code].should == 200
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(Hash)
89
+ context = net_http.send(:post, '/solr/update', '<rollback/>')
90
+ context.should be_a(Array)
95
91
 
96
- keys = [:data, :body, :status_code, :path, :url, :headers, :params, :message]
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[:data].should == '<rollback/>'
101
- context[:body].should == 'The Response'
102
- context[:status_code].should == 200
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 comlex queries, part 2' do
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
@@ -0,0 +1,12 @@
1
+ describe "Solr servlet error handling" do
2
+
3
+ it "should raise a RSolr::Request which contains the http request context" do
4
+ s = RSolr.connect
5
+ begin
6
+ s.select :qt => 'standard'
7
+ rescue RSolr::RequestError
8
+
9
+ end
10
+ end
11
+
12
+ 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
- version: 0.12.1
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-02-04 00:00:00 -05:00
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
- type: :runtime
18
- version_requirement:
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
- version:
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
- version: "0"
68
- version:
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.5
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