#Prometheus.js
Introducing Prometheus.js. A sensible solution to Templating.
###What we are: Prometheus.js is my own attempt at solving that pesky question, in a web 2.0 world, how do we do forms over data. There are dozens of people trying to answer this question, and Prometheus.js is an answer to many of those existing solutions.
Let me start by saying Prometheus.js is heavily influenced by other templating engines like PURE. Prometheus is a pure javascript and jquery implementation of a templating engine developed in CoffeeScript. A lot of names there right?
Prometheus provides the ability to provide a javascript method with JSON, and bind every relevant piece of the view to the data contained in there.
Since this is essentially the functionality provided by so many other tools out there, what makes Prometheus worth using? It's all about design philosophy.
I love PURE, PURE gives us a great jumping off point for so many things. Where my philosophy differs is that I feel that only the view should know how it wants to display the data. If you take an example using Object.as_json or Object.to_json the ORM dynamically picks up the structure of the table on boot, the data is grabbed and dynamically converted to json then passed to the javascript. At this point the PURE philosophy on some of the more basic tasks still follows the idea of knowing how to display the data in the javascript (via directives).
With Prometheus only the view worries about where the data goes in the view, or even if it does. The javascript is there to follow the same dynamic tendencies of the ORM and Rails server behind it, without needing any knowledge of what the view looks like.
I'm not picking on PURE in particular, it's simply the tool I pulled the greatest amount of inspiration from, and the standard I'll ultimately hold Prometheus up to.
What I wanted to do with Prometheus was provide a simple solution that would allow designers to continue to design (and modify) pages without having to understand the magic that gets the data on the screen one tiny bit, and minimize the work required to hook the back end data into the front end view.
"But you can always set your page entirely in javascript" you might say. Yes you can, I've done it. What you're doing is tightly coupling your javascript to your view. If an id changes everything breaks, if something gets removed from the view, you have to go remove it from the right place in your javascript or everything breaks, if you add something you have to get it from the database, insert it into your json object, insert it into your javascript then attach it to an element you created on the page. It's bad. I've done it many times, and it becomes a maintenance nightmare.
Of course there are other arguments, and Prometheus won't be ideal for every situation. Nothing is.
###The API In Short
Very simply Prometheus (to the outside world) consists of three calls.
- Prometheus.bindData()
- Prometheus.bindValues()
- Prometheus.bindSources()
These are the core prometheus binding methods, there are several more advanced features I'll go over in their own wiki articles, but these three are relatively simply.
Lets say we have the following JSON object (JSON is the only format currently supported):
[{"user":{"created_at":"2011-09-19T01:13:35Z","email":"[email protected]","id":1,"updated_at":"2011-09-19T01:13:35Z","username":"JSmith"}},{"user":{"create> d_at":"2011-09-19T03:49:44Z","email":"[email protected]","id":2,"updated_at":"2011-09-19T03:49:44Z","username":"GSmith"}}]
This code was generated by the rails to_json method from a users table. To bind this to a table your haml (I have a soft spot for APIs as you can tell) would look like:
%table{:data=>{:src=>"user"}} %thead %th{:data=>{:val=>"username", :options=>"inline-edit", :name=>"MyName", :change=>"alert('hello')"}} Username %th{:data=>{:val=>"email"}} Email %tbody
Alternatively you could set the val for each th to user. and get along without a src. That's a matter of taste.
You'll see I've used a lot of options here. The options available are specific to the element being edited. I should have a full list available on the wiki in the next few days.
The important thing here is that by calling either Prometheus.bindValues or Prometheus.bindData you can bind the user object to the table, in this example, setting the name, allowing inline-edit (click on username to edit it) and adding custom onchange events.
All of this occurs without writing a single line of custom javascript, or creating templating layouts that will leave designers scratching their heads.
###The Specifics Each of the three available methods has a slightly different effect, based mostly on the ability to bind select lists with data from a JSON object.
####Bind Data Prometheus.bindData will bind all available data sources (binding a list of states to a drop down for instance) once per session (page load) and then bind all data values on the page. bindData takes two values a data object and optionally a container (in proper jquery selector format), i.e:
Prometheus.bindData(userObject, "#usersContainer").
If no container is provided Prometheus will default to body. Depending on dom size working over the entire body can have a negative performance impact, especially on older versions of IE.
####Bind Values Prometheus.bindValues works exactly like bindData, except instead of binding both sources and data, bindValues will only bind data values. Use this if you want to avoid rebinding your sources. Rebound sources by default will overwrite existing content, it will not append content, the same thing applies to bind values.
An optional third parameter can be passed to bindValues which is a bool for appending data. If true is passed as the third option into bind values, data present in the object passed will be appended to existing on page data.
Example:
Prometheus.bindValues(userObject, "#usersContainer", true)
This would append the users in your user object to the table, leaving any existing users there and creating duplicates where duplicates exist.
####Bind Sources Prometheus.bindSources will rebind sources regardless of whether or not they have been bound during the current page instance. This will wipe all the current source bindings for anything that is rebound (if you do not pass the data for a specific source it will not be wiped, and will remain untouched). Bind
An optional third parameter can be passed to bindSources which is a bool for appending data. If true is passed as the third option into bind values, data present in the object passed will be appended to existing on page data.
Example:
Prometheus.bindSources(userObject, "#usersContainer", true)
###Disclaimer-Type stuff (oh, and licensing too) Prometheus is still in active early development. Everything in this article is true to the best of my knowledge, and if any bugs arise please post an issue for it and I'll do my best to fix it immediately.
Prometheus is licensed under the MIT license.
The API documented herein should not change any time soon, but there are no guarantees. Additional documentation will be coming in the following days, but if you're interested in where I'm heading you'll find the game plan, including stuff I'm just considering, is listed at the bottom. Before I call Prometheus 1.0 all tasks in the TODO list will need to be considered closed, this means either an idea that was scrapped or a task completed.
Contributors are always welcome. To contribute to Prometheus please create a fork via github and enter a pull request when completed.
Tests are appreciated, and you'll find the standalone Jasmine testing tool included in this project.
###TODO -
Add Support For
Links - Yay! (maybe?)
iFrames (figure out if this has value, or is even a thing)
Complex Arrays (Arrays in objects in Arrays in objects)
Ideas
Make inputs optional in tables/lists when non-editable
Add non-json object format support? (Any value?)
Add Options for
Bind Images
Bind with Select
Lists
Labels
Bind with bool
Tables
Lists
Labels
Blur/Change
Replace Blur/Change with jquery binds instead of HTML
Actions
Lists
Table Headers
Label
Generic Tags
Add catch all for tags not explicitly handled/generic text blocks.
Convert to JSON
Convert selects
Convert tables
Full form
Data Validations
Validates presence
Selects
Inputs
Validates format
Inputs
Validates Uniqueness => Scope (IE, JSOnified table, or other JSON object)
Clear Form (clear values from data-val'd inputs)
Reset form (resets form to initial loaded state, resetting all data to the first values/sources set to them)
Cleanup Tasks -
Clean up tables to use all the new editable methods
Get under proper test coverage
Remove try catches and replace with logical if checks
Switch string creation of elements to document.createElement
remove jQuery.each statements and use more performant for loops
Reduce number of loops overall
Clean up scopes (dumping Globally scoped objects)
General performance tuning
Documentation - Wiki documentation bindData bindSources bindValues appendData Validates Convert to JSON Cleanup and format samples Formal introduction to Prometheus Prometheus versioning (Formal) Prometheus roadmap