Rails: Single File App 0

Posted by luca
on Tuesday, June 03

I took inspiration from the Pratik Naik post, and realized a more simplistic version of its Rails single file app. My implementation has only Rails as unique dependency.

require 'rubygems'
require 'action_controller'
require 'webrick'
require 'webrick_server'

class HelloWorldController < ActionController::Base
  session :off
  def index; render :text => 'Hello World!' end
end

ActionController::Routing::Routes.draw do |map|
  map.root :controller => "hello_world"
end

DispatchServlet.dispatch :port => 3000,
    :server_root  => File.dirname(__FILE__)

Update 2008-06-04: I just wrote another version which also uses ActiveRecord and a template.

require 'rubygems'
require 'activerecord'
require 'action_controller'
require 'webrick'
require 'webrick_server'

ActiveRecord::Base.establish_connection(
  :adapter  => 'sqlite3',
  :database => 'tiny_rails.sqlite3',
  :timeout  => 5000)

ActiveRecord::Schema.define do
  create_table :people, :force => true do |t|
    t.string :first_name
  end
end
class Person < ActiveRecord::Base; end
Person.create :first_name => 'Luca'

File.open('index.html.erb', 'w') do |f|
  f << "Hello, my name is <%=h @person.first_name %>!\n"
end

class HelloWorldController < ActionController::Base
  session :off
  def index
    @person = Person.find :first
    render :file => 'index.html.erb'
  end
end

ActionController::Routing::Routes.draw do |map|
  map.root :controller => "hello_world"
end

DispatchServlet.dispatch :port => 3000,
    :server_root  => File.dirname(__FILE__)

Just start the script and point your browser at http://localhost:3000!

Ruby: Class Methods Proxy 2

Posted by luca
on Thursday, May 29

Did you ever used class methods inside your instance methods? If yes, you probably noticed how frustrating can be to use the self.class.my_class_method syntax. A solution could be to create a private method which encapsulates the class one.

class Repository
  def self.path
    @@path
  end
  
  def print_path
    puts path
  end
  
  private
    def path
      self.class.path
    end
end
In the above example, #print_path can print the @@code value, without worrying about it's a class value, because we have wrapped it.

When I developed Sashimi I've widely used this technique, with a bad impact on the code duplication, and in order to DRY-up my code I extended the Ruby's Class class in this way:

class Class
  def class_method_proxy(*method_names)
    method_names.each do |m|
      self.class_eval %{
        # Proxy method for <tt>#{self.class.name}##{m}</tt>
        def #{m}(*args)
          self.class.#{m}(*args)
        end
        private :#{m}
      }, __FILE__, __LINE__
    end
  end
end

This approach allow us to annotate our classes, choosing which class methods should be available as private methods.
Now, our example class should look like the following:

class Repository
  def self.path
    @@path
  end
  class_method_proxy :path

  def print_path
    puts path
  end
end

Of course you can pass multiple symbols to proxy many methods at the same time.

class_method_proxy :path, :another_path, :a_third_one

Ruby: XML Parsing With SAX 3

Posted by luca
on Wednesday, January 30

SAX is an event-driven parser for XML.

It sequentially reads the xml and generates special events. So, if you want to use SAX, you should implement the code to handle them. It's quite different from the DOM model, where the whole xml is parsed and loaded in an tree.
As you can see, the first approach is more difficult than the DOM one. Why we should use it? Depends.
If you want to extract certain informations from a big file, probably you should choose a SAX implementation, in this way you can avoid the initial DOM loading overhead.

The Ruby XML Library

The Ruby core library has a built-in XML parser (both DOM and SAX) called REXML, but it's terribly slow, it's highly advisable to use libxml. It's a binding to the popular library from Gnome and it was released as gem.

The Ruby Implementation

In first instance we need an handler, to deal with the SAX events.

class Handler
  def method_missing(method_name, *attributes, &block)
  end
end

Libxml generates several events and it expects to find certain methods into the class assigned ad handler. With method_missing we simply avoid any exception.

A More Useful Example

We try to extract the most recent headlines of a blog.

Download the feed:

curl http://feeds.feedburner.com/LucaGuidi >> luca.xml

Now we need our custom SAX parser:

require 'rubygems'
require 'xml/libxml'
require 'handler'

class SaxParser
  def initialize(xml)
    @parser = XML::SaxParser.new
    @parser.string = xml
    @parser.callbacks = Handler.new
  end

  def parse
    @parser.parse
    @parser.callbacks.elements
  end
end

We have just wrapped the SAX parser from libxml and we have registered our first class as callback handler.

Now we are going to improve the handler to recognize and save the post titles:

class Handler
  attr_accessor :elements

  def initialize
    @elements = []
  end

  def on_start_element(element, attributes)
    @print = true if element == 'title'
  end

  def on_characters(characters = '')
    @elements << characters if @print
  end

  def on_end_element(element)
    @print = false
  end

  # Handle all missing methods of the SAX events chain.
  # You can implement or omit one or many of those methods, without any raising Exception.
  # 
  # The complete chain is:
  #   on_start_document
  #   on_processing_instruction(instruction, arguments)
  #   on_start_element(element, attributes)
  #   on_characters(characters = '')
  #   on_end_element(element)
  #   on_end_document
  def method_missing(method_name, *attributes, &block)
  end
  end

When the handler is instantiated we create an internal array to store our results, then when we find and title element we set on true the print flag. When it's true we can store the data into elements, then we set on false on the ending handler of the element.

Usage

We create a trivial script:

#!/usr/bin/env ruby
require 'sax_parser'

xml = open(ARGV[0], 'r').collect { |l| l }.join
puts SaxParser.new(xml).parse

From the shell:

./parse luca.xml

Conclusion

SAX is less elegant and easy than DOM, but could be very useful in certain cases.

Ruby: Read A File With One Line Of Code 3

Posted by luca
on Tuesday, November 27

This snippet shows how to read a file and puts all the lines into an array.

f = *open('file.txt').map {|l| l.rstrip}
  # => ["Hi, from the", "txt file."]

Explanation

The open method returns an IO object, that include the Enumerable module, now we can just use #map (or #collect).

The splat operator is only a sugar syntactical shortcut for map

.

Rails: How to force plugins loading in 2.0 0

Posted by luca
on Monday, October 08

Sometimes a Rails plugin can be dependant from another one. Since plugins are loaded in alphabetic order, probably you need to load the third part plugin first.

The release 1.2.4 2.0PR introduces breaking changes for this mechanism.

I'm developing a plugin called ClickToGlobalize, that's dependant on Globalize, here the code for plugin initialization:

# Force Globalize loading.
if Rails::VERSION::STRING.match /^1\.2+/
  load_plugin(File.join(RAILS_ROOT, 'vendor', 'plugins', 'globalize'))
else
  Rails::Initializer.run { |config| config.plugins = [ :globalize ] }
end

require 'click_to_globalize'

Explanation

Use the old mechanism if the current release is minor than the last release with it (1.2.3 1.2.4), else use the Rails::Initializer class.

This code also works if you use the Rails edge into app/vendor/rails.

UPDATE: Ooops, wrong version reporting, the breaking changes are in 2.0PR. The release 1.2.4 still uses #load_plugin.

Ruby: How to check the operating system 0

Posted by luca
on Friday, October 05

Today I'm finishing the code cleanup for my latest Rails plugin (will be soon released) and I want to execute some rake tasks, only if the OS supports certain system calls.

The following snippet helps to check the current platform.

# (c) 2007 Luca Guidi (www.lucaguidi.com) - Released under MIT License.
# This code was inspired by Prototype rake test tasks.
require 'webrick'

class OperatingSystem
  class << self
    def host
      Config::CONFIG['host']
    end
  
    def macos?
      host.include?('darwin')
    end

    def linux?
      host.include?('linux')
    end
  
    def windows?
      host.include?('mswin')
    end
    
    def current
      case 
        when macos?:   'macos'
        when linux?:   'linux'
        when windows?: 'windows'
        else           'unknown'
      end
    end
  end
end