stretcher 0.0.1
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/.gitignore +20 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +19 -0
- data/README.md +64 -0
- data/Rakefile +8 -0
- data/lib/stretcher.rb +14 -0
- data/lib/stretcher/index.rb +86 -0
- data/lib/stretcher/index_type.rb +76 -0
- data/lib/stretcher/request_error.rb +10 -0
- data/lib/stretcher/search_results.rb +18 -0
- data/lib/stretcher/server.rb +104 -0
- data/lib/stretcher/util.rb +7 -0
- data/lib/stretcher/version.rb +3 -0
- data/spec/lib/stretcher_index_spec.rb +54 -0
- data/spec/lib/stretcher_index_type_spec.rb +47 -0
- data/spec/lib/stretcher_server_spec.rb +17 -0
- data/spec/spec_helper.rb +5 -0
- data/spec/stretcher_spec.rb +4 -0
- data/stretcher.gemspec +28 -0
- metadata +182 -0
data/.gitignore
ADDED
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
Copyright (c) 2012 & 2013 Gyroscope Technologies Inc., Andrew Cholakian, Micah Winkelspecht, Jonathan Hoyt
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
4
|
+
of this software and associated documentation files (the "Software"), to deal
|
5
|
+
in the Software without restriction, including without limitation the rights
|
6
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
7
|
+
copies of the Software, and to permit persons to whom the Software is
|
8
|
+
furnished to do so, subject to the following conditions:
|
9
|
+
|
10
|
+
The above copyright notice and this permission notice shall be included in
|
11
|
+
all copies or substantial portions of the Software.
|
12
|
+
|
13
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
14
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
15
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
16
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
17
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
18
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
19
|
+
THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,64 @@
|
|
1
|
+
# Stretcher
|
2
|
+
|
3
|
+
A concise, fast, ElasticSearch client designed to reflect the actual elastic search API as closely as possible, making minimal allowances for convenience.
|
4
|
+
|
5
|
+
# Features
|
6
|
+
|
7
|
+
* Cleanly matches up to elastic search's JSON api
|
8
|
+
* Efficiently re-uses connections on a per-server object basis (via net/http/persistent)
|
9
|
+
* Supports efficient bulk indexing operations
|
10
|
+
* Returns most responses in convenient Hashie::Mash form
|
11
|
+
* Easily extensible
|
12
|
+
* Configurable logging
|
13
|
+
|
14
|
+
## Installation
|
15
|
+
|
16
|
+
Add this line to your application's Gemfile:
|
17
|
+
|
18
|
+
gem 'stretcher'
|
19
|
+
|
20
|
+
## Usage
|
21
|
+
|
22
|
+
### Basic Usage
|
23
|
+
|
24
|
+
```ruby
|
25
|
+
# First Create a server
|
26
|
+
server = Stretcher::Server.new('http://localhost:9200')
|
27
|
+
# Delete an index (in case you already have this one)
|
28
|
+
server.index('foo').delete rescue nil
|
29
|
+
# Create an index
|
30
|
+
server.index('foo').create(mapping: {tweet: {properties: {text: 'string'}}})
|
31
|
+
# Add a document
|
32
|
+
server.index('foo').type('tweet').put(123, {text: 'Hello'})
|
33
|
+
# Retrieve a document
|
34
|
+
server.index('foo').type('tweet').get(123)
|
35
|
+
# Perform a search (Returns a Stretcher::SearchResults instance)
|
36
|
+
res = server.index('foo').search({size: 12}, {query: {match_all: {}}})
|
37
|
+
res.class # Stretcher::SearchResults
|
38
|
+
res.total # => 1
|
39
|
+
res.facets # => nil
|
40
|
+
res.results # => [#<Hashie::Mash _id="123" text="Hello">]
|
41
|
+
res.raw # => #<Hashie::Mash ...> Raw JSON from the search
|
42
|
+
```
|
43
|
+
|
44
|
+
### MultiSearch
|
45
|
+
|
46
|
+
```ruby
|
47
|
+
server.index('foo').msearch([{query: {match_all: {}}}])
|
48
|
+
# => Returns an array of Stretcher::SearchResults
|
49
|
+
```
|
50
|
+
|
51
|
+
### Bulk Indexing
|
52
|
+
|
53
|
+
```ruby
|
54
|
+
docs = [{"_type" => "tweet", "_id" => 91011, "text" => "Bulked"}]
|
55
|
+
server.index('foo').bulk_index(docs)
|
56
|
+
```
|
57
|
+
|
58
|
+
## Contributing
|
59
|
+
|
60
|
+
1. Fork it
|
61
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
62
|
+
3. Commit your changes (`git commit -am 'Add some feature'`)
|
63
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
64
|
+
5. Create new Pull Request
|
data/Rakefile
ADDED
data/lib/stretcher.rb
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
require 'logger'
|
2
|
+
require 'hashie'
|
3
|
+
require 'net/http/persistent'
|
4
|
+
require 'faraday'
|
5
|
+
require 'faraday_middleware'
|
6
|
+
require "stretcher/version"
|
7
|
+
require 'stretcher/request_error'
|
8
|
+
require 'stretcher/server'
|
9
|
+
require 'stretcher/index'
|
10
|
+
require 'stretcher/index_type'
|
11
|
+
require 'stretcher/util'
|
12
|
+
|
13
|
+
module Stretcher
|
14
|
+
end
|
@@ -0,0 +1,86 @@
|
|
1
|
+
require 'stretcher/search_results'
|
2
|
+
module Stretcher
|
3
|
+
class Index
|
4
|
+
attr_reader :server, :name, :logger
|
5
|
+
|
6
|
+
def initialize(server, name, options={})
|
7
|
+
@server = server
|
8
|
+
@name = name
|
9
|
+
@logger = options[:logger] || server.logger
|
10
|
+
end
|
11
|
+
|
12
|
+
def type(name)
|
13
|
+
IndexType.new(self, name)
|
14
|
+
end
|
15
|
+
|
16
|
+
# Given a hash of documents, will bulk index
|
17
|
+
def bulk_index(documents)
|
18
|
+
@server.bulk documents.reduce("") {|post_data, d_raw|
|
19
|
+
d = Hashie::Mash.new(d_raw)
|
20
|
+
action_meta = {"index" => {"_index" => name, "_type" => d["_type"], "_id" => d["id"]}}
|
21
|
+
action_meta["index"]["_parent"] = d["_parent"] if d["_parent"]
|
22
|
+
post_data << (action_meta.to_json + "\n")
|
23
|
+
post_data << (d.to_json + "\n")
|
24
|
+
}
|
25
|
+
end
|
26
|
+
|
27
|
+
# Creates the index, with the supplied hash as the optinos body (usually mappings: and settings:))
|
28
|
+
def create(options={})
|
29
|
+
@server.request(:put, path_uri) do |req|
|
30
|
+
req.body = options
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
# Deletes the index
|
35
|
+
def delete
|
36
|
+
@server.request :delete, path_uri
|
37
|
+
end
|
38
|
+
|
39
|
+
# Retrieves stats from the server
|
40
|
+
def stats
|
41
|
+
@server.request :get, path_uri("/_stats")
|
42
|
+
end
|
43
|
+
|
44
|
+
# Retrieve the mapping for this index
|
45
|
+
def get_mapping
|
46
|
+
@server.request :get, path_uri("/_mapping")
|
47
|
+
end
|
48
|
+
|
49
|
+
# Retrieve settings for this index
|
50
|
+
def get_settings
|
51
|
+
@server.request :get, path_uri("/_settings")
|
52
|
+
end
|
53
|
+
|
54
|
+
# Check if the index has been created on the remote server
|
55
|
+
def exists?
|
56
|
+
@server.http.head(path_uri).status != 404
|
57
|
+
end
|
58
|
+
|
59
|
+
def search(query_opts={}, body=nil)
|
60
|
+
uri = path_uri('/_search?' + Util.querify(query_opts))
|
61
|
+
logger.info { "Stretcher Search: curl -XGET #{uri} -d '#{body.to_json}'" }
|
62
|
+
response = @server.request(:get, uri) do |req|
|
63
|
+
req.body = body
|
64
|
+
end
|
65
|
+
SearchResults.new(raw: response)
|
66
|
+
end
|
67
|
+
|
68
|
+
# Searches a list of queries against only this index
|
69
|
+
# This deviates slightly from the official API in that *ONLY*
|
70
|
+
# queries are requried, the empty {} preceding them are not
|
71
|
+
# See: http://www.elasticsearch.org/guide/reference/api/multi-search.html
|
72
|
+
def msearch(queries=[])
|
73
|
+
raise ArgumentError, "msearch takes an array!" unless queries.is_a?(Array)
|
74
|
+
req_body = queries.reduce([]) {|acc,q|
|
75
|
+
acc << {index: name}
|
76
|
+
acc << q
|
77
|
+
acc
|
78
|
+
}
|
79
|
+
@server.msearch req_body
|
80
|
+
end
|
81
|
+
|
82
|
+
def path_uri(path="/")
|
83
|
+
@server.path_uri("/#{name}") + path.to_s
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
@@ -0,0 +1,76 @@
|
|
1
|
+
module Stretcher
|
2
|
+
# Represents an index, but scoped to a specific type
|
3
|
+
class IndexType
|
4
|
+
attr_reader :server, :index, :name, :logger
|
5
|
+
|
6
|
+
def initialize(index, name, options={})
|
7
|
+
@index = index
|
8
|
+
@server = index.server
|
9
|
+
@name = name
|
10
|
+
@logger = options[:logger] || index.logger
|
11
|
+
end
|
12
|
+
|
13
|
+
# Retrieves the document by ID
|
14
|
+
# Normally this returns the contents of _source, however, the 'raw' flag is passed in, it will return the full response hash
|
15
|
+
def get(id, raw=false)
|
16
|
+
res = server.request(:get, path_uri("/#{id}"))
|
17
|
+
raw ? res : res["_source"]
|
18
|
+
end
|
19
|
+
|
20
|
+
# Index an item with a specific ID
|
21
|
+
def put(id, source)
|
22
|
+
server.request(:put, path_uri("/#{id}"), source)
|
23
|
+
end
|
24
|
+
|
25
|
+
# Uses the update api to modify a document with a script
|
26
|
+
# To update a doc with ID 987 for example:
|
27
|
+
# type.update(987, script: "ctx._source.message = 'Updated!'")
|
28
|
+
# See http://www.elasticsearch.org/guide/reference/api/update.html
|
29
|
+
def update(id, body)
|
30
|
+
server.request(:post, path_uri("/#{id}/_update"), body)
|
31
|
+
end
|
32
|
+
|
33
|
+
# Deletes the document with the given ID
|
34
|
+
def delete(id)
|
35
|
+
res = server.http.delete path_uri("/#{id}")
|
36
|
+
|
37
|
+
# Since 404s are just not a problem here, let's simply return false
|
38
|
+
if res.status == 404
|
39
|
+
false
|
40
|
+
elsif res.status >= 200 && res.status <= 299
|
41
|
+
res.body
|
42
|
+
else
|
43
|
+
raise RequestError.new(res), "Error processing delete request! Status: #{res.status}\n Body: #{res.body}"
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
# Retrieve the mapping for this type
|
48
|
+
def get_mapping
|
49
|
+
@server.request :get, path_uri("/_mapping")
|
50
|
+
end
|
51
|
+
|
52
|
+
# Delete the mapping for this type. Note this will delete
|
53
|
+
# All documents of this type as well
|
54
|
+
# http://www.elasticsearch.org/guide/reference/api/admin-indices-delete-mapping.html
|
55
|
+
def delete_mapping
|
56
|
+
@server.request :delete, path_uri("/_mapping")
|
57
|
+
end
|
58
|
+
|
59
|
+
# Alter the mapping for this type
|
60
|
+
def put_mapping(body)
|
61
|
+
@server.request(:put, path_uri("/_mapping")) {|req|
|
62
|
+
req.body = body
|
63
|
+
}
|
64
|
+
end
|
65
|
+
|
66
|
+
# Check if this index type is defined, if passed an id
|
67
|
+
# this will check if the given document exists
|
68
|
+
def exists?(id=nil)
|
69
|
+
server.http.head(path_uri("/#{id}")).status != 404
|
70
|
+
end
|
71
|
+
|
72
|
+
def path_uri(path="/")
|
73
|
+
index.path_uri("/#{name}") + path.to_s
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
require 'hashie/dash'
|
2
|
+
module Stretcher
|
3
|
+
class SearchResults < Hashie::Dash
|
4
|
+
property :raw, required: true
|
5
|
+
property :total
|
6
|
+
property :facets
|
7
|
+
property :results
|
8
|
+
|
9
|
+
def initialize(*args)
|
10
|
+
super
|
11
|
+
self.total = raw.hits.total
|
12
|
+
self.facets = raw.facets
|
13
|
+
self.results = raw.hits.hits.collect {|r|
|
14
|
+
r['_source'].merge({"_id" => r['_id']})
|
15
|
+
}
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,104 @@
|
|
1
|
+
module Stretcher
|
2
|
+
class Server
|
3
|
+
attr_reader :uri, :http, :logger
|
4
|
+
|
5
|
+
def initialize(uri, options={})
|
6
|
+
@uri = uri
|
7
|
+
|
8
|
+
@http = Faraday.new(:url => @uri) do |builder|
|
9
|
+
builder.response :mashify
|
10
|
+
builder.response :json, :content_type => /\bjson$/
|
11
|
+
|
12
|
+
builder.request :json
|
13
|
+
|
14
|
+
builder.adapter :net_http_persistent
|
15
|
+
|
16
|
+
builder.options[:read_timeout] = 4
|
17
|
+
builder.options[:open_timeout] = 2
|
18
|
+
end
|
19
|
+
|
20
|
+
if options[:logger]
|
21
|
+
@logger = options[:logger]
|
22
|
+
else
|
23
|
+
@logger = Logger.new(STDOUT)
|
24
|
+
@logger.level = Logger::WARN
|
25
|
+
end
|
26
|
+
|
27
|
+
@logger.formatter = proc do |severity, datetime, progname, msg|
|
28
|
+
"[Stretcher][#{severity}]: #{msg}\n"
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
def index(name)
|
33
|
+
Index.new(self, name, logger: logger)
|
34
|
+
end
|
35
|
+
|
36
|
+
def bulk(data)
|
37
|
+
request(:post, path_uri("/_bulk")) do |req|
|
38
|
+
req.body = data
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
# Returns true if the server is currently reachable, raises an error otherwise
|
43
|
+
def up?
|
44
|
+
request(:get, path_uri)
|
45
|
+
true
|
46
|
+
end
|
47
|
+
|
48
|
+
# Takes an array of msearch data as per
|
49
|
+
# http://www.elasticsearch.org/guide/reference/api/multi-search.html
|
50
|
+
# Should look something like:
|
51
|
+
# {"index" : "test"}
|
52
|
+
# {"query" : {"match_all" : {}}, "from" : 0, "size" : 10}
|
53
|
+
# {"index" : "test", "search_type" : "count"}
|
54
|
+
# {"query" : {"match_all" : {}}}
|
55
|
+
def msearch(body=[])
|
56
|
+
raise ArgumentError, "msearch takes an array!" unless body.is_a?(Array)
|
57
|
+
fmt_body = body.map(&:to_json).join("\n") + "\n"
|
58
|
+
logger.info { "Stretcher msearch: curl -XGET #{uri} -d '#{fmt_body}'" }
|
59
|
+
res = request(:get, path_uri("/_msearch")) {|req|
|
60
|
+
req.body = fmt_body
|
61
|
+
}
|
62
|
+
|
63
|
+
# Is this necessary?
|
64
|
+
raise RequestError.new(res), "Could not msearch" if res['error']
|
65
|
+
|
66
|
+
res['responses'].map {|r| SearchResults.new(raw: r)}
|
67
|
+
end
|
68
|
+
|
69
|
+
# Retrieves multiple documents, possibly from multiple indexes
|
70
|
+
# as per: http://www.elasticsearch.org/guide/reference/api/multi-get.html
|
71
|
+
def mget(body={})
|
72
|
+
request(:get, path_uri("/_mget")) {|req|
|
73
|
+
req.body = body
|
74
|
+
}
|
75
|
+
end
|
76
|
+
|
77
|
+
def path_uri(path="/")
|
78
|
+
@uri.to_s + path.to_s
|
79
|
+
end
|
80
|
+
|
81
|
+
# Handy way to query the server, returning *only* the body
|
82
|
+
# Will raise an exception when the status is not in the 2xx range
|
83
|
+
def request(method, *args, &block)
|
84
|
+
logger.info("Stretcher: Issuing Request #{method.upcase}, #{args}")
|
85
|
+
res = if block
|
86
|
+
http.send(method, *args) do |req|
|
87
|
+
# Elastic search does mostly deal with JSON
|
88
|
+
req.headers["Content-Type"] = 'application/json'
|
89
|
+
block.call(req)
|
90
|
+
end
|
91
|
+
else
|
92
|
+
http.send(method, *args)
|
93
|
+
end
|
94
|
+
|
95
|
+
if res.status >= 200 && res.status <= 299
|
96
|
+
res.body
|
97
|
+
else
|
98
|
+
err_str = "Error processing request (#{res.status})! #{res.env[:method]} URL: #{res.env[:url]}"
|
99
|
+
err_str << "\n Resp Body: #{res.body}"
|
100
|
+
raise RequestError.new(res), err_str
|
101
|
+
end
|
102
|
+
end
|
103
|
+
end
|
104
|
+
end
|
@@ -0,0 +1,54 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Stretcher::Index do
|
4
|
+
let(:server) { Stretcher::Server.new(ES_URL) }
|
5
|
+
let(:index) { server.index('foo') }
|
6
|
+
let(:corpus) {
|
7
|
+
[
|
8
|
+
{text: "Foo", "_type" => 'tweet'},
|
9
|
+
{text: "Bar", "_type" => 'tweet'},
|
10
|
+
{text: "Baz", "_type" => 'tweet'}
|
11
|
+
]
|
12
|
+
}
|
13
|
+
|
14
|
+
def create_tweet_mapping
|
15
|
+
mdata = {tweet:{properties: {text: {type: :string}}}}
|
16
|
+
index.type('tweet').put_mapping(mdata)
|
17
|
+
end
|
18
|
+
|
19
|
+
def seed_corpus
|
20
|
+
create_tweet_mapping
|
21
|
+
index.bulk_index(corpus)
|
22
|
+
end
|
23
|
+
|
24
|
+
it "should work on an existential level" do
|
25
|
+
index.delete rescue nil
|
26
|
+
index.exists?.should be_false
|
27
|
+
index.create
|
28
|
+
index.exists?.should be_true
|
29
|
+
end
|
30
|
+
|
31
|
+
it "should return stats without error" do
|
32
|
+
index.stats['_all'].should_not be_nil
|
33
|
+
end
|
34
|
+
|
35
|
+
it "should put mappings for new types correctly" do
|
36
|
+
create_tweet_mapping
|
37
|
+
end
|
38
|
+
|
39
|
+
it "should retrieve settings properly" do
|
40
|
+
index.get_settings['foo']['settings']['index.number_of_replicas'].should_not be_nil
|
41
|
+
end
|
42
|
+
|
43
|
+
it "should bulk index documents properly" do
|
44
|
+
seed_corpus
|
45
|
+
end
|
46
|
+
|
47
|
+
# TODO: Actually use two indexes
|
48
|
+
it "should msearch across the index returning all docs" do
|
49
|
+
seed_corpus
|
50
|
+
res = index.msearch([{query: {match_all: {}}}])
|
51
|
+
res.length.should == 1
|
52
|
+
res[0].class.should == Stretcher::SearchResults
|
53
|
+
end
|
54
|
+
end
|
@@ -0,0 +1,47 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Stretcher::Index do
|
4
|
+
let(:server) { Stretcher::Server.new(ES_URL) }
|
5
|
+
let(:index) { server.index('foo') }
|
6
|
+
let(:type) { index.type('bar') }
|
7
|
+
|
8
|
+
it "should be existentially aware" do
|
9
|
+
type.delete rescue nil
|
10
|
+
type.exists?.should be_false
|
11
|
+
mapping = {"bar" => {"properties" => {"message" => {"type" => "string"}}}}
|
12
|
+
type.put_mapping mapping
|
13
|
+
type.exists?.should be_true
|
14
|
+
type.get_mapping.should == mapping
|
15
|
+
end
|
16
|
+
|
17
|
+
describe "put/get" do
|
18
|
+
before do
|
19
|
+
@doc = {message: "hello!"}
|
20
|
+
end
|
21
|
+
|
22
|
+
it "should put correctly" do
|
23
|
+
type.put(987, @doc).should_not be_nil
|
24
|
+
end
|
25
|
+
|
26
|
+
it "should get individual documents correctly" do
|
27
|
+
type.get(987).message.should == @doc[:message]
|
28
|
+
end
|
29
|
+
|
30
|
+
it "should get individual raw documents correctly" do
|
31
|
+
res = type.get(987, true)
|
32
|
+
res["_id"].should == "987"
|
33
|
+
res["_source"].message.should == @doc[:message]
|
34
|
+
end
|
35
|
+
|
36
|
+
it "should update individual docs correctly" do
|
37
|
+
type.update(987, script: "ctx._source.message = 'Updated!'")
|
38
|
+
type.get(987).message.should == 'Updated!'
|
39
|
+
end
|
40
|
+
|
41
|
+
it "should delete individual docs correctly" do
|
42
|
+
type.exists?(987).should be_true
|
43
|
+
type.delete(987)
|
44
|
+
type.exists?(987).should be_false
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Stretcher::Server do
|
4
|
+
let(:server) { Stretcher::Server.new(ES_URL) }
|
5
|
+
|
6
|
+
it "should initialize cleanly" do
|
7
|
+
server.class.should == Stretcher::Server
|
8
|
+
end
|
9
|
+
|
10
|
+
it "should properly return that our server is up" do
|
11
|
+
server.up?.should be_true
|
12
|
+
end
|
13
|
+
|
14
|
+
it "should beget an index object cleanly" do
|
15
|
+
server.index('foo').class.should == Stretcher::Index
|
16
|
+
end
|
17
|
+
end
|
data/spec/spec_helper.rb
ADDED
data/stretcher.gemspec
ADDED
@@ -0,0 +1,28 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'stretcher/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |gem|
|
7
|
+
gem.name = "stretcher"
|
8
|
+
gem.version = Stretcher::VERSION
|
9
|
+
gem.authors = ["Andrew Cholakian"]
|
10
|
+
gem.email = ["andrew@andrewvc.com"]
|
11
|
+
gem.description = %q{The elegant ElasticSearch client}
|
12
|
+
gem.summary = %q{The elegant ElasticSearch client, supporting persistent connections, and a clean DSL}
|
13
|
+
gem.homepage = ""
|
14
|
+
|
15
|
+
gem.files = `git ls-files`.split($/)
|
16
|
+
gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
|
17
|
+
gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
|
18
|
+
gem.require_paths = ["lib"]
|
19
|
+
|
20
|
+
gem.add_dependency('faraday', '~> 0.8')
|
21
|
+
gem.add_dependency('faraday_middleware', '~> 0.9.0')
|
22
|
+
gem.add_dependency('net-http-persistent', '~> 2.8')
|
23
|
+
gem.add_dependency('hashie', '~> 1.2.0')
|
24
|
+
|
25
|
+
gem.add_development_dependency 'rspec', '>= 2.5.0'
|
26
|
+
gem.add_development_dependency 'simplecov'
|
27
|
+
gem.add_development_dependency 'rake'
|
28
|
+
end
|
metadata
ADDED
@@ -0,0 +1,182 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: stretcher
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Andrew Cholakian
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2013-01-17 00:00:00.000000000 Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: faraday
|
16
|
+
requirement: !ruby/object:Gem::Requirement
|
17
|
+
none: false
|
18
|
+
requirements:
|
19
|
+
- - ~>
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: '0.8'
|
22
|
+
type: :runtime
|
23
|
+
prerelease: false
|
24
|
+
version_requirements: !ruby/object:Gem::Requirement
|
25
|
+
none: false
|
26
|
+
requirements:
|
27
|
+
- - ~>
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
version: '0.8'
|
30
|
+
- !ruby/object:Gem::Dependency
|
31
|
+
name: faraday_middleware
|
32
|
+
requirement: !ruby/object:Gem::Requirement
|
33
|
+
none: false
|
34
|
+
requirements:
|
35
|
+
- - ~>
|
36
|
+
- !ruby/object:Gem::Version
|
37
|
+
version: 0.9.0
|
38
|
+
type: :runtime
|
39
|
+
prerelease: false
|
40
|
+
version_requirements: !ruby/object:Gem::Requirement
|
41
|
+
none: false
|
42
|
+
requirements:
|
43
|
+
- - ~>
|
44
|
+
- !ruby/object:Gem::Version
|
45
|
+
version: 0.9.0
|
46
|
+
- !ruby/object:Gem::Dependency
|
47
|
+
name: net-http-persistent
|
48
|
+
requirement: !ruby/object:Gem::Requirement
|
49
|
+
none: false
|
50
|
+
requirements:
|
51
|
+
- - ~>
|
52
|
+
- !ruby/object:Gem::Version
|
53
|
+
version: '2.8'
|
54
|
+
type: :runtime
|
55
|
+
prerelease: false
|
56
|
+
version_requirements: !ruby/object:Gem::Requirement
|
57
|
+
none: false
|
58
|
+
requirements:
|
59
|
+
- - ~>
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '2.8'
|
62
|
+
- !ruby/object:Gem::Dependency
|
63
|
+
name: hashie
|
64
|
+
requirement: !ruby/object:Gem::Requirement
|
65
|
+
none: false
|
66
|
+
requirements:
|
67
|
+
- - ~>
|
68
|
+
- !ruby/object:Gem::Version
|
69
|
+
version: 1.2.0
|
70
|
+
type: :runtime
|
71
|
+
prerelease: false
|
72
|
+
version_requirements: !ruby/object:Gem::Requirement
|
73
|
+
none: false
|
74
|
+
requirements:
|
75
|
+
- - ~>
|
76
|
+
- !ruby/object:Gem::Version
|
77
|
+
version: 1.2.0
|
78
|
+
- !ruby/object:Gem::Dependency
|
79
|
+
name: rspec
|
80
|
+
requirement: !ruby/object:Gem::Requirement
|
81
|
+
none: false
|
82
|
+
requirements:
|
83
|
+
- - ! '>='
|
84
|
+
- !ruby/object:Gem::Version
|
85
|
+
version: 2.5.0
|
86
|
+
type: :development
|
87
|
+
prerelease: false
|
88
|
+
version_requirements: !ruby/object:Gem::Requirement
|
89
|
+
none: false
|
90
|
+
requirements:
|
91
|
+
- - ! '>='
|
92
|
+
- !ruby/object:Gem::Version
|
93
|
+
version: 2.5.0
|
94
|
+
- !ruby/object:Gem::Dependency
|
95
|
+
name: simplecov
|
96
|
+
requirement: !ruby/object:Gem::Requirement
|
97
|
+
none: false
|
98
|
+
requirements:
|
99
|
+
- - ! '>='
|
100
|
+
- !ruby/object:Gem::Version
|
101
|
+
version: '0'
|
102
|
+
type: :development
|
103
|
+
prerelease: false
|
104
|
+
version_requirements: !ruby/object:Gem::Requirement
|
105
|
+
none: false
|
106
|
+
requirements:
|
107
|
+
- - ! '>='
|
108
|
+
- !ruby/object:Gem::Version
|
109
|
+
version: '0'
|
110
|
+
- !ruby/object:Gem::Dependency
|
111
|
+
name: rake
|
112
|
+
requirement: !ruby/object:Gem::Requirement
|
113
|
+
none: false
|
114
|
+
requirements:
|
115
|
+
- - ! '>='
|
116
|
+
- !ruby/object:Gem::Version
|
117
|
+
version: '0'
|
118
|
+
type: :development
|
119
|
+
prerelease: false
|
120
|
+
version_requirements: !ruby/object:Gem::Requirement
|
121
|
+
none: false
|
122
|
+
requirements:
|
123
|
+
- - ! '>='
|
124
|
+
- !ruby/object:Gem::Version
|
125
|
+
version: '0'
|
126
|
+
description: The elegant ElasticSearch client
|
127
|
+
email:
|
128
|
+
- andrew@andrewvc.com
|
129
|
+
executables: []
|
130
|
+
extensions: []
|
131
|
+
extra_rdoc_files: []
|
132
|
+
files:
|
133
|
+
- .gitignore
|
134
|
+
- Gemfile
|
135
|
+
- LICENSE.txt
|
136
|
+
- README.md
|
137
|
+
- Rakefile
|
138
|
+
- lib/stretcher.rb
|
139
|
+
- lib/stretcher/index.rb
|
140
|
+
- lib/stretcher/index_type.rb
|
141
|
+
- lib/stretcher/request_error.rb
|
142
|
+
- lib/stretcher/search_results.rb
|
143
|
+
- lib/stretcher/server.rb
|
144
|
+
- lib/stretcher/util.rb
|
145
|
+
- lib/stretcher/version.rb
|
146
|
+
- spec/lib/stretcher_index_spec.rb
|
147
|
+
- spec/lib/stretcher_index_type_spec.rb
|
148
|
+
- spec/lib/stretcher_server_spec.rb
|
149
|
+
- spec/spec_helper.rb
|
150
|
+
- spec/stretcher_spec.rb
|
151
|
+
- stretcher.gemspec
|
152
|
+
homepage: ''
|
153
|
+
licenses: []
|
154
|
+
post_install_message:
|
155
|
+
rdoc_options: []
|
156
|
+
require_paths:
|
157
|
+
- lib
|
158
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
159
|
+
none: false
|
160
|
+
requirements:
|
161
|
+
- - ! '>='
|
162
|
+
- !ruby/object:Gem::Version
|
163
|
+
version: '0'
|
164
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
165
|
+
none: false
|
166
|
+
requirements:
|
167
|
+
- - ! '>='
|
168
|
+
- !ruby/object:Gem::Version
|
169
|
+
version: '0'
|
170
|
+
requirements: []
|
171
|
+
rubyforge_project:
|
172
|
+
rubygems_version: 1.8.23
|
173
|
+
signing_key:
|
174
|
+
specification_version: 3
|
175
|
+
summary: The elegant ElasticSearch client, supporting persistent connections, and
|
176
|
+
a clean DSL
|
177
|
+
test_files:
|
178
|
+
- spec/lib/stretcher_index_spec.rb
|
179
|
+
- spec/lib/stretcher_index_type_spec.rb
|
180
|
+
- spec/lib/stretcher_server_spec.rb
|
181
|
+
- spec/spec_helper.rb
|
182
|
+
- spec/stretcher_spec.rb
|