loco-rails 4.1.1 → 5.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.
@@ -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}"