rails_requests 0.1.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
+ SHA256:
3
+ metadata.gz: 6f848e6c17d52d814b96b2e76339d628be9054114d3b9b749ede9890236cd89b
4
+ data.tar.gz: bf2d49e8bb6552c64cba90124d6caf28b53223af3f7125f28540effc60c55ca5
5
+ SHA512:
6
+ metadata.gz: 1715b117aa52d3fe59d394483ae45d72aa90558dae6f1708f7cdbff8d2e5f272acf93685a1be3e7e382c521399116dec4b13b87eae77748fa346c344cc094070
7
+ data.tar.gz: 61910fc724f9b0e216847a40f4087df8e9aed1f1adbb0fc8c36087654ccfd8dd22cf22040c784b7fdfd0a1099e3cf2a6090b49100f8d8b144555ab4c980233ec
data/MIT-LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright 2020 Igor Kasyanchuk
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,43 @@
1
+ # RailsRequests
2
+
3
+ Short description and motivation.
4
+
5
+ ## Usage
6
+
7
+ Create `config/initializers/rails_requests.rb`
8
+
9
+ ```ruby
10
+ RailsRequests.setup do |config|
11
+ config.redis = Redis.new
12
+ config.days = 3
13
+ end
14
+ ```
15
+
16
+ ## Installation
17
+ Add this line to your application's Gemfile:
18
+
19
+ ```ruby
20
+ gem 'rails_requests'
21
+ ```
22
+
23
+ And then execute:
24
+ ```bash
25
+ $ bundle
26
+ ```
27
+
28
+ ## TODO
29
+
30
+ - time/zone
31
+ - redis namespaces
32
+ - skip for tests ?
33
+ - documentation
34
+ - add 1 if 0 (it means one request was)
35
+ - better hint
36
+
37
+ ## Contributing
38
+
39
+ You are welcome to contribute.
40
+
41
+ ## License
42
+
43
+ The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
data/Rakefile ADDED
@@ -0,0 +1,32 @@
1
+ begin
2
+ require 'bundler/setup'
3
+ rescue LoadError
4
+ puts 'You must `gem install bundler` and `bundle install` to run rake tasks'
5
+ end
6
+
7
+ require 'rdoc/task'
8
+
9
+ RDoc::Task.new(:rdoc) do |rdoc|
10
+ rdoc.rdoc_dir = 'rdoc'
11
+ rdoc.title = 'RailsRequests'
12
+ rdoc.options << '--line-numbers'
13
+ rdoc.rdoc_files.include('README.md')
14
+ rdoc.rdoc_files.include('lib/**/*.rb')
15
+ end
16
+
17
+ APP_RAKEFILE = File.expand_path("test/dummy/Rakefile", __dir__)
18
+ load 'rails/tasks/engine.rake'
19
+
20
+ load 'rails/tasks/statistics.rake'
21
+
22
+ require 'bundler/gem_tasks'
23
+
24
+ require 'rake/testtask'
25
+
26
+ Rake::TestTask.new(:test) do |t|
27
+ t.libs << 'test'
28
+ t.pattern = 'test/**/*_test.rb'
29
+ t.verbose = false
30
+ end
31
+
32
+ task default: :test
File without changes
@@ -0,0 +1,9 @@
1
+ class RailsRequestsController < ActionController::Base
2
+
3
+ def index
4
+ @data = RailsRequests.dump
5
+ @global = RailsRequests::GlobalReport.new.data(:controller_action)
6
+ #@full = RailsRequests::FullReport.new.data(:controller_action).sort{|a, b| b[:count] <=> a[:count]}
7
+ end
8
+
9
+ end
@@ -0,0 +1,106 @@
1
+ <link href="https://fonts.googleapis.com/css?family=Open+Sans&display=swap" rel="stylesheet">
2
+
3
+ <style>
4
+ body {
5
+ font-family: OpenSans, sans-serif;
6
+ margin: 0;
7
+ font-size: .8125rem;
8
+ font-weight: 400;
9
+ line-height: 1.5;
10
+ color: #a6b0cf;
11
+ text-align: left;
12
+ background-color: #282e38;
13
+ background: linear-gradient(45deg, #282e38, #382e38);
14
+ display: flex;
15
+ width: 100%;
16
+ flex-direction: column;
17
+ align-items: center;
18
+ }
19
+
20
+ #chart {
21
+ width: 100%;
22
+ min-height: 600px;
23
+ }
24
+
25
+ .card {
26
+ min-width: 90%;
27
+ padding: 1em 2em;
28
+ background-color: #323a46;
29
+ background-clip: border-box;
30
+ border: 0 solid #32394e;
31
+ border-radius: .25rem;
32
+ box-shadow: 0 0.75rem 6rem rgba(56,65,74,.03);
33
+ }
34
+
35
+ h1 {
36
+ text-align: center;
37
+ color: #f6f6f6;
38
+ font-size: 14px;
39
+ margin: 0 0 7px 0;
40
+ padding-top: 14px;
41
+ padding-bottom: 14px;
42
+ }
43
+
44
+ p.hint {
45
+ font-size: 10px;
46
+ text-align: center;
47
+ color: #aaa;
48
+ }
49
+
50
+ table {
51
+ border-collapse: collapse;
52
+ border-radius: 10px;
53
+ overflow: hidden;
54
+ width: 100%;
55
+ margin: 0 auto;
56
+ position: relative;
57
+ border-spacing: 1;
58
+ }
59
+ table * {
60
+ position: relative;
61
+ }
62
+ table td, table th {
63
+ padding-left: 8px;
64
+ }
65
+ table thead tr {
66
+ height: 60px;
67
+ }
68
+ table tbody tr {
69
+ height: 50px;
70
+ }
71
+ table tbody tr:last-child {
72
+ border: 0;
73
+ }
74
+ table td, table th {
75
+ text-align: left;
76
+ }
77
+ table td.l, table th.l {
78
+ text-align: right;
79
+ }
80
+ table td.c, table th.c {
81
+ text-align: center;
82
+ }
83
+ table td.r, table th.r {
84
+ text-align: center;
85
+ }
86
+
87
+ th {
88
+ font-size: 18px;
89
+ color: #f5f5f5;
90
+ line-height: 1.2;
91
+ font-weight: unset;
92
+ }
93
+
94
+ tbody tr {
95
+ font-size: 15px;
96
+ color: #f5f5f5;
97
+ line-height: 1.2;
98
+ font-weight: unset;
99
+ }
100
+
101
+ tbody tr:hover {
102
+ color: white;
103
+ background-color: #282e38;
104
+ }
105
+
106
+ </style>
@@ -0,0 +1,5 @@
1
+ <%= javascript_include_tag 'https://code.highcharts.com/highcharts.js' %>
2
+ <%= javascript_include_tag 'https://code.highcharts.com/modules/data.js' %>
3
+ <%= javascript_include_tag 'https://code.highcharts.com/modules/exporting.js' %>
4
+ <%= javascript_include_tag 'https://code.highcharts.com/modules/export-data.js' %>
5
+ <%= javascript_include_tag 'https://code.highcharts.com/modules/accessibility.js' %>
@@ -0,0 +1,107 @@
1
+ <%= render '/rails_requests/js' %>
2
+ <%= render '/rails_requests/css' %>
3
+
4
+ <title>Number of Requests to the Application</title>
5
+
6
+ <br/>
7
+ <br/>
8
+ <br/>
9
+
10
+ <div class="card">
11
+ <h1>Number of Requests to the Application<h1>
12
+ <div id="chart"></div>
13
+ <p class="hint">All requests (site visitors, search engines, bots, etc)</p>
14
+ </div>
15
+
16
+ <br/>
17
+ <br/>
18
+ <br/>
19
+
20
+ <div class="card">
21
+ <h1>Requests (path, total number, average response time)<h1>
22
+ <table>
23
+ <thead>
24
+ <tr>
25
+ <th></th>
26
+ <th>Requests</th>
27
+ <th>Average (ms)</th>
28
+ <th>Slowest (ms)</th>
29
+ </tr>
30
+ </thead>
31
+ <tbody>
32
+ <% @global.each do |e| %>
33
+ <tr>
34
+ <td><%= e[:group] %></td>
35
+ <td><%= e[:count] %></td>
36
+ <td><%= e[:average].round(2) %></td>
37
+ <td><%= e[:slowest].round(2) %></td>
38
+ </tr>
39
+ <% end %>
40
+ </tbody>
41
+ </table>
42
+ </div>
43
+
44
+ <br/>
45
+ <br/>
46
+ <br/>
47
+
48
+
49
+ <script>
50
+ var data = <%= raw @data.to_json %>;
51
+
52
+ Highcharts.chart('chart', {
53
+ chart: {
54
+ type: 'column',
55
+ zoomType: 'x',
56
+ backgroundColor: '#323a46'
57
+ },
58
+ title: {
59
+ text: ''
60
+ },
61
+ xAxis: {
62
+ type: 'datetime',
63
+ labels: {
64
+ style: {
65
+ color: "#a6b0cf"
66
+ }
67
+ }
68
+ },
69
+ yAxis: {
70
+ min: 0,
71
+ title: {
72
+ text: 'RPM',
73
+ style: {
74
+ color: "#f6f6f6"
75
+ }
76
+ },
77
+ labels: {
78
+ style: {
79
+ color: "#a6b0cf"
80
+ }
81
+ }
82
+ },
83
+ legend: {
84
+ enabled: false
85
+ },
86
+ exporting: {
87
+ buttons: {
88
+ contextButton: {
89
+ theme: {
90
+ fill: "#a6b0cf"
91
+ }
92
+ }
93
+ }
94
+ },
95
+ plotOptions: {
96
+ column: {
97
+ color: '#ff5b5b',
98
+ }
99
+ },
100
+ series: [{
101
+ type: 'column',
102
+ name: 'Requests per minute',
103
+ data: data
104
+ }]
105
+ });
106
+
107
+ </script>
data/config/routes.rb ADDED
@@ -0,0 +1,7 @@
1
+ RailsRequests::Engine.routes.draw do
2
+ get '/' => 'rails_requests#index', as: :rails_requests
3
+ end
4
+
5
+ Rails.application.routes.draw do
6
+ mount_routes
7
+ end
@@ -0,0 +1,19 @@
1
+ module RailsRequests
2
+ class BaseReport
3
+ def collect(group)
4
+ keys = RailsRequests.redis.keys("*#{Date.current.strftime("%Y%m%d")}*|*total_duration")
5
+ values = RailsRequests.redis.mget(keys)
6
+
7
+ @collection = RailsRequests::Collection.new
8
+
9
+ keys.each_with_index do |key, index|
10
+ @collection.data << RailsRequests::Record.new(key, values[index])
11
+ end
12
+
13
+ @collection.group_by(group).values.inject([]) do |res, (k,v)|
14
+ res << yield(k, v)
15
+ res
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,34 @@
1
+ module RailsRequests
2
+ class Collection
3
+ attr_reader :data
4
+
5
+ def initialize
6
+ @data = []
7
+ end
8
+
9
+ def group_by(type)
10
+ @data = case type
11
+ when :controller_action
12
+ data.group_by(&:controller_action)
13
+ when :path
14
+ data.group_by(&:path)
15
+ else
16
+ []
17
+ end
18
+ self
19
+ end
20
+
21
+ def values
22
+ return [] if @data.empty?
23
+ result = {}
24
+ @data.each do |key, records|
25
+ result[key] ||= []
26
+ records.each do |record|
27
+ result[key] << record.value
28
+ end
29
+ end
30
+ result
31
+ end
32
+
33
+ end
34
+ end
@@ -0,0 +1,18 @@
1
+ require_relative './middleware.rb'
2
+ require_relative './collection.rb'
3
+ require_relative './metrics.rb'
4
+
5
+ module RailsRequests
6
+ class Engine < ::Rails::Engine
7
+
8
+ config.app_middleware.use Middleware
9
+
10
+ initializer :configure_metrics, after: :initialize_logger do
11
+ ActiveSupport::Notifications.subscribe(
12
+ "process_action.action_controller",
13
+ Metrics.new
14
+ )
15
+ end
16
+
17
+ end
18
+ end
@@ -0,0 +1,14 @@
1
+ module RailsRequests
2
+ class FullReport < BaseReport
3
+ def data(group)
4
+ collect(group) do |k, v|
5
+ {
6
+ group: k,
7
+ average: v.sum.to_f / v.size,
8
+ count: v.size,
9
+ slowest: v.max
10
+ }
11
+ end.sort{|a, b| b[:count] <=> a[:count]}
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,14 @@
1
+ module RailsRequests
2
+ class GlobalReport < BaseReport
3
+ def data(group)
4
+ collect(group) do |k, v|
5
+ {
6
+ group: k,
7
+ average: v.sum.to_f / v.size,
8
+ count: v.size,
9
+ slowest: v.max
10
+ }
11
+ end.sort{|a, b| b[:count] <=> a[:count]}
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,45 @@
1
+ class Metrics
2
+
3
+ # payload
4
+ # {
5
+ # controller: "PostsController",
6
+ # action: "index",
7
+ # params: {"action" => "index", "controller" => "posts"},
8
+ # headers: #<ActionDispatch::Http::Headers:0x0055a67a519b88>,
9
+ # format: :html,
10
+ # method: "GET",
11
+ # path: "/posts",
12
+ # status: 200,
13
+ # view_runtime: 46.848,
14
+ # db_runtime: 0.157
15
+ # }
16
+
17
+ def call(event_name, started, finished, event_id, payload)
18
+ event = ActiveSupport::Notifications::Event.new(event_name, started, finished, event_id, payload)
19
+
20
+ record = {
21
+ controller: event.payload[:controller],
22
+ action: event.payload[:action],
23
+ format: event.payload[:format],
24
+ method: event.payload[:method],
25
+ path: event.payload[:path],
26
+ status: event.payload[:status],
27
+ view_runtime: event.payload[:view_runtime],
28
+ db_runtime: event.payload[:db_runtime],
29
+ duration: event.duration
30
+ }
31
+
32
+ namespace = "metrics|#{record[:controller]}|#{record[:action]}|#{finished.strftime("%Y%m%dT%H%M")}|#{finished.to_i}|#{record[:path]}"
33
+
34
+ # set("#{namespace}|all", record.to_json)
35
+ # set("#{namespace}|database", record[:db_runtime])
36
+ # set("#{namespace}|view_duration", record[:view_runtime])
37
+ set("#{namespace}|total_duration", record[:duration])
38
+ end
39
+
40
+ def set(key, value)
41
+ #puts "#{key} = #{value}"
42
+ RailsRequests.redis.set(key, value)
43
+ RailsRequests.redis.expire(key, RailsRequests.duration.to_i)
44
+ end
45
+ end
@@ -0,0 +1,23 @@
1
+ module RailsRequests
2
+ class Middleware
3
+ def initialize(app)
4
+ @app = app
5
+ end
6
+
7
+ def call(env)
8
+ @status, @headers, @response = @app.call(env)
9
+
10
+ # if RailsRequests.debug
11
+ # key = RailsRequests.cache_key
12
+ # field = RailsRequests.field_key
13
+ # puts "key: #{key}, field: #{field}"
14
+ # RailsRequests.redis.hincrby(key, field, 1)
15
+ # else
16
+ RailsRequests.redis.hincrby(RailsRequests.cache_key, RailsRequests.field_key, 1)
17
+ # end
18
+
19
+ [@status, @headers, @response]
20
+ end
21
+
22
+ end
23
+ end
@@ -0,0 +1,35 @@
1
+ module RailsRequests
2
+ class Record
3
+
4
+ attr_reader :controller, :action, :datetime, :datetimei, :path, :metric
5
+
6
+ # key = metrics|HomeController|index|20200123T0919|1579789173|/|database = 0
7
+ # value = string
8
+ def initialize(key, value)
9
+ @key = key
10
+ @value = value
11
+
12
+ items = key.split("|")
13
+
14
+ @controller = items[1]
15
+ @action = items[2]
16
+ @datetime = items[3]
17
+ @datetimei = items[4].to_i
18
+ @path = items[5]
19
+ @metric = items[6]
20
+ end
21
+
22
+ def value
23
+ case metric
24
+ when "database", "view_duration", "total_duration"
25
+ @value.to_f
26
+ when "all"
27
+ JSON.parse(@value)
28
+ end
29
+ end
30
+
31
+ def controller_action
32
+ "#{controller}##{action}"
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,12 @@
1
+ module ActionDispatch::Routing
2
+ class Mapper
3
+ def mount_routes(options = {})
4
+ begin
5
+ mount RailsRequests::Engine => '/rails/requests', :as => options[:as] || 'rails_requests'
6
+ rescue ArgumentError
7
+ # already added
8
+ # this cod exist here because engine not includes routing automatically
9
+ end
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,3 @@
1
+ module RailsRequests
2
+ VERSION = '0.1.1'
3
+ end
@@ -0,0 +1,86 @@
1
+ require "redis"
2
+ require_relative "rails_requests/middleware.rb"
3
+ require_relative "rails_requests/record.rb"
4
+ require_relative "rails_requests/base_report.rb"
5
+ require_relative "rails_requests/global_report.rb"
6
+ require_relative "rails_requests/full_report.rb"
7
+
8
+ require "rails_requests/engine"
9
+
10
+ module RailsRequests
11
+ mattr_accessor :redis
12
+ @@redis = Redis.new
13
+
14
+ mattr_accessor :duration
15
+ @@duration = 24.hours
16
+
17
+ mattr_accessor :debug
18
+ @@debug = false
19
+
20
+ def self.setup
21
+ yield(self)
22
+ end
23
+
24
+ # date key in redis store
25
+ def RailsRequests.cache_key(now = Date.current)
26
+ "date-#{now}"
27
+ end
28
+ # write to current slot
29
+ # time - date -minute
30
+ def RailsRequests.field_key(now = Time.now)
31
+ now.strftime("%H:%M")
32
+ end
33
+
34
+ def RailsRequests.days
35
+ (RailsRequests.duration % 24.days).parts[:days] + 1
36
+ end
37
+
38
+ def RailsRequests.median(array)
39
+ sorted = array.sort
40
+ size = sorted.size
41
+ center = size / 2
42
+
43
+ if size == 0
44
+ nil
45
+ elsif size.even?
46
+ (sorted[center - 1] + sorted[center]) / 2.0
47
+ else
48
+ sorted[center]
49
+ end
50
+ end
51
+
52
+ def RailsRequests.dump
53
+ @data = []
54
+
55
+ all = {}
56
+ RailsRequests.days.times do |e|
57
+ date = e.days.ago.to_date
58
+ all[date] = RailsRequests.redis.hgetall(RailsRequests.cache_key(date))
59
+ end
60
+
61
+ stop = Time.at(60 * (Time.now.to_i / 60))
62
+ current = stop - RailsRequests.duration
63
+
64
+ while current <= stop
65
+ views = all.dig(current.to_date, current.strftime("%H:%M")) || 0
66
+
67
+ @data << [current.to_i * 1000, views.to_i]
68
+
69
+ current += 1.minute
70
+ end
71
+
72
+ @data.sort!
73
+ end
74
+
75
+ # populate test data
76
+ # run in rails c
77
+ def RailsRequests.populate_test_data(seed = 20, limit = 10000, days = 7)
78
+ limit.times do
79
+ t = rand(86400*days).seconds.ago # within last 7 days
80
+ RailsRequests.redis.hincrby(RailsRequests.cache_key(t.to_date), RailsRequests.field_key(t), rand(seed))
81
+ end
82
+ end
83
+
84
+ end
85
+
86
+ require_relative 'rails_requests/routes'
metadata ADDED
@@ -0,0 +1,105 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: rails_requests
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.1
5
+ platform: ruby
6
+ authors:
7
+ - Igor Kasyanchuk
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2020-01-23 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: rails
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: '0'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: '0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: redis
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: sqlite3
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ description: Track number of requests to your app
56
+ email:
57
+ - igorkasyanchuk@gmail.com
58
+ executables: []
59
+ extensions: []
60
+ extra_rdoc_files: []
61
+ files:
62
+ - MIT-LICENSE
63
+ - README.md
64
+ - Rakefile
65
+ - app/assets/config/rails_requests_manifest.js
66
+ - app/controllers/rails_requests_controller.rb
67
+ - app/views/rails_requests/_css.html.erb
68
+ - app/views/rails_requests/_js.html.erb
69
+ - app/views/rails_requests/index.html.erb
70
+ - config/routes.rb
71
+ - lib/rails_requests.rb
72
+ - lib/rails_requests/base_report.rb
73
+ - lib/rails_requests/collection.rb
74
+ - lib/rails_requests/engine.rb
75
+ - lib/rails_requests/full_report.rb
76
+ - lib/rails_requests/global_report.rb
77
+ - lib/rails_requests/metrics.rb
78
+ - lib/rails_requests/middleware.rb
79
+ - lib/rails_requests/record.rb
80
+ - lib/rails_requests/routes.rb
81
+ - lib/rails_requests/version.rb
82
+ homepage: https://github.com/igorkasyanchuk/rails_requests
83
+ licenses:
84
+ - MIT
85
+ metadata: {}
86
+ post_install_message:
87
+ rdoc_options: []
88
+ require_paths:
89
+ - lib
90
+ required_ruby_version: !ruby/object:Gem::Requirement
91
+ requirements:
92
+ - - ">="
93
+ - !ruby/object:Gem::Version
94
+ version: '0'
95
+ required_rubygems_version: !ruby/object:Gem::Requirement
96
+ requirements:
97
+ - - ">="
98
+ - !ruby/object:Gem::Version
99
+ version: '0'
100
+ requirements: []
101
+ rubygems_version: 3.0.6
102
+ signing_key:
103
+ specification_version: 4
104
+ summary: Track number of requests to your app
105
+ test_files: []