Err the Blog Atom Feed Icon
Err the Blog
Rubyisms and Railities
  • “acts_as_textiled”
    – Chris on August 09, 2006

    I really like Textile. I think everyone does. Even if not, I like to pretend everyone does.

    What I want is an easy way to turn Textile into HTML on the frontend. Rails, always the gentleman, has graciously provided us with textilize and textilize_without_paragraph helpers. That’s fine… but I want to display HTML in more places than I want to display the raw Textile markup. What if I forget to wrap my attribute in a textilize call? Isn’t there an easier way?

    Yes.

    M@ McCray has a rendered_column plugin which will render Textile (actually, a variety of useful formats) as HTML and save it to a special rendered_* column in your database. This leaves us using something like story.rendered_description. Okay.

    Thing is, I’m lazy and I don’t want to add columns to my database or do all that extra typing. I want story.description to come out as HTML unless I tell my code otherwise.

    Enter: acts_as_textiled. Observe.

    class Story < ActiveRecord::Base
      acts_as_textiled :description
    end
    
    >> story = Story.find(3)
    => #<Story:0x245fed8 ... >
    
    >> story.description
    => "<p>Err the <strong>Blog</strong>.</p>"
    
    >> story.description_source
    => "Err the *Blog*."
    
    >> story.description_plain
    => "Err the Blog."
    
    >> story.description = "Peter flippin' _Cooper_"
    => "Peter flippin' _Cooper_"
    
    >> story.description
    => "<p>Peter flippin’ <em>Cooper</em></p>"
    
    >> story.textiled = false
    => false
    
    >> story.description
    => "Peter flippin' _Cooper_"
    
    >> story.textiled = true
    => true
    
    >> story.description
    => "<p>Peter flippin’ <em>Cooper</em></p>"
    

    That’s really all there is to it. Now I don’t have to wrap all my story.description calls in the textilize helper, or worry about forgetting to do so when displaying the description in new places.

    But what about the create / edit form? Do I have to use story.textiled = false somewhere? Where?

    No worries. Everything is handled for us.

    <% form_for :story, @story do |f| %>
      Description: <br/> <%= f.text_field :description %>
    <% end %>
    

    This will display the unrendered Textile, such as Rails _security patch_, in the textbox and work as expected. The raw Textile markup is always stored in the database.

    We’re basically saying this: add the acts_as_textiled call to your model and forget about it. If you’re using form_for and no Textile helpers, everything will Just Work.

    Somewhere a voice is heard crying, “But the point of saving rendered HTML to the database is efficiency. You don’t want to be parsing Textile 100 times per page!” Yes, that’s true. But it doesn’t apply to me for two reasons. First, my small apps don’t really need efficiency. acts_as_textiled uses internal caching just like ActiveRecord so if I hit the same field multiple times on a page load, the Textile is only parsed once. Second, my big apps use memcached and acts_as_textiled works great with it. The rendered HTML is cached, not the Textile itself.

    I’m not saying anything against database-based implementations. Use them, they are good. But acts_as_textiled may scratch your itch.

    Okay okay, give us the goods already. To install:

    $ ruby script/plugin install \ svn://errtheblog.com/svn/plugins/acts_as_textiled

    Check the README for other tricks you can do. Enjoy.

    Update: Version 0.2 is out and hot hot hot. Thanks to James the Commenter for finding and destroying some bugs. Check the diff.

  • Dave Snider, about 17 hours later:

    Wait, you don’t like BB Code?

    ...laughs.

  • todd, 1 day later:

    this is great!

  • James, 2 days later:

    Thank you!!

  • James, 2 days later:

    Last try to get it to render right:

    BUG:

    >> story.description
    => “

    Err the Blog.

    ” >> story.description_plain
    => “Err the Blog.” >> story.description
    => “Err the Blog.”

    FIX:

    Change this line:

    define_method(”#{attr}_plain”, proc { strip_redcloth_html(__send__(attr)) } ) to this: define_method(”#{attr}_plain”, proc { strip_redcloth_html(__send__(attr).dup) } )

  • James, 2 days later:

    ANOTHER BUG:

    story = Story.new

    story.description

    TypeError: can’t convert nil into String

    FIX:

    Add to the end of line 15 (the line after define_method(attr) do):

    unless read_attribute(attr).nil?

  • Peter Cooper, 2 days later:

    Hahahaha, wtf! :)

    Actually this is a really good post I’ve missed lately, so I should put this on Ruby Inside soon :)

  • Bill, 12 days later:

    Textile is OK but I like MarkDown a teensy bit better. Why didn’t you make this acts_as_rendered and keep it a little more flexible?

  • Chris, 12 days later:

    Bill: Because I don’t use MarkDown.

  • Gabe, 15 days later:

    Umm, where did your SVN go?

  • Chris, 15 days later:

    It’s back! Damn! That won’t happen again (today).

  • Chuck Bergeron, 2 months later:

    Lovely, can’t wait to give this a go! Thanks!

  • javascript, 2 months later:

    how to protect from <script>alert(1)</script>

    ?

  • javascript, 2 months later:

    It won’t escape < and > by default. How to make it escape them?

  • bitbutter, 6 months later:

    Really handy this. Doesn’t play well with Localize Models though at the moment (‘localizes’ fields don’t get translated if they’re subsequently declared as ‘acts_as_textiled’, and act_as_textiled fields don’t get textiled if theyre subsequently declared as ‘localizes’).

  • Bill Dandy, 7 months later:

    Do I need to install redcloth?

  • Stephane, 7 months later:

    Simply great and useful

    thanks !

  • Andi, 7 months later:

    Great stuff! I have a suggestion though. What about something like this:

    story.description.to_s(:plain) or .to_s(:html).

    Instead of the underscore stuff? Would tidy it up a bit IMHO.

  • Doug E. Fresh, 8 months later:

    Phat plugin, G. It is beefing with me though.. I done installed this biatch just like you say, I even got down and installed redcloth gem, restarted Mongrel, but this sucka is telling me

    uninitialized constant Err::Acts::Textiled::ClassMethods::RedCloth

    What the schizzle?

  • Doug E. Fresh, 8 months later:

    Yo, my bad doggie.

    I didn’t sudo my gem install. Your plugin is fast and fresh!

  • pingala, 8 months later:

    hey the plugin doesn’t exist?? tumma@tumma:~/MyDev/chefit$ ./script/plugin install svn://errtheblog.com/svn/plugins/acts_as_textiled sh: svn: not found

    where to find it?

  • apaer, 10 months later:

    I’m using rails 1.2.3 and acts_as_textiled (svn 254) don’t work :(

    How can I fixing?

  • arikan, about 1 year later:

    Any proper clipping features? Since the text may include HTML or TEXTILE we want to clip it properly, not from the center of a [strong] tag.

    Does anyone do propper clipping for textile?

    Thanks.

  • flan, about 1 year later:

    I can not install this plugin… throws back:

    sh: svn: not found

  • dr_bonzo, about 1 year later:

    flan: it seems that you dont have ‘svn’ installed (http://subversion.tigris.org/)

  • sunshine, about 1 year later:

    uninitialized constant Err::Acts::Textiled::ClassMethods::RedCloth

    please tell me why?

  • g, about 1 year later:

    sunshine, you have to install RedCloth before

  • arigold, about 1 year later:

    hmm.. i’m getting this error too:

    uninitialized constant Err::Acts::Textiled::ClassMethods::RedCloth

    although i have redcloth installed and i installed it sudo. i get it when i restart the machine – my server is started as a cronjob. i have to kill and then restart the server (manually) and then it works… any ideas?

  • arigold, about 1 year later:

    for those who it can help, i’ve noticed something in regards to this problem

    i’m developing and using the app on a machine that gets rebooted quite often so i put the ‘ruby script/server’ in a cron job set @reboot. since i can get RedCloth to work if i reboot the server, i got rid of the cronjob and started it manually. no problem, it worked from the get go.

    which means – i think – that i (cron) was starting the server before RedCloth could load.

    which seems to mean – i think – that i just need to write a script that would check to make sure that RedCloth was loaded before running the server.

    hmm…

  • some1 forgot to close the bold tag??, about 1 year later:

  • Matt, about 1 year later:

    This is a great plugin, however I recently implemented it on a fairly active forum and after a few hours my mongrels start locking up with 100% cpu. I’ve tried a few different things and acts_as_textiled seems to be the culprit as far as I can tell.

    Anyone run into anything like this? Any suggestions?

  • Matt Hooks, about 1 year later:

    Hey everyone. I was having some trouble getting hard breaks to work in 2.0.2 (i.e. “Hello\nWorld” => “

    Hello
    World

    ” instead of “

    Hello World

    “). In the README it states that you can pass options to the attribute that you’re textilizing, like so:

    acts_as_textiled :body => [:hard_breaks]

    That’s all it takes!

  • Matt Hooks, about 1 year later:

    Oops, formatting got a bit messed up on that. You get the idea. Oh, I forgot to add, I added the following code to hard_breaks.rb in my initializers folder:

    @class RedCloth # Patch for RedCloth. Fixed in RedCloth r128 but _why hasn’t released it yet. # http://code.whytheluckystiff.net/redcloth/changeset/128 def hard_break( text ) text.gsub!( /(.)\n(?!\n|\Z| ([#=]+(\s|$)|[{|]))/, ”\\1
    ” ) if hard_breaks end end@

  • Matt Hooks, about 1 year later:

    One more try on that code:

    class RedCloth

    def hard_break(text) text.gsub!( /(.)\n(?!\n|\Z| ([#=]+(\s|$)|[{|]))/, ”\\1
    ” ) if hard_breaks end

    end

  • Matt Hooks, about 1 year later:

    ... I really wish this blog had a preview comment feature.

  • bbqnj, about 1 year later:

    hello, i noticed an issue when i put javascript code in a blogpost. the blogpost body that is acting like textiled, that contains some javascript links (basically a link_to remote), crashes the rails app to unusable. not sure what to do. thanks

  • Thirty-five people have commented.
    Chime in.
    Sorry, no more comments :(
This is Err, the weblog of PJ Hyett and Chris Wanstrath.
All original content copyright ©2006-2008 the aforementioned.