s3search 0.0.1 → 0.0.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +2 -0
- data/.rspec +2 -0
- data/Gemfile +3 -0
- data/README.md +154 -6
- data/integration/document_spec.rb +13 -0
- data/integration/search_spec.rb +70 -0
- data/lib/s3search/api/documents.rb +37 -0
- data/lib/s3search/api/searches.rb +49 -0
- data/lib/s3search/client.rb +77 -0
- data/lib/s3search/request_error.rb +19 -0
- data/lib/s3search/result_page.rb +53 -0
- data/lib/s3search/version.rb +2 -2
- data/lib/s3search.rb +19 -3
- data/s3search.gemspec +21 -15
- data/spec/acceptance/document_create_many_spec.rb +20 -0
- data/spec/acceptance/document_create_spec.rb +21 -0
- data/spec/acceptance/document_delete_spec.rb +17 -0
- data/spec/acceptance/document_get_all_spec.rb +102 -0
- data/spec/acceptance/document_get_spec.rb +22 -0
- data/spec/acceptance/document_update_spec.rb +6 -0
- data/spec/acceptance/search_simple_spec.rb +154 -0
- data/spec/integration_helper.rb +10 -0
- data/spec/spec_helper.rb +30 -0
- metadata +108 -22
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: f2f409bf1d5943a4bba14de2d68380fd42de29e5
|
4
|
+
data.tar.gz: fa9aae7844c80c61be5fca34bfbf1d18be16b3f3
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 17497e746c81e2e54ab2690b64e4a27f8b98cca4f24a79772bbe81044cb70acec9d0c1ad95dc2c5b6e80e9e28917f46d61c0a236bd81588120dd544e5ce75672
|
7
|
+
data.tar.gz: 0d79ac7927201e5a74a407d865c83622b0e072f2ca3f966c0c79a4c24a04f163b6afaba16ab211d636401666e575c97166ec1f9ee940b8d0de3ee0606625f764
|
data/.gitignore
CHANGED
data/.rspec
ADDED
data/Gemfile
CHANGED
data/README.md
CHANGED
@@ -1,6 +1,12 @@
|
|
1
|
-
#
|
1
|
+
# S3Search
|
2
2
|
|
3
|
-
|
3
|
+
[S3Search](http://addons.heroku.com/s3search) is an [add-on](http://addons.heroku.com) for providing powerful document-based full-text indexing and search to your application.
|
4
|
+
|
5
|
+
Adding S3Search to your application will allow you search your documents based upon the actual text within the documents, as well as any metadata fields you assign to them. Yes, that's right! S3Search will index the text inside your documents.
|
6
|
+
|
7
|
+
Do you already have documents stored and want to index them and make them searchable? No worries. S3Search works by you sending it a URL to fetch the document content to index, along with a hash of metadata attributes to record against it. You can them perform powerful queries against that indexed data, based on the rich features of [elasticsearch](http://www.elasticsearch.org).
|
8
|
+
|
9
|
+
S3Search is a Heroku add-on.
|
4
10
|
|
5
11
|
## Installation
|
6
12
|
|
@@ -12,13 +18,155 @@ And then execute:
|
|
12
18
|
|
13
19
|
$ bundle
|
14
20
|
|
15
|
-
|
21
|
+
## Provisioning the add-on
|
22
|
+
|
23
|
+
S3Search can be attached to a Heroku application via the CLI:
|
24
|
+
|
25
|
+
<div class="callout" markdown="1">
|
26
|
+
A list of all plans available can be found [here](http://addons.heroku.com/s3search).
|
27
|
+
</div>
|
28
|
+
|
29
|
+
```term
|
30
|
+
$ heroku addons:add s3search
|
31
|
+
-----> Adding s3search to sharp-mountain-4005... done, v18 (free)
|
32
|
+
```
|
33
|
+
|
34
|
+
Once S3Search has been added a `S3SEARCH_URL` setting will be available in the app configuration and will contain your custom URL to access the newly provisioned S3Search service instance. This can be confirmed using the `heroku config:get` command.
|
35
|
+
|
36
|
+
```term
|
37
|
+
$ heroku config:get S3SEARCH_URL
|
38
|
+
https://user:pass@api.s3searchapp.com
|
39
|
+
```
|
40
|
+
|
41
|
+
After installing S3Search the application should be configured to fully integrate with the add-on.
|
42
|
+
|
43
|
+
## Using with Rails 3.x
|
44
|
+
|
45
|
+
Ruby on Rails applications will need to add the following entry into their `Gemfile` specifying the S3Search client library.
|
46
|
+
|
47
|
+
```ruby
|
48
|
+
gem 's3search'
|
49
|
+
```
|
50
|
+
|
51
|
+
Update application dependencies with bundler.
|
52
|
+
|
53
|
+
```term
|
54
|
+
$ bundle install
|
55
|
+
```
|
56
|
+
|
57
|
+
Write some application code to index some documents. Use the special field, `_content_url`, if you want to specify a location to download content and make it searchable. S3Search will download the content and make it searchable via the special field `_document_content`.
|
58
|
+
|
59
|
+
```ruby
|
60
|
+
S3Search.create title: 'MyDocument', _content_url: 'https://s3-us-east-1.amazonaws.com/my_bucket/my_document.pdf'
|
61
|
+
S3Search.create name: 'Bob Lob Law', resume_id: 25, _content_url: 'https://s3-us-east-1.amazonaws.com/resumes.mycompany.com/bob.pdf'
|
62
|
+
```
|
63
|
+
|
64
|
+
The documents don't even really need to be in S3.
|
65
|
+
|
66
|
+
```ruby
|
67
|
+
S3Search.create name: 'Bitcoin Pirate', resume_id: 42, _content_url: 'https://user:pass@authenticatedlocation.com/docs/jenny.pdf'
|
68
|
+
S3Search.create title: 'Bitcoin', author: 'santoshin@gmx.com', tags: ['bitcoin', 'manifesto'], _content_url: 'http://bitcoin.org/bitcoin.pdf'
|
69
|
+
```
|
70
|
+
|
71
|
+
The documents don't even really need to be documents! You can use S3Search to use its powerful search capability over just your custom metadata.
|
72
|
+
|
73
|
+
```ruby
|
74
|
+
S3Search.create customer_id: 32, first_name: 'His Holiness', last_name: 'The Dalia Lama', religion: 'Buddhist', twitter_handle: '@DalaiLama'
|
75
|
+
S3Search.create customer_id: 99, first_name: 'George', middle_name: 'R. R.', last_name: 'Martin', job_title: 'Author'
|
76
|
+
```
|
77
|
+
|
78
|
+
Now retrieve some documents via the powerful query API.
|
79
|
+
|
80
|
+
Search by a single metadata field
|
81
|
+
|
82
|
+
```ruby
|
83
|
+
results = S3Search.search('title:MyDocument')
|
84
|
+
```
|
85
|
+
|
86
|
+
Search all metadata fields AND the content of the documents.
|
87
|
+
|
88
|
+
```ruby
|
89
|
+
results = S3Search.search('bitcoin')
|
90
|
+
```
|
91
|
+
|
92
|
+
Search only the content of the documents.
|
93
|
+
|
94
|
+
```ruby
|
95
|
+
results = S3Search.search('_document_content:bitcoin')
|
96
|
+
```
|
97
|
+
|
98
|
+
Boost the search ranking of a certain field.
|
99
|
+
|
100
|
+
```ruby
|
101
|
+
results = S3Search.search('bitcoin', boost: { title: 2.5 })
|
102
|
+
```
|
103
|
+
Find a single document based on its unique id.
|
104
|
+
|
105
|
+
```ruby
|
106
|
+
document = S3Search.get '833FCA4EEEF2943AC2D8E0'
|
107
|
+
```
|
108
|
+
## Monitoring & Logging
|
109
|
+
|
110
|
+
Stats and the current state of S3Search can be displayed via the CLI.
|
111
|
+
|
112
|
+
```term
|
113
|
+
$ heroku s3search:status
|
114
|
+
documents_indexed: 32842
|
115
|
+
index_size: 640MB
|
116
|
+
```
|
117
|
+
|
118
|
+
S3Search activity can be observed within the Heroku log-stream.
|
119
|
+
|
120
|
+
```term
|
121
|
+
$ heroku logs -t | grep 's3search'
|
122
|
+
```
|
123
|
+
|
124
|
+
## Dashboard
|
125
|
+
|
126
|
+
<div class="callout" markdown="1">
|
127
|
+
For more information on the features available within the S3Search dashboard please see the docs at [heroku.s3searchapp.com/docs](heroku.s3searchapp.com/docs).
|
128
|
+
</div>
|
129
|
+
|
130
|
+
The S3Search dashboard allows you to view the current status of your S3Search cluster.
|
131
|
+
|
132
|
+
The dashboard can be accessed via the CLI:
|
133
|
+
|
134
|
+
```term
|
135
|
+
$ heroku addons:open s3search
|
136
|
+
Opening s3search for sharp-mountain-4005…
|
137
|
+
```
|
138
|
+
|
139
|
+
or by visiting the [Heroku apps web interface](http://heroku.com/myapps) and selecting the application in question. Select S3Search from the Add-ons menu.
|
140
|
+
|
141
|
+
## Migrating between plans
|
142
|
+
|
143
|
+
<div class="note" markdown="1">Application owners should carefully manage the migration timing to ensure proper application function during the migration process.</div>
|
144
|
+
|
145
|
+
Use the `heroku addons:upgrade` command to migrate to a new plan.
|
146
|
+
|
147
|
+
```term
|
148
|
+
$ heroku addons:upgrade s3search:newplan
|
149
|
+
-----> Upgrading s3search:newplan to sharp-mountain-4005... done, v18 ($49/mo)
|
150
|
+
Your plan has been updated to: s3search:newplan
|
151
|
+
```
|
152
|
+
|
153
|
+
## Removing the add-on
|
154
|
+
|
155
|
+
S3Search can be removed via the CLI.
|
156
|
+
|
157
|
+
<div class="warning" markdown="1">This will destroy all metadata and indexes stored in S3Search and cannot be undone! Of course, documents indexed in S3Search but stored elsewhere will remain untouched.</div>
|
158
|
+
|
159
|
+
```term
|
160
|
+
$ heroku addons:remove s3search
|
161
|
+
-----> Removing s3search from sharp-mountain-4005... done, v20 (free)
|
162
|
+
```
|
163
|
+
|
164
|
+
Before removing S3Search a data export can be performed by contacting support@s3searchapp.com directly.
|
16
165
|
|
17
|
-
|
166
|
+
## Support
|
18
167
|
|
19
|
-
|
168
|
+
All S3Search support and runtime issues should be submitted via on of the [Heroku Support channels](support-channels). Any non-support related issues or product feedback is welcome at feedback@s3searchapp.com.
|
20
169
|
|
21
|
-
TODO: Write usage instructions here
|
22
170
|
|
23
171
|
## Contributing
|
24
172
|
|
@@ -0,0 +1,13 @@
|
|
1
|
+
require 'integration_helper'
|
2
|
+
|
3
|
+
describe '/document' do
|
4
|
+
|
5
|
+
it 'is implemented correctly' do
|
6
|
+
document = S3Search.create title: 'blah'
|
7
|
+
expect(document.title).to eq('blah')
|
8
|
+
|
9
|
+
id = document.id
|
10
|
+
|
11
|
+
expect(S3Search.get(id).title).to eq('blah')
|
12
|
+
end
|
13
|
+
end
|
@@ -0,0 +1,70 @@
|
|
1
|
+
require 'integration_helper'
|
2
|
+
|
3
|
+
describe '/searches' do
|
4
|
+
|
5
|
+
it 'is implemented correctly' do
|
6
|
+
|
7
|
+
S3Search.get_all(per_page: 9999).map do |doc|
|
8
|
+
S3Search.delete doc.id
|
9
|
+
end
|
10
|
+
|
11
|
+
title_a = SecureRandom.hex
|
12
|
+
title_b = SecureRandom.hex
|
13
|
+
|
14
|
+
a_docs = 10.times.map do
|
15
|
+
{title: title_a}
|
16
|
+
end
|
17
|
+
|
18
|
+
b_docs = 10.times.map do
|
19
|
+
{title: title_b}
|
20
|
+
end
|
21
|
+
|
22
|
+
a_doc_ids = S3Search.create_many(a_docs).map(&:id)
|
23
|
+
b_doc_ids = S3Search.create_many(b_docs).map(&:id)
|
24
|
+
all_doc_ids = a_doc_ids + b_doc_ids
|
25
|
+
|
26
|
+
S3Search.get_all(per_page: 20).each do |doc|
|
27
|
+
expect(all_doc_ids).to include(doc.id)
|
28
|
+
end
|
29
|
+
|
30
|
+
results = S3Search.simple_search "title:#{title_a}", per_page: 4
|
31
|
+
expect(results.size).to eq(4)
|
32
|
+
results.each do |doc|
|
33
|
+
expect(a_doc_ids).to include(doc.id)
|
34
|
+
end
|
35
|
+
|
36
|
+
results.next
|
37
|
+
expect(results.size).to eq(4)
|
38
|
+
results.each do |doc|
|
39
|
+
expect(a_doc_ids).to include(doc.id)
|
40
|
+
end
|
41
|
+
|
42
|
+
results.next
|
43
|
+
expect(results.size).to eq(2)
|
44
|
+
results.each do |doc|
|
45
|
+
expect(a_doc_ids).to include(doc.id)
|
46
|
+
end
|
47
|
+
expect(results.next?).to be_false
|
48
|
+
|
49
|
+
results = S3Search.simple_search "title:#{title_b}", per_page: 999
|
50
|
+
expect(results.size).to eq(10)
|
51
|
+
results.each do |doc|
|
52
|
+
expect(b_doc_ids).to include(doc.id)
|
53
|
+
end
|
54
|
+
|
55
|
+
# begin
|
56
|
+
# results.each do |result|
|
57
|
+
# S3Search.delete result.id
|
58
|
+
# end
|
59
|
+
# end while results.next
|
60
|
+
|
61
|
+
# document = S3Search.create title: 'blah'
|
62
|
+
# expect(document.title).to eq('blah')
|
63
|
+
|
64
|
+
# id = document.id
|
65
|
+
|
66
|
+
# expect(S3Search.get(id).title).to eq('blah')
|
67
|
+
|
68
|
+
# puts S3Search.search 'blah'
|
69
|
+
end
|
70
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
require 's3search/result_page'
|
2
|
+
|
3
|
+
module S3Search
|
4
|
+
module API
|
5
|
+
module Documents
|
6
|
+
|
7
|
+
def create data
|
8
|
+
raise 'use create_many for multiple documents' if data.is_a?(Array)
|
9
|
+
_post('/v1/documents.json', {data: data})
|
10
|
+
end
|
11
|
+
|
12
|
+
def create_many data
|
13
|
+
response = _post('/v1/documents.json', {data: [data].flatten})
|
14
|
+
response.results
|
15
|
+
end
|
16
|
+
|
17
|
+
def delete id
|
18
|
+
_delete "/v1/documents/#{id}.json"
|
19
|
+
end
|
20
|
+
|
21
|
+
def get id
|
22
|
+
_get "/v1/documents/#{id}.json"
|
23
|
+
end
|
24
|
+
|
25
|
+
def update id, attributes
|
26
|
+
_put("/v1/documents/#{id}.json", {document: attributes})
|
27
|
+
end
|
28
|
+
|
29
|
+
def get_all options={}
|
30
|
+
options[:page] ||= 1
|
31
|
+
options[:per_page] ||= 25
|
32
|
+
response = _get("/v1/documents.json", options)
|
33
|
+
S3Search::ResultPage.new(response, self)
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
@@ -0,0 +1,49 @@
|
|
1
|
+
require 's3search/result_page'
|
2
|
+
|
3
|
+
module S3Search
|
4
|
+
module API
|
5
|
+
module Searches
|
6
|
+
|
7
|
+
def simple_search search_term, options={}
|
8
|
+
if search_term.include?(':')
|
9
|
+
field, value = search_term.split(':')[0..1]
|
10
|
+
options.merge!({where: { field => value }})
|
11
|
+
else
|
12
|
+
options.merge!({where: {_all: search_term }})
|
13
|
+
end
|
14
|
+
search options
|
15
|
+
end
|
16
|
+
|
17
|
+
def search options
|
18
|
+
raise 'where: {field: "value"} is a required option' unless options[:where]
|
19
|
+
query_terms = options.delete(:where).map do |field, value|
|
20
|
+
{
|
21
|
+
query_string: {
|
22
|
+
default_field: field,
|
23
|
+
query: value,
|
24
|
+
default_operator: 'AND'
|
25
|
+
}
|
26
|
+
}
|
27
|
+
end
|
28
|
+
query = {
|
29
|
+
query: {
|
30
|
+
bool: {
|
31
|
+
must: query_terms
|
32
|
+
}
|
33
|
+
}
|
34
|
+
}
|
35
|
+
|
36
|
+
page = options.delete(:page) || 1
|
37
|
+
per_page = options.delete(:per_page) || 25
|
38
|
+
|
39
|
+
query.merge!(options)
|
40
|
+
response = _post '/v1/searches.json', {
|
41
|
+
search: query,
|
42
|
+
page: page,
|
43
|
+
per_page: per_page
|
44
|
+
}
|
45
|
+
S3Search::ResultPage.new(response, self)
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
@@ -0,0 +1,77 @@
|
|
1
|
+
require 'active_support/core_ext/object/try'
|
2
|
+
require 'faraday'
|
3
|
+
require 'faraday_middleware'
|
4
|
+
require 'hashie'
|
5
|
+
require 'logger'
|
6
|
+
require 's3search/api/documents'
|
7
|
+
require 's3search/api/searches'
|
8
|
+
require 's3search/request_error'
|
9
|
+
require 's3search/version'
|
10
|
+
|
11
|
+
module S3Search
|
12
|
+
class Client
|
13
|
+
include S3Search::API::Documents
|
14
|
+
include S3Search::API::Searches
|
15
|
+
|
16
|
+
attr_reader :url, :http, :logger
|
17
|
+
|
18
|
+
def initialize
|
19
|
+
@url = ENV['S3SEARCH_URL'] || 'https://api.s3searchapp.com'
|
20
|
+
@http = Faraday.new(:url => @url) do |builder|
|
21
|
+
builder.response :mashify
|
22
|
+
builder.response :json, :content_type => /\bjson$/
|
23
|
+
builder.request :json
|
24
|
+
builder.request :basic_auth, ENV['S3SEARCH_API_KEY'], ENV['S3SEARCH_API_SECRET']
|
25
|
+
builder.options[:read_timeout] = 4
|
26
|
+
builder.options[:open_timeout] = 2
|
27
|
+
builder.adapter :excon
|
28
|
+
end
|
29
|
+
|
30
|
+
@http.headers = {
|
31
|
+
accept: 'application/json',
|
32
|
+
user_agent: "S3Search Ruby Gem #{S3Search::VERSION}",
|
33
|
+
"Content-Type" => "application/json"
|
34
|
+
}
|
35
|
+
|
36
|
+
@logger = Logger.new(STDOUT)
|
37
|
+
|
38
|
+
# 0=DEBUG, 1=INFO, 2=WARN, 3=ERROR, 4=FATAL
|
39
|
+
@logger.level = ENV['S3SEARCH_LOG_LEVEL'].try(:to_i) || 2
|
40
|
+
|
41
|
+
@logger.formatter = proc do |severity, datetime, progname, msg|
|
42
|
+
"[S3Search][#{severity}]: #{msg}\n"
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
def _delete path, params={}
|
47
|
+
request :delete, path, params
|
48
|
+
end
|
49
|
+
|
50
|
+
def _get path, params={}
|
51
|
+
request :get, path, params
|
52
|
+
end
|
53
|
+
|
54
|
+
def _post path, params={}
|
55
|
+
request :post, path, params
|
56
|
+
end
|
57
|
+
|
58
|
+
def _put path, params={}
|
59
|
+
request :put, path, params
|
60
|
+
end
|
61
|
+
|
62
|
+
def request method, path, params
|
63
|
+
response = http.send(method, path, params)
|
64
|
+
|
65
|
+
case response.status
|
66
|
+
when 200..299
|
67
|
+
response.body
|
68
|
+
when 404, 410
|
69
|
+
raise RequestError::NotFound.new(response)
|
70
|
+
when 401
|
71
|
+
raise RequestError::Unauthorized.new(response)
|
72
|
+
else
|
73
|
+
raise RequestError.new(response)
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
module S3Search
|
2
|
+
class RequestError < StandardError
|
3
|
+
attr_reader :response
|
4
|
+
|
5
|
+
def initialize(response)
|
6
|
+
@response = response
|
7
|
+
msg = "Error processing request: (#{response.status})! #{response.env[:method]} URL: #{response.env[:url]}"
|
8
|
+
msg << "\n Resp Body: #{response.body}"
|
9
|
+
super msg
|
10
|
+
end
|
11
|
+
|
12
|
+
class NotFound < RequestError
|
13
|
+
end
|
14
|
+
|
15
|
+
class Unauthorized < RequestError
|
16
|
+
end
|
17
|
+
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,53 @@
|
|
1
|
+
module S3Search
|
2
|
+
class ResultPage
|
3
|
+
|
4
|
+
attr_reader :response, :results
|
5
|
+
|
6
|
+
def initialize response, client
|
7
|
+
@response = response
|
8
|
+
@results = response.results
|
9
|
+
@client = client
|
10
|
+
end
|
11
|
+
|
12
|
+
def next
|
13
|
+
return unless next?
|
14
|
+
change_page(links.next)
|
15
|
+
end
|
16
|
+
|
17
|
+
def next?
|
18
|
+
links.next
|
19
|
+
end
|
20
|
+
|
21
|
+
def prev
|
22
|
+
return unless prev?
|
23
|
+
change_page(links.prev)
|
24
|
+
end
|
25
|
+
|
26
|
+
def prev?
|
27
|
+
links.prev
|
28
|
+
end
|
29
|
+
|
30
|
+
def respond_to_missing? method_name, include_private=false
|
31
|
+
results.respond_to?(method_name, include_private) || response.respond_to?(method_name, include_private)
|
32
|
+
end
|
33
|
+
|
34
|
+
private
|
35
|
+
|
36
|
+
def change_page link
|
37
|
+
@response = @client._get(link)
|
38
|
+
@results = @response.results
|
39
|
+
S3Search::ResultPage.new(@response, @client)
|
40
|
+
end
|
41
|
+
|
42
|
+
def method_missing method_name, *args, &block
|
43
|
+
if results.respond_to?(method_name)
|
44
|
+
results.send method_name, *args, &block
|
45
|
+
elsif response.respond_to?(method_name)
|
46
|
+
response.send method_name, *args, &block
|
47
|
+
else
|
48
|
+
super
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
end
|
53
|
+
end
|
data/lib/s3search/version.rb
CHANGED
@@ -1,3 +1,3 @@
|
|
1
|
-
module
|
2
|
-
VERSION = "0.0.
|
1
|
+
module S3Search
|
2
|
+
VERSION = "0.0.2"
|
3
3
|
end
|
data/lib/s3search.rb
CHANGED
@@ -1,5 +1,21 @@
|
|
1
|
-
require
|
1
|
+
require 'json'
|
2
|
+
require 's3search/client'
|
2
3
|
|
3
|
-
module
|
4
|
-
|
4
|
+
module S3Search
|
5
|
+
class << self
|
6
|
+
def client
|
7
|
+
@client ||= S3Search::Client.new
|
8
|
+
end
|
9
|
+
|
10
|
+
def respond_to_missing? method_name, include_private=false
|
11
|
+
client.respond_to?(method_name, include_private)
|
12
|
+
end
|
13
|
+
|
14
|
+
private
|
15
|
+
|
16
|
+
def method_missing method_name, *args, &block
|
17
|
+
return super unless client.respond_to?(method_name)
|
18
|
+
client.send(method_name, *args, &block)
|
19
|
+
end
|
20
|
+
end
|
5
21
|
end
|
data/s3search.gemspec
CHANGED
@@ -3,21 +3,27 @@ lib = File.expand_path('../lib', __FILE__)
|
|
3
3
|
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
4
|
require 's3search/version'
|
5
5
|
|
6
|
-
Gem::Specification.new do |
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
6
|
+
Gem::Specification.new do |gem|
|
7
|
+
gem.name = "s3search"
|
8
|
+
gem.version = S3Search::VERSION
|
9
|
+
gem.authors = ["Chris Aitchison"]
|
10
|
+
gem.email = ["chris.aitchison@sodalis.com.au"]
|
11
|
+
gem.description = %q{Ruby implementation of the S3Search API}
|
12
|
+
gem.summary = %q{S3Search Ruby API}
|
13
|
+
gem.homepage = "https://github.com/sodalis/s3search-ruby"
|
14
|
+
gem.license = "MIT"
|
15
15
|
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
16
|
+
gem.files = `git ls-files`.split($/)
|
17
|
+
gem.executables = gem.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
18
|
+
gem.test_files = gem.files.grep(%r{^(spec|integration)/})
|
19
|
+
gem.require_paths = ["lib"]
|
20
20
|
|
21
|
-
|
22
|
-
|
21
|
+
gem.add_dependency 'activesupport', ">= 3.0.0"
|
22
|
+
gem.add_dependency('faraday', '~> 0.8')
|
23
|
+
gem.add_dependency('faraday_middleware', '~> 0.9.0')
|
24
|
+
gem.add_dependency('excon', '>= 0.16')
|
25
|
+
gem.add_dependency('hashie', '~> 1.2.0')
|
26
|
+
|
27
|
+
gem.add_development_dependency "bundler", "~> 1.3"
|
28
|
+
gem.add_development_dependency "rake"
|
23
29
|
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe 'creating many documents at once' do
|
4
|
+
|
5
|
+
let(:docs){ (0..9).map {|i| {title: "Doc#{i}"} } }
|
6
|
+
|
7
|
+
before do
|
8
|
+
stub_api_http :post, '/v1/documents.json', {data: docs} do
|
9
|
+
{
|
10
|
+
status: 201,
|
11
|
+
body: {results: docs.map{|doc| Hashie::Mash.new(doc.merge(id: SecureRandom.hex))} }
|
12
|
+
}
|
13
|
+
end
|
14
|
+
@result = S3Search.create_many docs
|
15
|
+
end
|
16
|
+
|
17
|
+
it 'returns the created documents' do
|
18
|
+
expect(@result.size).to eq(10)
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe 'creating a simple document' do
|
4
|
+
|
5
|
+
let(:create_params){ {title: 'Simple Document'} }
|
6
|
+
let(:created_id) { 'zMitmokAR5O4utuyz7qXRw' }
|
7
|
+
before do
|
8
|
+
stub_api_http :post, '/v1/documents.json', {data: create_params} do
|
9
|
+
{
|
10
|
+
status: 201,
|
11
|
+
body: Hashie::Mash.new(create_params.merge(id: created_id))
|
12
|
+
}
|
13
|
+
end
|
14
|
+
@document = S3Search.create create_params
|
15
|
+
end
|
16
|
+
|
17
|
+
it 'returns the created document' do
|
18
|
+
expect(@document.title).to eq('Simple Document')
|
19
|
+
expect(@document.id).to eq(created_id)
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe 'deleting a document' do
|
4
|
+
|
5
|
+
let(:document_id) { '3cbv4PUcRcOGsHnvIX9lJQ' }
|
6
|
+
before do
|
7
|
+
stub_api_http :delete, "/v1/documents/#{document_id}.json" do
|
8
|
+
{
|
9
|
+
status: 204
|
10
|
+
}
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
it 'deletes the document' do
|
15
|
+
S3Search.delete document_id
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,102 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe 'getting all documents' do
|
4
|
+
let(:document_jsons) { (1..27).map{ |i| Hashie::Mash.new(id: "id#{i}", _score: 1.0, name: "doc#{i}") } }
|
5
|
+
|
6
|
+
def link page, per_page
|
7
|
+
"https://api.s3searchapp.test/v1/documents.json?page=#{page}&per_page=#{per_page}"
|
8
|
+
end
|
9
|
+
|
10
|
+
it 'takes per_page and page params' do
|
11
|
+
stub_get_all 2, 3
|
12
|
+
results = S3Search.get_all page: 2, per_page: 3
|
13
|
+
expect_page(results, 2, 3)
|
14
|
+
|
15
|
+
expect(results.map(&:name)).to eq(['doc4', 'doc5', 'doc6'])
|
16
|
+
end
|
17
|
+
|
18
|
+
it 'allows paging with next and prev' do
|
19
|
+
stub_get_all 4, 2
|
20
|
+
page = S3Search.get_all page: 4, per_page: 2
|
21
|
+
expect_page(page, 4, 2)
|
22
|
+
|
23
|
+
stub_get_all 5, 2
|
24
|
+
page.next
|
25
|
+
expect_page(page, 5, 2)
|
26
|
+
|
27
|
+
page.prev
|
28
|
+
expect_page(page, 4, 2)
|
29
|
+
|
30
|
+
stub_get_all 3, 2
|
31
|
+
page.prev
|
32
|
+
expect_page(page, 3, 2)
|
33
|
+
|
34
|
+
stub_get_all 2, 2
|
35
|
+
page.prev
|
36
|
+
expect_page(page, 2, 2)
|
37
|
+
|
38
|
+
stub_get_all 1, 2
|
39
|
+
page.prev
|
40
|
+
expect_page(page, 1, 2)
|
41
|
+
|
42
|
+
page.prev
|
43
|
+
expect_page(page, 1, 2)
|
44
|
+
end
|
45
|
+
|
46
|
+
it 'defaults to page: 1, per_page: 25' do
|
47
|
+
stub_get_all 1, 25
|
48
|
+
page = S3Search.get_all
|
49
|
+
expect_page(page, 1, 25)
|
50
|
+
|
51
|
+
stub_get_all 2, 25
|
52
|
+
page.next
|
53
|
+
expect_page(page, 2, 25)
|
54
|
+
|
55
|
+
page.next
|
56
|
+
expect_page(page, 2, 25)
|
57
|
+
end
|
58
|
+
|
59
|
+
def expect_page result, page, per_page
|
60
|
+
from = from(page, per_page)
|
61
|
+
to = to(page, per_page)
|
62
|
+
|
63
|
+
expect(result.size).to eq((to - from) + 1)
|
64
|
+
expect(result.from).to eq(from)
|
65
|
+
expect(result.to).to eq(to)
|
66
|
+
expect(result.first).to eq(document_jsons[from - 1])
|
67
|
+
expect(result.last).to eq(document_jsons[to - 1])
|
68
|
+
end
|
69
|
+
|
70
|
+
|
71
|
+
def from page, per_page
|
72
|
+
[(page - 1) * per_page + 1, 1].max
|
73
|
+
end
|
74
|
+
|
75
|
+
def to page, per_page, total=27
|
76
|
+
[page * per_page, total].min
|
77
|
+
end
|
78
|
+
|
79
|
+
def stub_get_all page, per_page
|
80
|
+
total = 27
|
81
|
+
from = from(page, per_page)
|
82
|
+
to = to(page, per_page)
|
83
|
+
links = {
|
84
|
+
self: link(page, per_page)
|
85
|
+
}
|
86
|
+
links[:next] = link(page + 1, per_page) if to < total
|
87
|
+
links[:prev] = link(page - 1, per_page) if page > 1
|
88
|
+
|
89
|
+
stub_api_http :get, "/v1/documents.json?page=#{page}&per_page=#{per_page}" do
|
90
|
+
{
|
91
|
+
body: Hashie::Mash.new(
|
92
|
+
total: total,
|
93
|
+
from: from,
|
94
|
+
to: to,
|
95
|
+
results: document_jsons[(from - 1)..(to - 1)],
|
96
|
+
links: links
|
97
|
+
),
|
98
|
+
status: 200
|
99
|
+
}
|
100
|
+
end
|
101
|
+
end
|
102
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe 'deleting a document' do
|
4
|
+
|
5
|
+
let(:document_id) { 'ZenfyafgRRGWAR6pqF0DeA' }
|
6
|
+
before do
|
7
|
+
stub_api_http :get, "/v1/documents/#{document_id}.json" do
|
8
|
+
{
|
9
|
+
body: Hashie::Mash.new({id: document_id, name: 'Some Document', some_property: 42}),
|
10
|
+
status: 200
|
11
|
+
}
|
12
|
+
end
|
13
|
+
@document = S3Search.get document_id
|
14
|
+
end
|
15
|
+
|
16
|
+
|
17
|
+
it 'gets the document' do
|
18
|
+
expect(@document.id).to eq(document_id)
|
19
|
+
expect(@document.name).to eq('Some Document')
|
20
|
+
expect(@document.some_property).to eq(42)
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,154 @@
|
|
1
|
+
require 'active_support/core_ext/hash/deep_merge'
|
2
|
+
|
3
|
+
describe 'search' do
|
4
|
+
|
5
|
+
def link id, page, per_page
|
6
|
+
"https://api.s3searchapp.test/v1/searches/abcde.json?page=#{page}&per_page=#{per_page}"
|
7
|
+
end
|
8
|
+
|
9
|
+
let(:found_documents) { (1..70).map{|i| {id: SecureRandom.hex, name: "doc#{i}", _score: 1.0 / i}}}
|
10
|
+
|
11
|
+
let(:simple_request_body) {
|
12
|
+
{
|
13
|
+
search: {
|
14
|
+
query: {
|
15
|
+
bool: {
|
16
|
+
must: [
|
17
|
+
{
|
18
|
+
query_string:{
|
19
|
+
default_field: '_all',
|
20
|
+
query: "searchterm",
|
21
|
+
default_operator:"AND"
|
22
|
+
}
|
23
|
+
}
|
24
|
+
]
|
25
|
+
}
|
26
|
+
}
|
27
|
+
},
|
28
|
+
page: 1,
|
29
|
+
per_page: 25
|
30
|
+
}
|
31
|
+
}
|
32
|
+
it 'finds by simple query' do
|
33
|
+
stub_api_http :post, "/v1/searches.json", simple_request_body do
|
34
|
+
{
|
35
|
+
body: Hashie::Mash.new(
|
36
|
+
total: 70,
|
37
|
+
from: 1,
|
38
|
+
to: 25,
|
39
|
+
results: found_documents[0..24],
|
40
|
+
links: {
|
41
|
+
self: link('abcde', 1, 25),
|
42
|
+
next: link('abcde', 2, 25)
|
43
|
+
}
|
44
|
+
),
|
45
|
+
status: 200
|
46
|
+
}
|
47
|
+
end
|
48
|
+
|
49
|
+
results = S3Search.simple_search 'searchterm'
|
50
|
+
expect(results.first).to eq(
|
51
|
+
{
|
52
|
+
"_score" => 1.0,
|
53
|
+
"id" => results[0].id,
|
54
|
+
"name" => "doc1"
|
55
|
+
}
|
56
|
+
)
|
57
|
+
expect(results.last).to eq(
|
58
|
+
{
|
59
|
+
"_score" => 0.04,
|
60
|
+
"id" => results[24].id,
|
61
|
+
"name" => "doc25"
|
62
|
+
}
|
63
|
+
)
|
64
|
+
|
65
|
+
stub_api_http :get, "/v1/searches/abcde.json?page=2&per_page=25" do
|
66
|
+
{
|
67
|
+
body: Hashie::Mash.new(
|
68
|
+
total: 70,
|
69
|
+
from: 26,
|
70
|
+
to: 50,
|
71
|
+
results: found_documents[25..49],
|
72
|
+
links: {
|
73
|
+
self: link('abcde', 2, 25),
|
74
|
+
next: link('abcde', 3, 25),
|
75
|
+
prev: link('abcde', 1, 25)
|
76
|
+
}
|
77
|
+
),
|
78
|
+
status: 200
|
79
|
+
}
|
80
|
+
end
|
81
|
+
|
82
|
+
results.next
|
83
|
+
expect(results.first).to eq(
|
84
|
+
{
|
85
|
+
"_score" => results[0]._score,
|
86
|
+
"id" => results[0].id,
|
87
|
+
"name" => "doc26"
|
88
|
+
}
|
89
|
+
)
|
90
|
+
expect(results.last).to eq(
|
91
|
+
{
|
92
|
+
"_score" => results[24]._score,
|
93
|
+
"id" => results[24].id,
|
94
|
+
"name" => "doc50"
|
95
|
+
}
|
96
|
+
)
|
97
|
+
|
98
|
+
end
|
99
|
+
|
100
|
+
let(:highlight_options) {
|
101
|
+
{
|
102
|
+
highlight: {
|
103
|
+
fields: [:name],
|
104
|
+
tag: '<strong class="highlight">',
|
105
|
+
number_of_fragments: 3,
|
106
|
+
fragment_size: 150
|
107
|
+
}
|
108
|
+
}
|
109
|
+
}
|
110
|
+
let(:highlight_request_body) {
|
111
|
+
simple_request_body.deep_merge(search: highlight_options)
|
112
|
+
}
|
113
|
+
|
114
|
+
let(:highlight_documents) {
|
115
|
+
(1..70).map do |i|
|
116
|
+
{
|
117
|
+
id: SecureRandom.hex,
|
118
|
+
name: "doc#{i}",
|
119
|
+
description: 'a searchterm here',
|
120
|
+
_score: 1.0 / i,
|
121
|
+
_highlight: {
|
122
|
+
_description: ['a <strong class="highlight">searchterm</strong> here']
|
123
|
+
}
|
124
|
+
}
|
125
|
+
end
|
126
|
+
}
|
127
|
+
|
128
|
+
it 'allows highlighting of fields' do
|
129
|
+
stub_api_http :post, "/v1/searches.json", highlight_request_body do
|
130
|
+
{
|
131
|
+
body: Hashie::Mash.new(
|
132
|
+
total: 70,
|
133
|
+
from: 1,
|
134
|
+
to: 25,
|
135
|
+
results: highlight_documents[0..24],
|
136
|
+
links: {
|
137
|
+
self: link('abcde', 1, 25),
|
138
|
+
next: link('abcde', 2, 25)
|
139
|
+
}
|
140
|
+
),
|
141
|
+
status: 200
|
142
|
+
}
|
143
|
+
end
|
144
|
+
results = S3Search.simple_search 'searchterm', highlight_options
|
145
|
+
expect(results.first).to eq({
|
146
|
+
"_highlight" => {"_description"=>['a <strong class="highlight">searchterm</strong> here']},
|
147
|
+
"_score" => 1.0,
|
148
|
+
"description" => "a searchterm here",
|
149
|
+
"id" => results[0].id,
|
150
|
+
"name" => "doc1"
|
151
|
+
})
|
152
|
+
end
|
153
|
+
|
154
|
+
end
|
@@ -0,0 +1,10 @@
|
|
1
|
+
require 's3search'
|
2
|
+
|
3
|
+
ENV['S3SEARCH_URL'] = 'http://s3search.dev'
|
4
|
+
|
5
|
+
RSpec.configure do |config|
|
6
|
+
config.treat_symbols_as_metadata_keys_with_true_values = true
|
7
|
+
config.run_all_when_everything_filtered = true
|
8
|
+
config.filter_run :focus
|
9
|
+
config.order = 'random'
|
10
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,30 @@
|
|
1
|
+
require 'webmock/rspec'
|
2
|
+
require 's3search'
|
3
|
+
require 'active_support/core_ext/object/try'
|
4
|
+
|
5
|
+
ENV['S3SEARCH_API_KEY'] = 'api_key'
|
6
|
+
ENV['S3SEARCH_API_SECRET'] = 'api_secret'
|
7
|
+
ENV['S3SEARCH_URL'] = 'https://@api.s3searchapp.test'
|
8
|
+
|
9
|
+
RSpec.configure do |config|
|
10
|
+
config.treat_symbols_as_metadata_keys_with_true_values = true
|
11
|
+
config.run_all_when_everything_filtered = true
|
12
|
+
config.filter_run :focus
|
13
|
+
config.order = 'random'
|
14
|
+
end
|
15
|
+
|
16
|
+
def stub_api_http method, resource, body=nil, &block
|
17
|
+
stub_request(
|
18
|
+
method,
|
19
|
+
"https://api.s3searchapp.test#{resource}"
|
20
|
+
).with(
|
21
|
+
body: body.try(:to_json),
|
22
|
+
headers: {
|
23
|
+
'Accept' => 'application/json',
|
24
|
+
'Content-Type' => 'application/json',
|
25
|
+
'Authorization' => 'Basic YXBpX2tleTphcGlfc2VjcmV0'
|
26
|
+
}
|
27
|
+
).to_return(
|
28
|
+
yield
|
29
|
+
)
|
30
|
+
end
|
metadata
CHANGED
@@ -1,20 +1,88 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: s3search
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
5
|
-
prerelease:
|
4
|
+
version: 0.0.2
|
6
5
|
platform: ruby
|
7
6
|
authors:
|
8
7
|
- Chris Aitchison
|
9
8
|
autorequire:
|
10
9
|
bindir: bin
|
11
10
|
cert_chain: []
|
12
|
-
date: 2013-06-
|
11
|
+
date: 2013-06-17 00:00:00.000000000 Z
|
13
12
|
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: activesupport
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - '>='
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: 3.0.0
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - '>='
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: 3.0.0
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: faraday
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ~>
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '0.8'
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - ~>
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '0.8'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: faraday_middleware
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - ~>
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: 0.9.0
|
48
|
+
type: :runtime
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - ~>
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: 0.9.0
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: excon
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - '>='
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0.16'
|
62
|
+
type: :runtime
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - '>='
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '0.16'
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: hashie
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - ~>
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: 1.2.0
|
76
|
+
type: :runtime
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - ~>
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: 1.2.0
|
14
83
|
- !ruby/object:Gem::Dependency
|
15
84
|
name: bundler
|
16
85
|
requirement: !ruby/object:Gem::Requirement
|
17
|
-
none: false
|
18
86
|
requirements:
|
19
87
|
- - ~>
|
20
88
|
- !ruby/object:Gem::Version
|
@@ -22,7 +90,6 @@ dependencies:
|
|
22
90
|
type: :development
|
23
91
|
prerelease: false
|
24
92
|
version_requirements: !ruby/object:Gem::Requirement
|
25
|
-
none: false
|
26
93
|
requirements:
|
27
94
|
- - ~>
|
28
95
|
- !ruby/object:Gem::Version
|
@@ -30,17 +97,15 @@ dependencies:
|
|
30
97
|
- !ruby/object:Gem::Dependency
|
31
98
|
name: rake
|
32
99
|
requirement: !ruby/object:Gem::Requirement
|
33
|
-
none: false
|
34
100
|
requirements:
|
35
|
-
- -
|
101
|
+
- - '>='
|
36
102
|
- !ruby/object:Gem::Version
|
37
103
|
version: '0'
|
38
104
|
type: :development
|
39
105
|
prerelease: false
|
40
106
|
version_requirements: !ruby/object:Gem::Requirement
|
41
|
-
none: false
|
42
107
|
requirements:
|
43
|
-
- -
|
108
|
+
- - '>='
|
44
109
|
- !ruby/object:Gem::Version
|
45
110
|
version: '0'
|
46
111
|
description: Ruby implementation of the S3Search API
|
@@ -51,42 +116,63 @@ extensions: []
|
|
51
116
|
extra_rdoc_files: []
|
52
117
|
files:
|
53
118
|
- .gitignore
|
119
|
+
- .rspec
|
54
120
|
- Gemfile
|
55
121
|
- LICENSE.txt
|
56
122
|
- README.md
|
57
123
|
- Rakefile
|
124
|
+
- integration/document_spec.rb
|
125
|
+
- integration/search_spec.rb
|
58
126
|
- lib/s3search.rb
|
127
|
+
- lib/s3search/api/documents.rb
|
128
|
+
- lib/s3search/api/searches.rb
|
129
|
+
- lib/s3search/client.rb
|
130
|
+
- lib/s3search/request_error.rb
|
131
|
+
- lib/s3search/result_page.rb
|
59
132
|
- lib/s3search/version.rb
|
60
133
|
- s3search.gemspec
|
134
|
+
- spec/acceptance/document_create_many_spec.rb
|
135
|
+
- spec/acceptance/document_create_spec.rb
|
136
|
+
- spec/acceptance/document_delete_spec.rb
|
137
|
+
- spec/acceptance/document_get_all_spec.rb
|
138
|
+
- spec/acceptance/document_get_spec.rb
|
139
|
+
- spec/acceptance/document_update_spec.rb
|
140
|
+
- spec/acceptance/search_simple_spec.rb
|
141
|
+
- spec/integration_helper.rb
|
142
|
+
- spec/spec_helper.rb
|
61
143
|
homepage: https://github.com/sodalis/s3search-ruby
|
62
144
|
licenses:
|
63
145
|
- MIT
|
146
|
+
metadata: {}
|
64
147
|
post_install_message:
|
65
148
|
rdoc_options: []
|
66
149
|
require_paths:
|
67
150
|
- lib
|
68
151
|
required_ruby_version: !ruby/object:Gem::Requirement
|
69
|
-
none: false
|
70
152
|
requirements:
|
71
|
-
- -
|
153
|
+
- - '>='
|
72
154
|
- !ruby/object:Gem::Version
|
73
155
|
version: '0'
|
74
|
-
segments:
|
75
|
-
- 0
|
76
|
-
hash: -1043739705409496661
|
77
156
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
78
|
-
none: false
|
79
157
|
requirements:
|
80
|
-
- -
|
158
|
+
- - '>='
|
81
159
|
- !ruby/object:Gem::Version
|
82
160
|
version: '0'
|
83
|
-
segments:
|
84
|
-
- 0
|
85
|
-
hash: -1043739705409496661
|
86
161
|
requirements: []
|
87
162
|
rubyforge_project:
|
88
|
-
rubygems_version:
|
163
|
+
rubygems_version: 2.0.2
|
89
164
|
signing_key:
|
90
|
-
specification_version:
|
165
|
+
specification_version: 4
|
91
166
|
summary: S3Search Ruby API
|
92
|
-
test_files:
|
167
|
+
test_files:
|
168
|
+
- integration/document_spec.rb
|
169
|
+
- integration/search_spec.rb
|
170
|
+
- spec/acceptance/document_create_many_spec.rb
|
171
|
+
- spec/acceptance/document_create_spec.rb
|
172
|
+
- spec/acceptance/document_delete_spec.rb
|
173
|
+
- spec/acceptance/document_get_all_spec.rb
|
174
|
+
- spec/acceptance/document_get_spec.rb
|
175
|
+
- spec/acceptance/document_update_spec.rb
|
176
|
+
- spec/acceptance/search_simple_spec.rb
|
177
|
+
- spec/integration_helper.rb
|
178
|
+
- spec/spec_helper.rb
|