rsolr 1.0.0.beta3 → 1.0.0.beta4

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/VERSION CHANGED
@@ -1 +1 @@
1
- 1.0.0.beta3
1
+ 1.0.0.beta4
data/lib/rsolr/client.rb CHANGED
@@ -1,24 +1,42 @@
1
1
  class RSolr::Client
2
2
 
3
- attr_reader :connection
3
+ attr_reader :connection, :uri, :proxy, :options
4
4
 
5
- def initialize connection
5
+ def initialize connection, options = {}
6
6
  @connection = connection
7
+ url = options[:url] || 'http://127.0.0.1:8983/solr/'
8
+ url << "/" unless url[-1] == ?/
9
+ proxy_url = options[:proxy]
10
+ proxy_url << "/" unless proxy_url.nil? or proxy_url[-1] == ?/
11
+ @uri = RSolr::Uri.create url
12
+ @proxy = RSolr::Uri.create proxy_url if proxy_url
13
+ @options = options
14
+ extend RSolr::Pagination::Client
7
15
  end
8
16
 
17
+ # returns the actual request uri object.
18
+ def base_request_uri
19
+ base_uri.request_uri
20
+ end
21
+
22
+ # returns the uri proxy if present,
23
+ # otherwise just the uri object.
24
+ def base_uri
25
+ @proxy || @uri
26
+ end
27
+
28
+ # Create the get, post, and head methods
9
29
  %W(get post head).each do |meth|
10
30
  class_eval <<-RUBY
11
31
  def #{meth} path, opts = {}, &block
12
- send_request path, opts.merge(:method => :#{meth}), &block
32
+ send_and_receive path, opts.merge(:method => :#{meth}), &block
13
33
  end
14
34
  RUBY
15
35
  end
16
36
 
17
- def method_missing name, *args
18
- send_request name, *args
19
- end
20
-
21
- # POST XML messages to /update with optional params
37
+ # POST XML messages to /update with optional params.
38
+ #
39
+ # http://wiki.apache.org/solr/UpdateXmlMessages#add.2BAC8-update
22
40
  #
23
41
  # If not set, opts[:headers] will be set to a hash with the key
24
42
  # 'Content-Type' set to 'text/xml'
@@ -27,7 +45,7 @@ class RSolr::Client
27
45
  #
28
46
  # :data - posted data
29
47
  # :headers - http headers
30
- # :params - query parameter hash
48
+ # :params - solr query parameter hash
31
49
  #
32
50
  def update opts = {}
33
51
  opts[:headers] ||= {}
@@ -37,6 +55,9 @@ class RSolr::Client
37
55
 
38
56
  #
39
57
  # +add+ creates xml "add" documents and sends the xml data to the +update+ method
58
+ #
59
+ # http://wiki.apache.org/solr/UpdateXmlMessages#add.2BAC8-update
60
+ #
40
61
  # single record:
41
62
  # solr.update(:id=>1, :name=>'one')
42
63
  #
@@ -54,14 +75,7 @@ class RSolr::Client
54
75
 
55
76
  # send "commit" xml with opts
56
77
  #
57
- # opts recognized by solr
58
- #
59
- # :maxSegments => N - optimizes down to at most N number of segments
60
- # :waitFlush => true|false - do not return until changes are flushed to disk
61
- # :waitSearcher => true|false - do not return until a new searcher is opened and registered
62
- # :expungeDeletes => true|false - merge segments with deletes into other segments #NOT
63
- #
64
- # *NOTE* :expungeDeletes is Solr 1.4 only
78
+ # http://wiki.apache.org/solr/UpdateXmlMessages#A.22commit.22_and_.22optimize.22
65
79
  #
66
80
  def commit opts = {}
67
81
  commit_attrs = opts.delete :commit_attributes
@@ -70,14 +84,7 @@ class RSolr::Client
70
84
 
71
85
  # send "optimize" xml with opts.
72
86
  #
73
- # opts recognized by solr
74
- #
75
- # :maxSegments => N - optimizes down to at most N number of segments
76
- # :waitFlush => true|false - do not return until changes are flushed to disk
77
- # :waitSearcher => true|false - do not return until a new searcher is opened and registered
78
- # :expungeDeletes => true|false - merge segments with deletes into other segments
79
- #
80
- # *NOTE* :expungeDeletes is Solr 1.4 only
87
+ # http://wiki.apache.org/solr/UpdateXmlMessages#A.22commit.22_and_.22optimize.22
81
88
  #
82
89
  def optimize opts = {}
83
90
  optimize_attributes = opts.delete :optimize_attributes
@@ -85,11 +92,14 @@ class RSolr::Client
85
92
  end
86
93
 
87
94
  # send </rollback>
95
+ #
96
+ # http://wiki.apache.org/solr/UpdateXmlMessages#A.22rollback.22
97
+ #
88
98
  # NOTE: solr 1.4 only
89
99
  def rollback opts = {}
90
100
  update opts.merge(:data => xml.rollback)
91
101
  end
92
-
102
+
93
103
  # Delete one or many documents by id
94
104
  # solr.delete_by_id 10
95
105
  # solr.delete_by_id([12, 41, 199])
@@ -97,7 +107,10 @@ class RSolr::Client
97
107
  update opts.merge(:data => xml.delete_by_id(id))
98
108
  end
99
109
 
100
- # delete one or many documents by query
110
+ # delete one or many documents by query.
111
+ #
112
+ # http://wiki.apache.org/solr/UpdateXmlMessages#A.22delete.22_by_ID_and_by_Query
113
+ #
101
114
  # solr.delete_by_query 'available:0'
102
115
  # solr.delete_by_query ['quantity:0', 'manu:"FQ"']
103
116
  def delete_by_query query, opts = {}
@@ -109,7 +122,7 @@ class RSolr::Client
109
122
  @xml ||= RSolr::Xml::Generator.new
110
123
  end
111
124
 
112
- # +send_request+ is the main request method.
125
+ # +send_and_receive+ is the main request method responsible for sending requests to the +connection+ object.
113
126
  #
114
127
  # "path" : A string value that usually represents a solr request handler
115
128
  # "opt" : A hash, which can contain the following keys:
@@ -117,25 +130,109 @@ class RSolr::Client
117
130
  # :params : optional - the query string params in hash form
118
131
  # :data : optional - post data -- if a hash is given, it's sent as "application/x-www-form-urlencoded"
119
132
  # :headers : optional - hash of request headers
120
- # All other options are passed right along to the connection request method (:get, :post, or :head)
133
+ # All other options are passed right along to the connection's +send_and_receive+ method (:get, :post, or :head)
121
134
  #
122
- # +send_request+ returns either a string or hash on a successful ruby request.
135
+ # +send_and_receive+ returns either a string or hash on a successful ruby request.
123
136
  # When the :params[:wt] => :ruby, the response will be a hash, else a string.
137
+ #
138
+ # creates a request context hash,
139
+ # sends it to the connection's +execute+ method
140
+ # which returns a simple hash,
141
+ # then passes the request/response into +adapt_response+.
142
+ def send_and_receive path, opts
143
+ request_context = build_request path, opts
144
+ execute request_context
145
+ end
146
+
124
147
  #
125
- def send_request path, opts = {}
126
- connection.send_request path, opts
148
+ def execute request_context
149
+ raw_response = connection.execute self, request_context
150
+ adapt_response(request_context, raw_response) unless raw_response.nil?
151
+ end
152
+
153
+ # +build_request+ accepts a path and options hash,
154
+ # then prepares a normalized hash to return for sending
155
+ # to a solr connection driver.
156
+ # +build_request+ sets up the uri/query string
157
+ # and converts the +data+ arg to form-urlencoded,
158
+ # if the +data+ arg is a hash.
159
+ # returns a hash with the following keys:
160
+ # :method
161
+ # :params
162
+ # :headers
163
+ # :data
164
+ # :uri
165
+ # :path
166
+ # :query
167
+ def build_request path, opts
168
+ raise "path must be a string or symbol, not #{path.inspect}" unless [String,Symbol].include?(path.class)
169
+ path = path.to_s
170
+ opts[:proxy] = proxy unless proxy.nil?
171
+ opts[:method] ||= :get
172
+ raise "The :data option can only be used if :method => :post" if opts[:method] != :post and opts[:data]
173
+ opts[:params] = opts[:params].nil? ? {:wt => :ruby} : {:wt => :ruby}.merge(opts[:params])
174
+ query = RSolr::Uri.params_to_solr(opts[:params]) unless opts[:params].empty?
175
+ opts[:query] = query
176
+ if opts[:data].is_a? Hash
177
+ opts[:data] = RSolr::Uri.params_to_solr opts[:data]
178
+ opts[:headers] ||= {}
179
+ opts[:headers]['Content-Type'] ||= 'application/x-www-form-urlencoded'
180
+ end
181
+ opts[:path] = path
182
+ opts[:uri] = base_uri.merge(path.to_s + (query ? "?#{query}" : "")) if base_uri
183
+ opts
127
184
  end
128
185
 
129
- # used for debugging/inspection
130
- # - accepts the same args as send_request
131
- def build_request path, opts = {}
132
- connection.build_request path, opts
186
+ # A mixin for used by #adapt_response
187
+ # This module essentially
188
+ # allows the raw response access to
189
+ # the original response and request.
190
+ module Context
191
+ attr_accessor :request, :response
133
192
  end
134
193
 
135
- # used for debugging/inspection
136
- # - accepts the same args as send_request
137
- def adapt_response request_context, response
138
- connection.adapt_response request_context, response
194
+ # This method will evaluate the :body value
195
+ # if the params[:uri].params[:wt] == :ruby
196
+ # ... otherwise, the body is returned as is.
197
+ # The return object has methods attached, :request and :response.
198
+ # These methods give you access to the original
199
+ # request and response from the connection.
200
+ #
201
+ # +adapt_response+ will raise an InvalidRubyResponse
202
+ # if :wt == :ruby and the body
203
+ # couldn't be evaluated.
204
+ def adapt_response request, response
205
+ raise "The response does not have the correct keys => :body, :headers, :status" unless
206
+ %W(body headers status) == response.keys.map{|k|k.to_s}.sort
207
+ raise RSolr::Error::Http.new request, response unless
208
+ [200,302].include? response[:status]
209
+ result = request[:params][:wt] == :ruby ? evaluate_ruby_response(request, response) : response[:body]
210
+ result.extend Context
211
+ result.request = request
212
+ result.response = response
213
+ result
214
+ end
215
+
216
+ protected
217
+
218
+ # converts the method name for the solr request handler path.
219
+ def method_missing name, *args
220
+ send_and_receive name, *args
221
+ end
222
+
223
+ # evaluates the response[:body],
224
+ # attemps to bring the ruby string to life.
225
+ # If a SyntaxError is raised, then
226
+ # this method intercepts and raises a
227
+ # RSolr::Error::InvalidRubyResponse
228
+ # instead, giving full access to the
229
+ # request/response objects.
230
+ def evaluate_ruby_response request, response
231
+ begin
232
+ Kernel.eval response[:body].to_s
233
+ rescue SyntaxError
234
+ raise RSolr::Error::InvalidRubyResponse.new request, response
235
+ end
139
236
  end
140
237
 
141
238
  end
@@ -1,24 +1,20 @@
1
1
  require 'net/http'
2
2
  require 'net/https'
3
3
 
4
- class RSolr::Http
5
-
6
- include RSolr::Connectable
7
-
8
- def initialize *args, &block
9
- # call the initialize method from RSolr::Connectable
10
- super
11
- end
4
+ # The default/Net::Http adapter for RSolr.
5
+ class RSolr::Connection
12
6
 
13
7
  # using the request_context hash,
14
- # issue a request,
8
+ # send a request,
15
9
  # then return the standard rsolr response hash {:status, :body, :headers}
16
- def execute request_context
10
+ def execute client, request_context
11
+ h = http request_context[:uri], request_context[:proxy]
17
12
  request = setup_raw_request request_context
18
13
  request.body = request_context[:data] if request_context[:method] == :post and request_context[:data]
19
14
  begin
20
- response = http.request request
15
+ response = h.request request
21
16
  {:status => response.code.to_i, :headers => response.to_hash, :body => response.body}
17
+ # catch the undefined closed? exception -- this is a confirmed ruby bug
22
18
  rescue NoMethodError
23
19
  $!.message == "undefined method `closed?' for nil:NilClass" ?
24
20
  raise(Errno::ECONNREFUSED.new) :
@@ -28,7 +24,8 @@ class RSolr::Http
28
24
 
29
25
  protected
30
26
 
31
- def http
27
+ # This returns a singleton of a Net::HTTP or Net::HTTP.Proxy request object.
28
+ def http uri, proxy = nil
32
29
  @http ||= (
33
30
  http = if proxy
34
31
  proxy_user, proxy_pass = proxy.userinfo.split(/:/) if proxy.userinfo
@@ -36,30 +33,12 @@ class RSolr::Http
36
33
  else
37
34
  Net::HTTP.new uri.host, uri.port
38
35
  end
39
-
40
- http.use_ssl = uri.port == 443 || uri.instance_of?(URI::HTTPS)
41
-
42
- if options[:timeout] && options[:timeout].is_a?(Integer)
43
- http.open_timeout = options[:timeout]
44
- http.read_timeout = options[:timeout]
45
- end
46
-
47
- if options[:pem] && http.use_ssl?
48
- http.cert = OpenSSL::X509::Certificate.new(options[:pem])
49
- http.key = OpenSSL::PKey::RSA.new(options[:pem])
50
- http.verify_mode = OpenSSL::SSL::VERIFY_PEER
51
- else
52
- http.verify_mode = OpenSSL::SSL::VERIFY_NONE
53
- end
54
-
55
- if options[:debug_output]
56
- http.set_debug_output(options[:debug_output])
57
- end
58
-
36
+ http.use_ssl = uri.port == 443 || uri.instance_of?(URI::HTTPS)
59
37
  http
60
38
  )
61
39
  end
62
40
 
41
+ #
63
42
  def setup_raw_request request_context
64
43
  http_method = case request_context[:method]
65
44
  when :get
@@ -74,26 +53,7 @@ class RSolr::Http
74
53
  headers = request_context[:headers] || {}
75
54
  raw_request = http_method.new request_context[:uri].to_s
76
55
  raw_request.initialize_http_header headers
77
- raw_request.basic_auth username, password if options[:basic_auth]
78
- if options[:digest_auth]
79
- res = http.head(request_context[:uri].to_s, headers)
80
- if res['www-authenticate'] != nil && res['www-authenticate'].length > 0
81
- raw_request.digest_auth username, password, res
82
- end
83
- end
84
56
  raw_request
85
57
  end
86
58
 
87
- def credentials
88
- options[:basic_auth] || options[:digest_auth]
89
- end
90
-
91
- def username
92
- credentials[:username]
93
- end
94
-
95
- def password
96
- credentials[:password]
97
- end
98
-
99
59
  end
@@ -0,0 +1,127 @@
1
+ module RSolr::Pagination
2
+
3
+ # Calculates the "start" and "rows" Solr params
4
+ # by inspecting the :per_page and :page params.
5
+ def self.calculate_start_and_rows page, per_page
6
+ per_page ||= 10
7
+ page = page.to_s.to_i-1
8
+ page = page < 1 ? 0 : page
9
+ start = page * per_page
10
+ [start, per_page]
11
+ end
12
+
13
+ # A mixin module for RSolr::Client
14
+ # -- note, this must mixed-in via
15
+ # "extend" on a RSolr::Client instance.
16
+ module Client
17
+
18
+ # A paginated request method.
19
+ def paginate page, per_page, path, opts = {}
20
+ request_context = build_paginated_request page, per_page, path, opts = {}
21
+ puts request_context.inspect
22
+ execute request_context
23
+ end
24
+
25
+ # Just like RSolr::Client #build_request
26
+ # but converts the page and per_page
27
+ # arguments into :rows and :start.
28
+ def build_paginated_request page, per_page, path, opts = {}
29
+ opts[:page] = page
30
+ opts[:per_page] = per_page
31
+ opts[:params] ||= {}
32
+ values = RSolr::Pagination.calculate_start_and_rows(page, per_page)
33
+ opts[:params][:start] = values[0]
34
+ opts[:params][:rows] = values[1]
35
+ build_request path, opts
36
+ end
37
+
38
+ protected
39
+
40
+ # Checks if the called method starts
41
+ # with "paginate_" and
42
+ # converts the paginate_* to the solr
43
+ # request path. It then calls paginate
44
+ # with the appropriate arguments.
45
+ # If the called method doesn't
46
+ # start with "paginate_",
47
+ # the original/super
48
+ # RSolr::Client #method_missing
49
+ # method is called.
50
+ def method_missing name, *args
51
+ if name.to_s =~ /^paginate_(.+)$/
52
+ paginate args[0], args[1], $1, *args[2..-1]
53
+ else
54
+ super name, *args
55
+ end
56
+ end
57
+
58
+ # Overrides the RSolr::Client #evaluate_ruby_response method.
59
+ # Calls the original/super
60
+ # RSolr::Client #evaluate_ruby_response method.
61
+ # Mixes in the PaginatedResponse if
62
+ # the request[:page] and request[:per_page]
63
+ # opts are set.
64
+ def evaluate_ruby_response request, response
65
+ result = super request, response
66
+ result.extend(PaginatedResponse) if request[:page] && request[:per_page]
67
+ result
68
+ end
69
+
70
+ end
71
+
72
+ module PaginatedResponse
73
+ # TODO: self["responseHeader"]["params"]["rows"]
74
+ # will not be available if omitHeader is false...
75
+ # so, a simple "extend" probably isn't going to cut it.
76
+ def self.extended base
77
+ return unless base["response"] && base["response"]["docs"]
78
+ d = base['response']['docs']
79
+ d.extend PaginatedDocSet
80
+ d.per_page = self["responseHeader"]["params"]["rows"].to_s.to_i rescue 10
81
+ d.start = base["response"]["start"].to_s.to_i
82
+ d.total = base["response"]["numFound"].to_s.to_i
83
+ end
84
+ end
85
+
86
+ # A response module which gets mixed into the solr ["response"]["docs"] array.
87
+ module PaginatedDocSet
88
+
89
+ attr_accessor :start, :per_page, :total
90
+
91
+ # Returns the current page calculated from 'rows' and 'start'
92
+ # WillPaginate hook
93
+ def current_page
94
+ return 1 if start < 1
95
+ per_page_normalized = per_page < 1 ? 1 : per_page
96
+ @current_page ||= (start / per_page_normalized).ceil + 1
97
+ end
98
+
99
+ # Calcuates the total pages from 'numFound' and 'rows'
100
+ # WillPaginate hook
101
+ def total_pages
102
+ @total_pages ||= per_page > 0 ? (total / per_page.to_f).ceil : 1
103
+ end
104
+
105
+ # returns the previous page number or 1
106
+ # WillPaginate hook
107
+ def previous_page
108
+ @previous_page ||= (current_page > 1) ? current_page - 1 : 1
109
+ end
110
+
111
+ # returns the next page number or the last
112
+ # WillPaginate hook
113
+ def next_page
114
+ @next_page ||= (current_page == total_pages) ? total_pages : current_page+1
115
+ end
116
+
117
+ def has_next?
118
+ current_page < total_pages
119
+ end
120
+
121
+ def has_previous?
122
+ current_page > 1
123
+ end
124
+
125
+ end
126
+
127
+ end
data/lib/rsolr.rb CHANGED
@@ -4,7 +4,7 @@ require 'rubygems'
4
4
 
5
5
  module RSolr
6
6
 
7
- %W(Char Client Connectable Error Http Uri Xml).each{|n|autoload n.to_sym, "rsolr/#{n.downcase}"}
7
+ %W(Char Client Error Connection Pagination Uri Xml).each{|n|autoload n.to_sym, "rsolr/#{n.downcase}"}
8
8
 
9
9
  def self.version
10
10
  @version ||= File.read(File.join(File.dirname(__FILE__), '..', 'VERSION')).chomp
@@ -13,7 +13,7 @@ module RSolr
13
13
  VERSION = self.version
14
14
 
15
15
  def self.connect *args
16
- Client.new Http.new(*args)
16
+ Client.new Connection.new, *args
17
17
  end
18
18
 
19
19
  # RSolr.escape
@@ -4,8 +4,8 @@ describe "RSolr::Client" do
4
4
  module ClientHelper
5
5
  def client
6
6
  @client ||= (
7
- connection = RSolr::Http.new :url => "http://localhost:9999/solr"
8
- RSolr::Client.new connection
7
+ connection = RSolr::Connection.new
8
+ RSolr::Client.new connection, :url => "http://localhost:9999/solr"
9
9
  )
10
10
  end
11
11
  end
@@ -16,13 +16,13 @@ describe "RSolr::Client" do
16
16
  end
17
17
  end
18
18
 
19
- context "send_request" do
19
+ context "send_and_receive" do
20
20
  include ClientHelper
21
21
  it "should forward these method calls the #connection object" do
22
22
  [:get, :post, :head].each do |meth|
23
- client.connection.should_receive(:send_request).
23
+ client.connection.should_receive(:execute).
24
24
  and_return({:status => 200, :body => "{}", :headers => {}})
25
- client.send_request '', :method => meth, :params => {}, :data => nil, :headers => {}
25
+ client.send_and_receive '', :method => meth, :params => {}, :data => nil, :headers => {}
26
26
  end
27
27
  end
28
28
  end
@@ -30,19 +30,16 @@ describe "RSolr::Client" do
30
30
  context "post" do
31
31
  include ClientHelper
32
32
  it "should pass the expected params to the connection's #post method" do
33
- client.connection.should_receive(:send_request).
33
+ client.connection.should_receive(:execute).
34
34
  with(
35
- "update", {:headers=>{"Content-Type"=>"text/plain"}, :method=>:post, :data=>"the data"}
35
+ client, hash_including({:path => "update", :headers=>{"Content-Type"=>"text/plain"}, :method=>:post, :data=>"the data"})
36
36
  ).
37
37
  and_return(
38
- :params=>{:wt=>:ruby},
39
- :query=>"wt=ruby",
40
- :path => "update",
41
- :data=>"the data",
42
- :method=>:post,
43
- :headers=>{"Content-Type"=>"text/plain"}
38
+ :body => "",
39
+ :status => 200,
40
+ :headers => {"Content-Type"=>"text/plain"}
44
41
  )
45
- client.post "update", :data => "the data", :headers => {"Content-Type" => "text/plain"}
42
+ client.post "update", :data => "the data", :method=>:post, :headers => {"Content-Type" => "text/plain"}
46
43
  end
47
44
  end
48
45
 
@@ -56,17 +53,14 @@ describe "RSolr::Client" do
56
53
  context "add" do
57
54
  include ClientHelper
58
55
  it "should send xml to the connection's #post method" do
59
- client.connection.should_receive(:send_request).
56
+ client.connection.should_receive(:execute).
60
57
  with(
61
- "update", {:headers=>{"Content-Type"=>"text/xml"}, :method=>:post, :data=>"<xml/>"}
58
+ client, hash_including({:path => "update", :headers=>{"Content-Type"=>"text/xml"}, :method=>:post, :data=>"<xml/>"})
62
59
  ).
63
60
  and_return(
64
- :path => "update",
65
- :data => "<xml/>",
66
- :headers => {"Content-Type"=>"text/xml"},
67
- :method => :post,
68
- :query => "wt=ruby",
69
- :params => {:wt=>:ruby}
61
+ :body => "",
62
+ :status => 200,
63
+ :headers => {"Content-Type"=>"text/xml"}
70
64
  )
71
65
  # the :xml attr is lazy loaded... so load it up first
72
66
  client.xml
@@ -80,17 +74,14 @@ describe "RSolr::Client" do
80
74
  context "update" do
81
75
  include ClientHelper
82
76
  it "should send data to the connection's #post method" do
83
- client.connection.should_receive(:send_request).
77
+ client.connection.should_receive(:execute).
84
78
  with(
85
- "update", {:headers=>{"Content-Type"=>"text/xml"}, :method=>:post, :data=>"<optimize/>"}
79
+ client, hash_including({:path => "update", :headers=>{"Content-Type"=>"text/xml"}, :method=>:post, :data=>"<optimize/>"})
86
80
  ).
87
81
  and_return(
88
- :path => "update",
89
- :data => "<optimize/>",
90
- :headers => {"Content-Type"=>"text/xml"},
91
- :method => :post,
92
- :query => "wt=ruby",
93
- :params => {:wt=>:ruby}
82
+ :body => "",
83
+ :status => 200,
84
+ :headers => {"Content-Type"=>"text/xml"}
94
85
  )
95
86
  client.update(:data => "<optimize/>")
96
87
  end
@@ -100,17 +91,14 @@ describe "RSolr::Client" do
100
91
  include ClientHelper
101
92
  [:commit, :optimize, :rollback].each do |meth|
102
93
  it "should send a #{meth} message to the connection's #post method" do
103
- client.connection.should_receive(:send_request).
94
+ client.connection.should_receive(:execute).
104
95
  with(
105
- "update", {:headers=>{"Content-Type"=>"text/xml"}, :method=>:post, :data=>"<?xml version=\"1.0\" encoding=\"UTF-8\"?><#{meth}/>"}
96
+ client, hash_including({:path => "update", :headers=>{"Content-Type"=>"text/xml"}, :method=>:post, :data=>"<?xml version=\"1.0\" encoding=\"UTF-8\"?><#{meth}/>"})
106
97
  ).
107
98
  and_return(
108
- :path => "update",
109
- :data => "<?xml version=\"1.0\" encoding=\"UTF-8\"?><#{meth}/>",
110
- :headers => {"Content-Type"=>"text/xml"},
111
- :method => :post,
112
- :query => "wt=ruby",
113
- :params => {:wt=>:ruby}
99
+ :body => "",
100
+ :status => 200,
101
+ :headers => {"Content-Type"=>"text/xml"}
114
102
  )
115
103
  client.send meth
116
104
  end
@@ -120,17 +108,14 @@ describe "RSolr::Client" do
120
108
  context "delete_by_id" do
121
109
  include ClientHelper
122
110
  it "should send data to the connection's #post method" do
123
- client.connection.should_receive(:send_request).
111
+ client.connection.should_receive(:execute).
124
112
  with(
125
- "update", {:headers=>{"Content-Type"=>"text/xml"}, :method=>:post, :data=>"<?xml version=\"1.0\" encoding=\"UTF-8\"?><delete><id>1</id></delete>"}
113
+ client, hash_including({:path => "update", :headers=>{"Content-Type"=>"text/xml"}, :method=>:post, :data=>"<?xml version=\"1.0\" encoding=\"UTF-8\"?><delete><id>1</id></delete>"})
126
114
  ).
127
115
  and_return(
128
- :path => "update",
129
- :data => "<?xml version=\"1.0\" encoding=\"UTF-8\"?><delete><id>1</id></delete>",
130
- :headers => {"Content-Type"=>"text/xml"},
131
- :method => :post,
132
- :query => "wt=ruby",
133
- :params => {:wt=>:ruby}
116
+ :body => "",
117
+ :status => 200,
118
+ :headers => {"Content-Type"=>"text/xml"}
134
119
  )
135
120
  client.delete_by_id 1
136
121
  end
@@ -139,20 +124,64 @@ describe "RSolr::Client" do
139
124
  context "delete_by_query" do
140
125
  include ClientHelper
141
126
  it "should send data to the connection's #post method" do
142
- client.connection.should_receive(:send_request).
127
+ client.connection.should_receive(:execute).
143
128
  with(
144
- "update", {:headers=>{"Content-Type"=>"text/xml"}, :method=>:post, :data=>"<?xml version=\"1.0\" encoding=\"UTF-8\"?><delete><query fq=\"category:&quot;trash&quot;\"/></delete>"}
129
+ client, hash_including({:path => "update", :headers=>{"Content-Type"=>"text/xml"}, :method=>:post, :data=>"<?xml version=\"1.0\" encoding=\"UTF-8\"?><delete><query fq=\"category:&quot;trash&quot;\"/></delete>"})
145
130
  ).
146
131
  and_return(
147
- :path => "update",
148
- :data => "<?xml version=\"1.0\" encoding=\"UTF-8\"?><delete><query fq=\"category:&quot;trash&quot;\"/></delete>",
149
- :headers => {"Content-Type"=>"text/xml"},
150
- :method => :post,
151
- :query => "wt=ruby",
152
- :params => {:wt=>:ruby}
132
+ :body => "",
133
+ :status => 200,
134
+ :headers => {"Content-Type"=>"text/xml"}
153
135
  )
154
136
  client.delete_by_query :fq => "category:\"trash\""
155
137
  end
156
138
  end
157
139
 
140
+ context "adapt_response" do
141
+ include ClientHelper
142
+ it 'should not try to evaluate ruby when the :qt is not :ruby' do
143
+ body = '{:time=>"NOW"}'
144
+ result = client.adapt_response({:params=>{}}, {:status => 200, :body => body, :headers => {}})
145
+ result.should be_a(String)
146
+ result.should == body
147
+ end
148
+
149
+ it 'should evaluate ruby responses when the :wt is :ruby' do
150
+ body = '{:time=>"NOW"}'
151
+ result = client.adapt_response({:params=>{:wt=>:ruby}}, {:status => 200, :body => body, :headers => {}})
152
+ result.should be_a(Hash)
153
+ result.should == {:time=>"NOW"}
154
+ end
155
+
156
+ it "ought raise a RSolr::Error::InvalidRubyResponse when the ruby is indeed frugged" do
157
+ lambda {
158
+ client.adapt_response({:params=>{:wt => :ruby}}, {:status => 200, :body => "<woops/>", :headers => {}})
159
+ }.should raise_error RSolr::Error::InvalidRubyResponse
160
+ end
161
+
162
+ end
163
+
164
+ context "build_request" do
165
+ include ClientHelper
166
+ it 'should return a request context array' do
167
+ result = client.build_request 'select', :method => :post, :params => {:q=>'test', :fq=>[0,1]}, :data => "data", :headers => {}
168
+ [/fq=0/, /fq=1/, /q=test/, /wt=ruby/].each do |pattern|
169
+ result[:query].should match pattern
170
+ end
171
+ result[:data].should == "data"
172
+ result[:headers].should == {}
173
+ end
174
+
175
+ it "should set the Content-Type header to application/x-www-form-urlencoded if a hash is passed in to the data arg" do
176
+ result = client.build_request 'select', :method => :post, :data => {:q=>'test', :fq=>[0,1]}, :headers => {}
177
+ result[:query].should == "wt=ruby"
178
+ [/fq=0/, /fq=1/, /q=test/].each do |pattern|
179
+ result[:data].should match pattern
180
+ end
181
+ result[:data].should_not match /wt=ruby/
182
+ result[:headers].should == {"Content-Type" => "application/x-www-form-urlencoded"}
183
+ end
184
+
185
+ end
186
+
158
187
  end
@@ -1,29 +1,4 @@
1
1
  require 'spec_helper'
2
2
  describe "RSolr::Http" do
3
- it "Should be an RSolr::Connectable and implement an execute method" do
4
- http = RSolr::Http.new
5
- http.should be_a(RSolr::Connectable)
6
- http.should respond_to(:execute)
7
- end
8
3
 
9
- context "execute" do
10
- it "should require a request_context hash" do
11
- http = RSolr::Http.new
12
- lambda {
13
- http.execute
14
- }.should raise_error(ArgumentError)
15
- end
16
- end
17
-
18
- # it "should raise an Http error if the response status code aint right" do
19
- # client.connection.should_receive(:get).
20
- # and_return({:status => 400, :body => "", :headers => {}})
21
- # lambda{
22
- # client.send_request '', :method => :get
23
- # }.should raise_error(RSolr::Error::Http) {|error|
24
- # error.should be_a(RSolr::Error::Http)
25
- # error.should respond_to(:request)
26
- # error.should respond_to(:response)
27
- # }
28
- # end
29
4
  end
metadata CHANGED
@@ -6,8 +6,8 @@ version: !ruby/object:Gem::Version
6
6
  - 1
7
7
  - 0
8
8
  - 0
9
- - beta3
10
- version: 1.0.0.beta3
9
+ - beta4
10
+ version: 1.0.0.beta4
11
11
  platform: ruby
12
12
  authors:
13
13
  - Matt Mitchell
@@ -15,7 +15,7 @@ autorequire:
15
15
  bindir: bin
16
16
  cert_chain: []
17
17
 
18
- date: 2010-08-18 00:00:00 -04:00
18
+ date: 2010-10-17 00:00:00 -04:00
19
19
  default_executable:
20
20
  dependencies:
21
21
  - !ruby/object:Gem::Dependency
@@ -48,9 +48,9 @@ files:
48
48
  - lib/rsolr.rb
49
49
  - lib/rsolr/char.rb
50
50
  - lib/rsolr/client.rb
51
- - lib/rsolr/connectable.rb
51
+ - lib/rsolr/connection.rb
52
52
  - lib/rsolr/error.rb
53
- - lib/rsolr/http.rb
53
+ - lib/rsolr/pagination.rb
54
54
  - lib/rsolr/uri.rb
55
55
  - lib/rsolr/xml.rb
56
56
  has_rdoc: true
@@ -88,7 +88,6 @@ summary: A Ruby client for Apache Solr
88
88
  test_files:
89
89
  - spec/api/char_spec.rb
90
90
  - spec/api/client_spec.rb
91
- - spec/api/connectable_spec.rb
92
91
  - spec/api/error_spec.rb
93
92
  - spec/api/http_spec.rb
94
93
  - spec/api/uri_spec.rb
@@ -1,97 +0,0 @@
1
- # Connectable is designed to be shared across solr driver implementations.
2
- # If the driver uses an http url/proxy and returns the standard http respon
3
- # data (status, body, headers) then this module could be used.
4
- module RSolr::Connectable
5
-
6
- attr_reader :uri, :proxy, :options
7
-
8
- def initialize options = {}
9
- url = options[:url] || 'http://127.0.0.1:8983/solr/'
10
- url << "/" unless url[-1] == ?/
11
- proxy_url = options[:proxy]
12
- proxy_url << "/" unless proxy_url.nil? or proxy_url[-1] == ?/
13
- @uri = RSolr::Uri.create url
14
- @proxy = RSolr::Uri.create proxy_url if proxy_url
15
- @options = options
16
- end
17
-
18
- #
19
- def base_request_uri
20
- base_uri.request_uri
21
- end
22
-
23
- def base_uri
24
- @proxy || @uri
25
- end
26
-
27
- # creates a request context hash,
28
- # sends it to the connection.execute method
29
- # which returns a simple hash,
30
- # then passes the request/response into adapt_response.
31
- def send_request path, opts
32
- request_context = build_request path, opts
33
- raw_response = execute request_context
34
- adapt_response request_context, raw_response
35
- end
36
-
37
- # all connection imlementations that use this mixin need to create an execute method
38
- def execute request_context
39
- raise "You gotta implement this method and return a hash like => {:status => <integer>, :body => <string>, :headers => <hash>}"
40
- end
41
-
42
- # build_request sets up the uri/query string
43
- # and converts the +data+ arg to form-urlencoded
44
- # if the +data+ arg is a hash.
45
- # returns a hash with the following keys:
46
- # :method
47
- # :params
48
- # :headers
49
- # :data
50
- # :uri
51
- # :path
52
- # :query
53
- def build_request path, opts
54
- raise "path must be a string or symbol, not #{path.inspect}" unless [String,Symbol].include?(path.class)
55
- path = path.to_s
56
- opts[:method] ||= :get
57
- raise "The :data option can only be used if :method => :post" if opts[:method] != :post and opts[:data]
58
- opts[:params] = opts[:params].nil? ? {:wt => :ruby} : {:wt => :ruby}.merge(opts[:params])
59
- query = RSolr::Uri.params_to_solr(opts[:params]) unless opts[:params].empty?
60
- opts[:query] = query
61
- if opts[:data].is_a? Hash
62
- opts[:data] = RSolr::Uri.params_to_solr opts[:data]
63
- opts[:headers] ||= {}
64
- opts[:headers]['Content-Type'] ||= 'application/x-www-form-urlencoded'
65
- end
66
- opts[:path] = path
67
- opts[:uri] = base_uri.merge(path.to_s + (query ? "?#{query}" : "")) if base_uri
68
- opts
69
- end
70
-
71
- # This method will evaluate the :body value
72
- # if the params[:uri].params[:wt] == :ruby
73
- # ... otherwise, the body is returned as is.
74
- # The return object has methods attached, :request and :response.
75
- # These methods give you access to the original
76
- # request and response from the connection.
77
- #
78
- # +adapt_response+ will raise an InvalidRubyResponse
79
- # if :wt == :ruby and the body
80
- # couldn't be evaluated.
81
- def adapt_response request, response
82
- raise "The response does not have the correct keys => :body, :headers, :status" unless
83
- %W(body headers status) == response.keys.map{|k|k.to_s}.sort
84
- raise RSolr::Error::Http.new request, response unless
85
- [200,302].include? response[:status]
86
- data = response[:body]
87
- if request[:params][:wt] == :ruby
88
- begin
89
- data = Kernel.eval data.to_s
90
- rescue SyntaxError
91
- raise RSolr::Error::InvalidRubyResponse.new request, response
92
- end
93
- end
94
- data
95
- end
96
-
97
- end
@@ -1,56 +0,0 @@
1
- require 'spec_helper'
2
-
3
- describe "RSolr::Connectable" do
4
-
5
- def connectable
6
- Object.new.extend RSolr::Connectable
7
- end
8
-
9
- context "adapt_response" do
10
-
11
- it 'should not try to evaluate ruby when the :qt is not :ruby' do
12
- body = '{:time=>"NOW"}'
13
- result = connectable.adapt_response({:params=>{}}, {:status => 200, :body => body, :headers => {}})
14
- result.should be_a(String)
15
- result.should == body
16
- end
17
-
18
- it 'should evaluate ruby responses when the :wt is :ruby' do
19
- body = '{:time=>"NOW"}'
20
- result = connectable.adapt_response({:params=>{:wt=>:ruby}}, {:status => 200, :body => body, :headers => {}})
21
- result.should be_a(Hash)
22
- result.should == {:time=>"NOW"}
23
- end
24
-
25
- it "ought raise a RSolr::Error::InvalidRubyResponse when the ruby is indeed frugged" do
26
- lambda {
27
- connectable.adapt_response({:params=>{:wt => :ruby}}, {:status => 200, :body => "<woops/>", :headers => {}})
28
- }.should raise_error RSolr::Error::InvalidRubyResponse
29
- end
30
-
31
- end
32
-
33
- context "build_request" do
34
-
35
- it 'should return a request context array' do
36
- result = connectable.build_request 'select', :method => :post, :params => {:q=>'test', :fq=>[0,1]}, :data => "data", :headers => {}
37
- [/fq=0/, /fq=1/, /q=test/, /wt=ruby/].each do |pattern|
38
- result[:query].should match pattern
39
- end
40
- result[:data].should == "data"
41
- result[:headers].should == {}
42
- end
43
-
44
- it "should set the Content-Type header to application/x-www-form-urlencoded if a hash is passed in to the data arg" do
45
- result = connectable.build_request 'select', :method => :post, :data => {:q=>'test', :fq=>[0,1]}, :headers => {}
46
- result[:query].should == "wt=ruby"
47
- [/fq=0/, /fq=1/, /q=test/].each do |pattern|
48
- result[:data].should match pattern
49
- end
50
- result[:data].should_not match /wt=ruby/
51
- result[:headers].should == {"Content-Type" => "application/x-www-form-urlencoded"}
52
- end
53
-
54
- end
55
-
56
- end