tag:blogger.com,1999:blog-41101802024-03-07T01:13:52.825-08:00Tapestry CentralThe inside scoop on what's happening with Tapestry ... from the creator of the Apache Tapestry framework. Plus all the normal, random thoughts on coding and technology.Anonymoushttp://www.blogger.com/profile/04486596490758986709noreply@blogger.comBlogger776125tag:blogger.com,1999:blog-4110180.post-83431771682076316142015-08-03T09:56:00.003-07:002015-08-05T14:55:17.969-07:00Seeking a New Clojure Engagment<p>My long-running Clojure project at <a href="http://aviso.io">Aviso</a> is ramping down now and I'm on the hunt for a new engagement ... preferably in Clojure.
<p>
I'm really thankful to Aviso for giving me the opportunity to spin up my real-world Clojure (and ClojureScript!) skills over the last two and a half years. I'm definitely in a position now where using Clojure feels like "cheating", compared to working in other languages I know and use. I know I have to work <i>much</i> harder in Java to accomplish the same goals.
<p>
I'm interested in either a long term engagement, or a full time position; in either case, it should be in Portland, Oregon, or support remote work.
<p>
I'm also very interested in providing Clojure training; I had a little preview of this recently at Uberconf, where Neal Ford and I presented an <a href="https://uberconf.com/conference/denver/2015/07/schedule">Introduction to Clojure</a>. I'll be using any upcoming downtime to create new Clojure training materials.
<p>
I'm also open to short-term "bootstrapping" engagements, to work with a small team interested in getting into the Clojure world. This could be a mix of training, mentoring, and prototyping.
<p>
Here's a few of our accomplishments, many of which have been open-sourced:
<ul>
<li>Created the <a href="https://clojars.org/io.aviso/pretty">pretty</a> library, which improves Clojure's dreadful exception output. It has been downloaded 227K times to date, and is also incorporated into the <a href="http://boot-clj.com/">Boot</a> build tool.</il>
<li>Created <a href="https://github.com/AvisoNovate/rook">Rook</a> -- sane, smart, fast, Clojure web services
</li>
<li>Created an amazing web application asset pipeline: <a href="https://github.com/AvisoNovate/twixt">Twixt</a></li>
<li>Built test tooling that leverages <a href="https://www.docker.com/">Docker</a> to start, stop, and maintain Docker containers (containing PostgreSQL databases).</li>
</ul>
<p>
Even with all the powerful developer features built into Tapestry, I feel even more productive in Clojure and ClojureScript. I'd like to share that with your team, in either a short-term or long-term relationship. Contact me!Anonymoushttp://www.blogger.com/profile/04486596490758986709noreply@blogger.com0tag:blogger.com,1999:blog-4110180.post-7736308847160581832014-09-29T16:10:00.001-07:002014-09-29T16:10:38.431-07:00Postgres, JDBC, Time Zones<p>I've been banging my head against the wall, trying to get things to work correctly with JDBC and PostgreSQL ... ah, time zones, every programmer's nemesis.</p>
<p>Does this seem familiar?</p>
<ul>
<li>Parse "1984-03-02" to a date, say <code>#inst "1984-03-02T00:00:00.000-00:00"</code></li>
<li>Insert that date into the database, as a PostgreSQL <a href="http://www.postgresql.org/docs/9.3/static/datatype-datetime.html">date</a> column</li>
<li>Read the value back from the database</li>
<li>Print it out and get <code>#inst "1984-03-01T08:00:00Z"</code></li>
<li>Curse!</i>
</ul>
<p>A little bit of digging shows that this is a pretty common problem unless both the client and the server are running in the UTC time zone. Now, it goes without saying that you are eligible for institutionalization unless you are running your servers in UTC, and that goes triple for your data store ... but the client? That really shouldn't matter.</p>
<p>Except it does; unless you use the full version of <a href="http://docs.oracle.com/javase/7/docs/api/java/sql/PreparedStatement.html#setTimestamp(int,%20java.sql.Timestamp,%20java.util.Calendar)">PreparedStatement.setTimestamp(int, Timestamp, Calendar)</a>, the PostgreSQL driver uses ... whatever the default client time zone is. Really, that's in the JDBC specification. So much for repeatable behavior!
<p>
My solution uses a dash of <a href="http://www.joda.org/joda-time/">Joda Time</a> (via <a href="https://github.com/clj-time/clj-time">clj-time</a>):
<script src="https://gist.github.com/hlship/34d93be1f292561a7ac8.js"></script>
<p>This hooks into the <a href="http://clojure.github.io/java.jdbc/">clojure.java.jdbc</a> library and extends the logic related to how a PreparedStatement is initialized for execution. It ensures the date or timestamp is interpreted in UTC.
Anonymoushttp://www.blogger.com/profile/04486596490758986709noreply@blogger.com4tag:blogger.com,1999:blog-4110180.post-38162907276122101272014-07-24T12:01:00.002-07:002014-07-24T12:01:38.109-07:00core.async article on Medium<p>
I've just started writing on medium.com, here's my first article, about a clever design I came up with involving core.async.
<p>
<a href="https://medium.com/@hlship/better-abstractions-with-core-async-1cd5fa509eca">Better Abstractions With core.async</a>.Anonymoushttp://www.blogger.com/profile/04486596490758986709noreply@blogger.com0tag:blogger.com,1999:blog-4110180.post-68307270093765668842014-06-04T14:41:00.001-07:002014-06-10T17:36:50.849-07:00Subtle nicety on defrecord<p>I just stumbled on a rather nice feature of <code>defrecord</code> that I've missed in the past; perhaps you have as well?
<p>I've been using Stuart Sierra's <a href="https://github.com/stuartsierra/component">component</a> library to manage startup of the server I'm building.
<p>A big part of component is the use of records. You define a record that stores your own data and have the record extend <code>component/Lifecycle</code>; this is provided to the component library and it will <code>assoc</code> in any dependencies before invoking the <code>start</code> method.
<script src="https://gist.github.com/hlship/4aa990d1febcbb13b0bb.js"></script>
<p>
Now, in practice, the values of the record's fields, <code>execution-mode</code>, <code>active-connection</code>, etc., don't change (at least, the component library doesn't change them). So the easiest thing is to just reference those symbols in the body of the <code>start</code> or <code>stop</code> method.
<p>
But what about other cases, where the field values may change? Do you need to to access the values via keyword? That is, if the field values are changed and you use the symbols, do you get the current value, or the value as passed to the record's constructor?
<script src="https://gist.github.com/hlship/5cf0206ead3796c9027d.js"></script>
<p>That will, of course, work ... but it's actually not necessary. From the <code>defrecord</code> documentation:
<blockquote>
Note that method bodies are
not closures, the local environment includes only the named fields,
and those fields can be accessed directly.
</blockquote>
<p>Our answer is right there in this typically too-terse description. The method bodies look like closures over
the <em>initial</em> values of the fields ... but they are not; they are translated into references to the actual fields.
<p>Here's a simpler version of <code>StateImpl</code>, with a simple experiment to prove its behavior:
<script src="https://gist.github.com/hlship/5761afbc1eeca8b005e8.js"></script>
<p>I haven't looked at the bytecode generated, but it appears that referencing a field in a method body is very similar to accessing a field in a normal Java object; the value retrieved is based on the <code>this</code>-like first parameter. In other words, it's designed to be very natural for Java developers. Even so, this behavior comes as a welcome surprise ... though I'm pretty sure any of the current books on Clojure have this information.
Anonymoushttp://www.blogger.com/profile/04486596490758986709noreply@blogger.com3tag:blogger.com,1999:blog-4110180.post-67482102610980114372014-03-28T12:52:00.001-07:002015-08-04T10:35:22.153-07:00Google Docs Thinks You are Offline? Work around their bug!<p>... and so suddenly, and without warning, Google Docs stopped responding for me. My spreadsheet (my invoice spreadsheet without which I can't get paid) would load and then immediately tell me "You are offline" (there's an icon for that).
<p>After trying all the obvious things ...
<ul>
<li>Clear my cache
<li>Try it in FireFox instead of Chrome
<li>Restart my computer
<li>Try a different Internet connection
</ul>
<p>... I started hunting around the web. The problem has been <a href="https://productforums.google.com/forum/#!topic/docs/5xOkq3677Ew">around since at least 2012</a>. This guy had a <a href="http://princeluck.blogspot.co.uk/2013/07/google-docs-you-are-offline.html">way to fix it that didn't work for me</a>.
<p>However, it did teach me about the Chrome's built in <a href="chrome://net-internals/#dns">DNS information page</a> where I could see <strong>0.docs.google.com ERR_NAME_NOT_RESOLVED</strong>.
<p>First I tried turning the Internal DNS client off (not on) ... that didn't help.
<p>Then I did a hack. First I <tt>ping</tt>ed docs.google.com and got <tt>173.194.33.32</tt> (due to the vagaries of DNS, you might get a different number).
<p>Then I added a line to my <tt>/etc/hosts</tt> file:
<pre>
173.194.33.32 0.docs.google.com
</pre>
<p>... which basically says: "don't use DNS, I've got your IP address right here".
<p><strong>Things seem to be working!</strong>
<p>My guess is that this represents some kind of load balancing solution implemented by Google Docs ... and either the 0 server has failed, or its configuration got munged, or this is simply a bug in the Google Docs client-side software.
Anonymoushttp://www.blogger.com/profile/04486596490758986709noreply@blogger.com0tag:blogger.com,1999:blog-4110180.post-53061901129720201202014-02-20T08:38:00.001-08:002014-02-20T08:48:26.723-08:00Gradle: overruling third-party dependencies<p>
We all hate dependency hell, and on any Java project of any size, you'll hit it eventually. My project, for <a href="http://www.aviso.io/">Aviso</a>, is a large Clojure code base, with tons of dependencies on third-party libraries, many of which are Java libraries with their own third-party dependencies. Despite using <a href="http://www.gradle.org/">Gradle</a> as our build tool, we don't get a free pass, we sometimes end up with conflicts.
<p>
It often plays out like this: module A as a dependency on library L, which has a transitive dependency on library Q. That's OK, module A has a consistent class path when it builds.
<p>
Meanwhile, module B has a dependency on library M, which has a transitive dependency on library Q ... but a different version. That's OK, module B also has a consistent class path when it builds.
<p>
However, inside IntelliJ, you see both version of library Q in the "External Libraries" folder of the Project explorer. That's unfortunate and can cause confusion when navigating your code.
<p>
Worse yet, in the final application, combining modules A and B, you will be executing one module with a different version of library Q than your tests. That alone makes me a touch nervous.
<p>
Fortunately, Gradle provides a quite reasonable way of dealing with this. The <b>hard</b> way would be to just turn off all transitive dependencies. But I consider that throwing out the baby with the bathwater.
<p>
Instead, we can selectively <em>override</em> transitive dependency, consistently across all modules. And we can do this in a <b>single place</b>, in our top-level <tt>build.gradle</tt>:
<script src="https://gist.github.com/hlship/9117680.js"></script>
<p>This one small change affects every child project; we have a single place to maintain and resolve these version conflicts and don't have to chase down which module (among the <b>37</b>
currently in our overall project) is the culprit for introducing a conflict. When we see a conflict, we add a new mapping to <tt>versionOverrides</tt> and we are done.
<p>This is a huge example of how powerful Gradle's Groovy DSL is; because the build script is also executable code, there's room to put logic in place that simply can't be defined declaratively.
<p>Our change hooks into the dependency resolution logic associated with each Gradle <a href="http://www.gradle.org/docs/current/dsl/org.gradle.api.artifacts.Configuration.html">configuration</a> (a configuration is essentially a way of declaring the class path for compiling, testing, or executing Java code).
<p>Gradle kindly exposes a step inside the overall process of analyzing the dependencies; this code hooks into this step. It sees the <em>requested</em> dependency, and if it's in the override map, forces the version number to a specific value. In fact, this mechanism is powerful enough to <em>replace</em> dependencies, but that's beyond our immediate needs.
<p>
This is one of the reasons I use Gradle in preference to Maven: Gradle has the tools to cleanly and easily address my specific problems and particular edge-cases.
Anonymoushttp://www.blogger.com/profile/04486596490758986709noreply@blogger.com4tag:blogger.com,1999:blog-4110180.post-55833368113256876512013-12-06T10:21:00.000-08:002013-12-06T10:22:25.318-08:00Tapestry Quicky: ConditionalComment<p>
Here's a quicky I just put together for a client to generate IE <a href="http://en.wikipedia.org/wiki/Conditional_comment">conditional comments</a>. This isn't a feature supported by Tapestry's <a href="http://tapestry.apache.org/current/apidocs/org/apache/tapestry5/services/javascript/JavaScriptSupport.html">JavaScriptSupport</a> (for libraries; it is support for CSS files).
<p>Fortunately, this is something that comes together in Tapestry in almost no code:
<script src="https://gist.github.com/hlship/fe2b72e3c966347c41b8.js"></script>
<p>
This is what it looks like in a template:
<script src="https://gist.github.com/hlship/7829555.js"></script>
<p>
And here's the markup it produces:
<script src="https://gist.github.com/hlship/7829589.js"></script>
<p>And that's the whole point ... the URLs for the referenced JavaScript libraries are now Tapestry assets, and will have access to Tapestry's asset pipeline as unique resources, including GZip compression and minimization.
<p>
It is always important to me that Tapestry <em>not get in the way</em>: frameworks that lock you down and prevent you from accomplishing your goals should not be used. Tapestry has a long history, and there are certainly many areas that could have been implemented differently, or simply not implemented at all, but it's nice that most edge cases, like this one, have a simple solution.Anonymoushttp://www.blogger.com/profile/04486596490758986709noreply@blogger.com3tag:blogger.com,1999:blog-4110180.post-67795343086328766692013-11-11T17:37:00.000-08:002013-11-11T17:42:09.095-08:00Improving Clojure Feedback : Stack Traces<p>Perhaps no subject in software development is as important to me as <a href="http://tapestryjava.blogspot.com/search/label/feedback">feedback</a>, which to my mind, is the language, library, or framework providing information back to the developer when things go wrong. A language, library, or framework that provides poor feedback is creating barriers to its own adoption.</p>
<p>There's a possibly apocryphal legend about early FORTRAN compilers. Back in the day (thankfully, even earlier than my personal
<em>back in the day</em>), you would code up your FORTRAN code on punch cards and deliver them to an operator. At some convenient time (for the operator), the compiler would be started and fed the stack of cards. Assuming the cards had not been bent, folded, spindled, or mutilated ... and that the program itself contained no errors, you would get a compiled program somewhere. If anything failed, you would simply get the error message "FAIL". Nothing else.</p>
<p>That's bad feedback.</p>
<p>It's important to feel that you are in control of your own tools; that's part of the love/hate relationship so many developers have with their IDEs: the IDEs certainly enable a lot of productivity ... but what you always end up remembering is when the IDE gets in the way of some really critical, really difficult stuff. Or when it crashes mysteriously at a critical moment. Or when it just
<em>won't do</em>something and won't tell you why not.</p>
<p>Likewise, any tool (or language, library, or framework) that provides bad feedback,
<em>especially when things go wrong</em>, is building a perception that it is hard to use overall. We're a fickle bunch ... we'll be off to find the Next Big Thing instead.</p>
<p>Which brings us to my favorite programming language, <a href="http://clojure.org">Clojure</a>. When things are going right in Clojure they are going
<em>very, very</em>right. However, when things go wrong in Clojure it can be very painful.</p>
<p>First of all, Clojure's basic mechanism for any kind of error is to simply throw an exception. Since Clojure is a Lisp, the difference between compile time and runtime is not so easily distinguished ... a running Clojure program is often loading source and compiling code as well as running the application.</p>
<p>And what happens to exceptions? They are thrown in one piece of code and, more often than not, caught, wrapped, and re-thrown in containing stack frames. Ultimately, this whole stack of exceptions gets spewed out to the console.</p>
<p>This isn't quite bad as that FORTRAN compiler; however, even on my big 30" cinema display, a Clojure compilation failure always sends me scrolling backwards in my terminal window to see the actual error and (with more than a pinch of luck) the file name and line number containing the error.</p>
<p>It's much worse when it comes to runtime failures in my own code; it is likely that the exceptions will be nested even deeper than the number of levels intituted by the Clojure compiler and REPL. Beyond that is the issue of laziness ... program execution goes a little topsy-turvey from your code due to laziness, as what code is executing is typically driven by what particular bit of lazily-computed data is needed at any particular instant. That takes a little getting used to for new Clojure developers.</p>
<p>And so, deep in your code, you passed a string when a keyword was expected, or you passed a map when a seq was expected, or any number of other similar situations ... and now you are faced with an exception and its stack trace.</p>
<p>Here's the ugly not-so-secret of Clojure: it's a light and tasty frosting on top of a cake of sawdust and rusty gears: Java. Every Clojure function must be converted into a Java class in order to execute within the JVM. Java places somewhat arbitrary limits on the class and method names: Where Clojure loves dashes in function names, Java doesn't allow them. Likewise, Clojure loves all kind of other punctuation, with functions named "+" or "string?", that Java forbids as well.</p>
<p>Clojure
<em>mangles</em>the Clojure namespace and function names to fit into the Java world, and nothing in the exception reporting chain does anything to make the result any prettier. Here's an example:</p>
<script src="https://gist.github.com/hlship/7423470.js"></script>
<p>All the information is available ... but you have to work to get at it: You need to mentally de-mangle the Java class names back to Clojure namespaces and function names. You have to ignore a lot of stack frames that are really the infrastructure supporting Clojure on top of Java. You have to assemble the meaningful stack trace (the last and deepest one) from the parts it extends off of the prior stack traces. You have to ignore the numeric ids tacked on to the end of names to help ensure uniqueness.</p>
<p>This is not great feedback.</p>
<p>What if we could provide just a little bit better feedback? What if we could do a lot of that name-demangling automatically, and present the same data in a more helpful way? That's what I've been working on as part of the <a href="https://github.com/AvisoNovate/pretty">io.aviso:pretty</a> library for Clojure.</p>
<p>
Pretty can be used to format that same exception just a little bit nicer:</p>
<div class="separator" style="clear: both; text-align: center;">
<a href="http://1.bp.blogspot.com/-35cpqHuccw0/UoF_rcbnP6I/AAAAAAAAAYM/u3hYKLiy3x0/s1600/1._java_and_Pandora-5.png" imageanchor="1" style="clear: left; float: left; margin-bottom: 1em; margin-right: 1em;">
<img border="0" src="http://1.bp.blogspot.com/-35cpqHuccw0/UoF_rcbnP6I/AAAAAAAAAYM/u3hYKLiy3x0/s400/1._java_and_Pandora-5.png" />
</a>
</div>
<p style="clear: both;">
The exact format is still in-flux because it is currently so wide; fortunate, the interesting stuff is always to the left!
</p>
<p>
This same approach appears in the related <a href="https://github.com/AvisoNovate/twixt">io.aviso:twixt</a> library, modified to take advantage of how much more richly data can be presented in a web page:
</p>
<div class="separator" style="clear: both; text-align: center;">
<a href="http://3.bp.blogspot.com/-xE_l-YNgdE0/UoGAplyNZuI/AAAAAAAAAYY/VCRLV41Ovis/s1600/Exception-3.png" imageanchor="1" style="clear: left; float: left; margin-bottom: 1em; margin-right: 1em;">
<img border="0" src="http://3.bp.blogspot.com/-xE_l-YNgdE0/UoGAplyNZuI/AAAAAAAAAYY/VCRLV41Ovis/s400/Exception-3.png" />
</a>
</div>
<p style="clear: both;">
However, this is just the start. Providing truly useful feedback is more than just putting a patch on exception reporting. It requires attention to detail throughout the tool. This has been the case for <a href="http://tapestry.apache.org/">Apache Tapestry</a>, where a significant amount of the framework is about detecting and reporting errors in a useful way.
<p>Twixt has a set of utilities to address this as well; a way of tracking what the application is doing so that exceptions can be reported. The end result is an "operations trace" that can nest to an arbitrary depth and is used to reconstruct how the application arrived at the point of failure.
</p>
<script src="https://gist.github.com/hlship/7423692.js"></script>
<p style="clear: both;">
This extra layer of code can pay big dividends when things go wrong; the nesting of operations can be critical to understanding the underlying problem; the operations trace combines the very local scope of the failure with a larger scope that explains why the failed code was invoked in the first place.
</p>
<p>
So I'm very excited by the possibilities, but to provide truly universal, useful feedback in Clojure may not be something that can be effectively added in from the outside: it has to be a priority for the core Clojure developers, and bred into the fabric of the Clojure compiler and runtime. That's something I'd love to see ... or help with.
</p>
Anonymoushttp://www.blogger.com/profile/04486596490758986709noreply@blogger.com0tag:blogger.com,1999:blog-4110180.post-90557579199645501372013-10-16T10:25:00.002-07:002013-10-16T10:25:32.263-07:00Gainesville in February 2014<p>
I'll be spending about three weeks in Gainesville, Florida this coming February (2014). For the wife and kids, its about visiting the grandparents, hiking, fishing, and playing. For me, its mostly about work, <em>sigh</em>. In any case, I'm told that Gainesville has gotten quite the start up culture going recently ... I'd love an opportunity to spread the word about Tapestry, Clojure, or AngularJS (or Spock, or Geb, or any other presentation I have in my bag of tricks). Think of this as a chance to put your "brown bag lunch" on steroids for a day.
<p>
Just get in contact with me if you are interested; see my contact information on <a href="http://howardlewisship.com/">my home page</a>.Anonymoushttp://www.blogger.com/profile/04486596490758986709noreply@blogger.com0tag:blogger.com,1999:blog-4110180.post-91540627593152750792013-09-23T12:05:00.001-07:002013-09-23T12:22:12.400-07:00Named Parameters for Clojure<p>Clojure can <i>simulate</i> named parameters, or a mix of positional and named parameters.
This is really old news, dating back to Clojure 1.2 if <a href="http://rosettacode.org/wiki/Named_parameters#Clojure">evidence serves</a>, but I always have trouble finding this exact syntax, and it is not fully obvious.
<p>Say you want to accept a certain number of optional parameters with names, and perhaps, defaults. It turns out you can combine rest parameters (the ones that come after a <code>&</code>) with map destructuring.
<p>
In the simple case, you don't care what the possible options are, and you don't have any defaults.
<script src="https://gist.github.com/hlship/6675052.js"></script>
<p>
The keys and values you pass to this function, say <code>(named-parameters :foo 1 :bar 2)</code>, are collected together as symbol <code>params</code>.
<blockquote>If you don't provide an even number of values (that is, the same number of keys and values), you'll get a reasonable exception, such as <code>java.lang.IllegalArgumentException: No value supplied for key: :bar</code>
</blockquote>
<p> Easy-peasey ... but you need to extract values from the <code>params</code> map to use them inside the function, e.g.: <code>(:foo params)</code>. It would be nicer to have them as symbols, just like with normal positional parameters. This is also easy, by leveraging more of the features of map destructuring:
<script src="https://gist.github.com/hlship/6675118.js"></script>
<p>
The <code>:keys</code> identifies the keywords expected in the map; it works backwards from the symbol name, <code>foo</code>, to the expected keyword, <code>:foo</code>.
<blockquote>There's also a <code>:syms</code> (for when the keys
are expected to be symbols) and <code>:strs</code> (for when the keys are expected to be strings).
</blockquote>
<p>
The <code>:or</code> identifies default values for each <em>symbol</em>.
The end result is that we can rely on defaults from <code>:or</code> or provide our own values when invoking the function:
<script src="https://gist.github.com/hlship/51ed6e6118adc954da05.js"></script>
<p>And since this is Clojure, you can combine all of these things together quite easily ... some positional parameters, some named, some identified by keywords, others identified by symbols.
Anonymoushttp://www.blogger.com/profile/04486596490758986709noreply@blogger.com4tag:blogger.com,1999:blog-4110180.post-81813582890689302902013-07-25T14:43:00.000-07:002013-07-25T14:53:20.864-07:00Tapestry 5.4: An Avalanche of Fixed Issues<p>I've been working through the Tapestry issue list over the last couple of days; I'm closing a lot of duplicate and invalid bugs, and fixing some low-hanging fruit. Still, the number of fixed bugs already is impressive:
<h2> Bug
</h2>
<ul>
<li>[<a href='https://issues.apache.org/jira/browse/TAP5-800'>TAP5-800</a>] - Server side error during provideCompletions event for Autocompleter mixin is not reported on the client side properly
</li>
<li>[<a href='https://issues.apache.org/jira/browse/TAP5-860'>TAP5-860</a>] - JavaScript errors when adding validations to checkboxes
</li>
<li>[<a href='https://issues.apache.org/jira/browse/TAP5-986'>TAP5-986</a>] - A request can fail with an NPE in some cases, when a Tapestry page is acting as the servlet container error page
</li>
<li>[<a href='https://issues.apache.org/jira/browse/TAP5-1010'>TAP5-1010</a>] - Fix Finnish validation message translation - corrected file attached
</li>
<li>[<a href='https://issues.apache.org/jira/browse/TAP5-1193'>TAP5-1193</a>] - tapestry.js prevents using the back/forward browser cache
</li>
<li>[<a href='https://issues.apache.org/jira/browse/TAP5-1200'>TAP5-1200</a>] - Nested Ajax calls result in a call to $T(null)
</li>
<li>[<a href='https://issues.apache.org/jira/browse/TAP5-1569'>TAP5-1569</a>] - Grid should implement the ClientElement interface
</li>
<li>[<a href='https://issues.apache.org/jira/browse/TAP5-1601'>TAP5-1601</a>] - Sometime a method that references a field with a conduit will not be instrumented, resulting in an NPE accessing the field itself
</li>
<li>[<a href='https://issues.apache.org/jira/browse/TAP5-1668'>TAP5-1668</a>] - JavaDoc for @Parameter.value() should be clearer about the empty string
</li>
<li>[<a href='https://issues.apache.org/jira/browse/TAP5-1691'>TAP5-1691</a>] - AssetPathConstructorImpl should URL-encode the application version
</li>
<li>[<a href='https://issues.apache.org/jira/browse/TAP5-1704'>TAP5-1704</a>] - Localizing the "Today" and "None" labels in the core datefield component
</li>
<li>[<a href='https://issues.apache.org/jira/browse/TAP5-1729'>TAP5-1729</a>] - Sometimes YUICompressor can fail with java.util.EmptyStackException
</li>
<li>[<a href='https://issues.apache.org/jira/browse/TAP5-1734'>TAP5-1734</a>] - Race condition loading JavaScript libraries with ProgressiveDisplay
</li>
<li>[<a href='https://issues.apache.org/jira/browse/TAP5-1735'>TAP5-1735</a>] - Most packages lack package-level javadocs
</li>
<li>[<a href='https://issues.apache.org/jira/browse/TAP5-1742'>TAP5-1742</a>] - AfterRender() in Loop component should not short circuit
</li>
<li>[<a href='https://issues.apache.org/jira/browse/TAP5-1762'>TAP5-1762</a>] - Some components do not have include a description of their parameters in their JavaDoc pages
</li>
<li>[<a href='https://issues.apache.org/jira/browse/TAP5-1765'>TAP5-1765</a>] - PerThread scope is not honored when service is created using autobuild
</li>
<li>[<a href='https://issues.apache.org/jira/browse/TAP5-1768'>TAP5-1768</a>] - @ActivationRequestParameter does not encode to be URL friendly
</li>
<li>[<a href='https://issues.apache.org/jira/browse/TAP5-1770'>TAP5-1770</a>] - PageTester causes StringIndexOutOfBoundsException for any page request path with query parameter
</li>
<li>[<a href='https://issues.apache.org/jira/browse/TAP5-1773'>TAP5-1773</a>] - FormFieldFocus mixin passes control name, not client id, to JavaScriptSupport.autofocus()
</li>
<li>[<a href='https://issues.apache.org/jira/browse/TAP5-1779'>TAP5-1779</a>] - Tapestry allows directory listing of assets via client browser
</li>
<li>[<a href='https://issues.apache.org/jira/browse/TAP5-1784'>TAP5-1784</a>] - Extra comma in tapestry-messages_de.js causes Internet Explorer to fail to work
</li>
<li>[<a href='https://issues.apache.org/jira/browse/TAP5-1785'>TAP5-1785</a>] - Exceptions while compressing JavaScript are not fully reported
</li>
<li>[<a href='https://issues.apache.org/jira/browse/TAP5-1787'>TAP5-1787</a>] - TextField should be usable with HTML5 type values (such as "number", "email", etc.)
</li>
<li>[<a href='https://issues.apache.org/jira/browse/TAP5-1788'>TAP5-1788</a>] - Service id 'environment' has already been defined by org.apache.tapestry5.services.TapestryModule with Spring 3.1
</li>
<li>[<a href='https://issues.apache.org/jira/browse/TAP5-1791'>TAP5-1791</a>] - On some JDKs, the complex regular expression used by ComponentEventLinkEncoderImpl will cause a stack overflow
</li>
<li>[<a href='https://issues.apache.org/jira/browse/TAP5-1798'>TAP5-1798</a>] - Grid and BeanDisplay should ignore properties that are actually static fields
</li>
<li>[<a href='https://issues.apache.org/jira/browse/TAP5-1822'>TAP5-1822</a>] - LinkSecurity should be public, not internal, as it is used with the public Link interface
</li>
<li>[<a href='https://issues.apache.org/jira/browse/TAP5-1825'>TAP5-1825</a>] - Incorrect order of parameter in localization messages within ValidationMessages Italian lang
</li>
<li>[<a href='https://issues.apache.org/jira/browse/TAP5-1831'>TAP5-1831</a>] - DelegatingInjectionResources does not pass generic type information to its first delegate
</li>
<li>[<a href='https://issues.apache.org/jira/browse/TAP5-1836'>TAP5-1836</a>] - "LocalhostOnly" WhitelistAnalyzer check "0:0:0:0:0:0:0:1%0" ip address instead "0:0:0:0:0:0:0:1"
</li>
<li>[<a href='https://issues.apache.org/jira/browse/TAP5-1844'>TAP5-1844</a>] - Datefield not fires onchange event on changes
</li>
<li>[<a href='https://issues.apache.org/jira/browse/TAP5-1848'>TAP5-1848</a>] - tapestry-jpa ignores persistence provider from persistence.xml
</li>
<li>[<a href='https://issues.apache.org/jira/browse/TAP5-1854'>TAP5-1854</a>] - AjaxComponentEventRequestHandler doesn't handle the case where a response has already be returned, and may append an empty JSON Object to the response
</li>
<li>[<a href='https://issues.apache.org/jira/browse/TAP5-1860'>TAP5-1860</a>] - Access to protected component fields does not always reflect in subclasses
</li>
<li>[<a href='https://issues.apache.org/jira/browse/TAP5-1868'>TAP5-1868</a>] - SRSCachingInterceptor returns compressed version of asset for all clients once it was compressed for some client
</li>
<li>[<a href='https://issues.apache.org/jira/browse/TAP5-1870'>TAP5-1870</a>] - javascript added while in the render phase of a component from an ajax request is never executed
</li>
<li>[<a href='https://issues.apache.org/jira/browse/TAP5-1873'>TAP5-1873</a>] - JavaScript execution exception is not logged
</li>
<li>[<a href='https://issues.apache.org/jira/browse/TAP5-1880'>TAP5-1880</a>] - GZip compression should be disabled if the request is over http 1.0
</li>
<li>[<a href='https://issues.apache.org/jira/browse/TAP5-1881'>TAP5-1881</a>] - TypeCoercion from Number to Boolean returns false for any number that is an even multiple of 256
</li>
<li>[<a href='https://issues.apache.org/jira/browse/TAP5-1887'>TAP5-1887</a>] - Client-side JavaScript error if console.info/debug/... is available but not a function
</li>
<li>[<a href='https://issues.apache.org/jira/browse/TAP5-1890'>TAP5-1890</a>] - PlaceholderBlock should implement RenderCommand
</li>
<li>[<a href='https://issues.apache.org/jira/browse/TAP5-1892'>TAP5-1892</a>] - FormFragment validates non-displayed fragments (reopen)
</li>
<li>[<a href='https://issues.apache.org/jira/browse/TAP5-1903'>TAP5-1903</a>] - Client-side exception when a Zone containing a Form with an Upload component is re-rendered
</li>
<li>[<a href='https://issues.apache.org/jira/browse/TAP5-1906'>TAP5-1906</a>] - Interaction between client-side validation and submit buttons can result in a server-side error parsing JSON array
</li>
<li>[<a href='https://issues.apache.org/jira/browse/TAP5-1907'>TAP5-1907</a>] - Client exception in IE9 when partial page render introduces stylesheets
</li>
<li>[<a href='https://issues.apache.org/jira/browse/TAP5-1911'>TAP5-1911</a>] - Tree leaf is still not selectable
</li>
<li>[<a href='https://issues.apache.org/jira/browse/TAP5-1926'>TAP5-1926</a>] - PropertyDisplayBlocks Date's display block is not thread safe
</li>
<li>[<a href='https://issues.apache.org/jira/browse/TAP5-1929'>TAP5-1929</a>] - High contention in method InternalComponentResourcesImpl.postRenderCleanup() and NamedSet.getValues()
</li>
<li>[<a href='https://issues.apache.org/jira/browse/TAP5-1931'>TAP5-1931</a>] - Alerts can not be dismissed in IE8.
</li>
<li>[<a href='https://issues.apache.org/jira/browse/TAP5-1933'>TAP5-1933</a>] - Email validation should use a different regular expression
</li>
<li>[<a href='https://issues.apache.org/jira/browse/TAP5-1935'>TAP5-1935</a>] - ContextResource uses a deprecated File.toURL
</li>
<li>[<a href='https://issues.apache.org/jira/browse/TAP5-1937'>TAP5-1937</a>] - removeCookieValue with custom path
</li>
<li>[<a href='https://issues.apache.org/jira/browse/TAP5-1938'>TAP5-1938</a>] - The ValueEncoder for JPA entity types should encode transient instances as null rather than throw an exception
</li>
<li>[<a href='https://issues.apache.org/jira/browse/TAP5-1945'>TAP5-1945</a>] - Clojure integration breaks if tapestry-core is on the classpath
</li>
<li>[<a href='https://issues.apache.org/jira/browse/TAP5-1949'>TAP5-1949</a>] - Alerts component does not show alerts added from a component that occurs later in the template
</li>
<li>[<a href='https://issues.apache.org/jira/browse/TAP5-1950'>TAP5-1950</a>] - ResourceStreamer sends SC_NOT_MODIFIED as an error, but it should be just status
</li>
<li>[<a href='https://issues.apache.org/jira/browse/TAP5-1952'>TAP5-1952</a>] - JavaScript Compressor should log warnings as well as errors, and identify the input clearly
</li>
<li>[<a href='https://issues.apache.org/jira/browse/TAP5-1956'>TAP5-1956</a>] - Tapestry.ZoneManager creates memory leaks
</li>
<li>[<a href='https://issues.apache.org/jira/browse/TAP5-1960'>TAP5-1960</a>] - @javax.annotations.Inject on a field, without @Named, does not inject service resources
</li>
<li>[<a href='https://issues.apache.org/jira/browse/TAP5-1963'>TAP5-1963</a>] - Original exception lost in CommitAfterWorker upon abort
</li>
<li>[<a href='https://issues.apache.org/jira/browse/TAP5-1964'>TAP5-1964</a>] - In production mode, placeholder timestamp needs to be limited to one-second accuracy
</li>
<li>[<a href='https://issues.apache.org/jira/browse/TAP5-1969'>TAP5-1969</a>] - Excessive warnings from YUICompressor
</li>
<li>[<a href='https://issues.apache.org/jira/browse/TAP5-1973'>TAP5-1973</a>] - :443 added to URLs when using the Link.toAbsoluteURI(true)
</li>
<li>[<a href='https://issues.apache.org/jira/browse/TAP5-1976'>TAP5-1976</a>] - XML Parser adds attributes with default values and produces invalid HTML5 markup
</li>
<li>[<a href='https://issues.apache.org/jira/browse/TAP5-1977'>TAP5-1977</a>] - Memory leak (perm gen) in component reloading
</li>
<li>[<a href='https://issues.apache.org/jira/browse/TAP5-1979'>TAP5-1979</a>] - Changing the implementation of a method after adding method advice does not work; the original implementation remains
</li>
<li>[<a href='https://issues.apache.org/jira/browse/TAP5-1983'>TAP5-1983</a>] - PerThreadManager does not cleanup on shutdown, can lead to memory leaks when application redeployed
</li>
<li>[<a href='https://issues.apache.org/jira/browse/TAP5-1984'>TAP5-1984</a>] - Adding alerts doesn't work during ajax rendering
</li>
<li>[<a href='https://issues.apache.org/jira/browse/TAP5-1985'>TAP5-1985</a>] - Clicking Alert component's "dismiss all" link scrolls to top of page
</li>
<li>[<a href='https://issues.apache.org/jira/browse/TAP5-1988'>TAP5-1988</a>] - Tapestry Security Violations
</li>
<li>[<a href='https://issues.apache.org/jira/browse/TAP5-1991'>TAP5-1991</a>] - YUICompressor should be less verbose about common warnings
</li>
<li>[<a href='https://issues.apache.org/jira/browse/TAP5-1992'>TAP5-1992</a>] - Switch YUICompressor dependency back to com.yahoo version 2.4.7
</li>
<li>[<a href='https://issues.apache.org/jira/browse/TAP5-1995'>TAP5-1995</a>] - Tapestry5 Application can not be deployed as Tomcat7 HotDeploy Package
</li>
<li>[<a href='https://issues.apache.org/jira/browse/TAP5-2001'>TAP5-2001</a>] - Race condition while loading javascript file via ajax
</li>
<li>[<a href='https://issues.apache.org/jira/browse/TAP5-2004'>TAP5-2004</a>] - Tapestry-Ioc fails to build on Windows (ReloadTest.java)
</li>
<li>[<a href='https://issues.apache.org/jira/browse/TAP5-2008'>TAP5-2008</a>] - Serialized object data stored on the client should be HMAC signed and validated
</li>
<li>[<a href='https://issues.apache.org/jira/browse/TAP5-2009'>TAP5-2009</a>] - Downgrade bundled Prototype version back to 1.7
</li>
<li>[<a href='https://issues.apache.org/jira/browse/TAP5-2010'>TAP5-2010</a>] - Broken links in Javadoc pages
</li>
<li>[<a href='https://issues.apache.org/jira/browse/TAP5-2014'>TAP5-2014</a>] - Zone highlight leaves behind an explicit background-color which overrides css background-color
</li>
<li>[<a href='https://issues.apache.org/jira/browse/TAP5-2015'>TAP5-2015</a>] - Tomcat .war path is not decoded properly
</li>
<li>[<a href='https://issues.apache.org/jira/browse/TAP5-2017'>TAP5-2017</a>] - Triggerfragment Mixin does not work when used within a table element for IE7
</li>
<li>[<a href='https://issues.apache.org/jira/browse/TAP5-2025'>TAP5-2025</a>] - Duplicate generated ids
</li>
<li>[<a href='https://issues.apache.org/jira/browse/TAP5-2034'>TAP5-2034</a>] - When the application operates with a context path, asset URLs are incorrectly formed
</li>
<li>[<a href='https://issues.apache.org/jira/browse/TAP5-2037'>TAP5-2037</a>] - ValidationTracker/Flash persistence race condition with AJAX
</li>
<li>[<a href='https://issues.apache.org/jira/browse/TAP5-2040'>TAP5-2040</a>] - TapestryAppInitializer should have access to system properties; this is a reversion in 5.4
</li>
<li>[<a href='https://issues.apache.org/jira/browse/TAP5-2041'>TAP5-2041</a>] - Links within subheadings are invisible on Javadoc pages
</li>
<li>[<a href='https://issues.apache.org/jira/browse/TAP5-2045'>TAP5-2045</a>] - Set default CSS class for Label component to be "control-label", but allow overrides
</li>
<li>[<a href='https://issues.apache.org/jira/browse/TAP5-2047'>TAP5-2047</a>] - ElementWrapper#find jQuery implementation is broken if there is no match
</li>
<li>[<a href='https://issues.apache.org/jira/browse/TAP5-2049'>TAP5-2049</a>] - Tapestry should provide locking semantics for attributes stored in the session, to prevent multiple simultaneous requests (due to Ajax) from conflicting
</li>
<li>[<a href='https://issues.apache.org/jira/browse/TAP5-2052'>TAP5-2052</a>] - tapestry-ioc has a compile dependency on tapestry-test
</li>
<li>[<a href='https://issues.apache.org/jira/browse/TAP5-2053'>TAP5-2053</a>] - Use of 'transient' property in alert options breaks JavaScript processors
</li>
<li>[<a href='https://issues.apache.org/jira/browse/TAP5-2054'>TAP5-2054</a>] - `dom.js` implementations break when minified
</li>
<li>[<a href='https://issues.apache.org/jira/browse/TAP5-2057'>TAP5-2057</a>] - CSS URL rewriting is incomplete
</li>
<li>[<a href='https://issues.apache.org/jira/browse/TAP5-2061'>TAP5-2061</a>] - core_**.properties got wrong encoding
</li>
<li>[<a href='https://issues.apache.org/jira/browse/TAP5-2062'>TAP5-2062</a>] - Ajax alerts not rendered after a JS call to dismissOne()
</li>
<li>[<a href='https://issues.apache.org/jira/browse/TAP5-2064'>TAP5-2064</a>] - Add 'info' style to info alerts
</li>
<li>[<a href='https://issues.apache.org/jira/browse/TAP5-2068'>TAP5-2068</a>] - Use <button>, not <a>, tags for the close buttons on alerts
</li>
<li>[<a href='https://issues.apache.org/jira/browse/TAP5-2071'>TAP5-2071</a>] - Tapestry should output valid HTML5 when using the HTML5 doctype in templates
</li>
<li>[<a href='https://issues.apache.org/jira/browse/TAP5-2073'>TAP5-2073</a>] - AbstractEventContext always emits "null" regardless of what values are defined by subclass
</li>
<li>[<a href='https://issues.apache.org/jira/browse/TAP5-2079'>TAP5-2079</a>] - Tapestry should handle the case where the context path is (incorrectly) "/" (not the empty string)
</li>
<li>[<a href='https://issues.apache.org/jira/browse/TAP5-2081'>TAP5-2081</a>] - datefield: cannot blank out date value via text input
</li>
<li>[<a href='https://issues.apache.org/jira/browse/TAP5-2082'>TAP5-2082</a>] - When the last alert that is removed is transient, the outer container is not removed
</li>
<li>[<a href='https://issues.apache.org/jira/browse/TAP5-2084'>TAP5-2084</a>] - Form should decode its link parameters
</li>
<li>[<a href='https://issues.apache.org/jira/browse/TAP5-2087'>TAP5-2087</a>] - Checkboxes in forms always return "on" (considered as "checked" server-side)
</li>
<li>[<a href='https://issues.apache.org/jira/browse/TAP5-2089'>TAP5-2089</a>] - JavaScript errors when using Autocomplete mixin
</li>
<li>[<a href='https://issues.apache.org/jira/browse/TAP5-2092'>TAP5-2092</a>] - Tapestry-mongodb has a compile dependency on tapestry-test
</li>
<li>[<a href='https://issues.apache.org/jira/browse/TAP5-2095'>TAP5-2095</a>] - Module assets should not be sent a far-future expires header
</li>
<li>[<a href='https://issues.apache.org/jira/browse/TAP5-2101'>TAP5-2101</a>] - BeanEditor should always provide a new BeanValidationContext (JSR-303)
</li>
<li>[<a href='https://issues.apache.org/jira/browse/TAP5-2103'>TAP5-2103</a>] - ElementWrapper#text() jQuery implementation does not work
</li>
<li>[<a href='https://issues.apache.org/jira/browse/TAP5-2106'>TAP5-2106</a>] - Tapestry incorrectly rewrites CSS urls in a variety of cases
</li>
<li>[<a href='https://issues.apache.org/jira/browse/TAP5-2110'>TAP5-2110</a>] - MinLength and MaxLength validators don't import "t5/core/validation"
</li>
<li>[<a href='https://issues.apache.org/jira/browse/TAP5-2119'>TAP5-2119</a>] - Depenencies excluded in Gradle build files are not excluded in the generated Maven POMs
</li>
<li>[<a href='https://issues.apache.org/jira/browse/TAP5-2120'>TAP5-2120</a>] - StringIndexOutOfBoundsException computing relative paths for certain Resources
</li>
<li>[<a href='https://issues.apache.org/jira/browse/TAP5-2121'>TAP5-2121</a>] - Broken image (for Tapestry Logo) in generated documentation page
</li>
<li>[<a href='https://issues.apache.org/jira/browse/TAP5-2122'>TAP5-2122</a>] - visible() on empty <div> is false; prevents valiation of Palette component
</li>
<li>[<a href='https://issues.apache.org/jira/browse/TAP5-2124'>TAP5-2124</a>] - events.zone.refresh must be triggered directly on zone element, can fail otherwise
</li>
<li>[<a href='https://issues.apache.org/jira/browse/TAP5-2125'>TAP5-2125</a>] - Ajax form submissions via jQuery may pass t:formdata as "t:formdata[]" if there are multiple values
</li>
<li>[<a href='https://issues.apache.org/jira/browse/TAP5-2126'>TAP5-2126</a>] - TAP5-2063 fix (multi-valued parameters in Link) conflicts with use of ActivationRequestParameter and PageLink.parameters parameter
</li>
<li>[<a href='https://issues.apache.org/jira/browse/TAP5-2132'>TAP5-2132</a>] - Generation of documentation for t5/core/dom should use the split (for jQuery, for Prototype) files, not the combined source file
</li>
<li>[<a href='https://issues.apache.org/jira/browse/TAP5-2137'>TAP5-2137</a>] - Tree component renders empty nodes as expandable, AssertionError when expanded
</li>
<li>[<a href='https://issues.apache.org/jira/browse/TAP5-2141'>TAP5-2141</a>] - Ajax form submission should ignore fields with no name
</li>
<li>[<a href='https://issues.apache.org/jira/browse/TAP5-2142'>TAP5-2142</a>] - Exceptions thrown during Ajax processing or validation force a traditional form submission
</li>
<li>[<a href='https://issues.apache.org/jira/browse/TAP5-2148'>TAP5-2148</a>] - VersionUtils should close the stream it opens
</li>
</ul>
<h2> Improvement
</h2>
<ul>
<li>[<a href='https://issues.apache.org/jira/browse/TAP5-336'>TAP5-336</a>] - The Cookies service interface could be simplified using a builder pattern
</li>
<li>[<a href='https://issues.apache.org/jira/browse/TAP5-672'>TAP5-672</a>] - Translation for Vietnamese
</li>
<li>[<a href='https://issues.apache.org/jira/browse/TAP5-1008'>TAP5-1008</a>] - The order in which zones are added to the MultiZoneUpdate should be honored on the client side, to allow nested zones to be updated all in a single request
</li>
<li>[<a href='https://issues.apache.org/jira/browse/TAP5-1268'>TAP5-1268</a>] - Have Tapestry's core library contribute to the global message catalog; move all validation messages and component catalogs to the single file
</li>
<li>[<a href='https://issues.apache.org/jira/browse/TAP5-1394'>TAP5-1394</a>] - Adding method to Cookies api that lets you set path, domain, and maxAge at once.
</li>
<li>[<a href='https://issues.apache.org/jira/browse/TAP5-1405'>TAP5-1405</a>] - XHR requests should be easily callable from javascript and not rely on a zone
</li>
<li>[<a href='https://issues.apache.org/jira/browse/TAP5-1560'>TAP5-1560</a>] - Remove internal dependancies on deprecated features
</li>
<li>[<a href='https://issues.apache.org/jira/browse/TAP5-1570'>TAP5-1570</a>] - Zone elements in the client should trigger events so that the application can react to changes
</li>
<li>[<a href='https://issues.apache.org/jira/browse/TAP5-1748'>TAP5-1748</a>] - Alerts component should render informal parameters
</li>
<li>[<a href='https://issues.apache.org/jira/browse/TAP5-1756'>TAP5-1756</a>] - Let the asset path prefix be configurable
</li>
<li>[<a href='https://issues.apache.org/jira/browse/TAP5-1775'>TAP5-1775</a>] - Improve javascript performance while creating zone events
</li>
<li>[<a href='https://issues.apache.org/jira/browse/TAP5-1781'>TAP5-1781</a>] - Improve javascript load time of pages improving onDomLoadedCallback
</li>
<li>[<a href='https://issues.apache.org/jira/browse/TAP5-1801'>TAP5-1801</a>] - Component fields should not need to be private, merely non-public
</li>
<li>[<a href='https://issues.apache.org/jira/browse/TAP5-1805'>TAP5-1805</a>] - Selectable Tree component look and feel is lacking in user affordances
</li>
<li>[<a href='https://issues.apache.org/jira/browse/TAP5-1808'>TAP5-1808</a>] - Change Form to (by default) immediately render markup when there are validation errors, to avoid creating the session
</li>
<li>[<a href='https://issues.apache.org/jira/browse/TAP5-1816'>TAP5-1816</a>] - Add CSS rule for DIV.t-exception-container to default.css that sets a very high z-index
</li>
<li>[<a href='https://issues.apache.org/jira/browse/TAP5-1824'>TAP5-1824</a>] - New translations for Norwegian Bokmål
</li>
<li>[<a href='https://issues.apache.org/jira/browse/TAP5-1827'>TAP5-1827</a>] - KaptchaField should have a parameter to allow it to operate as a visible text field rather than a password field
</li>
<li>[<a href='https://issues.apache.org/jira/browse/TAP5-1832'>TAP5-1832</a>] - Tapestry could do an even better job of filtering unnecessary stack frames from the exception report
</li>
<li>[<a href='https://issues.apache.org/jira/browse/TAP5-1840'>TAP5-1840</a>] - Add method Request.isSessionInvalidated()
</li>
<li>[<a href='https://issues.apache.org/jira/browse/TAP5-1847'>TAP5-1847</a>] - An extension component template (with a root t:extend element) should allow t:block elements to be nested
</li>
<li>[<a href='https://issues.apache.org/jira/browse/TAP5-1849'>TAP5-1849</a>] - The text "Dismiss All" used in client-side Alerts is not localized or localizable
</li>
<li>[<a href='https://issues.apache.org/jira/browse/TAP5-1878'>TAP5-1878</a>] - Default for parameter visible of component KaptchaField should change from false to true
</li>
<li>[<a href='https://issues.apache.org/jira/browse/TAP5-1889'>TAP5-1889</a>] - Improve integration test execution speed
</li>
<li>[<a href='https://issues.apache.org/jira/browse/TAP5-1905'>TAP5-1905</a>] - Not serializable: org.apache.tapestry5.tree.DefaultTreeExpansionModel - can't persist sessions across cluster
</li>
<li>[<a href='https://issues.apache.org/jira/browse/TAP5-1914'>TAP5-1914</a>] - Alerts needs a parameter to show/hide "Dismiss all"
</li>
<li>[<a href='https://issues.apache.org/jira/browse/TAP5-1940'>TAP5-1940</a>] - Typo in interface LocalizationSetter.setNonPeristentLocaleFromLocaleName()
</li>
<li>[<a href='https://issues.apache.org/jira/browse/TAP5-1970'>TAP5-1970</a>] - JSON API improvements
</li>
<li>[<a href='https://issues.apache.org/jira/browse/TAP5-1987'>TAP5-1987</a>] - ProgressiveDisplay update parameter as a symbol
</li>
<li>[<a href='https://issues.apache.org/jira/browse/TAP5-1990'>TAP5-1990</a>] - Link interface should be more fluid to allow setting of anchor, etc., more concisely
</li>
<li>[<a href='https://issues.apache.org/jira/browse/TAP5-1993'>TAP5-1993</a>] - Support returning an URL for an ajax request
</li>
<li>[<a href='https://issues.apache.org/jira/browse/TAP5-1996'>TAP5-1996</a>] - Add Severity.SUCCESS enum for alerts
</li>
<li>[<a href='https://issues.apache.org/jira/browse/TAP5-2013'>TAP5-2013</a>] - "Parameters" table often too wide in component javadocs
</li>
<li>[<a href='https://issues.apache.org/jira/browse/TAP5-2022'>TAP5-2022</a>] - Add PropertyAccess.getAnnotation
</li>
<li>[<a href='https://issues.apache.org/jira/browse/TAP5-2035'>TAP5-2035</a>] - Make it easier to choose another slf4j backend
</li>
<li>[<a href='https://issues.apache.org/jira/browse/TAP5-2039'>TAP5-2039</a>] - Element.attribute() should treat "class" specially, appending the new value to the old
</li>
<li>[<a href='https://issues.apache.org/jira/browse/TAP5-2046'>TAP5-2046</a>] - Should use OperationTracker to track type of request (ajax component event, traditional component event, page render request)
</li>
<li>[<a href='https://issues.apache.org/jira/browse/TAP5-2050'>TAP5-2050</a>] - Grid sort icons should be added via CSS instead of rendering an embedded img element
</li>
<li>[<a href='https://issues.apache.org/jira/browse/TAP5-2051'>TAP5-2051</a>] - Fix Plastic objects memory leaks
</li>
<li>[<a href='https://issues.apache.org/jira/browse/TAP5-2058'>TAP5-2058</a>] - Support X-Forwarded-Proto to identify a Request secured (https)
</li>
<li>[<a href='https://issues.apache.org/jira/browse/TAP5-2063'>TAP5-2063</a>] - Add support for multivalued parameters in Link
</li>
<li>[<a href='https://issues.apache.org/jira/browse/TAP5-2102'>TAP5-2102</a>] - Allow supplying EntityManager properties via TapestryPersistenceUnitInfo
</li>
<li>[<a href='https://issues.apache.org/jira/browse/TAP5-2131'>TAP5-2131</a>] - Update Javadoc CSS to reflect changes in the html generate by JDK7
</li>
</ul>
<h2> New Feature
</h2>
<ul>
<li>[<a href='https://issues.apache.org/jira/browse/TAP5-999'>TAP5-999</a>] - Implement an agnostic tapestry.js layer + adapters to allow developers to switch from prototype to jquery
</li>
<li>[<a href='https://issues.apache.org/jira/browse/TAP5-1168'>TAP5-1168</a>] - Add @Operation annotation that can be used to automatically track a component or service method invocation using the OperationTracker
</li>
<li>[<a href='https://issues.apache.org/jira/browse/TAP5-1794'>TAP5-1794</a>] - Allow for configuring Selenium-based integration tests using an annotation
</li>
<li>[<a href='https://issues.apache.org/jira/browse/TAP5-1807'>TAP5-1807</a>] - In development mode, Tapestry should include a <meta> tag to identify the active page
</li>
<li>[<a href='https://issues.apache.org/jira/browse/TAP5-1809'>TAP5-1809</a>] - Option to render a particular page instance in its current state (without page activation)
</li>
<li>[<a href='https://issues.apache.org/jira/browse/TAP5-1833'>TAP5-1833</a>] - Merge functionality of Tynamo.org's tapestry-exceptionpage module with the built-in ExceptionHandler
</li>
<li>[<a href='https://issues.apache.org/jira/browse/TAP5-1843'>TAP5-1843</a>] - Allow all of the properties of the Messages services to be accessed
</li>
<li>[<a href='https://issues.apache.org/jira/browse/TAP5-1857'>TAP5-1857</a>] - Add a SubmitMode (for Submit and LinkSubmit components) for unconditionally submitting the form
</li>
<li>[<a href='https://issues.apache.org/jira/browse/TAP5-1944'>TAP5-1944</a>] - Add basic integration between Tapestry and Clojure
</li>
<li>[<a href='https://issues.apache.org/jira/browse/TAP5-2055'>TAP5-2055</a>] - Polish translations
</li>
<li>[<a href='https://issues.apache.org/jira/browse/TAP5-2065'>TAP5-2065</a>] - Introduce support for MongoDB access as a service
</li>
<li>[<a href='https://issues.apache.org/jira/browse/TAP5-2094'>TAP5-2094</a>] - E-Tags support
</li>
</ul>
<h2> Task
</h2>
<ul>
<li>[<a href='https://issues.apache.org/jira/browse/TAP5-1518'>TAP5-1518</a>] - Remove ClassFactory / ClassFab / Javassist Dependency
</li>
<li>[<a href='https://issues.apache.org/jira/browse/TAP5-1780'>TAP5-1780</a>] - Upgrade Selenium dependency to 2.14.0
</li>
<li>[<a href='https://issues.apache.org/jira/browse/TAP5-1790'>TAP5-1790</a>] - Update Tapestry build to create a binary release archive (in addition to a source and javadoc archive)
</li>
<li>[<a href='https://issues.apache.org/jira/browse/TAP5-1792'>TAP5-1792</a>] - Upgrade Tapestry/Spring integration to Spring 3.1.0.RELEASE
</li>
<li>[<a href='https://issues.apache.org/jira/browse/TAP5-1810'>TAP5-1810</a>] - Remove deprecated "suppress redirects from action" code
</li>
<li>[<a href='https://issues.apache.org/jira/browse/TAP5-1826'>TAP5-1826</a>] - Setup the Sonar plugin to let analysis.apache.org analyze the source base
</li>
<li>[<a href='https://issues.apache.org/jira/browse/TAP5-1852'>TAP5-1852</a>] - Upgrade Plastic to use ASM 4.0
</li>
<li>[<a href='https://issues.apache.org/jira/browse/TAP5-1909'>TAP5-1909</a>] - Update Underscore JavaScript library to latest version 1.3.3
</li>
<li>[<a href='https://issues.apache.org/jira/browse/TAP5-1922'>TAP5-1922</a>] - Upgrade Hibernate dependency to latest, Hibernate 4.1.2.Final
</li>
<li>[<a href='https://issues.apache.org/jira/browse/TAP5-1965'>TAP5-1965</a>] - Replace use of Request.getContextPath() with a symbol defined at application startup
</li>
<li>[<a href='https://issues.apache.org/jira/browse/TAP5-1989'>TAP5-1989</a>] - Upgrade bundled Prototype to version 1.7.1
</li>
</ul>
Anonymoushttp://www.blogger.com/profile/04486596490758986709noreply@blogger.com0tag:blogger.com,1999:blog-4110180.post-91121248373458631582013-07-18T11:46:00.001-07:002013-07-19T09:39:38.703-07:00Closing in on Tapestry 5.4<p>We're getting very close to a beta release of Tapestry 5.4. I've spent the last couple of weeks converting an existing client's very large application (120+ pages, tons of components, lots of client-side JavaScript) from 5.3 to the current 5.4 alpha, and it's been a lot of work ... but also a lot of reward.
<p>Before the switch, the application was already using <a href="http://jquery.com/">jQuery</a>, <a href="http://twitter.github.io/bootstrap">Bootstrap</a>, and a limited amount of <a href="http://backbonejs.org/">Backbone</a>; under Tapestry 5.3, that means that <a href="http://prototypejs.org/">Prototype</a> and <a href="http://www.aculo.us/">Scriptaculous</a> were also in the mix ... and there were quite a few conflicts between Prototype and jQuery.
<p>
It's not just that Prototype and jQuery both want to use <code>window.$</code> as their point of entry; the conflicts can be deeper and more subtle. For example, I had a nasty time with a Boostrap modal dialog that didn't dismiss correctly. After a <em>lot</em> of debugging, I found out that jQuery treats a method on an element as an event handler for the event with the matching name; Bootstrap was triggering a <code>hide</code> event, and jQuery was invoking the <code>hide()</code> Element method added by Prototype. That kind of thing has been my life under the mixed stack.
<p>
Tapestry 5.4 manages this better; and with the new <em>abstraction layer</em> it is possible to turn off Prototype completely and use just jQuery. That removes these conflicts ... and also speeds things up, but reducing the amount of JavaScript code downloaded to the client.
<p>
The transition from 5.3 to 5.4 was a chance to review and improve all that code. Here's a few observations.
<p>The application has some very complex client-side forms; well beyond the abilities of Tapestry to manage using the FormInjector and FormFragment components. Instead, we use Backbone is a kind of hybrid mode, where the Backbone Model or Collection is persisted to a hidden field, so that the data collected or edited is transmitted to the server as part of an over-all form submission.
<p>Of course, this means creating a lot of content, including form control elements, on the fly. One of the main problem was integrating Tapestry's client-side validation for the newly created fields.
<p>Under 5.3, this required examining and often hacking (or monkey patching) the Tapestry client-side code, which create <code>FormEventManager</code> and <code>FieldEventManager</code> objects for each form or form control element ... and there was lots of hackery to tap into the form's submission cycle to perform validations.
<p>Under 5.4 it is much easier, when creating the new fields, we can specify the desired validations via attributes:
<script src="https://gist.github.com/hlship/6030909.js"></script>
<p>The <code>data-validation</code> attribute indicates the field participates in validation; it will have events triggered on it when the enclosing form submits. The <code>t5/core/validation</code> module supplies the code that handles fields with <code>data-optionality="required"</code> and ensures that a value is provided, displaying the <code>data-required-message</code> if the field is left blank.
<p>That's the pattern throughout 5.4; <code>data-</code> attributes are used to identify where special behavior is desired, and a well documented system of events is used handle the processing of the behavior.
<p>Very little on the server side changed with the upgrade to 5.4; but revisiting and rewriting all that JavaScript was more than enough work!
<p>In addition, it was an opportunity to convert all the code to <a href="http://coffeescript.org/">CoffeeScript</a>. CoffeeScript is my preferred way of writing client-side code: it is more concise and readable than JavaScript, but the compiled output is still quite readable. It also has great features like the block strings (from the example code above) which largely eliminates the need for a separate template engine.
<p>Working in CoffeeScript and Tapestry so much led to some quick evolution of the CoffeeScript support built into Tapestry (in the optional tapestry-wro4j module). Initially, any change to any CoffeeScript file forced <em>all</em> CoffeeScript files to be recompiled; when using the sluggish Rhino JavaScript engine to run the CoffeeScript compiler, we could see 10 - 20 seconds <em>per source file</em>!
<p>I made some improvements, adding a special development-mode in-memory cache that would only recompile the CoffeeScript if the underlying source file content had changed. Since application restarts are rare in Tapestry, this was sufficient. I eventually added a file-system cache so that compilation could be avoided, even over restarts of the application.
<p>I also started using <a href="http://lesscss.org/">Less</a> instead of CSS. Less is a meta-language that compiles down to CSS, much like the relationship between CoffeeScript and JavaScript ... but I think even more dramatically. Compilation is pretty fast, based on the <a href="https://github.com/SomMeri/less4j">Less4J</a> library.
<p>I found that the out-of-the-box support for Less4J supplied by WRO4J was insufficient: it didn't do a great job with <code>@import</code>; a change to an imported source file didn't force a recompilation. I've addressed that with a custom (for Tapestry) wrapper that properly tracks dependencies.
<p>So where does that leave us? There's still a huge amount of work to do before Tapestry 5.4 is ready for release, but it's mostly fixing bugs and other rough edges. As too often happens, the reality of earning a living have made me postpone some of my ideas for a later release.
<p>I think that Tapestry is aging, if not gracefully, then at least comfortably, into a growing age where rich, single-page applications built with Backbone, <a href="http://angularjs.org/">AngularJS</a>, or something else are the norm, and not the exception. I'm the first to admit that Tapestry was designed for a simpler time when Ajax was seasoning, not the meat-and-potatoes of the application. There's a lot of baggage in there, particularly related to forms and form controls. Yet, even so, there's some amazingly useful parts to Tapestry that apply equally well to modern applications:
<dl>
<dt>Asset Pipeline</dt>
<dd>Tapestry's asset pipeline does much of the work normally associated with a command-line build step, but does it at runtime, with the benefit of being live for development. Tapestry can not only compile files from one type to another (e.g., CoffeeScript to JavaScript), but can also aggregate many small files into a single combined file and pass it through a minimizer. Further, Tapestry builds URLs that incorporate a content checksum. This checksum means that we can have the browser aggressively cache the file (using both E-Tags and a far-future expires header) as any change to the file will change the checksum as well, resulting in what looks to the client like an entirely new resource. Further, Tapestry caches both the straight-up file, and the GZip compressed version of the file. All of this means that Tapestry provides not just great <em>throughput</em> by processing requests quickly, but also great <em>performance</em> by reducing the number of requests from clients as well.</dd>
<dt>Asynchronous Module Definition (AMD) Support</dt>
<dd>Tapestry 5.4 introduces direct support for AMD via <a href="http://requirejs.org/">RequireJS</a>. At the minimum, this means that JavaScript can be loaded in parallel ... but the real benefit is how AMD encourages you to organize your code in a manageable way. In addition, Tapestry ensures that your modules can be JavaScript or CoffeeScript, and provides a simple way on the server to override any built-in modules provided by Tapestry, should the need arise. Better yet, Tapestry has a server-side API for invoking client-side functions exported by AMD modules; these is a very clean and very useful way to knit together server-side and client-side logic.</dd>
<dt>Superior Feedback and Exception Reporting</dt>
<dd>When working on other stacks, the thing I miss the most now is Tapestry's exception reporting. This is cleanly integrated in Tapestry, where a server side failure will result in a detailed HTML report on the client. For Ajax requests, this report is presented in a floating <code><iframe></code>. The wealth of information available from the server side and reported directly in the client makes developing complex applications with involved server-side interaction much easier.</dd>
</dl>
<p>As Tapestry 5.4 transitions to beta, and to an eventual final release, I'm still targeted on the future ... and I'm focused on making Tapestry be a great choice for single-page apps as well as traditional applications. My head is bubbling with ideas to make this happen.Anonymoushttp://www.blogger.com/profile/04486596490758986709noreply@blogger.com4tag:blogger.com,1999:blog-4110180.post-31372171723354519532013-05-23T10:22:00.000-07:002013-05-23T12:09:23.156-07:00Once more ... feedback please!<p>You've probably heard about "Not Invented Here" syndrome: the drive among developers to create something of their own, rather than just use an off-the-shelf library or component. It's almost universally painted as a bad thing, a sign of immaturity, or even arrogance.
<p>But there's a flip side to this: every bit of code ever written contains within it tradeoffs: speed versus maintainability is a common tradeoff that everyone has seen. Perhaps the code is insufficiently flexible in the face of real-world requirements, but is really well tested for what it does cover. These choices reflect the developer's principles applied to the code. In fact, it is rare for it to be an easy give-and-take between two simple goals; more likely, there's lots of conflicting goals in the code, in the requirements, and in the developer's head. "Not Invented Here" can also mean "Not Reflecting My Principles".
<p>Tapestry has it own set of guiding principals: Simplicity, Consistency, Efficiency, and Feedback ... and as a reusable framework, <a href="http://tapestryjava.blogspot.com/2011/10/tapestry-feedback.html">Feedback</a> is very important. Feedback may be the <em>most</em> important principle when things go wrong. A framework that obscures problems, through bad feedback, is a framework that shouldn't be used.
<p>Which brings us back to "Not Invented Here". Only in an impossibly perfect world would there be some ideal blob of code out there, ready to be reused, with zero impedance mismatch issues. In fact, when you bring in other people's code, you are forced to mesh your goals and principals with theirs. In my case, I'm adding support to Tapestry for converting <a href="http://lesscss.org/">Less</a> files to CSS, using <a href="https://code.google.com/p/wro4j/">WRO4J</a> (Web Resource Optimizer for Java). They've been working on WRO4J for several years, it makes sense to reuse their code, and it would be arrogant to think I could whip something better together under any kind of time constraints.
<p>In fact, it's actually been pretty smooth sailing ... until we tripped across a violation of Tapestry's Feedback principle. As soon as I tested an error case, where the Less source file was not valid I hit bad feedback. Can you spot what's wrong with this exception report?
<a href="https://www.evernote.com/shard/s54/sh/a83ccda0-56a5-4cf6-b626-41d79d4dc222/9a361e3814c4321cbdbc8e119e4d569e/deep/0/Application%20Exception.png" imageanchor="1" ><img border="0" src="https://www.evernote.com/shard/s54/sh/a83ccda0-56a5-4cf6-b626-41d79d4dc222/9a361e3814c4321cbdbc8e119e4d569e/deep/0/Application%20Exception.png" /></a>
<p>Oops! Looks like someone didn't get the memo about <a href="http://tapestryjava.blogspot.com/2003/08/importance-of-tostring.html">the importance of toString()</a>. See, that Feedback principal is important to me, but for the majority of developers, useful feedback is too often an afterthought. I don't want to single out WRO4J here ... I'm pretty disdainful about feedback in nearly all software: open source <em>or</em> proprietary.
<p>So what are our options here?
<ul>
<li>Write our own wrappers around the Less Processor, and throw out WRO4J</li>
<li>Beg the WRO4J guys to implement a real toString(), and wait for the next release</li>
<li>Fork WRO4J in the short term, and hope they'll take a patch in the long term</li>
<li>Patch around this reporting problem</li>
</ul>
<p>Obviously, we should find a way to patch the reporting problem; we don't want to throw out the baby with the bath water. Fortunately, Tapestry provides the necessary hooks to override how it presents objects inside the exception report; it's all about providing a mapping from a Java type to a matching implementation of <a href="http://tapestry.apache.org/current/apidocs/org/apache/tapestry5/services/ObjectRenderer.html">ObjectRenderer</a>. Because of Tapestry's IoC container, this is actually quite straight forward:
<script src="https://gist.github.com/hlship/5637675.js"></script>
<p>And with those changes, the exception is presented quite differently:
<a href="https://www.evernote.com/shard/s54/sh/46b26caa-44d1-443b-b89c-463f8bf59060/95e50b6b8267824d0d2335af56d5bf06/deep/0/Application%20Exception.png" imageanchor="1" ><img border="0" src="https://www.evernote.com/shard/s54/sh/46b26caa-44d1-443b-b89c-463f8bf59060/95e50b6b8267824d0d2335af56d5bf06/deep/0/Application%20Exception.png" /></a>
<p>Well, those are actually the raw <a href="http://www.antlr.org/">ANTLR</a> parser errors, but at least that's enough to help you find location of the problem ... whereas, with the bad feedback, you would only know that there was an issue <em>somewhere</em> in your Less source file.Anonymoushttp://www.blogger.com/profile/04486596490758986709noreply@blogger.com4tag:blogger.com,1999:blog-4110180.post-53363180027601113192013-02-01T11:49:00.000-08:002013-02-01T16:58:26.411-08:00Crafting Code in Clojure<p>The other day, I was working on a little bit of code in Clojure, just touching up some exception reporting, when I was
suddenly struck by one of the fundamental reasons that Clojure is so enjoyable to code in. Clojure
is <em>craftable</em>: that is, in Clojure you have the option to craft at your code to make it more concise, easier
to read, and easier to maintain. That is not the case for all, or perhaps even most, programming languages.</p>
<p>In my case, I was constructing an error message where I needed to convert the keys of two maps into a comma-seperated
string (I don't like to say "you guessed wrong" without saying "here's what you could have said").
<p>
What I want my code to do is easily expressed as an informal recipe:</p>
<ul>
<li>Extract all the keys from both maps</li>
<li>Remove any duplicates</li>
<li>Convert the keys to strings</li>
<li>Sort the strings into ascending order</li>
<li>Build and return one big string, by concatinating all the key strings, using ", " as a seperator</li>
<li>Return "<none>" if both maps are empty</li>
</ul>
<p>If I was writing
this in Java, it would look something like this:</p>
<script src="https://gist.github.com/4693120.js"></script>
<p>There's enough looping and conditionals in this code (along with tip-toeing around Java Generics) that its easier to
look at its test specifiction (written in <a href="http://docs.spockframework.org/en/latest/">Spock</a>) to see what it
is supposed to do:
<script src="https://gist.github.com/4693138.js"></script>
<p>The first pass at a Clojure version is already simpler than the Java version ...</p>
<script src="https://gist.github.com/4693160.js"></script>
<p>I couldn't resist using
the <code><a href="http://clojuredocs.org/clojure_core/clojure.string/join">clojure.string/join</a></code> function,
rather than building the string directly (which would be slightly tedious in Clojure). In many ways, this is a lot
like the Java version; we're using <code>let</code> to create local symbols for each step in the process in just the
same way that the Java version defines local variables for each step.</p>
<p>However, there's room for improvement here. Let's start to <em>craft</em>. </p>
<p>For example, let's assume that both maps being empty is rare, or at least, that the cost of sorting an empty list is
low (it is!). Our code becomes much more readable if we merge it into one big <code>let</code>:</p>
<script src="https://gist.github.com/4693219.js"></script>
<p>Now we're getting somewhere. I think this version makes it much more clear what is going on that the prior Clojure
version, or the Java version.</p>
<p>However, if you've written enough code, you know one of the basic rules of all programming: <strong>names are
hard</strong>. Anything that frees you from having to come up with names is generally a Good Thing. In Java, we have
endless names: not just for methods and variables, but for classes and interfaces ... even packages. Long years of
coding Java has made me dread naming things, because names never quite encompass what a thing does, and often become
outdated as code evolves.</p>
<p>So, what names can we get rid of, and how? Well, if we look at the structure of our code, we can see that each step
creates a value that is passed to the next expression as the final parameter. So <code>all-keys</code> is passed as
the last parameter of the <code>(map)</code> expression, resulting in <code>key-names</code>, and
then <code>key-names</code> is passed as the last parameter of the <code>(sort)</code> expression. In fact, ignoring
the empty check for a moment, the <code>sorted-names</code> value is passed to the <code>(s/join)</code> expression as
the last parameter as well.</p>
<p>This is a very important concept in Clojure; you may have heard people trying to express that you code in Clojure in
terms of a "flow" of data through a series of expressions. We'll, you've just seen a very small example of this.</p>
<p>In fact, it is no simple coincidence that the last parameter is so important; this represents a careful and reasoned
alignment of the parameters of many different functions in clojure.core and elsewhere, to ensure that <em>flow</em> can
be passed as that final parameter, because it becomes central to the ability to combine functions and expressions
together with minimal fuss.</p>
<p>We can use the <code><a href="http://clojuredocs.org/clojure_core/clojure.core/-%3E%3E">->></a></code> macro
(pronounced "thread last") to
rebuild our flow without having to come up with names for each step:
<script src="https://gist.github.com/4693306.js"></script>
<p>
The <code>->></code> macro juggles our expressions into an appropriate order; without it we'd have to deeply
nest our expressions in an unreadable way: <code> (sort (map str (set (concat (keys map1) (keys map2)))))</code>. Even
with a short flow of expressions, that's hard to parse and interpret, so <code>->></code> is an invaluable and
frequently used tool in the Clojure toolbox.</p>
<p>We can continue to craft; the first expression (that builds the set from the keys), can itself be broken apart into a
few smaller steps. This is really to get us ready to do something a bit more dramatic:</p>
<script src="https://gist.github.com/4693353.js"></script>
<p>This is getting ever closer to our original recipe; you can more clearly see the extraction of keys from the maps
before building the set (which is only used to ensure key uniqueness), before continuing on to convert keys from objects
to strings, sort them, and combine the final result.</p>
<p>
In fact, we're going to go beyond our original brief, and support any number of input maps, not just two:</p>
<script src="https://gist.github.com/4693377.js"></script>
<p>The <code><a href="http://clojuredocs.org/clojure_core/clojure.core/mapcat">mapcat</a></code> function is like map,
but expects that each invocation will create a collection; <code>mapcat</code> concatinates all those collections together ... just
what we want to assemble a collection of all the keys of all the input maps.</p>
<p>
At this point, we don't have much more to go ... but can we get rid of the <code>sorted-names</code> symbol? In fact,
we can: what if part of our flow replaced the empty list with a list containing just the string "<none>"? It
would look like this:</p>
<script src="https://gist.github.com/4693400.js"></script>
<p>
... and that's about as far as I care to take it; a clean flow starting with the maps, and going through a series of
expressions to transform those input maps into a final result. But what's really important here is just how fast and
easy it is to start with an idea in Clojure and refine it from something clumsy (such as the initial too-much-like-Java
version) into something elegant and surgically precise, such as the final version.</p>
<p>That's simply not something you can do in less expressive languages such as Java. For example, Tapestry certainly
does quite a number of wonderful things, and supports some very concise and elegant code (especially
in <a href="http://tapestryjava.blogspot.com/2013/01/red-code-green-code-my-code-your-code.html">green code</a>) ...
but that is the result of organizing large amounts of code in service of specific goals. We're talking tons of
interfaces, a complete Inversion-Of-Control container, and runtime bytecode manipulation to support that level of
conciseness. That's the hallmark of a quite consequential framework.</p>
<p>That isn't <em>crafting</em> code; that's a big engineering effort. It isn't local and invisible, it tends to be
global and intrusive.</p>
<p>In Java, your only approach to simplifying code in one place is build up a <em>lot</em> of complexity somewhere else.</p>
<p>That is simply not the case in Clojure; by adopting, leveraging, and extending the wonderful patterns already present
in the language and its carefully designed standard library, you can reach a high level of readability. You are no
longer coding to make the compiler happy, you are in control, because the Clojure languge gives you the tools you need
to be in control. And that can be intoxicating.</p>
<p>The source code for this blog post is available on <a href="https://github.com/hlship/clojure-blog-post-20130201">GitHub</a>.</p>
Anonymoushttp://www.blogger.com/profile/04486596490758986709noreply@blogger.com12tag:blogger.com,1999:blog-4110180.post-62116154406475149542013-01-30T14:28:00.000-08:002013-01-30T14:28:13.288-08:00Red Code, Green Code, My Code, Your Code<p>
I had a very odd interchange with my friend Merlyn over lunch; he started talking about red code vs. green code (in the context of supporting both callbacks and promises inside NodeJS). At first I thought he was referring to code coverage of those lines of code ... the implication being that supporting multiple paths of execution may lead to laziness in testing every possible path (with some code going "red" in the code coverage report).
<p>
But that wasn't it at all: "red" code referred to framework code, "green" code referred to end-user code, leveraging the framework.
<p>
Odder still, Merlyn insisted that he first heard this term from ... <strong>me</strong>, a few years ago. That's what being in the baby-raising camp can do to you ... I know longer have any idea of what I've said or thought in the past. Actually, this isn't new for me ... I've always had a very vague memory for anything not code.
<p>In any case, this red vs. green terminology is a useful concept ... certainly I move the earth in Tapestry's red code to make end-user's green code as simple as possible.
<p>So, I want to do three things:
<ul>
<li>It's definitely worth reflecting on the fact that all code is <strong>not</strong> created equal, and that long-lived, reusable code ("red") will inevitably grow in complexity to a level that would not be acceptable in client ("green") code.</li>
<li>Let's promote this term, because it is so handy!</li>
<li>If I didn't create the term myself, let's track down the originator and thank them!</li>
</ul>Anonymoushttp://www.blogger.com/profile/04486596490758986709noreply@blogger.com2tag:blogger.com,1999:blog-4110180.post-32308976343604537182013-01-02T12:11:00.001-08:002013-01-02T12:14:39.656-08:00Tapestry 5.4: jQuery Support now in place<p>I've spent the last several months significantly reworking Tapestry 5's client-side JavaScript support, in an effort to move away form the tight binding to Prototype. After all of that refactoring, recoding, repositioning, and just-plain-hacking, I was able to do most of the job of introducing jQuery support in just a few hours, yesterday and today.
<p>
I'll be producing another preview release pretty soon, or you can get the lastest from Tapestry's master branch.
<p>
Currently, the code to switch over from using Prototype to jQuery looks like this (it will get simpler soon):
<script src="https://gist.github.com/1fb6eca0c1c6d28eb2a5.js"></script>
<p>The first part overrides the provider to be "jquery" (the default provider is "prototype"). In Tapestry terms, the "infastructure framework" provides the APIs for DOM queries, DOM manipulation, and Ajax request handling. If you don't like jQuery, you can easily create your own provider for your favorite framework.
<p>
Part of 5.4 is trying to manage all these different compatibility issues at a slightly higher level than configuration symbols; that's the Compatibility service with its Traits. Those two traits disable Scriptaculous support (which isn't needed by jQuery, and won't work without Prototype), and disables support for Tapestry 5.3-style initializers.
<p>
Underneath the covers, the way the switch between Prototype and jQuery works is quite simple:
<script src="https://gist.github.com/e8ef548553e015c5eeec.js"></script>
<p>
The ModuleManager service is the code that handles requests for modules from the client. It has a configuration that is used to handle edge cases, such as treating traditional JavaScript libraries as if they were AMD modules.
The code above sets up a server-side override for the module <code>t5/core/dom</code>. All the Tapestry client-side modules use <code>dom</code>; none of them uses Prototype or jQuery directly (except for a couple that access Twitter Bootstrap functionality).
<p>
These contributions ensure that when the client-side requests the <code>t5/core/dom</code> module, what will be sent back will be either the Prototype-specific implementation, or the jQuery-specific implementation. Without this contribution, we'd see a 404 error when the <code>dom</code> module was requested. Instead, the client-side doesn't have any idea that <code>dom</code> is special.
<p>
The primary job of the <code>dom</code> module is to wrap DOM elements inside a new object, thereby providing a new API that allows various kinds of manipulation, as well as listening to events, or triggering them. The API is a bit of a mashup between Prototype and jQuery, but leans pretty heavily towards jQuery-style operations and naming. <code>dom</code>'s secondary job is to support Ajax requests, plus adding event handlers to the document object.
<p>In practice, this can be very concise and readable (partially, thanks to CoffeeScript):
<script src="https://gist.github.com/4437380.js"></script>
<p>
In this listing (which supports some of the behavior of the Tapestry Zone component), <code>dom</code> holds the export from module <code>t5/core/dom</code>; it waits for the <code>events.zone.update</code> event to be triggered; the callback is invoked with <code>this</code> set to the wrapper around the element where the event was triggered.
<p>The event here is also a wrapper; its a minimal mix of Prototype and jQuery: I like the <code>memo</code> property from Prototype, so that's present. The event handler triggers a pair of before and after events, and updates the content of the zone. Why the before and after events? By default they do nothing, but it would be simple to add handlers to perform some kind of animation when content is added.
<p>
In any case, because all Tapestry client-side modules code against this API, they don't know or care whether the page has loaded Prototype, jQuery, or something else. If you are writing Tapestry components for reuse, coding against the <code>dom</code> API will help ensure that your component will work correctly in all kinds of Tapestry applications. However, when coding an application,. you reserve the right to choose what the infrastructure framework should be: you should feel free to use the infrastructure framework directly ... unless you find the <code>dom</code> API to be easier.
Anonymoushttp://www.blogger.com/profile/04486596490758986709noreply@blogger.com0tag:blogger.com,1999:blog-4110180.post-44299019845426293902012-11-06T13:22:00.001-08:002012-11-06T13:23:15.245-08:00How we can be more secure with mail-in ballots than touch screens<p>Yes, it's election day! I didn't wait in line ... I live in Oregon, where balloting is done by mail. That's a much better system.
<p>Of course, people are suspicious of voting electronically, or by mail. Somehow the mechanical interaction reassures people. However, security has been an issue for the entire history of voting. Ballots can be "misplaced", or electronically stored values from modern voting machines can be invisibly tampered with, after the fact. Certainly, as it is election day, we're hearing stories about devices that make it hard to select specific candidates ... and the conspiracy theorists are certain that the fix is in.
<p>It may be, but the cases I've heard of are simply bad touch screens. I wonder what the conspiracy theorists believe is going on when they attempt to withdraw $25 from an ATM and get $10 instead?
<p>Real attempts to fix the elections don't change what you enter; they change what is stored ... possibly long after you have left the voting booth.
<p>I would say that having fewer centralized counting locations would be more secure. First of all, we could use (as we do in Oregon), optical scanners ... the same, basic, trusted technology that's used for the SATs.
<p>Next, the process of opening the secure envelopes and feeding them to the scanners could be more easily monitored if it occurred in a few locations across each state, rather than in every polling location across the entire country. This process is subject to fraud, yes, but is probably more likely to be affected by simple incompetence.
<p>
With physical ballots, it is also possible to do a proper recount. Voting on a touch screen is too ephemeral for a recount to have any meaning.
<p>Ultimately, there is no way to ensure that your vote is actually counted. However, I think we can come up with a way in which we can at least ensure that your ballot made it to the processing center.
<p>Imagine if the ballot mailed to your home had a unique bar code on it, as well as a "tear off" tab with the corresponding unique number. In addition, there is a section on the ballot where you could enter a four or six digit PIN code of your own choice.
<p>
Later, at home, you visit a web site: you provide your unique, randomly assigned ballot id as well as your own secret, personally-selected, PIN code. The government web site (built using open-source software, of course) could then identify the status of your vote, just like UPS can tell you where your package is.
<p>
Since PIN codes are ultimately guessable, I don't think I'd want the system to do more than confirm the processing of the ballot. However, to someone like me, it would go a lot further towards assuring me that, yes, my vote was counted than anything I've used in the past ... and certainly it would be far more reassuring than the buggy, mis-calibrated, poorly designed touch screen devices in use today.
<p>
Further, I like the idea of the vote taking shape over the course of a few days, or even a week. Vote by mail from a week before "election day". Maybe the ballots accumulate but are not counted until the end of the election itself ... again, something that the ballot status check outlined above would help to ensure.
<p>
The big weakness of this is privacy concern over linking your identity to a specific ballot. Who would be able to view your vote at a later date? The answer to that should be: nobody; that is not only a sacred tradition, but the fact that your private vote can never be retrieved and used against you in any way at a later date is important to keep voting free and uncoerced.
<p>
So imagine if your ballot arrived with four or five unique id stickers, each five or ten digits long: you chose, say, three of them, in whatever order you like and place them on the ballot. Nothing else on the ballot could link that specific ballot to you ... but that now partially-random ballot id can still be tracked to the central counting location. Only you would have a link between your identity and the ballot. I leave it to someone smarter than me to determine just how many "stickers" are needed to ensure reasonably anonymity (and prevent any potential ballot id conflicts) but I think the basic concept is valid.
Anonymoushttp://www.blogger.com/profile/04486596490758986709noreply@blogger.com0tag:blogger.com,1999:blog-4110180.post-46443129083854634012012-10-19T12:52:00.000-07:002012-10-19T12:52:20.620-07:00Zeroing in on Tapestry 5.4<p>I've had just a bit of time this week to devote to furthering the work on Tapestry 5.4, and it feels like I'm near the turning point. You can follow the progress in the <a href="https://git-wip-us.apache.org/repos/asf?p=tapestry-5.git;a=log;h=refs/heads/5.4-js-rewrite">5.4-js-rewrite branch</a>
<p>I've <a href="http://tapestryjava.blogspot.com/2011/11/tapestry-54-focus-on-javascript.html">discussed my plans before</a>; this is a bit of a progress update.
<p>Basically, this release will be all about JavaScript:
<ul>
<li>Removing the hard-dependence on <a href="http://prototypejs.org/">Prototype</a></li>
<li>Creating an abstraction layer, allowing <a href="http://jquery.com/">jQuery</a> to be used instead</li>
<li>Introducing AMD (<a href="https://github.com/amdjs/amdjs-api/wiki/AMD">asynchronous module definition</a>) modules (using <a href="http://requirejs.org/">RequireJS</a>)</li>
<li>Switching to a more declarative style of JavaScript; leveraging <a href="http://ejohn.org/blog/html-5-data-attributes/">HTML5 data- attributes</a></li>
</ul>
<h3>Prototype vs. jQuery</h3>
<p>Tapestry has historically used Prototype as what I call its <strong>foundation</strong> framework. The foundation is responsible for element selection and creation, attaching event handlers, triggering events, and encapsulating Ajax requests.
<p>It is already possible to have both Prototype and jQuery on the same page; I do it regularly. There's a project for this as well, <a href="http://tapestry5-jquery.com/">tapestry-jquery</a>. That being said, there's some costs in having multiple foundation frameworks in place:
<p>First of all is <em>size</em>; there's a lot of code in each framework (160Kb for Prototype, 260Kb for jQuery -- much less after minification and compression), and tremendous overlap. This includes the fact that both frameworks have a version of the <a href="http://sizzlejs.com/">Sizzle CSS selector engine</a>.
<p>There's also some occasional clashes; I had a nasty problem where some <a href="http://twitter.github.com/bootstrap">Bootstrap</a> JavaScript was firing a "hide" event when a modal dialog was dismissed. After much tracing and hair pulling, I discovered that jQuery will treat a function attached to an element as an event handler for the event with the matching name. The "hide" event triggered by jQuery found the <code>hide()</code> method added by Prototype, and my modal dialog just winked out of existence, rather than animation the way I wanted.
<p>Finally, its not just an either-or world; there's also YUI, ExtJS, MooTools ... over the last few years, every single time I mentioned my desire to introduce an abstraction layer, I've received the question "will it support X?" and "X" was some new framework, that person's favorite, that I had not previously heard of.
<p>
So the abstraction layer will be important for helping ensure a fast download of JavaScript, and to minimize the amount of network and JavaScript execution overhead.
<p>
If you are an application developer, there will be nothing to prevent you from using the native framework of your choice, directly. In many cases, this will be the best approach, and it will enable greater efficiency, access to a more complete API, and access (in jQuery's case) to a huge community of plugins.
<p>
If you are creating reusable components, it is best to try to use the SPI (the service provider interface; the abstraction layer). This allows your library to be widely used without locking your users into any particular foundation framework.
<h3>Modules vs. Libraries</h3>
<p>Tapestry 5.3 and earlier have provided some great support for JavaScript in library form. The concept of a <a href="http://tapestry.apache.org/current/apidocs/org/apache/tapestry5/services/javascript/JavaScriptStack.html">JavaScriptStack</a> makes it possible to bundle any number of individual JavaScript libraries together, along with supporting CSS style sheet files. Even better, Tapestry can create a single virtual JavaScript library by concatenating all the stack libraries together. Tapestry does this at runtime, and conditionally ... so during development, you can work with many small JavaScript files and in production, they become one combined, minimized JavaScript file.
<p>
However, this JavaScript approach has its own problems. Page initialization logic is very dependent on the stacks (including the foundation frameworks) being present and loaded in a specific order. No page initialization logic can execute until all libraries have been loaded. If you have multiple stacks in production, then all the stacks must load before any logic executes.
<p>
All of this affects page load time, especially the time perceived by the end user. Too much is happening sequentially, and too much is happening over-all.
<p>In Tapestry 5.4, the focus is shifting from libraries to modules. Under AMD, a module is a JavaScript function that expresses a dependency on any number of other modules, and exports a value to other modules. The exported value may be a single function, or an object containing multiple functions (or other values).
<p>Here's a snapshot of one of modules, which provides support for the <a href="http://tapestry.apache.org/current/apidocs/org/apache/tapestry5/corelib/components/Zone.html">Zone</a> component:
<script src="https://gist.github.com/3919899.js?file=zone.coffee"></script>
<p>The <code>define()</code> function is used to define a module; the first parameter is an array of module names. The second parameter is a function invoked once all the dependencies have themselves been loaded; the parameters to <em>that</em> function are the exports of the named modules.
<p>This function performs some initialization, attaching event handlers to the document object; it also defines and <em>exports</em> a single named function ("deferredZoneUpdate") that may be used by other modules.
<blockquote>
Yes, I have a module named "_" which is <a href="http://underscorejs.org/">Underscore.js</a>. I'll probably add "$" for jQuery. Why not have short names?
</blockquote>
<p>The RequireJS library knows how to load individual modules and handle dependencies of those modules on others. Better yet, it knows how to do this <em>in parallel</em>. In fact, in 5.4, the only JavaScript loaded using a traditional <code><script></code> tag is RequireJS; all other libraries and modules are loaded through it.
<p>
At the end of the day, there will be less JavaScript loaded, and the JavaScript that is loaded will be loaded in parallel. I'm still working on some of the details about how module libraries may be aggregated into stacks.
<h3>Declarative JavaScript</h3>
<p>Even in earlier forms of Tapestry, JavaScript support has been powerful ... but clumsy. To do anything custom and significant on the client-side you had to:
<ul>
<li>Write a JavaScript library for your functionality</li>
<li><a href="http://en.wikipedia.org/wiki/Monkey_patch">Monkey-patch</a> a function onto the <code>T5.initializers</code> namespace</li>
<li>Package up your initialization data as a <a href="http://tapestry.apache.org/current/apidocs/org/apache/tapestry5/json/JSONObject.html">JSONObject</a> ... typically, client element ids, URLs, etc.</li>
<li>Have you Java component code invoke <a href="http://tapestry.apache.org/current/apidocs/org/apache/tapestry5/services/javascript/JavaScriptSupport.html#addInitializerCall(java.lang.String, org.apache.tapestry5.json.JSONObject)">JavaScriptSupport.addInitializerCall(String, JSONObject)</a> to link it all together.
</li>
</ul>
<p>Tapestry packages up all those <code>addInitializerCall()</code> initializations into one big block that executes at the bottom of the page (and does something similar for Ajax-oriented partial page updates).
<p>Whew! To make this works requires that elements have unique ids (which can be a challenge when there are Ajax updates to the page). On top of that, the typical behavior is to create controller objects and attach event handlers directly to the elements; that works fine for small pages, but if there are hundreds (or thousands) of elements on the page, it turns into quite a burden on the JavaScript environment: lots of objects.
<p>
There isn't a specific name for this approach, beyond perhaps "crufty". Let's call it <strong>active</strong> initialization.
<p>
A more modern approach is more <em>passive</em>. In this style, the extra behavior is defined primarily in terms of events an element may trigger, and a top-level handler for that event. The events may be DOM related such as "click", "change", or "focus", or application-specific one triggered on the element directly. The top-level handler, often attached to the document, handles the event when it bubbles up from the element to the top level; it often distinguishes which elements it is interested using a CSS selector that includes references to a data- attribute.
<p>For example, in the core/forms module, we need to track clicks on submit and image buttons as part of the form (this is related to supporting Ajax form submissions).
<script src="https://gist.github.com/3920111.js?file=forms.coffee"></script>
<p>That little bit of code attaches a "click" event handler to the document, for any submit or image element anywhere on the page ... or ever added to the page later via Ajax. Older versions of Tapestry might have put an individual event handler on each such element, but in 5.4, the single event handler is sufficient.
<p>
Inside the event handler, we have access to the element itself, including data- attributes. That means that what may have been done using page initialization in Tapestry 5.3 may, in Tapestry 5.4, be a document-level event handler and data- attributes on the specific element; no JSONObject, no <code>addInitializerCall()</code>. Less page initialization work, smaller pages, fewer objects at runtime.
<blockquote>
The flip side, however, is that the cost of triggering the extra events, and the cost of all the extra event bubbling, is hard to quantity. Still, the 5.3 approach introduces a lot of memory overhead at all times, whereas the 5.4 approach should at worst, introduce some marginal overhead when the user is actively interacting.
</blockquote>
<p>
There's another advantage; by attaching your own event handlers to specific elements, you have a way to augment or replace behavior for specific cases. It's kind of a topsy-turvy version of inheritance, where the behavior of an element is, effectively, partially determined by the elements it is contained within. Kind of crazy ... and kind of just like CSS.
<h3>Compatibility</h3>
<p>So you've written a fancy application in Tapestry 5.3 and you are thinking about upgrading to 5.4 when it is ready ... what should you be ready for?
<p>On the server side, Tapestry 5.4 introduces a number of new services and APIs, but does not change very much that was already present.
<blockquote>
A number of interfaces and services that were deprecated in the past have been removed entirely; even some dependencies, such as <a href="http://www.jboss.org/javassist">Javassist</a>.
</blockquote>
<p>
All built-in Tapestry components will operate through the SPI; they will work essentially the same regardless of whether you choose to operate using Prototype or jQuery or both.
<p>
The look-and-feel is changing from Tapestry's built-in CSS to Twitter Bootstrap. All of the old "t-" prefixed CSS classes are now meaningless.
<p>
On the other hand, what if you've built some complex client-side code? Well, if it is based on Prototype directly, that will be fine as well; just keep Prototype in the mix and start migrating your components to use the SPI.
<p>
The devil in the details is all about the Tapestry and T5 namespaces. The Tapestry object is the very oldest code; the T5 namespace was introduced in 5.2 in an attempt to organize things on the client-side a bit better.
<p>
It is not clear, at this time, just how much of those libraries will be left. Ideally, they will not be present at all unless an explicit compatibility mode is enabled (possibly, enabled by default). That being said, the conflict between the old active/crufty approach, and the new modular and passive/declarative approach is a difficult one to bridge.
<p>
In fact, the point of this post is partly to spur a discussion on what level of compatibility is required and realistic.
<h3>Conclusion</h3>
<p>I've completely pumped about where Tapestry 5.4 already is, and where it is headed. Although the future of web applications is on the client, there is still significant play for hybrid applications: partly page oriented, partly Ajax oriented – and it's nice to have a single tool that integrates both approaches.
<p>Beyond that, even for a Tapestry application that is a single "page", what Tapestry brings to the table is still very useful:
<ul>
<li>live class reloading
<li>JavaScript stacks (with minification, Gzip compression, and client-side caching)
<li>the coming runtime CoffeeScript-to-JavaScript support
<li>best-of-breed exception reporting
<li>staggeringly high performance
</ul>
<p>These benefits are all as compelling in a single page application as they are in a traditional application, if not more so. I'm looking forward to building even more impressive apps in 5.4 than I could accomplish in 5.3, and I hope a lot of you will join me.
Anonymoushttp://www.blogger.com/profile/04486596490758986709noreply@blogger.com5tag:blogger.com,1999:blog-4110180.post-54008975925485410302012-10-15T11:07:00.000-07:002012-10-16T12:26:43.133-07:00Not going to Clojure/Conj ... need a ticket?<p>Unfortunately, I will <strong>not be going to <a href="http://clojure-conj.org">Clojure/Conj</a> this year</strong>. And it is too late for a refund ... so I'm looking for someone to purchase my ticket at cost. The good news is the reason I'm not going ... <strong>I have a new project coming that will use Tapestry, Clojure, and perhaps <a href="http://www.datomic.com/">Datomic</a></strong> and I need to be on-site with them the week of the conference.
<p>So if you are looking to attend the conj, you can take over my ticket, for <strong>$475</strong>. Please contact me via email. Thanks!
<p>
<strong>Update: Sorry, the ticket is gone.</strong>Anonymoushttp://www.blogger.com/profile/04486596490758986709noreply@blogger.com3tag:blogger.com,1999:blog-4110180.post-28036020711080442512012-09-14T10:12:00.001-07:002013-01-03T09:06:24.966-08:00Stop sending template engines to the browser!<p>This <a href="http://andyet.net/blog/2012/sep/13/stop-sending-template-engines-to-the-browser-a-ret/">article about client-side templating</a> by Henrik Joreteg was quite interesting. In summary, it says don't send templates to the browser to be compiled and executed ... convert them to JavaScript functions that can be efficiently transfered to the browser and directly executed there.
<p>Currently, Tapestry does virtually all of its markup rendering on the server ... but with the <a href="http://tapestryjava.blogspot.com/2011/11/tapestry-54-focus-on-javascript.html">upcoming changes to Tapestry for 5.4,</a> that may start to shift, with increasing amounts of the presentation being rendered in-browser.
<p>
I'm already mid-way through building a module structure for Tapestry and I'm quite pleased with what I have so far. Part of that infrastructure is the ability to compile CoffeeScript to JavaScript on the fly. The tapestry-core module will provide the basic infrastructure, and a new module will provide the actual runtime compiler.
<blockquote>
I prefer for these kind of compilations and transformations to occur at runtime, rather than build time. It means no special build rules (in Maven or Gradle), no extra step when changing a file and reloading it in the browser, and it makes it easier in many ways for Tapestry to deliver the optimum bundle of compiled, aggregated, minified, and GZip-compressed JavaScript to the browser ... without having awkward conditionals and naming conventions to choose the correct version of each JavaScript file (not to mention ensuring that the minified version is derived from the correct non-minified version). Yes, there's a one-time startup cost, but that's always been a tradeoff I'm willing to make ... balanced by how much of Tapestry is lazy-loaded as needed.
</blockquote>
<p>
However, the infrastructure is actually more flexible than that; it would be quite reasonable to do something like Henrick's solution: there's no reason why the infrastructure could not be used to read a template file and convert it to JavaScript source that can be exposed on the client as a module.
<p>
That raises another interesting idea; a template-as-a-module would then be a uniform way to package pure data (a JSON object bundle of properties) with the name of a module that renders that specific data into markup. That also opens up some terrific ways to have the server efficiently deliver updates to the client, in a data-focused way (rather than the markup-focused way provided by the <a href="http://tapestry.apache.org/current/apidocs/org/apache/tapestry5/corelib/components/Zone.html">Zone</a> component). Combine that with server-push, and you have something really powerful brewing together!
Anonymoushttp://www.blogger.com/profile/04486596490758986709noreply@blogger.com3tag:blogger.com,1999:blog-4110180.post-26973200994037524472012-08-14T13:47:00.000-07:002012-08-15T13:01:08.829-07:00CoffeeScript Cautions<p>Over the last couple of months, I've coded nearly all my client-side code in <a href="http://coffeescript.org">CoffeeScript</a>. I prefer CoffeeScript to JavaScript ... but there are a few things to watch out for. I thought I'd share some practical experience, rather than the more typical hype for CoffeeScript or the reactionary vibe against it.
<p>My biggest problems with CoffeeScript has been with indentation, which is no surprise, given the basic value proposition of the language. There are places where I've accidentally deleted a single space and completely changed the meaning of my code.
<p>
Here's an adapted example. We'll start with the correct CoffeeScript source code, and JavaScript translation:
<script src="https://gist.github.com/3352145.js"> </script>
<p>Now, notice what happens when we delete <em>a single space</em>.
<script src="https://gist.github.com/3352159.js"> </script>
<p>The change in indentation confused the CoffeeScript compiler; it ended the call to <code>View.extend()</code> and created a new expression for an object literal that is created and thrown away.
<p>And remember, this isn't hypothetical; I really did this, and started seeing very odd errors: undefined properties and events not getting triggered. It took me a bit of time, with the debugger, and adding console logging, and eventually some viewing of the generated JavaScript, to realize what had happened.
<p>Part of my solution to this class of issue, in Emacs, is to automatically increase the font size when editing CoffeeScript files. I may have to consider a switch from spaces to tabs as well, something that I've never considered in the past. I've also noticed that some IDEs, including IntelliJ, draw a vertical line to connect code at the same indentation layer. I don't have enough Emacs hacking experience to accomplish that, but I hope someone does.
<p>Another problem I've hit is with function invocation; CoffeeScript style is to omit the parenthesis following the function name, and just immediately start listing function parameters:
<pre>
element.setAttribute "width", "100%"
</pre>
<p>
However, if your API allows chaining, you may be tempted to:
<pre>
element.setAttribute "width", "100%".setAttribute "color", "blue"
</pre>
<p>Alas, the <code>.setAttribute</code> binds to the immediate value, the string:
<pre>
element.setAttribute("width", "100%".setAttribute("color", "blue"));
</pre>
<p>
One option is to enclose the first call with parenthesis:
<pre>
(element.setAttribute "width", "100%").setAttribute "color", "blue"
</pre>
<p>I <em>want</em> to like that option, as it stays consistent with the normal format for function invocation; it just captures the result for chaining. However, each additional <code>setAttribute()</code> call will require an additional nesting layer of parenthesis. This leaves the following as the solution:
<pre>
element.setAttribute("width", "100%").setAttribute("color", "blue")
</pre>
<p>And now we are stuck having to parse two different syntaxes for function invocation, depending on some very specific context.
<p>
Another issue is that, once you start using the standard function invocation style, which doesn't use parenthesis, you can easily get tripped up:
<script src="https://gist.github.com/3352466.js"> </script>
Here the <code>is null</code> bound tightly to the <code>"div"</code> string literal; the result (true or false) was passed to the <code>find()</code> method, rather than the expected <code>"div"</code>.
<p>
Because of these subtleties, I seem to find myself using the live CoffeeScript to JavaScript preview to check that my guess at how the JavaScript will be generated is correct. Preview is available on the CoffeeScript home page, and also as the CoffeeConsole plugin to Chrome.
<p>
What's interesting is that the above concerns aside, most of the time my first pass at the CoffeeScript does produce exactly what I'd expect, making me feel silly to waste the time to check it! Other times, I find that my first pass is actually too verbose, and can be simplified. Here is an example from some code I've written for Tapestry, a simple DSL for constructing DOM elements on the fly:
<script src="https://gist.github.com/3352278.js?file=v1.coffee"></script>
This is invoking the <code>builder()</code> function, passing a string to define the element type and CSS class, then an object that adds attributes to the element, and lastly some additional values that define the body of the element: an array to define a nested element, and a string that will become literal text. This is converted to JavaScript:
<script src="https://gist.github.com/3352278.js?file=output.js"></script>
<p>
Turns out, we can get rid of the curly braces on the objects without changing the output JavaScript; CoffeeScript is able to figure it all out by context:
<script src="https://gist.github.com/3352278.js?file=v2.coffee"></script>
<p>
Even getting rid of the commas is allowed. The commas are equivalent to the line break and indentation that's already present:
<script src="https://gist.github.com/3352278.js?file=v3.coffee"></script>
<p>
And that's where I start really liking CoffeeScript, because quite often, the code you write looks very close to a bespoke DSL.
<p>
Then there's the debugging issue; CoffeeScript scores poorly here. First of all, the code has been translated to JavaScript; line numbers at execution time will no longer line up with source CoffeeScript at all, so the first and most useful bit of information in a stack trace is no longer useful. It's very easy to glance back and forth to match the generated JavaScript back to source CoffeeScript, but it still takes some effort.
<p>
Of course, in a production application, you'll likely have minimized your JavaScript, which mangles your JavaScript and your line numbers far more than the CoffeeScript compilation does!
<p>
The other weakness is that all functions in CoffeeScript are anonymous; this takes <em>another</em> useful bit of information out of stack traces: the local function name. This leaves the only way to map from compiled JavaScript back to source CoffeeScript as involving a lot of processing between your ears, and that's not exactly ideal, given everything else you have to mentally juggle.
<p>
Lastly, I've heard of people who think CoffeeScript is <em>only</em> for programming using objects and classes. I find this quite odd; it's nice to have some optimized syntax for classes in CoffeeScript even if it comes at some price (the generated JavaScript can become quite verbose adding runtime support for the syntax that allows super class methods and constructors to be invoked); however, far beyond 95% of the code I write is purely functional, with what state that's present captured in closures of local variables, they way it should be.
<p>
I like Scott Davis' summary: <strong>CoffeeScript will make you a better JavaScript programmer</strong>. It is, largely, JavaScript with better syntax, plus a number of new features. You should never think of CoffeeScript as an <em>alternative</em> to understanding the occasionally ugly nuances of JavaScript; instead I think of it as a "have your cake and eat it too" situation. I certainly don't consider it <strong>noobscript</strong>, the way many in the Node.js community seem to.
<p>At the end of the day, the translation from CoffeeScript to JavaScript is usually quite predictable, and turns into the JavaScript you probably <em>should</em> write, but often <em>would not</em>: for instance, JavaScript that always evaluates inside a hygienic function, or where every variable is defined at the top of its containing block.
<p>
My final take is that you are trading the very rough edges present in the JavaScript language for some sensible tradeoffs, and the occasional hidden "gotcha". I think that's a completely reasonable trade, and will continue to write as much as I can, client-side <em>and</em> server-side, in CoffeeScript.
Anonymoushttp://www.blogger.com/profile/04486596490758986709noreply@blogger.com6tag:blogger.com,1999:blog-4110180.post-2213998200450879682012-08-01T17:48:00.002-07:002012-08-01T17:49:27.356-07:00Tapestry 5 Book by Igor Drobiasko<p><div class="separator" style="clear: both; text-align: center;">
<div style="float: right; margin-left: 10px">
<iframe src="http://www.indiegogo.com/project/188439/widget/951100" width="224px" height="429px" frameborder="0" scrolling="no"></iframe>
</div>
</div>
Is the one thing holding you back from embracing Tapestry the lack of a current book? Well, that excuse is finally going by the wayside; Igor Drobiazko, a Tapestry contributor (second in activity only to me) is getting ready to finish and self-publish his book on Tapestry 5.3: <strong>Tapestry 5: Rapid Web Application Development in Java</strong>
<p>
Want early access to the book? Join his <a href="http://www.indiegogo.com/tapestry5book">funding campaign at indiegogo</a>.
<p>Igor hit his goal on day 1, but for as little as $25 you can get access to at least 400 pages of content as soon as the campaign ends, and the complete book when it's ready. You can also help assure that the book will be updated for the great improvements already cooking up in Tapestry 5.4.Anonymoushttp://www.blogger.com/profile/04486596490758986709noreply@blogger.com1tag:blogger.com,1999:blog-4110180.post-83904304208692219372012-08-01T09:59:00.001-07:002012-08-01T10:02:08.855-07:00More async: using auto() for parallel operations<p>One of the straw men that people often cite when discussing event-driven programming, ala <a href="http://nodejs.org/">Node.js</a>, is the fear that complex server-side behavior will take the form of unmanageably complex, deeply nested callbacks.
<p>Although the majority of the code I've so far written with Node is very simple, usually involving only a single callback, my mental image of Node is of a request arriving, and kicking off a series of database operations and other asynchronous requests that all combine, in some murky fashion, into a single response. I visualize such a request as a <a href="http://en.wikipedia.org/wiki/Pinball">pinball</a>
dropping into some bumpers and bouncing around knocking down targets, until shooting out, back down to the flippers.
<p>Here's an example workflow from the sample application I'm building; I'm managing a set of images used in a slide show; so I have a SlideImage entity in <a href="http://www.mongodb.org/">MongoDB</a> (using <a href="http://mongoosejs.com/">Mongoose</a>), and each SlideImage references a file stored in <a href="http://mongodb.github.com/node-mongodb-native/api-generated/gridstore.html">Mongo's GridFS</a>.
<p>
When it comes time to delete a SlideImage, it is necessary to delete the GridFS file as well. The pseudo-code for such an operation, in a non-event based system, might look something like:
<script src="https://gist.github.com/3228254.js?file=gistfile1.txt"></script>
<p>
Inside Node, where all code is event-driven and callback oriented, we should be able to improve on the pseudo-code by doing the deletes of the SlideImage document, and the GridFS file, in parallel. Well, that's the theory anyway, but I'd normally stick to a <a href="http://tapestryjava.blogspot.com/2012/07/a-little-gotcha-with-asynch-and-streams.html">waterfall approach</a>, as tracking when all operations have completed would be tedious and error prone, especially in light of correctly handling any errors that might occur.
<p>
Enter <a href="https://github.com/caolan/async/#autotasks-callback">async's <code>auto()</code> function</a>. With auto(), you define a set of tasks that have dependencies on each other. Each task is a function that receives a callback, and a results object. auto() figures out when each task is ready to be invoked.
<p>When a task fails, it passes the error to the callback function. When a task succeeds, it passes null and a result value to the callback function. Later executing tasks can see the results of prior tasks in the result object, keyed on the prior tasks's name.
<p>As with <code>waterfall()</code>, a single callback is passed the error object, or the final result object.
<p>
Let's see how it all fits together, in five steps:
<ul>
<li>Find the SlideImage document</li>
<li>Open the GridFS file</li>
<li>Delete the GridFS file</li>
<li>Delete the SlideImage document</li>
<li>Send a response (success or failure) to the client</li>
</ul>
<p>
The granularity here is partly driven by the specific APIs and their callbacks.
<p>The code for this is surprisingly tight:
<script src="https://gist.github.com/3228429.js?file=api.coffee"></script>
<p>auto() is passed two values; an object that maps keys to arrays, and the final callback. Each array consists of the names of dependencies for the task, followed by the task function (you can just specify the function if a task has no dependencies, but I prefer the consistency of each entry being an array).
<p>
So <code>find</code> has no dependencies, and kicks of the overall process. I think it is really significant how consistent Node.js APIs are: the basic callback consisting of an error and a result makes it very easy to integrate code from many different libraries and authors (I think there's a kind of <a href="http://en.wikipedia.org/wiki/Monad_(functional_programming)">Monad</a> hidden in there). In the code, the callback created by <code>auto()</code>, and passed to <code>find</code>, is perfectly OK to pass into <code>findById</code>. It's all low impedance: no need to write any kind of shim or adapter.
<p>
The later tasks take the additional <code>results</code> parameter; <code>results.find</code> is the SlideImage document provided by the <code>find</code> task.
<p>
The <code>remove</code> and <code>openFile</code> tasks both depend on <code>find</code>: they will run in no particular order after <code>find</code>; more importantly, their callbacks will be invoked in no predictable order, based on when the various asynchronous operations complete.
<p>
Only once all tasks have executed (or one task has passed an error to its callback), does the final callback get invoked; this is what sends a 500 error to the client, or a 200 success (with a <code>{ "result" : "ok" }</code> JSON response.
<p>
I think this code is both readable, and concise; in fact, I can't imagine it being much more concise. My brain is starting to really go parallel: part of my brain is evaluating everything in terms of Java code and idioms while the rest is embracing JavaScript and CoffeeScript and Node.js idioms; the Java part is impressed by the degree to which these JavaScript solutions eschew complex APIs in favor of working on a specific "shape" of data; if I was writing something like this in Java, I'd be up to my ears in fluid interfaces and hidden implementations, with a ton of code to write and test.
<p>
I'm not sure that the application I'm writing will have any processing workflows significantly more complex than this bit, but if it does, I'm quite relieved to have auto() in my toolbox.Anonymoushttp://www.blogger.com/profile/04486596490758986709noreply@blogger.com2tag:blogger.com,1999:blog-4110180.post-14362375890158436062012-07-30T09:26:00.000-07:002012-08-02T09:37:08.961-07:00A little Gotcha with asynch and Streams<p>I stumbled across a little gotcha using <a href="https://github.com/caolan/async/#waterfall">async</a> with Node.js <a href="http://nodejs.org/api/stream.html">Streams</a>: you can easily corrupt your output if you are not careful.
<blockquote>
Node.js Streams are an abstraction of Unix pipes; they let you push or pull data a little bit at a time, never keeping more in memory than its needed. async is a library used to organize all the asynchronous callbacks used in node applications without getting the kind of "Christmas Tree" deep nesting of callbacks that can occur too easily.
</blockquote>
<p>
I'm working on a little bit of code to pull an image file, stored in <a href="http://mongodb.github.com/node-mongodb-native/api-generated/gridstore.html">MongoDB GridFS</a>, scale the image using <a href="https://github.com/rsms/node-imagemagick#readme">ImageMagick</a>, then stream the result down to the browser.
<p>
My first pass at this didn't use ImageMagick or streams, and worked perfectly ... but as soon as I added in the use of async (even before adding in ImageMagick), I started getting broken images in the browser, meaning that my streams were getting corrupted.
<p>
Before adding async, my code was reasonable:
<script src="https://gist.github.com/3191108.js"> </script>
<p>
However, I knew I was going to add a few new steps here to pipe the file content through ImageMagick; that's when I decided to check out the async module.
<p>
The logic for handling this request is a <em>waterfall</em>; each step kicks off some work, then passes data to the next step via an asynchronous callback. The async library calls the steps "tasks"; you pass an array of these tasks to <code>async.waterfall()</code>, along with the end-of-waterfall callback. This special callback may be passed an error provided by any task, or the final result from the final task.
<p>
With <code>waterfall()</code>, each task is passed a special callback function. If the callback function is passed a non-null error as the first parameter, then remaining tasks are skipped, and the final result handler is invoked immediately, to handle the error.
<p>
Otherwise, you pass null as the first parameter, plus any additional result values. The next task is passed the result values, plus the next callback. It's all very clever.
<p>
My first pass was to duplicate the behavior of my original code, but to do so under the async model. That means lots of smaller functions; I also introduced an extra step between getting the opened file and streaming its contents to the browser. The extra step is intended for later, where ImageMagick will get threaded in.
<p>The code, despite the extra step, was quite readable:
<script src="https://gist.github.com/3191123.js?file=index.coffee"></script>
<p>
My style is to create local variables with each function; so <code>openFile</code> kicks off the process; once the file has been retrieved from MongoDB, the <code>readFileContents</code> task will be invoked ... unless there's an error, in which case <code>errorCallback</code> gets invoked immediately.
<p>
Inside <code>readFileContents</code> we convert the file to a stream with <code>file.stream(true)</code> (the <code>true</code> means to automatically close the stream once all of the file contents have been read from GridFS).
<p>
<code>streamToClient</code> comes next, it takes that stream and pipes it down to the browser via the <code>res</code> (response) object.
<p>
So, although its now broken up into more small functions, the logic is the same, as expressed on the very last line: open the file, read its contents as a stream, stream the data down to the client.
<p>
However, when I started testing this before moving on to add the image scaling step, <strong>it no longer worked</strong>. The image data was corrupted. I did quite a bit of thrashing: adding log messages, looking at library source, guessing, and experimenting (and I did pine for a real debugger!).
<p>
Eventually, I realized it came down to this bit of code from the async module:
<script src="https://gist.github.com/3191135.js?file=async.js"></script>
<p>The code on line 7 is the callback function passed to each task; notice that once it decides what to do, on line 21 it defers the execution until the "next tick".
<p>
The root of the problem was simply that the "next tick" was a little too late. By the time the next tick came along, and <code>streamToClient</code> got invoked, the first chunk of data had already been read from MongoDB ... but since the call to <code>pipe()</code> had not executed yet, it was simply discarded. The end result was that the stream to the client was missing a chunk at the beginning, or even entirely empty.
<p>The solution was to break things up a bit differently, so that the call to <code>file.stream()</code> happens inside the same task as the call to <code>stream.pipe()</code>.
<p>
So that's our <a href="http://en.wikipedia.org/wiki/Leaky_abstraction">Leaky Abstraction</a> for today; what looked like an immediate callback was deferred just enough to change the overall behavior. And that, in Node, anything that can be deferred, will be deferred, since that makes the overall application that much zippier.Anonymoushttp://www.blogger.com/profile/04486596490758986709noreply@blogger.com1tag:blogger.com,1999:blog-4110180.post-16248075431175416842012-07-02T10:27:00.001-07:002012-07-02T10:27:42.094-07:00You Cannot Correctly Represent Change Without Immutability<p>The title of this blog post is a quote by Rich Hickey, talking about the <a href="http://datomic.com/">Datomic</a> database. Its a beautiful statement, at once illuminating and paradoxical. It drives at the heart of the design of both Clojure and Datomic, and embraces the difference between identity and state.
<p>
What is change? That seems like an obvious question, but my first attempt at defining it was "some <em>change</em> to a quantifiable set of qualities about some object." Woops, I used <em>change</em> recursively there ... that's not going to help.
<p>
In the real world, things change in ways we can observe; the leaf falls from the tree, the water in the pot boils, the minute hand moves ever forward.
<p>
How do we recognize that things have changed? We can, in our memories, remember a prior state. We remember when the leaf was green and attached to a branch; we remember when the water came out of the tap, and we remember looking at the clock a few minutes ago. We can hold both states in our mind at the same time, and compare them.
<p>
How do we represent change in traditional, object-oriented technologies? Well, we have fields (or columnus) and we change the state in place:
<ul>
<li><code>leaf.setColor(BROWN).detachFromTree()</code></li>
<li><code>UPDATE LEAVES SET COLOR = 'BROWN' WHERE ID = ?ID</code></li>
<li><code>water.setTemperature(212)</code></li>
<li>or we see time advancing via <code>System.currentTimeMillis()</code></li>
</ul>
<p>
Here's the challenge: given an object, how do you ask it about its prior state? Can you ask <code>leaf.getTreeDetachedFrom()</code>? Generally, you can't unless you've gone to some herculean effort: the new state overwrites the old state in place.
<p>
When Rich talks about conflating state with identity, this is what he means. With the identity and state conflated, then after the change in state, the leaf will <em>now-have-always-been</em> fallen from the tree, the water will <em>now-have-always-been</em> boiled, and the clock will <em>now-eternally</em> be at 9:49 AM.
<p>
What Clojure does in memory, and Datomic does in the database, is split identity and state. We end up with <code>leaf<sub>1</sub></code> as <code>{:id "a317a439-50bb-4d37-838a-c8eef289e22f" :color :green :attached-to maple-tree}</code> and <code>leaf<sub>2</sub></code> as <code>{:id "a317a439-50bb-4d37-838a-c8eef289e22f" :color :brown :on-ground true}</code>. The id is the same, but the other attributes can vary.
<p>
With immutability, changes in state are really new objects; a new <em>version</em>, or "quantifiable set of qualities", that does not affect the original version. It is possible to compare two different iterations of the same object to see the "deltas". In Datomic, you even have more meta-data about when such state changes occur, what else changed within the same transaction, and who is the responsible party for that transaction.
<p>The essence here is not to think of an object as a set of slots you can put new data into. Instead, think of it as a time-line of different configurations of the object. The fact that late in the time-line, the leaf has fallen from the tree does not affect the fact that earlier on the time-line, the leaf was a bud on a branch. The identity of the leaf transcends all those different states.
<p>
In the past, I've built systems that required some of the features that Datomic provides; for example, being able to reconstruct the state of the entire database at some prior time, and strong auditing of what changes occurred to what entities at a specific time (or transaction). Rich knows that others have hit this class of problem; part of his selling point is to ask "and who really understands that query" (the one that reconstructs prior state). He knows people have done it, but he also knows no one is very happy about its performance, correctness, or maintainability ... precisely because traditional databases don't understand mutability: they live in that eternal-now, and drag your application into the same world view.
<p>
That's why I'm excited by Datomic; it embraces this key idea: <strong>separate identity from state by leveraging immutability</strong> and from the ensuing design, much goodness is an automatic by-product. Suddenly, we start seeing much of what we take as dogma when developing database-driven applications to be kludges on top of an unstable central idea: mutable state.
<p>For example: read transactions are a way to gain stable view of interrelated data even as the data is being changed (in place); with Datomic, you always have a stable view of all data, because you operate on an immutable view of the entire database at some instance in time. Other transactions may add, change, or replace Datoms in the database, but any code that is reading from the database will be completely unaware of those changes, even as they lazily navigate around the entire database.Anonymoushttp://www.blogger.com/profile/04486596490758986709noreply@blogger.com8