├── .gitignore ├── Gemfile ├── LICENSE ├── README.md ├── Rakefile ├── browser_details.gemspec └── lib ├── browser_details.rb └── browser_details ├── assets └── browser_details.js ├── railtie.rb └── version.rb /.gitignore: -------------------------------------------------------------------------------- 1 | *.gem 2 | *.rbc 3 | .bundle 4 | .config 5 | .yardoc 6 | Gemfile.lock 7 | InstalledFiles 8 | _yardoc 9 | coverage 10 | doc/ 11 | lib/bundler/man 12 | pkg 13 | rdoc 14 | spec/reports 15 | test/tmp 16 | test/version_tmp 17 | tmp 18 | NOTES.md 19 | -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | source 'https://rubygems.org' 2 | 3 | # Specify your gem's dependencies in check-js.gemspec 4 | gemspec 5 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2012 Garry Shutler 2 | 3 | MIT License 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining 6 | a copy of this software and associated documentation files (the 7 | "Software"), to deal in the Software without restriction, including 8 | without limitation the rights to use, copy, modify, merge, publish, 9 | distribute, sublicense, and/or sell copies of the Software, and to 10 | permit persons to whom the Software is furnished to do so, subject to 11 | the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be 14 | included in all copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 17 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 19 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 20 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 21 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 22 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Browser Details 2 | 3 | Have you ever had the conversation: 4 | 5 | > **Your site doesn't work.** 6 | > What browser are you using and do you have Javascript enabled? 7 | > 8 | > **What's a browser?** 9 | > :unamused: 10 | 11 | Browser Details makes that problem disappear by capturing a user's browser 12 | details in your logs. You will get the exact browser version they are using and 13 | their OS dumped into your logs, and when possible you'll get whether they have 14 | JS enabled too. All entirely unobtrustively. 15 | 16 | You may never need to speak to a user ever again! 17 | 18 | ### Before 19 | 20 | Started GET "/" for 127.0.0.1 at 2012-12-26 21:25:14 +0000 21 | Processing by HomeController#index as HTML 22 | ... 23 | Started POST "/posts" for 127.0.0.1 at 2012-12-26 21:25:19 +0000 24 | Processing by PostsController#create as HTML 25 | ... 26 | 27 | ### After 28 | 29 | Started GET "/" for 127.0.0.1 at 2012-12-26 21:25:14 +0000 30 | Chrome 23.0.1271.95 (Macintosh, Intel Mac OS X 10_7_5) 31 | Processing by HomeController#index as HTML 32 | ... 33 | Started POST "/posts" for 127.0.0.1 at 2012-12-26 21:25:19 +0000 34 | Chrome 23.0.1271.95 (Macintosh, Intel Mac OS X 10_7_5), JS enabled 35 | Processing by PostsController#create as HTML 36 | ... 37 | 38 | ### How? 39 | 40 | Browser Details is a Rack Middleware that logs information about the browser 41 | used to make a request. When possible this includes whether the browser has 42 | Javascript enabled or not. 43 | 44 | ## Installation 45 | 46 | Add this line to your application's Gemfile: 47 | 48 | gem 'browser_details' 49 | 50 | And then execute: 51 | 52 | $ bundle 53 | 54 | Or install it yourself as: 55 | 56 | $ gem install browser_details 57 | 58 | ### Rails 59 | 60 | The middleware will be installed automatically by the Railtie. 61 | 62 | To enable Browser Details to report whether the browser has Javascript enabled 63 | for form submissions you must add the following line to your 64 | `app/assets/javascripts/application.js`: 65 | 66 | //= require browser_details 67 | 68 | Browser Details requires jQuery to be present as it works by checking if the 69 | `utf8` form element has been changed to a large tick from a small tick by the 70 | Browser Details Javascript. 71 | 72 | If this script is not added then all browsers will report that Javascript is 73 | disabled. 74 | 75 | ### Other Rack applications 76 | 77 | To use the Browser Details middleware you must add the line: 78 | 79 | use BrowserDetails 80 | 81 | Wherever it may be appropriate for your application. 82 | 83 | The Javascript detection is currently reliant on Rails. If you would like your 84 | application to be able to detect whether Javascript is enabled too, please 85 | create an issue, or even better open a pull request. 86 | 87 | ### Custom usage 88 | 89 | If you want to use the message elsewhere, you can request the message directly: 90 | 91 | ```ruby 92 | details = BrowserDetails.message(request) 93 | ``` 94 | 95 | ## Contributing 96 | 97 | 1. Fork it 98 | 2. Create your feature branch (`git checkout -b my-new-feature`) 99 | 3. Commit your changes (`git commit -am 'Add some feature'`) 100 | 4. Push to the branch (`git push origin my-new-feature`) 101 | 5. Create new Pull Request 102 | -------------------------------------------------------------------------------- /Rakefile: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env rake 2 | require "bundler/gem_tasks" 3 | -------------------------------------------------------------------------------- /browser_details.gemspec: -------------------------------------------------------------------------------- 1 | # -*- encoding: utf-8 -*- 2 | require File.expand_path('../lib/browser_details/version', __FILE__) 3 | 4 | Gem::Specification.new do |gem| 5 | gem.authors = ["Garry Shutler"] 6 | gem.email = ["garry@robustsoftware.co.uk"] 7 | gem.description = %q{ 8 | Browser Details is a Rack Middleware that logs information about the browser 9 | used to make a request 10 | } 11 | gem.summary = %q{ 12 | Browser Details is a Rack Middleware that logs information about the browser 13 | used to make a request 14 | } 15 | gem.homepage = "https://github.com/gshutler/browser_details" 16 | gem.license = 'MIT' 17 | 18 | gem.files = Dir['lib/**/*'] + %w{LICENSE README.md} 19 | gem.name = "browser_details" 20 | gem.require_paths = ["lib"] 21 | gem.add_dependency('useragent', '>= 0.4') 22 | gem.version = BrowserDetails::VERSION 23 | end 24 | -------------------------------------------------------------------------------- /lib/browser_details.rb: -------------------------------------------------------------------------------- 1 | # -*- encoding: utf-8 -*- 2 | 3 | require "browser_details/version" 4 | require "useragent" 5 | 6 | # Public: Middleware for logging the browser details of each request. 7 | # 8 | class BrowserDetails 9 | # Set up the log_message method. 10 | if defined?(Hatchet) 11 | # If Hatchet is defined include it and define a method for its logger. 12 | include Hatchet 13 | 14 | def log_message(env, message) 15 | log.info(message) 16 | end 17 | elsif defined?(Rails) 18 | # If Rails is defined define a method for its logger. 19 | def log_message(env, message) 20 | Rails.logger.info(message) 21 | end 22 | else 23 | # Otherwise check if the env includes a logger and if so log to that. 24 | def log_message(env, message) 25 | if env['rack.logger'] 26 | env['rack.logger'].info(message) 27 | end 28 | end 29 | end 30 | 31 | # Make whatever log_message method that was defined private. 32 | private :log_message 33 | 34 | # Public: Creates a new instance. 35 | # 36 | # app - The application this middleware is wrapping. 37 | # 38 | def initialize(app) 39 | @app = app 40 | end 41 | 42 | # Public: Log the browser details if possible and then forward the request on 43 | # to the wrapped application. 44 | # 45 | # env - The environment of the request. 46 | # 47 | # Returns the result generated by the wrapped application. 48 | # 49 | def call(env) 50 | request = Rack::Request.new(env) 51 | message = self.class.message(request) 52 | 53 | # Log a message if any details were gathered. 54 | unless message.empty? 55 | log_message(env, message) 56 | end 57 | 58 | # Delegate to the application we are wrapping. 59 | @app.call(env) 60 | end 61 | 62 | # Public: Creates a browser details message for a request. 63 | # 64 | # request - The Rack::Request to extract brower details from. 65 | # 66 | # Returns a String of browser details for the request. 67 | # 68 | def self.message(request) 69 | message = [] 70 | 71 | # Add the user agent details to the message if present. 72 | if request.user_agent 73 | agent = UserAgent.parse(request.user_agent) 74 | 75 | agent_details = [agent.browser] 76 | agent_details << 'Mobile' if agent.mobile? 77 | agent_details << agent.version 78 | agent_details << "(#{agent.platform}, #{agent.os})" 79 | 80 | message << agent_details.join(' ') 81 | end 82 | 83 | # Add whether Javascript is enabled or not if it is possible to tell. 84 | if request.xhr? 85 | # AJAX request - JS probably enabled. 86 | message << 'JS enabled' 87 | elsif request['utf8'] 88 | # Have a utf8 parameter - check if changed by JS. 89 | message << if request['utf8'] == '✓' 90 | # Value unchanged - JS was not executed and therefore disabled. 91 | 'JS disabled' 92 | else 93 | # Value changed - JS was executed and therefore enabled. 94 | 'JS enabled' 95 | end 96 | end 97 | 98 | message.join(', ') 99 | end 100 | end 101 | 102 | # Require the Railtie if Rails is present. 103 | require 'browser_details/railtie' if defined?(Rails) 104 | -------------------------------------------------------------------------------- /lib/browser_details/assets/browser_details.js: -------------------------------------------------------------------------------- 1 | window.jQuery(function($) { 2 | $("form input[name=utf8]").each(function() { 3 | $(this).val('✔'); 4 | }); 5 | }); 6 | -------------------------------------------------------------------------------- /lib/browser_details/railtie.rb: -------------------------------------------------------------------------------- 1 | class BrowserDetails 2 | 3 | # Public: Railtie for initializing the BrowserDetails middleware and making 4 | # the browser_details.js file available. 5 | # 6 | class Railtie < Rails::Railtie 7 | initializer "browser_details.insert_middleware" do |app| 8 | app.config.middleware.use BrowserDetails 9 | end 10 | 11 | config.before_configuration do |app| 12 | app.config.assets.paths << File.join(File.dirname(__FILE__), 'assets') 13 | end 14 | end 15 | 16 | end 17 | -------------------------------------------------------------------------------- /lib/browser_details/version.rb: -------------------------------------------------------------------------------- 1 | class BrowserDetails 2 | 3 | VERSION = "0.0.6" 4 | 5 | end 6 | --------------------------------------------------------------------------------