Embedding API Sandboxes in Documentation

Last week I released a jQuery plugin called API Sandbox to help developers of web apps expose their API in a guided sandbox environment. The usage is simple. On a template, make sure there is a dedicated div element available to place a sandbox and then simply call the apiSandbox function on it.

$("#user").apiSandbox("get","/api/v1/users?user_id=")

This would create a nicely animated sandbox environment with proper fields for the parameters expressed in the path (in this case, one for user_id). API Sandbox supports generic URL parameters at the end of the API path, in addition to symbol wildcards anywhere in the path, preceded by a “:”.

There are a bunch of applications for this, and I’m still making improvements to the plugin, but I want to talk about one particularly cool application today. This plugin combined with the latest version of Redcarpet (2.0.0b), a Markdown parser, can allow you to easily embed sandboxes inline with your API documentation.

What’s so cool about the new version of Redcarpet is that instead of simply relying on the plugin author’s interpretation of how Markdown should be transformed into HTML, you can specify your own rendering rules and just let Redcarpet do all of the parsing. To easily embed sandboxes, I chose to override Redcarpet’s default rendering of links and change links to API paths into dynamic sandboxes. I created a subclass of Redcarpet::Render::HTML and created override methods for link and doc_header.

class DocsParser < Redcarpet::Render::HTML
  def link(link, title, content)
    if link.include? "SANDBOX: "
      path = link.gsub("SANDBOX: ","")
      id = path.gsub(/[\/?&=\[\]]/,"")
      rendered = "<div id='sandbox'>"
      rendered += content + "<div id='" + path + "' class='sandbox " + id + "'></div>"
      rendered += "</div>"
    else
      link = link || ""
      title = title || ""
      content = content || ""
      rendered = "<a href='" +link + "'>" + content + "</a>"
    end
    rendered
  end

  def doc_header
    "<script>new App.Views.Docs({ el: $('#main_content') });</script>"
  end
end

All I did here was render a div element with the id of sandbox instead of a link when the path has a prefix of SANDBOX:, and then added in a script in the header of all generated documentation pages to load the script that changes the divs into sandboxes (the site I’m working on uses Backbone.js, hence the new App.Views.Docs).

Then you have to write an epic 2 lines of CoffeeScript code to turn all of the links into awesome API sandboxes.

$("div.sandbox").each ->
      $(this).apiSandbox "get", $(this).attr("id")

Not bad at all.

API Sandbox is just the first part of a CoffeeScript/SASS/Ruby API explorer I’ll be releasing over the next couple of weeks, so get excited.

Advertisements

Building Site Navigation with Markdown and Nokogiri

Who knew that Markdown — in my opinion the best text-to-HTML syntax available — could be used for something other than blog posts (or Github readme files)? Turns out that Markdown can be used as a powerful way to make web app navigation really simple to edit, even by non-developers. The task I set out to do was to turn something like this:

* Home
* About
    * About Us
    * API
    * Terms of Use
* Browse
* Etc.

into a nice dropdown menu. Making this markdown text available to a view is a fairly simple task. In Ruby on Rails, if you have a Page model that can hold Markdown text, you simply have to fetch it in the controller and call .to_html.html_safe on it. Also, with the help of some ternary operator elegance, you should make sure that if the page doesn’t exist, the code doesn’t blow up.

c_nav = Page.find_by_slug 'client-nav'
@client_nav = c_nav.nil? ? '' : c_nav.to_html.html_safe

So now, with the addition of a simple line in the view where we dump the contents of @client_nav onto the page, we’ve accomplished displaying a terrible looking unordered list when we could have just hard coded a nice looking menu in Haml. Here comes the fun part.

The obvious first choice for this task was to use jQuery to dynamically add CSS styles to the unordered list to turn it into a nice navigation menu. With the use of jQuery’s addClass method this is fairly simple, and it worked when I implemented it with just a couple lines of code. Unfortunately it resulted in annoying flickering as the page loaded because the unordered list is displayed un-styled for a split second (unacceptable). This could probably be fixed, but why bother doing something on the browser when it can be handled perfectly well on the server? After a couple of painful hours attempting to add classes to the generated HTML using Ruby string manipulation techniques and having little success, I discovered that this problem had already been solved by the creators of the Nokogiri gem.

To begin turning the raw HTML into styled goodness, I first stripped out the opening and closing ul tags manually. I enclosed the code to do this in a beginrescueend block because the test suite I’m using (Rspec) didn’t like me using negative numbers for indexes in a string for some reason.

begin
   @client_nav[0..3] = ""
   @client_nav[-5..-1] = ""
rescue
end

Then I let Nokogiri work it’s magic.

@client_nav = Nokogiri::HTML::DocumentFragment.parse(@client_nav)
@client_nav.css("li:root").each do |anchor|
   anchor['class'] = 'nav_item'
end
@client_nav.css("li:has(ul)").each do |anchor|
   anchor['class'] = "nav_item more"
end
@client_nav.css("ul:first-child").each do |anchor|
   anchor['class'] = "dropdown"
end
@client_nav = @client_nav.to_html.html_safe

Because the HTML I was passing to Nokogiri is just a fragment of an unordered list, I used the DocumentFragment.parse method to prepare the HTML for Nokogiri to dynamically add CSS. Then, using basic CSS selectors (as well as one really useful one — has() — taken from jQuery), I added the proper classes to the list elements. It worked like a charm.

I’ll leave the CSS magic up to you — suffice it to say that with the proper stylesheets, this can look really great and is infinitely configurable. Why bother with bulky client-side code when Nokogiri provides a just as (perhaps even more) elegant solution?