I've long been a fan of James Duncan Davidson's photography. He's probably
best known for his exceptional work as the official shooter at O'Reilly
software conferences. You may not see him, but he's always there. Indeed,
Duncan is the lens through which many of us have truly seen the emotion of a
keynote speaker or the excitement of an attendee. But what I admire most about
Duncan is his versatility. He can capture amazing photos in the worst possible indoor lighting conditions all day, and then snap a gorgeous nature scene on his road trip home. He makes it look so easy. If only he knew how much he's cost me in camera gear. :-)
Earlier this year, Duncan started selling some of his non-conference prints
online. I had already fallen in love with a couple of the photos he chose, and
I was thrilled by the prospect of owning a print copy. But I must admit, I've
never ordered print photography online, and frankly I was hesitant to try it.
Part of my hesitation was just the unknown process of going from a digital
image I bought online to a print copy hanging on my wall. And I was also a bit
worried that some degree of quality might be sacrificed along the way. After
all, when you buy art, you're making an investment.
I'm extremely happy to report that I'm now the proud owner of two Duncan prints. The process was super easy and the quality is absolutely amazing! Rather than tell you how it all works, I thought I'd show you.
First, you go to the Zenfolio storefront and
pick your favorite Duncan photos. This is art; you get to pick the photos that
speak to you.
If you've followed Duncan's road trips over the last few years, you'll
recognize many of these shots. There's a story behind every picture. Duncan
has told some of the stories in his blog. I would encourage you to find the
story behind the picture you like, perhaps the next time you see Duncan at a
conference. Knowing the photographer and hearing his story makes the photo
more valuable to you.
The second decision you need to make is the size of each print you picked:
The size you choose might be as simple as the size of your wall or the size of
your budget. Remember that this is the print size, and does not account for
framing. In general, a frame will add 3-4" to each side of a print. In my
case, I used a tape measure to estimate which size fit on my office wall with
plenty of room to breathe. I ended up selecting one 16" x 24" print and one 12" x 18" print (displayed below).
Once you've picked prints and sizes, the checkout process is super easy (and does not require an account):
A couple days later, your prints show up on your doorstep:
I ordered on a Tuesday and the prints arrived on Friday. For some reason I
expected the shipping to be expensive, and was surprised that USPS Priority
Mail was only $5.45. (FedEx Next Day was also available for $10.25.) Although
you order the prints through Zenfolio, they use Mpix as their print and
fulfillment partner:
This single box contained both of my prints in a rigid cardboard structure:
I was very impressed with the care in which the prints were packaged and
shipped. Inside the box, each picture is lightly affixed to a cardboard
backing and carefully wrapped in a plastic sleeve:
The prints themselves are absolutely gorgeous!
Seriously, Duncan is meticulous when it comes to print color correction.
These images don't do it justice. My jaw dropped when I first saw this print, and I'm still amazed every day when I look at it.
Once you've received your prints, you'll want to get them framed and matted.
Now, I'm certainly no pro at this. And thankfully you don't have to be either.
I just left my prints unframed for a few days to enjoy their natural color.
Then I took the prints to a local frame shop and asked the friendly
gal: "If these were your prints, how would you frame and mat them?" She seemed
to appreciate that I asked for her advice. And after taking in the prints for
a few minutes, she drew my attention to the subtle colors. Then she
recommended a few framing and matting combos that would complement the colors and frame each
print nicely. Of course, I'd been thinking about ideas too, so we went through a couple iterations. I ended up framing both prints for around $200
(thanks in part to a sale at the frame shop). It was actually a lot of fun!
To give you some perspective on the sizes, here's the 16" x 24" print:
And here's the 12" x 18" print:
I haven't quite decided where to hang each one yet. When I placed the order I thought I knew where they would go, but I'm so happy with how they turned out that now I can't commit to just one place. :-)
I hope seeing these pictures fills in some of the unknowns you might have about ordering Duncan's prints online. He's really done a fantastic job making these prints easily accessible while at the same time staying true to the quality of his work. If you like Duncan's photography, this is a great way to support his work!
(Thu Sep 18, 2008) [/Books] #
I've been gearing up for some client work using Cocoa, and along the way I had
the pleasure of being a tech reviewer for Daniel Steinberg's latest book Cocoa Programming: A
Quick-Start Guide for Developers. I don't often review books here, but
this book is exceptional. I think it has the potential to help folks who are
interested in building native Mac (and iPhone) applications finally break into
Cocoa development. The book was just released as a beta. Here's a rundown of
what's inside and how it's been helpful to me.
After getting a quick lay of the land in the first chapter, Chapter
2 jumps right into building a simple web browser in Cocoa. That
sounds fairly complicated for your first Cocoa app. I remember bracing for the
unavoidable moment when I'd have to copy in a bunch of code I didn't
understand. Thankfully, it's not that kind of book.
What I like most about this book is that it makes learning Cocoa fun and
accessible. At the same time, I never felt like Daniel was waving his hands or
dumbing anything down. He respected that I was a programmer, but didn't take
advantage of that by making me wade through unfamiliar code to get to the good
stuff. After all, a simple web browser in Cocoa is just a text field, a web
view, and two buttons to navigate forward and back. You can lay out and wire
up those components to act like a browser using Interface Builder without
writing a single line of code. That's what the second chapter is all about:
getting visual early! When I'm learning a new set of tools, I like to focus on
one thing at a time. In this case, I was introduced to Interface Builder
without having to tackle Objective-C, and I chalked up a quick win.
Of course, you'll need to write some code to make your web browser different
from all the others. But again, you really don't want to stop and learn the
entire Objective-C language just to add a new feature to the web browser.
Chapter 3 is a gentle introduction to sending messages in
Objective-C. If you've never seen the selector syntax, it can be a little
awkward at first. The best way I've found to learn how it works is to start
sending messages to existing Cocoa libraries. It turns out you can practice
this in Interface Builder too. By the end of Chapter 3 my web browser and I
were a little smarter.
Once you're comfortable with sending messages to existing objects, you're
ready to create some Objective-C classes and objects of your own. Interface
Builder can only do so much. Chapter 4 is where the rubber
meets the road. Daniel walks you through creating a controller and how to make
connections with Interface Builder through actions and outlets. I really like
how he starts with a controller that's just a middleman, then incrementally
expands its responsibility. It doesn't take a lot of Objective-C code, but it
was just enough to make me feel comfortable creating classes with methods and
instance variables. Daniel kept building up my confidence, one step at a time.
And doing the exercises in each chapter kept me engaged.
Sending messages to a controller is one way to respond to user actions, but
it's only part of the picture. In most Cocoa applications there's lot of stuff
happening behind the scenes, too. The Cocoa frameworks can call back into your
application via delegate methods and send notifications when certain things
happen. Chapter 5 shows you how to use delegates and
notifications to update the browser's title bar when a page finishes loading
and update a progress bar as it's loading. Again, the process of building an
application is driving what you need to learn, and when. That's exactly the
way I like to new learn things.
At this point in the book I'm feeling pretty good about myself, and I'm really
enjoying learning something new. I'm also surprised to find myself liking
Objective-C. But I know that Daniel has been kindly delaying the topic of
memory management until it's necessary, and it's been a few years since I've
had to clean up after myself. The Cocoa event loop does a lot of this for you,
and Objective-C 2.0 includes a garbage collector, but Cocoa applications often
need to take more direct control (and iPhone apps always do).
Chapter 6 steps away from the web browser to show you how to
create objects with properties, and how to make them good stewards of memory.
I suspect everyone has a slightly different mental model for understanding the
retain-release cycle. This chapter was the first explanation of Objective-C
memory management that really worked for me.
As this book is currently in beta, it's not yet complete. I'm very much
looking forward to Daniel's new chapters as they come. That said, this beta
book has already given me everything I need to start building my own Cocoa
applications. And that's really the point. It's designed to get you up and
running quickly, and help you through the first couple weeks of moving to
Cocoa. To that end, I think Cocoa Programming: A
Quick-Start Guide for Developers is simply brilliant!
Disclaimer: I'm reviewing this book because I like it and Daniel is a friend. I'm not employed
by The Pragmatic Programmers, nor do I make any money from the sale of
their fine books (except those that I write, of course). I do, however, own and operate The Pragmatic Studio where
Daniel will be teaching an upcoming Cocoa Studio.
(Wed Aug 06, 2008) [/Tools] #
Let's suppose that last week your long-lost cousin Bubba called to see if
you'd design the website for his new bait and tackle shop. (Hey, he's a
progressive redneck.) You figured it would be a quick and easy job—he
just needed a few "purty pages to git my new bidness on the World Wide
Web." So you hacked together a couple static HTML pages with pictures of Bubba
and his charming bait, and uploaded them to his GoDaddy account. Bubba was
pleased as punch. You reckoned (hoped) you'd never hear from him again.
But it turns out Bubba was just getting started. Once he saw those first pages
light up, the ideas started flowing like tobacco to a spittoon. Then he asked
for new pages for all sorts of stuff: a mission statement, a series of tips on
how to fish like a pro, the GPS coordinates of his favorite fishing holes, and
so on.
Now you're in a pickle. The site was only supposed to have a couple pages, and
so you played the ol' copy/paste trick on the header, footer, and sidebar.
With all these new pages coming down the pike, you need to DRY things up in a
hurry.
Being a Rails programmer, the first thing that comes to mind is layouts,
helpers, and partials. But let's face it, managing a full Rails application is
overkill for this job. (Bubba's not that progressive.) Instead, you'd
prefer to simply use a familiar layout, helper, and partial style for now with
the option of easily migrating to Rails later, if necessary.
Hello, Webby!
Webby (created by Tim Pease) takes
text files written in your favorite markup language, combines them with
layouts, helpers, and partials, and generates HTML files. You upload
these HTML files to your web server and away you go. Simply put, Webby
makes it easy to build static websites without duplication.
1. Install It
Webby is a Ruby gem, which means even Bubba could handle the install.
$ gem install webby
Don't be too alarmed by all the other gems Webby relies on to get its job done. All the dependent gems are mostly common stuff.
You can also check out Webby on GitHub.
2. Build the Default Website
To build our website with Webby, the files need to live in a directory structure that Webby
understands. (Hey, Webby has opinions, too.) There's not much to it,
and the webby generator creates everything for us.
$ webby bubbas_bait
That creates a bubbas_bait directory with a
Rakefile and several directories:
Rakefile
content/
layouts/
lib/
output/
tasks/
templates/
We'll look at each of these directories as we go. For now, let's just pull the
trigger and see what happens.
$ cd bubbas_bait
$ rake
Running the default Rake task creates a bunch of files in the output
directory, including an index.html file. Where did that file come
from? Well, it's pretty simple.
When we run rake to "build" the site, Webby copies the files in the
content directory to the output directory. Some files, such
as images, are copied verbatim into the output directory. Nothing too
tricky there. However, the content directory can also contain special
files (called pages) that include a snippet of meta-data at the top.
These pages are first "compiled" into HTML files (using our favorite markup
language) and then copied into the output directory. In this case,
Webby transformed the default content/index.txt file into the
output/index.html file. (It also copied the Blueprint CSS framework and
the S5 presentation CSS into
the output directory.)
If you want to clear out the output directory and build the site from scratch, use
$ rake rebuild
If we now open the output/index.html file in our browser, we see the default page for our website. So far, so good.
3. Make a Page, Any Page
Before we dive into converting Bubba's existing HTML files into Webby-managed files, let's start by creating a new page for his mission statement.
$ rake create:page mission_statement
That creates a mission_statement.txt file in the content
directory, and opens the file in our default text editor. After changing the
default text to include Bubba's mission statement, the file looks like this:
---
title: Mission Statement
filter:
- erb
- textile
---
h1(title). <%= h(@page.title) %>
To sell world-class bait by the pound, and have fun doing it!
The YAML-formatted text at the top between the dashed lines is the meta-data
for the page. All the text after the meta-data section is content that gets
rendered into the resulting HTML file.
The meta-data section is simple, yet powerful. First, it contains page
attributes such as title in this example. We can add arbitrary page
attributes to the meta-data section and reference their values in the page
using the @page.attribute notation within an ERB expression. In this
case, we use @page.title to add the page title to the top of the
page.
The meta-data section also contains instructions that tell Webby how to
process the page. For example, the way a page is generated hinges on the
filter attribute. It lists the filters that need to be applied to the
page's contents in order to transform it into an HTML file in the
output directory. In this case, the ERB and Textile filters will be
run, in that order. We need the erb filter so that the stuff between
the <% and %> (the ERB expression) will get evaluated.
And since our page content is in the Textile format, we need the
textile filter to transform the page into HTML. If instead we wanted
to use raw HTML, which we'll use from here on, we'd simply remove the
textile filter. Other filter options include markdown,
haml, outline, basepath, and tidy. (See,
Webby isn't that opinionated.)
Having changed a page, if we rebuild the site, only those pages that have been modified are copied to the output directory.
$ rake
[13:43:04] INFO: creating output/mission_statement.html
4. Autobuild and Serve It Up
Now let's say we make another change to the
content/mission_statement.txt file. To have a look at the final HTML
file, we'd need to run the rake command to rebuild the site and open
the resulting HTML file in our browser. That's sorta tedious. Thankfully, we
can tighten up the feedback loop with Webby's autobuild feature.
$ rake autobuild
The autobuild task starts a build loop that keeps the output
directory up to date as files change. Whenever a page is created or updated,
its corresponding HTML file is automatically generated in the output
directory. Better yet, the autobuild task serves up the files in the
output directory on port 4331 using Heel , a small static
web server built using Rack and Thin. That way we can immediately
see changes reflected in our browser.
5. Frame It With a Layout
Next we need to put everything that's common to all the pages—the
header, footer, sidebar, etc.—in one file and let Webby combine it with
the contents of each page to produce the HTML file. That is, each page file
should contain only the information for that page and no more.
To do that, we update the layouts/default.rhtml file to include
our site's layout. Here's an example layout file:
---
extension: html
filter: erb
---
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
"DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en-us">
<head>
<meta http-equiv="content-type" content="text/html; charset=utf-8" />
<title><%= @page.title %></title>
<link rel="stylesheet" type="text/css" href="/css/site.css" />
</head>
<body>
<div id="header">
Bubba's Bait and Tackle Shop
</div>
<div id="content">
<%= @content %>
</div>
<div id="sidebar">
Side Dishes
</div>
<div id="footer">
Copyright <%= Time.now.strftime('%Y') %> Bubba's Bait Shop.
Steal it, and we'll hunt you down!
</div>
</body>
</html>
Notice that the layout file contains meta-data just like regular page files.
In this case, we're using raw HTML rather than Textile, so we only need the erb filter. The extension attribute specifies the
filename extension that will be appended to every file this layout generates.
The layout file's job is to wrap the page-specific content in a consistent
layout. The content of each page is accessible in the layout via the
@content variable. So, we simply add the <%= @content %> line where we want the page content to show up in the layout. In
addition to the @content variable, the layout also has access to all
the attributes of the current page being rendered via the @page
variable. For example, we're using @page.title to set the page title in the HTML header.
By default, the layouts/default.rhtml will be used as the layout file. However, if we need to generate a certain page with a specific layout, we can explicitly set the layout file for any page using the
layout page attribute. For example, perhaps the mission statement page should have a more corporate feel to it. To change the layout, the
mission_statement.txt page's meta-data would look something like this:
---
title: Mission Statement
layout: corporate
filter:
- erb
- textile
---
This assumes we have a layout file called _corporate.rhtml in the
layouts directory.
6. Summons a Helper
Bubba likes to show off by quoting the weight of fish landed by his aromatic bait. These weights are listed on various pages around the site. We need the weights to be formatted consistently. Specifically, the weights get reported in ounces, but should be displayed in pounds and ounces.
Rather than inline the code for the weight conversion in several pages, we can write a helper method and call it from any page. To do that, we create a weight_helper.rb file, for example, in the lib directory. Just like in Rails, a helper in Webby is a Ruby method in a module.
module WeightHelper
def format_weight(weight_in_ounces)
lbs, ounces = weight_in_ounces.divmod(16)
sprintf("%d lbs, %d ounces", lbs, ounces)
end
end
Webby::Helpers.register(WeightHelper)
Unlike Rails, helpers must be explicitly registered as we did on the last line.
Then, wherever we need to display a weight on a page, we call the helper method in an ERB expression like this:
<%= format_weight(70) %>
Pretty cool.
7. Paginate A Gogo
Remember that series of pro fishing tips that Bubba wrote? To let the drama
build, we can use pagination to spread them across multiple pages. Let's
assume that the individual tip pages are in a directory called tips
in the content directory. First we create a top-level tips page:
$ rake create:page tips
We want the contents of the tips.txt page to display the individual
tip pages ten at a time in reverse chronological order. To do that, we can use
the @pages.find method to fetch all the pages inside the
tips directory and sort them. Then we pass the collection of pages,
and the number of tips per page, to the paginate method. Here's the
full tips.txt file:
---
title: Pro Fishing Tips
filter:
- erb
- textile
---
<h1><%= h(@page.title) %></h1>
<%
tips = @pages.find(:all,
:in_directory => "tips",
:recursive => true,
:sort_by => "mtime",
:reverse => true)
paginate(tips, 10) do |page|
%>
<div class="tips">
<%= render(page) %>
</div>
<% end %>
<%= link_to("Prev", @pager.prev) if @pager.prev? %>
<%= link_to("Next", @pager.next) if @pager.next? %>
The paginate method hands each tip page to the block where we simply
render the page within a styled div. Finally, at the bottom of the page, we
use the link_to method to generate the pagination links. The
@pager object was created when we called the paginate
method. It has methods including prev and next to navigate
the pages.
8. Reuse Snippets with Partials
Let's say we end up including pagination on a couple more pages. Along the way we add page numbers and tart up the style of the pagination links. Instead of duplicating the pagination links on each page, we'd like to
encapsulate the pagination links in one file and reuse it on all the pages that have pagination. That's where partials come in handy.
First we create the partial file:
$ rake create:partial pagination
That generates a content/_pagination.txt file. (Note that partial
filenames begin with an underscore and they aren't copied to the
output directory.) Inside the partial file, we add the HTML that generates
the pagination links:
---
filter: erb
---
<div id="pagination" class="rounded">
<strong><%= name %> Pages</strong>:
<%= link_to("Prev", @pager.prev) if @pager.prev? %>
<% 1.upto(@pager.number_of_pages) do |page_num| %>
<% if @pager.number == page_num %>
<%= page_num %>
<% else %>
<%= link_to(page_num, @pager.page(page_num).url) %>
<% end %>
<% end %>
<%= link_to("Next", @pager.next) if @pager.next? %>
</div>
Then we use the render method at the bottom of our
content/tips.txt page, for example, to include the partial into the
page.
<%= render(:partial => "pagination", :locals => {:name => 'Tip'}) %>
Looking back at the _pagination.txt file, we see two variables:
@pager and name. Remember that the @pager object
was accessible in the original content/tips.txt page, so it's
accessible in this partial as well. We use the @pager object here to
generate navigation links and display page numbers. The name variable
is accessible because we added it to the locals hash when calling the
partial. We use it to change the text that appears next to the pagination
links.
If you're a Rails programmer, you'll notice that the partial syntax is exactly the same.
9. Generate Stylesheets, Too!
So far we've only used Webby to generate HTML files, but in fact Webby can
transform any kind of text into something else. For example, let's say we have
our site's stylesheet in the content/css/site.css file. When we build
the site, Webby will simply copy it over to the output/css/site.css
file. However, if we add meta-data to the top of the stylesheet file, Webby
will transform it for us.
What's this good for? Well, do you ever get tired of typing (and remembering)
those hex color codes in your CSS files? If so, you're not alone. Here's an
example content/css/site.css file that uses page attributes to name
the colors:
---
extension: css
filter: erb
layout: nil # no layout
color:
bubba_blue: "#3366FF"
bubba_brown: "#996600"
---
body {
font-family: Verdana, "Bitstream Vera Sans", sans-serif;
background: <%= @page.color['bubba_blue'] %>;
}
table {
border: 4px solid <%= @page.color['bubba_brown'] %>;
}
th,td {
border: 2px solid <%= @page.color['bubba_brown'] %>;
}
The color attribute is a hash of color names and hex codes that we
can reference in the page using the @page.color[] syntax. Notice that we also set the layout attribute to nil because the
resulting output/css/site.css doesn't need a layout.
10. Deploy It
Deploying our website is as easy as building the site and uploading the
contents of the output directory to our web server. Remember,
everything except partial files are copied from the content directory
to the output directory when we build the site. Your content directory can include
an arbitrary number of subdirectories to organize your content any way you
like. (Consider removing the s5 and css/blueprint
directories if you aren't using them.)
If we don't already have an easy way to upload our files, Webby gives us a few Rake tasks to automate that process using rsync or ssh.
In the top-level Rakefile, we need to add three variables:
SITE.user = 'bubba'
SITE.host = 'bubbas-bait.com'
SITE.remote_dir = '/path/to/webserver/root/'
Then to upload the contents of the output directory using rsync, use
$ rake deploy
By default, the deploy task will use rsync to copy the files
to the directory specified in the SITE.remote_dir variable. (Look in the tasks/deploy.rake file for details.) If you'd prefer uploading via ssh, use the deploy:ssh task.
And that's all there is to it! We have a simple, yet flexible, way to remove duplication from our content and upload it to our web server.
Importing Existing Files
Now that we've created pages, layouts, helpers, and partials, importing our existing static HTML files into Webby is a breeze. Each existing file currently includes all the layout and page-specific HTML. To DRY it up, we just repeat these steps for each file:
-
Copy the layout code into the layouts/default.rhtml file.
(Most of the files will likely have the same default layout, and you can
create custom layout files for those that are different.)
-
Copy the page-specific content into a corresponding .txt file
under the content directory. You can mirror the directory
structure of your existing site in the content directory.
-
Write helper methods to encapsulate view logic and keep the pages DRY.
-
Extract reusable snippets into partials so you can render them
from multiple pages.
Finally, copy any asset directories into the content directory. Webby
will copy it over wholesale. For example, if you have an images
directory in your existing site, just copy it into the content
directory.
Writing Your Own Tasks
Webby includes a number of Rake tasks for common chores when building a website. We've used several of them:
$ rake -T
rake autobuild # Continuously build the website
rake blog:post # Create a new blog post
rake build # Build the website
rake clobber # Delete the website
rake create:atom_feed # Create a new atom_feed
rake create:page # Create a new page
rake create:partial # Create a new partial
rake create:presentation # Create a new presentation
rake deploy # Deploy the site to the webserver
rake deploy:rsync # Deploy to the server using rsync
rake deploy:ssh # Deploy to the server using ssh
rake growl # Send log events to Growl (Mac OS X only)
rake heel:start # Start the heel server to view website (not for ...
rake heel:stop # Stop the heel server
rake rebuild # Rebuild the website
rake validate # Alias to validate:internal
rake validate:external # Validate hyperlinks (include external sites)
rake validate:internal # Validate hyperlinks (exclude external sites)
As you continue to work on your site, you may find opportunities to automate other chores. Just create a file with a .rake extension in the tasks directory and add your Rake tasks to it. It's worth looking at the built-in task files in the tasks directory for examples.
The Bubba Blog
Ok, we're going a little overboard here, but creating a blog illustrates the power of Webby. We said that Webby can be used to generate any kind of text, and that includes generating XML files for RSS or Atom news feeds. So if Bubba decides one day that he'd like to do some blogging, we can generate a blog post page:
$ rake blog:post i_got_me_a_blog
That creates a content/blog/2008/08/05/i_got_me_a_blog.txt file, for example, where we can add the text for a blog post. Then we generate an Atom feed file using
$ rake create:atom_feed bubba_blog
If we then build the site, we'll end up with an output/bubba_blog.xml file that includes all the blog entries found in the output/blog directory.
Transitioning to Rails
If at some point we need the full power of Rails, it's fairly easy to make the
transition. Webby and Rails both use ERB to render pages, so converting the
page files into Rails view files is straightforward. As well, the layout,
helper, and partial syntax match very closely to the same facilities in Rails.
It's great to be able to take incremental steps like this rather than jumping
straight to Rails before you need it.
So the next time you're faced with building a static website, give Webby a
whirl! I've happily used it to build several static websites now. Frankly, I'm a bit surprised it hasn't received more press. Perhaps this tutorial
will help Webby get the attention it deserves.
If you're transitioning to Rails and you're looking for high-quality
training, consider attending an upcoming Rails Studio or Advanced Rails
Studio. We also have screencast video tutorials on a
variety of timely topics, including the ExpressionEngine content
management system for building dynamic websites.