browser_sense 1.0.0

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: e198b13035e5ca4775150f1413cd09e67b84e0be7d6817e8855a4358d5f80b22
4
+ data.tar.gz: a479aff279f85768b99442bc4de1546c2c74c9830e65dea7d780305639c7c0c5
5
+ SHA512:
6
+ metadata.gz: e642b21b603aa52382ce1b94f838c6c66f4872e28f5f8aa470d523ae9e9a846a12081972b1a81e92b12b0445fbf84a609d5b049d12e95e36c89316810dbbf325
7
+ data.tar.gz: 9ff5e4816096babc640372678d7a11186ab39318435d433a9b8142e06b8291a83a0c4597771f9b97297ee876d16f3a2720a8f864ccae840ae91940d9ba8d7637
data/.rubocop.yml ADDED
@@ -0,0 +1,62 @@
1
+ # plugins:
2
+ # - rubocop-rails
3
+ # - rubocop-performance
4
+
5
+ AllCops:
6
+ NewCops: enable
7
+ TargetRubyVersion: 3.3
8
+ Exclude:
9
+ - 'db/**/*'
10
+ - 'config/**/*'
11
+ - 'script/**/*'
12
+ - 'bin/{rails,rake}'
13
+ - !ruby/regexp /old_and_unused\.rb$/
14
+
15
+ Style/EmptyMethod:
16
+ Enabled: false
17
+
18
+ Style/StringLiterals:
19
+ Enabled: true
20
+ EnforcedStyle: double_quotes
21
+
22
+ Style/StringLiteralsInInterpolation:
23
+ Enabled: true
24
+ EnforcedStyle: double_quotes
25
+
26
+ Style/Lambda:
27
+ Enabled: false
28
+
29
+ # Style/Lambda:
30
+ # Enabled: true
31
+ # EnforcedStyle: lambda
32
+
33
+ Metrics/ModuleLength:
34
+ Max: 200
35
+ CountAsOne: [
36
+ 'array', 'hash', 'heredoc', 'method_call'
37
+ ]
38
+
39
+ Metrics/ClassLength:
40
+ Max: 200
41
+ CountAsOne: [
42
+ 'array', 'hash', 'heredoc', 'method_call'
43
+ ]
44
+
45
+ Metrics/MethodLength:
46
+ Max: 25
47
+ CountAsOne: [
48
+ 'array', 'hash', 'heredoc', 'method_call'
49
+ ]
50
+
51
+ Layout/LineLength:
52
+ Max: 80
53
+
54
+ Layout:
55
+ # ArgumentAlignment: with_first_argument
56
+ # ArrayAlignment: with_first_argument
57
+ # BeginEndAlignment: start_of_line
58
+ # EnforcedStyleAlignWith: start_of_line
59
+ # FirstArrayElementIndentation: consistent
60
+ # FirstHashIndentation: consistent
61
+ #MultilineMethodCallIndentation:
62
+ # EnforcedStyle: indented_relative_to_receiver
data/CHANGELOG.org ADDED
@@ -0,0 +1,10 @@
1
+ #+TITLE: Browser Info ChangeLog
2
+
3
+ * [1.0.0] - 2025-08-31
4
+
5
+ - First public release
6
+ - Changes and improvements to the documenation for public release
7
+
8
+ * [0.1.0] - 2022-11-23
9
+
10
+ - Initial release (private)
data/Gemfile ADDED
@@ -0,0 +1,12 @@
1
+ # frozen_string_literal: true
2
+
3
+ source "https://rubygems.org"
4
+
5
+ # Specify your gem's dependencies in browser_sense.gemspec
6
+ gemspec
7
+
8
+ gem "rake", "~> 13.0"
9
+
10
+ gem "minitest", "~> 5.0"
11
+
12
+ gem "rubocop", "~> 1.21"
data/Gemfile.lock ADDED
@@ -0,0 +1,55 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ browser_sense (0.2.3)
5
+ browser (~> 6.2.0)
6
+
7
+ GEM
8
+ remote: https://rubygems.org/
9
+ specs:
10
+ ast (2.4.3)
11
+ browser (6.2.0)
12
+ json (2.11.3)
13
+ language_server-protocol (3.17.0.4)
14
+ lint_roller (1.1.0)
15
+ minitest (5.25.5)
16
+ parallel (1.27.0)
17
+ parser (3.3.8.0)
18
+ ast (~> 2.4.1)
19
+ racc
20
+ prism (1.4.0)
21
+ racc (1.8.1)
22
+ rainbow (3.1.1)
23
+ rake (13.2.1)
24
+ regexp_parser (2.10.0)
25
+ rubocop (1.75.5)
26
+ json (~> 2.3)
27
+ language_server-protocol (~> 3.17.0.2)
28
+ lint_roller (~> 1.1.0)
29
+ parallel (~> 1.10)
30
+ parser (>= 3.3.0.2)
31
+ rainbow (>= 2.2.2, < 4.0)
32
+ regexp_parser (>= 2.9.3, < 3.0)
33
+ rubocop-ast (>= 1.44.0, < 2.0)
34
+ ruby-progressbar (~> 1.7)
35
+ unicode-display_width (>= 2.4.0, < 4.0)
36
+ rubocop-ast (1.44.1)
37
+ parser (>= 3.3.7.2)
38
+ prism (~> 1.4)
39
+ ruby-progressbar (1.13.0)
40
+ unicode-display_width (3.1.4)
41
+ unicode-emoji (~> 4.0, >= 4.0.4)
42
+ unicode-emoji (4.0.4)
43
+
44
+ PLATFORMS
45
+ ruby
46
+ x86_64-linux
47
+
48
+ DEPENDENCIES
49
+ browser_sense!
50
+ minitest (~> 5.0)
51
+ rake (~> 13.0)
52
+ rubocop (~> 1.21)
53
+
54
+ BUNDLED WITH
55
+ 2.5.3
data/LICENSE.txt ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2022 Adolfo Villafiorita
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
data/README.org ADDED
@@ -0,0 +1,120 @@
1
+ #+TITLE: BrowserSense
2
+ #+AUTHOR: Shair.Tech
3
+ #+DATE: <2025-08-31 Sun>
4
+
5
+ Log information about the browsers accessings your Rails application.
6
+
7
+ * Installation
8
+
9
+ BrowserSense monitors which browser is accessing your Ruby on Rails app.
10
+
11
+ Add the gem to your application's Gemfile and then bundle:
12
+
13
+ #+begin_example sh
14
+ gem "browser_sense"
15
+ #+end_example
16
+
17
+ #+begin_example sh
18
+ bundle
19
+ #+end_example
20
+
21
+ To enable logging, add =browser_infro= to the =application_controller.rb= of
22
+ your app, e.g.:
23
+
24
+ #+begin_src ruby
25
+ class ApplicationController < ActionController::Base
26
+ [...]
27
+
28
+ browser_sense
29
+
30
+ [...]
31
+ end
32
+ #+end_src
33
+
34
+
35
+ * Data logged
36
+
37
+ Each invocation of a route of your application generates a line in the log storing the
38
+ following details:
39
+
40
+ 1. Browser name
41
+ 2. Platform
42
+ 3. Device name
43
+ 4. Controller being invoked
44
+ 5. Method of the controller being invoked
45
+ 6. Request format (e.g. html, json)
46
+ 7. Hashed IP, which allows to track requests from the same IP, while preserving
47
+ privacy
48
+ 8. Timestamp
49
+
50
+ Information is stored in the application log, each line prefixed by
51
+ =BrowserSense= and data presented in CSV.
52
+
53
+ *Example*
54
+
55
+ #+begin_example bash
56
+ $ cat production.log
57
+ [...]
58
+ BrowserSense: "Firefox","linux","Unknown","Devise::SessionsController","new","12ca17b49af2289436f303e0166030a21e525d266e209267433801a8fd4071a0","2022-06-09T10:08:09+02:00"
59
+ [...]
60
+ #+end_example
61
+
62
+
63
+ * Analyzing Data
64
+
65
+ When you want to analyze data, extract the information with:
66
+
67
+ #+begin_example
68
+ grep BrowserSense production.log | cut -f2- -d: > data.csv
69
+ #+end_example
70
+
71
+ and then, e.g., open the resulting file in a spreadsheet.
72
+
73
+ There is also a ruby script =browser_sense=. The script takes as input the
74
+ pathname of the file with the log and prints to stdout the result of various
75
+ analyses. The script is a quick hack, wrapping =sed=, =grep= and =miller=
76
+ ([[https://github.com/johnkerl/miller][Miller)]], which must be installed on your machine for the script to succeed.
77
+
78
+ If you want more reports and plots, you can use the =log_sense= gem
79
+ (https://rubygems.org/gems/log_sense), which understands the data generated by
80
+ this gem.
81
+
82
+
83
+ * Development
84
+
85
+ After checking out the repo, run =bin/setup= to install dependencies. Then,
86
+ run =rake test= to run the tests. You can also run =bin/console= for an
87
+ interactive prompt that will allow you to experiment.
88
+
89
+ To install this gem onto your local machine, run =bundle exec rake
90
+ install=. To release a new version, update the version number in =version.rb=,
91
+ and then run =bundle exec rake release=, which will create a git tag for the
92
+ version, push git commits and the created tag, and push the =.gem= file to
93
+ [[https://rubygems.org][rubygems.org]]
94
+
95
+
96
+ * Contributing
97
+
98
+ TODO
99
+
100
+
101
+ * Changelog
102
+
103
+ [[./CHANGELOG.org]]
104
+
105
+
106
+ * Authors and Contributors
107
+
108
+ [[https://shair.tech][Shair.Tech]]
109
+
110
+ * Known Bugs
111
+
112
+ We have been running BrowserSense for quite a few years with no particular
113
+ issues. There are no known bugs; there is an unknown number of unknown bugs.
114
+
115
+ You are most welcome to report issues and missing features, using the Issue
116
+ tracker.
117
+
118
+ * License
119
+
120
+ The gem is available as open source under the terms of the [[https://opensource.org/licenses/MIT][MIT License]].
data/Rakefile ADDED
@@ -0,0 +1,16 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "bundler/gem_tasks"
4
+ require "rake/testtask"
5
+
6
+ Rake::TestTask.new(:test) do |t|
7
+ t.libs << "test"
8
+ t.libs << "lib"
9
+ t.test_files = FileList["test/**/test_*.rb"]
10
+ end
11
+
12
+ require "rubocop/rake_task"
13
+
14
+ RuboCop::RakeTask.new
15
+
16
+ task default: %i[test rubocop]
@@ -0,0 +1,34 @@
1
+ require "active_support/concern"
2
+ require "browser"
3
+
4
+ module BrowserSenseFilter
5
+ extend ActiveSupport::Concern
6
+
7
+ included do
8
+ def self.browser_sense
9
+ # monitor which browser is being used
10
+ # stored in csv format in the log for each method invocation, extract with:
11
+ # grep BrowserSense development.log | cut -f2- -d:
12
+ before_action do |controller|
13
+ user_agent = request.env["HTTP_USER_AGENT"]
14
+ ip = request.env["REMOTE_ADDR"]
15
+
16
+ hashed_ip = Digest::SHA256.hexdigest ip
17
+ b = Browser.new(user_agent)
18
+ now = DateTime.now
19
+
20
+ logger = Rails.logger
21
+ browser_data = [
22
+ b.name, b.platform, b.device.name,
23
+ controller.class.name, controller.action_name,
24
+ request.format.symbol,
25
+ hashed_ip,
26
+ now
27
+ ]
28
+
29
+ browser_data_str = browser_data.map { |x| "\"#{x}\"" }.join(",")
30
+ logger.info "BrowserSense: #{browser_data_str}"
31
+ end
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,66 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "lib/browser_sense/version"
4
+
5
+ Gem::Specification.new do |spec|
6
+ spec.name = "browser_sense"
7
+ spec.version = BrowserSense::VERSION
8
+ spec.authors = ["Adolfo Villafiorita"]
9
+ spec.email = ["adolfo@shair.tech"]
10
+
11
+ spec.summary = "Monitor which browser is accessing your Ruby on Rails app."
12
+ spec.description = <<-EOS
13
+ BrowserSense logs information about the browser accessing your
14
+ RubyOnRails app.
15
+
16
+ Installation is pretty simple:
17
+
18
+ 1. Add =gem "browser_sense"= to your Gemfile
19
+
20
+ 2. Add the following line to your =application_controller.rb=:
21
+
22
+ browser_sense
23
+
24
+ Data is stored in csv format in Rails' production log. Each URL invocation
25
+ generates a log line.
26
+
27
+ Extract browser info data with:
28
+
29
+ grep BrowserSense development.log | cut -f2- -d:
30
+
31
+ Analyize with
32
+
33
+ browser-info log/production.log > output.txt
34
+
35
+ or with log_sense (https://rubygems.org/gems/log_sense)
36
+
37
+ BrowserSense relies on the browser gem to get informaton about the browsers.
38
+ EOS
39
+ spec.homepage = "https://github.com/shair-tech/browser_sense"
40
+ spec.license = "MIT"
41
+ spec.required_ruby_version = ">= 2.6.0"
42
+
43
+ spec.metadata["allowed_push_host"] = "https://rubygems.org/"
44
+
45
+ spec.metadata["homepage_uri"] = spec.homepage
46
+ spec.metadata["source_code_uri"] = "https://github.com/shair-tech/browser_sense"
47
+ spec.metadata["changelog_uri"] = "https://github.com/shair-tech/browser_sense/blob/main/CHANGELOG.org"
48
+
49
+ # Specify which files should be added to the gem when it is released. The
50
+ # `git ls-files -z` loads the files in the RubyGem that have been added into
51
+ # git.
52
+ spec.files = Dir.chdir(__dir__) do
53
+ `git ls-files -z`.split("\x0").reject do |f|
54
+ (f == __FILE__) || f.match(%r{\A(?:(?:bin|test|spec|features)/|\.(?:git|travis|circleci)|appveyor)})
55
+ end
56
+ end
57
+ spec.bindir = "exe"
58
+ spec.executables = spec.files.grep(%r{\Aexe/}) { |f| File.basename(f) }
59
+ spec.require_paths = ["lib"]
60
+
61
+ # Uncomment to register a new dependency of your gem
62
+ spec.add_dependency "browser", "~> 6.2.0"
63
+
64
+ # For more information and examples about making a new gem, check out our
65
+ # guide at: https://bundler.io/guides/creating_gem.html
66
+ end
data/exe/browser_sense ADDED
@@ -0,0 +1,92 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ # -*- ruby -*-
4
+ # A quick hack to analyze data produced by browser info
5
+
6
+ require "tempfile"
7
+
8
+ if ARGV.length != 1
9
+ puts "Usage browser_sense log_filename"
10
+ exit
11
+ end
12
+
13
+ LOG_FILE=ARGV[0]
14
+ TMP_FILE= Tempfile.new("browser_sense").path
15
+
16
+ def title(string)
17
+ puts %Q(
18
+ ==============================================================================
19
+ #{string}
20
+ ==============================================================================)
21
+ end
22
+
23
+ # Look for BrowserSense | cut info by Rails | remove lines with less than 7 fields
24
+ # No need to remove double quotes with sed -e 's/\"//g'
25
+ `grep -E -e 'Browser(Info|Sense)' #{LOG_FILE} | sed -E 's/.*Browser(Info|Sense): (.+,.+,.+,.+,.+,.+,.+,.+)/\\2/g' > #{TMP_FILE}`
26
+
27
+ # Look at the README file of BrowserSense for the field specification
28
+ #
29
+ # mlr template
30
+ #
31
+ # 1. Browser name
32
+ # 2. Platform
33
+ # 3. Device name
34
+ # 4. Controller being invoked
35
+ # 5. Method of the controller being invoked
36
+ # 6. Request format (e.g. html, json)
37
+ # 7. Hashed IP, which allows to track requests from the same IP, while preserving privacy
38
+ # 8. Timestamp
39
+ #
40
+
41
+ # methods by os
42
+ # the usage of reshape and unsparsify is still kind of magic to me.
43
+ # https://unix.stackexchange.com/questions/557124/bash-awk-pivot-csv-into-desired-table
44
+ title "Methods vs OS"
45
+
46
+ puts `mlr --icsv --implicit-csv-header --opprint put 'if ($2 == "android" || $2 == "ios") { $os_type="mobile"} elif ($2 == "unknown_platform") {$os_type = "unknown" } else {$os_type = "desktop"}' then cut -f 4,5,os_type then label controller,method,os_type then count-distinct -f controller,method,os_type then reshape -s os_type,count then unsparsify then sort -f controller,method #{TMP_FILE}`
47
+
48
+ # how many browser types?
49
+ title "Browsers"
50
+
51
+ puts `mlr --icsv --implicit-csv-header --opprint count-distinct -f 1 then label browser,count then sort -nr count #{TMP_FILE}`
52
+
53
+
54
+ # how many os?
55
+ title "OS"
56
+
57
+ puts `mlr --icsv --implicit-csv-header --opprint count-distinct -f 2 then label os,count then sort -nr count #{TMP_FILE}`
58
+
59
+ # how many os,browser?
60
+ title "OS and Browsers"
61
+
62
+ puts `mlr --icsv --implicit-csv-header --opprint count-distinct -f 2,1 then label os,browser,count then sort -nr count #{TMP_FILE}`
63
+
64
+ # how many distinct ips?
65
+ title "IP"
66
+
67
+ puts `mlr --icsv --implicit-csv-header --opprint count-distinct -f 7 then label ip,count then sort -nr count #{TMP_FILE}`
68
+
69
+ # ip,os,browser?
70
+ title "IP,OS,Browser"
71
+
72
+ puts `mlr --icsv --implicit-csv-header --opprint count-distinct -f 7,2,1 then label ip,platform,browser,count then sort -nr count #{TMP_FILE}`
73
+
74
+ # methods used on mobile
75
+ title "Methods on Mobile"
76
+
77
+ puts `mlr --icsv --implicit-csv-header --opprint filter '$2 == "android" || $2 == "ios"' then count-distinct -f 2,1,4,5 then label os,browser,controller,method,count then sort -f os,browser,controller,method then put '$count2=$count' then bar -f count2 -b ' ' #{TMP_FILE}`
78
+
79
+ # methods used on desktops
80
+ title "Methods on PC"
81
+
82
+ puts `mlr --icsv --implicit-csv-header --opprint filter '$2 != "android" && $2 != "ios"' then count-distinct -f 2,1,4,5 then label os,browser,controller,method,count then sort -f os,browser,controller,method,count then sort -nr count #{TMP_FILE}`
83
+
84
+ # methods used
85
+ title "Methods"
86
+
87
+ puts `mlr --icsv --implicit-csv-header --opprint count-distinct -f 4,5 then label controller,method then sort -nr count #{TMP_FILE}`
88
+
89
+ # methods vs format
90
+ title "Methods"
91
+
92
+ puts `mlr --icsv --implicit-csv-header --opprint count-distinct -f 4,5,6 then label controller,method,format then sort -nr count #{TMP_FILE}`
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ module BrowserSense
4
+ VERSION = "1.0.0"
5
+ end
@@ -0,0 +1,12 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "browser_sense/version"
4
+
5
+ module BrowserSense
6
+ #
7
+ # Mount us as an Engine
8
+ # (or the Rails app will know nothing about us)
9
+ #
10
+ class Engine < ::Rails::Engine
11
+ end
12
+ end
@@ -0,0 +1,4 @@
1
+ module BrowserSense
2
+ VERSION: String
3
+ # See the writing guide of rbs: https://github.com/ruby/rbs#guides
4
+ end
metadata ADDED
@@ -0,0 +1,80 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: browser_sense
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.0
5
+ platform: ruby
6
+ authors:
7
+ - Adolfo Villafiorita
8
+ bindir: exe
9
+ cert_chain: []
10
+ date: 1980-01-02 00:00:00.000000000 Z
11
+ dependencies:
12
+ - !ruby/object:Gem::Dependency
13
+ name: browser
14
+ requirement: !ruby/object:Gem::Requirement
15
+ requirements:
16
+ - - "~>"
17
+ - !ruby/object:Gem::Version
18
+ version: 6.2.0
19
+ type: :runtime
20
+ prerelease: false
21
+ version_requirements: !ruby/object:Gem::Requirement
22
+ requirements:
23
+ - - "~>"
24
+ - !ruby/object:Gem::Version
25
+ version: 6.2.0
26
+ description: " BrowserSense logs information about the browser accessing your\n
27
+ \ RubyOnRails app. \n\n Installation is pretty simple:\n\n 1. Add =gem
28
+ \"browser_sense\"= to your Gemfile\n\n 2. Add the following line to your =application_controller.rb=:\n\n
29
+ \ browser_sense\n\n Data is stored in csv format in Rails' production
30
+ log. Each URL invocation\n generates a log line.\n\n Extract browser info
31
+ data with:\n\n grep BrowserSense development.log | cut -f2- -d:\n\n Analyize
32
+ with\n\n browser-info log/production.log > output.txt\n\n or with log_sense
33
+ (https://rubygems.org/gems/log_sense)\n\n BrowserSense relies on the browser
34
+ gem to get informaton about the browsers.\n"
35
+ email:
36
+ - adolfo@shair.tech
37
+ executables:
38
+ - browser_sense
39
+ extensions: []
40
+ extra_rdoc_files: []
41
+ files:
42
+ - ".rubocop.yml"
43
+ - CHANGELOG.org
44
+ - Gemfile
45
+ - Gemfile.lock
46
+ - LICENSE.txt
47
+ - README.org
48
+ - Rakefile
49
+ - app/controllers/browser_sense_filter.rb
50
+ - browser_sense.gemspec
51
+ - exe/browser_sense
52
+ - lib/browser_sense.rb
53
+ - lib/browser_sense/version.rb
54
+ - sig/browser_sense.rbs
55
+ homepage: https://github.com/shair-tech/browser_sense
56
+ licenses:
57
+ - MIT
58
+ metadata:
59
+ allowed_push_host: https://rubygems.org/
60
+ homepage_uri: https://github.com/shair-tech/browser_sense
61
+ source_code_uri: https://github.com/shair-tech/browser_sense
62
+ changelog_uri: https://github.com/shair-tech/browser_sense/blob/main/CHANGELOG.org
63
+ rdoc_options: []
64
+ require_paths:
65
+ - lib
66
+ required_ruby_version: !ruby/object:Gem::Requirement
67
+ requirements:
68
+ - - ">="
69
+ - !ruby/object:Gem::Version
70
+ version: 2.6.0
71
+ required_rubygems_version: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - ">="
74
+ - !ruby/object:Gem::Version
75
+ version: '0'
76
+ requirements: []
77
+ rubygems_version: 3.6.7
78
+ specification_version: 4
79
+ summary: Monitor which browser is accessing your Ruby on Rails app.
80
+ test_files: []