jason-rails 0.4.1 → 0.6.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (72) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +4 -1
  3. data/.ruby-version +1 -0
  4. data/Gemfile.lock +152 -2
  5. data/README.md +117 -5
  6. data/app/controllers/jason/api/pusher_controller.rb +15 -0
  7. data/app/controllers/jason/api_controller.rb +44 -2
  8. data/client/lib/JasonContext.d.ts +6 -1
  9. data/client/lib/JasonContext.js +4 -1
  10. data/client/lib/JasonProvider.d.ts +2 -2
  11. data/client/lib/JasonProvider.js +5 -124
  12. data/client/lib/createJasonReducers.js +48 -3
  13. data/client/lib/createOptDis.js +0 -2
  14. data/client/lib/createPayloadHandler.d.ts +9 -1
  15. data/client/lib/createPayloadHandler.js +47 -55
  16. data/client/lib/createServerActionQueue.d.ts +10 -0
  17. data/client/lib/createServerActionQueue.js +48 -0
  18. data/client/lib/createServerActionQueue.test.d.ts +1 -0
  19. data/client/lib/createServerActionQueue.test.js +37 -0
  20. data/client/lib/createTransportAdapter.d.ts +5 -0
  21. data/client/lib/createTransportAdapter.js +20 -0
  22. data/client/lib/index.d.ts +5 -2
  23. data/client/lib/index.js +3 -1
  24. data/client/lib/makeEager.js +2 -2
  25. data/client/lib/pruneIdsMiddleware.d.ts +2 -0
  26. data/client/lib/pruneIdsMiddleware.js +24 -0
  27. data/client/lib/restClient.d.ts +2 -0
  28. data/client/lib/restClient.js +17 -0
  29. data/client/lib/transportAdapters/actionCableAdapter.d.ts +5 -0
  30. data/client/lib/transportAdapters/actionCableAdapter.js +35 -0
  31. data/client/lib/transportAdapters/pusherAdapter.d.ts +5 -0
  32. data/client/lib/transportAdapters/pusherAdapter.js +68 -0
  33. data/client/lib/useJason.d.ts +5 -0
  34. data/client/lib/useJason.js +94 -0
  35. data/client/lib/useJason.test.d.ts +1 -0
  36. data/client/lib/useJason.test.js +85 -0
  37. data/client/lib/useSub.d.ts +1 -1
  38. data/client/lib/useSub.js +6 -3
  39. data/client/package.json +5 -3
  40. data/client/src/JasonContext.ts +4 -1
  41. data/client/src/JasonProvider.tsx +5 -123
  42. data/client/src/createJasonReducers.ts +56 -3
  43. data/client/src/createOptDis.ts +0 -2
  44. data/client/src/createPayloadHandler.ts +53 -64
  45. data/client/src/createServerActionQueue.test.ts +42 -0
  46. data/client/src/createServerActionQueue.ts +47 -0
  47. data/client/src/createTransportAdapter.ts +13 -0
  48. data/client/src/index.ts +3 -1
  49. data/client/src/makeEager.ts +2 -2
  50. data/client/src/pruneIdsMiddleware.ts +24 -0
  51. data/client/src/restClient.ts +14 -0
  52. data/client/src/transportAdapters/actionCableAdapter.ts +38 -0
  53. data/client/src/transportAdapters/pusherAdapter.ts +72 -0
  54. data/client/src/useJason.test.ts +87 -0
  55. data/client/src/useJason.ts +110 -0
  56. data/client/src/useSub.ts +6 -3
  57. data/client/yarn.lock +71 -3
  58. data/config/routes.rb +5 -1
  59. data/jason-rails.gemspec +4 -0
  60. data/lib/jason.rb +61 -1
  61. data/lib/jason/api_model.rb +2 -12
  62. data/lib/jason/broadcaster.rb +19 -0
  63. data/lib/jason/channel.rb +50 -21
  64. data/lib/jason/graph_helper.rb +165 -0
  65. data/lib/jason/includes_helper.rb +108 -0
  66. data/lib/jason/lua_generator.rb +71 -0
  67. data/lib/jason/publisher.rb +82 -37
  68. data/lib/jason/publisher_old.rb +112 -0
  69. data/lib/jason/subscription.rb +349 -97
  70. data/lib/jason/subscription_old.rb +171 -0
  71. data/lib/jason/version.rb +1 -1
  72. metadata +80 -3
@@ -1,31 +1,317 @@
1
1
  class Jason::Subscription
2
2
  attr_accessor :id, :config
3
+ attr_reader :includes_helper, :graph_helper
3
4
 
4
5
  def initialize(id: nil, config: nil)
5
6
  if id
6
7
  @id = id
7
- raw_config = $redis_jason.hgetall("jason:subscriptions:#{id}").map { |k,v| [k, JSON.parse(v)] }.to_h.with_indifferent_access
8
+ raw_config = $redis_jason.hgetall("jason:subscriptions:#{id}").map { |k,v| [k, JSON.parse(v)] }.to_h
9
+ raise "Subscription ID #{id} does not exist" if raw_config.blank?
8
10
  set_config(raw_config)
9
11
  else
10
- @id = Digest::MD5.hexdigest(config.to_json)
12
+ @id = Digest::MD5.hexdigest(config.sort_by { |key| key }.to_h.to_json)
11
13
  configure(config)
12
14
  end
15
+ @includes_helper = Jason::IncludesHelper.new({ model => self.config['includes'] })
16
+ @graph_helper = Jason::GraphHelper.new(self.id, @includes_helper)
17
+
18
+ check_for_missing_keys
19
+ end
20
+
21
+ def broadcaster
22
+ @broadcaster ||= Jason::Broadcaster.new(channel)
23
+ end
24
+
25
+ def check_for_missing_keys
26
+ missing_keys = includes_helper.all_models - Jason.schema.keys.map(&:to_s)
27
+ if missing_keys.present?
28
+ raise "#{missing_keys.inspect} are not in the schema. Only models in the Jason schema can be subscribed."
29
+ end
30
+ end
31
+
32
+ def self.upsert_by_config(model, conditions: {}, includes: {})
33
+ self.new(config: {
34
+ model: model,
35
+ conditions: conditions || {},
36
+ includes: includes || {}
37
+ })
38
+ end
39
+
40
+ def self.find_by_id(id)
41
+ self.new(id: id)
42
+ end
43
+
44
+ def self.for_instance_with_child(model_name, id, child_model_name, include_all = true)
45
+ sub_ids = for_instance(model_name, id, include_all = true)
46
+ sub_ids.select do |sub_id|
47
+ find_by_id(sub_id).includes_helper.in_sub(model_name, child_model_name)
48
+ end
49
+ end
50
+
51
+ def self.for_instance(model_name, id, include_all = true)
52
+ subs = $redis_jason.smembers("jason:models:#{model_name}:#{id}:subscriptions")
53
+ if include_all
54
+ subs += $redis_jason.smembers("jason:models:#{model_name}:all:subscriptions")
55
+ end
56
+
57
+ subs
58
+ end
59
+
60
+ def self.for_model(model_name)
61
+
62
+ end
63
+
64
+ # Find and update subscriptions affected by a model changing foreign key
65
+ # comment, comment_id, post, old_post_id, new_post_id
66
+ def self.update_ids(changed_model_name, changed_model_id, foreign_model_name, old_foreign_id, new_foreign_id)
67
+ # There are 4 cases to consider.
68
+ # changed_instance ---/--- foreign_instance
69
+ # \--+--- new_foreign_instance
70
+ #
71
+ # foreign instance can either be parent or child for a given subscription
72
+ # 1. Child swap/add: foreign is child
73
+ # 2. Stay in the family: foreign is parent + both old and new foreign instances are part of the sub
74
+ # 3. Join the family: foreign is parent + only new foreign instance are part of the sub
75
+ # 4. Leave the family: foreign is parent + only the old foreign instance is part of the sub
76
+
77
+ #########
78
+ # Subs where changed is parent
79
+ sub_ids = for_instance_with_child(changed_model_name, changed_model_id, foreign_model_name, true)
80
+ sub_ids.each do |sub_id|
81
+ subscription = find_by_id(sub_id)
82
+
83
+ # If foreign key has been nulled, nothing to add
84
+ add = new_foreign_id.present? ? [
85
+ {
86
+ model_names: [changed_model_name, foreign_model_name],
87
+ instance_ids: [[changed_model_id, new_foreign_id]]
88
+ },
89
+ # Add IDs of child models
90
+ subscription.load_ids_for_sub_models(foreign_model_name, new_foreign_id)
91
+ ] : nil
92
+
93
+ id_changeset = subscription.graph_helper.apply_update({
94
+ remove: [{
95
+ model_names: [changed_model_name, foreign_model_name],
96
+ instance_ids: [[changed_model_id, old_foreign_id]]
97
+ }],
98
+ add: add
99
+ })
100
+
101
+ subscription.apply_id_changeset(id_changeset)
102
+ subscription.broadcast_id_changeset(id_changeset)
103
+ end
104
+
105
+ old_sub_ids = for_instance_with_child(foreign_model_name, old_foreign_id, changed_model_name, true)
106
+ new_sub_ids = for_instance_with_child(foreign_model_name, new_foreign_id, changed_model_name, true)
107
+
108
+ #########
109
+ # Subs where changed is child
110
+ # + parent in both old + new
111
+ # this is simple, only the edges need to change - no IDs can be changed
112
+ (old_sub_ids & new_sub_ids).each do |sub_id|
113
+ subscription = find_by_id(sub_id)
114
+ subscription.graph_helper.apply_update({
115
+ remove: [{
116
+ model_names: [changed_model_name, foreign_model_name],
117
+ instance_ids: [[changed_model_id, old_foreign_id]]
118
+ }],
119
+ add: [{
120
+ model_names: [changed_model_name, foreign_model_name],
121
+ instance_ids: [[changed_model_id, new_foreign_id]]
122
+ }]
123
+ })
124
+ end
125
+
126
+ #########
127
+ # Subs where changed is child
128
+ # + old parent wasn't in the sub, but new parent is
129
+ # IE the changed instance is joining the sub
130
+ # No edges are removed, just added
131
+ (new_sub_ids - old_sub_ids).each do |sub_id|
132
+ subscription = find_by_id(sub_id)
133
+ id_changeset = subscription.graph_helper.apply_update({
134
+ add: [
135
+ {
136
+ model_names: [changed_model_name, foreign_model_name],
137
+ instance_ids: [[changed_model_id, new_foreign_id]]
138
+ },
139
+ # Add IDs of child models
140
+ subscription.load_ids_for_sub_models(changed_model_name, changed_model_id)
141
+ ]
142
+ })
143
+
144
+ subscription.apply_id_changeset(id_changeset)
145
+ subscription.broadcast_id_changeset(id_changeset)
146
+ end
147
+
148
+ #########
149
+ # --> Leaving the family
150
+ # Subs where changed is child
151
+ # + old parent was in the sub, but new parent isn't
152
+ # Just need to remove the link, orphan detection will do the rest
153
+ (old_sub_ids - new_sub_ids).each do |sub_id|
154
+ subscription = find_by_id(sub_id)
155
+ id_changeset = subscription.graph_helper.apply_update({
156
+ remove: [
157
+ {
158
+ model_names: [changed_model_name, foreign_model_name],
159
+ instance_ids: [[changed_model_id, old_foreign_id]]
160
+ }
161
+ ]
162
+ })
163
+ subscription.apply_id_changeset(id_changeset)
164
+ subscription.broadcast_id_changeset(id_changeset)
165
+ end
166
+ end
167
+
168
+ def self.remove_ids(model_name, ids)
169
+ # td: finish this
170
+ ids.each do |instance_id|
171
+ for_instance(model_name, instance_id, false).each do |sub_id|
172
+ subscription = find_by_id(sub_id)
173
+
174
+ id_changeset = subscription.graph_helper.apply_remove_node("#{model_name}:#{instance_id}")
175
+ subscription.apply_id_changeset(id_changeset)
176
+ subscription.broadcast_id_changeset(id_changeset)
177
+ end
178
+ end
179
+ end
180
+
181
+ # Add ID to any _all_ subscriptions
182
+ def self.add_id(model_name, id)
183
+
184
+ end
185
+
186
+ def self.all
187
+ $redis_jason.smembers('jason:subscriptions').map { |id| Jason::Subscription.find_by_id(id) }
13
188
  end
14
189
 
15
190
  def set_config(raw_config)
16
- @config = raw_config.with_indifferent_access.map { |k,v| [k.underscore.to_s, v] }.to_h
191
+ @config = raw_config.deep_stringify_keys.deep_transform_values { |v| v.is_a?(Symbol) ? v.to_s : v }
192
+ end
193
+
194
+ def clear_id(model_name, id, parent_model_name)
195
+ remove_ids(model_name, [id])
196
+ end
197
+
198
+ # Add IDs that aren't present
199
+ def commit_ids(model_name, ids)
200
+ $redis_jason.sadd("jason:subscriptions:#{id}:ids:#{model_name}", ids)
201
+ ids.each do |instance_id|
202
+ $redis_jason.sadd("jason:models:#{model_name}:#{instance_id}:subscriptions", id)
203
+ end
204
+ end
205
+
206
+ def remove_ids(model_name, ids)
207
+ $redis_jason.srem("jason:subscriptions:#{id}:ids:#{model_name}", ids)
208
+ ids.each do |instance_id|
209
+ $redis_jason.srem("jason:models:#{model_name}:#{instance_id}:subscriptions", id)
210
+ end
211
+ end
212
+
213
+ def apply_id_changeset(changeset)
214
+ changeset[:ids_to_add].each do |model_name, ids|
215
+ commit_ids(model_name, ids)
216
+ end
217
+
218
+ changeset[:ids_to_remove].each do |model_name, ids|
219
+ remove_ids(model_name, ids)
220
+ end
221
+ end
222
+
223
+ def broadcast_id_changeset(changeset)
224
+ changeset[:ids_to_add].each do |model_name, ids|
225
+ ids.each { |id| add(model_name, id) }
226
+ end
227
+
228
+ changeset[:ids_to_remove].each do |model_name, ids|
229
+ ids.each { |id| destroy(model_name, id) }
230
+ end
231
+ end
232
+
233
+ # Take a model name and IDs and return an edge set of all the models that appear and
234
+ # their instance IDs
235
+ def load_ids_for_sub_models(model_name, ids)
236
+ # Limitation: Same association can't appear twice
237
+ includes_tree = includes_helper.get_tree_for(model_name)
238
+ all_models = includes_helper.all_models(model_name)
239
+
240
+ relation = model_name.classify.constantize.all.eager_load(includes_tree)
241
+
242
+ if model_name == model
243
+ if conditions.blank?
244
+ $redis_jason.sadd("jason:models:#{model_name}:all:subscriptions", id)
245
+ all_models -= [model_name]
246
+ else
247
+ relation = relation.where(conditions)
248
+ end
249
+ else
250
+ raise "Must supply IDs for sub models" if ids.nil?
251
+ return if ids.blank?
252
+ relation = relation.where(id: ids)
253
+ end
254
+
255
+ pluck_args = all_models.map { |m| "#{m.pluralize}.id" }
256
+ instance_ids = relation.pluck(*pluck_args)
257
+
258
+ # pluck returns only a 1D array if only 1 arg passed
259
+ if all_models.size == 1
260
+ instance_ids = [instance_ids]
261
+ end
262
+
263
+ return { model_names: all_models, instance_ids: instance_ids }
264
+ end
265
+
266
+ # 'posts', [post#1, post#2,...]
267
+ def set_ids_for_sub_models(model_name = model, ids = nil, enforce: false)
268
+ edge_set = load_ids_for_sub_models(model_name, ids)
269
+
270
+ # Build the tree
271
+ id_changeset = graph_helper.apply_update({
272
+ add: [edge_set]
273
+ })
274
+ apply_id_changeset(id_changeset)
275
+ end
276
+
277
+ def clear_all_ids
278
+ includes_helper.all_models.each do |model_name|
279
+ if model_name == model && conditions.blank?
280
+ $redis_jason.srem("jason:models:#{model_name}:all:subscriptions", id)
281
+ end
282
+
283
+ ids = $redis_jason.smembers("jason:subscriptions:#{id}:ids:#{model_name}")
284
+ ids.each do |instance_id|
285
+ $redis_jason.srem("jason:models:#{model_name}:#{instance_id}:subscriptions", id)
286
+ end
287
+ $redis_jason.del("jason:subscriptions:#{id}:ids:#{model_name}")
288
+ end
289
+ end
290
+
291
+ def ids(model_name = model)
292
+ $redis_jason.smembers("jason:subscriptions:#{id}:ids:#{model_name}")
293
+ end
294
+
295
+ def model
296
+ @config['model']
297
+ end
298
+
299
+ def model_klass(model_name)
300
+ model_name.to_s.classify.constantize
301
+ end
302
+
303
+ def conditions
304
+ @config['conditions']
17
305
  end
18
306
 
19
307
  def configure(raw_config)
20
308
  set_config(raw_config)
21
- $redis_jason.hmset("jason:subscriptions:#{id}", *config.map { |k,v| [k, v.to_json]}.flatten)
309
+ $redis_jason.sadd("jason:subscriptions", id)
310
+ $redis_jason.hmset("jason:subscriptions:#{id}", *config.map { |k,v| [k, v.to_json] }.flatten)
22
311
  end
23
312
 
24
313
  def destroy
25
- config.each do |model, value|
26
- $redis_jason.srem("jason:#{model.to_s.underscore}:subscriptions", id)
27
- end
28
- $redis_jason.del("jason:subscriptions:#{id}")
314
+ raise
29
315
  end
30
316
 
31
317
  def add_consumer(consumer_id)
@@ -33,8 +319,9 @@ class Jason::Subscription
33
319
  $redis_jason.sadd("jason:subscriptions:#{id}:consumers", consumer_id)
34
320
  $redis_jason.hset("jason:consumers", consumer_id, Time.now.utc)
35
321
 
36
- add_subscriptions
37
- publish_all
322
+ if before_consumer_count == 0
323
+ set_ids_for_sub_models
324
+ end
38
325
  end
39
326
 
40
327
  def remove_consumer(consumer_id)
@@ -42,7 +329,7 @@ class Jason::Subscription
42
329
  $redis_jason.hdel("jason:consumers", consumer_id)
43
330
 
44
331
  if consumer_count == 0
45
- remove_subscriptions
332
+ clear_all_ids
46
333
  end
47
334
  end
48
335
 
@@ -51,119 +338,84 @@ class Jason::Subscription
51
338
  end
52
339
 
53
340
  def channel
54
- "jason:#{id}"
341
+ "jason-#{id}"
55
342
  end
56
343
 
57
- def publish_all
58
- config.each do |model, model_config|
59
- klass = model.to_s.classify.constantize
60
- conditions = model_config['conditions'] || {}
61
- klass.where(conditions).find_each(&:cache_json)
62
- update(model)
63
- end
344
+ def user_can_access?(user)
345
+ # td: implement the authorization logic here
346
+ return true if Jason.authorization_service.blank?
347
+ Jason.authorization_service.call(user, model, conditions, includes_helper.all_models - [model])
64
348
  end
65
349
 
66
- def add_subscriptions
67
- config.each do |model, value|
68
- $redis_jason.hset("jason:#{model.to_s.underscore}:subscriptions", id, value.to_json)
69
- update(model)
70
- end
350
+ def get
351
+ includes_helper.all_models.map { |model_name| [model_name, get_for_model(model_name)] }.to_h
71
352
  end
72
353
 
73
- def remove_subscriptions
74
- config.each do |model, _|
75
- $redis_jason.hdel("jason:#{model.to_s.underscore}:subscriptions", id)
354
+ def get_for_model(model_name)
355
+ if $redis_jason.sismember("jason:models:#{model_name}:all:subscriptions", id)
356
+ instance_jsons_hash, idx = $redis_jason.multi do |r|
357
+ r.hgetall("jason:cache:#{model_name}")
358
+ r.get("jason:subscription:#{id}:#{model_name}:idx")
359
+ end
360
+ instance_jsons = instance_jsons_hash.values
361
+ else
362
+ instance_jsons, idx = Jason::LuaGenerator.new.get_payload(model_name, id)
76
363
  end
77
- end
78
364
 
79
- def self.publish_all
80
- JASON_API_MODEL.each do |model, _v|
81
- klass = model.to_s.classify.constantize
82
- klass.publish_all(klass.all) if klass.respond_to?(:publish_all)
365
+ payload = instance_jsons.map do |instance_json|
366
+ instance_json ? JSON.parse(instance_json) : {}
83
367
  end
84
- end
85
-
86
- def get(model)
87
- value = JSON.parse($redis_jason.get("#{channel}:#{model}:value") || '[]')
88
- idx = $redis_jason.get("#{channel}:#{model}:idx").to_i
89
368
 
90
369
  {
91
370
  type: 'payload',
371
+ model: model_name,
372
+ payload: payload,
92
373
  md5Hash: id,
93
- model: model,
94
- value: value,
95
- idx: idx
374
+ idx: idx.to_i
96
375
  }
97
376
  end
98
377
 
99
- def get_diff(old_value, value)
100
- JsonDiff.generate(old_value, value)
101
- end
378
+ def add(model_name, instance_id)
379
+ idx = $redis_jason.incr("jason:subscription:#{id}:#{model_name}:idx")
380
+ payload = JSON.parse($redis_jason.hget("jason:cache:#{model_name}", instance_id) || '{}')
102
381
 
103
- def deep_stringify(value)
104
- if value.is_a?(Hash)
105
- value.deep_stringify_keys
106
- elsif value.is_a?(Array)
107
- value.map { |x| x.deep_stringify_keys }
108
- end
109
- end
382
+ payload = {
383
+ id: instance_id,
384
+ model: model_name,
385
+ payload: payload,
386
+ md5Hash: id,
387
+ idx: idx.to_i
388
+ }
110
389
 
111
- def get_throttle
112
- if !$throttle_rate || !$throttle_timeout || Time.now.utc > $throttle_timeout
113
- $throttle_timeout = Time.now.utc + 5.seconds
114
- $throttle_rate = ($redis_jason.get('global_throttle_rate') || 0).to_i
115
- else
116
- $throttle_rate
117
- end
390
+ broadcaster.broadcast(payload)
118
391
  end
119
392
 
120
- # Atomically update and return patch
121
- def update(model)
122
- start_time = Time.now.utc
123
- conditions = config[model]['conditions']
124
-
125
- value = $redis_jason.hgetall("jason:#{model}:cache")
126
- .values.map { |v| JSON.parse(v) }
127
- .select { |v| (conditions || {}).all? { |field, value| v[field] == value } }
128
- .sort_by { |v| v['id'] }
129
-
130
- # lfsa = last finished, started at
131
- # If another job that started after this one, finished before this one, skip sending this state update
132
- if Time.parse($redis_jason.get("jason:#{channel}:lfsa") || '1970-01-01 00:00:00 UTC') < start_time
133
- $redis_jason.set("jason:#{channel}:lfsa", start_time)
134
- else
135
- return
136
- end
137
-
138
- value = deep_stringify(value)
393
+ def update(model_name, instance_id, payload, gidx)
394
+ idx = Jason::LuaGenerator.new.get_subscription(model_name, instance_id, id, gidx)
395
+ return if idx.blank?
139
396
 
140
- # If value has changed, return old value and new idx. Otherwise do nothing.
141
- cmd = <<~LUA
142
- local old_val=redis.call('get', ARGV[1] .. ':value')
143
- if old_val ~= ARGV[2] then
144
- redis.call('set', ARGV[1] .. ':value', ARGV[2])
145
- local new_idx = redis.call('incr', ARGV[1] .. ':idx')
146
- return { new_idx, old_val }
147
- end
148
- LUA
149
-
150
- result = $redis_jason.eval cmd, [], ["#{channel}:#{model}", value.to_json]
151
- return if result.blank?
397
+ payload = {
398
+ id: instance_id,
399
+ model: model_name,
400
+ payload: payload,
401
+ md5Hash: id,
402
+ idx: idx.to_i
403
+ }
152
404
 
153
- idx = result[0]
154
- old_value = JSON.parse(result[1] || '[]')
155
- diff = get_diff(old_value, value)
405
+ broadcaster.broadcast(payload)
406
+ end
156
407
 
157
- end_time = Time.now.utc
408
+ def destroy(model_name, instance_id)
409
+ idx = $redis_jason.incr("jason:subscription:#{id}:#{model_name}:idx")
158
410
 
159
411
  payload = {
160
- model: model,
412
+ id: instance_id,
413
+ model: model_name,
414
+ destroy: true,
161
415
  md5Hash: id,
162
- diff: diff,
163
- idx: idx.to_i,
164
- latency: ((end_time - start_time)*1000).round
416
+ idx: idx.to_i
165
417
  }
166
418
 
167
- ActionCable.server.broadcast("jason:#{id}", payload)
419
+ broadcaster.broadcast(payload)
168
420
  end
169
421
  end