indextank 0.0.4
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
+
|