Ruby's Louvre

每天学习一点点算法

导航

RailwayJS vs TowerJS

决定用node.js来动态生成我框架的文档,再次关注node.js。

下面内容转自stackoverflow

+-----------------------+------------------------------+------------------------------------+
|                       | RailwayJS                    | Tower.js                           |
+-----------------------+------------------------------+------------------------------------+
| First commit          | Jan 2011                     | Oct 2011                           |
| Rails                 | 2.3.x                        | 3.x                                |
| Node.js               | >= 0.4.x                     | >= 0.4.x                           |
| Server                | ✓                            | ✓                                  |
| Client                |                              | ✓                                  |
| Template agnostic     | ✓                            | ✓                                  |
| Default engine        | EJS                          | CoffeeKup                          |
| Database agnostic     | ✓                            | ✓                                  |
| Default datastore     | MongoDB                      | MongoDB                            |
| Model validations     | validatesPresenceOf('email') | validates('email', presence: true) |
| Query scopes          | ✓                            | ✓                                  |
| Chainable scopes      |                              | ✓                                  |
| Param parsing         |                              | ✓                                  |
| Controllers           | ✓                            | ✓                                  |
| Resource controllers  |                              | ✓                                  |
| File naming           | users_controller.js          | usersController.coffee             |
| vm.runInCustomContext | ✓                            |                                    |
| Asset pipeline        |                              | ✓                                  |
| Asset compression     |                              | ✓                                  |
| Routing               | map.resources('posts')       | @resources 'posts'                 |
| Nested routes         | ✓                            | ✓                                  |
| Generated url helpers | ✓                            |                                    |
| Generators            | ✓                            | ✓                                  |
| Command-line api      | ✓                            | ✓                                  |
| REPL (console)        | ✓                            | ✓                                  |
| CoffeeScript console  |                              | ✓                                  |
| Asset cache method    | timestamp                    | md5 hash                           |
| Production asset path | /app.css?123123123           | /app-859c828c89288hc8918741.css    |
| Preferred Language    | JavaScript                   | CoffeeScript                       |
| CoffeeScript support  | ✓                            | ✓                                  |
| Internationalization  | ✓                            | ✓                                  |
| Heroku support        | ✓                            | ✓                                  |
| String case           | snake_case                   | camelCase                          |
| Form builder          | ✓                            | ✓                                  |
| Semantic form builder |                              | ✓                                  |
| Table builer          |                              | ✓                                  |
| File watcher API      |                              | ✓                                  |
| Live-reload assets    |                              | ✓                                  |
| Test suite            |                              | ✓                                  |
| Generators for tests  |                              | ✓                                  |
| Twitter Bootstrap     | ✓                            | ✓                                  |
| HTML5 Boilerplate     |                              | ✓                                  |
+-----------------------+------------------------------+------------------------------------+

I created Tower.js to achieve several goals which none of the existing frameworks did adequately. Here are some of those goals.

1. Same code on the client and server

Since Node.js made JavaScript possible on the server, there's no reason to be writing one part of the app in Rails, and the other in Backbone. That's anything but DRY. You should be able to define the models once and use them on both the client and the server.

RailwayJS only works on the server because it was built around express. Tower.js is also built around express but in a way that makes it work for both the client and server. Tower.js provides the same exact API for the client and server. This meant I had to rewrite some things like the router so it works the same on the client and the server (plus it allows you to do things like history.pushState with the # fallback, using the same set of routes).

2. Same "views" on the client and server

I spent a lot of time in Rails and writing Haml templates. Alongside I was writing web and mobile JavaScript interfaces using template languages like Mustache. That's more code duplication… You should be able to use the same set of views/templates on both the client (as JavaScript templates) and server (rendering static HTML).

Since Haml was pretty awesome (super clean, allowed you to execute arbitrary ruby, built in pretty-printing, etc.), the closest JavaScript alternative was CoffeeKup. And it works on both the client and server. CoffeeKup allows you to write templates with all the power of JavaScript, so you have no limitations. Building a FormBuilder in Mustache is either going to take a lot of work or a lot of code, or both.

Do note though, you're free to swap out template engines and use Jade, Mustache, Handlebars, etc. for the client or server. CoffeeKup is just a clean and powerful default.

3. Rails-quality model API on the client and server

ActiveModel (implemented by ActiveRecord for SQL and Mongoid for MongoDB for Rails) is a very thorough and well-tested API allowing developers to define and interact with data. It's both powerful and enjoyable. All of the previous (and current) JavaScript implementations were never close to as robust and well designed, and I didn't see anything happening in the near future.

If you can write this in Rails:

User.where(:email => /[a-z/).page(2).limit(20)

You should be able to do that in JavaScript:

App.User.where(email: /[a-z/).page(2).limit(20)

Tower.js comes with "chainable scopes", meaning hardcore queries + pagination. It's modeled after the MongoDB Query API, but this API "input" is converted to appropriate database commands for the different datastores.

4. Uniform interface to the SQL and NoSQL datastores

Tower.js currently has a MongoDB and Memory (in-browser) store, and aims to provide a uniform interface to the rest of the popular databases (CouchDB, Neo4j, PostGreSQL, MySQL, SQLite, Cassandra, etc.).

RailwayJS seems to be doing this as well via JugglingDB, and it looks like a good start. But I choose not to use it for a few reasons. First, it looks like it's is being built around the Rails 2.x API (User.validatesUniquenessOf "email" vs. User.validates "email", presence: true). Second, it doesn't have the richness of chainable queries that Rails 3 does. Third, I want to be able to add code to the codebase quickly, and since I'm very picky I would probably end up refactoring the whole thing to use CoffeeScript, haha. And I don't want to build a layer around that because it has to work on the client as well, so keeping the library architecture as minimal as possible is a high priority.

5. Resourceful controllers

The inherited_resources Ruby gem cut out about 90% of the code from my Rails controllers. It figured out a set of conventions for implementing the 7 basic controller actions. Tower.js includes something like this, so by default you don't have to write any code in your controllers they'll still respond with JSON and HTML. It also makes it so you can defined nested routes.

6. Automatic URL-to-database query parser

In Tower.js, you can tell a controller to watch for specific parameters in the url and it will convert them to a hash ready to apply to a model query.

class App.UsersController extends App.ApplicationController
  @param "email"

  index: ->
    App.User.where(@criteria()).all (error, users) =>
      @respondTo (format) =>
        format.json => @render json: users
        format.html => @render "index", locals: {users}

Given a url that's like /users?email=abc&something=random, then @criteria() will give you a hash {email: /abc/}.

It's not it Rails, but I wish it was.

7. Semantic Forms

I'm super into semantic HTML. Rails' form builder generates pretty ugly HTML, so many people as well as myself used Formtastic, which generates more semantic forms. Tower.js uses pretty much the same API as Formtastic. It also has a semantic table builder, which makes it pretty easy to build searchable/sortable tables for admin views.

8. Asset Pipeline

Rails 3 had an awesome asset pipeline, where you could write your JavaScript in CoffeeScript, your CSS in SCSS, and it would automatically recompile. Then rake assets:precompile your assets and you'd get md5-hashed gzipped assets ready for S3. That's pretty hard to build out yourself, and I didn't see anyone working on that for Node.js.

RailwayJS uses the Rails 2 method of timestamping the asset path, so instead of this md5-hashed version:

/stylesheets/application-51e687ad72175b5629f3b1538b65ea2c.css

You'd get something like this:

/stylesheets/application.css?1306993455524

This is a problem for a few important reasons reasons. The Rails Asset Pipeline Guide has the details, but the big thing is S3 doesn't recognize the timestamp, so it's reading /stylesheets/application.css, and if you set a far-future Expires header and you've changed your CSS, anyone who visited your site before will have to purge their cache or force-refresh your page to see the updates.

RailwayJS also doesn't have the built in asset compilation pipeline (at least to my knowledge).

9. The Watchfile

Guard was a huge productivity booster in Rails. It allowed you to write quick "watch tasks", essentially like rake/cake tasks, that ran when a file matching a pattern was created/updated/deleted.

Tower has this built in (using design.io). This is actually what's telling the CoffeeScript and Stylus assets to compile into JavaScript and CSS. But you can do very powerful things with this feature, see https://github.com/guard/guard/wiki/List-of-available-Guards for examples.

10. CoffeeScript

Big fan of CoffeeScript.

CoffeeScript cuts the amount of JavaScript you need to write about in half (6,501 additions, 15,896 deletions converting the entire Node.js library to CoffeeScript). And it makes coding much faster and easier.

Also, CoffeeScript is the only way to keep that productive and enjoyable coding experience that Rails showed the world. JavaScript just doesn't do that.

The little things

I'm a fan of standards. RailwayJS stuck to the Ruby convention of using snake_case, and I wanted to do that too, but the JavaScript community uses camelCase so Tower went with that. CamelCase has a few added benefits as well, such as you don't need to convert server-side Rails snake_case to/from camelCase for the client, and removing that extra character gives you a tiny smaller file size.

I'm also in love with super clean code. Before I consider contributing to a project, I read through the source code... and if it's super messy, I'm probably just going to rewrite it.

I also love optimizing code. With Tower.js, a big goal is to structure it so it does everything that Rails does, providing the exact same API in both the client and server, using the minimal amount of code possible. There's a tradeoff though between minimizing the size of the codebase and writing code that's clear and fun/productive to use. Still finding ways to get the best of both worlds.

I'm definitely in this for the long-haul as well. This is the foundation for our company, and everything I personally will build in the future. I want to get to the point where you can pump out a nicely designed, functional, and highly optimized app in a day.

posted on 2012-07-11 08:52  司徒正美  阅读(1517)  评论(0编辑  收藏  举报