shamu 0.0.17 → 0.0.18
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.
- 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
|