jason-rails 0.6.8 → 0.7.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (44) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +27 -0
  3. data/Gemfile.lock +1 -1
  4. data/README.md +2 -12
  5. data/app/workers/jason/outbound_message_queue_worker.rb +1 -1
  6. data/client/lib/addRelations.d.ts +1 -0
  7. data/client/lib/addRelations.js +39 -0
  8. data/client/lib/createJasonReducers.js +4 -2
  9. data/client/lib/createOptDis.d.ts +1 -1
  10. data/client/lib/createOptDis.js +9 -8
  11. data/client/lib/createServerActionQueue.d.ts +3 -2
  12. data/client/lib/createServerActionQueue.js +32 -6
  13. data/client/lib/createServerActionQueue.test.js +61 -6
  14. data/client/lib/createThenable.d.ts +1 -0
  15. data/client/lib/createThenable.js +5 -0
  16. data/client/lib/transportAdapters/actionCableAdapter.js +24 -4
  17. data/client/lib/transportAdapters/pusherAdapter.js +1 -1
  18. data/client/lib/useDraft.d.ts +1 -0
  19. data/client/lib/useDraft.js +13 -0
  20. data/client/lib/useEager.d.ts +1 -1
  21. data/client/lib/useEager.js +10 -5
  22. data/client/lib/useJason.js +0 -3
  23. data/client/package.json +1 -1
  24. data/client/src/addRelations.ts +33 -0
  25. data/client/src/createJasonReducers.ts +4 -2
  26. data/client/src/createOptDis.ts +10 -8
  27. data/client/src/createServerActionQueue.test.ts +60 -6
  28. data/client/src/createServerActionQueue.ts +41 -6
  29. data/client/src/transportAdapters/actionCableAdapter.ts +24 -5
  30. data/client/src/transportAdapters/pusherAdapter.ts +1 -2
  31. data/client/src/useDraft.ts +17 -0
  32. data/client/src/useEager.ts +9 -6
  33. data/client/src/useJason.ts +0 -3
  34. data/lib/jason.rb +2 -0
  35. data/lib/jason/api_model.rb +0 -4
  36. data/lib/jason/channel.rb +0 -7
  37. data/lib/jason/conditions_matcher.rb +88 -0
  38. data/lib/jason/consistency_checker.rb +61 -0
  39. data/lib/jason/graph_helper.rb +4 -0
  40. data/lib/jason/publisher.rb +34 -5
  41. data/lib/jason/subscription.rb +49 -13
  42. data/lib/jason/version.rb +1 -1
  43. metadata +12 -3
  44. data/client/src/makeEager.ts +0 -46
data/lib/jason.rb CHANGED
@@ -12,6 +12,8 @@ require 'jason/engine'
12
12
  require 'jason/lua_generator'
13
13
  require 'jason/includes_helper'
14
14
  require 'jason/graph_helper'
15
+ require 'jason/conditions_matcher'
16
+ require 'jason/consistency_checker'
15
17
 
16
18
  module Jason
17
19
  class Error < StandardError; end
@@ -36,11 +36,7 @@ class Jason::ApiModel
36
36
  end
37
37
 
38
38
  def permit(params)
39
- pp self
40
- pp params
41
39
  params = params.require(:payload).permit(allowed_params).tap do |allowed|
42
- pp "ALLOWED"
43
- pp allowed
44
40
  allowed_object_params.each do |key|
45
41
  allowed[key] = params[:payload][key].to_unsafe_h if params[:payload][key]
46
42
  end
data/lib/jason/channel.rb CHANGED
@@ -12,7 +12,6 @@ class Jason::Channel < ActionCable::Channel::Base
12
12
  private
13
13
 
14
14
  def handle_message(message)
15
- pp message['createSubscription']
16
15
  @subscriptions ||= []
17
16
 
18
17
  begin # ActionCable swallows errors in this message - ensure they're output to logs.
@@ -38,18 +37,12 @@ class Jason::Channel < ActionCable::Channel::Base
38
37
 
39
38
  subscriptions.push(subscription)
40
39
  subscription.add_consumer(identifier)
41
- subscription.get.each do |payload|
42
- pp payload
43
- transmit(payload) if payload.present?
44
- end
45
40
  end
46
41
 
47
42
  def remove_subscription(config)
48
43
  subscription = Jason::Subscription.upsert_by_config(config['model'], conditions: config['conditions'], includes: config['includes'])
49
44
  subscriptions.reject! { |s| s.id == subscription.id }
50
45
  subscription.remove_consumer(identifier)
51
-
52
- # TODO Stop streams
53
46
  end
54
47
 
55
48
  def get_payload(config, force_refresh = false)
@@ -0,0 +1,88 @@
1
+ class Jason::ConditionsMatcher
2
+ attr_reader :klass
3
+
4
+ def initialize(klass)
5
+ @klass = klass
6
+ end
7
+
8
+ # key, rules = 'post_id', 123
9
+ # key, rules = 'post_id', { 'value': [123,C456], 'type': 'between' }
10
+ # key, rules = 'post_id', { 'value': [123,456], 'type': 'between', 'not': true }
11
+ # key, rules = 'post_id', { 'value': 123, 'type': 'equals', 'not': true }
12
+ def test_match(key, rules, previous_changes)
13
+ return nil if !previous_changes.keys.include?(key)
14
+
15
+ if rules.is_a?(Hash)
16
+ matches = false
17
+ value = convert_to_datatype(key, rules['value'])
18
+
19
+ if rules['type'] == 'equals'
20
+ matches = previous_changes[key][1] == value
21
+ elsif rules['type'] == 'between'
22
+ matches = (value[0]..value[1]).cover?(previous_changes[key][1])
23
+ else
24
+ raise "Unrecognized rule type #{rules['type']}"
25
+ end
26
+
27
+ if rules['not']
28
+ return !matches
29
+ else
30
+ return matches
31
+ end
32
+
33
+ elsif rules.is_a?(Array)
34
+ value = convert_to_datatype(key, rules)
35
+ return previous_changes[key][1].includes?(value)
36
+ else
37
+ value = convert_to_datatype(key, rules)
38
+ return previous_changes[key][1] == value
39
+ end
40
+ end
41
+
42
+ # conditions = { 'post_id' => 123, 'created_at' => { 'type' => 'between', 'value' => ['2020-01-01', '2020-01-02'] } }
43
+ def apply_conditions(relation, conditions)
44
+ conditions.each do |key, rules|
45
+ relation = apply_condition(relation, key, rules)
46
+ end
47
+
48
+ relation
49
+ end
50
+
51
+ private
52
+
53
+ def apply_condition(relation, key, rules)
54
+ if rules.is_a?(Hash)
55
+ value = convert_to_datatype(key, rules['value'])
56
+
57
+ if rules['type'] == 'equals'
58
+ arg = { key => value }
59
+ elsif rules['type'] == 'between'
60
+ arg = { key => value[0]..value[1] }
61
+ else
62
+ raise "Unrecognized rule type #{rules['type']}"
63
+ end
64
+
65
+ if rules['not']
66
+ return relation.where.not(arg)
67
+ else
68
+ return relation.where(arg)
69
+ end
70
+ else
71
+ value = convert_to_datatype(key, rules)
72
+ return relation.where({ key => value })
73
+ end
74
+ end
75
+
76
+ def convert_to_datatype(key, value)
77
+ datatype = klass.type_for_attribute(key).type
78
+ if datatype == :datetime || datatype == :date
79
+ if value.is_a?(Array)
80
+ value.map { |v| v&.to_datetime }
81
+ else
82
+ value&.to_datetime
83
+ end
84
+ else
85
+ value
86
+ end
87
+ end
88
+ end
@@ -0,0 +1,61 @@
1
+ class Jason::ConsistencyChecker
2
+ attr_reader :subscription
3
+ attr_reader :inconsistent
4
+
5
+ def self.check_all(fix: false)
6
+ Jason::Subscription.all.each do |sub|
7
+ next if sub.consumer_count == 0
8
+ checker = Jason::ConsistencyChecker.new(sub)
9
+ result = checker.check
10
+ if checker.inconsistent?
11
+ pp sub.config
12
+ pp result
13
+ if fix
14
+ sub.reset!(hard: true)
15
+ end
16
+ end
17
+ end
18
+ end
19
+
20
+ def self.fix_all
21
+ check_all(fix: true)
22
+ end
23
+
24
+ def initialize(subscription)
25
+ @subscription = subscription
26
+ @inconsistent = false
27
+ end
28
+
29
+ def inconsistent?
30
+ inconsistent
31
+ end
32
+
33
+ # Take a subscription, get the current cached payload, and compare it to the data retrieved from the database
34
+ def check
35
+ cached_payload = subscription.get
36
+ edge_set = subscription.load_ids_for_sub_models(subscription.model, nil)
37
+
38
+ result = cached_payload.map do |model_name, data|
39
+ cached_payload_instance_ids = data[:payload].map { |row| row['id'] }
40
+
41
+ model_idx = edge_set[:model_names].index(model_name)
42
+ if model_idx.present?
43
+ edge_set_instance_ids = edge_set[:instance_ids].map { |row| row[model_idx] }
44
+ else
45
+ next
46
+ end
47
+
48
+ missing = edge_set_instance_ids - cached_payload_instance_ids
49
+ intruding = cached_payload_instance_ids - edge_set_instance_ids
50
+
51
+ if missing.present? || intruding.present?
52
+ @inconsistent = true
53
+ end
54
+
55
+ [model_name, {
56
+ 'missing' => missing,
57
+ 'intruding' => intruding
58
+ }]
59
+ end.compact.to_h
60
+ end
61
+ end
@@ -26,6 +26,10 @@ class Jason::GraphHelper
26
26
  $redis_jason.srem("jason:subscriptions:#{id}:graph", edges)
27
27
  end
28
28
 
29
+ def apply_add_node_at_root(node)
30
+ diff_edges_from_graph(add_edges: ["root/#{node}"])
31
+ end
32
+
29
33
  def apply_remove_node(node)
30
34
  edges = $redis_jason.smembers("jason:subscriptions:#{id}:graph")
31
35
  edges = find_edges_with_node(edges, node)
@@ -73,13 +73,38 @@ module Jason::Publisher
73
73
  )
74
74
  end
75
75
 
76
- # - An instance is created where it belongs_to an _all_ subscription
77
- if previous_changes['id'].present?
78
- Jason::Subscription.add_id(self.class.name.underscore, id)
79
- end
80
-
81
76
  if persisted?
77
+ applied_sub_ids = []
78
+
79
+ jason_conditions.each do |row|
80
+ matches = row['conditions'].map do |key, rules|
81
+ Jason::ConditionsMatcher.new(self.class).test_match(key, rules, previous_changes)
82
+ end
83
+ next if matches.all? { |m| m.nil? } # None of the keys were in previous changes - therefore this condition does not apply
84
+ in_sub = matches.all? { |m| m }
85
+
86
+ if in_sub
87
+ row['subscription_ids'].each do |sub_id|
88
+ Jason::Subscription.find_by_id(sub_id).add_id(self.class.name.underscore, self.id)
89
+ applied_sub_ids.push(sub_id)
90
+ end
91
+ else
92
+ row['subscription_ids'].each do |sub_id|
93
+ jason_subscriptions.each do |already_sub_id|
94
+ # If this sub ID already has this instance, remove it
95
+ if already_sub_id == sub_id
96
+ sub = Jason::Subscription.find_by_id(already_sub_id)
97
+ sub.remove_id(self.class.name.underscore, self.id)
98
+ applied_sub_ids.push(already_sub_id)
99
+ end
100
+ end
101
+ end
102
+ end
103
+ end
104
+
82
105
  jason_subscriptions.each do |sub_id|
106
+ next if applied_sub_ids.include?(sub_id)
107
+
83
108
  Jason::Subscription.new(id: sub_id).update(self.class.name.underscore, id, payload, gidx)
84
109
  end
85
110
  end
@@ -94,6 +119,10 @@ module Jason::Publisher
94
119
  Jason::Subscription.for_instance(self.class.name.underscore, id)
95
120
  end
96
121
 
122
+ def jason_conditions
123
+ Jason::Subscription.conditions_for_model(self.class.name.underscore)
124
+ end
125
+
97
126
  def jason_cached_value
98
127
  JSON.parse($redis_jason.hget("jason:cache:#{self.class.name.underscore}", id) || '{}')
99
128
  end
@@ -60,6 +60,19 @@ class Jason::Subscription
60
60
  subs
61
61
  end
62
62
 
63
+ # returns [
64
+ # { condition: { post_id: 123 }, subscription_ids: [] }
65
+ # ]
66
+ def self.conditions_for_model(model_name)
67
+ rows = $redis_jason.smembers("jason:models:#{model_name}:conditions").map do |row|
68
+ JSON.parse(row)
69
+ end
70
+ conditions = rows.group_by { |row| row['conditions'] }
71
+ conditions.map do |conditions, rows|
72
+ { 'conditions' => conditions, 'subscription_ids' => rows.map { |row| row['subscription_id'] } }
73
+ end
74
+ end
75
+
63
76
  def self.for_model(model_name)
64
77
 
65
78
  end
@@ -166,10 +179,14 @@ class Jason::Subscription
166
179
  subscription.apply_id_changeset(id_changeset)
167
180
  subscription.broadcast_id_changeset(id_changeset)
168
181
  end
182
+
183
+ #########
184
+ # ---> Join the community
185
+ # Subs where changed is parent + parent is an _all_ or _condition_ subscription
186
+
169
187
  end
170
188
 
171
189
  def self.remove_ids(model_name, ids)
172
- # td: finish this
173
190
  ids.each do |instance_id|
174
191
  for_instance(model_name, instance_id, false).each do |sub_id|
175
192
  subscription = find_by_id(sub_id)
@@ -188,11 +205,6 @@ class Jason::Subscription
188
205
  end
189
206
  end
190
207
 
191
- # Add ID to any _all_ subscriptions
192
- def self.add_id(model_name, id)
193
-
194
- end
195
-
196
208
  def self.all
197
209
  $redis_jason.smembers('jason:subscriptions').map { |id| Jason::Subscription.find_by_id(id) }
198
210
  end
@@ -213,6 +225,28 @@ class Jason::Subscription
213
225
  end
214
226
  end
215
227
 
228
+ def remove_id(model_name, id)
229
+ id_changeset = graph_helper.apply_remove_node("#{model_name}:#{id}")
230
+ apply_id_changeset(id_changeset)
231
+ broadcast_id_changeset(id_changeset)
232
+ end
233
+
234
+ def add_id(model_name, id)
235
+ id_changeset = graph_helper.apply_update({
236
+ add: [
237
+ {
238
+ model_names: [model_name],
239
+ instance_ids: [[id]]
240
+ },
241
+ # Add IDs of child models
242
+ load_ids_for_sub_models(model_name, id)
243
+ ]
244
+ })
245
+
246
+ apply_id_changeset(id_changeset)
247
+ broadcast_id_changeset(id_changeset)
248
+ end
249
+
216
250
  def remove_ids(model_name, ids)
217
251
  $redis_jason.srem("jason:subscriptions:#{id}:ids:#{model_name}", ids)
218
252
  ids.each do |instance_id|
@@ -252,8 +286,14 @@ class Jason::Subscription
252
286
  if conditions.blank?
253
287
  $redis_jason.sadd("jason:models:#{model_name}:all:subscriptions", id)
254
288
  all_models -= [model_name]
255
- else
289
+ elsif conditions.keys == ['id']
256
290
  relation = relation.where(conditions)
291
+ else
292
+ $redis_jason.sadd("jason:models:#{model_name}:conditions", {
293
+ 'conditions' => conditions,
294
+ 'subscription_id' => self.id
295
+ }.to_json)
296
+ relation = Jason::ConditionsMatcher.new(relation.klass).apply_conditions(relation, conditions)
257
297
  end
258
298
  else
259
299
  raise "Must supply IDs for sub models" if ids.nil?
@@ -266,7 +306,7 @@ class Jason::Subscription
266
306
 
267
307
  # pluck returns only a 1D array if only 1 arg passed
268
308
  if all_models.size == 1
269
- instance_ids = [instance_ids]
309
+ instance_ids = instance_ids.map { |id| [id] }
270
310
  end
271
311
 
272
312
  return { model_names: all_models, instance_ids: instance_ids }
@@ -275,12 +315,12 @@ class Jason::Subscription
275
315
  # 'posts', [post#1, post#2,...]
276
316
  def set_ids_for_sub_models(model_name = model, ids = nil, enforce: false)
277
317
  edge_set = load_ids_for_sub_models(model_name, ids)
278
-
279
318
  # Build the tree
280
319
  id_changeset = graph_helper.apply_update({
281
320
  add: [edge_set],
282
321
  enforce: enforce
283
322
  })
323
+
284
324
  apply_id_changeset(id_changeset)
285
325
  end
286
326
 
@@ -338,10 +378,6 @@ class Jason::Subscription
338
378
  def remove_consumer(consumer_id)
339
379
  $redis_jason.srem("jason:subscriptions:#{id}:consumers", consumer_id)
340
380
  $redis_jason.hdel("jason:consumers", consumer_id)
341
-
342
- if consumer_count == 0
343
- clear_all_ids
344
- end
345
381
  end
346
382
 
347
383
  def consumer_count
data/lib/jason/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module Jason
2
- VERSION = "0.6.8"
2
+ VERSION = "0.7.0"
3
3
  end
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.6.8
4
+ version: 0.7.0
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-03-08 00:00:00.000000000 Z
11
+ date: 2021-04-02 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rails
@@ -152,6 +152,8 @@ files:
152
152
  - client/lib/JasonProvider.js
153
153
  - client/lib/actionFactory.d.ts
154
154
  - client/lib/actionFactory.js
155
+ - client/lib/addRelations.d.ts
156
+ - client/lib/addRelations.js
155
157
  - client/lib/createActions.d.ts
156
158
  - client/lib/createActions.js
157
159
  - client/lib/createJasonReducers.d.ts
@@ -164,6 +166,8 @@ files:
164
166
  - client/lib/createServerActionQueue.js
165
167
  - client/lib/createServerActionQueue.test.d.ts
166
168
  - client/lib/createServerActionQueue.test.js
169
+ - client/lib/createThenable.d.ts
170
+ - client/lib/createThenable.js
167
171
  - client/lib/createTransportAdapter.d.ts
168
172
  - client/lib/createTransportAdapter.js
169
173
  - client/lib/deepCamelizeKeys.d.ts
@@ -184,6 +188,8 @@ files:
184
188
  - client/lib/transportAdapters/pusherAdapter.js
185
189
  - client/lib/useAct.d.ts
186
190
  - client/lib/useAct.js
191
+ - client/lib/useDraft.d.ts
192
+ - client/lib/useDraft.js
187
193
  - client/lib/useEager.d.ts
188
194
  - client/lib/useEager.js
189
195
  - client/lib/useJason.d.ts
@@ -196,6 +202,7 @@ files:
196
202
  - client/src/JasonContext.ts
197
203
  - client/src/JasonProvider.tsx
198
204
  - client/src/actionFactory.ts
205
+ - client/src/addRelations.ts
199
206
  - client/src/createActions.ts
200
207
  - client/src/createJasonReducers.ts
201
208
  - client/src/createOptDis.ts
@@ -206,12 +213,12 @@ files:
206
213
  - client/src/deepCamelizeKeys.test.ts
207
214
  - client/src/deepCamelizeKeys.ts
208
215
  - client/src/index.ts
209
- - client/src/makeEager.ts
210
216
  - client/src/pruneIdsMiddleware.ts
211
217
  - client/src/restClient.ts
212
218
  - client/src/transportAdapters/actionCableAdapter.ts
213
219
  - client/src/transportAdapters/pusherAdapter.ts
214
220
  - client/src/useAct.ts
221
+ - client/src/useDraft.ts
215
222
  - client/src/useEager.ts
216
223
  - client/src/useJason.test.ts
217
224
  - client/src/useJason.ts
@@ -224,6 +231,8 @@ files:
224
231
  - lib/jason/api_model.rb
225
232
  - lib/jason/broadcaster.rb
226
233
  - lib/jason/channel.rb
234
+ - lib/jason/conditions_matcher.rb
235
+ - lib/jason/consistency_checker.rb
227
236
  - lib/jason/engine.rb
228
237
  - lib/jason/graph_helper.rb
229
238
  - lib/jason/includes_helper.rb