Skip to content

Commit 9c9c769

Browse files
author
Abderrahmane Boudi
committed
Add before_validation hooks to resources
1 parent b92d662 commit 9c9c769

File tree

7 files changed

+58
-3
lines changed

7 files changed

+58
-3
lines changed

lib/graphiti/resource.rb

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,13 @@ def resolve(scope)
112112
adapter.resolve(scope)
113113
end
114114

115+
def before_validation(model, metadata)
116+
hooks = self.class.config[:before_validation][metadata[:method]] || []
117+
hooks.each do |hook|
118+
instance_exec(model, metadata, &hook)
119+
end
120+
end
121+
115122
def before_commit(model, metadata)
116123
hooks = self.class.config[:before_commit][metadata[:method]] || []
117124
hooks.each do |hook|

lib/graphiti/resource/configuration.rb

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -186,6 +186,7 @@ def config
186186
sort_all: nil,
187187
sorts: {},
188188
pagination: nil,
189+
before_validation: {},
189190
before_commit: {},
190191
after_commit: {},
191192
attributes: {},

lib/graphiti/resource/dsl.rb

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,13 @@ def default_filter(name = nil, &blk)
8181
}
8282
end
8383

84+
def before_validation(only: [:create, :update, :destroy], &blk)
85+
Array(only).each do |verb|
86+
config[:before_validation][verb] ||= []
87+
config[:before_validation][verb] << blk
88+
end
89+
end
90+
8491
def before_commit(only: [:create, :update, :destroy], &blk)
8592
Array(only).each do |verb|
8693
config[:before_commit][verb] ||= []

lib/graphiti/resource_proxy.rb

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -124,6 +124,8 @@ def destroy
124124
validator = ::Graphiti::Util::ValidationResponse.new \
125125
model, @payload
126126
validator.validate!
127+
# FIXME: I don't know in which scenarios the following line is needed!
128+
# @resource.before_validation(model, metadata)
127129
@resource.before_commit(model, metadata)
128130

129131
{result: validator}

lib/graphiti/util/persistence.rb

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,8 @@ def run
6060

6161
post_process(persisted, parents)
6262
post_process(persisted, children)
63+
before_validation = -> { @resource.before_validation(persisted, metadata) }
64+
add_hook(before_validation, :before_validation)
6365
before_commit = -> { @resource.before_commit(persisted, metadata) }
6466
add_hook(before_commit, :before_commit)
6567
after_commit = -> { @resource.after_commit(persisted, metadata) }

lib/graphiti/util/transaction_hooks_recorder.rb

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ class TransactionHooksRecorder
88
#
99
# ```ruby
1010
# TransactionHooksRecorder.record do
11+
# TransactionHooksRecorder.add(->{ do_some_stuff() }, :before_validation)
1112
# TransactionHooksRecorder.add(->{ do_stuff() }, :before_commit)
1213
# TransactionHooksRecorder.add(->{ do_more_stuff() }, :after_commit)
1314
# {
@@ -27,6 +28,7 @@ def record
2728

2829
begin
2930
result = yield
31+
run(:before_validation)
3032
run(:before_commit)
3133

3234
unless result.is_a?(::Hash)
@@ -59,6 +61,7 @@ def _hooks
5961

6062
def reset_hooks
6163
Thread.current[:_graphiti_hooks] = {
64+
before_validation: [],
6265
before_commit: [],
6366
after_commit: [],
6467
}

spec/integration/rails/transaction_hooks_spec.rb

Lines changed: 36 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
# rubocop: disable Style/GlobalVars
22

33
if ENV["APPRAISAL_INITIALIZED"]
4-
RSpec.describe "before_ & after_commit hooks", type: :controller do
4+
RSpec.describe "before_validation, before_ & after_commit hooks", type: :controller do
55
class Callbacks
66
class << self
77
attr_accessor :fired, :in_transaction_during, :entities
@@ -27,6 +27,7 @@ def in_transaction?
2727
Callbacks.fired = {}
2828
Callbacks.in_transaction_during = {}
2929
Callbacks.entities = []
30+
$raise_on_before_validation = {employee: false}
3031
$raise_on_before_commit = {employee: true}
3132
end
3233

@@ -63,7 +64,7 @@ class PositionResource < ApplicationResource
6364
before_commit do |position|
6465
Callbacks.add(:before_position, position)
6566
if $raise_on_before_commit[:position]
66-
raise "rollitback_book"
67+
raise "rollitback_position"
6768
end
6869
end
6970

@@ -75,6 +76,13 @@ class EmployeeResource < ApplicationResource
7576

7677
attribute :first_name, :string
7778

79+
before_validation do |employee|
80+
Callbacks.add(:before_validation, employee)
81+
if $raise_on_before_validation[:employee]
82+
raise "rollitback"
83+
end
84+
end
85+
7886
before_commit only: [:create] do |employee|
7987
Callbacks.add(:before_create, employee)
8088
if $raise_on_before_commit[:employee]
@@ -191,7 +199,7 @@ def json
191199
post :create, params: payload
192200
}.to raise_error("rollitback")
193201
expect(Employee.count).to be_zero
194-
expect(Callbacks.entities.length).to eq(1)
202+
expect(Callbacks.entities.length).to eq(2)
195203
expect(Callbacks.fired[:before_create]).to be_a(Employee)
196204
end
197205

@@ -230,13 +238,31 @@ def json
230238
it "fires all before and after_commit hooks" do
231239
post :create, params: payload
232240
expect(Callbacks.entities).to eq([
241+
:before_validation,
233242
:before_create,
234243
:stacked_before_create,
235244
:employee_after_create,
236245
:employee_after_create_eval_test,
237246
])
238247
end
239248
end
249+
250+
context "when an error is raised before_validation" do
251+
before do
252+
$raise_on_before_validation = {employee: true}
253+
end
254+
255+
it "does not run before_commit callbacks" do
256+
expect_any_instance_of(Graphiti::Util::ValidationResponse)
257+
.to receive(:validate!)
258+
expect {
259+
post :create, params: payload
260+
}.to raise_error("rollitback")
261+
expect(Employee.count).to be_zero
262+
expect(Callbacks.entities.length).to eq(1)
263+
expect(Callbacks.fired[:before_validation]).to be_a(Employee)
264+
end
265+
end
240266
end
241267

242268
context "nested" do
@@ -281,6 +307,7 @@ def json
281307
post :create, params: payload
282308

283309
expect(Callbacks.entities).to eq([
310+
:before_validation,
284311
:before_create,
285312
:stacked_before_create,
286313
:before_position,
@@ -309,6 +336,12 @@ def json
309336
expect(Callbacks.in_transaction_during[:employee_after_create]).to eq false
310337
expect(Callbacks.fired[:employee_after_create_eval_test]).to be_a(IntegrationHooks::EmployeeResource)
311338
end
339+
340+
it "can access children resources from before_validation" do
341+
post :create, params: payload
342+
expect(Callbacks.fired[:employee_after_create].positions.length).to eq(1)
343+
expect(Callbacks.fired[:employee_after_create].positions[0]).to be_a(Position)
344+
end
312345
end
313346

314347
context "when yielding meta" do

0 commit comments

Comments
 (0)