columbo 0.1.5 → 0.2.1
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.
- 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: []
|