loco-rails 4.1.1 → 6.1.0

Sign up to get free protection for your applications and to get access to all the features.
data/lib/loco/sender.rb CHANGED
@@ -2,43 +2,60 @@
2
2
 
3
3
  module Loco
4
4
  class Sender
5
- def initialize(recipient, data = {})
6
- @recipients = [*recipient]
7
- @data = data
5
+ class << self
6
+ def call(recipient_s, payload = {})
7
+ payload = with_idempotency_key(payload)
8
+ recipients = recipient_s.is_a?(Array) ? recipient_s : [recipient_s]
9
+ new.(recipients, payload)
10
+ end
11
+
12
+ private
13
+
14
+ def with_idempotency_key(payload)
15
+ hash = payload.clone
16
+ hash[:loco] ||= {}
17
+ hash[:loco][:idempotency_key] ||= hash[:idempotency_key] || SecureRandom.hex
18
+ hash.delete(:idempotency_key)
19
+ hash
20
+ end
21
+ end
22
+
23
+ def initialize
24
+ @uuids = []
8
25
  end
9
26
 
10
- def emit
11
- uuids.each do |uuid|
12
- NotificationCenterChannel.broadcast_to(uuid, payload)
27
+ def call(recipients, payload)
28
+ recipients.each do |recipient|
29
+ case recipient
30
+ when String then broadcast_to(recipient, payload)
31
+ when Hash then process_hash(recipient, payload)
32
+ else find_and_broadcast_to(recipient, payload)
33
+ end
13
34
  end
14
35
  payload[:loco][:idempotency_key]
15
36
  end
16
37
 
17
38
  private
18
39
 
19
- def uuids
20
- @recipients.map do |r|
21
- if r.is_a? String
22
- r
23
- elsif r.is_a? Hub
24
- recipients_from_hub r
25
- else
26
- WsConnectionManager.new(r).connected_uuids
27
- end
28
- end.flatten.uniq
40
+ def process_hash(recipient, payload)
41
+ if recipient.key?('token')
42
+ find_and_broadcast_to(recipient['token'], payload)
43
+ elsif recipient.key?('class')
44
+ find_and_broadcast_to(recipient['class'].constantize, payload)
45
+ end
29
46
  end
30
47
 
31
- def recipients_from_hub(hub)
32
- hub.raw_members.map do |m|
33
- WsConnectionManager.new(m).connected_uuids
34
- end.flatten.uniq
48
+ def find_and_broadcast_to(recipient, payload)
49
+ WsConnectionFinder.(recipient) do |uuid|
50
+ broadcast_to(uuid, payload)
51
+ end
35
52
  end
36
53
 
37
- def payload
38
- @data[:loco] ||= {}
39
- @data[:loco][:idempotency_key] ||= @data[:idempotency_key] || SecureRandom.hex
40
- @data.delete(:idempotency_key)
41
- @data
54
+ def broadcast_to(uuid, payload)
55
+ return if @uuids.include?(uuid)
56
+
57
+ @uuids << uuid
58
+ NotificationCenterChannel.broadcast_to(uuid, payload)
42
59
  end
43
60
  end
44
61
  end
data/lib/loco/version.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Loco
4
- VERSION = '4.1.1'
4
+ VERSION = '6.1.0'
5
5
  end
@@ -0,0 +1,16 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Loco
4
+ module WsConnectionChecker
5
+ module_function
6
+
7
+ def call(identifier, skip: nil)
8
+ WsConnectionStorage.current.members(identifier).each do |uuid|
9
+ next if uuid == skip
10
+ next if WsConnectionStorage.current.get(uuid) == 'ok'
11
+
12
+ WsConnectionManager.new(identifier, identifier: true).del(uuid, skip_checker: true)
13
+ end
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,30 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Loco
4
+ class WsConnectionFinder
5
+ class << self
6
+ def call(resources, &block)
7
+ storage = WsConnectionStorage.current
8
+ resources = [resources] unless resources.is_a?(Array)
9
+ resources.each do |resource|
10
+ case resource
11
+ when :all then storage.scan(all: true, &block)
12
+ when Hub then search_the_hub(resource, &block)
13
+ when Class
14
+ storage.scan(match: "#{WsConnectionIdentifier.(resource)}:*", &block)
15
+ else
16
+ storage.members(WsConnectionIdentifier.(resource)).each(&block)
17
+ end
18
+ end
19
+ end
20
+
21
+ private
22
+
23
+ def search_the_hub(resource, &block)
24
+ WsConnectionStorage.current.members(resource.full_name).map do |serialized|
25
+ WsConnectionStorage.current.members(serialized).each(&block)
26
+ end
27
+ end
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Loco
4
+ module WsConnectionIdentifier
5
+ module_function
6
+
7
+ def call(resource)
8
+ case resource
9
+ when String then resource
10
+ when Class then resource.name.downcase
11
+ else "#{resource.class.name.downcase}:#{resource.id}"
12
+ end
13
+ end
14
+ end
15
+ end
@@ -2,92 +2,38 @@
2
2
 
3
3
  module Loco
4
4
  class WsConnectionManager
5
- def initialize(resource)
6
- @resource = resource
7
- end
8
-
9
- def identifier
10
- return @resource if @resource.is_a?(String)
11
-
12
- "#{@resource.class.name.downcase}:#{@resource.id}"
13
- end
14
-
15
- def connected?(uuid)
16
- connected_uuids.include? uuid
17
- end
5
+ EXPIRATION = 60 * 3
18
6
 
19
- def connected_uuids
20
- data.find_all { |_, v| v.is_a? String }.to_h.keys
7
+ def initialize(resource, opts = {})
8
+ if opts[:identifier]
9
+ @identifier = resource
10
+ else
11
+ @resource = resource
12
+ end
21
13
  end
22
14
 
23
15
  def add(uuid)
24
- update uuid
25
- check_connections
16
+ WsConnectionStorage.current.add(identifier, uuid)
17
+ WsConnectionStorage.current.add("uuid:#{uuid}", identifier)
18
+ update(uuid)
19
+ WsConnectionChecker.(identifier, skip: uuid)
26
20
  end
27
21
 
28
- def del(uuid)
29
- save(data.tap { |h| h.delete uuid })
30
- check_connections
22
+ def del(uuid, skip_checker: false)
23
+ WsConnectionStorage.current.rem(identifier, uuid)
24
+ WsConnectionStorage.current.rem("uuid:#{uuid}", identifier)
25
+ WsConnectionStorage.current.del(uuid)
26
+ WsConnectionChecker.(identifier) unless skip_checker
31
27
  end
32
28
 
33
29
  def update(uuid)
34
- save(data.tap { |h| h[uuid] = current_time })
30
+ WsConnectionStorage.current.set(uuid, 'ok', ex: EXPIRATION)
35
31
  end
36
32
 
37
- def destroy
38
- WsConnectionStorage.current.del identifier
39
- end
40
-
41
- protected
42
-
43
- def data
44
- serialized_uuids = WsConnectionStorage.current.get identifier
45
- return {} if serialized_uuids.blank?
46
-
47
- JSON.parse serialized_uuids
48
- end
49
-
50
- def uuids
51
- data.keys
52
- end
33
+ private
53
34
 
54
- def save(hash)
55
- WsConnectionStorage.current.set identifier, hash.to_json
56
- end
57
-
58
- def check_connections
59
- hash = data.to_a.map do |arr|
60
- uuid, val = check_connection arr.first, arr.last
61
- [uuid, val]
62
- end.to_h.compact
63
- save hash
64
- end
65
-
66
- def check_connection(uuid, val)
67
- case val
68
- when String
69
- val = check_connection_str uuid, val
70
- when Hash
71
- uuid, val = check_connection_hash uuid, val
72
- end
73
- [uuid, val]
74
- end
75
-
76
- def check_connection_str(uuid, val)
77
- return val if Time.zone.parse(val) >= 3.minutes.ago
78
-
79
- SenderJob.perform_later uuid, loco: { connection_check: true }
80
- { 'check' => current_time }
81
- end
82
-
83
- def check_connection_hash(uuid, val)
84
- return [uuid, val] if Time.zone.parse(val['check']) >= 5.seconds.ago
85
-
86
- [nil, nil]
87
- end
88
-
89
- def current_time
90
- Time.current.iso8601(6)
35
+ def identifier
36
+ @identifier ||= WsConnectionIdentifier.(@resource)
91
37
  end
92
38
  end
93
39
  end
@@ -13,37 +13,77 @@ module Loco
13
13
  end
14
14
 
15
15
  def initialize
16
- @storage = Config.redis_instance || {}
16
+ @storage = Config.redis_instance
17
17
  end
18
18
 
19
- def get(key)
20
- case @storage
21
- when Hash
22
- storage[proper_key(key)]
19
+ def type(key)
20
+ storage.type(proper_key(key))
21
+ end
22
+
23
+ def exists?(key)
24
+ storage.exists?(proper_key(key))
25
+ end
26
+
27
+ def get(key, hkey = nil)
28
+ if hkey.nil?
29
+ storage.get(proper_key("k:#{key}"))
23
30
  else
24
- storage.get proper_key(key)
31
+ storage.hget(proper_key("h:#{key}"), hkey)
25
32
  end
26
33
  end
27
34
 
28
- def set(key, val)
29
- case @storage
30
- when Hash
31
- storage[proper_key(key)] = val
35
+ def set(key, val, opts = {})
36
+ if val.is_a?(Hash)
37
+ storage.hset(proper_key("h:#{key}"), val)
32
38
  else
33
- storage.set proper_key(key), val
39
+ storage.set(proper_key("k:#{key}"), val, ex: opts[:ex])
34
40
  end
35
41
  end
36
42
 
37
- def del(key)
38
- case @storage
39
- when Hash
40
- storage.delete proper_key(key)
43
+ def del(key, hkey = nil)
44
+ if hkey.nil?
45
+ storage.del(proper_key("k:#{key}"))
41
46
  else
42
- storage.del proper_key(key)
47
+ storage.hdel(proper_key("h:#{key}"), hkey)
48
+ end
49
+ end
50
+
51
+ def scan(match: nil, all: false, &block)
52
+ match = 'uuid:*' if all
53
+ storage.scan_each(match: "#{proper_key('s:')}#{match}").each do |key|
54
+ if all
55
+ yield(key.split('uuid:').last)
56
+ else
57
+ storage.smembers(key).each(&block)
58
+ end
43
59
  end
44
60
  end
45
61
 
46
- protected
62
+ def scan_hash(key, &block)
63
+ storage.hscan_each(proper_key("h:#{key}"), &block)
64
+ end
65
+
66
+ def hlen(key)
67
+ storage.hlen(proper_key("h:#{key}"))
68
+ end
69
+
70
+ def add(key, val)
71
+ storage.sadd(proper_key("s:#{key}"), val)
72
+ end
73
+
74
+ def members(key)
75
+ storage.smembers(proper_key("s:#{key}"))
76
+ end
77
+
78
+ def member?(key, val)
79
+ storage.sismember(proper_key("s:#{key}"), val)
80
+ end
81
+
82
+ def rem(key, val)
83
+ storage.srem(proper_key("s:#{key}"), val)
84
+ end
85
+
86
+ private
47
87
 
48
88
  def proper_key(key)
49
89
  "#{Config.app_name}:#{key}"
data/lib/loco-rails.rb CHANGED
@@ -5,12 +5,15 @@ require 'loco-rails-core'
5
5
  require 'loco/broadcaster'
6
6
  require 'loco/config'
7
7
  require 'loco/emitter'
8
- require 'loco/engine'
8
+ require 'loco/rails/engine'
9
9
  require 'loco/helpers'
10
10
  require 'loco/hub'
11
+ require 'loco/permissions_presenter'
11
12
  require 'loco/sender'
13
+ require 'loco/ws_connection_checker'
12
14
  require 'loco/ws_connection_manager'
13
- require 'loco/ws_connected_resources_manager'
15
+ require 'loco/ws_connection_finder'
16
+ require 'loco/ws_connection_identifier'
14
17
  require 'loco/ws_connection_storage'
15
18
 
16
19
  module Loco
@@ -22,4 +25,32 @@ module Loco
22
25
  Config.configure config
23
26
  end
24
27
  end
28
+
29
+ def emit(obj, event = nil, opts = {})
30
+ Broadcaster.(
31
+ obj,
32
+ event,
33
+ payload: opts[:payload] || opts[:data],
34
+ recipients: opts[opts[:for] ? :for : :to]
35
+ )
36
+ end
37
+
38
+ def emit_to(recipient_s, data)
39
+ Sender.(recipient_s, data)
40
+ end
41
+
42
+ def add_hub(name, members = [])
43
+ Hub.set(name, members)
44
+ end
45
+
46
+ def get_hub(name)
47
+ Hub.get(name)
48
+ end
49
+
50
+ def del_hub(name)
51
+ hub = Hub.get(name)
52
+ return false if hub.nil?
53
+
54
+ hub.destroy
55
+ end
25
56
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: loco-rails
3
3
  version: !ruby/object:Gem::Version
4
- version: 4.1.1
4
+ version: 6.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Zbigniew Humeniuk
8
- autorequire:
8
+ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2020-09-13 00:00:00.000000000 Z
11
+ date: 2022-09-04 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: loco-rails-core
@@ -44,76 +44,70 @@ dependencies:
44
44
  requirements:
45
45
  - - "~>"
46
46
  - !ruby/object:Gem::Version
47
- version: 3.1.12
47
+ version: 3.1.16
48
48
  type: :development
49
49
  prerelease: false
50
50
  version_requirements: !ruby/object:Gem::Requirement
51
51
  requirements:
52
52
  - - "~>"
53
53
  - !ruby/object:Gem::Version
54
- version: 3.1.12
54
+ version: 3.1.16
55
55
  - !ruby/object:Gem::Dependency
56
56
  name: capybara
57
57
  requirement: !ruby/object:Gem::Requirement
58
58
  requirements:
59
59
  - - "~>"
60
60
  - !ruby/object:Gem::Version
61
- version: 3.28.0
61
+ version: 3.36.0
62
62
  type: :development
63
63
  prerelease: false
64
64
  version_requirements: !ruby/object:Gem::Requirement
65
65
  requirements:
66
66
  - - "~>"
67
67
  - !ruby/object:Gem::Version
68
- version: 3.28.0
68
+ version: 3.36.0
69
69
  - !ruby/object:Gem::Dependency
70
70
  name: database_cleaner
71
71
  requirement: !ruby/object:Gem::Requirement
72
72
  requirements:
73
73
  - - "~>"
74
74
  - !ruby/object:Gem::Version
75
- version: 1.7.0
75
+ version: 2.0.1
76
76
  type: :development
77
77
  prerelease: false
78
78
  version_requirements: !ruby/object:Gem::Requirement
79
79
  requirements:
80
80
  - - "~>"
81
81
  - !ruby/object:Gem::Version
82
- version: 1.7.0
82
+ version: 2.0.1
83
83
  - !ruby/object:Gem::Dependency
84
84
  name: jbuilder
85
85
  requirement: !ruby/object:Gem::Requirement
86
86
  requirements:
87
87
  - - "~>"
88
88
  - !ruby/object:Gem::Version
89
- version: 2.10.0
89
+ version: 2.11.5
90
90
  type: :development
91
91
  prerelease: false
92
92
  version_requirements: !ruby/object:Gem::Requirement
93
93
  requirements:
94
94
  - - "~>"
95
95
  - !ruby/object:Gem::Version
96
- version: 2.10.0
96
+ version: 2.11.5
97
97
  - !ruby/object:Gem::Dependency
98
98
  name: listen
99
99
  requirement: !ruby/object:Gem::Requirement
100
100
  requirements:
101
- - - ">="
102
- - !ruby/object:Gem::Version
103
- version: 3.1.5
104
- - - "<"
101
+ - - "~>"
105
102
  - !ruby/object:Gem::Version
106
- version: '3.2'
103
+ version: 3.7.1
107
104
  type: :development
108
105
  prerelease: false
109
106
  version_requirements: !ruby/object:Gem::Requirement
110
107
  requirements:
111
- - - ">="
112
- - !ruby/object:Gem::Version
113
- version: 3.1.5
114
- - - "<"
108
+ - - "~>"
115
109
  - !ruby/object:Gem::Version
116
- version: '3.2'
110
+ version: 3.7.1
117
111
  - !ruby/object:Gem::Dependency
118
112
  name: mysql2
119
113
  requirement: !ruby/object:Gem::Requirement
@@ -134,28 +128,56 @@ dependencies:
134
128
  requirements:
135
129
  - - "~>"
136
130
  - !ruby/object:Gem::Version
137
- version: '4.2'
131
+ version: 5.6.2
138
132
  type: :development
139
133
  prerelease: false
140
134
  version_requirements: !ruby/object:Gem::Requirement
141
135
  requirements:
142
136
  - - "~>"
143
137
  - !ruby/object:Gem::Version
144
- version: '4.2'
138
+ version: 5.6.2
145
139
  - !ruby/object:Gem::Dependency
146
140
  name: redis
147
141
  requirement: !ruby/object:Gem::Requirement
148
142
  requirements:
149
143
  - - "~>"
150
144
  - !ruby/object:Gem::Version
151
- version: 4.1.2
145
+ version: 4.5.1
146
+ type: :development
147
+ prerelease: false
148
+ version_requirements: !ruby/object:Gem::Requirement
149
+ requirements:
150
+ - - "~>"
151
+ - !ruby/object:Gem::Version
152
+ version: 4.5.1
153
+ - !ruby/object:Gem::Dependency
154
+ name: rspec-expectations
155
+ requirement: !ruby/object:Gem::Requirement
156
+ requirements:
157
+ - - "~>"
158
+ - !ruby/object:Gem::Version
159
+ version: 3.10.2
152
160
  type: :development
153
161
  prerelease: false
154
162
  version_requirements: !ruby/object:Gem::Requirement
155
163
  requirements:
156
164
  - - "~>"
157
165
  - !ruby/object:Gem::Version
158
- version: 4.1.2
166
+ version: 3.10.2
167
+ - !ruby/object:Gem::Dependency
168
+ name: rspec-mocks
169
+ requirement: !ruby/object:Gem::Requirement
170
+ requirements:
171
+ - - "~>"
172
+ - !ruby/object:Gem::Version
173
+ version: 3.10.2
174
+ type: :development
175
+ prerelease: false
176
+ version_requirements: !ruby/object:Gem::Requirement
177
+ requirements:
178
+ - - "~>"
179
+ - !ruby/object:Gem::Version
180
+ version: 3.10.2
159
181
  - !ruby/object:Gem::Dependency
160
182
  name: rubocop
161
183
  requirement: !ruby/object:Gem::Requirement
@@ -190,42 +212,42 @@ dependencies:
190
212
  requirements:
191
213
  - - "~>"
192
214
  - !ruby/object:Gem::Version
193
- version: '3.142'
215
+ version: 4.1.0
194
216
  type: :development
195
217
  prerelease: false
196
218
  version_requirements: !ruby/object:Gem::Requirement
197
219
  requirements:
198
220
  - - "~>"
199
221
  - !ruby/object:Gem::Version
200
- version: '3.142'
222
+ version: 4.1.0
201
223
  - !ruby/object:Gem::Dependency
202
- name: source_maps_fixer
224
+ name: sprockets-rails
203
225
  requirement: !ruby/object:Gem::Requirement
204
226
  requirements:
205
- - - ">="
227
+ - - "~>"
206
228
  - !ruby/object:Gem::Version
207
- version: '0'
229
+ version: 3.4.2
208
230
  type: :development
209
231
  prerelease: false
210
232
  version_requirements: !ruby/object:Gem::Requirement
211
233
  requirements:
212
- - - ">="
234
+ - - "~>"
213
235
  - !ruby/object:Gem::Version
214
- version: '0'
236
+ version: 3.4.2
215
237
  - !ruby/object:Gem::Dependency
216
238
  name: will_paginate
217
239
  requirement: !ruby/object:Gem::Requirement
218
240
  requirements:
219
241
  - - "~>"
220
242
  - !ruby/object:Gem::Version
221
- version: 3.1.8
243
+ version: 3.3.1
222
244
  type: :development
223
245
  prerelease: false
224
246
  version_requirements: !ruby/object:Gem::Requirement
225
247
  requirements:
226
248
  - - "~>"
227
249
  - !ruby/object:Gem::Version
228
- version: 3.1.8
250
+ version: 3.3.1
229
251
  description: Rails is awesome, but modern web needs Loco-motive.
230
252
  email:
231
253
  - hello@artofcode.co
@@ -239,7 +261,6 @@ files:
239
261
  - app/controllers/loco/application_controller.rb
240
262
  - app/controllers/loco/notification_center_controller.rb
241
263
  - app/jobs/loco/sender_job.rb
242
- - app/jobs/loco/uuid_job.rb
243
264
  - app/models/loco/notification.rb
244
265
  - app/services/loco/notification/fetcher.rb
245
266
  - config/routes.rb
@@ -260,19 +281,23 @@ files:
260
281
  - lib/loco/broadcaster.rb
261
282
  - lib/loco/config.rb
262
283
  - lib/loco/emitter.rb
263
- - lib/loco/engine.rb
264
284
  - lib/loco/helpers.rb
265
285
  - lib/loco/hub.rb
286
+ - lib/loco/permissions_presenter.rb
287
+ - lib/loco/rails/engine.rb
266
288
  - lib/loco/sender.rb
267
289
  - lib/loco/version.rb
268
- - lib/loco/ws_connected_resources_manager.rb
290
+ - lib/loco/ws_connection_checker.rb
291
+ - lib/loco/ws_connection_finder.rb
292
+ - lib/loco/ws_connection_identifier.rb
269
293
  - lib/loco/ws_connection_manager.rb
270
294
  - lib/loco/ws_connection_storage.rb
271
295
  homepage: http://locoframework.org
272
296
  licenses:
273
297
  - MIT
274
- metadata: {}
275
- post_install_message:
298
+ metadata:
299
+ rubygems_mfa_required: 'true'
300
+ post_install_message:
276
301
  rdoc_options: []
277
302
  require_paths:
278
303
  - lib
@@ -280,15 +305,15 @@ required_ruby_version: !ruby/object:Gem::Requirement
280
305
  requirements:
281
306
  - - ">="
282
307
  - !ruby/object:Gem::Version
283
- version: 2.6.0
308
+ version: 2.7.0
284
309
  required_rubygems_version: !ruby/object:Gem::Requirement
285
310
  requirements:
286
311
  - - ">="
287
312
  - !ruby/object:Gem::Version
288
313
  version: '0'
289
314
  requirements: []
290
- rubygems_version: 3.1.2
291
- signing_key:
315
+ rubygems_version: 3.3.7
316
+ signing_key:
292
317
  specification_version: 4
293
318
  summary: Framework on top of Rails.
294
319
  test_files: []