indextank 0.0.4
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/indextank.rb +23 -0
- data/lib/indextank/client.rb +36 -0
- data/lib/indextank/document.rb +98 -0
- data/lib/indextank/exceptions.rb +10 -0
- data/lib/indextank/function.rb +59 -0
- data/lib/indextank/index.rb +127 -0
- data/spec/lib/indextank/client_spec.rb +33 -0
- data/spec/lib/indextank/document_spec.rb +127 -0
- data/spec/lib/indextank/function_spec.rb +90 -0
- data/spec/lib/indextank/index_spec.rb +266 -0
- data/spec/spec_helper.rb +28 -0
- metadata +223 -0
data/lib/indextank.rb
ADDED
@@ -0,0 +1,23 @@
|
|
1
|
+
require 'faraday'
|
2
|
+
require 'uri'
|
3
|
+
|
4
|
+
directory = File.expand_path(File.dirname(__FILE__))
|
5
|
+
require File.join(directory, 'indextank', 'client')
|
6
|
+
|
7
|
+
module IndexTank
|
8
|
+
VERSION = "0.0.4"
|
9
|
+
|
10
|
+
def self.setup_connection(url, &block)
|
11
|
+
@conn = Faraday::Connection.new(:url => url) do |builder|
|
12
|
+
builder.adapter Faraday.default_adapter
|
13
|
+
builder.use Faraday::Response::Yajl
|
14
|
+
if block_given?
|
15
|
+
block.call builder
|
16
|
+
end
|
17
|
+
end
|
18
|
+
@uri = URI.parse(url)
|
19
|
+
@conn.basic_auth @uri.user,@uri.password
|
20
|
+
@conn
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
@@ -0,0 +1,36 @@
|
|
1
|
+
require 'indextank/index'
|
2
|
+
require 'uri'
|
3
|
+
|
4
|
+
module IndexTank
|
5
|
+
class Client
|
6
|
+
attr_reader :uri
|
7
|
+
|
8
|
+
def initialize(api_url)
|
9
|
+
@uri = api_url
|
10
|
+
@conn = IndexTank.setup_connection(api_url)
|
11
|
+
end
|
12
|
+
|
13
|
+
def indexes(name = nil)
|
14
|
+
if name.nil?
|
15
|
+
list_indexes
|
16
|
+
else
|
17
|
+
get_index(name)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
private
|
22
|
+
def list_indexes
|
23
|
+
indexes = Hash.new
|
24
|
+
|
25
|
+
@conn.get("/v1/indexes").body.each do |name, metadata|
|
26
|
+
indexes[name] = Index.new("#{@uri}/v1/indexes/#{name}", metadata)
|
27
|
+
end
|
28
|
+
|
29
|
+
indexes
|
30
|
+
end
|
31
|
+
|
32
|
+
def get_index(name)
|
33
|
+
Index.new("#{@uri}/v1/indexes/#{name}")
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
@@ -0,0 +1,98 @@
|
|
1
|
+
require 'json'
|
2
|
+
require 'faraday'
|
3
|
+
|
4
|
+
module IndexTank
|
5
|
+
class Document
|
6
|
+
attr_reader :docid
|
7
|
+
|
8
|
+
def initialize(document_url, docid)
|
9
|
+
@docid = docid
|
10
|
+
builder = Proc.new { |builder| builder.use ResponseDocument }
|
11
|
+
@conn = IndexTank.setup_connection(document_url, &builder)
|
12
|
+
end
|
13
|
+
|
14
|
+
# the options argument may contain a :variables key
|
15
|
+
# with a Hash from variable numbers to their float values
|
16
|
+
# this variables can be used in the scoring functions
|
17
|
+
# when sorting a search
|
18
|
+
def add(fields, options = {})
|
19
|
+
options.merge!(:docid => self.docid, :fields => fields)
|
20
|
+
|
21
|
+
resp = @conn.put do |req|
|
22
|
+
req.url ""
|
23
|
+
req.body = options.to_json
|
24
|
+
end
|
25
|
+
|
26
|
+
resp.status
|
27
|
+
end
|
28
|
+
|
29
|
+
def delete(options = {})
|
30
|
+
options.merge!(:docid => self.docid)
|
31
|
+
resp = @conn.delete do |req|
|
32
|
+
req.url ""
|
33
|
+
req.body = options.to_json
|
34
|
+
end
|
35
|
+
|
36
|
+
resp.status
|
37
|
+
end
|
38
|
+
|
39
|
+
def update_variables(variables, options = {})
|
40
|
+
options.merge!(:docid => self.docid, :variables => variables)
|
41
|
+
resp = @conn.put do |req|
|
42
|
+
req.url "variables"
|
43
|
+
req.body = options.to_json
|
44
|
+
end
|
45
|
+
|
46
|
+
resp.status
|
47
|
+
end
|
48
|
+
|
49
|
+
# updates the categories of a given document
|
50
|
+
# the categories argument should be a Hash from string
|
51
|
+
# to string defining the value for each category defined
|
52
|
+
# by this document.
|
53
|
+
def update_categories(categories, options = {} )
|
54
|
+
options.merge!(:docid => self.docid, :categories => categories)
|
55
|
+
resp = @conn.put do |req|
|
56
|
+
req.url "categories"
|
57
|
+
req.body = options.to_json
|
58
|
+
end
|
59
|
+
|
60
|
+
resp.status
|
61
|
+
end
|
62
|
+
#private
|
63
|
+
# Handles standard returns status. All methods on documents should return HTTP 200,
|
64
|
+
# and the errors are 'common' for any other value
|
65
|
+
#def handle_return_status(status)
|
66
|
+
# case status
|
67
|
+
# when 400
|
68
|
+
# raise InvalidArgument
|
69
|
+
# when 409
|
70
|
+
# raise IndexInitializing
|
71
|
+
# when 404
|
72
|
+
# raise IndexNotFound
|
73
|
+
# end
|
74
|
+
#end
|
75
|
+
end
|
76
|
+
|
77
|
+
class ResponseDocument < Faraday::Response::Middleware
|
78
|
+
def self.register_on_complete(env)
|
79
|
+
env[:response].on_complete do |finished_env|
|
80
|
+
case finished_env[:status]
|
81
|
+
when 401
|
82
|
+
raise InvalidApiKey
|
83
|
+
when 409
|
84
|
+
raise IndexInitializing
|
85
|
+
when 404
|
86
|
+
raise NonExistentIndex
|
87
|
+
when 400
|
88
|
+
raise InvalidArgument
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
def initialize(app)
|
94
|
+
super
|
95
|
+
@parser = nil
|
96
|
+
end
|
97
|
+
end
|
98
|
+
end
|
@@ -0,0 +1,10 @@
|
|
1
|
+
module IndexTank
|
2
|
+
class IndexAlreadyExists < StandardError; end
|
3
|
+
class NonExistentIndex < StandardError; end
|
4
|
+
class TooManyIndexes < StandardError; end
|
5
|
+
class MissingFunctionDefinition < StandardError; end
|
6
|
+
class InvalidApiKey < StandardError; end
|
7
|
+
class InvalidQuery < StandardError; end
|
8
|
+
class IndexInitializing < StandardError; end
|
9
|
+
class InvalidArgument < StandardError; end
|
10
|
+
end
|
@@ -0,0 +1,59 @@
|
|
1
|
+
require 'json'
|
2
|
+
|
3
|
+
module IndexTank
|
4
|
+
class Function
|
5
|
+
attr_reader :uri, :index, :definition
|
6
|
+
|
7
|
+
def initialize(function_url, index, definition)
|
8
|
+
@uri = "#{function_url}/#{index}"
|
9
|
+
@index = index
|
10
|
+
@definition = definition
|
11
|
+
builder = Proc.new { |builder| builder.use ResponseDocument }
|
12
|
+
@conn = IndexTank.setup_connection(@uri, &builder)
|
13
|
+
end
|
14
|
+
|
15
|
+
def add(options = {})
|
16
|
+
raise MissingFunctionDefinition unless self.definition
|
17
|
+
|
18
|
+
options.merge!(:definition => self.definition)
|
19
|
+
response = @conn.put do |req|
|
20
|
+
req.url ''
|
21
|
+
req.body = options.to_json
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
def delete(options = {})
|
26
|
+
resp = @conn.delete do |req|
|
27
|
+
req.url ''
|
28
|
+
req.body = options.to_json
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
def ==(other)
|
33
|
+
self.uri == other.uri and
|
34
|
+
self.index == other.index
|
35
|
+
self.definition == other.definition
|
36
|
+
end
|
37
|
+
end
|
38
|
+
class ResponseDocument < Faraday::Response::Middleware
|
39
|
+
def self.register_on_complete(env)
|
40
|
+
env[:response].on_complete do |finished_env|
|
41
|
+
case finished_env[:status]
|
42
|
+
when 401
|
43
|
+
raise InvalidApiKey
|
44
|
+
when 409
|
45
|
+
raise IndexInitializing
|
46
|
+
when 404
|
47
|
+
raise NonExistentIndex
|
48
|
+
when 400
|
49
|
+
raise InvalidArgument
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
def initialize(app)
|
55
|
+
super
|
56
|
+
@parser = nil
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
@@ -0,0 +1,127 @@
|
|
1
|
+
require 'indextank/exceptions'
|
2
|
+
require 'indextank/document'
|
3
|
+
require 'indextank/function'
|
4
|
+
require 'json'
|
5
|
+
|
6
|
+
module IndexTank
|
7
|
+
class Index
|
8
|
+
def initialize(index_url, metadata = nil)
|
9
|
+
@uri = index_url
|
10
|
+
@conn = IndexTank.setup_connection(index_url)
|
11
|
+
@metadata = metadata
|
12
|
+
end
|
13
|
+
|
14
|
+
def add
|
15
|
+
response = @conn.put('')
|
16
|
+
case response.status
|
17
|
+
when 201
|
18
|
+
true
|
19
|
+
when 204
|
20
|
+
raise IndexAlreadyExists
|
21
|
+
when 409
|
22
|
+
raise TooManyIndexes
|
23
|
+
when 401
|
24
|
+
raise InvalidApiKey
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
def refresh
|
29
|
+
response = @conn.get('')
|
30
|
+
if response.status == 200
|
31
|
+
@metadata = response.body
|
32
|
+
end
|
33
|
+
|
34
|
+
response
|
35
|
+
end
|
36
|
+
|
37
|
+
def delete
|
38
|
+
response = @conn.delete('')
|
39
|
+
case response.status
|
40
|
+
when 204
|
41
|
+
raise NonExistentIndex
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
def running?
|
46
|
+
refresh
|
47
|
+
@metadata['started']
|
48
|
+
end
|
49
|
+
|
50
|
+
def method_missing(sym, *args, &block)
|
51
|
+
refresh if @metadata.nil?
|
52
|
+
@metadata[sym.to_s]
|
53
|
+
end
|
54
|
+
|
55
|
+
def exists?
|
56
|
+
refresh.status != 404
|
57
|
+
end
|
58
|
+
|
59
|
+
# the options argument may contain an :index_code definition to override
|
60
|
+
# this instance's default index_code
|
61
|
+
# it can also contain any of the following:
|
62
|
+
# :start => an int with the number of results to skip
|
63
|
+
# :len => an int with the number of results to return
|
64
|
+
# :snippet => a comma separated list of field names for which a snippet
|
65
|
+
# should be returned. (requires an index that supports snippets)
|
66
|
+
# :fetch => a comma separated list of field names for which its content
|
67
|
+
# should be returned. (requires an index that supports storage)
|
68
|
+
# :function => an int with the index of the scoring function to be used
|
69
|
+
# for this query
|
70
|
+
# :variables => a hash int => float, with variables that can be later
|
71
|
+
# used in scoring :function
|
72
|
+
def search(query, options = {})
|
73
|
+
options = {:start => 0, :len => 10 }.merge(options).merge(:q => query)
|
74
|
+
if options[:variables]
|
75
|
+
options[:variables].each_pair { |k, v| options.merge!( :"var#{k}" => v ) }
|
76
|
+
end
|
77
|
+
|
78
|
+
if options[:category_filters]
|
79
|
+
options[:category_filters] = options[:category_filters].to_json
|
80
|
+
p options[:category_filters]
|
81
|
+
end
|
82
|
+
|
83
|
+
response = @conn.get do |req|
|
84
|
+
req.url 'search', options
|
85
|
+
end
|
86
|
+
case response.status
|
87
|
+
when 400
|
88
|
+
raise InvalidQuery
|
89
|
+
when 409
|
90
|
+
raise IndexInitializing
|
91
|
+
end
|
92
|
+
|
93
|
+
response.body
|
94
|
+
end
|
95
|
+
|
96
|
+
def suggest(query, options = {})
|
97
|
+
options.merge!({:query => query})
|
98
|
+
@conn.get do |req|
|
99
|
+
req.url 'autocomplete', options
|
100
|
+
end.body
|
101
|
+
end
|
102
|
+
|
103
|
+
# the options argument may contain an :index_code definition to override
|
104
|
+
# this instance's default index_code
|
105
|
+
def promote(docid, query, options={})
|
106
|
+
options.merge!( :docid => docid, :query => query )
|
107
|
+
resp = @conn.put do |req|
|
108
|
+
req.url 'promote'
|
109
|
+
req.body = options.to_json
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
113
|
+
def document(docid)
|
114
|
+
Document.new("#{@uri}/docs", docid)
|
115
|
+
end
|
116
|
+
|
117
|
+
def functions(index = -1, formula = nil)
|
118
|
+
if index == -1
|
119
|
+
@conn.get("functions").body.sort.collect do |index, formula|
|
120
|
+
Function.new("#{@uri}/functions", index, formula)
|
121
|
+
end
|
122
|
+
else
|
123
|
+
Function.new("#{@uri}/functions", index, formula)
|
124
|
+
end
|
125
|
+
end
|
126
|
+
end
|
127
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
require File.expand_path('../../../spec_helper', __FILE__)
|
2
|
+
|
3
|
+
describe IndexTank::Client do
|
4
|
+
let(:stubs) { Faraday::Adapter::Test::Stubs.new }
|
5
|
+
let(:client) { IndexTank::Client.new("http://:xxxx@dstqe.api.indextank.com") }
|
6
|
+
|
7
|
+
before { stub_setup_connection }
|
8
|
+
|
9
|
+
describe "indexes" do
|
10
|
+
context "with a param" do
|
11
|
+
subject { client.indexes('crawled-index') }
|
12
|
+
|
13
|
+
it "should return a single index object" do
|
14
|
+
should be_an_instance_of(IndexTank::Index)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
context "without a param" do
|
19
|
+
subject { client.indexes }
|
20
|
+
|
21
|
+
before do
|
22
|
+
stubs.get('/v1/indexes') { [200, {}, '{"crawled-index": {"started": true, "code": "dk4se", "creation_time": "2010-07-23T18:52:28", "size": 987}}'] }
|
23
|
+
end
|
24
|
+
|
25
|
+
it "should return a hash of indexes" do
|
26
|
+
indexes = subject
|
27
|
+
|
28
|
+
indexes.should be_an_instance_of(Hash)
|
29
|
+
indexes['crawled-index'].should be_an_instance_of(IndexTank::Index)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,127 @@
|
|
1
|
+
require File.expand_path('../../../spec_helper', __FILE__)
|
2
|
+
|
3
|
+
describe IndexTank::Document do
|
4
|
+
let(:stubs) { Faraday::Adapter::Test::Stubs.new }
|
5
|
+
let(:document) { IndexTank::Client.new("http://:xxxx@dstqe.api.indextank.com").indexes('new-index').document('document1') }
|
6
|
+
let(:path_prefix) { '/v1/indexes/new-index/docs/' }
|
7
|
+
|
8
|
+
before { stub_setup_connection }
|
9
|
+
|
10
|
+
describe "document management" do
|
11
|
+
describe "#add" do
|
12
|
+
subject { document.add(:text => 'some text') }
|
13
|
+
|
14
|
+
context "document was indexed" do
|
15
|
+
before do
|
16
|
+
stubs.put(path_prefix) { [200, {}, ''] }
|
17
|
+
end
|
18
|
+
|
19
|
+
it { subject.should be_true }
|
20
|
+
end
|
21
|
+
|
22
|
+
context "index was initializing" do
|
23
|
+
before do
|
24
|
+
stubs.put(path_prefix) { [409, {}, ''] }
|
25
|
+
end
|
26
|
+
|
27
|
+
it { subject.should be_false }
|
28
|
+
end
|
29
|
+
|
30
|
+
context "invalid or missing argument" do
|
31
|
+
before do
|
32
|
+
stubs.put(path_prefix) { [400, {}, ''] }
|
33
|
+
end
|
34
|
+
|
35
|
+
it { subject.should be_false }
|
36
|
+
end
|
37
|
+
|
38
|
+
context "no index existed for the given name" do
|
39
|
+
before do
|
40
|
+
stubs.put(path_prefix) { [404, {}, ''] }
|
41
|
+
end
|
42
|
+
|
43
|
+
it { subject.should be_false }
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
describe "#delete" do
|
48
|
+
subject { document.delete }
|
49
|
+
|
50
|
+
context "document was deleted" do
|
51
|
+
before do
|
52
|
+
stubs.delete(path_prefix) { [200, {}, ''] }
|
53
|
+
end
|
54
|
+
|
55
|
+
it { should be_true }
|
56
|
+
end
|
57
|
+
|
58
|
+
context "index is initializing" do
|
59
|
+
before do
|
60
|
+
stubs.delete(path_prefix) { [409, {}, ''] }
|
61
|
+
end
|
62
|
+
|
63
|
+
it { subject.should be_false }
|
64
|
+
end
|
65
|
+
|
66
|
+
context "invalid or missing argument" do
|
67
|
+
before do
|
68
|
+
stubs.delete(path_prefix) { [400, {}, ''] }
|
69
|
+
end
|
70
|
+
|
71
|
+
it { subject.should be_false }
|
72
|
+
end
|
73
|
+
|
74
|
+
context "no index existed for the given name" do
|
75
|
+
before do
|
76
|
+
stubs.delete(path_prefix) { [404, {}, ''] }
|
77
|
+
end
|
78
|
+
|
79
|
+
it { subject.should be_false }
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
describe "#update_variables" do
|
85
|
+
let(:new_variables) do
|
86
|
+
{
|
87
|
+
0 => 'new_rating',
|
88
|
+
1 => 'new_reputation',
|
89
|
+
2 => 'new_visits'
|
90
|
+
}
|
91
|
+
end
|
92
|
+
|
93
|
+
subject { document.update_variables(new_variables) }
|
94
|
+
|
95
|
+
context "variables indexed" do
|
96
|
+
before do
|
97
|
+
stubs.put("#{path_prefix}variables") { [200, {}, ''] }
|
98
|
+
end
|
99
|
+
|
100
|
+
it { should be_true }
|
101
|
+
end
|
102
|
+
|
103
|
+
context "index is initializing" do
|
104
|
+
before do
|
105
|
+
stubs.put("#{path_prefix}variables") { [409, {}, ''] }
|
106
|
+
end
|
107
|
+
|
108
|
+
it { subject.should be_false }
|
109
|
+
end
|
110
|
+
|
111
|
+
context "invalid or missing argument" do
|
112
|
+
before do
|
113
|
+
stubs.put("#{path_prefix}variables") { [400, {}, ''] }
|
114
|
+
end
|
115
|
+
|
116
|
+
it { subject.should be_false }
|
117
|
+
end
|
118
|
+
|
119
|
+
context "no index existed for the given name" do
|
120
|
+
before do
|
121
|
+
stubs.put("#{path_prefix}variables") { [404, {}, ''] }
|
122
|
+
end
|
123
|
+
|
124
|
+
it { subject.should be_false }
|
125
|
+
end
|
126
|
+
end
|
127
|
+
end
|
@@ -0,0 +1,90 @@
|
|
1
|
+
require File.expand_path('../../../spec_helper', __FILE__)
|
2
|
+
|
3
|
+
describe IndexTank::Function do
|
4
|
+
let(:stubs) { Faraday::Adapter::Test::Stubs.new }
|
5
|
+
let(:function) { IndexTank::Client.new("http://:xxxx@dstqe.api.indextank.com").indexes('new-index').functions(0, '-age') }
|
6
|
+
let(:path_prefix) { '/v1/indexes/new-index/functions/0/' }
|
7
|
+
|
8
|
+
before { stub_setup_connection }
|
9
|
+
|
10
|
+
describe "function management" do
|
11
|
+
describe "#add" do
|
12
|
+
subject { function.add }
|
13
|
+
|
14
|
+
context "no definition specified" do
|
15
|
+
let(:function) { IndexTank::Client.new("http://:xxxx@dstqe.api.indextank.com").indexes('new-index').functions(0) }
|
16
|
+
it "should raise an exception" do
|
17
|
+
expect { subject }.to raise_error(IndexTank::MissingFunctionDefinition)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
context "function saved" do
|
22
|
+
before do
|
23
|
+
stubs.put(path_prefix) { [200, {}, ''] }
|
24
|
+
end
|
25
|
+
|
26
|
+
it { should be_true }
|
27
|
+
end
|
28
|
+
|
29
|
+
context "index is initializing" do
|
30
|
+
before do
|
31
|
+
stubs.put(path_prefix) { [409, {}, ''] }
|
32
|
+
end
|
33
|
+
|
34
|
+
it { subject.should be_false }
|
35
|
+
end
|
36
|
+
|
37
|
+
context "invalid or missing argument" do
|
38
|
+
before do
|
39
|
+
stubs.put(path_prefix) { [400, {}, ''] }
|
40
|
+
end
|
41
|
+
|
42
|
+
it { subject.should be_false }
|
43
|
+
end
|
44
|
+
|
45
|
+
context "no index existed for the given name" do
|
46
|
+
before do
|
47
|
+
stubs.put(path_prefix) { [404, {}, ''] }
|
48
|
+
end
|
49
|
+
|
50
|
+
it { subject.should be_false }
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
describe "#delete" do
|
55
|
+
subject { function.delete }
|
56
|
+
|
57
|
+
context "function deleted" do
|
58
|
+
before do
|
59
|
+
stubs.delete(path_prefix) { [200, {}, ''] }
|
60
|
+
end
|
61
|
+
|
62
|
+
it { should be_true }
|
63
|
+
end
|
64
|
+
|
65
|
+
context "index is initializing" do
|
66
|
+
before do
|
67
|
+
stubs.delete(path_prefix) { [409, {}, ''] }
|
68
|
+
end
|
69
|
+
|
70
|
+
it { subject.should be_false }
|
71
|
+
end
|
72
|
+
|
73
|
+
context "invalid or missing argument" do
|
74
|
+
before do
|
75
|
+
stubs.delete(path_prefix) { [400, {}, ''] }
|
76
|
+
end
|
77
|
+
|
78
|
+
it { subject.should be_false }
|
79
|
+
end
|
80
|
+
|
81
|
+
context "no index existed for the given name" do
|
82
|
+
before do
|
83
|
+
stubs.delete(path_prefix) { [404, {}, ''] }
|
84
|
+
end
|
85
|
+
|
86
|
+
it { subject.should be_false }
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
90
|
+
end
|
@@ -0,0 +1,266 @@
|
|
1
|
+
require File.expand_path('../../../spec_helper', __FILE__)
|
2
|
+
|
3
|
+
describe IndexTank::Index do
|
4
|
+
let(:stubs) { Faraday::Adapter::Test::Stubs.new }
|
5
|
+
let(:index) { IndexTank::Client.new("http://:xxxx@dstqe.api.indextank.com").indexes('new-index') }
|
6
|
+
let(:path_prefix) { '/v1/indexes/new-index/' }
|
7
|
+
|
8
|
+
before { stub_setup_connection }
|
9
|
+
|
10
|
+
describe "index management" do
|
11
|
+
describe "add an index" do
|
12
|
+
subject { index.add }
|
13
|
+
# after do
|
14
|
+
# @index.delete
|
15
|
+
# end
|
16
|
+
|
17
|
+
context "the index does not exist" do
|
18
|
+
before do
|
19
|
+
stubs.put(path_prefix) { [201, {}, '{"started": false, "code": "dsyaj", "creation_time": "2010-08-14T13:01:48.454624", "size": 0}'] }
|
20
|
+
end
|
21
|
+
|
22
|
+
it { should be_true }
|
23
|
+
end
|
24
|
+
|
25
|
+
context "when an index already exists" do
|
26
|
+
before do
|
27
|
+
# @index.add
|
28
|
+
stubs.put(path_prefix) { [204, {}, ''] }
|
29
|
+
end
|
30
|
+
|
31
|
+
it "should raise an exception" do
|
32
|
+
expect { subject }.to raise_error(IndexTank::IndexAlreadyExists)
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
context "when the user has too many indexes" do
|
37
|
+
before do
|
38
|
+
stubs.put(path_prefix) { [409, {}, ''] }
|
39
|
+
end
|
40
|
+
|
41
|
+
it "should raise an exception" do
|
42
|
+
expect { subject }.to raise_error(IndexTank::TooManyIndexes)
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
describe "delete an index" do
|
48
|
+
subject { index.delete }
|
49
|
+
|
50
|
+
context "the index exists" do
|
51
|
+
before do
|
52
|
+
# @index.add
|
53
|
+
stubs.delete(path_prefix) { [200, {}, ''] }
|
54
|
+
end
|
55
|
+
|
56
|
+
it { should be_true }
|
57
|
+
end
|
58
|
+
|
59
|
+
context "the index does not exist" do
|
60
|
+
before do
|
61
|
+
stubs.delete(path_prefix) { [204, {}, ''] }
|
62
|
+
end
|
63
|
+
|
64
|
+
it { subject.should be_false }
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
context "when examining the metadata" do
|
70
|
+
subject { index }
|
71
|
+
|
72
|
+
shared_examples_for "metadata" do
|
73
|
+
it "should return the code" do
|
74
|
+
subject.code.should == 'dsyaj'
|
75
|
+
end
|
76
|
+
|
77
|
+
it "should update and return the running" do
|
78
|
+
# delete any preceding stubs if they exist.
|
79
|
+
stubs.match(:get, path_prefix, nil)
|
80
|
+
stubs.get(path_prefix) { [200, {}, '{"started": true, "code": "dsyaj", "creation_time": "2010-08-14T13:01:48.454624", "size": 0}'] }
|
81
|
+
subject.running?.should be_true
|
82
|
+
end
|
83
|
+
|
84
|
+
it "should return the size" do
|
85
|
+
subject.size.should == 0
|
86
|
+
end
|
87
|
+
|
88
|
+
it "should return the creation_time" do
|
89
|
+
subject.creation_time.should == "2010-08-14T13:01:48.454624"
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
context "pass in metadata" do
|
94
|
+
let(:metadata) do
|
95
|
+
{
|
96
|
+
'code' => "dsyaj",
|
97
|
+
'started' => false,
|
98
|
+
'size' => 0,
|
99
|
+
'creation_time' => '2010-08-14T13:01:48.454624'
|
100
|
+
}
|
101
|
+
end
|
102
|
+
let(:index) { IndexTank::Index.new("http://api.indextank.com#{path_prefix}", metadata) }
|
103
|
+
|
104
|
+
it_should_behave_like "metadata"
|
105
|
+
end
|
106
|
+
|
107
|
+
context "metadata is not passed in" do
|
108
|
+
let(:index) { IndexTank::Client.new("http://:uiTPmHg2JTjSMD@dstqe.api.indextank.com").indexes('new-index') }
|
109
|
+
|
110
|
+
before do
|
111
|
+
stubs.get(path_prefix) { [200, {}, '{"started": false, "code": "dsyaj", "creation_time": "2010-08-14T13:01:48.454624", "size": 0}'] }
|
112
|
+
end
|
113
|
+
|
114
|
+
it_should_behave_like "metadata"
|
115
|
+
end
|
116
|
+
end
|
117
|
+
|
118
|
+
describe "#exists?" do
|
119
|
+
subject { index.exists? }
|
120
|
+
|
121
|
+
context "when an index exists" do
|
122
|
+
before do
|
123
|
+
stubs.get(path_prefix) { [200, {}, '{"started": false, "code": "dsyaj", "creation_time": "2010-08-14T13:01:48.454624", "size": 0}'] }
|
124
|
+
end
|
125
|
+
|
126
|
+
it { should be_true }
|
127
|
+
end
|
128
|
+
|
129
|
+
context "when an index doesn't exist" do
|
130
|
+
before do
|
131
|
+
stubs.get(path_prefix) { [404, {}, ''] }
|
132
|
+
end
|
133
|
+
|
134
|
+
# rspec2 bug, implicit subject is calling subject twice
|
135
|
+
it { subject.should be_false }
|
136
|
+
end
|
137
|
+
end
|
138
|
+
|
139
|
+
describe "#search" do
|
140
|
+
subject { index.search('foo') }
|
141
|
+
|
142
|
+
context "search is successful" do
|
143
|
+
before do
|
144
|
+
stubs.get("#{path_prefix}search?q=foo&start=0&len=10") { [200, {}, '{"matches": 4, "search_time": "0.022", "results": [{"docid": "http://cnn.com/HEALTH"}, {"docid": "http://www.cnn.com/HEALTH/"}, {"docid": "http://cnn.com/HEALTH/?hpt=Sbin"}, {"docid": "http://cnn.com/HEALTH/"}]}'] }
|
145
|
+
end
|
146
|
+
|
147
|
+
it "should have the number of matches" do
|
148
|
+
subject['matches'].should == 4
|
149
|
+
end
|
150
|
+
|
151
|
+
it "should a list of docs" do
|
152
|
+
results = subject['results']
|
153
|
+
|
154
|
+
%w(http://cnn.com/HEALTH
|
155
|
+
http://www.cnn.com/HEALTH/
|
156
|
+
http://cnn.com/HEALTH/?hpt=Sbin
|
157
|
+
http://cnn.com/HEALTH/).each_with_index do |docid, index|
|
158
|
+
results[index]['docid'].should == docid
|
159
|
+
end
|
160
|
+
end
|
161
|
+
end
|
162
|
+
|
163
|
+
context "index is initializing", :pending => true do
|
164
|
+
before do
|
165
|
+
stubs.get("#{path_prefix}search") { [409, {}, ''] }
|
166
|
+
end
|
167
|
+
|
168
|
+
it "should return an empty body"
|
169
|
+
end
|
170
|
+
|
171
|
+
context "index is invalid/missing argument", :pending => true do
|
172
|
+
before do
|
173
|
+
stubs.get("#{path_prefix}search") { [400, {}, ''] }
|
174
|
+
end
|
175
|
+
|
176
|
+
it "should return a descriptive error message"
|
177
|
+
end
|
178
|
+
|
179
|
+
context "no index existed for the given name", :pending => true do
|
180
|
+
before do
|
181
|
+
stubs.get("#{path_prefix}search") { [404, {}, ''] }
|
182
|
+
end
|
183
|
+
|
184
|
+
it "should return a descriptive error message"
|
185
|
+
end
|
186
|
+
end
|
187
|
+
|
188
|
+
describe "#promote" do
|
189
|
+
subject { index.promote(4, 'foo') }
|
190
|
+
|
191
|
+
context "when the document is promoted" do
|
192
|
+
before do
|
193
|
+
stubs.get("#{path_prefix}promote?docid=4&query=foo") { [200, {}, ''] }
|
194
|
+
end
|
195
|
+
|
196
|
+
it { should be_true }
|
197
|
+
end
|
198
|
+
|
199
|
+
context "when the index is initializing" do
|
200
|
+
before do
|
201
|
+
stubs.get("#{path_prefix}promote?docid=4&query=foo") { [409, {}, ''] }
|
202
|
+
end
|
203
|
+
|
204
|
+
it { subject.should be_false }
|
205
|
+
end
|
206
|
+
|
207
|
+
context "when invalid or missing argument" do
|
208
|
+
before do
|
209
|
+
stubs.get("#{path_prefix}promote?docid=4&query=foo") { [400, {}, ''] }
|
210
|
+
end
|
211
|
+
|
212
|
+
it { subject.should be_false }
|
213
|
+
end
|
214
|
+
|
215
|
+
context "when no index exists for the given name" do
|
216
|
+
before do
|
217
|
+
stubs.get("#{path_prefix}promote?docid=4&query=foo") { [404, {}, ''] }
|
218
|
+
end
|
219
|
+
|
220
|
+
it { subject.should be_false }
|
221
|
+
end
|
222
|
+
end
|
223
|
+
|
224
|
+
describe "#document" do
|
225
|
+
subject { index.document('foo') }
|
226
|
+
|
227
|
+
it "should create a document object" do
|
228
|
+
should be_an_instance_of(IndexTank::Document)
|
229
|
+
end
|
230
|
+
end
|
231
|
+
|
232
|
+
describe "#function" do
|
233
|
+
context "with no params" do
|
234
|
+
subject { index.functions }
|
235
|
+
|
236
|
+
before do
|
237
|
+
stubs.get("#{path_prefix}functions") { [200, {}, '{"0": "0-A", "1": "-age", "2": "relevance"}'] }
|
238
|
+
end
|
239
|
+
|
240
|
+
it "should return an array of functions" do
|
241
|
+
should == [
|
242
|
+
IndexTank::Function.new("#{path_prefix}functions", 0, '0-A'),
|
243
|
+
IndexTank::Function.new("#{path_prefix}functions", 1, '-age'),
|
244
|
+
IndexTank::Function.new("#{path_prefix}functions", 2, 'relevance')
|
245
|
+
]
|
246
|
+
end
|
247
|
+
end
|
248
|
+
|
249
|
+
context "with a function name and definition" do
|
250
|
+
subject { index.functions(0, '-age') }
|
251
|
+
|
252
|
+
it "should return an instance of Function" do
|
253
|
+
should be_an_instance_of(IndexTank::Function)
|
254
|
+
end
|
255
|
+
end
|
256
|
+
|
257
|
+
context "with a function name" do
|
258
|
+
subject { index.functions(0) }
|
259
|
+
|
260
|
+
it "should return an instance of Function" do
|
261
|
+
should be_an_instance_of(IndexTank::Function)
|
262
|
+
end
|
263
|
+
end
|
264
|
+
end
|
265
|
+
end
|
266
|
+
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,28 @@
|
|
1
|
+
$:.unshift File.expand_path("../../lib", __FILE__)
|
2
|
+
|
3
|
+
require 'rspec/core'
|
4
|
+
require 'rspec/expectations'
|
5
|
+
require 'rr'
|
6
|
+
|
7
|
+
require 'indextank'
|
8
|
+
|
9
|
+
def not_in_editor?
|
10
|
+
!(ENV.has_key?('TM_MODE') || ENV.has_key?('EMACS') || ENV.has_key?('VIM'))
|
11
|
+
end
|
12
|
+
|
13
|
+
RSpec.configure do |c|
|
14
|
+
c.run_all_when_everything_filtered = true
|
15
|
+
c.filter_run :focused => true
|
16
|
+
c.alias_example_to :fit, :focused => true
|
17
|
+
c.color_enabled = not_in_editor?
|
18
|
+
c.mock_with :rr
|
19
|
+
end
|
20
|
+
|
21
|
+
def stub_setup_connection
|
22
|
+
stub(IndexTank).setup_connection(anything) do |url|
|
23
|
+
Faraday::Connection.new(:url => url) do |builder|
|
24
|
+
builder.adapter :test, stubs
|
25
|
+
builder.use Faraday::Response::Yajl
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
metadata
ADDED
@@ -0,0 +1,223 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: indextank
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
hash: 23
|
5
|
+
prerelease: false
|
6
|
+
segments:
|
7
|
+
- 0
|
8
|
+
- 0
|
9
|
+
- 4
|
10
|
+
version: 0.0.4
|
11
|
+
platform: ruby
|
12
|
+
authors:
|
13
|
+
- Santiago Perez
|
14
|
+
- Terence Lee
|
15
|
+
autorequire:
|
16
|
+
bindir: bin
|
17
|
+
cert_chain: []
|
18
|
+
|
19
|
+
date: 2010-08-24 00:00:00 -03:00
|
20
|
+
default_executable:
|
21
|
+
dependencies:
|
22
|
+
- !ruby/object:Gem::Dependency
|
23
|
+
name: jeweler
|
24
|
+
prerelease: false
|
25
|
+
requirement: &id001 !ruby/object:Gem::Requirement
|
26
|
+
none: false
|
27
|
+
requirements:
|
28
|
+
- - "="
|
29
|
+
- !ruby/object:Gem::Version
|
30
|
+
hash: 7
|
31
|
+
segments:
|
32
|
+
- 1
|
33
|
+
- 4
|
34
|
+
- 0
|
35
|
+
version: 1.4.0
|
36
|
+
type: :development
|
37
|
+
version_requirements: *id001
|
38
|
+
- !ruby/object:Gem::Dependency
|
39
|
+
name: rspec-core
|
40
|
+
prerelease: false
|
41
|
+
requirement: &id002 !ruby/object:Gem::Requirement
|
42
|
+
none: false
|
43
|
+
requirements:
|
44
|
+
- - ">="
|
45
|
+
- !ruby/object:Gem::Version
|
46
|
+
hash: 62196421
|
47
|
+
segments:
|
48
|
+
- 2
|
49
|
+
- 0
|
50
|
+
- 0
|
51
|
+
- beta
|
52
|
+
- 19
|
53
|
+
version: 2.0.0.beta.19
|
54
|
+
type: :development
|
55
|
+
version_requirements: *id002
|
56
|
+
- !ruby/object:Gem::Dependency
|
57
|
+
name: rspec-expectations
|
58
|
+
prerelease: false
|
59
|
+
requirement: &id003 !ruby/object:Gem::Requirement
|
60
|
+
none: false
|
61
|
+
requirements:
|
62
|
+
- - ">="
|
63
|
+
- !ruby/object:Gem::Version
|
64
|
+
hash: 62196421
|
65
|
+
segments:
|
66
|
+
- 2
|
67
|
+
- 0
|
68
|
+
- 0
|
69
|
+
- beta
|
70
|
+
- 19
|
71
|
+
version: 2.0.0.beta.19
|
72
|
+
type: :development
|
73
|
+
version_requirements: *id003
|
74
|
+
- !ruby/object:Gem::Dependency
|
75
|
+
name: rr
|
76
|
+
prerelease: false
|
77
|
+
requirement: &id004 !ruby/object:Gem::Requirement
|
78
|
+
none: false
|
79
|
+
requirements:
|
80
|
+
- - "="
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
hash: 33
|
83
|
+
segments:
|
84
|
+
- 0
|
85
|
+
- 10
|
86
|
+
- 11
|
87
|
+
version: 0.10.11
|
88
|
+
type: :development
|
89
|
+
version_requirements: *id004
|
90
|
+
- !ruby/object:Gem::Dependency
|
91
|
+
name: rake
|
92
|
+
prerelease: false
|
93
|
+
requirement: &id005 !ruby/object:Gem::Requirement
|
94
|
+
none: false
|
95
|
+
requirements:
|
96
|
+
- - "="
|
97
|
+
- !ruby/object:Gem::Version
|
98
|
+
hash: 49
|
99
|
+
segments:
|
100
|
+
- 0
|
101
|
+
- 8
|
102
|
+
- 7
|
103
|
+
version: 0.8.7
|
104
|
+
type: :development
|
105
|
+
version_requirements: *id005
|
106
|
+
- !ruby/object:Gem::Dependency
|
107
|
+
name: ruby-debug
|
108
|
+
prerelease: false
|
109
|
+
requirement: &id006 !ruby/object:Gem::Requirement
|
110
|
+
none: false
|
111
|
+
requirements:
|
112
|
+
- - ">="
|
113
|
+
- !ruby/object:Gem::Version
|
114
|
+
hash: 3
|
115
|
+
segments:
|
116
|
+
- 0
|
117
|
+
version: "0"
|
118
|
+
type: :development
|
119
|
+
version_requirements: *id006
|
120
|
+
- !ruby/object:Gem::Dependency
|
121
|
+
name: parka
|
122
|
+
prerelease: false
|
123
|
+
requirement: &id007 !ruby/object:Gem::Requirement
|
124
|
+
none: false
|
125
|
+
requirements:
|
126
|
+
- - "="
|
127
|
+
- !ruby/object:Gem::Version
|
128
|
+
hash: 17
|
129
|
+
segments:
|
130
|
+
- 0
|
131
|
+
- 3
|
132
|
+
- 1
|
133
|
+
version: 0.3.1
|
134
|
+
type: :development
|
135
|
+
version_requirements: *id007
|
136
|
+
- !ruby/object:Gem::Dependency
|
137
|
+
name: faraday
|
138
|
+
prerelease: false
|
139
|
+
requirement: &id008 !ruby/object:Gem::Requirement
|
140
|
+
none: false
|
141
|
+
requirements:
|
142
|
+
- - ">="
|
143
|
+
- !ruby/object:Gem::Version
|
144
|
+
hash: 3
|
145
|
+
segments:
|
146
|
+
- 0
|
147
|
+
version: "0"
|
148
|
+
type: :runtime
|
149
|
+
version_requirements: *id008
|
150
|
+
- !ruby/object:Gem::Dependency
|
151
|
+
name: yajl-ruby
|
152
|
+
prerelease: false
|
153
|
+
requirement: &id009 !ruby/object:Gem::Requirement
|
154
|
+
none: false
|
155
|
+
requirements:
|
156
|
+
- - "="
|
157
|
+
- !ruby/object:Gem::Version
|
158
|
+
hash: 13
|
159
|
+
segments:
|
160
|
+
- 0
|
161
|
+
- 7
|
162
|
+
- 7
|
163
|
+
version: 0.7.7
|
164
|
+
type: :runtime
|
165
|
+
version_requirements: *id009
|
166
|
+
description: Ruby Client for the IndexTank API
|
167
|
+
email:
|
168
|
+
- santip@santip.com.ar
|
169
|
+
- hone02@gmail.com
|
170
|
+
executables: []
|
171
|
+
|
172
|
+
extensions: []
|
173
|
+
|
174
|
+
extra_rdoc_files: []
|
175
|
+
|
176
|
+
files:
|
177
|
+
- lib/indextank.rb
|
178
|
+
- lib/indextank/client.rb
|
179
|
+
- lib/indextank/document.rb
|
180
|
+
- lib/indextank/exceptions.rb
|
181
|
+
- lib/indextank/function.rb
|
182
|
+
- lib/indextank/index.rb
|
183
|
+
- spec/lib/indextank/client_spec.rb
|
184
|
+
- spec/lib/indextank/document_spec.rb
|
185
|
+
- spec/lib/indextank/function_spec.rb
|
186
|
+
- spec/lib/indextank/index_spec.rb
|
187
|
+
- spec/spec_helper.rb
|
188
|
+
has_rdoc: true
|
189
|
+
homepage: http://www.indextank.com
|
190
|
+
licenses: []
|
191
|
+
|
192
|
+
post_install_message:
|
193
|
+
rdoc_options: []
|
194
|
+
|
195
|
+
require_paths:
|
196
|
+
- lib
|
197
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
198
|
+
none: false
|
199
|
+
requirements:
|
200
|
+
- - ">="
|
201
|
+
- !ruby/object:Gem::Version
|
202
|
+
hash: 3
|
203
|
+
segments:
|
204
|
+
- 0
|
205
|
+
version: "0"
|
206
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
207
|
+
none: false
|
208
|
+
requirements:
|
209
|
+
- - ">="
|
210
|
+
- !ruby/object:Gem::Version
|
211
|
+
hash: 3
|
212
|
+
segments:
|
213
|
+
- 0
|
214
|
+
version: "0"
|
215
|
+
requirements: []
|
216
|
+
|
217
|
+
rubyforge_project: nowarning
|
218
|
+
rubygems_version: 1.3.7
|
219
|
+
signing_key:
|
220
|
+
specification_version: 3
|
221
|
+
summary: Ruby Client for the IndexTank API
|
222
|
+
test_files: []
|
223
|
+
|