u-observers 1.0.0 → 2.3.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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