Skip to content

Commit 7f368ea

Browse files
authored
Merge pull request #95 from wadetandy/after_commit_hook
Add after_commit hooks to resources
2 parents c8d7a51 + c269823 commit 7f368ea

File tree

11 files changed

+517
-381
lines changed

11 files changed

+517
-381
lines changed

lib/graphiti.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,7 @@
6161
require "graphiti/util/persistence"
6262
require "graphiti/util/validation_response"
6363
require "graphiti/util/sideload"
64-
require "graphiti/util/hooks"
64+
require "graphiti/util/transaction_hooks_recorder"
6565
require "graphiti/util/attribute_check"
6666
require "graphiti/util/serializer_attributes"
6767
require "graphiti/util/serializer_relationships"

lib/graphiti/resource.rb

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -121,14 +121,21 @@ def before_commit(model, metadata)
121121
end
122122
end
123123

124+
def after_commit(model, metadata)
125+
hooks = self.class.config[:after_commit][metadata[:method]] || []
126+
hooks.each do |hook|
127+
instance_exec(model, metadata, &hook)
128+
end
129+
end
130+
124131
def transaction
125132
response = nil
126133
begin
127134
adapter.transaction(model) do
128135
response = yield
129136
end
130137
rescue Errors::ValidationError => e
131-
response = e.validation_response
138+
response = { result: e.validation_response }
132139
end
133140
response
134141
end

lib/graphiti/resource/configuration.rb

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -181,6 +181,7 @@ def config
181181
sorts: {},
182182
pagination: nil,
183183
before_commit: {},
184+
after_commit: {},
184185
attributes: {},
185186
extra_attributes: {},
186187
sideloads: {},

lib/graphiti/resource/dsl.rb

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,13 @@ def before_commit(only: [:create, :update, :destroy], &blk)
8787
end
8888
end
8989

90+
def after_commit(only: [:create, :update, :destroy], &blk)
91+
Array(only).each do |verb|
92+
config[:after_commit][verb] ||= []
93+
config[:after_commit][verb] << blk
94+
end
95+
end
96+
9097
def attribute(name, type, options = {}, &blk)
9198
raise Errors::TypeNotFound.new(self, name, type) unless Types[type]
9299
attribute_option(options, :readable)

lib/graphiti/resource_proxy.rb

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -114,17 +114,18 @@ def save(action: :create)
114114
end
115115

116116
def destroy
117-
validator = @resource.transaction do
117+
transaction_response = @resource.transaction do
118118
metadata = { method: :destroy }
119119
model = @resource.destroy(@query.filters[:id], metadata)
120120
model.instance_variable_set(:@__serializer_klass, @resource.serializer)
121121
validator = ::Graphiti::Util::ValidationResponse.new \
122122
model, @payload
123123
validator.validate!
124124
@resource.before_commit(model, metadata)
125-
validator
125+
126+
{ result: validator }
126127
end
127-
@data, success = validator.to_a
128+
@data, success = transaction_response[:result].to_a
128129
success
129130
end
130131

@@ -154,15 +155,24 @@ def debug_requested?
154155
private
155156

156157
def persist
157-
@resource.transaction do
158-
::Graphiti::Util::Hooks.record do
158+
transaction_response = @resource.transaction do
159+
::Graphiti::Util::TransactionHooksRecorder.record do
159160
model = yield
160161
validator = ::Graphiti::Util::ValidationResponse.new \
161162
model, @payload
162163
validator.validate!
163164
validator
164165
end
165166
end
167+
168+
data, success = transaction_response[:result].to_a
169+
if success
170+
transaction_response[:after_commit_hooks].each do |hook|
171+
hook.call
172+
end
173+
end
174+
175+
transaction_response[:result]
166176
end
167177
end
168178
end

lib/graphiti/util/hooks.rb

Lines changed: 0 additions & 33 deletions
This file was deleted.

lib/graphiti/util/persistence.rb

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,9 @@ def run
6363
post_process(persisted, parents)
6464
post_process(persisted, children)
6565
before_commit = -> { @resource.before_commit(persisted, metadata) }
66-
add_hook(before_commit)
66+
add_hook(before_commit, :before_commit)
67+
after_commit = -> { @resource.after_commit(persisted, metadata) }
68+
add_hook(after_commit, :after_commit)
6769
persisted
6870
end
6971

@@ -83,8 +85,8 @@ def typecast_attributes
8385
end
8486
end
8587

86-
def add_hook(prc)
87-
::Graphiti::Util::Hooks.add(prc)
88+
def add_hook(prc, lifecycle_event)
89+
::Graphiti::Util::TransactionHooksRecorder.add(prc, lifecycle_event)
8890
end
8991

9092
# The child's attributes should be modified to nil-out the
@@ -215,7 +217,7 @@ def post_process(caller_model, processed)
215217
group.group_by { |g| g[:sideload] }.each_pair do |sideload, members|
216218
objects = members.map { |x| x[:object] }
217219
hook = -> { sideload.fire_hooks!(caller_model, objects, method) }
218-
add_hook(hook)
220+
add_hook(hook, :before_commit)
219221
end
220222
end
221223
end
Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
module Graphiti
2+
module Util
3+
class TransactionHooksRecorder
4+
# This is a thread-global singleton class which is used to capture
5+
# the correct hooks to run when before and after transactions are
6+
# committed. Consuming code will call the #record method, which will
7+
# yield to the passed block:
8+
#
9+
# ```ruby
10+
# TransactionHooksRecorder.record do
11+
# TransactionHooksRecorder.add(->{ do_stuff() }, :before_commit)
12+
# TransactionHooksRecorder.add(->{ do_more_stuff() }, :after_commit)
13+
# {
14+
# result: do_the_main_thing_and_return_a_result()
15+
# }
16+
# end
17+
# ```
18+
#
19+
# before_commit hooks will be executed before the record method returns.
20+
# All after_commit hooks will be added to the returned hash so that consumers
21+
# can decide when and whether to execute the callbacks.
22+
#
23+
# Returns a hash with `result` and `after_commit_hooks` keys.
24+
class << self
25+
def record
26+
reset_hooks
27+
28+
begin
29+
result = yield
30+
run(:before_commit)
31+
32+
unless result.kind_of?(::Hash)
33+
result = { result: result }
34+
end
35+
36+
result.tap do |r|
37+
r[:after_commit_hooks] = hook_set(:after_commit)
38+
end
39+
ensure
40+
reset_hooks
41+
end
42+
end
43+
44+
# Because hooks will be added from the outer edges of
45+
# the graph, working inwards
46+
def add(prc, lifecycle_event)
47+
hook_set(lifecycle_event).unshift(prc)
48+
end
49+
50+
def run(lifecycle_event)
51+
_hooks[lifecycle_event].each { |h| h.call }
52+
end
53+
54+
private
55+
def _hooks
56+
Thread.current[:_graphiti_hooks]
57+
end
58+
59+
def reset_hooks
60+
Thread.current[:_graphiti_hooks] = {
61+
before_commit: [],
62+
after_commit: [],
63+
}
64+
end
65+
66+
def hook_set(lifecycle_event)
67+
_hooks[lifecycle_event]
68+
end
69+
end
70+
end
71+
end
72+
end

lib/graphiti/version.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
11
module Graphiti
2-
VERSION = "1.0.rc.19"
2+
VERSION = "1.0.rc.20"
33
end

0 commit comments

Comments
 (0)