observers 0.8.0 → 1.0.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: '03108108c365bb1857d0ff575823ba87a1c530e0c83014534e8f6d696ea0a3dd'
4
- data.tar.gz: be85d6ddfe681c52ea79b3f09dc1538c1da6c494fbf8667cc2a98237f3e1c12e
3
+ metadata.gz: 1baa5ca1c0866fe6e708bc669db53424411099c44a7747cbe441f3e50191b3b8
4
+ data.tar.gz: 361100827e869fa95814d94ae4789637bb7b794d3e55063414adc647683e47e9
5
5
  SHA512:
6
- metadata.gz: d3845675c5749e181bea80f4082ab86df3072258867847562a214e872f02bc668688dc41e02e95e5ab91c3c2bd5649986cc850c108141b06f8c40054fd97b010
7
- data.tar.gz: 94ca9aaedd1c66bc410115e4bd9426a38dd497eaa8563d68488a4fb4b917d9e7150f31e64735fdffd058bb669570bcf81bb9c91e70904882cc902933b2fafb89
6
+ metadata.gz: 319423e0cd9b9cc4b2c68dd5887eef9c0cae72102957d79bddb86e13f18dcc369fdb5b5d361d88c5b27fb1503c80883c2c3692a50fb847c01b3caa206a622a07
7
+ data.tar.gz: afcfe235cc45aad95d657d3da28c118d116d767cda8f24790c7c99467cfc660493c18896eb0b0dcda893a0117b45b0e74b80ec290439a79e16821e54b0183811
data/lib/models/key.rb CHANGED
@@ -1,6 +1,8 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Observers
4
+ class NoObserversError < StandardError; end
5
+
4
6
  class Key
5
7
  attr_reader :observers
6
8
 
@@ -10,21 +12,19 @@ module Observers
10
12
  end
11
13
 
12
14
  def observe(object:, action:)
13
- # TODO: We can observe objects directly, no need to wrap in an observer... no need to let the object's observer override the action?
15
+ # TODO: We can observe objects directly, no need to wrap in an observer, unless need to let the object's observer override the action?
14
16
  # A future reason I can think of for keeping observer wrapper is to track whether the object has implemented certain actions/methods.
15
17
  @observers << Observer.new(object:, action:)
16
18
  end
17
19
 
18
20
  # @returns: The result of the last observer with a non-nil value.
19
21
  def trigger(action: nil, event: nil)
20
- empty_observers_callback
21
-
22
- action = event.action if event && action.nil?
23
- action = :handle if action.nil?
22
+ # TODO: Breaks some tests... should it? Yes. Should it be disabled when testing? Yes.
23
+ # raise NoObserversError, "No observers configured for #{@key}" if @observers.empty?
24
24
 
25
25
  last_result = nil
26
26
 
27
- @observers.each do |observer|
27
+ per_observer_per_action(action:, event:) do |observer, action|
28
28
  result = observer.trigger(action:, event:)
29
29
  last_result = result unless result.nil?
30
30
  yield if block_given?
@@ -33,26 +33,41 @@ module Observers
33
33
  last_result
34
34
  end
35
35
 
36
- # @returns: The result of the first observer with a non-nil value.
36
+ # @returns: The result of the first observer and the first action with a non-nil value.
37
37
  def take(action: nil, event: nil)
38
- empty_observers_callback
39
-
40
- action = event.action if event && action.nil?
41
- action = :handle if action.nil?
38
+ # TODO: Breaks some tests... should it? Yes. Should it be disabled when testing? Yes.
39
+ # raise NoObserversError, "No observers configured for #{@key}" if @observers.empty?
42
40
 
43
- @observers.each do |observer|
41
+ per_observer_per_action(action:, event:) do |observer, action|
44
42
  result = observer.trigger(action:, event:)
45
43
  yield if block_given?
46
44
  return result unless result.nil?
47
45
  end
48
46
 
49
- nil # This is a bad day for the take method, one of the worst.
47
+ # None of the observers returned a non-nil value.
48
+ nil
50
49
  end
51
50
 
52
51
  private
53
52
 
54
- def empty_observers_callback
55
- Observers.config.empty_observers_callback.call(@key) if Observers.config.empty_observers_callback && @observers.empty?
53
+ def per_observer_per_action(action:, event:)
54
+ action ||= event&.action
55
+
56
+ if action
57
+ @observers.each do |observer|
58
+ yield observer, action
59
+ end
60
+ elsif event&.actions
61
+ @observers.each do |observer|
62
+ event.actions.each do |action|
63
+ yield observer, action
64
+ end
65
+ end
66
+ else
67
+ @observers.each do |observer|
68
+ yield observer, :handle
69
+ end
70
+ end
56
71
  end
57
72
  end
58
73
  end
@@ -1,5 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require_relative 'observer_error'
4
+
3
5
  module Observers
4
6
  class Observer
5
7
  attr_reader :object, :action
@@ -12,21 +14,18 @@ module Observers
12
14
  def trigger(action:, event:)
13
15
  action = @action if @action
14
16
 
17
+ return unless @object.respond_to?(action)
18
+
15
19
  # Assumption that if method has params then that param is "event:".
16
20
  if event && @object.method(action).parameters.count > 0
17
21
  @object.send(action, **{ event: })
18
22
  else
19
23
  @object.send(action)
20
24
  end
21
- rescue ArgumentError => e
22
- type = @object.instance_of?(Class) ? @object : @object.class
23
- method_type = @object.instance_of?(Class) ? '.' : '#'
24
-
25
- raise ArgumentError, "#{type}##{action} has an 'event:' keyword argument but no event arg was sent" if event.nil?
26
-
27
- # Events trigger events, so the error bubbles up to becomes the error message for the next rescue's error message:
28
- # "RequestEvent sent to Rain::Router#handle -> StatusEvent sent to Error404Node.render -> unknown keyword: :props"
29
- raise ArgumentError, "#{event.class} sent to #{type}#{method_type}#{action} -> #{e.message}"
25
+ rescue StandardError => e
26
+ # Events trigger events, so this error bubbles up to become the error for the next rescue:
27
+ # "RequestEvent -> Rain::Router#route_request -> StatusEvent -> Error404Node.render -> unknown keyword: :props"
28
+ raise ObserverError.new(e.message, object:, event:, action:, previous_error: e)
30
29
  end
31
30
  end
32
31
  end
@@ -0,0 +1,40 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Observers
4
+ # Map the events and actions along the way when showing an exception.
5
+ # Any exceptions in this class itself will fail silently as they will error out into itself.
6
+ class ObserverError < StandardError
7
+ def initialize(message, object:, event:, action:, previous_error:)
8
+ super(message)
9
+
10
+ @event = event
11
+ @object = object
12
+ @action = action
13
+ @previous_error = previous_error
14
+ end
15
+
16
+ def link
17
+ class_type = @object.instance_of?(Class) ? @object : @object.class
18
+ method_type = @object.instance_of?(Class) ? '.' : '#'
19
+ "#{@event.class} -> #{class_type}#{method_type}#{@action}"
20
+ end
21
+
22
+ def to_s
23
+ chain = [link]
24
+ first_error = first_error(@previous_error, chain:)
25
+ error_location = first_error.backtrace.first
26
+
27
+ "#{chain.join(' -> ')} -> #{first_error.message}\n#{error_location}"
28
+ end
29
+
30
+ private
31
+
32
+ def first_error(error, chain:)
33
+ chain << error.link if error.respond_to?(:link)
34
+
35
+ return error unless error.respond_to?(:previous_error)
36
+
37
+ first_error(error.previous_error, chain:)
38
+ end
39
+ end
40
+ end
data/lib/version.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Observers
4
- VERSION = '0.8.0'
4
+ VERSION = '1.0.0'
5
5
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: observers
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.8.0
4
+ version: 1.0.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - maedi
@@ -19,6 +19,7 @@ files:
19
19
  - lib/keys.rb
20
20
  - lib/models/key.rb
21
21
  - lib/models/observer.rb
22
+ - lib/models/observer_error.rb
22
23
  - lib/observers.rb
23
24
  - lib/version.rb
24
25
  homepage: https://github.com/raindeer-rb/observers
@@ -40,7 +41,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
40
41
  - !ruby/object:Gem::Version
41
42
  version: '0'
42
43
  requirements: []
43
- rubygems_version: 4.0.10
44
+ rubygems_version: 4.0.6
44
45
  specification_version: 4
45
46
  summary: Observe objects of any kind
46
47
  test_files: []