csdn-tire 0.5
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +15 -0
- data/.travis.yml +13 -0
- data/Gemfile +4 -0
- data/MIT-LICENSE +20 -0
- data/README.markdown +291 -0
- data/Rakefile +78 -0
- data/examples/tire.rb +81 -0
- data/lib/tire.rb +26 -0
- data/lib/tire/configuration.rb +31 -0
- data/lib/tire/count.rb +24 -0
- data/lib/tire/dsl.rb +26 -0
- data/lib/tire/http/client.rb +63 -0
- data/lib/tire/http/clients/curb.rb +62 -0
- data/lib/tire/http/response.rb +28 -0
- data/lib/tire/index.rb +98 -0
- data/lib/tire/logger.rb +61 -0
- data/lib/tire/results/pagination.rb +55 -0
- data/lib/tire/rubyext/ruby_1_8.rb +54 -0
- data/lib/tire/rubyext/to_json.rb +22 -0
- data/lib/tire/search.rb +80 -0
- data/lib/tire/state.rb +27 -0
- data/lib/tire/utils.rb +40 -0
- data/lib/tire/version.rb +4 -0
- data/test/integration/count_test.rb +20 -0
- data/test/integration/index_test.rb +58 -0
- data/test/integration/search_test.rb +23 -0
- data/test/integration/state_test.rb +22 -0
- data/test/test_helper.rb +104 -0
- data/test/unit/configuration_test.rb +74 -0
- data/test/unit/http_client_test.rb +77 -0
- data/test/unit/http_response_test.rb +50 -0
- data/test/unit/logger_test.rb +126 -0
- data/tire.gemspec +49 -0
- metadata +275 -0
data/lib/tire.rb
ADDED
@@ -0,0 +1,26 @@
|
|
1
|
+
# -*- encoding : utf-8 -*-
|
2
|
+
require 'rest_client'
|
3
|
+
require 'multi_json'
|
4
|
+
require 'hashr'
|
5
|
+
require 'cgi'
|
6
|
+
|
7
|
+
require 'active_support/core_ext'
|
8
|
+
|
9
|
+
# Ruby 1.8 compatibility
|
10
|
+
require 'tire/rubyext/ruby_1_8' if defined?(RUBY_VERSION) && RUBY_VERSION < '1.9'
|
11
|
+
require 'tire/rubyext/to_json'
|
12
|
+
require 'tire/utils'
|
13
|
+
require 'tire/logger'
|
14
|
+
require 'tire/configuration'
|
15
|
+
require 'tire/http/response'
|
16
|
+
require 'tire/http/client'
|
17
|
+
require 'tire/search'
|
18
|
+
require 'tire/count'
|
19
|
+
require 'tire/state'
|
20
|
+
require 'tire/results/pagination'
|
21
|
+
require 'tire/index'
|
22
|
+
require 'tire/dsl'
|
23
|
+
|
24
|
+
module Tire
|
25
|
+
extend DSL
|
26
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
# -*- encoding : utf-8 -*-
|
2
|
+
module Tire
|
3
|
+
|
4
|
+
class Configuration
|
5
|
+
|
6
|
+
def self.nodes_count(n=2)
|
7
|
+
@nodes_count ||= n
|
8
|
+
end
|
9
|
+
|
10
|
+
def self.url(value=nil)
|
11
|
+
@url = (value ? value.to_s.gsub(%r|/*$|, '') : nil) || @url || ENV['ELASTICSEARCH_URL'] || "http://localhost:9200"
|
12
|
+
end
|
13
|
+
|
14
|
+
def self.client(klass=nil)
|
15
|
+
@client = klass || @client || HTTP::Client::RestClient
|
16
|
+
end
|
17
|
+
|
18
|
+
def self.logger(device=nil, options={})
|
19
|
+
return @logger = Logger.new(device, options) if device
|
20
|
+
@logger || nil
|
21
|
+
end
|
22
|
+
|
23
|
+
def self.reset(*properties)
|
24
|
+
reset_variables = properties.empty? ? instance_variables : instance_variables.map { |p| p.to_s} & \
|
25
|
+
properties.map { |p| "@#{p}" }
|
26
|
+
reset_variables.each { |v| instance_variable_set(v.to_sym, nil) }
|
27
|
+
end
|
28
|
+
|
29
|
+
end
|
30
|
+
|
31
|
+
end
|
data/lib/tire/count.rb
ADDED
@@ -0,0 +1,24 @@
|
|
1
|
+
# -*- encoding : utf-8 -*-
|
2
|
+
module Tire
|
3
|
+
class SearchRequestFailed < StandardError; end
|
4
|
+
|
5
|
+
class Count < Search
|
6
|
+
|
7
|
+
def initialize(indices, types, payload)
|
8
|
+
@indices = Array(indices)
|
9
|
+
@types = Array(types).map { |type| Utils.escape(type) }
|
10
|
+
if payload.is_a?(String)
|
11
|
+
@payload = payload
|
12
|
+
else
|
13
|
+
@payload_hash = payload
|
14
|
+
end
|
15
|
+
|
16
|
+
@path = ['/', @indices.join(','), @types.join(','), '_count'].compact.join('/').squeeze('/')
|
17
|
+
end
|
18
|
+
|
19
|
+
def results
|
20
|
+
perform
|
21
|
+
return @json["totalHits"]
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
data/lib/tire/dsl.rb
ADDED
@@ -0,0 +1,26 @@
|
|
1
|
+
# -*- encoding : utf-8 -*-
|
2
|
+
module Tire
|
3
|
+
module DSL
|
4
|
+
|
5
|
+
def configure(&block)
|
6
|
+
Configuration.class_eval(&block)
|
7
|
+
end
|
8
|
+
|
9
|
+
def index(name)
|
10
|
+
Index.new(name)
|
11
|
+
end
|
12
|
+
|
13
|
+
def search(names, types, payload)
|
14
|
+
Search.new(names, types, payload)
|
15
|
+
end
|
16
|
+
|
17
|
+
def count(names, types, payload)
|
18
|
+
Count.new(names, types, payload)
|
19
|
+
end
|
20
|
+
|
21
|
+
def state
|
22
|
+
State.new
|
23
|
+
end
|
24
|
+
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,63 @@
|
|
1
|
+
# -*- encoding : utf-8 -*-
|
2
|
+
module Tire
|
3
|
+
|
4
|
+
module HTTP
|
5
|
+
|
6
|
+
module Client
|
7
|
+
|
8
|
+
class RestClient
|
9
|
+
ConnectionExceptions = [::RestClient::ServerBrokeConnection, ::RestClient::RequestTimeout]
|
10
|
+
|
11
|
+
def self.get(url, data=nil)
|
12
|
+
perform ::RestClient::Request.new(:method => :get, :url => url, :payload => data).execute
|
13
|
+
rescue *ConnectionExceptions
|
14
|
+
raise
|
15
|
+
rescue ::RestClient::Exception => e
|
16
|
+
Response.new e.http_body, e.http_code
|
17
|
+
end
|
18
|
+
|
19
|
+
def self.post(url, data)
|
20
|
+
perform ::RestClient.post(url, data)
|
21
|
+
rescue *ConnectionExceptions
|
22
|
+
raise
|
23
|
+
rescue ::RestClient::Exception => e
|
24
|
+
Response.new e.http_body, e.http_code
|
25
|
+
end
|
26
|
+
|
27
|
+
def self.put(url, data)
|
28
|
+
perform ::RestClient.put(url, data)
|
29
|
+
rescue *ConnectionExceptions
|
30
|
+
raise
|
31
|
+
rescue ::RestClient::Exception => e
|
32
|
+
Response.new e.http_body, e.http_code
|
33
|
+
end
|
34
|
+
|
35
|
+
def self.delete(url)
|
36
|
+
perform ::RestClient.delete(url)
|
37
|
+
rescue *ConnectionExceptions
|
38
|
+
raise
|
39
|
+
rescue ::RestClient::Exception => e
|
40
|
+
Response.new e.http_body, e.http_code
|
41
|
+
end
|
42
|
+
|
43
|
+
def self.head(url)
|
44
|
+
perform ::RestClient.head(url)
|
45
|
+
rescue *ConnectionExceptions
|
46
|
+
raise
|
47
|
+
rescue ::RestClient::Exception => e
|
48
|
+
Response.new e.http_body, e.http_code
|
49
|
+
end
|
50
|
+
|
51
|
+
private
|
52
|
+
|
53
|
+
def self.perform(response)
|
54
|
+
Response.new response.body, response.code, response.headers
|
55
|
+
end
|
56
|
+
|
57
|
+
end
|
58
|
+
|
59
|
+
end
|
60
|
+
|
61
|
+
end
|
62
|
+
|
63
|
+
end
|
@@ -0,0 +1,62 @@
|
|
1
|
+
# -*- encoding : utf-8 -*-
|
2
|
+
require 'curb'
|
3
|
+
|
4
|
+
module Tire
|
5
|
+
|
6
|
+
module HTTP
|
7
|
+
|
8
|
+
module Client
|
9
|
+
|
10
|
+
class Curb
|
11
|
+
@client = ::Curl::Easy.new
|
12
|
+
@client.resolve_mode = :ipv4
|
13
|
+
|
14
|
+
# @client.verbose = true
|
15
|
+
|
16
|
+
def self.get(url, data=nil)
|
17
|
+
@client.url = url
|
18
|
+
|
19
|
+
# FIXME: Curb cannot post bodies with GET requests?
|
20
|
+
# Roy Fielding seems to approve:
|
21
|
+
# <http://tech.groups.yahoo.com/group/rest-discuss/message/9962>
|
22
|
+
if data
|
23
|
+
@client.post_body = data
|
24
|
+
@client.http_post
|
25
|
+
else
|
26
|
+
@client.http_get
|
27
|
+
end
|
28
|
+
Response.new @client.body_str, @client.response_code
|
29
|
+
end
|
30
|
+
|
31
|
+
def self.post(url, data)
|
32
|
+
@client.url = url
|
33
|
+
@client.post_body = data
|
34
|
+
@client.http_post
|
35
|
+
Response.new @client.body_str, @client.response_code
|
36
|
+
end
|
37
|
+
|
38
|
+
def self.put(url, data)
|
39
|
+
@client.url = url
|
40
|
+
@client.http_put data
|
41
|
+
Response.new @client.body_str, @client.response_code
|
42
|
+
end
|
43
|
+
|
44
|
+
def self.delete(url)
|
45
|
+
@client.url = url
|
46
|
+
@client.http_delete
|
47
|
+
Response.new @client.body_str, @client.response_code
|
48
|
+
end
|
49
|
+
|
50
|
+
def self.head(url)
|
51
|
+
@client.url = url
|
52
|
+
@client.http_head
|
53
|
+
Response.new @client.body_str, @client.response_code
|
54
|
+
end
|
55
|
+
|
56
|
+
end
|
57
|
+
|
58
|
+
end
|
59
|
+
|
60
|
+
end
|
61
|
+
|
62
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
# -*- encoding : utf-8 -*-
|
2
|
+
module Tire
|
3
|
+
|
4
|
+
module HTTP
|
5
|
+
|
6
|
+
class Response
|
7
|
+
attr_reader :body, :code, :headers
|
8
|
+
|
9
|
+
def initialize(body, code, headers={})
|
10
|
+
@body, @code, @headers = body, code.to_i, headers
|
11
|
+
end
|
12
|
+
|
13
|
+
def success?
|
14
|
+
code > 0 && code < 400
|
15
|
+
end
|
16
|
+
|
17
|
+
def failure?
|
18
|
+
! success?
|
19
|
+
end
|
20
|
+
|
21
|
+
def to_s
|
22
|
+
[code, body].join(' : ')
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
end
|
27
|
+
|
28
|
+
end
|
data/lib/tire/index.rb
ADDED
@@ -0,0 +1,98 @@
|
|
1
|
+
# -*- encoding : utf-8 -*-
|
2
|
+
module Tire
|
3
|
+
class Index
|
4
|
+
include Utils
|
5
|
+
|
6
|
+
attr_reader :name, :response
|
7
|
+
|
8
|
+
def initialize(name)
|
9
|
+
@name = name
|
10
|
+
end
|
11
|
+
|
12
|
+
def url
|
13
|
+
"#{Configuration.url}/#{@name}"
|
14
|
+
end
|
15
|
+
|
16
|
+
def regist_shard(total = 6)
|
17
|
+
notes_count = Configuration.nodes_count
|
18
|
+
regist_json = MultiJson.encode(build_regist_options(total, notes_count))
|
19
|
+
@response = Configuration.client.put("#{url}/_shard", regist_json)
|
20
|
+
@response.success?
|
21
|
+
|
22
|
+
ensure
|
23
|
+
curl = %Q|curl -X PUT "#{url}/_shard" -d '#{regist_json}'|
|
24
|
+
logged('_regist_shard', curl)
|
25
|
+
end
|
26
|
+
|
27
|
+
def shard_info
|
28
|
+
@response = Configuration.client.get("#{url}/_shard")
|
29
|
+
if @response.failure?
|
30
|
+
STDERR.puts "[REQUEST FAILED] \n"
|
31
|
+
raise @response.to_s
|
32
|
+
end
|
33
|
+
|
34
|
+
MultiJson.decode(@response.body)
|
35
|
+
ensure
|
36
|
+
curl = %Q|curl -X GET #{url}/_shard|
|
37
|
+
logged('_regist_shard_info', curl)
|
38
|
+
end
|
39
|
+
|
40
|
+
def delete
|
41
|
+
@response = Configuration.client.delete url
|
42
|
+
@response.success?
|
43
|
+
|
44
|
+
ensure
|
45
|
+
curl = %Q|curl -X DELETE #{url}|
|
46
|
+
logged('DELETE', curl)
|
47
|
+
end
|
48
|
+
|
49
|
+
def mapping(type = "csdn")
|
50
|
+
@response = Configuration.client.get("#{url}/#{type}/_mapping")
|
51
|
+
MultiJson.decode(@response.body)
|
52
|
+
end
|
53
|
+
|
54
|
+
def create_mapping(type, options)
|
55
|
+
@options = options
|
56
|
+
@response = Configuration.client.put("#{url}/#{type}/_mapping", MultiJson.encode(options))
|
57
|
+
@response.success?
|
58
|
+
|
59
|
+
ensure
|
60
|
+
curl = %Q|curl -X PUT #{url}/#{type}/_mapping -d '#{MultiJson.encode(options)}'|
|
61
|
+
logged('CREATE MAPPING', curl)
|
62
|
+
end
|
63
|
+
|
64
|
+
def bulk(type, document)
|
65
|
+
@response = Configuration.client.put("#{url}/#{type}/_bulk", MultiJson.encode(document))
|
66
|
+
@response.success?
|
67
|
+
|
68
|
+
ensure
|
69
|
+
curl = %Q|curl -X PUT #{url}/#{type}/_pulk -d '#{MultiJson.encode(document)}'|
|
70
|
+
logged('BULK', curl)
|
71
|
+
end
|
72
|
+
|
73
|
+
def flush
|
74
|
+
@response = Configuration.client.put("#{url}/_flush", "")
|
75
|
+
@response.success?
|
76
|
+
|
77
|
+
ensure
|
78
|
+
curl = %Q|curl -X PUT #{url}/_flush|
|
79
|
+
logged('FLUSH', curl)
|
80
|
+
end
|
81
|
+
|
82
|
+
def refresh
|
83
|
+
@response = Configuration.client.put("#{url}/_refresh", "")
|
84
|
+
@response.success?
|
85
|
+
|
86
|
+
ensure
|
87
|
+
curl = %Q|curl -X PUT #{url}/_refresh|
|
88
|
+
logged('REFRESH', curl)
|
89
|
+
end
|
90
|
+
|
91
|
+
private
|
92
|
+
|
93
|
+
def build_regist_options(total, notes_count)
|
94
|
+
Hash[(0..total - 1).group_by{|x| x.modulo notes_count}.map{|key, value| ["cs#{key + 1}", value]}]
|
95
|
+
end
|
96
|
+
|
97
|
+
end
|
98
|
+
end
|
data/lib/tire/logger.rb
ADDED
@@ -0,0 +1,61 @@
|
|
1
|
+
# -*- encoding : utf-8 -*-
|
2
|
+
module Tire
|
3
|
+
class Logger
|
4
|
+
|
5
|
+
def initialize(device, options={})
|
6
|
+
@device = if device.respond_to?(:write)
|
7
|
+
device
|
8
|
+
else
|
9
|
+
File.open(device, 'a')
|
10
|
+
end
|
11
|
+
@device.sync = true if @device.respond_to?(:sync)
|
12
|
+
@options = options
|
13
|
+
at_exit { @device.close unless @device.closed? } if @device.respond_to?(:closed?) && @device.respond_to?(:close)
|
14
|
+
end
|
15
|
+
|
16
|
+
def level
|
17
|
+
@options[:level] || 'info'
|
18
|
+
end
|
19
|
+
|
20
|
+
def write(message)
|
21
|
+
@device.write message
|
22
|
+
end
|
23
|
+
|
24
|
+
def log_request(endpoint, params=nil, curl='')
|
25
|
+
# 2001-02-12 18:20:42:32 [_search] (articles,users)
|
26
|
+
#
|
27
|
+
# curl -X POST ....
|
28
|
+
#
|
29
|
+
content = "# #{time}"
|
30
|
+
content += " [#{endpoint}]"
|
31
|
+
content += " (#{params.inspect})" if params
|
32
|
+
content += "\n#\n"
|
33
|
+
content += curl
|
34
|
+
content += "\n\n"
|
35
|
+
write content
|
36
|
+
end
|
37
|
+
|
38
|
+
def log_response(status, took=nil, json='')
|
39
|
+
# 2001-02-12 18:20:42:32 [200] (4 msec)
|
40
|
+
#
|
41
|
+
# {
|
42
|
+
# "took" : 4,
|
43
|
+
# "hits" : [...]
|
44
|
+
# ...
|
45
|
+
# }
|
46
|
+
#
|
47
|
+
content = "# #{time}"
|
48
|
+
content += " [#{status}]"
|
49
|
+
content += " (#{took} msec)" if took
|
50
|
+
content += "\n#\n" unless json.to_s !~ /\S/
|
51
|
+
json.to_s.each_line { |line| content += "# #{line}" } unless json.to_s !~ /\S/
|
52
|
+
content += "\n\n"
|
53
|
+
write content
|
54
|
+
end
|
55
|
+
|
56
|
+
def time
|
57
|
+
Time.now.strftime('%Y-%m-%d %H:%M:%S:%L')
|
58
|
+
end
|
59
|
+
|
60
|
+
end
|
61
|
+
end
|
@@ -0,0 +1,55 @@
|
|
1
|
+
# -*- encoding : utf-8 -*-
|
2
|
+
module Tire
|
3
|
+
module Results
|
4
|
+
|
5
|
+
# Adds support for WillPaginate and Kaminari
|
6
|
+
#
|
7
|
+
module Pagination
|
8
|
+
|
9
|
+
def total_entries
|
10
|
+
@total
|
11
|
+
end
|
12
|
+
|
13
|
+
def per_page
|
14
|
+
(@options[:per_page] || @options[:size] || 10 ).to_i
|
15
|
+
end
|
16
|
+
|
17
|
+
def total_pages
|
18
|
+
( @total.to_f / per_page ).ceil
|
19
|
+
end
|
20
|
+
|
21
|
+
def current_page
|
22
|
+
if @options[:page]
|
23
|
+
@options[:page].to_i
|
24
|
+
else
|
25
|
+
(per_page + @options[:from].to_i) / per_page
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
def previous_page
|
30
|
+
current_page > 1 ? (current_page - 1) : nil
|
31
|
+
end
|
32
|
+
|
33
|
+
def next_page
|
34
|
+
current_page < total_pages ? (current_page + 1) : nil
|
35
|
+
end
|
36
|
+
|
37
|
+
def offset
|
38
|
+
per_page * (current_page - 1)
|
39
|
+
end
|
40
|
+
|
41
|
+
def out_of_bounds?
|
42
|
+
current_page > total_pages
|
43
|
+
end
|
44
|
+
|
45
|
+
# Kaminari support
|
46
|
+
#
|
47
|
+
alias :limit_value :per_page
|
48
|
+
alias :total_count :total_entries
|
49
|
+
alias :num_pages :total_pages
|
50
|
+
alias :offset_value :offset
|
51
|
+
|
52
|
+
end
|
53
|
+
|
54
|
+
end
|
55
|
+
end
|