ruby

Different content in Rails based on UserAgent

I was recently working on a website built using Rails that needed to render different content for certain user agents. Specifically, we needed simpler versions of certain pages for BlackBerry devices. Here's how I accomplished.

First, I added a new mime-type for BlackBerry by adding the following line to config/initializers/mime_types.rb:

Mime::Type.register_alias "text/html", :blackberry

Next, I added two utility methods to app/controllers/application.rb:

# Checks UserAgent
def is_blackberry?
  ua = request.user_agent
  return false if ua.nil?
  return false if ! ua.downcase.index('blackberry')
 
  # Don't call the BlackBerry 9800 a BlackBerry, since it has a modern browser
  # based on WebKit:
  # Mozilla/5.0 (BlackBerry; U; BlackBerry 9800; en) AppleWebKit/534.1+ (KHTML, Like Gecko) Version/6.0.0.141 Mobile Safari/534.1+
  return false if ua.downcase.index('webkit')
 
  # Must be a BlackBerry!
  true
end
 
# Sets the respond_to format to blackberry if blackberry
def set_blackberry_format
  if !request.xhr? && is_blackberry?
    request.format = :blackberry
  end
end

With that in hand, it's easy to render BlackBerry specific content on specific pages:

set_blackberry_format
respond_to do |format|
  format.blackberry
  format.html
  format.js { render :layout => false }
end

Ruby mixin for the Enum pattern

Sometimes you just want to use an Enum. Unfortunately, if you're a Ruby developer, Ruby does not offer a native enum structure. Here's a simple approach using a mixin module:

module Enum
  def const_missing(key)
    @enum_hash[key]
  end
 
  def add_enum(key, value)
    @enum_hash ||= {}
    @enum_hash[key] = NameValuePair.new(value, key.to_s.downcase)
  end
 
  def each
    @enum_hash.each {|key, value| yield(key, value) }
  end
 
  def enums
    @enum_hash.keys
  end
 
  def enum_values
    @enum_hash.values
  end
 
  def get_enum_hash
    @enum_hash
  end
 
  def find_by_key(key)
    @enum_hash[key.upcase.to_sym]
  end
end

The Enum mixin depends on a NameValuePair class to hold the data:

class NameValuePair
  attr_reader :label, :value
 
  def initialize(label, value)
    @label = label
    @value = value
  end
 
  def first
    @label
  end
 
  def last
    @value
  end
end

I included first and last methods to better support the select and options_for_select helper methods in Rails. Here's how you might use it:

class FooEnum
  extend Enum
 
   self.add_enum(:APPLE, "Apple")
   self.add_enum(:PEAR, "Pear")
   self.add_enum(:ALL, "All Fruit")
end
 
FooEnum::APPLE ==> #<NameValuePair @value="apple", @label="Apple">
FooEnum::ALL.value ==> "all"
FooEnum::ALL.label ==> "All Fruit"
FooEnum.find_by_key('apple') ==> #<NameValuePair @value="apple", @label="Apple">

Capistrano: Set Subversion User at Deployment

When deploying a Rails application using Capistrano, I discovered I needed a way to specify the username and password for Subversion and I didn't want to hardcode this information. I found an excellent suggestion on Jonathan.inspect. I didn't follow his advice exactly, but I ended up with something fairly close that I added to my deploy.rb script:

Syndicate content