eventish 0.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.
- checksums.yaml +7 -0
- data/LICENSE.txt +20 -0
- data/README.md +158 -0
- data/lib/eventish/active_job_event.rb +21 -0
- data/lib/eventish/adapters/active_support.rb +24 -0
- data/lib/eventish/callback.rb +48 -0
- data/lib/eventish/event_api.rb +27 -0
- data/lib/eventish/simple_event.rb +17 -0
- data/lib/eventish/version.rb +5 -0
- data/lib/eventish.rb +38 -0
- metadata +54 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: '08c9cf6785fc35a3174bf04df8a3f46011a10f1dc465ed24903d3924c293927a'
|
4
|
+
data.tar.gz: 53efbd04a12d69149c594f375b3b79da0cda0a9ed6a339e09d88ff4a9d4d830b
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 3aabb2135effcd4234379ef10beda4cb8cd2a5489b6b7318e7bc9cc84de6ef84daa5972b35496636dc3d439f7829cf2ef3ef27786c7512339a24b87404b4bce8
|
7
|
+
data.tar.gz: 63197732c22986bfc7f9d647808350886b71121cc9ccd3125ba35fa3f6eb3db2cc152b36c4f0fbd347224c46b9be50381a18e595014088341879769590593535
|
data/LICENSE.txt
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright (c) 2022 Mattia Roccoberton
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,158 @@
|
|
1
|
+
# Eventish
|
2
|
+
|
3
|
+
Yet another opinionated events library which proposes a simple API to handle... events 🎉
|
4
|
+
|
5
|
+
The main features:
|
6
|
+
- composable: just require the components that you need;
|
7
|
+
- with adapters: support _ActiveSupport::Notifications_;
|
8
|
+
- with async events: support _ActiveJob_;
|
9
|
+
- with callbacks: support _ActiveRecord_.
|
10
|
+
|
11
|
+
## Install
|
12
|
+
|
13
|
+
- Add to your Gemfile: `gem 'eventish'` (and execute `bundle`)
|
14
|
+
- Create an initializer - _config/initializers/eventish.rb_:
|
15
|
+
|
16
|
+
```rb
|
17
|
+
require 'eventish/adapters/active_support'
|
18
|
+
|
19
|
+
Eventish.setup do |config|
|
20
|
+
config.adapter = Eventish::Adapters::ActiveSupport
|
21
|
+
end
|
22
|
+
|
23
|
+
Rails.configuration.to_prepare do
|
24
|
+
# NOTE: required to load the event descendants when eager_load is off
|
25
|
+
unless Rails.configuration.eager_load
|
26
|
+
events = Rails.root.join('app/events/**/*.rb').to_s
|
27
|
+
Dir[events].sort.each { |event| require event }
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
Rails.configuration.after_initialize do
|
32
|
+
Eventish::SimpleEvent.subscribe_all # NOTE: events will be available after this point
|
33
|
+
|
34
|
+
Eventish.adapter.publish('app_loaded') # just a test event
|
35
|
+
end
|
36
|
+
```
|
37
|
+
|
38
|
+
- Create some events - _app/events/main/app_loaded_event.rb_:
|
39
|
+
|
40
|
+
```rb
|
41
|
+
module Main
|
42
|
+
class AppLoadedEvent < Eventish::SimpleEvent
|
43
|
+
def call(_none, _options = {})
|
44
|
+
puts '> App loaded event'
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
```
|
49
|
+
|
50
|
+
For a complete example please take a look at the [dummy app](spec/dummy) in the specs.
|
51
|
+
|
52
|
+
### Adatpers
|
53
|
+
|
54
|
+
Only _ActiveSupport_ is supported for now.
|
55
|
+
|
56
|
+
```rb
|
57
|
+
# initializer setup
|
58
|
+
require 'eventish/adapters/active_support'
|
59
|
+
|
60
|
+
Eventish.setup do |config|
|
61
|
+
config.adapter = Eventish::Adapters::ActiveSupport
|
62
|
+
end
|
63
|
+
```
|
64
|
+
|
65
|
+
### Simple events
|
66
|
+
|
67
|
+
Generic events not related to a specific component.
|
68
|
+
|
69
|
+
```rb
|
70
|
+
# initializer setup
|
71
|
+
Rails.configuration.after_initialize do
|
72
|
+
# Subscribe all Eventish::SimpleEvent descendants using the configured adapter
|
73
|
+
# The descendants event classes must be loaded before this point - see eager_load notes in the Install section
|
74
|
+
Eventish::SimpleEvent.subscribe_all
|
75
|
+
end
|
76
|
+
```
|
77
|
+
|
78
|
+
Sample event - _app/events/main/test_event.rb_:
|
79
|
+
|
80
|
+
```rb
|
81
|
+
module Main
|
82
|
+
class TestEvent < Eventish::SimpleEvent
|
83
|
+
def call(_none, _options = {})
|
84
|
+
puts '> A test event'
|
85
|
+
end
|
86
|
+
|
87
|
+
class << self
|
88
|
+
def event_name
|
89
|
+
# this is optional, if not set the event name will be inferred from the class name
|
90
|
+
# in this sample it would be "test" - TestEvent => underscore => remove _event suffix
|
91
|
+
'some_event'
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
96
|
+
```
|
97
|
+
|
98
|
+
Publish the event: `Eventish.adapter.publish('some_event')`
|
99
|
+
|
100
|
+
### Async events
|
101
|
+
|
102
|
+
Events executed in a background process. Only _ActiveJob_ is supported for now.
|
103
|
+
|
104
|
+
```rb
|
105
|
+
# initializer setup
|
106
|
+
require 'eventish/active_job_event'
|
107
|
+
|
108
|
+
Rails.configuration.after_initialize do
|
109
|
+
Eventish::ActiveJobEvent.subscribe_all
|
110
|
+
end
|
111
|
+
```
|
112
|
+
|
113
|
+
Sample event - _app/events/notifications/user_after_save_commit_event.rb_:
|
114
|
+
|
115
|
+
```rb
|
116
|
+
module Notifications
|
117
|
+
class UserAfterCommitEvent < Eventish::ActiveJobEvent
|
118
|
+
def call(user, _options = {})
|
119
|
+
Rails.logger.info ">>> User ##{user.id} after commit notification"
|
120
|
+
end
|
121
|
+
end
|
122
|
+
end
|
123
|
+
```
|
124
|
+
|
125
|
+
### Callbacks
|
126
|
+
|
127
|
+
Wrapper for callbacks. Only _ActiveRecord_ is supported for now.
|
128
|
+
This is just a glue component to have callbacks with a nice syntax.
|
129
|
+
|
130
|
+
```rb
|
131
|
+
# initializer setup
|
132
|
+
require 'eventish/callback'
|
133
|
+
```
|
134
|
+
|
135
|
+
Sample model usage:
|
136
|
+
|
137
|
+
```rb
|
138
|
+
class User < ActiveRecord::Base
|
139
|
+
before_validation ::Eventish::Callback
|
140
|
+
after_save_commit ::Eventish::Callback
|
141
|
+
end
|
142
|
+
```
|
143
|
+
|
144
|
+
For events definition see Simple or Async events.
|
145
|
+
|
146
|
+
## Do you like it? Star it!
|
147
|
+
|
148
|
+
If you use this component just star it. A developer is more motivated to improve a project when there is some interest.
|
149
|
+
|
150
|
+
Or consider offering me a coffee, it's a small thing but it is greatly appreciated: [about me](https://www.blocknot.es/about-me).
|
151
|
+
|
152
|
+
## Contributors
|
153
|
+
|
154
|
+
- [Mattia Roccoberton](https://www.blocknot.es): author
|
155
|
+
|
156
|
+
## License
|
157
|
+
|
158
|
+
The gem is available as open-source under the terms of the [MIT](LICENSE.txt).
|
@@ -0,0 +1,21 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Eventish
|
4
|
+
class ActiveJobEvent < ::ActiveJob::Base
|
5
|
+
def call(_target, _args, &_block)
|
6
|
+
raise NotImplementedError
|
7
|
+
end
|
8
|
+
|
9
|
+
def perform(target, args)
|
10
|
+
call(target, args)
|
11
|
+
end
|
12
|
+
|
13
|
+
class << self
|
14
|
+
include Eventish::EventApi
|
15
|
+
|
16
|
+
def trigger(target, args, &_block)
|
17
|
+
perform_later(target, args)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Eventish
|
4
|
+
class Adapters
|
5
|
+
class ActiveSupport
|
6
|
+
class << self
|
7
|
+
def publish(event_name, target = nil, options: {})
|
8
|
+
::ActiveSupport::Notifications.instrument(event_name, target: target, event_options: options)
|
9
|
+
end
|
10
|
+
|
11
|
+
def subscribe(event_name, handler)
|
12
|
+
::ActiveSupport::Notifications.subscribe(event_name) do |name, start, finish, id, payload|
|
13
|
+
args = { event: name, id: id, start: start, finish: finish }
|
14
|
+
handler.trigger(payload[:target], args, &payload[:block])
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
def unsubscribe(event_name)
|
19
|
+
::ActiveSupport::Notifications.unsubscribe(event_name)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,48 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Eventish
|
4
|
+
class Callback
|
5
|
+
class << self
|
6
|
+
def callback_event(target, &block)
|
7
|
+
event = "#{Eventish.underscore(target.class.to_s)}_#{__callee__}"
|
8
|
+
Eventish.adapter.publish(event, target, &block)
|
9
|
+
end
|
10
|
+
|
11
|
+
def callback_commit_event(target, &block)
|
12
|
+
event = "#{Eventish.underscore(target.class.to_s)}_after_commit"
|
13
|
+
Eventish.adapter.publish(event, target, &block)
|
14
|
+
end
|
15
|
+
|
16
|
+
alias_method :after_initialize, :callback_event
|
17
|
+
alias_method :after_find, :callback_event
|
18
|
+
|
19
|
+
alias_method :before_validation, :callback_event
|
20
|
+
alias_method :after_validation, :callback_event
|
21
|
+
|
22
|
+
alias_method :before_create, :callback_event
|
23
|
+
alias_method :around_create, :callback_event
|
24
|
+
alias_method :after_create, :callback_event
|
25
|
+
|
26
|
+
alias_method :before_update, :callback_event
|
27
|
+
alias_method :around_update, :callback_event
|
28
|
+
alias_method :after_update, :callback_event
|
29
|
+
|
30
|
+
alias_method :before_save, :callback_event
|
31
|
+
alias_method :around_save, :callback_event
|
32
|
+
alias_method :after_save, :callback_event
|
33
|
+
|
34
|
+
alias_method :before_destroy, :callback_event
|
35
|
+
alias_method :around_destroy, :callback_event
|
36
|
+
alias_method :after_destroy, :callback_event
|
37
|
+
|
38
|
+
alias_method :after_commit, :callback_commit_event
|
39
|
+
alias_method :after_save_commit, :callback_commit_event # => after_commit
|
40
|
+
alias_method :after_create_commit, :callback_commit_event # => after_commit
|
41
|
+
alias_method :after_update_commit, :callback_commit_event # => after_commit
|
42
|
+
alias_method :after_destroy_commit, :callback_commit_event # => after_commit
|
43
|
+
alias_method :after_rollback, :callback_event
|
44
|
+
|
45
|
+
alias_method :after_touch, :callback_event
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Eventish
|
4
|
+
module EventApi
|
5
|
+
def <=>(other)
|
6
|
+
other&.priority <=> priority
|
7
|
+
end
|
8
|
+
|
9
|
+
def event_name
|
10
|
+
# If event_name is not set, infer the event from the class name
|
11
|
+
@event_name ||= Eventish.underscore(to_s).delete_suffix('_event')
|
12
|
+
end
|
13
|
+
|
14
|
+
def priority
|
15
|
+
0
|
16
|
+
end
|
17
|
+
|
18
|
+
def subscribe
|
19
|
+
Eventish.adapter.subscribe(event_name, self)
|
20
|
+
end
|
21
|
+
|
22
|
+
def subscribe_all
|
23
|
+
# iterate the descendants
|
24
|
+
ObjectSpace.each_object(singleton_class).sort.each(&:subscribe)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Eventish
|
4
|
+
class SimpleEvent
|
5
|
+
def call(_target, _args, &_block)
|
6
|
+
raise NotImplementedError
|
7
|
+
end
|
8
|
+
|
9
|
+
class << self
|
10
|
+
include EventApi
|
11
|
+
|
12
|
+
def trigger(target, args, &block)
|
13
|
+
new.call(target, args, &block)
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
data/lib/eventish.rb
ADDED
@@ -0,0 +1,38 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative 'eventish/event_api'
|
4
|
+
require_relative 'eventish/simple_event'
|
5
|
+
|
6
|
+
module Eventish
|
7
|
+
OPTIONS = %i[adapter].freeze
|
8
|
+
|
9
|
+
MissingAdapterError = Class.new(StandardError)
|
10
|
+
|
11
|
+
module_function
|
12
|
+
|
13
|
+
def adapter
|
14
|
+
config.adapter
|
15
|
+
end
|
16
|
+
|
17
|
+
def config
|
18
|
+
@options ||= Struct.new(*OPTIONS).new # rubocop:disable Naming/MemoizedInstanceVariableName
|
19
|
+
end
|
20
|
+
|
21
|
+
def setup
|
22
|
+
@options ||= Struct.new(*OPTIONS).new
|
23
|
+
yield(@options) if block_given?
|
24
|
+
raise MissingAdapterError, 'Please specify an event adapter' unless @options.adapter
|
25
|
+
|
26
|
+
@options
|
27
|
+
end
|
28
|
+
|
29
|
+
def underscore(camel_cased_word)
|
30
|
+
return camel_cased_word.to_s unless /[A-Z-]|::/.match?(camel_cased_word)
|
31
|
+
|
32
|
+
word = camel_cased_word.to_s.gsub(/\A.*::/, '')
|
33
|
+
word.gsub!(/([A-Z]+)(?=[A-Z][a-z])|([a-z\d])(?=[A-Z])/) { ($1 || $2) << "_" }
|
34
|
+
word.tr!("-", "_")
|
35
|
+
word.downcase!
|
36
|
+
word
|
37
|
+
end
|
38
|
+
end
|
metadata
ADDED
@@ -0,0 +1,54 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: eventish
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Mattia Roccoberton
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2022-05-17 00:00:00.000000000 Z
|
12
|
+
dependencies: []
|
13
|
+
description: A simple and composable event library
|
14
|
+
email: mat@blocknot.es
|
15
|
+
executables: []
|
16
|
+
extensions: []
|
17
|
+
extra_rdoc_files: []
|
18
|
+
files:
|
19
|
+
- LICENSE.txt
|
20
|
+
- README.md
|
21
|
+
- lib/eventish.rb
|
22
|
+
- lib/eventish/active_job_event.rb
|
23
|
+
- lib/eventish/adapters/active_support.rb
|
24
|
+
- lib/eventish/callback.rb
|
25
|
+
- lib/eventish/event_api.rb
|
26
|
+
- lib/eventish/simple_event.rb
|
27
|
+
- lib/eventish/version.rb
|
28
|
+
homepage: https://github.com/blocknotes/eventish
|
29
|
+
licenses:
|
30
|
+
- MIT
|
31
|
+
metadata:
|
32
|
+
homepage_uri: https://github.com/blocknotes/eventish
|
33
|
+
source_code_uri: https://github.com/blocknotes/eventish
|
34
|
+
rubygems_mfa_required: 'true'
|
35
|
+
post_install_message:
|
36
|
+
rdoc_options: []
|
37
|
+
require_paths:
|
38
|
+
- lib
|
39
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
40
|
+
requirements:
|
41
|
+
- - ">="
|
42
|
+
- !ruby/object:Gem::Version
|
43
|
+
version: 2.6.0
|
44
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
45
|
+
requirements:
|
46
|
+
- - ">="
|
47
|
+
- !ruby/object:Gem::Version
|
48
|
+
version: '0'
|
49
|
+
requirements: []
|
50
|
+
rubygems_version: 3.1.6
|
51
|
+
signing_key:
|
52
|
+
specification_version: 4
|
53
|
+
summary: Yet another events library
|
54
|
+
test_files: []
|