columbo 0.1.5 → 0.2.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/README.md +24 -11
- data/lib/columbo.rb +3 -3
- data/lib/columbo/api_client.rb +49 -0
- data/lib/columbo/capture.rb +70 -15
- data/lib/columbo/compressor.rb +51 -0
- data/lib/columbo/inspector.rb +54 -65
- data/lib/columbo/version.rb +1 -1
- metadata +23 -77
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 7e4dc0fa4f95aac130e637f09794b94404bf22e5
|
4
|
+
data.tar.gz: 64c82608f49ff6b51a6a60a054392d25c8ae93e4
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 997eae7d6bf3e6e6595168508f3cc9d435b59b6f70bff1a47fc1c84f8aaec557ee3769bea9cda05486f94193a659c39204fb16962c2e759afc49fc40dc2633b9
|
7
|
+
data.tar.gz: b62b4e55bbae5cc25aaed09673e9ad5bec5ab5f7b38c35549d97cd6d5bde64825441349fb8abf61bac5b83e3ea2d4522a5fed62c26cf377e860edf2d71948b13
|
data/README.md
CHANGED
@@ -5,6 +5,12 @@ that captures users browsing sessions for Rack applications.
|
|
5
5
|
|
6
6
|
Tribute to [Inspector Columbo](http://www.imdb.com/title/tt1466074/)
|
7
7
|
|
8
|
+
## Disclaimer
|
9
|
+
|
10
|
+
This is an alpha release, it is tested with Sinatra and Rails 3 only.
|
11
|
+
The UI to explore sessions is in progress (ETA: 2013'Q4) therefore
|
12
|
+
there is no point using this gem at the moment.
|
13
|
+
|
8
14
|
## Using with Rack application
|
9
15
|
|
10
16
|
*Columbo* can be used with any Rack application,
|
@@ -16,8 +22,8 @@ simply require and use as follows:
|
|
16
22
|
require 'columbo/capture'
|
17
23
|
use Columbo::Capture, {
|
18
24
|
capture: true,
|
19
|
-
bench:
|
20
|
-
|
25
|
+
bench: true,
|
26
|
+
api_key: 'your-private-api-key',
|
21
27
|
logger: 'log/columbo.log'
|
22
28
|
}
|
23
29
|
run app
|
@@ -25,28 +31,35 @@ simply require and use as follows:
|
|
25
31
|
## Using with Rails 3
|
26
32
|
|
27
33
|
In order to use, include the following in a Rails application
|
28
|
-
|
34
|
+
**Gemfile** file:
|
29
35
|
|
30
36
|
gem 'columbo'
|
31
37
|
|
32
|
-
|
38
|
+
**config/application.rb** file:
|
33
39
|
|
34
40
|
require 'columbo/capture'
|
35
41
|
config.middleware.insert 0, Columbo::Capture, {
|
36
42
|
capture: Rails.env.production?,
|
37
|
-
|
38
|
-
|
39
|
-
|
43
|
+
api_key: 'your-private-api-key',
|
44
|
+
logger: 'log/columbo.log',
|
45
|
+
filter_uri: ['admin']
|
40
46
|
}
|
41
47
|
|
42
48
|
Check the Rack configuration:
|
43
49
|
|
44
50
|
rake middleware
|
45
51
|
|
46
|
-
##
|
52
|
+
## Options
|
47
53
|
|
48
|
-
|
49
|
-
|
54
|
+
- `api_key`: the api key for authentication (mandatory),
|
55
|
+
- `capture`: set to true to collect data, default `false`
|
56
|
+
- `bench`: set to true to benchmark middleware overhead, default `false`
|
57
|
+
- `logger`: file to write logs to, default `env['rack.errors']`
|
58
|
+
- `capture_crawlers`: set to true to capture hits from crawlers, default `false`
|
59
|
+
- `api_uri`: overwrite api endpoint uri
|
60
|
+
- `crawlers`: overwrite crawlers user agent regex
|
61
|
+
- `filter_params`: array of parameters to filter, for `Rails` default to `Rails.configuration.filter_parameters`
|
62
|
+
- `filter_uri`: array of uri for which **not** to capture data
|
50
63
|
|
51
64
|
## Author
|
52
65
|
|
@@ -54,5 +67,5 @@ Jerome Touffe-Blin, [@jtblin](https://twitter.com/jtlbin), [http://www.linkedin.
|
|
54
67
|
|
55
68
|
## License
|
56
69
|
|
57
|
-
Columbo is copyright
|
70
|
+
Columbo is copyright 2013 Jerome Touffe-Blin and contributors. It is licensed under the BSD license. See the include LICENSE file for details.
|
58
71
|
|
data/lib/columbo.rb
CHANGED
@@ -1,11 +1,11 @@
|
|
1
1
|
require 'columbo/version'
|
2
|
-
require 'columbo/
|
2
|
+
require 'columbo/api_client'
|
3
|
+
require 'columbo/capture'
|
3
4
|
require 'columbo/inspector'
|
4
5
|
require 'columbo/log_writer'
|
6
|
+
require 'columbo/compressor'
|
5
7
|
|
6
8
|
module Columbo
|
7
|
-
MONGO_COLLECTION = "hits".freeze
|
8
|
-
|
9
9
|
def self.logger
|
10
10
|
@logger
|
11
11
|
end
|
@@ -0,0 +1,49 @@
|
|
1
|
+
require 'net/http'
|
2
|
+
require 'json'
|
3
|
+
|
4
|
+
module Columbo
|
5
|
+
class APIClient
|
6
|
+
|
7
|
+
API_URI = "http://localhost:15080".freeze
|
8
|
+
|
9
|
+
def initialize(api_key, api_uri)
|
10
|
+
@api_key = api_key
|
11
|
+
@api_uri = api_uri || API_URI
|
12
|
+
end
|
13
|
+
|
14
|
+
def handshake
|
15
|
+
Thread.new do
|
16
|
+
headers = { "Content-Type" => "application/json; charset=utf-8" }
|
17
|
+
|
18
|
+
uri = URI(@api_uri + '/' + @api_key + '/handshake')
|
19
|
+
Net::HTTP.new(uri.host, uri.port).start do |http|
|
20
|
+
response = http.get(uri.request_uri, headers)
|
21
|
+
json = JSON.parse(response.body)
|
22
|
+
json.each { |k, v| yield k, v }
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
def post_data(hash)
|
28
|
+
headers = {
|
29
|
+
"Accept-Encoding" => "gzip, deflate",
|
30
|
+
"Content-Encoding" => "deflate",
|
31
|
+
"Content-Type" => "application/json; charset=utf-8"
|
32
|
+
}
|
33
|
+
|
34
|
+
zlib = Columbo::Compressor
|
35
|
+
json = hash.merge({api_key: @api_key}).to_json
|
36
|
+
payload = zlib.deflate(json)
|
37
|
+
uri = URI(@api_uri + '/' + @api_key + '/capture')
|
38
|
+
|
39
|
+
start = Time.now
|
40
|
+
Net::HTTP.new(uri.host, uri.port).start do |http|
|
41
|
+
response = http.post(uri.request_uri, payload, headers)
|
42
|
+
stop = Time.now
|
43
|
+
duration = ((stop-start) * 1000).round(3)
|
44
|
+
zlib.unzip(response.body, response['Content-Encoding']) + ' - Time: ' + duration.to_s + 'ms'
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
end
|
49
|
+
end
|
data/lib/columbo/capture.rb
CHANGED
@@ -1,27 +1,40 @@
|
|
1
1
|
require 'columbo'
|
2
2
|
require 'rack/utils'
|
3
3
|
require 'rack/logger'
|
4
|
+
require 'securerandom'
|
4
5
|
|
5
6
|
module Columbo
|
6
7
|
class Capture
|
7
8
|
include Rack::Utils
|
8
9
|
|
10
|
+
attr_reader :client, :filter_params, :filter_uri
|
11
|
+
|
9
12
|
FORMAT = %{[Columbo #{Columbo::VERSION}] [%s] %s - %s "%s%s %s\n}
|
10
13
|
|
11
14
|
def initialize(app, opts={})
|
12
15
|
@app = app
|
13
16
|
# Options
|
14
|
-
@capture = opts[:capture]
|
17
|
+
@capture = !!opts[:capture]
|
15
18
|
@bench = opts[:capture] && opts[:bench]
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
+
capture_crawlers = opts[:capture_crawlers]
|
20
|
+
crawlers = opts[:crawlers] || "(Baidu|Gigabot|Googlebot|libwww-perl|lwp-trivial|msnbot|SiteUptime|Slurp|WordPress|ZIBB|ZyBorg|bot|crawler|spider|robot|crawling|facebook|w3c|coccoc|Daumoa|panopta)"
|
21
|
+
api_key = opts[:api_key]
|
22
|
+
api_uri = opts[:api_uri]
|
23
|
+
@filter_params = opts[:filter_params] || []
|
24
|
+
@filter_uri = opts[:filter_uri] || []
|
25
|
+
|
26
|
+
@cookie_name = 'clickstream.io'
|
27
|
+
filter_params.concat(Rails.configuration.filter_parameters || []) if defined?(Rails)
|
19
28
|
|
20
29
|
Columbo.logger = opts[:logger] if opts[:logger]
|
21
30
|
|
22
|
-
raise ArgumentError, '
|
31
|
+
raise ArgumentError, 'API key missing.' if api_key.nil?
|
23
32
|
|
24
|
-
@inspector = Columbo::Inspector.new
|
33
|
+
@inspector = Columbo::Inspector.new api_key, api_uri, crawlers, capture_crawlers, filter_params
|
34
|
+
@cookie_regex = Regexp.new "#{@cookie_name}="
|
35
|
+
|
36
|
+
@client = {}
|
37
|
+
Columbo::APIClient.new(api_key, api_uri).handshake { |k, v| @client[k] = v}
|
25
38
|
end
|
26
39
|
|
27
40
|
def call(env)
|
@@ -35,30 +48,36 @@ module Columbo
|
|
35
48
|
|
36
49
|
start = Time.now if @bench
|
37
50
|
|
38
|
-
headers = HeaderHash.new(headers)
|
51
|
+
headers = HeaderHash.new(headers)
|
39
52
|
|
40
|
-
if !STATUS_WITH_NO_ENTITY_BODY.include?(status) &&
|
41
|
-
|
42
|
-
|
43
|
-
|
53
|
+
if @capture && !STATUS_WITH_NO_ENTITY_BODY.include?(status) && !headers['transfer-encoding'] && headers['content-type'] && (
|
54
|
+
headers['content-type'].include?('text/html') || headers['content-type'].include?('application/json') ||
|
55
|
+
headers['content-type'].include?('application/xml') || headers['content-type'].include?('text/javascript') ||
|
56
|
+
headers['content-type'].include?('text/plain')
|
57
|
+
) && !filtered_uri?(env['REQUEST_URI'])
|
44
58
|
|
45
|
-
|
59
|
+
cookie = session_cookie(env, headers)
|
60
|
+
pid = SecureRandom.uuid
|
61
|
+
body = response.clone
|
46
62
|
|
63
|
+
Thread.abort_on_exception = false
|
47
64
|
Thread.new do
|
48
65
|
begin
|
49
|
-
@inspector.investigate env, status, headers,
|
66
|
+
result = @inspector.investigate env, status, headers, body, start_processing, stop_processing, cookie, pid
|
67
|
+
log env, result
|
50
68
|
rescue Exception => e
|
51
69
|
log_error env, e
|
52
70
|
end
|
53
|
-
end
|
71
|
+
end
|
54
72
|
|
73
|
+
response = insert_js(response, headers, cookie, pid) if headers['content-type'].include?('text/html') #&& headers['content-length'].to_i > 0
|
55
74
|
end
|
56
75
|
|
57
76
|
if @bench
|
58
77
|
stop = Time.now
|
59
78
|
duration = ((stop-start) * 1000).round(3)
|
60
|
-
log(env, "Time: #{duration}ms")
|
61
79
|
headers['Columbo'] = "version #{Columbo::VERSION}, time #{duration}ms"
|
80
|
+
Thread.new { log(env, "Time: #{duration}ms") }
|
62
81
|
end
|
63
82
|
|
64
83
|
[status, headers, response]
|
@@ -66,6 +85,42 @@ module Columbo
|
|
66
85
|
|
67
86
|
private
|
68
87
|
|
88
|
+
def filtered_uri?(uri)
|
89
|
+
filter_uri.select {|filter| uri.match filter}.size > 0
|
90
|
+
end
|
91
|
+
|
92
|
+
def session_cookie(env, headers)
|
93
|
+
cookie = extract_cookie(env['HTTP_COOKIE'])
|
94
|
+
set_cookie(headers, cookie)
|
95
|
+
end
|
96
|
+
|
97
|
+
def extract_cookie(string)
|
98
|
+
return unless string
|
99
|
+
match = string.match(/columbo=([^;]*)/)
|
100
|
+
match[1] if match && match.length > 1
|
101
|
+
end
|
102
|
+
|
103
|
+
def set_cookie(headers, cookie)
|
104
|
+
expires = Time.now+60*60
|
105
|
+
cookie = cookie.nil? ? {value: SecureRandom.uuid} : {value: cookie}
|
106
|
+
cookie[:expires] = expires
|
107
|
+
Rack::Utils.set_cookie_header!(headers, @cookie_name, cookie)
|
108
|
+
cookie[:value]
|
109
|
+
end
|
110
|
+
|
111
|
+
def insert_js(body, headers, sid, pid)
|
112
|
+
html = ''
|
113
|
+
body.each { |part| html += part }
|
114
|
+
body.close if body.respond_to?(:close)
|
115
|
+
str_filter_params = filter_params.map { |filter| filter.to_s }
|
116
|
+
if html.size > 0
|
117
|
+
script = "<script>(function(){var uri='#{client['ws']}', cid='#{client['clientId']}', sid='#{sid}', pid='#{pid}', paramsFilter = #{str_filter_params}; #{client['js']}})();</script>"
|
118
|
+
html += "\n" + script
|
119
|
+
end
|
120
|
+
headers['content-length'] = html.size.to_s
|
121
|
+
[html]
|
122
|
+
end
|
123
|
+
|
69
124
|
def log(env, message)
|
70
125
|
now = Time.now
|
71
126
|
|
@@ -0,0 +1,51 @@
|
|
1
|
+
require 'stringio'
|
2
|
+
require 'zlib'
|
3
|
+
|
4
|
+
module Columbo
|
5
|
+
class Compressor
|
6
|
+
|
7
|
+
def self.encoding_handled?(content_encoding)
|
8
|
+
%w(gzip deflate).include? content_encoding
|
9
|
+
end
|
10
|
+
|
11
|
+
def self.unzip(source, content_encoding)
|
12
|
+
case content_encoding
|
13
|
+
when 'gzip' then decompress(source)
|
14
|
+
when 'deflate' then inflate(source)
|
15
|
+
else source
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
def self.zip(source, accept_encoding)
|
20
|
+
if accept_encoding.match 'deflate'
|
21
|
+
deflate(source)
|
22
|
+
elsif accept_encoding.match 'gzip'
|
23
|
+
compress(source)
|
24
|
+
else
|
25
|
+
source
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
# Compresses a string using gzip inspired by ActiveSupport::Gzip
|
30
|
+
def self.compress(source)
|
31
|
+
output = StringIO.new
|
32
|
+
gz = Zlib::GzipWriter.new(output)
|
33
|
+
gz.write(source)
|
34
|
+
gz.close
|
35
|
+
output.string
|
36
|
+
end
|
37
|
+
|
38
|
+
def self.deflate(source)
|
39
|
+
Zlib::Deflate.deflate(source)
|
40
|
+
end
|
41
|
+
|
42
|
+
def self.decompress(source)
|
43
|
+
Zlib::GzipReader.new(StringIO.new(source)).read
|
44
|
+
end
|
45
|
+
|
46
|
+
def self.inflate(source)
|
47
|
+
Zlib::Inflate.inflate(source.read)
|
48
|
+
end
|
49
|
+
|
50
|
+
end
|
51
|
+
end
|
data/lib/columbo/inspector.rb
CHANGED
@@ -1,88 +1,77 @@
|
|
1
1
|
require 'rack/request'
|
2
2
|
require 'rack/response'
|
3
|
-
require '
|
4
|
-
require 'html_mini'
|
3
|
+
require 'socket'
|
5
4
|
|
6
5
|
module Columbo
|
7
6
|
class Inspector
|
8
7
|
|
9
|
-
def initialize(
|
10
|
-
@
|
8
|
+
def initialize(api_key, api_uri, crawlers, capture_crawlers, filter_params)
|
9
|
+
@client = Columbo::APIClient.new(api_key, api_uri)
|
10
|
+
@crawlers, @capture_crawlers, @filter_params = crawlers, capture_crawlers, filter_params
|
11
|
+
@hostname = Socket.gethostname
|
12
|
+
@rg = Regexp.new(crawlers, Regexp::IGNORECASE)
|
11
13
|
end
|
12
14
|
|
13
|
-
def investigate(env, status, headers, body, start, stop,
|
14
|
-
# Lazy connection to MongoDB
|
15
|
-
client = Columbo::DbClient.new @mongo_uri
|
15
|
+
def investigate(env, status, headers, body, start, stop, cookie, pid)
|
16
16
|
# Normalise request from env
|
17
17
|
request = Rack::Request.new(env)
|
18
18
|
# Don't capture bots traffic by default
|
19
|
-
|
20
|
-
return if request.user_agent.match(rg) && !capture_crawlers
|
19
|
+
return unless @capture_crawlers || !request.user_agent.match(@rg)
|
21
20
|
html = ''
|
22
|
-
|
23
|
-
|
24
|
-
text, title = to_plain_text(html)
|
25
|
-
# Get request headers
|
21
|
+
# in case of gzipping has been done by the app
|
22
|
+
body.each { |part| html += Columbo::Compressor.unzip(part, headers['Content-Encoding']) }
|
26
23
|
request_headers = {}
|
27
|
-
request.env.each { |key, value| request_headers[key.sub(/^HTTP_/, '').downcase] = value if key.start_with? 'HTTP_'}
|
24
|
+
request.env.each { |key, value| request_headers[key.sub(/^HTTP_/, '').gsub(/_/, '-').downcase] = value if key.start_with? 'HTTP_'}
|
25
|
+
params = request.params.clone || {}
|
26
|
+
@filter_params.each {|param| params[param.to_s] = '[FILTERED]' if params[param.to_s]}
|
27
|
+
session_opts = request.session_options.clone || {}
|
28
|
+
session_opts.delete :secret
|
28
29
|
data = {
|
30
|
+
sid: cookie,
|
31
|
+
pid: pid,
|
32
|
+
hostname: @hostname,
|
33
|
+
filter_params: @filter_params,
|
29
34
|
request: {
|
30
|
-
params:
|
31
|
-
|
35
|
+
params: params,
|
36
|
+
ip: request.ip,
|
32
37
|
user_agent: request.user_agent,
|
33
|
-
|
38
|
+
referer: request.referer,
|
39
|
+
method: request.request_method,
|
40
|
+
path: request.path, # script_name + path_info
|
41
|
+
fullpath: request.fullpath, # "#{path}?#{query_string}"
|
42
|
+
script_name: request.script_name,
|
43
|
+
path_info: request.path_info,
|
34
44
|
uri: request.env['REQUEST_URI'],
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
45
|
+
querystring: request.query_string,
|
46
|
+
scheme: request.scheme,
|
47
|
+
host: request.host,
|
48
|
+
port: request.port,
|
49
|
+
url: request.url, # base_url + fullpath
|
50
|
+
base_url: request.base_url, # scheme + host [+ port]
|
51
|
+
content_type: request.content_type,
|
52
|
+
content_charset: request.content_charset,
|
53
|
+
media_type: request.media_type,
|
54
|
+
media_type_params: request.media_type_params,
|
55
|
+
protocol: request.env['HTTP_VERSION'],
|
56
|
+
session: request.session,
|
57
|
+
session_options: session_opts,
|
58
|
+
cookies: request.cookies,
|
44
59
|
path_parameters: request.env['action_dispatch.request.path_parameters'],
|
45
|
-
headers: request_headers
|
60
|
+
headers: request_headers,
|
61
|
+
xhr: request.xhr?
|
46
62
|
},
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
63
|
+
response: {
|
64
|
+
status: status,
|
65
|
+
headers: headers,
|
66
|
+
size: html.length,
|
67
|
+
body: html,
|
68
|
+
start: start,
|
69
|
+
stop: stop,
|
70
|
+
time: stop-start
|
71
|
+
}
|
56
72
|
}
|
57
|
-
#
|
58
|
-
client.
|
59
|
-
end
|
60
|
-
|
61
|
-
def to_plain_text(html)
|
62
|
-
html_doc = Nokogiri::HTML(html)
|
63
|
-
html_doc.xpath('//script').each {|node| node.remove}
|
64
|
-
html_doc.xpath('//style').each {|node| node.remove}
|
65
|
-
text = ''
|
66
|
-
html_doc.xpath('//body').each {|node| text += node.text.gsub(/\s{2,}/, ' ')}
|
67
|
-
title_tag = html_doc.xpath('//title').first
|
68
|
-
title = title_tag.nil? ? nil : title_tag.text
|
69
|
-
[text, title]
|
70
|
-
end
|
71
|
-
|
72
|
-
private
|
73
|
-
|
74
|
-
def sanitize(data)
|
75
|
-
Hash[
|
76
|
-
data.map do |key, value|
|
77
|
-
value = sanitize(value) if value.is_a? Hash
|
78
|
-
# replace $ and . in keys by Unicode full width equivalent
|
79
|
-
# http://docs.mongodb.org/manual/faq/developers/#faq-dollar-sign-escaping
|
80
|
-
key = key.gsub('.', 'U+FF0E').gsub('$', 'U+FF04') if key.is_a? String
|
81
|
-
# transform symbol into string to avoid auto transformation into $symbol
|
82
|
-
value = value.to_s if value.is_a? Symbol
|
83
|
-
[key, value]
|
84
|
-
end
|
85
|
-
]
|
73
|
+
# Send data to API
|
74
|
+
@client.post_data data
|
86
75
|
end
|
87
76
|
|
88
77
|
end
|
data/lib/columbo/version.rb
CHANGED
metadata
CHANGED
@@ -1,113 +1,58 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: columbo
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1
|
5
|
-
prerelease:
|
4
|
+
version: 0.2.1
|
6
5
|
platform: ruby
|
7
6
|
authors:
|
8
7
|
- Jerome Touffe-Blin
|
9
8
|
autorequire:
|
10
9
|
bindir: bin
|
11
10
|
cert_chain: []
|
12
|
-
date: 2013-
|
11
|
+
date: 2013-12-07 00:00:00.000000000 Z
|
13
12
|
dependencies:
|
14
13
|
- !ruby/object:Gem::Dependency
|
15
14
|
name: rack
|
16
15
|
requirement: !ruby/object:Gem::Requirement
|
17
|
-
none: false
|
18
16
|
requirements:
|
19
|
-
- -
|
17
|
+
- - '>='
|
20
18
|
- !ruby/object:Gem::Version
|
21
19
|
version: 1.4.0
|
22
20
|
type: :runtime
|
23
21
|
prerelease: false
|
24
22
|
version_requirements: !ruby/object:Gem::Requirement
|
25
|
-
none: false
|
26
23
|
requirements:
|
27
|
-
- -
|
24
|
+
- - '>='
|
28
25
|
- !ruby/object:Gem::Version
|
29
26
|
version: 1.4.0
|
30
27
|
- !ruby/object:Gem::Dependency
|
31
|
-
name:
|
32
|
-
requirement: !ruby/object:Gem::Requirement
|
33
|
-
none: false
|
34
|
-
requirements:
|
35
|
-
- - ! '>='
|
36
|
-
- !ruby/object:Gem::Version
|
37
|
-
version: 1.5.9
|
38
|
-
type: :runtime
|
39
|
-
prerelease: false
|
40
|
-
version_requirements: !ruby/object:Gem::Requirement
|
41
|
-
none: false
|
42
|
-
requirements:
|
43
|
-
- - ! '>='
|
44
|
-
- !ruby/object:Gem::Version
|
45
|
-
version: 1.5.9
|
46
|
-
- !ruby/object:Gem::Dependency
|
47
|
-
name: html_mini
|
48
|
-
requirement: !ruby/object:Gem::Requirement
|
49
|
-
none: false
|
50
|
-
requirements:
|
51
|
-
- - ! '>='
|
52
|
-
- !ruby/object:Gem::Version
|
53
|
-
version: 0.0.2
|
54
|
-
type: :runtime
|
55
|
-
prerelease: false
|
56
|
-
version_requirements: !ruby/object:Gem::Requirement
|
57
|
-
none: false
|
58
|
-
requirements:
|
59
|
-
- - ! '>='
|
60
|
-
- !ruby/object:Gem::Version
|
61
|
-
version: 0.0.2
|
62
|
-
- !ruby/object:Gem::Dependency
|
63
|
-
name: mongo
|
64
|
-
requirement: !ruby/object:Gem::Requirement
|
65
|
-
none: false
|
66
|
-
requirements:
|
67
|
-
- - ! '>='
|
68
|
-
- !ruby/object:Gem::Version
|
69
|
-
version: 1.6.1
|
70
|
-
type: :runtime
|
71
|
-
prerelease: false
|
72
|
-
version_requirements: !ruby/object:Gem::Requirement
|
73
|
-
none: false
|
74
|
-
requirements:
|
75
|
-
- - ! '>='
|
76
|
-
- !ruby/object:Gem::Version
|
77
|
-
version: 1.6.1
|
78
|
-
- !ruby/object:Gem::Dependency
|
79
|
-
name: bson_ext
|
28
|
+
name: sinatra
|
80
29
|
requirement: !ruby/object:Gem::Requirement
|
81
|
-
none: false
|
82
30
|
requirements:
|
83
|
-
- -
|
31
|
+
- - '>='
|
84
32
|
- !ruby/object:Gem::Version
|
85
|
-
version: 1.
|
86
|
-
type: :
|
33
|
+
version: 1.3.0
|
34
|
+
type: :development
|
87
35
|
prerelease: false
|
88
36
|
version_requirements: !ruby/object:Gem::Requirement
|
89
|
-
none: false
|
90
37
|
requirements:
|
91
|
-
- -
|
38
|
+
- - '>='
|
92
39
|
- !ruby/object:Gem::Version
|
93
|
-
version: 1.
|
40
|
+
version: 1.3.0
|
94
41
|
- !ruby/object:Gem::Dependency
|
95
|
-
name:
|
42
|
+
name: lorem
|
96
43
|
requirement: !ruby/object:Gem::Requirement
|
97
|
-
none: false
|
98
44
|
requirements:
|
99
|
-
- -
|
45
|
+
- - '>='
|
100
46
|
- !ruby/object:Gem::Version
|
101
|
-
version: 1.
|
47
|
+
version: 0.1.2
|
102
48
|
type: :development
|
103
49
|
prerelease: false
|
104
50
|
version_requirements: !ruby/object:Gem::Requirement
|
105
|
-
none: false
|
106
51
|
requirements:
|
107
|
-
- -
|
52
|
+
- - '>='
|
108
53
|
- !ruby/object:Gem::Version
|
109
|
-
version: 1.
|
110
|
-
description:
|
54
|
+
version: 0.1.2
|
55
|
+
description: 'A Ruby client library for Columbo: a Customer Experience Management
|
111
56
|
tool'
|
112
57
|
email: jtblin@gmail.com
|
113
58
|
executables: []
|
@@ -116,7 +61,9 @@ extra_rdoc_files:
|
|
116
61
|
- LICENSE
|
117
62
|
- README.md
|
118
63
|
files:
|
64
|
+
- lib/columbo/api_client.rb
|
119
65
|
- lib/columbo/capture.rb
|
66
|
+
- lib/columbo/compressor.rb
|
120
67
|
- lib/columbo/db_client.rb
|
121
68
|
- lib/columbo/inspector.rb
|
122
69
|
- lib/columbo/log_writer.rb
|
@@ -127,27 +74,26 @@ files:
|
|
127
74
|
- README.md
|
128
75
|
homepage: http://github.com/jtblin/columbo-rb
|
129
76
|
licenses: []
|
77
|
+
metadata: {}
|
130
78
|
post_install_message:
|
131
79
|
rdoc_options:
|
132
80
|
- --charset=UTF-8
|
133
81
|
require_paths:
|
134
82
|
- lib
|
135
83
|
required_ruby_version: !ruby/object:Gem::Requirement
|
136
|
-
none: false
|
137
84
|
requirements:
|
138
|
-
- -
|
85
|
+
- - '>='
|
139
86
|
- !ruby/object:Gem::Version
|
140
87
|
version: '0'
|
141
88
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
142
|
-
none: false
|
143
89
|
requirements:
|
144
|
-
- -
|
90
|
+
- - '>='
|
145
91
|
- !ruby/object:Gem::Version
|
146
92
|
version: 1.3.6
|
147
93
|
requirements: []
|
148
94
|
rubyforge_project:
|
149
|
-
rubygems_version: 1.
|
95
|
+
rubygems_version: 2.1.10
|
150
96
|
signing_key:
|
151
|
-
specification_version:
|
97
|
+
specification_version: 4
|
152
98
|
summary: A Ruby client library for Inspector Columbo
|
153
99
|
test_files: []
|