jason-rails 0.4.1 → 0.6.2
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.
- checksums.yaml +4 -4
- data/.gitignore +4 -1
- data/.ruby-version +1 -0
- data/Gemfile.lock +152 -2
- data/README.md +117 -5
- data/app/controllers/jason/api/pusher_controller.rb +15 -0
- data/app/controllers/jason/api_controller.rb +44 -2
- data/client/lib/JasonContext.d.ts +6 -1
- data/client/lib/JasonContext.js +4 -1
- data/client/lib/JasonProvider.d.ts +2 -2
- data/client/lib/JasonProvider.js +5 -124
- data/client/lib/createJasonReducers.js +48 -3
- data/client/lib/createOptDis.js +0 -2
- data/client/lib/createPayloadHandler.d.ts +9 -1
- data/client/lib/createPayloadHandler.js +47 -55
- data/client/lib/createServerActionQueue.d.ts +10 -0
- data/client/lib/createServerActionQueue.js +48 -0
- data/client/lib/createServerActionQueue.test.d.ts +1 -0
- data/client/lib/createServerActionQueue.test.js +37 -0
- data/client/lib/createTransportAdapter.d.ts +5 -0
- data/client/lib/createTransportAdapter.js +20 -0
- data/client/lib/index.d.ts +5 -2
- data/client/lib/index.js +3 -1
- data/client/lib/makeEager.js +2 -2
- data/client/lib/pruneIdsMiddleware.d.ts +2 -0
- data/client/lib/pruneIdsMiddleware.js +24 -0
- data/client/lib/restClient.d.ts +2 -0
- data/client/lib/restClient.js +17 -0
- data/client/lib/transportAdapters/actionCableAdapter.d.ts +5 -0
- data/client/lib/transportAdapters/actionCableAdapter.js +35 -0
- data/client/lib/transportAdapters/pusherAdapter.d.ts +5 -0
- data/client/lib/transportAdapters/pusherAdapter.js +68 -0
- data/client/lib/useJason.d.ts +5 -0
- data/client/lib/useJason.js +94 -0
- data/client/lib/useJason.test.d.ts +1 -0
- data/client/lib/useJason.test.js +85 -0
- data/client/lib/useSub.d.ts +1 -1
- data/client/lib/useSub.js +6 -3
- data/client/package.json +5 -3
- data/client/src/JasonContext.ts +4 -1
- data/client/src/JasonProvider.tsx +5 -123
- data/client/src/createJasonReducers.ts +56 -3
- data/client/src/createOptDis.ts +0 -2
- data/client/src/createPayloadHandler.ts +53 -64
- data/client/src/createServerActionQueue.test.ts +42 -0
- data/client/src/createServerActionQueue.ts +47 -0
- data/client/src/createTransportAdapter.ts +13 -0
- data/client/src/index.ts +3 -1
- data/client/src/makeEager.ts +2 -2
- data/client/src/pruneIdsMiddleware.ts +24 -0
- data/client/src/restClient.ts +14 -0
- data/client/src/transportAdapters/actionCableAdapter.ts +38 -0
- data/client/src/transportAdapters/pusherAdapter.ts +72 -0
- data/client/src/useJason.test.ts +87 -0
- data/client/src/useJason.ts +110 -0
- data/client/src/useSub.ts +6 -3
- data/client/yarn.lock +71 -3
- data/config/routes.rb +5 -1
- data/jason-rails.gemspec +4 -0
- data/lib/jason.rb +61 -1
- data/lib/jason/api_model.rb +2 -12
- data/lib/jason/broadcaster.rb +19 -0
- data/lib/jason/channel.rb +50 -21
- data/lib/jason/graph_helper.rb +165 -0
- data/lib/jason/includes_helper.rb +108 -0
- data/lib/jason/lua_generator.rb +71 -0
- data/lib/jason/publisher.rb +82 -37
- data/lib/jason/publisher_old.rb +112 -0
- data/lib/jason/subscription.rb +349 -97
- data/lib/jason/subscription_old.rb +171 -0
- data/lib/jason/version.rb +1 -1
- metadata +80 -3
data/client/src/useSub.ts
CHANGED
@@ -1,10 +1,13 @@
|
|
1
1
|
import JasonContext from './JasonContext'
|
2
2
|
import { useContext, useEffect } from 'react'
|
3
3
|
|
4
|
-
export default function useSub(config) {
|
4
|
+
export default function useSub(config, options = {}) {
|
5
|
+
// useEffect uses strict equality
|
6
|
+
const configJson = JSON.stringify(config)
|
5
7
|
const subscribe = useContext(JasonContext).subscribe
|
6
8
|
|
7
9
|
useEffect(() => {
|
8
|
-
|
9
|
-
|
10
|
+
// @ts-ignore
|
11
|
+
return subscribe(config, options).remove
|
12
|
+
}, [configJson])
|
10
13
|
}
|
data/client/yarn.lock
CHANGED
@@ -813,7 +813,7 @@
|
|
813
813
|
"@babel/helper-validator-option" "^7.12.1"
|
814
814
|
"@babel/plugin-transform-typescript" "^7.12.1"
|
815
815
|
|
816
|
-
"@babel/runtime@^7.12.1", "@babel/runtime@^7.8.4":
|
816
|
+
"@babel/runtime@^7.12.1", "@babel/runtime@^7.12.5", "@babel/runtime@^7.8.4":
|
817
817
|
version "7.12.5"
|
818
818
|
resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.12.5.tgz#410e7e487441e1b360c29be715d870d9b985882e"
|
819
819
|
integrity sha512-plcc+hbExy3McchJCEQG3knOsuh3HH+Prx1P6cLIkET/0dLuQDEnrT+s27Axgc9bqfsmNUNHfscgMUdBpC9xfg==
|
@@ -1082,6 +1082,18 @@
|
|
1082
1082
|
dependencies:
|
1083
1083
|
"@sinonjs/commons" "^1.7.0"
|
1084
1084
|
|
1085
|
+
"@testing-library/react-hooks@^5.0.3":
|
1086
|
+
version "5.0.3"
|
1087
|
+
resolved "https://registry.yarnpkg.com/@testing-library/react-hooks/-/react-hooks-5.0.3.tgz#dd0d2048817b013b266d35ca45e3ea48a19fd87e"
|
1088
|
+
integrity sha512-UrnnRc5II7LMH14xsYNm/WRch/67cBafmrSQcyFh0v+UUmSf1uzfB7zn5jQXSettGwOSxJwdQUN7PgkT0w22Lg==
|
1089
|
+
dependencies:
|
1090
|
+
"@babel/runtime" "^7.12.5"
|
1091
|
+
"@types/react" ">=16.9.0"
|
1092
|
+
"@types/react-dom" ">=16.9.0"
|
1093
|
+
"@types/react-test-renderer" ">=16.9.0"
|
1094
|
+
filter-console "^0.1.1"
|
1095
|
+
react-error-boundary "^3.1.0"
|
1096
|
+
|
1085
1097
|
"@types/babel__core@^7.0.0", "@types/babel__core@^7.1.7":
|
1086
1098
|
version "7.1.12"
|
1087
1099
|
resolved "https://registry.yarnpkg.com/@types/babel__core/-/babel__core-7.1.12.tgz#4d8e9e51eb265552a7e4f1ff2219ab6133bdfb2d"
|
@@ -1164,6 +1176,33 @@
|
|
1164
1176
|
resolved "https://registry.yarnpkg.com/@types/prettier/-/prettier-2.1.5.tgz#b6ab3bba29e16b821d84e09ecfaded462b816b00"
|
1165
1177
|
integrity sha512-UEyp8LwZ4Dg30kVU2Q3amHHyTn1jEdhCIE59ANed76GaT1Vp76DD3ZWSAxgCrw6wJ0TqeoBpqmfUHiUDPs//HQ==
|
1166
1178
|
|
1179
|
+
"@types/prop-types@*":
|
1180
|
+
version "15.7.3"
|
1181
|
+
resolved "https://registry.yarnpkg.com/@types/prop-types/-/prop-types-15.7.3.tgz#2ab0d5da2e5815f94b0b9d4b95d1e5f243ab2ca7"
|
1182
|
+
integrity sha512-KfRL3PuHmqQLOG+2tGpRO26Ctg+Cq1E01D2DMriKEATHgWLfeNDmq9e29Q9WIky0dQ3NPkd1mzYH8Lm936Z9qw==
|
1183
|
+
|
1184
|
+
"@types/react-dom@>=16.9.0":
|
1185
|
+
version "17.0.0"
|
1186
|
+
resolved "https://registry.yarnpkg.com/@types/react-dom/-/react-dom-17.0.0.tgz#b3b691eb956c4b3401777ee67b900cb28415d95a"
|
1187
|
+
integrity sha512-lUqY7OlkF/RbNtD5nIq7ot8NquXrdFrjSOR6+w9a9RFQevGi1oZO1dcJbXMeONAPKtZ2UrZOEJ5UOCVsxbLk/g==
|
1188
|
+
dependencies:
|
1189
|
+
"@types/react" "*"
|
1190
|
+
|
1191
|
+
"@types/react-test-renderer@>=16.9.0":
|
1192
|
+
version "17.0.0"
|
1193
|
+
resolved "https://registry.yarnpkg.com/@types/react-test-renderer/-/react-test-renderer-17.0.0.tgz#9be47b375eeb906fced37049e67284a438d56620"
|
1194
|
+
integrity sha512-nvw+F81OmyzpyIE1S0xWpLonLUZCMewslPuA8BtjSKc5XEbn8zEQBXS7KuOLHTNnSOEM2Pum50gHOoZ62tqTRg==
|
1195
|
+
dependencies:
|
1196
|
+
"@types/react" "*"
|
1197
|
+
|
1198
|
+
"@types/react@*", "@types/react@>=16.9.0":
|
1199
|
+
version "17.0.0"
|
1200
|
+
resolved "https://registry.yarnpkg.com/@types/react/-/react-17.0.0.tgz#5af3eb7fad2807092f0046a1302b7823e27919b8"
|
1201
|
+
integrity sha512-aj/L7RIMsRlWML3YB6KZiXB3fV2t41+5RBGYF8z+tAKU43Px8C3cYUZsDvf1/+Bm4FK21QWBrDutu8ZJ/70qOw==
|
1202
|
+
dependencies:
|
1203
|
+
"@types/prop-types" "*"
|
1204
|
+
csstype "^3.0.2"
|
1205
|
+
|
1167
1206
|
"@types/stack-utils@^2.0.0":
|
1168
1207
|
version "2.0.0"
|
1169
1208
|
resolved "https://registry.yarnpkg.com/@types/stack-utils/-/stack-utils-2.0.0.tgz#7036640b4e21cc2f259ae826ce843d277dad8cff"
|
@@ -1742,6 +1781,11 @@ cssstyle@^2.2.0:
|
|
1742
1781
|
dependencies:
|
1743
1782
|
cssom "~0.3.6"
|
1744
1783
|
|
1784
|
+
csstype@^3.0.2:
|
1785
|
+
version "3.0.6"
|
1786
|
+
resolved "https://registry.yarnpkg.com/csstype/-/csstype-3.0.6.tgz#865d0b5833d7d8d40f4e5b8a6d76aea3de4725ef"
|
1787
|
+
integrity sha512-+ZAmfyWMT7TiIlzdqJgjMb7S4f1beorDbWbsocyK4RaiqA5RTX3K14bnBWmmA9QEM0gRdsjyyrEmcyga8Zsxmw==
|
1788
|
+
|
1745
1789
|
dashdash@^1.12.0:
|
1746
1790
|
version "1.14.1"
|
1747
1791
|
resolved "https://registry.yarnpkg.com/dashdash/-/dashdash-1.14.1.tgz#853cfa0f7cbe2fed5de20326b8dd581035f6e2f0"
|
@@ -2081,6 +2125,11 @@ fill-range@^7.0.1:
|
|
2081
2125
|
dependencies:
|
2082
2126
|
to-regex-range "^5.0.1"
|
2083
2127
|
|
2128
|
+
filter-console@^0.1.1:
|
2129
|
+
version "0.1.1"
|
2130
|
+
resolved "https://registry.yarnpkg.com/filter-console/-/filter-console-0.1.1.tgz#6242be28982bba7415bcc6db74a79f4a294fa67c"
|
2131
|
+
integrity sha512-zrXoV1Uaz52DqPs+qEwNJWJFAWZpYJ47UNmpN9q4j+/EYsz85uV0DC9k8tRND5kYmoVzL0W+Y75q4Rg8sRJCdg==
|
2132
|
+
|
2084
2133
|
find-up@^4.0.0, find-up@^4.1.0:
|
2085
2134
|
version "4.1.0"
|
2086
2135
|
resolved "https://registry.yarnpkg.com/find-up/-/find-up-4.1.0.tgz#97afe7d6cdc0bc5928584b7c8d7b16e8a9aa5d19"
|
@@ -3596,12 +3645,19 @@ punycode@^2.1.0, punycode@^2.1.1:
|
|
3596
3645
|
resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.1.1.tgz#b58b010ac40c22c5657616c8d2c2c02c7bf479ec"
|
3597
3646
|
integrity sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==
|
3598
3647
|
|
3648
|
+
pusher-js@^7.0.3:
|
3649
|
+
version "7.0.3"
|
3650
|
+
resolved "https://registry.yarnpkg.com/pusher-js/-/pusher-js-7.0.3.tgz#f81c78cdf2ad32f546caa7532ec7f9081ef00b8d"
|
3651
|
+
integrity sha512-HIfCvt00CAqgO4W0BrdpPsDcAwy51rB6DN0VMC+JeVRRbo8mn3XTeUeIFjmmlRLZLX8rPhUtLRo7vPag6b8GCw==
|
3652
|
+
dependencies:
|
3653
|
+
tweetnacl "^1.0.3"
|
3654
|
+
|
3599
3655
|
qs@~6.5.2:
|
3600
3656
|
version "6.5.2"
|
3601
3657
|
resolved "https://registry.yarnpkg.com/qs/-/qs-6.5.2.tgz#cb3ae806e8740444584ef154ce8ee98d403f3e36"
|
3602
3658
|
integrity sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA==
|
3603
3659
|
|
3604
|
-
react-dom@^16.
|
3660
|
+
react-dom@^16.9.x:
|
3605
3661
|
version "16.14.0"
|
3606
3662
|
resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-16.14.0.tgz#7ad838ec29a777fb3c75c3a190f661cf92ab8b89"
|
3607
3663
|
integrity sha512-1gCeQXDLoIqMgqD3IO2Ah9bnf0w9kzhwN5q4FGnHZ67hBm9yePzB5JJAIQCc8x3pFnNlwFq4RidZggNAAkzWWw==
|
@@ -3611,6 +3667,13 @@ react-dom@^16.8.3:
|
|
3611
3667
|
prop-types "^15.6.2"
|
3612
3668
|
scheduler "^0.19.1"
|
3613
3669
|
|
3670
|
+
react-error-boundary@^3.1.0:
|
3671
|
+
version "3.1.0"
|
3672
|
+
resolved "https://registry.yarnpkg.com/react-error-boundary/-/react-error-boundary-3.1.0.tgz#9487443df2f9ba1db90d8ab52351814907ea4af3"
|
3673
|
+
integrity sha512-lmPrdi5SLRJR+AeJkqdkGlW/CRkAUvZnETahK58J4xb5wpbfDngasEGu+w0T1iXEhVrYBJZeW+c4V1hILCnMWQ==
|
3674
|
+
dependencies:
|
3675
|
+
"@babel/runtime" "^7.12.5"
|
3676
|
+
|
3614
3677
|
react-is@^16.13.1, react-is@^16.7.0, react-is@^16.8.1:
|
3615
3678
|
version "16.13.1"
|
3616
3679
|
resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.13.1.tgz#789729a4dc36de2999dc156dd6c1d9c18cea56a4"
|
@@ -3632,7 +3695,7 @@ react-redux@^7.2.2:
|
|
3632
3695
|
prop-types "^15.7.2"
|
3633
3696
|
react-is "^16.13.1"
|
3634
3697
|
|
3635
|
-
react@^16.
|
3698
|
+
react@^16.9.x:
|
3636
3699
|
version "16.14.0"
|
3637
3700
|
resolved "https://registry.yarnpkg.com/react/-/react-16.14.0.tgz#94d776ddd0aaa37da3eda8fc5b6b18a4c9a3114d"
|
3638
3701
|
integrity sha512-0X2CImDkJGApiAlcf0ODKIneSwBPhqJawOa5wCtKbu7ZECrmS26NvtSILynQ66cgkT/RJ4LidJOc3bUESwmU8g==
|
@@ -4302,6 +4365,11 @@ tweetnacl@^0.14.3, tweetnacl@~0.14.0:
|
|
4302
4365
|
resolved "https://registry.yarnpkg.com/tweetnacl/-/tweetnacl-0.14.5.tgz#5ae68177f192d4456269d108afa93ff8743f4f64"
|
4303
4366
|
integrity sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q=
|
4304
4367
|
|
4368
|
+
tweetnacl@^1.0.3:
|
4369
|
+
version "1.0.3"
|
4370
|
+
resolved "https://registry.yarnpkg.com/tweetnacl/-/tweetnacl-1.0.3.tgz#ac0af71680458d8a6378d0d0d050ab1407d35596"
|
4371
|
+
integrity sha512-6rt+RN7aOi1nGMyC4Xa5DdYiukl2UWCbcJft7YhxReBGQD7OAM8Pbxw6YMo4r2diNEA8FEmu32YOn9rhaiE5yw==
|
4372
|
+
|
4305
4373
|
type-check@~0.3.2:
|
4306
4374
|
version "0.3.2"
|
4307
4375
|
resolved "https://registry.yarnpkg.com/type-check/-/type-check-0.3.2.tgz#5884cab512cf1d355e3fb784f30804b2b520db72"
|
data/config/routes.rb
CHANGED
@@ -1,4 +1,8 @@
|
|
1
1
|
Jason::Engine.routes.draw do
|
2
|
-
get '/api/
|
2
|
+
get '/api/config', to: 'api#configuration'
|
3
3
|
post '/api/action', to: 'api#action'
|
4
|
+
post '/api/create_subscription', to: 'api#create_subscription'
|
5
|
+
post '/api/remove_subscription', to: 'api#remove_subscription'
|
6
|
+
post '/api/get_payload', to: 'api#get_payload'
|
7
|
+
post '/api/pusher/auth', to: 'api/pusher#auth'
|
4
8
|
end
|
data/jason-rails.gemspec
CHANGED
@@ -27,4 +27,8 @@ Gem::Specification.new do |spec|
|
|
27
27
|
spec.add_dependency "connection_pool", ">= 2.2.3"
|
28
28
|
spec.add_dependency "redis", ">= 4"
|
29
29
|
spec.add_dependency "jsondiff"
|
30
|
+
|
31
|
+
spec.add_development_dependency "rspec-rails"
|
32
|
+
spec.add_development_dependency "sqlite3"
|
33
|
+
spec.add_development_dependency "pry"
|
30
34
|
end
|
data/lib/jason.rb
CHANGED
@@ -7,10 +7,70 @@ require 'jason/api_model'
|
|
7
7
|
require 'jason/channel'
|
8
8
|
require 'jason/publisher'
|
9
9
|
require 'jason/subscription'
|
10
|
+
require 'jason/broadcaster'
|
10
11
|
require 'jason/engine'
|
12
|
+
require 'jason/lua_generator'
|
13
|
+
require 'jason/includes_helper'
|
14
|
+
require 'jason/graph_helper'
|
11
15
|
|
12
16
|
module Jason
|
13
17
|
class Error < StandardError; end
|
14
18
|
|
15
|
-
|
19
|
+
self.mattr_accessor :schema
|
20
|
+
self.mattr_accessor :transport_service
|
21
|
+
self.mattr_accessor :redis
|
22
|
+
self.mattr_accessor :pusher
|
23
|
+
self.mattr_accessor :pusher_key
|
24
|
+
self.mattr_accessor :pusher_region
|
25
|
+
self.mattr_accessor :pusher_channel_prefix
|
26
|
+
self.mattr_accessor :authorization_service
|
27
|
+
|
28
|
+
self.schema = {}
|
29
|
+
self.transport_service = :action_cable
|
30
|
+
self.pusher_region = 'eu'
|
31
|
+
self.pusher_channel_prefix = 'jason'
|
32
|
+
|
33
|
+
def self.init
|
34
|
+
# Check if the schema has changed since last time app was started. If so, do some work to ensure cache contains the correct data
|
35
|
+
got_lock = $redis_jason.set('jason:schema:lock', nx: true, ex: 3600) # Basic lock mechanism for multi-process environments
|
36
|
+
return if !got_lock
|
37
|
+
|
38
|
+
previous_schema = JSON.parse($redis_jason.get('jason:last_schema') || '{}')
|
39
|
+
current_schema = Jason.schema.deep_stringify_keys.deep_transform_values { |v| v.is_a?(Symbol) ? v.to_s : v }
|
40
|
+
pp current_schema
|
41
|
+
current_schema.each do |model, config|
|
42
|
+
if config != previous_schema[model]
|
43
|
+
puts "Config changed for #{model}"
|
44
|
+
puts "Old config was #{previous_schema[model]}"
|
45
|
+
puts "New config is #{config}"
|
46
|
+
puts "Rebuilding cache for #{model}"
|
47
|
+
model.classify.constantize.cache_all
|
48
|
+
puts "Done"
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
$redis_jason.set('jason:last_schema', current_schema.to_json)
|
53
|
+
ensure
|
54
|
+
$redis_jason.del('jason:schema:lock')
|
55
|
+
|
56
|
+
previous_config = 'test'
|
57
|
+
end
|
58
|
+
|
59
|
+
|
60
|
+
# this function maps the vars from your app into your engine
|
61
|
+
def self.setup(&block)
|
62
|
+
yield self
|
63
|
+
|
64
|
+
$redis_jason = self.redis || ::ConnectionPool::Wrapper.new(size: 5, timeout: 3) { ::Redis.new(url: ENV['REDIS_URL']) }
|
65
|
+
|
66
|
+
if ![:action_cable, :pusher].include?(self.transport_service)
|
67
|
+
raise "Unknown transport service '#{self.transport_service}' specified"
|
68
|
+
end
|
69
|
+
|
70
|
+
if self.transport_service == :pusher && self.pusher.blank?
|
71
|
+
raise "Pusher specified as transport service but no Pusher client provided. Please configure with config.pusher = Pusher::Client.new(...)"
|
72
|
+
end
|
73
|
+
|
74
|
+
init
|
75
|
+
end
|
16
76
|
end
|
data/lib/jason/api_model.rb
CHANGED
@@ -8,7 +8,7 @@ class Jason::ApiModel
|
|
8
8
|
|
9
9
|
def initialize(name)
|
10
10
|
@name = name
|
11
|
-
@model = OpenStruct.new(
|
11
|
+
@model = OpenStruct.new(Jason.schema[name.to_sym])
|
12
12
|
end
|
13
13
|
|
14
14
|
def allowed_params
|
@@ -19,10 +19,6 @@ class Jason::ApiModel
|
|
19
19
|
model.allowed_object_params || []
|
20
20
|
end
|
21
21
|
|
22
|
-
def include_models
|
23
|
-
model.include_models || []
|
24
|
-
end
|
25
|
-
|
26
22
|
def include_methods
|
27
23
|
model.include_methods || []
|
28
24
|
end
|
@@ -52,12 +48,6 @@ class Jason::ApiModel
|
|
52
48
|
end
|
53
49
|
|
54
50
|
def as_json_config
|
55
|
-
|
56
|
-
reflection = name.classify.constantize.reflect_on_association(assoc.to_sym)
|
57
|
-
api_model = Jason::ApiModel.new(reflection.klass.name.underscore)
|
58
|
-
{ assoc => { only: api_model.subscribed_fields, methods: api_model.include_methods } }
|
59
|
-
end
|
60
|
-
|
61
|
-
{ only: subscribed_fields, include: include_configs, methods: include_methods }
|
51
|
+
{ only: subscribed_fields, methods: include_methods }
|
62
52
|
end
|
63
53
|
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
class Jason::Broadcaster
|
2
|
+
attr_reader :channel
|
3
|
+
|
4
|
+
def initialize(channel)
|
5
|
+
@channel = channel
|
6
|
+
end
|
7
|
+
|
8
|
+
def pusher_channel_name
|
9
|
+
"private-#{Jason.pusher_channel_prefix}-#{channel}"
|
10
|
+
end
|
11
|
+
|
12
|
+
def broadcast(message)
|
13
|
+
if Jason.transport_service == :action_cable
|
14
|
+
ActionCable.server.broadcast(channel, message)
|
15
|
+
elsif Jason.transport_service == :pusher
|
16
|
+
Jason.pusher.trigger(pusher_channel_name, 'changed', message)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
data/lib/jason/channel.rb
CHANGED
@@ -1,32 +1,27 @@
|
|
1
1
|
class Jason::Channel < ActionCable::Channel::Base
|
2
2
|
attr_accessor :subscriptions
|
3
3
|
|
4
|
+
def subscribe
|
5
|
+
stream_from 'jason'
|
6
|
+
end
|
7
|
+
|
4
8
|
def receive(message)
|
5
|
-
|
9
|
+
handle_message(message)
|
10
|
+
end
|
11
|
+
|
12
|
+
private
|
13
|
+
|
14
|
+
def handle_message(message)
|
15
|
+
pp message['createSubscription']
|
16
|
+
@subscriptions ||= []
|
6
17
|
|
7
18
|
begin # ActionCable swallows errors in this message - ensure they're output to logs.
|
8
19
|
if (config = message['createSubscription'])
|
9
|
-
|
10
|
-
subscriptions.push(subscription)
|
11
|
-
subscription.add_consumer(identifier)
|
12
|
-
config.keys.each do |model|
|
13
|
-
transmit(subscription.get(model.to_s.underscore))
|
14
|
-
end
|
15
|
-
stream_from subscription.channel
|
20
|
+
create_subscription(config['model'], config['conditions'], config['includes'])
|
16
21
|
elsif (config = message['removeSubscription'])
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
# Rails for some reason removed stop_stream_from, so we need to stop all and then restart the other streams
|
22
|
-
# stop_all_streams
|
23
|
-
# subscriptions.each do |s|
|
24
|
-
# stream_from s.channel
|
25
|
-
# end
|
26
|
-
elsif (data = message['getPayload'])
|
27
|
-
config = data['config']
|
28
|
-
model = data['model']
|
29
|
-
Jason::Subscription.new(config: config).get(model.to_s.underscore)
|
22
|
+
remove_subscription(config)
|
23
|
+
elsif (config = message['getPayload'])
|
24
|
+
get_payload(config, message['forceRefresh'])
|
30
25
|
end
|
31
26
|
rescue => e
|
32
27
|
puts e.message
|
@@ -34,4 +29,38 @@ class Jason::Channel < ActionCable::Channel::Base
|
|
34
29
|
raise e
|
35
30
|
end
|
36
31
|
end
|
32
|
+
|
33
|
+
def create_subscription(model, conditions, includes)
|
34
|
+
subscription = Jason::Subscription.upsert_by_config(model, conditions: conditions || {}, includes: includes || nil)
|
35
|
+
|
36
|
+
return if !subscription.user_can_access?(current_user)
|
37
|
+
stream_from subscription.channel
|
38
|
+
|
39
|
+
subscriptions.push(subscription)
|
40
|
+
subscription.add_consumer(identifier)
|
41
|
+
subscription.get.each do |payload|
|
42
|
+
pp payload
|
43
|
+
transmit(payload) if payload.present?
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
def remove_subscription(config)
|
48
|
+
subscription = Jason::Subscription.upsert_by_config(config['model'], conditions: config['conditions'], includes: config['includes'])
|
49
|
+
subscriptions.reject! { |s| s.id == subscription.id }
|
50
|
+
subscription.remove_consumer(identifier)
|
51
|
+
|
52
|
+
# TODO Stop streams
|
53
|
+
end
|
54
|
+
|
55
|
+
def get_payload(config, force_refresh = false)
|
56
|
+
subscription = Jason::Subscription.upsert_by_config(config['model'], conditions: config['conditions'], includes: config['includes'])
|
57
|
+
|
58
|
+
return if !subscription.user_can_access?(current_user)
|
59
|
+
if force_refresh
|
60
|
+
subscription.set_ids_for_sub_models
|
61
|
+
end
|
62
|
+
subscription.get.each do |model_name, payload|
|
63
|
+
transmit(payload) if payload.present?
|
64
|
+
end
|
65
|
+
end
|
37
66
|
end
|
@@ -0,0 +1,165 @@
|
|
1
|
+
class Jason::GraphHelper
|
2
|
+
attr_reader :id, :includes_helper
|
3
|
+
|
4
|
+
def initialize(id, includes_helper)
|
5
|
+
@id = id
|
6
|
+
@includes_helper = includes_helper
|
7
|
+
end
|
8
|
+
|
9
|
+
def add_edge(parent_model, parent_id, child_model, child_id)
|
10
|
+
edge = "#{parent_model}:#{parent_id}/#{child_model}:#{child_id}"
|
11
|
+
$redis_jason.sadd("jason:subscriptions:#{id}:graph", edge)
|
12
|
+
end
|
13
|
+
|
14
|
+
def remove_edge(parent_model, parent_id, child_model, child_id)
|
15
|
+
edge = "#{parent_model}:#{parent_id}/#{child_model}:#{child_id}"
|
16
|
+
$redis_jason.srem("jason:subscriptions:#{id}:graph", edge)
|
17
|
+
end
|
18
|
+
|
19
|
+
def add_edges(all_models, instance_ids)
|
20
|
+
edges = build_edges(all_models, instance_ids)
|
21
|
+
$redis_jason.sadd("jason:subscriptions:#{id}:graph", edges)
|
22
|
+
end
|
23
|
+
|
24
|
+
def remove_edges(all_models, instance_ids)
|
25
|
+
edges = build_edges(all_models, instance_ids)
|
26
|
+
$redis_jason.srem("jason:subscriptions:#{id}:graph", edges)
|
27
|
+
end
|
28
|
+
|
29
|
+
def apply_remove_node(node)
|
30
|
+
edges = $redis_jason.smembers("jason:subscriptions:#{id}:graph")
|
31
|
+
edges = find_edges_with_node(edges, node)
|
32
|
+
diff_edges_from_graph(remove_edges: edges)
|
33
|
+
end
|
34
|
+
|
35
|
+
# Add and remove edges, return graph before and after
|
36
|
+
def apply_update(add: nil, remove: nil)
|
37
|
+
add_edges = []
|
38
|
+
remove_edges = []
|
39
|
+
|
40
|
+
if add.present?
|
41
|
+
add.each do |edge_set|
|
42
|
+
add_edges += build_edges(edge_set[:model_names], edge_set[:instance_ids])
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
if remove.present?
|
47
|
+
remove.each do |edge_set|
|
48
|
+
remove_edges += build_edges(edge_set[:model_names], edge_set[:instance_ids], include_root: false)
|
49
|
+
end
|
50
|
+
end
|
51
|
+
diff_edges_from_graph(add_edges: add_edges, remove_edges: remove_edges)
|
52
|
+
end
|
53
|
+
|
54
|
+
def diff_edges_from_graph(add_edges: [], remove_edges: [])
|
55
|
+
old_edges, new_edges = Jason::LuaGenerator.new.update_set_with_diff("jason:subscriptions:#{id}:graph", add_edges.flatten, remove_edges.flatten)
|
56
|
+
|
57
|
+
old_graph = build_graph_from_edges(old_edges)
|
58
|
+
new_graph = build_graph_from_edges(new_edges)
|
59
|
+
|
60
|
+
old_nodes = (old_graph.values + old_graph.keys).flatten.uniq - ['root']
|
61
|
+
new_nodes = (new_graph.values + new_graph.keys).flatten.uniq - ['root']
|
62
|
+
orphan_nodes = find_orphans_in_graph(new_graph)
|
63
|
+
|
64
|
+
added_nodes = new_nodes - old_nodes - orphan_nodes
|
65
|
+
removed_nodes = old_nodes - new_nodes + orphan_nodes
|
66
|
+
|
67
|
+
orphaned_edges = orphan_nodes.map do |node|
|
68
|
+
find_edges_with_node(new_edges, node)
|
69
|
+
end.flatten
|
70
|
+
|
71
|
+
if orphaned_edges.present?
|
72
|
+
$redis_jason.srem("jason:subscriptions:#{id}:graph", orphaned_edges)
|
73
|
+
end
|
74
|
+
|
75
|
+
ids_to_add = {}
|
76
|
+
ids_to_remove = {}
|
77
|
+
|
78
|
+
added_nodes.each do |node|
|
79
|
+
model_name, instance_id = node.split(':')
|
80
|
+
ids_to_add[model_name] ||= []
|
81
|
+
ids_to_add[model_name].push(instance_id)
|
82
|
+
end
|
83
|
+
|
84
|
+
removed_nodes.each do |node|
|
85
|
+
model_name, instance_id = node.split(':')
|
86
|
+
ids_to_remove[model_name] ||= []
|
87
|
+
ids_to_remove[model_name].push(instance_id)
|
88
|
+
end
|
89
|
+
|
90
|
+
{ ids_to_remove: ids_to_remove, ids_to_add: ids_to_add }
|
91
|
+
end
|
92
|
+
|
93
|
+
def find_edges_with_node(edges, node)
|
94
|
+
edges.select do |edge|
|
95
|
+
parent, child = edge.split('/')
|
96
|
+
parent == node || child == node
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
def find_orphans
|
101
|
+
edges = $redis_jason.smembers("jason:subscriptions:#{id}:graph")
|
102
|
+
graph = build_graph_from_edges(edges)
|
103
|
+
find_orphans_in_graph(graph)
|
104
|
+
end
|
105
|
+
|
106
|
+
def find_orphans_in_graph(graph)
|
107
|
+
reachable_nodes = get_reachable_nodes(graph)
|
108
|
+
all_nodes = (graph.values + graph.keys).flatten.uniq - ['root']
|
109
|
+
all_nodes - reachable_nodes
|
110
|
+
end
|
111
|
+
|
112
|
+
def get_reachable_nodes(graph, parent = 'root')
|
113
|
+
reached_nodes = graph[parent] || []
|
114
|
+
reached_nodes.each do |child|
|
115
|
+
reached_nodes += get_reachable_nodes(graph, child)
|
116
|
+
end
|
117
|
+
reached_nodes
|
118
|
+
end
|
119
|
+
|
120
|
+
def build_graph_from_edges(edges)
|
121
|
+
graph = {}
|
122
|
+
edges.each do |edge|
|
123
|
+
parent, child = edge.split('/')
|
124
|
+
graph[parent] ||= []
|
125
|
+
graph[parent].push(child)
|
126
|
+
end
|
127
|
+
graph
|
128
|
+
end
|
129
|
+
|
130
|
+
private
|
131
|
+
|
132
|
+
def build_edges(all_models, instance_ids, include_root: true)
|
133
|
+
# Build the tree
|
134
|
+
# Find parent -> child model relationships
|
135
|
+
edges = []
|
136
|
+
|
137
|
+
all_models.each_with_index do |parent_model, parent_idx|
|
138
|
+
all_models.each_with_index do |child_model, child_idx|
|
139
|
+
next if parent_model == child_model
|
140
|
+
next if !includes_helper.in_sub(parent_model, child_model)
|
141
|
+
|
142
|
+
pairs = instance_ids.map { |row| [row[parent_idx], row[child_idx]] }
|
143
|
+
.uniq
|
144
|
+
.reject{ |pair| pair[0].blank? || pair[1].blank? }
|
145
|
+
|
146
|
+
edges += pairs.map.each do |pair|
|
147
|
+
"#{parent_model}:#{pair[0]}/#{child_model}:#{pair[1]}"
|
148
|
+
end
|
149
|
+
end
|
150
|
+
end
|
151
|
+
|
152
|
+
root_model = includes_helper.root_model
|
153
|
+
|
154
|
+
if include_root && all_models.include?(root_model)
|
155
|
+
root_idx = all_models.find_index(root_model)
|
156
|
+
root_ids = instance_ids.map { |row| row[root_idx] }.uniq.compact
|
157
|
+
|
158
|
+
edges += root_ids.map do |id|
|
159
|
+
"root/#{root_model}:#{id}"
|
160
|
+
end
|
161
|
+
end
|
162
|
+
|
163
|
+
edges
|
164
|
+
end
|
165
|
+
end
|