diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..5daafc7 --- /dev/null +++ b/.gitignore @@ -0,0 +1,6 @@ +.DS_Store +couchdb-flex/.actionScriptProperties +couchdb-flex/.flexLibProperties +couchdb-flex/.project +couchdb-flex/.settings +couchdb-flex/bin diff --git a/couchdb-flex/lib/corelib-20070830.swc b/couchdb-flex/lib/corelib-20070830.swc new file mode 100644 index 0000000..cd25454 Binary files /dev/null and b/couchdb-flex/lib/corelib-20070830.swc differ diff --git a/couchdb-flex/src/com/gravityblast/couchdb/CouchDb.as b/couchdb-flex/src/com/gravityblast/couchdb/CouchDb.as new file mode 100644 index 0000000..45e985d --- /dev/null +++ b/couchdb-flex/src/com/gravityblast/couchdb/CouchDb.as @@ -0,0 +1,36 @@ +package com.gravityblast.couchdb +{ + import com.adobe.net.URI; + + public class CouchDb + { + + public var host:String; + public var port:uint; + private var _uri:URI; + + public static var defaultDatabase:Database; + + public function CouchDb(host:String="127.0.0.1", port:uint=5984) + { + this.host = host; + this.port = port; + this._uri = new URI(); + this._uri.unknownToURI(this.host); + this._uri.port = this.port.toString(); + } + + public function database(name:String):Database + { + var database:Database = new Database(this, name); + if (defaultDatabase == null) + defaultDatabase = database; + return database; + } + + public function get uri():String + { + return this._uri.toString(); + } + } +} diff --git a/couchdb-flex/src/com/gravityblast/couchdb/CouchRest.as b/couchdb-flex/src/com/gravityblast/couchdb/CouchRest.as new file mode 100644 index 0000000..a812152 --- /dev/null +++ b/couchdb-flex/src/com/gravityblast/couchdb/CouchRest.as @@ -0,0 +1,112 @@ +package com.gravityblast.couchdb +{ + import com.adobe.serialization.json.JSON; + import com.adobe.serialization.json.JSONParseError; + import com.gravityblast.couchdb.errors.CouchRestError; + import com.gravityblast.couchdb.events.CouchRestEvent; + + import flash.events.Event; + import flash.events.EventDispatcher; + import flash.events.HTTPStatusEvent; + import flash.events.IOErrorEvent; + import flash.net.URLLoader; + import flash.net.URLRequest; + import flash.net.URLRequestMethod; + import flash.net.URLVariables; + + public class CouchRest extends EventDispatcher + { + public var loader:URLLoader; + + public function CouchRest():void + { + this.loader = new URLLoader(); + loader.addEventListener(Event.COMPLETE, this.completeEventHandler); + loader.addEventListener(IOErrorEvent.IO_ERROR, this.ioErrorEventHandler); + loader.addEventListener(HTTPStatusEvent.HTTP_STATUS, this.httpStatusEventHandler); + super(); + } + + private function completeEventHandler(event:Event):void + { + var jsonResponse:Object = JSON.decode(this.loader.data); + var event:Event = new CouchRestEvent(CouchRestEvent.COMPLETE, jsonResponse, this.loader.data); + this.dispatchEvent(event); + } + + private function ioErrorEventHandler(event:Event):void + { + try + { + var jsonResponse:Object = JSON.decode(this.loader.data); + var event:Event = new CouchRestEvent(CouchRestEvent.COMPLETE, jsonResponse, this.loader.data); + this.dispatchEvent(event); + } + catch (error:JSONParseError) + { + if (this.hasEventListener(CouchRestEvent.IO_ERROR)) + { + var _event:CouchRestEvent = new CouchRestEvent(CouchRestEvent.IO_ERROR); + this.dispatchEvent(_event); + } + else + { + throw new CouchRestError("CouchDb Connection Error"); + } + } + } + + private function httpStatusEventHandler(event:Event):void + { + trace("http status: " + event) + } + + public function get(uri:String, params:*=null):void + { + this.load(uri, URLRequestMethod.GET, params); + } + + public function put(uri:String, params:*=null, body:*=null):void + { + this.load(uri, URLRequestMethod.PUT, params, body); + } + + public function post(uri:String, params:*=null, body:*=null):void + { + this.load(uri, URLRequestMethod.POST, params, body); + } + + public function del(uri:String, params:*=null, body:*=null):void + { + this.load(uri, URLRequestMethod.DELETE); + } + + public function load(uri:String, method:String, params:*=null, body:*=null, callback:Function=null):void + { + var request:URLRequest = new URLRequest(uri); + request.contentType = "application/json"; + request.method = method; + if (params) + { + request.data = this.objectToUrlVariables(params); + } + else if (body) + { + request.data = body; + } + + this.loader.load(request); + } + + public function objectToUrlVariables(object:Object):URLVariables + { + var variables:URLVariables = new URLVariables(); + for (var propertyName:String in object) + { + variables[propertyName] = object[propertyName]; + } + + return variables; + } + } +} \ No newline at end of file diff --git a/couchdb-flex/src/com/gravityblast/couchdb/Database.as b/couchdb-flex/src/com/gravityblast/couchdb/Database.as new file mode 100644 index 0000000..475d1d7 --- /dev/null +++ b/couchdb-flex/src/com/gravityblast/couchdb/Database.as @@ -0,0 +1,79 @@ +package com.gravityblast.couchdb +{ + import com.adobe.serialization.json.JSON; + import com.gravityblast.couchdb.errors.DatabaseError; + import com.gravityblast.couchdb.events.CouchRestEvent; + import com.gravityblast.couchdb.events.DatabaseEvent; + + import flash.events.EventDispatcher; + + + public class Database extends EventDispatcher + { + + public var couchdb:CouchDb; + public var name:String; + + public function Database(couchdb:CouchDb, name:String) + { + this.couchdb = couchdb; + this.name = name; + super(); + } + + public function create(completeCallback:Function=null):void + { + var couchRest:CouchRest = this.buildCouchRest(completeCallback); + couchRest.put(this.couchdb.uri + "/" + this.name); + } + + public function del(completeCallback:Function=null):void + { + var couchRest:CouchRest = this.buildCouchRest(completeCallback); + couchRest.del(this.couchdb.uri + "/" + this.name); + } + + public function saveDocument(document:Object, completeCallback:Function=null):void + { + var couchRest:CouchRest = this.buildCouchRest(completeCallback); + if (document._id) + couchRest.put(this.couchdb.uri + "/" + this.name + "/" + document._id, null, JSON.encode(document)); + else + couchRest.post(this.couchdb.uri + "/" + this.name, null, JSON.encode(document)); + } + + public function get(id:String, params:Object=null, completeCallback:Function=null):void + { + var couchRest:CouchRest = this.buildCouchRest(completeCallback); + couchRest.get(this.couchdb.uri + "/" + this.name + "/" + id); + } + + public function documents(params:Object=null, completeCallback:Function=null):void + { + var couchRest:CouchRest = this.buildCouchRest(completeCallback); + couchRest.get(this.couchdb.uri + "/" + this.name + "/" + "_all_docs"); + } + + protected function couchRestErrorHandler(event:CouchRestEvent):void + { + if (hasEventListener(DatabaseEvent.ERROR)) + { + var _event:DatabaseEvent = new DatabaseEvent(DatabaseEvent.ERROR); + this.dispatchEvent(_event); + } + else + { + throw new DatabaseError(); + } + } + + protected function buildCouchRest(completeCallback:Function=null):CouchRest + { + var couchRest:CouchRest = new CouchRest(); + couchRest.addEventListener(CouchRestEvent.IO_ERROR, this.couchRestErrorHandler); + if (completeCallback != null) + couchRest.addEventListener(CouchRestEvent.COMPLETE, completeCallback); + return couchRest; + } + } +} \ No newline at end of file diff --git a/couchdb-flex/src/com/gravityblast/couchdb/Design.as b/couchdb-flex/src/com/gravityblast/couchdb/Design.as new file mode 100644 index 0000000..002365d --- /dev/null +++ b/couchdb-flex/src/com/gravityblast/couchdb/Design.as @@ -0,0 +1,42 @@ +package com.gravityblast.couchdb +{ + public class Design extends Document + { + public var name:String; + private var documentClass:Class; + private var _views:Array; + + public function Design(name:String, documentClass:Class=null) + { + this.name = name; + this.documentClass = documentClass; + this._views = new Array(); + super(); + } + + public function addView(view:View):void + { + this._views.push(view); + } + + override public function get attributes():Object + { + var _attributes:Object = super.attributes; + delete _attributes["couchdb-flex-type"]; + _attributes.language = "javascript"; + _attributes._id = "_design/" + this.name; + var views:Object = new Object(); + for each (var _view:View in this._views) + { + var view:Object = new Object(); + view.map = _view.map; + if (_view.reduce) + view.reduce = _view.reduce; + views[_view.name] = view; + } + _attributes.views = views; + + return _attributes; + } + } +} \ No newline at end of file diff --git a/couchdb-flex/src/com/gravityblast/couchdb/Document.as b/couchdb-flex/src/com/gravityblast/couchdb/Document.as new file mode 100644 index 0000000..193b5f9 --- /dev/null +++ b/couchdb-flex/src/com/gravityblast/couchdb/Document.as @@ -0,0 +1,80 @@ +package com.gravityblast.couchdb +{ + import com.gravityblast.couchdb.events.CouchRestEvent; + import com.gravityblast.couchdb.events.DocumentEvent; + + import flash.utils.describeType; + import flash.utils.getDefinitionByName; + import flash.utils.getQualifiedClassName; + + dynamic public class Document + { + public static var database:Database; + + public var _id:String; + public var _rev:String; + + public function Document() + { + if (Document.database == null) + database = CouchDb.defaultDatabase; + super(); + } + + public function get attributes():Object + { + var _attributes:Object = {}; + for each (var attributeName:String in this.attributeNames()) + _attributes[attributeName] = this[attributeName]; + for (var dynamicAttributeName:String in this) + _attributes[dynamicAttributeName] = this[dynamicAttributeName]; + _attributes["couchdb-flex-type"] = getQualifiedClassName(this); + + return _attributes; + } + + public function set attributes(value:Object):void + { + for (var propertyName:String in value) + { + try + { + this[propertyName] = value[propertyName]; + } + catch(error:Error) + { + trace(error); + } + } + } + + public function save(completeCallback:Function=null):void + { + var saver:DocumentSaver = new DocumentSaver(database, this); + if (completeCallback != null) + saver.addEventListener(CouchRestEvent.COMPLETE, completeCallback); + saver.save(); + } + + private function attributeNames():Array + { + var names:Array = new Array(); + var classInfo:XML = describeType(this); + for each ( var v:XML in classInfo..*.((true) && (name() == "variable" || (name() == "accessor" && attribute( "access" ).charAt( 0 ) == "r") ))) + { + if (v.@name != "attributes" && v.@name != "database" && v.@name != "_id" && v.@name != "_rev") + names.push(v.@name); + } + + return names; + } + + public static function load(id:String, completeCallback:Function=null):void + { + var loader:DocumentLoader = new DocumentLoader(); + if (completeCallback != null) + loader.addEventListener(DocumentEvent.LOADED, completeCallback); + loader.load(id); + } + } +} \ No newline at end of file diff --git a/couchdb-flex/src/com/gravityblast/couchdb/DocumentLoader.as b/couchdb-flex/src/com/gravityblast/couchdb/DocumentLoader.as new file mode 100644 index 0000000..ea6ff2d --- /dev/null +++ b/couchdb-flex/src/com/gravityblast/couchdb/DocumentLoader.as @@ -0,0 +1,41 @@ +package com.gravityblast.couchdb +{ + import com.gravityblast.couchdb.events.CouchRestEvent; + import com.gravityblast.couchdb.events.DocumentEvent; + + import flash.events.EventDispatcher; + import flash.utils.getDefinitionByName; + + public class DocumentLoader extends EventDispatcher + { + public function DocumentLoader() + { + super(null); + } + + public function load(id:String):void + { + CouchDb.defaultDatabase.get(id, null, this.loadHandler); + } + + public function loadHandler(event:CouchRestEvent):void + { + if (this.hasEventListener(DocumentEvent.LOADED)) + { + var document:*; + if (event.json["couchdb-flex-type"]) + { + var ClassReference:Class = getDefinitionByName(event.json["couchdb-flex-type"]) as Class; + document = new ClassReference(); + document.attributes = event.json; + } + else + { + document = event.json; + } + var e:DocumentEvent = new DocumentEvent(DocumentEvent.LOADED, document); + this.dispatchEvent(e); + } + } + } +} \ No newline at end of file diff --git a/couchdb-flex/src/com/gravityblast/couchdb/DocumentSaver.as b/couchdb-flex/src/com/gravityblast/couchdb/DocumentSaver.as new file mode 100644 index 0000000..2c92634 --- /dev/null +++ b/couchdb-flex/src/com/gravityblast/couchdb/DocumentSaver.as @@ -0,0 +1,47 @@ +package com.gravityblast.couchdb +{ + import com.gravityblast.couchdb.events.CouchRestEvent; + + import flash.events.EventDispatcher; + import flash.utils.getQualifiedClassName; + + public class DocumentSaver extends EventDispatcher + { + + protected var database:Database; + protected var document:Document; + + public function DocumentSaver(database:Database, document:Document) + { + this.database = database; + this.document = document; + super(); + } + + public function save():void + { + var attributes:Object = this.document.attributes; + if (this.document._rev) + { + attributes._id = this.document._id; + attributes._rev = this.document._rev; + } + this.database.saveDocument(attributes, this.saveHandler); + } + + protected function saveHandler(event:CouchRestEvent):void + { + if (event.json.ok) + { + this.document._id = event.json.id; + this.document._rev = event.json.rev; + } + if (this.hasEventListener(CouchRestEvent.COMPLETE)) + { + // FIXME: + var e:CouchRestEvent = new CouchRestEvent(CouchRestEvent.COMPLETE, event.json, event.data); + this.dispatchEvent(e); + } + } + } +} \ No newline at end of file diff --git a/couchdb-flex/src/com/gravityblast/couchdb/View.as b/couchdb-flex/src/com/gravityblast/couchdb/View.as new file mode 100644 index 0000000..4455588 --- /dev/null +++ b/couchdb-flex/src/com/gravityblast/couchdb/View.as @@ -0,0 +1,27 @@ +package com.gravityblast.couchdb +{ + import com.gravityblast.couchdb.events.ViewEvent; + + public class View + { + + public var name:String; + public var map:String; + public var reduce:String; + + public function View(name:String, map:String, reduce:String=null) + { + this.name = name; + this.map = map; + this.reduce = reduce; + } + + public static function load(designName:String, viewName:String, params:Object, completeCallback:Function=null):void + { + var loader:ViewLoader = new ViewLoader(); + if (completeCallback != null) + loader.addEventListener(ViewEvent.LOADED, completeCallback); + loader.load("_design/" + designName + "/_view/" + viewName); + } + } +} \ No newline at end of file diff --git a/couchdb-flex/src/com/gravityblast/couchdb/ViewLoader.as b/couchdb-flex/src/com/gravityblast/couchdb/ViewLoader.as new file mode 100644 index 0000000..5b127b2 --- /dev/null +++ b/couchdb-flex/src/com/gravityblast/couchdb/ViewLoader.as @@ -0,0 +1,38 @@ +package com.gravityblast.couchdb +{ + import com.gravityblast.couchdb.events.CouchRestEvent; + import com.gravityblast.couchdb.events.ViewEvent; + + import flash.utils.getDefinitionByName; + + public class ViewLoader extends DocumentLoader + { + public function ViewLoader() + { + super(); + } + + override public function loadHandler(event:CouchRestEvent):void + { + if (this.hasEventListener(ViewEvent.LOADED)) + { + var documents:Array = new Array(); + if (event.json.rows) { + for each (var row:Object in event.json.rows ) + { + if (row.value) + { + var ClassReference:Class; + ClassReference = row.value["couchdb-flex-type"] ? (getDefinitionByName(row.value["couchdb-flex-type"]) as Class) : Object; + var document:Object = new ClassReference(); + document.attributes = row.value; + documents.push(document); + } + } + } + var e:ViewEvent = new ViewEvent(ViewEvent.LOADED, documents); + this.dispatchEvent(e); + } + } + } +} \ No newline at end of file diff --git a/couchdb-flex/src/com/gravityblast/couchdb/errors/CouchRestError.as b/couchdb-flex/src/com/gravityblast/couchdb/errors/CouchRestError.as new file mode 100644 index 0000000..a0583a1 --- /dev/null +++ b/couchdb-flex/src/com/gravityblast/couchdb/errors/CouchRestError.as @@ -0,0 +1,10 @@ +package com.gravityblast.couchdb.errors +{ + public class CouchRestError extends Error + { + public function CouchRestError(message:*="", id:*=0) + { + super(message, id); + } + } +} \ No newline at end of file diff --git a/couchdb-flex/src/com/gravityblast/couchdb/errors/DatabaseError.as b/couchdb-flex/src/com/gravityblast/couchdb/errors/DatabaseError.as new file mode 100644 index 0000000..4aba0a3 --- /dev/null +++ b/couchdb-flex/src/com/gravityblast/couchdb/errors/DatabaseError.as @@ -0,0 +1,10 @@ +package com.gravityblast.couchdb.errors +{ + public class DatabaseError extends Error + { + public function DatabaseError(message:*="", id:*=0) + { + super(message, id); + } + } +} \ No newline at end of file diff --git a/couchdb-flex/src/com/gravityblast/couchdb/events/CouchRestEvent.as b/couchdb-flex/src/com/gravityblast/couchdb/events/CouchRestEvent.as new file mode 100644 index 0000000..83cb0e6 --- /dev/null +++ b/couchdb-flex/src/com/gravityblast/couchdb/events/CouchRestEvent.as @@ -0,0 +1,21 @@ +package com.gravityblast.couchdb.events +{ + import flash.events.Event; + + public class CouchRestEvent extends Event + { + + public static const COMPLETE:String = "COUCH_REST_COMPLETE" + public static const IO_ERROR:String = "COUCH_REST_IO_ERROR" + + public var json:Object; + public var data:String; + + public function CouchRestEvent(type:String, json:Object=null, data:String=null, bubbles:Boolean=false, cancelable:Boolean=false) + { + this.json = json; + this.data = data; + super(type, bubbles, cancelable); + } + } +} \ No newline at end of file diff --git a/couchdb-flex/src/com/gravityblast/couchdb/events/DatabaseEvent.as b/couchdb-flex/src/com/gravityblast/couchdb/events/DatabaseEvent.as new file mode 100644 index 0000000..708d977 --- /dev/null +++ b/couchdb-flex/src/com/gravityblast/couchdb/events/DatabaseEvent.as @@ -0,0 +1,15 @@ +package com.gravityblast.couchdb.events +{ + import flash.events.Event; + + public class DatabaseEvent extends Event + { + + public static const ERROR:String = "COUCH_DB_DATABASE_ERROR_EVENT"; + + public function DatabaseEvent(type:String, bubbles:Boolean=false, cancelable:Boolean=false) + { + super(type, bubbles, cancelable); + } + } +} \ No newline at end of file diff --git a/couchdb-flex/src/com/gravityblast/couchdb/events/DocumentEvent.as b/couchdb-flex/src/com/gravityblast/couchdb/events/DocumentEvent.as new file mode 100644 index 0000000..4fa10d8 --- /dev/null +++ b/couchdb-flex/src/com/gravityblast/couchdb/events/DocumentEvent.as @@ -0,0 +1,18 @@ +package com.gravityblast.couchdb.events +{ + import flash.events.Event; + + public class DocumentEvent extends Event + { + + public static const LOADED:String = "COUCH_DB_DOCUMENT_LOADED_EVENT"; + + public var document:Object; + + public function DocumentEvent(type:String, document:Object, bubbles:Boolean=false, cancelable:Boolean=false) + { + this.document = document; + super(type, bubbles, cancelable); + } + } +} \ No newline at end of file diff --git a/couchdb-flex/src/com/gravityblast/couchdb/events/ViewEvent.as b/couchdb-flex/src/com/gravityblast/couchdb/events/ViewEvent.as new file mode 100644 index 0000000..19358ba --- /dev/null +++ b/couchdb-flex/src/com/gravityblast/couchdb/events/ViewEvent.as @@ -0,0 +1,17 @@ +package com.gravityblast.couchdb.events +{ + import flash.events.Event; + + public class ViewEvent extends Event + { + public static const LOADED:String = "COUCH_DB_VIEW_LOADED_EVENT"; + + public var documents:Array + + public function ViewEvent(type:String, documents:Array, bubbles:Boolean=false, cancelable:Boolean=false) + { + this.documents = documents; + super(type, bubbles, cancelable); + } + } +} \ No newline at end of file