Markaby for Rails #
You know that WebPage code I’ve been playing with lately? Tim Fletcher e-mailed me a revamped version, a plugin for Rails. We’ve gone back and forth on this and found a very satisfying resting point, which I’m calling Markaby. Markup as Ruby.
To install in your Rails app:
script/plugin install http://code.whytheluckystiff.net/svn/markaby/trunk
To use it, add templates with a .mab
extension.
Examples
For example, an app/views/layout/application.mab
could look like:
html do head do title action_name stylesheet_link_tag 'scaffold' end body do p flash[:notice], :style => "color: green" self << @content_for_layout end end
As you can see all the normal helpers and variables are present. There is one caveat: in default Markaby, helper methods are automatically output when called. So, in the above, the stylesheet_link_tag
gets output. (To turn that off, use @output_helpers = false
.)
A scaffolding page could look like this (app/views/products/list.mab
):
h1 'Listing products' table.editor.classic do tr do for column in Product.content_columns th column.human_name end end for product in @products tr do for column in Product.content_columns td product.send(column.name) end td { link_to 'Show', :action => 'show', :id => product } td { link_to 'Edit', :action => 'edit', :id => product } td { link_to 'Destroy', { :action => 'destroy', :id => product }, :confirm => 'Are you sure?' } end end end link_to 'Previous page', { :page => @product_pages.current.previous } if @product_pages.current.previous link_to 'Next page', { :page => @product_pages.current.next } if @product_pages.current.next br link_to 'New product', :action => 'new'
As you can see classes can be assigned just like in previous experiments. The table.editor.classic
gets output as <table class="editor classic">
.
One really wonderfully trippy thing about Markaby is that you can use capture
to treat elements as strings, if you like. This satisfies one of Dgtized’s feature requests from a few days ago.
div.menu do %w[5.gets bits inspect cult -h].map do |m| capture { link_to m, "/#{m}" } end.join(" | ") end
So, in the above example, the output of link_to
is captured for each element of the array and then patched together with pipes between. But how does it get output to div.menu
?
The rule is: each element with a block opens a new buffer. You can fill up that buffer by printing elements to it. Or by returning a string from the block.
Markup Shortcuts
Really, there’s not much more to this at present. For example, RedCloth support isn’t built in, nor is the auto-header and auto-body stuff from WebPage.
Just a few shortcuts:
img
tags will default to:alt => '', :border => 0
.head
automatically includestag!(:meta, 'http-equiv' => 'Content-Type', 'content' => 'text/html; charset=utf-8')
.html
defaults to an xml instruction, the XHTML 1.0 Transitional doctype and:xmlns => "http://www.w3.org/1999/xhtml", "xml:lang" => "en", :lang => "en"
on the html element.xhtml_transitional
is an alias forhtml
.xhtml_strict
does the same, but with the proper XHTML 1.0 Strict doctype and so on.
If you don’t like any of this, override or undef the methods. So far so good?
Lastly, importantly: Rails 1.0 doesn’t support using Markaby for layouts unless you explicitly state the name of your layout in the controller. But I think Marcel has fixed this in Edge Rails.
Xian
It’s nothing a little getting of my ass and testing wouldn’t tell me, but does this work for partials called form inside normal rhtml templates?
why
Yes, this works for partials.
Dgtized
Mmm I like the capture result.
self << @content_for_layout is definitely cute.
Not certain about table.editor.classic resulting in “editor classic”, something about that smells wrong, but I can’t think of an alternate method of doing this either.
What happens if you def a function anywhere in there? They just get added to that particular instance of a tr or whatever correct?
Which means most helpers should be defined elsewhere as helpers. I assume that:is necessary?
I still think auto-css integration would make this the most awesome of awesome though.
Amr
Brilliant!
why
I agree on the auto-css, Dgtized. I’m just trying to figure out if we have hook into the rcss templates or if we’ve got to do our own thing.
riffraff
wow, this is soo similar to the html extension in cgi.rb, except this is side effectish.. :)
why
Right, the main differences between cgi.rb and Markaby are:
rather than
rather than
Ezra Zygmuntowicz
Very cool stuff there _why & Tim. I just saw the .mab extensions added to edge rails in the svn commit mail list. And can confirm that it does work with edge rails.
I’m a little unclear on what you mean about the stylesheet_link_tag and “in default Markaby, helper methods are automatically output when called” Isn’t that what you want? i mean if you put the stylesheet_link_tag there in the head don’t you want it to output there? I guess i am missing something there.
But this looks very cool. Soon we won’t have to write any html or any sql. Using this and my where plugin it all comes together in pure ruby. Take a peek at this syntax extension I am working on for AR quries:
why
Ezra: Yes, most helper methods are designed for output, so it’s the default.
However, there are still a few problems. Like this:
...doesn’t do what you’d expect. I don’t think most people want
human_size
ornumber_to_percentage
to output directly.topfunky
One step closer to the unified Rubiverse where everyone speaks Ruby and Unicode is no longer needed. Yay!
Paul
Is this completely handled by RAILS ?
Meaning, do I need to define media types for .mab in Apache and Lighttpd?
why
No, Rails does it. You just run the
script/plugin
line above. You don’t actually see the .mab extension in the URLs. It’s just the extension used on files inapp/views/
directory.Rapha
Is there anything special you have to do in order to make it work on Edge? The .mab files aren’t recognized here… (yes, I did svn up)
Rapha
why
Rapha: Make sure you don’t have similarly named .rhtml templates. An
edit.rhtml
will take precedence overedit.mab
. I also don’t know if you can mix template types (anrhtml
layout with amab
template.)You can assign IDs and classes with the hash syntax:
Identical to:
test
kxzlfksaldk
Rapha
Why: Thanks for explaining the syntax for id’s! I tried making a new `Test’ application and removed all RHTML files. Doesn’t work. When the plugin is installed, it works.
Btw., the above command (
script/plugin install http://code.whytheluckystiff.net/svn/markaby/trunk
) doesn’t appear to work. As a matter of fact, checking out markaby/trunk doesn’t work (please do tell if it’s my fault!). For the time being, I had toRapha
Trying to use Markaby here in my main application here now and have to say it rocks! The only thing that I see could use improvement is the form tag. Would it be possible to give it the same defaults as Rails’ form_tag(), i.e. action=”/controller/action” and method=”post”? Apart from that, it really does make your templates look much nicer than before. Thanks a lot for it!
Dave
_why: This is great! I’ve been looking for a slightly more programmatic template language for a while. I understand that it cuts out some of your average designers, but for me on my own it’s ok for now!
The only issue I’ve found is that the opening ‘head’ tag is missing in output. I have a head block, and the closing head tag is present at the right spot, but the opening tag is missing. I’ll see if I can find the problem here…
Also note that combining template types seems to work, at least for the mab as layout, rhtml as template scenario. The only thing you can’t do is pass the @content_for_layout variable as the parameter for a tag, since that’ll convert all the lt/gt signs in your html to their respective entities (among other things).
Dave
Well, the opening head tag is actually there, but hidden after my stylesheet/javascript tags. Since the helpers are outputting right away, they seem to get printed before the opening tag of the enclosing block. Are you able to duplicate this on your side, _why?
murphy
Markaby is great, thank you Tim and why!
I would really love to use it everywhere in my views/, but this is difficult. The problem with nested helper methods outputting everything twice is really annoying…especially frequently used idoms like
<%= h pluralize( users.size, 'user' ) %> online
are hard to translate correctly into Markaby. I wrote an article with a possible solution to the capture problem (excuse the poor layout, this is under construction.)Basically, I think Markaby needs to be less surprising; I still haven’t figured out why some strange errors occur, so I suggest to minimize the magic behind it. For example, it seems to html_escape arguments when tag methods are called without block…useful, but not always wanted.
Is it possible to report better error messages? Markaby templates with errors are hard still to debug.
murphy
About combining Markaby an RHTML : I use a Markaby view with an RHTML partial, and it works perfectly.
cyx
how do you output helpers if you turn them off?
ncv
How can I install Markaby on my own stand-alone machine without Internet. Thank you very much for your help. ncv
tim
cyx: either return them (a string) from a block like so:
or pass them as the first argument to a tag i.e.
pavel
Has anybody experience with Markaby performance compared to the Erb?
Peter
This could be a boost to having less html to hand code and more cool ruby code
jakdak
murphy – just read ure doc and it’s got my vote, – it makes sense.
i find the the capture method is a little unintuitive.
topfunky
Possibly the first Markaby-powered site in the wild…Ruby on Rails Workshops.
Uses Markaby mostly, but has a few rhtml partials.
I had success with using ’@output_helpers = false’ on the last partial in the tree, and using capture {} otherwise.
Michael Geary
Very sweet! I really like this programmatic style of generating HTML .
One suggestion: I find the code to be more readable with {} instead of do/end. The do/end blends in with the HTML tag names, while {} helps the tags stand out more.
For example:
vs.
why
Okay, so let’s get this resolved. You guys are using
@output_helpers = false
only because of stuff that you’re sending toh
, right?murphy, doesn’t the following work?
The capture method should only be used if you want to do a tricky loop, like the menu generation snippet in the post way up tippy top. It should be rare or never.
why
The bug with
head
is fixed.I’m also trying out scanning for helpers which are used as arguments.
So this works now:
However, this doesn’t:
So I think argument matching will only complicate things.
I wish there were a way to tell between helpers which output strings containing formatted HTML and helpers which output plain strings. The format should be output (
link_to
,start_form_tag
) whereas the latter should be held for questioning.murphy
I hope you don’t see my article as rant or something ;) but Markaby is so cool that it needs critic. I want it to be a success.
Not perfect. It doesn’t html_escape (not a real problem) but it also swallows the
'found'
. It feels a bit like narrowing my possibilities – it’s complicated to useh
here, so I omit it and hope XSS doesn’t bite me. ;) We all are lazy, ain’t we?It’s not that there are no other solutions, without switching
@output_helpers
off. But my intent is to make Markaby more simple, and less surprising. It does a lot behind the scenes to be shorter, and I see that my Markaby style can lead to longer templates. But I think it is easier to understand and handle for Nookabys.I’m always struggling with languages that tend to be too short – YAML , Textile and Markaby all have this fresh new beautiful look of shortness – but then, you find new exceptions on the other side. It seems to be a consequence: Whenever you try to make something shorter, you have exceptions which make it complex. Try Perl. If you are an experienced user, you are productive with it. If you are new, you’re lost.
Ruby is short, but tries hard to avoid these problems. Rails also does, in my opinion. And if we want to target on those users, we should make Markaby extremely simple to learn an maintain, even if we give up some neat shortcuts. Not everything. I love
div.class do
.Oh, I got that wrong. Then we all agree it should not be frequently used :)
What about adding
out
as an alias forself <<
?Rapha
If it helps… I’m a noob and like “self << ” better then “out”. Actually, it’s quite neat. But then, I’m a noob :-)
why
Right now
text
is an alias for<<
, but I thinkout
is preferrable. I will change it!I agree that
@output_helpers
is easier to understand, but having to do:Seems too verbose.
Another maybe-too-tricky thing… What if we have a version of
h
which turns off@output_helpers
and escapes output?murphy
@Rapha: thanks, I shouldn’t think I knew what noobs like. Original noob comments are certanly more helpful than me trying to guess what noobs think or like – in fact, it’s me who likes it better. We should make a noob election for every new feature for more democracy. Sorry.
Oh, I didn’t know
text
. This is also a good name. Is there a Markaby docu yet? Or some guide with cartoon foxes?Mmh…what about
This could be used if you turn automatic html_escaping off. Stack this:
Rapha
Now we have both …
out
andself <<
... :-) Isn’t that worth a celebration? Let the Champaign flow!Why, what about my earlier point of having the same defaults as the “old” Rails methods do? Like,
form
defaulting to<form action="post">
and so on?Btw, am I the only one who sees the comic foxes analogically to Monthy Python movies? (Read: incomprehensible to a certain breed of people—nothing against Why ;-) )
Rapha
The preview button shows
<
and>
as < and > when they’re enclosed by code tags, whereas the submitted comments do not. Just something I noticed…Rapha
Not sure, have I found a bug here?
list.mab:
item.mab:
output of first thing.inspect:
output of second thing.inspect:
This is just code that I translated from RHTML . In RTHML it works.
why
Rapha: You were right no local assignments were being merged. Please update your plugins. Thankyou!
jakdak
”.. there’s not much more to this at present. For example, RedCloth support isn’t built in”
What are your thoughts on putting Redcloth in ? Or would it ruin the rubiness of all ?
Rapha
Thanks Why! Just tried it out and it’s working perfectly now.
Still no comment on my sensible defaults for attributes-request though? :)
Alciato
Very nice plugin! It’s possible to include attributes from a helper function?
murphy
Just discovered that you don’t have to delete the .rhtml files to get the .mabs running. Rails prefers additional template handlers over the standard ones, see
ActionView::Base#pick_template_extension
.why
Markaby is in Gems for general use now:
why
Hrm, well, for now:
tim
Alciato: yes you can, e.g.
helper:
markaby:
result:
Alciato
Thank you, tim. I notice also that the select function conflicts with the Markaby select tag. When i use the select method as usual markaby tries to render as a tag and throws the following error `select’: can’t convert Hash into time interval
This works
and use select_mab in the views
...any alternative?
tim
Alternative would be to remove
:select
fromlib/markaby/tags.rb
– depends how often you use the two.murphy
The gem works now – thank you.
Paul
OK, Wheres the nooby users manual?
This is what Builder for HTML should be!
Dan
I’ve finally realized why I find this plugin so downright luscious. It decimates the shift-count of writing markup!
Looking at your first example (and substituting ’ for “) I find 9 shifts necessary, all but 1 of which involve inescapable ’_’ and ’:’ characters. I count 28 in the equivalent ERB markup (this all assumes more efficient use of the shift key than I normally make in my typing).
(The royal) we the anti-shift-while-typing-code community salute you! Were it not for my previous sentence, I would seriously be considering swapping – and _ on my keyboard.
rubynoobie
I’m very new to ruby. I like Markaby.
I’m trying to get the select tag to work in a form. Apparently I haven’t a clue (based on all the errors I get back from Markaby). Could someone please give an example of how to use the select tag and options? I can do this in RoR with actionview but I like Markaby better
Markby = less LISP (lots of Insidious Silly Parenthesis (or Braces, anglebrackets) for me to track.
thanks
jouy
thabks
Thijs
The select element gets in a fight with a select function somewhere. Just use tag! ‘select’ instead for now…
Thijs
Broken for Rails 1.1 RC1 ?
murphy
Should work now (revision 33)
Comments are closed for this entry.