Permalink
Browse files

make schema setup a lot more like the ruby library, and fix up event …

…sets a bit.
  • Loading branch information...
1 parent bbeb4f8 commit e4e4b863f1aaaac832ae40c384e8767cf35490be @robey committed Apr 4, 2013
Showing with 196 additions and 91 deletions.
  1. +25 −20 src/fauna.coffee
  2. +81 −48 src/schema.coffee
  3. +21 −16 test/test_fauna.coffee
  4. +69 −7 test/test_schema.coffee
View
@@ -80,25 +80,23 @@ class Fauna
collection = (x for x in collection when x.ref != event.resource)
else
# ignore
- collection = collection.map (item) ->
+ collection = collection.map (item) =>
newrefs = {}
for k, v of item.references
v = if v instanceof Array then v.map((x) -> data.references[x]) else data.references[v]
newrefs[k] = v
item.references = newrefs
- item
- collection.map (item) => @schema.inflate(item)
+ @schema.inflate(item, @)
+ { before: data.resource.before, after: data.resource.after, items: collection }
# tell fauna to create classes & event sets for us.
# return: { class, event_sets[] }[]
setupSchemas: ->
Q.all(
- for name, schema of @schema.getSchemas()
- eventSets = (k for k, v of schema when v == EventSet)
- do (eventSets) =>
- @createClass(name).then (klass) =>
- Q.all(for setName in eventSets then @createEventSet(name, setName)).then (sets) =>
- { "class": klass, "event_sets": sets }
+ @schema.foreachClass (name, metadata) =>
+ @createClass(name).then (klass) =>
+ Q.all(for setName in metadata.eventSets then @createEventSet(name, setName)).then (sets) =>
+ { "class": klass, "event_sets": sets }
)
@@ -115,7 +113,7 @@ class Fauna
# auth: publisher email
getPublisherKeys: ->
@get("keys/publisher").then (data) =>
- @collapseEvents(data)
+ @collapseEvents(data).items
# auth: publisher email
createPublisherKey: ->
@@ -145,7 +143,7 @@ class Fauna
# auth: publisher email
getClientKeys: ->
@get("keys/client").then (data) =>
- @collapseEvents(data)
+ @collapseEvents(data).items
# auth: publisher email
createClientKey: ->
@@ -179,8 +177,9 @@ class Fauna
@debug "Created class #{data.resource.ref}"
data.resource
- listInstancesOfClass: (className, count=null) ->
- @listEventSet("classes/#{className}", count)
+ # auth: token or publisher
+ listInstancesOfClass: (className, params) ->
+ @getEventSet("classes/#{className}", params)
## ----- event sets
@@ -192,10 +191,16 @@ class Fauna
data.resource
# auth: token or publisher
- listEventSet: (setName, count=null) ->
- countArg = if count? then "?count=#{count}" else ""
- @get("#{setName}#{countArg}").then (data) => @collapseEvents(data)
+ # returns: { before, after, items }
+ getEventSet: (setName, params) ->
+ list = []
+ if params.count? then list.push "count=#{params.count}"
+ if params.before? then list.push "before=#{params.before}"
+ if params.after? then list.push "after=#{params.after}"
+ @get("#{setName}?#{list.join("&")}").then (data) => @collapseEvents(data)
+ # auth: token or publisher
+ # returns: list containing the new object
addToEventSet: (setName, ref) ->
@post("#{setName}", resource: ref).then (data) =>
@collapseEvents(data)
@@ -209,11 +214,11 @@ class Fauna
# auth: publisher
getUserByEmail: (email) ->
- @get("users/email/#{email}").then (data) => @schema.inflate(data.resource)
+ @get("users/email/#{email}").then (data) => @schema.inflate(data.resource, @)
# auth: token or publisher
getUser: (ref) ->
- @get("#{ref}").then (data) => @schema.inflate(data.resource)
+ @get("#{ref}").then (data) => @schema.inflate(data.resource, @)
# auth: client or publisher
createUser: (user) ->
@@ -241,13 +246,13 @@ class Fauna
# auth: token or publisher
# returns: object
getInstance: (ref) ->
- @get("#{ref}").then (data) => @schema.inflate(data.resource)
+ @get("#{ref}").then (data) => @schema.inflate(data.resource, @)
# auth: token or publisher
createInstance: (obj) ->
className = @schema.classNameFor(obj)
@post("classes/#{className}", @schema.deflate(obj)).then (data) =>
- @schema.setMeta(obj, className, data.resource)
+ @schema.setFaunaData(obj, @, className, data.resource)
obj
deleteInstance: (ref) ->
View
@@ -7,30 +7,71 @@ dump = (x) -> util.inspect(x, false, null, true)
class Class
constructor: (attrs) ->
for k, v of attrs then @[k] = v
- for k, v of @schema
- if v == EventSet
- @[k] = new EventSetWrapper(@, k)
+ for name in metadataForClass(@.constructor).eventSets
+ @[name] = new CustomEventSet(@, name)
+
+ @faunaClass = (name) ->
+ metadataForClass(@).className = name
+
+ @field = (names...) ->
+ for name in names
+ metadataForClass(@).schema[name] = "data"
+
+ @reference = (names...) ->
+ for name in names
+ metadataForClass(@).schema[name] = "reference"
+
+ @eventSet = (names...) ->
+ for name in names
+ metadataForClass(@).eventSets.push name
+
+ @getInstances = (fauna, params) ->
+ fauna.listInstancesOfClass(metadataForClass(@).getClassName(), params)
- faunaClass: null
- schema: {}
exports.Class = Class
+class ClassMetadata
+ constructor: (@jsClass) ->
+ @className = null
+ @schema = {}
+ @eventSets = []
+
+ getClassName: ->
+ if not @className?
+ # if a fauna class name isn't manually set, assume it's the lowercase
+ # version of the js class name + "s" (User -> users)
+ @className = @jsClass.toString().match(/function (\w+)/)[1].toLowerCase() + "s"
+ @className
+
+
+metadataForClass = (c) ->
+ if not c.__fauna__?
+ c.__fauna__ = new ClassMetadata(c)
+ c.__fauna__
+
+
+# some functions for dealing with event sets
class EventSetWrapper
- constructor: (@instance, @fieldName) ->
-
- name: -> "#{@instance.meta?.id}/sets/#{@fieldName}"
+ # this constructor is only used for fauna built-in sets.
+ constructor: (@instance, @setName) ->
+ name: -> @setName
-# things that can be in a js schema:
-Reference = "reference"
-Data = "data"
-EventSet = "eventset"
+ add: (obj) ->
+ @instance._fauna.fauna.addToEventSet(@name(), obj._fauna.id)
-exports.Reference = Reference
-exports.Data = Data
-exports.EventSet = EventSet
+ page: (params) ->
+ @instance._fauna.fauna.getEventSet(@name(), params)
+ # add(instance)
+ # page(count, before, after)
+
+
+class CustomEventSet extends EventSetWrapper
+ constructor: (@instance, @fieldName) ->
+
+ name: -> "#{@instance._fauna?.id}/sets/#{@fieldName}"
# lives inside Fauna and handles transforming fauna objects to/from js objects.
@@ -40,65 +81,57 @@ class Schema
addClasses: (classes...) ->
for c in classes
- className = if c.prototype.faunaClass?
- c.prototype.faunaClass
- else
- c.toString().match(/function (\w+)/)[1].toLowerCase() + "s"
- c.prototype.faunaClass = className
- @classes[className] =
- schema: c.prototype.schema
- prototype: c
-
- schemaFor: (name) ->
- descriptor = @classes[name]
- if descriptor?.schema? then descriptor.schema else {}
+ metadata = metadataForClass(c)
+ @classes[metadata.getClassName()] = c
+
+ # f(className, metadata)
+ foreachClass: (f) ->
+ for name, c of @classes then f(name, metadataForClass(c))
classNameFor: (obj) ->
- obj.__proto__.faunaClass
+ metadataForClass(obj.constructor).getClassName()
- getSchemas: ->
- rv = {}
- for k, v of @classes then rv[k] = @schemaFor(k)
- rv
+ metadataForClassName: (name) ->
+ metadataForClass(@classes[name])
- setMeta: (obj, className, resource) ->
- obj.meta =
+ setFaunaData: (obj, fauna, className, resource) ->
+ obj._fauna =
id: resource.ref
className: className
ts: new Date(resource.ts / 1000)
+ fauna: fauna
- inflate: (resource) ->
+ inflate: (resource, fauna) ->
className = resource["class"]
if "/" in className then className = resource["class"]?.split("/")[1]
fclass = @classes[className]
- obj = if fclass?.prototype? then new fclass.prototype() else new Object()
- @setMeta(obj, className, resource)
+ obj = if fclass? then new fclass() else new Object()
+ @setFaunaData(obj, fauna, className, resource)
# copy over anything interesting that isn't data/reference
for k, v of resource
if not (k in [ "ref", "class", "ts", "deleted", "data", "references" ])
obj[k] = v
# pull in any data/resources
- if fclass?.schema? then for k, fieldType of fclass.schema
- if fieldType == Data
+ if fclass? then for k, fieldType of metadataForClass(fclass).schema
+ if fieldType == "data"
obj[k] = resource.data[k]
- else if fieldType == Reference
+ else if fieldType == "reference"
ref = resource.references[k]
- if ref? then obj[k] = @inflate(ref)
+ if ref? then obj[k] = @inflate(ref, fauna)
obj
deflate: (obj) ->
- className = @classNameFor(obj)
- schema = if className? then @classes[className]?.schema else {}
+ metadata = metadataForClass(obj.constructor)
rv = { data: {}, references: {} }
- for k, fieldType of schema then if obj[k]?
- if fieldType == Data
+ for k, fieldType of metadata.schema then if obj[k]?
+ if fieldType == "data"
rv.data[k] = obj[k]
- else if fieldType = Reference
+ else if fieldType = "reference"
id = if typeof obj[k] == "string"
obj[k]
else
- if not obj[k].meta? then throw new Error("Unable to find reference metadata on #{k} in #{obj}")
- obj[k].meta.id
+ if not obj[k]._fauna? then throw new Error("Unable to find reference metadata on #{k} in #{obj}")
+ obj[k]._fauna.id
rv.references[k] = id
rv
View
@@ -1,12 +1,14 @@
should = require 'should'
-inspect = require("util").inspect
+util = require 'util'
fauna = require("../lib/fauna")
Q = require("q")
Rest = fauna.Rest
Fauna = fauna.Fauna
+dump = (x) -> util.inspect(x, false, null, true)
+
withSuccessfulRequest = (json, f) ->
Rest.withRequestHandler(((options) -> [ null, 200, JSON.stringify(json) ]), f)
@@ -65,17 +67,21 @@ describe "Fauna", ->
"classes/messages/1": message_1
"classes/messages/2": message_2
f = new Fauna()
- f.collapseEvents(response).should.eql [
- { meta: { id: "classes/messages/2", className: "messages", ts: new Date(930) } }
- { meta: { id: "classes/messages/1", className: "messages", ts: new Date(900) } }
- ]
+ f.collapseEvents(response).should.eql {
+ before: undefined,
+ after: undefined,
+ items: [
+ { _fauna: { id: "classes/messages/2", className: "messages", ts: new Date(930), fauna: f } }
+ { _fauna: { id: "classes/messages/1", className: "messages", ts: new Date(900), fauna: f } }
+ ]
+ }
it "unpacks schemas", ->
class Message extends fauna.Class
- schema: { author: fauna.Reference }
+ @reference "author"
getAuthor: -> @author
- class User
- schema: { email: fauna.Data }
+ class User extends fauna.Class
+ @field "email"
response =
resource:
ref: "classes/messages"
@@ -87,17 +93,17 @@ describe "Fauna", ->
"users/11": user_11
f = new Fauna()
f.addClasses Message, User
- obj = f.collapseEvents(response)
- (obj[0] instanceof Message).should.equal(true)
- obj.should.eql [
+ page = f.collapseEvents(response)
+ (page.items[0] instanceof Message).should.equal(true)
+ page.items.should.eql [
{
- meta: { id: "classes/messages/3", className: "messages", ts: new Date(991) }
+ _fauna: { id: "classes/messages/3", className: "messages", ts: new Date(991), fauna: f }
author:
- meta: { id: "users/11", className: "users", ts: new Date(999) }
+ _fauna: { id: "users/11", className: "users", ts: new Date(999), fauna: f }
email: "root@example.com"
}
]
- obj[0].getAuthor().email.should.eql("root@example.com")
+ page.items[0].getAuthor().email.should.eql("root@example.com")
it "can handle 204 responses", futureTest ->
f = new Fauna()
@@ -108,8 +114,7 @@ describe "Fauna", ->
it "can setup a schema", futureTest ->
class Fish extends fauna.Class
- schema:
- scales: fauna.EventSet
+ @eventSet "scales"
handler = (options) ->
if options.url.match(/classes\/fishs\/config/)
[ null, 200, JSON.stringify(resource: { ref: "classes/fishs/config" }) ]
Oops, something went wrong.

0 comments on commit e4e4b86

Please sign in to comment.