u-observers 0.7.0 → 2.1.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.
@@ -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.flatten_and_compact(callback_methods).each do |callback_method|
22
+ self.public_send(callback_method, &notify_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,178 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Micro
4
+ module Observers
5
+
6
+ class Set
7
+ EMPTY_HASH = {}.freeze
8
+
9
+ MapSubscriber = -> (observer, options) { [:observer, observer, options[:context]] }
10
+
11
+ MapSubscribers = -> (value) do
12
+ array = Utils::Arrays.flatten_and_compact(value.kind_of?(Array) ? value : [])
13
+ array.map { |observer| MapSubscriber[observer, EMPTY_HASH] }
14
+ end
15
+
16
+ GetObserver = -> subscriber { subscriber[0] == :observer ? subscriber[1] : subscriber[2][0] }
17
+
18
+ EqualTo = -> (observer) { -> subscriber { GetObserver[subscriber] == observer } }
19
+
20
+ def self.for(subject)
21
+ new(subject)
22
+ end
23
+
24
+ def initialize(subject, subscribers: nil)
25
+ @subject = subject
26
+
27
+ @subject_changed = false
28
+
29
+ @subscribers = MapSubscribers.call(subscribers)
30
+ end
31
+
32
+ def count
33
+ @subscribers.size
34
+ end
35
+
36
+ def none?
37
+ @subscribers.empty?
38
+ end
39
+
40
+ def some?
41
+ !none?
42
+ end
43
+
44
+ def subject_changed?
45
+ @subject_changed
46
+ end
47
+
48
+ INVALID_BOOLEAN_MSG = 'expected a boolean (true, false)'.freeze
49
+
50
+ def subject_changed(state)
51
+ return @subject_changed = state if state == true || state == false
52
+
53
+ raise ArgumentError, INVALID_BOOLEAN_MSG
54
+ end
55
+
56
+ def subject_changed!
57
+ subject_changed(true)
58
+ end
59
+
60
+ def included?(observer)
61
+ @subscribers.any?(&EqualTo[observer])
62
+ end
63
+
64
+ def attach(*args)
65
+ options = args.last.is_a?(Hash) ? args.pop : EMPTY_HASH
66
+
67
+ Utils::Arrays.flatten_and_compact(args).each do |observer|
68
+ @subscribers << MapSubscriber[observer, options] unless included?(observer)
69
+ end
70
+
71
+ self
72
+ end
73
+
74
+ def detach(*args)
75
+ Utils::Arrays.flatten_and_compact(args).each do |observer|
76
+ @subscribers.delete_if(&EqualTo[observer])
77
+ end
78
+
79
+ self
80
+ end
81
+
82
+ def on(options = EMPTY_HASH)
83
+ event, callable, with, context = options[:event], options[:call], options[:with], options[:context]
84
+
85
+ return self unless event.is_a?(Symbol) && callable.respond_to?(:call)
86
+
87
+ @subscribers << [:callable, event, [callable, with, context]] unless included?(callable)
88
+
89
+ self
90
+ end
91
+
92
+ def notify(*events, data: nil)
93
+ broadcast_if_subject_changed(Event::Names.fetch(events), data)
94
+
95
+ self
96
+ end
97
+
98
+ def notify!(*events, data: nil)
99
+ broadcast(Event::Names.fetch(events), data)
100
+
101
+ self
102
+ end
103
+
104
+ CALL_EVENT = [:call].freeze
105
+
106
+ def call(*events, data: nil)
107
+ broadcast_if_subject_changed(Event::Names[events, default: CALL_EVENT], data)
108
+
109
+ self
110
+ end
111
+
112
+ def call!(*events, data: nil)
113
+ broadcast(Event::Names[events, default: CALL_EVENT], data)
114
+
115
+ self
116
+ end
117
+
118
+ def inspect
119
+ subs = @subscribers.empty? ? @subscribers : @subscribers.map(&GetObserver)
120
+
121
+ '<#%s @subject=%s @subject_changed=%p @subscribers=%p>' % [self.class, @subject, @subject_changed, subs]
122
+ end
123
+
124
+ private
125
+
126
+ def broadcast_if_subject_changed(events, data = nil)
127
+ return unless subject_changed?
128
+
129
+ broadcast(events, data)
130
+
131
+ subject_changed(false)
132
+ end
133
+
134
+ def broadcast(event_names, data)
135
+ return if @subscribers.empty?
136
+
137
+ event_names.each do |event_name|
138
+ @subscribers.each do |strategy, observer, context|
139
+ case strategy
140
+ when :observer then notify_observer(observer, event_name, context, data)
141
+ when :callable then notify_callable(observer, event_name, context, data)
142
+ end
143
+ end
144
+ end
145
+ end
146
+
147
+ def notify_observer(observer, event_name, context, data)
148
+ return unless observer.respond_to?(event_name)
149
+
150
+ handler = observer.is_a?(Proc) ? observer : observer.method(event_name)
151
+
152
+ return handler.call(@subject) if handler.arity == 1
153
+
154
+ handler.call(@subject, Event.new(event_name, @subject, context, data))
155
+ end
156
+
157
+ def notify_callable(expected_event_name, event_name, opt, data)
158
+ return if expected_event_name != event_name
159
+
160
+ callable, with, context = opt[0], opt[1], opt[2]
161
+ callable_arg =
162
+ if with && !with.is_a?(Proc)
163
+ with
164
+ else
165
+ event = Event.new(event_name, @subject, context, data)
166
+
167
+ with.is_a?(Proc) ? with.call(event) : event
168
+ end
169
+
170
+ callable.call(callable_arg)
171
+ end
172
+
173
+ private_constant :EMPTY_HASH, :INVALID_BOOLEAN_MSG, :CALL_EVENT
174
+ private_constant :MapSubscriber, :MapSubscribers, :GetObserver, :EqualTo
175
+ end
176
+
177
+ end
178
+ end
@@ -4,10 +4,10 @@ module Micro
4
4
  module Observers
5
5
 
6
6
  module Utils
7
- EMPTY_HASH = {}.freeze
8
-
9
- def self.compact_array(value)
10
- Array(value).flatten.tap(&:compact!)
7
+ module Arrays
8
+ def self.flatten_and_compact(value)
9
+ Array(value).flatten.tap(&:compact!)
10
+ end
11
11
  end
12
12
  end
13
13
 
@@ -1,5 +1,5 @@
1
1
  module Micro
2
2
  module Observers
3
- VERSION = '0.7.0'
3
+ VERSION = '2.1.0'
4
4
  end
5
5
  end
@@ -0,0 +1,2 @@
1
+ require 'micro/observers'
2
+ require 'micro/observers/for/active_model'
@@ -0,0 +1,2 @@
1
+ require 'micro/observers'
2
+ require 'micro/observers/for/active_record'
data/test.sh ADDED
@@ -0,0 +1,11 @@
1
+ #!/bin/bash
2
+
3
+ bundle
4
+
5
+ rm Gemfile.lock
6
+
7
+ source $(dirname $0)/.travis.sh
8
+
9
+ rm Gemfile.lock
10
+
11
+ bundle
@@ -10,7 +10,6 @@ Gem::Specification.new do |spec|
10
10
  spec.description = %q{Simple and powerful implementation of the observer pattern.}
11
11
  spec.homepage = 'https://github.com/serradura/u-observers'
12
12
  spec.license = 'MIT'
13
- spec.required_ruby_version = Gem::Requirement.new('>= 2.2.0')
14
13
 
15
14
  spec.metadata['homepage_uri'] = spec.homepage
16
15
  spec.metadata['source_code_uri'] = 'https://github.com/serradura/u-observers'
@@ -24,4 +23,9 @@ Gem::Specification.new do |spec|
24
23
  spec.bindir = 'exe'
25
24
  spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
26
25
  spec.require_paths = ['lib']
26
+
27
+ spec.required_ruby_version = Gem::Requirement.new('>= 2.2.0')
28
+
29
+ spec.add_development_dependency 'bundler'
30
+ spec.add_development_dependency 'rake', '~> 13.0'
27
31
  end
metadata CHANGED
@@ -1,15 +1,43 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: u-observers
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.7.0
4
+ version: 2.1.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-09-26 00:00:00.000000000 Z
12
- dependencies: []
11
+ date: 2020-10-16 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: bundler
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: '0'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: '0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rake
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '13.0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '13.0'
13
41
  description: Simple and powerful implementation of the observer pattern.
14
42
  email:
15
43
  - rodrigo.serradura@gmail.com
@@ -18,20 +46,29 @@ extensions: []
18
46
  extra_rdoc_files: []
19
47
  files:
20
48
  - ".gitignore"
49
+ - ".tool-versions"
50
+ - ".travis.sh"
21
51
  - ".travis.yml"
22
52
  - CODE_OF_CONDUCT.md
23
53
  - Gemfile
24
54
  - LICENSE.txt
25
55
  - README.md
56
+ - README.pt-BR.md
26
57
  - Rakefile
27
58
  - bin/console
28
59
  - bin/setup
29
60
  - lib/micro/observers.rb
30
- - lib/micro/observers/events_or_actions.rb
31
- - lib/micro/observers/manager.rb
61
+ - lib/micro/observers/event.rb
62
+ - lib/micro/observers/event/names.rb
63
+ - lib/micro/observers/for/active_model.rb
64
+ - lib/micro/observers/for/active_record.rb
65
+ - lib/micro/observers/set.rb
32
66
  - lib/micro/observers/utils.rb
33
67
  - lib/micro/observers/version.rb
34
68
  - lib/u-observers.rb
69
+ - lib/u-observers/for/active_model.rb
70
+ - lib/u-observers/for/active_record.rb
71
+ - test.sh
35
72
  - u-observers.gemspec
36
73
  homepage: https://github.com/serradura/u-observers
37
74
  licenses:
@@ -1,25 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Micro
4
- module Observers
5
-
6
- module EventsOrActions
7
- DEFAULTS = [:call]
8
-
9
- def self.[](value)
10
- values = Utils.compact_array(value)
11
-
12
- values.empty? ? DEFAULTS : values
13
- end
14
-
15
- def self.fetch_actions(hash)
16
- return self[hash[:actions] || hash.fetch(:action)] if hash.is_a?(Hash)
17
-
18
- raise ArgumentError, 'expected a hash with the key :action or :actions'
19
- end
20
-
21
- private_constant :DEFAULTS
22
- end
23
-
24
- end
25
- end