Underscore-Awesomer: _.parseJSON for CouchDB model

This is an example showing a recent modification to _.parseJSON. Besides introducing a convention for serializing plain-old-JSON (POJ) into live instances, what I wanted to do was 1) introduce some flexibility into which field is used to look up the parseJSON methods, and 2) provide a way to not pollute the global namespace with your parseJSON methods.

# set up for CouchDB 'type' convention 
_.PARSE_JSON_TYPE_FIELD = 'type'
# make a Constructors namespace known to Underscore
root = this; root.Constructors||root.Constructors={}
_.PARSE_JSON_CONSTRUCTOR_ROOTS.push(root.Constructors)

class SomeModel
  constructor: (@key, @value) ->
  toJSON = -> return { type:'some_model', key:this.key, value:this.value }
  @parseJSON = (json) ->
    throw new Error('unexpected type') if (json.type!='some_model')
    return new SomeModel(json.key, json.value)
root.Constructors.some_model = SomeModel # add constructor to Constructors namespace

# assume this comes back from the server
json = {type:'some_model', key:'abcdef', value: {x:1, y:2, z:3} }

# deserialize - _.parseJSON finds 'root.Constructors.some_model'
some_model_instance = _.parseJSON(json)

Personal uses: Using my Backbone-Articulation.js library (Backbone-Articulation.js), I now automatically serialize the JSON from the server into instances inside my model attributes on the client. It means I am able to combine data with functionality on the client behind-the-scenes and just start interacting with the attributes directly.

You can find this example and more here: https://github.com/kmalakoff/examples-kmalakoff

Enjoy!

Download now (Right-click, and use “Save As”):

Or check out the project here: https://github.com/kmalakoff/underscore-awesomer

git flow release finish first_missing_piece

Today, I published what I solution to the first missing piece:

Allow for offline CouchApp loading since you cannot assume there will be a connection to download a CouchApp

that I hope others will find useful and efficient to use in their day-to-day development workflow.

Workflow

To end up with a nicely packaged couchapp, views, and application seed data, you need to decide what workflow you want to follow during development, testing, release, etc. I chose the following process:

  1. use source files for everything. I originally tried to edit views in futon, but besides not having source files that could be checked into version control, after the first edit, the web formatting made it difficult to maintain.
  2. develop and debug on my laptop to take advantage of a full suite of browser developer tools. If you want to get a feel for how difficult directly testing in phonegap can be, take a look at the following post: http://www.phonegap.com/2011/05/18/debugging-phonegap-javascript.
  3. run on a mobile simulator or hardware periodically after testing on laptop/PC. This should catch device-specific problems and differences after the general problems have been fixed in a more productive laptop/PC development environment.

In order to achieve this result, I am using the following development workflow:

  1. use soca on my local filesystem to have source files for everything including the couchapp, views, and application seed data.
  2. use ‘soca autopush’ to propagate any local changes into a couch development server. Note: I added a new flag to soca’s config.js file so until the main branch picks it up (fingers crossed), you will need to use my copy in GitHub.
  3. use ‘couchpack database_url destination_file —auto’ to watch for and pack all changes into my iOS project’s Resources directory. I wrote the couchpack and couchwatcher gems for this purpose.
  4. build and run in XCode
  5. use an Objective-C class called ‘CouchMover’ to check packed version files for changes at runtime and to upload on app launch. I released this class in the phonegap-couchbase-xplatform GitHub repository.

Note: currently, this workflow has only been tested on the Mac OSX platform and does not yet have sample code for an android version. Volunteers are welcome!

Setup details

Install: soca 

For now, you’ll need to get a local copy from GitHub and to build/install a local copy:

sudo gem uninstall soca
git clone git@github.com:kmalakoff/soca.git
cd soca
sudo rake install

Install: couchpack 

sudo gem install couchpack

Usage - Laptop/PC

I recommend you download the cross-platform demo project at: https://github.com/kmalakoff/phonegap-couchbase-xplatform. It demonstrates the workflow and you won’t be slowed down with setting up a PhoneGap/Couchbase project from scratch.

In the demo project, you will find a file called couch_automate.rb. It demonstrates how you can launch all of the soca and couchpack watching with one command ‘ruby couch_automate.rb’. Here’s the meaningful parts:

exec 'cd soca/mycouchapp; soca autopush'
exec 'cd phonegap-couchbase-ios/phonegap-couchbase-ios/Resources; 
   couchpack document http://localhost:5984/mycouchapp_db/_design/mycouchapp 
      mycouchapp --auto'

The first line starts watching the soca project and pushes changes into the couchdb document; the second line starts watching the couchdb document and packs any changes into the iOS Xcode project Resources directory.

Usage - Objective-C

When you receive the CouchbaseDelegate's method callback couchbaseDidStart you can use the CouchMover class to check for version changes in your packed Resources and if there are changes, upload them to the iOS Couchbase CouchDB using the following:

NSURLCredential *credential = [NSURLCredential credentialWithUser:@"admin" 
  password:@"admin" persistence:NSURLCredentialPersistenceForSession];
CouchMover* couchMover = [[CouchMover alloc] init:serverURL 
  serverCredential:credential databaseName:@"mycouchapp_db"];
[couchMover loadDocumentFromBundle:[NSBundle mainBundle] 
  documentName:@"_design/mycouchapp" 
  documentBundlePath:@"mycouchapp.json" 
  versionBundlePath:@"mycouchapp.version"];
[couchMover gotoAppPage:@"_design/mycouchapp" webView:self.webView 
  page:@"index.html"];
[couchMover release];

The CouchMover class is flexible enough to upload documents which can be packaged couchapps, views, or application seed data to any database (just call ‘[couchMover setDatabaseName:@”yourdatadatabasename”]’ to switch databases) and can be used without the bundles if you prefer (just call ‘[couchMover loadDocument:documentName version:version getAppAsJSONDataBlock:yourblock_returningNSData]’)

Gotchas

There are many and I’ll maintain this list based on your feedback:

  1. To get the sample running on actual hardware, you’ll need to register the app with Apple using these PhoneGap instructions: http://wiki.phonegap.com/w/page/39991939/Getting-Started-with-PhoneGap-iOS-using-Xcode-4-(Template-Version
  2. After you pack your couchdb documents for the first time, you need to drag them into your XCode project’s Resources directory.
  3. In order to not have your PhoneApp switch to the local Safari app when you call [webView stringByEvaluatingJavaScriptFromString:couchappApgeURL], you need to add localhost to the ExternalHosts key in your PhoneGap.plist. Select your PhoneGap.plist in the “Supporting Files” directory, and add the following 2 items as separate entries: 127.0.0.1 and 0.0.0.0.
  4. The CouchMover Objective-C class  current has dependencies on TouchFoundation’s classes on NSData asBase64EncodedString. If you aren’t linking with TouchFoundation, include the files that you find in the sample project’s Vendor/TouchFoundation_MinimalExtraction in your project.

Done! Well, if only reality were that simple… I’m happy to update this post and answer your questions as you are confronted with real-world problems in getting this to work.

Good luck!

Kevin

Brain.new(“code”)

Hello and Welcome!

This is my first blog and so my first blog post. So why start now? Well, simply put, I want to share my recent experiences on my path to creating a cross-platform mobile application…I’m done yet!

Recently, I switched from Objective-C native to Rhodes and finally to PhoneGap + Couchbase for various reasons. Objective-C felt too heavy given all the memory management boilerplate code. Rhodes was very efficient, but RhoSync licensing was too expensive. So despite the pioneering efforts required to use Couchbase (http://www.couchbase.com), which provides mobile versions of CouchDB (http://couchdb.apache.org), I have decided to start blazing a trail and help the community make the Couchbase ecosystem fullfil its full potential.

I have released the following libraries and ruby gems, which, among other things, I will be providing tutorials and background on in the future:

In addition to the choice of base application technologies, in order to build something cross-platform that is easier to maintain, I have based the client on the following web technologies: brunch (http://brunchwithcoffee.com) which packages CoffeeScript, Backbone.js, Underscore.js, Eco, Stylus, and CommonJS into a really nice working environment; iScroll 4; and many great query libraries including JQuery Mobile and JQuery Global.

I look forward to hearing from you and I hope you find this useful on your own journey.

Cheers!

Kevin