F55e8e113669f6ea7d1d99f38907ce54

i have a simple browser detection script where i detect a users' browser, and using instance variables, associate the browser as being "good" or "bad", and also associate the browser with a url for the browser download page. my code works, but feels sloppy. i bet there's a way to achieve this with an array (below).

the array would be formatted like: "alias", "user agent", "display name", "url", "good or bad" - where "alias", "user agent", and "display name" could be consolidated (see safari and firefox). "good or bad" is optional, defaulting to good.

browsers = [
["ff", "Firefox", "firefox", "http://www.firefox.com/"],
["safari", "http://www.apple.com/safari/download/"]
["ie", "MSIE", "Microsoft Internet Explorer", "http://www.microsoft.com/windows/downloads/ie/getitnow.mspx", "bad"]
]

once refactored, i'd like to be able to do something like:
- if browser?(iphone)
and
- browser(iphone).url - would refer to array
- browser(iphone).name
- browser.url - would use current user_agent

def browser?(b)
    user_agents = {
      :ff   => /Firefox/,
      :iphone=> /(Mobile\/.+Safari)/,
      :safari=> /Safari/,
      :ie   => /MSIE/
    }
    user_agents[b] &&
      request.env["HTTP_USER_AGENT"] &&
      request.env["HTTP_USER_AGENT"] =~ user_agents[b]
  end
  # which browser aliases urls
  def whichbrowser
    if browser?(:iphone)
      @browser="<a href='http://www.apple.com/iphone/features/safari.html'>mobile safari</a>"
    elsif browser?(:safari)
      @browser="<a href='http://www.apple.com/safari/download/'>safari</a>"
    elsif browser?(:ff)
      @browser="<a href='http://www.firefox.com/'>firefox</a>"
    end
  end
  # good or bad can be overridden by a param in sinatra for testing purposes, thus @goodorbad = @goodorbad
  def goodorbad
    if @goodorbad
      @goodorbad = @goodorbad
    elsif browser?(:iphone) || browser?(:safari) || browser?(:ff)
      @goodorbad = "good"
    else
      @goodorbad = "bad"
    end
  end

Refactorings

No refactoring yet !

A8d3f35baafdaea851914b17dae9e1fc

Adam

October 17, 2008, October 17, 2008 05:27, permalink

No rating. Login to rate!
require 'ostruct'
require 'configatron'

module Browser
  class Base < OpenStruct
    class AgentNotFound < StandardError; end
  
    class << self
      def browser
        Configatron.instance.browser
      end
    
      def aliases
        browser.to_hash.inject([]) do |keys,(key,value)|
          keys << key unless value.nil?
          keys
        end
      end
    
      def find(user_agent)
        if key = aliases.find { |name| Browser.get(name).match?(user_agent) }
          Browser.get(key)
        else
          raise(AgentNotFound, "#{user_agent} is not a browser")
        end
      end
    
      def get(alias_name)
        attributes = browser.send(alias_name)
        raise(AgentNotFound, "#{alias_name} is not a browser") if attributes.nil?
        new(attributes.to_hash)
      end
    end
  
    def good?
      good
    end
  
    def match?(user_agent)
      user_agent.match(match)
    end
  end

  class UserAgent < Base
    # Firefox
    browser.ff.name  = 'Firefox'
    browser.ff.match = /Firefox/
    browser.ff.url   = 'http://www.firefox.com/'
    browser.ff.good  = true

    # MobileSafari
    browser.iphone.name  = 'MobileSafari'
    browser.iphone.match = /(Mobile\/.+Safari)/
    browser.iphone.url   = 'http://www.apple.com/iphone/features/safari.html'
    browser.iphone.good  = true
  end
end

module BrowserHelper
  include Browser
  
  def current_browser
    UserAgent.find(request.user_agent)
  rescue UserAgent::AgentNotFound
  end
  
  def browser(name)
    UserAgent.get(name)
  rescue UserAgent::AgentNotFound
  end
  
  def browser?(name)
    UserAgent.get(name).match?(request.user_agent)
  rescue UserAgent::AgentNotFound
  end
  
  def link_to_browser_download
    return unless current_browser
    link_to(current_browser.name, current_browser.url) if current_browser.good?
  end
end
F55e8e113669f6ea7d1d99f38907ce54

seaofclouds

October 17, 2008, October 17, 2008 15:12, permalink

No rating. Login to rate!

@adam - thanks for having a look! this is a huge step in making my code more extensible. i also appreciate the addition of rescues. this is a simple sinatra app and i'd like to keep it as light as possible. could your refactoring work without configatron? i don't mind using yaml, as i am for translation (i think you helped with that refactoring as well).

57e65eb3edfc6811910484f7e5393693

diovan

January 17, 2011, January 17, 2011 13:57, permalink

No rating. Login to rate!

I have the same., http://diovan.webeden.co.uk/ diovan free, unf,

I have the same., http://diovan.webeden.co.uk/ diovan free,  unf,
1919bfc4fa95c7f6b231e583da677a17

jonn1

April 25, 2011, April 25, 2011 19:20, permalink

No rating. Login to rate!

comment2,

comment2,

Your refactoring





Format Copy from initial code

or Cancel