shamu 0.0.17 → 0.0.18
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/Gemfile.lock +1 -1
- data/lib/shamu/attributes.rb +1 -0
- data/lib/shamu/attributes/assignment.rb +6 -0
- data/lib/shamu/auditing/support.rb +27 -25
- data/lib/shamu/entities/entity_path.rb +2 -1
- data/lib/shamu/events/support.rb +43 -4
- data/lib/shamu/services/active_record_crud.rb +59 -70
- data/lib/shamu/services/request.rb +31 -1
- data/lib/shamu/services/request_support.rb +5 -10
- data/lib/shamu/services/result.rb +15 -2
- data/lib/shamu/services/service.rb +0 -9
- data/lib/shamu/version.rb +1 -1
- data/spec/lib/shamu/auditing/support_spec.rb +17 -4
- data/spec/lib/shamu/events/support_spec.rb +21 -9
- data/spec/lib/shamu/services/active_record_crud_spec.rb +48 -55
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: dd0f794321cd3d306d36b16935e133e3ded9270b
|
4
|
+
data.tar.gz: 244f8025c48879a691c7faa6f6e76b3b45efe2e0
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 57d00e95ce2bf7a4d1ddbc224aff0a2790255a6bd1c1ac907f7450b95a30b63c7ca2cc404d0754c7ee363761386fc5c1567f1f50114957488d538be85d2cbda8
|
7
|
+
data.tar.gz: e2ab805eb2ba0db7969601cb825f827c8d5346c8ebd34db8e836543a6726b7327c20b68f092681555b56f1832d8dd8512d08d8bc1ddaf3170e6f221d5b3ab1a0
|
data/Gemfile.lock
CHANGED
data/lib/shamu/attributes.rb
CHANGED
@@ -46,6 +46,7 @@ module Shamu
|
|
46
46
|
self.class.attributes.each_with_object({}) do |(name, options), attrs|
|
47
47
|
next if ( only && !match_attribute?( only, name ) ) || ( except && match_attribute?( except, name ) )
|
48
48
|
next unless serialize_attribute?( name, options )
|
49
|
+
|
49
50
|
attrs[name] = send( name )
|
50
51
|
end
|
51
52
|
end
|
@@ -12,6 +12,12 @@ module Shamu
|
|
12
12
|
public :assign_attributes
|
13
13
|
end
|
14
14
|
|
15
|
+
# @param [Symbol] name of the attribute to assign.
|
16
|
+
# @param [Object] value to assign.
|
17
|
+
def []=( name, value )
|
18
|
+
send :"assign_#{ name }", value if attribute?( name )
|
19
|
+
end
|
20
|
+
|
15
21
|
# @return [Array<Symbol>] the attributes that have been assigned.
|
16
22
|
def assigned_attributes
|
17
23
|
@assigned_attributes.to_a || []
|
@@ -20,50 +20,52 @@ module Shamu
|
|
20
20
|
#
|
21
21
|
# @!endgroup Dependencies
|
22
22
|
|
23
|
+
private
|
24
|
+
|
25
|
+
# Override {Shamu::Services::RequestSupport#with_partial_request} and to yield
|
26
|
+
# a {Transaction} as an additional argument to automatically
|
27
|
+
# {#audit_request audit the request}.
|
28
|
+
def with_partial_request( *args, &block )
|
29
|
+
super( *args ) do |request|
|
30
|
+
audit_request request do |transaction|
|
31
|
+
yield request, transaction
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
23
36
|
end
|
24
37
|
|
25
38
|
private
|
26
39
|
|
27
|
-
# @!visibility public
|
28
|
-
#
|
29
|
-
# Same as {#audit_request} but does not validate the request before
|
30
|
-
# yielding to the block.
|
31
|
-
def audit_partial_request( params, request_class, action: :smart, &block )
|
32
|
-
perform_audit_request( :with_partial_request, params, request_class, action: action, &block )
|
33
|
-
end
|
34
40
|
|
35
41
|
# @!visibility public
|
36
42
|
#
|
37
43
|
# Audit the requested changes and report the request to the
|
38
44
|
# {#auditing_service}.
|
39
45
|
#
|
40
|
-
#
|
41
|
-
#
|
42
|
-
# @param (see Shamu::Services::RequestSupport#with_request)
|
46
|
+
# @param [Services::Request] request the coerced request params.
|
43
47
|
# @return (see Shamu::Services::RequestSupport#with_request)
|
44
|
-
# @yield (
|
45
|
-
# @yieldparam [Services::Request] request coerced from `params`.
|
48
|
+
# @yield (transaction)
|
46
49
|
# @yieldparam [Transaction] transaction the audit transaction. Most fields
|
47
50
|
# will be populated automatically from the request but the block
|
48
51
|
# should call {Transaction#append_entity} to include any parent
|
49
52
|
# entities in the entity path.
|
50
|
-
|
51
|
-
|
52
|
-
end
|
53
|
-
|
54
|
-
def perform_audit_request( method, params, request_class, action: :smart, &block )
|
53
|
+
# @yieldreturn [Services::Result]
|
54
|
+
def audit_request( request, action: :smart, &block )
|
55
55
|
transaction = Transaction.new \
|
56
|
-
user_id_chain: auditing_security_principal.user_id_chain
|
56
|
+
user_id_chain: auditing_security_principal.user_id_chain,
|
57
|
+
changes: request.to_attributes( only: request.assigned_attributes ),
|
58
|
+
action: audit_request_action( request, action )
|
57
59
|
|
58
|
-
result =
|
59
|
-
|
60
|
-
transaction.changes = request.to_attributes
|
61
|
-
|
62
|
-
yield request, transaction
|
63
|
-
end
|
60
|
+
result = yield transaction if block_given?
|
61
|
+
result = Services::Result.coerce( result, request: request )
|
64
62
|
|
65
63
|
if result.valid?
|
66
|
-
|
64
|
+
if result.entity
|
65
|
+
transaction.append_entity result.entity
|
66
|
+
elsif request.respond_to?( :id ) && defined? entity_class
|
67
|
+
transaction.append_entity [ entity_class, request.id ]
|
68
|
+
end
|
67
69
|
auditing_service.commit( transaction )
|
68
70
|
end
|
69
71
|
|
@@ -70,12 +70,13 @@ module Shamu
|
|
70
70
|
def entity_path_name( entity )
|
71
71
|
case entity
|
72
72
|
when String then entity.sub( /Entity$/, "" )
|
73
|
-
when Class then
|
73
|
+
when Class then entity.model_name.name
|
74
74
|
else fail "Don't know how to compose #{ entity }"
|
75
75
|
end
|
76
76
|
end
|
77
77
|
|
78
78
|
def build_composed_entity_path( name, id )
|
79
|
+
id = id.to_model_id if id.respond_to?( :to_model_id )
|
79
80
|
"#{ name }[#{ id }]"
|
80
81
|
end
|
81
82
|
|
data/lib/shamu/events/support.rb
CHANGED
@@ -21,20 +21,37 @@ module Shamu
|
|
21
21
|
|
22
22
|
end
|
23
23
|
|
24
|
+
# (see Support.event_channel)
|
25
|
+
def event_channel
|
26
|
+
self.class.event_channel
|
27
|
+
end
|
28
|
+
|
24
29
|
private
|
25
30
|
|
26
31
|
# @!visibility public
|
27
32
|
#
|
28
33
|
# Publish the given `message` to the {#events_service}.
|
29
34
|
#
|
30
|
-
# @param [Events::Message] message the custom event specific message to
|
35
|
+
# @param [Events::Message, Symbol] message the custom event specific message to
|
31
36
|
# publish.
|
32
37
|
# @param [String] channel to publish to.
|
38
|
+
# @param [Hash] message_attrs arguments to use when creating an
|
39
|
+
# instance of `message`.
|
40
|
+
#
|
41
|
+
# If `message` is a symbol, looks for a {Message} class in
|
42
|
+
# {ServiceNamespace}::{OptionalServiceDomain}Events::{name.caemlize}.
|
33
43
|
# @return [void]
|
34
|
-
def event!( message, channel: event_channel )
|
44
|
+
def event!( message, channel: event_channel, **message_attrs )
|
45
|
+
if message.is_a?( Symbol )
|
46
|
+
message = self.class
|
47
|
+
.event_message_namespace
|
48
|
+
.const_get( message.to_s.camelize )
|
49
|
+
.new( message_attrs )
|
50
|
+
end
|
35
51
|
events_service.publish channel, message
|
36
52
|
end
|
37
53
|
|
54
|
+
class_methods do
|
38
55
|
# @!visibility public
|
39
56
|
#
|
40
57
|
# The channel to {#publish_event publish events} to. Defaults to the
|
@@ -47,7 +64,7 @@ module Shamu
|
|
47
64
|
# @return [String] the name of the channel.
|
48
65
|
def event_channel
|
49
66
|
@event_channel ||= begin
|
50
|
-
base_name =
|
67
|
+
base_name = name || "Events"
|
51
68
|
parts = base_name.split( "::" )
|
52
69
|
parts[-1].sub!( /Service$/, "" )
|
53
70
|
parts.pop if parts[-1] == parts[-2] || ( parts.length > 1 && parts[-1].blank? )
|
@@ -55,6 +72,28 @@ module Shamu
|
|
55
72
|
end
|
56
73
|
end
|
57
74
|
|
75
|
+
# The module that holds the per-message event classes for the service.
|
76
|
+
# @return [Module]
|
77
|
+
def event_message_namespace
|
78
|
+
@event_message_namespace ||=
|
79
|
+
begin
|
80
|
+
namespace = name.deconstantize
|
81
|
+
return unless namespace.present?
|
82
|
+
|
83
|
+
namespace = namespace.constantize
|
84
|
+
domain = name.demodulize.sub( /Service/, "" ).singularize
|
85
|
+
|
86
|
+
# Must use exceptions instead of const_defined? so that rails has
|
87
|
+
# a change to autoload the constant.
|
88
|
+
begin
|
89
|
+
namespace.const_get( "#{ domain }Events" )
|
90
|
+
rescue NameError
|
91
|
+
namespace.const_get( "Events" )
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
58
97
|
end
|
59
98
|
end
|
60
|
-
end
|
99
|
+
end
|
@@ -14,22 +14,22 @@ module Shamu
|
|
14
14
|
#
|
15
15
|
# # Define finder methods #find, #list and #lookup using the given
|
16
16
|
# # default scope.
|
17
|
-
#
|
17
|
+
# define_finders Models::User.active
|
18
18
|
#
|
19
19
|
# # Define change methods
|
20
|
-
#
|
21
|
-
#
|
20
|
+
# define_create
|
21
|
+
# define_update
|
22
22
|
#
|
23
23
|
# # Common update/change behavior for #create and #update
|
24
|
-
#
|
24
|
+
# define_change do |request, model|
|
25
25
|
# model.last_updated_at = Time.now
|
26
26
|
# end
|
27
27
|
#
|
28
28
|
# # Standard destroy method
|
29
|
-
#
|
29
|
+
# define_destroy
|
30
30
|
#
|
31
31
|
# # Build the entity class from the given record.
|
32
|
-
#
|
32
|
+
# define_build_entities do |records|
|
33
33
|
# records.map do |record|
|
34
34
|
# parent = lookup_association( record.parent_id, self ) do
|
35
35
|
# records.pluck( :parent_id )
|
@@ -123,7 +123,7 @@ module Shamu
|
|
123
123
|
send method
|
124
124
|
end
|
125
125
|
|
126
|
-
|
126
|
+
define_build_entities( &block )
|
127
127
|
end
|
128
128
|
|
129
129
|
# @return [Class] the {Entities::Entity} class that the service will
|
@@ -138,49 +138,60 @@ module Shamu
|
|
138
138
|
resource_not_configured
|
139
139
|
end
|
140
140
|
|
141
|
+
# Define all basic CRUD methods without any customization.
|
142
|
+
def define_crud
|
143
|
+
define_create
|
144
|
+
define_update
|
145
|
+
define_destroy
|
146
|
+
define_finders
|
147
|
+
end
|
148
|
+
|
141
149
|
# Define a `#create` method on the service that takes a single {Request}
|
142
150
|
# parameter.
|
143
151
|
#
|
144
|
-
#
|
145
|
-
# @
|
146
|
-
# @yieldparam
|
152
|
+
# @yield ( request, record, *args )
|
153
|
+
# @yieldparam [Services::Request] request object.
|
154
|
+
# @yieldparam [ActiveRecord::Base] record.
|
155
|
+
# @yieldparam [Array] args any additional arguments injected by an
|
156
|
+
# overridden {#with_request} method.
|
147
157
|
# @return [void]
|
148
|
-
def
|
158
|
+
def define_create( &block )
|
149
159
|
define_method :create do |params = nil|
|
150
|
-
with_request params, request_class( :create ) do |request|
|
151
|
-
authorize! :create, entity_class, request
|
152
|
-
|
160
|
+
with_request params, request_class( :create ) do |request, *args|
|
153
161
|
record = request.apply_to( model_class.new )
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
162
|
+
|
163
|
+
if block_given?
|
164
|
+
result = instance_exec record, request, *args, &block
|
165
|
+
next result if result.is_a?( Services::Result )
|
158
166
|
end
|
159
167
|
|
168
|
+
authorize! :create, build_entity( record ), request
|
169
|
+
|
160
170
|
next record unless record.save
|
161
171
|
build_entity record
|
162
172
|
end
|
163
173
|
end
|
164
174
|
end
|
165
175
|
|
166
|
-
# rubocop:disable Metrics/MethodLength
|
167
|
-
|
168
176
|
# Define an change `method` on the service that takes the id of the
|
169
177
|
# resource to modify and a corresponding {Request} parameter.
|
170
178
|
#
|
171
|
-
#
|
172
|
-
# @
|
173
|
-
# @yieldparam
|
179
|
+
# @yield ( request, record, *args )
|
180
|
+
# @yieldparam [Services::Request] request object.
|
181
|
+
# @yieldparam [ActiveRecord::Base] record.
|
182
|
+
# @yieldparam [Array] args any additional arguments injected by an
|
183
|
+
# overridden {#with_request} method.
|
174
184
|
# @return [Result] the result of the request.
|
175
185
|
# @return [void]
|
176
|
-
def
|
186
|
+
def define_change( method, default_scope = model_class, &block ) # rubocop:disable Metrics/AbcSize, Metrics/MethodLength, Metrics/PerceivedComplexity, Metrics/LineLength
|
177
187
|
define_method method do |id, params = nil|
|
178
188
|
klass = request_class( method )
|
179
189
|
|
180
190
|
params, id = id, id[ :id ] if !params && !id.respond_to?( :to_model_id )
|
191
|
+
params[ :id ] ||= id if params
|
181
192
|
|
182
|
-
with_partial_request params, klass do |request|
|
183
|
-
record =
|
193
|
+
with_partial_request params, klass do |request, *args|
|
194
|
+
record = default_scope.find( id.to_model_id || request.id )
|
184
195
|
entity = build_entity( record )
|
185
196
|
|
186
197
|
backfill_attributes = entity.to_attributes( only: request.unassigned_attributes )
|
@@ -190,11 +201,9 @@ module Shamu
|
|
190
201
|
authorize! method, entity, request
|
191
202
|
|
192
203
|
request.apply_to( record )
|
193
|
-
|
194
|
-
|
195
|
-
|
196
|
-
elsif respond_to? :apply_changes
|
197
|
-
apply_changes( request, record )
|
204
|
+
if block_given?
|
205
|
+
result = instance_exec record, request, *args, &block
|
206
|
+
next result if result.is_a?( Services::Result )
|
198
207
|
end
|
199
208
|
|
200
209
|
next record unless record.save
|
@@ -203,69 +212,49 @@ module Shamu
|
|
203
212
|
end
|
204
213
|
end
|
205
214
|
|
206
|
-
#
|
207
|
-
|
208
|
-
|
209
|
-
# Define an `update` method on the service that takes the id of the
|
210
|
-
# resource to update and a {Request} parameter. After applying the
|
211
|
-
# changes the record is persisted and the updated entity result is
|
212
|
-
# returned.
|
213
|
-
#
|
214
|
-
# See {.apply_changes} for details.
|
215
|
-
# @yield (see .apply_changes)
|
216
|
-
# @yieldparam (see .apply_changes)
|
217
|
-
# @return [void]
|
218
|
-
def update( &block )
|
219
|
-
change :update, &block
|
215
|
+
# Defines an #update method. See {#define_change} for details.
|
216
|
+
def define_update( default_scope = model_class, &block )
|
217
|
+
define_change :update, default_scope, &block
|
220
218
|
end
|
221
219
|
|
222
220
|
# Define a `destroy( id )` method that takes an {Entities::Entity} {Entities::Entity#id}
|
223
221
|
# and destroys the resource.
|
224
222
|
#
|
223
|
+
# @yield ( request, record, *args )
|
224
|
+
# @yieldparam [Services::Request] request object.
|
225
|
+
# @yieldparam [ActiveRecord::Base] record.
|
226
|
+
# @yieldparam [Array] args any additional arguments injected by an
|
227
|
+
# overridden {#with_request} method.
|
225
228
|
# @param [ActiveRecord::Relation] default_scope to use when finding
|
226
229
|
# records.
|
227
230
|
# @return [void]
|
228
|
-
def
|
231
|
+
def define_destroy( default_scope = model_class, &block )
|
229
232
|
define_method :destroy do |params|
|
230
233
|
klass = request_class( :destroy )
|
231
234
|
|
232
235
|
params = { id: params } if params.respond_to?( :to_model_id )
|
233
236
|
|
234
|
-
with_request params, klass do |request|
|
237
|
+
with_request params, klass do |request, *args|
|
235
238
|
record = default_scope.find( request.id )
|
236
239
|
authorize! :destroy, build_entity( record )
|
240
|
+
|
241
|
+
instance_exec record, request, *args, &block if block_given?
|
237
242
|
next record unless record.destroy
|
238
243
|
end
|
239
244
|
end
|
240
245
|
end
|
241
246
|
|
242
|
-
# Define a private method `apply_changes` on the service used by the
|
243
|
-
# {.create} and {.change} defined methods to apply changes in a
|
244
|
-
# {Request} to the model.
|
245
|
-
#
|
246
|
-
# @yield ( request, record ) a block that applies changes in the
|
247
|
-
# `request` to the `record`.
|
248
|
-
# @yieldparam [Request] request the {Request} containing all the changes
|
249
|
-
# that should be applied to the `record`.
|
250
|
-
# @yieldparam [ActiveRecord::Base] record the record to be updated.
|
251
|
-
# @yieldreturn [void]
|
252
|
-
# @return [void]
|
253
|
-
def apply_changes( &block )
|
254
|
-
define_method :apply_changes, &block
|
255
|
-
private :apply_changes
|
256
|
-
end
|
257
|
-
|
258
247
|
# Define the standard finder methods {.find}, {.lookup} and {.list}.
|
259
248
|
#
|
260
249
|
# @param [ActiveRecord::Relation] default_scope to use when finding
|
261
250
|
# records.
|
262
251
|
# @return [void]
|
263
|
-
def
|
252
|
+
def define_finders( default_scope = model_class.all, only: nil, except: nil )
|
264
253
|
methods = Array( only || [ :find, :lookup, :list ] )
|
265
254
|
methods -= Array( except ) if except
|
266
255
|
|
267
256
|
methods.each do |method|
|
268
|
-
send method, default_scope
|
257
|
+
send :"define_#{ method }", default_scope
|
269
258
|
end
|
270
259
|
end
|
271
260
|
|
@@ -278,7 +267,7 @@ module Shamu
|
|
278
267
|
# @yield (id)
|
279
268
|
# @yieldreturn (ActiveRecord::Base) the found record.
|
280
269
|
# @return [void]
|
281
|
-
def
|
270
|
+
def define_find( default_scope = model_class.all, &block )
|
282
271
|
if block_given?
|
283
272
|
define_method :find do |id|
|
284
273
|
wrap_not_found do
|
@@ -305,7 +294,7 @@ module Shamu
|
|
305
294
|
# @yieldreturn [ActiveRecord::Relation] records for ids found in the
|
306
295
|
# underlying resource.
|
307
296
|
# @return [void]
|
308
|
-
def
|
297
|
+
def define_lookup( default_scope = model_class.all, &block )
|
309
298
|
define_method :lookup do |*ids|
|
310
299
|
cached_lookup( ids ) do |uncached_ids|
|
311
300
|
records = block_given? ? yield( uncached_ids ) : default_scope.where( id: uncached_ids )
|
@@ -325,7 +314,7 @@ module Shamu
|
|
325
314
|
# @yieldparam [ListScope] scope to apply.
|
326
315
|
# @yieldreturn [ActiveRecord::Relation] records matching the given scope.
|
327
316
|
# @return [void]
|
328
|
-
def
|
317
|
+
def define_list( default_scope = model_class.all, &block )
|
329
318
|
define_method :list do |params = nil|
|
330
319
|
list_scope = Entities::ListScope.for( entity_class ).coerce( params )
|
331
320
|
authorize! :list, entity_class, list_scope
|
@@ -351,7 +340,7 @@ module Shamu
|
|
351
340
|
# entities.
|
352
341
|
# @yieldreturn [Array<Entities::Entity>] the projected entities.
|
353
342
|
# @return [void]
|
354
|
-
def
|
343
|
+
def define_build_entities( &block )
|
355
344
|
if block_given?
|
356
345
|
define_method :build_entities, &block
|
357
346
|
else
|
@@ -374,7 +363,7 @@ module Shamu
|
|
374
363
|
|
375
364
|
def inferred_resource_name
|
376
365
|
inferred = name || "Resource"
|
377
|
-
inferred.split( "::" ).last.sub( /Service/, "" )
|
366
|
+
inferred.split( "::" ).last.sub( /Service/, "" ).singularize
|
378
367
|
end
|
379
368
|
|
380
369
|
def inferred_namespace
|
@@ -25,7 +25,6 @@ module Shamu
|
|
25
25
|
class Request
|
26
26
|
include Shamu::Attributes
|
27
27
|
include Shamu::Attributes::Assignment
|
28
|
-
include Shamu::Attributes::FluidAssignment
|
29
28
|
include Shamu::Attributes::Validation
|
30
29
|
|
31
30
|
# Applies the attributes of the request to the given model. Only handles
|
@@ -53,6 +52,37 @@ module Shamu
|
|
53
52
|
end
|
54
53
|
end
|
55
54
|
|
55
|
+
# Execute block if the request is satisfied by the service successfully.
|
56
|
+
def on_success( &block )
|
57
|
+
@on_success_callbacks ||= []
|
58
|
+
@on_success_callbacks << block
|
59
|
+
end
|
60
|
+
|
61
|
+
# Execute block if the request is not satisfied by the service.
|
62
|
+
def on_fail( &block )
|
63
|
+
@on_fail_callbacks ||= []
|
64
|
+
@on_fail_callbacks << block
|
65
|
+
end
|
66
|
+
|
67
|
+
# Execute block when the service is done processing the request.
|
68
|
+
def on_complete( &block )
|
69
|
+
@on_complete_callbacks ||= []
|
70
|
+
@on_complete_callbacks << block
|
71
|
+
end
|
72
|
+
|
73
|
+
# Run any {#on_success} or #{on_fail} callbacks.
|
74
|
+
# @param [Boolean] success true if the request was completed
|
75
|
+
# successfully.
|
76
|
+
def run_callbacks( success )
|
77
|
+
if success
|
78
|
+
@on_success_callbacks && @on_success_callbacks.each( &:call )
|
79
|
+
else
|
80
|
+
@on_fail_callbacks && @on_fail_callbacks.each( &:call )
|
81
|
+
end
|
82
|
+
|
83
|
+
@on_fail_callbacks && @on_fail_callbacks.each( &:call )
|
84
|
+
end
|
85
|
+
|
56
86
|
class << self
|
57
87
|
# Coerces a hash or params object to a proper {Request} object.
|
58
88
|
# @param [Object] params to be coerced.
|
@@ -70,10 +70,10 @@ module Shamu
|
|
70
70
|
# end
|
71
71
|
# end
|
72
72
|
def with_request( params, request_class, &block )
|
73
|
-
with_partial_request params, request_class do |request|
|
73
|
+
with_partial_request params, request_class do |request, *args|
|
74
74
|
next unless request.valid?
|
75
75
|
|
76
|
-
yield request
|
76
|
+
yield request, *args
|
77
77
|
end
|
78
78
|
end
|
79
79
|
|
@@ -90,15 +90,10 @@ module Shamu
|
|
90
90
|
request = request_class.coerce( params )
|
91
91
|
sources = yield( request )
|
92
92
|
|
93
|
-
|
94
|
-
|
95
|
-
request.valid?
|
93
|
+
result = Result.coerce( sources, request: request )
|
94
|
+
request.run_callbacks( result.valid? )
|
96
95
|
|
97
|
-
|
98
|
-
sources
|
99
|
-
else
|
100
|
-
result( *Array.wrap( sources ), request: request )
|
101
|
-
end
|
96
|
+
result
|
102
97
|
end
|
103
98
|
|
104
99
|
# Static methods added to {RequestSupport}
|
@@ -48,7 +48,7 @@ module Shamu
|
|
48
48
|
# the first {Request} object found in the `values`.
|
49
49
|
# @param [Entities::Entity] entity submitted to the service. If :not_set,
|
50
50
|
# uses the first {Entity} object found in the `values`.
|
51
|
-
def initialize( *values, request: :not_set, entity: :not_set )
|
51
|
+
def initialize( *values, request: :not_set, entity: :not_set ) # rubocop:disable Metrics/LineLength, Metrics/MethodLength, Metrics/PerceivedComplexity
|
52
52
|
@values = values
|
53
53
|
@value = values.first
|
54
54
|
|
@@ -60,6 +60,10 @@ module Shamu
|
|
60
60
|
end
|
61
61
|
|
62
62
|
unless request == :not_set
|
63
|
+
# Make sure the request captures errors even if the block doesn't
|
64
|
+
# check
|
65
|
+
request && request.valid?
|
66
|
+
|
63
67
|
@request = request
|
64
68
|
append_error_source request
|
65
69
|
end
|
@@ -93,8 +97,16 @@ module Shamu
|
|
93
97
|
self
|
94
98
|
end
|
95
99
|
|
96
|
-
|
100
|
+
# @return [Result] the value coerced to a {Result}.
|
101
|
+
def self.coerce( value, **args )
|
102
|
+
if value.is_a?( Result )
|
103
|
+
value
|
104
|
+
else
|
105
|
+
Result.new( *Array.wrap( value ), **args )
|
106
|
+
end
|
107
|
+
end
|
97
108
|
|
109
|
+
private
|
98
110
|
|
99
111
|
def append_error_source( source )
|
100
112
|
return unless source.respond_to?( :errors )
|
@@ -103,6 +115,7 @@ module Shamu
|
|
103
115
|
errors.add attr, message unless errors[attr].include? message
|
104
116
|
end
|
105
117
|
end
|
118
|
+
|
106
119
|
end
|
107
120
|
end
|
108
121
|
end
|
@@ -340,15 +340,6 @@ module Shamu
|
|
340
340
|
end
|
341
341
|
end
|
342
342
|
|
343
|
-
# @!visibility public
|
344
|
-
#
|
345
|
-
# @overload result( *values, request: nil, entity: nil )
|
346
|
-
# @param (see Result#initialize)
|
347
|
-
# @return [Result]
|
348
|
-
def result( *args )
|
349
|
-
Result.new( *args )
|
350
|
-
end
|
351
|
-
|
352
343
|
# @!visibility public
|
353
344
|
#
|
354
345
|
# Return an error {#result} from a service request.
|
data/lib/shamu/version.rb
CHANGED
@@ -5,6 +5,7 @@ module AuditingSupportSpec
|
|
5
5
|
include Shamu::Auditing::Support
|
6
6
|
|
7
7
|
public :audit_request
|
8
|
+
public :with_request
|
8
9
|
end
|
9
10
|
|
10
11
|
class Change < Shamu::Services::Request
|
@@ -14,19 +15,22 @@ end
|
|
14
15
|
|
15
16
|
describe Shamu::Auditing::Support do
|
16
17
|
hunt( :service, Shamu::Auditing::AuditingService )
|
18
|
+
|
17
19
|
let( :example_service ) { scorpion.new AuditingSupportSpec::ExampleService }
|
20
|
+
let( :request ) { AuditingSupportSpec::Change.new name: "Penguin" }
|
18
21
|
|
19
22
|
it "audits the request on success" do
|
20
23
|
expect( service ).to receive( :commit )
|
21
24
|
|
22
|
-
example_service.audit_request(
|
25
|
+
example_service.audit_request( request ) do |transaction|
|
23
26
|
end
|
24
27
|
end
|
25
28
|
|
26
29
|
it "skips the request on failure" do
|
27
30
|
expect( service ).not_to receive( :commit )
|
31
|
+
request.name = nil
|
28
32
|
|
29
|
-
example_service.audit_request(
|
33
|
+
example_service.audit_request( request ) do |transaction|
|
30
34
|
end
|
31
35
|
end
|
32
36
|
|
@@ -35,7 +39,16 @@ describe Shamu::Auditing::Support do
|
|
35
39
|
expect( transaction.action ).to eq "change"
|
36
40
|
end
|
37
41
|
|
38
|
-
example_service.audit_request(
|
42
|
+
example_service.audit_request( request ) do |request|
|
39
43
|
end
|
40
44
|
end
|
41
|
-
|
45
|
+
|
46
|
+
it "wraps with_request" do
|
47
|
+
expect( service ).to receive( :commit )
|
48
|
+
|
49
|
+
example_service.with_request( { name: "Example" }, AuditingSupportSpec::Change ) do |request, transaction|
|
50
|
+
expect( request ).to be_a AuditingSupportSpec::Change
|
51
|
+
expect( transaction ).to be_a Shamu::Auditing::Transaction
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
@@ -1,5 +1,19 @@
|
|
1
1
|
require "spec_helper"
|
2
2
|
|
3
|
+
module EventsSupportSpec
|
4
|
+
class Service < Shamu::Services::Service
|
5
|
+
include Shamu::Events::Support
|
6
|
+
|
7
|
+
public :event!
|
8
|
+
end
|
9
|
+
|
10
|
+
module Events
|
11
|
+
class Boom < Shamu::Events::Message
|
12
|
+
attribute :name
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
3
17
|
describe Shamu::Events::Support do
|
4
18
|
describe "#event_channel" do
|
5
19
|
{
|
@@ -27,18 +41,16 @@ describe Shamu::Events::Support do
|
|
27
41
|
describe "event!" do
|
28
42
|
hunt( :events_service, Shamu::Events::EventsService )
|
29
43
|
|
30
|
-
let( :
|
31
|
-
Class.new( Shamu::Services::Service ) do
|
32
|
-
include Shamu::Events::Support
|
33
|
-
|
34
|
-
public :event!
|
35
|
-
end
|
36
|
-
end
|
37
|
-
let( :service ) { scorpion.new klass }
|
44
|
+
let( :service ) { scorpion.new EventsSupportSpec::Service }
|
38
45
|
|
39
46
|
it "publishes message to events_service" do
|
40
47
|
expect( events_service ).to receive( :publish )
|
41
48
|
service.event! Shamu::Events::Message.new
|
42
49
|
end
|
50
|
+
|
51
|
+
it "creates message from attributes" do
|
52
|
+
expect( events_service ).to receive( :publish )
|
53
|
+
service.event! :boom, name: "Me"
|
54
|
+
end
|
43
55
|
end
|
44
|
-
end
|
56
|
+
end
|
@@ -33,10 +33,8 @@ module ActiveRecordCrudSpec
|
|
33
33
|
include Shamu::Services::ActiveRecordCrud
|
34
34
|
|
35
35
|
resource FavoriteEntity, ActiveRecordSpec::Favorite
|
36
|
-
|
37
|
-
|
38
|
-
finders
|
39
|
-
destroy
|
36
|
+
define_crud
|
37
|
+
|
40
38
|
end
|
41
39
|
|
42
40
|
class FavoriteListScope < Shamu::Entities::ListScope
|
@@ -46,6 +44,7 @@ end
|
|
46
44
|
|
47
45
|
describe Shamu::Services::ActiveRecordCrud do
|
48
46
|
use_active_record
|
47
|
+
|
49
48
|
before( :all ) { Shamu::ToModelIdExtension.extend! }
|
50
49
|
|
51
50
|
let( :klass ) { ActiveRecordCrudSpec::Service }
|
@@ -125,15 +124,6 @@ describe Shamu::Services::ActiveRecordCrud do
|
|
125
124
|
service.create request
|
126
125
|
end
|
127
126
|
|
128
|
-
it "calls apply_changes if present" do
|
129
|
-
expect( service ).to receive( :apply_changes )
|
130
|
-
.with(
|
131
|
-
request,
|
132
|
-
kind_of( ActiveRecord::Base )
|
133
|
-
)
|
134
|
-
service.create( request )
|
135
|
-
end
|
136
|
-
|
137
127
|
it "returns a Result" do
|
138
128
|
expect( service.create( request ) ).to be_a Shamu::Services::Result
|
139
129
|
end
|
@@ -148,24 +138,27 @@ describe Shamu::Services::ActiveRecordCrud do
|
|
148
138
|
it "yields if block given" do
|
149
139
|
expect do |b|
|
150
140
|
yield_klass = Class.new( klass ) do
|
151
|
-
|
141
|
+
define_create( &b )
|
152
142
|
end
|
153
143
|
|
154
144
|
scorpion.new( yield_klass ).create( request )
|
155
145
|
end.to yield_with_args(
|
156
|
-
|
157
|
-
|
146
|
+
kind_of( ActiveRecord::Base ),
|
147
|
+
request
|
158
148
|
)
|
159
149
|
end
|
160
150
|
|
161
|
-
it "
|
151
|
+
it "short-circuits if block yields a Services::Result" do
|
162
152
|
yield_klass = Class.new( klass ) do
|
163
|
-
|
153
|
+
define_create do
|
154
|
+
Shamu::Services::Result.new
|
155
|
+
end
|
164
156
|
end
|
165
157
|
|
166
158
|
service = scorpion.new( yield_klass )
|
167
|
-
|
168
|
-
service.
|
159
|
+
|
160
|
+
expect( service ).not_to receive( :build_entity )
|
161
|
+
service.create request
|
169
162
|
end
|
170
163
|
|
171
164
|
it "calls #authorize!" do
|
@@ -198,37 +191,34 @@ describe Shamu::Services::ActiveRecordCrud do
|
|
198
191
|
service.update entity, request
|
199
192
|
end
|
200
193
|
|
201
|
-
it "calls apply_changes if present" do
|
202
|
-
expect( service ).to receive( :apply_changes )
|
203
|
-
.with(
|
204
|
-
kind_of( Shamu::Services::Request ),
|
205
|
-
kind_of( ::ActiveRecord::Base )
|
206
|
-
)
|
207
|
-
service.create( request )
|
208
|
-
end
|
209
|
-
|
210
194
|
it "yields if block given" do
|
211
195
|
entity = service.create.entity
|
212
196
|
|
213
197
|
expect do |b|
|
214
198
|
yield_klass = Class.new( klass ) do
|
215
|
-
|
199
|
+
define_change( :update, &b )
|
216
200
|
end
|
217
201
|
|
218
202
|
scorpion.new( yield_klass ).update entity.id, request
|
219
|
-
end.to yield_with_args(
|
203
|
+
end.to yield_with_args( kind_of( ::ActiveRecord::Base ), request )
|
220
204
|
end
|
221
205
|
|
222
|
-
it "
|
223
|
-
entity
|
206
|
+
it "short-circuits if block yields a Services::Result" do
|
207
|
+
entity
|
208
|
+
record = service.class.model_class.all.first
|
209
|
+
records = [ record ]
|
210
|
+
|
211
|
+
expect( records ).to receive( :find ).and_return record
|
212
|
+
expect( record ).not_to receive( :save )
|
224
213
|
|
225
214
|
yield_klass = Class.new( klass ) do
|
226
|
-
|
215
|
+
define_update records do
|
216
|
+
Shamu::Services::Result.new
|
217
|
+
end
|
227
218
|
end
|
228
219
|
|
229
220
|
service = scorpion.new( yield_klass )
|
230
|
-
|
231
|
-
service.update entity.id
|
221
|
+
service.update entity.id, request
|
232
222
|
end
|
233
223
|
|
234
224
|
it "calls #authorize!" do
|
@@ -244,19 +234,6 @@ describe Shamu::Services::ActiveRecordCrud do
|
|
244
234
|
end
|
245
235
|
end
|
246
236
|
|
247
|
-
describe ".apply_changes" do
|
248
|
-
it "defines an apply_changes method from the block" do
|
249
|
-
klass = Class.new( Shamu::Services::Service ) do
|
250
|
-
include Shamu::Services::ActiveRecordCrud
|
251
|
-
|
252
|
-
apply_changes do |record, request|
|
253
|
-
end
|
254
|
-
end
|
255
|
-
|
256
|
-
expect( klass.new.respond_to?( :apply_changes, true ) ).to be_truthy
|
257
|
-
end
|
258
|
-
end
|
259
|
-
|
260
237
|
describe ".finders" do
|
261
238
|
it "builds #lookup" do
|
262
239
|
expect( service ).to respond_to :lookup
|
@@ -279,7 +256,7 @@ describe Shamu::Services::ActiveRecordCrud do
|
|
279
256
|
end
|
280
257
|
|
281
258
|
it "excludes unwanted methods" do
|
282
|
-
klass.
|
259
|
+
klass.define_finders except: :list
|
283
260
|
|
284
261
|
expect( klass.new ).to respond_to :find
|
285
262
|
expect( klass.new ).to respond_to :lookup
|
@@ -287,7 +264,7 @@ describe Shamu::Services::ActiveRecordCrud do
|
|
287
264
|
end
|
288
265
|
|
289
266
|
it "includes only specific methods" do
|
290
|
-
klass.
|
267
|
+
klass.define_finders only: :list
|
291
268
|
|
292
269
|
expect( klass.new ).not_to respond_to :find
|
293
270
|
expect( klass.new ).not_to respond_to :lookup
|
@@ -307,7 +284,7 @@ describe Shamu::Services::ActiveRecordCrud do
|
|
307
284
|
it "yields to block if block given" do
|
308
285
|
find_klass = Class.new( klass )
|
309
286
|
expect do |b|
|
310
|
-
find_klass.
|
287
|
+
find_klass.define_find do |_|
|
311
288
|
b.to_proc.call
|
312
289
|
ActiveRecordSpec::Favorite.all.first
|
313
290
|
end
|
@@ -353,7 +330,7 @@ describe Shamu::Services::ActiveRecordCrud do
|
|
353
330
|
it "yields to block if given?" do
|
354
331
|
expect do |b|
|
355
332
|
yield_klass = Class.new( klass ) do
|
356
|
-
|
333
|
+
define_lookup( &b )
|
357
334
|
end
|
358
335
|
|
359
336
|
scorpion.new( yield_klass ).lookup entity.id
|
@@ -392,7 +369,7 @@ describe Shamu::Services::ActiveRecordCrud do
|
|
392
369
|
it "yields if block given" do
|
393
370
|
expect do |b|
|
394
371
|
yield_klass = Class.new( klass ) do
|
395
|
-
|
372
|
+
define_list( &b )
|
396
373
|
end
|
397
374
|
|
398
375
|
scorpion.new( yield_klass ).list
|
@@ -438,6 +415,22 @@ describe Shamu::Services::ActiveRecordCrud do
|
|
438
415
|
end.to change( ActiveRecordSpec::Favorite, :count ).by( -1 )
|
439
416
|
end
|
440
417
|
|
418
|
+
it "short-circuits if block yields a Services::Result" do
|
419
|
+
record = klass.model_class.all.first
|
420
|
+
records = [ record ]
|
421
|
+
|
422
|
+
expect( records ).to receive( :find ).and_return record
|
423
|
+
|
424
|
+
yield_klass = Class.new( klass ) do
|
425
|
+
define_destroy records do
|
426
|
+
Shamu::Services::Result.new
|
427
|
+
end
|
428
|
+
end
|
429
|
+
|
430
|
+
service = scorpion.new( yield_klass )
|
431
|
+
service.destroy entity
|
432
|
+
end
|
433
|
+
|
441
434
|
it "calls #authorize!" do
|
442
435
|
expect( service ).to receive( :authorize! ).with(
|
443
436
|
:destroy,
|
@@ -458,7 +451,7 @@ describe Shamu::Services::ActiveRecordCrud do
|
|
458
451
|
let( :klass ) do
|
459
452
|
ec = entity_class
|
460
453
|
Class.new( super() ) do
|
461
|
-
|
454
|
+
define_build_entities do |records|
|
462
455
|
records.map do |record|
|
463
456
|
scorpion.fetch ec, { record: record }, {}
|
464
457
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: shamu
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.18
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Paul Alexander
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2017-06-
|
11
|
+
date: 2017-06-08 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activemodel
|