Skip to content

Commit 23aa237

Browse files
authored
Split callbacks into separate modules (#1086)
1 parent f366c8d commit 23aa237

8 files changed

Lines changed: 555 additions & 539 deletions

File tree

src/avram/callbacks.cr

Lines changed: 0 additions & 535 deletions
This file was deleted.
Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
require "./callback_helpers"
2+
3+
module Avram::AfterCommitCallback
4+
include Avram::CallbackHelpers
5+
6+
# Run the given method after save and after successful transaction commit
7+
#
8+
# Optionally you can pass an `if` or `unless` argument which allows you to
9+
# run this conditionally. The symbol should reference a method you've defined
10+
# that returns a truthy/falsey value
11+
#
12+
# The newly saved record will be passed to the method.
13+
#
14+
# ```
15+
# class SaveComment < Comment::SaveOperation
16+
# after_commit notify_post_author
17+
#
18+
# private def notify_post_author(comment : Comment)
19+
# NewCommentNotificationEmail.new(comment, to: comment.author!).deliver_now
20+
# end
21+
# end
22+
# ```
23+
#
24+
macro after_commit(method_name, if _if = nil, unless _unless = nil)
25+
{% unless _if.is_a?(SymbolLiteral) || _if.is_a?(NilLiteral) %}
26+
conditional_error_for_inline_callbacks(:after_commit, {{ method_name }}, :if)
27+
{% end %}
28+
{% unless _unless.is_a?(SymbolLiteral) || _unless.is_a?(NilLiteral) %}
29+
conditional_error_for_inline_callbacks(:after_commit, {{ method_name }}, :unless)
30+
{% end %}
31+
after_commit(if: {{ _if }}, unless: {{ _unless }}) do |object|
32+
{{ method_name.id }}(object)
33+
end
34+
end
35+
36+
# Run the given block after save and after successful transaction commit
37+
#
38+
# The newly saved record will be passed to the method.
39+
#
40+
# ```
41+
# class SaveComment < Comment::SaveOperation
42+
# after_commit do |comment|
43+
# NewCommentNotificationEmail.new(comment, to: comment.author!).deliver_now
44+
# end
45+
# end
46+
# ```
47+
macro after_commit(if _if = nil, unless _unless = nil, &block)
48+
{% if _if != nil && _unless != nil %}
49+
{% raise "Your after_commit callbacks should only specify `if` or `unless`, but not both." %}
50+
{% end %}
51+
{% unless _if.is_a?(SymbolLiteral) || _if.is_a?(NilLiteral) %}
52+
conditional_error_for_block_callbacks(:after_commit, :if)
53+
{% end %}
54+
{% unless _unless.is_a?(SymbolLiteral) || _unless.is_a?(NilLiteral) %}
55+
conditional_error_for_block_callbacks(:after_commit, :unless)
56+
{% end %}
57+
{%
58+
if block.args.size != 1
59+
raise <<-ERR
60+
The 'after_commit' callback requires exactly 1 block arg to be passed.
61+
Example:
62+
after_commit do |saved_user|
63+
some_method(saved_user)
64+
end
65+
ERR
66+
end
67+
%}
68+
def after_commit(%object : T)
69+
{% if @type.methods.map(&.name).includes?(:after_commit.id) %}
70+
previous_def
71+
{% else %}
72+
super
73+
{% end %}
74+
75+
{% if _if %}
76+
if {{ _if.id }}
77+
{{ block.args.first }} = %object
78+
{{ block.body }}
79+
end
80+
{% elsif _unless %}
81+
unless {{ _unless.id }}
82+
{{ block.args.first }} = %object
83+
{{ block.body }}
84+
end
85+
{% else %}
86+
{{ block.args.first }} = %object
87+
{{ block.body }}
88+
{% end %}
89+
end
90+
end
91+
end
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
module Avram::CallbackHelpers
2+
# :nodoc:
3+
macro conditional_error_for_inline_callbacks(callback, method_name, condition)
4+
\{%
5+
raise <<-ERROR
6+
You must pass a Symbol to `{{ condition.id }}` in {{ @type }}. The Symbol will reference a method you define.
7+
8+
Try this...
9+
10+
{{ callback.id }} {{ method_name.id }}, {{ condition.id }}: :check_condition?
11+
12+
def check_condition?
13+
# return your bool value here
14+
end
15+
ERROR
16+
%}
17+
end
18+
19+
macro conditional_error_for_block_callbacks(callback, condition)
20+
\{%
21+
raise <<-ERROR
22+
You must pass a Symbol to `{{ condition.id }}` in {{ @type }}. The Symbol will reference a method you define.
23+
24+
Try this...
25+
26+
{{ callback.id }}({{ condition.id }}: :check_condition?) do
27+
# your callback block
28+
end
29+
30+
def check_condition?
31+
# return your bool value here
32+
end
33+
ERROR
34+
%}
35+
end
36+
end

src/avram/callbacks/callbacks.cr

Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
module Avram::Callbacks
2+
# Run the given method before `run` is called on an `Operation`.
3+
#
4+
# ```
5+
# before_run :validate_inputs
6+
#
7+
# private def validate_inputs
8+
# validate_required data
9+
# end
10+
# ```
11+
macro before_run(method_name)
12+
before_run do
13+
{{ method_name.id }}
14+
end
15+
end
16+
17+
# Run the given block before `run` is called on an `Operation`.
18+
#
19+
# ```
20+
# before_run do
21+
# validate_required data
22+
# end
23+
# ```
24+
macro before_run
25+
def before_run
26+
{% if @type.methods.map(&.name).includes?(:before_run.id) %}
27+
previous_def
28+
{% else %}
29+
super
30+
{% end %}
31+
32+
{{ yield }}
33+
end
34+
end
35+
36+
# Run the given method after `run` is called on an `Operation`.
37+
# The return value of the `run` method is passed to `method_name`.
38+
#
39+
# ```
40+
# after_run :log_entry
41+
#
42+
# private def log_entry(value)
43+
# log_stuff(value)
44+
# end
45+
# ```
46+
macro after_run(method_name)
47+
after_run do |object|
48+
{{ method_name.id }}(object)
49+
end
50+
end
51+
52+
# Run the given block after the operation runs
53+
#
54+
# The return value from `run` will be passed to this block.
55+
#
56+
# ```
57+
# class GenerateReport < Avram::Operation
58+
# after_run do |value|
59+
# value == "some report"
60+
# end
61+
#
62+
# def run
63+
# "some report"
64+
# end
65+
# end
66+
# ```
67+
macro after_run(&block)
68+
{%
69+
if block.args.size != 1
70+
raise <<-ERR
71+
The 'after_run' callback requires exactly 1 block arg to be passed.
72+
Example:
73+
after_run { |value| some_method(value) }
74+
ERR
75+
end
76+
%}
77+
def after_run(%object)
78+
{% if @type.methods.map(&.name).includes?(:after_run.id) %}
79+
previous_def
80+
{% else %}
81+
super
82+
{% end %}
83+
84+
{{ block.args.first }} = %object
85+
{{ block.body }}
86+
end
87+
end
88+
end
Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,108 @@
1+
require "./after_commit_callback"
2+
3+
module Avram::DeleteCallbacks
4+
include Avram::AfterCommitCallback
5+
6+
# Same as `before_save`, but with a different name
7+
macro before_delete(method_name, if _if = nil, unless _unless = nil)
8+
{% unless _if.is_a?(SymbolLiteral) || _if.is_a?(NilLiteral) %}
9+
conditional_error_for_inline_callbacks(:before_delete, {{ method_name }}, :if)
10+
{% end %}
11+
{% unless _unless.is_a?(SymbolLiteral) || _unless.is_a?(NilLiteral) %}
12+
conditional_error_for_inline_callbacks(:before_delete, {{ method_name }}, :unless)
13+
{% end %}
14+
before_delete(if: {{ _if }}, unless: {{ _unless }}) do
15+
{{ method_name.id }}
16+
end
17+
end
18+
19+
macro before_delete(if _if = nil, unless _unless = nil)
20+
{% if _if != nil && _unless != nil %}
21+
{% raise "Your before_delete callbacks should only specify `if` or `unless`, but not both." %}
22+
{% end %}
23+
{% unless _if.is_a?(SymbolLiteral) || _if.is_a?(NilLiteral) %}
24+
conditional_error_for_block_callbacks(:before_delete, :if)
25+
{% end %}
26+
{% unless _unless.is_a?(SymbolLiteral) || _unless.is_a?(NilLiteral) %}
27+
conditional_error_for_block_callbacks(:before_delete, :unless)
28+
{% end %}
29+
30+
def before_delete
31+
{% if @type.methods.map(&.name).includes?(:before_delete.id) %}
32+
previous_def
33+
{% else %}
34+
super
35+
{% end %}
36+
37+
{% if _if %}
38+
if {{ _if.id }}
39+
{{ yield }}
40+
end
41+
{% elsif _unless %}
42+
unless {{ _unless.id }}
43+
{{ yield }}
44+
end
45+
{% else %}
46+
{{ yield }}
47+
{% end %}
48+
end
49+
end
50+
51+
# Same as `after_save` but with a different name
52+
macro after_delete(method_name, if _if = nil, unless _unless = nil)
53+
{% unless _if.is_a?(SymbolLiteral) || _if.is_a?(NilLiteral) %}
54+
conditional_error_for_inline_callbacks(:after_delete, {{ method_name }}, :if)
55+
{% end %}
56+
{% unless _unless.is_a?(SymbolLiteral) || _unless.is_a?(NilLiteral) %}
57+
conditional_error_for_inline_callbacks(:after_delete, {{ method_name }}, :unless)
58+
{% end %}
59+
after_delete(if: {{ _if }}, unless: {{ _unless }}) do |object|
60+
{{ method_name.id }}(object)
61+
end
62+
end
63+
64+
macro after_delete(if _if = nil, unless _unless = nil, &block)
65+
{% if _if != nil && _unless != nil %}
66+
{% raise "Your after_delete callbacks should only specify `if` or `unless`, but not both." %}
67+
{% end %}
68+
{% unless _if.is_a?(SymbolLiteral) || _if.is_a?(NilLiteral) %}
69+
conditional_error_for_block_callbacks(:after_delete, :if)
70+
{% end %}
71+
{% unless _unless.is_a?(SymbolLiteral) || _unless.is_a?(NilLiteral) %}
72+
conditional_error_for_block_callbacks(:after_delete, :unless)
73+
{% end %}
74+
{%
75+
if block.args.size != 1
76+
raise <<-ERR
77+
The 'after_delete' callback requires exactly 1 block arg to be passed.
78+
Example:
79+
after_delete do |deleted_user|
80+
some_method(deleted_user)
81+
end
82+
ERR
83+
end
84+
%}
85+
def after_delete(%object : T)
86+
{% if @type.methods.map(&.name).includes?(:after_delete.id) %}
87+
previous_def
88+
{% else %}
89+
super
90+
{% end %}
91+
92+
{% if _if %}
93+
if {{ _if.id }}
94+
{{ block.args.first }} = %object
95+
{{ block.body }}
96+
end
97+
{% elsif _unless %}
98+
unless {{ _unless.id }}
99+
{{ block.args.first }} = %object
100+
{{ block.body }}
101+
end
102+
{% else %}
103+
{{ block.args.first }} = %object
104+
{{ block.body }}
105+
{% end %}
106+
end
107+
end
108+
end

0 commit comments

Comments
 (0)