csdn-tire 0.5
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 +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
|