2009-05-31

Syntax highlighted Python

Example syntax highlighted Python code: (please ignore)
def Hello(self, x, y): 
return x * y + "foo" # bar
The syntax highlighting was done in the borland style on http://pygments.org/demo/.

2009-05-21

Ruby on Rails 2.3 unit tests

The blog post Ruby on Rails Unit Tests explains why it is a bad idea to give the unit tests access to the database, and how to set up your Rails project so that you get an exception if you try to access the database with ActiveRecord model objects from a unit test. Unfortunately, that blog post doesn't work with the latest Rails. This blog post explains how to set up a Ruby on Rails 2.3 project to disallow database accees in the unit tests. The instructions were tested with Rails 2.3.2.

Step 1. Create file lib/tasks/unit_tests.rake with the following contents:
Rake::Task[:'test:units'].prerequisites.delete('db:test:prepare')
Step 2. Create file test/unit_test_helper.rb with the following contents:
# similar to autogenerated test/test_helper.rb
#
fail "some of the unit tests has loaded test_helper.rb. Please change " +
"(require 'test_helper') to (require 'unit_test_helper') in " +
"tests/unit/**/*.rb" if $".include?('test_helper.rb')
fail "some of the unit tests has loaded test_help.rb. Please make sure that " +
"the first line is (require 'unit_test_helper') in tests/unit/**/*.rb" if
$".include?('test_help.rb')
require File.expand_path(File.dirname(__FILE__) + "/../config/environment")
HIDE_ActiveRecord = self.class.send(:remove_const, :ActiveRecord)
require 'test_help' # rails 2.3.2 standard module
ActiveRecord = self.class.send(:remove_const, :HIDE_ActiveRecord)

class FakeConnection
class InvalidActionError < StandardError
end
COLUMNS = {}
def self.columns(table_name, name=nil)
if COLUMNS.has_key?(table_name)
COLUMNS[table_name]
else
raise InvalidActionError, "please create something like this first: " +
"FakeConnection::COLUMNS[#{table_name.inspect}] = [ " +
"ActiveRecord::ConnectionAdapters::Column.new(name=..., " +
"default=nil, sql_type=\"text\", null=false), ...]"
end
end
DB_ERROR_MSG = 'You cannot access the database from a unit test'
def self.quote_table_name(*args) # called from ActiveRecord::Base.find
raise InvalidActionError, DB_ERROR_MSG, caller
end
def self.quote_column_name(*args) # called from ActiveRecord::Base.delete
raise InvalidActionError, DB_ERROR_MSG, caller
end
def self.select_all(*args) # called from ActiveRecord::Base.find_by_sql
raise InvalidActionError, DB_ERROR_MSG, caller
end
def self.transaction(*args) # called from ActiveRecord::Base.save
raise InvalidActionError, DB_ERROR_MSG, caller
end
end
class << ActiveRecord::Base
def connection
FakeConnection
end
end
Step 3. Make sure all your unit tests (i.e. =*.rb= files in =test/unit=, recursively) start with the line require 'unit_test_helper' instead of require 'test_helper'. Don't forget any of the =*.rb= files, otherwise you'll get an error message at test file load time.

Step 4. If you need access to your model objects, add the necessary column declaration to your test file. For example, for model class Foo:
FakeConnection::COLUMNS['foos'] = [
ActiveRecord::ConnectionAdapters::Column.new(
name="bar1", default=nil, sql_type="varchar(255)", null=false),
ActiveRecord::ConnectionAdapters::Column.new(
name="bar2", default=nil, sql_type="integer", null=false),
]
After this, you can do a Foo.new in your tests – but you won't be able to save the object, and Foo.find, Foo.find_by_sql and Foo.delete won't work either. If you try any of those, a FakeConnection::InvalidAction gets raised. To test such a functionality, use an integration test instead of a unit test. To do so, create your test file as test/integration/*.rb instead of test/unit/*.rb.

Once all steps are done, run your unit tests with rake test:units. This will run all tests in all *.rb files in the test/units directory (recursively). If you get an exception, and you need the full backtrace, rerun with BACKTRACE=1 rake test:units. For an even longer backtrace, run rake --trace test:units.

2009-05-14

Java .class file converter which makes method and fields public available

ClassPublic.Java is a Java .class file converter which makes a Java class public and non-final, its fields public and its methods public and non-final. ClassPublic.java has a compact implementation, depending only on a J2SE 1.5.0 or newer. ClassPublic.java can be used to increase interoperability and code reusability of a .class file whose .java source is not available or it is not feasible to recompile.

Download classpublic-latest.zip.

2009-05-13

Java class disassembler with offset display available

jdisasm.py is a Java class file disassembler implemented as a Python script. jdisasm.py displays a java .class file in a human readable form, showing the class name, the field names and types, the method names, types and codes (including instruction mnemonics). For each item shown, the file offset is prepended. (Neither javap or jad can display the file offset.)

Download jdisasm-latest.tar.gz.

2009-05-05

How to deploy a Ruby on Rails application using Phusion Passenger and Rails Enterprise Edition on Debian Etch or Ubuntu Hardy

This post gives step-by-step instructions on deploying a Ruby on Rails application on a Linux-based server using Phusion Passenger and Ruby Enterprise Edition. The instructions are for a Debian Etch system, but they should work with minor modifications on any Linux system based on Debian or Ubuntu (including Debian Etch Mini, available as Minimal Gnome Desktop from http://www.visoracle.com/download/debian/.

It doesn't matter if you have Ruby installed on the server, because you'll compile and install Ruby Enterprise Edition (REE) 1.8.6 into its own directory, and run the deployed Rails application using REE. REE is Ruby with some garbage collection improvements to allow memory page reuse across fork()ed subprocesses.

So the Ruby interpreter, Ruby packages and Ruby gems already installed to the system won't be used or needed. But some libraries (such as SQLite client and MySQL client) will be used. You have to install those libraries including the development packages using the system's package manager. Example:
# apt-get install libsqlite3-dev
# apt-get install libpq-dev # optional, needed if Rails app connects to PostgreSQL
# apt-get install libmysqlclient15-dev # needed if app connects to MySQL
You also have to install some development packages in order to be able to compile and install REE. Do this:
# apt-get install wget gcc g++ make libc6-dev libreadline5-dev zlib1g-dev libssl-dev
Should other packages be missing, the installer script run below will tell you the apt-get install command to run.

You can get the REE installer tarball from http://www.rubyenterpriseedition.com/. Feel free to substitute any newer ruby-enterprise-*.tar.gz. filename to the command below. To download and install REE (including a bundled Ruby, rubygems, Rake, Rails and Ruby client modules for SQLite 3, MySQL and PostgreSQL), run this:
# cd /usr/src
# wget http://rubyforge.org/frs/download.php/55511/\
ruby-enterprise-1.8.6-20090421.tar.gz
# tar xzvf ruby-enterprise-1.8.6-20090421.tar.gz
# ruby-enterprise-1.8.6-20090421/installer \
-c--enable-pthread -a/usr/local/ruby-enterprise-1.8.6
# strip /usr/local/ruby-enterprise-1.8.6/bin/ruby
The installer is an interactive script, and it would have asked questions and waited for you to press Enter occasionally if you hadn't called it with the -a flag. Nevertheless, it show you some nice, colorful messages indicating progress. In a few minutes, it finishes compilation and installation to /usr/local/ruby-enterprise-1.8.6. Please note that the installer runs /usr/local/ruby-enterprise-1.8.6/gem install to download, compile and install some gems (Ruby modules). Please note that -c--enable-pthread is necessary to work around a bug in ruby-enterprise-1.8.6-20090421.tar.gz, which causes fork()ed subprocesses to exit early with SIGVTALRM.

Scroll up and check that the MySQL and PostgreSQL Ruby client modules were installed properly. If not (and you need those), then apt-get install the necessary libraries, and run the installer or gem install again. Example command lines (optional, needed only if you need MySQL or PostgreSQL and the installer failed the compile the relavant Ruby modules):
# apt-get install libmysqlclient15-dev
# /usr/local/ruby-enterprise-1.8.6/bin/gem install --no-ri --no-rdoc mysql

# apt-get install libpq-dev
# /usr/local/ruby-enterprise-1.8.6/bin/gem install --no-ri --no-rdoc postgres
If you have any other gems needed by your Rails application, install them now (similarly to the postgres gem above). Please note that all gems should be installed as root in the setup described in this tutorial, and gems are shared among Rails applications.

Create a user (non-root) and download your Rails application to the user's home (e.g. /home/myrails/myapp). The best way to download the application is the checkout function of a version control system (such as Subversion), because using that you can easily re-deploy the application later. (Please note that Capistrano can be used to fully automate Rails application deployment, but this is beyond the scope of this tutorial.) If you have multiple Rails applications, you can create as many users as you want, and map applications to users freely. The application would run in production as the UID of that user (myrails). As myrails, add these lines to /home/myrails/.bash_profile:
export GEM_HOME=/usr/local/ruby-enterprise-1.8.6/lib/ruby/gems/1.8
export PATH="/usr/local/ruby-enterprise-1.8.6/bin:$PATH"
Log in again, and check that type -p ruby gem rake rails prints a filename inside /usr/local/ruby-enterprise-1.8.6. Make sure that all scripts in /home/myrails/myapp/scrit/* start with /usr/bin/env ruby, and they don't have a specific Ruby interpreter (such as /usr/bin/ruby) hardcoded. Create the initial database of your Rails application (by running rake db:migrate etc.). Quickly try your application by starting
/home/myrails/myapp/script/server --environment=production
and visiting http://localhost:3000/ . Try some functionality which accesses the database. As soon as it works fine, stop the server script.

Now it is time to install Apache2 (please note that Apache 1.3 won't suffice, because Phusion Passenger, the Rails--Apache connector this tutorial uses needs Apache 2). Run this:
# apt-get install apache2 apache2-prefork-dev libapr1-dev
# /usr/local/ruby-enterprise-1.8.6/bin/passenger-install-apache2-module -a
The passenger-install script gives you nice, colorful instructions how to configure your Apache. Memorize those instructions, and feel free to use them in place of the instructions given in this tutorial. Configure your Apache2 as usual. (Setting up and populating a DocumentRoot, setting up SSL (https://) and setting up Apache VirtualHost entries is not covered in this tutorial.) Create file /etc/apache2/mods-available/passenger.conf containing the right paths, for example (type it without the line break):
PassengerRoot /usr/local/ruby-enterprise-1.8.6/lib/ruby/gems/1.8/gems
/passenger-2.2.2
PassengerRuby /usr/local/ruby-enterprise-1.8.6/bin/ruby
Crete file /etc/apache2/mods-available/passenger.load containing the right paths, for example (type it without the line break):
LoadModule passenger_module /usr/local/ruby-enterprise-1.8.6/lib/ruby/gems/
1.8/gems/passenger-2.2.2/ext/apache2/mod_passenger.so
Run this:
# ln -s ../mods-available/passenger.load /etc/apache2/mods-enabled/
# ln -s ../mods-available/passenger.conf /etc/apache2/mods-enabled/
Restart apache with
# /etc/init.d/apache2 restart
Wait about 60 seconds, and make sure you don't get any Passenger-related error messages at the end of /var/log/apache2/error.log.

If you want to deploy your Rails application to the root URI of an Apache VirtualHost, all you have to do is specifying
<VirtualHost server.name:80>
ServerName server.name
DocumentRoot /home/myrails/myapp/public
</VirtualHost>
in your /etc/apache2/sites-available/default. Do not specify RailsBaseUrl /, that wouldn't work. Restart Apache, and visit http://server.name/ . The first page download may take a few seconds, because Phusion Passenger starts the Rails application at that time. The latency of further downloads should be negligible. If you get a colorful, but unhelpful error message like The page you were looking for doesn't exist.; You may have mistyped the address or the page may have moved., then visit http://localhost/ instead, on which Phusion Passenger doesn't hide the exception raised by Rails. Examining /var/log/apache2/error.log can also help you diagnose the problem.

If you want to deploy your Rails application to a non-root URI (e.g. http://server.name/myapp), then first make sure that the necessary workarounds are applied. Follow the instructions on http://ptspts.blogspot.com/2009/05/how-to-fix-railsbaseuri-sub-uri-with.html of adding files /home/myrails/myapp/config/initializers/!fix_relative_url_root.rb and /home/myrails/myapp/config/initializers/!relative_url_for.rb. Make sure those files are added to your source code repository as well, if applicable. Then create a symlink to your application's public directory. Assuming that your Apache2 DocumentRoot is /var/www, create the symlink by running
# ln -s /home/myrails/myapp/public /var/www/myapp
Make sure you have these lines in your Apache2 configuration (most probably /etc/apache2/sites-available/default):
DocumentRoot /var/www
<Directory /var/www>
Options +FollowSymlinks
</Directory>
RailsBaseUri /myrails
It's OK to have multiple RailsBaseUri directives, both for multiple or a single application. Restart Apache2 if needed. Visit http://server.name/myrails . If you see an unhelpful error message instead of your application's main page, then visit http://localhost/myrails to get the details. Examining /var/log/apache2/error.log can also help you diagnose the problem.

Please note that if you have mutiple Rails applications (i.e. multiple public directories (after following symlinks)), Phusion Passenger will not
reuse the same Ruby worker process for running multiple applications. Please also note that Ruby worker processes spawned by Phusion Passenger are single-threaded. Phusion Passenger treats two Rails applications the same if they have the same public directory (after following symlinks), so the http:// and https:// versions and aliases of your Rails application may run on the same Ruby worker process.

Rails applications running under Phusion Passenger's control in the production environment don't pick up file changes automatically. So if you change your application's code or config, you have to restart the application: run (as myrails)
# touch /home/myrails/myapp/tmp/restart.txt
and visit the application's URL. The first download should take a few seconds, because Phusion Passenger is restarting your Rails application.

Please refer to Phusion Passenger's documentation for more information about running your Rails application with Phusion Passenger with an Apache2 frontend.

2009-05-04

How to fix RailsBaseUri (sub URI) with Phusion Passenger without setting relative_url_root

There is a bug which prevents correct routing with Phusion Passenger 2.2.2 and Rails 2.3.2 (>= 2.2.2) and RailsBaseUri. For example, if you have RailsBaseUri myapp, and you visit http://localhost/myapp/mycontroller/myview, then the request gets routed to controller myapp instead of mycontroller, and you most probably get the error message Routing Error; No route matches "/myapp/mycontroller/myview" with {:method=>:get}. If you visit http://myserver/myapp/mycontroller/myview, you'll get the error message The page you were looking for doesn't exist. You may have mistyped the address or the page may have moved.. This post gives instructions how to work around the bug.

The bug is mentioned in various places, such as
http://blog.ashchan.com/archive/2008/12/10/passengers-railsbaseuri-not-working/ and https://rails.lighthouseapp.com/projects/8994-ruby-on-rails/tickets/1946-setting-a-relative-root-url-via-a- and http://github.com/rails/rails/commit/a87462afcb6c642e59bfcd2e11e3351e2b718c38 .

The manual workaround (according to what is suggested on the pages above) is adding the line below to config/environments/production.rb:
config.action_controller.relative_url_root = '/myapp'
We propose a completely automatic workaround here, which doesn't need hardcoding URIs to the Rails application configuration. To fix this, create a file config/initializers/!fix_relative_url_root.rb with the following contents:
# automatic relative_url_root fix
# for Phusion Passenger 2.2.2 and Rails 2.3.2 (>= 2.2.2)
# by pts@fazekas.hu at Mon May 4 20:48:38 CEST 2009
# from http://ptspts.blogspot.com/2009/05/how-to-fix-railsbaseuri-sub-uri-with.html
fail unless ActionController::Request # check loaded
module ActionController
class Request
def initialize(env)
@env = env # Rack::Request#initialize does only this
path = request_uri.to_s[/\A[^\?]*/]
sn = @env['SCRIPT_NAME']
if (RAILS_ENV == 'production' and
(sn.empty? or sn.starts_with?('/')) and
path == sn + @env['PATH_INFO'])
Base.relative_url_root = sn
end
end
end
end
In addition to the fix above, here is another fix for url_for and redirect_to so they automatically prepend ActionController::Base.relative_url_root when they get a string starting with a slash. To apply the fix, create file config/initializers/!relative_url_for.rb with the following contents:
# fix url_for and redirect_to to use ActionController::Base.relative_url_for
# fix for Rails 2.3.2
# by pts@fazekas.hu at Mon May 4 22:38:44 CEST 2009
# from http://ptspts.blogspot.com/2009/05/how-to-fix-railsbaseuri-sub-uri-with.html
fail unless ActionController::Base # check loaded
fail unless ActionView::Helpers::UrlHelper # check loaded
module ActionController
class Base
alias url_for__ptsroot__ url_for
def url_for(options = {})
options = Base.relative_url_root.to_s + options if
options.kind_of?(String) and options.starts_with?('/')
url_for__ptsroot__(options)
end
alias redirect_to__ptsroot__ redirect_to
def redirect_to(options = {})
options = Base.relative_url_root.to_s + options if
options.kind_of?(String) and options.starts_with?('/')
redirect_to__ptsroot__(options)
end
end
end
module ActionView
module Helpers
module UrlHelper
alias url_for__ptsroot__ url_for
def url_for(options = {})
return escape_once(
::ActionController::Base.relative_url_root.to_s + options) if
options.kind_of?(String) and options.starts_with?('/')
url_for__ptsroot__(options)
end
end
end
end