Changeset - e19018a039f7
[Not reviewed]
default
0 2 0
drewp@bigasterisk.com - 7 years ago 2018-04-30 07:25:12
drewp@bigasterisk.com
4 of 17 passing on new n3.js
Ignore-this: 26d1489c3ee5d32646d67593ef1e4549
2 files changed with 25 insertions and 19 deletions:
0 comments (0 inline, 0 general)
light9/web/graph.coffee
Show inline comments
 
@@ -3,12 +3,13 @@ log = console.log
 
# Patch is {addQuads: <quads>, delQuads: <quads>}
 
# <quads> is [{subject: s, ...}, ...]
 

	
 
# for mocha
 
if require?
 
  `window = {}`
 
  `_ = require('./lib/underscore/underscore-min.js')`
 
  `N3 = require('../../node_modules/n3/n3-browser.js')`
 
  `d3 = require('../../node_modules/d3/dist/d3.min.js')`
 
  `RdfDbClient = require('./rdfdbclient.js').RdfDbClient`
 
  module.exports = window
 

	
 
RDF = 'http://www.w3.org/1999/02/22-rdf-syntax-ns#'
 
@@ -141,35 +142,40 @@ class window.SyncedGraph
 
      @_client = new RdfDbClient(@patchSenderUrl, @_clearGraphOnNewConnection.bind(@),
 
                                 @_applyPatch.bind(@), @setStatus)
 
    
 
  clearGraph: ->
 
    # just deletes the statements; watchers are unaffected.
 
    if @graph?
 
      @_applyPatch({addQuads: [], delQuads: @graph.find()})
 
      @_applyPatch({addQuads: [], delQuads: @graph.getQuads()})
 

	
 
    # if we had a Store already, this lets N3.Store free all its indices/etc
 
    @graph = N3.Store()
 
    @_addPrefixes(@prefixes)
 
    @cachedFloatValues = new Map();
 

	
 
  _clearGraphOnNewConnection: -> # must not send a patch to the server!
 
    log('graph: clearGraphOnNewConnection')
 
    @clearGraph()
 
    log('graph: clearGraphOnNewConnection done')
 
      
 
  _addPrefixes: (prefixes) ->
 
    @graph.addPrefixes(prefixes)
 
    for k in (prefixes or {})
 
      @prefixes[k] = prefixes[k]
 
    @prefixFuncs = N3.Util.prefixes(@prefixes)
 
        
 
  Uri: (curie) ->
 
    N3.Util.expandPrefixedName(curie, @graph._prefixes)
 
    if curie.match(/^http/)
 
      return N3.DataFactory.namedNode(curie)
 
    part = curie.split(':')
 
    return @prefixFuncs(part[0])(part[1])
 

	
 
  Literal: (jsValue) ->
 
    N3.Util.createLiteral(jsValue)
 
    N3.DataFactory.literal(jsValue)
 

	
 
  LiteralRoundedFloat: (f) ->
 
    N3.Util.createLiteral(d3.format(".3f")(f),
 
    N3.DataFactory.literal(d3.format(".3f")(f),
 
                          "http://www.w3.org/2001/XMLSchema#decimal")
 

	
 
  toJs: (literal) ->
 
    # incomplete
 
    parseFloat(N3.Util.getLiteralValue(literal))
 

	
 
@@ -182,13 +188,13 @@ class window.SyncedGraph
 
                  else
 
                    @_applyPatch(patch)
 
                    @_addPrefixes(prefixes)
 
                    cb() if cb
 
                    
 
  quads: () -> # for debugging
 
    [q.subject, q.predicate, q.object, q.graph] for q in @graph.find()
 
    [q.subject, q.predicate, q.object, q.graph] for q in @graph.getQuads()
 

	
 
  applyAndSendPatch: (patch) ->
 
    console.time('applyAndSendPatch')
 
    if !Array.isArray(patch.addQuads) || !Array.isArray(patch.delQuads)
 
      throw new Error("corrupt patch: #{JSON.stringify(patch)}")
 

	
 
@@ -207,22 +213,22 @@ class window.SyncedGraph
 
  _applyPatch: (patch) ->
 
    # In most cases you want applyAndSendPatch.
 
    # 
 
    # This is the only method that writes to @graph!
 
    @cachedFloatValues.clear()
 
    for quad in patch.delQuads
 
      @graph.removeTriple(quad)
 
      @graph.removeQuad(quad)
 
    for quad in patch.addQuads
 
      @graph.addTriple(quad)
 
      @graph.addQuad(quad)
 
    #log('applied patch locally', patchSizeSummary(patch))
 
    @_autoDeps.graphChanged(patch)
 

	
 
  getObjectPatch: (s, p, newObject, g) ->
 
    # make a patch which removes existing values for (s,p,*,c) and
 
    # adds (s,p,newObject,c). Values in other graphs are not affected.
 
    existing = @graph.findByIRI(s, p, null, g)
 
    existing = @graph.getQuads(s, p, null, g)
 
    return {
 
      delQuads: existing,
 
      addQuads: [{subject: s, predicate: p, object: newObject, graph: g}]
 
    }
 

	
 
  patchObject: (s, p, newObject, g) ->
 
@@ -241,13 +247,13 @@ class window.SyncedGraph
 
    #label = label + @serial
 
    
 
    @_autoDeps.runHandler(func, label)
 

	
 
  _singleValue: (s, p) ->
 
    @_autoDeps.askedFor(s, p, null, null)
 
    quads = @graph.findByIRI(s, p)
 
    quads = @graph.getQuads(s, p)
 
    objs = new Set(q.object for q in quads)
 
    
 
    switch objs.size
 
      when 0
 
        throw new Error("no value for "+s+" "+p)
 
      when 1
 
@@ -278,44 +284,44 @@ class window.SyncedGraph
 
    catch
 
      words = uri.split('/')
 
      words[words.length-1]
 

	
 
  objects: (s, p) ->
 
    @_autoDeps.askedFor(s, p, null, null)
 
    quads = @graph.findByIRI(s, p)
 
    quads = @graph.getQuads(s, p)
 
    return (q.object for q in quads)
 

	
 
  subjects: (p, o) ->
 
    @_autoDeps.askedFor(null, p, o, null)
 
    quads = @graph.findByIRI(null, p, o)
 
    quads = @graph.getQuads(null, p, o)
 
    return (q.subject for q in quads)
 

	
 
  items: (list) ->
 
    out = []
 
    current = list
 
    while true
 
      if current == RDF + 'nil'
 
        break
 
        
 
      @_autoDeps.askedFor(current, null, null, null) # a little loose
 

	
 
      firsts = @graph.findByIRI(current, RDF + 'first', null)
 
      rests = @graph.findByIRI(current, RDF + 'rest', null)
 
      firsts = @graph.getQuads(current, RDF + 'first', null)
 
      rests = @graph.getQuads(current, RDF + 'rest', null)
 
      if firsts.length != 1
 
        throw new Error("list node #{current} has #{firsts.length} rdf:first edges")
 
      out.push(firsts[0].object)
 

	
 
      if rests.length != 1
 
        throw new Error("list node #{current} has #{rests.length} rdf:rest edges")
 
      current = rests[0].object
 
    
 
    return out
 

	
 
  contains: (s, p, o) ->
 
    @_autoDeps.askedFor(s, p, o, null)
 
    return @graph.findByIRI(s, p, o).length > 0
 
    return @graph.getQuads(s, p, o).length > 0
 

	
 
  nextNumberedResources: (base, howMany) ->
 
    results = []
 
    # we could cache [base,lastSerial]
 
    for serial in [0..1000]
 
      uri = @Uri("#{base}#{serial}")
 
@@ -328,10 +334,10 @@ class window.SyncedGraph
 
  nextNumberedResource: (base) ->
 
    @nextNumberedResources(base, 1)[0]       
 

	
 
  contextsWithPattern: (s, p, o) ->
 
    @_autoDeps.askedFor(s, p, o, null)
 
    ctxs = []
 
    for q in @graph.find(s, p, o)
 
    for q in @graph.getQuads(s, p, o)
 
      ctxs.push(q.graph)
 
    return _.unique(ctxs)
 

	
light9/web/graph_test.coffee
Show inline comments
 
@@ -36,34 +36,34 @@ describe 'SyncedGraph', ->
 
      ", done)
 
    
 
    it 'calls a handler right away', ->
 
      called = 0
 
      hand = ->
 
        called++
 
      graph.runHandler(hand)
 
      graph.runHandler(hand, 'run')
 
      assert.equal(1, called)
 
      
 
    it 'calls a handler a 2nd time if the graph is patched with relevant data', ->
 
      called = 0
 
      hand = ->
 
        called++
 
        graph.uriValue(A1, A2)
 
      graph.runHandler(hand)
 
      graph.runHandler(hand, 'run')
 
      graph.applyAndSendPatch({
 
        delQuads: [quad(A1, A2, A3)], addQuads: [quad(A1, A2, A4)]})
 
      assert.equal(2, called) 
 

	
 
    it 'notices new queries a handler makes upon rerun', ->
 
      called = 0
 
      objsFound = []
 
      hand = ->
 
        called++
 
        graph.uriValue(A1, A2)
 
        if called > 1
 
          objsFound.push(graph.objects(A1, A3))
 
      graph.runHandler(hand)
 
      graph.runHandler(hand, 'run')
 
      # first run looked up A1,A2,*
 
      graph.applyAndSendPatch({
 
        delQuads: [quad(A1, A2, A3)], addQuads: [quad(A1, A2, A4)]})
 
      # second run also looked up A1,A3,* (which matched none)
 
      graph.applyAndSendPatch({
 
        delQuads: [], addQuads: [quad(A1, A3, A4)]})
0 comments (0 inline, 0 general)