cuprum 0.5.0 → 0.6.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
  SHA1:
3
- metadata.gz: 4420d672d1c4560ed615b9d40100423cffba35bc
4
- data.tar.gz: 76697dbef40ba29900b3cfc53716f244f8c007c5
3
+ metadata.gz: 6c749e3c0c387227604f9ca4be639d3cabf06dcc
4
+ data.tar.gz: 779b36680827fb53d901fbe44436e679cf703cfd
5
5
  SHA512:
6
- metadata.gz: 5e9afc3c3bb45b356098825bb077e7d1e0764cec2b1bc1e75e8fb1ea334f3707041b187fadc69ddbb556135f03d8b474d417615620c71b89048d19436d8e532a
7
- data.tar.gz: 69acd4b378b7828f656d53e5293ea01bf6a174f2247cabfde5e3b86400c121e6f7026f60985a3ffd5c8b2ae5eb94728295f3b32e563c7c1afac9c5e0c416a97b
6
+ metadata.gz: 95cb4d600263513ef199f4c578f45569eb62052fcd9806fe3f8195ee707fa8fddbaf3704b136192e66186751febefac90050deb51cfaeb502c39eecc75817ea4
7
+ data.tar.gz: c4571461ea2b47e78598fa46ae21273d439ea8c3b06bb1129b54135850197d5fbc80dc583547464b4d1fcbef89333dc1a042d3aba9c5323887c8abcedef6b05b
@@ -1,5 +1,23 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.6.0
4
+
5
+ The "By Your Command" Update.
6
+
7
+ ### Commands
8
+
9
+ Refactored `Cuprum::Function` to `Cuprum::Command` to better reflect its role as an implementation of the Command pattern.
10
+
11
+ Extracted `Cuprum::BasicCommand` as a base class for all commands, implementing `#call` and its associated methods but not including methods or functionality related to command chains.
12
+
13
+ Extracted the `Cuprum::Chaining` mixin, which encapsulates all of the methods and functionality necessary to implement command chaining.
14
+
15
+ ### Built In Commands
16
+
17
+ Refactored `Cuprum::BuiltIn::IdentityFunction` to `Cuprum::BuiltIn::IdentityCommand`.
18
+
19
+ Refactored `Cuprum::BuiltIn::NullFunction` to `Cuprum::BuiltIn::NullCommand`.
20
+
3
21
  ## 0.5.0
4
22
 
5
23
  The "Name Not Found For NullFunction" Update.
@@ -1,18 +1,52 @@
1
1
  # Development
2
2
 
3
- - Rename Cuprum::Function to Cuprum::Command.
4
- - Extract Cuprum::BasicCommand (excludes chaining functionality).
3
+ - Update documentation.
5
4
 
6
5
  ## Core
7
6
 
8
- ## Function
7
+ ## Command
9
8
 
10
- - Predefined functions/operations:
11
- - IdentityFunction
12
- - MapFunction
13
- - RetryFunction
14
- - allow_result_argument? - defaults to false. if false, there is one argument,
15
- and the argument is a Result, process the value instead.
9
+ - Chaining Methods:
10
+ - #chain:
11
+ - with a command, sets the command's #result to the last result and calls
12
+ #process with the last result's #value; returns result of calling chained
13
+ command.
14
+ - with a block creates anonymous command, then see above
15
+ - if the command does not accept any arguments (or keywords if #value is a
16
+ Hash), does not pass #value. Requires #arity, #apply_chained?
17
+ - one argument (on), values nil (default), :success, :failure, :always
18
+ - #success - as chain(:success)
19
+ - #failure - as chain(:failure)
20
+ - #tap_result:
21
+ - block form only, block is yielded and returns the last result
22
+ - same argument as #chain
23
+ - #yield_result:
24
+ - block form only, block is yielded the last result, returns the return
25
+ value of the block (wrapped in a result if needed)
26
+ - same argument as #chain
27
+ - Protected Chaining Methods:
28
+ - #chain!, #success!, #failure!, #tap_chain!, #yield_result!
29
+ - adds chained command to current command instead of a clone.
30
+ - private #build_result
31
+
32
+ ### Built In
33
+
34
+ - MapCommand - wraps a command (or proc) and returns Result with value, errors
35
+ as array
36
+ - RetryCommand
37
+
38
+ ### DSL
39
+
40
+ - class-level methods
41
+ - ::chain (::success, ::failure):
42
+ on #initialize, chains the given command. Can be given a command class
43
+ (if ::new takes no arguments) or a block that returns a command.
44
+ - ::process - shortcut for defining #process
45
+ - ::rescue - `rescue StandardError do ... end`, rescues matched errors in #process
46
+
47
+ ### Hooks
48
+
49
+ - :before, :around, :after hooks
16
50
 
17
51
  ## Operation
18
52
 
data/README.md CHANGED
@@ -1,441 +1,644 @@
1
1
  # Cuprum
2
2
 
3
- A lightweight, functional-lite toolkit for making business logic a first-class
4
- citizen of your application.
3
+ An opinionated implementation of the Command pattern for Ruby applications. Cuprum wraps your business logic in a consistent, object-oriented interface and features status and error management, composability and control flow management.
5
4
 
6
- ## Support
5
+ It defines the following concepts:
7
6
 
8
- Cuprum is tested against Ruby 2.4.
7
+ - [Commands](#label-Commands) - A function-like object that responds to `#call` and returns a `Result`.
8
+ - [Operations](#label-Operations) - A stateful `Command` that wraps and delegates to its most recent `Result`.
9
+ - [Results](#label-Results) - A data object with a `#value`, an `#errors` object, and `#success?` and `#failure?` status methods.
9
10
 
10
- ## Documentation
11
+ ## About
12
+
13
+ [comment]: # "Status Badges will go here."
14
+
15
+ Traditional frameworks such as Rails focus on the objects of your application - the "nouns" such as User, Post, or Item. Using Cuprum or a similar library allows you the developer to make your business logic - the "verbs" such as Create User, Update Post or Ship Item - a first-class citizen of your project. This provides several advantages:
16
+
17
+ - **Consistency:** Use the same Commands to underlie controller actions, worker processes and test factories.
18
+ - **Encapsulation:** Each Command is defined and run in isolation, and dependencies must be explicitly provided to the command when it is initialized or run. This makes it easier to reason about the command's behavior and keep it insulated from changes elsewhere in the code.
19
+ - **Testability:** Because the logic is extracted from unnecessary context, testing its behavior is much cleaner and easier.
20
+ - **Composability:** Complex logic such as "find the object with this ID, update it with these attributes, and log the transaction to the reporting service" can be extracted into a series of simple Commands and composed together. The [Chaining](#label-Chaining) feature allows for complex control flows.
21
+ - **Reusability:** Logic common to multiple data models or instances in your code, such as "persist an object to the database" or "find all records with a given user and created in a date range" can be refactored into parameterized commands.
22
+
23
+ ### Alternatives
24
+
25
+ If you want to extract your logic but Cuprum is not the right solution for you, here are several alternatives:
26
+
27
+ - Service objects. A common pattern used when first refactoring an application that has outgrown its abstractions. Service objects are simple and let you group related functionality, but they are harder to compose and require firm conventions to tame.
28
+ - The [Interactor](https://github.com/collectiveidea/interactor) gem. Provides an `Action` module to implement logic and an `Organizer` module to manage control flow. Supports before, around, and after hooks.
29
+ - The [Waterfall](https://github.com/apneadiving/waterfall) gem. Focused more on control flow.
30
+ - [Trailblazer](http://trailblazer.to/) Operations. A pipeline-based approach to control flow, and can integrate tightly with other Trailblazer elements.
31
+
32
+ ### Compatibility
33
+
34
+ Cuprum is tested against Ruby (MRI) 2.4.
35
+
36
+ ### Documentation
11
37
 
12
38
  Method and class documentation is available courtesy of [RubyDoc](http://www.rubydoc.info/github/sleepingkingstudios/cuprum/master).
13
39
 
14
40
  Documentation is generated using [YARD](https://yardoc.org/), and can be generated locally using the `yard` gem.
15
41
 
16
- ## Contribute
42
+ ### License
43
+
44
+ Copyright (c) 2017 Rob Smith
17
45
 
18
- ### GitHub
46
+ Cuprum is released under the [MIT License](https://opensource.org/licenses/MIT).
47
+
48
+ ### Contribute
19
49
 
20
50
  The canonical repository for this gem is located at https://github.com/sleepingkingstudios/cuprum.
21
51
 
22
- ### A Note From The Developer
52
+ To report a bug or submit a feature request, please use the [Issue Tracker](https://github.com/sleepingkingstudios/cuprum/issues).
23
53
 
24
- Hi, I'm Rob Smith, a Ruby Engineer and the developer of this library. I use these tools every day, but they're not just written for me. If you find this project helpful in your own work, or if you have any questions, suggestions or critiques, please feel free to get in touch! I can be reached on GitHub (see above, and feel encouraged to submit bug reports or merge requests there) or via email at merlin@sleepingkingstudios.com. I look forward to hearing from you!
54
+ To contribute code, please fork the repository, make the desired updates, and then provide a [Pull Request](https://github.com/sleepingkingstudios/cuprum/pulls). Pull requests must include appropriate tests for consideration, and all code must be properly formatted.
25
55
 
26
- ## Functions
56
+ ### Credits
27
57
 
28
- require 'cuprum/function'
58
+ Hi, I'm Rob Smith, a Ruby Engineer and the developer of this library. I use these tools every day, but they're not just written for me. If you find this project helpful in your own work, or if you have any questions, suggestions or critiques, please feel free to get in touch! I can be reached [on GitHub](https://github.com/sleepingkingstudios/cuprum) or [via email](mailto:merlin@sleepingkingstudios.com). I look forward to hearing from you!
29
59
 
30
- [Class Documentation](http://www.rubydoc.info/github/sleepingkingstudios/cuprum/master/Cuprum%2FFunction)
60
+ ## Concepts
31
61
 
32
- Functions are the core feature of Cuprum. In a nutshell, each Cuprum::Function is a functional object that encapsulates a business logic operation. A Function provides a consistent interface and tracking of result value and status. This minimizes boilerplate and allows for interchangeability between different implementations or strategies for managing your data and processes.
62
+ ### Commands
33
63
 
34
- Each Function implements a `#call` method that wraps your defined business logic and returns an instance of Cuprum::Result. The result wraps the returned data (with the `#value` method), any `#errors` generated when running the Function, and the overall status with the `#success?` and `#failure` methods. For more details about Cuprum::Result, see below.
64
+ require 'cuprum'
35
65
 
36
- ### Methods
66
+ Commands are the core feature of Cuprum. In a nutshell, each Cuprum::Command is a functional object that encapsulates a business logic operation. A Command provides a consistent interface and tracking of result value and status. This minimizes boilerplate and allows for interchangeability between different implementations or strategies for managing your data and processes.
37
67
 
38
- A Cuprum::Function defines the following methods:
68
+ Each Command implements a `#call` method that wraps your defined business logic and returns an instance of Cuprum::Result. The result wraps the returned data (with the `#value` method), any `#errors` generated when running the Command, and the overall status with the `#success?` and `#failure` methods. For more details about Cuprum::Result, [see below](#label-Results).
39
69
 
40
- #### #initialize
70
+ [Class Documentation](http://www.rubydoc.info/github/sleepingkingstudios/cuprum/master/Cuprum%2FCommand)
41
71
 
42
- initialize { |*arguments, **keywords, &block| ... } #=> Cuprum::Function
72
+ #### Defining Commands
43
73
 
44
- Returns a new instance of Cuprum::Function. If a block is given, the `#call` method will wrap the block and set the result `#value` to the return value of the block. This overrides the implementation in `#process`, if any.
74
+ The recommended way to define commands is to create a subclass of `Cuprum::Command` and override the `#process` method.
45
75
 
46
- [Method Documentation](http://www.rubydoc.info/github/sleepingkingstudios/cuprum/master/Cuprum/Function#initialize-instance_method)
76
+ ```ruby
77
+ class BuildBookCommand < Cuprum::Command
78
+ def process attributes
79
+ Book.new(attributes)
80
+ end # method process
81
+ end # class
47
82
 
48
- #### #call
83
+ command = BuildPostCommand.new
84
+ result = command.call(:title => 'The Hobbit') # an instance of Cuprum::Result
85
+ result.value #=> an instance of Book with title 'The Hobbit'
86
+ ```
49
87
 
50
- call(*arguments, **keywords) { ... } #=> Cuprum::Result
88
+ There are several takeaways from this example. First, we are defining a custom command class that inherits from `Cuprum::Command`. We are defining the `#process` method, which takes a single `attributes` parameter and returns an instance of `Book`. Then, we are creating an instance of the command, and invoking the `#call` method with an attributes hash. These attributes are passed to our `#process` implementation. Invoking `#call` returns a result, and the `#value` of the result is our new Book.
51
89
 
52
- Executes the logic encoded in the constructor block, or the #process method if no block was passed to the constructor.
90
+ Because a command is just a Ruby object, we can also pass values to the constructor.
53
91
 
54
- [Method Documentation](http://www.rubydoc.info/github/sleepingkingstudios/cuprum/master/Cuprum/Function#call-instance_method)
92
+ ```ruby
93
+ class SaveBookCommand < Cuprum::Command
94
+ def initialize repository
95
+ @repository = repository
96
+ end # constructor
55
97
 
56
- #### #chain
98
+ def process book
99
+ @repository.persist(book)
100
+ end # method process
101
+ end # class
57
102
 
58
- Registers a function or block to run after the current function, or after the last chained function if the current function already has one or more chained function(s). This creates and modifies a copy of the current function. See Chaining Functions, below.
103
+ books = [
104
+ Book.new(:title => 'The Fellowship of the Ring'),
105
+ Book.new(:title => 'The Two Towers'),
106
+ Book.new(:title => 'The Return of the King')
107
+ ]
108
+ command = SaveBookCommand.new(books_repository)
109
+ books.each { |book| command.call(book) }
110
+ ```
59
111
 
60
- chain(function, on: nil) #=> Cuprum::Function
112
+ Here, we are reusing the same command three times, rather than creating a new save command for each book. Each book is persisted to the `books_repository`. This is also an example of how using commands can simplify code - notice that nothing about the `SaveBookCommand` is specific to the `Book` model. Thus, we could refactor this into a generic `SaveModelCommand`.
61
113
 
62
- The function will be passed the `#value` of the previous function result as its parameter, and the result of the chained function will be returned (or passed to the next chained function, if any).
114
+ A command can also be defined by passing block to `Cuprum::Command.new`.
63
115
 
64
- chain(on: nil) { |result| ... } #=> Cuprum::Function
116
+ ```ruby
117
+ increment_command = Cuprum::Command.new { |int| int + 1 }
65
118
 
66
- The block will be passed the #result of the previous function as its parameter. If your use case depends on the status of the previous function or on any errors generated, use the block form of #chain.
119
+ increment_command.call(2).value #=> 3
120
+ ```
67
121
 
68
- If the block returns a Cuprum::Result (or an object responding to #value and #success?), the block result will be returned (or passed to the next chained function, if any). If the block returns any other value (including nil), the #result of the previous function will be returned or passed to the next function.
122
+ Commands defined using `Cuprum::Command.new` are quick to use, but more difficult to read and to reuse. Defining your own command class is recommended if a command definition takes up more than one line, or if the command will be used in more than one place.
69
123
 
70
- [Method Documentation](http://www.rubydoc.info/github/sleepingkingstudios/cuprum/master/Cuprum/Function#chain-instance_method)
124
+ #### Success, Failure, and Errors
71
125
 
72
- #### `#then`
126
+ Whether defined with a block or in the `#process` method, the Command implementation can access an `#errors` object while in the `#call` method. Any errors added to the errors object will be exposed by the `#errors` method on the result object.
73
127
 
74
- Shorthand for `function.chain(:on => :success)`. Registers a function or block to run after the current function. The chained function will only run if the previous function was successfully run.
128
+ ```ruby
129
+ class PublishBookCommand < Cuprum::Command
130
+ private
75
131
 
76
- then(function) #=> Cuprum::Function
132
+ def process book
133
+ if book.cover.nil?
134
+ errors << 'This book does not have a cover.'
77
135
 
78
- The function will be passed the `#value` of the previous function result as its parameter, and the result of the chained function will be returned (or passed to the next chained function, if any).
136
+ return
137
+ end # if
79
138
 
80
- then() { |result| ... } #=> Cuprum::Function
139
+ book.published = true
81
140
 
82
- The block will be passed the #result of the previous function as its parameter. If your use case depends on the status of the previous function or on any errors generated, use the block form of #chain.
141
+ book
142
+ end # method process
143
+ end # class
144
+ ```
83
145
 
84
- If the block returns a Cuprum::Result (or an object responding to #value and #success?), the block result will be returned (or passed to the next chained function, if any). If the block returns any other value (including nil), the #result of the previous function will be returned or passed to the next function.
146
+ In addition, the result object defines `#success?` and `#failure?` predicates. If the result has no errors, then `#success?` will return true and `#failure?` will return false.
85
147
 
86
- [Method Documentation](http://www.rubydoc.info/github/sleepingkingstudios/cuprum/master/Cuprum/Function#then-instance_method)
148
+ ```ruby
149
+ book = Book.new(:title => 'The Silmarillion', :cover => Cover.new)
150
+ book.published? #=> false
87
151
 
88
- #### `#else`
152
+ result = PublishBookCommand.new.call(book)
153
+ result.errors #=> []
154
+ result.success? #=> true
155
+ result.failure? #=> false
156
+ result.value #=> book
157
+ book.published? #=> true
158
+ ```
89
159
 
90
- Shorthand for `function.chain(:on => :failure)`. Registers a function or block to run after the current function. The chained function will only run if the previous function was unsuccessfully run.
160
+ If the result does have errors, `#success?` will return false and `#failure?` will return true.
91
161
 
92
- else(function) #=> Cuprum::Function
162
+ ```ruby
163
+ book = Book.new(:title => 'The Silmarillion', :cover => nil)
164
+ book.published? #=> false
93
165
 
94
- The function will be passed the `#value` of the previous function result as its parameter, and the result of the chained function will be returned (or passed to the next chained function, if any).
166
+ result = PublishBookCommand.new.call(book)
167
+ result.errors #=> ['This book does not have a cover.']
168
+ result.success? #=> false
169
+ result.failure? #=> true
170
+ result.value #=> book
171
+ book.published? #=> false
172
+ ```
95
173
 
96
- else() { |result| ... } #=> Cuprum::Function
174
+ ### Chaining Commands
97
175
 
98
- The block will be passed the #result of the previous function as its parameter. If your use case depends on the status of the previous function or on any errors generated, use the block form of #chain.
176
+ Because Cuprum::Command instances are proper objects, they can be composed like any other object. Cuprum::Command also defines methods for chaining commands together. When a chain of commands is called, each command in the chain is called in sequence and passed the value of the previous command. The result of the last command in the chain is returned from the chained call.
99
177
 
100
- If the block returns a Cuprum::Result (or an object responding to #value and #success?), the block result will be returned (or passed to the next chained function, if any). If the block returns any other value (including nil), the #result of the previous function will be returned or passed to the next function.
178
+ class AddCommand < Cuprum::Command
179
+ def initialize addend
180
+ @addend = addend
181
+ end # constructor
101
182
 
102
- [Method Documentation](http://www.rubydoc.info/github/sleepingkingstudios/cuprum/master/Cuprum/Function#else-instance_method)
183
+ private
103
184
 
104
- #### `#build_errors`
185
+ def process int
186
+ int + @addend
187
+ end # method process
188
+ end # class
105
189
 
106
- *Private method*. Generates an empty errors object. When the function is called, the result will have its `#errors` property initialized to the value returned by `#build_errors`. By default, this is an array. If you want to use a custom errors object type, override this method in a subclass.
190
+ double_and_add_one = MultiplyCommand.new(2).chain(AddCommand.new(1))
191
+ result = double_and_add_one(5)
107
192
 
108
- build_errors() #=> Array
193
+ result.value #=> 5
109
194
 
110
- [Method Documentation](http://www.rubydoc.info/github/sleepingkingstudios/cuprum/master/Cuprum/Function#build_errors-instance_method)
195
+ For finer control over the returned result, `#chain` can instead be called with a block that yields the most recent result. If the block returns a Cuprum::Result, that result is returned or passed to the next command.
111
196
 
112
- ### Implementation Hooks
197
+ MultiplyCommand.new(3).
198
+ chain { |result| Cuprum::Result.new(result + 1) }.
199
+ call(3)
200
+ #=> Returns a Cuprum::Result with a value of 10.
113
201
 
114
- These methods are only available while the Function is being called, and allow the implementation to update the errors of and override the results of the result object.
202
+ Otherwise, the block is still called but the previous result is returned or passed to the next command in the chain.
115
203
 
116
- #### `#errors`
204
+ AddCommand.new(2).
205
+ chain { |result| puts "There are #{result.value} lights!" }.
206
+ call(2)
207
+ #=> Writes "There are 4 lights!" to STDOUT.
208
+ #=> Returns a Cuprum::Result with a value of 4.
117
209
 
118
- Only available while the Function is being called. Provides access to the errors object of the generated Cuprum::Result, which is by default an instance of Array.
210
+ #### Conditional Chaining
119
211
 
120
- errors() #=> Array
212
+ The `#chain` method can be passed an optional `:on` keyword, with values of `:success` and `:failure` accepted. If `#chain` is called with `:on => :success`, then the chained command or block will **only** be called if the previous result `#success?` returns true. Conversely, if `#chain` is called with `:on => :failure`, then the chained command will only be called if the previous result `#failure?` returns true.
121
213
 
122
- Inside of the Function block or the `#process` method, you can add errors to the result.
214
+ In either case, execution will then pass to the next command in the chain, which may itself be called or not if it was conditionally chained. Calling a conditional command chain will return the result of the last called command.
123
215
 
124
- function =
125
- Cuprum::Function.new do
126
- errors << "I'm sorry, something went wrong."
216
+ The methods `#then` and `#else` serve as shortcuts for `#chain` with `:on => :success` and `:on => :failure`, respectively.
127
217
 
128
- nil
129
- end # function
218
+ class EvenCommand < Cuprum::Command
219
+ private
130
220
 
131
- result = function.call
132
- result.failure?
133
- #=> true
134
- result.errors
135
- #=> ["I'm sorry, something went wrong."]
221
+ def process int
222
+ errors << 'errors.messages.not_even' unless int.even?
136
223
 
137
- [Method Documentation](http://www.rubydoc.info/github/sleepingkingstudios/cuprum/master/Cuprum/Function#errors-instance_method)
224
+ int
225
+ end # method process
226
+ end # class
138
227
 
139
- #### `#success!`
228
+ # The next step in a Collatz sequence is determined as follows:
229
+ # - If the number is even, divide it by 2.
230
+ # - If the number is odd, multiply it by 3 and add 1.
231
+ collatz_command =
232
+ EvenCommand.new.
233
+ then(DivideCommand.new(2)).
234
+ else(MultiplyCommand.new(3).chain(AddCommand.new(1)))
140
235
 
141
- Only available while the Function is being called. If called, marks the result object as passing, even if the result has errors.
236
+ result = collatz_command.new(5)
237
+ result.value #=> 16
142
238
 
143
- success!() #=> NilClass
239
+ result = collatz_command.new(16)
240
+ result.value #=> 8
144
241
 
145
- #### `#failure!`
242
+ #### Halting A Command Chain
146
243
 
147
- Only available while the Function is being called. If called, marks the result object as failing, even if the result does not have errors.
244
+ If the `#halt` method is called as part of a Command block or `#process` method, the command chain is halted. Any subsequent chained commands will not be called unless they were chained with the `:on => :always` option. This allows you to terminate a Command chain early without having to raise and rescue an exception.
148
245
 
149
- failure!() #=> NilClass
246
+ panic_command =
247
+ Cuprum::Command.new do |value|
248
+ halt!
150
249
 
151
- #### `#halt!`
250
+ value
251
+ end # command
152
252
 
153
- Only available while the Function is being called. If called, halts the function chain (see Chaining Functions, below). Subsequent chained functions will not be called unless they were chained with the `:on => :always` option.
253
+ result =
254
+ double_command.
255
+ then(panic_command).
256
+ then(AddCommand.new(1)). #=> This is never executed.
257
+ chain(:on => :always) { |count| puts "There are #{count} lights!" }.
258
+ call(2)
259
+ #=> Writes "There are 4 lights!" to STDOUT.
154
260
 
155
- halt!() #=> NilClass
261
+ result.value #= 4
262
+ result.halted? #=> true
156
263
 
157
- ### Defining With a Block
264
+ ### Results
158
265
 
159
- Functions can be used right out of the box by passing a block to the Cuprum::Function constructor, as follows:
266
+ require 'cuprum'
160
267
 
161
- # A Function with a block
162
- double_function = Cuprum::Function.new { |int| 2 * int }
163
- result = double_function.call(5)
268
+ [Class Documentation](http://www.rubydoc.info/github/sleepingkingstudios/cuprum/master/Cuprum%2FResult)
164
269
 
165
- result.value #=> 10
270
+ A Cuprum::Result is a data object that encapsulates the result of calling a Cuprum command. Each result has a `#value`, an `#errors` object (defaults to an Array), and status methods `#success?`, `#failure?`, and `#halted?`.
166
271
 
167
- The constructor block will be called each time `Function#call` is executed, and will be passed all of the arguments given to `#call`. You can even define a block parameter, which will be passed along to the constructor block when `#call` is called with a block argument.
272
+ ```ruby
273
+ value = 'A result value'.freeze
274
+ result = Cuprum::Result.new(value)
168
275
 
169
- ### Defining With a Subclass
276
+ result.value #=> 'A result value'
277
+ result.errors #=> []
278
+ result.success? #=> true
279
+ result.failure? #=> false
280
+ result.halted? #=> false
281
+ ```
170
282
 
171
- Larger applications will want to create Function subclasses that encapsulate their business logic in a reusable, composable fashion. The implementation for each subclass is handled by the `#process` private method. If a subclass or its ancestors does not implement `#process`, a `Cuprum::Function::NotImplementedError` will be raised.
283
+ Adding errors to the `#errors` object will change the status of the result.
172
284
 
173
- # A Function subclass
174
- class MultiplyFunction < Cuprum::Function
175
- def initialize multiplier
176
- @multiplier = multiplier
177
- end # constructor
285
+ ```ruby
286
+ result.errors << "I'm sorry, something went wrong."
287
+ result.success? #=> false
288
+ result.failure? #=> true
289
+ ```
178
290
 
179
- private
291
+ The status can also be overriden with the `#success!` and `#failure!` methods, which will set the status regardless of the presence or absence of errors.
180
292
 
181
- def process int
182
- int * @multiplier
183
- end # method process
184
- end # class
293
+ ```ruby
294
+ result.success!
295
+ result.errors #=> ["I'm sorry, something went wrong."]
296
+ result.success? #=> true
297
+ result.failure? #=> false
298
+ ```
185
299
 
186
- triple_function = MultiplyFunction.new(3)
187
- result = triple_function.call(5)
300
+ ### Operations
188
301
 
189
- result.value #=> 15
302
+ require 'cuprum'
190
303
 
191
- As with the block syntax, a Function whose implementation is defined via the `#process` method will call `#process` each time that `#call` is executed, and will pass all arguments from `#call` on to `#process`. The value returned by `#process` will be assigned to the result `#value`.
304
+ [Class Documentation](http://www.rubydoc.info/github/sleepingkingstudios/cuprum/master/Cuprum%2FOperation)
192
305
 
193
- ### Success, Failure, and Errors
306
+ An Operation is like a Command, but with two key differences. First, an Operation retains a reference to the result object from the most recent time the operation was called, and delegates the methods defined by `Cuprum::Result` to the most recent result. This allows a called Operation to replace a `Cuprum::Result` in any code that expects or returns a result. Second, the `#call` method returns the operation instance, rather than the result itself.
194
307
 
195
- Whether defined with a block or in the `#process` method, the Function implementation can access an `#errors` object while in the `#call` method. Any errors added to the errors object will be exposed by the `#errors` method on the result object.
308
+ These two features allow developers to simplify logic around calling and using the results of operations, and reduce the need for boilerplate code (particularly when using an operation as part of an existing framework, such as inside of an asynchronous worker or a Rails controller action).
196
309
 
197
- # A Function with errors
198
- class DivideFunction < Cuprum::Function
199
- def initialize divisor
200
- @divisor = divisor
201
- end # constructor
310
+ ```ruby
311
+ class CreateBookOperation < Cuprum::Operation
312
+ def process
313
+ # Implementation here.
314
+ end # method process
315
+ end # class
202
316
 
203
- private
317
+ # Defining a controller action using an operation.
318
+ def create
319
+ operation = CreateBookOperation.new.call(book_params)
204
320
 
205
- def process int
206
- if @divisor.zero?
207
- errors << 'errors.messages.divide_by_zero'
321
+ if operation.success?
322
+ redirect_to(operation.value)
323
+ else
324
+ @book = operation.value
208
325
 
209
- return
210
- end # if
326
+ render :new
327
+ end # if-else
328
+ end # create
329
+ ```
211
330
 
212
- int / @divisor
213
- end # method process
214
- end # class
331
+ Like a Command, an Operation can be defined directly by passing an implementation block to the constructor or by creating a subclass that overwrites the #process method.
215
332
 
216
- In addition, the result object defines `#success?` and `#failure?` predicates. If the result has no errors, then `#success?` will return true and `#failure?` will return false.
333
+ An operation inherits the `#call` method from Cuprum::Command (see above), and delegates the `#value`, `#errors`, `#success?`, and `#failure` methods to the most recent result. If the operation has not been called, these methods will return default values.
217
334
 
218
- halve_function = DivideFunction.new(2)
219
- result = halve_function.call(10)
335
+ #### The Operation Mixin
220
336
 
221
- result.errors #=> []
222
- result.success? #=> true
223
- result.failure? #=> false
224
- result.value #=> 5
337
+ [Module Documentation](http://www.rubydoc.info/github/sleepingkingstudios/cuprum/master/Cuprum%2FOperation%2FMixin)
225
338
 
226
- If the result does have errors, `#success?` will return false and `#failure?` will return true.
339
+ The implementation of `Cuprum::Operation` is defined by the `Cuprum::Operation::Mixin` module, which provides the methods defined above. Any command class or instance can be converted to an operation by including (for a class) or extending (for an instance) the operation mixin.
227
340
 
228
- function_with_errors = DivideFunction.new(0)
229
- result = function_with_errors.call(10)
341
+ Finally, the result can be halted using the `#halt` method, which indicates that further chained commands should not be executed.
230
342
 
231
- result.errors #=> ['errors.messages.divide_by_zero']
232
- result.success? #=> false
233
- result.failure? #=> true
234
- result.value #=> nil
343
+ ```ruby
344
+ result.halt!
345
+ result.halted? #=> true
346
+ ```
235
347
 
236
- ### Chaining Functions
348
+ ### Built In Commands
237
349
 
238
- Because Cuprum::Function instances are proper objects, they can be composed like any other object. Cuprum::Function also defines methods for chaining functions together. When a chain of functions is called, each function in the chain is called in sequence and passed the value of the previous function. The result of the last function in the chain is returned from the chained call.
350
+ Cuprum includes a small number of predefined commands and their equivalent operations.
239
351
 
240
- class AddFunction < Cuprum::Function
241
- def initialize addend
242
- @addend = addend
243
- end # constructor
352
+ #### IdentityCommand
244
353
 
245
- private
354
+ require 'cuprum/built_in/identity_command'
246
355
 
247
- def process int
248
- int + @addend
249
- end # method process
250
- end # class
356
+ [Class Documentation](http://www.rubydoc.info/github/sleepingkingstudios/cuprum/master/Cuprum%2FBuiltIn%2FIdentityCommand)
251
357
 
252
- double_and_add_one = MultiplyFunction.new(2).chain(AddFunction.new(1))
253
- result = double_and_add_one(5)
358
+ A pregenerated command that returns the value or result with which it was called.
254
359
 
255
- result.value #=> 5
360
+ ```ruby
361
+ command = Cuprum::BuiltIn::IdentityCommand.new
362
+ result = command.call('expected value')
363
+ result.value #=> 'expected value'
364
+ result.success? #=> true
365
+ ```
256
366
 
257
- For finer control over the returned result, `#chain` can instead be called with a block that yields the most recent result. If the block returns a Cuprum::Result, that result is returned or passed to the next function.
367
+ #### IdentityOperation
258
368
 
259
- MultiplyFunction.new(3).
260
- chain { |result| Cuprum::Result.new(result + 1) }.
261
- call(3)
262
- #=> Returns a Cuprum::Result with a value of 10.
369
+ require 'cuprum/built_in/identity_operation'
263
370
 
264
- Otherwise, the block is still called but the previous result is returned or passed to the next function in the chain.
371
+ [Class Documentation](http://www.rubydoc.info/github/sleepingkingstudios/cuprum/master/Cuprum%2FBuiltIn%2FIdentityOperation)
265
372
 
266
- AddFunction.new(2).
267
- chain { |result| puts "There are #{result.value} lights!" }.
268
- call(2)
269
- #=> Writes "There are 4 lights!" to STDOUT.
270
- #=> Returns a Cuprum::Result with a value of 4.
373
+ A pregenerated operation that sets its result to the value or result with which it was called.
271
374
 
272
- #### Conditional Chaining
375
+ ```ruby
376
+ operation = Cuprum::BuiltIn::IdentityOperation.new.call('expected value')
377
+ operation.value #=> 'expected value'
378
+ operation.success? #=> true
379
+ ```
273
380
 
274
- The `#chain` method can be passed an optional `:on` keyword, with values of `:success` and `:failure` accepted. If `#chain` is called with `:on => :success`, then the chained function or block will **only** be called if the previous result `#success?` returns true. Conversely, if `#chain` is called with `:on => :failure`, then the chained function will only be called if the previous result `#failure?` returns true.
381
+ #### NullCommand
275
382
 
276
- In either case, execution will then pass to the next function in the chain, which may itself be called or not if it was conditionally chained. Calling a conditional function chain will return the result of the last called function.
383
+ require 'cuprum/built_in/null_command'
277
384
 
278
- The methods `#then` and `#else` serve as shortcuts for `#chain` with `:on => :success` and `:on => :failure`, respectively.
385
+ [Class Documentation](http://www.rubydoc.info/github/sleepingkingstudios/cuprum/master/Cuprum%2FBuiltIn%2FNullCommand)
279
386
 
280
- class EvenFunction < Cuprum::Function
281
- private
387
+ A pregenerated command that does nothing when called. Accepts any arguments.
282
388
 
283
- def process int
284
- errors << 'errors.messages.not_even' unless int.even?
389
+ ```ruby
390
+ command = Cuprum::BuiltIn::NullCommand.new
391
+ result = command.call
392
+ result.value #=> nil
393
+ result.success? #=> true
394
+ ```
285
395
 
286
- int
287
- end # method process
288
- end # class
396
+ #### NullOperation
289
397
 
290
- # The next step in a Collatz sequence is determined as follows:
291
- # - If the number is even, divide it by 2.
292
- # - If the number is odd, multiply it by 3 and add 1.
293
- collatz_function =
294
- EvenFunction.new.
295
- then(DivideFunction.new(2)).
296
- else(MultiplyFunction.new(3).chain(AddFunction.new(1)))
398
+ require 'cuprum/built_in/null_operation'
297
399
 
298
- result = collatz_function.new(5)
299
- result.value #=> 16
400
+ [Class Documentation](http://www.rubydoc.info/github/sleepingkingstudios/cuprum/master/Cuprum%2FBuiltIn%2FNullOperation)
300
401
 
301
- result = collatz_function.new(16)
302
- result.value #=> 8
402
+ A pregenerated operation that does nothing when called. Accepts any arguments.
303
403
 
304
- #### Halting A Function Chain
404
+ ```ruby
405
+ operation = Cuprum::BuiltIn::NullOperation.new.call
406
+ operation.value #=> nil
407
+ operation.success? #=> true
408
+ ```
305
409
 
306
- If the `#halt` method is called as part of a Function block or `#process` method, the function chain is halted. Any subsequent chained functions will not be called unless they were chained with the `:on => :always` option. This allows you to terminate a Function chain early without having to raise and rescue an exception.
410
+ ## Reference
307
411
 
308
- panic_function =
309
- Cuprum::Function.new do |value|
310
- halt!
412
+ ### Cuprum::BuiltIn::IdentityCommand
311
413
 
312
- value
313
- end # function
414
+ require 'cuprum/built_in/identity_command'
314
415
 
315
- result =
316
- double_function.
317
- then(panic_function).
318
- then(AddFunction.new(1)). #=> This is never executed.
319
- chain(:on => :always) { |count| puts "There are #{count} lights!" }.
320
- call(2)
321
- #=> Writes "There are 4 lights!" to STDOUT.
416
+ [Class Documentation](http://www.rubydoc.info/github/sleepingkingstudios/cuprum/master/Cuprum%2FBuiltIn%2FIdentityCommand)
322
417
 
323
- result.value #= 4
324
- result.halted? #=> true
418
+ Cuprum::BuiltIn::IdentityCommand defines the following methods:
325
419
 
326
- ## Operations
420
+ #### `#call`
327
421
 
328
- require 'cuprum/operation'
422
+ call(value) #=> Cuprum::Result
329
423
 
330
- [Class Documentation](http://www.rubydoc.info/github/sleepingkingstudios/cuprum/master/Cuprum%2FOperation)
424
+ Returns a result, whose `#value` is equal to the given value.
331
425
 
332
- An Operation is like a Function, but with two key differences. First, an Operation retains a reference to the result object from the most recent time the operation was called and delegates the methods defined by `Cuprum::Result` to the most recent result. This allows a called Operation to replace a `Cuprum::Result` in any code that expects or returns a result. Second, the `#call` method returns the operation instance, rather than the result itself.
426
+ ### Cuprum::BuiltIn::IdentityOperation
333
427
 
334
- These two features allow developers to simplify logic around calling and using the results of operations, and reduce the need for boilerplate code (particularly when using an operation as part of an existing framework, such as inside of an asynchronous worker or a Rails controller action).
428
+ require 'cuprum/built_in/identity_operation'
335
429
 
336
- class CreateBookOperation < Cuprum::Operation
337
- def process
338
- # Implementation here.
339
- end # method process
340
- end # class
430
+ [Class Documentation](http://www.rubydoc.info/github/sleepingkingstudios/cuprum/master/Cuprum%2FBuiltIn%2FIdentityOperation)
341
431
 
342
- def create
343
- operation = CreateBookOperation.new.call(book_params)
432
+ Cuprum::BuiltIn::IdentityOperation defines the following methods:
344
433
 
345
- if operation.success?
346
- redirect_to(operation.value)
347
- else
348
- @book = operation.value
434
+ #### `#call`
349
435
 
350
- render :new
351
- end # if-else
352
- end # create
436
+ call(value) #=> Cuprum::BuiltIn::IdentityOperation
353
437
 
354
- Like a Function, an Operation can be defined directly by passing an implementation block to the constructor or by creating a subclass that overwrites the #process method.
438
+ Sets the last result to a new result, whose `#value` is equal to the given value.
355
439
 
356
- An operation inherits the `#call` method from Cuprum::Function (see above), and delegates the `#value`, `#errors`, `#success?`, and `#failure` methods to the most recent result (see below). If the operation has not been called, the operation will return default values.
440
+ ### Cuprum::BuiltIn::NullCommand
357
441
 
358
- ### Methods
442
+ require 'cuprum/built_in/null_command'
359
443
 
360
- A Cuprum::Operation inherits the methods from Cuprum::Function (see above), and defines the following additional methods:
444
+ [Class Documentation](http://www.rubydoc.info/github/sleepingkingstudios/cuprum/master/Cuprum%2FBuiltIn%2FNullCommand)
361
445
 
362
- #### `#result`
446
+ Cuprum::BuiltIn::NullCommand defines the following methods:
363
447
 
364
- result() #=> Cuprum::Result
448
+ #### `#call`
365
449
 
366
- The most recent result, from the previous time `#call` was executed for the operation.
450
+ call(*args, **keywords) { ... } #=> Cuprum::Result
367
451
 
368
- [Method Documentation](http://www.rubydoc.info/github/sleepingkingstudios/cuprum/master/Cuprum/Operation#result-instance_method)
452
+ Returns a result with nil value. Any arguments or keywords are ignored.
369
453
 
370
- #### `#called?`
454
+ ### Cuprum::BuiltIn::NullOperation
371
455
 
372
- called?() #=> true, false
456
+ require 'cuprum/built_in/null_operation'
373
457
 
374
- True if the operation has been called and there is a result available by calling `#result` or one of the delegated methods, otherwise false.
458
+ [Class Documentation](http://www.rubydoc.info/github/sleepingkingstudios/cuprum/master/Cuprum%2FBuiltIn%2FNullOperation)
375
459
 
376
- [Method Documentation](http://www.rubydoc.info/github/sleepingkingstudios/cuprum/master/Cuprum/Operation#called%3F-instance_method)
460
+ Cuprum::BuiltIn::NullOperation defines the following methods:
377
461
 
378
- #### `#reset!`
462
+ #### `#call`
379
463
 
380
- reset!()
464
+ call(*args, **keywords) { ... } #=> Cuprum::BuiltIn::NullOperation
381
465
 
382
- Clears the most recent result and resets `#called?` to false. This frees the result and any linked data for garbage collection. It also clears any internal state from the operation.
466
+ Sets the last result to a result with nil value. Any arguments or keywords are ignored.
383
467
 
384
- [Method Documentation](http://www.rubydoc.info/github/sleepingkingstudios/cuprum/master/Cuprum/Operation#reset!-instance_method)
468
+ ### Cuprum::Command
385
469
 
386
- ### The Operation Mixin
470
+ require 'cuprum'
387
471
 
388
- [Module Documentation](http://www.rubydoc.info/github/sleepingkingstudios/cuprum/master/Cuprum%2FOperation%2FMixin)
472
+ [Class Documentation](http://www.rubydoc.info/github/sleepingkingstudios/cuprum/master/Cuprum%2FCommand)
389
473
 
390
- The implementation of `Cuprum::Operation` is defined by the `Cuprum::Operation::Mixin` module, which provides the methods defined above. Any function class or instance can be converted to an operation by including (for a class) or extending (for an instance) the operation mixin.
474
+ A Cuprum::Command defines the following methods:
391
475
 
392
- ## Results
476
+ #### `#initialize`
393
477
 
394
- [Class Documentation](http://www.rubydoc.info/github/sleepingkingstudios/cuprum/master/Cuprum%2FResult)
478
+ initialize { |*arguments, **keywords, &block| ... } #=> Cuprum::Command
395
479
 
396
- A Cuprum::Result is a data object that encapsulates the result of calling a Cuprum function - the returned value, the success or failure status, and any errors generated by the function.
480
+ Returns a new instance of Cuprum::Command. If a block is given, the `#call` method will wrap the block and set the result `#value` to the return value of the block. This overrides the implementation in `#process`, if any.
397
481
 
398
- value = 'A result value'.freeze
399
- result = Cuprum::Result.new(value)
482
+ [Method Documentation](http://www.rubydoc.info/github/sleepingkingstudios/cuprum/master/Cuprum/Command#initialize-instance_method)
400
483
 
401
- result.value
402
- #=> 'A result value'
484
+ #### `#build_errors`
403
485
 
404
- ### Methods
486
+ *(Private Method)*
405
487
 
406
- A Cuprum::Result defines the following methods:
488
+ build_errors() #=> Array
407
489
 
408
- #### `#value`
490
+ Generates an empty errors object. When the command is called, the result will have its `#errors` property initialized to the value returned by `#build_errors`. By default, this is an array. If you want to use a custom errors object type, override this method in a subclass.
409
491
 
410
- value() #=> Object
492
+ [Method Documentation](http://www.rubydoc.info/github/sleepingkingstudios/cuprum/master/Cuprum/Command#build_errors-instance_method)
411
493
 
412
- The value returned by the function. For example, for an increment function that added 1 to a given integer, the `#value` of the result object would be the incremented integer.
494
+ #### #call
413
495
 
414
- [Method Documentation](http://www.rubydoc.info/github/sleepingkingstudios/cuprum/master/Cuprum/Result#value-instance_method)
496
+ call(*arguments, **keywords) { ... } #=> Cuprum::Result
497
+
498
+ Executes the logic encoded in the constructor block, or the #process method if no block was passed to the constructor.
499
+
500
+ [Method Documentation](http://www.rubydoc.info/github/sleepingkingstudios/cuprum/master/Cuprum/Command#call-instance_method)
501
+
502
+ #### #chain
503
+
504
+ chain(on: nil) { |result| ... } #=> Cuprum::Command
505
+
506
+ Registers a command or block to run after the current command, or after the last chained command if the current command already has one or more chained command(s). This creates and modifies a copy of the current command. See Chaining Commands, below.
507
+
508
+ chain(command, on: nil) #=> Cuprum::Command
509
+
510
+ The command will be passed the `#value` of the previous command result as its parameter, and the result of the chained command will be returned (or passed to the next chained command, if any).
511
+
512
+ The block will be passed the #result of the previous command as its parameter. If your use case depends on the status of the previous command or on any errors generated, use the block form of #chain.
513
+
514
+ If the block returns a Cuprum::Result (or an object responding to #value and #success?), the block result will be returned (or passed to the next chained command, if any). If the block returns any other value (including nil), the #result of the previous command will be returned or passed to the next command.
515
+
516
+ [Method Documentation](http://www.rubydoc.info/github/sleepingkingstudios/cuprum/master/Cuprum/Command#chain-instance_method)
517
+
518
+ #### `#else`
519
+
520
+ else(command) #=> Cuprum::Command
521
+
522
+ Shorthand for `command.chain(:on => :failure)`. Registers a command or block to run after the current command. The chained command will only run if the previous command was unsuccessfully run.
523
+
524
+ The command will be passed the `#value` of the previous command result as its parameter, and the result of the chained command will be returned (or passed to the next chained command, if any).
525
+
526
+ else() { |result| ... } #=> Cuprum::Command
527
+
528
+ The block will be passed the #result of the previous command as its parameter. If your use case depends on the status of the previous command or on any errors generated, use the block form of #chain.
529
+
530
+ If the block returns a Cuprum::Result (or an object responding to #value and #success?), the block result will be returned (or passed to the next chained command, if any). If the block returns any other value (including nil), the #result of the previous command will be returned or passed to the next command.
531
+
532
+ [Method Documentation](http://www.rubydoc.info/github/sleepingkingstudios/cuprum/master/Cuprum/Command#else-instance_method)
415
533
 
416
534
  #### `#errors`
417
535
 
536
+ *(Private Method)*
537
+
418
538
  errors() #=> Array
419
539
 
420
- The errors generated by the function, or an empty array if no errors were generated.
540
+ Only available while the Command is being called. Provides access to the errors object of the generated Cuprum::Result, which is by default an instance of Array.
421
541
 
422
- [Method Documentation](http://www.rubydoc.info/github/sleepingkingstudios/cuprum/master/Cuprum/Result#errors-instance_method)
542
+ Inside of the Command block or the `#process` method, you can add errors to the result.
423
543
 
424
- #### `#success?`
544
+ command =
545
+ Cuprum::Command.new do
546
+ errors << "I'm sorry, something went wrong."
425
547
 
426
- success?() #=> true, false
548
+ nil
549
+ end # command
427
550
 
428
- True if the function did not generate any errors, otherwise false.
551
+ result = command.call
552
+ result.failure?
553
+ #=> true
554
+ result.errors
555
+ #=> ["I'm sorry, something went wrong."]
429
556
 
430
- [Method Documentation](http://www.rubydoc.info/github/sleepingkingstudios/cuprum/master/Cuprum/Result#success%3F-instance_method)
557
+ [Method Documentation](http://www.rubydoc.info/github/sleepingkingstudios/cuprum/master/Cuprum/Command#errors-instance_method)
431
558
 
432
- #### `#failure?`
559
+ #### `#failure!`
433
560
 
434
- failure?() #=> true, false
561
+ failure!() #=> NilClass
435
562
 
436
- True if the function generated one or more errors, otherwise false.
563
+ *(Private Method)*
437
564
 
438
- [Method Documentation](http://www.rubydoc.info/github/sleepingkingstudios/cuprum/master/Cuprum/Result#failure%3F-instance_method)
565
+ Only available while the Command is being called. If called, marks the result object as failing, even if the result does not have errors.
566
+
567
+ [Method Documentation](http://www.rubydoc.info/github/sleepingkingstudios/cuprum/master/Cuprum/Command#failure!-instance_method)
568
+
569
+ #### `#halt!`
570
+
571
+ halt!() #=> NilClass
572
+
573
+ *(Private Method)*
574
+
575
+ Only available while the Command is being called. If called, halts the command chain (see Chaining Commands, below). Subsequent chained commands will not be called unless they were chained with the `:on => :always` option.
576
+
577
+ [Method Documentation](http://www.rubydoc.info/github/sleepingkingstudios/cuprum/master/Cuprum/Command#halt!-instance_method)
578
+
579
+ #### `#success!`
580
+
581
+ success!() #=> NilClass
582
+
583
+ *(Private Method)*
584
+
585
+ Only available while the Command is being called. If called, marks the result object as passing, even if the result has errors.
586
+
587
+ [Method Documentation](http://www.rubydoc.info/github/sleepingkingstudios/cuprum/master/Cuprum/Command#success!-instance_method)
588
+
589
+ #### `#then`
590
+
591
+ then(command) #=> Cuprum::Command
592
+
593
+ Shorthand for `command.chain(:on => :success)`. Registers a command or block to run after the current command. The chained command will only run if the previous command was successfully run.
594
+
595
+ The command will be passed the `#value` of the previous command result as its parameter, and the result of the chained command will be returned (or passed to the next chained command, if any).
596
+
597
+ then() { |result| ... } #=> Cuprum::Command
598
+
599
+ The block will be passed the #result of the previous command as its parameter. If your use case depends on the status of the previous command or on any errors generated, use the block form of #chain.
600
+
601
+ If the block returns a Cuprum::Result (or an object responding to #value and #success?), the block result will be returned (or passed to the next chained command, if any). If the block returns any other value (including nil), the #result of the previous command will be returned or passed to the next command.
602
+
603
+ [Method Documentation](http://www.rubydoc.info/github/sleepingkingstudios/cuprum/master/Cuprum/Command#then-instance_method)
604
+
605
+ ### Cuprum::Operation
606
+
607
+ require 'cuprum'
608
+
609
+ [Class Documentation](http://www.rubydoc.info/github/sleepingkingstudios/cuprum/master/Cuprum%2FOperation)
610
+
611
+ A Cuprum::Operation inherits the methods from Cuprum::Command (see above), and defines the following additional methods:
612
+
613
+ #### `#called?`
614
+
615
+ called?() #=> true, false
616
+
617
+ True if the operation has been called and there is a result available by calling `#result` or one of the delegated methods, otherwise false.
618
+
619
+ [Method Documentation](http://www.rubydoc.info/github/sleepingkingstudios/cuprum/master/Cuprum/Operation#called%3F-instance_method)
620
+
621
+ #### `#reset!`
622
+
623
+ reset!()
624
+
625
+ Clears the most recent result and resets `#called?` to false. This frees the result and any linked data for garbage collection. It also clears any internal state from the operation.
626
+
627
+ [Method Documentation](http://www.rubydoc.info/github/sleepingkingstudios/cuprum/master/Cuprum/Operation#reset!-instance_method)
628
+
629
+ #### `#result`
630
+
631
+ result() #=> Cuprum::Result
632
+
633
+ The most recent result, from the previous time `#call` was executed for the operation.
634
+
635
+ [Method Documentation](http://www.rubydoc.info/github/sleepingkingstudios/cuprum/master/Cuprum/Operation#result-instance_method)
636
+
637
+ ### Cuprum::Result
638
+
639
+ [Class Documentation](http://www.rubydoc.info/github/sleepingkingstudios/cuprum/master/Cuprum%2FResult)
640
+
641
+ A Cuprum::Result defines the following methods:
439
642
 
440
643
  #### `#==`
441
644
 
@@ -449,118 +652,114 @@ Performs a fuzzy comparison with the other object. At a minimum, the other objec
449
652
 
450
653
  Helper method that returns true for a new result. The method returns false if `result.value` is not nil, if `result.errors` is not empty, if the status has been manually set with `#success!` or `#failure!`, or if the result has been halted.
451
654
 
452
- ## Utilities
655
+ #### `#errors`
453
656
 
454
- Cuprum provides these utility modules to grant additional functionality under specific circumstances.
657
+ errors() #=> Array
455
658
 
456
- ### InstanceSpy
659
+ The errors generated by the command, or an empty array if no errors were generated.
457
660
 
458
- require 'cuprum/utils/instance_spy'
661
+ [Method Documentation](http://www.rubydoc.info/github/sleepingkingstudios/cuprum/master/Cuprum/Result#errors-instance_method)
459
662
 
460
- [Class Documentation](http://www.rubydoc.info/github/sleepingkingstudios/cuprum/master/Cuprum%2FUtils%2FInstanceSpy)
663
+ #### `#failure!`
461
664
 
462
- Utility module for instrumenting calls to the #call method of any instance of a function class. This can be used to unobtrusively test the functionality of code that calls a function without providing a reference to the function instance, such as chained functions or methods that create and call a function instance.
665
+ failure!() #=> Cuprum::Result
463
666
 
464
- #### `::spy_on`
667
+ Marks the result as failing and returns the result. Calling `#failure?` will return true, even if the result has no errors.
465
668
 
466
- spy_on(function_class) #=> InstanceSpy
467
- spy_on(function_class) { |spy| ... } #=> nil
669
+ [Method Documentation](http://www.rubydoc.info/github/sleepingkingstudios/cuprum/master/Cuprum/Result#failure!-instance_method)
468
670
 
469
- Finds or creates a spy object for the given module or class. Each time that the #call method is called for an object of the given type, the spy's #call method will be invoked with the same arguments and block. If `#spy_on` is called with a block, the instance spy will be yielded to the block; otherwise, the spy will be returned.
671
+ #### `#failure?`
470
672
 
471
- # Observing calls to instances of a function.
472
- spy = Cuprum::Utils::InstanceSpy.spy_on(CustomFunction)
673
+ failure?() #=> true, false
473
674
 
474
- expect(spy).to receive(:call).with(1, 2, 3, :four => '4')
675
+ True if the command generated one or more errors or was marked as failing. Otherwise false.
475
676
 
476
- CustomFunction.new.call(1, 2, 3, :four => '4')
677
+ [Method Documentation](http://www.rubydoc.info/github/sleepingkingstudios/cuprum/master/Cuprum/Result#failure%3F-instance_method)
477
678
 
478
- # Observing calls to a chained function.
479
- spy = Cuprum::Utils::InstanceSpy.spy_on(ChainedFunction)
679
+ #### `#halt!`
480
680
 
481
- expect(spy).to receive(:call)
681
+ halt!() #=> Cuprum::Result
482
682
 
483
- Cuprum::Function.new {}.
484
- chain { |result| ChainedFunction.new.call(result) }.
485
- call
683
+ Marks the result as halted and returns the result. Calling `#halted?` will return true.
486
684
 
487
- # Block syntax
488
- Cuprum::Utils::InstanceSpy.spy_on(CustomFunction) do |spy|
489
- expect(spy).to receive(:call)
685
+ [Method Documentation](http://www.rubydoc.info/github/sleepingkingstudios/cuprum/master/Cuprum/Result#halt!-instance_method)
490
686
 
491
- CustomFunction.new.call
492
- end # spy_on
687
+ #### `#halted?`
493
688
 
494
- [Method Documentation](http://www.rubydoc.info/github/sleepingkingstudios/cuprum/master/Cuprum/Utils/InstanceSpy#spy_on%3F-instance_method)
689
+ halted?() #=> true, false
495
690
 
496
- #### `::clear_spies`
691
+ True if the result is halted, which prevents chained commands from executing.
497
692
 
498
- clear_spies() #=> nil
693
+ #### `#success!`
499
694
 
500
- Retires all spies. Subsequent calls to the #call method on function instances will not be mirrored to existing spy objects. Calling this method after each test or example that uses an instance spy is recommended.
695
+ success!() #=> Cuprum::Result
501
696
 
502
- after(:example) { Cuprum::Utils::InstanceSpy.clear_spies }
697
+ Marks the result as passing and returns the result. Calling `#success?` will return true, even if the result has errors.
503
698
 
504
- [Method Documentation](http://www.rubydoc.info/github/sleepingkingstudios/cuprum/master/Cuprum/Utils/InstanceSpy#clear_spies%3F-instance_method)
699
+ [Method Documentation](http://www.rubydoc.info/github/sleepingkingstudios/cuprum/master/Cuprum/Result#success!-instance_method)
505
700
 
506
- ## Built In Functions
701
+ #### `#success?`
507
702
 
508
- Cuprum includes a small number of predefined functions and their equivalent operations.
703
+ success?() #=> true, false
509
704
 
510
- ### IdentityFunction
705
+ True if the command did not generate any errors, or the result has errors but was marked as passing. Otherwise false.
511
706
 
512
- require 'cuprum/built_in/identity_function'
707
+ [Method Documentation](http://www.rubydoc.info/github/sleepingkingstudios/cuprum/master/Cuprum/Result#success%3F-instance_method)
513
708
 
514
- [Class Documentation](http://www.rubydoc.info/github/sleepingkingstudios/cuprum/master/Cuprum%2FBuiltIn%2FIdentityFunction)
709
+ #### `#value`
515
710
 
516
- A pregenerated function that returns the value or result with which it was called.
711
+ value() #=> Object
517
712
 
518
- function = Cuprum::BuiltIn::IdentityFunction.new
519
- result = function.call('expected value')
520
- result.value
521
- #=> 'expected value'
522
- result.success?
523
- #=> true
713
+ The value returned by the command. For example, for an increment command that added 1 to a given integer, the `#value` of the result object would be the incremented integer.
524
714
 
525
- ### IdentityOperation
715
+ [Method Documentation](http://www.rubydoc.info/github/sleepingkingstudios/cuprum/master/Cuprum/Result#value-instance_method)
526
716
 
527
- require 'cuprum/built_in/identity_operation'
717
+ ### Cuprum::Utilities::InstanceSpy
528
718
 
529
- [Class Documentation](http://www.rubydoc.info/github/sleepingkingstudios/cuprum/master/Cuprum%2FBuiltIn%2FIdentityOperation)
719
+ require 'cuprum/utils/instance_spy'
530
720
 
531
- A pregenerated operation that sets its result to the value or result with which it was called.
721
+ [Class Documentation](http://www.rubydoc.info/github/sleepingkingstudios/cuprum/master/Cuprum%2FUtils%2FInstanceSpy)
532
722
 
533
- operation = Cuprum::BuiltIn::IdentityFunction.new.call('expected value')
534
- operation.value
535
- #=> 'expected value'
536
- operation.success?
537
- #=> true
723
+ Utility module for instrumenting calls to the #call method of any instance of a command class. This can be used to unobtrusively test the functionality of code that calls a command without providing a reference to the command instance, such as chained commands or methods that create and call a command instance.
538
724
 
539
- ### NullFunction
725
+ #### `::clear_spies`
540
726
 
541
- require 'cuprum/built_in/null_function'
727
+ clear_spies() #=> nil
542
728
 
543
- [Class Documentation](http://www.rubydoc.info/github/sleepingkingstudios/cuprum/master/Cuprum%2FBuiltIn%2FNullFunction)
729
+ Retires all spies. Subsequent calls to the #call method on command instances will not be mirrored to existing spy objects. Calling this method after each test or example that uses an instance spy is recommended.
544
730
 
545
- A pregenerated function that does nothing when called.
731
+ after(:example) { Cuprum::Utils::InstanceSpy.clear_spies }
546
732
 
547
- function = Cuprum::BuiltIn::NullFunction.new
548
- result = function.call
549
- result.value
550
- #=> nil
551
- result.success?
552
- #=> true
733
+ [Method Documentation](http://www.rubydoc.info/github/sleepingkingstudios/cuprum/master/Cuprum/Utils/InstanceSpy#clear_spies%3F-instance_method)
553
734
 
554
- ### NullOperation
735
+ #### `::spy_on`
555
736
 
556
- require 'cuprum/built_in/null_operation'
737
+ spy_on(command_class) #=> InstanceSpy
738
+ spy_on(command_class) { |spy| ... } #=> nil
557
739
 
558
- [Class Documentation](http://www.rubydoc.info/github/sleepingkingstudios/cuprum/master/Cuprum%2FBuiltIn%2FNullFunction)
740
+ Finds or creates a spy object for the given module or class. Each time that the #call method is called for an object of the given type, the spy's #call method will be invoked with the same arguments and block. If `#spy_on` is called with a block, the instance spy will be yielded to the block; otherwise, the spy will be returned.
559
741
 
560
- A pregenerated operation that does nothing when called.
742
+ # Observing calls to instances of a command.
743
+ spy = Cuprum::Utils::InstanceSpy.spy_on(CustomCommand)
561
744
 
562
- operation = Cuprum::BuiltIn::NullOperation.new.call
563
- operation.value
564
- #=> nil
565
- operation.success?
566
- #=> true
745
+ expect(spy).to receive(:call).with(1, 2, 3, :four => '4')
746
+
747
+ CustomCommand.new.call(1, 2, 3, :four => '4')
748
+
749
+ # Observing calls to a chained command.
750
+ spy = Cuprum::Utils::InstanceSpy.spy_on(ChainedCommand)
751
+
752
+ expect(spy).to receive(:call)
753
+
754
+ Cuprum::Command.new {}.
755
+ chain { |result| ChainedCommand.new.call(result) }.
756
+ call
757
+
758
+ # Block syntax
759
+ Cuprum::Utils::InstanceSpy.spy_on(CustomCommand) do |spy|
760
+ expect(spy).to receive(:call)
761
+
762
+ CustomCommand.new.call
763
+ end # spy_on
764
+
765
+ [Method Documentation](http://www.rubydoc.info/github/sleepingkingstudios/cuprum/master/Cuprum/Utils/InstanceSpy#spy_on%3F-instance_method)