--
You received this message because you are subscribed to the Google Groups "Enlive" group.
To post to this group, send email to enliv...@googlegroups.com.
To unsubscribe from this group, send email to enlive-clj+...@googlegroups.com.
For more options, visit this group at http://groups.google.com/group/enlive-clj?hl=en.
On 28 February 2011 13:26, Christophe Grand <chris...@cgrand.net> wrote:
> Short term:
> * ship a 1.0 quickly
In your mind, what's keeping Enlive from going 1.0 right now? I have
only used it in simple applications so far, but in what it does
(transform HTML into HTML) it seems very feature-complete to me.
> * "automatic" (convetion over configuration) templates
This sounds interesting. Would you care to expand a bit on what you've
imagined so far?
Daniel
On 28 February 2011 13:26, Christophe Grand <chris...@cgrand.net> wrote:In your mind, what's keeping Enlive from going 1.0 right now? I have
> Short term:
> * ship a 1.0 quickly
only used it in simple applications so far, but in what it does
(transform HTML into HTML) it seems very feature-complete to me.
> * "automatic" (convetion over configuration) templatesThis sounds interesting. Would you care to expand a bit on what you've
imagined so far?
Very true.
> In this case I was providing a form instead of a function, is it hard to
> actually report that? Perhaps with fewer of the RuntimeExceptions in the
> msg?
It should be possible to add some argument validation by using
clojure.core/assert-args in some of the public functions and macros.
If you can point out the worst offenders in your opinion(s), I'd be
willing to give this a shot.
Daniel
Daniel
Validating input to the important functions/macros turns out to be
trickier than anticipated, mostly due to their interactions. For
example, it's easy to spot what's wrong with this line (error message
is new):
=> (sniptest "<p><a href='foo'>bla</a></p>" [:a] (set-attr :rel
"nofollow") [:p])
CompilerException java.lang.IllegalArgumentException: at requires an
even number of forms (selector-transformation pairs),
compiling:(NO_SOURCE_PATH:91)
But the same logic doesn't catch this similar mistake:
=> (sniptest "<p><a href='foo'>bla</a></p>" [:a])
IllegalArgumentException Key must be integer
clojure.lang.APersistentVector.invoke (APersistentVector.java:250)
Why? Because sniptest uses 'transform, and calling 'transform with
only one form will simply return the form unchanged. This is needed by
'snippet, 'clone-for and several other core macros. The only way I see
to add useful checking here would involve being more stringent about
what's allowed as a transformation, namely: Everything that looks like
a selector in the one-argument case of 'transform will trigger an
IllegalArgumentError and thus couldn't be used as a transformation.
This would include only vectors, sets, symbols (for node selectors)
and maps (for fragment selectors) -- correct?
> I've seen a few situations:
> - a form instead of a transformation fn, i.e.
> [:selector] (map foofn barlist)
> - a plain string instead of a transformation:
> [:selector] "foo"
It will be known only at runtime whether the form evaluates to a valid
translation or not. I've added some basic checks at runtime to give a
more meaningful message:
=> (sniptest "<p><a href='foo'>bla</a></p>" [:p] (map inc [1 2 3]))
IllegalArgumentException transform requires transformation to be an
IFn, not clojure.lang.LazySeq net.cgrand.enlive-html/transform
(enlive_html.clj:481)
> - wrong number of args to transformation fn
>
> [:selector] (fn [match x] )
The only way to have Enlive catch something like this would be
reflection, which I'd consider the wrong way to go. Since a function's
metadata contains the line it has been defined on (is this new in
Clojure 1.3?), it's now possible to find the function you're calling
with a wrong number of arguments.
> I appreciate most of these are a case of me making mistakes, nicer error
> messages, particuarly if line numbers would have permitted me to find these
> quicker in our growing and highly fluid code base.
I've pushed my first cut to GitHub:
https://github.com/danwerner/enlive
This is still very basic, but I'd be happy to hear whether this helps
your debugging efforts in practice, and what other cases should be
covered.
Daniel
> I've seen a few situations:It will be known only at runtime whether the form evaluates to a valid
> - a form instead of a transformation fn, i.e.
> [:selector] (map foofn barlist)
> - a plain string instead of a transformation:
> [:selector] "foo"
translation or not. I've added some basic checks at runtime to give a
more meaningful message:
Speaking of the number of forms expected by at, I plan to change it, I have already discussed it, so (now that 1.0 is out) here is my idea:
I want to make parallel (lockstep) execution of selectors the default, and sequential execution the rule.
--
You received this message because you are subscribed to the Google Groups "Enlive" group.
To post to this group, send email to enliv...@googlegroups.com.
To unsubscribe from this group, send email to enlive-clj+...@googlegroups.com.
For more options, visit this group at http://groups.google.com/group/enlive-clj?hl=en.
Another potential addition to Enlive: #'fill
The #'fill transformation would:
* for checkboxes and radios, check them if they are equal or present (contains?) in the argument
* on other input tags: fill the value attr
* on select tags, select options them when their value is equal or present (contains?) in the argument
* act as #'content on other tags -- or maybe differ from #'content in the way nil is treated (fill nil) would left the element untouched
feedback welcome
(#'fill is the first step on the way to automatic templates.)
Christophe
On 4 March 2011 00:01, Christophe Grand <chris...@cgrand.net> wrote:
>> 'snippet, 'clone-for and several other core macros. The only way I see
>> to add useful checking here would involve being more stringent about
>> what's allowed as a transformation, namely: Everything that looks like
>> a selector in the one-argument case of 'transform will trigger an
>> IllegalArgumentError and thus couldn't be used as a transformation.
>> This would include only vectors, sets, symbols (for node selectors)
>> and maps (for fragment selectors) -- correct?
>
> Even if it may seem like a corner case, a map or a set could be meaningful
> as a transformation.
> Ditto for symbols which may refer to a global or a local.
> The only one which can't ever denote a transformation is a lonely vector.
Indeed, that's true for #'at and #'transform since they have only
static code to look at, not the values that will be passed at runtime.
I've added a check for single vectors.
> Right now, I think validating that at expects one or an even number of forms
> would be already a nice addition.
Okay, I've added a few more checks in that vein.
> Speaking of the number of forms expected by at, I plan to change it, I have
> already discussed it, so (now that 1.0 is out) here is my idea:
> I want to make parallel (lockstep) execution of selectors the default, and
> sequential execution the rule.
That's good news performance-wise. I could imagine, though, that with
more complex layouts, something like this could feasibly happen: add a
class to an outer container div, then do something to a deeply nested
element within this div. If I understand lockstep transformation
correctly, this would lead to an undefined result in this case?
On the other hand, if a template becomes so complex that cases like
the above aren't easy to spot anymore, it would be best to split them
up into several snippets, which would introduce sequential execution
automatically and solve the problem.
>> > I've seen a few situations:
>> > Â - a form instead of a transformation fn, i.e.
>> > Â Â [:selector] (map foofn barlist)
>> > Â - a plain string instead of a transformation:
>> > Â Â [:selector] "foo"
>>
>> It will be known only at runtime whether the form evaluates to a valid
>> translation or not. I've added some basic checks at runtime to give a
>> more meaningful message:
>
> Ok for runtime checks as long as they are assertation (or pre/postconds),
> that is they can be disabled.
The original #'assert-args in clojure.core throws InvalidArgumentError
and is not disable-able, so I've modified the one in my branch to
behave more like #'assert.
Since they are runtime checks and have access to the eventual data
being passed in, I've added type checks as well. The definition of
#'node-selector? may not be quite right yet, though.
One case I wasn't yet able to cover is use of a LazySeq-returning
expression instead of a node fn. In which function/macro would the
argument check have to happen? The following finally breaks in
#'transform-loc:
(sniptest "<p>Foo" [:p] (lazy-seq []))
Note that I've re-done the previously pushed (buggy) commits, so
please 'fetch' and 'reset --hard' instead of 'pull'ing the branch.
Daniel
PS: Unit tests for the argument checks are disabled for now since I
haven't quite figured out how to test for exceptions thrown by macros.
Sounds great! HTML generation has been a weak point of all forms
libraries I've looked at thus far. It's often been neccessary to punt
on automatic generation and write the HTML by hand, so why not write
it all by hand and populate it via Enlive? This could get rid of nasty
boilerplate code such as:
{% for thing, label in thingies %}
<option {% if thing == selected_thing %}selected="selected"{% endif
%} value={{thing}}>{{label}}</option>
{% endfor %}
Daniel
Note that I've re-done the previously pushed (buggy) commits, so
please 'fetch' and 'reset --hard' instead of 'pull'ing the branch.
Daniel
PS: Unit tests for the argument checks are disabled for now since I
haven't quite figured out how to test for exceptions thrown by macros.
> already discussed it, so (now that 1.0 is out) here is my idea:> Speaking of the number of forms expected by at, I plan to change it, I have
> I want to make parallel (lockstep) execution of selectors the default, and
> sequential execution the rule.
That's good news performance-wise. I could imagine, though, that with
more complex layouts, something like this could feasibly happen: add a
class to an outer container div, then do something to a deeply nested
element within this div. If I understand lockstep transformation
correctly, this would lead to an undefined result in this case?