rsolr 1.0.0.beta3 → 1.0.0.beta4

Sign up to get free protection for your applications and to get access to all the features.
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