what is Enlive development status?

112 views
Skip to first unread message

Jevgeni Holodkov

unread,
Feb 28, 2011, 7:01:07 AM2/28/11
to Enlive
Hi all,

Is the development on the Enlive stopped? I checked https://github.com/cgrand/enlive
and the latest commit was more than half a year ago.. Am I looking at
the wrong place or now there is some other prefered way to go in the
web-dev landscape?

With best regards,
Jevgeni Holodkov

Christophe Grand

unread,
Feb 28, 2011, 7:26:47 AM2/28/11
to enliv...@googlegroups.com
Hi Jevgeni,

Since September I've been totally overbooked but Enlive is not abandoned.
I'm really longing recovering some capacity to work on OSS projects.

Short term:
* ship a 1.0 quickly
Mid term
* add proper XML support to Enlive (clojure.xml lazy-xml share most of the problems)
* modernize Enlive code (targetting Clojure 1.2, new zipper impl)
* change default mode from sequential to parallel ("lock-step") processing of selectors
* contribute Enlive to the new clojure.contrib (as discussed at the Conj)
Long term
* generalized Enlive (would work on any data structure)
* "automatic" (convetion over configuration) templates

Christophe


--
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.




--
Professional: http://cgrand.net/ (fr)
On Clojure: http://clj-me.cgrand.net/ (en)

Daniel Werner

unread,
Feb 28, 2011, 2:32:21 PM2/28/11
to enliv...@googlegroups.com, Christophe Grand
Hi Christophe,

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

Christophe Grand

unread,
Mar 1, 2011, 9:58:51 AM3/1/11
to enlive-clj
Hi Daniel,

On Mon, Feb 28, 2011 at 8:32 PM, Daniel Werner <daniel....@googlemail.com> wrote:
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.

One or two minor fixes to the tests and getting rid of the SNAPSHOT which annoys some people.
 

> * "automatic" (convetion over configuration) templates

This sounds interesting. Would you care to expand a bit on what you've
imagined so far?

Nothing set in stone yet but implicit iteration over sequential types and automatic (though parametrized) mapping between ids/names/classes and keywords in maps.
With no abysmal perfs.

Christophe

PS: your mail and patch about regex is still on my todo list.

Michael Ossareh

unread,
Mar 2, 2011, 1:42:58 AM3/2/11
to enliv...@googlegroups.com, Christophe Grand
Less ambiguous error messages?

It is a little too easy to cause something like:

java.lang.RuntimeException: java.lang.RuntimeException: java.lang.RuntimeException: java.lang.RuntimeException: java.lang.RuntimeException: java.lang.RuntimeException: java.lang.RuntimeException: java.lang.RuntimeException: java.lang.RuntimeException: java.lang.RuntimeException: java.lang.RuntimeException: java.lang.RuntimeException: java.lang.RuntimeException: java.lang.RuntimeException: java.lang.RuntimeException: java.lang.RuntimeException: java.lang.RuntimeException: java.lang.RuntimeException: java.lang.RuntimeException: java.lang.RuntimeException: java.lang.RuntimeException: java.lang.ClassCastException: clojure.lang.LazySeq cannot be cast to clojure.lang.IFn
at clojure.lang.LazySeq.sval(LazySeq.java:47)
at clojure.lang.LazySeq.seq(LazySeq.java:56)
at clojure.lang.Cons.next(Cons.java:39)


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?

Daniel Werner

unread,
Mar 2, 2011, 4:09:18 AM3/2/11
to enliv...@googlegroups.com, Michael Ossareh, Christophe Grand
On 2 March 2011 07:42, Michael Ossareh <oss...@gmail.com> wrote:
> Less ambiguous error messages?
> It is a little too easy to cause something like:

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

Michael Ossareh

unread,
Mar 2, 2011, 6:15:13 PM3/2/11
to Daniel Werner, enliv...@googlegroups.com, Christophe Grand
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"

 - wrong number of args to transformation fn
  
  [:selector] (fn [match x] )



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.
 

Daniel

Daniel Werner

unread,
Mar 3, 2011, 5:02:36 PM3/3/11
to enliv...@googlegroups.com, Michael Ossareh
On 3 March 2011 00:15, Michael Ossareh <oss...@gmail.com> wrote:
>> If you can point out the worst offenders in your opinion(s), I'd be
>> willing to give this a shot.

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

Christophe Grand

unread,
Mar 3, 2011, 6:01:36 PM3/3/11
to enlive-clj
Hi,

First, thanks for the effort.

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.

Right now, I think validating that at expects one or an even number of forms would be already a nice addition.

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.

BEFORE
(at :lockstep {sel1 transform1
               sel2 transform2})


AFTER
(at
  sel1 transform1
  sel2 transform2)


BEFORE
(at
  sel1 transform1
  sel2 transform2)


AFTER
(at
  sel1 transform1
  :>>
  sel2 transform2)

 
Discuss!



> 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.
 
Thanks again,

  Christophe

David Nolen

unread,
Mar 4, 2011, 8:38:03 AM3/4/11
to enliv...@googlegroups.com
On Thu, Mar 3, 2011 at 6:01 PM, Christophe Grand <chris...@cgrand.net> wrote:

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.
   

Seems reasonable to me and probably the behavior that most people are expecting.

David 

Christophe Grand

unread,
Mar 4, 2011, 8:55:45 AM3/4/11
to enliv...@googlegroups.com
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


--
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.

David Nolen

unread,
Mar 4, 2011, 9:01:36 AM3/4/11
to enliv...@googlegroups.com
On Fri, Mar 4, 2011 at 8:55 AM, Christophe Grand <chris...@cgrand.net> wrote:
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

Cool! :D

David 

Daniel Werner

unread,
Mar 4, 2011, 4:31:29 PM3/4/11
to enliv...@googlegroups.com
Hi,

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.

Daniel Werner

unread,
Mar 4, 2011, 4:49:27 PM3/4/11
to enliv...@googlegroups.com
On 4 March 2011 14:55, Christophe Grand <chris...@cgrand.net> wrote:
> 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

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

Michael Ossareh

unread,
Mar 4, 2011, 4:50:13 PM3/4/11
to enliv...@googlegroups.com
Note that I've re-done the previously pushed (buggy) commits, so
please 'fetch' and 'reset --hard' instead of 'pull'ing the branch.

I'll try to get to this soon, we're trying really hard to get a major release out of the door before... eerrr.. 2hours from now :)

 

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.

Christophe Grand

unread,
Mar 7, 2011, 3:38:03 AM3/7/11
to enliv...@googlegroups.com
On Fri, Mar 4, 2011 at 10:31 PM, Daniel Werner <daniel....@googlemail.com> wrote:
> 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.

> Speaking of the number of forms expected by at, I plan to change it, I have
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?

Nope, undefined results only happen when two (or more) selectors select the same node, not when they select two different nodes (even if one is an ancestor of the other).

Reply all
Reply to author
Forward
0 new messages