RailwayJS vs TowerJS [closed]

流过昼夜 提交于 2019-11-28 02:43:15
Lance Pollard

Here's a brief table to overview, I'll talk about some of the stuff below.

+-----------------------+------------------------------+------------------------------------+
|                       | 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 in 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. 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.

Hope that helps.

Have you paid attention to Derbyjs? This one although not yet beta, is quite exciting. It is being authored by an ex google employee and the author of everyauth. You will have to write minimal client side javascript with this one. See the excerpts taken from the official page:

Why not use Rails and Backbone? Derby represents a new breed of application frameworks, which we believe will replace currently popular libraries like Rails and Backbone.

Adding dynamic features to apps written with Rails, Django, and other server-side frameworks tends to produce a tangled mess. Server code renders various initial states while jQuery selectors and callbacks desperately attempt to make sense of the DOM and user events. Adding new features typically involves changing both server and client code, often in different languages.

Many developers now include a client MVC framework like Backbone to better structure client code. A few have started to use declarative model-view binding libraries, such as Knockout and Angular, to reduce boilerplate DOM manipulation and event bindings. These are great concepts, and adding some structure certainly improves client code. However, they still lead to duplicating rendering code and manually synchronizing changes in increasingly complex server and client code bases. Not only that, each of these pieces must be manually wired together and packaged for the client.

Derby radically simplifies this process of adding dynamic interactions. It runs the same code in servers and browsers, and it syncs data automatically. Derby takes care of template rendering, packaging, and model-view bindings out of the box. Since all features are designed to work together, no code duplication and glue code are needed. Derby equips developers for a future when all data in all apps are realtime.

Flexibility without the glue code Derby eliminates the tedium of wiring together a server, server templating engine, CSS compiler, script packager, minifier, client MVC framework, client JavaScript library, client templating and/or bindings engine, client history library, realtime transport, ORM, and database. It elminates the complexity of keeping state synchronized among models and views, clients and servers, multiple windows, multiple users, and models and databases.

At the same time, it plays well with others. Derby is built on top of popular libraries, including Node.js, Express, Socket.IO, Browserify, Stylus, UglifyJS, MongoDB, and soon other popular databases and datastores. These libraries can also be used directly. The data synchronization layer, Racer, can be used separately. Other client libraries, such as jQuery, and other Node.js modules from npm work just as well along with Derby.

When following the default file structure, templates, styles, and scripts are automatically packaged and included in the appropriate pages. In addition, Derby can be used via a dynamic API, as seen in the simple example above.

But it also comes with following disclaimer

Derby and Racer are alpha software. While Derby should work well enough for prototyping and weekend projects, it is still undergoing major development. APIs are subject to change.

It doesn't yet have an authorization implementation and is fraught with security issues, although they will be taken care of in months to come. If you can wait for a few months this seems to be a promising framework.

Selecting a framework comes down to your comfort level with it.. usually based on..

  • How active is the project? When was the last commit? If it's not on github that's an immediate concern for me since that makes user contributions harder.

  • How many blog posts can I find on the framework? If nobody is talking about it that's usually a bad sign since people naturally talk about things that excite them.

  • What do I think of the framework? This can be harder to judge but there should be enough examples out there that you can get a basic idea at least. If there aren't then that in of itself is a big problem.

Erm.. of course the obvious question is if you want a RoR framework.. why not just use RoR? ;)

It looks like TowerJS is more tightly coupled with MongoDB as its data store, whereas RailwayJS seems to have model adapter flexibility. That might affect your choice between the two. Personally I'd choose to write Rails sites using RoR. Node seems to lend itself more to different kinds of services don't you think? (I'm thinking Backbone in the client with AJAX REST services).

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!