Desligado is a proof-of-concept application made by junior developers.
It's a simple CRUD web app that runs both online and offline. Since it is meant to work when the client is disconnected from the web, and/or when the server is unavailable, it is heavily javascript based. Despite having a view for each one of the common actions (show, index, new and edit), it's a single-page application, since all necessary code (HTML, Javascript, and CSS) is retrieved with a single page load. Further communication is made using Ajax and HTML5 Web Sockets with JSON.
The resources are cached thanks to a HTML5 Cache Manifest file and the client keeps a synced local version of the data on a HTML5 WebSQL database (or, if not supported by his browser, on his Local Storage).
A special Thank You to James Yu and Zef Hemel.
Desligado is a simple item management application. Those are the functional requirements:
- Users must be able to create, edit, destroy and list items
- The item list must be updated, every time the server's database changes (without requiring any clicks)
- Users must be able to use the app offline (static contents should be presented, and dynamic contents must function) even after closing and reopening the browser
- Built with Ruby 1.9.2 and Rails 3.0.5
- Faye gem (tested with 0.5.5)
- Jammit! gem (tested with 0.6.0)
- jQuery (included; version 1.4.4)
- backbone.js (included; version 0.3.3)
- persistence.js (included; version 0.2.4)
- Entire demo app (version 1.1.8 stable)
- Rails Sync for persistence.sync
- Backbone.sync adapter for persistence.js
Should work with, at least:
- Chrome 11+
- Mozilla Firefox 4+
It doesn't seem to work on Safari 5 (not fully tested), and Firefox will use local storage instead of WebSQL. Beware that, if the HTML layout file contains the reference to the application manifest, server pushing will not work. Comment out the reference <... manifest="application.manifest"> or just remove the public/application.manifest file to try it. This is a known issue.
The goal for this project was to create a multi-client simple web application with some basic functionalities (the creation, manipulation and deletion of shared items) that worked both online and offline.
To achieve the behaviour of keeping functionality in offline mode, we used several techniques introduced in HTML5 - Cache Manifest, WebSQL, Local Storage and Web Sockets.
Starting with the cache manifest, since we used Ruby on Rails for our back-end, to dynamically generate this manifest file we used the rack-offline gem, as suggested by Ryan Bates on his interesting railscast. Every time the user opens the website, all files required by the application are downloaded and saved for offline browsing. This solves the problem of accessing the static content in offline mode.
Another important aspect is that only one HTML page is fetched from the
server. This page contains the necessary components to generate four views —
index, show, edit and new. We used backbone.js and
underscore templates to build them. Backbone.js let us
implement a well-structured client-side application, but is not so
rigid opinionated as Rails. To organize our client-side code,
we followed the approach designed by James Yu on his
article about backbone.js.
Backbone is composed by several modules. It contains one module called Backbone.sync which lets us persist data through RESTful JSON requests to the server. Since we want it to work offline too, we need it to persist the data locally, using the HTML5 WebSQL technology (if supported by the browser) or the Local Storage. This would be synced to the server, if possible, when both client and server are online. This is tougher than it may look, since we are talking about a multi-client app, with desynchronized clocks, etc..
In what concerns persistence, we used persistence.js, and specifically the persistence.sync.js plugin, to keep the databases synced together. We created an adapter, a new Backbone.sync module, which maps the CRUD actions with the persistence.sync.js library's API.
Another problem is that persistence.sync was primarly made to communicate with node.js servers. Since we are using Rails, we coded the necessary mechanisms to make it work with RoR.
At last, we wanted the server to broadcast to all connected clients when one of them modifies the server's database. We integrated Faye for the server to client communication, which uses WebSockets, XMLHttpRequest (if WebSockets not supported) or JSON-P (if no other alternative is supported). We are still having some crazy issues regarding having both Faye and the application.manifest) which are listed on the bottom of this document.
app/
controllers/
application_controller.rb
home_controller.rb
items_controller.rb
models/
item.rb
views/
home/
index.html.erb
items/
edit.jst
index.jst
show.jst
public/
javascripts/
app/
collections/
items.js
controllers/
items.js
models/
item.js
views/
edit.js
index.js
notice.js
show.js
application.js
server.push.js
sync.js
To initialize the application, run:
mv config/database.yml.sample config/database.yml
mv public/application.manifest.sample public/application.manifest
bundle install
rake db:migrate
Run Faye:
rackup script/faye.ru -s thin -E production
Run the Rails sever:
rails server
A list of the current issues will be added soon.
For now, you may check our github page to keep track of all the undergoing changes.
Feel free to use the code for your own projects. Improvements are very welcome (keep in mind that we are junior developers, with no background on Javascript, asynchronous programming and all the like). Pull requests to our git repository would be greatly appreciated!
- David Francisco { hello@dmfranc.com; @dmfrancisco }
- José Dias { jacdias@student.dei.uc.pt }