About This Blog: Custom Textile 16 comments

posted Wednesday, February 27, 2008 by topfunky

When I suggested that you write your own blog software, I had no idea that some people would actually take me seriously!

So here’s another tidbit to help you on your way: custom Textile tags.

The Feature

As a developer, it’s nice to use Textile or Markdown for text entry. Simple tags will be converted to HTML, which will save typing and automatically apply some typographic rules.

RedCloth 3 Logo
Figure A RedCloth 3 Logo

But it can go one step further if you’re using RedCloth. You can subclass RedCloth and add methods that define your own tags. This is a great way to add shortcuts for complicated sets of HTML or inject hooks for CSS and Javascript.

To create Figure A, I used a technique mentioned by Garrett Dimon in a Digital Web article. It involves a div enhanced with a few CSS classes, enclosing a caption and an image. In addition, I have the power to make it small, medium, or large. A more complete solution would even allow for alternate backgrounds and placement.

But that’s a lot to type every time I want to reference a captioned image! Instead, I wrote a custom Textile tag to do it for me.

Usage

Here’s the custom tag I use:

figure(med). RedCloth 3 Logo | redcloth3-title.jpg

Let’s break it down:

  • figure is the custom tag name. It’s a hook into a textile_figure method in my application’s subclass of RedCloth.
  • med is a CSS class that will be applied to the div that wraps the figure. This gives me easy control over the size of the figure, and could even be used to pass multiple classes for other effects.
  • Next comes the caption. I chose to use a pipe as the delimiter between the caption and the image name.
  • It ends with the name of the image.

In that single line, I have all the information I need to construct a figure. Those 50 characters will result in over 200 characters of HTML. I can also use a few lines of Ruby to assign an auto-incrementing figure letter (“Figure A”) and reuse the caption as an alt tag for the image.

Implementation

First, choose a color for your subclass. CaneSugarBrownCloth? MintJulepGreenCloth? It could take hours.

Next, create a file for your subclass. I put yellow_cloth.rb in the lib directory and required it from environment.rb.

unless RedCloth
  require 'RedCloth'
end
class YellowCloth < RedCloth
  # ...

Next, define your custom method. I have a simple ruby tag that applies a CSS class for use with my code highlighter.

def textile_ruby( tag, atts, cite, content )
  # TODO Use the arguments to build some HTML 
  #      and return a string.
end

The arguments passed in can be reused or ignored.

  • tag is the name of the tag passed in (ruby in this case).
  • atts is a pre-assembled string with passed CSS classes, IDs, hard-coded styles, and language identifiers. If you want to augment this, you’ll need to parse the string and append your own CSS classes (I did this for the figure tag).
  • cite is rarely used.
  • content This is the actual code, text, or paragraph that follows the tag. In the figure tag, I decided to parse this further and use the elements as the caption and image name.

The figure tag is more complicated.

  • I add a figure class to any CSS classes passed in. In order to do this, I need to parse the atts and extract existing CSS classes.
  • I split the content on the pipe and use the elements as the caption and image name. You could prepend a filepath onto the image name if all your images are stored in the same place.
  • The figure letter is dynamically incremented (Figure A, Figure B, etc.).
def textile_figure(tag, atts, cite, content)
  span_class = "" 
  if atts =~ /class="([^\"]+)"/
    span_class = $1
  end
  (caption, img_url) = content.split("|").map { |w| w.strip! }
  figure_name = "Figure " + @figure_counter.chr
  figure_id   = figure_name.downcase.gsub(" ", "-")
  @figure_counter += 1
  # TODO Construct the HTML
end

Finally, here’s the code to setup the counter:

def initialize(*args)
  @figure_counter = ?A
  super
end

To use your new class, you can override the built-in textilize helper or write a before_save callback on your model to convert the text to HTML with your custom subclass.

before_save :textilize_body
def textilize_body
  self.body_html = YellowCloth.new(self.body).to_html
end

The entire code can be downloaded: yellow_cloth.rb

Bonus

Pipe Text
Figure B Pipe Text

If you use TextMate, you may be aware of the “Show Web Preview” menu item.

It can pipe the text of the current document through its own textile.rb script or through your custom script. I wrote a simple script that uses my subclass instead of RedCloth.

# script/textile.rb
require 'rubygems'
require 'yellow_cloth'
textile = ARGV[0] ? File.read(ARGV[0]) : STDIN.read
puts YellowCloth.new(textile).to_html

Then use the full path to this script as the argument to ‘Pipe text through’:

/Users/topfunky/repos/funkyblog/script/textile.rb

Product Placement

I’ll be speaking at both RailsConf Portland and RubyFringe.

After many requests, I’m proud to introduce the new PeepCode Unlimited plan that gives you a year of access to all PeepCode content. On sale for only $139 until March 21.

Merb is a great compliment to Rails or a capable web framework in its own right. A draft of the PeepCode PDF on Merb is now available (includes a free upgrade when the final is published in the next few weeks).

PeepCode has been growing quickly and we have a ton of new content in the works. Ryan Daigle’s Rails 2 PDF recently became the best-selling PeepCode product of all time. Cody Fauser’s ActiveMerchant PDF is already being called a must read.

16 comments

Leave a response

  • Given the number of good blogging engines out there, I was skeptical of your post about building your own blog engine.

    But lately at work I’ve been building one for our product Tumblon and it has been an interesting, rewarding experience—especially trying to build something that’s easy for people to use.

  • Your post certainly inspired me to create my own blog/CMS running on Slicehost. It’s quite interesting to see how many things I didn’t need in blog software. The only two things I would like to implement are future-posting and categorisation of posts. But both of these can wait for the time being.

  • Gravatar icon Mikko Lehtonen

    But if you happen to post a mega-article which more than 25 or so figures, the figure labels start getting interesting! ;)

    I would of used:

     # In init
     @figure_counter="A" 
     # In tag handler
     @figure_counter.succ!

    Yielding somewhat coherent Z, AA, AB figures, and would work with numbers just as well ;)

  • Great article, nice to see that RedCloth is easily extensible.

    How would one go about creating custom inline tags? The method above only seems to work for tags on their own line.

    Thanks.

  • This is going to be very useful. I especially need help with my html coding. All that I do seem to fail on me because I do not follow instructions clearly.

  • Gravatar icon topfunky

    @Mikko Yes, it appears that my figure numbering solution does not scale!

  • For a client project we needed to build a friendly way to allow copy editors add various foreign characters in an easy manner so we did things like:

    For example:

    • (e’) would render é
    • (n~) would render ñ
    • (?) would render ¿
    • (C=) would render € (character for the euro)

    ..etc.

    Textile provided a nice starting point and we were able to easily extend this to provide this.

  • Gravatar icon mrb

    I also wrote my own blog software because of your recommendations. Thanks, it was a great experience!

  • D’oh! Why didn’t I think of this?

  • I tried writing my own … nearly got divorced, gave up and used Typo!

  • @Danners: You make this old typo maintainer very happy.

  • Hi! Really interesting. Thanks for sharing all this valuable knowledge. I read many good books (also your PDFs) and now I’m trying to write my own blog engine. Not an easy task.

  • This is great. I’d love to see some examples using Markdown instead of Textile!

  • Has anyone tried to extend the latest stable RedCloth gem? Maybe I’m just doing something dumb, but it appears that it breaks YellowCloth:

    # Using RedCloth version 3.274
    
    require 'RedCloth'
    => true
    
    class YellowCloth < RedCloth
      def textile_ruby( tag, atts, cite, content )
        %(<pre><code class="ruby">#{content}</pre>)
      end
    end
    => nil
    
    YellowCloth.new(‘ruby. def blah()’).to_html
    => ”<p>ruby. def blah()</p>” 
    

  • Gravatar icon James King

    @Chris: I have the same problem. I’ve been using the same technique for a while but my YellowCloth class doesn’t work with the latest build either.

    I think the latest RedCloth gem (v3.290) is an extensive re-write to the previous versions and isn’t as easily extended as the version used in this article.

  • Just a quick follow-up on my previous comment. Those people using the latest version of RedCloth (v3.290) who wish to used customised textile markup and HTML output might be interested in this thread.

    http://www.ruby-forum.com/topic/149632

    I think Geoffrey’s technique works best in version v3.0.3 or v3.0.4.

Your Comment

Nuby on Rails

Geoffrey Grosenbach / Ruby / Code / Graphics / Design / Rails / Merb / Javascript / CSS

Ads by The Lounge

Manufactured with

Subscribe

Subscribe (RSS)