active_delivery 1.0.0.rc2 → 1.0.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.
@@ -1,25 +1,53 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module AbstractNotifier
4
- # Notificaiton payload wrapper which contains
4
+ # NotificationDelivery payload wrapper which contains
5
5
  # information about the current notifier class
6
6
  # and knows how to trigger the delivery
7
- class Notification
8
- attr_reader :payload, :owner
7
+ class NotificationDelivery
8
+ attr_reader :action_name
9
+
10
+ def initialize(owner_class, action_name, params: {}, args: [], kwargs: {})
11
+ @owner_class = owner_class
12
+ @action_name = action_name
13
+ @params = params
14
+ @args = args
15
+ @kwargs = kwargs
16
+ end
9
17
 
10
- def initialize(owner, payload)
11
- @owner = owner
12
- @payload = payload
18
+ def processed
19
+ return @processed if instance_variable_defined?(:@processed)
20
+
21
+ @processed = notifier.process_action(action_name, *args, **kwargs) || Notification.new(nil)
13
22
  end
14
23
 
24
+ alias_method :notification, :processed
25
+
15
26
  def notify_later
16
- return if AbstractNotifier.noop?
17
- owner.async_adapter.enqueue owner, payload
27
+ owner_class.async_adapter.enqueue(owner_class.name, action_name, params:, args:, kwargs:)
18
28
  end
19
29
 
20
30
  def notify_now
21
- return if AbstractNotifier.noop?
22
- owner.driver.call(payload)
31
+ return unless notification.payload
32
+
33
+ notifier.deliver!(notification)
34
+ end
35
+
36
+ private
37
+
38
+ attr_reader :owner_class, :params, :args, :kwargs
39
+
40
+ def notifier
41
+ @notifier ||= owner_class.new(action_name, **params)
42
+ end
43
+ end
44
+
45
+ # Notification object contains the compiled payload to be delivered
46
+ class Notification
47
+ attr_reader :payload
48
+
49
+ def initialize(payload)
50
+ @payload = payload
23
51
  end
24
52
  end
25
53
 
@@ -35,11 +63,7 @@ module AbstractNotifier
35
63
 
36
64
  # rubocop:disable Style/MethodMissingSuper
37
65
  def method_missing(method_name, *args, **kwargs)
38
- if kwargs.empty?
39
- notifier_class.new(method_name, **params).public_send(method_name, *args)
40
- else
41
- notifier_class.new(method_name, **params).public_send(method_name, *args, **kwargs)
42
- end
66
+ NotificationDelivery.new(notifier_class, method_name, params:, args:, kwargs:)
43
67
  end
44
68
  # rubocop:enable Style/MethodMissingSuper
45
69
 
@@ -112,9 +136,9 @@ module AbstractNotifier
112
136
  end
113
137
  end
114
138
 
115
- def method_missing(method_name, *args)
139
+ def method_missing(method_name, *args, **kwargs)
116
140
  if action_methods.include?(method_name.to_s)
117
- new(method_name).public_send(method_name, *args)
141
+ NotificationDelivery.new(self, method_name, args:, kwargs:)
118
142
  else
119
143
  super
120
144
  end
@@ -152,16 +176,31 @@ module AbstractNotifier
152
176
  @params = params.freeze
153
177
  end
154
178
 
179
+ def process_action(...)
180
+ public_send(...)
181
+ end
182
+
183
+ def deliver!(notification)
184
+ self.class.driver.call(notification.payload)
185
+ end
186
+
155
187
  def notification(**payload)
156
188
  merge_defaults!(payload)
157
189
 
190
+ payload[:body] = implicit_payload_body unless payload.key?(:body)
191
+
158
192
  raise ArgumentError, "Notification body must be present" if
159
193
  payload[:body].nil? || payload[:body].empty?
160
- Notification.new(self.class, payload)
194
+
195
+ @notification = Notification.new(payload)
161
196
  end
162
197
 
163
198
  private
164
199
 
200
+ def implicit_payload_body
201
+ # no-op — override to provide custom logic
202
+ end
203
+
165
204
  def merge_defaults!(payload)
166
205
  defaults =
167
206
  if self.class.defaults_generator
@@ -0,0 +1,94 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "active_support/version"
4
+ require "active_support/callbacks"
5
+ require "active_support/concern"
6
+
7
+ module AbstractNotifier
8
+ # Add callbacks support to Abstract Notifier (requires ActiveSupport::Callbacks)
9
+ #
10
+ # # Run method before seding notification
11
+ # # NOTE: when `false` is returned the execution is halted
12
+ # before_action :do_something
13
+ #
14
+ # # after_ and around_ callbacks are also supported
15
+ # after_action :cleanup
16
+ #
17
+ # around_action :set_context
18
+ #
19
+ # # Deliver callbacks are also available
20
+ # before_deliver :do_something
21
+ #
22
+ # # after_ and around_ callbacks are also supported
23
+ # after_deliver :cleanup
24
+ #
25
+ # around_deliver :set_context
26
+ module Callbacks
27
+ extend ActiveSupport::Concern
28
+
29
+ include ActiveSupport::Callbacks
30
+
31
+ CALLBACK_TERMINATOR = ->(_target, result) { result.call == false }
32
+
33
+ included do
34
+ define_callbacks :action,
35
+ terminator: CALLBACK_TERMINATOR,
36
+ skip_after_callbacks_if_terminated: true
37
+
38
+ define_callbacks :deliver,
39
+ terminator: CALLBACK_TERMINATOR,
40
+ skip_after_callbacks_if_terminated: true
41
+
42
+ prepend InstanceExt
43
+ end
44
+
45
+ module InstanceExt
46
+ def process_action(...)
47
+ run_callbacks(:action) { super(...) }
48
+ end
49
+
50
+ def deliver!(...)
51
+ run_callbacks(:deliver) { super(...) }
52
+ end
53
+ end
54
+
55
+ class_methods do
56
+ def _normalize_callback_options(options)
57
+ _normalize_callback_option(options, :only, :if)
58
+ _normalize_callback_option(options, :except, :unless)
59
+ end
60
+
61
+ def _normalize_callback_option(options, from, to)
62
+ if (from = options[from])
63
+ from_set = Array(from).map(&:to_s).to_set
64
+ from = proc { |c| from_set.include? c.notification_name.to_s }
65
+ options[to] = Array(options[to]).unshift(from)
66
+ end
67
+ end
68
+
69
+ %i[before after around].each do |kind|
70
+ %i[action deliver].each do |event|
71
+ define_method "#{kind}_#{event}" do |*names, on: event, **options, &block|
72
+ _normalize_callback_options(options)
73
+
74
+ names.each do |name|
75
+ set_callback on, kind, name, options
76
+ end
77
+
78
+ set_callback on, kind, block, options if block
79
+ end
80
+
81
+ define_method "skip_#{kind}_#{event}" do |*names, on: event, **options|
82
+ _normalize_callback_options(options)
83
+
84
+ names.each do |name|
85
+ skip_callback(on, kind, name, options)
86
+ end
87
+ end
88
+ end
89
+ end
90
+ end
91
+ end
92
+ end
93
+
94
+ AbstractNotifier::Base.include AbstractNotifier::Callbacks
@@ -27,23 +27,27 @@ module AbstractNotifier
27
27
  end
28
28
  end
29
29
 
30
- module Notification
30
+ module NotificationDelivery
31
31
  def notify_now
32
32
  return super unless AbstractNotifier.test?
33
33
 
34
- Driver.send_notification payload.merge(via: owner)
34
+ payload = notification.payload
35
+
36
+ Driver.send_notification payload.merge(via: notifier.class)
35
37
  end
36
38
 
37
39
  def notify_later
38
40
  return super unless AbstractNotifier.test?
39
41
 
40
- Driver.enqueue_notification payload.merge(via: owner)
42
+ payload = notification.payload
43
+
44
+ Driver.enqueue_notification payload.merge(via: notifier.class)
41
45
  end
42
46
  end
43
47
  end
44
48
  end
45
49
 
46
- AbstractNotifier::Notification.prepend AbstractNotifier::Testing::Notification
50
+ AbstractNotifier::NotificationDelivery.prepend AbstractNotifier::Testing::NotificationDelivery
47
51
 
48
52
  require "abstract_notifier/testing/rspec" if defined?(RSpec::Core)
49
53
  require "abstract_notifier/testing/minitest" if defined?(Minitest::Assertions)
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module AbstractNotifier
4
- VERSION = "0.3.2"
4
+ VERSION = "1.0.0"
5
5
  end
@@ -69,6 +69,7 @@ end
69
69
  require "abstract_notifier/base"
70
70
  require "abstract_notifier/async_adapters"
71
71
 
72
+ require "abstract_notifier/callbacks" if defined?(ActiveSupport)
72
73
  require "abstract_notifier/async_adapters/active_job" if defined?(ActiveJob)
73
74
 
74
75
  require "abstract_notifier/testing" if ENV["RACK_ENV"] == "test" || ENV["RAILS_ENV"] == "test"
@@ -16,7 +16,7 @@ module ActiveDelivery
16
16
  @id = id
17
17
  @owner = owner
18
18
  @options = options.tap(&:freeze)
19
- @resolver = options[:resolver]
19
+ @resolver = options[:resolver] || build_pattern_resolver(options[:resolver_pattern])
20
20
  end
21
21
 
22
22
  def dup_for(new_owner)
@@ -65,6 +65,25 @@ module ActiveDelivery
65
65
  private
66
66
 
67
67
  attr_reader :resolver
68
+
69
+ def build_pattern_resolver(pattern)
70
+ return unless pattern
71
+
72
+ proc do |delivery|
73
+ delivery_class = delivery.name
74
+
75
+ next unless delivery_class
76
+
77
+ *namespace, delivery_name = delivery_class.split("::")
78
+
79
+ delivery_namespace = ""
80
+ delivery_namespace = "#{namespace.join("::")}::" unless namespace.empty?
81
+
82
+ delivery_name = delivery_name.sub(/Delivery$/, "")
83
+
84
+ (pattern % {delivery_class:, delivery_name:, delivery_namespace:}).safe_constantize
85
+ end
86
+ end
68
87
  end
69
88
  end
70
89
  end
@@ -18,7 +18,7 @@ module ActiveDelivery
18
18
 
19
19
  def initialize(**opts)
20
20
  super
21
- @resolver = opts.fetch(:resolver, build_resolver(options.fetch(:suffix, DEFAULT_SUFFIX)))
21
+ @resolver ||= build_resolver(options.fetch(:suffix, DEFAULT_SUFFIX))
22
22
  end
23
23
 
24
24
  def resolve_class(klass)
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module ActiveDelivery
4
- VERSION = "1.0.0.rc2"
4
+ VERSION = "1.0.0"
5
5
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: active_delivery
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.0.rc2
4
+ version: 1.0.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Vladimir Dementyev
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2023-04-05 00:00:00.000000000 Z
11
+ date: 2023-08-29 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -67,7 +67,7 @@ dependencies:
67
67
  - !ruby/object:Gem::Version
68
68
  version: '4.0'
69
69
  - !ruby/object:Gem::Dependency
70
- name: ruby-next
70
+ name: ruby-next-core
71
71
  requirement: !ruby/object:Gem::Requirement
72
72
  requirements:
73
73
  - - ">="
@@ -93,16 +93,20 @@ files:
93
93
  - README.md
94
94
  - bin/console
95
95
  - bin/setup
96
+ - lib/.rbnext/3.0/abstract_notifier/async_adapters/active_job.rb
96
97
  - lib/.rbnext/3.0/active_delivery/base.rb
97
98
  - lib/.rbnext/3.0/active_delivery/callbacks.rb
98
99
  - lib/.rbnext/3.0/active_delivery/lines/base.rb
99
100
  - lib/.rbnext/3.0/active_delivery/lines/mailer.rb
101
+ - lib/.rbnext/3.0/active_delivery/testing.rb
102
+ - lib/.rbnext/3.1/abstract_notifier/base.rb
100
103
  - lib/.rbnext/3.1/active_delivery/base.rb
101
104
  - lib/.rbnext/3.1/active_delivery/lines/base.rb
102
105
  - lib/abstract_notifier.rb
103
106
  - lib/abstract_notifier/async_adapters.rb
104
107
  - lib/abstract_notifier/async_adapters/active_job.rb
105
108
  - lib/abstract_notifier/base.rb
109
+ - lib/abstract_notifier/callbacks.rb
106
110
  - lib/abstract_notifier/testing.rb
107
111
  - lib/abstract_notifier/testing/minitest.rb
108
112
  - lib/abstract_notifier/testing/rspec.rb
@@ -138,9 +142,9 @@ required_ruby_version: !ruby/object:Gem::Requirement
138
142
  version: '2.7'
139
143
  required_rubygems_version: !ruby/object:Gem::Requirement
140
144
  requirements:
141
- - - ">"
145
+ - - ">="
142
146
  - !ruby/object:Gem::Version
143
- version: 1.3.1
147
+ version: '0'
144
148
  requirements: []
145
149
  rubygems_version: 3.4.8
146
150
  signing_key: