dezi-client 1.0.0
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/lib/dezi/client.rb +262 -0
- data/lib/dezi/doc.rb +69 -0
- data/lib/dezi/response.rb +100 -0
- metadata +124 -0
data/lib/dezi/client.rb
ADDED
@@ -0,0 +1,262 @@
|
|
1
|
+
# DeziClient is a Ruby client for the Dezi search platform.
|
2
|
+
#
|
3
|
+
# Copyright 2013 by Peter Karman
|
4
|
+
#
|
5
|
+
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
# of this software and associated documentation files (the "Software"), to deal
|
7
|
+
# in the Software without restriction, including without limitation the rights
|
8
|
+
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
# copies of the Software, and to permit persons to whom the Software is
|
10
|
+
# furnished to do so, subject to the following conditions:
|
11
|
+
#
|
12
|
+
# The above copyright notice and this permission notice shall be included in
|
13
|
+
# all copies or substantial portions of the Software.
|
14
|
+
#
|
15
|
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
21
|
+
# THE SOFTWARE.
|
22
|
+
|
23
|
+
# dependencies
|
24
|
+
require 'rubygems'
|
25
|
+
require 'rest_client'
|
26
|
+
require 'uri'
|
27
|
+
require 'json'
|
28
|
+
require 'pathname'
|
29
|
+
require 'mime/types'
|
30
|
+
|
31
|
+
# related classes
|
32
|
+
require File.dirname(__FILE__) + '/response'
|
33
|
+
require File.dirname(__FILE__) + '/doc'
|
34
|
+
|
35
|
+
# DeziClient is a Ruby client for indexing and searching documents
|
36
|
+
# with the Dezi REST search platform. See http://dezi.org/ for details
|
37
|
+
# on the server.
|
38
|
+
#
|
39
|
+
# See the test/test_dezi_client.rb for full example.
|
40
|
+
#
|
41
|
+
# Usage:
|
42
|
+
#
|
43
|
+
# client = DeziClient.new(
|
44
|
+
# :server => 'http://localhost:5000',
|
45
|
+
# :username => 'foo',
|
46
|
+
# :password => 'secret',
|
47
|
+
# )
|
48
|
+
#
|
49
|
+
# doc = 'some/path/file.html'
|
50
|
+
# response = client.add(doc) # DeziResponse returned
|
51
|
+
# if !response.is_success
|
52
|
+
# raise "Failed to add #{doc} to server"
|
53
|
+
# end
|
54
|
+
#
|
55
|
+
# # if Dezi server has auto_commit==false
|
56
|
+
# # then must call commit()
|
57
|
+
# client.commit()
|
58
|
+
#
|
59
|
+
# response = client.search('q' => 'search string') # DeziResponse returned
|
60
|
+
# if !response.is_success
|
61
|
+
# raise "Failed to search"
|
62
|
+
# end
|
63
|
+
#
|
64
|
+
# response.results.each |result|
|
65
|
+
# puts "result: #{result.uri}"
|
66
|
+
# end
|
67
|
+
#
|
68
|
+
# Related classes: DeziResponse and DeziDoc
|
69
|
+
#
|
70
|
+
|
71
|
+
class DeziClient
|
72
|
+
|
73
|
+
# attributes
|
74
|
+
attr_accessor :server
|
75
|
+
attr_accessor :about_server
|
76
|
+
attr_accessor :search_uri
|
77
|
+
attr_accessor :index_uri
|
78
|
+
attr_accessor :commit_uri
|
79
|
+
attr_accessor :rollback_uri
|
80
|
+
attr_accessor :fields
|
81
|
+
attr_accessor :facets
|
82
|
+
attr_accessor :last_response
|
83
|
+
attr_accessor :debug
|
84
|
+
attr_accessor :user_agent
|
85
|
+
|
86
|
+
def version
|
87
|
+
return '1.0.0'
|
88
|
+
end
|
89
|
+
|
90
|
+
def initialize(args)
|
91
|
+
@debug = ENV['DEZI_DEBUG']
|
92
|
+
|
93
|
+
if (args.has_key? :server)
|
94
|
+
@server = args[:server]
|
95
|
+
else
|
96
|
+
@server = 'http://localhost:5000'
|
97
|
+
end
|
98
|
+
|
99
|
+
# sanity check
|
100
|
+
begin
|
101
|
+
uri = URI.parse(@server)
|
102
|
+
rescue URI::InvalidURIError => err
|
103
|
+
raise "Bad :server value " + err
|
104
|
+
end
|
105
|
+
if (!uri.host || !uri.port)
|
106
|
+
raise "Bad :server value " + @server
|
107
|
+
end
|
108
|
+
|
109
|
+
if (args.has_key? :username and args.has_key? :password)
|
110
|
+
@un = args[:username]
|
111
|
+
@pw = args[:password]
|
112
|
+
end
|
113
|
+
|
114
|
+
if (args.has_key? :user_agent)
|
115
|
+
@user_agent = args[:user_agent]
|
116
|
+
else
|
117
|
+
@user_agent = 'dezi-client-ruby/'+version()
|
118
|
+
end
|
119
|
+
|
120
|
+
if (args.has_key? :search and args.has_key? :index)
|
121
|
+
@search_uri = @server + args[:search]
|
122
|
+
@index_uri = @server + args[:index]
|
123
|
+
else
|
124
|
+
response = RestClient.get @server, :accept => :json, :user_agent => @user_agent
|
125
|
+
|
126
|
+
if response.code != 200
|
127
|
+
raise "Bad about response from server #{@server}: " . response.to_str
|
128
|
+
end
|
129
|
+
|
130
|
+
@about_server = JSON.parse(response.to_str)
|
131
|
+
if @debug
|
132
|
+
#puts @about_server.inspect
|
133
|
+
end
|
134
|
+
|
135
|
+
@search_uri = @about_server['search']
|
136
|
+
@index_uri = @about_server['index']
|
137
|
+
@commit_uri = @about_server['commit']
|
138
|
+
@rollback_uri = @about_server['rollback']
|
139
|
+
@fields = @about_server['fields']
|
140
|
+
@facets = @about_server['facets']
|
141
|
+
end
|
142
|
+
|
143
|
+
@searcher = RestClient::Resource.new( @search_uri )
|
144
|
+
|
145
|
+
end
|
146
|
+
|
147
|
+
private
|
148
|
+
|
149
|
+
def _put_doc(doc, uri=nil, content_type=nil)
|
150
|
+
body_buf = ""
|
151
|
+
|
152
|
+
if (doc.is_a?(DeziDoc))
|
153
|
+
body_buf = doc.as_string()
|
154
|
+
if (uri == nil)
|
155
|
+
uri = doc.uri
|
156
|
+
end
|
157
|
+
if (content_type == nil)
|
158
|
+
content_type = doc.mime_type
|
159
|
+
end
|
160
|
+
|
161
|
+
elsif (Pathname.new(doc).exist?)
|
162
|
+
file = File.new(doc, 'r')
|
163
|
+
body_buf = file.read()
|
164
|
+
if (uri == nil)
|
165
|
+
uri = doc
|
166
|
+
end
|
167
|
+
|
168
|
+
else
|
169
|
+
body_buf = doc
|
170
|
+
if (uri == nil)
|
171
|
+
raise "uri required"
|
172
|
+
end
|
173
|
+
|
174
|
+
end
|
175
|
+
|
176
|
+
if (!content_type or !content_type.length)
|
177
|
+
content_type = MIME::Types.type_for(uri)[0]
|
178
|
+
end
|
179
|
+
|
180
|
+
server_uri = '/' + uri
|
181
|
+
if (@debug)
|
182
|
+
puts "uri=#{uri}"
|
183
|
+
puts "body=#{body_buf}"
|
184
|
+
puts "content_type="#{content_type}"
|
185
|
+
end
|
186
|
+
|
187
|
+
resource = RestClient::Resource.new(@index_uri + server_uri, @un, @pw)
|
188
|
+
resp = resource.post( body_buf,
|
189
|
+
:accept => :json,
|
190
|
+
:content_type => content_type,
|
191
|
+
:user_agent => @user_agent
|
192
|
+
)
|
193
|
+
|
194
|
+
return DeziResponse.new(resp)
|
195
|
+
end
|
196
|
+
|
197
|
+
public
|
198
|
+
|
199
|
+
# add() takes an initial argument of "doc" which can be a DeziDoc object,
|
200
|
+
# a string representing a Pathname, or a string representing the content of a document.
|
201
|
+
# If "doc" represents the content of a document, additional arguments
|
202
|
+
# "uri" and "content_type" are required.
|
203
|
+
|
204
|
+
def add(doc, uri=nil, content_type=nil)
|
205
|
+
return _put_doc(doc, uri, content_type)
|
206
|
+
end
|
207
|
+
|
208
|
+
# update() takes an initial argument of "doc" which can be a DeziDoc object,
|
209
|
+
# a string representing a Pathname, or a string representing the content of a document.
|
210
|
+
# If "doc" represents the content of a document, additional arguments
|
211
|
+
# "uri" and "content_type" are required.
|
212
|
+
|
213
|
+
def update(doc, uri=nil, content_type=nil)
|
214
|
+
return _put_doc(doc, uri, content_type)
|
215
|
+
end
|
216
|
+
|
217
|
+
def delete(uri)
|
218
|
+
doc_uri = @index_uri + '/' + uri
|
219
|
+
resource = RestClient::Resource.new(doc_uri, @un, @pw)
|
220
|
+
resp = resource.delete(:accept => :json, :user_agent => @user_agent)
|
221
|
+
return DeziResponse.new(resp)
|
222
|
+
end
|
223
|
+
|
224
|
+
# commit() and rollback() are only relevant if the Dezi server
|
225
|
+
# has "auto_commit" turned off.
|
226
|
+
|
227
|
+
def commit()
|
228
|
+
ua = RestClient::Resource.new( @commit_uri, @un, @pw )
|
229
|
+
resp = ua.post('/', :accept => :json, :user_agent => @user_agent)
|
230
|
+
return DeziResponse.new(resp)
|
231
|
+
end
|
232
|
+
|
233
|
+
# commit() and rollback() are only relevant if the Dezi server
|
234
|
+
# has "auto_commit" turned off.
|
235
|
+
|
236
|
+
def rollback()
|
237
|
+
ua = RestClient::Resource.new( @rollback_uri, @un, @pw )
|
238
|
+
resp = ua.post('/', :accept => :json, :user_agent => @user_agent)
|
239
|
+
return DeziResponse.new(resp)
|
240
|
+
end
|
241
|
+
|
242
|
+
def search(params)
|
243
|
+
if (!params.has_key?("q"))
|
244
|
+
raise "'q' param required"
|
245
|
+
end
|
246
|
+
|
247
|
+
resp = @searcher.get(
|
248
|
+
:params => params,
|
249
|
+
:accept => :json,
|
250
|
+
:user_agent => @user_agent
|
251
|
+
)
|
252
|
+
dr = DeziResponse.new(resp)
|
253
|
+
if (!dr.is_success())
|
254
|
+
@last_response = dr
|
255
|
+
return false
|
256
|
+
else
|
257
|
+
return dr
|
258
|
+
end
|
259
|
+
|
260
|
+
end
|
261
|
+
|
262
|
+
end
|
data/lib/dezi/doc.rb
ADDED
@@ -0,0 +1,69 @@
|
|
1
|
+
# DeziDoc is part of a Ruby client for the Dezi search platform.
|
2
|
+
#
|
3
|
+
# Copyright 2013 by Peter Karman
|
4
|
+
#
|
5
|
+
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
# of this software and associated documentation files (the "Software"), to deal
|
7
|
+
# in the Software without restriction, including without limitation the rights
|
8
|
+
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
# copies of the Software, and to permit persons to whom the Software is
|
10
|
+
# furnished to do so, subject to the following conditions:
|
11
|
+
#
|
12
|
+
# The above copyright notice and this permission notice shall be included in
|
13
|
+
# all copies or substantial portions of the Software.
|
14
|
+
#
|
15
|
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
21
|
+
# THE SOFTWARE.
|
22
|
+
|
23
|
+
require 'rubygems'
|
24
|
+
require 'xmlsimple'
|
25
|
+
|
26
|
+
class DeziDoc
|
27
|
+
|
28
|
+
attr_accessor :mime_type
|
29
|
+
attr_accessor :summary
|
30
|
+
attr_accessor :title
|
31
|
+
attr_accessor :content
|
32
|
+
attr_accessor :uri
|
33
|
+
attr_accessor :mtime
|
34
|
+
attr_accessor :size
|
35
|
+
attr_accessor :score
|
36
|
+
attr_accessor :fields
|
37
|
+
|
38
|
+
def initialize(args)
|
39
|
+
@fields = {}
|
40
|
+
args.each {|k,v| send("#{k}=",v)}
|
41
|
+
end
|
42
|
+
|
43
|
+
def set_field(args)
|
44
|
+
args.each {|k,v| @fields[k] = v}
|
45
|
+
@mime_type = 'application/xml'
|
46
|
+
end
|
47
|
+
|
48
|
+
def get_field(fname)
|
49
|
+
if @fields.has_key?(fname)
|
50
|
+
return @fields[fname]
|
51
|
+
else
|
52
|
+
return nil
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
def as_string()
|
57
|
+
#puts "fields.length=" + @fields.length.to_s
|
58
|
+
if @fields.length > 0
|
59
|
+
return self.as_xml()
|
60
|
+
else
|
61
|
+
return @content
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
def as_xml()
|
66
|
+
return XmlSimple.xml_out(@fields, {'rootname' => 'doc', 'noattr' => true})
|
67
|
+
end
|
68
|
+
|
69
|
+
end
|
@@ -0,0 +1,100 @@
|
|
1
|
+
# DeziResponse is part of a Ruby client for the Dezi search platform.
|
2
|
+
#
|
3
|
+
# Copyright 2013 by Peter Karman
|
4
|
+
#
|
5
|
+
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
# of this software and associated documentation files (the "Software"), to deal
|
7
|
+
# in the Software without restriction, including without limitation the rights
|
8
|
+
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
# copies of the Software, and to permit persons to whom the Software is
|
10
|
+
# furnished to do so, subject to the following conditions:
|
11
|
+
#
|
12
|
+
# The above copyright notice and this permission notice shall be included in
|
13
|
+
# all copies or substantial portions of the Software.
|
14
|
+
#
|
15
|
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
21
|
+
# THE SOFTWARE.
|
22
|
+
|
23
|
+
require 'rubygems'
|
24
|
+
require 'json'
|
25
|
+
|
26
|
+
class DeziResponse
|
27
|
+
|
28
|
+
# most attributes are assigned dynamically in initialize().
|
29
|
+
# Try:
|
30
|
+
#
|
31
|
+
# puts response.inspect
|
32
|
+
#
|
33
|
+
# to see them.
|
34
|
+
|
35
|
+
attr_accessor :results
|
36
|
+
|
37
|
+
def initialize(http_resp)
|
38
|
+
@http_resp = http_resp
|
39
|
+
|
40
|
+
#warn http_resp.headers.inspect
|
41
|
+
#warn "code=" + http_resp.code.to_s
|
42
|
+
|
43
|
+
@is_ok = false
|
44
|
+
if (http_resp.code.to_s =~ /^2\d\d/)
|
45
|
+
@is_ok = true
|
46
|
+
end
|
47
|
+
|
48
|
+
if (!@is_ok)
|
49
|
+
return
|
50
|
+
end
|
51
|
+
|
52
|
+
#warn "is_ok=#{@is_ok}"
|
53
|
+
|
54
|
+
body = JSON.parse(http_resp.to_s)
|
55
|
+
|
56
|
+
#warn body.inspect
|
57
|
+
|
58
|
+
# set body keys as attributes in the object
|
59
|
+
body.each {|k,v|
|
60
|
+
|
61
|
+
# results are special
|
62
|
+
if k == 'results'
|
63
|
+
next
|
64
|
+
end
|
65
|
+
|
66
|
+
# create the attribute
|
67
|
+
self.instance_eval { class << self; self end }.send(:attr_accessor, k)
|
68
|
+
|
69
|
+
# assign the value
|
70
|
+
send("#{k}=",v)
|
71
|
+
}
|
72
|
+
|
73
|
+
if !body.has_key?('results')
|
74
|
+
return
|
75
|
+
end
|
76
|
+
|
77
|
+
# make each result Hash into a DeziDoc object
|
78
|
+
@results = []
|
79
|
+
body['results'].each {|r|
|
80
|
+
result = r
|
81
|
+
result['fields'] = {}
|
82
|
+
@fields.each {|f|
|
83
|
+
result['fields'][f] = r.delete(f)
|
84
|
+
}
|
85
|
+
|
86
|
+
doc = DeziDoc.new(result)
|
87
|
+
@results.push(doc)
|
88
|
+
}
|
89
|
+
|
90
|
+
end
|
91
|
+
|
92
|
+
def status()
|
93
|
+
return @http_resp.code.to_s
|
94
|
+
end
|
95
|
+
|
96
|
+
def is_success()
|
97
|
+
return @is_ok
|
98
|
+
end
|
99
|
+
|
100
|
+
end
|
metadata
ADDED
@@ -0,0 +1,124 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: dezi-client
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
hash: 23
|
5
|
+
prerelease: false
|
6
|
+
segments:
|
7
|
+
- 1
|
8
|
+
- 0
|
9
|
+
- 0
|
10
|
+
version: 1.0.0
|
11
|
+
platform: ruby
|
12
|
+
authors:
|
13
|
+
- Peter Karman
|
14
|
+
autorequire:
|
15
|
+
bindir: bin
|
16
|
+
cert_chain: []
|
17
|
+
|
18
|
+
date: 2013-01-08 00:00:00 -06:00
|
19
|
+
default_executable:
|
20
|
+
dependencies:
|
21
|
+
- !ruby/object:Gem::Dependency
|
22
|
+
name: rest-client
|
23
|
+
prerelease: false
|
24
|
+
requirement: &id001 !ruby/object:Gem::Requirement
|
25
|
+
none: false
|
26
|
+
requirements:
|
27
|
+
- - ">="
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
hash: 3
|
30
|
+
segments:
|
31
|
+
- 0
|
32
|
+
version: "0"
|
33
|
+
type: :runtime
|
34
|
+
version_requirements: *id001
|
35
|
+
- !ruby/object:Gem::Dependency
|
36
|
+
name: json
|
37
|
+
prerelease: false
|
38
|
+
requirement: &id002 !ruby/object:Gem::Requirement
|
39
|
+
none: false
|
40
|
+
requirements:
|
41
|
+
- - ">="
|
42
|
+
- !ruby/object:Gem::Version
|
43
|
+
hash: 3
|
44
|
+
segments:
|
45
|
+
- 0
|
46
|
+
version: "0"
|
47
|
+
type: :runtime
|
48
|
+
version_requirements: *id002
|
49
|
+
- !ruby/object:Gem::Dependency
|
50
|
+
name: mime-types
|
51
|
+
prerelease: false
|
52
|
+
requirement: &id003 !ruby/object:Gem::Requirement
|
53
|
+
none: false
|
54
|
+
requirements:
|
55
|
+
- - ">="
|
56
|
+
- !ruby/object:Gem::Version
|
57
|
+
hash: 3
|
58
|
+
segments:
|
59
|
+
- 0
|
60
|
+
version: "0"
|
61
|
+
type: :runtime
|
62
|
+
version_requirements: *id003
|
63
|
+
- !ruby/object:Gem::Dependency
|
64
|
+
name: xml-simple
|
65
|
+
prerelease: false
|
66
|
+
requirement: &id004 !ruby/object:Gem::Requirement
|
67
|
+
none: false
|
68
|
+
requirements:
|
69
|
+
- - ">="
|
70
|
+
- !ruby/object:Gem::Version
|
71
|
+
hash: 3
|
72
|
+
segments:
|
73
|
+
- 0
|
74
|
+
version: "0"
|
75
|
+
type: :runtime
|
76
|
+
version_requirements: *id004
|
77
|
+
description: Ruby client for the Dezi search engine. See http://dezi.org/
|
78
|
+
email: karpet@dezi.org
|
79
|
+
executables: []
|
80
|
+
|
81
|
+
extensions: []
|
82
|
+
|
83
|
+
extra_rdoc_files: []
|
84
|
+
|
85
|
+
files:
|
86
|
+
- lib/dezi/client.rb
|
87
|
+
- lib/dezi/doc.rb
|
88
|
+
- lib/dezi/response.rb
|
89
|
+
has_rdoc: true
|
90
|
+
homepage: https://github.com/karpet/dezi-client-ruby
|
91
|
+
licenses: []
|
92
|
+
|
93
|
+
post_install_message:
|
94
|
+
rdoc_options: []
|
95
|
+
|
96
|
+
require_paths:
|
97
|
+
- lib
|
98
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
99
|
+
none: false
|
100
|
+
requirements:
|
101
|
+
- - ">="
|
102
|
+
- !ruby/object:Gem::Version
|
103
|
+
hash: 3
|
104
|
+
segments:
|
105
|
+
- 0
|
106
|
+
version: "0"
|
107
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
108
|
+
none: false
|
109
|
+
requirements:
|
110
|
+
- - ">="
|
111
|
+
- !ruby/object:Gem::Version
|
112
|
+
hash: 3
|
113
|
+
segments:
|
114
|
+
- 0
|
115
|
+
version: "0"
|
116
|
+
requirements: []
|
117
|
+
|
118
|
+
rubyforge_project: nowarning
|
119
|
+
rubygems_version: 1.3.7
|
120
|
+
signing_key:
|
121
|
+
specification_version: 3
|
122
|
+
summary: Ruby client for the Dezi search engine
|
123
|
+
test_files: []
|
124
|
+
|