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 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: false,
20
- mongo_uri: 'mongodb://user:password@mongodb_host:port/database',
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
- *Gemfile* file:
34
+ **Gemfile** file:
29
35
 
30
36
  gem 'columbo'
31
37
 
32
- *config/application.rb* file:
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
- bench: false,
38
- mongo_uri: 'mongodb://user:password@mongodb_host:port/database',
39
- logger: 'log/columbo.log'
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
- ## Disclaimer
52
+ ## Options
47
53
 
48
- This is an alpha release, it is tested with Sinatra and Rails 3 only.
49
- UI to explore sessions will be completed later (ETA: 2013'Q2).
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 2012 Jerome Touffe-Blin and contributors. It is licensed under the BSD license. See the include LICENSE file for details.
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/db_client'
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
@@ -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
- @capture_crawlers = opts[:capture_crawlers]
17
- @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)"
18
- @mongo_uri = opts[:mongo_uri]
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, 'mongo URI missing.' if @mongo_uri.nil?
31
+ raise ArgumentError, 'API key missing.' if api_key.nil?
23
32
 
24
- @inspector = Columbo::Inspector.new @mongo_uri
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) # Is it required?
51
+ headers = HeaderHash.new(headers)
39
52
 
40
- if !STATUS_WITH_NO_ENTITY_BODY.include?(status) &&
41
- !headers['transfer-encoding'] &&
42
- headers['content-type'] &&
43
- headers['content-type'].include?("text/html")
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
- Thread.abort_on_exception = true
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, response, start_processing, stop_processing, @crawlers, @capture_crawlers
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 if @capture
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
@@ -1,88 +1,77 @@
1
1
  require 'rack/request'
2
2
  require 'rack/response'
3
- require 'nokogiri'
4
- require 'html_mini'
3
+ require 'socket'
5
4
 
6
5
  module Columbo
7
6
  class Inspector
8
7
 
9
- def initialize(mongo_uri)
10
- @mongo_uri = mongo_uri
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, crawlers, capture_crawlers)
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
- rg = Regexp.new(crawlers, Regexp::IGNORECASE)
20
- return if request.user_agent.match(rg) && !capture_crawlers
19
+ return unless @capture_crawlers || !request.user_agent.match(@rg)
21
20
  html = ''
22
- body.each { |part| html += part }
23
- # Retrieve plain text body for full text search
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: request.params,
31
- remote_ip: request.ip,
35
+ params: params,
36
+ ip: request.ip,
32
37
  user_agent: request.user_agent,
33
- method: request.env['REQUEST_METHOD'],
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
- script: request.env['SCRIPT_NAME'],
36
- path: request.env['PATH_INFO'],
37
- query_string: request.env['QUERY_STRING'],
38
- protocol: request.env['rack.url_scheme'],
39
- server_name: request.env['SERVER_NAME'],
40
- server_port: request.env['SERVER_PORT'],
41
- http: request.env['SERVER_PROTOCOL'],
42
- session: request.env['rack.session'],
43
- cookie: request.env['rack.request.cookie_hash'],
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
- status: status,
48
- headers: headers,
49
- size: html.length,
50
- body: HtmlMini.minify(html),
51
- text: text,
52
- title: title,
53
- start: start,
54
- stop: stop,
55
- time: stop-start
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
- # Insert data in MongoDB
58
- client.insert sanitize(data)
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
@@ -1,3 +1,3 @@
1
1
  module Columbo
2
- VERSION = '0.1.5'
2
+ VERSION = '0.2.1'
3
3
  end
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
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-05-14 00:00:00.000000000 Z
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: nokogiri
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.6.1
86
- type: :runtime
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.6.1
40
+ version: 1.3.0
94
41
  - !ruby/object:Gem::Dependency
95
- name: sinatra
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.3.0
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.3.0
110
- description: ! 'A Ruby client library for Columbo: a Customer Experience Management
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.8.25
95
+ rubygems_version: 2.1.10
150
96
  signing_key:
151
- specification_version: 3
97
+ specification_version: 4
152
98
  summary: A Ruby client library for Inspector Columbo
153
99
  test_files: []