Less method_missing in Markaby #
In the new Markaby branch, I’ve got a list of valid tags and attributes generated from the XHTML 1.0 Transitional and XHTML 1.0 Strict DTDs. Which means Markaby can validate as you go.
>> div :styl => 'margin:10px' do >> h1 "Never To Be Seen" >> end Markaby::InvalidXhtmlError: no attribute `styl' on div elements >> salect :onclick => 'javascript:alert("GIMMICK!")' do >> option '- please -' >> end NoMethodError: no such method `salect'
However, there is no check right now to make sure that the nesting is right. It’ll let you put a tbody
in a div
, for example. But it’s a start.
The implicit stringification of tags and helpers is working fantastic now as well. Can ERB compete with this (from CampSh [103]):
strong(author) + " worked on: " + cmds.map { |cmd| a cmd.name, :href => R(Show, cmd.name) }.join(", ")
There’s little need for the capture
statement any longer. If you need to force a capture, call to_s
on the HTML fragment.
div { @a = small('Miniature').to_s @b = strong('Large').to_s h1 "Monkeys" h2 { "Giraffes #{@a} and #{@b}" } h3 "Donkeys" h4 { "Parakeet #{@b} as well..." } }
It’s not needed in the above example, though, since the string interpolation will call to_s
for you. Does that make sense?
Justin
And here I’ve been thinking the small tag was deprecated (it ain’t).
FlashHater
OK, what exactly is CampSH? It’s never seemed to do anything for me. At all.
RyanA
Oh… Validate as you go feature… that’s very nice!
jm
Is there any easy way to restrict method calls to only Markaby tags?
For security reasons – like if you wanted to grant customer access to markaby view templates without them being able to plug in any ol’ arbitrary ruby?
why
That’s a good question. The primary problem being that top-level modules are still addressable from a closure anywhere in script.
It can be done, but you’ll need do some serious sandboxing: removing all possible harmful methods and modules, then executing their script inside a Thread with heightened $SAFE.
I mean you’re still going to need some Ruby around. (Time.now, string and array methods, etc.)
MenTaLguY
Nice job on the validity constraints, why! Do good for the cause of validation everywhere.
MenTaLguY
As far as sandboxing goes, I still haven’t been able to figure out a robust solution that didn’t involve setting up a whole other separate Ruby interpreter with a gutted environment (as TryRuby does).
I think everyone would like something like TCL ’s interp stuff, which as far as I can remember are basically all in the same environment but names resolve differently inside each.
To do something like that in Ruby, it looks like you can hack the name part of things pretty easily in variable.c, and then elsewhere make those “namespaces” selectable on a per-thread basis. The problem is that you still have to cope with things like “who is rb_cObject to-day?”
MenTaLguY
Well, alternately to having different rb_cObjects in different “namespaces”, you could have ivar tables look different on a per-namespace basis.
That’s got to be simpler than swapping around rb_cObject. Now, to figure out how to make the overhead not suck…
why
Yeah, it’s tricky, but I think it can be done. If you eval within a binding where everything is a proxy object, it’d be a bit slower, but you could watch objects pass in and out and make sure there’s no reference to any thing in the actual environment. It’d have to be in a C extension, though, since you can’t hide somethings.
The rb_cObject point hits it though. I mean if there’s anyway to get a handle to that, you’re sunk.
jm
Wow, great response.
OK, what about a psuedo markaby where the whole template looks/acts like real markaby but is really interpreted by another process. Then when a decent sandboxing exists, make the switch and all the templates still work, users will be none the wiser and champagne will flow forth, etc.
MenTaLguY
why: no, you can’t proxy everything. If nothing else,
NIL_P()
needs the realQfalse
andQnil
to work with.But you’ve also got to deal with the various sorts of Ruby expressions that create objects without an intervening method call. If someone creates an array and gets ahold of the real rb_cArray or rb_cString (or rb_cProc!), that’s just as bad really.
I’m becoming certain that only solution is to have rb_cObject, rb_cArray, rb_cString, etc. offer different constants and methods depending on which “partition” you’re in.
It’s not hard to see how to make this work—just add an interface from variable.c to create and select a current “partition”, and then make the notion of a “current partition” part of the thread state. Threads inherit the partition of their parent until the link is severed and they receive a copy to modify.
Thoughts?
MenTaLguY
Well, hmm. So we create a fresh object in one partition and pass it to another partition. Will that work? It’s got a fresh blank ivar table in the other partition…
MenTaLguY
I guess the only problem is if certain ivar entries are expected to always be there by the Ruby runtime itself? Are there any?
MenTaLguY
Ah, but how are proxy objects supposed to work? I’ll have to think about this some more.
why
Okay, so each sandbox represents a new, blank symbol table. Eval is hacked to point to that symbol table. Remember: all the default VALU Es (rb_cObject, rb_cString, etc.) are just numbers. So it’s okay if those get passed around, so long as eval always hits the sandboxed st_table.
I need to do some studying on rb_class_tbl and sym_tbl. I think the latter could be shared.
MenTaLguY
Hmm, that sounds promising.
Mathieu
I wrote the word ‘bacon’ on every single page of my year 9 diary.
MenTaLguY
One more thing to think about—isthis sandboxing stuff similar to selector namespaces? Maybe it should be approached as a generalization of them?
MenTaLguY
I guess something else to think about is how to bootstrap an empty partition/namespace/sandbox. I do think the ability to create one that is totally empty is really important to be able to use it for sandboxing.
hogepodge
I might be a bit dense, but I can’t for the life of me get markaby to validate the headers that it wants to generate. I patched, pleaded, and cajoled and markaby gives up on xml:lang, then meta, then me. Any suggestions? Should I just hurry up and wait?
carmen
yeah i had to tweak this version for Raelian purposes..
- tag!(:html, :xmlns => “http://www.w3.org/1999/xhtml”, “xml:lang” => “en”, :lang => “en”, &block)
otherwise it was devalidating its own header! + tag!(:html, :xmlns => “http://www.w3.org/1999/xhtml”, :lang => “en”, &block)
carmen
and set the metatag and xml_struction config keys to False (in the source code, since it didnt seem to be picking up on environment.rb)
maybe i just dont know enough about this XHTML thing, can you start a doc with or something instead?
why
carmen, hogepodge: Okay, the branch is updated. I think xml:lang wasn’t part of xhtml 1.0. Anyway, give it a try.
why
I take that back. Namespaced tags aren’t policed anymore.
_ugly
how would i write a helper method that looked something like this:
foobar do h1 “here” end
and produced:
here
_ugly
that ‘here’ was supposed to be a h1 tag enclosed in a div tag. i wasn’t using fancy stuff right.
so how can i produce some markup with #foobar and have the given block produce some markup inside of #foobar’s markup?
dogberry
why
Good job, dogberry. _why feeds dogberry a handful of catraisins.
enn
Maybe I’m missing something, but shouldnt it be…
:table => Attrs + [ :cellpadding, :cellspacing, :border, :width, :frame, :rules ]
to satisfy: http://www.w3.org/TR/xhtml-modularization/abstract_modules.html#sec_5.6.
And actually, it seems to me that all elements should have AttrCustom…
zem
mentalguy, i was wondering about the selector namespace thing too. is there a writeup anywhere about the proposed implementation of selector namespaces in 2.0?
MenTaLguY
zem: not that I can find.