service_actor 2.0.0 → 3.0.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: e103cfa965d8a142d4d47ad910bd4411f90392ab4057b8536a265e12febbf42b
4
- data.tar.gz: cf297af32084c0a7ff7d3f85ecab5f52137516d5fc2d2c0aa1f5a1fb1aceccfd
3
+ metadata.gz: 1ce3e96190aef06e630c4e930ad942876f27b8f0dc6a00b33c0ff2104d9be9be
4
+ data.tar.gz: 7badaaa07f340dfd66924417d214f070fc50346ec3d9805c4d115757b2845e22
5
5
  SHA512:
6
- metadata.gz: 76cc57cc0c3eca0c951f3bf046e7132bd28157081ff7a815fa44b9ef6375180f035aecee14bf735d0a655e72fa48f840610694c505f90e81cb72e84bc67ea7b7
7
- data.tar.gz: 77c0cae0fe870e7a771023a45fbe919eeda92b8ed6e6d32187b4a85ddf906fbae27b1b615830d659214754914a7c0038ecdbba4a7b88b81234e2b96cdc08486c
6
+ metadata.gz: f186543f09b78804ea899b57bc8c78e174de83fa205d0a36d74b3089a17b1e6e71f2ddc6c285ae673b5e796b69ab76f327879b57ff6d97a95c4473aa62d1f763
7
+ data.tar.gz: 7ecedf103e0b66a462a3cfb9f97a7bdb9ec1ea349b3fe7e2824ac716d9a68f68d29f7dd3828099d7ab6569ae90eac573112317c97a3619a7f1072856727fd1c3
data/README.md CHANGED
@@ -6,6 +6,8 @@ This Ruby gem lets you move your application logic into into small composable
6
6
  service objects. It is a lightweight framework that helps you keep your models
7
7
  and controllers thin.
8
8
 
9
+ ![Photo of theater seats](https://user-images.githubusercontent.com/132/78340166-e7567000-7595-11ea-97c0-b3e5da2de7a1.png)
10
+
9
11
  ## Contents
10
12
 
11
13
  - [Installation](#installation)
@@ -21,9 +23,11 @@ and controllers thin.
21
23
  - [Rollback](#rollback)
22
24
  - [Lambdas](#lambdas)
23
25
  - [Play conditions](#play-conditions)
24
- - [Build your own actor](#build-your-own-actor)
26
+ - [Use with Rails](#use-with-rails)
25
27
  - [Testing](#testing)
28
+ - [Build your own actor](#build-your-own-actor)
26
29
  - [Influences](#influences)
30
+ - [Thanks](#thanks)
27
31
  - [Development](#development)
28
32
  - [Contributing](#contributing)
29
33
  - [License](#contributing)
@@ -59,7 +63,8 @@ SendNotification.call # => <ServiceActor::Result…>
59
63
  ```
60
64
 
61
65
  When called, actors return a Result. Reading and writing to this result allows
62
- actors to accept and return multiple arguments. Let's find out how to do that.
66
+ actors to accept and return multiple arguments. Let's find out how to do that
67
+ and then we'll see how to chain multiple actors togethor.
63
68
 
64
69
  ### Inputs
65
70
 
@@ -137,8 +142,21 @@ result = BuildGreeting.call
137
142
 
138
143
  ### Conditions
139
144
 
140
- You can add simple conditions that the inputs must verify, with the name of your
141
- choice under `must`:
145
+ You can ensure an input is included in a collection by using `in` (unreleased
146
+ yet):
147
+
148
+ ```rb
149
+ class Pay < Actor
150
+ input :currency, in: %w[EUR USD]
151
+
152
+ # …
153
+ end
154
+ ```
155
+
156
+ This raises an argument error if the input does not match one of the given
157
+ values.
158
+
159
+ You can also add custom conditions with the name of your choice by using `must`:
142
160
 
143
161
  ```rb
144
162
  class UpdateAdminUser < Actor
@@ -151,7 +169,7 @@ class UpdateAdminUser < Actor
151
169
  end
152
170
  ```
153
171
 
154
- In case the input does not match, it will raise an argument error.
172
+ This raises an argument error if the given lambda returns a falsey value.
155
173
 
156
174
  ### Allow nil
157
175
 
@@ -210,8 +228,7 @@ end
210
228
  This will raise an error in your app with the given data added to the result.
211
229
 
212
230
  To test for the success of your actor instead of raising an exception, use
213
- `.result` instead of `.call`. This lets you use `success?` and `failure?` on the
214
- result.
231
+ `.result` instead of `.call` and call `success?` or `failure?` on the result.
215
232
 
216
233
  For example in a Rails controller:
217
234
 
@@ -246,9 +263,9 @@ class PlaceOrder < Actor
246
263
  end
247
264
  ```
248
265
 
249
- This creates a `call` method where each actor will be called, taking their
250
- arguments from the previous actor's result. In fact, every actor along shares
251
- the same result instance to help shape the final result your application needs.
266
+ This creates a `call` method that will call every actor along the way. Inputs
267
+ and outputs will go from one actor to the next, all sharing the same result set
268
+ until it is finally returned.
252
269
 
253
270
  ### Rollback
254
271
 
@@ -320,6 +337,19 @@ end
320
337
 
321
338
  You can use this to trigger an early success.
322
339
 
340
+ ## Use with Rails
341
+
342
+ The [service_actor-rails](https://github.com/sunny/actor-rails/) gem includes a
343
+ Rails generator to create an actor and its spec.
344
+
345
+ ## Testing
346
+
347
+ In your application, add automated testing to your actors as you would do to any
348
+ other part of your applications.
349
+
350
+ You will find that cutting your business logic into single purpose actors will
351
+ make it easier for you to test your application.
352
+
323
353
  ## Build your own actor
324
354
 
325
355
  If you application already uses an "Actor" class, you can build your own by
@@ -337,14 +367,6 @@ class ApplicationActor
337
367
  end
338
368
  ```
339
369
 
340
- ## Testing
341
-
342
- In your application, add automated testing to your actors as you would do to any
343
- other part of your applications.
344
-
345
- You will find that cutting your business logic into single purpose actors makes
346
- your application much simpler to test.
347
-
348
370
  ## Influences
349
371
 
350
372
  This gem is heavily influenced by
@@ -367,9 +389,16 @@ However there are a few key differences which make `actor` unique:
367
389
  - No `before`, `after` and `around` hooks, prefer using `play` with lambdas or
368
390
  overriding `call`.
369
391
 
392
+ Actor supports mixing actors & interactors when using `play` for a smooth
393
+ migration.
394
+
395
+ ## Thanks
396
+
370
397
  Thank you to @nicoolas25, @AnneSottise & @williampollet for the early thoughts
371
398
  and feedback on this gem.
372
399
 
400
+ Photo by [Lloyd Dirks](https://unsplash.com/photos/4SLz_RCk6kQ).
401
+
373
402
  ## Development
374
403
 
375
404
  After checking out the repo, run `bin/setup` to install dependencies. Then, run
@@ -377,9 +406,10 @@ After checking out the repo, run `bin/setup` to install dependencies. Then, run
377
406
  interactive prompt.
378
407
 
379
408
  To release a new version, update the version number in `version.rb`, and in the
380
- `CHANGELOG.md`, run `rake`, and create a commit for this version. You can then
381
- run `rake release`, which will create a git tag for the version, push git
382
- commits and tags, and push the gem to [rubygems.org](https://rubygems.org).
409
+ `CHANGELOG.md`. Update the `README.md` if there are missing segments, make sure
410
+ tests and linting are pristine by calling `rake`, then create a commit for this
411
+ version. You can then run `rake release`, which will create a git tag, push
412
+ using git, and push the gem to [rubygems.org](https://rubygems.org).
383
413
 
384
414
  ## Contributing
385
415
 
@@ -389,7 +419,7 @@ Bug reports and pull requests are welcome
389
419
  This project is intended to be a safe, welcoming space for collaboration, and
390
420
  everyone interacting in the project’s codebase and issue tracker is expected to
391
421
  adhere to the [Contributor Covenant code of
392
- conduct](https://github.com/sunny/actor/blob/master/CODE_OF_CONDUCT.md).
422
+ conduct](https://github.com/sunny/actor/blob/main/CODE_OF_CONDUCT.md).
393
423
 
394
424
  ## License
395
425
 
@@ -5,32 +5,35 @@ require 'service_actor/core'
5
5
  # Exceptions
6
6
  require 'service_actor/error'
7
7
  require 'service_actor/failure'
8
- require 'service_actor/success'
9
8
  require 'service_actor/argument_error'
10
9
 
11
10
  # Core
12
- require 'service_actor/result'
11
+ require 'service_actor/core'
13
12
  require 'service_actor/attributable'
14
13
  require 'service_actor/playable'
15
- require 'service_actor/core'
14
+ require 'service_actor/result'
16
15
 
17
16
  # Concerns
18
17
  require 'service_actor/type_checkable'
19
18
  require 'service_actor/nil_checkable'
20
19
  require 'service_actor/conditionable'
21
20
  require 'service_actor/defaultable'
21
+ require 'service_actor/collectionable'
22
22
 
23
23
  module ServiceActor
24
24
  module Base
25
25
  def self.included(base)
26
26
  # Core
27
27
  base.include(Core)
28
+ base.include(Attributable)
29
+ base.include(Playable)
28
30
 
29
31
  # Concerns
30
32
  base.include(TypeCheckable)
31
33
  base.include(NilCheckable)
32
34
  base.include(Conditionable)
33
35
  base.include(Defaultable)
36
+ base.include(Collectionable)
34
37
  end
35
38
  end
36
39
  end
@@ -0,0 +1,32 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ServiceActor
4
+ # Add checks to your inputs, by specifying what values are authorized
5
+ # under the "in" key.
6
+ #
7
+ # Example:
8
+ #
9
+ # class Pay < Actor
10
+ # input :provider, in: ['MANGOPAY', 'PayPal', 'Stripe']
11
+ # end
12
+ module Collectionable
13
+ def self.included(base)
14
+ base.prepend(PrependedMethods)
15
+ end
16
+
17
+ module PrependedMethods
18
+ def _call
19
+ self.class.inputs.each do |key, options|
20
+ next unless options[:in]
21
+
22
+ next if options[:in].include?(result[key])
23
+
24
+ raise ArgumentError,
25
+ "Input #{key} must be included in #{options[:in].inspect}"
26
+ end
27
+
28
+ super
29
+ end
30
+ end
31
+ end
32
+ end
@@ -1,8 +1,10 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module ServiceActor
4
- # Add checks to your inputs, by calling lambdas with the name of you choice.
5
- # Will raise an error if any check does return a truthy value.
4
+ # Add checks to your inputs, by calling lambdas with the name of you choice
5
+ # under the "must" key.
6
+ #
7
+ # Will raise an error if any check returns a truthy value.
6
8
  #
7
9
  # Example:
8
10
  #
@@ -1,13 +1,9 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module ServiceActor
4
- # Actors should start with a verb, inherit from Actor and implement a `call`
5
- # method.
6
4
  module Core
7
5
  def self.included(base)
8
6
  base.extend(ClassMethods)
9
- base.include(Attributable)
10
- base.include(Playable)
11
7
  end
12
8
 
13
9
  module ClassMethods
@@ -18,15 +14,6 @@ module ServiceActor
18
14
  result = Result.to_result(options).merge!(arguments)
19
15
  new(result)._call
20
16
  result
21
- # DEPRECATED
22
- rescue Success
23
- result
24
- end
25
-
26
- # :nodoc:
27
- def call!(**arguments)
28
- warn "DEPRECATED: Prefer `#{name}.call` to `#{name}.call!`."
29
- call(**arguments)
30
17
  end
31
18
 
32
19
  # Call an actor with arguments. Returns the result and does not raise on
@@ -61,20 +48,9 @@ module ServiceActor
61
48
  # Returns the current context from inside an actor.
62
49
  attr_reader :result
63
50
 
64
- def context
65
- warn "DEPRECATED: Prefer `result.` to `context.` in #{self.class.name}."
66
-
67
- result
68
- end
69
-
70
51
  # Can be called from inside an actor to stop execution and mark as failed.
71
52
  def fail!(**arguments)
72
53
  result.fail!(**arguments)
73
54
  end
74
-
75
- # DEPRECATED
76
- def succeed!(**arguments)
77
- result.succeed!(**arguments)
78
- end
79
55
  end
80
56
  end
@@ -20,13 +20,14 @@ module ServiceActor
20
20
  self.class.inputs.each do |name, input|
21
21
  next if result.key?(name)
22
22
 
23
- unless input.key?(:default)
24
- raise ArgumentError, "Input #{name} on #{self.class} is missing."
23
+ if input.key?(:default)
24
+ default = input[:default]
25
+ default = default.call if default.respond_to?(:call)
26
+ result[name] = default
27
+ next
25
28
  end
26
29
 
27
- default = input[:default]
28
- default = default.call if default.respond_to?(:call)
29
- result[name] = default
30
+ raise(ArgumentError, "Input #{name} on #{self.class} is missing.")
30
31
  end
31
32
 
32
33
  super
@@ -27,8 +27,6 @@ module ServiceActor
27
27
 
28
28
  def check_context_for_nil(definitions, origin:)
29
29
  definitions.each do |name, options|
30
- warn_of_deprecated_required_option(options, name, origin)
31
-
32
30
  next if !result[name].nil? || allow_nil?(options)
33
31
 
34
32
  raise ArgumentError,
@@ -37,20 +35,9 @@ module ServiceActor
37
35
  end
38
36
  end
39
37
 
40
- def warn_of_deprecated_required_option(options, name, origin)
41
- return unless options.key?(:required)
42
-
43
- warn 'DEPRECATED: The "required" option is deprecated. Replace ' \
44
- "`#{origin} :#{name}, required: #{options[:required]}` by " \
45
- "`#{origin} :#{name}, allow_nil: #{!options[:required]}` in " \
46
- "#{self.class}."
47
- end
48
-
49
38
  def allow_nil?(options)
50
39
  if options.key?(:allow_nil)
51
40
  options[:allow_nil]
52
- elsif options.key?(:required)
53
- !options[:required]
54
41
  elsif options.key?(:default) && options[:default].nil?
55
42
  true
56
43
  elsif options[:type]
@@ -64,7 +64,8 @@ module ServiceActor
64
64
  actor = actor.new(result)
65
65
  actor._call
66
66
  else
67
- actor.call(result)
67
+ new_result = actor.call(result)
68
+ result.merge!(new_result.to_h) if new_result.respond_to?(:to_h)
68
69
  end
69
70
 
70
71
  (@played ||= []).unshift(actor)
@@ -3,7 +3,8 @@
3
3
  require 'ostruct'
4
4
 
5
5
  module ServiceActor
6
- # Represents the result of an actor.
6
+ # Represents the context of an actor, holding the data from both its inputs
7
+ # and outputs.
7
8
  class Result < OpenStruct
8
9
  def self.to_result(data)
9
10
  return data if data.is_a?(self)
@@ -22,16 +23,6 @@ module ServiceActor
22
23
  raise Failure, self
23
24
  end
24
25
 
25
- def succeed!(result = {})
26
- warn 'DEPRECATED: Early success with `succeed!` is deprecated in favor ' \
27
- 'of adding conditions to `play` calls.'
28
-
29
- merge!(result)
30
- merge!(failure?: false)
31
-
32
- raise Success, self
33
- end
34
-
35
26
  def success?
36
27
  !failure?
37
28
  end
@@ -1,14 +1,13 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module ServiceActor
4
- # Adds `type:` checking to inputs and outputs. Accepts classes or class names
4
+ # Adds `type:` checking to inputs and outputs. Accepts class names or classes
5
5
  # that should match an ancestor. Also accepts arrays.
6
6
  #
7
7
  # Example:
8
8
  #
9
9
  # class ReduceOrderAmount < Actor
10
- # input :order, type: Order
11
- # input :coupon, type: 'Coupon'
10
+ # input :order, type: 'Order'
12
11
  # input :amount, type: [Integer, Float]
13
12
  # input :bonus_applied, type: [TrueClass FalseClass]
14
13
  # end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module ServiceActor
4
- VERSION = '2.0.0'
4
+ VERSION = '3.0.0'
5
5
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: service_actor
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.0.0
4
+ version: 3.0.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Sunny Ripert
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2020-03-24 00:00:00.000000000 Z
11
+ date: 2020-08-20 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rspec
@@ -80,6 +80,34 @@ dependencies:
80
80
  - - ">="
81
81
  - !ruby/object:Gem::Version
82
82
  version: '0'
83
+ - !ruby/object:Gem::Dependency
84
+ name: code-scanning-rubocop
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - ">="
88
+ - !ruby/object:Gem::Version
89
+ version: '0'
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - ">="
95
+ - !ruby/object:Gem::Version
96
+ version: '0'
97
+ - !ruby/object:Gem::Dependency
98
+ name: interactor
99
+ requirement: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - ">="
102
+ - !ruby/object:Gem::Version
103
+ version: '0'
104
+ type: :development
105
+ prerelease: false
106
+ version_requirements: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - ">="
109
+ - !ruby/object:Gem::Version
110
+ version: '0'
83
111
  description: Service objects for your application logic
84
112
  email:
85
113
  - sunny@sunfox.org
@@ -95,6 +123,7 @@ files:
95
123
  - lib/service_actor/argument_error.rb
96
124
  - lib/service_actor/attributable.rb
97
125
  - lib/service_actor/base.rb
126
+ - lib/service_actor/collectionable.rb
98
127
  - lib/service_actor/conditionable.rb
99
128
  - lib/service_actor/core.rb
100
129
  - lib/service_actor/defaultable.rb
@@ -103,7 +132,6 @@ files:
103
132
  - lib/service_actor/nil_checkable.rb
104
133
  - lib/service_actor/playable.rb
105
134
  - lib/service_actor/result.rb
106
- - lib/service_actor/success.rb
107
135
  - lib/service_actor/type_checkable.rb
108
136
  - lib/service_actor/version.rb
109
137
  homepage: https://github.com/sunny/actor
@@ -112,7 +140,7 @@ licenses:
112
140
  metadata:
113
141
  homepage_uri: https://github.com/sunny/actor
114
142
  source_code_uri: https://github.com/sunny/actor
115
- changelog_uri: https://github.com/sunny/actor/blob/master/CHANGELOG.md
143
+ changelog_uri: https://github.com/sunny/actor/blob/main/CHANGELOG.md
116
144
  post_install_message:
117
145
  rdoc_options: []
118
146
  require_paths:
@@ -121,7 +149,10 @@ required_ruby_version: !ruby/object:Gem::Requirement
121
149
  requirements:
122
150
  - - ">="
123
151
  - !ruby/object:Gem::Version
124
- version: '0'
152
+ version: 2.3.7
153
+ - - "<"
154
+ - !ruby/object:Gem::Version
155
+ version: 2.8.0
125
156
  required_rubygems_version: !ruby/object:Gem::Requirement
126
157
  requirements:
127
158
  - - ">="
@@ -1,7 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module ServiceActor
4
- # Raised when using `succeed!` to halt the progression of an organizer.
5
- # DEPRECATED in favor of adding conditions to your play.
6
- class Success < Error; end
7
- end