clever
Version:
Node.js library for interacting with the Clever API
104 lines (92 loc) • 4.17 kB
text/coffeescript
_ = require 'underscore'
dotty = require 'dotty'
fs = require 'fs'
Readable = require 'readable-stream'
sinon = require 'sinon'
Understream = require 'understream'
_.mixin require('underscore.string').exports()
_.mixin require('underscore.deep')
# This is still an unstable interface.
module.exports = (api_key, data_dir) ->
throw new Error "Must provide api_key" unless api_key?
throw new Error "Must provide data_dir" unless data_dir?
clever = require("./index") api_key, "localhost"
trequire = (fp) ->
val = []
if fs.existsSync("#{fp}.coffee") or fs.existsSync("#{fp}.js") or fs.existsSync("#{fp}.json")
try
val = require(fp)
catch err
throw new Error "Error loading file at #{fp}: #{err}"
val
# clever.db[resource] are arrays of resources
clever.db =
districts: trequire("#{data_dir}/districts")
districtproperties: trequire("#{data_dir}/districtproperties")
students: trequire("#{data_dir}/students")
studentproperties: trequire("#{data_dir}/studentproperties")
teachers: trequire("#{data_dir}/teachers")
teacherproperties: trequire("#{data_dir}/teacherproperties")
schools: trequire("#{data_dir}/schools")
schoolproperties: trequire("#{data_dir}/schoolproperties")
# clever.map[resources] are maps from ids to resources (for faster lookup)
# This doesn't just replace clever.db for compatibility reasons..for now..
clever.refresh_map = ->
map_id_to_val = (db, resource, id_key) ->
map = {}
map[obj[id_key]] = obj for i, obj of db[resource]
map
clever.map =
districts: map_id_to_val clever.db, 'districts', 'id'
districtproperties: map_id_to_val clever.db, 'districtproperties', 'district'
students: map_id_to_val clever.db, 'students', 'id'
studentproperties: map_id_to_val clever.db, 'studentproperties', 'student'
teachers: map_id_to_val clever.db, 'teachers', 'id'
teacherproperties: map_id_to_val clever.db, 'teacherproperties', 'teacher'
schools: map_id_to_val clever.db, 'schools', 'id'
schoolproperties: map_id_to_val clever.db, 'schoolproperties', 'school'
clever.refresh_map()
sandbox = sinon.sandbox.create()
apply_query = (undersomething, conditions, resource) ->
return undersomething.filter (obj) ->
for key, val of conditions
return false unless obj[key] is val
return true
.map (obj) ->
if obj._shadow? then _.extend({}, obj, obj._shadow) else obj
.map (raw_json) ->
Klass = clever[_(resource).chain().capitalize().rtrim('s').value()]
return new Klass _.deepClone raw_json
sandbox.stub clever.Query.prototype, 'exec', (cb) ->
resource = _.strRightBack(@_url, '/')
s = apply_query _(clever.db[resource]).chain(), @_conditions, resource
return setImmediate cb, null, s.value().length if @_options.count
setImmediate cb, null, s.value()
sandbox.stub clever.Query.prototype, 'stream', ->
resource = _.strRightBack(@_url, '/')
s = apply_query new Understream(clever.db[resource]), @_conditions, resource
return s.stream()
sandbox.stub clever.Resource.prototype, 'properties', (obj, cb) ->
if arguments.length is 1
cb = obj
obj = undefined
else if arguments.length isnt 2
throw new Error("expected 1 or 2 arguments to properties")
resource = @constructor.name.toLowerCase() + 's'
resource_singular = _(resource).rtrim('s')
id = @_properties.id
clever_resource = clever.map[resource][id]
return setImmediate cb(new Error("404")) unless clever_resource?
resource_property = "#{resource_singular}properties"
prop_obj = clever.map[resource_property][id]
if not prop_obj
prop_obj = _.object [[resource_singular, id], ['data', {}]]
clever.db[resource_property].push prop_obj
clever.map[resource_property][id] = prop_obj
if obj
dotty.put prop_obj.data, k, v for k, v of obj
if obj.google_apps?.username?
clever_resource._shadow ?= {}
clever_resource._shadow.email = obj.google_apps.username
setImmediate cb, null, _.deepClone prop_obj.data
clever