loco-rails 4.1.1 → 5.0.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -3,23 +3,28 @@
3
3
  module Loco
4
4
  module Emitter
5
5
  def emit(obj, event = nil, opts = {})
6
- Broadcaster.new(obj, event, opts).emit
6
+ Broadcaster.(
7
+ obj,
8
+ event,
9
+ payload: opts[:payload] || opts[:data],
10
+ recipients: opts[opts[:for] ? :for : :to]
11
+ )
7
12
  end
8
13
 
9
- def emit_to(recipient, data)
10
- Sender.new(recipient, data).emit
14
+ def emit_to(recipient_s, data)
15
+ Sender.(recipient_s, data)
11
16
  end
12
17
 
13
18
  def add_hub(name, members = [])
14
- Hub.new(name, members).save
19
+ Hub.set(name, members)
15
20
  end
16
21
 
17
22
  def get_hub(name)
18
- Hub.get name
23
+ Hub.get(name)
19
24
  end
20
25
 
21
26
  def del_hub(name)
22
- hub = Hub.get name
27
+ hub = Hub.get(name)
23
28
  return false if hub.nil?
24
29
 
25
30
  hub.destroy
@@ -2,71 +2,64 @@
2
2
 
3
3
  module Loco
4
4
  class Hub
5
- PREFIX = 'loco:hub:'
6
-
7
- attr_reader :raw_members
8
-
9
- def initialize(name, members = [])
10
- @name = "#{PREFIX}#{name}"
11
- @raw_members = members.map { |m| serialize m }
12
- end
5
+ PREFIX = 'hub:'
13
6
 
14
7
  class << self
8
+ def set(name, members)
9
+ new(name, members)
10
+ end
11
+
15
12
  def get(name)
16
- hub = WsConnectionStorage.current.get "#{PREFIX}#{name}"
17
- return nil if hub.blank?
13
+ return nil if WsConnectionStorage.current.type("s:#{full_name(name)}") != 'set'
18
14
 
19
- new name, JSON.parse(hub)
15
+ new(name)
20
16
  end
17
+
18
+ def full_name(val)
19
+ "#{PREFIX}#{val}"
20
+ end
21
+ end
22
+
23
+ def initialize(name, members = [])
24
+ @name = self.class.full_name(name)
25
+ members.map { |member| add_member(member) }
21
26
  end
22
27
 
23
28
  def name
24
- @name.split(PREFIX).last
29
+ full_name.split(PREFIX).last
25
30
  end
26
31
 
27
- def add_member(member)
28
- serialized = serialize member
29
- return raw_members if raw_members.include? serialized
32
+ def full_name
33
+ @name
34
+ end
30
35
 
31
- raw_members << serialized
32
- save
33
- raw_members
36
+ def add_member(member)
37
+ WsConnectionStorage.current.add(@name, WsConnectionIdentifier.(member))
34
38
  end
35
39
 
36
40
  def del_member(member)
37
- serialized = serialize member
38
- return nil unless raw_members.include? serialized
39
-
40
- raw_members.delete serialized
41
- save
42
- serialized
41
+ WsConnectionStorage.current.rem(@name, WsConnectionIdentifier.(member))
43
42
  end
44
43
 
45
44
  def destroy
46
- WsConnectionStorage.current.del @name
47
- true
45
+ WsConnectionStorage.current.members(@name).each do |member|
46
+ WsConnectionStorage.current.rem(@name, member)
47
+ end
48
48
  end
49
49
 
50
- def save
51
- WsConnectionStorage.current.set @name, raw_members.to_json
52
- self
50
+ def include?(resource)
51
+ WsConnectionStorage.current.member?(@name, WsConnectionIdentifier.(resource))
53
52
  end
54
53
 
55
- def include?(resource)
56
- raw_members.include? serialize(resource)
54
+ def raw_members
55
+ WsConnectionStorage.current.members(@name)
57
56
  end
58
57
 
59
58
  def members
60
- raw_members.map do |str|
61
- klass, id = str.split ':'
62
- klass.classify.constantize.find_by id: id
59
+ raw_members.map do |serialized|
60
+ klass, id = serialized.split(':')
61
+ klass.classify.constantize.find_by(id: id)
63
62
  end
64
63
  end
65
-
66
- private
67
-
68
- def serialize(member)
69
- WsConnectionManager.new(member).identifier
70
- end
71
64
  end
72
65
  end
@@ -0,0 +1,27 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Loco
4
+ module PermissionsPresenter
5
+ module_function
6
+
7
+ def indexed(loco_permissions, opts = {})
8
+ h = signed_in(loco_permissions).index_by do |o|
9
+ o.class.name.downcase.to_sym
10
+ end
11
+ if opts[:except] == :uuid
12
+ h.reject { |k, _| k == :string }
13
+ else
14
+ h
15
+ end
16
+ end
17
+
18
+ def signed_in(loco_permissions, opts = {})
19
+ arr = loco_permissions.compact
20
+ if opts[:except] == :uuid
21
+ arr.reject { |e| e.is_a?(String) }
22
+ else
23
+ arr
24
+ end
25
+ end
26
+ end
27
+ end
@@ -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
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Loco
4
- VERSION = '4.1.1'
4
+ VERSION = '5.0.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.hget(proper_key("h:#{key}"), hkey)
23
30
  else
24
- storage.get proper_key(key)
31
+ storage.get(proper_key("k:#{key}"))
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.hdel(proper_key("h:#{key}"), hkey)
41
46
  else
42
- storage.del proper_key(key)
47
+ storage.del(proper_key("k:#{key}"))
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}"