rails_requests 0.1.1

Sign up to get free protection for your applications and to get access to all the features.
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: []