u-observers 1.0.0 → 2.3.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,11 +3,13 @@ require 'micro/observers/version'
3
3
  module Micro
4
4
  module Observers
5
5
  require 'micro/observers/utils'
6
- require 'micro/observers/events'
7
- require 'micro/observers/manager'
6
+ require 'micro/observers/event'
7
+ require 'micro/observers/subscribers'
8
+ require 'micro/observers/broadcast'
9
+ require 'micro/observers/set'
8
10
 
9
11
  def observers
10
- @__observers ||= Observers::Manager.for(self)
12
+ @__observers ||= Observers::Set.for(self)
11
13
  end
12
14
  end
13
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
@@ -3,9 +3,11 @@
3
3
  module Micro
4
4
  module Observers
5
5
 
6
- module Events
7
- def self.[](value, default: Utils::EMPTY_ARRAY)
8
- values = Utils.compact_array(value)
6
+ class Event::Names
7
+ EMPTY_ARRAY = [].freeze
8
+
9
+ def self.[](value, default: EMPTY_ARRAY)
10
+ values = Utils::Arrays.flatten_and_compact(value)
9
11
 
10
12
  values.empty? ? default : values
11
13
  end
@@ -14,11 +14,11 @@ module Micro
14
14
  end
15
15
 
16
16
  def notify_observers(*events)
17
- notify_observers!(Events.fetch(events))
17
+ notify_observers!(Event::Names.fetch(events))
18
18
  end
19
19
 
20
20
  def notify_observers_on(*callback_methods)
21
- Utils.compact_array(callback_methods).each do |callback_method|
21
+ Utils::Arrays.fetch_from_args(callback_methods).each do |callback_method|
22
22
  self.public_send(callback_method, &notify_observers!([callback_method]))
23
23
  end
24
24
  end
@@ -0,0 +1,107 @@
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
+ CallableOptions = -> (arg, block) do
46
+ arg.is_a?(Symbol) && block ? { event: arg, call: block } : arg
47
+ end
48
+
49
+ def on(arg = Utils::EMPTY_HASH, &block)
50
+ @subscribers.on(CallableOptions[arg, block]) and self
51
+ end
52
+
53
+ def once(arg = Utils::EMPTY_HASH, &block)
54
+ @subscribers.once(CallableOptions[arg, block]) and self
55
+ end
56
+
57
+ def off(*args); @subscribers.off(args) and self; end
58
+
59
+ def notify(*events, data: nil)
60
+ broadcast_if_subject_changed(Event::Names.fetch(events), data)
61
+ end
62
+
63
+ def notify!(*events, data: nil)
64
+ broadcast(Event::Names.fetch(events), data)
65
+ end
66
+
67
+ CALL_EVENT = [:call].freeze
68
+
69
+ def call(*events, data: nil)
70
+ broadcast_if_subject_changed(Event::Names[events, default: CALL_EVENT], data)
71
+ end
72
+
73
+ def call!(*events, data: nil)
74
+ broadcast(Event::Names[events, default: CALL_EVENT], data)
75
+ end
76
+
77
+ def inspect
78
+ subs = @subscribers.to_inspect
79
+
80
+ '#<%s @subject=%s @subject_changed=%p @subscribers=%p>' % [self.class, @subject, @subject_changed, subs]
81
+ end
82
+
83
+ private
84
+
85
+ def broadcast_if_subject_changed(event_names, data = nil)
86
+ return self if none? || !subject_changed?
87
+
88
+ broadcast(event_names, data)
89
+
90
+ subject_changed(false)
91
+
92
+ self
93
+ end
94
+
95
+ def broadcast(event_names, data)
96
+ return self if none?
97
+
98
+ Broadcast.call(@subscribers, @subject, data, event_names)
99
+
100
+ self
101
+ end
102
+
103
+ private_constant :INVALID_BOOLEAN_MSG, :CALL_EVENT; :CallableOptions
104
+ end
105
+
106
+ end
107
+ 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
@@ -5,10 +5,19 @@ module Micro
5
5
 
6
6
  module Utils
7
7
  EMPTY_HASH = {}.freeze
8
- EMPTY_ARRAY = [].freeze
9
8
 
10
- def self.compact_array(value)
11
- Array(value).flatten.tap(&:compact!)
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
12
21
  end
13
22
  end
14
23
 
@@ -1,5 +1,5 @@
1
1
  module Micro
2
2
  module Observers
3
- VERSION = '1.0.0'
3
+ VERSION = '2.3.0'
4
4
  end
5
5
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: u-observers
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.0
4
+ version: 2.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Rodrigo Serradura
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2020-10-05 00:00:00.000000000 Z
11
+ date: 2020-11-25 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -53,14 +53,18 @@ files:
53
53
  - Gemfile
54
54
  - LICENSE.txt
55
55
  - README.md
56
+ - README.pt-BR.md
56
57
  - Rakefile
57
58
  - bin/console
58
59
  - bin/setup
59
60
  - lib/micro/observers.rb
60
- - lib/micro/observers/events.rb
61
+ - lib/micro/observers/broadcast.rb
62
+ - lib/micro/observers/event.rb
63
+ - lib/micro/observers/event/names.rb
61
64
  - lib/micro/observers/for/active_model.rb
62
65
  - lib/micro/observers/for/active_record.rb
63
- - lib/micro/observers/manager.rb
66
+ - lib/micro/observers/set.rb
67
+ - lib/micro/observers/subscribers.rb
64
68
  - lib/micro/observers/utils.rb
65
69
  - lib/micro/observers/version.rb
66
70
  - lib/u-observers.rb
@@ -1,172 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Micro
4
- module Observers
5
-
6
- class Manager
7
- MapSubscriber = -> (observer, options) { [:observer, observer, options[:context]] }
8
-
9
- MapSubscribers = -> (value) do
10
- array = Utils.compact_array(value.kind_of?(Array) ? value : [])
11
- array.map { |observer| MapSubscriber[observer, Utils::EMPTY_HASH] }
12
- end
13
-
14
- GetObserver = -> subscriber { subscriber[0] == :observer ? subscriber[1] : subscriber[2][0] }
15
-
16
- EqualTo = -> (observer) { -> subscriber { GetObserver[subscriber] == observer } }
17
-
18
- def self.for(subject)
19
- new(subject)
20
- end
21
-
22
- def initialize(subject, subscribers: nil)
23
- @subject = subject
24
-
25
- @subject_changed = false
26
-
27
- @subscribers = MapSubscribers.call(subscribers)
28
- end
29
-
30
- def count
31
- @subscribers.size
32
- end
33
-
34
- def none?
35
- @subscribers.empty?
36
- end
37
-
38
- def some?
39
- !none?
40
- end
41
-
42
- def subject_changed?
43
- @subject_changed
44
- end
45
-
46
- INVALID_BOOLEAN_MSG = 'expected a boolean (true, false)'.freeze
47
-
48
- def subject_changed(state)
49
- if state == true || state == false
50
- @subject_changed = state
51
-
52
- return self
53
- end
54
-
55
- raise ArgumentError, INVALID_BOOLEAN_MSG
56
- end
57
-
58
- def subject_changed!
59
- subject_changed(true)
60
- end
61
-
62
- def included?(observer)
63
- @subscribers.any?(&EqualTo[observer])
64
- end
65
-
66
- def attach(*args)
67
- options = args.last.is_a?(Hash) ? args.pop : Utils::EMPTY_HASH
68
-
69
- Utils.compact_array(args).each do |observer|
70
- @subscribers << MapSubscriber[observer, options] unless included?(observer)
71
- end
72
-
73
- self
74
- end
75
-
76
- def detach(*args)
77
- Utils.compact_array(args).each do |observer|
78
- @subscribers.delete_if(&EqualTo[observer])
79
- end
80
-
81
- self
82
- end
83
-
84
- def on(options = Utils::EMPTY_HASH)
85
- event, callable, with = options[:event], options[:call], options[:with]
86
-
87
- return self unless event.is_a?(Symbol) && callable.respond_to?(:call)
88
-
89
- @subscribers << [:callable, event, [callable, with]] unless included?(callable)
90
-
91
- self
92
- end
93
-
94
- def notify(*events)
95
- broadcast_if_subject_changed(Events.fetch(events))
96
-
97
- self
98
- end
99
-
100
- def notify!(*events)
101
- broadcast(Events.fetch(events))
102
-
103
- self
104
- end
105
-
106
- CALL_EVENT = [:call].freeze
107
-
108
- def call(*events)
109
- broadcast_if_subject_changed(Events[events, default: CALL_EVENT])
110
-
111
- self
112
- end
113
-
114
- def call!(*events)
115
- broadcast(Events[events, default: CALL_EVENT])
116
-
117
- self
118
- end
119
-
120
- def inspect
121
- subs = @subscribers.empty? ? @subscribers : @subscribers.map(&GetObserver)
122
-
123
- '<#%s @subject=%s @subject_changed=%p @subscribers=%p>' % [self.class, @subject, @subject_changed, subs]
124
- end
125
-
126
- private
127
-
128
- def broadcast_if_subject_changed(events)
129
- return unless subject_changed?
130
-
131
- broadcast(events)
132
-
133
- subject_changed(false)
134
- end
135
-
136
- def broadcast(events)
137
- return if @subscribers.empty?
138
-
139
- events.each do |event|
140
- @subscribers.each do |strategy, observer, context|
141
- case strategy
142
- when :observer then notify_observer(observer, event, context)
143
- when :callable then notify_callable(observer, event, context)
144
- end
145
- end
146
- end
147
- end
148
-
149
- def notify_observer(observer, method_name, context)
150
- return unless observer.respond_to?(method_name)
151
-
152
- handler = observer.is_a?(Proc) ? observer : observer.method(method_name)
153
-
154
- handler.arity == 1 ? handler.call(@subject) : handler.call(@subject, context)
155
- end
156
-
157
- def notify_callable(expected_event, event, context)
158
- return if expected_event != event
159
-
160
- callable, with = context[0], context[1]
161
-
162
- arg = with.is_a?(Proc) ? with.call(@subject) : (with || @subject)
163
-
164
- callable.call(arg)
165
- end
166
-
167
- private_constant :INVALID_BOOLEAN_MSG, :CALL_EVENT
168
- private_constant :MapSubscriber, :MapSubscribers, :GetObserver, :EqualTo
169
- end
170
-
171
- end
172
- end