prose :: and :: conz


Pakyow: View-oriented functional goodness for Ruby

This past week, I had the pleasure of attending a presentation of the view-oriented Ruby web framework Pakyow from founder Bryan Powell. He spoke at the monthly HuntFunc meetup where a bunch of developers in Huntsville gather for functional programming topics. I came away very impressed with the framework, and proud to know that such a thing is brewing here in my home town. These guys have put a lot of thought into web development, and it shows.

Before I go any further, let me caution the reader (singular, because I suspect it’s still just my mom) that I have had zero exposure to Ruby, much less RoR. I’ve done some MVC web development in my spare time with Grails. While I like Grails, and would enjoy building something with it, I just never loved the approach. Anyway…

I won’t attempt to recap his presentation in a blog post. Instead I will provide a cursory glance at how Pakyow compares to my strongest point of reference. As you may know, I’m a HUGE fan of Lift. It’s precisely what I’m using to build warwithone.com. Well… it’s quite convenient that my reference framework is Lift, because Pakyow is strikingly similar to Lift.

For starters, both Lift and Pakyow are view-driven/view-first/designer-friendly frameworks. That is to say, your view templates are valid HTML. So a web designer can use whatever tools he likes to produce the view, and that view just needs a little sprinkling of HTML5 data attributes (for Pakyow) or class attributes (for Lift). This is where you let the respective framework know where to do its thing. This is a nice separation of view from logic. Any MVC stuff I’ve worked with (JSPs and GSPs, and apparently RoR does this too) has this view that is basically HTML complected with little tags of logic-looking crap all mixed in. The model/data and controller are indeed separated, but the logic’s tangled up in it.

The two frameworks put the data into the view differently from one another, and not just because you sprinkled different attributes (data-* vs. class). The Pakyow approach is to bind a data object to the HTML. In the HTML, you define a scope to bind to the data object and the names of the fields to grab. Then in the code, you bind the data object to the scope and Pakyow sticks the contents into the document. Let’s take a look. Suppose this is your data object:

{
  title: "Pop-Pop Says:",
  body: "There's always money in the banana stand."
}

And this is your HTML:

<html>
<body>
  <div data-scope="post">
    <h1 data-prop="title">
      Title
    </h1>
    <p data-prop="body">
      Body goes here.
    </p>
  </div>
</body>
</html>

You bind the data to the view with one line of Ruby:

view.scope(:post).apply(data)

Then “Title” and “Body goes here.” get replaced by the values in the data to yield:

<html>
<body>
  <div data-scope="post">
    <h1 data-prop="title">
      Pop-Pop Says:
    </h1>
    <p data-prop="body">
      There's always money in the banana stand.
    </p>
  </div>
</body>
</html>

The data-* tags get ignored by the browser, and you get the view with your data mixed in the way you expect. That dollop of Ruby code to bind the data to the view may look a little boiler-plate. However, there are a handful of different functions you can call for binding. This example I’ve shown doesn’t illustrate the need for different binding functions. They become necessary when you begin having lists of objects and so forth. However, that’s beyond the scope of this post.

Now if you were to write this with Lift, the HTML will be a little bit different. Analogous to the data-scope in Pakyow, you want to sprinkle in a class attribute into the div that you want to transform. This div (or whatever type of tag) will get passed to a chunk of Scala code called a snippet. A snippet is a function which takes HTML and returns HTML. The template HTML gets passed to your function, and you do whatever you think is best.

Let’s assume the same data, but this time it’s a Scala case class:

val p = Post("Pop-Pop Says:", "There's always money in the banana stand.")

The HTML with the class tag:

<html>
<body>
  <div class="lift:MySnippet">
    <h1>
      Title
    </h1>
    <p>
      Body goes here.
    </p>
  </div>
</body>
</html>

Now there are several ways to write the transformation code. I’m going to pick my favorite which is the CSS selector:

class MySnippet {
  val p = Post("Pop-Pop Says:", "There's always money in the banana stand.")
  
  def render = {
    "h1 *" #> p.title &
    "p *" #> p.body
  }
}

Then the final HTML that gets produced is nearly identical (that is, no data-* tags)

<html>
<body>
  <div>
    <h1>
      Pop-Pop Says:
    </h1>
    <p>
      There's always money in the banana stand.
    </p>
  </div>
</body>
</html>

The first thing that happens is Lift uses reflection to find MySnippets. Since I didn’t specify a function name, it then defaults to the function named render. Those familiar with CSS will probably guess how it worked from there. The entire chunk of HTML in the div with class="lift:MySnippet" is passed to these CSS selectors. These selectors are matching against the type of tag (h1 and p) and the * tells the selector to replace all of the children of that tag with the stuff after the #> function.

Initially, these two frameworks seem to be roughly the same. This is in part due to how they both break the mold of MVC/Rails. However, Bryan has pointed out some interesting strengths of Pakyow. In the Lift snippet code, there is intimate knowledge of the view. There’s also no need for a dedicated data object. You’re free to return whatever XML or CSS transform you like with or without a data object. The Pakyow code on the other hand is just a declarative binding of the data object to some tag in the view. Since the data-* attributes are always ignored by the browser, you’re further detached from the view. It also behooves the developer to use a pure data object, encouraging a cleaner architecture. While these are compelling strengths, the Lift approach always leaves me feeling like I’m just doing my DOM twiddling on the server side. That gives rise to a nice holistic feel to web development where I can just think it as DOM manipulation on the server when possible, on the client when needed for good UX, and prettied up by CSS.

As I mentioned in the beginning, this barely scratches the surface of these two frameworks. Nonetheless, I hope that you can see that it’s an interesting approach to producing web pages. They both feature a clean separation of the view from the logic and provide a declarative and functional approach to tying the data and logic to the view. I hope to one day get around to trying out Pakyow. I’m pretty committed to building warwithone.com for now, so I probably won’t get distracted by this any time soon. But if you are a Rubyist or a JVM junkie like me, I highly recommend you try one of these view-oriented frameworks. It’s a great way to think about solving the problem of producing web pages with data that I’ve found to be far more flexible than the tried and true rails/MVC approach.


Olde Comments
  1. Good post. I think one point is worth elaboration. It is what Bryan was getting at when mentioning that the Lift snippet has intimate knowledge of the view. Pakyow was much the same in this regard up to version 0.7. We found that the view manipulation logic was too brittle with respect to changes in the particular style and markup used. In a way, this worked against the goal of separation that Pakyow strives for by keeping the logic out if the view. We would like for a designer to make, say, a class change and not have to worry about that requiring a code change elsewhere just because it was used to select a node for manipulation. In our way of thinking, if it’s good to keep the logic out of the view then it’s also good to keep the view out of the logic. Hence, the use of data- attributes and a focus on the structure of the view and data. To put it in terms recently made popular by Rich Hickey, it is an attempt to avoid complecting view structure, style/markup, and data.

    Not that Pakyow can’t be improved and not that Lift isn’t a very impressive framework but this is one of the drivers behind why Pakyow is the way it is.

    • barnesjd says:

      That is indeed a good point to elaborate on. Prior to seeing Pakyow, I had always figured that the view and logic were destined to be complected somewhere; it is just a matter of preference/design if that happens in the view or in the logic. However, Pakyow has opened my eyes to the ability to truly distill the two from one another.

Tagged with: scala (41), web-development (19), liftweb (9), huntfunc (2), pakyow (1), ruby (1)