src/databound.coffee
_ = require 'lodash' jQuery = require 'jquery'

You can specify scope for the connection.

  User = new Databound '/users', city: 'New York'

  User.where(name: 'John').then (users) ->
    alert 'You are a New Yorker called John'

  User.create(name: 'Peter').then (new_user) ->
    # I am from New York
    alert "I am from #{new_user.city}"
class Databound constructor: (@endpoint, @scope = {}, @options = {}) -> @extra_where_scopes = @options.extra_where_scopes or [] @records = [] @seeds = [] @properties = []

Start of Configs

Functions request and promise are overritable

Databound.API_URL = ""

Does a POST request and returns a promise

request: (action, params) -> jQuery.post @url(action), @data(params), 'json'

Returns a promise which resolves with result

promise: (result) -> deferred = jQuery.Deferred() deferred.resolve result deferred.promise()

End of Configs

where: (params) -> _this = @ @wrappedRequest('where', params).then (resp) -> records = JSON.parse(resp.records).concat(_this.seeds) _this.records = _.sortBy(records, 'id') _this.promise _this.records all: -> @where()

Return a single record by id

User.find(15).then (user) ->
  alert "Yo, #{user.name}"
find: (id) -> @checkUndefinedId('find', id) _this = @ @where(id: id).then -> record = _this.take(id) unless record throw new DataboundError("Couldn't find record with id: #{id}") _this.promise record

Return a single record by params

User.findBy(name: 'John', city: 'New York').then (user) ->
  alert "I'm John from New York"
findBy: (params) -> _this = @ @where(params).then (resp) -> _this.promise _.first(_.values(resp)) create: (params) -> @requestAndRefresh 'create', params

Specify id when updating or destroying the record.

  User = new Databound '/users'

  User.update(id: 15, name: 'Saint John').then (updated_user) ->
    alert updated_user

  User.destroy(15).then (resp) ->
    alert resp.success
update: (params) -> @requestAndRefresh 'update', params destroy: (id) -> @checkUndefinedId('destroy', id) @requestAndRefresh 'destroy', id: id

Just take already dowloaded records

take: (id) -> _.detect @records, (record) -> id.toString() == record.id.toString() takeAll: -> @records

f.e. Have default records

injectSeedRecords: (records) -> @seeds = records requestAndRefresh: (action, params) -> _this = @

backend responds with:

  {
    success: true,
    id: record.id,
    scoped_records: []
  }
@wrappedRequest(action, params).then (resp) -> records = JSON.parse(resp.scoped_records) records_with_seeds = records.concat(_this.seeds) _this.records = _.sortBy(records_with_seeds, 'id') if resp.id _this.promise _this.take(resp.id) else _this.promise resp.success url: (action) -> if _.isEmpty(Databound.API_URL) "#{@endpoint}/#{action}" else "#{Databound.API_URL}/#{@endpoint}/#{action}" data: (params) -> scope: JSON.stringify(@scope) extra_where_scopes: JSON.stringify(@extra_where_scopes) data: JSON.stringify(params) wrappedRequest: (args...) -> @request(args...).then(_.bind(@handleSuccess, @)).fail(@handleFailure) handleSuccess: (resp) -> throw new Error 'Error in the backend' unless resp?.success @promise(resp) handleFailure: (e) -> if e.status == DataboundError.STATUS throw new DataboundError(e.responseJSON.message) else throw new Error "Error in the backend with status #{e.status}" checkUndefinedId: (action, id) -> return unless _.isUndefined(id) throw new DataboundError("Couldn't #{action} a record without an id") class DataboundError constructor: (text) -> @message = "Databound: #{text}" @STATUS: 405 DataboundError:: = new Error() module.exports = Databound