├── .gitignore ├── .yardopts ├── Gemfile ├── History.txt ├── Manifest.txt ├── README.md ├── Rakefile ├── VERSION ├── browser_detect.gemspec ├── init.rb ├── lib ├── browser_detect.rb └── railtie.rb └── test ├── browser_detect_test.rb ├── fixtures └── user_agents.yml └── test_helper.rb /.gitignore: -------------------------------------------------------------------------------- 1 | *.gemspec 2 | .svn 3 | .yardoc/ 4 | .bundle/ 5 | .rvmrc 6 | /pkg/ -------------------------------------------------------------------------------- /.yardopts: -------------------------------------------------------------------------------- 1 | --readme ./README.md 2 | --markup markdown 3 | --default-return "" 4 | --title "BrowserDetect Documentation" 5 | --template default 6 | -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | source :gemcutter 2 | 3 | # Specify your gem's dependencies in browser_detect.gemspec 4 | gemspec 5 | -------------------------------------------------------------------------------- /History.txt: -------------------------------------------------------------------------------- 1 | === 0.0.5 2011-10-6 2 | 3 | * 2 Bugs fixed: 4 | * Fixed Rails 3 Railtie support (thanks to eric1234) 5 | * Fix detection of Safari 6 | 7 | === 0.0.4 2010-11-07 8 | 9 | * 1 major enhancement: 10 | * Initial support for Rails 3 via Railtie 11 | 12 | === 0.0.3 2010-11-03 13 | 14 | * 2 major enhancements: 15 | * Moved documentation from RDoc to Yard 16 | * Changed Module name to BrowserDetect 17 | 18 | === 0.0.2 2010-11-01 19 | 20 | * 4 major enhancements: 21 | * fixed webkit detection and added webkit version querying 22 | * merge changes with tmlee 23 | * added ios name and changed browser_name to return 'unknown' if nothing matches 24 | * corrected issues with test mocking, added tests for robots 25 | 26 | === 0.0.1 2010-10-21 27 | 28 | * 1 major enhancement: 29 | * Added chrome, iphone, ipad to list of browsers and added support for these browsers to the browser_is? method -------------------------------------------------------------------------------- /Manifest.txt: -------------------------------------------------------------------------------- 1 | History.txt 2 | Manifest.txt 3 | PostInstall.txt 4 | README.rdoc 5 | Rakefile 6 | lib/browser_detect_test.rb 7 | script/console 8 | script/destroy 9 | script/generate 10 | test/test_browser_detect_test.rb 11 | test/test_helper.rb 12 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Browser Detect 2 | _It's like a crystal ball for user agents._ 3 | 4 | Browser Detect identifies the client browser using the user agent string that was supplied in the page request. Browser Detect searches the user agent string for any string you provide, and it also supports some special groupings and shortcuts: 5 | 6 | * ie (Any version of IE, excluding browsers like Opera and WebTV that identify themselves as IE) 7 | * ie{integer} (A specific major version of IE — "ie6", "ie7", "ie8", etc.) 8 | * robot (Google bot, MSN bot, Yahoo! bot) 9 | * ios (iPhone, iPod, iPad) 10 | * webkit (Any WebKit based browser) 11 | * mobile (Any modern mobile browser — iOS, Android, Palm webOS) 12 | 13 | ## Install it. 14 | 15 | Using Bundler, all you need to do is add the source to your Gemfile: 16 | 17 | gem "browser_detect" 18 | 19 | Then run: 20 | 21 | bundle install 22 | 23 | or, install it as a plugin using Rails 2: 24 | 25 | script/plugin install git://github.com/traction/browser_detect.git 26 | 27 | ## Wield it. 28 | 29 | To check if a particular browser made the request, use browser_is?(name) 30 | 31 | def index 32 | if browser_is?("chrome") 33 | # load some chrome-specific content 34 | end 35 | end 36 | 37 | or in a view: 38 | 39 | <%= browser_is?(:chrome) ? "secrets" : "buzz off" %> 40 | 41 | Don't forget you can use the special groupings listed above to target IE, robots, iOS, etc. 42 | 43 | <%= stylesheet_link_tag "ugly_styles" if browser_is?('ie') %> 44 | <%= stylesheet_link_tag "even_uglier_styles" if browser_is?('ie6') %> 45 | <%= render "seo_spam_content" if browser_is?('robot') %> 46 | 47 | There's also a convenience method `browser_is_mobile?` which is just a shortcut to `browser_is?('mobile')` 48 | 49 | def index 50 | if browser_is_mobile? 51 | # redirect to the mobile site 52 | end 53 | end 54 | 55 | For WebKit-based browsers, you can also check the WebKit version using `browser_webkit_version`. This can be useful for determining what rendering capabilities the browser has. For instance, early versions of Mobile WebKit crash intermittently when you use multiple faces or weights of web fonts. So you might prevent your web fonts from rendering on old WebKit versions: 56 | 57 | <%= stylesheet_link_tag "fonts" unless (browser_is?('ios') and browser_webkit_version < 532) %> 58 | 59 | ## Documentation 60 | 61 | See http://rdoc.info/github/traction/browser_detect/master/frames 62 | 63 | ## Contribute! 64 | 65 | Additions to the text fixtures (list of user agent strings for testing) are always welcome, as are new definitions of browser groupings. Please fork and submit a pull request! 66 | 67 | ## Authors 68 | 69 | Originally based on work by [rlivsey](http://github.com/rlivsey). Current contributors: 70 | 71 | * [faunzy](http://github.com/faunzy) 72 | * [ggilder](http://github.com/ggilder) 73 | * [tmlee](http://github.com/tmlee) 74 | -------------------------------------------------------------------------------- /Rakefile: -------------------------------------------------------------------------------- 1 | require 'bundler' 2 | Bundler::GemHelper.install_tasks 3 | -------------------------------------------------------------------------------- /VERSION: -------------------------------------------------------------------------------- 1 | 0.0.5 -------------------------------------------------------------------------------- /browser_detect.gemspec: -------------------------------------------------------------------------------- 1 | # -*- encoding: utf-8 -*- 2 | files = `git ls-files`.split("\n") 3 | Gem::Specification.new do |s| 4 | s.name = "browser_detect" 5 | s.version = File.read(File.expand_path(File.dirname(__FILE__)+ "/VERSION")).strip 6 | s.platform = Gem::Platform::RUBY 7 | s.authors = ["rlivsey", "faunzy", "tmlee", "ggilder"] 8 | s.email = [] 9 | s.has_rdoc = 'yard' 10 | s.homepage = "http://github.com/traction/browser_detect" 11 | s.summary = "A simple rails browser detection plugin" 12 | s.description = <<-END 13 | Simple rails browser detection based on original plugin by Richard Livsey" 14 | END 15 | 16 | s.required_rubygems_version = ">= 1.3.6" 17 | s.rubyforge_project = "browser_detect" 18 | s.add_development_dependency "bundler", ">= 1.0.0.rc.6" 19 | 20 | s.files = files.reject{|f| f =~ /\.gem$/} 21 | s.executables = files.map{|f| f =~ /^bin\/(.*)/ ? $1 : nil}.compact 22 | s.require_path = 'lib' 23 | end 24 | -------------------------------------------------------------------------------- /init.rb: -------------------------------------------------------------------------------- 1 | if Rails::VERSION::MAJOR == 2 2 | ActionView::Base.send(:include, BrowserDetect) 3 | else 4 | require "railtie" if defined?(Rails) 5 | end 6 | -------------------------------------------------------------------------------- /lib/browser_detect.rb: -------------------------------------------------------------------------------- 1 | module BrowserDetect 2 | # Define browser groupings (mobile, robots, etc.) 3 | # Also define complex queries like IE where we weed out user agents that pose as IE 4 | # The default case just checks if the user agent contains the query string 5 | def browser_is? query 6 | query = query.to_s.strip.downcase 7 | result = case query 8 | when /^ie(\d+)$/ 9 | ua.index("msie #{$1}") && !ua.index('opera') && !ua.index('webtv') 10 | when 'ie' 11 | ua.match(/msie \d/) && !ua.index('opera') && !ua.index('webtv') 12 | when 'yahoobot' 13 | ua.index('yahoo! slurp') 14 | when 'mozilla' 15 | ua.index('gecko') || ua.index('mozilla') 16 | when 'webkit' 17 | ua.match(/webkit|iphone|ipad|ipod/) 18 | when 'safari' 19 | ua.index('safari') && !ua.index('chrome') 20 | when 'ios' 21 | ua.match(/iphone|ipad|ipod/) 22 | when /^robot(s?)$/ 23 | ua.match(/googlebot|msnbot/) || browser_is?('yahoobot') 24 | when 'mobile' 25 | browser_is?('ios') || ua.match(/android|webos|mobile/) 26 | else 27 | ua.index(query) 28 | end 29 | not (result.nil? || result == false) 30 | end 31 | 32 | # Determine the version of webkit. 33 | # Useful for determing rendering capabilities 34 | # For instance, Mobile Webkit versions lower than 532 don't handle webfonts very well (intermittent crashing when using multiple faces/weights) 35 | def browser_webkit_version 36 | if browser_is? 'webkit' 37 | match = ua.match(%r{\bapplewebkit/([\d\.]+)\b}) 38 | match[1].to_f if (match) 39 | end or 0 40 | end 41 | 42 | def browser_is_mobile? 43 | browser_is? 'mobile' 44 | end 45 | 46 | # Gather the user agent and store it for use. 47 | def ua 48 | @ua ||= begin 49 | request.env['HTTP_USER_AGENT'].downcase 50 | rescue 51 | '' 52 | end 53 | end 54 | end 55 | 56 | require 'railtie' if defined? Rails -------------------------------------------------------------------------------- /lib/railtie.rb: -------------------------------------------------------------------------------- 1 | # lib/browser_detect/railtie.rb 2 | module BrowserDetectHelper 3 | class Railtie < Rails::Railtie 4 | initializer "browser_detect.configure_rails_initialization" do 5 | ActionController::Base.send(:include, BrowserDetect) 6 | end 7 | end 8 | end 9 | -------------------------------------------------------------------------------- /test/browser_detect_test.rb: -------------------------------------------------------------------------------- 1 | require File.expand_path(File.dirname(__FILE__)+'/test_helper') 2 | require 'browser_detect' 3 | 4 | class BrowserDetectTest < Test::Unit::TestCase 5 | fixtures :user_agents 6 | 7 | def mock_browser(ua=nil) 8 | BrowserDetectMock.new(ua) 9 | end 10 | 11 | must "deal with nil user agent gracefully" do 12 | assert_nothing_raised do 13 | mock_browser.browser_is?('something') 14 | end 15 | end 16 | 17 | must "correctly mock a user agent string" do 18 | mock = mock_browser("Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1)") 19 | assert_equal("Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1)", mock.request.env['HTTP_USER_AGENT']) 20 | end 21 | 22 | must "identify googlebot" do 23 | mock = mock_browser("Mozilla/5.0 (compatible; Googlebot/2.1; +http://www.google.com/bot.html)") 24 | assert(mock.browser_is?('googlebot')) 25 | end 26 | 27 | must "correctly identify known user agents" do 28 | user_agents(:browsers).each do |browser| 29 | mock = mock_browser(browser['ua']) 30 | browser['name'].each do |name| 31 | assert(mock.browser_is?(name), "Browser '#{browser['nickname']}' did not match name '#{name}'!") 32 | end 33 | end 34 | end 35 | 36 | must "correctly identify webkit versions" do 37 | mock = mock_browser("Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_7; en-us) AppleWebKit/533.4 (KHTML, like Gecko) Version/4.1 Safari/533.4") 38 | assert(mock.browser_is?('webkit')) 39 | assert_equal(533.4, mock.browser_webkit_version) 40 | end 41 | 42 | must "not identify chrome as safari" do 43 | mock = mock_browser("Mozilla/5.0 (Macintosh; Intel Mac OS X 10_7_1) AppleWebKit/535.2 (KHTML, like Gecko) Chrome/15.0.874.81 Safari/535.2") 44 | assert(mock.browser_is?('webkit')) 45 | assert_equal(false, mock.browser_is?('safari')) 46 | assert(mock.browser_is?('chrome')) 47 | 48 | assert_equal(535.2, mock.browser_webkit_version) 49 | end 50 | 51 | must "handle strange user agent strings for iOS apps" do 52 | mock = mock_browser("Times/(null) (iPad; http://www.acrylicapps.com/pulp/)") 53 | assert(mock.browser_is?('ios')) 54 | assert(mock.browser_is?('webkit')) 55 | assert_equal(0, mock.browser_webkit_version) 56 | end 57 | end 58 | 59 | class BrowserDetectMock 60 | include BrowserDetect 61 | 62 | def initialize(user_agent=nil) 63 | @user_agent = user_agent 64 | end 65 | 66 | def request 67 | @req ||= mock_req 68 | end 69 | 70 | def mock_req 71 | req = Object.new 72 | metaclass = class << req; self; end 73 | user_agent = @user_agent 74 | metaclass.send :define_method, :env, Proc.new { {'HTTP_USER_AGENT' => user_agent} } 75 | req 76 | end 77 | end 78 | -------------------------------------------------------------------------------- /test/fixtures/user_agents.yml: -------------------------------------------------------------------------------- 1 | browsers: 2 | - nickname: ie6 3 | ua: "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1)" 4 | name: 5 | - ie 6 | - ie6 7 | - nickname: ie7 8 | ua: "Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 6.0)" 9 | name: 10 | - ie 11 | - ie7 12 | - nickname: ie8 13 | ua: "Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 6.1)" 14 | name: 15 | - ie 16 | - ie8 17 | 18 | - nickname: googlebot 19 | ua: "Mozilla/5.0 (compatible; Googlebot/2.1; +http://www.google.com/bot.html)" 20 | name: 21 | - googlebot 22 | - robots 23 | 24 | - nickname: msnbot 25 | ua: "msnbot/1.1 (+http://search.msn.com/msnbot.htm)" 26 | name: 27 | - msnbot 28 | - robots 29 | - robot 30 | 31 | - nickname: yahoobot 32 | ua: "Mozilla/5.0 (compatible; Yahoo! Slurp; http://help.yahoo.com/help/us/ysearch/slurp)" 33 | name: 34 | - yahoobot 35 | - robots 36 | 37 | - nickname: "iphone 3" 38 | ua: "Mozilla/5.0 (iPhone; U; CPU iPhone OS 3_0 like Mac OS X; en-us) AppleWebKit/528.18 (KHTML, like Gecko) Version/4.0 Mobile/7A341 Safari/528.16" 39 | name: 40 | - iphone 41 | - ios 42 | - webkit 43 | - mobile 44 | - safari 45 | 46 | - nickname: "iphone 4" 47 | ua: "Mozilla/5.0 (iPhone; U; CPU iPhone OS 4_0 like Mac OS X; en-us) AppleWebKit/532.9 (KHTML, like Gecko) Version/4.0.5 Mobile/8A293 Safari/6531.22.7" 48 | name: 49 | - iphone 50 | - ios 51 | - webkit 52 | - mobile 53 | - safari 54 | 55 | - nickname: "safari 4.1" 56 | ua: "Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_7; en-us) AppleWebKit/533.4 (KHTML, like Gecko) Version/4.1 Safari/533.4" 57 | name: 58 | - safari 59 | - webkit 60 | 61 | - nickname: "safari 4.0.4" 62 | ua: "Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_6_3; en-us) AppleWebKit/531.21.11 (KHTML, like Gecko) Version/4.0.4 Safari/531.21.10" 63 | name: 64 | - safari 65 | - webkit 66 | 67 | - nickname: "Android G1" 68 | ua: "Mozilla/5.0 (Linux; U; Android 1.1; en-gb; dream) AppleWebKit/525.10+ (KHTML, like Gecko) Version/3.0.4 Mobile Safari/523.12.2" 69 | name: 70 | - webkit 71 | - safari 72 | - mobile 73 | - android 74 | 75 | - nickname: "Android Emulator" 76 | ua: "Mozilla/5.0 (Linux; U; Android 1.0; en-us; generic) AppleWebKit/525.10+ (KHTML, like Gecko) Version/3.0.4 Mobile Safari/523.12.2" 77 | name: 78 | - webkit 79 | - safari 80 | - mobile 81 | - android 82 | 83 | - nickname: "Android Nexus" 84 | ua: "Mozilla/5.0 (Linux; U; Android 2.1; en-us; Nexus One Build/ERD62) AppleWebKit/530.17 (KHTML, like Gecko) Version/4.0 Mobile Safari/530.17" 85 | name: 86 | - webkit 87 | - safari 88 | - mobile 89 | - android 90 | 91 | - nickname: "Palm Pre" 92 | ua: "Mozilla/5.0 (webOS/1.4.0; U; en-US) AppleWebKit/532.2 (KHTML, like Gecko) Version/1.0 Safari/532.2 Pre/1.1" 93 | name: 94 | - webkit 95 | - safari 96 | - webos 97 | - mobile 98 | 99 | - nickname: "Palm Pixi" 100 | ua: "Mozilla/5.0 (webOS/1.4.0; U; en-US) AppleWebKit/532.2 (KHTML, like Gecko) Version/1.0 Safari/532.2 Pixi/1.1" 101 | name: 102 | - webkit 103 | - safari 104 | - webos 105 | - mobile 106 | -------------------------------------------------------------------------------- /test/test_helper.rb: -------------------------------------------------------------------------------- 1 | require 'rubygems' 2 | require 'test/unit' 3 | require 'yaml' 4 | 5 | $LOAD_PATH << File.expand_path(File.dirname(__FILE__) + '/../lib') 6 | 7 | module Test::Unit 8 | # @ see http://gist.github.com/316780 9 | # Used to fix a minor minitest/unit incompatibility in flexmock 10 | # AssertionFailedError = Class.new(StandardError) 11 | 12 | class TestCase 13 | 14 | def self.must(name, &block) 15 | test_name = "test_#{name.gsub(/\s+/,'_')}".to_sym 16 | defined = instance_method(test_name) rescue false 17 | raise "#{test_name} is already defined in #{self}" if defined 18 | if block_given? 19 | define_method(test_name, &block) 20 | else 21 | define_method(test_name) do 22 | flunk "No implementation provided for #{name}" 23 | end 24 | end 25 | end 26 | 27 | end 28 | end 29 | 30 | # @see http://push.cx/2007/fixtures-in-ruby-unit-tests 31 | class Test::Unit::TestCase 32 | @@fixtures = {} 33 | def self.fixtures *args 34 | [args].flatten.each do |fixture| 35 | self.class_eval do 36 | # add a method name for this fixture type 37 | define_method(fixture) do |item| 38 | # load and cache the YAML 39 | @@fixtures[fixture] ||= YAML::load_file(File.dirname(__FILE__)+"/fixtures/#{fixture.to_s}.yml") 40 | @@fixtures[fixture][item.to_s] 41 | end 42 | end 43 | end 44 | end 45 | end 46 | --------------------------------------------------------------------------------