Web Development

How to make a Javascript Bookmarklet

Posted in Javascript, Programming on March 1st, 2010 by tommy – View Comments

There’s a niffy trick in browsers that you you execute Javascript code in the url location bar. (This is how a lot of security holes are created too!)
Try typing the below into your browser url bar:

javascript:alert('look how cool this is')

Cool, it runs javascript!

You may have noticed a lot of websites take advantage of this to enhance a service. Instapaper’s “Read Later” saves the current page and Pastefire’s “Pastefire this” saves the selected text.

To make your own, just write javascript inside a link tag as a one liner. Then just ask users to click and drag the link to the bookmark toolbar.

Here’s an example for my Next Sprocket site which will start you on a new open source task with the title and description partially filled out:
Sprocket it!


<a href="javascript:(function(){location.href='http://nextsprocket.com/tasks/new?task[title]='+encodeURIComponent(document.title)+'&task[description]=More details at '+encodeURIComponent(window.location.href)})()">Sprocket it!</a>

Sorting a HashMap by Value in Java vs Ruby

Posted in Ruby on December 12th, 2009 by tommy – View Comments

I’m working a graph problem in Java where I need to sort nodes by its edge count. I have a HashMap of nodes to edge count so I just need to sort a HashMap by its value.
A quick Google search brought up a few solutions. Below is a snippet of a typical solution from StackOverFlow


public static > List getKeysSortedByValue(Map map) {
    final int size = map.size();
    final List> list = new ArrayList>(size);
    list.addAll(map.entrySet());
    final ValueComparator cmp = new ValueComparator();
    Collections.sort(list, cmp);
    final List keys = new ArrayList(size);
    for (int i = 0; i < size; i++) {
        keys.set(i, list.get(i).getKey());
    }
    return keys;
}
private static final class ValueComparator>
                                     implements Comparator> {
    public int compare(Map.Entry o1, Map.Entry o2) {
        return o1.getValue().compareTo(o2.getValue());
    }
}

Can you believe it?? >10 lines of verbose madness! The Ruby solution is:

>> x = {:a => 1, :b => 2, :c => 1, :d => 5}
>> x.keys.sort_by{|k| x[k]}
=> [:a, :c, :b, :d]

This makes you really appreciate closure support in languages. Java 7’s support of closure should bring more sanity to the Java world.

I’m also learning Clojure right now, the solution is just as nice as Ruby.

user=> (def x {:a 1 :b 2 :c 1 :d 5})
user=> (sort-by #(x %) (keys x))
(:a :c :b :d)

Twitter OAuth Ruby Gem PIN-based Authentication API Change

Posted in Ruby, Web Development on August 4th, 2009 by tommy – View Comments

I have been using the moomerman-twitter_oauth gem for to allow users to login to our web app via Twitter Connect. Unfortunately, it stopped working when a change in the Twitter API happened. During the OAuth authentication process, instead of being redirected back to our web app, it would show a screen that said:

"You've successfully granted access...enter the following PIN to complete the process"

After browsing the net and getting help from the Twitter API team, I learned that Twitter recently made a change to their OAuth process to allow this PIN type authentication for applications. See for more info: http://groups.google.com/group/twitter-development-talk/browse_thread/thread/472500cfe9e7cdb9/848f834227d3e64d?pli=1

The oauth ruby gem defaults to using PIN-based process instead of the regular web app redirect process. To fix this, explicitly set the oauth_callback url parameter when getting the request token:

    @twitter_client = TwitterOAuth::Client.new(
        :consumer_key => TWITTER_CONSUMER_KEY,
        :consumer_secret => TWITTER_CONSUMER_SECRET
    )
    request_token = @twitter_client.request_token(:oauth_callback => oauth_confirm_url)

If you are getting a (OAuth::Unauthorized) “401 Unauthorized” error after adding the oauth_callback parameter, try altering your oauth callback method to explicitly state the oauth_verifier as well:

  def oauth_callback
    @twitter_client = TwitterOAuth::Client.new(
        :consumer_key => TWITTER_CONSUMER_KEY,
        :consumer_secret => TWITTER_CONSUMER_SECRET
    )

    @twitter_access_token = @twitter_client.authorize(
      session[:request_token],
      session[:request_token_secret],
      :o auth_verifier =>params[:oauth_verifier]
    )

Apparently, this was posted on the Twitter API Development Group in late May, but it would have been nice if Twitter DMed every web app signed up on the twitter app list of the change. I guess this is a warning for any Twitter API consumers to follow the dev list closely..

Ruby on Rails 2.3.2 Upgrade Gotchas

Posted in Ruby, Web Development on June 26th, 2009 by tommy – View Comments

While cleaning/re-factoring the PeopleJar codebase, we decided to make the move from Rails 2.2.2 to Rails 2.3.2. Rack integration was the biggest single “under the hood” change. Adam Wiggins of Heroku explains the benefits in his Rails Metal, Rack and Sinatra presentation from RailsConf. He has a great example of incorporating a Sinatra app with a Rails app.

No upgrades would be complete without some incompatibilities. As noted on the official Ruby on Rails release notes, test Sessions, Cookies, File uploads, JSON/XML APIs. We found conflicts in the facebooker and ar-extensions gems. Luckily, the developers already pushed out fixes so you can just do a gem update.

There was one other cryptic error in one of our ajax calls:

  Status: 500 Internal Server Error
  private method `split' called for #
    /opt/local/lib/ruby/gems/1.8/gems/actionpack-2.3.2/lib/action_controller/mime_type.rb:206:in `method_missing'
    /opt/local/lib/ruby/gems/1.8/gems/rack-1.0.0/lib/rack/request.rb:51:in `media_type'
    /opt/local/lib/ruby/gems/1.8/gems/rack-1.0.0/lib/rack/request.rb:117:in `parseable_data?'
    /opt/local/lib/ruby/gems/1.8/gems/rack-1.0.0/lib/rack/request.rb:138:in `POST'
    /opt/local/lib/ruby/gems/1.8/gems/actionpack-2.3.2/lib/action_controller/request.rb:428:in `request_parameters'

We could fix this with some good old monkey patching! Add this to an initializer in your config/initializers directory. this is a reported bug at the rails lighthouse and should be fixed in the next release.

module Mime
  class Type
    def split(*args)
      to_s.split(*args)
    end
  end
end

Overall, the upgrade went pretty well: just a few gem upgrades and minor tweaks. If you are on Rails 2.2.2 or older, give the upgrade a shot!

Organizing Models in Rails without Namespacing

Posted in Ruby, Web Development on June 25th, 2009 by tommy – View Comments

I was doing some code cleanup and noticed we have over 50 models in our models directory. Not good. The first thing I thought was to add namespacing. I googled the topic and turns out namespacing models is a bad idea because of how Rails collapses the model namespaces. Adding sub directories is a better and cleaner solution. Read Josh Susser’s post on the topic.

To use sub directories, you must add the directories to Rails’ config.load_paths in your environment.rb

   config.load_paths << "#{Rails.root}/app/models/profile"

To make this more flexible, you can use this snippet to add all sub directories:

  #Add all sub directories in models to load paths
  Dir.entries("#{Rails.root}/app/models").each do |file|
    next if file.eql?('.') || file.eql?('..')
    full_path = "#{Rails.root}/app/models/#{file}"
    config.load_paths << full_path if File.directory?(full_path)
  end

Easy Javascript Validation with Javascript Lint For Rails Testing

Posted in Javascript, Ruby, Web Development on June 22nd, 2009 by tommy – View Comments

I found this great Javascript Lint tool. It checks for common(missing semicolons) and not so common(use of the void type) javascript errors. A full featured Selenium test suite would be the ideal for full javascript testing coverage but Javascript Lint is a no hassle addition.

I wrote a simple Rails integration test case to scan all the javascript files in the public/javascripts folder. Download the javascript lint tool and add the file below to your integration test suite. You easily adapt this to Rspec if you would like.


require 'test_helper'

class JavascriptTest < ActionController::IntegrationTest
  # Download jsl from http://www.javascriptlint.com and add the jsl to your PATH environment variable
  def setup
    @js_paths = File.join(Rails.root, 'public', 'javascripts', '*.js')
  end

  test "validate javascript files for errors" do
    Dir[@js_paths].each do |js_path|
      output = %x[jsl -process #{js_path}]

      is_valid_js_file = output.include?("0 error(s)") ?  true : false
      assert(is_valid_js_file, "JSLint on #{js_path} should return no errors: \n #{output}")
    end
  end
end

Here's an example of the error output:


  2) Failure:
test_validate_javascript_files_for_errors(JavascriptTest)
    [/test/integration/javascript_test.rb:14:in `test_validate_javascript_files_for_errors'
     /test/integration/javascript_test.rb:10:in `each'
     /test/integration/javascript_test.rb:10:in `test_validate_javascript_files_for_errors']:
JSLint on ..../public/javascripts/application.js should return no errors:
 JavaScript Lint 0.3.0 (JavaScript-C 1.5 2004-09-24)
Developed by Matthias Miller (http://www.JavaScriptLint.com)

application.js
.../public/javascripts/application.js(3): lint warning: missing semicolon
        num = ;
........^

Deploying a Sinatra app on Nginx/Passenger with Capistrano and Git

Posted in Ruby, Web Development on June 9th, 2009 by tommy – View Comments

Need a quick web app done in Ruby? While Ruby on Rails is a great framework, it is overkill for a simple one page application. Enter: Sinatra.

Sinatra is a light-weight Ruby framework intended for fast web applications. In fact, here is all you need for a hello world app:


# myapp.rb
require 'rubygems'
require 'sinatra'
get '/' do
'Hello world!'
end

You can read more about Sinatra syntax on their documentation page.

This guide will get you started from the ground up to setting up and deploying a sinatra app on Nginx/Passenger with Capistrano and Git. I choose Passenger because it allows you to run multiple ruby applications easier than the traditional Mongrel setup.

Install Passenger/Nginx

Install the passenger gem:
gem install passenger

Install Nginx and Passenger Module:
passenger-install-nginx-module

The instructions from the install scripts are pretty much self-explanatory. More information on the official Phusion Passenger page. I also recommend installing Phusion’s Ruby Enterprise Edition for more memory savings.

Make sure your nginx.conf file is configured to include external files, notice the include statements at the end:


user deploy;
worker_processes 1;
error_log /var/log/nginx/error.log;
pid /var/run/nginx.pid;
events {
worker_connections 1024;
}
http {
include /opt/nginx/conf/mime.types;
default_type application/octet-stream;
passenger_root /opt/ruby-enterprise-1.8.6-20090421/lib/ruby/gems/1.8/gems/passenger-2.2.2;
passenger_ruby /usr/bin/ruby;
passenger_max_pool_size 2;
access_log /var/log/nginx/access.log;
sendfile on;
keepalive_timeout 65;
tcp_nodelay on;
gzip on;
server_names_hash_bucket_size 128;
include /opt/nginx/conf/conf.d/*.conf;
include /opt/nginx/conf/sites-enabled/*;
}

Create a Nginx entry file in /opt/nginx/conf/sites-available/APP_NAME:


server {
listen 80;
server_name YOURDOMAINNAME.HERE;
root /u/apps/production/APP_NAME/current/public;
passenger_enabled on;
}

Symlink this in the sites-enabled folder:

ln -s /opt/nginx/conf/sites-available/APP_NAME /opt/nginx/conf/sites-enabled/APP_NAME

A basic Sinatra template

Now, let’s get a basic Sinatra app on the server. To speed things along, clone this Sinatra template app:
git clone git://github.com/tc/sinatra-template.git

This includes a Rack config file(config.ru) and a cap deploy script.

This was forked from zapnap/sinatra-template but I removed datamapper since I didn’t intend to use a database. I also added a cap template script.

Edit the deploy.rb file and add in your git url/username/app_name


#========================
#CONFIG
#========================
set :application, "APP_NAME"
set :scm, :git
set :git_enable_submodules, 1
set :repository, "GIT_URL"
set :branch, "master"
set :ssh_options, { :forward_agent => true }
set :stage, :production
set :user, "deploy"
set :use_sudo, false
set :runner, "deploy"
set :deploy_to, "/u/apps/#{stage}/#{application}"
set :app_server, :passenger
set :domain, "DOMAIN_URL"
#========================
#ROLES
#========================
role :app, domain
role :web, domain
role :db, domain, :primary => true
#========================
#CUSTOM
#========================
namespace :deploy do
task :start, :roles => :app do
run "touch #{current_release}/tmp/restart.txt"
end
task :stop, :roles => :app do
# Do nothing.
end
desc "Restart Application"
task :restart, :roles => :app do
run "touch #{current_release}/tmp/restart.txt"
end
end

Deploying to Server

Let’s deploy the app onto the server now. I will assume you already have ssh key authentication setup on the server.

Now, setup your directories using

cap deploy:setup

and deploy your code from your git repo.

cap deploy

That’s it! Passenger is able to recognize Sinatra app through the RACK interface. You should be able to visit the domain and see your app up:
sinatra_template

The cap script will automatically restart the app by touching the restart.txt file.