eventish 0.4.0 → 0.5.1

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: b680cd59cf6faa489efed4b75bd284699213c249ff2d29c82f62d67facf634a2
4
- data.tar.gz: 52bb544e7348bf01b9d0b60b236123488a0bdc5cb4e3138dac817292710517a3
3
+ metadata.gz: 5074ba83f3c48572d1074e1f212c2220a7fe98f21868d2a331439b642fb44745
4
+ data.tar.gz: dcb4ff6ea905f77414c74c369c803e582c2e143b50b59f8903e7eec91a4d0538
5
5
  SHA512:
6
- metadata.gz: 69cc41ae3fb78d9cee65ee21cd71b480534fc0381acedf41bf6d4e26c3d5fe6ec9377359ed44b01b3834af2e2f4a35a39462e2be39f47c672290fc49585e08e7
7
- data.tar.gz: 98718a8527924fe2e6396fdba0170b5998398aa4427f9b7af92fbf103e4c4a631da4ad9a349ec8131905ec651d3b5225d654200e01445312536313fb68ada425
6
+ metadata.gz: fdbf059ef0be7568fa12a5df0fde30920adb881f5e5df40b2bb0b130dad3e620b5f415fdf9ceb3f0c18075bf77996606653ef40a982558546cfb1f708864c40f
7
+ data.tar.gz: 7f8395c61e2662fd4b801ef99577d4b8085b5b0d484867444ef12a25bf7df2657d68a5edf329b70de0b48196839424fa90cba933e6c3f3f848be05b1f5547a40
data/README.md CHANGED
@@ -1,22 +1,34 @@
1
1
  # Eventish
2
2
 
3
3
  [![Gem Version](https://badge.fury.io/rb/eventish.svg)](https://badge.fury.io/rb/eventish)
4
- [![Specs](https://github.com/blocknotes/eventish/actions/workflows/main.yml/badge.svg)](https://github.com/blocknotes/eventish/actions/workflows/main.yml)
4
+ [![Specs Rails 6](https://github.com/blocknotes/eventish/actions/workflows/rails6.yml/badge.svg)](https://github.com/blocknotes/eventish/actions/workflows/rails6.yml)
5
+ [![Specs Rails 7](https://github.com/blocknotes/eventish/actions/workflows/rails7.yml/badge.svg)](https://github.com/blocknotes/eventish/actions/workflows/rails7.yml)
5
6
  [![Linters](https://github.com/blocknotes/eventish/actions/workflows/linters.yml/badge.svg)](https://github.com/blocknotes/eventish/actions/workflows/linters.yml)
6
7
 
7
- Yet another opinionated events library which proposes a simple API to handle... events 🎉
8
+ Yet another events library with a _simple_ API to handle... events :tada:
8
9
 
9
- The main features:
10
+ Main features:
10
11
  - _composable_: just require the components that you need;
11
- - with [adapters](#adapters): support ActiveSupport::Notifications for pub/sub events;
12
- - with [async events](#async-events): support ActiveJob for background execution;
13
- - with [callbacks wrapper](#callbacks): support ActiveRecord.
14
- - with [plugins](#plugins): logger and Rails logger included.
12
+ - with [adapters](#adapters): support ActiveSupport::Notifications and Wisper for pub/sub events;
13
+ - with [async events](#async-events): support ActiveJob for events background execution;
14
+ - with [callbacks wrapper](#callbacks): support ActiveRecord callbacks;
15
+ - with [plugins](#plugins): a simple logger and a Rails logger are included;
16
+ - with conditional event execution (overriding `callable?`).
17
+
18
+ This component can be useful to:
19
+ - decouple callbacks from the side-effect;
20
+ - permit to test the side-effects in isolation;
21
+ - permit to spy/block the side-effects;
22
+ - permit to execute the side-effects from other contexts.
23
+
24
+ Please :star: if you like it.
25
+
26
+ > Do you want to speak by events? Try _eventish_ :smile:
15
27
 
16
28
  ## Install
17
29
 
18
30
  - Add to your Gemfile: `gem 'eventish'` (and execute `bundle`)
19
- - Create an initializer - _config/initializers/eventish.rb_:
31
+ - Create an initializer - ex. _config/initializers/eventish.rb_:
20
32
 
21
33
  ```rb
22
34
  require 'eventish/adapters/active_support'
@@ -40,7 +52,7 @@ Rails.configuration.after_initialize do
40
52
  end
41
53
  ```
42
54
 
43
- - Create some events - _app/events/main/app_loaded_event.rb_:
55
+ - Create some events - ex. _app/events/main/app_loaded_event.rb_:
44
56
 
45
57
  ```rb
46
58
  module Main
@@ -56,7 +68,9 @@ For a complete example please take a look at the [dummy app](spec/dummy) in the
56
68
 
57
69
  ### Adapters
58
70
 
59
- Only _ActiveSupport_ is supported for now.
71
+ Used for events subscription and publishing.
72
+
73
+ _ActiveSupport Notification_:
60
74
 
61
75
  ```rb
62
76
  # initializer setup
@@ -67,6 +81,18 @@ Eventish.setup do |config|
67
81
  end
68
82
  ```
69
83
 
84
+ _Wisper_ (NOTE: around callback wrappers are not supported):
85
+
86
+ ```rb
87
+ # initializer setup
88
+ require 'wisper'
89
+ require 'eventish/adapters/wisper'
90
+
91
+ Eventish.setup do |config|
92
+ config.adapter = Eventish::Adapters::Wisper
93
+ end
94
+ ```
95
+
70
96
  ### Simple events
71
97
 
72
98
  Generic events not related to a specific component.
@@ -80,7 +106,7 @@ Rails.configuration.after_initialize do
80
106
  end
81
107
  ```
82
108
 
83
- Sample event - _app/events/main/test_event.rb_:
109
+ Sample event - ex. _app/events/main/test_event.rb_:
84
110
 
85
111
  ```rb
86
112
  module Main
@@ -99,14 +125,20 @@ module Main
99
125
  end
100
126
  ```
101
127
 
102
- Publish the event: `Eventish.publish('some_event')`
128
+ Publish the event with:
129
+
130
+ ```rb
131
+ Eventish.publish('some_event')
132
+ ```
103
133
 
104
134
  ### Async events
105
135
 
106
- Events executed in a background process. Only _ActiveJob_ is supported for now.
136
+ Events executed in a background process.
137
+ Only _ActiveJob_ is supported for now.
107
138
 
108
139
  ```rb
109
140
  # initializer setup
141
+ require 'active_job/railtie'
110
142
  require 'eventish/active_job_event'
111
143
 
112
144
  Rails.configuration.after_initialize do
@@ -114,7 +146,7 @@ Rails.configuration.after_initialize do
114
146
  end
115
147
  ```
116
148
 
117
- Sample event - _app/events/notifications/user_after_save_commit_event.rb_:
149
+ Sample event - ex. _app/events/notifications/user_after_save_commit_event.rb_:
118
150
 
119
151
  ```rb
120
152
  module Notifications
@@ -128,7 +160,7 @@ end
128
160
 
129
161
  ### Callbacks
130
162
 
131
- Only for ActiveRecord, callbacks wrapper are available with the postfix `_event` (ex. `after_commit_event SomeEvent`).
163
+ Wrappers for ActiveRecord callbacks using the postfix `_event` (ex. `after_commit_event SomeEvent`).
132
164
 
133
165
  ```rb
134
166
  # initializer setup
@@ -136,6 +168,7 @@ require 'eventish/active_record/callback'
136
168
  ```
137
169
 
138
170
  ```rb
171
+ # sample model
139
172
  class SomeModel < ActiveRecord::Base
140
173
  extend ::Eventish::ActiveRecord::Callback
141
174
 
@@ -171,6 +204,26 @@ module Eventish::Plugins::RailsLogger
171
204
  end
172
205
  ```
173
206
 
207
+ Plugins can also be configured for single events overriding _before_event_ and _after_event_.
208
+
209
+ ### Conditional events
210
+
211
+ Optionally an event can override the `callable?` method to define preconditions on the execution of the `call` method.
212
+
213
+ Example:
214
+
215
+ ```rb
216
+ class TestEvent < Eventish::SimpleEvent
217
+ def callable?(target)
218
+ target.ready?
219
+ end
220
+
221
+ def call(target, _options = {})
222
+ puts '> A test event'
223
+ end
224
+ end
225
+ ```
226
+
174
227
  ## Do you like it? Star it!
175
228
 
176
229
  If you use this component just star it. A developer is more motivated to improve a project when there is some interest.
@@ -6,6 +6,10 @@ module Eventish
6
6
  raise NotImplementedError
7
7
  end
8
8
 
9
+ def callable?(_target)
10
+ true
11
+ end
12
+
9
13
  def perform(target, args)
10
14
  self.class.before_event.each { |plugin| plugin.call(target, args, event: self, hook: :before) }
11
15
  call(target, args)
@@ -17,6 +21,8 @@ module Eventish
17
21
  include Eventish::EventApi
18
22
 
19
23
  def trigger(target, args, &_block)
24
+ return unless new.callable?(target)
25
+
20
26
  perform_later(target, args)
21
27
  end
22
28
  end
@@ -4,103 +4,126 @@ module Eventish
4
4
  module ActiveRecord
5
5
  module Callback
6
6
  # Init events
7
- def after_initialize_event(event)
8
- after_initialize -> { ::Eventish.publish(event, self) }
7
+ def after_initialize_event(*args)
8
+ event = args.shift
9
+ after_initialize -> { ::Eventish.publish(event, self) }, *args
9
10
  end
10
11
 
11
- def after_find_event(event)
12
- after_find -> { ::Eventish.publish(event, self) }
12
+ def after_find_event(*args)
13
+ event = args.shift
14
+ after_find -> { ::Eventish.publish(event, self) }, *args
13
15
  end
14
16
 
15
17
  # Validation events
16
- def before_validation_event(event)
17
- before_validation -> { ::Eventish.publish(event, self) }
18
+ def before_validation_event(*args)
19
+ event = args.shift
20
+ before_validation -> { ::Eventish.publish(event, self) }, *args
18
21
  end
19
22
 
20
- def after_validation_event(event)
21
- after_validation -> { ::Eventish.publish(event, self) }
23
+ def after_validation_event(*args)
24
+ event = args.shift
25
+ after_validation -> { ::Eventish.publish(event, self) }, *args
22
26
  end
23
27
 
24
28
  # Create events
25
- def before_create_event(event)
26
- before_create -> { ::Eventish.publish(event, self) }
29
+ def before_create_event(*args)
30
+ event = args.shift
31
+ before_create -> { ::Eventish.publish(event, self) }, *args
27
32
  end
28
33
 
29
- def around_create_event(event)
30
- around_create ->(_object, block) { ::Eventish.publish(event, self, block: block) }
34
+ def around_create_event(*args)
35
+ event = args.shift
36
+ around_create ->(_object, block) { ::Eventish.publish(event, self, block: block) }, *args
31
37
  end
32
38
 
33
- def after_create_event(event)
34
- after_create -> { ::Eventish.publish(event, self) }
39
+ def after_create_event(*args)
40
+ event = args.shift
41
+ after_create -> { ::Eventish.publish(event, self) }, *args
35
42
  end
36
43
 
37
44
  # Update events
38
- def before_update_event(event)
39
- before_update -> { ::Eventish.publish(event, self) }
45
+ def before_update_event(*args)
46
+ event = args.shift
47
+ before_update -> { ::Eventish.publish(event, self) }, *args
40
48
  end
41
49
 
42
- def around_update_event(event)
43
- around_update ->(_object, block) { ::Eventish.publish(event, self, block: block) }
50
+ def around_update_event(*args)
51
+ event = args.shift
52
+ around_update ->(_object, block) { ::Eventish.publish(event, self, block: block) }, *args
44
53
  end
45
54
 
46
- def after_update_event(event)
47
- after_update -> { ::Eventish.publish(event, self) }
55
+ def after_update_event(*args)
56
+ event = args.shift
57
+ after_update -> { ::Eventish.publish(event, self) }, *args
48
58
  end
49
59
 
50
60
  # Save events
51
- def before_save_event(event)
52
- before_save -> { ::Eventish.publish(event, self) }
61
+ def before_save_event(*args)
62
+ event = args.shift
63
+ before_save -> { ::Eventish.publish(event, self) }, *args
53
64
  end
54
65
 
55
- def around_save_event(event)
56
- around_save ->(_object, block) { ::Eventish.publish(event, self, block: block) }
66
+ def around_save_event(*args)
67
+ event = args.shift
68
+ around_save ->(_object, block) { ::Eventish.publish(event, self, block: block) }, *args
57
69
  end
58
70
 
59
- def after_save_event(event)
60
- after_save -> { ::Eventish.publish(event, self) }
71
+ def after_save_event(*args)
72
+ event = args.shift
73
+ after_save -> { ::Eventish.publish(event, self) }, *args
61
74
  end
62
75
 
63
76
  # Destroy events
64
- def before_destroy_event(event)
65
- before_destroy -> { ::Eventish.publish(event, self) }
77
+ def before_destroy_event(*args)
78
+ event = args.shift
79
+ before_destroy -> { ::Eventish.publish(event, self) }, *args
66
80
  end
67
81
 
68
- def around_destroy_event(event)
69
- around_destroy ->(_object, block) { ::Eventish.publish(event, self, block: block) }
82
+ def around_destroy_event(*args)
83
+ event = args.shift
84
+ around_destroy ->(_object, block) { ::Eventish.publish(event, self, block: block) }, *args
70
85
  end
71
86
 
72
- def after_destroy_event(event)
73
- after_destroy -> { ::Eventish.publish(event, self) }
87
+ def after_destroy_event(*args)
88
+ event = args.shift
89
+ after_destroy -> { ::Eventish.publish(event, self) }, *args
74
90
  end
75
91
 
76
92
  # Commit events
77
- def after_commit_event(event)
78
- after_commit -> { ::Eventish.publish(event, self) }
93
+ def after_commit_event(*args)
94
+ event = args.shift
95
+ after_commit -> { ::Eventish.publish(event, self) }, *args
79
96
  end
80
97
 
81
- def after_create_commit_event(event)
82
- after_create_commit -> { ::Eventish.publish(event, self) }
98
+ def after_create_commit_event(*args)
99
+ event = args.shift
100
+ after_create_commit -> { ::Eventish.publish(event, self) }, *args
83
101
  end
84
102
 
85
- def after_update_commit_event(event)
86
- after_update_commit -> { ::Eventish.publish(event, self) }
103
+ def after_update_commit_event(*args)
104
+ event = args.shift
105
+ after_update_commit -> { ::Eventish.publish(event, self) }, *args
87
106
  end
88
107
 
89
- def after_save_commit_event(event)
90
- after_save_commit -> { ::Eventish.publish(event, self) }
108
+ def after_save_commit_event(*args)
109
+ event = args.shift
110
+ after_save_commit -> { ::Eventish.publish(event, self) }, *args
91
111
  end
92
112
 
93
- def after_destroy_commit_event(event)
94
- after_destroy_commit -> { ::Eventish.publish(event, self) }
113
+ def after_destroy_commit_event(*args)
114
+ event = args.shift
115
+ after_destroy_commit -> { ::Eventish.publish(event, self) }, *args
95
116
  end
96
117
 
97
- def after_rollback_event(event)
98
- after_rollback -> { ::Eventish.publish(event, self) }
118
+ def after_rollback_event(*args)
119
+ event = args.shift
120
+ after_rollback -> { ::Eventish.publish(event, self) }, *args
99
121
  end
100
122
 
101
123
  # Touch events
102
- def after_touch_event(event)
103
- after_touch -> { ::Eventish.publish(event, self) }
124
+ def after_touch_event(*args)
125
+ event = args.shift
126
+ after_touch -> { ::Eventish.publish(event, self) }, *args
104
127
  end
105
128
  end
106
129
  end
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Eventish
4
- class Adapters
4
+ module Adapters
5
5
  class ActiveSupport
6
6
  class << self
7
7
  def publish(event, target = nil, block: nil)
@@ -15,16 +15,19 @@ module Eventish
15
15
  raise ArgumentError, 'Missing event to subscribe' if event.nil?
16
16
  raise ArgumentError, 'Missing handler for subscription' if handler.nil?
17
17
 
18
- ::ActiveSupport::Notifications.subscribe(event.to_s) do |name, start, finish, id, payload|
18
+ subscriber = ::ActiveSupport::Notifications.subscribe(event.to_s) do |name, start, finish, id, payload|
19
19
  args = { event: name, id: id, start: start, finish: finish }
20
20
  handler.trigger(payload[:target], args, &payload.dig(:options, :block))
21
21
  end
22
+ Eventish.subscribers[event.to_s] = subscriber
23
+ subscriber
22
24
  end
23
25
 
24
26
  def unsubscribe(event)
25
27
  raise ArgumentError, 'Missing event to unsubscribe' if event.nil?
26
28
 
27
29
  ::ActiveSupport::Notifications.unsubscribe(event.to_s)
30
+ Eventish.subscribers.delete(event.to_s)
28
31
  end
29
32
  end
30
33
  end
@@ -0,0 +1,35 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Eventish
4
+ module Adapters
5
+ class Wisper
6
+ class << self
7
+ include ::Wisper::Publisher
8
+
9
+ def publish(event, target = nil, block: nil)
10
+ raise ArgumentError, 'Missing event to publish' if event.nil?
11
+
12
+ options = block ? { block: block } : {} # TODO: verify block feature
13
+ broadcast(event.to_s, target, options)
14
+ end
15
+
16
+ def subscribe(event, handler)
17
+ raise ArgumentError, 'Missing event to subscribe' if event.nil?
18
+ raise ArgumentError, 'Missing handler for subscription' if handler.nil?
19
+
20
+ ::Wisper.subscribe(handler.new, on: event.to_s, with: :call).tap do |subscriber|
21
+ Eventish.subscribers[event.to_s] = subscriber
22
+ end
23
+ end
24
+
25
+ def unsubscribe(event)
26
+ raise ArgumentError, 'Missing event to unsubscribe' if event.nil?
27
+
28
+ subscriber = Eventish.subscribers[event.to_s]
29
+ ::Wisper.unsubscribe(subscriber)
30
+ Eventish.subscribers.delete(event.to_s)
31
+ end
32
+ end
33
+ end
34
+ end
35
+ end
@@ -27,8 +27,14 @@ module Eventish
27
27
  end
28
28
 
29
29
  def subscribe_all
30
- # iterate the descendants
31
- ObjectSpace.each_object(singleton_class).sort.each(&:subscribe)
30
+ ignore_events = [Eventish::SimpleEvent]
31
+ ignore_events.push(Eventish::ActiveJobEvent) if defined? Eventish::ActiveJobEvent
32
+ events = Eventish.descendants(self).sort
33
+ (events - ignore_events).each(&:subscribe)
34
+ end
35
+
36
+ def unsubscribe
37
+ Eventish.adapter.unsubscribe(event_name)
32
38
  end
33
39
  end
34
40
  end
@@ -5,7 +5,7 @@ module Eventish
5
5
  class RailsLogger
6
6
  class << self
7
7
  def call(target, _args, event:, hook: nil, &_block)
8
- Rails.logger.debug "EVENT: #{hook} #{event.class.event_name} on #{target.inspect}"
8
+ ::Rails.logger.debug "EVENT: #{hook} #{event.class.event_name} on #{target.inspect}"
9
9
  end
10
10
  end
11
11
  end
@@ -6,11 +6,17 @@ module Eventish
6
6
  raise NotImplementedError
7
7
  end
8
8
 
9
+ def callable?(_target)
10
+ true
11
+ end
12
+
9
13
  class << self
10
14
  include EventApi
11
15
 
12
16
  def trigger(target, args, &block)
13
17
  event = new
18
+ return unless event.callable?(target)
19
+
14
20
  before_event.each { |plugin| plugin.call(target, args, event: event, hook: :before, &block) }
15
21
  event.call(target, args, &block)
16
22
  after_event.each { |plugin| plugin.call(target, args, event: event, hook: :after, &block) }
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Eventish # :nodoc:
4
- VERSION = '0.4.0'
4
+ VERSION = '0.5.1'
5
5
  end
data/lib/eventish.rb CHANGED
@@ -18,6 +18,17 @@ module Eventish
18
18
  @options ||= Struct.new(*OPTIONS).new # rubocop:disable Naming/MemoizedInstanceVariableName
19
19
  end
20
20
 
21
+ # From Rails 6.0
22
+ def descendants(klass)
23
+ descendants = []
24
+ ObjectSpace.each_object(klass.singleton_class) do |k|
25
+ next if k.singleton_class?
26
+
27
+ descendants.unshift k unless k == self
28
+ end
29
+ descendants
30
+ end
31
+
21
32
  def publish(event_name, target = nil, block: nil)
22
33
  config.adapter&.publish(event_name, target, block: block)
23
34
  end
@@ -29,4 +40,8 @@ module Eventish
29
40
 
30
41
  @options
31
42
  end
43
+
44
+ def subscribers
45
+ @subscribers ||= {}
46
+ end
32
47
  end
metadata CHANGED
@@ -1,15 +1,29 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: eventish
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.4.0
4
+ version: 0.5.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Mattia Roccoberton
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2022-05-21 00:00:00.000000000 Z
12
- dependencies: []
11
+ date: 2022-06-01 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: appraisal
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '2.4'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '2.4'
13
27
  description: A simple and composable event library
14
28
  email: mat@blocknot.es
15
29
  executables: []
@@ -22,6 +36,7 @@ files:
22
36
  - lib/eventish/active_job_event.rb
23
37
  - lib/eventish/active_record/callback.rb
24
38
  - lib/eventish/adapters/active_support.rb
39
+ - lib/eventish/adapters/wisper.rb
25
40
  - lib/eventish/event_api.rb
26
41
  - lib/eventish/plugins/logger.rb
27
42
  - lib/eventish/plugins/rails_logger.rb