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