webmate 0.1.0

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