eventish 0.4.0 → 0.5.1

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