rack-analytics 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +4 -0
- data/Gemfile +4 -0
- data/Gemfile.lock +48 -0
- data/README.markdown +74 -0
- data/Rakefile +15 -0
- data/lib/rack-analytics.rb +1 -0
- data/lib/rack/analytics.rb +37 -0
- data/lib/rack/analytics/request_logger.rb +18 -0
- data/lib/rack/analytics/request_parser.rb +33 -0
- data/lib/rack/analytics/version.rb +5 -0
- data/rack-analytics.gemspec +30 -0
- data/test/request_logger_test.rb +107 -0
- data/test/request_parser_test.rb +47 -0
- data/test/support/dummy_app.rb +23 -0
- data/test/support/test_helpers.rb +15 -0
- data/test/teststrap.rb +13 -0
- metadata +184 -0
data/.gitignore
ADDED
data/Gemfile
ADDED
data/Gemfile.lock
ADDED
@@ -0,0 +1,48 @@
|
|
1
|
+
PATH
|
2
|
+
remote: .
|
3
|
+
specs:
|
4
|
+
rack-analytics (0.0.1)
|
5
|
+
activesupport
|
6
|
+
bson_ext
|
7
|
+
mongo
|
8
|
+
rack
|
9
|
+
|
10
|
+
GEM
|
11
|
+
remote: http://rubygems.org/
|
12
|
+
specs:
|
13
|
+
activesupport (3.0.3)
|
14
|
+
bson (1.2.0)
|
15
|
+
bson_ext (1.2.0)
|
16
|
+
mongo (1.2.0)
|
17
|
+
bson (>= 1.2.0)
|
18
|
+
nokogiri (1.4.4)
|
19
|
+
rack (1.2.1)
|
20
|
+
rack-test (0.5.7)
|
21
|
+
rack (>= 1.0)
|
22
|
+
riot (0.12.1)
|
23
|
+
rr
|
24
|
+
term-ansicolor
|
25
|
+
rr (1.0.2)
|
26
|
+
sinatra (1.1.2)
|
27
|
+
rack (~> 1.1)
|
28
|
+
tilt (~> 1.2)
|
29
|
+
term-ansicolor (1.0.5)
|
30
|
+
tilt (1.2.1)
|
31
|
+
webrat (0.7.3)
|
32
|
+
nokogiri (>= 1.2.0)
|
33
|
+
rack (>= 1.0)
|
34
|
+
rack-test (>= 0.5.3)
|
35
|
+
|
36
|
+
PLATFORMS
|
37
|
+
ruby
|
38
|
+
|
39
|
+
DEPENDENCIES
|
40
|
+
activesupport
|
41
|
+
bson_ext
|
42
|
+
mongo
|
43
|
+
rack
|
44
|
+
rack-analytics!
|
45
|
+
rack-test
|
46
|
+
riot
|
47
|
+
sinatra
|
48
|
+
webrat
|
data/README.markdown
ADDED
@@ -0,0 +1,74 @@
|
|
1
|
+
# Rack-Analytics #
|
2
|
+
|
3
|
+
**Rack-Analytics** is a rack middleware that creates a log of all user requests to your application and saves them on a MongoDB database.
|
4
|
+
|
5
|
+
All requests are created on a separated thread, so it won't add a lot of overhead on each requests.
|
6
|
+
|
7
|
+
## Instalation ##
|
8
|
+
|
9
|
+
If you're using Bundler (like Rails 3, for example), you can add this line on your `Gemfile`:
|
10
|
+
|
11
|
+
gem 'rack-analytics'
|
12
|
+
|
13
|
+
Then, run `bundle` to install the gem. If you don't use Bundler, you will need to install the gem manually with the following command:
|
14
|
+
|
15
|
+
gem install rack-analytics
|
16
|
+
|
17
|
+
Either way, with the gem installed, you can add this on your `config.ru`:
|
18
|
+
|
19
|
+
require 'rack/analytics'
|
20
|
+
use Rack::Analytics::RequestLogger
|
21
|
+
|
22
|
+
It will do the trick, connecting to MongoDB and start to log all your requests. If you need to change some of the configuration of the database, you can do:
|
23
|
+
|
24
|
+
require 'rack/analytics'
|
25
|
+
|
26
|
+
# To change the database name only
|
27
|
+
Rack::Analytics.db_name = 'mydb'
|
28
|
+
|
29
|
+
# To change the database connection completely
|
30
|
+
Rack::Analytics.db = Mongo::Connection.new.db 'mydb'
|
31
|
+
|
32
|
+
use Rack::Analytics::RequestLogger
|
33
|
+
|
34
|
+
**Rack-Analytics** runs on a threaded environment, and just like `db` and `db_name`, you can set the Queue and the Thread for the parallelism with the keys `queue` and `thread`, respectively **(i don't advise doing that, though)**.
|
35
|
+
|
36
|
+
You can also change the parser, to remove some of the fields (on the future, you'll be able to create your own fields based on the request headers):
|
37
|
+
|
38
|
+
require 'rack/analytics'
|
39
|
+
|
40
|
+
parser = Rack::Analytics::RequestParser.new
|
41
|
+
|
42
|
+
# Will log only the path and time
|
43
|
+
parser.only = ['time', 'path']
|
44
|
+
|
45
|
+
# Won't log the time
|
46
|
+
parser.except = 'time'
|
47
|
+
|
48
|
+
Rack::Analytics.parser = parser
|
49
|
+
|
50
|
+
use Rack::Analytics::RequestLogger
|
51
|
+
|
52
|
+
Be sure to just use one of those, since they are mutually excludent (and `only` has a preference over `except`).
|
53
|
+
|
54
|
+
## Notes on Patches/Pull Requests ##
|
55
|
+
|
56
|
+
* Fork the project.
|
57
|
+
* Make your feature addition or bug fix.
|
58
|
+
* Add tests for it. This is important so I don't break it in a future version unintentionally.
|
59
|
+
* Commit, do not mess with rakefile, version, or history. (if you want to have your own version, that is fine but bump version in a commit by itself I can ignore when I pull)
|
60
|
+
* Send me a pull request. Bonus points for topic branches.
|
61
|
+
|
62
|
+
## Licence ##
|
63
|
+
|
64
|
+
Copyright (c) 2011, Cainã Costa <cainan.costa@gmail.com>
|
65
|
+
|
66
|
+
Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted.
|
67
|
+
|
68
|
+
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
69
|
+
|
70
|
+
## Thanks ##
|
71
|
+
|
72
|
+
Those are the people that helped me with this project, both with code and/or with guidance. Put your name here if you create a pull request and think you deserves it!
|
73
|
+
|
74
|
+
* Rafael França <rafael.ufs@gmail.com>
|
data/Rakefile
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
require 'bundler'
|
2
|
+
Bundler::GemHelper.install_tasks
|
3
|
+
|
4
|
+
require 'rake/testtask'
|
5
|
+
|
6
|
+
desc "Run all our tests"
|
7
|
+
task :test do
|
8
|
+
Rake::TestTask.new do |t|
|
9
|
+
t.libs << "test"
|
10
|
+
t.pattern = "test/**/*_test.rb"
|
11
|
+
t.verbose = false
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
task :default => :test
|
@@ -0,0 +1 @@
|
|
1
|
+
require 'rack/analytics'
|
@@ -0,0 +1,37 @@
|
|
1
|
+
require 'rack/analytics/request_logger'
|
2
|
+
require 'rack/analytics/request_parser'
|
3
|
+
|
4
|
+
require 'active_support/core_ext/module/attribute_accessors'
|
5
|
+
|
6
|
+
module Rack
|
7
|
+
module Analytics
|
8
|
+
mattr_accessor :queue
|
9
|
+
@@queue = Queue.new
|
10
|
+
|
11
|
+
mattr_accessor :parser
|
12
|
+
@@parser = RequestParser.new
|
13
|
+
|
14
|
+
mattr_accessor :db_name
|
15
|
+
@@db_name = 'rack-analytics'
|
16
|
+
|
17
|
+
mattr_accessor :db
|
18
|
+
@@db = Mongo::Connection.new.db(@@db_name)
|
19
|
+
|
20
|
+
mattr_accessor :thread
|
21
|
+
def self.thread
|
22
|
+
@@thread ||= Thread.new do
|
23
|
+
while env = queue.pop
|
24
|
+
db['views'].insert parser.parse(env).data
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
def self.finish!
|
30
|
+
queue << nil
|
31
|
+
thread.join
|
32
|
+
@@thread = nil
|
33
|
+
|
34
|
+
thread
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
require "thread"
|
2
|
+
|
3
|
+
module Rack
|
4
|
+
module Analytics
|
5
|
+
class RequestLogger
|
6
|
+
def initialize app, options = {}
|
7
|
+
@app = app
|
8
|
+
@options = options
|
9
|
+
end
|
10
|
+
|
11
|
+
def call env
|
12
|
+
Rack::Analytics.queue << env if env['REQUEST_METHOD'] == 'GET'
|
13
|
+
|
14
|
+
@app.call(env)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
module Rack
|
2
|
+
module Analytics
|
3
|
+
class RequestParser
|
4
|
+
attr_reader :data
|
5
|
+
|
6
|
+
DEFAULT_KEYS = ['time', 'path', 'user_agent', 'referral']
|
7
|
+
|
8
|
+
def except=(values)
|
9
|
+
@except = values.to_a
|
10
|
+
end
|
11
|
+
|
12
|
+
def only=(values)
|
13
|
+
@only = values.to_a
|
14
|
+
end
|
15
|
+
|
16
|
+
def parse request
|
17
|
+
@data = {}
|
18
|
+
|
19
|
+
@data['time'] = Time.now if to_parse.include? 'time'
|
20
|
+
@data['path'] = request['PATH_INFO'] if to_parse.include? 'path'
|
21
|
+
@data['user_agent'] = request['HTTP_USER_AGENT'] if to_parse.include? 'user_agent'
|
22
|
+
@data['referral'] = request['HTTP_REFERER'] if to_parse.include? 'referral'
|
23
|
+
|
24
|
+
return self
|
25
|
+
end
|
26
|
+
|
27
|
+
private
|
28
|
+
def to_parse
|
29
|
+
@only ? @only.to_a : DEFAULT_KEYS - @except.to_a
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
$:.push File.expand_path("../lib", __FILE__)
|
3
|
+
require "rack/analytics/version"
|
4
|
+
|
5
|
+
Gem::Specification.new do |s|
|
6
|
+
s.name = "rack-analytics"
|
7
|
+
s.version = Rack::Analytics::VERSION
|
8
|
+
s.platform = Gem::Platform::RUBY
|
9
|
+
s.authors = ["Cainã Costa"]
|
10
|
+
s.email = ["cainan.costa@gmail.com"]
|
11
|
+
s.homepage = "http://rubygems.org/gems/rack-analytics"
|
12
|
+
s.summary = %q{A rack middleware that collects access statistics}
|
13
|
+
s.description = %q{A rack middleware that collects access statistics and saves them on a MongoDB database.}
|
14
|
+
|
15
|
+
s.rubyforge_project = "rack-analytics"
|
16
|
+
|
17
|
+
s.files = `git ls-files`.split("\n")
|
18
|
+
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
19
|
+
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
20
|
+
s.require_paths = ["lib"]
|
21
|
+
|
22
|
+
s.add_runtime_dependency('rack')
|
23
|
+
s.add_runtime_dependency('bson_ext')
|
24
|
+
s.add_runtime_dependency('mongo')
|
25
|
+
s.add_runtime_dependency('activesupport')
|
26
|
+
|
27
|
+
s.add_development_dependency('riot')
|
28
|
+
s.add_development_dependency('sinatra')
|
29
|
+
s.add_development_dependency('rack-test')
|
30
|
+
end
|
@@ -0,0 +1,107 @@
|
|
1
|
+
require 'teststrap'
|
2
|
+
|
3
|
+
context "Rack::Analytics::RequestLogger" do
|
4
|
+
helper(:db) { mongo }
|
5
|
+
teardown { Rack::Analytics.finish! }
|
6
|
+
|
7
|
+
context "should render a get request correctly" do
|
8
|
+
setup { get '/' }
|
9
|
+
|
10
|
+
asserts('response is ok') { last_response.ok? }
|
11
|
+
asserts('response has correct body') { last_response.body }.equals "homepage"
|
12
|
+
end
|
13
|
+
|
14
|
+
context "should render a get request on a inner path correctly" do
|
15
|
+
setup { get '/inner-page' }
|
16
|
+
|
17
|
+
asserts('response is ok') { last_response.ok? }
|
18
|
+
asserts('response has correct body') { last_response.body }.equals "inner page"
|
19
|
+
end
|
20
|
+
|
21
|
+
context "should render a post request correctly" do
|
22
|
+
setup { post '/' }
|
23
|
+
|
24
|
+
asserts('response is ok') { last_response.ok? }
|
25
|
+
asserts('response has correct body') { last_response.body }.equals "homepage with post"
|
26
|
+
end
|
27
|
+
|
28
|
+
context "should render a put request correctly" do
|
29
|
+
setup { put '/' }
|
30
|
+
|
31
|
+
asserts('response is ok') { last_response.ok? }
|
32
|
+
asserts('response has correct body') { last_response.body }.equals "homepage with put"
|
33
|
+
end
|
34
|
+
|
35
|
+
context "should render a delete request correctly" do
|
36
|
+
setup { delete '/' }
|
37
|
+
|
38
|
+
asserts('response is ok') { last_response.ok? }
|
39
|
+
asserts('response has correct body') { last_response.body }.equals "homepage with delete"
|
40
|
+
end
|
41
|
+
|
42
|
+
context "should create a access document when visiting the page" do
|
43
|
+
setup do
|
44
|
+
db.drop_collection 'views'
|
45
|
+
|
46
|
+
get '/'
|
47
|
+
end
|
48
|
+
|
49
|
+
asserts('counter has incremented') { db['views'].count }.equals 1
|
50
|
+
end
|
51
|
+
|
52
|
+
context "shouldn't create a access document when with post, put and delete" do
|
53
|
+
setup do
|
54
|
+
db.drop_collection 'views'
|
55
|
+
|
56
|
+
post '/'
|
57
|
+
put '/'
|
58
|
+
delete '/'
|
59
|
+
end
|
60
|
+
|
61
|
+
asserts("counter hasn't incremented") { db['views'].count }.equals 0
|
62
|
+
end
|
63
|
+
|
64
|
+
context "should save the path of the access" do
|
65
|
+
setup do
|
66
|
+
db.drop_collection 'views'
|
67
|
+
|
68
|
+
get '/'
|
69
|
+
end
|
70
|
+
|
71
|
+
asserts('it should have a time key') { db['views'].find_one }.includes 'path'
|
72
|
+
asserts('it should have a time set') { db['views'].find_one['path'] }.equals '/'
|
73
|
+
end
|
74
|
+
|
75
|
+
context "should save the time of the access" do
|
76
|
+
setup do
|
77
|
+
db.drop_collection 'views'
|
78
|
+
|
79
|
+
get '/'
|
80
|
+
end
|
81
|
+
|
82
|
+
asserts('it should have a time key') { db['views'].find_one }.includes 'time'
|
83
|
+
asserts('it should have a time set') { db['views'].find_one['time'] }.kind_of Time
|
84
|
+
end
|
85
|
+
|
86
|
+
context "should save the referral information" do
|
87
|
+
setup do
|
88
|
+
db.drop_collection 'views'
|
89
|
+
|
90
|
+
get '/', {}, 'HTTP_REFERER' => 'http://www.google.com'
|
91
|
+
end
|
92
|
+
|
93
|
+
asserts('it should have a referral key') { db['views'].find_one }.includes 'referral'
|
94
|
+
asserts('it should have a correct referral set') { db['views'].find_one['referral'] }.equals 'http://www.google.com'
|
95
|
+
end
|
96
|
+
|
97
|
+
context "should save the user agent information" do
|
98
|
+
setup do
|
99
|
+
db.drop_collection 'views'
|
100
|
+
|
101
|
+
get 'views', {}, 'HTTP_USER_AGENT' => 'Firefox'
|
102
|
+
end
|
103
|
+
|
104
|
+
asserts('it should have a user agent key') { db['views'].find_one }.includes 'user_agent'
|
105
|
+
asserts('it should have a correct user agent set') { db['views'].find_one['user_agent'] }.equals 'Firefox'
|
106
|
+
end
|
107
|
+
end
|
@@ -0,0 +1,47 @@
|
|
1
|
+
require 'teststrap'
|
2
|
+
|
3
|
+
context 'Rack::Analytics::RequestParser' do
|
4
|
+
helper(:request) { {"HTTP_HOST"=>"example.org", "SERVER_NAME"=>"example.org",
|
5
|
+
"HTTP_USER_AGENT"=>"Firefox", "CONTENT_LENGTH"=>"0", "HTTPS"=>"off",
|
6
|
+
"REMOTE_ADDR"=>"127.0.0.1", "PATH_INFO"=>"/",
|
7
|
+
"SCRIPT_NAME"=>"", "HTTP_COOKIE"=>"", "SERVER_PORT"=>"80",
|
8
|
+
"REQUEST_METHOD"=>"GET", "QUERY_STRING"=>"",
|
9
|
+
"HTTP_REFERER"=> "http://www.google.com"} }
|
10
|
+
|
11
|
+
context "should parse the default attributes correctly" do
|
12
|
+
setup { Rack::Analytics::RequestParser.new.parse(request) }
|
13
|
+
|
14
|
+
asserts('it should save the time') { topic.data['time'] }.kind_of Time
|
15
|
+
asserts('it should save the path') { topic.data['path'] }.equals '/'
|
16
|
+
asserts('it should save the user agent') { topic.data['user_agent'] }.equals 'Firefox'
|
17
|
+
asserts('it should save the referral') { topic.data['referral'] }.equals 'http://www.google.com'
|
18
|
+
end
|
19
|
+
|
20
|
+
context "should accept exceptions" do
|
21
|
+
setup { Rack::Analytics::RequestParser.new }
|
22
|
+
|
23
|
+
asserts ('it should accept single arguments') do
|
24
|
+
topic.except = 'time'
|
25
|
+
topic.parse(request).data['time']
|
26
|
+
end.nil
|
27
|
+
|
28
|
+
asserts ('it should accept multiple values as arguments') do
|
29
|
+
topic.except = ['time']
|
30
|
+
topic.parse(request).data['time']
|
31
|
+
end.nil
|
32
|
+
end
|
33
|
+
|
34
|
+
context "should handle 'only'" do
|
35
|
+
setup { Rack::Analytics::RequestParser.new }
|
36
|
+
|
37
|
+
asserts ('it should accept single arguments') do
|
38
|
+
topic.only = 'time'
|
39
|
+
topic.parse(request).data['path']
|
40
|
+
end.nil
|
41
|
+
|
42
|
+
asserts ('it should accept multiple values as arguments') do
|
43
|
+
topic.except = ['time', 'path']
|
44
|
+
topic.parse(request).data['user_agent']
|
45
|
+
end.nil
|
46
|
+
end
|
47
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
require 'sinatra'
|
2
|
+
|
3
|
+
class DummyApp < Sinatra::Application
|
4
|
+
get '/' do
|
5
|
+
'homepage'
|
6
|
+
end
|
7
|
+
|
8
|
+
get '/inner-page' do
|
9
|
+
'inner page'
|
10
|
+
end
|
11
|
+
|
12
|
+
post '/' do
|
13
|
+
'homepage with post'
|
14
|
+
end
|
15
|
+
|
16
|
+
put '/' do
|
17
|
+
'homepage with put'
|
18
|
+
end
|
19
|
+
|
20
|
+
delete '/' do
|
21
|
+
'homepage with delete'
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
module TestHelpers
|
2
|
+
def app
|
3
|
+
Rack::Builder.new do
|
4
|
+
Rack::Analytics.db = mongo
|
5
|
+
|
6
|
+
use Rack::Analytics::RequestLogger
|
7
|
+
run DummyApp
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
def mongo
|
12
|
+
connection = Mongo::Connection.new
|
13
|
+
connection.db 'rack-analytics-test'
|
14
|
+
end
|
15
|
+
end
|
data/test/teststrap.rb
ADDED
metadata
ADDED
@@ -0,0 +1,184 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: rack-analytics
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
hash: 29
|
5
|
+
prerelease: false
|
6
|
+
segments:
|
7
|
+
- 0
|
8
|
+
- 0
|
9
|
+
- 1
|
10
|
+
version: 0.0.1
|
11
|
+
platform: ruby
|
12
|
+
authors:
|
13
|
+
- "Cain\xC3\xA3 Costa"
|
14
|
+
autorequire:
|
15
|
+
bindir: bin
|
16
|
+
cert_chain: []
|
17
|
+
|
18
|
+
date: 2011-01-31 00:00:00 -02:00
|
19
|
+
default_executable:
|
20
|
+
dependencies:
|
21
|
+
- !ruby/object:Gem::Dependency
|
22
|
+
name: rack
|
23
|
+
prerelease: false
|
24
|
+
requirement: &id001 !ruby/object:Gem::Requirement
|
25
|
+
none: false
|
26
|
+
requirements:
|
27
|
+
- - ">="
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
hash: 3
|
30
|
+
segments:
|
31
|
+
- 0
|
32
|
+
version: "0"
|
33
|
+
type: :runtime
|
34
|
+
version_requirements: *id001
|
35
|
+
- !ruby/object:Gem::Dependency
|
36
|
+
name: bson_ext
|
37
|
+
prerelease: false
|
38
|
+
requirement: &id002 !ruby/object:Gem::Requirement
|
39
|
+
none: false
|
40
|
+
requirements:
|
41
|
+
- - ">="
|
42
|
+
- !ruby/object:Gem::Version
|
43
|
+
hash: 3
|
44
|
+
segments:
|
45
|
+
- 0
|
46
|
+
version: "0"
|
47
|
+
type: :runtime
|
48
|
+
version_requirements: *id002
|
49
|
+
- !ruby/object:Gem::Dependency
|
50
|
+
name: mongo
|
51
|
+
prerelease: false
|
52
|
+
requirement: &id003 !ruby/object:Gem::Requirement
|
53
|
+
none: false
|
54
|
+
requirements:
|
55
|
+
- - ">="
|
56
|
+
- !ruby/object:Gem::Version
|
57
|
+
hash: 3
|
58
|
+
segments:
|
59
|
+
- 0
|
60
|
+
version: "0"
|
61
|
+
type: :runtime
|
62
|
+
version_requirements: *id003
|
63
|
+
- !ruby/object:Gem::Dependency
|
64
|
+
name: activesupport
|
65
|
+
prerelease: false
|
66
|
+
requirement: &id004 !ruby/object:Gem::Requirement
|
67
|
+
none: false
|
68
|
+
requirements:
|
69
|
+
- - ">="
|
70
|
+
- !ruby/object:Gem::Version
|
71
|
+
hash: 3
|
72
|
+
segments:
|
73
|
+
- 0
|
74
|
+
version: "0"
|
75
|
+
type: :runtime
|
76
|
+
version_requirements: *id004
|
77
|
+
- !ruby/object:Gem::Dependency
|
78
|
+
name: riot
|
79
|
+
prerelease: false
|
80
|
+
requirement: &id005 !ruby/object:Gem::Requirement
|
81
|
+
none: false
|
82
|
+
requirements:
|
83
|
+
- - ">="
|
84
|
+
- !ruby/object:Gem::Version
|
85
|
+
hash: 3
|
86
|
+
segments:
|
87
|
+
- 0
|
88
|
+
version: "0"
|
89
|
+
type: :development
|
90
|
+
version_requirements: *id005
|
91
|
+
- !ruby/object:Gem::Dependency
|
92
|
+
name: sinatra
|
93
|
+
prerelease: false
|
94
|
+
requirement: &id006 !ruby/object:Gem::Requirement
|
95
|
+
none: false
|
96
|
+
requirements:
|
97
|
+
- - ">="
|
98
|
+
- !ruby/object:Gem::Version
|
99
|
+
hash: 3
|
100
|
+
segments:
|
101
|
+
- 0
|
102
|
+
version: "0"
|
103
|
+
type: :development
|
104
|
+
version_requirements: *id006
|
105
|
+
- !ruby/object:Gem::Dependency
|
106
|
+
name: rack-test
|
107
|
+
prerelease: false
|
108
|
+
requirement: &id007 !ruby/object:Gem::Requirement
|
109
|
+
none: false
|
110
|
+
requirements:
|
111
|
+
- - ">="
|
112
|
+
- !ruby/object:Gem::Version
|
113
|
+
hash: 3
|
114
|
+
segments:
|
115
|
+
- 0
|
116
|
+
version: "0"
|
117
|
+
type: :development
|
118
|
+
version_requirements: *id007
|
119
|
+
description: A rack middleware that collects access statistics and saves them on a MongoDB database.
|
120
|
+
email:
|
121
|
+
- cainan.costa@gmail.com
|
122
|
+
executables: []
|
123
|
+
|
124
|
+
extensions: []
|
125
|
+
|
126
|
+
extra_rdoc_files: []
|
127
|
+
|
128
|
+
files:
|
129
|
+
- .gitignore
|
130
|
+
- Gemfile
|
131
|
+
- Gemfile.lock
|
132
|
+
- README.markdown
|
133
|
+
- Rakefile
|
134
|
+
- lib/rack-analytics.rb
|
135
|
+
- lib/rack/analytics.rb
|
136
|
+
- lib/rack/analytics/request_logger.rb
|
137
|
+
- lib/rack/analytics/request_parser.rb
|
138
|
+
- lib/rack/analytics/version.rb
|
139
|
+
- rack-analytics.gemspec
|
140
|
+
- test/request_logger_test.rb
|
141
|
+
- test/request_parser_test.rb
|
142
|
+
- test/support/dummy_app.rb
|
143
|
+
- test/support/test_helpers.rb
|
144
|
+
- test/teststrap.rb
|
145
|
+
has_rdoc: true
|
146
|
+
homepage: http://rubygems.org/gems/rack-analytics
|
147
|
+
licenses: []
|
148
|
+
|
149
|
+
post_install_message:
|
150
|
+
rdoc_options: []
|
151
|
+
|
152
|
+
require_paths:
|
153
|
+
- lib
|
154
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
155
|
+
none: false
|
156
|
+
requirements:
|
157
|
+
- - ">="
|
158
|
+
- !ruby/object:Gem::Version
|
159
|
+
hash: 3
|
160
|
+
segments:
|
161
|
+
- 0
|
162
|
+
version: "0"
|
163
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
164
|
+
none: false
|
165
|
+
requirements:
|
166
|
+
- - ">="
|
167
|
+
- !ruby/object:Gem::Version
|
168
|
+
hash: 3
|
169
|
+
segments:
|
170
|
+
- 0
|
171
|
+
version: "0"
|
172
|
+
requirements: []
|
173
|
+
|
174
|
+
rubyforge_project: rack-analytics
|
175
|
+
rubygems_version: 1.3.7
|
176
|
+
signing_key:
|
177
|
+
specification_version: 3
|
178
|
+
summary: A rack middleware that collects access statistics
|
179
|
+
test_files:
|
180
|
+
- test/request_logger_test.rb
|
181
|
+
- test/request_parser_test.rb
|
182
|
+
- test/support/dummy_app.rb
|
183
|
+
- test/support/test_helpers.rb
|
184
|
+
- test/teststrap.rb
|