webmate-client 0.1.1

Sign up to get free protection for your applications and to get access to all the features.
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
+ ###