Rails on Maui

Programming in Paradise

Testing Error Handling

| Comments

How do ensure that your application properly handles errors, especially when relying on third parties, such as payment processors? Is it easy to verify that the right things happen when the wrong things happen? Last week’s article Strategies for Rails Logging and Error Handling discussed some techniques to setup a good error handling strategy. Here’s some techniques to verify that your application does what you expect it to do when things go wrong. The key message is to check how your application handles errors, before your customers do.

Your Code Depends on Outside Systems (That Might Raise Errors)

Suppose you’ve created the super-duper Rails storefront application that takes online payments. You may even have some unit tests that verify the code. Then you get the dreaded call that customers are being charged twice and their orders are not processed. WTF?

It’s not entirely obvious how to verify proper error handling when outside systems fail, or even when odd errors are raised from your own code. Payment processing deserves some special attention because it’s a dependency on an outside service (the payment processor) and will typically require database updates based on the result of the payment processing. If you’re updating several tables, then you’ll want to use a transaction to ensure that all or nothing saves. While code review and manual testing are good first steps, you should consider a few extra steps with error handling for sensitive parts of your application.

Verification of Error Handling Strategy

Typically, error handling code is not well tested. It’s much more common to test the “happy path” of everything going right.

Let’s look at hypothetical example and some tests that can flush out some errors.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
class Order
  def purchase_cart
    error_message = nil
    Order.transaction do
      # self.user record has charge info, and self.total is the order total
      # PaymentGateway.charge returns either error_message if failed or charge_details if success
      error_message, charge_details = PaymentGateway.charge user, total
      # update the order and the user records with the charge_details
      set_charge_fields_and_save user, charge_details unless error_message # update the order to indicated purchased
      fulfill_order # do lots of complicated stuff to fulfill the order
    end
    error_message # return any error message if there is one
  end
end

So what can go wrong?

Payment Processing is Like a 2-Phase Commit

Conceptually, you want a transaction, such that it’s all or nothing. If the charge goes through, then so does everything else. Payment processing like a 2-phase commit, except one has to handle all the what-ifs to be sure that it’s handled correctly.

The general steps of payment processing are like this:

  1. Connect to outside resource to make charge.
  2. Update database records indicating charge successful.
  3. Fulfill the order.

Rails transactions work such than any exception in the block will cause the transaction to be rolled back. The problem with the above code is what happens if fulfill_order throws an exception? The customer has been charged, the order was updated to reflect payment, but then ka-boom and an exception is raised, and any database updates to the order are rolled back, but the payment is not refunded. The customer is confused as there is a charge but nothing else. How could you have tested (and avoided) this?

Brute Force Methodology

You can simulate error conditions by manually placing =raise “any error message”= statements in your code, and then testing, say in the UI manually. This is a good first step to verify that your error handling is working correctly. You might raise a specific error, if say your payment processor throws a specific type of error.

For the above example, the different methods referenced, such as process_order can get modified with a single line at the beginning, which would be:

1
2
3
4
def process_order
  raise "Any error message"
  # Lots of other code that can be commented out
end

Then go into the UI and test placing an order. Consider the following questions:

  1. Was the right error message displayed to the user?
  2. Was the right information logged at the correct log level?
  3. Was an automatic email sent regarding the error?

See my prior article Saner Rails Logging for the answers to #2 and #3.

By applying this technique to each of the components of completing a purchase, one can flush out (and handle) nearly all of the different possible errors that could affect a purchase. Give this technique a try in some critical section of the code. You’ll be surprised how well it works. Before giving you the fix to the above code, let’s see if we can write unit and feature tests on our error handling.

RSpec Unit Testing of Errors

It turns out that with stubbing in rspec, it’s easy to test error handling! RSpec provides a nice mocking library. The test code would look something like this. Pay attention to the call to stub.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
describe Order do
  describe "#purchase_cart" do
    context "process_order fails" do
      let(:order) { create :order } # factory_girl creation of order and related objects
      before do
        # The magic stubbing of every instance
        Order.any_instance.stub(:fulfill_order) { raise ArgumentError, "test error" }
        # The call to purchase_cart will first call 'charge'
        PaymentGateway.should_receive(:charge).and_return([nil, "charge_details"])
        # The error from within purchase_cart should do a refund
        PaymentGateway.should_receive(:refund).and_return("refund_details")
      end
      it "should throw an error" do
        expect {
          order.purchase_cart
        }.to raise_error
        order.reload
        order.purchased.should_not be
        # charge refunded verified in mock
      end
    end

This test code ensures that the error handling of purchase_cart will catch an error from fulfill_order, and properly refund the payment and rollback any changes to the order record.

Here’s an improved version of the Order#payment_method above:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
class Order
  def purchase_cart
    error_message = nil
    begin
      Order.transaction do
        # user has a credit card info, returns either error_message if failed or charge_details if success
        error_message, charge_details = PaymentGateway.charge user, total
        set_charge_fields_and_save user, charge_details unless error_message # update the order to indicated purchased
      end
      fulfill_order # do lots of complicated stuff to process the order, do this outside of the original tx, so that the payment info can be committed.
    rescue => e
      Utility.log_exception e # Unified strategy for error handling including email notification, see below
      refund_charge if charge_details # If there's an error here, then sys admins will have to manually refund the charge.
      throw e
    end
    error_message # return any error message if there is one
  end
end
Here are the key points to the improved code:

  1. There’s a block to catch the exception which is separate from the transaction block. The rescue properly handles the case of an a charge being made and needing to be refunded. Utility.log_exception will ensure that all the right things happen with this sort of error (see code for Utlity.logException).
  2. fulfill_order is moved outside of the transaction block. This allows the transaction to complete, and then the order_fulfillment takes place. If there’s an issue in fulfilling the order, that can be dealt with separately from the original charge. In other words, the customer can successfully pay for the order, and the store can deal with the failure to fulfill the order.

RSpec Capybara Feature (Integration) Tests of UI Errors

It’s possibly more important and sometimes easier to do the verification at the integration level in RSpec feature specs using Capybara with PhantomJs and Poltergeist. The secret sauce is the same use of the same stubbing technique as above to replace some key methods such that they throw an exception. This sort of technique works amazingly well to ensure that application will do the right then when an unexpected failure occurs, from the logging and emailing of the error message to the browser display to then end user.

I tend to develop such a test in an iterative manner:

  1. Make sure you’ve got tests on the “happy” case where the story goes as planned.
  2. Then introduce test cases where have bits of code like this that will raise an error at an opportune time.
1
     Order.any_instance.stub(:fulfill_order) { raise ArgumentError, "test error" }
  1. Allow the test cases to fail, and put in screen shots (in Capybara with phantomjs, that looks like this:
1
   render_page "a-descriptive-name"

Setup this method render_page in a spec helper file like this:

1
2
3
4
   def render_page name
     path = File.join Rails.application.config.integration_test_render_dir, "#{name}.png"
     page.driver.render(path)
   end

  1. Put in some assertions that the page shows the correct error and the records in the database have the right values.
  2. You can even

Here’s an example that tests a failure of the Stripe payment API, including verification that an email was sent signifying an error:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
  # using gem vcr to record http communication for faster performance
  let(:order) { create :order } # lots of setup in factory girl for non-purchased order

  scenario "Purchase cart, Strip payment error", :vcr do
    # Setup the stub -- the secret sauce to this test
    error_content = "Testing error handling exception message"
    PaymentGateway.stub(:charge) { raise Stripe::InvalidRequestError.new(error_content, 'id') }
    place_order
    page.should have_content error_content
    page.should have_content "Error purchasing"
    order.reload
    order.purchased.should_not be
  end

  def place_order
   login_as(user, :scope => :user)
   visit shopping_cart_path
   page.should have_selector('.total .price', :text => in_dollars(order.total))
   page.render_page("purchase-cart-1")
   click_link "CHECKOUT"
   fill_in_credit_card_info # utility test method to fill in credit card data
   page.should have_selector('.total .price', :text => in_dollars(order.total))
   render_page("purchase-cart-with-payment-info-2")
   click_on "PURCHASE"
   wait_for_spinners # method to wait for the busy spinner to stop
   render_page("purchase-cart-after-click-purchase-3")
   validate_error_emailed
 end

 # example of how you verify that an error was emailed
 def validate_error_emailed
   email = ActionMailer::Base.deliveries.last
   email.should_not be_nil
   email.to.should_not include(order.user.email)
   email.to.should include('whoever@gets-error.com')
 end

Conclusion

If you aren’t simulating how your application responds to errors, then you’ll eventually find out, and the result might not be as good as you’d prefer. You can simulate errors with the very simple and quick technique of a well placed =raise “some error”=, and then testing in a UI. Or you might prefer the robustness of unit or feature tests using stubbing. Either way, the key message is to check how your application handles errors, before your customers do.

Related Post: Strategies for Rails Logging and Error Handling

Strategies for Rails Logging and Error Handling

| Comments

TLDR: Clean logging and error handling is a critical aspect of a RoR app. What’s a good strategy? Why does this matter?

A Rails app can have awesome unit and functional tests, and then in production, something goes wrong and the right error handling does not happen, making a bad situation worse. By this, I mean, it’s bad enough that something went wrong in production. It’s even worse if:

  1. You don’t have clear log messages that identify exactly what went wrong.
  2. You didn’t get automatically notified via email that something went wrong. Instead, the customer told the customer service rep that there’s an issue. Ideally, when an error happens, the responsible developers should be notified.

Here’s some tips on logging setup and error handling, including a utility method to log the stack trace and send an email.

The next article, Testing Error Handling, will show you how to verify that your error handling strategy is properly implemented, includind with rspec tests.

Log Setup

Notification of any Exceptions via Email with Gem exception_notification

Check out the gem exception_notification. It works great. One things the docs don’t point out is that it works great with MailCatcher. This allows you to “test” that your exception notification emails are being sent as expected without using a real mail account. Thus, do enable exception logging in development mode, contrary to the basic setup. Here’s a config example at this post on MailCatcher and mail_view.

Log the Browser Details with Gem ‘browser_details’

The gem browser_details will tell you what type of browser was used, which can be very important when errors occur. I cracked up when I read this from the gem info page:

Have you ever had the conversation:

Your site doesn’t work. What browser are you using and do you have Javascript enabled?

What’s a browser?

Control Rails Log Verbosity with Gem lograge

Sometimes too much of a good thing (log info) is a bad thing, and that’s true with Rails default logging. Check out the gem ’lograge’. The big difference is that a single request will take a single line. To quote the README, instead of logs like this:

Started GET "/" for 127.0.0.1 at 2012-03-10 14:28:14 +0100
Processing by HomeController#index as HTML
  Rendered text template within layouts/application (0.0ms)
  Rendered layouts/_assets.html.erb (2.0ms)
  Rendered layouts/_top.html.erb (2.6ms)
  Rendered layouts/_about.html.erb (0.3ms)
  Rendered layouts/_google_analytics.html.erb (0.4ms)
Completed 200 OK in 79ms (Views: 78.8ms | ActiveRecord: 0.0ms)

After installing lograge, you’ll have one line for the request:

method=GET path=/jobs/833552.json format=json controller=jobs action=show status=200 duration=58.33 view=40.43 db=15.26

The one issue with lograge is that the default configuration does not log request parameters, which can be useful for debugging. This blog post, How to add request parameters to lograge logs, addresses that shortcoming.

Utility Method to Log Exceptions

This sample method Utility.log_exception takes care of logging an exception along with sending out an email notification.

Example of calling Utility.log_exception:

1
2
3
4
5
def my_method_with_error foobar
  do_something_that_raises foobar
rescue => e # catches StandardError (don't use rescue Esception => e)
  Utility.log_exception e, info: "called do_something_that_raises wihh #{foobar}"
end

Definition of Utility.log_exception:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
class Utility
  # Logs and emails exception
  # Optional args:
  # request: request Used for the ExceptionNotifier
  # info: "A descriptive messsage"
  def self.log_exception e, args
    extra_info = args[:info]

    Rails.logger.error extra_info if extra_info
    Rails.logger.error e.message
    st = e.backtrace.join("\n")
    Rails.logger.error st

    extra_info ||= "<NO DETAILS>"
    request = args[:request]
    env = request ? request.env : nil
    if env
      ExceptionNotifier::Notifier.exception_notification(env, e, :data => {:message => "Exception: #{extra_info}"}).deliver
    else
      ExceptionNotifier::Notifier.background_exception_notification(e, :data => {:message => "Exception: #{extra_info}"}).deliver
     end
  end
end

Strategy: Error Handling and Logging

  1. Avoid rescuing/catching if you can’t do anything with the exception. For example, in a model method, you might be calling that from a controller, but you also might be calling that from some scheduled job. Thus, it’s hard to say what the right action should be. A special case is calling raise without arguments: sometimes it is reasonable to catch all exceptions, logging the exception, and then re-raising it like it was never caught.
  2. If you catch an exception, consider if you should re-throw the exception because code at a different level will be able to handle the exception more properly.
  3. Consider how the code is being invoked, such as from a call to generate HTML or an ajax request, or maybe a batch job. All of these cases have very different needs for how the error should be handled.
  4. Be sure you understand the order of your rescue clauses matter. This article The Universe between begin and end provides a good explanation. Basically put the most specific exception types first and something like rescue => e last.
  5. Ruby does not support the concept of a “cause” with an exception. Thus, if you catch an exception and are going to rethrow a different exception, then it’s important to log the stack of the original exception, or else that information will be lost.
  6. Test the logging of the exception in both development and production mode. You want to ensure that any exception prints clearly regardless of Rails environment.
  7. A good way to test error handling is to temporarily put in raise ArgumentError (or whatever other error), and see how the exception is handled, both by the logger and the UI.
  8. The worst scenario is catching an exception and failing to log any messages. This can make troubleshooting a problem very tricky.

Verification and Testing

The next article, Testing Error Handling, will show you how to verify that your error handling strategy is properly implemented, and how to add rspec unit and functional tests on error handling.

Buy Apple Products With American Express

| Comments

Here’s a few tips on buying Apple gear and credit card extended warranties. The bottom line is:

  • Consider buying AppleCare with American Express to get an extended warranty that extends 12 months past AppleCare’s 3 years. That’s 4 years total! Visa and MasterCard do not extend AppleCare’s 3 years.
  • If you don’t need AppleCare, then try to buy with American Express, as you know you’ll still get an extra 12 months. Visa or MasterCard may or may not offer a warranty extension depending on your card.
  • Avoid buying AppleCare with a Visa or MasterCard if the card already has a warranty extension. You would get 24 months of basic coverage (one year original plus one year card). For the price of AppleCare, you get only an extra 12 months, although some will want the telephone question coverage of AppleCare.

Buying Tips

Buy products when they just come out, or wait until the next version comes out. It seems like Apple products don’t really go on sale, so you might as well get the newest stuff, or wait if you can. A good place to check is MacRumors Buyers Guide. That being said, if you need something, then you need it, so just buy it. If you’re not sure, then you can probably wait.

AppleCare

In general, you’d probably be off without extended warranties unless there’s something special about your situation that suggest one is worth it. Plus, if you buy with the right credit card, you’ll get one year extension, giving you a full years without paying a cent. Here’s an article on LifeHacker: Skip the Extended Warranty; Use a Good Credit Card Instead. I live on the beach, so everything tends to rust, so I tend to buy extended warranties for this reason.

American Express, not Mastercard, if you buy AppleCare

Both American Express and Mastercard offer warranty extensions of up to one year for using their cards. However, there’s a huge difference in their policies. American Express specifically states that they extend past supplemental warranty extensions like AppleCare. MasterCard explicitly says they do not. Visa is ambiguous. I quoted the details below. American Express will provide up to one year additional for warranties up to 5 years. MasterCard only goes to one year. Visa goes to 3 years.

Details (the fine print)

American Express

I’ve got the Costco True Earnings card. The details of the plan are here:EXTENDED WARRANTY DESCRIPTION OF COVERAGE. Kudos to the American Express for website for making it easy to find the information (unlike MasterCard).

Just to be sure, I found the spot which ensures that I get one year past when AppleCare ends:

Where a Loss has occurred during this Plan’s extended warranty time period of up to one (1) additional year, We will provide a benefit equal to the coverage of the original manufacturer’s warranty on warranties of up to five (5) years. We will pay up to the actual amount charged to Your Account for the product for which a Loss is claimed, but not to exceed $10,000. If the product also is covered by a purchased service contract, this Plan’s extended warranty time period begins at the end of the service contract and extends the original manufacturer’s warranty for a period of time equal to that warranty, up to one (1) additional year. If the combined coverage of the original manufacturer’s warranty and the purchased service contract exceed five (5) years, the product purchased is not eligible under this Plan and no coverage applies.

MasterCard

Your bank’s website will not have any information about any extended warranty. You have to go to MasterCard’s website: Guide to Benefits EXTENDED WARRANTY COVERAGE. MasterCard is the stingiest. If the product has more than a one year warranty, then MasterCard does not help.

> Extended Warranty doubles the original warranty time period and duplicates the coverage of the original manufacturer’s (or U.S. store brand) warranty up to a maximum of twelve (12) months on most items you purchase. For products with multiple warranty components, each warranty time period will be duplicated up to a maximum of twelve (12) months. Should you fail to properly register the original warranty as required by manufacturer, Extended Warranty will only double the actual warranty time period that you received from the manufacturer. An example of a product with multiple warranty components includes an appliance with original manufacturer’s (or U.S. store brand) warranties that differ for parts, labor, compressor, etc.

> If you purchase a service contract or an optional extended warranty of twelve (12) months or less on your item, Extended Warranty will cover up to an additional twelve (12) months after both the original manufacturer’s (or U.S. store brand) warranty and the purchased service contract or extended warranty coverage period end. If your service contract or extended warranty exceeds twelve (12) months, this coverage does not apply.

> If you do not have an additional service contract or an optional extended warranty, this Extended Warranty benefit commences the day after your original manufacturer’s (or U.S. store brand) warranty expires.

> If either the original manufacturer’s (or U.S. store brand) warranty or the service contract covers more than twelve (12) months, Extended Warranty benefits will not apply.

Visa

Here’s the Visa benefits for one type of card: VISA PURCHASE PROTECTION. Note, each Visa card may be different. Visa is a lot better than MasterCard in that if your product has more than a 1 year warranty, Visa still gives you an extra year.

Extended Protection Yes, as long as you purchased the item entirely with your eligible U.S.- issued Visa Business card and the eligible item has a valid original manufacturer’s written U.S. repair warranty or assembler warranty of three (3) years or less.

Octopress Setup With Github, Org Mode, and LiveReload

| Comments

WordPress seemed like a good blogging platform, but it just didn’t feel right. I spend all my day editing text files using vim key-bindings, and I love Org Mode for all non-coding writing. If you don’t know Org Mode, it’s like having Markdown mode on steroids. You can have a numbered list in Markdown, but org-mode lets you re-order the list, and that’s just the beginning. Editing blog documents in the WordPress editor felt almost as bad as being told to use MS Word. I found that ergonomics of Org Mode, including all the goodness of recent versions of Emacs, including Evil (Vim emulation), just made organization of creative thoughts so much more enjoyable.

So I bit the bullet one weekend, and dove into Octopress. You’re looking at the results of this endeavor, including my first Octopress article, and the latest tips on recreating this sort of blog using Octopress with Org Mode authoring, using LiveReload, and deployed at no charge on github.com.

If you used to writing real web applications, rather than know the intricacies of a giant monolithic blogging platform, then the customization of Octopress seems so much more straightforward. This is so much more like the Unix philosophy that so many of us love, which is small and modular, rather than monolithic.

I like Rob Dodson’s summary (noting Org Mode plus Emacs):

Octopress is a blogging framework written by Brandon Mathis (@imathis) which sits on top of Jekyll. Jekyll is a static site generator, meaning there’s no database associated with your blog. Instead of writing everything in a WSYWIG linked to MySQL (like Wordpress or Blogger) you produce text files using Markdown which are then converted to static HTML. There are 3 huge benefits to this approach. First, writing in Markdown [org-mode for Justin] is awesome. Once you learn the syntax it’s incredibly fast and you don’t have to spend time playing with a tiny little editor window just to add some style to your posts. Second, writing in your favorite text editor is also awesome. I produce everything in Sublime Text 2 [Emacs for Justin] and every day I discover new tricks to make the process better. If you’ve ever had to write a blog post using one of those horrible little TinyMCE editors you will appreciate this feature. And lastly, static HTML is fast.

I found it totally neat that I could embed markdown inside the org-mode document. See below for how this is done.

This article should be useful for:

  1. Any interest in using org-mode to publish to Octopress including some reasons I use Org Mode (with Emacs).
  2. Some explanation of what Octopress and git are doing.
  3. How to use LiveReload with Octopress and Org Mode.
  4. Anybody curious about how using free github pages works to host Octopress.

Basic setup

  1. Rob Dodson on Octopress: Start off with these instructions from this posting on April 30th, 2012. There are a few differences worth noting:
    1. You may wish to change the .rvmrc to a .ruby-version file
    2. Github recommends your deployment repository be named yourname.github.io, not yourname.github.com.
    3. After you run rake setup_github_pages and before running rake generate, you should run rake install. If you forget, there’s a clear message indicating this omission.
  2. Customize octopress/_config.yml. The yaml file contains descriptions.
  3. Update the DNS to use your custom domain if you wish: Github directions on setting up a custom domain
  4. At this point, you can create a post:
1
  rake new_post["my post name"]

Create a page:

1
  rake new_page["my page name"]
Generate and deploy:
1
  rake gen_deploy
Watch the site and regenerate when it changes:
1
  rake watch
Preview the site in a web browser:
1
  rake preview
See all the available rake options:
1
  rake -T
Save changes to source branch:
1
2
3
  git add .
  git commit -m "save changes to source"
  git push origin source

Some Perspectives on how Octopress Works

Posts

Posts are created using the rake task rake new_post[“Post Title”]. The key things about a post are:

  1. File is located in =source/_posts.
  2. File has a header containing the meta-data for the post. The post URL and date are determined by the by the title and date fields. If you want to change the date of your post, then you change the meta-data. Changing the file name is useful only for file navigation. Here’s a gist for a rake task to update the file names to match the metadata.

Pages

Pages are created using the rake task rake new_page[“Page Title”]. The key things about a page are:

  1. Files are located in =source/page-title
  2. File has a header containing the meta-data for the post. The post URL and

POW

POW allows you to point your browser to http://octopress.dev to see your local, unpublished Octopress website. It’s very convenient to not have to remember to run a local server, and it works great with LiveReload. Scroll to the bottom of this link for details on POW. The alternative to running POW is to run rake preview and then point your browser at http://0.0.0.0:4000 (or whatever port you configured).

LiveReload

LiveReload is a Chrome browser extension that will automatically refresh the browser after you publish your file. This works with or without POW.

1
2
   gem 'guard'
   gem 'guard-livereload'
  • Create a file called Guardfile containing something like:
1
2
3
4
     guard 'livereload' do
       watch(%r{public/generated})
       watch(%r{public/.+\.(css|js|html)})
     end
  • Start 2 shell tabs running these commands: rake generate && rake watch and guard
1
     > rake generate && rake watch

This screen shot shows watch updating the deployment files. start guard LiveReload

1
     > guard
This screen grab shows guard detecting the browser and telling the browser to update.

It’s neat to get LiveReload working with Octopress. However, the generation can finish after your page does a reload, so you won’t see your latest changes. I’ll update this blog post when I figure out a solution to that one. Until then, you may find it more convenient to manually refresh the blog page yourself.

It’s worth noting that if you’re running any other instance of guard- LiveReload, then one of these two copies will win and one won’t work. If you run a rails server this way, then this can bite you. It took me a bit of time to figure out why guard wasn’t working.

Org-Mode

You can skip this section if you’re not interested in org-mode. However, it’s super cool!

Why org-mode for blog publishing?

Org-mode offers quite a bit more than plain markdown. It’s quite the hacker’s delight for note taking and authoring of blog articles. Down below I list a few reasons why org-mode. Here’s a few org-mode features I love (Some are Emacs ones):

  1. All headers and list items can be reordered with minimal keystrokes (think super powerful outliner).
  2. Numbered lists.
  3. Editable tables in text editor, with movable columns, movable rows.
  4. Ergonomics of insertion of URLs and images.
  5. Includes the basics of markdown, such as source code blocks and much more.

Org-mode Integration

I found a plugin that automates the process of converting an org-mode document (.org file) in source/org_posts into a .markdown document in source/_posts. Once the markdown document is saved in _posts, the rake watch task picks up the change and deploys the file, and LiveReload can then automatically update your web browser. Neat!

Here are the basic steps:

  1. Follow the instructions here: Introducing Octopress Blogging for Org-Mode
  2. At the time of this article, April 27, there’s a bug with the latest org-mode. I posted a workaround. By the time you read this, you probably won’t need that tip.
  3. You can embed Markdown (or other Octopress/Jekyll directives) by embedding inside of a #+begin_html and #+end_html block.
  4. Images work fine. Well, almost fine. The big gotcha is that the standard inclusion of images in org-mode results in broken paths at deployment. The workaround is to embed the Octopress syntax for an image, and to place the images under source/images. Note, you’ll want to be sure to use an absolute path, or else your article might look OK on the home page, but might now work in the postings directory. If I’m creating a document with many images, I’ll group the images for that document in a sub-directory of images named like the document. This is how it should look. The trick is to place the octopress img directive within a HTML begin/end block.

  5. Links: Links for relative pages are simple.
    [[file:telecommuting.html][Why telecommuting works and how I maximize productivity]]
    

    Links from a page to a post can use a relative link like this:

    [[file:../blog/2013/04/27/octopress-setup-with-github-and-org-mode/index.html][here]]
    

    However, links from posts to other posts require an absolute URL. The big gotcha is blog posts due to the nested URL structure that does not map to the where you’re putting your org-posts. To avoid trouble, you need to follow a format as this example shows. It’s easiest to copy the URL from the browser address bar.

    [[http://www.railsonmaui.com/blog/2013/05/08/strategies-for-rails-logging-and-error-handling/][Strategies for Rails Logging and Error Handling]]
    

    I tried using a file:../blog syntax, but that either would work on the home page or in the specific blog article, but not both. http:/blog links would work in Chrome but not mobile Safari. Markdown to get parsed by Jekyll does not work if the link is within a block level html tag (i.e., a paragraph). Hopefully, one day we’ll be able to specify relative links from blog article to blog article.

  6. Bold styling was a bit of mystery using the standard theme. I had to add this line to _typography.scss:
1
   b { font-weight: bold; }

Useful Scripts

  • Just configure OCTO_HOME
  • Emacs tip: Visit the created file by placing cursor over file name and then hit Ctrl-x, f.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
export OCTO_HOME=~/octopress
ogen () {
  cd $OCTO_HOME; rake generate; cd -
}

osave () {
  cd $OCTO_HOME; git commit -am "Updates" && git push origin source; cd -
}

odeploy () {
  cd $OCTO_HOME; osave; rake gen_deploy; cd -
}

# this one is for orgmode only
opost() {
  cd $OCTO_HOME
  output=$(rake new_post["${1}"])
  new_file=$(echo $output | awk '{print $4}')
  base=$(basename $new_file)
  new_location=$OCTO_HOME/source/org_posts/
  mv $OCTO_HOME/$new_file $new_location
  echo created $new_location/$base
  cd -
}

opage() {
  cd $OCTO_HOME
  rake new_page["${1}"]
  cd -
}

Deploying to Github: Directory Structure of Octopress and the master and source Git Branches

Github offers free hosting of both the blog deployment and source. You’re looking at the deployment right now. You can find the source here at my git repo justin808.github.io. I doubt you could beat the price, performance, and convenience. You can look inside of this repo, clone it, etc. and you have everything that it took to make this blog.

I originally was quite confused by the concept of using two separate git branches to make up what gets deployed on the live website versus the git repository of my articles. Plus, there’s the issue of Octopress git repository that you clone when starting out. Eventually, I figured out that the two branches simply contain different files, with one containing the original Octopress files. Here’s a few screen grabs that might clarify the situation for you.

Don’t forget that you never push to the master branch, but rather the rake deploy task does it for you. Instead, you run git push origin source to push the content of your blog to github.

The octopress/public directory corresponds to what you’ll find on the github site for your deployment (master branch).

The octopress/.gitignore file contains entries like public, which essentially keeps the rake generate files out of the source branch.

Here’s the github master branch right after creation. Note the correspondence with public. This is what gets deployed as your blog.

Here’s the github source branch. This contains the octopress environment, as well as your customizations and blog posts.

Useful Links

  1. Getting Started with Octopress: Nice overall tutorial. Very current! March 2013.
  2. Rob Dodson on Octopress: Most of the instructions I show below are from this posting on April 30th, 2012.
  3. Joel McCracken on Octopress: Use Jekyll? You Really Should Be Using Octopress
  4. Github directions on setting up a custom domain
  5. dblock.org Article on Octopress: A good explanation from Jan 17, 2012, especially on the difference of the source and master branches.
  6. Introducing Octopress Blogging for Org-Mode: For org-mode. See below.
  7. 18 Months of Octopress: Nice article on why Octopress was worth the switch.
  8. Shell Aliases for Octopress: Save time with these shortcuts

Parting words…

Thanks in advance for any suggestions on this article. I hope you find it helpful. Check me out on Twitter: @RailsOnMaui.

Included file 'twitter_sharing.html' not found in _includes directory