Browserino

Browserino is a tool that parses a user agent string into a convenient object to work with. This allows developers to gather information about the system of a user on a website without having to ask countless questions about their browser / OS versions.

Status

Licence Gem Version Issues Build Status Coverage Status

Table of Contents

Click to expand Table of Contents
  • Usage
    show 5 items
  • Methods
    show 31 items
  • Magic methods
  • Supported
    show 8 items
  • Sources

    Many thanks to the creators and maintainers of the following sources of user agents and timelines:

    Changelog

    dates are in dd-mm-yyyy format older changes can be found in the changelog

    10-07-2019 VERSION 4.5.0

    • Add: browser user-agents
      • :basilisk
      • :facebook_app
    • Add: :goanna rendering engine detection
    • Add: :catalina label for MacOS 10.15.x

    21-10-2018 VERSION 4.4.1

    • Fix: Browserino.parse handles nil and false by converting them to an empty string

    23-06-2018 VERSION 4.4.0

    • Update: Gem dependencies
    • Add: ability to supply HTTP headers to Browserino for extra information
    • Improve: location will be detected from HTTP headers when available (on by default in Rails)
    • Add: prop_missing DSL for last-minute info gathering
    • Add: Android :pie label
    • Add: :falkon alias for :qupzilla orange_browser
    • Add: :alohabrowser
    • Add: :iris
    • Add: :otter
    • Add: :chedot
    • Add: :cm_browser
    • Add: :diglo
    • Add: :diigo_browser
    • Add: :flyflow
    • Add: :freebox
    • Add: :kuaiso
    • Add: :lovense
    • Add: :slimjet
    • Add: :zetakey
    • Add: :wkbrowser
    • Add: :yolobrowser
    • Add: :whale
    • Add: :vivo
    • Add: :jasmine
    • Add: :seraphic_sraf
    • Add: :phantomjs
    • Add: :slimerjs
    • Add: :liebao
    • Add: :w3m
    • Add: :charon
    • Add: :cent
    • Add: :jig_browser_web
    • Add: :blazer

    Installation

    Add this line to your application's Gemfile:

    gem 'browserino'
    

    And then execute:

    $ bundle
    

    Or install it yourself with:

    $ gem install browserino
    

    After installing the gem globally or in your application you'll have to require the gem before being able to use it.

    require 'browserino'
    

    Afterwards, the gem is loaded and you can proceed by calling:

    Browserino.parse '<user agent>'[, {'request-header' => 'header-value', ...}]
    

    The first argument to Browserino.parse is a user agent string. This is the string that browsers send to webservers for identification. The second (optional) argument to Browserino.parse is a hash that contains HTTP headers. When a property could not be found, these headers will be used by Browserino to attempt to extract it from there if a prop_missing handler is defined for it.

    Browserino is tested with the following ruby versions

    • 2.0.0
    • 2.1.0
    • 2.2.0
    • 2.3.0
    • 2.4.0
    • 2.5.0

    Rails (>= 3.2.0)

    If you're using Rails (>= 3.2.0) you'll have access to a client object. Browserino will initialize itself using request.headers['User-Agent'] and will also use Rails request.headers to find extra information that could not be found in the user agent (e.g. user locales).

    A quick example on how to get going:

    class ExampleController < ApplicationController
      def example_method
        render json: client.to_json
      end
    end
    

    Initializer

    If you would like to extend Browserino within your rails app, this is certainly possible. Create a file config/initializers/browserino.rb and add the following:

    Browserino.config.define do
      # your magic here
    end
    

    Please skip ahead to the Config part of the documentation, that section contains information on how to make use of Browserino's builtin DSL

    Usage

    Client

    the parse method will always return a Browserino::Client object. This object is special in that it simply resonds to everything. If a property does not exist or isn't defined on the current instance of client then it will simply return nil. Parsing an empty user agent will result in the following output:

    client = Browserino.parse ''
    client.is_a? Browserino::Client # => true
    

    Prior to version 4.4.1, Browserino.parse nil or Browserino.parse false were invalid. As of 4.4.1 an empty user agent ('') is assumed when these values are used.

    Formatting

    This section explains the output / return values given by calling a method on an instantiated Browserino::client object.

    Non version-like properties

    Non-version-like properties are properties that are not solely digits seperated by a delimiter like '.' or '_'. All the non-version-like property values (which aren't Array or Hash instances) will be formatted in the following order:

    • skip following steps if Array or Hash
    • stringify value
    • lowercase all characters
    • strip spaces on either side
    • replace one or more spaces or dashes with a single underscore (_)
    • convert to symbol
    client = Browserino.parse 'Mozilla/5.0 (Windows NT 6.1; Win64; x64; rv:24.0) Gecko/20100101 Firefox/24.0 Waterfox/24.0'
    
    client.name           # => :waterfox
    client.type           # => :browser
    client.platform       # => :windows
    client.engine         # => :gecko
    client.platform_label # => :windows7
    

    Version-like properties

    All the version-like property values will be converted to Browserino::Version objects. These objects are comparable to numbers and version-like strings (e.g. '1.2.4')

    client = Browserino.parse 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/37.0.2062.124 Safari/537.36'
    
    client.version                           # => [37, 0, 2062, 124]
    client.version.is_a? Browserino::Version # => true
    client.version.is_a? Array               # => true
    
    # get full version
    client.version.full # => '37.0.2062.124'
    client.version.to_s # => '37.0.2062.124'
    
    # get version components [major, minor, patch]
    client.version.major # => 37
    client.version.minor # => 0
    client.version.patch # => 2062
    
    # comparing with integers
    client.version >= 32          # => true
    client.version < 40           # => true
    
    # comparing with strings
    client.version >= '37.0.2000' # => true
    client.version > '37.1.2000'  # => false
    

    Config

    Matchers

    Browserino is based on a concept of matchers, a matcher is a single client entity. They are defined in a Browserino.config.define { ... } block. A small example:

    Browserino.config.define do
      # define a matcher that looks for the pattern %r{mybrowser}i in the UA
      # when found, give it a name of :mybrowser and a type of :browser
      match %r{mybrowser}i, name: :mybrowser, type: :browser
    end
    
    ua = "Mozilla/5.0 (Windows NT 5.1; rv: 1.8.3.2) Gecko/13370420 MyBrowser/1.4.9"
    
    client = Browserino.parse ua
    client.to_h
    # => { :name             => :mybrowser,
    #      :type             => :browser,
    #      :locale           => nil,
    #      :locales          => [],
    #      :architecture     => nil,
    #      :mobile           => false,
    #      :smarttv          => false,
    #      :tablet           => false,
    #      :platform         => :windows,
    #      :platform_version => [5, 1],
    #      :version          => [1, 4, 9],
    #      :engine_version   => [],
    #      :device           => nil,
    #      :label            => nil,
    #      :engine_label     => nil,
    #      :platform_label   => :xp }
    
    client.name
    # => :mybrowser
    

    In the above example, I've simply created a fictive UA to simulate how to add a matcher to Browserino. This matcher however, isn't all that powerful as all it does is match a pattern and assign a name and type. Browserino hosts a large list of defaults that you don't have to support anymore, these are all matchers themselves. The matcher for Firefox looks like this for instance:

    Browserino.config.define do
      # this matcher is written using the dsl style, calling a method and giving
      # it a value, something to look for in the user agent, or a block will
      # add that method name as a property with the parsed value as result
    
      # match pattern %r{firefox|phoenix}i
      match %r{firefox|phoenix}i do
        # give this matcher a name of :firefox
        name           :firefox
        # engine is a regexp, these will be matched on the UA and return their
        # respective match (what is captured in a capture group)
        engine         %r{(gecko|servo)}i
        # engine_version is also a regexp, because the gecko version isn't defined
        # like gecko/[VERSION] but rather rv:[VERSION] while for servo it is
        # servo/[VERSION]
        engine_version %r{(?:rv:\s?|servo/)([\d\.]+)}i
        # last but not least we can also define a block, the block
        # will be executed once after all other properties have been defined on a
        # client, the block itself will be executed in client context so
        # everything you can do on a `client` can also be used within blocks
        modern?        { version >= 50 }
      end
    end
    

    Now as you may have noticed, there is no type defined in the above matcher, so how does Browserino know that Firefox is a browser? In truth, you aren't seeing the whole file here, if you want to go take a look, you can find it here. To elaborate, Browserino has a few builtin convenience methods That allow presetting of type in a nice way using:

    Browserino.config.define do
      # sets type: :browser for each matcher defined within the block
      browsers do
        # matcher %r{...}i
        # matcher %r{...}i
        # matcher %r{...}i
      end
    
      # sets type: :email for each matcher defined within the block
      emails do
        # ....
      end
    
      # sets type: :validator for each matcher defined within the block
      validators do
        # ....
      end
    
      # sets type: :bot for each matcher defined within the block
      bot do
        # ....
      end
    
      # sets type: :library for each matcher defined within the block
      libraries do
        # ....
      end
    end
    

    Under the hood, these all use a more generic preset method as also seen in the source:

    # snipped from Browserino::Config
    def emails(opts = {}, &block)
      preset opts.merge(type: :email), &block
    end
    
    def validators(opts = {}, &block)
      preset opts.merge(type: :validator), &block
    end
    
    def browsers(opts = {}, &block)
      preset opts.merge(type: :browser), &block
    end
    
    def bots(opts = {}, &block)
      preset opts.merge(type: :bot), &block
    end
    
    def libraries(opts = {}, &block)
      preset opts.merge(type: :library), &block
    end
    

    All that these methods, and preset do is they set an instance variable with all the properties you'll have supplied in an options hash and these will simply be applied to every matcher defined within them. The Firefox matcher could have been written in the following ways:

    Browserino.config.define do
      # using `browsers`
    
      browsers do
        match %r{firefox|phoenix}i do
          name           :firefox
          engine         %r{(gecko|servo)}i
          engine_version %r{(?:rv:\s?|servo/)([\d\.]+)}i
          modern?        { version >= 50 }
        end
      end
    
      # using `preset`
      preset type: :browser do
        match %r{firefox|phoenix}i do
          name           :firefox
          engine         %r{(gecko|servo)}i
          engine_version %r{(?:rv:\s?|servo/)([\d\.]+)}i
          modern?        { version >= 50 }
        end
      end
    
      # define type inline
      match %r{firefox|phoenix}i, type: :browser do
        name           :firefox
        engine         %r{(gecko|servo)}i
        engine_version %r{(?:rv:\s?|servo/)([\d\.]+)}i
        modern?        { version >= 50 }
      end
      # or
      match %r{firefox|phoenix}i do
        name           :firefox
        type           :browser
        engine         %r{(gecko|servo)}i
        engine_version %r{(?:rv:\s?|servo/)([\d\.]+)}i
        modern?        { version >= 50 }
      end
    end
    

    You'll have also noticed that so far, no version property has been defined either, and that when we created the mybrowser matcher, it could actually find it's version without supplying it. This is a different type of matcher at work. It is not a matcher for a single entity but rather, one that will be applied by default if nothing is specified. It is then capable of extracting preprocessed information into a pattern that will magically detect the version for you.

    They look like this:

    Browserino.config.define do
      #           prop             pattern                          flags
      smart_match :version,        with: ':name[\s/]?v?([\d\._]+)', flags: [:i]
      smart_match :engine_version, with: ':engine[\s/]?([\d\._]+)', flags: [:i]
    end
    

    What the above code does is, if no version property has been supplied to a matcher, it will try to look for a smart_watcher with a prop of version, then, using other parsed properties, it will replace the :name in this case with mybrowser (if we were to use our made-up UA). The pattern would then look like this: /mybrowser[\s/]?v?([\d\.]+)/i and go a head and match itself against the UA to get a value back.

    If you supply your own version property, this smart_matcher will not be applied.

    Aliasses

    Aliasses are a bit simpler than Matchers, they look like this taken from the source:

    Browserino.config.define do
      # aliasses will be defined after a Client has been initialized
      # only the aliasses matching the Client will be defined
      alias_for :firefox,    :ff
      alias_for :windows,    :win
      alias_for :windows7,   :win7
      alias_for :windows8,   :win8
      alias_for :windows10,  :win10
      alias_for :macintosh,  :mac, :osx, :macos
      alias_for :blackberry, :bb
      alias_for :ie,         :internet_explorer
      alias_for :facebook,   :fb
      alias_for :duckduckgo, :ddg
      alias_for :chromeos,   :cros
    end
    

    What they do is they look for a value within the properties of a specific client and if found, all the aliasses specified will also be applied to that client. This means that for a client.is? :firefox you can do client.is? :ff or client.ff? instead. This works for all the names like name, label, engine, engine_label, platform and platform_label

    Labels

    A label could be a codename or a specific OS name for instance, it applies to a name and it's respective version combination. It autodetects against which version it should match, this will always be the correct version. Here are some examples (also not the complete file - source):

    # small subset of labels from the source
    Browserino.config.define do
      label :yosemite,           for: :macintosh, range: '10.10'..'10.10.9'
      label :el_capitan,         for: :macintosh, range: '10.11'..'10.11.9'
      label :sierra,             for: :macintosh, range: '10.12'..'10.12.9'
    end
    

    So now it looks for a value of :macintosh in the properties gathered from a client, if it finds it, it will remember it's key (e.g. platform in this case.) and use it to find out the current version (e.g. platform_version in this case). If a version is supplied it will be compared to be >= min and <= max (which is inclusive) where min and max are the left and right values of the range option respectively.

    HTTP headers

    Browserino as of version 4.4.0 accepts a hash of HTTP headers as a second argument. This adds more detailed information (primarily #locales property is added and #locale is read from it when not found). For Rails, this is already implemented and will work out of the box, adding support for it is as simple as supplying a Hash of HTTP headers (say, what you get from a request.headers in Rails or request from Sinatra) as second argument to Browserino.parse.

    This user agent does not have a locale but when we supply the HTTP_ACCEPT_LANGUAGE (or just Accept-Language) header it will find all languages ordered by user preference in a #locales property. For the #locale itself, if it is not found it will attempt to use #locales.first:

    client = Browserino.parse(
        'Mozilla/5.0 (Linux; Android 5.1; ZTE Blade L6 Build/LMY47I) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/52.0.2743.91 Mobile Safari/537.36',
        {'HTTP_ACCEPT_LANGUAGE' => 'en-US,en;q=0.9,nl-NL;q=0.6,nl;q=0.7'}
    )
    
    client.locales
    # => [:en_us :en :nl :nl_nl]
    
    client.locale
    # => :en_us
    

    The behavior is defined by prop_missing hooks which will be run just before a new Client (and after all data gathering / processing is done) is initialized. The hooks for :locales and :locale look like this:

    Browserino.config.define do
      prop_missing :locales do |http_headers|
        http_headers[:accept_language]
          .to_s.scan(/(\w{2}(?:[_\-]\w{2})?)(?:;q=([\d.]+))?/i)
          .map { |(locale, quality)| [locale, (quality || 1).to_f] }
          .sort_by { |a| -a[1] }
          .map(&:first)
      end
    
      prop_missing :locale do |_http_headers, props|
        props[:locales].first
      end
    end
    

    The first block argument is a processed version of a hash with HTTP headers. Keys of this hash are "normalized", any HTTP_ prefix is removed, - is replaced with _ and finally the key is lowercased and converted to a symbol. This allows you to pass in raw header names as seen in the request like Accept-Language or Rails / Sinatra (read: rack) HTTP headers like HTTP_ACCEPT_LANGUAGE. Both will end up as :accept_language allowing for easy usage.

    The second block argument are the processed properties. This is all the information from the UA / HTTP headers that Browserino could find combined.

    What you may also notice here is that in the second prop_missing we are leveraging the first prop_missing. They are executed in order of definition to make it easy to "chain" property fallbacks.

    Methods

    The below methods are available by default, all methods with a questionmark at the end return either boolean true or false or nil.

    #name

    Returns either a symbol containing the name of the agent or nil otherwise.

    client = Browserino.parse 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_1) AppleWebKit/537.36 (KHTML, like Gecko) Colibri/1.0.0-beta.9 Chrome/52.0.2743.82 Electron/1.3.13 Safari/537.36'
    
    client.name
    # => :colibri
    

    #name?(sym, opts = {})

    Returns true if supplied sym equals (==) the current client.name, if opts contains a :version key, it will be compared against #version Returns false if the supplied criteria doesn't match the current client. Returns nil if sym isn't the current client, regardless of version constraint

    client = Browserino.parse 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_1) AppleWebKit/537.36 (KHTML, like Gecko) Colibri/1.0.0-beta.9 Chrome/52.0.2743.82 Electron/1.3.13 Safari/537.36'
    
    client.name? :colibri
    # => true
    
    client.name? :colibri, version: '1.0.0'
    # => true
    
    client.name? :colibri, version: '1.1.0'
    # => false
    
    client.name? :not_colibri
    # => nil
    
    client.name? :not_colibri, version: '1.0.0'
    # => nil
    

    #version

    Returns a Browserino::Version object. This object allows for more flexible comparing of long version numbers where simple numbers or floats don't suffice.

    client = Browserino.parse 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_1) AppleWebKit/537.36 (KHTML, like Gecko) Colibri/1.0.0-beta.9 Chrome/52.0.2743.82 Electron/1.3.13 Safari/537.36'
    
    client.version
    # => [1, 0, 0]
    
    client.version.is_a? Browserino::Version
    # => true
    
    # logical operators can be used for comparison
    
    # it can work with integers
    client.version >= 1
    # => true
    
    # or strings
    client.version >= '1.0.0'
    # true
    
    client.version.to_s
    # or
    client.version.full
    # => '1.0.0'
    

    #version?(version)

    Returns true if the supplied version equals (==) the current clients version. Returns false otherwise

    client = Browserino.parse 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_1) AppleWebKit/537.36 (KHTML, like Gecko) Colibri/1.0.0-beta.9 Chrome/52.0.2743.82 Electron/1.3.13 Safari/537.36'
    
    client.version? 1
    # true
    
    client.version? '1.0'
    # true
    
    client.version? '1.0.0'
    # true
    
    client.version? '1.0.1'
    # false
    

    #engine

    Returns either a symbol containing the engine of the agent or nil otherwise.

    client = Browserino.parse 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_1) AppleWebKit/537.36 (KHTML, like Gecko) Colibri/1.0.0-beta.9 Chrome/52.0.2743.82 Electron/1.3.13 Safari/537.36'
    
    client.engine
    # => :webkit
    

    #engine?(sym, opts = {})

    Returns true if supplied sym equals (==) the current client.version, if opts contains a :version key, it will be compared against #engine_version Returns false if the supplied criteria doesn't match the current client. Returns nil if sym isn't the current client, regardless of version constraint

    client = Browserino.parse 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_1) AppleWebKit/537.36 (KHTML, like Gecko) Colibri/1.0.0-beta.9 Chrome/52.0.2743.82 Electron/1.3.13 Safari/537.36'
    
    client.engine? :webkit
    # => true
    
    client.engine? :webkit, version: '537.36'
    # => true
    
    client.engine? :webkit, version: '537.38'
    # => false
    
    client.engine? :not_webkit
    # => nil
    
    client.engine? :not_webkit, version: '537.36'
    # => nil
    

    #engine_version

    Returns a Browserino::Version object.

    client = Browserino.parse 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_1) AppleWebKit/537.36 (KHTML, like Gecko) Colibri/1.0.0-beta.9 Chrome/52.0.2743.82 Electron/1.3.13 Safari/537.36'
    
    client.engine_version
    # => [537, 36]
    
    client.engine_version >= 530
    # true
    
    client.engine_version.to_s
    # or
    client.engine_version.full
    # => '537.36'
    

    #engine_version?(version)

    Returns true if the supplied version equals (==) the current clients engine_version. Returns false otherwise

    client = Browserino.parse 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_1) AppleWebKit/537.36 (KHTML, like Gecko) Colibri/1.0.0-beta.9 Chrome/52.0.2743.82 Electron/1.3.13 Safari/537.36'
    
    client.engine_version? '537.36'
    # true
    

    #platform

    Returns either a symbol containing the platform of the agent or nil otherwise.

    client = Browserino.parse 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_1) AppleWebKit/537.36 (KHTML, like Gecko) Colibri/1.0.0-beta.9 Chrome/52.0.2743.82 Electron/1.3.13 Safari/537.36'
    
    client.platform
    # => :macintosh
    

    #platform?(sym, opts = {})

    Returns true if supplied sym equals (==) the current client.platform, if opts contains a :version key, it will be compared against #platform_version Returns false if the supplied criteria doesn't match the current client. Returns nil if sym isn't the current client, regardless of version constraint

    client = Browserino.parse 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_1) AppleWebKit/537.36 (KHTML, like Gecko) Colibri/1.0.0-beta.9 Chrome/52.0.2743.82 Electron/1.3.13 Safari/537.36'
    
    client.platform? :macintosh
    # => true
    
    client.platform? :macintosh, version: '10.12'
    # => true
    
    client.platform? :macintosh, version: '10.12.2'
    # => false
    
    client.platform? :not_macintosh
    # => nil
    
    client.platform? :not_macintosh, version: '10.12.1'
    # => nil
    

    #platform_label

    Returns either a symbol containing the platform_label of the agent or nil otherwise.

    client  = Browserino.parse 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_1) AppleWebKit/537.36 (KHTML, like Gecko) Colibri/1.0.0-beta.9 Chrome/52.0.2743.82 Electron/1.3.13 Safari/537.36'
    client2 = Browserino.parse 'Mozilla/5.0 (Linux; Android 7.0; LG-H910 Build/NRD90C) AppleWebKit/537.26 (KHTML, like Gecko) Chrome/51.0.2704.90 Mobile Safari/537.36'
    
    # macOS 10.12's name is Sierra
    client.platform_label
    # => :sierra
    
    # Android 7's name is Nougat
    client2.platform_label
    # => :nougat
    

    #platform_version

    Returns a Browserino::Version object.

    client = Browserino.parse 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_1) AppleWebKit/537.36 (KHTML, like Gecko) Colibri/1.0.0-beta.9 Chrome/52.0.2743.82 Electron/1.3.13 Safari/537.36'
    
    client.platform_version
    # => [10, 12, 1]
    
    client.platform_version >= 10
    # => true
    
    client.platform_version.to_s
    # or
    client.platform_version.full
    # => '10.12.1'
    

    #platform_version?(version)

    Returns true if the supplied version equals (==) the current clients platform_version. Returns false otherwise

    client = Browserino.parse 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_1) AppleWebKit/537.36 (KHTML, like Gecko) Colibri/1.0.0-beta.9 Chrome/52.0.2743.82 Electron/1.3.13 Safari/537.36'
    
    client.platform_version? '10.12'
    # => true
    

    #device

    Returns either a symbol containing the device of the client or nil otherwise

    client = Browserino.parse 'Mozilla/5.0 (Linux; Android 5.1; ZTE Blade L6 Build/LMY47I) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/52.0.2743.91 Mobile Safari/537.36'
    
    client.device
    # => :zte
    

    #device?(sym)

    Returns true if the supplied sym equals (==) the current clients device.

    client = Browserino.parse 'Mozilla/5.0 (Linux; Android 5.1; ZTE Blade L6 Build/LMY47I) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/52.0.2743.91 Mobile Safari/537.36'
    
    client.device
    # => :zte
    

    #architecture

    Returns either nil, :x32 or :x64 depending on wether it could be found in the user agent

    client  = Browserino.parse 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_1) AppleWebKit/537.36 (KHTML, like Gecko) Colibri/1.0.0-beta.9 Chrome/52.0.2743.82 Electron/1.3.13 Safari/537.36'
    client2 = Browserino.parse 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/45.0.2454.99 Safari/537.36 Vivaldi/1.0.288.3'
    
    client.architecture
    # => nil
    
    client2.architecture
    # => :x64
    

    #architecture?(sym)

    sym must be one of :x32 or :x64, returns true if it matches the current client architecture, false otherwise.

    client = Browserino.parse 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_1) AppleWebKit/537.36 (KHTML, like Gecko) Colibri/1.0.0-beta.9 Chrome/52.0.2743.82 Electron/1.3.13 Safari/537.36'
    
    client.architecture? :x64
    # => false
    

    #locale

    Returns either nil or a locale if present. Locales are formatted like :en_us for en-US and en_US and just :en for en.

    client = Browserino.parse 'Mozilla/5.0 (Macintosh; U; Macintosh; en-US; Valve Steam GameOverlay/1329175982; ) AppleWebKit/534.1 (KHTML, like Gecko) Chrome/6.0.444.0 Safari/534.1'
    
    client.locale
    # => :en_us
    

    #locale?

    Returns true or false based on locale presence.

    client = Browserino.parse 'Mozilla/5.0 (Macintosh; U; Macintosh; en-US; Valve Steam GameOverlay/1329175982; ) AppleWebKit/534.1 (KHTML, like Gecko) Chrome/6.0.444.0 Safari/534.1'
    
    client.locale?
    # => true
    

    #locales

    Returns an array of locales found in Accept-Language HTTP header. This only works when Browserino.parse is invoked with a hash containing the headers as second argument. When locales are found using this method, they will be used instead of the #locale found in the user agent (the locale will be replaced with the highest quality locale from the header).

    client = Browserino.parse 'Mozilla/5.0 (Macintosh; U; Macintosh; pt-BR; Valve Steam GameOverlay/1329175982; ) AppleWebKit/534.1 (KHTML, like Gecko) Chrome/6.0.444.0 Safari/534.1',
                              {'HTTP_ACCEPT_LANGUAGE' => 'en-US,en;q=0.9,nl-NL;q=0.8,nl;q=0.7'}
    
    # locale is :en_us because it is present in headers and thus overwrites :pt_br
    client.locale
    # => :en_us
    
    # the :pt_br locale gets added as the least preferred locale instead
    client.locales
    # => [:en_us, :en, :nl_nl, :nl, :pt_br]
    

    #locales?

    Returns true or false depending on wether any locales were found at all (e.g. from UA and/or HTTP headers).

    client = Browserino.parse 'Mozilla/5.0 (Macintosh; U; Macintosh; pt-BR; Valve Steam GameOverlay/1329175982; ) AppleWebKit/534.1 (KHTML, like Gecko) Chrome/6.0.444.0 Safari/534.1',
                              {'HTTP_ACCEPT_LANGUAGE' => 'en-US,en;q=0.9,nl-NL;q=0.8,nl;q=0.7'}
    
    client.locales?
    # => true
    

    #x64?

    A shorthand for client.architecture? :x64 returns a boolean value

    client = Browserino.parse 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_1) AppleWebKit/537.36 (KHTML, like Gecko) Colibri/1.0.0-beta.9 Chrome/52.0.2743.82 Electron/1.3.13 Safari/537.36'
    
    client.x64?
    # => false
    

    #x32?

    A shorthand for client.architecture? :x32 returns a boolean value

    client = Browserino.parse 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_1) AppleWebKit/537.36 (KHTML, like Gecko) Colibri/1.0.0-beta.9 Chrome/52.0.2743.82 Electron/1.3.13 Safari/537.36'
    
    client.x32?
    # => false
    

    #mobile and #mobile?

    Returns boolean true or false depending on wether the device user agent is a mobile device

    client = Browserino.parse 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_1) AppleWebKit/537.36 (KHTML, like Gecko) Colibri/1.0.0-beta.9 Chrome/52.0.2743.82 Electron/1.3.13 Safari/537.36'
    
    client.mobile
    # or
    client.mobile?
    # => false
    

    #type

    There are currently 6 types defined, :browser, :bot, :library, :validator, :email and :unknown (default) This method simply returns the type of the current client, e.g.

    client = Browserino.parse 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_1) AppleWebKit/537.36 (KHTML, like Gecko) Colibri/1.0.0-beta.9 Chrome/52.0.2743.82 Electron/1.3.13 Safari/537.36'
    
    client.type
    # => :browser
    

    #type?(sym)

    Returns true if sym equals (==) the current clients type

    client = Browserino.parse 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_1) AppleWebKit/537.36 (KHTML, like Gecko) Colibri/1.0.0-beta.9 Chrome/52.0.2743.82 Electron/1.3.13 Safari/537.36'
    
    client.type? :browser
    # => true
    
    client.type? :bot
    # => false
    

    #like

    Like is something different. Within the defaults a few browsers are defined to be like others. This is the full list:

    • Like :chrome includes:
      • :brave
      • :vivaldi
      • :colibri
      • :rockmelt
      • :flock
      • :comodo_dragon
      • :steam (Game interface)
      • :chromeplus
      • :bluechrome
      • :awesomium
      • :qqbrowser
      • :yabrowser
      • :coolnovo
      • :iridium
      • :origin
      • :puffin
      • :fluid
      • :hana
      • :iron
    • Like :safari includes:
      • :bolt
      • :samsungbrowser
      • :webosbrowser
      • :stainless
      • :omnibrowser
      • :cheshire
      • :skyfire
      • :mercury
      • :rekonq
      • :arora
      • :raptr
      • :icab
      • :silk
      • :qt
    • Like :firefox includes:
      • :seamonkey
      • :minefield (FF nightly)
      • :kmeleon
      • :kninja
      • :granparadiso
      • :tenfourfox
      • :enigmafox
      • :kazehakase
      • :shiretoko
      • :classilla
      • :cometbird
      • :icedragon
      • :palemoon
      • :namoroka
      • :songbird
      • :vonkeror
      • :conkeror
      • :bonecho
      • :chimera
      • :lolifox
      • :lorentz
      • :myibrow
      • :sylera
      • :iceape
      • :madfox
      • :kapiko
      • :kmlite
      • :beonex
      • :galeon
      • :vision
      • :pogo
      • :orca
      • :superswan
      • :firebird
      • :iceweasel
      • :icecat
      • :waterfox
      • :netscape
      • :prism
      • :lunascape
      • :camino
    • Like :ie includes:
      • :avant_browser
      • :deepnet_explorer
      • :slimbrowser
      • :sleipnir
      • :greenbrowser
      • :browzar
      • :theworld
      • :crazy_browser
      • :solid_core
      • :tencenttraveler
      • :enigma_browser
      • :simulbrowse
      • :netcaptor
      • :irider
      • :kkman
      • :lobo
      • :aol
    # In this case, seamonkey is like firefox so like will represent the same user agent parsed as firefox to gather information
    client = Browserino.parse 'Mozilla/5.0 (Windows NT 5.2; RW; rv:7.0a1) Gecko/20091211 SeaMonkey/9.23a1pre'
    
    client.name
    # => :seamonkey
    
    client.is_a? Browserino::Client
    # => true
    
    client.like.name
    # => :firefox
    
    client.like.is_a? Browserino::Client
    # => true
    
    client == client.like
    # => false
    
    # This client isn't `like` anything, it's just chrome so in this case, we assign the value of like to itself.
    client2 = Browserino.parse 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/47.0.2526.106 Safari/537.36'
    
    client2.name
    # => :chrome
    
    client2.is_a? Browserino::Client
    
    client.like.name
    # => :chrome
    
    client2.like.is_a? Browserino::Client
    # => true
    
    client == client.like
    # => true
    

    #like?(sym, opts = {})

    This method acts much like the name? method on a client except that like like method will match a broader set of things like it. Additionally, you can also supply an optional :version option which will then be compared to the like.version property. Seamonkey in the last example is the browser I chose to be an example for the like documentation, since Seamonkey is like firefox we can do this:

    # In this case, seamonkey is like firefox so like will represent the same user agent parsed as firefox to gather information
    client = Browserino.parse 'Mozilla/5.0 (Windows NT 5.2; RW; rv:7.0a1) Gecko/20091211 SeaMonkey/9.23a1pre'
    
    client.name
    # => :seamonkey
    
    client.name? :seamonkey
    # => true
    
    client.name? :firefox
    # => false
    
    client.like? :firefox
    # => true
    
    # A version can be supplied too but in this case, FireFox's version number
    # isn't present in the user agent
    # client.like? :firefox, version: '9.23'
    # => false
    

    #is?(sym, opts = {})

    The is? method is a bit more loose, it will recognize what it is and what it isn't automagically depending on what's passed in. It can be a more general replacement for #name?, #engine? and #platform?

    #not

    Inverts the result returned by any method ending in a question mark (e.g. client.version? == true && client.not.version? == false)

    #not?(sym, opts = {})

    An inverted version of #is?

    client = Browserino.parse 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/47.0.2526.106 Safari/537.36'
    client.to_h
    # => { :name             => :chrome,
    #      :type             => :browser,
    #      :locale           => nil,
    #      :architecture     => nil,
    #      :mobile           => false,
    #      :smarttv          => false,
    #      :tablet           => false,
    #      :platform         => :macintosh,
    #      :platform_version => [10, 11, 2],
    #      :version          => [47, 0, 2526, 106],
    #      :engine           => :webkit,
    #      :engine_version   => [537, 36],
    #      :device           => nil
    #      :label            => nil,
    #      :engine_label     => nil,
    #      :platform_label   => :el_capitan }
    
    client.not? :chrome
    # => false
    
    # compares to a name of chrome with version of 47.0
    client.not? :chrome, version: '47.0'
    # => false
    
    # but it also knows about platforms
    client.not? :macintosh
    # => false
    
    # the version is matched against the platform_version in this case
    client.not? :macintosh, version: '10.11'
    # => false
    
    # and engine names
    client.not? :webkit
    # => false
    
    # where the version is compared to the engine_version
    client.not? :webkit, version: 537
    # => false
    

    Magic methods

    Names

    For each of #name, #engine, #platform, #platform_label and #device, upon instantiation of the current client object, the results of these methods will also be defined as methods if they aren't nil e.g:

    client = Browserino.parse 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/47.0.2526.106 Safari/537.36'
    client.to_h
    # => { :name             => :chrome,
    #      :type             => :browser,
    #      :locale           => nil,
    #      :architecture     => nil,
    #      :mobile           => false,
    #      :smarttv          => false,
    #      :tablet           => false,
    #      :platform         => :macintosh,
    #      :platform_version => [10, 11, 2],
    #      :version          => [47, 0, 2526, 106],
    #      :engine           => :webkit,
    #      :engine_version   => [537, 36],
    #      :device           => nil,
    #      :label            => nil,
    #      :engine_label     => nil,
    #      :platform_label   => :el_capitan }
    
    ### NAME ###
    
    # since client.name is :chrome, a method #chrome? is defined
    client.chrome?
    # => true
    
    # an optional version can also be suppied
    client.chrome? 47
    # => true
    
    # since client.name isn't :firefox, no method named #firefox? is defined
    # instead, it is sent to method_missing which will return nil
    client.firefox?
    # => nil
    
    # supplying a version of course, makes no difference in this case
    client.firefox? 47
    # => nil
    
    ### ENGINE NAME ###
    
    # since client.engine_name is :webkit, a method #webkit? is defined
    client.webkit?
    # => true
    
    # an optional version can also be suppied
    client.webkit? '537.36'
    # => true
    
    # since client.engine_name isn't :gecko, no method named #gecko? is defined
    # instead, it is sent to method_missing which will return nil
    client.gecko?
    # => nil
    
    # supplying a version of course, makes no difference in this case
    client.gecko? 50
    # => nil
    
    ### PLATFORM NAME ###
    
    # since client.platform_name is :macintosh, a method #macintosh? is defined
    client.macintosh?
    # => true
    
    # an optional version can also be suppied
    client.macintosh? '10.11'
    # => true
    
    # since client.platform_name isn't :windows, no method named #windows? is defined
    # instead, it is sent to method_missing which will return nil
    client.windows?
    # => nil
    
    # supplying a version of course, makes no difference in this case
    client.windows? 10
    # => nil
    
    ### PLATFORM LABEL ###
    
    # since client.platform_label is :el_capitan, a method #el_capitan? is defined
    client.el_capitan?
    # => true
    
    # an optional version can also be suppied
    client.el_capitan? '10.10'
    # => true
    

    Aliasses

    For each of the defined keys in Browserino.config.aliasses that matches with any value of #name, #engine, #platform or #platform_label, define every alias as a method.

    client = Browserino.parse 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10; rv:33.0) Gecko/20100101 Firefox/33.0'
    
    ### NAME ###
    
    client.name
    # => :firefox
    
    Browserino.config.aliasses[client.name]
    # => [:ff]
    
    client.firefox?
    # => true
    
    client.ff?
    # => true
    
    ### PLATFORM NAME ###
    
    client.platform
    # => :macintosh
    
    Browserino.config.aliasses[client.platform]
    # => [:macos, :osx, :mac]
    
    client.macos?
    # => true
    
    client.osx?
    # => true
    
    client.mac?
    # => true
    

    Aliasses can also be used in #name?, #engine?, #platform? and #is? methods, e.g:

    client = Browserino.parse 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10; rv:33.0) Gecko/20100101 Firefox/33.0'
    
    ### NAME ###
    
    client.name
    # => :firefox
    
    client.name? :firefox
    # => true
    
    client.name? :ff
    # => true
    
    client.name? :ff, version: 33
    # => true
    
    client.is? :firefox
    # => true
    
    client.is? :ff
    # => true
    
    client.is? :ff, version: 33
    # => true
    
    ### PLATFORM NAME ###
    
    client.platform
    # => :macintosh
    
    # Browserino.config.aliasses[:macintosh] defines [:mac, :osx, :macos] as aliasses
    
    client.platform? :macintosh
    # => true
    
    client.platform? :mac
    # => true
    
    client.platform? :osx
    # => true
    
    client.platform? :macos
    # => true
    
    client.platform? :macos, version: '10.10'
    # => true
    

    Supported

    Browsers

    aol, ion, foxy, lobo, kkman, irider, sleipnir, netcaptor, sitekiosk, gomezagent, solid_core, simulbrowse, crazy_browser, enigma_browser, tencenttraveler, deepnet_explorer, sogou_browser, netflix_desktop, browzar, theworld, smart_bro, slimbrowser, greenbrowser, avant_browser, orca, pogo, epic, kylo, wyzo, light, prism, strata, vision, galeon, icecat, minimo, beonex, kmlite, kapiko, madfox, iceape, fennec, sylera, myibrow, lorentz, lolifox, chimera, sundial, bonecho, k_ninja, k_meleon, netscape, conkeror, vonkeror, firebird, songbird, namoroka, cyberfox, palemoon, swiftfox, cunaguaro, icedragon, blackbird, cometbird, classilla, shiretoko, lunascape, superswan, lightning, multizilla, seamonkey, iceweasel, enigmafox, kazehakase, tenfourfox, swiftweasel, granparadiso, maemo_browser, sailfishbrowser, monyq, waterfox, minefield, fireweb_navigator, camino, maemo, opera_mobile, blackberry_browser, webosbrowser, leechcraft, kindle, nook, xiaomi_miui, qt, coda, silk, bolt, icab, raptr, maple, arora, coast, rekonq, mercury, skyfire, omniweb, teashark, cheshire, stainless, webbrowser, mqqbrowser, nokiabrowser, samsungbrowser, playbook_browser, obigo, qtweb_browser, maxthon_nitro, nichrome, iron, hana, perk, brave, swing, kinza, fluid, amigo, puffin, origin, yowser, iridium, colibri, vivaldi, safepay, slimboat, coolnovo, rockmelt, polarity, chromeum, yabrowser, qqbrowser, awesomium, mxbrowser, fabrowser, blackhawk, taobrowser, bluechrome, chromeplus, comodo_dragon, coc_coc_browser, whitehat_aviator, steam, maxthon, ucbrowser, edge, opera_mini, opera, escape, flock, sunrise, ie, origyn, webpositive, nintendobrowser, deskbrowse, qupzilla, midori, shiira, element_browser, amigavoyager, browse, mothra, surf, spray_can, bunjalloo, inet_browser, webpro, sundance, ibm_webexplorer, navscape, firefox, chrome, safari, tizenbrowser, epiphany, uzbl, roccat, dolfin, dooble, adobeair, abrowse, vimprobable, osb_browser, edbrowse, amaya, lynx, linemode, elinks, netpositive, mucommander, onebrowser, flashfire, konqueror, cyberdog, offbyone, hotjava, netsurf, contiki, mosaic, netbox, dillo, ice_browser, emacs, openwave_browser, alienblue, ovibrowser, links, oregano, browsex, doris, retawq, orange_browser, alohabrowser, iris, otter, chedot, cm_browser, diglo, diigo_browser, flyflow, freebox, kuaiso, lovense, slimjet, zetakey, wkbrowser, yolobrowser, whale, vivo, jasmine, seraphic_sraf, phantomjs, slimerjs, liebao, w3m, charon, cent, jig_browser_web, blazer

    Bots

    huaweisymantecspider, atomic_email_hunter, netresearchserver, auto_email_spider, flaming_attackbot, addsugarspiderbot, semanticdiscovery, xaldon_webspider, yooglifetchagent, keyword_density, mass_downloader, safetynet_robot, download_demon, internet_ninja, dataparksearch, boston_project, emailcollector, webemailextrac, sitelockspider, morning_paper, four_anything, aqua_products, arachnophilia, smartdownload, emeraldshield, womlpefactory, israelisearch, issuecrawler, jaxified_bot, stackrambler, turnitinbot, covario_ids, alkalinebot, yahoo_slurp, propowerbot, emailsiphon, backdoorbot, terrawizbot, searchsight, baiduspider, sandcrawler, fyberspider, linguee_bot, big_brother, yahooseeker, noxtrumbot, black_hole, blackwidow, sosospider, duckduckgo, beslistbot, twitterbot, linkdexbot, aitcsrobot, litefinder, mabontland, yasaklibot, httpclient, ahrefsbot, mojeekbot, incywincy, seznambot, girafabot, becomebot, dts_agent, emailwolf, googlebot, omgilibot, labelgrab, altavista, yandexbot, newsgator, instagram, pinterest, gurujibot, lapozzbot, mvaclient, ng_search, youdaobot, webcopier, ips_agent, yodaobot, ldspider, lexxebot, scoutjet, linkedin, bullseye, alexibot, whatsapp, facebook, getright, asterias, catchbot, discobot, geniebot, koepabot, synoobot, rufusbot, rampybot, mogimogi, lmspider, blowfish, superbot, valkyrie, yacybot, jyxobot, orbiter, polybot, accoona, mj12bot, aspider, blexbot, bspider, auresys, bingbot, gaisbot, zealbot, zspider, backrub, harvest, nymesis, radian6, scrubby, gcreep, snappy, vortex, tineye, zyborg, sqworm, qseero, pompos, solbot, ichiro, bizbot, msnbot, exabot, msrbot, dotbot, cosmos, ecatch, scrapy, tumblr, holmes, okhttp, mxbot, moget, occam, acoon, nutch, alexa, atomz, htdig, peew, yeti, wf84, vyu2, acoi, obot, ask, b2w, ipd, zao, furlbot, jakarta, oegp, lwebis, cerberian_drtrs, gigamega, sogou_spider, megaindex, gozilla, larbin, netseer, ntent, sheenbot

    Validators

    xenu_link_sleuth, screaming_frog_seo_spider, rel_link_checker_lite, a1_website_analyzer, w3c_multipage_validator, cse_html_validator, p3p_validator, w3c_checklink, w3c_i18n_checker, w3c_unicorn, w3c_mobileok, wdg_validator, w3c_validator, fplinkchecker, linkchecker, linkwalker, linkexaminer, feedvalidator, htmlparser, csscheck, checkbot, cynthia, validator_nu, anw_htmlchecker, w3c_css_validator

    Libraries

    golang, python, luakit, webfetch, pycurl, perl, java, curl, wget, php

    Email Clients

    :outlook, :airmail, :barca, :gmail, :thunderbird, :postbox, spicebird, icedove

    RSS

    windows_rss, apple_pubsub, safari_rss, sharpreader, netnewswire, omea_reader, rssbandit, feeddemon, lucknews, inoreader, rss_menu, rss_popper, seznam_rss, nfreader, magpierss, gregarius, newsbreak, blogbridge, yeahreader, newsbeuter, greatnews, bloglines, icatcher, newsfox, quiterss, liferea, aiderss, reeder, rssowl, feedly, abilon, shrook, awasu, sage, akregator, trileet_newsroom, dragonfly_rss, digg_feed_fetcher, yahoofeedseeker, universalfeedparser, feedfetcher_google, fastladder_feedfetcher

    Platforms

    In addition to just supporting regular platform names as client.android?, there are also specific methods that get added if a certain version matches a platform. This is the platform_label property and allows you to do things like client.froyo? for instance.

    Below are a list of versions the agent can identify so far

    Android

    Regular: :android

    :android, :cupcake, :eclair, :froyo, :gingerbread, :honeycomb, :ice_cream_sandwich, :jelly_bean, :kitkat, :lollipop, :marshmallow, :nougat, :oreo, :pie

    Since android version releases follow the alphabet and since every new version since android 5 is contained within a single major version number, additional methods are defined for all "future" android releases e.g:

    label :android_p, for: :android, version: '9'..'9.9.9'
    label :android_q, for: :android, version: '10'..'10.9.9'
    # ... snipped ...
    label :android_y, for: :android, version: '18'..'18.9.9'
    label :android_z, for: :android, version: '19'..'19.9.9'
    

    So for every unreleased version of android, you can identify it by using :android_ followed by the letter for that version: client.android_p?. Since some android versions are already known, this system is supported from the letter p up to and including z.

    Windows

    Regular: :windows

    :dos, :windows98, :windows2000, :xp, :vista, :windows7, :windows8, :windows10

    NOTE: Windows 10 will be the last major version bump for Windows (for now, at least).

    Macintosh

    Regular: :macintosh

    :cheetah, :puma, :jaguar, :panther, :tiger, :leopard, :snow_leopard, :lion, :mountain_lion, :mavericks, :yosemite, :el_capitan, :sierra, :high_sierra, :mojave

    NOTE: macOS version 10 and up increment the minor version instead of the major version in a new major release.

    Devices

    Finally, Browserino can also detect devices in addition to the above.

    htc, kindle, alcatel, appletv, iphone, ipad, archos, asus, zte, blackberry, oneplus, lenovo, nokia, motorola, huawei, nexus, hp, lg, dell, oppo