sliding-stats 0.2.8

Sign up to get free protection for your applications and to get access to all the features.
File without changes
@@ -0,0 +1,94 @@
1
+ # Rakefile for SlidingStats. -*-ruby-*-
2
+ # Shamelessly stolen from Rack::Contrib
3
+
4
+ require 'rake/rdoctask'
5
+ require 'rake/testtask'
6
+
7
+ desc "Run all the tests"
8
+ #task :default => [:test]
9
+
10
+ #desc "Generate RDox"
11
+ #task "RDOX" do
12
+ # sh "specrb -Ilib:test -a --rdox >RDOX"
13
+ #end
14
+
15
+ #desc "Run all the fast tests"
16
+ #task :test do
17
+ # sh "specrb -Ilib:test -w #{ENV['TEST'] || '-a'} #{ENV['TESTOPTS']}"
18
+ #end
19
+
20
+ #desc "Run all the tests"
21
+ #task :fulltest do
22
+ # sh "specrb -Ilib:test -w #{ENV['TEST'] || '-a'} #{ENV['TESTOPTS']}"
23
+ #end
24
+
25
+ desc "Generate RDoc documentation"
26
+ Rake::RDocTask.new(:rdoc) do |rdoc|
27
+ rdoc.options << '--line-numbers' << '--inline-source' <<
28
+ '--main' << 'README' <<
29
+ '--title' << "Sliding Stats Documentation" <<
30
+ '--charset' << 'utf-8'
31
+ rdoc.rdoc_dir = "doc"
32
+ rdoc.rdoc_files.include 'README.rdoc'
33
+ rdoc.rdoc_files.include 'RDOX'
34
+ rdoc.rdoc_files.include("lib/sliding-stats/*.rb")
35
+ rdoc.rdoc_files.include("lib/sliding-stats/*/*.rb")
36
+ end
37
+ task :rdoc => ["RDOX"]
38
+
39
+
40
+ # PACKAGING =================================================================
41
+
42
+ # load gemspec like github's gem builder to surface any SAFE issues.
43
+ require 'rubygems/specification'
44
+ $spec = eval(File.read("sliding-stats.gemspec"))
45
+
46
+ def package(ext='')
47
+ "pkg/sliding-stats-#{$spec.version}" + ext
48
+ end
49
+
50
+ desc 'Build packages'
51
+ task :package => %w[.gem .tar.gz].map {|e| package(e)}
52
+
53
+ desc 'Build and install as local gem'
54
+ task :install => package('.gem') do
55
+ sh "gem install #{package('.gem')}"
56
+ end
57
+
58
+ directory 'pkg/'
59
+
60
+ file package('.gem') => %w[pkg/ sliding-stats.gemspec] + $spec.files do |f|
61
+ sh "gem build sliding-stats.gemspec"
62
+ mv File.basename(f.name), f.name
63
+ end
64
+
65
+ file package('.tar.gz') => %w[pkg/] + $spec.files do |f|
66
+ sh "git archive --format=tar HEAD | gzip > #{f.name}"
67
+ end
68
+
69
+ # desc 'Publish gem and tarball to rubyforge'
70
+ # task 'publish:gem' => [package('.gem'), package('.tar.gz')] do |t|
71
+ # sh < <-end
72
+ # rubyforge add_release rack rack-contrib #{$spec.version} #{package('.gem')} &&
73
+ # rubyforge add_file rack rack-contrib #{$spec.version} #{package('.tar.gz')}
74
+ # end
75
+ # end
76
+
77
+ # GEMSPEC ===================================================================
78
+
79
+ file 'sliding-stats.gemspec' => FileList['{lib,test}/**','Rakefile', 'README.rdoc'] do |f|
80
+ # read spec file and split out manifest section
81
+ spec = File.read(f.name)
82
+ parts = spec.split(" # = MANIFEST =\n")
83
+ fail 'bad spec' if parts.length != 3
84
+ # determine file list from git ls-files
85
+ files = `git ls-files`.
86
+ split("\n").sort.reject{ |file| file =~ /^\./ }.
87
+ map{ |file| " #{file}" }.join("\n")
88
+ # piece file back together and write...
89
+ parts[1] = " s.files = %w[\n#{files}\n ]\n"
90
+ spec = parts.join(" # = MANIFEST =\n")
91
+ spec.sub!(/s.date = '.*'/, "s.date = '#{Time.now.strftime("%Y-%m-%d")}'")
92
+ File.open(f.name, 'w') { |io| io.write(spec) }
93
+ puts "updated #{f.name}"
94
+ end
@@ -0,0 +1,60 @@
1
+
2
+ # This demonstrates how to configure the stats and generates an SVG of 1000 requests by page.
3
+ # The exclusion patterns are geared towards my website, and so you'd want to adapt them.
4
+
5
+ require 'sliding-stats'
6
+
7
+ opts = {
8
+ # The number of requests that is considered
9
+ :limit => 1000,
10
+
11
+ # If set to an integer, the number of requests between each time the data is persisted
12
+ # (using Marshal) to /var/tmp/slidingstats. You can provide a path by passing
13
+ # SlidingStats::Persist.new(number, path) instead, or you can provide any class that
14
+ # provides a #load and #save method -- see SlidingStats::Persist
15
+ :persist => nil,
16
+
17
+ # Pages where either the request or referrer match :ignore is not processed further,
18
+ # and doesn't count towards :limit
19
+ :ignore => [
20
+ /\.xml/, /\/feed/, /\.rdf/, /\.ico/, /\/static\//,/\/robots.txt/
21
+ /\/referers/, /\/stats.*/,
22
+ /http:\/\/search.live.com\/results.aspx/, # MSN referer spam
23
+ ],
24
+
25
+ # Exclude entries from the referer graph and the referer to pages table
26
+ :exclude_referers => [
27
+ /http:\/\/www\.hokstad\.com/, # Not interested in seeing internal clicks
28
+ /^-/ # Direct traffic.
29
+ ],
30
+
31
+ # Exclude entries from the page graph and referer to pages table.
32
+ :exclude_pages => [
33
+ ],
34
+
35
+ # Rewrite referrer entries to make them more friendly, and group together
36
+ # referrers that don't have exactly the same URL
37
+ :rewrite_referers =>
38
+ [
39
+ [/http:\/\/.*\.google\..*?[?&]q=([^&]*)?&*.*/,"Google Search: '\\1'"],
40
+ [/http:\/\/www.google..*\/reader.*/,"Google Reader"]
41
+ ]
42
+ }
43
+
44
+ view = SlidingStats::Controller.new(nil,"/stats")
45
+ window = SlidingStats::Window.new(view, opts)
46
+
47
+ # First we feed it stats from STDIN:
48
+
49
+ STDIN.each do |line|
50
+ line = line.split(" ")
51
+ window.call({"REQUEST_URI" => line[6],
52
+ "HTTP_REFERER" => line[10][1..-2]})
53
+ end
54
+
55
+ # Then we fake a stats request:
56
+
57
+ window.call({"REQUEST_URI" => "/stats/pages.svg"}).each do |line|
58
+ puts line
59
+ end
60
+
@@ -0,0 +1,34 @@
1
+
2
+ Feature: Maintain stats
3
+ In order to keep an eye on traffic the
4
+ Stats Module must keep track of
5
+ referrers and pages at all times.
6
+
7
+ Scenario Outline: Adding requests
8
+ Given there are <start> requests in the stats
9
+ When I add <requests> requests that are not excluded
10
+ Then there should be <total> pageviews
11
+ And there should be <total> referrers
12
+
13
+ Examples:
14
+ | start | requests | total |
15
+ | 5 | 1 | 6 |
16
+ | 10 | 23 | 33 |
17
+ | 0 | 5 | 5 |
18
+
19
+ Scenario Outline: Removing requests
20
+ Given there are <start> requests in the stats
21
+ When I remove <requests> requests that are not excluded
22
+ Then there should be <total> pageviews
23
+ And there should be <total> referrers
24
+ And there should be no pages rows with value 0
25
+ And there should be no referers rows with value 0
26
+ And there should be no rows with 0 in referers_to_pages
27
+
28
+ Examples:
29
+ | start | requests | total |
30
+ | 0 | 1 | 0 |
31
+ | 1 | 1 | 0 |
32
+ | 3 | 2 | 1 |
33
+ | 3 | 5 | 0 |
34
+
@@ -0,0 +1,40 @@
1
+ $: << File.expand_path(File.dirname(__FILE__)+"/../../lib/")
2
+ require 'sliding-stats'
3
+ require 'spec/expectations'
4
+
5
+ def valid_request
6
+ {"HTTP_REFERER" => "valid_referer",
7
+ "REQUEST_URI" => "valid_uri"}
8
+ end
9
+
10
+ Given /^there are (\d+) requests in the stats$/ do |n|
11
+ r = []
12
+ n.to_i.times { r << valid_request }
13
+ @stats = SlidingStats::Stats.new(r,{},{})
14
+ end
15
+
16
+ When /^I add (\d+) request[s]? that are not excluded$/ do |n|
17
+ n.to_i.times { @stats.add(valid_request) }
18
+ end
19
+
20
+ When /^I remove (\d+) request[s]? that are not excluded$/ do |n|
21
+ n.to_i.times { @stats.sub(valid_request) }
22
+ end
23
+
24
+ Then /^there should be (\d+) pageview[s]?$/ do |n|
25
+ @stats.pages.to_a.inject(0) {|s,a| s+a[1]}.should == n.to_i
26
+ end
27
+
28
+ Then /^there should be (\d+) referrer[s]?$/ do |n|
29
+ @stats.referers.to_a.inject(0) {|s,a| s+a[1]}.should == n.to_i
30
+ end
31
+
32
+ Then /^there should be no (\w+) rows with value (\d+)$/ do |r,n|
33
+ @stats.send(r.to_sym).to_a.detect {|k,v| v == n.to_i }.should == nil
34
+ end
35
+
36
+ Then /^there should be no rows with 0 in referers_to_pages$/ do
37
+ @stats.referers_to_pages.each do |r|
38
+ r[1].to_a.detect {|k,v| v == 0}.should == nil
39
+ end
40
+ end
@@ -0,0 +1,24 @@
1
+ $: << File.expand_path(File.dirname(__FILE__)+"/../../lib/")
2
+ require 'sliding-stats'
3
+
4
+ def valid_request
5
+ {"HTTP_REFERER" => "valid_referer",
6
+ "REQUEST_URI" => "valid_uri"}
7
+ end
8
+
9
+ Given /^there is a limit of (\d+) requests in the window$/ do |n|
10
+ @window = SlidingStats::Window.new(Proc.new {},
11
+ {:limit => n})
12
+ end
13
+
14
+ When /^I add (\d+) request[s]? that are not excluded to the window$/ do |n|
15
+ end
16
+
17
+ Then /^there should be (\d+) pageview[s]? in the window$/ do |n|
18
+ @window.stats.pages.to_a.inject(0) {|s,a| s+a[1]} == n.to_i
19
+ end
20
+
21
+ Then /^there should be (\d+) referrer[s]? in the window$/ do |n|
22
+ @window.stats.referers.to_a.inject(0) {|s,a| s+a[1]} == n.to_i
23
+ end
24
+
@@ -0,0 +1,19 @@
1
+
2
+ Feature: Maintain a sliding window
3
+ In order to keep an eye on traffic the
4
+ Window class must maintain a sliding window
5
+ with an upper size limit at all times.
6
+
7
+ Scenario Outline: Adding requests
8
+ Given there is a limit of <limit> requests in the window
9
+ When I add <requests> requests that are not excluded to the window
10
+ Then there should be <total> pageviews in the window
11
+ And there should be <total> referrers in the window
12
+
13
+ Examples:
14
+ | limit | requests | total |
15
+ | 0 | 1 | 0 |
16
+ | 5 | 3 | 3 |
17
+ | 5 | 5 | 5 |
18
+ | 5 | 10 | 5 |
19
+
@@ -0,0 +1,6 @@
1
+
2
+ require 'sliding-stats/stats'
3
+ require 'sliding-stats/window'
4
+ require 'sliding-stats/view'
5
+ require 'sliding-stats/controller'
6
+ require 'sliding-stats/persist'
@@ -0,0 +1,38 @@
1
+
2
+ require 'rack'
3
+
4
+ module SlidingStats
5
+
6
+ class Controller
7
+ def initialize app, opts
8
+ @app = app
9
+ @base = opts[:base] || "/stats"
10
+ @view = opts[:view] || View.new
11
+ @max_entries = opts[:max_entries] || 100
12
+ end
13
+
14
+ def call env
15
+ return Rack::Response.new("Missing 'slidingstats' object -- did you forget to set up SlidingStats::Window before SlidingStats::Controller ? ").finish if !env["slidingstats"]
16
+
17
+ uri = env["REQUEST_URI"]
18
+ @window = env["slidingstats"]
19
+
20
+ case uri
21
+ when @base
22
+ r_to_p = @window.stats.referers_to_pages.sort_by{|k,v| -v[:total]}[0..@max_entries-1]
23
+ referers = @window.stats.referers.sort_by{|k,v| -v}[0..@max_entries-1]
24
+ pages = @window.stats.pages.sort_by{|k,v| -v}[0..@max_entries-1]
25
+ return @view.show({:referers => referers, :pages => pages, :referers_to_pages => r_to_p, :base => @base})
26
+ when @base+"/referers.svg"
27
+ data = @window.stats.referers.sort_by{|k,v| -v}[0..@max_entries-1]
28
+ return @view.show_svg(data)
29
+ when @base+"/pages.svg"
30
+ data = @window.stats.pages.sort_by{|k,v| -v}[0..@max_entries-1]
31
+ return @view.show_svg(data)
32
+ else
33
+ return @app.call(env) if @app
34
+ return Rack::Response.new("(empty)").finish
35
+ end
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,33 @@
1
+
2
+
3
+ module SlidingStats
4
+
5
+ # This class provides basic persistence for SlidingStats
6
+ # To use it, simply add add :persist => [number of requests
7
+ # between saves] to the SlidingStats::Window options,
8
+ # or pass a different persistence class.
9
+ class Persist
10
+ def initialize every = 10,path="/var/tmp/slidingstats"
11
+ @every = every
12
+ @num = 0
13
+ @path = path
14
+ end
15
+
16
+ def load
17
+ begin
18
+ Marshal.load(File.read(@path))
19
+ rescue
20
+ []
21
+ end
22
+ end
23
+
24
+ def save requests
25
+ @num += 1
26
+ if (@num % @every) == 0
27
+ File.open(@path,"w") do |f|
28
+ f.write(Marshal.dump(requests))
29
+ end
30
+ end
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,72 @@
1
+
2
+ module SlidingStats
3
+
4
+ # Calculates and maintains stats for a set of
5
+ # requests.
6
+ class Stats
7
+ attr_reader :referers, :pages, :referers_to_pages
8
+ def initialize request,ex_referers,ex_pages
9
+ @exclude_referers = ex_referers || []
10
+ @exclude_pages = ex_pages || []
11
+
12
+ @referers = {}
13
+ @pages = {}
14
+ @referers_to_pages = {} # Two level
15
+
16
+ request.each { |r| self.add(r) }
17
+ end
18
+
19
+ # Add a single line of stats data
20
+ def add r
21
+ ref = r["HTTP_REFERER"]
22
+ req = r["REQUEST_URI"]
23
+
24
+ ex_ref = @exclude_referers.detect{|pat| ref =~ pat}
25
+ ex_req = @exclude_pages.detect{|pat| req =~ pat}
26
+
27
+ if !ex_ref
28
+ @referers[ref] ||= 0
29
+ @referers[ref] += 1
30
+ end
31
+
32
+ if !ex_req
33
+ @pages[req] ||= 0
34
+ @pages[req] += 1
35
+ end
36
+
37
+ if !ex_ref && !ex_req
38
+ @referers_to_pages[ref] ||= {:total => 0}
39
+ @referers_to_pages[ref][req] ||= 0
40
+ @referers_to_pages[ref][req] += 1
41
+ @referers_to_pages[ref][:total] += 1
42
+ end
43
+ end
44
+
45
+ def sub r
46
+ ref = r["HTTP_REFERER"]
47
+ req = r["REQUEST_URI"]
48
+
49
+ ex_ref = @exclude_referers.detect{|pat| ref =~ pat}
50
+ ex_req = @exclude_pages.detect{|pat| req =~ pat}
51
+
52
+ if !ex_ref && @referers[ref]
53
+ @referers[ref] -= 1
54
+ @referers.delete(ref) if @referers[ref] <= 0
55
+ end
56
+
57
+ if !ex_req && @pages[req]
58
+ @pages[req] -= 1
59
+ @pages.delete(req) if @pages[req] <= 0
60
+ end
61
+
62
+ if !ex_ref && !ex_req && @referers_to_pages[ref]
63
+ if @referers_to_pages[ref][req]
64
+ @referers_to_pages[ref][req] -= 1
65
+ @referers_to_pages[ref].delete(req) if @referers_to_pages[ref][req] <= 0
66
+ end
67
+ @referers_to_pages[ref][:total] -= 1
68
+ @referers_to_pages.delete(ref) if @referers_to_pages[ref][:total] <= 0
69
+ end
70
+ end
71
+ end
72
+ end
@@ -0,0 +1,90 @@
1
+ require 'svg_graph'
2
+ require 'rack'
3
+
4
+ module SlidingStats
5
+
6
+ # Provides a basic view of the stats. You can easily provide a custom
7
+ # view by subclassing and overriding the #show method, or replacing it
8
+ # completely.
9
+ class View
10
+ FOOTER = <<-end_footer
11
+ </table>
12
+ <div style='margin-top: 50px'>Stats by <a href='http://www.hokstad.com/slidingstats'>Sliding Stats</a> -- Copyright 2009 <a href='http://www.hokstad.com/'>Vidar Hokstad</a>. </div>
13
+ </body></html>
14
+ end_footer
15
+
16
+ CSS = <<-end_css
17
+ h1, h2 { font-family: 'Lucida Sans Unicode', 'Lucida Grande', sans-serif; }
18
+ h2 { margin-top: 20px;}
19
+
20
+ table { display: inline; margin-top: 20px; margin-left: 100px; width: 90%; border: outset 1px grey;
21
+ background: #aaaaff; padding: 0px; align: left; text-align: left;
22
+ }
23
+ table.breakdown { background: #ccccff; margin-top: 1px; width: 100%; margin-left: 0px; padding: 5px; }
24
+ table.breakdown td.count { width: 40px; }
25
+ td.name { width: 50%; }
26
+ tr.odd { background: #aaaaff; }
27
+ tr.even { background: #bbbbff; }
28
+ end_css
29
+
30
+ def show(data)
31
+ r = Rack::Response.new
32
+ r.write("<html><head><title>Sliding Stats</title><style>" + CSS + "</style> <body>")
33
+ r.write("<h1>Sliding Stats</h1>")
34
+ # Setting the size here is a *hack*. Need to fix that
35
+ r.write("<h2>Most recent referrers</h2>")
36
+ r.write("<div style='width: 1000px;'><embed pluginspage=\"http://www.adobe.com/svg/viewer/install/\" type=\"image/svg+xml\" src=\"#{data[:base]}/referers.svg\" style=\"margin-left: 50px; width: 1000px; height: #{40 + 20*data[:referers].size}px;\"></div>")
37
+ r.write("<h2>Most recent pages</h2>")
38
+ r.write("<div style='width: 1000px;'><embed pluginspage=\"http://www.adobe.com/svg/viewer/install/\" type=\"image/svg+xml\" src=\"#{data[:base]}/pages.svg\" style=\"margin-left: 50px; width: 1000px; height: #{40 + 20*data[:pages].size}px;\"></div>")
39
+ r.write("<h2>Most recent referrers broken down by pages</h2>")
40
+ r.write("<table><tr><th>Referer</th><th>Pages</th></tr>\n")
41
+ odd = true
42
+ data[:referers_to_pages].each do |k,v|
43
+ k = k[0..79] + "..." if k.length > 80
44
+ r.write("<tr class='#{odd ? 'odd':'even'}'><td class='name'>#{CGI.escapeHTML(k)}</td> <td><table class='breakdown'>")
45
+ total = v[:total]
46
+ if v.size > 2 # include :total
47
+ r.write("<tr><td class='count'>#{total}</td><td><strong>total</strong></td></tr>")
48
+ end
49
+ v.sort_by{|page,count| -count}.each do |page,count|
50
+ r.write("<tr><td class='count'>#{count}</td><td>#{page.to_s}</td></tr>") if page != :total
51
+ end
52
+ r.write("</table></td></tr>\n")
53
+ odd = !odd
54
+ end
55
+ r.write(FOOTER)
56
+ r.finish
57
+ end
58
+
59
+ def show_svg(src)
60
+ fields = []
61
+ data = []
62
+ src.each do |k,v|
63
+ if k != "-" # Excluding because of referers
64
+ k = k[0..79] + "..." if k.length > 80
65
+ fields << CGI.escapeHTML(k)
66
+ data << v
67
+ end
68
+ end
69
+
70
+ if fields.empty?
71
+ r = Rack::Response.new("No data")
72
+ return r.finish
73
+ end
74
+
75
+ graph = SVG::Graph::BarHorizontal.new(
76
+ :height => 40 + 20 * data.size,
77
+ :width => 1000,
78
+ :fields => fields.reverse
79
+ )
80
+ graph.add_data(:data => data.reverse)
81
+ graph.rotate_y_labels = false
82
+ graph.scale_integers = true
83
+ graph.key = false
84
+ r = Rack::Response.new
85
+ r["Content-Type"] = "image/svg+xml"
86
+ r.write(graph.burn)
87
+ r.finish
88
+ end
89
+ end
90
+ end
@@ -0,0 +1,81 @@
1
+
2
+ require 'sliding-stats/stats'
3
+ require 'sliding-stats/persist'
4
+ require 'cgi'
5
+
6
+ module SlidingStats
7
+ DEFAULT_WINDOW = 500
8
+
9
+ # Provides a "sliding window" over the stats. You provide
10
+ # a limit, and then feeds data into it. When the number of
11
+ # lines of data exceeds the limit, the oldest gets removed.
12
+ #
13
+ # The actual stats calculation is handled by the Stats class
14
+ #
15
+ # At any point you can extract stats from from the current
16
+ # window.
17
+ #
18
+ # The following options can be passed in the opts argument:
19
+ # * :limit => the number of stats lines to keep
20
+ # * :ignore => Requests where this matches *either* the referer *or* the request
21
+ # *or* the user agent will not be considered at all.
22
+ # * :request_methods => Array of HTTP methods to track. Defaults to :get
23
+ # as POST, PUT etc. on "normal" sites rarely happen
24
+ # on inbound referrals, and so we'd be likely to overcount
25
+ # access to a specific page
26
+ # * :exclude_[referers|pages] => Arrays that will be matched against
27
+ # REQUEST_URI and HTTP_REFERER to decide
28
+ # whether or not to exclude this request from
29
+ # the appropriate stats.
30
+ # * :rewrite_referer =>
31
+ # An Array of arrays consisting of regexps
32
+ # and a rewrite pattern to filter the
33
+ # HTTP_REFERER against
34
+ class Window
35
+ attr_reader :stats
36
+
37
+ def initialize app, opts = {}
38
+ @app = app
39
+ @limit = (opts[:limit] || DEFAULT_WINDOW).to_i
40
+ @exclude_referers = opts[:exclude_referers] || []
41
+ @rewrite_referers = opts[:rewrite_referers] || []
42
+ @request_methods = opts[:request_methods] || [:get]
43
+ @exclude_pages = opts[:exclude_pages] || []
44
+ @ignore = opts[:ignore] || []
45
+ @persist = opts[:persist]
46
+
47
+ @requests = []
48
+ if @persist.is_a?(Numeric)
49
+ @persist = SlidingStats::Persist.new(@persist)
50
+ @requests = @persist.load
51
+ end
52
+ @stats = Stats.new(@requests,@exclude_referers,@exclude_pages)
53
+ end
54
+
55
+ def call env
56
+ ref = env["HTTP_REFERER"] || "-"
57
+ req = env["REQUEST_URI"]
58
+ ua = env["HTTP_USER_AGENT"]
59
+ req_meth = env["REQUEST_METHOD"].downcase.to_sym
60
+
61
+ if @request_methods.include?(req_meth) && !@ignore.detect{|pat| ref =~ pat || req =~ pat || ua =~ pat}
62
+ newref = @rewrite_referers.inject(ref) { |nr,r| nr.gsub(r[0],r[1]) }
63
+ ref = CGI.unescape(newref) if newref != ref
64
+
65
+ stats = {
66
+ "HTTP_REFERER" => ref,
67
+ "REQUEST_URI" => req
68
+ }
69
+ @requests << stats
70
+ @stats.add(stats)
71
+ while @requests.size > @limit
72
+ @stats.sub(@requests.shift)
73
+ end
74
+ @persist.save(@requests) if @persist
75
+ end
76
+
77
+ env["slidingstats"] = self
78
+ @app.call(env)
79
+ end
80
+ end
81
+ end
@@ -0,0 +1,47 @@
1
+
2
+ Gem::Specification.new do |s|
3
+ s.specification_version = 2 if s.respond_to? :specification_version=
4
+ s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
5
+
6
+ s.name = 'sliding-stats'
7
+ s.version = '0.2.8'
8
+ s.date = '2011-10-14'
9
+
10
+ s.description = "Rack Middleware to provide a 'sliding view' over the last N requests to your web app"
11
+ s.summary = s.description
12
+
13
+ s.authors = ["vidarh"]
14
+ s.email = "vidar@hokstad.com"
15
+
16
+ # = MANIFEST =
17
+ s.files = %w[
18
+ README.rdoc
19
+ Rakefile
20
+ example/test.rb
21
+ features/stats.feature
22
+ features/step_definitions/stats_steps.rb
23
+ features/step_definitions/window_steps.rb
24
+ features/window.feature
25
+ lib/sliding-stats.rb
26
+ lib/sliding-stats/controller.rb
27
+ lib/sliding-stats/persist.rb
28
+ lib/sliding-stats/stats.rb
29
+ lib/sliding-stats/view.rb
30
+ lib/sliding-stats/window.rb
31
+ sliding-stats.gemspec
32
+ ]
33
+ # = MANIFEST =
34
+
35
+ s.test_files = s.files.select {|path| path =~ /^test\/spec_.*\.rb/}
36
+
37
+ s.extra_rdoc_files = %w[]
38
+ s.add_dependency 'rack', '>= 0.9.1'
39
+ s.add_dependency 'svg_graph', '>= 0.7'
40
+ #s.add_development_dependency 'json', '>= 1.1'
41
+
42
+ s.has_rdoc = true
43
+ s.homepage = "http://www.hokstad.com/slidingstats"
44
+ s.rdoc_options = ["--line-numbers", "--inline-source", "--title", "slidingstats", "--main", "README.rdoc"]
45
+ s.require_paths = %w[lib]
46
+ s.rubygems_version = '1.1.1'
47
+ end
metadata ADDED
@@ -0,0 +1,113 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: sliding-stats
3
+ version: !ruby/object:Gem::Version
4
+ hash: 7
5
+ prerelease:
6
+ segments:
7
+ - 0
8
+ - 2
9
+ - 8
10
+ version: 0.2.8
11
+ platform: ruby
12
+ authors:
13
+ - vidarh
14
+ autorequire:
15
+ bindir: bin
16
+ cert_chain: []
17
+
18
+ date: 2011-10-14 00:00:00 Z
19
+ dependencies:
20
+ - !ruby/object:Gem::Dependency
21
+ name: rack
22
+ prerelease: false
23
+ requirement: &id001 !ruby/object:Gem::Requirement
24
+ none: false
25
+ requirements:
26
+ - - ">="
27
+ - !ruby/object:Gem::Version
28
+ hash: 57
29
+ segments:
30
+ - 0
31
+ - 9
32
+ - 1
33
+ version: 0.9.1
34
+ type: :runtime
35
+ version_requirements: *id001
36
+ - !ruby/object:Gem::Dependency
37
+ name: svg_graph
38
+ prerelease: false
39
+ requirement: &id002 !ruby/object:Gem::Requirement
40
+ none: false
41
+ requirements:
42
+ - - ">="
43
+ - !ruby/object:Gem::Version
44
+ hash: 5
45
+ segments:
46
+ - 0
47
+ - 7
48
+ version: "0.7"
49
+ type: :runtime
50
+ version_requirements: *id002
51
+ description: Rack Middleware to provide a 'sliding view' over the last N requests to your web app
52
+ email: vidar@hokstad.com
53
+ executables: []
54
+
55
+ extensions: []
56
+
57
+ extra_rdoc_files: []
58
+
59
+ files:
60
+ - README.rdoc
61
+ - Rakefile
62
+ - example/test.rb
63
+ - features/stats.feature
64
+ - features/step_definitions/stats_steps.rb
65
+ - features/step_definitions/window_steps.rb
66
+ - features/window.feature
67
+ - lib/sliding-stats.rb
68
+ - lib/sliding-stats/controller.rb
69
+ - lib/sliding-stats/persist.rb
70
+ - lib/sliding-stats/stats.rb
71
+ - lib/sliding-stats/view.rb
72
+ - lib/sliding-stats/window.rb
73
+ - sliding-stats.gemspec
74
+ homepage: http://www.hokstad.com/slidingstats
75
+ licenses: []
76
+
77
+ post_install_message:
78
+ rdoc_options:
79
+ - --line-numbers
80
+ - --inline-source
81
+ - --title
82
+ - slidingstats
83
+ - --main
84
+ - README.rdoc
85
+ require_paths:
86
+ - lib
87
+ required_ruby_version: !ruby/object:Gem::Requirement
88
+ none: false
89
+ requirements:
90
+ - - ">="
91
+ - !ruby/object:Gem::Version
92
+ hash: 3
93
+ segments:
94
+ - 0
95
+ version: "0"
96
+ required_rubygems_version: !ruby/object:Gem::Requirement
97
+ none: false
98
+ requirements:
99
+ - - ">="
100
+ - !ruby/object:Gem::Version
101
+ hash: 3
102
+ segments:
103
+ - 0
104
+ version: "0"
105
+ requirements: []
106
+
107
+ rubyforge_project:
108
+ rubygems_version: 1.8.6
109
+ signing_key:
110
+ specification_version: 2
111
+ summary: Rack Middleware to provide a 'sliding view' over the last N requests to your web app
112
+ test_files: []
113
+