jason-rails 0.6.8 → 0.7.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 (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