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.
Wait, you don’t like BB Code?
...laughs.
this is great!
Thank you!!
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) } )
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?
Hahahaha, wtf! :)
Actually this is a really good post I’ve missed lately, so I should put this on Ruby Inside soon :)
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?
Bill: Because I don’t use MarkDown.
Umm, where did your SVN go?
It’s back! Damn! That won’t happen again (today).
Lovely, can’t wait to give this a go! Thanks!
how to protect from <script>alert(1)</script>
?
It won’t escape < and > by default. How to make it escape them?
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’).
Do I need to install redcloth?
Simply great and useful
thanks !
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.
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?
Yo, my bad doggie.
I didn’t sudo my gem install. Your plugin is fast and fresh!
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?
I’m using rails 1.2.3 and acts_as_textiled (svn 254) don’t work :(
How can I fixing?
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.
I can not install this plugin… throws back:
sh: svn: not found
flan: it seems that you dont have ‘svn’ installed (http://subversion.tigris.org/)
uninitialized constant Err::Acts::Textiled::ClassMethods::RedCloth
please tell me why?
sunshine, you have to install RedCloth before
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?
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…
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?
Hey everyone. I was having some trouble getting hard breaks to work in 2.0.2 (i.e. “Hello\nWorld” => “
Hello
” instead of “World
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!
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@
One more try on that code:
class RedCloth
def hard_break(text) text.gsub!( /(.)\n(?!\n|\Z| ([#=]+(\s|$)|[{|]))/, ”\\1
” ) if hard_breaks end
end
... I really wish this blog had a preview comment feature.
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
Chime in.