jason-rails 0.7.0 → 0.7.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +3 -0
- data/Gemfile.lock +3 -2
- data/README.md +6 -3
- data/app/controllers/jason/jason_controller.rb +26 -4
- data/client/package.json +1 -1
- data/lib/jason.rb +2 -1
- data/lib/jason/publisher.rb +0 -29
- data/lib/jason/subscription.rb +2 -2
- data/lib/jason/version.rb +1 -1
- metadata +2 -4
- data/lib/jason/publisher_old.rb +0 -112
- data/lib/jason/subscription_old.rb +0 -171
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 0e0d37099f467ff5bd4ab868732ce57c53bf055e6ecb8d0f0506bb14ee723d77
|
4
|
+
data.tar.gz: 6912d141317dbc95edb96bc11ed29235a90fc6dd8575a4dcba18017b81a9964f
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 3cce0310a94bba9c73237d3d18bcd7aa949fb35905b7dd76c69c05b6d8259f1fc6d0b79da2a01b3db34b372cf438ea96777c14d2bd3bdbfacf9ba27578e81f19
|
7
|
+
data.tar.gz: f2460fea5f459d24966741cce29855b97b9361a900a848e8655ccdfb4cbaa03aa4d47135b2fa661961f7c8d46d37f5e9bc821ff97a6f5cdd22265f7a8d755758
|
data/CHANGELOG.md
CHANGED
@@ -1,3 +1,6 @@
|
|
1
|
+
## v0.7.1
|
2
|
+
- Added: Authorization for REST endpoints. Previously these just inherited logic from ApplicationController. Pass a `update_authorization_service` option to the Jason initializer to use this.
|
3
|
+
|
1
4
|
## v0.7.0
|
2
5
|
- Added: New forms of conditional subscription. You can now add conditions on fields other than the primary key.
|
3
6
|
E.g.
|
data/Gemfile.lock
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
jason-rails (0.
|
4
|
+
jason-rails (0.7.0)
|
5
5
|
connection_pool (>= 2.2.3)
|
6
6
|
jsondiff
|
7
7
|
rails (>= 5)
|
@@ -89,7 +89,8 @@ GEM
|
|
89
89
|
marcel (0.3.3)
|
90
90
|
mimemagic (~> 0.3.2)
|
91
91
|
method_source (1.0.0)
|
92
|
-
mimemagic (0.3.
|
92
|
+
mimemagic (0.3.8)
|
93
|
+
nokogiri (~> 1)
|
93
94
|
mini_mime (1.0.2)
|
94
95
|
mini_portile2 (2.5.0)
|
95
96
|
minitest (5.14.3)
|
data/README.md
CHANGED
@@ -152,13 +152,15 @@ export default function Comment({ id }) {
|
|
152
152
|
|
153
153
|
## Authorization
|
154
154
|
|
155
|
-
By default all models can be subscribed to and updated without authentication or authorization. Probably you want to lock down access.
|
155
|
+
By default all models can be subscribed to and updated without authentication or authorization. Probably you want to lock down access. At the moment Jason has no opinion on how to handle authorization, it simply forwards parameters to a service that you provide - so the implementation can be as simple or as complex as you need.
|
156
156
|
|
157
157
|
### Authorizing subscriptions
|
158
158
|
You can do this by providing an class to Jason in the initializer under the `subscription_authorization_service` key. This must be a class receiving a message `call` with the parameters `user`, `model`, `conditions`, `sub_models` and return true or false for whether the user is allowed to access a subscription with those parameters. You can decide the implementation details of this to be as simple or complex as your app requires.
|
159
159
|
|
160
160
|
### Authorizing updates
|
161
|
-
Similarly to authorizing subscriptions, you can do this by providing an class to Jason in the initializer under the `update_authorization_service` key. This must be a class receiving a message `call` with the parameters `user`, `model`, `
|
161
|
+
Similarly to authorizing subscriptions, you can do this by providing an class to Jason in the initializer under the `update_authorization_service` key. This must be a class receiving a message `call` with the parameters `user`, `model`, `action`, `instance`, `params` and return true or false for whether the user is allowed to make this update.
|
162
|
+
|
163
|
+
See the specs for some examples of this.
|
162
164
|
|
163
165
|
## Roadmap
|
164
166
|
|
@@ -169,9 +171,10 @@ Development is primarily driven by the needs of projects we're using Jason in. I
|
|
169
171
|
- Utilities for "Draft editing" - both storing client-side copies of model trees which can be committed or discarded, as well as persisting a shadow copy to the database (to allow resumable editing, or possibly collaborative editing features)
|
170
172
|
- Benchmark and migrate if necessary ConnectionPool::Wrapper vs ConnectionPool
|
171
173
|
- Assess using RedisGraph for the graph diffing functionality, to see if this would provide a performance boost
|
174
|
+
- Improve the Typescript definitions (ie remove the abundant `any` typing currently used)
|
172
175
|
|
173
176
|
## License
|
174
177
|
|
175
|
-
The gem
|
178
|
+
The gem, npm package and source code in the git repository are available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
|
176
179
|
|
177
180
|
|
@@ -21,14 +21,17 @@ class Jason::JasonController < ::ApplicationController
|
|
21
21
|
def action
|
22
22
|
type = params[:type]
|
23
23
|
entity = type.split('/')[0].underscore
|
24
|
-
|
25
|
-
|
24
|
+
model_name = entity.singularize
|
25
|
+
api_model = Jason::ApiModel.new(model_name)
|
26
|
+
model = model_name.camelize.constantize
|
26
27
|
action = type.split('/')[1].underscore
|
27
28
|
|
28
29
|
if action == 'move_priority'
|
29
30
|
id, priority = params[:payload].values_at(:id, :priority)
|
30
31
|
|
31
32
|
instance = model.find(id)
|
33
|
+
|
34
|
+
return head :forbidden if !action_permitted?(model_name, action, instance, params)
|
32
35
|
priority_filter = instance.as_json.with_indifferent_access.slice(*api_model.priority_scope)
|
33
36
|
|
34
37
|
all_instance_ids = model.send(api_model.scope || :all).where(priority_filter).where.not(id: instance.id).order(:priority).pluck(:id)
|
@@ -40,10 +43,24 @@ class Jason::JasonController < ::ApplicationController
|
|
40
43
|
|
41
44
|
model.find(all_instance_ids).each(&:force_publish_json)
|
42
45
|
elsif action == 'upsert' || action == 'add'
|
46
|
+
id = params[:payload][:id]
|
43
47
|
payload = api_model.permit(params)
|
44
|
-
|
48
|
+
|
49
|
+
instance = model.find_by(id: id)
|
50
|
+
return head :forbidden if !action_permitted?(model_name, action, instance, params)
|
51
|
+
|
52
|
+
if instance.present?
|
53
|
+
instance.update!(payload)
|
54
|
+
else
|
55
|
+
instance = model.create!(payload)
|
56
|
+
end
|
57
|
+
|
58
|
+
return render json: instance.as_json(api_model.as_json_config)
|
45
59
|
elsif action == 'remove'
|
46
|
-
model.find(params[:payload])
|
60
|
+
instance = model.find(params[:payload])
|
61
|
+
return head :forbidden if !action_permitted?(model_name, action, instance, params)
|
62
|
+
|
63
|
+
instance.destroy!
|
47
64
|
end
|
48
65
|
|
49
66
|
return head :ok
|
@@ -75,4 +92,9 @@ class Jason::JasonController < ::ApplicationController
|
|
75
92
|
return head :forbidden
|
76
93
|
end
|
77
94
|
end
|
95
|
+
|
96
|
+
def action_permitted?(model_name, action, instance, params)
|
97
|
+
return true if Jason.update_authorization_service.blank?
|
98
|
+
Jason.update_authorization_service.call(current_user, model_name, action, instance, params)
|
99
|
+
end
|
78
100
|
end
|
data/client/package.json
CHANGED
data/lib/jason.rb
CHANGED
@@ -25,7 +25,8 @@ module Jason
|
|
25
25
|
self.mattr_accessor :pusher_key
|
26
26
|
self.mattr_accessor :pusher_region
|
27
27
|
self.mattr_accessor :pusher_channel_prefix
|
28
|
-
self.mattr_accessor :
|
28
|
+
self.mattr_accessor :subscription_authorization_service
|
29
|
+
self.mattr_accessor :update_authorization_service
|
29
30
|
self.mattr_accessor :sidekiq_queue
|
30
31
|
|
31
32
|
self.schema = {}
|
data/lib/jason/publisher.rb
CHANGED
@@ -150,35 +150,6 @@ module Jason::Publisher
|
|
150
150
|
self.after_commit :force_publish_json, on: [:create, :destroy]
|
151
151
|
self.after_commit :publish_json_if_changed, on: [:update]
|
152
152
|
end
|
153
|
-
|
154
|
-
def find_or_create_by_id(params)
|
155
|
-
object = find_by(id: params[:id])
|
156
|
-
|
157
|
-
if object
|
158
|
-
object.update(params)
|
159
|
-
elsif params[:hidden]
|
160
|
-
return false ## If an object is passed with hidden = true but didn't already exist, it's safe to never create it
|
161
|
-
else
|
162
|
-
object = create!(params)
|
163
|
-
end
|
164
|
-
|
165
|
-
object
|
166
|
-
end
|
167
|
-
|
168
|
-
def find_or_create_by_id!(params)
|
169
|
-
object = find_by(id: params[:id])
|
170
|
-
|
171
|
-
if object
|
172
|
-
object.update!(params)
|
173
|
-
elsif params[:hidden]
|
174
|
-
## TODO: We're diverging from semantics of the Rails bang! methods here, which would normally either raise or return an object. Find a way to make this better.
|
175
|
-
return false ## If an object is passed with hidden = true but didn't already exist, it's safe to never create it
|
176
|
-
else
|
177
|
-
object = create!(params)
|
178
|
-
end
|
179
|
-
|
180
|
-
object
|
181
|
-
end
|
182
153
|
end
|
183
154
|
|
184
155
|
included do
|
data/lib/jason/subscription.rb
CHANGED
@@ -390,8 +390,8 @@ class Jason::Subscription
|
|
390
390
|
|
391
391
|
def user_can_access?(user)
|
392
392
|
# td: implement the authorization logic here
|
393
|
-
return true if Jason.
|
394
|
-
Jason.
|
393
|
+
return true if Jason.subscription_authorization_service.blank?
|
394
|
+
Jason.subscription_authorization_service.call(user, model, conditions, includes_helper.all_models - [model])
|
395
395
|
end
|
396
396
|
|
397
397
|
def get
|
data/lib/jason/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: jason-rails
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.7.
|
4
|
+
version: 0.7.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- James Rees
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2021-04-
|
11
|
+
date: 2021-04-05 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rails
|
@@ -238,9 +238,7 @@ files:
|
|
238
238
|
- lib/jason/includes_helper.rb
|
239
239
|
- lib/jason/lua_generator.rb
|
240
240
|
- lib/jason/publisher.rb
|
241
|
-
- lib/jason/publisher_old.rb
|
242
241
|
- lib/jason/subscription.rb
|
243
|
-
- lib/jason/subscription_old.rb
|
244
242
|
- lib/jason/version.rb
|
245
243
|
homepage: https://github.com/jamesr2323/jason
|
246
244
|
licenses:
|
data/lib/jason/publisher_old.rb
DELETED
@@ -1,112 +0,0 @@
|
|
1
|
-
module Jason::PublisherOld
|
2
|
-
extend ActiveSupport::Concern
|
3
|
-
|
4
|
-
def cache_json
|
5
|
-
as_json_config = api_model.as_json_config
|
6
|
-
scope = api_model.scope
|
7
|
-
|
8
|
-
if self.persisted? && (scope.blank? || self.class.unscoped.send(scope).exists?(self.id))
|
9
|
-
payload = self.reload.as_json(as_json_config)
|
10
|
-
$redis_jason.hset("jason:#{self.class.name.underscore}:cache", self.id, payload.to_json)
|
11
|
-
else
|
12
|
-
$redis_jason.hdel("jason:#{self.class.name.underscore}:cache", self.id)
|
13
|
-
end
|
14
|
-
end
|
15
|
-
|
16
|
-
def publish_json
|
17
|
-
cache_json
|
18
|
-
return if skip_publish_json
|
19
|
-
|
20
|
-
self.class.jason_subscriptions.each do |id, config_json|
|
21
|
-
config = JSON.parse(config_json)
|
22
|
-
|
23
|
-
if (config['conditions'] || {}).all? { |field, value| self.send(field) == value }
|
24
|
-
Jason::Subscription.new(id: id).update(self.class.name.underscore)
|
25
|
-
end
|
26
|
-
end
|
27
|
-
end
|
28
|
-
|
29
|
-
def publish_json_if_changed
|
30
|
-
subscribed_fields = api_model.subscribed_fields
|
31
|
-
publish_json if (self.previous_changes.keys.map(&:to_sym) & subscribed_fields).present? || !self.persisted?
|
32
|
-
end
|
33
|
-
|
34
|
-
class_methods do
|
35
|
-
def subscriptions
|
36
|
-
$redis_jason.hgetall("jason:#{self.name.underscore}:subscriptions")
|
37
|
-
end
|
38
|
-
|
39
|
-
def jason_subscriptions
|
40
|
-
$redis_jason.hgetall("jason:#{self.name.underscore}:subscriptions")
|
41
|
-
end
|
42
|
-
|
43
|
-
def publish_all(instances)
|
44
|
-
instances.each(&:cache_json)
|
45
|
-
|
46
|
-
subscriptions.each do |id, config_json|
|
47
|
-
Jason::Subscription.new(id: id).update(self.name.underscore)
|
48
|
-
end
|
49
|
-
end
|
50
|
-
|
51
|
-
def flush_cache
|
52
|
-
$redis_jason.del("jason:#{self.name.underscore}:cache")
|
53
|
-
end
|
54
|
-
|
55
|
-
def setup_json
|
56
|
-
self.after_initialize -> {
|
57
|
-
@api_model = Jason::ApiModel.new(self.class.name.underscore)
|
58
|
-
}
|
59
|
-
self.after_commit :publish_json_if_changed
|
60
|
-
|
61
|
-
include_models = Jason::ApiModel.new(self.name.underscore).include_models
|
62
|
-
|
63
|
-
include_models.map do |assoc|
|
64
|
-
puts assoc
|
65
|
-
reflection = self.reflect_on_association(assoc.to_sym)
|
66
|
-
reflection.klass.after_commit -> {
|
67
|
-
subscribed_fields = Jason::ApiModel.new(self.class.name.underscore).subscribed_fields
|
68
|
-
puts subscribed_fields.inspect
|
69
|
-
|
70
|
-
if (self.previous_changes.keys.map(&:to_sym) & subscribed_fields).present?
|
71
|
-
self.send(reflection.inverse_of.name)&.publish_json
|
72
|
-
end
|
73
|
-
}
|
74
|
-
end
|
75
|
-
end
|
76
|
-
|
77
|
-
def find_or_create_by_id(params)
|
78
|
-
object = find_by(id: params[:id])
|
79
|
-
|
80
|
-
if object
|
81
|
-
object.update(params)
|
82
|
-
elsif params[:hidden]
|
83
|
-
return false ## If an object is passed with hidden = true but didn't already exist, it's safe to never create it
|
84
|
-
else
|
85
|
-
object = create!(params)
|
86
|
-
end
|
87
|
-
|
88
|
-
object
|
89
|
-
end
|
90
|
-
|
91
|
-
def find_or_create_by_id!(params)
|
92
|
-
object = find_by(id: params[:id])
|
93
|
-
|
94
|
-
if object
|
95
|
-
object.update!(params)
|
96
|
-
elsif params[:hidden]
|
97
|
-
## TODO: We're diverging from semantics of the Rails bang! methods here, which would normally either raise or return an object. Find a way to make this better.
|
98
|
-
return false ## If an object is passed with hidden = true but didn't already exist, it's safe to never create it
|
99
|
-
else
|
100
|
-
object = create!(params)
|
101
|
-
end
|
102
|
-
|
103
|
-
object
|
104
|
-
end
|
105
|
-
end
|
106
|
-
|
107
|
-
included do
|
108
|
-
attr_accessor :skip_publish_json, :api_model
|
109
|
-
|
110
|
-
setup_json
|
111
|
-
end
|
112
|
-
end
|
@@ -1,171 +0,0 @@
|
|
1
|
-
class Jason::SubscriptionOld
|
2
|
-
attr_accessor :id, :config
|
3
|
-
|
4
|
-
def initialize(id: nil, config: nil)
|
5
|
-
if id
|
6
|
-
@id = id
|
7
|
-
raw_config = $redis_jason.hgetall("jason:subscriptions:#{id}").map { |k,v| [k, JSON.parse(v)] }.to_h
|
8
|
-
set_config(raw_config)
|
9
|
-
else
|
10
|
-
@id = Digest::MD5.hexdigest(config.to_json)
|
11
|
-
configure(config)
|
12
|
-
end
|
13
|
-
end
|
14
|
-
|
15
|
-
def set_config(raw_config)
|
16
|
-
@config = raw_config.with_indifferent_access.map { |k,v| [k.underscore.to_s, v] }.to_h
|
17
|
-
end
|
18
|
-
|
19
|
-
def configure(raw_config)
|
20
|
-
set_config(raw_config)
|
21
|
-
$redis_jason.hmset("jason:subscriptions:#{id}", *config.map { |k,v| [k, v.to_json]}.flatten)
|
22
|
-
end
|
23
|
-
|
24
|
-
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}")
|
29
|
-
end
|
30
|
-
|
31
|
-
def add_consumer(consumer_id)
|
32
|
-
before_consumer_count = consumer_count
|
33
|
-
$redis_jason.sadd("jason:subscriptions:#{id}:consumers", consumer_id)
|
34
|
-
$redis_jason.hset("jason:consumers", consumer_id, Time.now.utc)
|
35
|
-
|
36
|
-
add_subscriptions
|
37
|
-
publish_all
|
38
|
-
end
|
39
|
-
|
40
|
-
def remove_consumer(consumer_id)
|
41
|
-
$redis_jason.srem("jason:subscriptions:#{id}:consumers", consumer_id)
|
42
|
-
$redis_jason.hdel("jason:consumers", consumer_id)
|
43
|
-
|
44
|
-
if consumer_count == 0
|
45
|
-
remove_subscriptions
|
46
|
-
end
|
47
|
-
end
|
48
|
-
|
49
|
-
def consumer_count
|
50
|
-
$redis_jason.scard("jason:subscriptions:#{id}:consumers")
|
51
|
-
end
|
52
|
-
|
53
|
-
def channel
|
54
|
-
"jason:#{id}"
|
55
|
-
end
|
56
|
-
|
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
|
64
|
-
end
|
65
|
-
|
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
|
71
|
-
end
|
72
|
-
|
73
|
-
def remove_subscriptions
|
74
|
-
config.each do |model, _|
|
75
|
-
$redis_jason.hdel("jason:#{model.to_s.underscore}:subscriptions", id)
|
76
|
-
end
|
77
|
-
end
|
78
|
-
|
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)
|
83
|
-
end
|
84
|
-
end
|
85
|
-
|
86
|
-
def get(model_name)
|
87
|
-
LuaGenerator.new.index_hash_by_set("jason:cache:#{model_name}", "")
|
88
|
-
|
89
|
-
value = JSON.parse($redis_jason.get("#{channel}:#{model}:value") || '[]')
|
90
|
-
idx = $redis_jason.get("#{channel}:#{model}:idx").to_i
|
91
|
-
|
92
|
-
{
|
93
|
-
type: 'payload',
|
94
|
-
md5Hash: id,
|
95
|
-
model: model,
|
96
|
-
value: value,
|
97
|
-
idx: idx
|
98
|
-
}
|
99
|
-
end
|
100
|
-
|
101
|
-
def get_diff(old_value, value)
|
102
|
-
JsonDiff.generate(old_value, value)
|
103
|
-
end
|
104
|
-
|
105
|
-
def deep_stringify(value)
|
106
|
-
if value.is_a?(Hash)
|
107
|
-
value.deep_stringify_keys
|
108
|
-
elsif value.is_a?(Array)
|
109
|
-
value.map { |x| x.deep_stringify_keys }
|
110
|
-
end
|
111
|
-
end
|
112
|
-
|
113
|
-
def get_throttle
|
114
|
-
if !$throttle_rate || !$throttle_timeout || Time.now.utc > $throttle_timeout
|
115
|
-
$throttle_timeout = Time.now.utc + 5.seconds
|
116
|
-
$throttle_rate = ($redis_jason.get('global_throttle_rate') || 0).to_i
|
117
|
-
else
|
118
|
-
$throttle_rate
|
119
|
-
end
|
120
|
-
end
|
121
|
-
|
122
|
-
# Atomically update and return patch
|
123
|
-
def update(model)
|
124
|
-
start_time = Time.now.utc
|
125
|
-
conditions = config[model]['conditions']
|
126
|
-
|
127
|
-
value = $redis_jason.hgetall("jason:#{model}:cache")
|
128
|
-
.values.map { |v| JSON.parse(v) }
|
129
|
-
.select { |v| (conditions || {}).all? { |field, value| v[field] == value } }
|
130
|
-
.sort_by { |v| v['id'] }
|
131
|
-
|
132
|
-
# lfsa = last finished, started at
|
133
|
-
# If another job that started after this one, finished before this one, skip sending this state update
|
134
|
-
if Time.parse($redis_jason.get("jason:#{channel}:lfsa") || '1970-01-01 00:00:00 UTC') < start_time
|
135
|
-
$redis_jason.set("jason:#{channel}:lfsa", start_time)
|
136
|
-
else
|
137
|
-
return
|
138
|
-
end
|
139
|
-
|
140
|
-
value = deep_stringify(value)
|
141
|
-
|
142
|
-
# If value has changed, return old value and new idx. Otherwise do nothing.
|
143
|
-
cmd = <<~LUA
|
144
|
-
local old_val=redis.call('get', ARGV[1] .. ':value')
|
145
|
-
if old_val ~= ARGV[2] then
|
146
|
-
redis.call('set', ARGV[1] .. ':value', ARGV[2])
|
147
|
-
local new_idx = redis.call('incr', ARGV[1] .. ':idx')
|
148
|
-
return { new_idx, old_val }
|
149
|
-
end
|
150
|
-
LUA
|
151
|
-
|
152
|
-
result = $redis_jason.eval cmd, [], ["#{channel}:#{model}", value.to_json]
|
153
|
-
return if result.blank?
|
154
|
-
|
155
|
-
idx = result[0]
|
156
|
-
old_value = JSON.parse(result[1] || '[]')
|
157
|
-
diff = get_diff(old_value, value)
|
158
|
-
|
159
|
-
end_time = Time.now.utc
|
160
|
-
|
161
|
-
payload = {
|
162
|
-
model: model,
|
163
|
-
md5Hash: id,
|
164
|
-
diff: diff,
|
165
|
-
idx: idx.to_i,
|
166
|
-
latency: ((end_time - start_time)*1000).round
|
167
|
-
}
|
168
|
-
|
169
|
-
ActionCable.server.broadcast("jason:#{id}", payload)
|
170
|
-
end
|
171
|
-
end
|