Category: life

RDoc on your iPad

Posted by – April 12, 2010

Oh snap! I haven’t posted here in a long time. My day job and my night jobs have been keeping me too busy! Hopefully I’ll have more time to blog in the future. I have a bunch of ideas, I just need to find the time to write!

Anyway, let’s talk RDoc, iPad, and epub! I like documentation. I especially like consuming documentation. I thought it would be neat if I could read documentation on my iPad. As it turns out, getting RDoc documentation on your iPad isn’t that hard!

Nokogiri on iPad

According to Wikipedia, iBooks is an EPUB reader. EPUB is a standard format for making books. The EPUB format is basically a zip file that contains a bunch of XHTML and XML documents. The XHTML documents are the “meat” of your book, where the XML documents tell the reader where to find everything, and the order in which to put things. RDoc already emits HTML, so our job is to make sure it emits XHTML along with the special XML files. How do we do that?

RDoc supports a plugin system where we can hook in and emit anything we want. To hook in to RDoc, we just add a special file to our gem (“lib/rdoc/discover.rb”), and register with the RDoc plugin system. So I wrote a gem called paddle that plugs in to RDoc, emits the documentation as XHTML along with the supporting XML files. It even comes with a nice Ruby logo! I encourage you to take a look at the source. The code is quite short, but could be refactored even smaller!

Using the Paddle

Creating your own books with Paddle is really easy. First, install paddle:

  $ sudo gem install paddle

Then find a project for which you want to create a book. For this example, I’ll generate a book for one of my gems called texticle. From the project root, use the rdoc command. Make sure to tell rdoc to use the “paddle” formatter, and supply a title (very important to supply a title!):

  $ cd git/texticle
  $ rdoc -f paddle -t 'Texticle Documentation' -o epub lib

Now there should be an “epub” directory that contains your book. But we’re not quite done yet. There is one more step. The book must be in a zipfile, and the zipfile requires a particular format. Let’s create the zipfile now using the “zip” command:

  $ cd epub
  $ zip -Xr9D texticle.epub mimetype *

You should end up with a file named “texticle.epub”. Just drag that file to iTunes, sync up your iPad, and boom!

Problems

I hacked this out in an evening, so there are a few problems. I’ll mention them here, just so you’re not surprised, and to give you ideas for patches to submit! ;-)

  • Right now, the links don’t work:
  • I haven’t figured out why, but they don’t. That will come soon.

  • The author field isn’t filled out in the book:
  • I need to teach RDoc to take more command line options so we can tell Paddle what to use for the book’s author field

  • Only classes, modules, and the things they contain are documented:
  • Right now, your README file won’t show up in the book. That is just missing right now. It should be easy to add, I just haven’t done it.

A couple books to get you started

THE END!

Thanks for reading! Have fun making books for your iPad, and don’t forget to send patches back to me! :-D

Writing Ruby C extensions: Part 1

Posted by – December 18, 2009

Writing Ruby C extensions: Part 1

I like writing C extensions for Ruby. In this series of blog posts we’re
going to explore writing C extensions. I will cover topics including setting
up the development environment, TDD, debugging techniques, dealing with
Ruby’s garbage collector, cross compiling for windows, and more.

By the end of this series, we should end up with a Ruby C extension that wraps
libstree. libstree is a Suffix tree implementation written in C.

In this part, we’re going to set up our development environment, examine the
layout of a typical C extension, and implement our first method in C. Of
course we will be doing this TDD, so we’ll also get autotest running.

Prerequisite gems

First up, we need to install a few gems to make building our extension easier.
Install the following three gems, and while they install, you should read about
why we need them:

$ sudo gem install ZenTest hoe rake-compiler

ZenTest

ZenTest contains autotest, which we’ll be using to automatically run the tests
while we’re developing

hoe

Hoe abstracts gem specifications for us. It knows how to properly build a
gemspec, and provides us with a few rake tasks that make development simple.

rake-compiler

This gem provides us with compilation tasks, and generally makes building
native gems easier. We’ll be looking further in to rake-compiler’s
capabilities in later articles.

Create the project

We’re going to call this gem “stree”. The first thing we’ll do is use the “sow”
command supplied by Hoe to create the initial project structure.

$ sow stree

You should now have an initial project tree set up under the “stree” directory.
Remove the “bin” directory, as we won’t need that. I rename all of my
documentation files to end in “rdoc”, but that is just my personal preference.

Writing our first test

First thing we need to do is write our first failing test. Open up
“test/test_stree.rb” and make it look like this:

require "test/unit"
require "stree"

class TestStree < Test::Unit::TestCase
  def test_hello_world
    assert_equal 'hello world', Stree.hello_world
  end
end

This test is very simple. The trick though, is that the “hello_world” method
will be implemented in C. At this point, you should be able to run “rake” and
see a failing test.

Native extension project layout

Native extension layouts look very similar to normal pure ruby layouts. We just
add one more directory called “ext”. Under the “ext” directory we’ll add
another directory that is the same name as our gem, “stree”. Under
“ext/stree” is where we’ll keep all of our C code. Make those directories,
and you should have a file list that looks similar to this:

$ tree
.
|-- CHANGELOG.rdoc
|-- Manifest.txt
|-- README.rdoc
|-- Rakefile
|-- ext
|   `-- stree
|-- lib
|   `-- stree.rb
`-- test
    `-- test_stree.rb

The next step is to modify our Rakefile.

Modifying the Rakefile

The next step is to modify the Rakefile to teach it how to compile our
extension. Once we get done with this step, our Rakefile will have a task
called “compile”.

Modify your Rakefile so that it looks similar to this:

require 'rubygems'
require 'hoe'

Hoe.spec 'stree' do
  developer('Aaron Patterson', 'aaron@tenderlovemaking.com')
  self.readme_file   = 'README.rdoc'
  self.history_file  = 'CHANGELOG.rdoc'
  self.extra_rdoc_files  = FileList['*.rdoc']
  self.extra_dev_deps << ['rake-compiler', '>= 0']
  self.spec_extras = { :extensions => ["ext/stree/extconf.rb"] }

  Rake::ExtensionTask.new('stree', spec) do |ext|
    ext.lib_dir = File.join('lib', 'stree')
  end
end

Rake::Task[:test].prerequisites << :compile

I've modified the readme and history file sections to use custom named files.
The important parts are the "spec_extras", the "Rake::ExtensionTask" line and
the "Rake::Task" line.

The "spec_extras" line modifies the gemspec. When someone installs our gem,
this line tells the gem command to execute the "ext/stree/extconf.rb" file.
We'll talk a little bit more about the extconf.rb file later.

The "Rake::ExtensionTask" is the line where we get our "compile" task. It comes
from the rake-compiler gem. This block also configures rake-compiler to tell
it where to copy the compiled extension when it's finished. We want our
compiled extension to end up in "lib/stree/". This is my convention, and I'll
explain why this convention is good in later posts.

The final line tells Rake to always compile our extension before the tests run.
Some people might not want to use this, but I like compiling my extension
before every test run.

Configuring autotest

Autotest doesn't use the normal rake tasks when running your tests. That means
we need to teach autotest to compile our extension before running the tests.
We're going to hook in to the autotest run command and have it build our
extension before running the tests.

While we're at it, we'll also teach autotest to run the tests after any .c
files get modified.

Open up ".autotest" and make it look like this:

require 'autotest/restart'

Autotest.add_hook :initialize do |at|
  at.add_mapping(/.*\.c/) do |f, _|
    at.files_matching(/test_.*rb$/)
  end
end

Autotest.add_hook :run_command do |at|
  system "rake clean compile"
end

Start up autotest and let it run in the background. By the end of this blog
post, autotest should show one passing test.

At this point, we should see Rake complaining with a message:

rake aborted!
Don't know how to build task 'ext/stree/extconf.rb'

Let's deal with that error now.

extconf.rb

The responsibility of the extconf.rb file is generate a Makefile that will
be used to build your extension. Eventually, we will need to teach extconf.rb
how to examine the target system to make sure that the libstree library is
installed.

Right now, we don't need to do any inspection of the system. We simply want
to create a Makefile. To build our Makefile, we're going to use a library
that ships with ruby called "mkmf". Open up "ext/stree/extconf.rb" and
modify it to look like this:

require 'mkmf'
create_makefile('stree/stree')

That is the minimum code required to get our Makefile generated.

As I mentioned earlier, the extconf.rb file is executed by the RubyGems system
when installing our gem. While we're developing our gem, rake-compiler will
take care of executing that file for us.

Our first C code

Great! Our environment knows how to compile things, but we don't have anything
to compile! Let's write our first bit of C code.

We are going to write the file named "ext/stree/stree.c". The name of this
file is important. It corresponds to the "create_makefile" line from our
extconf.rb. After our extension is built, we'll end up with a file
"lib/stree/stree.dylib" (or .so depending on your system). This convention is
important, but I'm going to talk about why it's important in a later post.

When Ruby loads the dynamic library we're building, it must supply us with
some way to define our native methods. The way it does this is with another
naming convention using the dynamic library's file name. When "stree.dylib"
is loaded, ruby will automatically try to call a function called "Init_stree".
The second part matches the name of the file it loaded. In the Init_stree
function is where we'll do our native extension initialization.

In stree.c, define the Init_stree function to look like this:

#include <ruby.h>
void Init_stree()
{
  VALUE mStree = rb_define_module("Stree");
  rb_define_singleton_method(mStree, "hello_world", hello_world, 0);
}

This function does two things, defines the "Stree" module, and the "hello_world"
method on the Stree module.

The first line actually defines the module, the second line tells ruby to
define the singleton method "hello_world", and when that method gets called,
to call the "hello_world" C function pointer. The 0 indicates the number of
arguments.

Let's actually add the hello_world C function now:

static VALUE hello_world(VALUE mod)
{
  return rb_str_new2("hello world");
}

We declare this function as static because it's not needed outside this file.
All ruby methods must return a VALUE. The first argument to the C function is
always the recipient of the message, in this case it will be the Stree module.

We create a new ruby string with rb_str_new2() and return it.

The final stree.c file should look like this:

#include <ruby.h>

static VALUE hello_world(VALUE klass)
{
  return rb_str_new2("hello world");
}

void Init_stree()
{
  VALUE mStree = rb_define_module("Stree");
  rb_define_singleton_method(mStree, "hello_world", hello_world, 0);
}

A note on types.

When we write ruby, everything is an object. When we write ruby in C,
everything is a VALUE. We'll learn more about the VALUE type in later posts.

Finishing up

We've written our C code, everything should now compile and copy in to the
right place, but our tests are still failing. What gives?

We've got one more tiny modification to make. We need to actually require
the dynamic library that we built. Open up "lib/stree.rb" and modify it to
look like this:

require 'stree/stree'

module Stree
  VERSION = '1.0.0'
end

We've now told ruby to load the dynamic library we built, and changed the
definition of Stree to a module in our ruby code. At this point, our test
should be passing. Congratulations! You have now successfully mixed C and
Ruby code.

If you were successful, your project tree should look like this:

$ tree -I tmp
.
|-- CHANGELOG.rdoc
|-- Manifest.txt
|-- README.rdoc
|-- Rakefile
|-- ext
|   `-- stree
|       |-- extconf.rb
|       `-- stree.c
|-- lib
|   |-- stree
|   |   `-- stree.bundle
|   `-- stree.rb
`-- test
    `-- test_stree.rb

The "tmp" directory is where rake-compiler stashes your .o files when
compiling your extension. I've omitted that from the tree to keep it short.

Last notes

I've posted the code for stree here in case you're having troubles. I've
made tags for each post so you can follow along.

Next time, we'll tackle making sure libstree is installed, compiling and
linking against libstree, and making a few calls in to libstree from Ruby.

In the mean time, your homework is to read through README.EXT.

Rubyconf 2009 Slides

Posted by – December 7, 2009

I’ve posted the slides for the talk that Ryan and I gave at Rubyconf 2009.

You can grab the pdf here.

I’ve also put them on slideshare here.

I replaced the videos in the slides with links to youtube. If you want to jump straight to the videos though, you can find them here, here, and here.

We need a new version of rake

Posted by – February 26, 2009

We’ve all seen this warning:
/Library/Ruby/Gems/1.8/gems/rake-0.8.3/lib/rake/gempackagetask.rb:13:Warning: Gem::manage_gems is deprecated and will be removed on or after March 2009.[/sourcecode]
Rake is using a deprecated API from RubyGems. Jim knows about the problem, we just need to get him to release a new version of Rake.

In order to get Jim to release a new version of Rake, I have decided to start a letter writing campaign:

IMG_0150.JPG

If you would like to help get a new version of Rake released, I encourage you to send a letter to Jim, thanking him for his hard work on Rake, and asking him kindly to release the new "warning free" version.

Send your letters here:

Rake
c/o Jim Weirich
EdgeCase
1130 Congress Ave
Cincinnati, OH 45246

Together we can get Jim to release a new version of Rake. Together we can build a "warning free" future for our children.

Custom CSS functions in Nokogiri

Posted by – February 7, 2009

In CSS we can use different pseudo selectors like a:focus or a:hover to get access to links that have focus or are being hovered over. The CSS spec also defines a pseudo selector called :lang() which allows us to select an element based on it’s language.

The :lang() selector is interesting to us because the CSS parser in Nokogiri must support syntax like a:lang(‘en’), but it doesn’t care what token comes before the parenthesis. If it finds a function it doesn’t know about, it simply pushes it in to the libxml2 xpath engine, hoping that libxml knows what that function is.

We can take advantage of this behavior and define our own set of CSS pseudo selectors by defining functions for the xpath engine to use. Let’s look at some sample code and pick it apart.

Let’s say we have a document where we want to find all “a” tags based on a regular expression. We want to find all link tags with an href that matches a particular expression. Normally we would search the document for all “a” tags, then filter our list down. What if we could have the xpath engine filter our links for us? With Nokogiri, that is very easy:

doc = Nokogiri::HTML(<<-eohtml)
  <html>
    <body>
      <a href="http://example.com/">One</a>
      <a href="http://tenderlovemaking.com/">TLM</a>

      <div>
        <a href="http://example.org/">Two</a>
      </div>
    </body>
  </html
eohtml

doc.css('a:match_href("ampl")', Class.new {
  def match_href list, expression
    list.find_all { |node| node['href'] =~ /#{expression}/ }
  end
}.new).each do |node|
  puts node['href']
end

This works with basically a “method_missing” call in the xpath engine. When the xpath engine finds a function it doesn’t know, it asks Nokogiri, “do you know where this function is?”. Nokogiri looks for a method on your object with the same name. If it can find one, it calls your method with the current matching items and feeds the return value of your function back in to the xpath engine.

The first argument passed to your method is always a list of the current matching nodes. In this case, a list of “a” tags. The rest of the arguments are the ones that you passed in to the function in your CSS expression. In this case “ampl”.

This functionality isn’t limited to just CSS expressions. You can use it in your XPath expressions too. Here is the same expression using XPath:

doc.xpath('//a[match_href(., "ampl")]', Class.new {
  def match_href list, expression
    list.find_all { |node| node['href'] =~ /#{expression}/ }
  end
}.new).each do |node|
  puts node['href']
end

I find this functionality useful because it lets me define reusable search criteria. Many times when searching HTML, I find myself doing similar searches. This lets me refactor those searches in to a different class and keep my code DRY and my CSS expressions concise.

I hope you find this as useful as I do!

RubyConf Slides

Posted by – November 11, 2008

Hey everyone! I’ve finally settled myself after returning from RubyConf.

Here are the slides from the Seattle.RB presentation at RubyConf.

Enjoy!

Nokogiri Is Released

Posted by – October 30, 2008

Hey internet. How are you doing? Ya. It’s been a while. I know, I know. I suck at blogging. Couldn’t you tell by my horrible layout? But seriously, I’ve been really busy lately. We used to have such good times together. I’d write a blog post, you would show it to everyone on the internet. But that spark just doesn’t seem to be there anymore.

Well, I’m doing my best to keep this relationship together. With the help of super awesome ruby hacker Mike Dalessio, I wrote an XML/HTML parsing library for ruby called Nokogiri. What is so great about Nokogiri? Well, for one it is really easy to parse HTML or XML:

require 'nokogiri'

doc = Nokogiri::HTML(<<-eohtml)
<html>
  <body>
    <div id="wrapper">
      <h1>Hello world</h1>
      <p>Paragraph</p>
    </div>
  </body>
</html>
eohtml

Oh, I know what you’re saying internet. Ya, sure, it’s easy to parse, but is it easy to search? Well, it is. I promise. You know XPath, right? Well you can search by XPath very easily:

doc.xpath('//p').each do |paragraph|
  puts paragraph.text
end

Oh, you don’t know XPath very well? That’s OK. I know you know CSS. You use it everywhere! I’ve viewed your source (*wink* *wink*). Well you can search using CSS selectors as well:

doc.css('div#wrapper').each do |div_with_wrapper_id|
  puts div_with_wrapper_id['id']
end

Oh, I see how it is. You don’t want to commit. You want to search with CSS selectors *and* XPath. Well fine. You can have that too. Just use the “search” method, and you can mix and match your selectors:

css.search('//p', 'div#wrapper') do |node|
  puts node.name
end

Well, I hope you’re feeling better about our relationship now. I just want to tell you that you shouldn’t worry about that old legacy code that uses Hpricot. Nokogiri can be used as a drop in replacement! Really! Nokogiri doesn’t reproduce the bugs that are in Hpricot, but should work in most cases. Just use “Nokogir::Hpricot()” to parse your HTML. Of course, I’ve tried to keep the syntax of Hpricot that I like. For example, you can use slashes for searching, subsearching:

(doc/'div').each { |div| puts div.at('p').text }

You even get a speed increase. For free!

Want to install Nokogiri? No problem. Just do “gem install nokogiri”. It’s that easy!

Well, now that we’re back together, why don’t you send some twitters if you like it! Thanks innernet. I promise to update you more often. I swear.

Mushrooms, Beef Jerky, and Programming

Posted by – September 8, 2008

This weekend I went mushroom hunting and found about 6 Chanterelle mushrooms, and they were delicious! I made cream of mushroom soup with Chanterelles for the main course, and a Tres Leches cake for desert. I still have about three pound left. I think I’ll go hunting them again next weekend. I can’t wait! I’m thinking about giving a few to zenspider a few since I know how much he loves them.

IMG_0206.JPGIMG_0210.JPG

Recently, I’ve been refactoring Mechanize and I’ve added support for a few new things. First, mechanize now (and when I say “now”, I mean whats checked in) supports “file://” urls. For example:

agent = WWW::Mechanize.new
page = agent.get("file:///Users/aaron/some_file.html")

Directories work too. Mechanize will turn directories in to a list of links for your navigation convenience. I’ve also added criteria based searching to links and frames. For example:

agent = WWW::Mechanize.new
agent.get('http://google.com/') do |page|
  page = page.form_with(:name => 'f') do |form|
    form.q = 'Aaron Patterson'
  end.submit

  page.links_with(:text => /tender/i).each do |link|
    puts link.text
  end
end

There is a singular and plural form. So you can locate many or one form, link, iframes, etc.

These changes will go in to an 0.8.0 release. However, I’m planning larger changes for a 1.0.0 release. I want to get rid of the WWW namespace, and stick with just Mechanize. I think that would probably be the largest change between 0.8.0 and 1.0.0 that I can think of. If you’d like to try out the new changes, just grab the gem from github: “gem install tenderlove-mechanize -s http://gems.github.com”.

Finally, I will be teaching the Ruby Certificate course at the UW. There are 3 courses, beginning Ruby, Ruby with Rails, and Advanced Topics in Ruby. Ryan will be teaching the beginning course, I’ll be teaching the Rails course, and I will be co-instructing the advanced course with Ryan. I am considering using The Rails Way or Agile Web Development with Rails (3rd ed). I’m leaning towards the Agile book, but we’ll see! Anyway, you should sign up!

Identifying unknown music with Ruby

Posted by – August 4, 2008

Ben Bleything inspired me (or rather distracted me from my yak shaving) to get my music library cleaned up and remove duplicates. Unfortunately my duplicates don’t necessarily have ID3 tags, and they may be in different formats, so I wrote a gem called “earworm” which will identify unknown music. First I’ll give you a code sample, then explain how to get earworm working, and finally explain how earworm works.

Here is the code sample:

ew = Earworm::Client.new('MY Music DNS Key')
info = ew.identify(:file => '/home/aaron/unknown.wav')
puts "#{info.artist_name} - #{info.title}"

Earworm just needs a MusicDNS key and a file name to return information about your unknown audio.

Okay, now for getting the gem to work. Unfortunately this takes a little work right now. You need to install a few libraries before earworm will work. These libraries let you decode and identify mp3, ogg, and wav files.

For OS X (assuming you have MacPorts already installed) do this:

$ sudo port install libogg libvorbis lame libofa
[/sourcecode]

For Linux:

$ sudo yum install libogg-devel libvorbis-devel lame-devel libofa
[/sourcecode]

Then just install the gem like so:

$ sudo gem install earworm
[/sourcecode]

The final thing you need is a key from MusicDNS which you can obtain here. The key is free for non-commercial applications. Now you should be set! Earworm will automatically decode mp3 files and ogg files for you, so you don't have to do that yourself.

How does earworm work? First, it relies on icanhasaudio for audio decoding. Second, it uses DL to wrap libofa. libofa takes between 10 and 135 seconds of audio and generates a hash. After getting the hash for the audio snippet, earworm posts the hash (along with your key and some other info) to MusicDNS's web service. The web services returns an XML document with information about the audio snippet. Earworm parses the XML document and returns a nice object containing the information you requested. Thats it! No magic, and very simple (IMO) source code.

Enjoy!

Back Home!

Posted by – July 8, 2008

I’m finally back home. I went to Japan a few weeks ago for vacation, and I also spoke at Ruby Kaigi 2008. Ruby Kaigi was so much fun! I’ve been studying Japanese for a little over a year, but I’ve never been to Japan. It was exciting and fun to talk to people, and I made a bunch of new Japanese friends. I’d really like to thank Leonard Chin for helping out at the Kaigi. My language skills aren’t good enough, and he was kind enough to fill in the gaps. Thank you!

While I was in Japan, I noticed QR Codes everywhere. QR Codes are basically really awesome bar codes. They can hold much more information in a smaller amount of space. They can be easily decoded from images taken with digital cameras. They have these codes everywhere in Japan, and the idea is that people can take a photo with the camera on their cell phone, then the phone decodes the QR Code. I believe most of the QR codes contain information about the company, or possibly a URL to the company’s website.

The company that created the format says that the format is open, but unfortunately I have to pay for the spec. I can download the spec in Japanese for free, but my Japanese isn’t that good! So unfortunately I’m stuck with either the ISO spec (which is over $200) or the AIM spec ($85). I don’t understand why they are so expensive….. I think I’ll buy the AIM one, and hope that it is the same as the ISO one.