u-observers 0.7.0 → 2.1.0

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