DZone
Java Zone
Thanks for visiting DZone today,
Edit Profile
  • Manage Email Subscriptions
  • How to Post to DZone
  • Article Submission Guidelines
Sign Out View Profile
  • Post an Article
  • Manage My Drafts
Over 2 million developers have joined DZone.
Log In / Join
  • Refcardz
  • Trend Reports
  • Webinars
  • Zones
  • |
    • Agile
    • AI
    • Big Data
    • Cloud
    • Database
    • DevOps
    • Integration
    • IoT
    • Java
    • Microservices
    • Open Source
    • Performance
    • Security
    • Web Dev
DZone > Java Zone > Changes to Cascade, and a cautionary tale about defrecord

Changes to Cascade, and a cautionary tale about defrecord

Howard Lewis Ship user avatar by
Howard Lewis Ship
·
Sep. 23, 11 · Java Zone · Interview
Like (0)
Save
Tweet
4.63K Views

Join the DZone community and get the full member experience.

Join For Free

Since I've been talking more about Clojure lately, I've spent a little more time working on Cascade. I've been stripping out a lot of functionality, so that Cascade will no work with Ring and Compojure, rather than being in competition. Its Clojure, after all, ... there's less of a reason to build a full framework since its so easy to simply assemble your functionality from proper libraries.

It's also been a chance to update some of my code with more modern constructs. For example, the earlier version of Cascade used a (defstruct) for the DOM nodes; the new code uses (defrecord).

Along the way I discovered something interesting about defrecord. Consider this code:

(defrecord Comment [text]

  NodeStreaming
  (stream [node strategy out]
    (write out "<!--" text "-->")))

Technically, this is just an optimized way to define a Clojure Map. If I have an instance, I can (:text node) to get the text out of the map.

However, (defrecord) does one other thing that is barely mentioned in the documentation (and not referenced, that I can tell, in Joy of Clojure). Notice the implementation of the stream function (part of the NodeStreaming protocol). It just says text; not (:text node). Inside a protocol method, the fields of the record are bound to local variables, making them easy to use ... another benefit.

I actually found this the hard way, when writing a more complicated example, for the Element DOM node:
(defrecord Element [name attributes content]

  NodeStreaming
  (stream [node strategy out]
    (let [element-name (clojure.core/name name)
          attr-quote (strategy :attribute-quote)]
      (write out "<" element-name)
      ; Write out normal attributes
      (doseq [[attr-name attr-value] attributes]
        (if-not (nil? attr-value)
          (write out
            " " (clojure.core/name attr-name) "=" attr-quote (to-attr-string attr-value) attr-quote)))

      (if (empty? content)
        ((strategy :write-empty-element-close) element-name out)
        (do
          (write out ">")
          (stream-nodes content strategy out)
          (write out "</" element-name ">"))))))

Notice the use of clojure.core/name to convert a keyword to a string; originally this was (name (:name node)) and returned nil. This confused me quite a bit!

What ended up happening was that name was bound to the keyword from the record's name field. However, Clojure keywords can be used as functions,and was incidentally passed itself, which is to say (for an Element node representing a <p> element): (name (:name node)) --> (:p :p) --> nil.

So, (defrecord) giveth, but it also taketh away, at least, the first time. In other words, watch out for name collisions between the names of the record's fields, and the names of functions you want to reference from your protocol method implementations.

Back to Cascade; I don't have any metrics available about performance changes with the new code (using records and protocols), but I suspect its faster and more efficient.

A lot of the features that were in Cascade are gone and will come back soon. Ultimately, I'll have Cascade flavors of context and classpath assets from Tapestry, as well as mechanisms similar to Tapestry for adding JavaScript libraries and CSS stylesheets, along with a mechanism similar to Tapestry for organizing them into stacks.

Looking further forward, adding support for Enlive, both reading parsed XML templates in as DOM structure and allowing Enlive transformations onto the DOM structure, seems like a good direction.

When will all this happen? I'm not certain, but I hope that Cascade will become a "must-have" layer on top of Compojure, adding some of the industrial strength concepts from Tapestry into the fast-and-loose world of Clojure web applications.

 

From http://tapestryjava.blogspot.com/2011/09/changes-to-cascade-and-cautionary-tale.html

Cascade (computer virus)

Opinions expressed by DZone contributors are their own.

Popular on DZone

  • Monolith vs Microservices Architecture: To Split or Not to Split?
  • What's the Difference Between Static Class vs. Singleton Patterns in C#?
  • Understand Source Code — Deep Into the Codebase, Locally and in Production
  • 10 Programming Habits a Web Developer Should Embrace

Comments

Java Partner Resources

X

ABOUT US

  • About DZone
  • Send feedback
  • Careers
  • Sitemap

ADVERTISE

  • Advertise with DZone

CONTRIBUTE ON DZONE

  • Article Submission Guidelines
  • MVB Program
  • Become a Contributor
  • Visit the Writers' Zone

LEGAL

  • Terms of Service
  • Privacy Policy

CONTACT US

  • 600 Park Offices Drive
  • Suite 300
  • Durham, NC 27709
  • support@dzone.com
  • +1 (919) 678-0300

Let's be friends:

DZone.com is powered by 

AnswerHub logo