Software Development 2.0 China
November 28, 2007 @ 07:29 PM by nap · 0 comments
Links For 11.24.07
November 24, 2007 @ 02:35 PM by nap · 0 comments
- JRails - Drop-in JQuery replacement for Rails prototype/script.aculo.us helpers.
- NetBeans 6.0 RC2 - Available for download.
- Gemtacular - Rate and Review Ruby Gems.
- LiveConsole - IRb over TCP remote console. Cool idea.
- Google TV?
- Rake tasks for QueryTrace
- Rails 1.2.6 - Includes an important security fix. gem update rails -y
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!
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."
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.
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!
Turn On Your Buzzword Filter
November 13, 2007 @ 12:46 AM by nap · 0 comments
zapnap [12:38am]: turn on your buzzword filter. OMFG WHERE DID T EH INTERNETS GO?
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!
Links For 11.10.07
November 10, 2007 @ 08:18 AM by nap · 1 comment

- FreeBSD 7.0 is is going to own scaling. Srsly. RC1 should be available this week.
- Rails 2.0 RC1 is here. Have you fixed those deprecation warnings in 1.2.5 yet?
- Heroku is coming next week. Holy crap those screencasts are cool. So the future of building and hosting webapps looks a lot like Seaside, eh?
- More Heroku coverage and commentary by Giles Bowkett.
- The Great Merb Speedup. Keep your eyes peeled for 0.4.1, things in this camp keep getting better and better.
- Interesting critique of OpenSocial from Tim O'Reilly.
- Google's OpenSocial API Blog, because it's the new hotness.
- Using OpenSocial with Ruby on Rails, because hey, you're curious, right?
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.
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.
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.
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.
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.
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.
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!