APR 30
Because I’m a) lazy and b) a huge nerd, I added support for drafts to this blog, which runs on Webby. It’s fairly straightforward, requiring two new tasks in blog.rake and of course, the draft.erb template. As always, the latest code can be found at my blog’s github repo.
In blog.rake:
desc 'Create a new draft' task 'draft' do |t| page, title, dir = Webby::Builder.new_page_info title = Webby.site.args.raw[0] # undo the titlecasing raise "Don't specify a directory for a blog post!" unless dir.empty? page = File.join(Webby.site.draft_dir, File.basename(page)) page = Webby::Builder.create(page, :from => File.join(Webby.site.template_dir, 'blog', 'draft.erb'), :locals => { :title => title, :directory => Webby.site.draft_dir }) Webby.exec_editor(page) end desc 'Publish a draft' task 'publish_draft' do |t| site = Webby.site draft = site.args.raw[0] drafts = Dir["#{site.content_dir}/#{site.draft_dir}/*#{draft}*.txt"] raise "No drafts matching '#{draft}'" if drafts.empty? raise "Found multiple drafts matching '#{draft}': #{drafts.map {|d| File.basename(d) }.join(', ')}" if drafts.size > 1 # drafts.size == 1 draft = drafts[0] draft = Webby::Resources::Page.new(draft) now = Time.now year = now.strftime('%Y') month = now.strftime('%m') day = now.strftime('%d') dir = File.join(Webby.site.blog_dir, year, month, day) page = File.join(dir, draft.name) page = Webby::Builder.create(page, :from => File.join(Webby.site.template_dir, 'blog', 'post.erb'), :locals => { :title => draft._meta_data['title'], :directory => dir, :created_at => now.to_y, :body => draft._read }) Logging::Logger['Webby'].info "deleting #{draft.path}" FileUtils.rm(draft.path) end
This does require the modifications to the create_year_index and create_month_index tasks I made, but should be relatively straightforward to port back into the blog.rake file included with Webby’s blog template.
To use this, I create draft posts using webby blog:draft "Some draft title" and when I’m ready to actually post the article, webby blog:publish_draft title. (The argument for publish_draft is used in a regex to locate the draft to publish.)
APR 27
The code for making a sitemap in Webby:
<?xml version="1.0" encoding="UTF-8"?> <urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9"> <% @pages.find(:all, :in_directory => '', :extension => 'html', :recursive => true, :draft => nil).each do |page| -%> <url> <loc><%= Webby.site.base %><%= page.url %></loc> <lastmod><%= page.mtime.strftime('%Y-%m-%d') %></lastmod> <priority><%= page.blog_post ? 1 : 0.8 %></priority> </url> <% end -%> </urlset>
In the process of moving this blog from Typo to Webby, it quickly became obvious that copying the blog posts over by hand was not an option. Searching on Google led me to a task for importing the posts using an RSS feed, but since I used an SQLite3 database with Typo, I thought it would be easier to just grab the posts directly from the database file.
The bulk of the changes are in this changelist on GitHub, but I made a few more modifications to the process that are included in later changelists.
tasks/blog.rake differs mainly in that it uses the actual database instead of an RSS feed and has regular expressions to convert my Typo code highlighting to use Webby’s UltraVioletHelper. The code is also cleaned up a bit, to use Rake arguments instead of passing around the created_at time as an ENV variable and also to avoid copying Webby::Apps::Main#capture_command_line_args. Changing the database source is left as an exercise for the reader.
desc "Import blog posts" task :import_posts, :dbfile do |t,args| require 'active_record' ActiveRecord::Base.establish_connection( :adapter => 'sqlite3', :dbfile => args.dbfile ) class Content < ActiveRecord::Base; end class Page < Content; end class Article < Content; end Article.find(:all, :conditions => { :published => true }).each do |post| published_at = post.published_at page = ::Webby::Resources.basename(post.title).to_url title = post.title dir = "#{Webby.site.blog_dir}/#{published_at.strftime('%Y/%m/%d')}" ::Webby.site.args = OpenStruct.new(:raw => [title], :page => page, :title => title, :dir => '') year = published_at.strftime('%Y') month = published_at.strftime('%m') Rake::Task['blog:create_year_index'].execute(Rake::TaskArguments.new([:year], [year])) Rake::Task['blog:create_month_index'].execute(Rake::TaskArguments.new([:year, :month], [year, month])) page = File.join(dir, File.basename(page)) # Colons in the title isn't correct YAML title = "\"#{title}\"" if title =~ /:/ body = post.body # Convert the Ultraviolet textfilter blocks body.gsub!(/<typo:ultraviolet(.*?)>/, '<% uv\1 do -%>') body.gsub!('</typo:ultraviolet>', '<% end -%>') body.gsub!(/<% uv (.*) do -%>/) do s = $1.split(' ').map {|i| i.gsub(/(.*)="(.*)"/) { ":#$1 => '#$2'" }}.join(', ') s.sub!('linenumber', 'line_numbers') "<% uv #{s} do -%>" end # Remove extra carriage returns from the SQLite3 string. body.gsub!("\r", '') Webby::Builder.create(page, :from => File.join(Webby.site.template_dir, 'blog', 'post.erb'), :locals => { :title => title, :directory => dir, :body => body, :created_at => published_at.to_y }) end end
You’ll have to of course add the <%= body %> tag in post.erb.
Adding arguments to the year and month indices isn’t too complicated. At the same time, I disabled the dirty flag for the indices created by the imported posts, as they don’t need to be re-rendered more than once.
task :create_year_index, :year do |t,args| args.with_defaults(:year => Time.now.strftime('%Y')) year = args.year ... snip ... Webby::Builder.create(fn, :from => tmpl, :locals => {:title => year, :directory => dir, :dirty => (year == Time.now.strftime('%Y'))}) end end
task :create_month_index, :year, :month do |t,args| args.with_defaults(:year => Time.now.strftime('%Y'), :month => Time.now.strftime('%m')) year = args.year month = args.month ... snip ... Webby::Builder.create(fn, :from => tmpl, :locals => {:title => month, :directory => dir, :dirty => ("#{year}.#{month}" == Time.now.strftime('%Y.%m'))}) end end
The month.erb and year.erb templates also need to be changed to support disabling the dirty flag:
title: <%= title %> created_at: <%= Time.now.to_y %> filter: erb <%= 'dirty: true' if dirty %>
Finally, running webby blog:import_posts[typo.db] finishes the import process, populating the content directory with all of the Typo posts.
Edit (2009.04.20): Although this blog post is still technically correct, it’s probably better just to grab the latest versions of my tasks and templates from my github repo. I’ve cleaned up the tasks a bit and added arguments to the blog:post task to specify the date.