Adding Keyboard Shortcuts in Web Pages

Adding keyboard shortcuts to interact with your web pages seems like a useless feature when the rest of the world is using a mouse. But for a programmer who wants everything to be a few keystrokes away, keyboard shortcuts are very handy.

In this tutorial, we will add a simple scrolling shortcuts to our webpage. This is just to illustrate what is possible. So please, do not copy-and-paste this to your production code.

What do we need?

Actually, the only critical piece we need is jQuery and knowledge of Javascript. However, since I am more of a Ruby guy, we will use Sinatra to build the page and CoffeeScript to write the Javascript.

Build the pages

The screenshot below (left side) shows how our directory structure would look like. It is pretty much a standard Sinatra structure.

Our HTML page displays 10 entries where each is grouped under a “div” element with an “.entry” class and an ID. We also add in some styling in our page to distinguish each entry.

<!DOCTYPE HTML>
<html>
<head>
  <meta http-equiv="content-type" content="text/html; charset=utf-8" />
  <title>Index</title>
  <link rel="stylesheet" href="css/style.css"/> <script type="text/javascript" charset="utf-8" src="http://code.jquery.com/jquery-1.7.1.min.js"></script>
  <script type="text/javascript" charset="utf-8" src="js/app.js">
  </script>
</head>
<body>
  <% 1.upto(10) do |i| %>
    <div id="<%= "entry_#{i}" %>"class="entry">
      <%= "Title #{i}" %>
      <p>Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. It has survived not only five centuries, but also the leap into electronic typesetting, remaining essentially unchanged. It was popularised in the 1960s with the release of Letraset sheets containing Lorem Ipsum passages, and more recently with desktop publishing software like Aldus PageMaker including versions of Lorem Ipsum.</p>
    </div>
  <% end %>
</body>
</html>

If everything is setup correctly, you should be able to run the app and see 10 entries.

$ ruby app.rb
[2012-08-30 13:48:44] INFO WEBrick 1.3.1
[2012-08-30 13:48:44] INFO ruby 1.9.2 (2012-04-20) [x86_64-darwin12.1.0]
== Sinatra/1.3.3 has taken the stage on 4567 for development with backup from WEBrick
[2012-08-30 13:48:44] INFO WEBrick::HTTPServer#start: pid=12415 port=4567

Now for the juicy part. When the user presses ‘j’, we will scroll to the next entry while ‘k’ scrolls to the previous. If you are a Vim user, you know why.

current_entry = -1

$(document).keydown (e) ->
  switch(e.keyCode)
  when 74 then scroll_to_next() # j
  when 75 then scroll_to_previous() # k

scroll_to_next = ->
  #alert "scroll to next"
  current_entry++
  scroll_to_entry(current_entry)

scroll_to_previous = ->
  if current_entry > 0
    current_entry–
  scroll_to_entry(current_entry)

scroll_to_entry = (entry) ->
  # Get the element we need to scroll to
  id = $(".entry")[entry].id
  $("html, body").animate { scrollTop: $("##{id}").offset().top }, "slow"

That’s it! As I’ve mentioned before, this is not production ready. For example, the shortcut should not interfere with other actions in your page like when the user is interacting with an input field. This also assumes the current visible entry is the first one.

This post is based from the book Web Development Recipes. If you are looking for quick reference on how to improve your project, I suggest reading the book.

How to Create a Wrapper Gem for Service APIs - Part 1

APIs are getting more and more popular as apps and services move to the cloud. Whenever you need to integrate a popular web service API into your Ruby app, 99.99% of the time there already exists a gem ready for use. That is a testament to how active Ruby developers are in supporting the community.

Even if integrating with the popular APIs is not in your radar, you may still have a need to create an API wrapper for internal use. For example, if your Rails application has grown tremendously (congratulations!), you may eventually need to adopt a services architecture to support upcoming features and make things manageable.

I created the gem for the Open Amplify API as part of my exploration to data mining. When I first created it, my primary goal was simply to wrap the API. Though I still didn’t write spaghetti code, it wasn’t a good example of structured code either. Two years later (yep, that’s how long I let the code rot), I decided to re-write the gem and adopt the architecture from the Twitter gem. It was a good exercise because not only I updated the gem for the newest API version, I also learned a great deal on how to write a gem.

Setup the project

We will create a wrapper for the fictitious Awesome API and thus call our gem ‘awesome’. To get things started, let’s use bundler to set up our initial code.

$> bundle gem awesome
create awesome/Gemfile
create awesome/Rakefile
create awesome/LICENSE
create awesome/README.md
create awesome/.gitignore
create awesome/awesome.gemspec
create awesome/lib/awesome.rb
create awesome/lib/awesome/version.rb
Initializating git repo in /Users/greg/dev/code/awesome

This is the standard directory structure and naming convention of Ruby gems. The files Gemfile, Rakefile, and .gitignore are not necessary but they would be very useful while developing your gem.

Gem dependencies

All gem dependencies should go into awesome.gemspec and not in Gemfile. Inside your Gemfile, the line ‘gemspec’ takes care of identifying the gems you needed in your local.

$> more Gemfile
source 'https://rubygems.org'

# Specify your gem's dependencies in awesome.gemspec
gemspec

Versioning

You specify the version of your gem inside lib/awesome/version.rb

$> more lib/awesome/version.rb
module Awesome
  VERSION = "0.0.1"
end

You may be wondering how is this used by the gem. Take a peek at awesome.gemspec and you’ll see that Awesome::VERSION is used by the .gemspec file.

$> more awesome.gemspec
require File.expand_path('../lib/awesome/version', __FILE__)

Gem::Specification.new do |gem|
gem.authors = ["Greg Moreno"]
gem.email = ["greg.moreno@gmail.com"]
gem.description = %q{TODO: Write a gem description}
gem.summary = %q{TODO: Write a gem summary}
gem.homepage = ""

gem.files = `git ls-files`.split($\)
gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
gem.name = "awesome"
gem.require_paths = ["lib"]
gem.version = Awesome::VERSION
end

Additional modules and classes

You can write all your code in the file lib/awesome.rb and it would still work. However, in the spirit of making code maintainable, it is highly recommended that you put your classes and modules under the directory lib/awesome just like what we did with lib/awesome/version.rb

Testing the gem

We will use minitest but you can always use any test framework you prefer. For our test setup, we need to do the ff:

  • Setup our test directory manually since bundler didn’t do this for us.
  • Create a rake task to run our tests.
  • Specify gem dependencies in our common test helper file.

And the steps and code below.

$> mkdir test
$> touch test/helper.rb
$> mkdir test/awesome
$> touch test/awesome/awesome_test.rb

# Rakefile
require 'bundler/gem_tasks'

require 'rake/testtask'
Rake::TestTask.new do |test|
  test.libs << 'lib' << 'test'
  test.ruby_opts << "-rubygems"
  test.pattern = 'test/**/*_test.rb'
  test.verbose = true
end

# test/helper.rb
require 'awesome'
require 'minitest/spec'
require 'minitest/autorun'

Now that we have our testing in place, let’s write a simple test and see if everything works.

# test/awesome/awesome_test.rb
require 'helper'

describe Awesome do
  it 'should have a version' do
    Awesome::VERSION.wont_be_nil
  end
end

# Then, let's run the test
$> rake test
(in /Users/greg/dev/code/awesome)
/Users/greg/.rbenv/versions/1.9.2-p290/bin/ruby -I"lib:lib:test" -rubygems "/Users/greg/.rbenv/versions/1.9.2-p290/lib/ruby/1.9.1/rake/rake_test_loader.rb" "test/awesome/awesome_test.rb"
Loaded suite /Users/greg/.rbenv/versions/1.9.2-p290/lib/ruby/1.9.1/rake/rake_test_loader
Started
.
Finished in 0.000695 seconds.

1 tests, 1 assertions, 0 failures, 0 errors, 0 skips

Test run options: --seed 55984

Perfect! Now, let’s start working on the juicy parts of our gem.

Configuration

Every webservice API would definitely require a per-user configuration and your gem should be able to support that. For example in the Twitter gem, some methods require authentication and you setup the default configuration with this:

Twitter.configure do |config|
  config.consumer_key = YOUR_CONSUMER_KEY
  config.consumer_secret = YOUR_CONSUMER_SECRET
  config.oauth_token = YOUR_OAUTH_TOKEN
  config.oauth_token_secret = YOUR_OAUTH_TOKEN_SECRET
end

Every API has different options but if you are wrapping a webservice, the options often fall into two categories - connections and functional options. For example, connection-related options include the endpoint, user agent, and authentication keys while functional options include request format (e.g. json), number of pages to return and other parameters required by specific API functions. In some APIs, the api key is passed as a parameter to GET calls so while it may be connection-related, it is better to group it with parameter options so you can easily encode all parameters in a single call.Our Awesome API is simple and will not deal with OAuth like the Twitter gem does. For the configuration, we should be able to do this:

Awesome.api_key = 'YOUR_API_KEY'
Awesome.format = :json
# Other options are: user_agent, method

Now, let’s write some tests. Of course, these should fail at first :)

# test/awesome/configuration_test.rbrequire 'helper'

describe 'configuration' do
  describe '.api_key' do
    it 'should return default key' do
      Awesome.api_key.must_equal Awesome::Configuration::DEFAULT_API_KEY
    end
  end

  describe '.format' do
    it 'should return default format' do
      Awesome.format.must_equal Awesome::Configuration::DEFAULT_FORMAT
    end
  end

  describe '.user_agent' do
    it 'should return default user agent' do
      Awesome.user_agent.must_equal Awesome::Configuration::DEFAULT_USER_AGENT
    end
  end

  describe '.method' do
    it 'should return default http method' do
      Awesome.method.must_equal Awesome::Configuration::DEFAULT_METHOD
    end
  end
end

As I mentioned before, the best way to write your gem (or any program for that matter) is to cleary separate the functionalities into modules and classes. In our case, we will put all configuration defaults inside a module (i.e. lib/awesome/configuration.rb). We also want to provide class methods for the module Awesome which we can easily do using Ruby’s ‘extend’.

# lib/awesome/configuration.rb

module Awesome
  module Configuration
    VALID_CONNECTION_KEYS = [:endpoint, :user_agent, :method].freeze
    VALID_OPTIONS_KEYS = [:api_key, :format].freeze
    VALID_CONFIG_KEYS = VALID_CONNECTION_KEYS + VALID_OPTIONS_KEYS

    DEFAULT_ENDPOINT = 'http://awesome.dev/api'
    DEFAULT_METHOD = :get
    DEFAULT_USER_AGENT = "Awesome API Ruby Gem #{Awesome::VERSION}".freeze

    DEFAULT_API_KEY = nil
    DEFAULT_FORMAT = :json

    # Build accessor methods for every config options so we can do this, for example:
    # Awesome.format = :xml
    attr_accessor *VALID_CONFIG_KEYS

    # Make sure we have the default values set when we get 'extended'
    def self.extended(base)
      base.reset
    end

    def reset
      self.endpoint = DEFAULT_ENDPOINT
      self.method = DEFAULT_METHOD
      self.user_agent = DEFAULT_USER_AGENT

      self.api_key = DEFAULT_API_KEY
      self.format = DEFAULT_FORMAT
    end

  end # Configuration
end

# lib/awesome.rb
require 'awesome/version'
require 'awesome/configuration'

module Awesome
  extend Configuration
end

$> rake test
(in /Users/greg/dev/code/awesome)
/Users/greg/.rbenv/versions/1.9.2-p290/bin/ruby -I"lib:lib:test" -rubygems "/Users/greg/.rbenv/versions/1.9.2-p290/lib/ruby/1.9.1/rake/rake_test_loader.rb" "test/awesome/awesome_test.rb" "test/awesome/configuration_test.rb"
Loaded suite /Users/greg/.rbenv/versions/1.9.2-p290/lib/ruby/1.9.1/rake/rake_test_loader
Started
.....
Finished in 0.001600 seconds.

5 tests, 5 assertions, 0 failures, 0 errors, 0 skips

Our gem will not be awesome if we don’t support a ‘configure’ block like what the Twitter gem does. We want to setup the configuration like this:

Awesome.configure do |config|
  config.api_key = 'YOUR_API_KEY'
  config.method = :post
  config.format = :json
end

Fortunately, it’s an easy fix. We just need to add a ‘configure’ method to the Configuration module. We also update our tests to make sure this new method works.

# lib/awesome/configuration.rb
def configure
  yield self
end

# test/awesome/configuration_test.rb
after do
  Awesome.reset
end

describe '.configure' do
  Awesome::Configuration::VALID_CONFIG_KEYS.each do |key|
    it "should set the #{key}" do
      Awesome.configure do |config|
        config.send("#{key}=", key)
        Awesome.send(key).must_equal key
      end
    end
  end
end

Before we move on, let’s take a second look at our configuration tests. We have tests for checking default values and setting-up new ones. What if we added a new configuration key for our gem? The ‘configure’ tests will be able to handle the new key but we still have to add another test for checking the default value. And we don’t want to right another test code, right? More importantly, we don’t want our tests to yield false positives. If we fail to add the ‘default value’ check, our tests will still pass even though we forgot to set a default value.Let us remove all our default value tests and replace it with code that relies on VALID_CONFIG_KEYS instead.

# test/awesome/configuration_test.rb
Awesome::Configuration::VALID_CONFIG_KEYS.each do |key|
  describe ".#{key}" do
    it 'should return the default value' do
      Awesome.send(key).must_equal Awesome::Configuration.const_get("DEFAULT_#{key.upcase}")
    end
  end
end

$> rake test
(in /Users/greg/dev/code/awesome)
/Users/greg/.rbenv/versions/1.9.2-p290/bin/ruby -I"lib:lib:test" -rubygems "/Users/greg/.rbenv/versions/1.9.2-p290/lib/ruby/1.9.1/rake/rake_test_loader.rb" "test/awesome/awesome_test.rb" "test/awesome/configuration_test.rb"
Loaded suite /Users/greg/.rbenv/versions/1.9.2-p290/lib/ruby/1.9.1/rake/rake_test_loader
Started
...........
Finished in 0.002935 seconds.

11 tests, 11 assertions, 0 failures, 0 errors, 0 skips

Test run options: --seed 21540

Configuring clients

Our end goal is to wrap API calls that fits nicely into our application and the common approach to do that is to wrap the API calls under a ‘Client’ class. Depending on the size of the API you want to support, the Client class maybe delegating the method calls to other classes and modules but from the point of view your program, the action happens inside the Client class. There are two ways to configure the Client class:

  • It inherits the configuration values defined in the Awesome module;
  • It overrides the configuration values per client

    # Use the values defined in the Awesome module client = Awesome::Client.new client.make_me_awesome(‘gregmoreno’)

    client_xml = Awesome::Client.new :format => :xml client_json = Awesome::Client.new :format => :json

We are not going to show our tests in here but if you are interested, you can view the test code from the github repository. Instead, we show the code that handles the two scenarios for client configuration.

# lib/awesome/client.rb

module Awesome
  class Client

    # Define the same set of accessors as the Awesome module
    attr_accessor *Configuration::VALID_CONFIG_KEYS

    def initialize(options={})
      # Merge the config values from the module and those passed
      # to the client.
      merged_options = Awesome.options.merge(options)

      # Copy the merged values to this client and ignore those
      # not part of our configuration
      Configuration::VALID_CONFIG_KEYS.each do |key|
        send("#{key}=", merged_options[key])
      end
    end

  end # Client
end

We also need to update our Awesome module. First, we need to require the new file awesome/client.rb so it will be loaded when we require the gem. Second, we need to implement a method that returns all the configuration values inside the Awesome module. Since this is still about configuration, our new method should go inside the Configuration module.

# lib/awesome/configuration.rb
def options
  Hash[ * VALID_CONFIG_KEYS.map { |key| [key, send(key)] }.flatten ]
end

We’re finally done with the configuration part of our gem. I know it’s a lot of work for a simple task but we managed to put a good structure in our code. Plus, we learned how to make our tests less brittle, and use Ruby’s awesome power to make our code better. In our next installment, we’ll discuss requests and error handling.

Create Your Own Rails 3 Engine

Engine is an interesting feature of Rails. Engines are miniature applications that live inside your application and they have structure that you would normally find in a typical Rails application. If you have used the Devise gem, which itself is an engine, you know the benefits of being able to add functionality to your application with just a few lines of code. Another great benefit of engines is when you or your team are maintaining a number of applications the common functionalities can be extracted into engines.

Engines are already available prior to Rails 3 but it is not a core feature of the framework. As such, engine developers resorted to monkey-patching which, oftentimes, lead to engines breaking when Rails gets updated. In Rails 3.1, engines are now supported by the framework and there is now a clealy defined place where to hook your engines into Rails.

Now, let us go through the steps of building a simple engine. We will be working on authentication engine (like Devise) that allows users of your application to use their Twitter or Facebook credentials.

# This is the app that will use our engine
$> rails new social_app

# This is our engine
$> rails plugin new undevise --mountable

The –mountable option tells Rails you want to generate a mountable plugin, commonly known as engine. When you look at the directory structure of your engine, it is much different from your Rails app. The engine has controllers, models, views, mailers, lib, and even its own config/routes.rb (didn’t we just said it is a miniature Rails app).

Include the engine in your app

Just like any gem, you should update your app’s Gemfile to use the engine we created.

# social_app/Gemfile
gem 'undevise', :path => '../undevise'

Of course, you can set :path to any location or if it is in a git repository, you can use the :git option. If you are developing your engine alongside your app, a better approach is to use gem groups in your Gemfile. For example:

group :development do
  gem 'undevise', :path => '../undevise'
end

group :production do
  gem 'undevise', :git => 'git://github.com/yourname/undevise.git'
end

After adding the engine in your Gemfile, let’s make sure all dependencies are available for the application. If everything works, you should be able to see a reference to undevise inside Gemfile.lock

$> cd social_app
$> bundle install
$> more Gemfile.lock
PATH
  remote: ../undevise
  specs:
    undevise (0.0.1)
      rails (~> 3.2.1)

Mount the engine

Next, we will mount the engine and see if we can route requests to it. What this does is make sure requests starting with /auth will be passed to our engine.

# social_app/config/routes.rb
SocialApp::Application.routes.draw do
  mount Undevise::Engine, :at => '/auth'
end

# Run the social app. Make sure you are in the social_app directory.
$> rails s

# Then visit http://localhost:3000/auth

When you visit ‘/auth‘, you will get a routing error because you haven’t defined any routes in your engine yet.

# undevise/config/routes.rb
Undevise::Engine.routes.draw do
  root :to => 'auth#index'
end

Remember even though your engine is mounted at ‘/auth’, what your engine sees is the path after the ‘/auth’. Routes in engines are namespaced to avoid conflicts with your app. You can change the mounted path in your Rails app anytime and your engine wouldn’t care. Let’s try again and see what Rails would tell us.

$> cd social_app
$> rails s

Perfect! Now we know the request is being passed to our engine. We now just have to define our controller.

$ cd undevise
$ rails g controller auth

# undevise/app/controllers/undevise/auth_controller.rb
module Undevise
  class AuthController < ApplicationController
    def index
      render :text => 'Hello world'
    end

  end
end

# Now, visit http://localhost:3000/auth

Cool! We have the obligatory hello world program working. At the the risk of sounding like a broken record, remember your engine code should be namespaced. If you forget this, strange things will happen to your application and Rails will not usually complain about it.

Gem dependencies

I’m sure your idea for an engine is very far from what we have shown so far. When you generate an engine, it also creates a .gemspec file. While in your Rails app you list the gems in Gemfile, in your engine you list the gems inside the .gemspec file. This can be confusing because the engine also contains a Gemfile.

$> cd undevise
$> more Gemfile

source "http://rubygems.org"

# Declare your gem's dependencies in undevise.gemspec.
# Bundler will treat runtime dependencies like base dependencies, and
# development dependencies will be added by default to the :development group.
gemspec

As you can see, there is no need to list the gems your engine needs in the Gemfile. The line ‘gemspec’ makes sure the gems you listed in your .gemspec file are installed when you run bundle install.

Now, let’s add some gems in our engine and see how it will affect our Rails app.

# undevise/undevise.gemspec
s.add_dependency "rails", "~> 3.2.1"
s.add_dependency "omniauth"
s.add_dependency "omniauth-twitter"
s.add_dependency "omniauth-facebook"


$ cd social_app
$ bundle install
$ more Gemfile.lock
PATH
  remote: ../undevise
  specs:
    undevise (0.0.1)
      omniauth
      omniauth-facebook
      omniauth-twitter
      rails (~> 3.2.1)

Here we can see the gems we specifed in undevise/undevise.gemspec are also included in the main Rails app.

Configure OmniAuth

If you are using omniauth directly in your app, your will configuration will definitely be in the file config/initializers/omniauth.rb. Since our engine is pretty much just another Rails app, it will also have its own config/initializers/omniauth.rb file. The only consideration with regards to the configuration is where would the Twitter or Facebook credentials be located. You definitely don’t want to embed it in your engine.

Our solution is to store the credentials inside a config/twitter.yml file (or config/facebook.yml) inside your main Rails app. Then have our engine pull the values out of these files to configure omniauth.

$> cd undevise/config
$> mkdir initializers
$> touch initializers/omniauth.rb

# We have to create the initializers directory because it not created by default.

# undevise/config/initializers/omniauth.rb
providers = %w(twitter facebook).inject([]) do |providers, provider|
  fpath = Rails.root.join('config', "#{provider}.yml")

  if File.exists?(fpath)
    config = YAML.load_file(fpath)
    providers << [ provider, config['consumer_key'], config['consumer_secret'] ]
  end

  providers
end

raise 'You have not created config/twitter.yml or config/facebook.yml' if providers.empty?

Rails.application.config.middleware.use OmniAuth::Builder do
  providers.each do |p|
    provider *p
  end
end

Now, let’s go back to our main Rails app and start the server.

$> cd social_app
$> rails s
=> Booting WEBrick
=> Rails 3.2.1 application starting in development on http://0.0.0.0:3000
=> Call with -d to detach
=> Ctrl-C to shutdown server
Exiting
/Users/greg/dev/tmp/ruby/engine-tutorial/undevise/config/initializers/omniauth.rb:12:in `<top (required)>': You have not created config/twitter.yml or config/facebook.yml (RuntimeError)

Oops! We forgot to create our Twitter or Facebook configuration file. In your main Rails app, go ahead and create config/twitter.yml. If you are not familiar with Twitter apps, visit their developer site at https://dev.twitter.com/

Gemfile dependencies and sub-depencies

# social_app/config/twitter.yml
consumer_key:  'APP_CONSUMER_KEY'
consumer_secret: 'APP_CONSUMER_SECRET'

$> rails s
=> Booting WEBrick
=> Rails 3.2.1 application starting in development on http://0.0.0.0:3000
=> Call with -d to detach
=> Ctrl-C to shutdown server
Exiting
/Users/greg/dev/tmp/ruby/engine-tutorial/undevise/config/initializers/omniauth.rb:12:in `<top (required)>': uninitialized constant OmniAuth (NameError)
     from /Users/greg/.rbenv/versions/1.9.3-p0/lib/ruby/gems/1.9.1/gems/railties-3.2.1/lib/rails/engine.rb:588:in `block (2 levels) in <class:Engine>'

The NameError occurs because during the main Rails’ app boot up, Bundler will only require dependencies listed in the Gemfile but not the sub-dependencies. As you can see from Gemfile.lock, omniauth is not a direct dependency. You could list the gems in your main app’s Gemfile but that’s defeating the purpose of isolating the gem dependencies through your engine.

The solution right now is to require your dependencies inside your engine and the place to do that is inside lib/undevise/engine.rb

# undevise/lib/undevise/engine.rb
require 'omniauth'
require 'omniauth-twitter'
require 'omniauth-facebook'

module Undevise
  class Engine < ::Rails::Engine
    isolate_namespace Undevise
  end
end

After listing required dependencies inside your engine, restart your main Rails app, then visit http://localhost:3000/auth/twitter/

When you visit http://localhost:3000/auth/twitter/, you should see the error above. The callback url is part of OmniAuth’s behaviour and should be fixed by adding a route in your engine and adding the method to handle it in your controller.

# undevise/config/routes.rb
Undevise::Engine.routes.draw do
  root :to => 'auth#index'
  match ':provider/callback' => 'auth#callback'
end

# undevise/app/controllers/undevise/auth_controller.rb
module Undevise
  class AuthController < ApplicationController

    def index
      render :text => 'Hello world'
    end

    def callback
      render :text => "Hello from #{params[:provider]}"
    end

  end
end

If everything work fine, you should see a message from Twitter.

We only scratched the surface with Rails 3 engine. Your engine, much like any normal Rails app, can have models and migrations, javascripts, css, specs, etc. If you want to dig deeper into engines, I recommend Rails 3 in Action by Ryan Bigg and Yehuda Katz. It includes a whole chapter about engines, discussion of middleware, and how tests your engine.

Architecture the Lost Years by Robert Martin

Architecture the Lost Years by Robert Martin

http://youtu.be/WpkDN78P884

More Ruby Tips and Tricks

String to number conversion gotcha

>> Float('3.14159')
=> 3.14159 
>> '3.14159'.to_f
=> 3.14159 

# However, Float() method will return an exception if given
# a bad input while to_f() will ignore everything from the 
# offending character.

>> Float('3.x14159')
ArgumentError: invalid value for Float(): "3.x14159"
  from (irb):4:in 'Float'
  from (irb):4

>> '3.x14159'.to_f
=> 3.0


# Similar case with to_i() and Integer().

>> Integer('19x69')
ArgumentError: invalid value for Integer(): "19x69"
  from (irb):15:in 'Integer'
  from (irb):15
  from /Users/greg/.rvm/rubies/ruby-1.9.2-p0/bin/irb:17:in '<main>'

>> '19x69'.to_i
=> 19

Case insensitive regular expression

# Regex is case sensitive by default.
# Adding 'i' for insensitive match
puts 'matches' if  /AM/i =~ 'am'

Hash is ordered in 1.9

# new syntax in 1.9
h = {first: 'a', second: 'b', third: 'c'}

# hashes in 1.9 are ordered
h.each do |e|
  pp e
end

Filter a list using several conditions

conditions = [
    proc { |i| i > 5 },
    proc { |i| (i % 2).zero? },
    proc { |i| (i % 3).zero? }
  ]

matches = (1..100).select do |i|
  conditions.all? { |c| c[i] }
end

Randomly pick an element from an array

>> [1,2,3,4,5].sample
=> 2 
>> [1,2,3,4,5].sample
=> 1 

# pick 2 random elements
>> [1,2,3,4,5].sample(2)
=> [1, 5]

List methods unique to a class

# List all instance methods that starts with `re` including those inherited by String.

>> String.instance_methods.grep /^re/
=> [:replace, :reverse, :reverse!, :respond_to?, :respond_to_missing?] 

# List methods unique to String, i.e. not include
# those defined by its ancestors.

>> String.instance_methods(false).grep /^re/
=> [:replace, :reverse, :reverse!]

Globbing key-value pairs

>> h = Hash['a', 1, 'b', 2]
=> {"a"=>1, "b"=>2}

>> h = Hash[ [ ['a', 1], ['b', 2] ] ] 
=> {"a"=>1, "b"=>2}

>> h = Hash[ 'a' => 1, 'b' => 2 ]
=> {"a"=>1, "b"=>2}

# The first form is very useful for globbing key-value pairs in Rails’ routes. For example, if you have the following:

# route definition in Rails 3
match 'items/*specs' => 'items#specs'

# sample url
http://localhost:3000/items/year/1969/month/7/day/21

# params[:specs] will be set

>> params[:specs]
=> "year/1969/month/7/day/21"

>> h = Hash[*params[:specs].split('/')]
=> {"year"=>"1969", "month"=>"7", "day"=>"21"}

Ruby Tips and Tricks

Generate random numbers within a given range

irb(main):019:0> rand(10..20)
=> 12
irb(main):020:0> rand(10...20) # works with exclusive range
=> 16

Dump your object using awesome_print

# Install the gem first
gem install awesome_print

irb(main):001:0> require 'ap'
=> true
irb(main):002:0> ap :a => 1, :b => 'greg', :c => [1,2,3]
{
    :a => 1,
    :b => "greg",
    :c => [
        [0] 1,
        [1] 2,
        [2] 3
    ]
}
=> {:a=>1, :b=>"greg", :c=>[1, 2, 3]}

Concatenating strings

irb(main):005:0> "abc" + "def"
=> "abcdef"
irb(main):006:0> "abc".concat("def")
=> "abcdef"
irb(main):007:0> x = "abc" "def"
=> "abcdef"

Include modules in a single line

class MyClass
  include Module1, Module2, Module3
  # However, the modules are included in reverse order. Confusing eh!
end

Instance variable interpolation

irb(main):008:0> @name = "greg"
=> "greg"
irb(main):009:0> "my name is #{@name}"
=> "my name is greg"
irb(main):010:0> "my name is #@name"
=> "my name is greg"

I still prefer the curly braces.

Syntax checking

$ ruby -c facu.rb 
facu.rb:12: syntax error, unexpected keyword_end, expecting $end

Zipping arrays

irb(main):027:0> names = %w(fred jess john)
=> ["fred", "jess", "john"]
irb(main):028:0> ages = [38, 47,91]
=> [38, 47, 91]
irb(main):029:0> locations = %w(spain france usa)
=> ["spain", "france", "usa"]
irb(main):030:0> names.zip(ages)
=> [["fred", 38], ["jess", 47], ["john", 91]]
irb(main):031:0> names.zip(ages, locations)
=> [["fred", 38, "spain"], ["jess", 47, "france"], ["john", 91, "usa"]]

Range into arrays

irb(main):034:0> (10..20).to_a  # what I used to do
=> [10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20]
irb(main):035:0> [*10..20]
=> [10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20]

Using parameter as default value

irb(main):047:0> def method(a, b=a); "#{a} #{b}"; end
=> nil
irb(main):048:0> method 1
=> "1 1"
irb(main):049:0> method 1, 2
=> "1 2"

Put regex match in a variable

irb(main):058:0> s = "Greg Moreno"
=> "Greg Moreno"
irb(main):059:0> /(?<first>\w+) (?<second>\w+)/ =~ s
=> 0
irb(main):060:0> first
=> "Greg"
irb(main):061:0> second
=> "Moreno"

Preventing Model Explosion via Rails Serialization

A great thing about ActiveRecord is you can easily add a new model to your application and play around with it as you progress. However, this power can easily be overused leading to unnecessary overhead in your code.

Consider the case where you have preferences for each user. For example, a user may opt to show or hide his email address, adjust his timezone, or language. One solution is to simply add new columns to the users table that correspond to each preference type. For example, you can have a ‘show_email’, ‘timezone’, ‘locale’ columns in the ‘users’ table, which can make your table become wide as you add more preferences options. Another option is to use a separate ‘preferences’ table.

class User < ActiveRecord::Base
  has_many :preferences
end

class Preferences < ActiveRecord::Base
  belongs_to :user

  # name  - preference name
  # value - preference value
end

Note there is no user interface to add or remove ‘preferences’, i.e. the kinds of preferences are fixed. Of course, in the future you may add a new kind of preference but this kind of work is better done outside of the user interface. Since that is the case, there is no need to represent ‘preferences’ as a separate model.

One better alternative is to use Rails serialization to store the different kinds and user-specific values. The code would look like this:

class User < ActiveRecord::Base
  serialize :preferences, Hash
end

u = User.new
u.preferences = {:show_email => true, :locale => :en }
u.save

# somewhere in your view using haml
- if @user.preferences[:show_email]
  = @user.email

Using ‘serialize’ results in less code, fewer tables, and less overall complexity. However, with serialization you lose the ability to efficiently search the preferences data. The million-dollar question is do you need to query these preferences? Do you need a finder that returns all users who wants to show their email?

One issue I had with ‘serialize’ is that by using it, I expose the implementation details. In the display example above, it is obvious I had it stored as a Hash. I would rather hide this detail and present the preferences attributes as user attributes instead. I also want default values for every user.

For example:

u = User.new  # automatically assigns the default preferences
u.preferences
=> {:show_email => false, :locale => :en}

u.show_email = true  # I can change it like an attribute via @user.update_attributes(params[:user])
u.preferences
=> {:show_email => true, :locale => :en}

I have created a module to support this. It is not a unique problem so others may have probably released a gem or plugin to do this. (I actually never bothered to search for one.) Nevertheless, it was a good exercise in metaprogramming.

To use my implementation, simply call ‘serializeable’ with the column you want to serialize and the default values.

class User < ActiveRecord::Base
  serializeable :preferences, :show_email => true, :locale => :en
end

Below is the implementation of ‘serializeable’. The convention is to save it under your ‘lib’ folder and include it in your ‘config/application.rb’ if you are using Rails 3.

module AttributeSerializer
  module ActiveRecordExtensions
    module ClassMethods

      def serializeable(serialized, serialized_accessors={})  
        serialize serialized, serialized_accessors.class

        serialized_attr_accessor serialized, serialized_accessors
        default_serialized_attr serialized,  serialized_accessors
      end

      # Creates the accessors
      def serialized_attr_accessor(serialized, accessors)
        accessors.keys.each do |k|
          define_method("#{k}") do
            self[serialized] && self[serialized][k]
          end

          define_method("#{k}=") do |value|
            self[serialized][k] = value
          end
        end
      end

      # Sets the default value of the serialized field
      def default_serialized_attr(serialized, accessors)
        method_name =  "set_default_#{serialized}"
        after_initialize method_name 

        define_method(method_name) do
          self[serialized] = accessors if self[serialized].nil?
        end
      end

    end
  end
end

class ActiveRecord::Base
  extend AttributeSerializer::ActiveRecordExtensions::ClassMethods
end

ActiveRecord is both easy and powerful. It can also lead to misuse and abuse. Even though you are adding just one model, remember that it is not just the model class itself. You are also adding the database migrations, unit tests, factories, finders, and validations that go along with the model. Next time you have a new requirement, see if serialization can do a better job.

Update: Adam Cuppy converted this code into a Rails plugin while Jay added dynamic finder methods. I also moved this into a gem I called fancy_serializer.

Ruby 101: Improving Your Code by Defining Methods Dynamically

Let’s say you have a user and you want to check its role.

class User
  attr_accessor :role
end

u = User.new
u.role = 'admin'

# somewhere in your code you check the role

if u.role == 'admin'
  puts 'admin'
elsif u.role == 'moderator'
  puts 'moderator'
elsif u.role == 'guest'
  puts 'guest'
end

Using a string value is bad code and you can improve this by using constants instead. But still, this is bad code becauses it exposes implementation details of your User class.

For our first improvement, we define methods that check the user’s role and hide the implementation of the role checking inside the User class.

class User
  attr_accessor :role

  def is_admin?
    self.role == 'admin'
  end

  def is_moderator?
    self.role == 'moderator'
  end

  def is_guest?
    self.role == 'guest'
  end

end

u = User.new
u.role = 'guest'

if u.is_admin?
  puts 'admin'
elsif u.is_moderator?
  puts 'moderator'
elsif u.is_guest?
  puts 'guest'
end

Our first improvement is definitely better than the original but there are duplicate code in the role checking. You can eliminate the duplicate code by delegating the role checking to a single method.

class User
  attr_accessor :role

  def is_admin?
    is_role? 'admin'
  end

  def is_moderator?
    is_role? 'moderator'
  end

  def is_guest?
    is_role? 'guest'
  end

  protected

  def is_role?(name)
    self.role == name
  end

end

Our second improvement is a classic refactoring technique and common in any modern programming language. In other words, there is nothing “Ruby” about it. Before you get bored, I will now show the Ruby version.

The Ruby version uses #define_method to further eliminate duplicate code.

class User
  attr_accessor :role

  def self.has_role(name)
    define_method("is_#{name}?") do
      self.role == "#{name}"
    end
  end

  has_role :admin
  has_role :moderator
  has_role :guest

end

By using #define_method, we were able to add instance methods to our class User. You can check the new instance methods via irb.

ruby-1.9.2-p0 > User.instance_methods.grep /^is/
=> [:is_admin?, :is_moderator?, :is_guest?, :is_a?]

Note that #has_role is just another method and as such you can modify it to accept several parameters, an array, or other class. For example, we can make ‘has_role’ accept a list of roles.

class User
  attr_accessor :role

  def self.has_roles(*names)
    names.each do |name|
      define_method("is_#{name}?") do
        self.role == "#{name}"
      end
    end
  end

  has_roles :admin, :moderator, :guest
end

How to Use OpenAmplify With Ruby

The OpenAmplify API reads text you supply and returns linguistic data explaining and classifying the content. What you do with that analysis is, in the fine tradition of APIs and mashups, up to you. Some possibilities might include pairing ads with articles, creating rich tag-clouds, or monitoring the tone of forum threads.

I created a ruby gem to simplify the use of the OpenAmplify API. It’s still in the early stages but should be enough to get you started.

In case you need a different format, OpenAmplify supports XML, JSON, RDF, CSV. It can also return the result as a fancy HTML page.

The source code is available in github http://github.com/gregmoreno/openamplify

Because Nothing Is Happening on the Screen

When we looked at the actual download speeds of the sites we tested, we found that there was no correlation between these and the perceived speeds reported by our users. About.com, rated slowest by our users, was actually the fastest site (average: 8 seconds). Amazon.com, rated as one of the fastest sites by users, was really the slowest (average: 36 seconds). — The Truth About Download Time

Was Jakob Nielsen, the usability guru, wrong when he concluded that to avoid annoying users, pages should load in less than 10 seconds? I think he’s still right because it is no fun staring at the hourglass for 5 minutes. But when we tell our friends that Flickr is faster than Picasa, we don’t say uploading a 1MB JPEG takes 2.35 seconds in Flickr while it takes 4.86 seconds in Picasa. We just say Flickr is faster.

What is missing from Nielsen’s conclusion is that when users say a website is slow, they talk about their feelings and not what they see in the stopwatch. This does not mean that programmers should abandon measuring website performance. We still need to make that slow function run faster and there is no way to tell if we are progressing or not if we don’t know the score.

Browsers follow a fetch-parse-flow-paint process to load web pages. Given a URL, the fetch engine finds it and stores the page into a cache. The parse engine discovers the various HTML elements and produces a tree that represents the internal structure of the page. The flow engine handles the layout while the paint engine’s job is to display the web page on the screen. Nothing unusual except that when the parse engine sees an image, it would stop and ask the fetch engine to read the image. The parse engine continues only after it has determined the image’s size. The end result is that the browser will wait until all the elements of the page has been processed before it shows the page. During this processing, all the user sees is a blank page. This is how things work with the 1st widely-used web browser, Mosaic.

Netscape Navigator 1.0 took a different approach. When the parse engine sees an image, it still asks the fetch engine to load the image. The difference is that the parse engine will put a placeholder in the internal structure of the page to mark where the image is and let the flow and paint engines do their job. When the image is loaded and analyzed, the paint engine does a repaint on the screen. This can happen several times if the page has a lot of images. If you measure the overall time it takes to finalize the page display, Mosaic is faster than Netscape. But, users would say otherwise. Mosaic lacks a sign of progress while the appearance of text, then an image, then another image makes users think that Netscape is faster.

My first encounter with the world wide web was in April 1996 while working as a student assistant at the Advanced Science Technology Institute. Back then, web pages consist mostly of text. Nowadays, it is not uncommon for a page to contain lots of big images, embedded videos, references to several CSS and JavaScript files. So while computing power and bandwidth has improved over the years, content has also bloated making performance issues still a problem.

The common opinion is that if you want to improve a website’s performance, you focus on the database, web server, and other back-end stuff. But, Yahoo! engineers found out that most optimization opportunities are present after the web server has responded with the page. When a URL is entered into the browser, 62% to 95% of the time is spent fetching the images, CSS, JavaScript contained in the page. It is clear that reducing the number of HTTP requests will also reduce response time.

The Yahoo! Exceptional Performance Group has identified 13 rules for making fast-loading web pages. The group’s lead, Steve Souders, has also written a book on website performance.

Another cool product from the team is YSlow (nice name). It analyzes a web page and tells you why it is slow based on the 13 rules. YSlow is a Firefox plugin and works alongside another web developer’s indispensable tool, Firebug. When I first used YSlow with SchoolPad, my initial grade was F (58). I first set out to address Rule #10 - Minify JavaScript. Since SchoolPad is written on Ruby on Rails, the asset_packager plugin came in handly in merging all my CSS files and JavaScript files. Using the plugin, CSS and JavaScript files can be used in a single reference.The asset_packager is also smart. During development mode where CSS and JavaScripts files are often updated, the plugin references the original script files but in production mode it uses the minified versions of your CSS and JS files. A few changes in your Capistrano file, then you can make the minification (is that a word?) process automatic every time you deploy your application. After a few more tweaks, my overall grade is now C (78) with an F on rules #2 (Use a CDN) and #3 (Add an expires header). I can’t address rule #2 because that requires money and #3 has to wait because it requires more Googling.

It might happen that you have an A in YSlow yet users complain that your website is slow. Talk about an unlucky day. Don’t despair. Maybe, it is time to focus on managing expectations instead of performance.

I wrote my first GUI-based program using Visual Basic on Windows 3.1. (I’m sure Evan would argue that Basic is not a programming language). Any Visual Basic book would tell you to change the mouse pointer to an hourglass before a lengthy operation such as a database query, and change it back to the default pointer afterwards.

Screen.MousePointer = vbHourglass
'Do lots of stuff.......
Screen.MousePointer = vbNormal

I think many people got addicted to the mouse pointer that along with the release of Windows 95 was a variety of themes that replace the default mouse pointer icon with a rocketship, a barking dog, or a wiggling clownfish.

Anyway, back to the web. Wait! The reason the hourglass is widely used in desktop applications is because it tells the user that the application has accepted the action, and it is now working on it. In your web apps, you should do the same.

Traditionally, when you click a link in a website, the browser sends a request to the server, receives a page, and repaints the content. Feedback in most browsers is in the form of a spinning logo at the top right part or an expanding strip at the bottom or at the status bar.

Here comes Ajax. With Ajax you can do away with refreshing everything and update only a portion of your page. Unlike the page-based model, the browser cannot give feedback that something is going on after an Ajax-based action. No more spinning logo. No more expanding strip bar. We went backward in interface design.

Fortunately, there is a way to give feedback that requires writing code using JavaScript. It could be as simple as writing a message “Loading…” on the page or using an animated GIF file. One implementation would be just to show a previously hidden text or image or create an ‘img’ element and append it to where you want to display (usually a ‘div’ element). Many web applications nowadays use various style of animation but the key idea is the same — a smoothly looping animation to indicate an activity.

Animated activity indicators are useful for tasks that will incur short delays. If the delay takes too long, the user may think the application is going on circles or has become a zombie. For longer delays, it is best to show how much progress has been made, an estimate of time remaining, or a sequence of messages telling the user what’s happening at the present. This is very useful but tricky to implement because HTTP requests don’t give tracking information at regular intervals. If you are making a sequence of 4 requests, you can guesstimate that progress is 25% done when the 1st call has completed. Flickr uses the best activity indicator I’ve seen so far when uploading images. Image files are usually big and Flickr does a great job of giving progress feedback to its users — an overall progress and a per-file progress indicator.

file_upload_progress_flickr

During the modem days, it is acceptable that many sites are slow given the hardware limitation. But now in the broadband era, amplified by tons of marketing, users expect websites to be lightning fast.

Nobody wants a slow website. But when the user say it is slow, oftentimes, it is because nothing is happening on the screen.