zerosum dirt(nap)

evolution through a series of accidents

zerosum dirt(nap)

Software Development 2.0 China

November 28, 2007 @ 07:29 PM by nap · 0 comments

Today I'm in Beijing, attending the CSDN-Dr. Dobb's Software Development 2.0 conference. It's my first time in China and it's quite an honor to be here. I'll be giving a Ruby on Rails overview and code analysis talk as part of tomorrow afternoon's session. Looking forward to talks by Andrei Alexandrescu, Tenni Theurer, Dan Theurer, Jonathan Palley and of course DDJ's own Jon Erickson.

0 comments

Links For 11.24.07

November 24, 2007 @ 02:35 PM by nap · 0 comments

0 comments

Ruby Method Visibility

November 21, 2007 @ 08:41 PM by nap · 3 comments

In Java (and most OO languages), declaring a method as private means that the method can only be accessed within the context of the defining class. Similarly, protected means that the method can only be accessed from that class or any of its subclasses.

In Ruby, things are a bit different. Both private and protected methods can be called by any instance of the defining class and its subclasses. Inheritance plays absolutely no role in determining the visibility of a method. The difference instead is that private methods can never be called with an explicit receiver, even if the receiver is self. This means that it's not possible to access another object's private methods, even if the object is of the same type as the caller. A private method must be called from within the calling object.

Consider the following example:

class Person
  def public_method
    "public"
  end

  protected
  def protected_method
    "protected"
  end

  private
  def private_method
    "private"
  end
end

class SalesPerson < Person
  def check_protected_method_explicit_receiver
    "#{self.protected_method} method OK with explicit receiver"
  rescue
    "failure accessing protected method with explicit receiver"
  end

  def check_protected_method_implicit_receiver
    "#{protected_method} method OK with implicit receiver"
  rescue
    "failure accessing protected method with implicit receiver"
  end

  def check_private_method_explicit_receiver
    "#{self.private_method} method OK with explicit receiver"
  rescue
    "failure accessing private method with explicit receiver"
  end

  def check_private_method_implicit_receiver
    "#{private_method} method OK with implicit receiver"
  rescue
    "failure accessing private method with implicit receiver"
  end
end

The public method can of course be accessed from anywhere (in this case, outside the class with an explicit receiver) and both private and protected methods of Person will obviously raise a NoMethodError.

Person.new.public_method
=> "public"
Person.new.private_method
=> NoMethodError: private method `private_method' called for #<Person:0x6ec698>
Person.new.protected_method
=> NoMethodError: protected method `protected_method' called for #<Person:0x6ea5c8>

So the protected method cannot be accessed outside of the class or it's subclass, but from within the subclass, using it with either an implicit or explicit receiver works fine:

SalesPerson.new.check_protected_method_explicit_receiver
=> "protected method OK with explicit receiver"
SalesPerson.new.check_protected_method_implicit_receiver
=> "protected method OK with implicit receiver"

The private method can also be called from the subclass, but note how it only works with an implicit receiver:

SalesPerson.new.check_private_method_explicit_receiver
=> "failure accessing private method with explicit receiver"
SalesPerson.new.check_private_method_implicit_receiver
=> "private method OK with implicit receiver"

This also means you can do stuff like:

class Person
  def ==(other)
    protected_method == other.protected_method
  end
end

x = SalesPerson.new
y = Person.new
x == y
=> true

We're accessing the protected_method of another class instance that shares our type here, specifying an explicit receiver. If you were to try to use private_method instead, a NoMethodError would be raised. You could also just call other.send("private_method") if you really wanted to, violating our encapsulation and angering the gods of object orientation. This is only a "little sin" though, and can be permissible if the situation calls for it (like, cough cough, my latest commit to DataMapper).

In summary, method visibility and access control can be a bit confusing at first, especially if you're coming over to Ruby from some other OO language. If you're still confused, there's more information available here and here. Do yourself a favor and make sure you understand, cuz it's important stuff!

3 comments

Steinbeck on Ruby?

November 20, 2007 @ 11:44 PM by nap · 0 comments

"Syntax, my lad. It has been restored to the highest place in the republic."

-- John Steinbeck

0 comments

Book Review: Practical Ruby Gems

November 17, 2007 @ 03:33 PM by nap · 0 comments

I’ve been meaning to start posting book reviews for some time now. I don’t really have a good excuse as to why I haven’t, so I’ll spare you the overly verbose excuses and get right to the good stuff.

The first book in my stack is David Berube’s Practical Ruby Gems. Before diving into it I should note that Dave is a friend of mine and that I’m currently working on a book with him for Apress. My opinions here are, of course, my own.

Practical Ruby Gems is a survey book, covering a number of 3rd party Ruby libraries as well as general usage of the Gems system itself. Although it’s a bit short on details in some places, the breadth over depth approach is really quite useful in that the book will doubtlessly introduce you to some libraries that you weren’t aware of before. This is particularly true if the majority of your Ruby experience has been within the context of Rails. A number of the chapters do make use of Rails (hey, it is a Gem after all), a few use the Camping web framework, but the majority of the examples are constructed as simple command line utilities.

The more popular Gems, such as hrpicot and rmagick are of course covered here, but the real (ahem) gems are the lesser known libraries such as multi, memoize, runt and cmdparse. Dave’s examples cover a wide variety of topics and use each Gem in an interesting way, going beyond the rdocs to show you how to combine and use these libraries in real applications. Using Fxruby and the YahooFinance Gem to create a graphical stock ticker is a good example of this. Another example is an RSS news archiving service built using ActiveRecord and FeedTools. Both practical and well-dissected in the text, this sort of thing is sure to help new Ruby developers grasp the concepts for re-use in their own developments.

The chapters on creating and distributing gems (with Rubyforge, gem_server, etc) are short and to the point. The one omission, from my point of view, is that Dr Nic’s New Gem Generator isn’t mentioned as a resource for bootstrapping gem structure. This is an invaluable tool for creating new gems.

In any case, if you’re looking to broaden your Ruby tool belt, Practical Ruby Gems is sure to give you exposure to a few new tools. It would be a particularly good ‘bridge book’ for Rails developers who are looking to explore the world of Ruby possibilities outside of the web framework.

0 comments

Digital Comics, Unlimited

November 13, 2007 @ 06:17 PM by nap · 1 comment

I’m not ashamed to admit that I’m a bit of a comic book geek.

When I was a kid, I was a hardcore fanboy who spent pretty nearly all his paper route money on weekly trips to the local comic shop in Dover. I have no apology for that. As a technology geek also, I’ve built a social comic book pull list organizer that has yet to officially launch (we’re looking for someone who’s excited about comics and community building, no techie skills required, email me if you’re interested!) and I’ve done a little ancillary plugin work for the geeky folks over at Heavy Ink too.

These days I buy mostly graphic novels, but I’ve always loved the feel of a floppy, and back when I used to buy titles monthly, there was just something unspeakably awesome about ‘new comic book day’ and the excitement of pouring through the stacks in a local shop looking for that one key back issue…

Like most fans, I have mixed feelings about the digitization of comics. On one hand, I’d probably read a lot more of them if they were available (ahem, legally of course) through a cheap, easy to use digital distribution service like iTunes. On the other hand, I’m sort of glad this hasn’t happened, because it would kill a lot of the allure of it for me if the comics on paper were to become a thing of the past.

I imagine I’m not the only one who was both excited and, at the same time, just a little tiny bit bummed about the announcement of Marvel Comics Unlimited yesterday (NOTE: as of right now the site appears to be down for maintenance, coming soon). The basic premise is that Marvel will offer an online archive of over 2500 back issues online in high-resolution format, starting at about $5/mo. They’re also making a free sampler of 250 titles available to wet your appetites.

Marvel’s hedging their bets a little though, which is smart. New issues won’t appear on the Marvel Unlimited site until six months after their initial print publication. This is great for those of us who read graphic novels, and aren’t used to picking up books every week anyway, and it keeps the floppies in circulation. After a bit of consternation, I’ve decided that it’s pretty much a win/win. Of course, as it always is on the web, the user experience will determine the ultimate success or failure of the venture. The flash-based digital comics I’ve seen from Marvel up to this point haven’t exactly been the most pleasant things to read, so here’s to hoping this is an entirely new interface to the library.

Anyway, I’m looking forward to reading more about this, both as a comic book enthusiast and as a technologist. Welcome to the 21st century, comic fans. For better or for worse.

It’s about time, I guess!

1 comment

Turn On Your Buzzword Filter

November 13, 2007 @ 12:46 AM by nap · 0 comments

BigLove [12:37am]: my eyes crossed from all the buzzwords zapnap
zapnap [12:38am]: turn on your buzzword filter. OMFG WHERE DID T EH INTERNETS GO?

0 comments

OS X Leopard Upgrade Notes

November 11, 2007 @ 11:02 AM by nap · 0 comments

Starting this blog entry to document any possible gotchas I experience on my upgrade to OS X 10.5. So far everything has been relatively clean. My only problem as of this writing has been with SSH and MacPorts. I have SSH installed via MacPorts and I noticed when doing any SVN+SSH operations, I was getting the following error:

percent_expand: NULL replacement
svn: Connection closed unexpectedly

Hmm, looks like Leopard is expanding some SSH environmental variable to NULL. An easy fix is to add the following line to your ~/.ssh/config:

IdentityFile ~/.ssh/id_rsa

This overrides the default search for your private key, and therefore you don’t get the percent_expand error. If anyone has more information about this particular issue, please let me know. Thanks!

0 comments

Links For 11.10.07

November 10, 2007 @ 08:18 AM by nap · 1 comment

1 comment

Flexible Payments With Rails & REST

November 09, 2007 @ 12:20 PM by nap · 3 comments

Anyone who’s been playing around with the original Rails version of the Amazon Flexible Payments Service sample application has probably spent a fair amount of time swearing and complaining about how incredibly un-idiomatic and ugly it is. I know I have. It’s not that we don’t appreciate Amazon wanting to play ball with us, it’s just that it’s… well… it’s damn ugly.

But wait, a few weeks ago, a new REST-based Rails sample app was posted. This time around it’s a far more basic “hello world”-ish app, but it’s much more idiomatic and clean, a far better starting place for anyone who wants to learn how to make pay calls with FPS using Ruby and Rails. It’s also thankfully devoid of soap4r dependencies.

Just thought I’d note that, in case you’re working with FPS and haven’t stumbled upon it yet.

In related news, there’s a RubyForge project for a library called Remit, which purports to be a proper Ruby API wrapper for FPS. Tyler Hunt is the developer who’s registered the project and is working on it. With no updates since early September though, and no files released, I wonder if the project hasn’t maybe been aborted? Tyler, if you’re out there, give a shout and let us know if you need a hand.

3 comments

NH Ruby Next Monday Night

November 09, 2007 @ 12:39 AM by nap · 0 comments

As Scott notes over on his blog, this month’s NH Ruby meeting will be off by a week and a day this time around, due to holiday stuff. Instead of the third Tuesday of the month, it’ll be the second Monday. Yessir, that’s this coming Monday.

My good friend Dave Berube will be the main speaker, and he’ll be discussing reporting techniques with Ruby and Rails. Dave is putting the finishing touches on a new book on the subject, so he knows of that which he speaks. It’s sure to be a good conversation and if you’re in the area, you should definitely check us out.

0 comments

Merbivore!

November 07, 2007 @ 12:16 AM by nap · 0 comments

Merb 0.4 was released earlier today, and the men of Montreal were thus elated. For those that are unfamiliar with it, Merb is a Ruby MVC web framework not unlike Rails, but focused more on speed and minimalism. It’s a lot like a thread-safe closer-to-the-metal reimplementation of ActionPack. This also means it’s ORM agnostic (big, big win) and JavaScript library agnostic. Plugins are just gems (dependency management ftw), and it’s fast and easily extensible. If you haven’t used it before, now’s the time to get started!

gem install merb -y

The release announcement on Ezra’s blog has a nice summary to some of the more significant improvements, and links to more in-depth coverage. Along with the gem update comes a brand spankin new website, merbivore.com to boot. Congrats guys, on what looks like a huge leap forward. I can’t wait to dig in tomorrow.

0 comments

Drop A PID For Monit

November 06, 2007 @ 04:46 PM by nap · 1 comment

If you ever need to drop a pid from a Ruby process it’s dead simple:

File.open('myapp.pid', 'w') { |f| f.write(Process.pid) }

Now you can use Monit to keep Sinatra alive, for instance ;-). Monit is great for monitoring UN*X processes and keeping them running under ideal conditions, and that means it’s great for Mongrel, and anything that runs through Mongrel. Monit can even check the memory consumption of your application and restart it if it seems to be leaking. Not that that ever happens, of course.

1 comment

I Am Not A Freeloader

November 05, 2007 @ 08:21 PM by nap · 1 comment

So have you heard the new Radiohead album yet? What did you think? And more importantly, what did you pay for it? ComScore estimates that 2 out of 5 of you did. They released a study today suggesting that, during the month of October, 40% of visitors were willing to pay an average of $6.00 for the digital downloads. Click that link for the full details. It’s also interesting to note that US consumers were will to pay more, on average, than the rest of the world.

The press is having a field day with this, and opinions are mixed. The ‘glass is half empty’ point of view seems to be that, holy crap, there are a lot of freeloaders on the ‘net.

No kidding, really?

On the other hand, the ‘glass is half full’ folks point out that, hey, people are actually willing to pay for stuff, and that music on the ‘net still has a perceived value after all.

I’m siding with the latter camp. Official sales figures won’t be released until after the holidays, but shit, I think these initial estimates are fantastic. Moreover, I think they show tremendous potential for non-compulsory tipping for digital goods in the public space. Software and media piracy is only a problem because of how we perceive and hope to profit from selling media on the web. Labels don’t need a new type of DRM, they need a new approach to what they’re selling. It’s information, and once that information is out there, it’s free, regardless of how much you perceive it’s worth to be. Magazine publishers figured this out a while back, and make their money through online advertising.

What Radiohead has done is adapt, and prove that, at least to some extent, a donation-driven model can work here. Of course, public radio beat them to the punch by at least 50 years, and they’re not the first band to sell music online, but it certainly signals a big win for those of us who believe that all web users aren’t freeloading scum. Even if the average user is a freeloader, the point is that the band can make enough from their efforts such that producing art for public consumption is profitable.

Anyway, the average worldwide price for all downloads, including freeloaders, was $2.26. I’d love to know what the bands’ actual per-album net was on their previous album, 2003’s Hail To The Thief. I’d be shocked if it was much higher than, say, $6.00 (update: this article estimates that it was probably between $3 and $5 USD). Personally I paid about $7 USD.

Oh, and the album is pretty good too.

1 comment

Clone Pastie in 15 Minutes with Sinatra & DataMapper

November 05, 2007 @ 05:25 PM by nap · 14 comments

Hey there, put on your tutorial hat. Today we're going to learn how to write a dirt simple pastie clone using Sinatra and Datamapper. For those of you unfamiliar with pastie systems, they're commonly used in IRC to paste bits of code to a globally-visible place outside of the channel. So you copy some code from your IDE, paste it into a web page, submit it, and the Pastie system fancies it up with some syntax highlighting or whatnot. You can then copy the resultant URL and paste it back into the channel so other people can view it.

Examples of this type of service include pastie.caboo.se and Nopaste @ rafb.net. Those sites do a lot of extra cool stuff, like allow you to select the language for different syntax highlighting, select themes for viewing, and so on. But the core concept itself isn't a terribly complex one, and our example is going to be about as barebones as they come. The interesting thing isn't the application we're going to build here so much as it is the tools we'll use. That is, we'll be using the pastie example to introduce you to two cool new pieces of Ruby tech: Sinatra, a new web micro-framework, and the DataMapper ORM package.

Sinatra, on the surface, is a lot like Camping, another Ruby web all-in-one-file micro-framework. Camping hasn't seen any active development in quite some time though, and the syntax can be a bit strange for new users. Sinatra is much more straight-forward and accessible. It's really a kind of domain-specific language for writing simple web applications, used to define RESTful actions and how they should be handled. This makes it perfect for lightweight mini-apps. It's also completely ORM-agnostic, instead of being married to ActiveRecord like both Rails and Camping are. For more information, check out the 'official' tutorial.

DataMapper is the new ORM package on the block, and an alternative to ActiveRecord (and Og and Sequel and so on). It just moved into town but it's already sitting at the cool kid lunch table. Whereas AR implements the ActiveRecord Pattern, DataMapper (surprise!) implements the DataMapper pattern. There are a number of reasons why I prefer this approach, but that's probably fodder for an entirely separate blog post, so I won't get into that here. Besides, the team has already written a great summary of why DataMapper rocks. Read it. Oh, and performance kicks ass now too.

OK, anyway. Tutorial time. Found your plastic hat? Good. Let's go. First let's get the gems we'll need. As of this writing, DM is at v0.2.3 and Sinatra is at v0.1.7. We're also going to retrieve the Syntaxi gem, which we'll use for syntax highlighting.

sudo gem install sinatra datamapper json syntaxi -y

Since DM uses the DataObjects.rb drivers, you'll want to install them. They're packaged with the distribution. For our purposes here we're going to assume you're on MySQL but all the standard drivers are there, so don't fret. Change to your DM gem directory (mine is /opt/local/lib/ruby/gems/1.8/gems/datamapper-0.2.3) and issue the following command. Ignore any warnings that are generated. If you're on OS X 10.5, you may want to check out Heimidal's blog for instructions.

sudo rake dm:install:mysql

Now that our prerequisites are satisfied, let's get started by creating the file toopaste.rb:

require 'rubygems'
require 'sinatra'
require 'data_mapper'
require 'syntaxi'

### SETUP

DataMapper::Database.setup({
  :adapter  => 'mysql',
  :host     => 'localhost',
  :username => 'root',
  :password => '',
  :database => 'toopaste_development'
})

### MODELS

class Snippet < DataMapper::Base
  property :body, :text
  property :created_at, :datetime
  property :updated_at, :datetime

  validates_presence_of :body
  validates_length_of :body, :minimum => 1

  Syntaxi.line_number_method = 'floating'

  def formatted_body
    html = Syntaxi.new("[code lang='ruby']#{self.body}[/code]").process
    "<div class=\"syntax syntax_ruby\">#{html}</div>"
  end
end

database.table_exists?(Snippet) or database.save(Snippet)

### CONTROLLER ACTIONS

layout 'default.erb'

# new
get '/' do
  erb :new, :layout => 'default.erb'
end

# create
post '/' do
  @snippet = Snippet.new(:body => params[:snippet_body])
  if @snippet.save
    redirect "/#{@snippet.id}"
  else
    redirect '/'
  end
end

# show
get '/:id' do
  @snippet = Snippet.find(params[:id])
  erb :show, :layout => 'default.erb'
end

Next we'll dissect this code listing to give you a feel for how Sinatra and DataMapper work, and show the code listings for our views as we get to them.

First of all, we define our database connection for DataMapper. Looks pretty similar to its AR equivalent, eh? Before we can run this we'll need to create the MySQL database mentioned in the source. You can create the toopaste_development database by issuing the following mysql command:

mysqladmin -u root -p create toopaste_development

We're not going to define a migration here, as this is a very simple case, but DM does support migrations in case you're curious. We next define our model, Snippet, which inherits from DataMapper::Base. The validators we're specifying look familiar to anyone coming over from AR but the property declarations might look a little bit odd at first.. OMG I don't have to put comments in my source file to remind me what attributes are available on my models?!

property :body, :text
property :created_at, :datetime
property :updated_at, :datetime

It's like a little slice of heaven, isn't it?

Skip over the formatted_body method for a moment and look at the line after the model class declaration that checks for pre-existence of the table. You'll note that if it's not found we call database.save with the model name as a parameter. This creates our table, with all the appropriate fields, as specified in the model.

database.table_exists?(Snippet) or database.save(Snippet)

The formatted_body method is simple. It just takes the body of our snippet (a property on the model) and wraps it in some code that gives us pretty syntax highlighting and line numbering. Syntaxi leverages Jamis Buck's Syntax gem to mark up the specified code, wrapping CSS span tags around things that should be colored, adding line numbers, and some other goodies too. You'll see the CSS in our layout shortly.

Another interesting DM optimization worth noting: by default, the text field (body) is lazily loaded. Text columns are expensive in databases, and by using lazy loading, we only access them when they're needed. This speeds things up significantly in most cases. However, if you don't like it, you can just pass a :lazy => false option in the text column property declaration.

Moving on to the controller actions, you'll see that routes and actions are intimately married in Sinatra. Although this isn't desirable for a larger application, it's great for quickie one-off web apps like this pastie project. We only have three actions and they're only ever available at these three URL patterns.

get '/' do
  erb :new, :layout => 'default.erb'
end

The first action displays a form to create a new paste, and it's always available at '/', your application root. Note that it's a GET request. The body of this action renders the new.erb template, which should be in your views/ subdirectory. Instead of pulling in an external template, you could just stash your HTML inline here. This may work fine for dirt-simple applications but I prefer to keep it separate. Here's that first view, /views/new.erb:

<div class="snippet">
  <form action="/" method="POST">
    <textarea name="snippet_body" id="snippet_body" rows="20"></textarea>
    <br/><input type="submit" value="Save"/>
  </form>
</div>

The next action is analogous to a #create action in RESTful Rails. The action is requested by a POST to the application root. Sinatra supports the standard GET and POST HTTP methods as well as PUT and DELETE, meaning that you can build RESTful applications with it.

post '/' do
  @snippet = Snippet.new(:body => params[:snippet_body])
  if @snippet.save
    redirect "/#{@snippet.id}"
  else
    redirect '/'
  end
end

Pretty standard stuff, right? We retrieve the parameter posted from the submit action in the new paste form, instantiate a new model and try to save it. If the validations pass, we redirect to the #show action equivalent. If not, we're just going to dump you back to the #new form again. Since the only way the action will fail is if the body property is empty, we're not going to bother with any sort of error message at this time. We're not rendering anything here (merely redirecting), so no template is required.

If our post is successful, we're going to be taken to the #show action, which lives at /:id, where :id is the primary key of the corresponding database record. This action will also get accessed directly when you paste that URL to the chat room, and people click to view your code.

get '/:id' do
  @snippet = Snippet.find(params[:id])
  erb :show, :layout => 'default.erb'
end

In the code listing above, we look up the particular snippet specified in params[:id] and set an instance variable. We then render an ERb template, which of course has access to that instance variable. Here's the code you'll want in /views/show.erb:

<div class="snippet">
  <div class="sbody" id="content"><%= @snippet.formatted_body %></div>
  <div class="sdate">Created on <%= @snippet.created_at.strftime("%B %d, %Y at %I:%M %p") %></div>
  <br/><a href="/">New Paste!</a>
</div>

If you've been paying attention you've noticed that both of our erb method calls (used to render an embedded ruby HTML view, you can also use haml) have specified a layout. That layout does the sorts of things a layout usually does; it sets up the page body, the title of the page, the styles, and so on. For completeness' sake, we'll list it here:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
  <title><%= @title || 'Toopaste!' %></title>
  <style>
    html {
      background-color: #eee;
    }
    .snippet {
      margin: 5px;
    }
    .snippet textarea, .snippet .sbody {
      border: 5px dotted #eee;
      padding: 5px;
      width: 700px;
      color: #fff;
      background-color: #333;
    }
    .snippet textarea {
      padding: 20px;
    }
    .snippet input, .snippet .sdate {
      margin-top: 5px;
    }

    /* Syntax highlighting */
    #content .syntax_ruby .normal {}
    #content .syntax_ruby .comment { color: #CCC; font-style: italic; border: none; margin: none; }
    #content .syntax_ruby .keyword { color: #C60; font-weight: bold; }
    #content .syntax_ruby .method { color: #9FF; }
    #content .syntax_ruby .class { color: #074; }
    #content .syntax_ruby .module { color: #050; }
    #content .syntax_ruby .punct { color: #0D0; font-weight: bold; }
    #content .syntax_ruby .symbol { color: #099; }
    #content .syntax_ruby .string { color: #C03; }
    #content .syntax_ruby .char { color: #F07; }
    #content .syntax_ruby .ident { color: #0D0; }
    #content .syntax_ruby .constant { color: #07F; }
    #content .syntax_ruby .regex { color: #B66; }
    #content .syntax_ruby .number { color: #FF0; }
    #content .syntax_ruby .attribute { color: #7BB; }
    #content .syntax_ruby .global { color: #7FB; }
    #content .syntax_ruby .expr { color: #909; }
    #content .syntax_ruby .escape { color: #277; }
    #content .syntax {
      background-color: #333;
      padding: 2px;
      margin: 5px;
      margin-left: 1em;
      margin-bottom: 1em;
    }
    #content .syntax .line_number {
      text-align: right;
      font-family: monospace;
      padding-right: 1em;
      color: #999;
    }
  </style>
</head>
<body>
  <%= yield %>
</body>
</html>

Save that listing as views/default.erb. The rendered page content will be inserted into this layout where it yields. And that's pretty much it. You can fire up your new Sinatra and DataMapper-powered application by issuing the following command:

ruby toopaste.rb
== Sinatra has taken the stage on port 4567!

Sinatra sits on top of Mongrel, making it super-easy to use (and thread-safe to boot!). If you open up a web browser and point it at http://localhost:4567 you'll see the results. You now have a fully functional (albeit slightly retarded) pastie clone with syntax highlighting for Ruby code snippets. And the core logic is all contained in a single file, with a few external ERb templates for cleanliness.

If you like, you can play with the finished app or download the sources. Enjoy, and please comment if you have any problems or suggestions for improvement. This code was written and tested on OS X 10.4 and Debian Etch.

UPDATED 07/02/08: I finally got around to updating this tutorial for DataMapper 0.9.2. About time, eh? Click here to see the new version.

14 comments

Blog Updates

November 01, 2007 @ 07:54 PM by nap · 0 comments

So I finally got around to migrating the blog site over to Mephisto. I've only been planning on doing that for like 9 months. Horray for progress!

0 comments