This post is one of a three part series


For a while, I’ve had the intention to start writing a blog using Emacs’s org-mode. And today, I’m glad to write my first blog post in that fashion; and I’ve decided to show you a small tool I built to do so.

Before starting, I have to warn you that this is not a finished tool and that I intend to make it a gem at some point. But not yet, now that I have “something”, it’s better for me to stop procrastinating on the writing… I don’t need another excuse to put off this blog.

Lastly I’ll point out that the code is messy and that I won’t cover all of it, just the most important parts in favor of brevity.

How it works

It currently has a directory structure similar to this one:

$ tree
.
├── Gemfile
├── Gemfile.lock
├── Rakefile
├── org_post.rb
├── wordpress.rb
├── posts
│   ├── WIP_some_future_post.org
│   ├── post_1.org
│   └── post_2.org
└── html
    ├── post_1.html
    └── post_2.html

The way it does it’s magic is pretty simple. When we run rake, it looks up all the posts in the “posts” directory, excluding the ones that start with the WIP keyword. Then, for each one of them it checks if it has been exported to HTML, if they exist on the actual blog and if the modification date on the local machine is older or newer than the one on the server. Based on this information, it will create, update or ignore a post.

So the only thing I need to do to create a new post is to write it’s org file and run rake.
If I want to make an edit, I just edit away and, again, run rake.

Easy enough.

Rake

I’ve taken quite a bit of the code from RubyTapas. Avdi Grimm have a series of episodes over there that shows various Rake concepts and I’ve shamefully stolen them.

Why Rake? Well, it has a very cool way of dealing with dependencies and saving itself a lot of work, so it’s an ideal candidate for this. It’s also a widely used tool in the Ruby community and this was a chance for me to learn by playing while making something useful (for me).

Org-ruby

For dealing with parsing org files I’m using org-ruby, an awesome and easy to use gem.

An OrgPost class was defined in order to read the org input file, parse it, capture some metadata and render it as HTML.

Here’s a slightly simplified version of this OrgPost class:

require "org-ruby"

class OrgPost
  attr_reader :file
  attr_reader :content

  def initialize(file)
    @file = file
    @content = File.read(file)
  end

  def parser
    @parser ||= Orgmode::Parser.new(content)
  end

  def html
    parser.to_html
  end

  def title
    parser.in_buffer_settings["TITLE"]
  end

  def categories
    keywords = parser.in_buffer_settings.keys.select { |k| %w[CATEGORY CATEGORIES].include?(k)}

    keywords.flat_map { |keyword|
      parser.in_buffer_settings[keyword].split(/\s*,\s*/)
    }.uniq
  end
end

Let’s take it apart bit by bit:

It receives a file path on it’s constructor and it uses it to extract it’s contents.

class OrgPost
  attr_reader :content

  def initialize(file)
    @content = File.read(file)
  end

  # ...
end

Then it defines the parser method, that memoizes a new parser. This parser only needs no receive the content on the constructor.

class OrgPost
  # ...

  def parser
    @parser ||= Orgmode::Parser.new(content)
  end

  # ..
end

In hindsight, this could have been done on the constructor, but the method came in from a later refactoring. I’ll probably change it at some point in favor of readability.

Then comes the HTML-export part of the process. Luckily, org-ruby makes this extremely easy to do.

class OrgPost
  # ...

  def html
    parser.to_html
  end

  # ...
end

Finally, two methods that extract some metadata to be used on the post creation

The first one extracts the title from the TITLE header option on the file. It uses the in_buffer_settings hash provided by the parser.

class OrgPost
  # ...

  def title
    parser.in_buffer_settings["TITLE"]
  end

  # ...
end

The second method extracts the categories to be set for the post. It uses either the “CATEGORY” or “CATEGORIES” header option. This sounds like adding extra complexity for complexity’s sake, but I really didn’t want to have to remember if it needed to be infinitive or plural… let just make the computer take care of that.

class OrgPost
  # ...

  def categories
    keywords = parser.in_buffer_settings.keys.select { |k| %w[CATEGORY CATEGORIES].include?(k)}

    keywords.flat_map { |keyword|
      parser.in_buffer_settings[keyword].split(/\s*,\s*/)
    }.uniq
  end
end

With that code, we’re now able to parse an org file and use some interesting metadata. Cool.

Let’s now try to connect to WordPress, shall we?

WordPress

For connecting with WordPress, I’m using rubypress, the same gem Avdi used on his RubyTapas episode.

The first thing we do is create a client by passing our blog credentials and storing it on a constant.

WpClient = Rubypress::Client.new(
  host:     ENV.fetch("WP_HOST"),
  username: ENV.fetch("WP_USERNAME"),
  password: ENV.fetch("WP_PASSWORD"),
)

Then, we can fetch the post list by calling

WpClient.getPosts(fields: %w[post_id post_name post_modified_gmt])

This will get the list of all the posts on the server, but only with the fields we need.

Then we can create or update posts by using the WpClient.editPost and WpClient.newPost methods.

Our own type of Rake task

Now that we know how to fetch our posts, we need a way to tell if what we have on our local machine is up to date in reference with the actual posts on the server. To achieve this, we’ll create our own special Rake Task:

class PostTask < Rake::Task
  def needed?
    !post_exist? || out_of_date?
  end

  def post_exist?
    !! post_info
  end

  def out_of_date?
    @prerequisites.any? { |n|
      application[n, @scope].timestamp.utc > timestamp
    }
  end

  def timestamp
    post_info["post_modified_gmt"].to_time
  end

  def post_id
    post_info && post_info["post_id"]
  end

  def post_info
    post_list.detect{ |post|
      post["post_name"] == name
    }
  end

  def post_list
    @post_list ||= WpClient.getPosts(
      fields: %w[post_id post_name post_modified_gmt])
  end

  def post_title
    name.capitalize.gsub(/[-_]+/, " ")
  end
end

Lets bisect this…

The only method we need to define for creating a Rake task is called needed?, and it is the one who is in charge of deciding whether the current task needs to be run. In our case, we want to run a PostTask only if the post doesn’t exist on the server or if it does, but our local copy is more recent.

For determining if the post exist on the server we define a post_exist? method, that will return true if we have data relative to the post on the post list, and false if it doesn’t.

Then, for deciding if the post is out_of_date, we scan the task’s prerequisites comparing their time stamp with the remote post’s time stamp.

class PostTask < Rake::Task
  def needed?
    !post_exist? || out_of_date?
  end

  def post_exist?
    !! post_info
  end

  def out_of_date?
    @prerequisites.any? { |n|
      application[n, @scope].timestamp.utc > timestamp
    }
  end
  
  # ...
end

Lets take a look at all those helpers we used.

The post_list method will get the list of posts from the server using the WordPress client (as previously explained).

post_info scans the post list comparing the post_name attribute with the task name and returns the first occurrence (or nil).

For the time stamp, we take the post_modified_gmt attribute from post_info.

class PostTask < Rake::Task
  # ...

  def post_list
    @post_list ||= WpClient.getPosts(
      fields: %w[post_id post_name post_modified_gmt])
  end

  def timestamp
    post_info["post_modified_gmt"].to_time
  end

  def post_info
    post_list.detect { |post|
      post["post_name"] == name
    }
  end

  # ...
end

Finally, we have two extra methods, one that extracts the post id (needed when creating posts)

class PostTask < Rake::Task
  # ..

  def post_id
    post_info && post_info["post_id"]
  end

  # ...
end

And the other gives us a default post title in case we doesn’t define one on the org file

class PostTask < Rake::Task
  # ..

  def post_title
    name.capitalize.gsub(/[-_]+/, " ")
  end

  # ...
end

Then, as a convenience, we define a post method that will create a PostTask for us.

We use the define_task method provided by Rake::Task like this:

def post(*args, &block)
  PostTask.define_task(*args, &block)
end

Putting it all together

I didn’t expect this post to get so long and explaining the dependency management will take another big chunk. Because of that, I’ve decided to do it in a follow up post.

I hope you got something useful from this experiment.

Saluti.