turnstile-rb 2.0.0 → 2.0.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
- SHA256:
3
- metadata.gz: 3cd7be07aee113fee488b2839dc8eadc611a3be257e6c37a7de50618e55fce94
4
- data.tar.gz: f13265ed097e7bb4f4625ed6fcf7da9e45208f33e382c6af0df7157345af3efa
2
+ SHA1:
3
+ metadata.gz: 64573d2b1f394251482822e130a7d9ca777554d3
4
+ data.tar.gz: ef96805bb0b4dc31541894c950b102e389a6cc05
5
5
  SHA512:
6
- metadata.gz: d68205747142bb284446c06221046a8746c7cf301e4d0306624872fc9ecc8a443386325a00581a22add14816822ac08fa98356ae36246bad49e1080001f04f02
7
- data.tar.gz: 3f84e37343fd99ae8bed3fad5182b5f66c716d945db21e2c1d8be27258a1b97fd26aa5f8696a1f52226ad3a07b303b56ff5ccdf678bef8f2089c8b9dd128253f
6
+ metadata.gz: 3a66e638ee0a591f9ea4acc1c72f9b20e59a35f682bb147e0bf688f57b7d4e2ee36b0e97e540168218351fc875f22f253a1bad00c4fcc414b689d4e238be6c09
7
+ data.tar.gz: 139bf132ff6ab3f18f31f9f4196484889974a1eb7137556f1963e044c1cbffb3c61cfc731d5b4b204b7a4ae8ad51a77a2067279155feab5974f37355b57aac44
data/.gitignore CHANGED
@@ -18,3 +18,5 @@ tmp
18
18
  .DS_Store
19
19
  coverate
20
20
  Gemfile.lock
21
+ .ruby-version
22
+ .rake_tasks*
data/.travis.yml CHANGED
@@ -6,7 +6,7 @@ services:
6
6
  - redis-server
7
7
  env:
8
8
  global:
9
- - CC_TEST_REPORTER_ID=d17e91219ca5c271ca9bdd9eb447611f1be6b750abe8e4bc48146f5dec1154d4
9
+ - CC_TEST_REPORTER_ID=c6e51bc4755c4602fccb935a436625bbac4be498193c4f40ba8c8e2ee0745182
10
10
  sudo: false
11
11
  language: ruby
12
12
  cache: bundler
data/Gemfile CHANGED
@@ -2,5 +2,5 @@
2
2
 
3
3
  source 'https://rubygems.org'
4
4
 
5
- # Specify your gem's dependencies in turnstile.gemspec
5
+ # Specify your gem's dependencies in turnstile-rb.gemspec
6
6
  gemspec
data/README.md CHANGED
@@ -1,7 +1,7 @@
1
- [![Build Status](https://travis-ci.org/kigster/turnstile.svg?branch=master)](https://travis-ci.org/kigster/turnstile)
2
- [![Test Coverage](https://codeclimate.com/github/wanelo/turnstile/badges/coverage.svg)](https://codeclimate.com/github/kigster/turnstile/coverage)
3
- [![Code Climate](https://codeclimate.com/github/wanelo/turnstile/badges/gpa.svg)](https://codeclimate.com/github/kigster/turnstile)
4
- [![Issue Count](https://codeclimate.com/github/wanelo/turnstile/badges/issue_count.svg)](https://codeclimate.com/github/kigster/turnstile)
1
+ [![Gem Version](https://badge.fury.io/rb/turnstile-rb.svg)](https://badge.fury.io/rb/turnstile-rb)
2
+ [![Build Status](https://travis-ci.org/kigster/turnstile-rb.svg?branch=master)](https://travis-ci.org/kigster/turnstile-rb)
3
+ [![Maintainability](https://api.codeclimate.com/v1/badges/8031931b7924461f6a90/maintainability)](https://codeclimate.com/github/kigster/turnstile-rb/maintainability)
4
+ [![Test Coverage](https://api.codeclimate.com/v1/badges/8031931b7924461f6a90/test_coverage)](https://codeclimate.com/github/kigster/turnstile-rb/test_coverage)
5
5
 
6
6
  # Turnstile
7
7
 
@@ -11,17 +11,9 @@ The gem uses (and depends on) a [Redis](http://redis.io/) instance in order to k
11
11
 
12
12
  ## Installation
13
13
 
14
- Add this line to your application's Gemfile (Note that another gem with a competing name is on RubyGems, so you **must** specify the path below):
14
+ Add this line to your application's Gemfile:
15
15
 
16
- gem 'turnstile-rb'
17
-
18
- And then execute:
19
-
20
- $ bundle
21
-
22
- Or install it yourself as:
23
-
24
- $ gem install turnstile
16
+ $ gem install turnstile-rb
25
17
 
26
18
  ## Usage
27
19
 
@@ -42,20 +34,27 @@ Asynchronous tracking has a slight initial setup overhead, but has zero run-time
42
34
 
43
35
  With Real Time tracking you can use sampling to _estimate_ the number of online users.
44
36
 
45
- * To do sampling, use the ```Turnstile::Tracker#track``` method
46
- * To store and analyze 100% of your data use ```Turnstile::Adapter#add```.
37
+ * To possibly use sampling, use the ```Turnstile::Tracker#track``` method.
38
+ * To store and analyze 100% of your data use ```Turnstile::Tracker#add```.
47
39
 
48
40
  **Example:**
49
41
 
50
42
  ```ruby
51
- @turnstile = Turnstile::Adapter.new
43
+ @tracker = Turnstile::Tracker.new
52
44
 
53
45
  user_id = 12345
54
46
  platform = 'desktop'
55
47
  ip = '224.247.12.4'
56
48
 
57
49
  # register user
58
- @turnstile.add(user_id, platform, ip)
50
+ @tracker.add(user_id, platform, ip)
51
+
52
+ # or you can add a colon-delimited string token:
53
+ @tracker.add_token("ios:172.2.5.3:39898098098")
54
+
55
+ # or you with a custom delimiter:
56
+ @tracker.add_token("ios|172.2.5.3|39898098098", '|')
57
+
59
58
  ```
60
59
 
61
60
  Without any further calls to ```track()``` method for this particular user/platform/ip combination, the user is considered _online_ for 60 seconds. Each subsequent call to ```track()``` resets the TTL.
@@ -145,25 +144,48 @@ this does not incur any additional cost for the application (as user tracking is
145
144
 
146
145
  Once the tracking information is sent, the data can be queried.
147
146
 
148
- If you used sampling, then you should query using ```Turnstile::Observer``` class that provides
149
- exprapolation of the results based on sample size configuration.
147
+ If you used sampling, then you should query using ```Turnstile::Observer``` class that provides exprapolation of the results based on sample size configuration.
148
+
150
149
  ```ruby
151
150
  # Return data for sampled users and the summary
152
151
  Turnstile::Observer.new.stats
153
- # => { stats: { total: 3, platforms: 2 }, users: [ { uid: 1, platform: 'desktop', ip: '123.2.4.54' }, ... ]
152
+ # => { stats: {
153
+ total: 3,
154
+ platforms: 2 },
155
+ users: [ { uid: 1, platform: 'desktop', ip: '123.2.4.54' }, ... ]
154
156
  ```
155
- If you did not use sampling, you can get some answers from the ```Turnstile::Adapter``` class:
157
+
158
+ If you did not use sampling, you can get some answers from the`Turnstile::Adapter` class:
159
+
156
160
  ```ruby
157
161
  Turntstile::Adapter.new.fetch
158
162
  # => [ { uid: 213, :platform: 'desktop', '123.2.4.54' }, { uid: 215, ... } ]
159
163
  ```
164
+
160
165
  You can also request an aggregate results, suitable for sending to graphing systems or displaying on a dashboard:
166
+
161
167
  ```ruby
162
168
  Turntstile::Adapter.new.aggregate
163
169
  # => { 'desktop' => 234, 'ios' => 3214, ..., 'total' => 4566 }
164
170
  ```
165
171
 
166
- ## Circonus NAD Integration
172
+ ### Summary Printing
173
+
174
+ ### JSON and CSV
175
+
176
+ Use the following syntax:
177
+
178
+ ```bash
179
+ # To see JSON summary:
180
+ turnstile -s json
181
+
182
+ # Or, for CSV
183
+ turnstile -s csv
184
+ ```
185
+
186
+
187
+
188
+ #### Circonus NAD
167
189
 
168
190
  We use Circonus to collect and graph data. You can use ```turnstile```
169
191
  to dump the current aggregate statistics from redis to standard output,
@@ -171,17 +193,18 @@ which is a tab-delimited format consumable by the nad daemon.
171
193
 
172
194
  (below output is formatted to show tabs as aligned for readability).
173
195
 
174
- ```ruby
175
- > turnstile --summary
196
+ ```bash
197
+ > turnstile -s
176
198
 
177
- turnstile.iphone n 383
199
+ turnstile.iphone n 383
178
200
  turnstile.ipad n 34
179
- turnstile.android n 108
201
+ turnstile.android n 108
180
202
  turnstile.ipod_touch n 34
181
203
  turnstile.unknown n 36
182
- turnstile.total n 595
204
+ turnstile.total n 595
183
205
  ```
184
206
 
207
+
185
208
  ## TODO:
186
209
 
187
210
  * Allow users of the gem to easier customize log reader to fit their own custom log files
data/Rakefile CHANGED
@@ -1 +1,21 @@
1
1
  require "bundler/gem_tasks"
2
+ require 'yard'
3
+
4
+ def shell(*args)
5
+ puts "running: #{args.join(' ')}"
6
+ system(args.join(' '))
7
+ end
8
+
9
+ task :permissions do
10
+ shell('rm -rf pkg/')
11
+ shell("chmod -v o+r,g+r * */* */*/* */*/*/* */*/*/*/* */*/*/*/*/*")
12
+ shell("find . -type d -exec chmod o+x,g+x {} \\;")
13
+ end
14
+
15
+ task :build => :permissions
16
+
17
+ YARD::Rake::YardocTask.new(:doc) do |t|
18
+ t.files = %w(lib/**/*.rb exe/*.rb - README.md LICENSE)
19
+ t.options.unshift('--title','"Turnstile — Active User Counter"')
20
+ t.after = ->() { exec('open doc/index.html') }
21
+ end
data/bin/turnstile CHANGED
@@ -1,91 +1,11 @@
1
1
  #!/usr/bin/env ruby
2
2
  ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../../Gemfile', __FILE__)
3
+
3
4
  require 'rubygems'
4
5
  require 'bundler/setup' if File.exists?(ENV['BUNDLE_GEMFILE'])
5
6
  require 'turnstile'
6
- require 'optparse'
7
- require 'colored2'
8
-
9
- options = {}
10
- ARGV << '-?' if ARGV.empty?
11
-
12
- Colored2.disable! unless STDOUT.tty?
13
-
14
- DESCRIPTION = <<-EOF
15
- Turnstile consists of two components:
16
-
17
- 1. A daemon process started with #{'turnstile -f <log-file> [ --daemon ]'.bold.green}
18
- This process should run on every server that generates a log
19
- file, and they should all connect to the same Redis server.
20
-
21
- 2. Query command, ran as #{'turnstile --summary'.bold.green}
22
-
23
- In the first form, turnstile can tail a given log file,
24
- parsing and tallying and periodically syncing to the master
25
- Redis server.
26
-
27
- When the query is invoked, data from all reporters is shown on STDOUT.
28
- EOF
29
-
30
- OptionParser.new do |opts|
31
- opts.banner = "Usage:\n".bold.magenta +
32
- " turnstile -f <file> [ --daemon ] [options]\n".green +
33
- " turnstile --summary [options]\n".green
34
-
35
- opts.separator "Description:".bold.magenta
36
- opts.separator ' ' + DESCRIPTION.gsub(/\n/, "\n ")
37
7
 
38
- opts.separator "Log File Specification:".bold.magenta
39
- opts.on('-f', '--file FILE', 'File to monitor') do |file|
40
- options[:file] = file
41
- end
42
- opts.on('-t', '--file-type TYPE',
43
- 'Either: json_formatted, pipe_delimited,',
44
- 'or comma_delimited (default).') do |type|
45
- options[:filetype] = type
46
- end
47
- opts.on('-D', '--delimiter CHAR',
48
- 'Forces "delimited" file type, and uses ',
49
- 'the character in the argument as the delimiter') do |v|
50
- options[:delimiter] = v
51
- end
52
- opts.separator "\nRedis Server:".bold.magenta
53
- opts.on('-h', '--redis-host HOST', 'Redis server host') do |host|
54
- Turnstile.config.redis_host = host
55
- end
56
- opts.on('-p', '--redis-port PORT', 'Redis server port') do |port|
57
- Turnstile.config.redis_port = port
58
- end
59
- opts.on('-n', '--redis-db DB', 'Redis server db') do |db|
60
- Turnstile.config.redis_db = db
61
- end
62
- opts.separator "\nMode of Operation:".bold.magenta
63
- opts.on('-d', '--daemonize', 'Daemonize to watch the logs') do |v|
64
- options[:daemonize] = true
65
- end
66
- opts.on('-s', '--summary', 'Print current stats (using NAD format) and exit') do |v|
67
- options[:summary] = true
68
- end
69
- opts.separator "\nTiming Adjustments:".bold.magenta
70
- opts.on('-b', '--buffer-interval INTERVAL', 'Buffer for this many seconds') do |v|
71
- options[:buffer_interval] = v.to_i
72
- end
73
- opts.on('-i', '--flush-interval INTERVAL', 'Flush then sleep for this many seconds') do |v|
74
- options[:flush_interval] = v.to_i
75
- end
76
- opts.separator "\nMiscellaneous:".bold.magenta
77
- opts.on('-v', '--verbose', 'Print status to stdout') do |v|
78
- options[:debug] = true
79
- end
80
- opts.on_tail('-?', '--help', 'Show this message') do
81
- puts opts
82
- exit
83
- end
84
- end.parse!
8
+ require 'turnstile/runner'
85
9
 
86
- if options[:summary]
87
- STDOUT.puts Turnstile::Nad.new.data
88
- else
89
- Turnstile::Collector::Runner.new(options).run
90
- end
10
+ Turnstile::Runner.new(ARGV).execute!
91
11
 
@@ -7,9 +7,11 @@ module Turnstile
7
7
  include Timeout
8
8
 
9
9
  def initialize
10
- self.redis = ::Redis.new(host: config.redis.host,
11
- port: config.redis.port,
12
- db: config.redis.db)
10
+ self.redis = config.redis.url ?
11
+ ::Redis.new(url: config.redis.url) :
12
+ ::Redis.new(host: config.redis.host,
13
+ port: config.redis.port,
14
+ db: config.redis.db)
13
15
  end
14
16
 
15
17
  def add(uid, platform, ip)
@@ -51,7 +53,6 @@ module Turnstile
51
53
  Turnstile.config
52
54
  end
53
55
 
54
-
55
56
  def compose_key(uid, platform = nil, ip = nil)
56
57
  "t:#{uid}:#{platform}:#{ip}"
57
58
  end
@@ -14,7 +14,7 @@ module Turnstile
14
14
  end
15
15
 
16
16
  def matches?(line)
17
- regexp && regexp.match?(line)
17
+ regexp && regexp.match(line)
18
18
  end
19
19
  end
20
20
  end
@@ -1,5 +1,6 @@
1
1
  require 'thread'
2
2
  require 'daemons/daemonize'
3
+ require 'colored2'
3
4
 
4
5
 
5
6
  module Turnstile
@@ -26,7 +27,7 @@ module Turnstile
26
27
  def wait_for_file(file)
27
28
  sleep_period = 1
28
29
  while !File.exist?(file)
29
- STDERR.puts "File #{file} does not exist, waiting for it to appear..."
30
+ STDERR.puts "File #{file.bold.yellow} does not exist, waiting for it to appear..."
30
31
  STDERR.puts 'Press Ctrl-C to abort.' if sleep_period == 1
31
32
 
32
33
  sleep sleep_period
@@ -5,6 +5,7 @@ module Turnstile
5
5
  class RedisConfig < ::Hashie::Dash
6
6
  include Hashie::Extensions::Dash::PropertyTranslation
7
7
 
8
+ property :url, required: false
8
9
  property :host, default: '127.0.0.1', required: true
9
10
  property :port, default: 6379, required: true, transform_with: ->(value) { value.to_i }
10
11
  property :db, default: 1, required: true, transform_with: ->(value) { value.to_i }
@@ -0,0 +1,107 @@
1
+ require 'optparse'
2
+ require 'colored2'
3
+ require 'forwardable'
4
+
5
+ require 'turnstile/version'
6
+
7
+ module Turnstile
8
+ class Parser
9
+ extend Forwardable
10
+ def_delegators :@system, :stdout, :stdin, :stderr
11
+
12
+ attr_accessor :options, :argv, :system
13
+
14
+ def initialize(argv, system)
15
+ self.system = system
16
+ self.argv = argv.dup
17
+ self.options = Hashie::Mash.new
18
+ self.argv << '-h' if argv.empty?
19
+ end
20
+
21
+ def parse
22
+ OptionParser.new do |opts|
23
+ opts.banner = "Usage:\n".bold.magenta +
24
+ " turnstile -f <file> [ --daemon ] [ options ]\n".yellow +
25
+ " turnstile -s [ json | csv | nad ] [ options ]\n".yellow +
26
+ " turnstile -a 'platform:ip:user' [ options ]\n".yellow
27
+
28
+ opts.separator 'Description:'.bold.magenta
29
+ opts.separator ' ' + ::Turnstile::DESCRIPTION.gsub(/\n/, "\n ")
30
+
31
+ opts.separator 'Log File Specification:'.bold.magenta
32
+ opts.on('-f', '--file FILE', 'File to monitor') do |file|
33
+ options[:file] = file
34
+ end
35
+ opts.on('-t', '--file-type TYPE',
36
+ 'Either: json_formatted, pipe_delimited,',
37
+ 'or comma_delimited (default).') do |type|
38
+ options[:filetype] = type
39
+ end
40
+ opts.on('-D', '--delimiter CHAR',
41
+ 'Forces "delimited" file type, and uses ',
42
+ 'the character in the argument as the delimiter') do |v|
43
+ options[:delimiter] = v
44
+ end
45
+ opts.separator "\nRedis Server:".bold.magenta
46
+ opts.on('-r', '--redis-url URL', 'Redis server URL') do |host|
47
+ Turnstile.config.redis_url = host
48
+ end
49
+ opts.on('--redis-host HOST', 'Redis server host') do |host|
50
+ Turnstile.config.redis_host = host
51
+ end
52
+ opts.on('--redis-port PORT', 'Redis server port') do |port|
53
+ Turnstile.config.redis_port = port
54
+ end
55
+ opts.on('--redis-db DB', 'Redis server db') do |db|
56
+ Turnstile.config.redis_db = db
57
+ end
58
+ opts.separator "\nMode of Operation:".bold.magenta
59
+ opts.on('-d', '--daemonize', 'Daemonize to watch the logs') do |v|
60
+ options[:daemonize] = true
61
+ end
62
+ opts.on('-s', '--summary [FORMAT]',
63
+ 'Print current stats and exit. Optional format can be',
64
+ 'json (default), nad, yaml, or csv') do |v|
65
+ options[:summary] = true
66
+ options[:summary_format] = (v || 'json').to_sym
67
+ end
68
+ opts.on('-a', '--add TOKEN',
69
+ 'Registers an event from the token, such as ',
70
+ '"ios:123.4.4.4:32442". Use -d to customize delimiter.') do |v|
71
+ options[:add] = v
72
+ end
73
+ opts.separator "\nTiming Adjustments:".bold.magenta
74
+ opts.on('-b', '--buffer-interval INTERVAL', 'Buffer for this many seconds') do |v|
75
+ options[:buffer_interval] = v.to_i
76
+ end
77
+ opts.on('-i', '--flush-interval INTERVAL', 'Flush then sleep for this many seconds') do |v|
78
+ options[:flush_interval] = v.to_i
79
+ end
80
+ opts.separator "\nMiscellaneous:".bold.magenta
81
+ opts.on('-v', '--verbose', 'Print status to stdout') do |v|
82
+ options[:debug] = true
83
+ end
84
+ opts.on_tail('-h', '--help', 'Show this message') do
85
+ puts opts
86
+ return
87
+ end
88
+ end.parse!(argv)
89
+
90
+ if options[:summary]
91
+ Turnstile::Summary.print(options[:summary_format] || :json, options[:delimiter])
92
+ elsif options[:add]
93
+ Turnstile::Tracker.new.add_token(options[:add], options[:delimiter] || ':')
94
+ Turnstile::Summary.print(options[:summary_format] || :json)
95
+ else
96
+ Turnstile::Collector::Runner.new(options).run
97
+ end
98
+
99
+ rescue OptionParser::MissingArgument => e
100
+ STDERR.puts e.message.bold.red
101
+ rescue Exception => e
102
+ STDERR.puts e.message.bold.red
103
+ end
104
+ end
105
+ end
106
+
107
+
@@ -0,0 +1,54 @@
1
+ require 'optparse'
2
+ require 'colored2'
3
+
4
+ require 'turnstile/version'
5
+ require 'turnstile/parser'
6
+
7
+ module Turnstile
8
+
9
+
10
+ class Runner
11
+ attr_reader :argv, :stdin, :stdout, :stderr, :kernel
12
+
13
+ # Allow everything fun to be injected from the outside while defaulting to normal implementations.
14
+ def initialize(argv, stdin = STDIN, stdout = STDOUT, stderr = STDERR, kernel = Kernel)
15
+ @argv, @stdin, @stdout, @stderr, @kernel = argv, stdin, stdout, stderr, kernel
16
+ end
17
+
18
+ def execute!
19
+ exit_code = begin
20
+
21
+ Colored2.disable! unless stdout.tty?
22
+
23
+ $stderr = stderr
24
+ $stdin = stdin
25
+ $stdout = stdout
26
+
27
+ Turnstile::Parser.new(argv, self).parse
28
+
29
+ # Thor::Base#start does not have a return value, assume success if no exception is raised.
30
+ 0
31
+ rescue StandardError => e
32
+ # The ruby interpreter would pipe this to STDERR and exit 1 in the case of an unhandled exception
33
+ b = e.backtrace
34
+ @stderr.puts("#{b.shift}: #{e.message} (#{e.class})")
35
+ @stderr.puts(b.map { |s| "\tfrom #{s}" }.join("\n"))
36
+ 1
37
+ rescue SystemExit => e
38
+ e.status
39
+ ensure
40
+ $stderr = STDERR
41
+ $stdin = STDIN
42
+ $stdout = STDOUT
43
+ end
44
+
45
+ # Proxy our exit code back to the injected kernel.
46
+ @kernel.exit(exit_code)
47
+ end
48
+ end
49
+ end
50
+
51
+ module Turnstile
52
+
53
+ end
54
+
@@ -0,0 +1,57 @@
1
+ require 'csv'
2
+ module Turnstile
3
+ class Summary
4
+
5
+ class << self
6
+ def print(method, delimiter = nil)
7
+ summary = new
8
+ if summary.respond_to?(method)
9
+ STDOUT.puts summary.send(method, delimiter)
10
+ else
11
+ STDERR.puts "don't know how to use format '#{method}'mac"
12
+ end
13
+ end
14
+ end
15
+
16
+ def json(*)
17
+ out = "{\n"
18
+ first = true
19
+ aggregate.each_pair do |key, value|
20
+ out << ",\n" unless first
21
+ first = false
22
+ out << json_row(key, value)
23
+ end
24
+ out << "\n}"
25
+ out
26
+ end
27
+
28
+ def nad(*)
29
+ out = ''
30
+ aggregate.each_pair do |key, value|
31
+ out << nad_row(key, value)
32
+ end
33
+ out
34
+ end
35
+
36
+ def csv(delimiter = nil)
37
+ out = CSV.generate do |csv|
38
+ aggregate.each_pair do |key, value|
39
+ csv << [key, value]
40
+ end
41
+ end
42
+ delimiter ? out.gsub(/,/m, delimiter) : out
43
+ end
44
+
45
+ def nad_row(key, value)
46
+ %Q(turnstile:#{key}#{"\tn\t"}#{value}\n)
47
+ end
48
+
49
+ def json_row(key, value)
50
+ %Q( "#{key}": #{value})
51
+ end
52
+
53
+ def aggregate
54
+ Turnstile::Adapter.new.aggregate
55
+ end
56
+ end
57
+ end
@@ -1,9 +1,20 @@
1
1
  module Turnstile
2
2
  class Tracker
3
- def track(uid, platform = 'unknown', ip = nil)
3
+ def sample(uid, platform = 'unknown', ip = nil)
4
4
  adapter.add(uid, platform, ip) if sampler.sample(uid)
5
5
  end
6
6
 
7
+ alias track sample
8
+
9
+ def add(uid, platform = 'unknown', ip = nil)
10
+ adapter.add(uid, platform, ip)
11
+ end
12
+
13
+ def add_token(token, delimiter = ':')
14
+ platform, ip, uid = token.split(delimiter)
15
+ adapter.add(uid, platform, ip) if uid
16
+ end
17
+
7
18
  private
8
19
 
9
20
  def adapter
@@ -1,3 +1,21 @@
1
1
  module Turnstile
2
- VERSION = '2.0.0'
2
+ VERSION = '2.0.1'
3
+
4
+ GEM_DESCRIPTION = <<-EOF
5
+ Turnstile is a Redis-based library that can accurately track total number of concurrent
6
+ users accessing a web/API based server application. It can break it down by "platform"
7
+ or a device type, and returns data in JSON, CSV of NAD formats. While user tracking
8
+ may happen synchronously using a Rack middleware, another method is provided that is
9
+ based on log file analysis, and can therefore be performed outside web server process.
10
+ EOF
11
+
12
+ DESCRIPTION = <<-EOF
13
+ Turnstile can run as a daemon, in which mode it monitors a given log file.
14
+ Alternatively, turnstile binary can be used to print current stats, and even
15
+ add new data into the registry.
16
+
17
+ If you are using Turnstile to tail log files, make sure you run on each app sever
18
+ that's generating log files.
19
+ EOF
20
+
3
21
  end
data/lib/turnstile.rb CHANGED
@@ -6,7 +6,7 @@ require 'turnstile/tracker'
6
6
  require 'turnstile/observer'
7
7
  require 'turnstile/logger'
8
8
  require 'turnstile/collector'
9
- require 'turnstile/nad'
9
+ require 'turnstile/summary'
10
10
 
11
11
  module Turnstile
12
12
  def self.configure(&block)
@@ -0,0 +1,41 @@
1
+ require 'spec_helper'
2
+
3
+ describe Turnstile::Summary do
4
+ subject { described_class.new }
5
+
6
+ let(:aggregate) {
7
+ {
8
+ 'android' => 3,
9
+ 'ios' => 2,
10
+ 'total' => 5
11
+ }
12
+ }
13
+
14
+ before { expect(subject).to receive(:aggregate).once.and_return(aggregate) }
15
+
16
+ describe '#nad' do
17
+ context 'have some data' do
18
+ let(:expected_string) {
19
+ <<-EOF
20
+ turnstile:android#{"\t"}n#{"\t"}3
21
+ turnstile:ios#{"\t"}n#{"\t"}2
22
+ turnstile:total#{"\t"}n#{"\t"}5
23
+ EOF
24
+ }
25
+
26
+ it 'return data in NAD format' do
27
+ expect(subject.nad).to eql(expected_string)
28
+ end
29
+ end
30
+ end
31
+
32
+ describe '#json' do
33
+ context 'have some data' do
34
+ let(:json) { subject.json }
35
+ let(:hash) { JSON.load(json) }
36
+ it 'return data in NAD format' do
37
+ expect(hash).to eql(aggregate)
38
+ end
39
+ end
40
+ end
41
+ end
data/turnstile-rb.gemspec CHANGED
@@ -8,8 +8,11 @@ Gem::Specification.new do |spec|
8
8
  spec.version = Turnstile::VERSION
9
9
  spec.authors = ['Konstantin Gredeskoul']
10
10
  spec.email = %w(kigster@gmail.com)
11
+
11
12
  spec.summary = %q{Asynchronous and non-invasive concurrent user tracking with Redis, by scanning application logs across all servers.}
12
- spec.description = %q{Asynchronous and non-invasive concurrent user tracking with Redis, by scanning application logs across all servers.}
13
+
14
+ spec.description = Turnstile::GEM_DESCRIPTION
15
+
13
16
  spec.homepage = 'https://github.com/kigster/turnstile-rb'
14
17
  spec.license = 'MIT'
15
18
 
@@ -27,10 +30,9 @@ Gem::Specification.new do |spec|
27
30
 
28
31
  spec.add_development_dependency 'bundler'
29
32
  spec.add_development_dependency 'rake'
33
+ spec.add_development_dependency 'yard'
30
34
 
31
35
  spec.add_development_dependency 'rspec'
32
36
  spec.add_development_dependency 'rspec-its'
33
- spec.add_development_dependency 'guard-rspec'
34
- spec.add_development_dependency 'rb-fsevent'
35
37
  spec.add_development_dependency 'simplecov'
36
38
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: turnstile-rb
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.0.0
4
+ version: 2.0.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Konstantin Gredeskoul
@@ -123,7 +123,7 @@ dependencies:
123
123
  - !ruby/object:Gem::Version
124
124
  version: '0'
125
125
  - !ruby/object:Gem::Dependency
126
- name: rspec
126
+ name: yard
127
127
  requirement: !ruby/object:Gem::Requirement
128
128
  requirements:
129
129
  - - ">="
@@ -137,21 +137,7 @@ dependencies:
137
137
  - !ruby/object:Gem::Version
138
138
  version: '0'
139
139
  - !ruby/object:Gem::Dependency
140
- name: rspec-its
141
- requirement: !ruby/object:Gem::Requirement
142
- requirements:
143
- - - ">="
144
- - !ruby/object:Gem::Version
145
- version: '0'
146
- type: :development
147
- prerelease: false
148
- version_requirements: !ruby/object:Gem::Requirement
149
- requirements:
150
- - - ">="
151
- - !ruby/object:Gem::Version
152
- version: '0'
153
- - !ruby/object:Gem::Dependency
154
- name: guard-rspec
140
+ name: rspec
155
141
  requirement: !ruby/object:Gem::Requirement
156
142
  requirements:
157
143
  - - ">="
@@ -165,7 +151,7 @@ dependencies:
165
151
  - !ruby/object:Gem::Version
166
152
  version: '0'
167
153
  - !ruby/object:Gem::Dependency
168
- name: rb-fsevent
154
+ name: rspec-its
169
155
  requirement: !ruby/object:Gem::Requirement
170
156
  requirements:
171
157
  - - ">="
@@ -192,8 +178,12 @@ dependencies:
192
178
  - - ">="
193
179
  - !ruby/object:Gem::Version
194
180
  version: '0'
195
- description: Asynchronous and non-invasive concurrent user tracking with Redis, by
196
- scanning application logs across all servers.
181
+ description: |2
182
+ Turnstile is a Redis-based library that can accurately track total number of concurrent
183
+ users accessing a web/API based server application. It can break it down by "platform"
184
+ or a device type, and returns data in JSON, CSV of NAD formats. While user tracking
185
+ may happen synchronously using a Rack middleware, another method is provided that is
186
+ based on log file analysis, and can therefore be performed outside web server process.
197
187
  email:
198
188
  - kigster@gmail.com
199
189
  executables:
@@ -205,7 +195,6 @@ files:
205
195
  - ".rspec"
206
196
  - ".travis.yml"
207
197
  - Gemfile
208
- - Guardfile
209
198
  - LICENSE.txt
210
199
  - README.md
211
200
  - Rakefile
@@ -220,10 +209,11 @@ files:
220
209
  - lib/turnstile/collector/updater.rb
221
210
  - lib/turnstile/configuration.rb
222
211
  - lib/turnstile/logger.rb
223
- - lib/turnstile/nad.rb
224
212
  - lib/turnstile/observer.rb
225
- - lib/turnstile/rb.rb
213
+ - lib/turnstile/parser.rb
214
+ - lib/turnstile/runner.rb
226
215
  - lib/turnstile/sampler.rb
216
+ - lib/turnstile/summary.rb
227
217
  - lib/turnstile/tracker.rb
228
218
  - lib/turnstile/version.rb
229
219
  - spec/fixtures/sample-production.log
@@ -232,9 +222,9 @@ files:
232
222
  - spec/turnstile/adapter_spec.rb
233
223
  - spec/turnstile/collector/log_reader_spec.rb
234
224
  - spec/turnstile/configuration_spec.rb
235
- - spec/turnstile/nad_spec.rb
236
225
  - spec/turnstile/observer_spec.rb
237
226
  - spec/turnstile/sampler_spec.rb
227
+ - spec/turnstile/summary_spec.rb
238
228
  - spec/turnstile/tracker_spec.rb
239
229
  - spec/turnstile_spec.rb
240
230
  - turnstile-rb.gemspec
@@ -258,7 +248,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
258
248
  version: '0'
259
249
  requirements: []
260
250
  rubyforge_project:
261
- rubygems_version: 2.6.14
251
+ rubygems_version: 2.6.11
262
252
  signing_key:
263
253
  specification_version: 4
264
254
  summary: Asynchronous and non-invasive concurrent user tracking with Redis, by scanning
@@ -270,8 +260,8 @@ test_files:
270
260
  - spec/turnstile/adapter_spec.rb
271
261
  - spec/turnstile/collector/log_reader_spec.rb
272
262
  - spec/turnstile/configuration_spec.rb
273
- - spec/turnstile/nad_spec.rb
274
263
  - spec/turnstile/observer_spec.rb
275
264
  - spec/turnstile/sampler_spec.rb
265
+ - spec/turnstile/summary_spec.rb
276
266
  - spec/turnstile/tracker_spec.rb
277
267
  - spec/turnstile_spec.rb
data/Guardfile DELETED
@@ -1,13 +0,0 @@
1
- #!/usr/bin/env ruby
2
- #^syntax detection
3
-
4
- # A sample Guardfile
5
- # More info at https://github.com/guard/guard#readme
6
-
7
- guard 'rspec' do
8
- watch(%r{^lib/(.+)\.rb$}) { "spec" }
9
- watch(%r{^spec/.+_spec\.rb$})
10
- watch('spec/spec_helper.rb') { "spec" }
11
- watch(%r{spec/support/.*}) { "spec" }
12
- end
13
-
data/lib/turnstile/nad.rb DELETED
@@ -1,16 +0,0 @@
1
- module Turnstile
2
- class Nad
3
-
4
- def data
5
- out = ""
6
- aggregate.each_pair do |key, value|
7
- out << %Q(turnstile:#{key}#{"\tn\t"}#{value}\n)
8
- end
9
- out
10
- end
11
-
12
- def aggregate
13
- Turnstile::Adapter.new.aggregate
14
- end
15
- end
16
- end
data/lib/turnstile/rb.rb DELETED
@@ -1 +0,0 @@
1
- require_relative '../turnstile'
@@ -1,31 +0,0 @@
1
- require 'spec_helper'
2
-
3
- describe 'Turnstile::Nad' do
4
-
5
- subject { Turnstile::Nad.new }
6
-
7
- describe '#data' do
8
- context 'have some data' do
9
- let(:aggregate) { {
10
- 'android' => 3,
11
- 'ios' => 2,
12
- 'total' => 5
13
- }
14
- }
15
-
16
- let(:expected_string) {
17
- <<-EOF
18
- turnstile:android#{"\t"}n#{"\t"}3
19
- turnstile:ios#{"\t"}n#{"\t"}2
20
- turnstile:total#{"\t"}n#{"\t"}5
21
- EOF
22
- }
23
-
24
- it "return data in NAD tab dilimited format" do
25
- expect(subject).to receive(:aggregate).once.and_return(aggregate)
26
- expect(subject.data).to eql(expected_string)
27
- end
28
- end
29
-
30
- end
31
- end