Showing posts with label Ruby. Show all posts
Showing posts with label Ruby. Show all posts

Saturday, May 07, 2011

Rails 3 MongoDB recipe

Here's a quick and dirty recipe for getting started with MongoDB and Rails 3 using mongo_mapper. We'll get set up and test out a Restful web service.

I installed MongoDB with MacPorts.

sudo port selfupdate
sudo port install mongodb

Start a rails project. You may want to leave out ActiveRecord using the switch --skip-active-record as suggested by the Getting Started guide. That's optional. You might want to leave it in, if you intend to use a relational DB alongside Mongo.

  rails new <myproject>

Add to Gemfile

source 'http://gemcutter.org'
gem 'mongrel'
gem 'mongo_mapper'
gem 'bson'
gem 'bson_ext'
gem 'SystemTimer'
gem 'rails3-generators'
gem 'json'

Bundler happily installs all this stuff.

bundle install

Create config/initializers/mongodb.rb

MongoMapper.connection = Mongo::Connection.new('localhost', 27017)
MongoMapper.database = "#myapp-#{Rails.env}"

if defined?(PhusionPassenger)
   PhusionPassenger.on_event(:starting_worker_process) do |forked|
     MongoMapper.connection.connect if forked
   end
end

Start the MongoDB server, giving it a place to put data files:

mkdir <path>/data
mongod --dbpath <path>/data

Create an entity to be stored in MongoDB. I like to use a database of Nerds as my test application.

rails generate scaffold Nerd name:string description:string iq:integer --orm=mongo_mapper 

I originally gave description type "text", but that didn't work for me, producing, "NameError in NerdsController#show, uninitialized constant Nerd::Text". So, I edited the model file to look like this:

class Nerd
  include MongoMapper::Document         
  key :name, String
  key :description, String
  key :iq, Integer
end

Getting mongodb objects as XML doesn't work?

I saw an error: undefined method `each' with mongo_mapper (0.8.6), which was fixed by upgrading to (0.9.0)

Started GET "/nerds/4dc5c41a1ff2367744000004.xml" for 127.0.0.1 at Sat May 07 15:41:54 -0700 2011
  Processing by NerdsController#show as XML
  Parameters: {"id"=>"4dc5c41a1ff2367744000004"}
Completed 200 OK in 9ms (Views: 2.6ms | ActiveRecord: 0.0ms)
Sat May 07 15:41:54 -0700 2011: Read error: #<NoMethodError: undefined method `each' for #<Nerd:0x1040bfb80>>

JSON web services in Rails

You'll likely want to serve up and accept JSON in HTTP requests. For some crazy reason, Rails doesn't generate code for JSON in the controller, only HTML and XML. You have to add a line to the respond_to code block, like this:

class NerdsController < ApplicationController
  # GET /nerds
  # GET /nerds.xml
  def index
    @nerds = Nerd.all

    respond_to do |format|
      format.html # index.html.erb
      format.xml  { render :xml => @nerds }
      format.json { render :json => @nerds }
    end
  end

  # GET /nerds/1
  # GET /nerds/1.xml
  def show
    @nerd = Nerd.find(params[:id])

    respond_to do |format|
      format.html # show.html.erb
      format.xml  { render :xml => @nerd }
      format.json { render :json => @nerds }
    end
  end
  ...
end

Use curl to test that this works. First, get HTML, then ask for JSON using the accept header.

curl --request GET -H "accept: application/json" http://localhost:3000/nerds/4dc5c41a1ff2367744000004

OK, now we can serve JSON, how about receiving it in POST requests? Another couple quick additions and we're in business.

# POST /nerds
# POST /nerds.xml
def create
  @nerd = Nerd.new(params[:nerd])

  respond_to do |format|
    if @nerd.save
      format.html { redirect_to(@nerd, :notice => 'Nerd was successfully created.') }
      format.xml  { render :xml => @nerd, :status => :created, :location => @nerd }
      format.json  { render :json => @nerd, :status => :created, :location => @nerd }
    else
      format.html { render :action => "new" }
      format.xml  { render :xml => @nerd.errors, :status => :unprocessable_entity }
      format.json  { render :json => @nerd.errors, :status => :unprocessable_entity }
    end
  end
end
curl --request POST -H "Content-Type: application/json" -H "Accept: application/json" --data '{"nerd":{"name":"Donald Knuth", "iq":199, "description":"Writes hard books."}}' http://localhost:3000/nerds

If you have an entity named "nerd" it's pretty reasonable to expect the XML representation to look something like <nerd>...</nerd>. By analogy to that, they expect your JSON to look like this {"nerd":{...}}, which I'm not sure I like. I coded around the issue, which is might be unwise, by making the controller's create method look like this:

# POST /nerds
# POST /nerds.xml
def create

  input = params[:nerd] || request.body.read
  if request.content_type == 'application/json'
    @nerd = Nerd.new(JSON.parse(input))
  else
    @nerd = Nerd.new(input)
  end

  respond_to do |format|
    if @nerd.save
      format.html { redirect_to(@nerd, :notice => 'Nerd was successfully created.') }
      format.xml  { render :xml => @nerd, :status => :created, :location => @nerd }
      format.json  { render :json => @nerd, :status => :created, :location => @nerd }
    else
      format.html { render :action => "new" }
      format.xml  { render :xml => @nerd.errors, :status => :unprocessable_entity }
      format.json  { render :json => @nerd.errors, :status => :unprocessable_entity }
    end
  end
end

...which you can test with curl like so:

curl --request POST -H "content-type: application/json" --data '{"name":"Bozo", "iq":178, "description":"A very smart clown."}' http://localhost:3000/nerds

So, there you have it - Rails 3 and MongoDB playing ReSTfully together.

Warning: Current release versions of MongoDB have a 4MB maximum document size. This makes some sense as documents are often serialized and deserialized in memory. Apparently, this is being raised in later versions. Fully streaming APIs would certainly help, but that brings up the question of how much of the XML dog-pile of technologies will (or should) be replicated in JSON. Guess we'll see.

Sunday, April 10, 2011

Thinking about CRUD has damaged your karma

I'm spending some time trying out MongoDB. Mongo is a NoSQL database that stores documents in a binary variant of JSON called BSON.

Mongo is often compared to CouchDB. But, aside from the fact that they both store JSON documents, their approaches are quite different.

Mongo stores documents in collections, which are vaguely like tables in a SQL database. Mongo queries are partial JSON documents matched against existing documents in the database. Couch builds views with map-reduce and is, in general, a little more conceptually heavy while Mongo is more straight forward with direct analogs to most SQL features.

One feature I like is the mongo console. It's a full javascript interpreter, which means you can easily script bulk updates and maintenance tasks.

The MongoDB site has a quickstart guide for popular OS's and a tutorial that will get you started using the console, as well as specific guides for loads of client languages.

You have not yet reached enlightenment...

From within Ruby, we can communicate with MongoDB with the mongo, bson and bson_ext gems. One fun way to learn about using MongoDB from Ruby is through the nicely kooky MongoDB_Koans, a series of unit tests with small omissions or bugs. You fix the bugs and make the tests pass, while the test harness gently urges "Please meditate on the following code...".

Thursday, April 07, 2011

Installing the Ruby mysql gem

I've had issues installing the ruby mysql gem a couple of times, so I thought I'd document what finally worked here. I had Rails 2.3.8 running on the pre-installed Ruby 1.8.7 that comes with OS X 10.6. I needed to install Rails 3.x for another project. Having multiple versions of Rails is supposed to work OK, so I just did:

sudo gem install rails

Problems began here, but I flailed rather than keeping careful notes. At some point, I ended up thinking an fresh install of MySQL might help, so I installed version 5.5.9 from the DMG on dev.mysql.com. Maybe, I shoulda used MacPorts, cause that made things worse. After that, neither version of rails could connect to MySQL and my cubical-neighbors had new respect for my colorful vocabulary. Rails would fail, croaking up this cryptic message:

uninitialized constant MysqlCompat::MysqlRes

This thread helped. You need to be careful that MySQL, Ruby and MySQL/Ruby are compiled to a common architecture. You can do this by specifying ARCHFLAGS in the environment.

sudo env ARCHFLAGS="-arch x86_64" gem install --no-rdoc --no-ri mysql -- --with-mysql-config=/usr/local/mysql/bin/mysql_config

Compiling the gem properly is one step. The gem depends at run time on the mysql client library, which it needs to be able to find. Specify that, like so:

export DYLD_LIBRARY_PATH="/usr/local/mysql/lib:$DYLD_LIBRARY_PATH"

With the combination of these two clues, both versions of Rails seem to work happily.

Diagnosing problems with Ruby and Gems

A couple key commands for debugging RubyGems are gem list and gem env. Also, gem uninstall for removing the wreckage of failed attempts. Some recommend RVM, which I may try out some time. Also, I use the deprecated practice of installing gems with sudo. I guess I should learn to install them in my user directory.

The Ruby that comes with OS X 10.6, at least on my machine, looks like this:

$ /usr/bin/ruby --version
ruby 1.8.7 (2009-06-12 patchlevel 174) [universal-darwin10.0]

$ file /usr/bin/ruby
/usr/bin/ruby: Mach-O universal binary with 3 architectures
/usr/bin/ruby (for architecture x86_64): Mach-O 64-bit executable x86_64
/usr/bin/ruby (for architecture i386): Mach-O executable i386
/usr/bin/ruby (for architecture ppc7400): Mach-O executable ppc

Installing a fresh Ruby from MacPorts probably wasn't necessary, but that didn't stop me:

$ which ruby
/opt/local/bin/ruby

$ /opt/local/bin/ruby --version
ruby 1.8.7 (2011-02-18 patchlevel 334) [i686-darwin10]

$ file /opt/local/bin/ruby 
/opt/local/bin/ruby: Mach-O 64-bit executable x86_64

I hope this helps someone. This is a pain, but the same thing in Python is worse.

Tuesday, February 01, 2011

Annotated source code

We programmers are told that reading code is a good idea. It may be good for you, but it's hard work. Jeremy Ashkenas has come up with a simple tool that makes it easier: docco. Ashkenas is also behind underscore.js and coffeescript, a dialect of javascript in which docco is written.

Interesting ways to mix prose and code have appealed to me ever since I first discovered Mathematica's live notebook, which lets you author documents that combine executable source code, typeset text and interactive graphics. For those who remember the early 90's chiefly for their potty training, running Mathematica on the Next pizza boxes was like a trip to the future. Combining the quick cycles of a Read-evaluate-print-loop with complete word processing and mathematical typesetting encourages you to keep lovely notes on your thinking and trials and errors.

Along the same lines, there's Sweave for R and sage for Python.

Likewise, one of the great innovations of Java was Javadoc. Javadoc doesn't get nearly enough credit for the success of Java as a language. It made powerful API's like the collections classes a snap and even helped navigate the byzantine complexities of Swing and AWT.

These days, automated documentation is expected for any language. Nice examples are: RubyDoc, scaladoc, Haddock (for Haskell). Doxygen works with a number of languages. Python has pydoc, but in practice seems to rely more on the library reference. Anyway, there are a bunch, and if your favorite language doesn't have one, start coding now.

The grand-daddy of these ideas is Donald Knuth's literate programming.

I believe that the time is ripe for significantly better documentation of programs, and that we can best achieve this by considering programs to be works of literature. Hence, my title: "Literate Programming."

Let us change our traditional attitude to the construction of programs: Instead of imagining that our main task is to instruct a computer what to do, let us concentrate rather on explaining to human beings what we want a computer to do.

The practitioner of literate programming can be regarded as an essayist, whose main concern is with exposition and excellence of style. Such an author, with thesaurus in hand, chooses the names of variables carefully and explains what each variable means. He or she strives for a program that is comprehensible because its concepts have been introduced in an order that is best for human understanding, using a mixture of formal and informal methods that reinforce each other.

Indeed, Ashkenas references Knuth, calling docco "quick-and-dirty, hundred-line-long, literate-programming".

This goodness needs to come to more language. There's a ruby port called rocco by Ryan Tomayko. And for Clojure there's marginalia.

I love the quick-and-dirty aspect and that will be the key to encouraging programmers to do more documentation that looks like this. I hope they build docco, or something like it, into github. Maybe one day there will be a Norton's anthology of annotated source code.

Vaguely related

Monday, October 04, 2010

Worse than linear performance in Rails ActiveRecords with associations

The other day I was doing a task that required iterating through 142 thousand active record objects and computing simple aggregate statistics. Taking convenience over efficiency, I thought I'd just whip up a script to run in a the Ruby on Rails script runner. I usually put little print-line debugging statements in programs like that as a poor man's progress bar. I noticed that as the script ran, it seemed to get slower and slower.

My ActiveRecord abjects are linked up as shown below. The names are changed to protect the guilty, so I might be neglecting something important.

class Basket < ActiveRecord::Base
  has_many :basket_items
  has_many :items, :through => :basket_items
end

class Item < ActiveRecord::Base
  belongs_to :category
  has_many :basket_items
  has_many :baskets, :through => :basket_items
end
Basket.find(:all).each do |basket|
  puts "basket.id = #{basket.id-20000}\t#{Time.now - start_time}" if basket.id % 1000 == 0
end

This part is super fast, 'cause nothing much is done inside the loop other than printing. I don't know exactly how Rails does the conversion from DB fields to objects. That bit seems to be taking place outside the loop. Anyway, what happens if we access the associated items?

counter = 0
Basket.find(:all).each do |basket|
  puts "basket.id = #{basket.id-20000}\t#{Time.now - start_time}" if basket.id % 1000 == 0
  counter += basket.items.length
end

In the second case, we trigger the lazy-load of the list of items. Baskets average 18.7 items. The distribution of item count within the data is more or less random and, on average, flat. Now, we see the timings below.

In other words, this is an nxm operation, but m (the number of items) is more or less constant. I can't guess why this wouldn't be linear. Garbage collection? Should that level off? Maybe, we're just seeing the cost of maintaining the heap? Items are also associated with baskets. Maybe Rails is spending time fixing up that association?

The real script, only a little more complex that the one above, ran in about 30 hours. I realize this is all a little half-baked and I don't intend to chase it down further, but I'm hoping to nerd snipe someone into figuring it out. I'm probably missing a lot and leaving out too much.

Monday, January 18, 2010

Split with escaped delimiters

I have a lot of little scripts that read tab-delimited text files, 'cause that's the way things get done in the creaky world of bioinformatics. Due to the limitations of flat files, you often see an attributes field, which holds key-value pairs in some more-or-less parsable way, typically something like this:

key1=value1;key2=value2;

Parsing this with regular expressions is pretty easy. Take this fragment of Ruby:

 attributes = {}
 File.foreach('attributes.txt') do |line|
   line.chomp!()
   line.scan(/([^;=]+)=([^;]+)/) do |key, value|
     attributes[key] = value
   end
 end

 attributes.each_pair do |key, value|
   puts("#{key}=#{value}")
 end

Because I'm a knucklehead, I decided to make it hard. What if we wanted to allow escaped delimeter characters? RegexGuru tells you that Split() is Not Always The Best Way to Split a String. This will work.

 def unescape(string)
   string.gsub("\\\\","\\").gsub("\\=","=").gsub("\\;",";")
 end

 attributes = {}
 File.foreach('attributes.txt') do |line|
   line.chomp!()
   line.scan(/((?:\\\\|\\=|\\;|[^;=])+)=((?:\\\\|\\;|[^;])+)/) do |key, value|
     attributes[unescape(key)] = unescape(value)
   end
 end

 attributes.each_pair do |key, value|
   puts("#{key}=#{value}")
 end

Take home message: This is way more trouble than it's worth. Also, don't forget that split works differently with regards to empty fields from one language to another.

Thursday, March 12, 2009

Split split

Apparently, there's some disagreement about what it means to split a string into substrings. Biological data frequently comes in good old fashioned tab-delimited text files. That's OK 'cause they're easily parsed in the language and platform of your choice. Most languages with any pretention of string processing offer a split function. So, you read the files line-by-line and split each line on the tab character to get an array of fields.

The disagreement comes about when there are empty fields. Since we're talking text files, there's no saying, "NOT NULL", so it's my presumption that empty fields are possible. Consider the following JUnit test.

import org.apache.log4j.Logger;
import static org.junit.Assert.*;
import org.junit.Test;

public class TestSplit {
  private static final Logger log = Logger.getLogger("unit-test");

  @Test
  public void test1() {
    String[] fields = "foo\t\t\t\t\t\t\tbar".split("\t");
    log.info("fields.length = " + fields.length);
    assertEquals(fields.length, 8);
  }

  @Test
  public void test2() {
    // 7 tabs
    String[] fields = "\t\t\t\t\t\t\t".split("\t");
    log.info("fields.length = " + fields.length);
    assertEquals(fields.length, 8);
  }
}

The first test works. You end up with 8 fields, of which the middle 6 are empty. The second test fails. You get an empty array. I expected this to return an array of 8 empty strings. Java's mutant cousin, Javascript get's this right, as does Python.

Rhino 1.6 release 5 2006 11 18
js> a = "\t\t\t\t\t\t\t";
js> fields = a.split("\t")
,,,,,,,
js> fields.length
8
Python 2.5.1 (r251:54863, Jan 17 2008, 19:35:17)
>>> a = "\t\t\t\t\t\t\t"
>>> fields = a.split("\t")
['', '', '', '', '', '', '', '']
>>> len(fields)
8

Oddly enough, Ruby agrees with Java as does Perl.

>> str = "\t\t\t\t\t\t\t"
=> "\t\t\t\t\t\t\t"
>> fields = str.split("\t")
=> []
>> fields.length
=> 0

My perl is way rusty, so sue me. but, I think this is more or less it:

$str = "\t\t\t\t\t\t\t";
@fields = split(/\t/, $str);
print("fields = [@fields]\n");
$len = @fields;
print("length = $len\n");

Which yields:

fields = []
length = 0

How totally annoying!

Wednesday, February 25, 2009

Ruby Docs

RubyRuby cheat sheet. Quick links to Ruby documentation on the web.

ruby-lang.org

Ruby-doc

List operations

Test membership in an Array

Does the array contain the element?

my_array.include?('element')

Ruby on Rails

Ruby QuickRef

Pickaxe book

21 Ruby Tricks You Should Be Using In Your Own Code

Also see Ruby quirks and What is a Ruby code block?

Rubular - regex tester

Monday, February 09, 2009

Ruby Love

I've dissed Ruby here, here and here. But, I really like Ruby, so it's time to show Ruby some love. Here's some little snippets that I came up with that I think are slicker than shit.

You could hardly ask for a more concise yet readable way for taking the reverse compliment of a piece of DNA sequence.

# given a DNA sequence in a string return its reverse complement
def reverse_complement(seq)
  return seq.reverse().tr!('ATCGatcg','TAGCtagc')
end

Next, I wanted to parse out a pair of coordinates of the form "1600481-1600540". The first number is the start position and the second is the end. The interval is inclusive and one-based. I want the coordinates converted to zero-based integers.

s,e = coords.split("-").collect {|x| x.to_i - 1}

On second thought, why doesn't Ruby's Range class have a from_string method? Oops, another quirk.

Ruby Quirks

Ruby is a fun language, but here are a few things that tripped up this n00b whilst stumbling along the learning curve.

Why does string[n] return an integer character code instead of the character? Well, Ruby has no such thing as a character (prior to 2.0, anyway?), so then why not a string of length 1?

>> a = "asdf"
=> "asdf"
>> a[0]
=> 97

Either of these works, although they look a little funny:

>> a[0...1]
=> "a"
>> a[0..0]
=> "a"

Also, a[0].chr works. The integer.chr method is described as follows:

int.chr => string
Returns a string containing the ASCII character represented by the receiver‘s value.

It's non-obvious how to iterate through the characters of a string. The string.each_char is listed in the Ruby core 1.8.6 ruby-docs, but, confusingly, you have to require "jcode" for it to work. Maybe I'm just confused about whether core means "loaded by default" or "included in the Ruby distribution".

Two toString methods?In place of object.toString() Ruby has two methods: to_s and inspect. When coercion to a string is required, to_s is called. Docs for inspect say this:

Returns a string containing a human-readable representation of obj. If not overridden, uses the to_s method to generate the string.

If, then, else, elsif

If statements are confusing...

if x==123 then
   puts 'wazoo'
end

# then is optional, as long as you have the line break
if x==123
   puts 'wazoo'
end

For one liners, then is required. Or colon, if you prefer.

if x==123 then puts 'wazoo' end
if x==123 : puts 'wazoo' end

Curly braces seem not to work at all for if statements. For more curly brace related philosophy and WTFs see this issue. DON'T DO THIS:

if x==123 { puts 'qwer' }

Finally, would someone tell all these languages that crawl out of the primordial Bourne-shell ooze that neither elif nor elsif means jack shit in the english language?!?!?! (sputter... rant... fume...)

if x==123
  puts 'wazoo'
elsif x==456
  puts 'flapdoodle'
else
  puts 'foo'
end

An if statement is an expression and returns a value, but ruby also offers the good old ternary operator.

Require vs. Load

There are two ways to import code in Ruby, require and load. See the ruby docs for Kernel (require and load).

Defined? and nil?

Ruby has nil instead of null. Ok, and unlike Java's null, nil is a real object. I appreciate the difference between nil and undefined, but I wouldn't have guessed that defined? nil would return "nil". Not to be confused with a truly undefined variable, in which case defined? asdf returns nil. The pickaxe book explains the other strange return values of defined?. Then, there's nil?.

>> asdf.nil?
NameError: undefined local variable or method `asdf' for main:Object
 from (irb):47
 from :0
>> asdf = nil
=> nil
>> asdf.nil?
=> true

Command line arguments

Just the array ARGV. Not a quirk. Good.

Return

Return behaves oddly; to exit a script you use Kernel::exit(integer). Trying to return 1 instead causes a LocalJumpError whatever that means?? Trying to return from a code block returns from the surrounding context. That hurts my head.

Ruby Exception Handling

Ruby's equivalent of try-catch-finally is begin-rescue-ensure-end.

No Boolean

There's no Boolean class in Ruby. Instead, there's TrueClass and FalseClass. So, what type of value does the logical expression p ^ q produce? Everything has an implicit boolean value, which is true for everything except false and nil.

List operations

(see also Array and Enumerable)

More

Sunday, December 21, 2008

Principle of least surprise my ass

Let's start off by saying I'm not anti-Ruby. I like Ruby. Ruby is cool. Matz is cool. But, a while back I was wondering, What is a Ruby code block? My feeble curiosity has been revealed for the half-assed dilettantery it is by Paul Cantrell. Mr. Cantrell chases this question down, grabs it by the scruff of the neck, and wrings it out like a bulldog with a new toy. He also rocks on the piano, by the way.

So in fact, there are no less than seven -- count 'em, SEVEN -- different closure-like constructs in Ruby:
  1. block (implicitly passed, called with yield)
  2. block (&b => f(&b) => yield)
  3. block (&b => b.call)
  4. Proc.new
  5. proc
  6. lambda
  7. method

This is quite a dizzing array of syntactic options, with subtle semantics differences that are not at all obvious, and riddled with minor special cases. It's like a big bear trap from programmers who expect the language to just work. Why are things this way? Because Ruby is:

  1. designed by implementation, and
  2. defined by implementation.

Again, neither I nor Mr. P.C. are bashing Ruby. He shows how to pull off some tasty functional goodness like transparent lazy lists later in the article. Thanks to railspikes for the link.

Tuesday, May 20, 2008

What is a Ruby code block?

You can't believe everything you read on the internet. For example, you'll read that, in Ruby, use of the return keyword is optional. Like this:

def foo(a)
  return a*a
end

foo(9)
>>81

def bar(a)
  a*a
end

bar(8)
>>64

So far, so good. You'll also read that a code block in Ruby is an anonymous function. And what could be more natural than to return from a function? But try this:

[1,2,3].map {|a| a*a}
>> [1,4,9]

[4,5,6].map {|a| return a*a}
>> LocalJumpError: unexpected return
>> from (irb):14
>> from (irb):14:in `map'
>> from (irb):14
>> from :0

Well then, apparently one or both of our premises is wrong. Anyway, what is this LocalJump thing that is apparently in error?

My guess as to what's really going on is this: The code block is an attempt at creating a function-like artifact that matches the behavior of a code block in a non-functional language. That is, the code in the block doesn't execute in its own environment. Calling a code block doesn't cause a stack frame to be created. The reason the return causes problems is that there's no separate environment to return from.

It bothers me that I can't seem to google up an explanation of exactly what a Ruby code block (or Proc or lambda or method for that matter) is in terms of what does or doesn't happen to stack frames or environments. There's plenty of tutorials on how to use these constructs, but, little information on what they are, how they work, or why there are so many flavors of function-like things in Ruby.

Here's what I got from comp.lang.ruby.

Monday, November 26, 2007

Formatting Ruby source code in HTML

BTW, I used the syntax library to format the Ruby source code snippets to HTML. It works like this:

require 'rubygems'
require 'syntax/convertors/html'

code = File.read("my_nifty_program.rb")
convertor = Syntax::Convertors::HTML.for_syntax "ruby"
code_html = convertor.convert( code )
print code_html

You'll also need the corresponding CSS:

pre {
    background-color: #f1f1f3;
    border: 1px dashed #333333;
    padding: 10px;
    overflow: auto;
    margin: 4px 0px;
    width: 95%;
}

/* Syntax highlighting */
pre .normal {}
pre .comment { color: #999999; font-style: italic; }
pre .keyword { color: #006699; font-weight: bold; }
pre .method { color: #077; }
pre .class { color: #074; }
pre .module { color: #050; }
pre .punct { color: #000099; font-weight: bold; }
pre .symbol { color: #099; }
pre .string { color: #00CC00; }
pre .char { color: #00CC00; }
pre .ident { color: #333333; }
pre .constant { color: #0099CC; }
pre .regex { color: #00CC00; }
pre .number { color: #0099CC; }
pre .attribute { color: #5bb; }
pre .global { color: #7FB; }
pre .expr { color: #227; }
pre .escape { color: #277; }

I found this nifty information in an article called, "Howto format ruby code for blogs".

Powersets in Ruby

Another nifty exercise in Ruby - this one generates all subsets of a given array. Maybe I should have used a set.

# Powerset
# find all subsets of a set

# recursively compute the all subsets of the given set
# based on the idea that for any given subset, either an
# item is in the subset or it isn't
def powerset(set)
  if (set.length == 0)
    return [set]
  end
  result = []
  
  # remove an item from the list
  item = set.shift
  
  # compute the powerset of the smaller list
  sps = powerset(set)
  
  # for each subset, either readd the item or don't
  sps.each do |subset|
    result << subset
    result << (subset + [item])
  end
  return result
end

set = %w{ a b c d }
p = powerset(set)

# sort by length of subset
p.sort! do |a,b|
  a.length - b.length
end

p.each do |subset|
  puts "[#{subset.join(", ")}]"
end

Sunday, November 25, 2007

Permutations in Ruby

During the long weekend I decided to break it down geek style with some Ruby, because that's the way I roll. Or anyway, I'm starting to learn a little Ruby and here's a first little exercise. I'm not sure if it's officially sanctioned Ruby idiom, but it's a start.

# find and print all permutations of a list

# find permutations recursively based on the idea that
# for each item in the list, there is a set of permutations
# that start with that item followed by some permutation of
# the remaining items.
def permute(list)
 if (list.length <= 1)
   return [list]
 end
 permutations = []
 list.each do |item|
   sublist_permutations = permute(list - [item])
   sublist_permutations.each do |permutation|
     permutation.unshift(item)
     permutations << permutation
   end
 end
 return permutations
end

# test our permute function
list = %w{ a b c d }
p = permute(list)
p.each do |permutation|
 puts "#{permutation.join(", ")}"
end