webmate-client 0.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
data/.gitignore ADDED
@@ -0,0 +1,2 @@
1
+ .DS_Store
2
+ pkg
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in webmate.gemspec
4
+ gemspec
data/Gemfile.lock ADDED
@@ -0,0 +1,22 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ webmate-client (0.1.1)
5
+ coffee-script
6
+
7
+ GEM
8
+ remote: https://rubygems.org/
9
+ specs:
10
+ coffee-script (2.2.0)
11
+ coffee-script-source
12
+ execjs
13
+ coffee-script-source (1.6.3)
14
+ execjs (1.4.0)
15
+ multi_json (~> 1.0)
16
+ multi_json (1.7.7)
17
+
18
+ PLATFORMS
19
+ ruby
20
+
21
+ DEPENDENCIES
22
+ webmate-client!
data/LICENSE.txt ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2013 Iskander Haziev
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
File without changes
data/Rakefile ADDED
@@ -0,0 +1 @@
1
+ require "bundler/gem_tasks"
@@ -0,0 +1 @@
1
+ require 'webmate-client/version'
@@ -0,0 +1,3 @@
1
+ module WebmateClient
2
+ VERSION = '0.1.1'
3
+ end
@@ -0,0 +1,10 @@
1
+ //= require webmate/libs/underscore
2
+ //= require webmate/libs/backbone
3
+ //= require webmate/libs/icanhaz
4
+ //= require webmate/libs/benchmark
5
+ //= require webmate/libs/socket.io
6
+ //= require webmate/init
7
+ //= require webmate/client
8
+ //= require webmate/auth
9
+ //= require webmate/backbone_ext/sync
10
+ //= require webmate/backbone_ext/resources
@@ -0,0 +1,63 @@
1
+ window.Webmate or= {}
2
+
3
+ # this will assign to WebmateAuth
4
+ # {
5
+ # token: "string"
6
+ # getToken: "function(callback)"
7
+ # isAuthorized: "function()"
8
+ # }
9
+ #
10
+ # token - is reference to currentUserToken.
11
+ #
12
+ # getToken(callback)
13
+ # - returns current current user token
14
+ # - callback invoked if/when token will be available
15
+ #
16
+ # authorized?
17
+ # returns true if user already received a non-blank token
18
+ #
19
+ # for now, delayed applies of callback not implemented
20
+
21
+ window.Webmate.Auth = (->
22
+ authToken = null
23
+
24
+ _setToken = (token) ->
25
+ authToken = token
26
+ if $("meta[name='websocket-token']").length > 0
27
+ $("meta[name='websocket-token']").attr('content', token)
28
+ else
29
+ $('head').append( $("<meta>", { name: 'websocket-token', content: token }))
30
+
31
+ _getToken = ->
32
+ if authToken then authToken else $("meta[name='websocket-token']").attr('content')
33
+
34
+ _fetchToken = (callback) ->
35
+ $.ajax
36
+ url: "/users/sessions/token"
37
+ dataType: 'JSON',
38
+ success: (data) ->
39
+ if data && data.token
40
+ _setToken(data.token)
41
+ callback.call(window, data.token)
42
+
43
+ publicGetToken = (callback) ->
44
+ if publicIsAuthorized()
45
+ callback.call(window, _getToken()) if callback
46
+ else
47
+ _fetchToken(callback)
48
+
49
+ _getToken()
50
+
51
+ # check null or empty string
52
+ publicIsAuthorized = () ->
53
+ not (not _getToken())
54
+
55
+ publicUnauthorize = () ->
56
+ _setToken(null)
57
+
58
+
59
+ # return object
60
+ getToken: publicGetToken
61
+ isAuthorized: publicIsAuthorized
62
+ unAuthorize: publicUnauthorize
63
+ )()
@@ -0,0 +1,60 @@
1
+ Backbone.Model::idAttribute = 'id'
2
+ Backbone.Model::resourceName = -> @collection.resourceName()
3
+ Backbone.Model::collectionName = -> @collection.collectionName()
4
+ Backbone.Model::channel = -> _.result(@collection, 'channel')
5
+
6
+ Backbone.Collection::resourceName = -> @resource
7
+ Backbone.Collection::collectionName = -> "#{@resource}s"
8
+
9
+ Backbone.Collection::bindSocketEvents = () ->
10
+ return false if not @channel?
11
+ collection = @
12
+
13
+ # note: possible, this should be in webmate
14
+ client = Webmate.channels[@channel]
15
+ client or= Webmate.connect(@channel)
16
+
17
+ path = _.result(@, 'url')
18
+
19
+ client.on "#{path}/read", (response, params) =>
20
+ if collection.set(collection.parse(response))
21
+ collection.trigger('sync', collection, response, {})
22
+ collection.trigger('reset', collection, response, {})
23
+
24
+ client.on "#{path}/create", (response, params) =>
25
+ if collection.add(collection.parse(response))
26
+ collection.trigger('add', collection, response, {})
27
+
28
+ client.on "#{path}/update", (response, params) =>
29
+ if collection.add(collection.parse(response), { merge: true })
30
+ collection.trigger('change', collection, response, {})
31
+
32
+ client.on "#{path}/delete", (response, params) =>
33
+ if collection.remove(collection.parse(response))
34
+ collection.trigger('change', collection, response, {})
35
+
36
+ ###
37
+ # bind
38
+ client.on "#{path}/update", (response, params) =>
39
+ return unless response._id
40
+ @get(response._id).set(response)
41
+
42
+ client.on "#{@collectionName()}/read", (response, params)->
43
+ model.reset(response)
44
+ model.trigger "sync", model, response
45
+
46
+ client.on "#{@collectionName()}/create", (response, params)->
47
+ model.add(response)
48
+ #model.get(response._id).trigger 'sync', response
49
+ #if clientId is params._client_id
50
+ # model.add(response)
51
+ #else
52
+ # model.get(params._cid).set(response)
53
+ ###
54
+
55
+ # update existing functions
56
+
57
+ Backbone.Collection::_prepareModelWithoutAssociations = Backbone.Collection::_prepareModel
58
+ Backbone.Collection::_prepareModel = (attrs, options) ->
59
+ attrs = _.extend(attrs, @sync_data) if @sync_data
60
+ @._prepareModelWithoutAssociations(attrs, options)
@@ -0,0 +1,131 @@
1
+ # Improved Backbone Sync
2
+ (->
3
+
4
+ methodMap =
5
+ create: "POST"
6
+ update: "PUT"
7
+ patch: 'PATCH'
8
+ delete: "DELETE"
9
+ read: "GET"
10
+ read_all: "GET"
11
+
12
+ # get an alias
13
+ window.Backbone.sync_with_ajax = window.Backbone.sync
14
+
15
+ window.Backbone.sync = (method, model, options) ->
16
+ # use default behaviour
17
+ if not (window.Webmate && window.Webmate.websocketsEnabled)
18
+ # clean options?
19
+ window.Backbone.sync_with_ajax(method, model, options)
20
+ else
21
+ # websocket messages protocol.
22
+ # method: 'post'
23
+ # path: '/projects/:project_id/tasks'
24
+ # params: {}
25
+ # metadata: {} # data to passback
26
+ url = _.result(model, 'url')
27
+ collection_url = if model.collection then _.result(model.collection, 'url') else url
28
+
29
+ packet_data = {
30
+ method: methodMap[method],
31
+ path: url,
32
+ metadata: {
33
+ collection_url: collection_url,
34
+ method: method,
35
+ user_websocket_token: Webmate.Auth.getToken()
36
+ },
37
+ params: {}
38
+ }
39
+ if (method == 'create' || method == 'update' || method == 'patch')
40
+ packet_data.params = JSON.stringify(options.attrs || model.toJSON(options))
41
+
42
+ Webmate.channels['api'].send(url, packet_data, methodMap[method])
43
+ model.trigger "request", model
44
+
45
+ ###
46
+ # TODO use prepare model for this logic
47
+ if model and model.sync_data
48
+ data = _.extend(data, model.sync_data)
49
+ token = $('meta[name="websocket-token"]').attr('content')
50
+ data.user_websocket_token = token
51
+ client = Webmate.channels[getChannel(model)]
52
+ client.send("#{model.collectionName()}/#{method}", data, type)
53
+ model.trigger "request", model
54
+ ###
55
+
56
+ ).call(this)
57
+ ###
58
+ (->
59
+ methodMap =
60
+ create: "POST"
61
+ update: "PUT"
62
+ patch: 'PATCH'
63
+ delete: "DELETE"
64
+ read: "GET"
65
+ read_all: "GET"
66
+
67
+ getUrl = (object, method) ->
68
+ channel = _.result(object, "channel")
69
+ if channel
70
+ "/#{channel}/#{object.collectionName()}/#{method}"
71
+ else
72
+ return null unless object and object.url
73
+ (if _.isFunction(object.url) then object.url() else object.url)
74
+
75
+ getChannel = (object) ->
76
+ return null unless object and object.channel
77
+ (if _.isFunction(object.channel) then object.channel() else object.channel)
78
+
79
+ urlError = ->
80
+ throw new Error("A 'url' property or function must be specified")
81
+
82
+ window.Backbone.sync = (method, model, options) ->
83
+ type = methodMap[method]
84
+ data = {}
85
+
86
+ if model and (method is "create")
87
+ data['_cid'] = model.cid
88
+ if model and (method is "create" or method is "update" or method is 'patch')
89
+ data[model.resourceName()] = (options.attrs || model.toJSON())
90
+ if model and (method is "update" or method is 'patch')
91
+ delete data[model.resourceName()][model.idAttribute]
92
+ if model and (method is "delete" or method is "update" or method is 'patch')
93
+ data[model.idAttribute] = model.id
94
+
95
+ if window.Webmate && window.Webmate.websocketsEnabled
96
+ # TODO use prepare model for this logic
97
+ if model and model.sync_data
98
+ data = _.extend(data, model.sync_data)
99
+ token = $('meta[name="websocket-token"]').attr('content')
100
+ data.user_websocket_token = token
101
+ client = Webmate.channels[getChannel(model)]
102
+ client.send("#{model.collectionName()}/#{method}", data, type)
103
+ model.trigger "request", model
104
+
105
+ else
106
+ params =
107
+ type: type
108
+ dataType: "json"
109
+
110
+ # Ensure that we have a URL.
111
+ params.url = getUrl(model, method) or urlError()
112
+ params.contentType = "application/json"
113
+ params.data = JSON.stringify(data)
114
+
115
+ # Don't process data on a non-GET request.
116
+ params.processData = false if params.type isnt "GET"
117
+ success = options.success
118
+ options.success = (resp, status, xhr) ->
119
+ success resp.response, status, xhr if success
120
+ model.trigger "sync", model, resp.response, options
121
+
122
+ error = options.error
123
+ options.error = (xhr, status, thrown) ->
124
+ error model, xhr, options if error
125
+ model.trigger "error", model, xhr, options
126
+ # Make the request, allowing the user to override any Ajax options.
127
+ xhr = Backbone.ajax(_.extend(params, options))
128
+ model.trigger "request", model, xhr, options
129
+
130
+ ).call(this)
131
+ ###
@@ -0,0 +1,133 @@
1
+ class Webmate.Client
2
+ constructor: (channel_name) ->
3
+ self = @
4
+ @bindings = {}
5
+ @channel_name = channel_name
6
+
7
+ if @useWebsockets()
8
+ @websocket = @createConnection( (message) ->
9
+ metadata = message.request.metadata
10
+ eventBindings = @bindings["#{metadata.collection_url}/#{metadata.method}"]
11
+
12
+ _.each eventBindings, (eventBinding) ->
13
+ eventBinding(message.response.body, message.request.metadata)
14
+ )
15
+
16
+ useWebsockets: ->
17
+ window.Webmate.websocketsEnabled isnt false && io && io.Socket
18
+
19
+ createConnection: (onMessageHandler) ->
20
+ self = @
21
+ @clientId or= Math.random().toString(36).substr(2)
22
+
23
+ # pass callback func, if needed be sure what callback exists
24
+ token = Webmate.Auth.getToken()
25
+ return false unless token?
26
+
27
+ socket = new io.Socket
28
+ resource: @channel_name
29
+ host: location.hostname
30
+ port: Webmate.websocketsPort or location.port
31
+ query: $.param(token: token)
32
+
33
+ socket.on "connect", () ->
34
+ console.log("connection established")
35
+
36
+ socket.onPacket = (packet) ->
37
+ console.log(packet)
38
+ return unless packet.type is 'message'
39
+ parsed_packet = Webmate.Client::parsePacketData(packet.data)
40
+ onMessageHandler.call(self, parsed_packet)
41
+
42
+ socket.connect()
43
+ socket
44
+
45
+ on: (action, callback) ->
46
+ @bindings[action] = [] if !@bindings[action]
47
+ @bindings[action].push(callback)
48
+ @
49
+
50
+ send: (path, data, method) ->
51
+ data.path = path
52
+ data.method = method
53
+ packet = {
54
+ type: 'message',
55
+ data: JSON.stringify(data)
56
+ }
57
+ @websocket.packet(packet)
58
+
59
+ Webmate.Client::parsePacketData = (packet_data) ->
60
+ data = JSON.parse(packet_data)
61
+ data.response.body = JSON.parse(data.response.body)
62
+ data
63
+
64
+ Webmate.connect = (channel, callback)->
65
+ client = new Webmate.Client(channel, callback)
66
+ Webmate.channels[channel] = client
67
+ client
68
+
69
+ ###
70
+ class Webmate.Client
71
+ getFullPath: ->
72
+ "#{location.hostname}:#{Webmate.websocketsPort or location.port}/#{@channel}"
73
+
74
+ getClientId: ->
75
+ @clientId or= Math.random().toString(36).substr(2)
76
+
77
+ buildSocket: (onMessageHandler) ->
78
+
79
+ constructor: (channel, callback) ->
80
+ self = @
81
+ @bindings = {}
82
+ @channel = channel
83
+
84
+ if window.Webmate.websocketsEnabled isnt false && window.WebSocket
85
+ @websocket = buildSocket( (message) ->
86
+ )
87
+
88
+ @websocket = new WebSocket("ws://#{@fullPath}")
89
+ # prepare queue to store requests if socket not ready
90
+ @callsQueue = new Array()
91
+ @websocket.onmessage = (e) ->
92
+ data = JSON.parse(e.data)
93
+ eventBinding = self.bindings[data.action]
94
+ _.each eventBinding, (binding)->
95
+ binding(data.response, data.params)
96
+ @websocket.onopen = (e) ->
97
+ # process pending queues
98
+ while data = self.callsQueue.pop()
99
+ self.websocket.send(JSON.stringify(data))
100
+ callback() if callback
101
+ else
102
+ if window.Webmate.websocketsEnabled is false
103
+ console.log("Websockets is disabled. Using http.")
104
+ else
105
+ console.log("Websocket not supported. Using http.")
106
+ callback() if callback
107
+ @
108
+ on: (action, callback)->
109
+ @bindings[action] = [] if !@bindings[action]
110
+ @bindings[action].push(callback)
111
+ @
112
+ send: (action, data, method)->
113
+ data = {} if !data
114
+ method = 'get' if !method
115
+ data.action = action
116
+ data.channel = @channel
117
+ data._client_id = @clientId
118
+
119
+ if @websocket
120
+ if @websocket.readyState == @websocket.OPEN
121
+ @websocket.send(JSON.stringify(data))
122
+ else
123
+ @callsQueue.push(data)
124
+ else
125
+ $.ajax("http://#{@fullPath}/#{action}", type: method).success (data) ->
126
+ console.log(data)
127
+ @
128
+
129
+ Webmate.connect = (channel, callback)->
130
+ client = new Webmate.Client(channel, callback)
131
+ Webmate.channels[channel] = client
132
+ client
133
+ ###