webmate 0.1.0

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.
Files changed (62) hide show
  1. data/.gitignore +19 -0
  2. data/GUIDE.md +115 -0
  3. data/Gemfile +10 -0
  4. data/LICENSE.txt +22 -0
  5. data/README.md +47 -0
  6. data/Rakefile +1 -0
  7. data/bin/webmate +70 -0
  8. data/lib/webmate/application.rb +138 -0
  9. data/lib/webmate/config.rb +23 -0
  10. data/lib/webmate/decorators/base.rb +38 -0
  11. data/lib/webmate/env.rb +17 -0
  12. data/lib/webmate/logger.rb +20 -0
  13. data/lib/webmate/observers/base.rb +24 -0
  14. data/lib/webmate/presenters/base.rb +69 -0
  15. data/lib/webmate/presenters/base_presenter.rb +115 -0
  16. data/lib/webmate/presenters/scoped.rb +30 -0
  17. data/lib/webmate/responders/abstract.rb +127 -0
  18. data/lib/webmate/responders/base.rb +21 -0
  19. data/lib/webmate/responders/callbacks.rb +79 -0
  20. data/lib/webmate/responders/exceptions.rb +4 -0
  21. data/lib/webmate/responders/response.rb +36 -0
  22. data/lib/webmate/responders/templates.rb +65 -0
  23. data/lib/webmate/route_helpers/route.rb +91 -0
  24. data/lib/webmate/route_helpers/routes_collection.rb +273 -0
  25. data/lib/webmate/socket.io/actions/connection.rb +14 -0
  26. data/lib/webmate/socket.io/actions/handshake.rb +34 -0
  27. data/lib/webmate/socket.io/packets/ack.rb +5 -0
  28. data/lib/webmate/socket.io/packets/base.rb +156 -0
  29. data/lib/webmate/socket.io/packets/connect.rb +5 -0
  30. data/lib/webmate/socket.io/packets/disconnect.rb +5 -0
  31. data/lib/webmate/socket.io/packets/error.rb +5 -0
  32. data/lib/webmate/socket.io/packets/event.rb +5 -0
  33. data/lib/webmate/socket.io/packets/heartbeat.rb +5 -0
  34. data/lib/webmate/socket.io/packets/json.rb +5 -0
  35. data/lib/webmate/socket.io/packets/message.rb +5 -0
  36. data/lib/webmate/socket.io/packets/noop.rb +5 -0
  37. data/lib/webmate/support/em_mongoid.rb +53 -0
  38. data/lib/webmate/version.rb +3 -0
  39. data/lib/webmate/views/scope.rb +25 -0
  40. data/lib/webmate/websockets.rb +50 -0
  41. data/lib/webmate.rb +129 -0
  42. data/spec/lib/route_helpers/route_spec.rb +41 -0
  43. data/spec/spec_helper.rb +18 -0
  44. data/vendor/.DS_Store +0 -0
  45. data/vendor/assets/.DS_Store +0 -0
  46. data/vendor/assets/javascripts/.DS_Store +0 -0
  47. data/vendor/assets/javascripts/webmate/.DS_Store +0 -0
  48. data/vendor/assets/javascripts/webmate/auth.coffee +63 -0
  49. data/vendor/assets/javascripts/webmate/backbone_ext/.DS_Store +0 -0
  50. data/vendor/assets/javascripts/webmate/backbone_ext/resources.coffee +60 -0
  51. data/vendor/assets/javascripts/webmate/backbone_ext/sync.coffee +131 -0
  52. data/vendor/assets/javascripts/webmate/client.coffee +133 -0
  53. data/vendor/assets/javascripts/webmate/init.coffee +7 -0
  54. data/vendor/assets/javascripts/webmate/libs/.DS_Store +0 -0
  55. data/vendor/assets/javascripts/webmate/libs/backbone.js +1572 -0
  56. data/vendor/assets/javascripts/webmate/libs/benchmark.coffee +27 -0
  57. data/vendor/assets/javascripts/webmate/libs/icanhaz.js +542 -0
  58. data/vendor/assets/javascripts/webmate/libs/socket.io.js +3871 -0
  59. data/vendor/assets/javascripts/webmate/libs/underscore.js +1 -0
  60. data/vendor/assets/javascripts/webmate.js +10 -0
  61. data/webmate.gemspec +31 -0
  62. metadata +290 -0
data/lib/webmate.rb ADDED
@@ -0,0 +1,129 @@
1
+ ENV["RACK_ENV"] ||= "development"
2
+
3
+ require "em-synchrony"
4
+ require "sinatra"
5
+ require "sinatra/cookies"
6
+ require "sinatra/reloader"
7
+ require "sinatra-websocket"
8
+ require 'sinatra_more/markup_plugin'
9
+ require "configatron"
10
+ require "rack/contrib/post_body_content_type_parser"
11
+ require "yajl"
12
+
13
+ require 'webmate/env'
14
+ require 'webmate/application'
15
+ require 'webmate/config'
16
+ require 'webmate/websockets'
17
+ require 'webmate/logger'
18
+
19
+ require 'bundler'
20
+ Bundler.setup
21
+ if Webmate.env == 'development'
22
+ Bundler.require(:assets)
23
+ end
24
+
25
+ require 'webmate/views/scope'
26
+ require 'webmate/responders/exceptions'
27
+ require 'webmate/responders/abstract'
28
+ require 'webmate/responders/base'
29
+ require 'webmate/responders/response'
30
+ require 'webmate/responders/templates'
31
+ require 'webmate/observers/base'
32
+ require 'webmate/decorators/base'
33
+ require 'webmate/route_helpers/routes_collection'
34
+ require 'webmate/route_helpers/route'
35
+
36
+ Bundler.require(:default, Webmate.env.to_sym)
37
+
38
+ require 'webmate/socket.io/actions/handshake'
39
+ require 'webmate/socket.io/actions/connection'
40
+ require 'webmate/socket.io/packets/base'
41
+ require 'webmate/socket.io/packets/disconnect'
42
+ require 'webmate/socket.io/packets/connect'
43
+ require 'webmate/socket.io/packets/heartbeat'
44
+ require 'webmate/socket.io/packets/message'
45
+ require 'webmate/socket.io/packets/json'
46
+ require 'webmate/socket.io/packets/event'
47
+ require 'webmate/socket.io/packets/ack'
48
+ require 'webmate/socket.io/packets/error'
49
+ require 'webmate/socket.io/packets/noop'
50
+
51
+ require 'webmate/presenters/base'
52
+ require 'webmate/presenters/scoped'
53
+ require 'webmate/presenters/base_presenter'
54
+
55
+ # it's not correct. app config file should be required by app
56
+ file = "#{Webmate.root}/config/config.rb"
57
+ require file if FileTest.exists?(file)
58
+
59
+ configatron.app.load_paths.each do |path|
60
+ Dir[ File.join( Webmate.root, path, '**', '*.rb') ].each do |file|
61
+ class_name = File.basename(file, '.rb')
62
+ eval <<-EOV
63
+ autoload :#{class_name.camelize}, "#{file}"
64
+ EOV
65
+ end
66
+ end
67
+
68
+ # run observers
69
+ Dir[ File.join( Webmate.root, 'app', 'observers', '**', '*.rb')].each do |file|
70
+ require file
71
+ end
72
+
73
+ class Webmate::Application
74
+ #register Webmate::RouteHelpers::Channels
75
+ register Sinatra::Reloader
76
+ register SinatraMore::MarkupPlugin
77
+
78
+ #helpers Webmate::Views::Helpers
79
+ helpers Sinatra::Cookies
80
+ helpers Webmate::Sprockets::Helpers
81
+
82
+ set :public_path, "#{Webmate.root}/public"
83
+ set :root, Webmate.root
84
+ set :reloader, !configatron.app.cache_classes
85
+
86
+ set :views, Proc.new { File.join(root, 'app', "views") }
87
+ set :layouts, Proc.new { File.join(root, 'app', "views", "layouts") }
88
+
89
+ if Webmate.env == 'development' # use cache classes here?
90
+ set :template_cache, Proc.new { Tilt::Cache.new }
91
+ else
92
+ set :template_cache, Tilt::Cache.new
93
+ end
94
+
95
+ # auto-reloading dirs
96
+ also_reload("#{Webmate.root}/config/config.rb")
97
+ also_reload("#{Webmate.root}/config/application.rb")
98
+ configatron.app.load_paths.each do |path|
99
+ also_reload("#{Webmate.root}/#{path}/**/*.rb")
100
+ end
101
+
102
+ use Webmate::Logger
103
+ use Rack::PostBodyContentTypeParser
104
+ use Rack::Session::Cookie, key: configatron.cookies.key,
105
+ domain: configatron.cookies.domain,
106
+ path: '/',
107
+ expire_after: 14400,
108
+ secret: configatron.cookies.secret
109
+ end
110
+
111
+ Webmate::Sprockets.configure do |config|
112
+ config.app = Webmate::Application
113
+ ['stylesheets', 'javascripts', 'images'].each do |dir|
114
+ # require application assets
115
+ config.append_path(File.join('app', 'assets', dir))
116
+ end
117
+ config.precompile = [ /\w+\.(?!js|css).+/, /application.(css|js)/ ]
118
+ config.compress = configatron.assets.compress
119
+ config.debug = configatron.assets.debug
120
+ config.compile = configatron.assets.compile
121
+ config.digest = configatron.assets.digest
122
+ end
123
+
124
+ path = File.expand_path("#{WEBMATE_ROOT}/config/initializers/*.rb")
125
+ Dir[path].each { |initializer| require_relative(initializer) }
126
+
127
+ # it's not correct. app config file should be required by app
128
+ file_path = "#{WEBMATE_ROOT}/config/application.rb"
129
+ require file_path if FileTest.exists?(file_path)
@@ -0,0 +1,41 @@
1
+ require 'spec_helper'
2
+
3
+ # responder to use as param for route creation.
4
+ # should not be used for another
5
+ class TestResponder; end
6
+
7
+ def build_route_for(path, action = "any", responder = "test_responder")
8
+ route_args = {
9
+ method: "any",
10
+ transport: "transport",
11
+ }.merge(
12
+ path: path,
13
+ action: action,
14
+ responder: responder
15
+ )
16
+
17
+ Webmate::Route.new(route_args)
18
+ end
19
+
20
+ describe Webmate::Route do
21
+ it "should match simple routes" do
22
+ result = build_route_for('/projects').match("/projects")
23
+ result.should_not be_nil
24
+ end
25
+
26
+ it "should match empty routes" do
27
+ result = build_route_for('/').match("/")
28
+ result.should_not be_nil
29
+ end
30
+
31
+ it "should match routes with placements" do
32
+ result = build_route_for('/projects/:project_id').match("/projects/qwerty")
33
+ result.should_not be_nil
34
+ result[:params][:project_id].should == 'qwerty'
35
+ end
36
+
37
+ it "should match routes with wildcards" do
38
+ route = build_route_for('/projects/*')
39
+ result = build_route_for('/projects/*').match("/projects/qwerty/code")
40
+ end
41
+ end
@@ -0,0 +1,18 @@
1
+ dir = File.expand_path(File.dirname(__FILE__))
2
+
3
+ WEBMATE_ROOT = File.join(dir, '..')
4
+
5
+ SPECDIR = dir
6
+ $LOAD_PATH.unshift("#{dir}/../lib")
7
+
8
+ require 'rubygems'
9
+ #require 'mocha'
10
+ #require 'rspec'
11
+ #require 'facter'
12
+ #require 'fileutils'
13
+
14
+ require File.join(dir, '..', 'lib', 'webmate.rb')
15
+
16
+ RSpec.configure do |config|
17
+ #config.mock_with :mocha
18
+ end
data/vendor/.DS_Store ADDED
Binary file
Binary file
Binary file
@@ -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
+ ###
@@ -0,0 +1,7 @@
1
+ window.Webmate or= {}
2
+ window.Webmate.channels = {}
3
+
4
+ window.App = {}
5
+ window.App.Models = {}
6
+ window.App.Collections = {}
7
+ window.App.Views = {}