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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: f6f49a347f5a63383390bc51b9cba9616d7b7fec
4
- data.tar.gz: 732419b2df2e8757cc4772f8e03d59411abbbcbe
3
+ metadata.gz: dd0f794321cd3d306d36b16935e133e3ded9270b
4
+ data.tar.gz: 244f8025c48879a691c7faa6f6e76b3b45efe2e0
5
5
  SHA512:
6
- metadata.gz: 7355e1c846de3a9c53dbcaa6cc301433a0f901464f92f557ca2c0a439fa2385320e7faf53248b895b7364ea17c8ae71fdac828d14b779b0d31b57434c67b016e
7
- data.tar.gz: 61c004a0899ae0707d5d6f1a2b21e200cb80622302c9ac0ed77496c79019888d72006e99cd34f5df07d899354066584d04ada782cf8fba1743f0f0c4f1be4500
6
+ metadata.gz: 57d00e95ce2bf7a4d1ddbc224aff0a2790255a6bd1c1ac907f7450b95a30b63c7ca2cc404d0754c7ee363761386fc5c1567f1f50114957488d538be85d2cbda8
7
+ data.tar.gz: e2ab805eb2ba0db7969601cb825f827c8d5346c8ebd34db8e836543a6726b7327c20b68f092681555b56f1832d8dd8512d08d8bc1ddaf3170e6f221d5b3ab1a0
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- shamu (0.0.17)
4
+ shamu (0.0.18)
5
5
  activemodel (>= 5.0)
6
6
  activesupport (>= 5.0)
7
7
  crc32 (~> 1)
@@ -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
- # See {Shamu::Services::RequestSupport#with_request}
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 (request, transaction)
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
- def audit_request( params, request_class, action: :smart, &block )
51
- perform_audit_request( :with_request, params, request_class, action: action, &block )
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 = send method, params, request_class do |request|
59
- transaction.action = audit_request_action( request, action )
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
- transaction.append_entity result.entity if result.entity
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 Class.model_name.name
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
 
@@ -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 = self.class.name || "Events"
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
- # finders Models::User.active
17
+ # define_finders Models::User.active
18
18
  #
19
19
  # # Define change methods
20
- # create
21
- # update
20
+ # define_create
21
+ # define_update
22
22
  #
23
23
  # # Common update/change behavior for #create and #update
24
- # change do |request, model|
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
- # destroy
29
+ # define_destroy
30
30
  #
31
31
  # # Build the entity class from the given record.
32
- # build_entities do |records|
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
- build_entities( &block )
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
- # See {.apply_changes} for details.
145
- # @yield (see .apply_changes)
146
- # @yieldparam (see .apply_changes)
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 create( &block )
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
- if block
155
- yield( request, record )
156
- elsif respond_to? :apply_changes
157
- apply_changes( request, record )
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
- # See {.apply_changes} for details.
172
- # @yield (see .apply_changes)
173
- # @yieldparam (see .apply_changes)
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 change( method = :update, &block ) # rubocop:disable Metrics/PerceivedComplexity, Metrics/AbcSize
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 = model_class.find( id.to_model_id || request.id )
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
- if block
195
- yield( request, record )
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
- # rubocop:enabled Metrics/MethodLength
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 destroy( default_scope = model_class.all )
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 finders( default_scope = model_class.all, only: nil, except: nil )
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 find( default_scope = model_class.all, &block )
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 lookup( default_scope = model_class.all, &block )
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 list( default_scope = model_class.all, &block )
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 build_entities( &block )
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
- # Make sure the request captures errors even if the block doesn't
94
- # check
95
- request.valid?
93
+ result = Result.coerce( sources, request: request )
94
+ request.run_callbacks( result.valid? )
96
95
 
97
- if sources.is_a?( Result )
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
- private
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
@@ -2,7 +2,7 @@
2
2
 
3
3
  module Shamu
4
4
  # The primary version number
5
- VERSION_NUMBER = "0.0.17"
5
+ VERSION_NUMBER = "0.0.18"
6
6
 
7
7
  # Version suffix such as 'beta' or 'alpha'
8
8
  VERSION_SUFFIX = ""
@@ -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( { name: "Penguin" }, AuditingSupportSpec::Change ) do |request, transaction|
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( {}, AuditingSupportSpec::Change ) do |request, transaction|
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( { name: "Penguin" }, AuditingSupportSpec::Change ) do |request, transaction|
42
+ example_service.audit_request( request ) do |request|
39
43
  end
40
44
  end
41
- end
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( :klass ) do
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
- create
37
- change
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
- create( &b )
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
- request,
157
- kind_of( ActiveRecord::Base )
146
+ kind_of( ActiveRecord::Base ),
147
+ request
158
148
  )
159
149
  end
160
150
 
161
- it "does not call apply_changes if block given" do
151
+ it "short-circuits if block yields a Services::Result" do
162
152
  yield_klass = Class.new( klass ) do
163
- create { |_, _| }
153
+ define_create do
154
+ Shamu::Services::Result.new
155
+ end
164
156
  end
165
157
 
166
158
  service = scorpion.new( yield_klass )
167
- expect( service ).not_to receive :apply_changes
168
- service.create
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
- change( :update, &b )
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( request, kind_of( ::ActiveRecord::Base ) )
203
+ end.to yield_with_args( kind_of( ::ActiveRecord::Base ), request )
220
204
  end
221
205
 
222
- it "does not call apply_changes if block given" do
223
- entity = service.create.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
- change( :update ) { |_, _| }
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
- expect( service ).not_to receive :apply_changes
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.finders except: :list
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.finders only: :list
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.find do |_|
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
- lookup( &b )
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
- list( &b )
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
- build_entities do |records|
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.17
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-05 00:00:00.000000000 Z
11
+ date: 2017-06-08 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activemodel