with_events 0.1.12

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: c3091e63784daa01d3274a7671cd51fb5be39d20
4
+ data.tar.gz: c107a1982c4443e0592bfd92167e165ca2d19063
5
+ SHA512:
6
+ metadata.gz: 8df5a9dca0608230736415d0bfc03065f241c6efd31b85735cd89d30ea7e16500cb869cb1dfea3810321708abd9298908947338f0c9c817c44ae1938b1228c3a
7
+ data.tar.gz: d36c94f875c2208ffadfd163a731285b5b24d798610230e950584923b14a599771393a4b7cb35af5109674c15898ee101643a048ec7a9a786af97a3b553fdb5e
@@ -0,0 +1,20 @@
1
+ Copyright 2018 Vlad Gramuzov
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.
@@ -0,0 +1,279 @@
1
+ ![Build status](https://travis-ci.org/pandomic/with_events.svg?branch=master)
2
+
3
+ # WithEvents
4
+ A simple events system for Ruby apps which supports
5
+ bi-directional SNS/SQS messaging.
6
+
7
+ ## Dependencies
8
+ * Ruby >= 2.3.3
9
+ * Rake >= 12.3.1
10
+ * Activesupport >= 4.2.7
11
+ * Sidekiq >= 3.5.3
12
+ * Circuitry 3.2
13
+
14
+ ## Installation
15
+ Add this line to your application's Gemfile:
16
+
17
+ ```ruby
18
+ gem 'with_events'
19
+ ```
20
+
21
+ And then execute:
22
+ ```bash
23
+ $ bundle
24
+ ```
25
+
26
+ Or install it yourself as:
27
+ ```bash
28
+ $ gem install with_events
29
+ ```
30
+
31
+ ## Configuration
32
+
33
+ ### Setting up a Rakefile
34
+
35
+ If you are going to use included rake tasks, add this to your
36
+ Rakefile:
37
+
38
+ ```ruby
39
+ spec = Gem::Specification.find_by_name 'with_events'
40
+ load "#{spec.gem_dir}/lib/tasks/with_events/with_events_tasks.rake"
41
+ ```
42
+
43
+ ### Setting up Circuitry
44
+
45
+ If you would like to use SNS/SQS subscribing/publishing features,
46
+ you need to configure Circuitry gem. Just follow [this instructions](https://github.com/kapost/circuitry#usage).
47
+
48
+ ## Usage
49
+
50
+ ### Basic Usage (in-app publish/subscribe)
51
+ This type of messaging does not require Rakefile or Circuitry configuration.
52
+
53
+ ```ruby
54
+ require 'with_events'
55
+
56
+ class MyHeroClass
57
+ include WithEvents
58
+
59
+ stream :my_lovely_stream do
60
+ event :game_over,
61
+ condition: :really_game_over?,
62
+ callback: :call_me_if_game_over
63
+ end
64
+
65
+ def really_game_over?
66
+ true
67
+ end
68
+
69
+ def call_me_if_game_over
70
+ puts 'Game over'
71
+ end
72
+ end
73
+
74
+ hero = MyHeroClass.new
75
+ hero.game_over! if hero.game_over?
76
+ ```
77
+
78
+ There might be situations where you will have a lot of events
79
+ which have pretty same configuration. To make life easier, you
80
+ can use `configure_all` method, which will aply configuration for
81
+ all events in the stream.
82
+
83
+ ```ruby
84
+ require 'with_events'
85
+
86
+ class MyHeroClass
87
+ include WithEvents
88
+
89
+ stream :my_lovely_stream do
90
+ configure_all callback: :call_me_if_game_over
91
+
92
+ event :event_one,
93
+ condition: -> { true }
94
+
95
+ event :event_one,
96
+ condition: -> { false }
97
+ end
98
+
99
+ def really_game_over?
100
+ true
101
+ end
102
+
103
+ def call_me_if_game_over
104
+ puts 'Game over'
105
+ end
106
+ end
107
+
108
+ hero = MyHeroClass.new
109
+ hero.event_one!
110
+ #=> Game over
111
+ hero.event_two!
112
+ #=> Game over
113
+ ```
114
+
115
+ ### Using with daily/hourly rake triggers for batch processing
116
+
117
+ You may want to automate a bit the process of asking resources if
118
+ they are ready to trigger events (by calling `#*?`). This can
119
+ be easily done by using `background: true`
120
+ with `appearance: :daily # or hourly` options.
121
+
122
+ `appearance` option sets by which rake task your event may
123
+ be processed.
124
+
125
+ ```ruby
126
+ require 'with_events'
127
+
128
+ class MyHeroClass
129
+ include WithEvents
130
+
131
+ stream :my_lovely_stream do
132
+ event :game_over,
133
+ condition: :really_game_over?,
134
+ callback: :call_me_if_game_over,
135
+ background: true,
136
+ appearance: :daily, # or :hourly
137
+ batch: User.active.find_each # any Enumerable
138
+ end
139
+
140
+ def really_game_over?
141
+ true
142
+ end
143
+
144
+ def call_me_if_game_over
145
+ puts 'Game over'
146
+ end
147
+ end
148
+ ```
149
+ Schedule for hourly/daily execution
150
+ ```bash
151
+ $ rake with_events:daily
152
+ $ rake with_events:hourly
153
+ ```
154
+
155
+ ### "Third-party" subscriptions
156
+
157
+ It is also possible to subscribe to events not only by using
158
+ a `callback` option:
159
+
160
+ ```ruby
161
+ WithEvents::Stream.find(:my_lovely_stream).on(:game_over) do
162
+ # ...
163
+ end
164
+ ```
165
+
166
+ **NOTE that this will also subscribe you to SQS/SNS events.**
167
+
168
+ ### Sending events to SNS/SQS
169
+
170
+ You may send messages to SNS/SQS by setting a `topic` option for
171
+ the stream. In addition, you need to specify `identifier` option.
172
+
173
+ `identifier` option (symbol, Proc, Class) allows to identify incoming message and
174
+ bind an `id` for outgoing ones.
175
+
176
+ ```ruby
177
+ require 'with_events'
178
+
179
+ class MyModel < ActiveRecord::Base
180
+ include WithEvents
181
+
182
+ stream :my_lovely_stream, topic: 'my-topic' do
183
+ event :game_over,
184
+ condition: :really_game_over?,
185
+ callback: :call_me_if_game_over,
186
+ identifier: :id, # symbol, Proc or Class
187
+ end
188
+
189
+ def really_game_over?
190
+ true
191
+ end
192
+
193
+ def call_me_if_game_over
194
+ puts 'Game over'
195
+ end
196
+ end
197
+ ```
198
+
199
+ ### Subscribing to SNS/SQS events
200
+
201
+ To subscribe to SNS/SQS events you need to specify `topic` and
202
+ `finder` options.
203
+
204
+ The `finder` option represents invokable type which should return
205
+ resource identified by `identifier` invokable by the sender.
206
+
207
+ **NOTE that subscriber will take the process. Run it in a separate process**
208
+
209
+ ```ruby
210
+ require 'with_events'
211
+
212
+ class MyClass
213
+ include WithEvents
214
+
215
+ stream :my_lovely_stream, topic: 'my-topic' do
216
+ event :game_over,
217
+ condition: :really_game_over?,
218
+ callback: :call_me_if_game_over,
219
+ finder: ->(message) { SomeModel.find(message.id) }
220
+ end
221
+
222
+ def really_game_over?
223
+ true
224
+ end
225
+
226
+ def call_me_if_game_over
227
+ puts 'Game over'
228
+ end
229
+ end
230
+
231
+ WithEvents::Stream.subscribe # NOTE this line
232
+ ```
233
+
234
+ ### Supported invokable types
235
+ * Proc
236
+ * Symbol
237
+ * Class
238
+
239
+ You may use them for `condition`, `callback`, `identifier` or `finder` options.
240
+
241
+ ```ruby
242
+ class CallbackClass
243
+ def call(resource, *arguments)
244
+ puts 'Game over'
245
+ end
246
+ end
247
+
248
+ class MyHeroClass
249
+ include WithEvents
250
+
251
+ stream :my_lovely_stream do
252
+ event :game_over,
253
+ condition: :really_game_over?,
254
+ callback: CallbackClass
255
+
256
+ event :you_won,
257
+ condition: -> { really_won? },
258
+ callback: CallbackClass
259
+ end
260
+
261
+ def really_won?
262
+ false
263
+ end
264
+
265
+ def really_game_over?
266
+ true
267
+ end
268
+ end
269
+
270
+ ```
271
+
272
+ ## Contributing
273
+ If you are going to contribute to this repo, please follow these simple rules:
274
+ * Cover you wrote with specs
275
+ * Check you wrote with Rubocop
276
+ * Use Karma-style comit messages
277
+
278
+ ## License
279
+ The gem is available as open source under the terms of the [MIT License](http://opensource.org/licenses/MIT).
@@ -0,0 +1,29 @@
1
+ # frozen_string_literal: true
2
+
3
+ begin
4
+ require 'bundler/setup'
5
+ rescue LoadError
6
+ puts 'You must `gem install bundler` and `bundle install` to run rake tasks'
7
+ end
8
+
9
+ require 'rdoc/task'
10
+
11
+ RDoc::Task.new(:rdoc) do |rdoc|
12
+ rdoc.rdoc_dir = 'rdoc'
13
+ rdoc.title = 'RailsEvents'
14
+ rdoc.options << '--line-numbers'
15
+ rdoc.rdoc_files.include('README.md')
16
+ rdoc.rdoc_files.include('lib/**/*.rb')
17
+ end
18
+
19
+ require 'bundler/gem_tasks'
20
+
21
+ require 'rake/testtask'
22
+
23
+ Rake::TestTask.new(:test) do |t|
24
+ t.libs << 'test'
25
+ t.pattern = 'test/**/*_test.rb'
26
+ t.verbose = false
27
+ end
28
+
29
+ task default: :test
@@ -0,0 +1,13 @@
1
+ # frozen_string_literal: true
2
+
3
+ namespace :with_events do
4
+ desc 'Run daily tasks'
5
+ task daily: :environment do
6
+ WithEvents::Trigger.new.call(WithEvents::Trigger::DAILY_APPEARANCE)
7
+ end
8
+
9
+ desc 'Run hourly tasks'
10
+ task hourly: :environment do
11
+ WithEvents::Trigger.new.call(WithEvents::Trigger::HOURLY_APPEARANCE)
12
+ end
13
+ end
@@ -0,0 +1,18 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'active_support/concern'
4
+ require 'require_all'
5
+
6
+ module WithEvents
7
+ extend ActiveSupport::Concern
8
+
9
+ autoload_all __dir__ + '/with_events'
10
+
11
+ module ClassMethods
12
+ def stream(name, options = {}, &block)
13
+ Stream.find_or_initialize(name, self, options)
14
+ .reset_configure_all
15
+ .instance_exec(&block)
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,33 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'active_support/core_ext/string'
4
+ require 'active_support/core_ext/hash'
5
+
6
+ module WithEvents
7
+ module Aws
8
+ class Message
9
+ attr_reader :event, :stream, :identifier
10
+
11
+ alias id identifier
12
+
13
+ def initialize(options = {})
14
+ @options = options.with_indifferent_access
15
+ @event = @options[:event]
16
+ @stream = @options[:stream]
17
+ @identifier = @options[:identifier]
18
+ end
19
+
20
+ def serialize
21
+ options.deep_transform_keys { |key| key.to_s.camelize(:lower) }
22
+ end
23
+
24
+ def self.from_sqs(options = {})
25
+ new(options.deep_transform_keys { |key| key.to_s.underscore })
26
+ end
27
+
28
+ private
29
+
30
+ attr_reader :options
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,38 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'active_support/core_ext/module/delegation'
4
+
5
+ module WithEvents
6
+ module Aws
7
+ class Publisher
8
+ def initialize(event, resource)
9
+ @event = event
10
+ @resource = resource
11
+ end
12
+
13
+ def publish
14
+ return unless event.identifier && identifier
15
+ topic.publish(message)
16
+ end
17
+
18
+ private
19
+
20
+ attr_reader :event, :resource
21
+ delegate :stream, to: :event
22
+
23
+ def identifier
24
+ @identifier ||= Invoker.new(event.identifier).invoke(resource)
25
+ end
26
+
27
+ def topic
28
+ @topic ||= Topic.new(stream.topic)
29
+ end
30
+
31
+ def message
32
+ @message ||= Message.new(event: event.name,
33
+ stream: stream.name,
34
+ identifier: identifier)
35
+ end
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,59 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'circuitry'
4
+
5
+ module Circuitry
6
+ Subscriber.prepend(
7
+ Module.new do
8
+ def handle_message_with_middleware(message, &block)
9
+ middleware.invoke(message.topic.name, message.body) do
10
+ handle_with_skip_delete(message, &block)
11
+ end
12
+ end
13
+
14
+ def handle_with_skip_delete(message, &block)
15
+ catch :skip_delete do
16
+ handle_message(message, &block)
17
+ delete_message(message)
18
+ end
19
+ end
20
+ end
21
+ )
22
+ end
23
+
24
+ module WithEvents
25
+ module Aws
26
+ class Topic
27
+ def initialize(topic = nil)
28
+ @topic = topic
29
+ end
30
+
31
+ def publish(message)
32
+ Circuitry.publish(topic, message.serialize)
33
+ end
34
+
35
+ def subscribe(options = {}, &block)
36
+ Circuitry.subscribe(options) do |message, topic_name|
37
+ skip_delete unless positive_result?(topic_name, message, &block)
38
+ end
39
+ end
40
+
41
+ private
42
+
43
+ attr_reader :topic
44
+
45
+ def positive_result?(topic_name, message)
46
+ message = Message.from_sqs(message)
47
+ valid_message?(message) && yield(message, topic_name)
48
+ end
49
+
50
+ def valid_message?(message)
51
+ message.stream && message.event && message.identifier
52
+ end
53
+
54
+ def skip_delete
55
+ throw :skip_delete
56
+ end
57
+ end
58
+ end
59
+ end
@@ -0,0 +1,58 @@
1
+ # frozen_string_literal: true
2
+
3
+ module WithEvents
4
+ class Event
5
+ attr_reader :name, :identifier, :options, :callback,
6
+ :condition, :stream, :finder
7
+
8
+ ##
9
+ # ==Options:
10
+ #
11
+ # +name+ - event name
12
+ # +klass+ - resource class name
13
+ # +options[:condition]+ - condition to check whether event can be triggered
14
+ # +options[:callback]+ - callback to invoke on event
15
+ # +options[:stream]+ - stream object event belongs to
16
+ # +options[:identifier]+ - resource identifier (symbol, Proc or Class)
17
+ # +options[:finder]+ - resource finder (symbol, Proc or Class)
18
+ # +options[:subscribe]+ - subscribe to SQS queue
19
+ def initialize(name, klass, options = {})
20
+ @name = name
21
+ @klass = klass
22
+ @options = options
23
+ @condition = options[:condition]
24
+ @callback = options[:callback]
25
+ @stream = options[:stream]
26
+ @identifier = options[:identifier]
27
+ @finder = options[:finder]
28
+
29
+ define_condition
30
+ define_callback
31
+ end
32
+
33
+ private
34
+
35
+ attr_reader :klass
36
+
37
+ def define_condition
38
+ return unless condition
39
+
40
+ klass.instance_exec(self) do |event|
41
+ define_method("#{event.name}?") do
42
+ return false unless event.condition
43
+ Invoker.new(event.condition).invoke(self)
44
+ end
45
+ end
46
+ end
47
+
48
+ def define_callback
49
+ klass.instance_exec(self) do |event|
50
+ define_method("#{event.name}!") do
51
+ event.stream.notify(event, self)
52
+ return if event.stream.subscribe || !event.callback
53
+ Invoker.new(event.callback).invoke(self)
54
+ end
55
+ end
56
+ end
57
+ end
58
+ end
@@ -0,0 +1,33 @@
1
+ # frozen_string_literal: true
2
+
3
+ module WithEvents
4
+ class Invoker
5
+ def initialize(callable)
6
+ @callable = callable
7
+ end
8
+
9
+ def invoke(context, *args)
10
+ return context.instance_exec(*args, &callable) if proc?
11
+ return callable.new.call(context, *args) if class?
12
+ return context.public_send(callable, *args) if symbol?(context)
13
+
14
+ raise NotImplementedError, 'Argument can not be invoked'
15
+ end
16
+
17
+ private
18
+
19
+ attr_reader :callable
20
+
21
+ def proc?
22
+ callable.is_a?(Proc)
23
+ end
24
+
25
+ def class?
26
+ callable.is_a?(Class) && callable.instance_methods.include?(:call)
27
+ end
28
+
29
+ def symbol?(context)
30
+ callable.is_a?(Symbol) && context.respond_to?(callable)
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,101 @@
1
+ # frozen_string_literal: true
2
+
3
+ module WithEvents
4
+ class Stream
5
+ attr_reader :name, :klass, :events, :watchers, :topic, :subscribe
6
+
7
+ def initialize(name, klass, options = {})
8
+ @name = name
9
+ @klass = klass
10
+ @events = []
11
+ @watchers = {}
12
+ @topic = options[:topic]
13
+ @configuration = {}
14
+ @subscribe = options[:subscribe]
15
+
16
+ self.class.streams << self
17
+ end
18
+
19
+ def event(name, options = {})
20
+ events <<
21
+ Event.new(name, klass, options.merge(configuration).merge(stream: self))
22
+ end
23
+
24
+ def reset_configure_all
25
+ @configuration = {}
26
+ self
27
+ end
28
+
29
+ def configure_all(options = {})
30
+ @configuration = options
31
+ end
32
+
33
+ def on(name, &block)
34
+ watchers[name] ||= []
35
+ watchers[name] << block
36
+ end
37
+
38
+ def notify(event, resource)
39
+ notify_sqs(event, resource) if topic
40
+ notify_watchers(event, resource)
41
+ end
42
+
43
+ def notify_watchers(event, resource)
44
+ return if watchers[event.name].nil?
45
+ watchers[event.name].each { |watcher| resource.instance_exec(&watcher) }
46
+ end
47
+
48
+ class << self
49
+ attr_accessor :subscribed
50
+
51
+ def streams
52
+ @streams ||= []
53
+ end
54
+
55
+ def find_or_initialize(name, klass, options = {})
56
+ find(name) || new(name, klass, options)
57
+ end
58
+
59
+ def find(name)
60
+ streams.find { |s| s.name == name }
61
+ end
62
+
63
+ def subscribe
64
+ return if subscribed || !streams.find { |s| s.topic && s.subscribe }
65
+ self.subscribed = true
66
+
67
+ Aws::Topic.new.subscribe(async: true, timeout: 0) do |message, topic|
68
+ selected = stream_events(message, topic)
69
+ selected.each { |event| notify_event(event, message) }.size.positive?
70
+ end
71
+ end
72
+
73
+ private
74
+
75
+ def stream_events(message, topic_name)
76
+ stream = find(message.stream.to_sym)
77
+ return [] unless stream&.subscribe && stream&.topic&.to_s == topic_name
78
+ stream.events.select { |event| valid_event?(event, message) }
79
+ end
80
+
81
+ def valid_event?(event, message)
82
+ event.finder && event.callback && message.event.to_sym == event.name
83
+ end
84
+
85
+ def notify_event(event, message)
86
+ context = Invoker.new(event.finder)
87
+ .invoke(TOPLEVEL_BINDING.eval('self'), message)
88
+ event.stream.notify_watchers(event, context)
89
+ Invoker.new(event.callback).invoke(context)
90
+ end
91
+ end
92
+
93
+ private
94
+
95
+ attr_reader :configuration
96
+
97
+ def notify_sqs(event, resource)
98
+ Aws::Publisher.new(event, resource).publish
99
+ end
100
+ end
101
+ end
@@ -0,0 +1,22 @@
1
+ # frozen_string_literal: true
2
+
3
+ module WithEvents
4
+ class Trigger
5
+ include WithEvents::Validator
6
+
7
+ def call(appearance)
8
+ Stream.streams.each do |stream|
9
+ process_stream(stream, appearance.to_sym)
10
+ end
11
+ end
12
+
13
+ private
14
+
15
+ def process_stream(stream, appearance)
16
+ stream.events.each do |event|
17
+ next unless valid_event?(event, appearance)
18
+ WithEvents::Worker.perform_async(stream.name, event.name, appearance)
19
+ end
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,30 @@
1
+ # frozen_string_literal: true
2
+
3
+ module WithEvents
4
+ module Validator
5
+ HOURLY_APPEARANCE = :hourly
6
+ DAILY_APPEARANCE = :daily
7
+
8
+ def valid_event?(event, appearance)
9
+ background_event?(event) &&
10
+ valid_appearance?(event, appearance) &&
11
+ valid_batch?(event)
12
+ end
13
+
14
+ private
15
+
16
+ def valid_batch?(event)
17
+ event.options[:batch].is_a?(Proc)
18
+ end
19
+
20
+ def background_event?(event)
21
+ event.options[:background]
22
+ end
23
+
24
+ def valid_appearance?(event, appearance)
25
+ [HOURLY_APPEARANCE, DAILY_APPEARANCE]
26
+ .include?(event.options[:appearance]) &&
27
+ event.options[:appearance] == appearance.to_sym
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ module WithEvents
4
+ VERSION = '0.1.12'
5
+ end
@@ -0,0 +1,58 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'sidekiq'
4
+
5
+ module WithEvents
6
+ class Worker
7
+ include WithEvents::Validator
8
+ include Sidekiq::Worker
9
+
10
+ sidekiq_options retry: false
11
+
12
+ def perform(stream, event_name, appearance)
13
+ events(stream, event_name, appearance).each do |event|
14
+ event.options[:batch].call.each do |resource|
15
+ call(event, resource) if may_call?(event, resource)
16
+ end
17
+ end
18
+
19
+ reraise_last_exception
20
+ end
21
+
22
+ private
23
+
24
+ # rubocop:disable Lint/RescueWithoutErrorClass
25
+ def call(event, resource)
26
+ resource.public_send("#{event.name}!")
27
+ rescue => e
28
+ exceptions << e
29
+ end
30
+
31
+ def may_call?(event, resource)
32
+ resource.public_send("#{event.name}?")
33
+ rescue => e
34
+ exceptions << e
35
+ end
36
+ # rubocop:enable Lint/RescueWithoutErrorClass
37
+
38
+ def stream(stream)
39
+ @stream ||= Stream.find(stream.to_sym)
40
+ end
41
+
42
+ def events(stream, name, appearance)
43
+ return [] unless stream(stream)
44
+
45
+ @events ||= stream(stream).events.select do |event|
46
+ event.name == name.to_sym && valid_event?(event, appearance)
47
+ end
48
+ end
49
+
50
+ def exceptions
51
+ @exceptions ||= []
52
+ end
53
+
54
+ def reraise_last_exception
55
+ raise exceptions.last if exceptions.size.positive?
56
+ end
57
+ end
58
+ end
metadata ADDED
@@ -0,0 +1,171 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: with_events
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.12
5
+ platform: ruby
6
+ authors:
7
+ - Vlad Gramuzov
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2018-05-14 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: activesupport
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: 4.2.7
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: 4.2.7
27
+ - !ruby/object:Gem::Dependency
28
+ name: circuitry
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - '='
32
+ - !ruby/object:Gem::Version
33
+ version: '3.2'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - '='
39
+ - !ruby/object:Gem::Version
40
+ version: '3.2'
41
+ - !ruby/object:Gem::Dependency
42
+ name: require_all
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: 1.4.0
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: 1.4.0
55
+ - !ruby/object:Gem::Dependency
56
+ name: sidekiq
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: 3.5.3
62
+ type: :runtime
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: 3.5.3
69
+ - !ruby/object:Gem::Dependency
70
+ name: pry
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - ">="
74
+ - !ruby/object:Gem::Version
75
+ version: '0'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - ">="
81
+ - !ruby/object:Gem::Version
82
+ version: '0'
83
+ - !ruby/object:Gem::Dependency
84
+ name: rspec
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - ">="
88
+ - !ruby/object:Gem::Version
89
+ version: '0'
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - ">="
95
+ - !ruby/object:Gem::Version
96
+ version: '0'
97
+ - !ruby/object:Gem::Dependency
98
+ name: rubocop
99
+ requirement: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - "~>"
102
+ - !ruby/object:Gem::Version
103
+ version: 0.51.0
104
+ type: :development
105
+ prerelease: false
106
+ version_requirements: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - "~>"
109
+ - !ruby/object:Gem::Version
110
+ version: 0.51.0
111
+ - !ruby/object:Gem::Dependency
112
+ name: sqlite3
113
+ requirement: !ruby/object:Gem::Requirement
114
+ requirements:
115
+ - - ">="
116
+ - !ruby/object:Gem::Version
117
+ version: '0'
118
+ type: :development
119
+ prerelease: false
120
+ version_requirements: !ruby/object:Gem::Requirement
121
+ requirements:
122
+ - - ">="
123
+ - !ruby/object:Gem::Version
124
+ version: '0'
125
+ description: A simple events system for Ruby apps.
126
+ email:
127
+ - vlad.gramuzov@gmail.com
128
+ executables: []
129
+ extensions: []
130
+ extra_rdoc_files: []
131
+ files:
132
+ - MIT-LICENSE
133
+ - README.md
134
+ - Rakefile
135
+ - lib/tasks/with_events/with_events_tasks.rake
136
+ - lib/with_events.rb
137
+ - lib/with_events/aws/message.rb
138
+ - lib/with_events/aws/publisher.rb
139
+ - lib/with_events/aws/topic.rb
140
+ - lib/with_events/event.rb
141
+ - lib/with_events/invoker.rb
142
+ - lib/with_events/stream.rb
143
+ - lib/with_events/trigger.rb
144
+ - lib/with_events/validator.rb
145
+ - lib/with_events/version.rb
146
+ - lib/with_events/worker.rb
147
+ homepage: https://github.com/pandomic/with_events
148
+ licenses:
149
+ - MIT
150
+ metadata: {}
151
+ post_install_message:
152
+ rdoc_options: []
153
+ require_paths:
154
+ - lib
155
+ required_ruby_version: !ruby/object:Gem::Requirement
156
+ requirements:
157
+ - - ">="
158
+ - !ruby/object:Gem::Version
159
+ version: '0'
160
+ required_rubygems_version: !ruby/object:Gem::Requirement
161
+ requirements:
162
+ - - ">="
163
+ - !ruby/object:Gem::Version
164
+ version: '0'
165
+ requirements: []
166
+ rubyforge_project:
167
+ rubygems_version: 2.6.14
168
+ signing_key:
169
+ specification_version: 4
170
+ summary: A simple events system for Ruby apps.
171
+ test_files: []