Skip to content

Commit

Permalink
Merge branch 'feature/serialization' (issue #75)
Browse files Browse the repository at this point in the history
  • Loading branch information
aseemk committed Nov 17, 2013
2 parents a95225f + 0cedf4c commit e07fa95
Show file tree
Hide file tree
Showing 4 changed files with 116 additions and 2 deletions.
20 changes: 18 additions & 2 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,26 @@
(issue [#76][]) and to take advantage of new *standalone* compilation,
meaning Streamline is no longer a regular (production) dependency!

- Source maps are now generated alongside the compiled JS, for both
[#76]: https://github.com/thingdom/node-neo4j/issues/76

- **New:** Source maps are now generated alongside the compiled JS, for both
CoffeeScript and Streamline. Suh-weet!

[#76]: https://github.com/thingdom/node-neo4j/issues/76
- **New:** nodes and relationships can now be serialized to and from JSON!
(Issue [#75][])

Specifically, you can now freely `JSON.stringify()` any `Node` or
`Relationship` instance — or any object or array containing `Node` and/or
`Relationship` instances — and the resulting string will be deserializable
back into `Node` and/or `Relationship` instances.

To deserialize, the `GraphDatabase` class has a new `reviveJSON()`
instance method that can be passed as the second argument to `JSON.parse()`;
this method will take care of the automatic transformation above.

This is perfect for caching, e.g. to disk or Redis. Try it out!

[#75]: https://github.com/thingdom/node-neo4j/issues/75

## Version 1.0.0 — October 2, 2013

Expand Down
49 changes: 49 additions & 0 deletions lib/GraphDatabase._coffee
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@
# this can be done in Streamline syntax by adding one line before cases where
# we're returning immediately: process.nextTick _

PACKAGE = require '../package'

status = require 'http-status'

util = require './util'
Expand Down Expand Up @@ -602,6 +604,53 @@ module.exports = class GraphDatabase
catch error
throw adjustError error

## Serialization: ##

#
# Helper for other classes to serialize their data in a format that this
# GraphDatabase class will understand for *de*-serialization.
#
# @private
#
_toJSON: (obj) ->
# save this lib's basic info both for identification purposes and in
# case we ever need it in the future (e.g. for breaking changes):
package:
name: PACKAGE.name
version: PACKAGE.version
# save the object's constructor name, so we can deserialize it:
constructor: obj.constructor.name
# important: we don't save this db's URL, because it might contain a
# basic auth password!

#
# Transforms the given node or relationship object, parsed from JSON,
# to its appropriate node or relationship instance.
#
fromJSON: (obj) ->
if obj?.package?.name isnt PACKAGE.name
throw new Error "Invalid JSON object: #{JSON.stringify obj}"

{constructor} = obj
Constructor = require "./#{constructor}"
Constructor.fromJSON @, obj

#
# A "reviver" function for JSON.parse() that'll transform any serialized
# nodes or relationships into their appropriate instances.
#
# To use, pass this method as the second parameter to JSON.parse().
# For convenience, it'll be bound to this GraphDatabase instance.
#
# https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON/parse
#
reviveJSON: (k, v) =>
# only transform objects we recognize; ignore (pass through) the rest:
if v?.package?.name is PACKAGE.name
@fromJSON v
else
v

### Misc/Other: ###

#
Expand Down
22 changes: 22 additions & 0 deletions lib/PropertyContainer._coffee
Original file line number Diff line number Diff line change
Expand Up @@ -115,3 +115,25 @@ module.exports = class PropertyContainer
#
del: ->
@delete.apply @, arguments

#
# Return a JSON representation of this property container, suitable for
# serialization (e.g. caching).
#
toJSON: ->
# take the basic info for this db, then just add the data object
# directly since we need that for deserialization/construction.
# TODO it'd be great if we could store a trimmed down version of
# the data object instead of e.g. all the hypermedia URLs...
# but we need those hypermedia URLs for making requests for now.
json = @db._toJSON @
json._data = @_data
json

#
# Returns an instance of this property container for the given object,
# parsed from JSON.
#
@fromJSON: (db, obj) ->
{_data} = obj
new @ db, _data
27 changes: 27 additions & 0 deletions test/crud._coffee
Original file line number Diff line number Diff line change
Expand Up @@ -150,6 +150,33 @@ relCustomIndexName2 = 'testFollowsFullTextNoLowercase'
expect(relationship.start).to.eq daniel
expect(relationship.end).to.eq aseem

'serialize & de-serialize nodes': (_) ->
json = JSON.stringify [aseem, daniel]
obj = JSON.parse json, db.reviveJSON

expect(obj).to.be.an 'array'
expect(obj).to.have.length 2

[aseem2, daniel2] = obj

expect(aseem2).to.be.an 'object'
expect(aseem2.data).to.eql aseem.data

expect(daniel2).to.be.an 'object'
expect(daniel2.data).to.eql daniel.data

'serialize & de-serialize relationship': (_) ->
json = JSON.stringify {foo: {bar: relationship}}
obj = JSON.parse json, db.reviveJSON

expect(obj).to.be.an 'object'
expect(obj.foo).to.be.an 'object'

rel2 = obj.foo.bar

expect(rel2).to.be.an 'object'
expect(rel2.data).to.eql relationship.data

'fetch relationships': (_) ->
# test futures by *initiating* getRelationships() for both aseem and daniel in
# parallel. note how we'll still "collect" (process) the futures in sequence.
Expand Down

0 comments on commit e07fa95

Please sign in to comment.