loco-rails 3.0.6 → 4.0.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.
- checksums.yaml +4 -4
- data/app/channels/loco/notification_center_channel.rb +32 -28
- data/app/controllers/loco/notification_center_controller.rb +36 -33
- data/app/jobs/loco/sender_job.rb +1 -1
- data/app/jobs/loco/uuid_job.rb +25 -22
- data/app/models/loco/notification.rb +35 -31
- data/app/services/loco/notification/fetcher.rb +44 -42
- data/lib/generators/loco/file_injector/file_injector_generator.rb +3 -12
- data/lib/generators/loco/initializer/templates/initializer.rb +5 -5
- data/lib/generators/loco/notification_center/USAGE +1 -1
- data/lib/generators/loco/notification_center/templates/services/loco/notification_center.rb +2 -2
- data/lib/loco-rails.rb +8 -0
- data/lib/loco/broadcaster.rb +56 -66
- data/lib/loco/config.rb +9 -8
- data/lib/loco/emitter.rb +6 -5
- data/lib/loco/hub.rb +11 -8
- data/lib/loco/sender.rb +26 -18
- data/lib/loco/version.rb +1 -1
- data/lib/loco/ws_connected_resources_manager.rb +14 -9
- data/lib/loco/ws_connection_manager.rb +46 -42
- data/lib/loco/ws_connection_storage.rb +6 -6
- metadata +47 -5
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 14ddadb549243851cfae8f6260dbe08b6813392116f03b83d4618615598ec2d3
|
4
|
+
data.tar.gz: c5cdfc9d981ce63a5e8237cb794d7eb2a9d8435941070bba84f65f188e08e215
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: '0921bb8ffff692bede6999ae0dc2de20d1822c0ca3b5a8bac0aabcd50909a12369a6987da6b309356e4306dfd3891aea85adff8c418cce1eacabc4bd73348509'
|
7
|
+
data.tar.gz: f8d9d259ae99c421bcbdbf03df5fa1878ae6dd590eb835d6c432a6c6e91b103df5d5efbcdfe9daff6ae1d4fbb2bab1c193d3416b1e6213b7e134842d7f2dea48
|
@@ -4,58 +4,62 @@ module Loco
|
|
4
4
|
class NotificationCenterChannel < ApplicationCable::Channel
|
5
5
|
def subscribed
|
6
6
|
return unless loco_permissions.is_a?(Array)
|
7
|
+
|
7
8
|
stream_for_resources
|
8
9
|
return if loco_permissions.compact.size > 1
|
10
|
+
|
9
11
|
SenderJob.perform_later @uuid, loco: { start_ajax_polling: true }
|
10
12
|
end
|
11
13
|
|
12
14
|
def unsubscribed
|
13
15
|
loco_permissions.each do |resource|
|
14
16
|
next if resource.nil? || resource.is_a?(String)
|
17
|
+
|
15
18
|
UuidJob.perform_later serialize_resource(resource), @uuid, 'del'
|
16
19
|
end
|
17
20
|
end
|
18
21
|
|
19
|
-
def receive
|
22
|
+
def receive(data)
|
20
23
|
update_connections if data['loco'] && data['loco']['connection_check']
|
21
|
-
NotificationCenter.new.
|
24
|
+
NotificationCenter.new.received_message permissions, data
|
22
25
|
end
|
23
26
|
|
24
27
|
protected
|
25
28
|
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
end
|
29
|
+
def stream_for_resources
|
30
|
+
loco_permissions.compact.each do |resource|
|
31
|
+
if resource.is_a? String
|
32
|
+
@uuid = resource
|
33
|
+
stream_for_resource resource
|
34
|
+
SenderJob.perform_later @uuid, loco: { uuid: @uuid }
|
35
|
+
else
|
36
|
+
UuidJob.perform_later serialize_resource(resource), @uuid, 'add'
|
37
|
+
stream_for_resource resource
|
36
38
|
end
|
37
39
|
end
|
40
|
+
end
|
38
41
|
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
42
|
+
def stream_for_resource(resource)
|
43
|
+
identifier = WsConnectionManager.new(resource).identifier
|
44
|
+
stream_from "loco:notification_center:#{identifier}"
|
45
|
+
end
|
43
46
|
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
end.to_h
|
47
|
+
def permissions
|
48
|
+
loco_permissions.compact.index_by do |o|
|
49
|
+
o.class.name.downcase.to_sym
|
48
50
|
end
|
51
|
+
end
|
49
52
|
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
UuidJob.perform_later serialize_resource(val), @uuid, 'update'
|
54
|
-
end
|
55
|
-
end
|
53
|
+
def update_connections
|
54
|
+
permissions.each do |key, val|
|
55
|
+
next if key == :string
|
56
56
|
|
57
|
-
|
58
|
-
{ 'class' => resource.class.name, 'id' => resource.id }
|
57
|
+
UuidJob.perform_later serialize_resource(val), @uuid, 'update'
|
59
58
|
end
|
59
|
+
end
|
60
|
+
|
61
|
+
def serialize_resource(resource)
|
62
|
+
{ 'class' => resource.class.name, 'id' => resource.id }
|
63
|
+
end
|
60
64
|
end
|
61
65
|
end
|
@@ -16,43 +16,46 @@ module Loco
|
|
16
16
|
|
17
17
|
private
|
18
18
|
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
end
|
24
|
-
fetcher = Notification::Fetcher.new notif_fetcher_args
|
25
|
-
render json: [
|
26
|
-
fetcher.formatted_notifications,
|
27
|
-
fetcher.next_sync_time.iso8601(6)
|
28
|
-
]
|
19
|
+
def fetch_notifications
|
20
|
+
if params[:synced_at].blank?
|
21
|
+
render json: [[], Time.current.iso8601(6)]
|
22
|
+
return
|
29
23
|
end
|
24
|
+
fetcher = Notification::Fetcher.new notif_fetcher_args
|
25
|
+
render json: [
|
26
|
+
fetcher.formatted_notifications,
|
27
|
+
fetcher.next_sync_time.iso8601(6)
|
28
|
+
]
|
29
|
+
end
|
30
30
|
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
31
|
+
def notif_fetcher_args
|
32
|
+
{
|
33
|
+
synced_at: params[:synced_at],
|
34
|
+
permissions: permissions,
|
35
|
+
recipient_token: params[:token]
|
36
|
+
}
|
37
|
+
end
|
38
38
|
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
process_loco_permissions
|
43
|
-
end
|
39
|
+
def permissions
|
40
|
+
return [] unless defined? loco_permissions
|
41
|
+
return loco_permissions if params[:uuid].blank?
|
44
42
|
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
43
|
+
process_loco_permissions
|
44
|
+
end
|
45
|
+
|
46
|
+
def process_loco_permissions
|
47
|
+
resources_to_del = []
|
48
|
+
resources_to_add = []
|
49
|
+
loco_permissions.each do |resource|
|
50
|
+
next if resource.nil?
|
51
|
+
|
52
|
+
ws_conn_manager = WsConnectionManager.new resource
|
53
|
+
next unless ws_conn_manager.connected?(params[:uuid])
|
54
|
+
|
55
|
+
resources_to_del << resource
|
56
|
+
resources_to_add << resource.class
|
56
57
|
end
|
58
|
+
loco_permissions - resources_to_del + resources_to_add.uniq
|
59
|
+
end
|
57
60
|
end
|
58
61
|
end
|
data/app/jobs/loco/sender_job.rb
CHANGED
data/app/jobs/loco/uuid_job.rb
CHANGED
@@ -4,9 +4,10 @@ module Loco
|
|
4
4
|
class UuidJob < ActiveJob::Base
|
5
5
|
queue_as :loco
|
6
6
|
|
7
|
-
def perform
|
7
|
+
def perform(serialized_resource, uuid, action)
|
8
8
|
ws_conn_manager = init_ws_conn_manager serialized_resource
|
9
9
|
return unless ws_conn_manager
|
10
|
+
|
10
11
|
case action
|
11
12
|
when 'add'
|
12
13
|
add ws_conn_manager, uuid
|
@@ -19,30 +20,32 @@ module Loco
|
|
19
20
|
|
20
21
|
protected
|
21
22
|
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
23
|
+
def add(ws_conn_manager, uuid)
|
24
|
+
ws_conn_manager.add uuid
|
25
|
+
WsConnectedResourcesManager.add ws_conn_manager.identifier
|
26
|
+
end
|
26
27
|
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
WsConnectedResourcesManager.del ws_conn_manager.identifier
|
31
|
-
end
|
28
|
+
def del(ws_conn_manager, uuid)
|
29
|
+
ws_conn_manager.del uuid
|
30
|
+
return if ws_conn_manager.connected_uuids.any?
|
32
31
|
|
33
|
-
|
34
|
-
|
35
|
-
WsConnectedResourcesManager.add ws_conn_manager.identifier
|
36
|
-
end
|
32
|
+
WsConnectedResourcesManager.del ws_conn_manager.identifier
|
33
|
+
end
|
37
34
|
|
38
|
-
|
39
|
-
|
40
|
-
|
35
|
+
def update(ws_conn_manager, uuid)
|
36
|
+
ws_conn_manager.update uuid
|
37
|
+
WsConnectedResourcesManager.add ws_conn_manager.identifier
|
38
|
+
end
|
41
39
|
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
40
|
+
def deserialize_resource(hash)
|
41
|
+
hash['class'].constantize.find_by id: hash['id']
|
42
|
+
end
|
43
|
+
|
44
|
+
def init_ws_conn_manager(serialized_resource)
|
45
|
+
resource = deserialize_resource serialized_resource
|
46
|
+
return unless resource
|
47
|
+
|
48
|
+
WsConnectionManager.new resource
|
49
|
+
end
|
47
50
|
end
|
48
51
|
end
|
@@ -17,7 +17,7 @@ module Loco
|
|
17
17
|
end
|
18
18
|
end
|
19
19
|
|
20
|
-
def obj=
|
20
|
+
def obj=(val)
|
21
21
|
if val.instance_of? Class
|
22
22
|
self.obj_class = val.to_s
|
23
23
|
else
|
@@ -27,9 +27,10 @@ module Loco
|
|
27
27
|
end
|
28
28
|
end
|
29
29
|
|
30
|
-
def recipient=
|
30
|
+
def recipient=(val)
|
31
31
|
return if val.nil?
|
32
32
|
return if val == :all
|
33
|
+
|
33
34
|
if val.is_a? String
|
34
35
|
self.recipient_token = val
|
35
36
|
elsif val.instance_of? Class
|
@@ -40,10 +41,11 @@ module Loco
|
|
40
41
|
end
|
41
42
|
end
|
42
43
|
|
43
|
-
def recipient
|
44
|
+
def recipient(opts = {})
|
44
45
|
return recipient_token if recipient_token
|
45
46
|
return unless regular_recipient?
|
46
47
|
return class_recipient unless recipient_id
|
48
|
+
|
47
49
|
obj_recipient opts[:shallow]
|
48
50
|
end
|
49
51
|
|
@@ -58,40 +60,42 @@ module Loco
|
|
58
60
|
|
59
61
|
private
|
60
62
|
|
61
|
-
|
62
|
-
|
63
|
-
|
63
|
+
def regular_recipient?
|
64
|
+
recipient_class && recipient_id
|
65
|
+
end
|
64
66
|
|
65
|
-
|
66
|
-
|
67
|
-
|
67
|
+
def class_recipient
|
68
|
+
recipient_class.constantize
|
69
|
+
end
|
68
70
|
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
end
|
71
|
+
def obj_recipient(shallow = false)
|
72
|
+
if shallow
|
73
|
+
recipient_class.constantize.new id: recipient_id
|
74
|
+
else
|
75
|
+
recipient_class.constantize.find recipient_id
|
75
76
|
end
|
77
|
+
end
|
76
78
|
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
if obj.new_record?
|
81
|
-
self.event = 'creating'
|
82
|
-
else
|
83
|
-
set_event_for_persisted_obj
|
84
|
-
end
|
85
|
-
end
|
79
|
+
def set_event
|
80
|
+
return if event.present?
|
81
|
+
return if obj.instance_of? Class
|
86
82
|
|
87
|
-
|
88
|
-
self.event =
|
83
|
+
if obj.new_record?
|
84
|
+
self.event = 'creating'
|
85
|
+
else
|
86
|
+
set_event_for_persisted_obj
|
89
87
|
end
|
88
|
+
end
|
90
89
|
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
90
|
+
def set_event_for_persisted_obj
|
91
|
+
self.event = obj.created_at == obj.updated_at ? 'created' : 'updated'
|
92
|
+
end
|
93
|
+
|
94
|
+
def set_data
|
95
|
+
self.data ||= {}
|
96
|
+
return if obj.nil?
|
97
|
+
|
98
|
+
self.data.merge!(id: obj.id)
|
99
|
+
end
|
96
100
|
end
|
97
101
|
end
|
@@ -32,56 +32,58 @@ module Loco
|
|
32
32
|
|
33
33
|
private
|
34
34
|
|
35
|
-
|
36
|
-
|
37
|
-
|
35
|
+
def sync_time
|
36
|
+
Time.zone.parse @synced_at
|
37
|
+
end
|
38
38
|
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
39
|
+
def default_scope
|
40
|
+
Notification.order('created_at ASC')
|
41
|
+
.where('created_at > ?', sync_time)
|
42
|
+
end
|
43
43
|
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
notifications = notifications_for_all
|
48
|
-
notifications += notifications_behind_permissions
|
49
|
-
notifications += notifications_behind_token if @recipient_token
|
50
|
-
@notifications = notifications.sort_by(&:created_at)[0, max_size]
|
51
|
-
end
|
44
|
+
# OPTIMIZE: one query
|
45
|
+
def notifications
|
46
|
+
return @notifications if @notifications
|
52
47
|
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
).first max_size
|
59
|
-
end
|
48
|
+
notifications = notifications_for_all
|
49
|
+
notifications += notifications_behind_permissions
|
50
|
+
notifications += notifications_behind_token if @recipient_token
|
51
|
+
@notifications = notifications.sort_by(&:created_at)[0, max_size]
|
52
|
+
end
|
60
53
|
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
end
|
54
|
+
def notifications_for_all
|
55
|
+
default_scope.where(
|
56
|
+
recipient_class: nil,
|
57
|
+
recipient_id: nil,
|
58
|
+
recipient_token: nil
|
59
|
+
).first max_size
|
60
|
+
end
|
69
61
|
|
70
|
-
|
71
|
-
|
62
|
+
def notifications_behind_permissions
|
63
|
+
notifications = []
|
64
|
+
@permissions.each do |resource|
|
65
|
+
next unless resource
|
66
|
+
|
67
|
+
notifications += notification_for_resource resource
|
72
68
|
end
|
69
|
+
notifications
|
70
|
+
end
|
73
71
|
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
sql = "#{cond1} OR #{cond2}"
|
83
|
-
default_scope.where(sql, klass, resource.id, klass).first max_size
|
72
|
+
def notifications_behind_token
|
73
|
+
default_scope.where(recipient_token: @recipient_token).first max_size
|
74
|
+
end
|
75
|
+
|
76
|
+
def notification_for_resource(resource)
|
77
|
+
if resource.instance_of? Class
|
78
|
+
sql = 'recipient_class = ? AND recipient_id IS NULL'
|
79
|
+
return default_scope.where(sql, resource.to_s).first max_size
|
84
80
|
end
|
81
|
+
klass = resource.class.name
|
82
|
+
cond1 = '(recipient_class = ? AND recipient_id = ?)'
|
83
|
+
cond2 = '(recipient_class = ? AND recipient_id IS NULL)'
|
84
|
+
sql = "#{cond1} OR #{cond2}"
|
85
|
+
default_scope.where(sql, klass, resource.id, klass).first max_size
|
86
|
+
end
|
85
87
|
end
|
86
88
|
end
|
87
89
|
end
|
@@ -5,30 +5,21 @@ module Loco
|
|
5
5
|
source_root File.expand_path('templates', __dir__)
|
6
6
|
|
7
7
|
def routes
|
8
|
-
file_path = Rails.root.join 'config
|
8
|
+
file_path = Rails.root.join 'config/routes.rb'
|
9
9
|
line = %( mount Loco::Engine => '/notification-center'\n\n)
|
10
10
|
str = "Rails.application.routes.draw do\n"
|
11
11
|
inject_into_file file_path, line, after: str
|
12
12
|
end
|
13
13
|
|
14
14
|
def application_controller
|
15
|
-
file_path = Rails.root.join
|
16
|
-
'app',
|
17
|
-
'controllers',
|
18
|
-
'application_controller.rb'
|
19
|
-
)
|
15
|
+
file_path = Rails.root.join 'app/controllers/application_controller.rb'
|
20
16
|
data = File.read find_in_source_paths('application_controller.rb')
|
21
17
|
after_line = "class ApplicationController < ActionController::Base\n"
|
22
18
|
inject_into_file file_path, data, after: after_line
|
23
19
|
end
|
24
20
|
|
25
21
|
def connection
|
26
|
-
file_path = Rails.root.join
|
27
|
-
'app',
|
28
|
-
'channels',
|
29
|
-
'application_cable',
|
30
|
-
'connection.rb'
|
31
|
-
)
|
22
|
+
file_path = Rails.root.join 'app/channels/application_cable/connection.rb'
|
32
23
|
data = File.read find_in_source_paths('connection.rb')
|
33
24
|
inject_into_class file_path, 'Connection', data
|
34
25
|
end
|
@@ -1,7 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
Loco
|
4
|
-
silence_logger
|
5
|
-
notifications_size
|
6
|
-
app_name
|
7
|
-
|
3
|
+
Loco.configure do |c|
|
4
|
+
c.silence_logger = false # false by default
|
5
|
+
c.notifications_size = 100 # 100 by default
|
6
|
+
c.app_name = "loco_#{Rails.env}" # your app's name (required for namespacing)
|
7
|
+
end
|
@@ -1,5 +1,5 @@
|
|
1
1
|
Description:
|
2
|
-
Creates a notification center where you can receive
|
2
|
+
Creates a notification center where you can receive messages sent by JS clients through WebSockets.
|
3
3
|
|
4
4
|
Example:
|
5
5
|
rails generate loco:notification_center
|
data/lib/loco-rails.rb
CHANGED
data/lib/loco/broadcaster.rb
CHANGED
@@ -4,7 +4,7 @@ module Loco
|
|
4
4
|
class Broadcaster
|
5
5
|
attr_reader :obj, :event, :recipients, :data, :notifications
|
6
6
|
|
7
|
-
def initialize
|
7
|
+
def initialize(obj, event = nil, opts = {})
|
8
8
|
recipient_key = opts[:for] ? :for : :to
|
9
9
|
@obj = obj
|
10
10
|
@event = event
|
@@ -15,15 +15,6 @@ module Loco
|
|
15
15
|
@conn_res_manager = WsConnectedResourcesManager.new @recipients.compact
|
16
16
|
end
|
17
17
|
|
18
|
-
def signals
|
19
|
-
notifications
|
20
|
-
end
|
21
|
-
|
22
|
-
def prepare
|
23
|
-
init_notifications if notifications.empty?
|
24
|
-
prepare_notifications
|
25
|
-
end
|
26
|
-
|
27
18
|
def emit
|
28
19
|
init_notifications if notifications.empty?
|
29
20
|
send_notifications
|
@@ -36,77 +27,76 @@ module Loco
|
|
36
27
|
|
37
28
|
private
|
38
29
|
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
end
|
30
|
+
def init_notifications
|
31
|
+
recipients.each do |recipient|
|
32
|
+
@notifications << Notification.new(
|
33
|
+
obj: obj,
|
34
|
+
event: event,
|
35
|
+
recipient: recipient,
|
36
|
+
data: data
|
37
|
+
)
|
48
38
|
end
|
39
|
+
end
|
49
40
|
|
50
|
-
|
51
|
-
|
52
|
-
|
41
|
+
def send_notifications
|
42
|
+
notifications.each do |notification|
|
43
|
+
notification.save!
|
44
|
+
next if notification.recipient_id.nil?
|
53
45
|
|
54
|
-
|
55
|
-
|
56
|
-
notification.save!
|
57
|
-
next if notification.recipient_id.nil?
|
58
|
-
shallow_recipient = notification.recipient shallow: true
|
59
|
-
next unless @conn_res_manager.connected? shallow_recipient
|
60
|
-
send_via_ws notification
|
61
|
-
end
|
62
|
-
end
|
46
|
+
shallow_recipient = notification.recipient shallow: true
|
47
|
+
next unless @conn_res_manager.connected? shallow_recipient
|
63
48
|
|
64
|
-
|
65
|
-
recipient = notification.recipient shallow: true
|
66
|
-
data = { loco: { notification: notification.compact } }
|
67
|
-
SenderJob.perform_later recipient, data
|
68
|
-
@sent_via_ws += 1
|
49
|
+
send_via_ws notification
|
69
50
|
end
|
51
|
+
end
|
70
52
|
|
71
|
-
|
72
|
-
|
73
|
-
|
53
|
+
def send_via_ws(notification)
|
54
|
+
recipient = notification.recipient shallow: true
|
55
|
+
data = { loco: { notification: notification.compact } }
|
56
|
+
SenderJob.perform_later recipient, data
|
57
|
+
@sent_via_ws += 1
|
58
|
+
end
|
59
|
+
|
60
|
+
def notify_about_xhr_notifications?
|
61
|
+
@sent_via_ws < notifications.size
|
62
|
+
end
|
63
|
+
|
64
|
+
def notify_about_xhr_notifications
|
65
|
+
uuids = []
|
66
|
+
fetch_identifiers.each do |ident|
|
67
|
+
Loco::WsConnectionManager.new(ident).connected_uuids.each do |uuid|
|
68
|
+
next if uuids.include? uuid
|
74
69
|
|
75
|
-
|
76
|
-
|
77
|
-
fetch_identifiers.each do |ident|
|
78
|
-
Loco::WsConnectionManager.new(ident).connected_uuids.each do |uuid|
|
79
|
-
next if uuids.include? uuid
|
80
|
-
uuids << uuid
|
81
|
-
SenderJob.perform_later uuid, loco: { xhr_notifications: true }
|
82
|
-
end
|
70
|
+
uuids << uuid
|
71
|
+
SenderJob.perform_later uuid, loco: { xhr_notifications: true }
|
83
72
|
end
|
84
73
|
end
|
74
|
+
end
|
85
75
|
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
end
|
76
|
+
def set_sync_time_via_ws
|
77
|
+
sync_time = notifications.last.created_at.iso8601(6)
|
78
|
+
notifications.each do |notification|
|
79
|
+
recipient = notification.recipient shallow: true
|
80
|
+
SenderJob.perform_later recipient, loco: { sync_time: sync_time }
|
92
81
|
end
|
82
|
+
end
|
93
83
|
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
end
|
84
|
+
def notifications_recipients
|
85
|
+
notifications.map { |n| n.recipient shallow: true }.map do |o|
|
86
|
+
o.instance_of?(Class) ? o.to_s.downcase : nil
|
98
87
|
end
|
88
|
+
end
|
99
89
|
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
end
|
90
|
+
def fetch_identifiers
|
91
|
+
recipients = notifications_recipients
|
92
|
+
uniq_recipients = recipients.compact.uniq
|
93
|
+
Loco::WsConnectedResourcesManager.identifiers.find_all do |str|
|
94
|
+
if recipients.include? nil
|
95
|
+
true
|
96
|
+
else
|
97
|
+
uniq_recipients.include? str.split(':').first
|
109
98
|
end
|
110
99
|
end
|
100
|
+
end
|
111
101
|
end
|
112
102
|
end
|
data/lib/loco/config.rb
CHANGED
@@ -2,28 +2,29 @@
|
|
2
2
|
|
3
3
|
module Loco
|
4
4
|
class Config
|
5
|
+
CONFIGURATION = Struct.new :silence_logger, :notifications_size, :app_name, :redis_instance
|
6
|
+
|
5
7
|
cattr_accessor(:silence_logger) { false }
|
6
8
|
cattr_accessor(:notifications_size) { 100 }
|
7
9
|
cattr_accessor(:app_name) { 'loco' }
|
8
10
|
cattr_accessor(:redis_instance) { nil }
|
9
11
|
|
10
|
-
def self.configure
|
11
|
-
self.silence_logger =
|
12
|
-
if
|
13
|
-
|
14
|
-
|
15
|
-
self.app_name = opts[:app_name] if opts[:app_name]
|
16
|
-
configure_redis opts[:redis_instance]
|
12
|
+
def self.configure(config)
|
13
|
+
self.silence_logger = config.silence_logger if config.silence_logger
|
14
|
+
self.notifications_size = config.notifications_size if config.notifications_size
|
15
|
+
self.app_name = config.app_name if config.app_name
|
16
|
+
configure_redis config.redis_instance
|
17
17
|
ensure
|
18
18
|
true
|
19
19
|
end
|
20
20
|
|
21
|
-
def self.configure_redis
|
21
|
+
def self.configure_redis(redis_instance)
|
22
22
|
if redis_instance
|
23
23
|
self.redis_instance = redis_instance
|
24
24
|
return
|
25
25
|
end
|
26
26
|
return unless defined? Redis
|
27
|
+
|
27
28
|
Redis.current.get 'random_redis_key'
|
28
29
|
self.redis_instance = Redis.current
|
29
30
|
rescue Redis::CannotConnectError
|
data/lib/loco/emitter.rb
CHANGED
@@ -2,25 +2,26 @@
|
|
2
2
|
|
3
3
|
module Loco
|
4
4
|
module Emitter
|
5
|
-
def emit
|
5
|
+
def emit(obj, event = nil, opts = {})
|
6
6
|
Broadcaster.new(obj, event, opts).emit
|
7
7
|
end
|
8
8
|
|
9
|
-
def emit_to
|
9
|
+
def emit_to(recipient, data)
|
10
10
|
Sender.new(recipient, data).emit
|
11
11
|
end
|
12
12
|
|
13
|
-
def add_hub
|
13
|
+
def add_hub(name, members = [])
|
14
14
|
Hub.new(name, members).save
|
15
15
|
end
|
16
16
|
|
17
|
-
def get_hub
|
17
|
+
def get_hub(name)
|
18
18
|
Hub.get name
|
19
19
|
end
|
20
20
|
|
21
|
-
def del_hub
|
21
|
+
def del_hub(name)
|
22
22
|
hub = Hub.get name
|
23
23
|
return false if hub.nil?
|
24
|
+
|
24
25
|
hub.destroy
|
25
26
|
end
|
26
27
|
end
|
data/lib/loco/hub.rb
CHANGED
@@ -6,15 +6,16 @@ module Loco
|
|
6
6
|
|
7
7
|
attr_reader :raw_members
|
8
8
|
|
9
|
-
def initialize
|
9
|
+
def initialize(name, members = [])
|
10
10
|
@name = "#{PREFIX}#{name}"
|
11
11
|
@raw_members = members.map { |m| serialize m }
|
12
12
|
end
|
13
13
|
|
14
14
|
class << self
|
15
|
-
def get
|
15
|
+
def get(name)
|
16
16
|
hub = WsConnectionStorage.current.get "#{PREFIX}#{name}"
|
17
17
|
return nil if hub.blank?
|
18
|
+
|
18
19
|
new name, JSON.parse(hub)
|
19
20
|
end
|
20
21
|
end
|
@@ -23,17 +24,19 @@ module Loco
|
|
23
24
|
@name.split(PREFIX).last
|
24
25
|
end
|
25
26
|
|
26
|
-
def add_member
|
27
|
+
def add_member(member)
|
27
28
|
serialized = serialize member
|
28
29
|
return raw_members if raw_members.include? serialized
|
30
|
+
|
29
31
|
raw_members << serialized
|
30
32
|
save
|
31
33
|
raw_members
|
32
34
|
end
|
33
35
|
|
34
|
-
def del_member
|
36
|
+
def del_member(member)
|
35
37
|
serialized = serialize member
|
36
38
|
return nil unless raw_members.include? serialized
|
39
|
+
|
37
40
|
raw_members.delete serialized
|
38
41
|
save
|
39
42
|
serialized
|
@@ -49,7 +52,7 @@ module Loco
|
|
49
52
|
self
|
50
53
|
end
|
51
54
|
|
52
|
-
def include?
|
55
|
+
def include?(resource)
|
53
56
|
raw_members.include? serialize(resource)
|
54
57
|
end
|
55
58
|
|
@@ -62,8 +65,8 @@ module Loco
|
|
62
65
|
|
63
66
|
private
|
64
67
|
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
+
def serialize(member)
|
69
|
+
WsConnectionManager.new(member).identifier
|
70
|
+
end
|
68
71
|
end
|
69
72
|
end
|
data/lib/loco/sender.rb
CHANGED
@@ -2,35 +2,43 @@
|
|
2
2
|
|
3
3
|
module Loco
|
4
4
|
class Sender
|
5
|
-
def initialize
|
5
|
+
def initialize(recipient, data = {})
|
6
6
|
@recipients = [*recipient]
|
7
7
|
@data = data
|
8
8
|
end
|
9
9
|
|
10
10
|
def emit
|
11
11
|
uuids.each do |uuid|
|
12
|
-
NotificationCenterChannel.broadcast_to
|
12
|
+
NotificationCenterChannel.broadcast_to(uuid, payload)
|
13
13
|
end
|
14
|
+
@data[:loco][:idempotency_key]
|
14
15
|
end
|
15
16
|
|
16
17
|
private
|
17
18
|
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
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
|
29
|
+
end
|
29
30
|
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
31
|
+
def recipients_from_hub(hub)
|
32
|
+
hub.raw_members.map do |m|
|
33
|
+
WsConnectionManager.new(m).connected_uuids
|
34
|
+
end.flatten.uniq
|
35
|
+
end
|
36
|
+
|
37
|
+
def payload
|
38
|
+
@data[:loco] ||= {}
|
39
|
+
@data[:loco][:idempotency_key] ||= @data[:idempotency_key] || SecureRandom.hex
|
40
|
+
@data.delete(:idempotency_key)
|
41
|
+
@data
|
42
|
+
end
|
35
43
|
end
|
36
44
|
end
|
data/lib/loco/version.rb
CHANGED
@@ -2,7 +2,7 @@
|
|
2
2
|
|
3
3
|
module Loco
|
4
4
|
class WsConnectedResourcesManager
|
5
|
-
def initialize
|
5
|
+
def initialize(resources)
|
6
6
|
@resources = resources
|
7
7
|
@connected_resources = nil
|
8
8
|
end
|
@@ -11,19 +11,22 @@ module Loco
|
|
11
11
|
def identifiers
|
12
12
|
val = WsConnectionStorage.current.get key
|
13
13
|
return [] if val.blank?
|
14
|
+
|
14
15
|
JSON.parse val
|
15
16
|
end
|
16
17
|
|
17
|
-
def add
|
18
|
+
def add(identifier)
|
18
19
|
ids = identifiers
|
19
20
|
return if ids.include? identifier
|
21
|
+
|
20
22
|
ids << identifier
|
21
23
|
WsConnectionStorage.current.set key, ids.to_json
|
22
24
|
end
|
23
25
|
|
24
|
-
def del
|
26
|
+
def del(identifier)
|
25
27
|
ids = identifiers
|
26
28
|
return unless ids.include? identifier
|
29
|
+
|
27
30
|
ids.delete identifier
|
28
31
|
WsConnectionStorage.current.set key, ids.to_json
|
29
32
|
end
|
@@ -35,14 +38,16 @@ module Loco
|
|
35
38
|
|
36
39
|
def connected_resources
|
37
40
|
return @connected_resources if @connected_resources
|
41
|
+
|
38
42
|
@resources.each do |resource|
|
39
43
|
next if WsConnectionManager.new(resource).connected_uuids.empty?
|
44
|
+
|
40
45
|
add resource
|
41
46
|
end
|
42
47
|
@connected_resources || []
|
43
48
|
end
|
44
49
|
|
45
|
-
def connected?
|
50
|
+
def connected?(resource)
|
46
51
|
connected_resources.map do |res|
|
47
52
|
WsConnectionManager.new(res).identifier
|
48
53
|
end.include? WsConnectionManager.new(resource).identifier
|
@@ -50,10 +55,10 @@ module Loco
|
|
50
55
|
|
51
56
|
private
|
52
57
|
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
+
def add(resource)
|
59
|
+
@connected_resources ||= []
|
60
|
+
@connected_resources << resource
|
61
|
+
@connected_resources.uniq!
|
62
|
+
end
|
58
63
|
end
|
59
64
|
end
|
@@ -2,16 +2,17 @@
|
|
2
2
|
|
3
3
|
module Loco
|
4
4
|
class WsConnectionManager
|
5
|
-
def initialize
|
5
|
+
def initialize(resource)
|
6
6
|
@resource = resource
|
7
7
|
end
|
8
8
|
|
9
9
|
def identifier
|
10
10
|
return @resource if @resource.is_a?(String)
|
11
|
+
|
11
12
|
"#{@resource.class.name.downcase}:#{@resource.id}"
|
12
13
|
end
|
13
14
|
|
14
|
-
def connected?
|
15
|
+
def connected?(uuid)
|
15
16
|
connected_uuids.include? uuid
|
16
17
|
end
|
17
18
|
|
@@ -19,17 +20,17 @@ module Loco
|
|
19
20
|
data.find_all { |_, v| v.is_a? String }.to_h.keys
|
20
21
|
end
|
21
22
|
|
22
|
-
def add
|
23
|
+
def add(uuid)
|
23
24
|
update uuid
|
24
25
|
check_connections
|
25
26
|
end
|
26
27
|
|
27
|
-
def del
|
28
|
+
def del(uuid)
|
28
29
|
save(data.tap { |h| h.delete uuid })
|
29
30
|
check_connections
|
30
31
|
end
|
31
32
|
|
32
|
-
def update
|
33
|
+
def update(uuid)
|
33
34
|
save(data.tap { |h| h[uuid] = current_time })
|
34
35
|
end
|
35
36
|
|
@@ -39,51 +40,54 @@ module Loco
|
|
39
40
|
|
40
41
|
protected
|
41
42
|
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
JSON.parse serialized_uuids
|
46
|
-
end
|
43
|
+
def data
|
44
|
+
serialized_uuids = WsConnectionStorage.current.get identifier
|
45
|
+
return {} if serialized_uuids.blank?
|
47
46
|
|
48
|
-
|
49
|
-
|
50
|
-
end
|
47
|
+
JSON.parse serialized_uuids
|
48
|
+
end
|
51
49
|
|
52
|
-
|
53
|
-
|
54
|
-
|
50
|
+
def uuids
|
51
|
+
data.keys
|
52
|
+
end
|
55
53
|
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
[uuid, val]
|
60
|
-
end.to_h.compact
|
61
|
-
save hash
|
62
|
-
end
|
54
|
+
def save(hash)
|
55
|
+
WsConnectionStorage.current.set identifier, hash.to_json
|
56
|
+
end
|
63
57
|
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
val = check_connection_str uuid, val
|
68
|
-
when Hash
|
69
|
-
uuid, val = check_connection_hash uuid, val
|
70
|
-
end
|
58
|
+
def check_connections
|
59
|
+
hash = data.to_a.map do |arr|
|
60
|
+
uuid, val = check_connection arr.first, arr.last
|
71
61
|
[uuid, val]
|
72
|
-
end
|
62
|
+
end.to_h.compact
|
63
|
+
save hash
|
64
|
+
end
|
73
65
|
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
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
|
78
72
|
end
|
73
|
+
[uuid, val]
|
74
|
+
end
|
79
75
|
|
80
|
-
|
81
|
-
|
82
|
-
[nil, nil]
|
83
|
-
end
|
76
|
+
def check_connection_str(uuid, val)
|
77
|
+
return val if Time.zone.parse(val) >= 3.minutes.ago
|
84
78
|
|
85
|
-
|
86
|
-
|
87
|
-
|
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)
|
91
|
+
end
|
88
92
|
end
|
89
93
|
end
|
@@ -16,7 +16,7 @@ module Loco
|
|
16
16
|
@storage = Config.redis_instance || {}
|
17
17
|
end
|
18
18
|
|
19
|
-
def get
|
19
|
+
def get(key)
|
20
20
|
case @storage
|
21
21
|
when Hash
|
22
22
|
storage[proper_key(key)]
|
@@ -25,7 +25,7 @@ module Loco
|
|
25
25
|
end
|
26
26
|
end
|
27
27
|
|
28
|
-
def set
|
28
|
+
def set(key, val)
|
29
29
|
case @storage
|
30
30
|
when Hash
|
31
31
|
storage[proper_key(key)] = val
|
@@ -34,7 +34,7 @@ module Loco
|
|
34
34
|
end
|
35
35
|
end
|
36
36
|
|
37
|
-
def del
|
37
|
+
def del(key)
|
38
38
|
case @storage
|
39
39
|
when Hash
|
40
40
|
storage.delete proper_key(key)
|
@@ -45,8 +45,8 @@ module Loco
|
|
45
45
|
|
46
46
|
protected
|
47
47
|
|
48
|
-
|
49
|
-
|
50
|
-
|
48
|
+
def proper_key(key)
|
49
|
+
"#{Config.app_name}:#{key}"
|
50
|
+
end
|
51
51
|
end
|
52
52
|
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
|
+
version: 4.0.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Zbigniew Humeniuk
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2020-07-26 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: loco-rails-core
|
@@ -80,6 +80,20 @@ dependencies:
|
|
80
80
|
- - "~>"
|
81
81
|
- !ruby/object:Gem::Version
|
82
82
|
version: 1.7.0
|
83
|
+
- !ruby/object:Gem::Dependency
|
84
|
+
name: jbuilder
|
85
|
+
requirement: !ruby/object:Gem::Requirement
|
86
|
+
requirements:
|
87
|
+
- - "~>"
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: 2.10.0
|
90
|
+
type: :development
|
91
|
+
prerelease: false
|
92
|
+
version_requirements: !ruby/object:Gem::Requirement
|
93
|
+
requirements:
|
94
|
+
- - "~>"
|
95
|
+
- !ruby/object:Gem::Version
|
96
|
+
version: 2.10.0
|
83
97
|
- !ruby/object:Gem::Dependency
|
84
98
|
name: listen
|
85
99
|
requirement: !ruby/object:Gem::Requirement
|
@@ -106,14 +120,14 @@ dependencies:
|
|
106
120
|
requirements:
|
107
121
|
- - "~>"
|
108
122
|
- !ruby/object:Gem::Version
|
109
|
-
version: 0.5.
|
123
|
+
version: 0.5.3
|
110
124
|
type: :development
|
111
125
|
prerelease: false
|
112
126
|
version_requirements: !ruby/object:Gem::Requirement
|
113
127
|
requirements:
|
114
128
|
- - "~>"
|
115
129
|
- !ruby/object:Gem::Version
|
116
|
-
version: 0.5.
|
130
|
+
version: 0.5.3
|
117
131
|
- !ruby/object:Gem::Dependency
|
118
132
|
name: puma
|
119
133
|
requirement: !ruby/object:Gem::Requirement
|
@@ -184,6 +198,34 @@ dependencies:
|
|
184
198
|
- - "~>"
|
185
199
|
- !ruby/object:Gem::Version
|
186
200
|
version: '3.142'
|
201
|
+
- !ruby/object:Gem::Dependency
|
202
|
+
name: source_maps_fixer
|
203
|
+
requirement: !ruby/object:Gem::Requirement
|
204
|
+
requirements:
|
205
|
+
- - ">="
|
206
|
+
- !ruby/object:Gem::Version
|
207
|
+
version: '0'
|
208
|
+
type: :development
|
209
|
+
prerelease: false
|
210
|
+
version_requirements: !ruby/object:Gem::Requirement
|
211
|
+
requirements:
|
212
|
+
- - ">="
|
213
|
+
- !ruby/object:Gem::Version
|
214
|
+
version: '0'
|
215
|
+
- !ruby/object:Gem::Dependency
|
216
|
+
name: will_paginate
|
217
|
+
requirement: !ruby/object:Gem::Requirement
|
218
|
+
requirements:
|
219
|
+
- - "~>"
|
220
|
+
- !ruby/object:Gem::Version
|
221
|
+
version: 3.1.8
|
222
|
+
type: :development
|
223
|
+
prerelease: false
|
224
|
+
version_requirements: !ruby/object:Gem::Requirement
|
225
|
+
requirements:
|
226
|
+
- - "~>"
|
227
|
+
- !ruby/object:Gem::Version
|
228
|
+
version: 3.1.8
|
187
229
|
description: Rails is awesome, but modern web needs Loco-motive.
|
188
230
|
email:
|
189
231
|
- hello@artofcode.co
|
@@ -245,7 +287,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
245
287
|
- !ruby/object:Gem::Version
|
246
288
|
version: '0'
|
247
289
|
requirements: []
|
248
|
-
rubygems_version: 3.
|
290
|
+
rubygems_version: 3.1.2
|
249
291
|
signing_key:
|
250
292
|
specification_version: 4
|
251
293
|
summary: Framework on top of Rails.
|