u-observers 0.9.0 → 2.2.1
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/.travis.sh +3 -0
- data/Gemfile +10 -8
- data/README.md +495 -11
- data/README.pt-BR.md +529 -0
- data/lib/micro/observers.rb +5 -22
- data/lib/micro/observers/broadcast.rb +77 -0
- data/lib/micro/observers/event.rb +21 -0
- data/lib/micro/observers/event/names.rb +29 -0
- data/lib/micro/observers/for/active_model.rb +36 -0
- data/lib/micro/observers/for/active_record.rb +16 -0
- data/lib/micro/observers/set.rb +100 -0
- data/lib/micro/observers/subscribers.rb +110 -0
- data/lib/micro/observers/utils.rb +12 -2
- data/lib/micro/observers/version.rb +1 -1
- data/lib/u-observers/for/active_model.rb +2 -0
- data/lib/u-observers/for/active_record.rb +2 -0
- metadata +12 -4
- data/lib/micro/observers/events.rb +0 -19
- data/lib/micro/observers/manager.rb +0 -155
data/lib/micro/observers.rb
CHANGED
@@ -3,30 +3,13 @@ require 'micro/observers/version'
|
|
3
3
|
module Micro
|
4
4
|
module Observers
|
5
5
|
require 'micro/observers/utils'
|
6
|
-
require 'micro/observers/
|
7
|
-
require 'micro/observers/
|
8
|
-
|
9
|
-
|
10
|
-
def notify_observers!(events)
|
11
|
-
proc do |object|
|
12
|
-
object.observers.subject_changed!
|
13
|
-
object.observers.send(:notify!, events)
|
14
|
-
end
|
15
|
-
end
|
16
|
-
|
17
|
-
def notify_observers(*events)
|
18
|
-
notify_observers!(Events[events])
|
19
|
-
end
|
20
|
-
end
|
21
|
-
|
22
|
-
def self.included(base)
|
23
|
-
base.extend(ClassMethods)
|
24
|
-
base.send(:private_class_method, :notify_observers!)
|
25
|
-
end
|
6
|
+
require 'micro/observers/event'
|
7
|
+
require 'micro/observers/subscribers'
|
8
|
+
require 'micro/observers/broadcast'
|
9
|
+
require 'micro/observers/set'
|
26
10
|
|
27
11
|
def observers
|
28
|
-
@
|
12
|
+
@__observers ||= Observers::Set.for(self)
|
29
13
|
end
|
30
|
-
|
31
14
|
end
|
32
15
|
end
|
@@ -0,0 +1,77 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'set'
|
4
|
+
|
5
|
+
module Micro
|
6
|
+
module Observers
|
7
|
+
|
8
|
+
module Broadcast
|
9
|
+
extend self
|
10
|
+
|
11
|
+
def call(subscribers, subject, data, event_names)
|
12
|
+
subscribers_list = subscribers.list
|
13
|
+
subscribers_to_be_deleted = ::Set.new
|
14
|
+
|
15
|
+
event_names.each(&BroadcastWith[subscribers_list, subject, data, subscribers_to_be_deleted])
|
16
|
+
|
17
|
+
subscribers_to_be_deleted.each { |subscriber| subscribers_list.delete(subscriber) }
|
18
|
+
end
|
19
|
+
|
20
|
+
private
|
21
|
+
|
22
|
+
BroadcastWith = -> (subscribers, subject, data, subscribers_to_be_deleted) do
|
23
|
+
-> (event_name) do
|
24
|
+
subscribers.each do |subscriber|
|
25
|
+
notified = NotifySubscriber.(subscriber, subject, data, event_name)
|
26
|
+
perform_once = subscriber[3]
|
27
|
+
|
28
|
+
subscribers_to_be_deleted.add(subscriber) if notified && perform_once
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
NotifySubscriber = -> (subscriber, subject, data, event_name) do
|
34
|
+
NotifyObserver.(subscriber, subject, data, event_name) ||
|
35
|
+
NotifyCallable.(subscriber, subject, data, event_name) ||
|
36
|
+
false
|
37
|
+
end
|
38
|
+
|
39
|
+
NotifyObserver = -> (subscriber, subject, data, event_name) do
|
40
|
+
strategy, observer, context = subscriber
|
41
|
+
|
42
|
+
return unless strategy == :observer && observer.respond_to?(event_name)
|
43
|
+
|
44
|
+
event = Event.new(event_name, subject, context, data)
|
45
|
+
|
46
|
+
handler = observer.is_a?(Proc) ? observer : observer.method(event.name)
|
47
|
+
handler.arity == 1 ? handler.call(event.subject) : handler.call(event.subject, event)
|
48
|
+
|
49
|
+
true
|
50
|
+
end
|
51
|
+
|
52
|
+
NotifyCallable = -> (subscriber, subject, data, event_name) do
|
53
|
+
strategy, observer, options = subscriber
|
54
|
+
|
55
|
+
return unless strategy == :callable && observer == event_name
|
56
|
+
|
57
|
+
callable, with, context = options[0], options[1], options[2]
|
58
|
+
|
59
|
+
if with && !with.is_a?(Proc)
|
60
|
+
callable.call(with)
|
61
|
+
|
62
|
+
return true
|
63
|
+
end
|
64
|
+
|
65
|
+
event = Event.new(event_name, subject, context, data)
|
66
|
+
|
67
|
+
callable.call(with.is_a?(Proc) ? with.call(event) : event)
|
68
|
+
|
69
|
+
true
|
70
|
+
end
|
71
|
+
|
72
|
+
private_constant :BroadcastWith, :NotifySubscriber, :NotifyCallable, :NotifyObserver
|
73
|
+
end
|
74
|
+
|
75
|
+
private_constant :Broadcast
|
76
|
+
end
|
77
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Micro
|
4
|
+
module Observers
|
5
|
+
|
6
|
+
class Event
|
7
|
+
require 'micro/observers/event/names'
|
8
|
+
|
9
|
+
attr_reader :name, :subject, :context, :data
|
10
|
+
|
11
|
+
def initialize(name, subject, context, data)
|
12
|
+
@name, @subject = name, subject
|
13
|
+
@context, @data = context, data
|
14
|
+
end
|
15
|
+
|
16
|
+
alias ctx context
|
17
|
+
alias subj subject
|
18
|
+
end
|
19
|
+
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Micro
|
4
|
+
module Observers
|
5
|
+
|
6
|
+
class Event::Names
|
7
|
+
EMPTY_ARRAY = [].freeze
|
8
|
+
|
9
|
+
def self.[](value, default: EMPTY_ARRAY)
|
10
|
+
values = Utils::Arrays.flatten_and_compact(value)
|
11
|
+
|
12
|
+
values.empty? ? default : values
|
13
|
+
end
|
14
|
+
|
15
|
+
NO_EVENTS_MSG = 'no events (expected at least 1)'.freeze
|
16
|
+
|
17
|
+
def self.fetch(value)
|
18
|
+
values = self[value]
|
19
|
+
|
20
|
+
return values unless values.empty?
|
21
|
+
|
22
|
+
raise ArgumentError, NO_EVENTS_MSG
|
23
|
+
end
|
24
|
+
|
25
|
+
private_constant :NO_EVENTS_MSG
|
26
|
+
end
|
27
|
+
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Micro
|
4
|
+
module Observers
|
5
|
+
module For
|
6
|
+
|
7
|
+
module ActiveModel
|
8
|
+
module ClassMethods
|
9
|
+
def notify_observers!(events)
|
10
|
+
proc do |object|
|
11
|
+
object.observers.subject_changed!
|
12
|
+
object.observers.send(:broadcast_if_subject_changed, events)
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
def notify_observers(*events)
|
17
|
+
notify_observers!(Event::Names.fetch(events))
|
18
|
+
end
|
19
|
+
|
20
|
+
def notify_observers_on(*callback_methods)
|
21
|
+
Utils::Arrays.fetch_from_args(callback_methods).each do |callback_method|
|
22
|
+
self.public_send(callback_method, ¬ify_observers!([callback_method]))
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
def self.included(base)
|
28
|
+
base.extend(ClassMethods)
|
29
|
+
base.send(:private_class_method, :notify_observers!)
|
30
|
+
base.send(:include, ::Micro::Observers)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Micro
|
4
|
+
module Observers
|
5
|
+
module For
|
6
|
+
require 'micro/observers/for/active_model'
|
7
|
+
|
8
|
+
module ActiveRecord
|
9
|
+
def self.included(base)
|
10
|
+
base.send(:include, ::Micro::Observers::For::ActiveModel)
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,100 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Micro
|
4
|
+
module Observers
|
5
|
+
|
6
|
+
class Set
|
7
|
+
def self.for(subject)
|
8
|
+
new(subject)
|
9
|
+
end
|
10
|
+
|
11
|
+
def initialize(subject, subscribers: nil)
|
12
|
+
@subject = subject
|
13
|
+
@subject_changed = false
|
14
|
+
@subscribers = Subscribers.new(subscribers)
|
15
|
+
end
|
16
|
+
|
17
|
+
def count; @subscribers.count; end
|
18
|
+
def none?; @subscribers.none?; end
|
19
|
+
def some?; !none?; end
|
20
|
+
|
21
|
+
def subject_changed?
|
22
|
+
@subject_changed
|
23
|
+
end
|
24
|
+
|
25
|
+
INVALID_BOOLEAN_MSG = 'expected a boolean (true, false)'.freeze
|
26
|
+
|
27
|
+
def subject_changed(state)
|
28
|
+
return @subject_changed = state if state == true || state == false
|
29
|
+
|
30
|
+
raise ArgumentError, INVALID_BOOLEAN_MSG
|
31
|
+
end
|
32
|
+
|
33
|
+
def subject_changed!
|
34
|
+
subject_changed(true)
|
35
|
+
end
|
36
|
+
|
37
|
+
def include?(observer)
|
38
|
+
@subscribers.include?(observer)
|
39
|
+
end
|
40
|
+
alias included? include?
|
41
|
+
|
42
|
+
def attach(*args); @subscribers.attach(args) and self; end
|
43
|
+
def detach(*args); @subscribers.detach(args) and self; end
|
44
|
+
|
45
|
+
def on(options = Utils::EMPTY_HASH); @subscribers.on(options) and self; end
|
46
|
+
def once(options = Utils::EMPTY_HASH); @subscribers.once(options) and self; end
|
47
|
+
|
48
|
+
def off(*args)
|
49
|
+
@subscribers.off(args) and self
|
50
|
+
end
|
51
|
+
|
52
|
+
def notify(*events, data: nil)
|
53
|
+
broadcast_if_subject_changed(Event::Names.fetch(events), data)
|
54
|
+
end
|
55
|
+
|
56
|
+
def notify!(*events, data: nil)
|
57
|
+
broadcast(Event::Names.fetch(events), data)
|
58
|
+
end
|
59
|
+
|
60
|
+
CALL_EVENT = [:call].freeze
|
61
|
+
|
62
|
+
def call(*events, data: nil)
|
63
|
+
broadcast_if_subject_changed(Event::Names[events, default: CALL_EVENT], data)
|
64
|
+
end
|
65
|
+
|
66
|
+
def call!(*events, data: nil)
|
67
|
+
broadcast(Event::Names[events, default: CALL_EVENT], data)
|
68
|
+
end
|
69
|
+
|
70
|
+
def inspect
|
71
|
+
subs = @subscribers.to_inspect
|
72
|
+
|
73
|
+
'#<%s @subject=%s @subject_changed=%p @subscribers=%p>' % [self.class, @subject, @subject_changed, subs]
|
74
|
+
end
|
75
|
+
|
76
|
+
private
|
77
|
+
|
78
|
+
def broadcast_if_subject_changed(event_names, data = nil)
|
79
|
+
return self if none? || !subject_changed?
|
80
|
+
|
81
|
+
broadcast(event_names, data)
|
82
|
+
|
83
|
+
subject_changed(false)
|
84
|
+
|
85
|
+
self
|
86
|
+
end
|
87
|
+
|
88
|
+
def broadcast(event_names, data)
|
89
|
+
return self if none?
|
90
|
+
|
91
|
+
Broadcast.call(@subscribers, @subject, data, event_names)
|
92
|
+
|
93
|
+
self
|
94
|
+
end
|
95
|
+
|
96
|
+
private_constant :INVALID_BOOLEAN_MSG, :CALL_EVENT
|
97
|
+
end
|
98
|
+
|
99
|
+
end
|
100
|
+
end
|
@@ -0,0 +1,110 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Micro
|
4
|
+
module Observers
|
5
|
+
|
6
|
+
class Subscribers
|
7
|
+
EqualTo = -> (observer) { -> subscriber { GetObserver[subscriber] == observer } }
|
8
|
+
GetObserver = -> subscriber { subscriber[0] == :observer ? subscriber[1] : subscriber[2][0] }
|
9
|
+
MapObserver = -> (observer, options, once) { [:observer, observer, options[:context], once] }
|
10
|
+
MapObserversToInitialize = -> arg do
|
11
|
+
Utils::Arrays.flatten_and_compact(arg).map do |observer|
|
12
|
+
MapObserver[observer, Utils::EMPTY_HASH, false]
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
attr_reader :list
|
17
|
+
|
18
|
+
def initialize(arg)
|
19
|
+
@list = arg.is_a?(Array) ? MapObserversToInitialize[arg] : []
|
20
|
+
end
|
21
|
+
|
22
|
+
def to_inspect
|
23
|
+
none? ? @list : @list.map(&GetObserver)
|
24
|
+
end
|
25
|
+
|
26
|
+
def count
|
27
|
+
@list.size
|
28
|
+
end
|
29
|
+
|
30
|
+
def none?
|
31
|
+
@list.empty?
|
32
|
+
end
|
33
|
+
|
34
|
+
def include?(subscriber)
|
35
|
+
@list.any?(&EqualTo[subscriber])
|
36
|
+
end
|
37
|
+
|
38
|
+
def attach(args)
|
39
|
+
options = args.last.is_a?(Hash) ? args.pop : Utils::EMPTY_HASH
|
40
|
+
|
41
|
+
once = options.frozen? ? false : options.delete(:perform_once)
|
42
|
+
|
43
|
+
Utils::Arrays.fetch_from_args(args).each do |observer|
|
44
|
+
@list << MapObserver[observer, options, once] unless include?(observer)
|
45
|
+
end
|
46
|
+
|
47
|
+
true
|
48
|
+
end
|
49
|
+
|
50
|
+
def detach(args)
|
51
|
+
Utils::Arrays.fetch_from_args(args).each do |observer|
|
52
|
+
delete_observer(observer)
|
53
|
+
end
|
54
|
+
|
55
|
+
true
|
56
|
+
end
|
57
|
+
|
58
|
+
def on(options)
|
59
|
+
on!(options, once: false)
|
60
|
+
end
|
61
|
+
|
62
|
+
def once(options)
|
63
|
+
on!(options, once: true)
|
64
|
+
end
|
65
|
+
|
66
|
+
EventNameToCall = -> event_name { -> subscriber { subscriber[0] == :callable && subscriber[1] == event_name } }
|
67
|
+
|
68
|
+
def off(args)
|
69
|
+
Utils::Arrays.fetch_from_args(args).each do |value|
|
70
|
+
if value.is_a?(Symbol)
|
71
|
+
@list.delete_if(&EventNameToCall[value])
|
72
|
+
else
|
73
|
+
delete_observer(value)
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
private
|
79
|
+
|
80
|
+
def delete_observer(observer)
|
81
|
+
@list.delete_if(&EqualTo[observer])
|
82
|
+
end
|
83
|
+
|
84
|
+
def on!(options, once:)
|
85
|
+
event, callable, with, context = options[:event], options[:call], options[:with], options[:context]
|
86
|
+
|
87
|
+
return true unless event.is_a?(Symbol) && callable.respond_to?(:call)
|
88
|
+
|
89
|
+
observer = [callable, with, context]
|
90
|
+
|
91
|
+
@list << [:callable, event, observer, once] unless include_callable?(event, observer)
|
92
|
+
|
93
|
+
true
|
94
|
+
end
|
95
|
+
|
96
|
+
CallableHaving = -> (event, observer) do
|
97
|
+
-> subs { subs[0] == :callable && subs[1] == event && subs[2] == observer }
|
98
|
+
end
|
99
|
+
|
100
|
+
def include_callable?(event, observer)
|
101
|
+
@list.any?(&CallableHaving[event, observer])
|
102
|
+
end
|
103
|
+
|
104
|
+
private_constant :EqualTo, :EventNameToCall, :CallableHaving
|
105
|
+
private_constant :GetObserver, :MapObserver, :MapObserversToInitialize
|
106
|
+
end
|
107
|
+
|
108
|
+
private_constant :Subscribers
|
109
|
+
end
|
110
|
+
end
|
@@ -6,8 +6,18 @@ module Micro
|
|
6
6
|
module Utils
|
7
7
|
EMPTY_HASH = {}.freeze
|
8
8
|
|
9
|
-
|
10
|
-
|
9
|
+
module Arrays
|
10
|
+
def self.fetch_from_args(args)
|
11
|
+
args.size == 1 && (first = args[0]).is_a?(::Array) ? first : args
|
12
|
+
end
|
13
|
+
|
14
|
+
def self.flatten_and_compact(value)
|
15
|
+
return [] unless value
|
16
|
+
|
17
|
+
array = Array(value).flatten
|
18
|
+
array.compact!
|
19
|
+
array
|
20
|
+
end
|
11
21
|
end
|
12
22
|
end
|
13
23
|
|