telvue-rsolr 2.2.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/.gitignore +13 -0
- data/.rspec +2 -0
- data/.travis.yml +14 -0
- data/CHANGES.txt +47 -0
- data/Gemfile +5 -0
- data/LICENSE +13 -0
- data/README.rdoc +229 -0
- data/Rakefile +19 -0
- data/lib/rsolr.rb +52 -0
- data/lib/rsolr/char.rb +6 -0
- data/lib/rsolr/client.rb +342 -0
- data/lib/rsolr/document.rb +59 -0
- data/lib/rsolr/error.rb +136 -0
- data/lib/rsolr/field.rb +87 -0
- data/lib/rsolr/generator.rb +5 -0
- data/lib/rsolr/json.rb +60 -0
- data/lib/rsolr/response.rb +95 -0
- data/lib/rsolr/uri.rb +25 -0
- data/lib/rsolr/version.rb +7 -0
- data/lib/rsolr/xml.rb +150 -0
- data/rsolr.gemspec +46 -0
- data/spec/api/client_spec.rb +355 -0
- data/spec/api/document_spec.rb +48 -0
- data/spec/api/error_spec.rb +47 -0
- data/spec/api/json_spec.rb +198 -0
- data/spec/api/pagination_spec.rb +31 -0
- data/spec/api/rsolr_spec.rb +31 -0
- data/spec/api/uri_spec.rb +37 -0
- data/spec/api/xml_spec.rb +255 -0
- data/spec/fixtures/basic_configs/_rest_managed.json +1 -0
- data/spec/fixtures/basic_configs/currency.xml +67 -0
- data/spec/fixtures/basic_configs/lang/stopwords_en.txt +54 -0
- data/spec/fixtures/basic_configs/protwords.txt +21 -0
- data/spec/fixtures/basic_configs/schema.xml +530 -0
- data/spec/fixtures/basic_configs/solrconfig.xml +572 -0
- data/spec/fixtures/basic_configs/stopwords.txt +14 -0
- data/spec/fixtures/basic_configs/synonyms.txt +29 -0
- data/spec/integration/solr5_spec.rb +34 -0
- data/spec/spec_helper.rb +94 -0
- metadata +232 -0
data/lib/rsolr/uri.rb
ADDED
@@ -0,0 +1,25 @@
|
|
1
|
+
require 'uri'
|
2
|
+
|
3
|
+
module RSolr::Uri
|
4
|
+
# Creates a Solr based query string.
|
5
|
+
# Keys that have arrays values are set multiple times:
|
6
|
+
# params_to_solr(:q => 'query', :fq => ['a', 'b'])
|
7
|
+
# is converted to:
|
8
|
+
# ?q=query&fq=a&fq=b
|
9
|
+
# @param [boolean] escape false if no URI escaping is to be performed. Default true.
|
10
|
+
# @return [String] Solr query params as a String, suitable for use in a url
|
11
|
+
def self.params_to_solr(params, escape = true)
|
12
|
+
return URI.encode_www_form(params.reject{|k,v| k.to_s.empty? || v.to_s.empty?}) if escape
|
13
|
+
|
14
|
+
# escape = false if we are here
|
15
|
+
mapped = params.map do |k, v|
|
16
|
+
next if v.to_s.empty?
|
17
|
+
if v.class == ::Array
|
18
|
+
params_to_solr(v.map { |x| [k, x] }, false)
|
19
|
+
else
|
20
|
+
"#{k}=#{v}"
|
21
|
+
end
|
22
|
+
end
|
23
|
+
mapped.compact.join("&")
|
24
|
+
end
|
25
|
+
end
|
data/lib/rsolr/xml.rb
ADDED
@@ -0,0 +1,150 @@
|
|
1
|
+
module RSolr::Xml
|
2
|
+
Document = RSolr::Document
|
3
|
+
Field = RSolr::Field
|
4
|
+
|
5
|
+
class Generator < RSolr::Generator
|
6
|
+
class << self
|
7
|
+
attr_accessor :use_nokogiri
|
8
|
+
|
9
|
+
def builder_proc
|
10
|
+
if use_nokogiri
|
11
|
+
require 'nokogiri' unless defined?(::Nokogiri::XML::Builder)
|
12
|
+
:nokogiri_build
|
13
|
+
else
|
14
|
+
require 'builder' unless defined?(::Builder::XmlMarkup)
|
15
|
+
:builder_build
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
self.use_nokogiri = defined?(::Nokogiri::XML::Builder) ? true : false
|
20
|
+
|
21
|
+
CONTENT_TYPE = 'text/xml'.freeze
|
22
|
+
|
23
|
+
def content_type
|
24
|
+
CONTENT_TYPE
|
25
|
+
end
|
26
|
+
|
27
|
+
|
28
|
+
def nokogiri_build &block
|
29
|
+
b = ::Nokogiri::XML::Builder.new do |xml|
|
30
|
+
block_given? ? yield(xml) : xml
|
31
|
+
end
|
32
|
+
'<?xml version="1.0" encoding="UTF-8"?>'+b.to_xml(:indent => 0, :encoding => 'UTF-8', :save_with => ::Nokogiri::XML::Node::SaveOptions::AS_XML | ::Nokogiri::XML::Node::SaveOptions::NO_DECLARATION).strip
|
33
|
+
end
|
34
|
+
protected :nokogiri_build
|
35
|
+
|
36
|
+
def builder_build &block
|
37
|
+
b = ::Builder::XmlMarkup.new(:indent => 0, :margin => 0, :encoding => 'UTF-8')
|
38
|
+
b.instruct!
|
39
|
+
block_given? ? yield(b) : b
|
40
|
+
end
|
41
|
+
protected :builder_build
|
42
|
+
|
43
|
+
def build &block
|
44
|
+
self.send(self.class.builder_proc,&block)
|
45
|
+
end
|
46
|
+
|
47
|
+
# generates "add" xml for updating solr
|
48
|
+
# "data" can be a hash or an array of hashes.
|
49
|
+
# - each hash should be a simple key=>value pair representing a solr doc.
|
50
|
+
# If a value is an array, multiple fields will be created.
|
51
|
+
#
|
52
|
+
# "add_attrs" can be a hash for setting the add xml element attributes.
|
53
|
+
#
|
54
|
+
# This method can also accept a block.
|
55
|
+
# The value yielded to the block is a Message::Document; for each solr doc in "data".
|
56
|
+
# You can set xml element attributes for each "doc" element or individual "field" elements.
|
57
|
+
#
|
58
|
+
# For example:
|
59
|
+
#
|
60
|
+
# solr.add({:id=>1, :nickname=>'Tim'}, {:boost=>5.0, :commitWithin=>1.0}) do |doc_msg|
|
61
|
+
# doc_msg.attrs[:boost] = 10.00 # boost the document
|
62
|
+
# nickname = doc_msg.field_by_name(:nickname)
|
63
|
+
# nickname.attrs[:boost] = 20 if nickname.value=='Tim' # boost a field
|
64
|
+
# end
|
65
|
+
#
|
66
|
+
# would result in an add element having the attributes boost="10.0"
|
67
|
+
# and a commitWithin="1.0".
|
68
|
+
# Each doc element would have a boost="10.0".
|
69
|
+
# The "nickname" field would have a boost="20.0"
|
70
|
+
# if the doc had a "nickname" field with the value of "Tim".
|
71
|
+
#
|
72
|
+
def add data, add_attrs = nil, &block
|
73
|
+
add_attrs ||= {}
|
74
|
+
data = RSolr::Array.wrap(data)
|
75
|
+
build do |xml|
|
76
|
+
xml.add(add_attrs) do |add_node|
|
77
|
+
data.each do |doc|
|
78
|
+
doc = RSolr::Document.new(doc) if doc.respond_to?(:each_pair)
|
79
|
+
yield doc if block_given?
|
80
|
+
doc_node_builder = to_xml(doc)
|
81
|
+
self.class.use_nokogiri ? add_node.doc_(doc.attrs,&doc_node_builder) : add_node.doc(doc.attrs,&doc_node_builder)
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
# generates a <commit/> message
|
88
|
+
def commit opts = nil
|
89
|
+
opts ||= {}
|
90
|
+
build {|xml| xml.commit(opts) }
|
91
|
+
end
|
92
|
+
|
93
|
+
# generates a <optimize/> message
|
94
|
+
def optimize opts = nil
|
95
|
+
opts ||= {}
|
96
|
+
build {|xml| xml.optimize(opts) }
|
97
|
+
end
|
98
|
+
|
99
|
+
# generates a <rollback/> message
|
100
|
+
def rollback
|
101
|
+
build {|xml| xml.rollback({}) }
|
102
|
+
end
|
103
|
+
|
104
|
+
# generates a <delete><id>ID</id></delete> message
|
105
|
+
# "ids" can be a single value or array of values
|
106
|
+
def delete_by_id ids
|
107
|
+
ids = RSolr::Array.wrap(ids)
|
108
|
+
build do |xml|
|
109
|
+
xml.delete do |delete_node|
|
110
|
+
ids.each do |id|
|
111
|
+
self.class.use_nokogiri ? delete_node.id_(id) : delete_node.id(id)
|
112
|
+
end
|
113
|
+
end
|
114
|
+
end
|
115
|
+
end
|
116
|
+
|
117
|
+
# generates a <delete><query>ID</query></delete> message
|
118
|
+
# "queries" can be a single value or an array of values
|
119
|
+
def delete_by_query(queries)
|
120
|
+
queries = RSolr::Array.wrap(queries)
|
121
|
+
build do |xml|
|
122
|
+
xml.delete do |delete_node|
|
123
|
+
queries.each { |query| delete_node.query(query) }
|
124
|
+
end
|
125
|
+
end
|
126
|
+
end
|
127
|
+
|
128
|
+
private
|
129
|
+
|
130
|
+
def to_xml(doc)
|
131
|
+
lambda do |doc_node|
|
132
|
+
doc.fields.each do |field_obj|
|
133
|
+
value = field_obj.value
|
134
|
+
|
135
|
+
if field_obj.name.to_s == RSolr::Document::CHILD_DOCUMENT_KEY
|
136
|
+
child_node_builder = to_xml(field_obj.value)
|
137
|
+
self.class.use_nokogiri ? doc_node.doc_(&child_node_builder) : doc_node.doc(&child_node_builder)
|
138
|
+
elsif value.is_a?(Hash) && value.length == 1 && field_obj.attrs[:update].nil?
|
139
|
+
update_attr, real_value = value.first
|
140
|
+
doc_node.field real_value, field_obj.attrs.merge(update: update_attr)
|
141
|
+
elsif value.nil?
|
142
|
+
doc_node.field field_obj.value, field_obj.attrs.merge(null: true)
|
143
|
+
else
|
144
|
+
doc_node.field field_obj.value, field_obj.attrs
|
145
|
+
end
|
146
|
+
end
|
147
|
+
end
|
148
|
+
end
|
149
|
+
end
|
150
|
+
end
|
data/rsolr.gemspec
ADDED
@@ -0,0 +1,46 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
$:.push File.expand_path("../lib", __FILE__)
|
3
|
+
|
4
|
+
require "rsolr/version"
|
5
|
+
|
6
|
+
Gem::Specification.new do |s|
|
7
|
+
s.name = "telvue-rsolr"
|
8
|
+
s.summary = "A Ruby client for Apache Solr"
|
9
|
+
s.description = %q{RSolr aims to provide a simple and extensible library for working with Solr}
|
10
|
+
s.version = RSolr::VERSION
|
11
|
+
s.authors = ["Antoine Latter", "Dmitry Lihachev",
|
12
|
+
"Lucas Souza", "Peter Kieltyka",
|
13
|
+
"Rob Di Marco", "Magnus Bergmark",
|
14
|
+
"Jonathan Rochkind", "Chris Beer",
|
15
|
+
"Craig Smith", "Randy Souza",
|
16
|
+
"Colin Steele", "Peter Kieltyka",
|
17
|
+
"Lorenzo Riccucci", "Mike Perham",
|
18
|
+
"Mat Brown", "Shairon Toledo",
|
19
|
+
"Matthew Rudy", "Fouad Mardini",
|
20
|
+
"Jeremy Hinegardner", "Nathan Witmer",
|
21
|
+
"Naomi Dushay",
|
22
|
+
"\"shima\"", "Ben Liu"]
|
23
|
+
s.email = ["bliu@telvue.com"]
|
24
|
+
s.license = 'Apache-2.0'
|
25
|
+
s.homepage = "https://github.com/telvue/rsolr"
|
26
|
+
s.rubyforge_project = "rsolr"
|
27
|
+
s.files = `git ls-files`.split("\n")
|
28
|
+
s.test_files = `git ls-files -- {spec}/*`.split("\n")
|
29
|
+
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
30
|
+
s.require_paths = ["lib"]
|
31
|
+
|
32
|
+
s.required_ruby_version = '>= 1.9.3'
|
33
|
+
|
34
|
+
s.requirements << 'Apache Solr'
|
35
|
+
|
36
|
+
s.add_dependency 'builder', '>= 2.1.2'
|
37
|
+
s.add_dependency 'faraday', '>= 0.9.0'
|
38
|
+
|
39
|
+
s.add_development_dependency 'activesupport'
|
40
|
+
s.add_development_dependency 'nokogiri', '>= 1.4.0'
|
41
|
+
s.add_development_dependency 'rake', '>= 10.0'
|
42
|
+
s.add_development_dependency 'rdoc', '>= 4.0'
|
43
|
+
s.add_development_dependency 'rspec', '~> 3.0'
|
44
|
+
s.add_development_dependency 'simplecov'
|
45
|
+
s.add_development_dependency 'solr_wrapper'
|
46
|
+
end
|
@@ -0,0 +1,355 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
RSpec.describe RSolr::Client do
|
4
|
+
let(:connection) { nil }
|
5
|
+
let(:url) { "http://localhost:9999/solr" }
|
6
|
+
let(:connection_options) { { url: url, read_timeout: 42, open_timeout: 43, update_format: :xml } }
|
7
|
+
|
8
|
+
let(:client) do
|
9
|
+
RSolr::Client.new connection, connection_options
|
10
|
+
end
|
11
|
+
|
12
|
+
let(:client_with_proxy) do
|
13
|
+
RSolr::Client.new connection, connection_options.merge(proxy: 'http://localhost:8080')
|
14
|
+
end
|
15
|
+
|
16
|
+
context "initialize" do
|
17
|
+
it "should accept whatevs and set it as the @connection" do
|
18
|
+
expect(RSolr::Client.new(:whatevs).connection).to eq(:whatevs)
|
19
|
+
end
|
20
|
+
|
21
|
+
it "should use :update_path from options" do
|
22
|
+
client = RSolr::Client.new(:whatevs, { update_path: 'update_test' })
|
23
|
+
expect(client.update_path).to eql('update_test')
|
24
|
+
end
|
25
|
+
|
26
|
+
it "should use 'update' for update_path by default" do
|
27
|
+
client = RSolr::Client.new(:whatevs)
|
28
|
+
expect(client.update_path).to eql('update')
|
29
|
+
end
|
30
|
+
|
31
|
+
it "should use :proxy from options" do
|
32
|
+
client = RSolr::Client.new(:whatevs, { proxy: 'http://my.proxy/' })
|
33
|
+
expect(client.proxy.to_s).to eql('http://my.proxy/')
|
34
|
+
end
|
35
|
+
|
36
|
+
it "should use 'nil' for proxy by default" do
|
37
|
+
client = RSolr::Client.new(:whatevs)
|
38
|
+
expect(client.proxy).to be_nil
|
39
|
+
end
|
40
|
+
|
41
|
+
it "should use 'false' for proxy if passed 'false'" do
|
42
|
+
client = RSolr::Client.new(:whatevs, { proxy: false })
|
43
|
+
expect(client.proxy).to eq(false)
|
44
|
+
end
|
45
|
+
|
46
|
+
context "with an non-HTTP url" do
|
47
|
+
let(:url) { "fake://localhost:9999/solr" }
|
48
|
+
|
49
|
+
it "raises an argument error" do
|
50
|
+
expect { client }.to raise_error ArgumentError, "You must provide an HTTP(S) url."
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
context "with an HTTPS url" do
|
55
|
+
let(:url) { "https://localhost:9999/solr" }
|
56
|
+
|
57
|
+
it "creates a connection" do
|
58
|
+
expect(client.uri).to be_kind_of URI::HTTPS
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
end
|
63
|
+
|
64
|
+
context "send_and_receive" do
|
65
|
+
it "should forward these method calls the #connection object" do
|
66
|
+
[:get, :post, :head].each do |meth|
|
67
|
+
expect(client).to receive(:execute).
|
68
|
+
and_return({:status => 200, :body => "{}", :headers => {}})
|
69
|
+
client.send_and_receive '', :method => meth, :params => {}, :data => nil, :headers => {}
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
context "post" do
|
75
|
+
it "should pass the expected params to the connection's #execute method" do
|
76
|
+
request_opts = {:data => "the data", :method=>:post, :headers => {"Content-Type" => "text/plain"}}
|
77
|
+
expect(client).to receive(:execute).
|
78
|
+
with(hash_including(request_opts)).
|
79
|
+
and_return(
|
80
|
+
:body => "",
|
81
|
+
:status => 200,
|
82
|
+
:headers => {"Content-Type"=>"text/plain"}
|
83
|
+
)
|
84
|
+
client.post "update", request_opts
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
context "add" do
|
89
|
+
it "should send xml to the connection's #post method" do
|
90
|
+
expect(client).to receive(:execute).
|
91
|
+
with(
|
92
|
+
hash_including({
|
93
|
+
:path => "update",
|
94
|
+
:headers => {"Content-Type"=>"text/xml"},
|
95
|
+
:method => :post,
|
96
|
+
:data => "<xml/>"
|
97
|
+
})
|
98
|
+
).
|
99
|
+
and_return(
|
100
|
+
:body => "",
|
101
|
+
:status => 200,
|
102
|
+
:headers => {"Content-Type"=>"text/xml"}
|
103
|
+
)
|
104
|
+
expect(client.builder).to receive(:add).
|
105
|
+
with({:id=>1}, {:commitWith=>10}).
|
106
|
+
and_return("<xml/>")
|
107
|
+
client.add({:id=>1}, :add_attributes => {:commitWith=>10})
|
108
|
+
end
|
109
|
+
|
110
|
+
context 'when the client is configured for json updates' do
|
111
|
+
let(:client) do
|
112
|
+
RSolr::Client.new nil, :url => "http://localhost:9999/solr", :read_timeout => 42, :open_timeout=>43, :update_format => :json
|
113
|
+
end
|
114
|
+
it "should send json to the connection's #post method" do
|
115
|
+
expect(client).to receive(:execute).
|
116
|
+
with(hash_including({
|
117
|
+
:path => 'update',
|
118
|
+
:headers => {"Content-Type" => 'application/json'},
|
119
|
+
:method => :post,
|
120
|
+
:data => '{"hello":"this is json"}'
|
121
|
+
})
|
122
|
+
).
|
123
|
+
and_return(
|
124
|
+
:body => "",
|
125
|
+
:status => 200,
|
126
|
+
:headers => {"Content-Type"=>"text/xml"}
|
127
|
+
)
|
128
|
+
expect(client.builder).to receive(:add).
|
129
|
+
with({:id => 1}, {:commitWith=>10}).
|
130
|
+
and_return('{"hello":"this is json"}')
|
131
|
+
client.add({:id=>1}, :add_attributes => {:commitWith=>10})
|
132
|
+
end
|
133
|
+
|
134
|
+
it "should send json to the connection's #post method" do
|
135
|
+
expect(client).to receive(:execute).
|
136
|
+
with(hash_including({
|
137
|
+
:path => 'update',
|
138
|
+
:headers => {'Content-Type'=>'application/json'},
|
139
|
+
:method => :post,
|
140
|
+
:data => '{"optimise" : {}}'
|
141
|
+
})
|
142
|
+
).
|
143
|
+
and_return(
|
144
|
+
:body => "",
|
145
|
+
:status => 200,
|
146
|
+
:headers => {"Content-Type"=>"text/xml"}
|
147
|
+
)
|
148
|
+
client.update(:data => '{"optimise" : {}}')
|
149
|
+
end
|
150
|
+
end
|
151
|
+
end
|
152
|
+
|
153
|
+
context "update" do
|
154
|
+
it "should send data to the connection's #post method" do
|
155
|
+
expect(client).to receive(:execute).
|
156
|
+
with(hash_including({
|
157
|
+
:path => "update",
|
158
|
+
:headers => {"Content-Type"=>"text/xml"},
|
159
|
+
:method => :post,
|
160
|
+
:data => "<optimize/>"
|
161
|
+
})
|
162
|
+
).
|
163
|
+
and_return(
|
164
|
+
:body => "",
|
165
|
+
:status => 200,
|
166
|
+
:headers => {"Content-Type"=>"text/xml"}
|
167
|
+
)
|
168
|
+
client.update(:data => "<optimize/>")
|
169
|
+
end
|
170
|
+
|
171
|
+
it "should use #update_path" do
|
172
|
+
expect(client).to receive(:post).with('update_test', any_args)
|
173
|
+
expect(client).to receive(:update_path).and_return('update_test')
|
174
|
+
client.update({})
|
175
|
+
end
|
176
|
+
|
177
|
+
it "should use path from opts" do
|
178
|
+
expect(client).to receive(:post).with('update_opts', any_args)
|
179
|
+
allow(client).to receive(:update_path).and_return('update_test')
|
180
|
+
client.update({path: 'update_opts'})
|
181
|
+
end
|
182
|
+
end
|
183
|
+
|
184
|
+
context "post based helper methods:" do
|
185
|
+
[:commit, :optimize, :rollback].each do |meth|
|
186
|
+
it "should send a #{meth} message to the connection's #post method" do
|
187
|
+
expect(client).to receive(:execute).
|
188
|
+
with(hash_including({
|
189
|
+
:path => "update",
|
190
|
+
:headers => {"Content-Type"=>"text/xml"},
|
191
|
+
:method => :post,
|
192
|
+
:data => "<?xml version=\"1.0\" encoding=\"UTF-8\"?><#{meth}/>"
|
193
|
+
})
|
194
|
+
).
|
195
|
+
and_return(
|
196
|
+
:body => "",
|
197
|
+
:status => 200,
|
198
|
+
:headers => {"Content-Type"=>"text/xml"}
|
199
|
+
)
|
200
|
+
client.send meth
|
201
|
+
end
|
202
|
+
end
|
203
|
+
end
|
204
|
+
|
205
|
+
context "delete_by_id" do
|
206
|
+
it "should send data to the connection's #post method" do
|
207
|
+
expect(client).to receive(:execute).
|
208
|
+
with(
|
209
|
+
hash_including({
|
210
|
+
:path => "update",
|
211
|
+
:headers => {"Content-Type"=>"text/xml"},
|
212
|
+
:method => :post,
|
213
|
+
:data => "<?xml version=\"1.0\" encoding=\"UTF-8\"?><delete><id>1</id></delete>"
|
214
|
+
})
|
215
|
+
).
|
216
|
+
and_return(
|
217
|
+
:body => "",
|
218
|
+
:status => 200,
|
219
|
+
:headers => {"Content-Type"=>"text/xml"}
|
220
|
+
)
|
221
|
+
client.delete_by_id 1
|
222
|
+
end
|
223
|
+
end
|
224
|
+
|
225
|
+
context "delete_by_query" do
|
226
|
+
it "should send data to the connection's #post method" do
|
227
|
+
expect(client).to receive(:execute).
|
228
|
+
with(
|
229
|
+
hash_including({
|
230
|
+
:path => "update",
|
231
|
+
:headers => {"Content-Type"=>"text/xml"},
|
232
|
+
:method => :post,
|
233
|
+
:data => "<?xml version=\"1.0\" encoding=\"UTF-8\"?><delete><query fq=\"category:"trash"\"/></delete>"
|
234
|
+
})
|
235
|
+
).
|
236
|
+
and_return(
|
237
|
+
:body => "",
|
238
|
+
:status => 200,
|
239
|
+
:headers => {"Content-Type"=>"text/xml"}
|
240
|
+
)
|
241
|
+
client.delete_by_query :fq => "category:\"trash\""
|
242
|
+
end
|
243
|
+
end
|
244
|
+
|
245
|
+
context "adapt_response" do
|
246
|
+
it 'should not try to evaluate ruby when the :qt is not :ruby' do
|
247
|
+
body = '{"time"=>"NOW"}'
|
248
|
+
result = client.adapt_response({:params=>{}}, {:status => 200, :body => body, :headers => {}})
|
249
|
+
expect(result).to eq(body)
|
250
|
+
end
|
251
|
+
|
252
|
+
it 'should evaluate ruby responses when the :wt is :ruby' do
|
253
|
+
body = '{"time"=>"NOW"}'
|
254
|
+
result = client.adapt_response({:params=>{:wt=>:ruby}}, {:status => 200, :body => body, :headers => {}})
|
255
|
+
expect(result).to eq({"time"=>"NOW"})
|
256
|
+
end
|
257
|
+
|
258
|
+
it 'should evaluate json responses when the :wt is :json' do
|
259
|
+
body = '{"time": "NOW"}'
|
260
|
+
result = client.adapt_response({:params=>{:wt=>:json}}, {:status => 200, :body => body, :headers => {}})
|
261
|
+
if defined? JSON
|
262
|
+
expect(result).to eq({"time"=>"NOW"})
|
263
|
+
else
|
264
|
+
# ruby 1.8 without the JSON gem
|
265
|
+
expect(result).to eq('{"time": "NOW"}')
|
266
|
+
end
|
267
|
+
end
|
268
|
+
|
269
|
+
it 'should return a response for a head request' do
|
270
|
+
result = client.adapt_response({:method=>:head,:params=>{}}, {:status => 200, :body => nil, :headers => {}})
|
271
|
+
expect(result.response[:status]).to eq 200
|
272
|
+
end
|
273
|
+
|
274
|
+
it "ought raise a RSolr::Error::InvalidRubyResponse when the ruby is indeed frugged, or even fruggified" do
|
275
|
+
expect {
|
276
|
+
client.adapt_response({:params=>{:wt => :ruby}}, {:status => 200, :body => "<woops/>", :headers => {}})
|
277
|
+
}.to raise_error RSolr::Error::InvalidRubyResponse
|
278
|
+
end
|
279
|
+
|
280
|
+
end
|
281
|
+
|
282
|
+
context "indifferent access" do
|
283
|
+
it "should raise a RuntimeError if the #with_indifferent_access extension isn't loaded" do
|
284
|
+
hide_const("::RSolr::HashWithIndifferentAccessWithResponse")
|
285
|
+
hide_const("ActiveSupport::HashWithIndifferentAccess")
|
286
|
+
body = "{'foo'=>'bar'}"
|
287
|
+
result = client.adapt_response({:params=>{:wt=>:ruby}}, {:status => 200, :body => body, :headers => {}})
|
288
|
+
expect { result.with_indifferent_access }.to raise_error RuntimeError
|
289
|
+
end
|
290
|
+
|
291
|
+
it "should provide indifferent access" do
|
292
|
+
require 'active_support/core_ext/hash/indifferent_access'
|
293
|
+
body = "{'foo'=>'bar'}"
|
294
|
+
result = client.adapt_response({:params=>{:wt=>:ruby}}, {:status => 200, :body => body, :headers => {}})
|
295
|
+
indifferent_result = result.with_indifferent_access
|
296
|
+
|
297
|
+
expect(result).to be_a(RSolr::Response)
|
298
|
+
expect(result['foo']).to eq('bar')
|
299
|
+
expect(result[:foo]).to be_nil
|
300
|
+
|
301
|
+
expect(indifferent_result).to be_a(RSolr::Response)
|
302
|
+
expect(indifferent_result['foo']).to eq('bar')
|
303
|
+
expect(indifferent_result[:foo]).to eq('bar')
|
304
|
+
end
|
305
|
+
end
|
306
|
+
|
307
|
+
context "build_request" do
|
308
|
+
let(:data) { 'data' }
|
309
|
+
let(:params) { { q: 'test', fq: [0,1] } }
|
310
|
+
let(:options) { { method: :post, params: params, data: data, headers: {} } }
|
311
|
+
subject { client.build_request('select', options) }
|
312
|
+
|
313
|
+
context "when params are symbols" do
|
314
|
+
it 'should return a request context array' do
|
315
|
+
[/fq=0/, /fq=1/, /q=test/, /wt=json/].each do |pattern|
|
316
|
+
expect(subject[:query]).to match pattern
|
317
|
+
end
|
318
|
+
expect(subject[:data]).to eq("data")
|
319
|
+
expect(subject[:headers]).to eq({})
|
320
|
+
end
|
321
|
+
end
|
322
|
+
|
323
|
+
context "when params are strings" do
|
324
|
+
let(:params) { { 'q' => 'test', 'wt' => 'json' } }
|
325
|
+
it 'should return a request context array' do
|
326
|
+
expect(subject[:query]).to eq 'q=test&wt=json'
|
327
|
+
expect(subject[:data]).to eq("data")
|
328
|
+
expect(subject[:headers]).to eq({})
|
329
|
+
end
|
330
|
+
end
|
331
|
+
|
332
|
+
context "when a Hash is passed in as data" do
|
333
|
+
let(:data) { { q: 'test', fq: [0,1] } }
|
334
|
+
let(:options) { { method: :post, data: data, headers: {} } }
|
335
|
+
|
336
|
+
it "sets the Content-Type header to application/x-www-form-urlencoded; charset=UTF-8" do
|
337
|
+
expect(subject[:query]).to eq("wt=json")
|
338
|
+
[/fq=0/, /fq=1/, /q=test/].each do |pattern|
|
339
|
+
expect(subject[:data]).to match pattern
|
340
|
+
end
|
341
|
+
expect(subject[:data]).not_to match(/wt=json/)
|
342
|
+
expect(subject[:headers]).to eq({"Content-Type" => "application/x-www-form-urlencoded; charset=UTF-8"})
|
343
|
+
end
|
344
|
+
end
|
345
|
+
|
346
|
+
it "should properly handle proxy configuration" do
|
347
|
+
result = client_with_proxy.build_request('select',
|
348
|
+
:method => :post,
|
349
|
+
:data => {:q=>'test', :fq=>[0,1]},
|
350
|
+
:headers => {}
|
351
|
+
)
|
352
|
+
expect(result[:uri].to_s).to match %r{^http://localhost:9999/solr/}
|
353
|
+
end
|
354
|
+
end
|
355
|
+
end
|