aye_commander 1.0.0

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 ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: fa72a9da4e159367e9e08edd1a76360e61f105e7
4
+ data.tar.gz: 3c9cbe60d130049f9d6a16db3eadf6c8df609914
5
+ SHA512:
6
+ metadata.gz: 17422f08ebb5442a8f43f9a00cd48439957d4ba194bc39440ded506f28d44f547a37dba268b0f568063e4b2b6d7cbb61481da8a6f5d104755a03635726318aa7
7
+ data.tar.gz: df4f7f0e63fa53bbf4e99d388253f20089b3f70cd311620e700641baea51a92317038a50837be04d62011e77ce45b1790678d1787e4f1f56c5163d0c1d884cc3
data/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2016 Juan Ramón
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
data/README.adoc ADDED
@@ -0,0 +1,720 @@
1
+ // Asciidoctor Source
2
+ // AyeCommander README
3
+ //
4
+ // Original author:
5
+ // - pyzlnar
6
+ //
7
+ // Notes:
8
+ // Compile with: $ asciidoctor README.adoc
9
+
10
+ = AyeCommander
11
+ [A small command gem]
12
+ :toc:
13
+ :showtitle:
14
+ :source-highlighter: coderay
15
+
16
+ image:https://travis-ci.org/pyzlnar/aye_commander.svg?branch=master["Build Status", link="https://travis-ci.org/pyzlnar/aye_commander"]
17
+ image:https://codeclimate.com/github/pyzlnar/aye_commander/badges/gpa.svg["Code Climate", link="https://codeclimate.com/github/pyzlnar/aye_commander"]
18
+ image:https://codeclimate.com/github/pyzlnar/aye_commander/badges/coverage.svg["Test Coverage", link="https://codeclimate.com/github/pyzlnar/aye_commander/coverage"]
19
+
20
+ == Requirements
21
+
22
+ - Ruby version >= 2.0
23
+
24
+ == Installation
25
+
26
+ To use AyeCommander add it to your Gemfile:
27
+
28
+ [source,ruby]
29
+ gem 'aye_commander'
30
+
31
+ And bundle!
32
+
33
+ [source,ruby]
34
+ bundle install
35
+
36
+ Or to use without bundler, install the gem:
37
+
38
+ [source,ruby]
39
+ gem install aye_commander
40
+
41
+ And then require it from your code
42
+
43
+ [source,ruby]
44
+ require 'aye_commander'
45
+
46
+ == Introduction
47
+
48
+ AyeCommander is a gem that helps to develop classes that follow the command pattern.
49
+
50
+ === What is a Command?
51
+
52
+ [quote, Russ Oslen]
53
+ ____
54
+ A command is an object that does nothing but wait to be executed and, when executed, goes out and
55
+ performs an application-specific task.
56
+ ____
57
+
58
+ Simply put, a command is an object that does but one thing. So if only does one thing... why would
59
+ you need to use them?
60
+
61
+ === When to use a Command
62
+
63
+ Let's imagine that we have to do a complicated operation in a web application, like charging money.
64
+ Just the charging alone might involved consuming one or more services to authorize and charge the
65
+ card, save several records with information about the payments and so on and so forth.
66
+
67
+ Writing all this code in a model is not exactly correct since it handles way more than just one
68
+ model and using a controller would not only make a fat controller, but also harder to read.
69
+
70
+ If we instead write all this logic in one (or more) commands, the code becomes not only easier to
71
+ read and understand, but also easier to reuse on a different context.
72
+
73
+ [source,ruby]
74
+ ----
75
+ # Instead of letting the model handle more responsability than it should
76
+ class Order
77
+ def create_order
78
+ charge_card
79
+ save_payment
80
+ update_order
81
+ end
82
+ end
83
+
84
+ # Or polluting the controller with more than just, "How to respond to the end user?"
85
+ class OrdersController
86
+ def create
87
+ charge_card
88
+ save_payment
89
+ update_order
90
+ flash[:notice] = "Everything went well"
91
+ end
92
+ end
93
+
94
+ # Why not extract it all a command
95
+ class CheckoutOrderCommand
96
+ include AyeCommander::Command
97
+ def call
98
+ charge_card
99
+ save_payment
100
+ update_order
101
+ end
102
+ end
103
+
104
+ # Or maybe even several commands
105
+ class CheckoutOrderCommand
106
+ include AyeCommander::Commander
107
+ execute ChargeCardCommand, SavePaymentCommand, UpdateOrderCommand
108
+ end
109
+ ----
110
+
111
+ === When to NOT use a Command
112
+
113
+ As stated before a command is an object that does one thing. +
114
+ This simple definition may make it tempting to write commands left and right, but never forget that
115
+ you need to https://en.wikipedia.org/wiki/KISS_principle[KISS]. If what you're trying to do is
116
+ simple it doesn't really need be extracted into a command.
117
+
118
+ Ok, lets get cracking!
119
+
120
+ == The Command
121
+
122
+ Creating a command is really easy, you only need to do two things to get rocking:
123
+
124
+ - Include the `AyeCommander::Command` module
125
+ - Define a method named `call`
126
+
127
+ [source,ruby]
128
+ ----
129
+ class ObtainRandomCommand
130
+ include AyeCommander::Command
131
+
132
+ def call
133
+ @random = array.sample
134
+ end
135
+ end
136
+ ----
137
+
138
+ To use the command, you simply call it from somewhere else.
139
+
140
+ [source,ruby]
141
+ ----
142
+ result = ObtainRandomCommand.call(array: [1, 2, 3])
143
+ => #<ObtainRandomCommand::Result @status: success, @array: [1, 2, 3], @random: 3>
144
+
145
+ result.random
146
+ => 3
147
+ ----
148
+
149
+ It really doesn't get simpler than that, but there's actually more to a command than that, so lets
150
+ have a look at the more complicated parts.
151
+
152
+ == Limiting the arguments
153
+
154
+ As you keep working with commands, you may realize that's actually a bit complicated to know what a
155
+ command expects to receive as arguments, what's the minimum necessary it needs to work and which of
156
+ all the variables returned in the result are actually relevant to you.
157
+
158
+ === Receiving Arguments
159
+
160
+ AyeCommander comes with two ways of limiting the arguments that your command needs to be able to
161
+ run: `requires` and `receives`.
162
+
163
+ A `requires` tells the command that it can't run properly without having said arguments so it will
164
+ in fact raise a `MissingRequiredArgumentError` if the command is called without said arguments.
165
+
166
+ A `receives` tells the command that it can *ONLY* run the command with that set of arguments, and
167
+ that receiving any extra is actually an error. In this case if a command receives any surplus, an
168
+ error is raised.
169
+
170
+ Arguments in `requires` are automatically added to `receives`, but no exception error is raised
171
+ unless you actually use a `receives`.
172
+
173
+ All validations can be skipped by sending the `:skip_validations` option when calling the command.
174
+
175
+ === Returning Arguments
176
+
177
+ So now that your command ran, your result might end up with a bunch of variables that you may
178
+ actually not even need. If that's the case then you can use the `returns` method which as you might
179
+ imagine, cleans up the result by just returning the variables that you specified.
180
+
181
+ === Limiters Examples
182
+
183
+ [source,ruby]
184
+ ----
185
+ class SimpleCommand
186
+ include AyeCommander::Command
187
+ end
188
+
189
+ # At this point, our command will receive and return everything and anything.
190
+ SimpleCommand.call(something: :or, other: :var)
191
+ => #<SimpleCommand::Result @status: success, @something: or, @other: var>
192
+
193
+ class SimpleCommand
194
+ requires :these, :two
195
+ end
196
+
197
+ # Now calling the command without :these and :two will raise an error
198
+ SimpleCommand.call
199
+ => AyeCommander::MissingRequiredArgumentError: Missing required arguments: [:these, :two]
200
+
201
+ SimpleCommand.call(these: 1, two: 2)
202
+ => #<SimpleCommand::Result @status: success, @these: 1, @two: 2>
203
+
204
+ # Adding any extras at this point is still ok!
205
+ SimpleCommand.call(these: 1, two: 2, three: 3)
206
+ => #<SimpleCommand::Result @status: success, @these: 1, @two: 2, @three: 3>
207
+
208
+ class SimpleCommand
209
+ receives :four
210
+ end
211
+
212
+ # Now that a receives has been used, any extra arguments sent will raise an error
213
+ SimpleCommand.call(these: 1, two: 2, three: 3)
214
+ => AyeCommander::UnexpectedReceivedArgumentError: Received unexpected arguments: [:three]
215
+
216
+ SimpleCommand.call(these: 1, two: 2, four: 4)
217
+ => #<SimpleCommand::Result @status: success, @these: 1, @two: 2, @four: 4>
218
+
219
+ # Not sending something that is on the receives is ok as well!
220
+ SimpleCommand.call(these: 1, two: 2)
221
+ => #<SimpleCommand::Result @status: success, @these: 1, @two: 2>
222
+
223
+ class SimpleCommand
224
+ returns :sum
225
+
226
+ def call
227
+ @sum = these + two
228
+ end
229
+ end
230
+
231
+ # Finally a returns will help clean up the result at the end!
232
+ SimpleCommand.call(these: 1, two: 2, four: 4)
233
+ => #<SimpleCommand::Result @status: success, @sum: 3>
234
+
235
+ # At any point you can override the receives requires or returns.
236
+
237
+ # Skips receives and requires
238
+ SimpleCommand.call(skip_validations: true)
239
+
240
+ # Skips either
241
+ SimpleCommand.call(skip_validations: :receives)
242
+ SimpleCommand.call(skip_validations: :requires)
243
+
244
+ # Skips result cleanup
245
+ SimpleCommand.call(skip_cleanup: true)
246
+ ----
247
+
248
+ == What's in a status?
249
+
250
+ As you may have noticed by now, every time a command is called a `status` is returned regardless
251
+ of whether or not we cleanup. So what exactly is a status?
252
+
253
+ Well, at its simplest form the status tells us the whether or not the command has succeeded. By
254
+ default a command will be successful, and will fail if you change the status to *ANYTHING* that's
255
+ not `:success`.
256
+
257
+ [source,ruby]
258
+ ----
259
+ class ReactorStatusCommand
260
+ include AyeCommander::Command
261
+
262
+ def call?
263
+ success? # => true
264
+ @status = :meltdown
265
+ success? # => false
266
+ end
267
+ end
268
+
269
+ ReactorStatusCommand.call.failure?
270
+ => true
271
+ ----
272
+
273
+ As a side note you can use the `fail!` method to fail the command at any point.
274
+ [source,ruby]
275
+ ----
276
+ def call
277
+ # These lines are functionally identical
278
+ @status = :failure
279
+ fail!
280
+
281
+ # So are these
282
+ @status = :meltdown
283
+ fail!(:meltdown)
284
+ end
285
+ ----
286
+
287
+ NOTE: Failing a command *WILL NOT* stop the rest of the code from running. (More on that later)
288
+
289
+ === Multiple succeeds
290
+
291
+ Up to this point the status may seem a bit bland... And you may be right!
292
+
293
+ A status can tell you more than just a simple suceed and fail! It can tell you how it succeeded or
294
+ how it failed. Doing this with failures is fairly easy, since anything that's not `:success` is
295
+ considered a failure, but how do you we add more statuses as successes?
296
+
297
+ [source,ruby]
298
+ ----
299
+ class CreateUserTokenCommand
300
+ include AyeCommander::Command
301
+ succeeds_with :previously_created
302
+
303
+ def call
304
+ status # => :success
305
+ if user.token.present?
306
+ @status = :previously_created
307
+ success? # => true
308
+ else
309
+ user.create_random_token
310
+ fail!(:token_not_created) if user.token.blank?
311
+ end
312
+ end
313
+ end
314
+ ----
315
+
316
+ This contrived example hopefully helps you understand when multiple success status can be useful.
317
+ In fact, you can actually even exclude success from the successful status. If you do, the status
318
+ will be initialized as the first in your successful statuses.
319
+
320
+ [source,ruby]
321
+ ----
322
+ class ProcessCommand
323
+ include AyeCommander::Command
324
+ succeeds_with :started, :progress, :complete, exclude_success: true
325
+
326
+ def call
327
+ status # => :started
328
+ do_something
329
+ @status = :progress
330
+ do_something_else
331
+ @status = everything_ok? ? :complete : :failure
332
+ end
333
+ end
334
+ ----
335
+
336
+ == Abort!
337
+
338
+ Now let's imagine that at point in time you want stop running the command. Not necessarily because
339
+ something went wrong, but you don't need to do anything more for the time being. What can you do?
340
+
341
+ Well the most obvious (and possibly more correct) answer is you can use `return` to exit out of the
342
+ flow. However at times you may define other methods in a command you kinda wish to exit from them,
343
+ something you can't do with a return.
344
+
345
+ [source,ruby]
346
+ ----
347
+ def call
348
+ do_something
349
+ # A return may work here
350
+ return if status == :cant_do_next
351
+ end
352
+
353
+ private
354
+
355
+ def do_something
356
+ # But it doesn't work if you want to use it from here instead
357
+ return if status == :cant_do_next
358
+ end
359
+ ----
360
+
361
+ To solve this problem, command has a method named `#abort!`.
362
+ Calling abort will stop the command on it's trails and will immediately return the result. It *WILL
363
+ NOT* change the status so if you need change or fail the status, do it before aborting.
364
+
365
+ [source,ruby]
366
+ ----
367
+ class ProcessCommand
368
+ include AyeCommander::Command
369
+ succeeds_with :processed
370
+
371
+ def call
372
+ do_something
373
+ # These lines will never be called
374
+ do_something_else
375
+ end
376
+
377
+ private
378
+
379
+ def do_something
380
+ if true
381
+ @status = :processed
382
+ abort!
383
+ end
384
+ end
385
+
386
+ def do_something_else
387
+ @status = :something_else
388
+ end
389
+ end
390
+
391
+ ProcessCommand.call
392
+ => #<SimpleCommand::Result @status: processed>
393
+ ----
394
+
395
+ == Getting Hooked
396
+
397
+ A command also comes with your standard set of before, around and after hooks to tweak the command.
398
+ Additionaly commands come bundled with a fourth kind of hook, the aborted hook. The easiest way to
399
+ understand them, it to see the order of execution of a command.
400
+
401
+ [source,ruby]
402
+ ----
403
+ # Rough representation of your typical call command
404
+ def call
405
+ initialize_command
406
+ validate_args
407
+ before_hooks
408
+ around_hooks { call_command }
409
+ after_hooks
410
+ aborted_hooks if aborted
411
+ return_result
412
+ end
413
+ ----
414
+
415
+ Before going deeper into each kind of hook it's worth mentioning the behavior which all hooks share:
416
+
417
+ - All hooks can be declared either using a block, a symbol, a proc or a lambda.
418
+ - Multiple hooks of the same kind can be declared, they will always be run from the first one that
419
+ was declared to the last one.
420
+ - If you need a hook to be run before some that have already been declared, you can use the
421
+ `prepend: true` option.
422
+ - It might be obvious but worth noting that hooks are run in the command instance; as such you have
423
+ access to everything the command has.
424
+
425
+ [source,ruby]
426
+ ----
427
+ # Basic hook order
428
+ before do
429
+ # I run first!
430
+ # If I wanted, I could abort the rest of the command from here!
431
+ end
432
+
433
+ before :my_hook
434
+
435
+ lambda_from_somewhere_else = -> { "I run third!" }
436
+ before lambda_from_somewhere_else
437
+
438
+ private
439
+
440
+ def my_hook
441
+ # I run second
442
+ end
443
+ ----
444
+
445
+ [source,ruby]
446
+ ----
447
+ # More complicated hook behavior
448
+ after :third do
449
+ # fourth
450
+ end
451
+
452
+ after :first, :second, prepend: true
453
+ ----
454
+
455
+ IMPORTANT: Just because there's a lot of liberty with hook order it doesn't mean that its
456
+ recommended to abuse it. Always try to keep the order of your hooks clear, and use `prepend` only
457
+ if you *NEED* to.
458
+
459
+ === Before Hooks
460
+
461
+ The most important thing to note of before hooks is that while indeed they're called before the
462
+ command, they're also called *AFTER* the validations have run. This is important because it does
463
+ mean that you if your command requires any arguments they can't be added through a before hook.
464
+
465
+ While it was possible to make the before hooks run before the validations this decision was taken
466
+ because `requires` and `receives` are meant to be *ARGUMENT* validators. This also means a couple of
467
+ things:
468
+
469
+ - Receives and requires become a way to tell the _users_ of your command how to use it properly
470
+ - When a validator error is raised you always know it's because of the arguments you sent
471
+
472
+ === After Hooks
473
+
474
+ After hooks are the easiest to understand. They run after your command was called, but before the
475
+ result is created, so if you need to tweak your results a bit you can do it in here!
476
+
477
+ === Aborted Hooks
478
+
479
+ As you might imagine, these hooks are only run if you abort the command. Why do we need them in the
480
+ first place? Well as you may remember, calling `abort!` will stop the command on its tracks and
481
+ return the result immediately. This means that if you call `abort!` during `call`, after_hooks
482
+ *WILL NOT* run. For these cases, you might want to use an abort hook instead.
483
+
484
+ === Around Hooks
485
+
486
+ Oh man, around hooks. It seems that every time I see an implementation of around hooks they work in
487
+ a different way, so it's kinda hard to standarize them.
488
+
489
+ Around hooks in a command are sadly no different, as they just try to make sense.
490
+
491
+ First things first, when you use an around hook you must compromise to *ALWAYS* be able to receive
492
+ an object and call it at some point in your method/block. If you don't, your command will never be
493
+ called.
494
+
495
+ Now, when there are multiple around hooks the first one will call the second one and so forth until
496
+ the command is called. This means that before the `call` the code is run in the order the arounds
497
+ were, but after the `call` it is run in the *REVERSE* order.
498
+
499
+ Always keep this in mind.
500
+
501
+ [source,ruby]
502
+ ----
503
+ around do |next_step|
504
+ puts "First before call"
505
+ next_step.call
506
+ puts "First after call"
507
+ end
508
+
509
+ around do |next_step|
510
+ puts "Second before call"
511
+ next_step.call
512
+ puts "Second after call"
513
+ end
514
+
515
+ def call
516
+ puts "Command called"
517
+ end
518
+
519
+ # Would output:
520
+ => First before call
521
+ => Second before call
522
+ => Command called
523
+ => Second after call
524
+ => First after call
525
+ ----
526
+
527
+ == Aye Aye Commander!
528
+
529
+ I've been waiting this whole README to write that.
530
+
531
+ A commander is actually a command which task is to run other commands. There are two ways to do this
532
+ so lets start with the simpler one.
533
+
534
+ === Run and Done
535
+
536
+ Similarly to the command, on its simplest form you only need to do two things to use a commander.
537
+
538
+ - Include `AyeCommander::Commander`, not `AyeCommander::Command`
539
+ - Use `execute` with the `Command` s you want to be runned.
540
+
541
+ Calling the commander will run the commands one by one... and that's pretty much it.
542
+
543
+ [source,ruby]
544
+ ----
545
+ class Palpatine
546
+ include AyeCommander::Commander
547
+ execute HelpRepublic, Order66, BuildEmpire
548
+ end
549
+
550
+ Palpatine.call
551
+ => #<Palpatine::Result @status: success, @executed: [#<HelpRepublic @status: success>, #<Order66 @status: success>, #<BuildEmpire @status: success>]>
552
+ ----
553
+
554
+ ==== Commander Result
555
+
556
+ As you may have noticed, the commander result not only includes a status, but also an array with
557
+ the instances of the command that were run. Handy!
558
+
559
+ The commander result will not only contain this set of variables; at the end it will take all the
560
+ variables that were present on the last executed command. Which brings us to an important point:
561
+ commands run by the commander *ALWAYS* skip both cleanup and receives validations (requires are
562
+ still run).
563
+
564
+ This is done so that the complete set of variable is sent to the next command to be run. If you want
565
+ to cleanup the commander, you must declare its own set of returns.
566
+
567
+ [source,ruby]
568
+ ----
569
+ class BadgerCommand
570
+ include AyeCommander::Command
571
+ returns :badger
572
+ end
573
+
574
+ class TheCommander
575
+ include AyeCommander::Commander
576
+ end
577
+
578
+ # Notice how the command returns is ignored
579
+ TheCommander.call(extra: :params)
580
+ => #<TheCommander::Result @status: success, @executed: [...], @extra: params>
581
+
582
+ class TheCommander
583
+ returns :extra
584
+ end
585
+
586
+ # With returns defined, commander now cleans up the result
587
+ TheCommander.call(extra: :params)
588
+ => #<TheCommander::Result @status: success, @extra: params>
589
+ ----
590
+
591
+ ==== Aborting and Failing
592
+
593
+ So what happens when the command we're running aborts? Absolutely Nothing! Remember that we can
594
+ abort! on success, so a commander doesn't really cares.
595
+
596
+ On the other hand if the command we're running *fails* the commander itself will fail and abort.
597
+
598
+ [source,ruby]
599
+ ----
600
+ class Palpatine
601
+ include AyeCommander::Commander
602
+ execute HelpRepublic, Order66, BuildEmpire
603
+ end
604
+
605
+ # If Order66 were to fail
606
+ Palpatine.call
607
+ => #<Palpatine::Result @status: failure, @executed: [#<HelpRepublic @status: success>, #<Order66 @status: jedi_escaped>]>
608
+ ----
609
+
610
+ === When we need more tweaking
611
+
612
+ Now, while executing several commands in a row is nice, sometimes you need a bit more of control on
613
+ when to run command A or B.
614
+
615
+ Don't worry, AyeCommander has you covered!
616
+ The only thing you need to do is define your own call method!
617
+
618
+ [source,ruby]
619
+ ----
620
+ class PickyCommander
621
+ include AyeCommander::Commander
622
+
623
+ def call
624
+ execute FirstCommand
625
+
626
+ if command.failure?
627
+ execute ThisCommand, ThatCommand
628
+ else
629
+ execute AnotherCommand
630
+ end
631
+ end
632
+ end
633
+ ----
634
+
635
+ There are a couple of things that we must notice here.
636
+
637
+ First of all, the `command` instance variable. This variable will always have the last command that
638
+ was executed. If no command has been run yet, it will have an anonymous command instance to which
639
+ you can add extras for the following commands to run.
640
+
641
+ [source,ruby]
642
+ ----
643
+ before do
644
+ command.extra_arg = 'This extra arg'
645
+ end
646
+
647
+ after do
648
+ command.some_other = 'This' if command.that.blank?
649
+ end
650
+
651
+ def call
652
+ # Command instance will have extra_arg available
653
+ execute Command
654
+ # Commander Result will have some_other if that is blank after running Command
655
+ end
656
+ ----
657
+
658
+ IMPORTANT: The `command` variable is available for *BOTH* kinds of commanders, so you can use it to
659
+ prepare and finalize your commander. This marks the biggest difference between a `Commander` and a
660
+ `Command`. While everything in a command operates on it's own instance, a commander operates over
661
+ the instance of the commands it executes.
662
+
663
+ The second thing to notice is that as opposed to their simple counterpart, the commander *DOES NOT*
664
+ abort nor fail when one of the commands you run fails. This is done so you can tweak the behavior
665
+ of the commander to your necessities, however recognizing that it is quite likely that you want
666
+ that behaviour for your commander there are ways to reenable it.
667
+
668
+ [source,ruby]
669
+ ----
670
+ class UndecisiveCommander
671
+ include AyeCommander::Commander
672
+
673
+ # Using this will re-enable failing on all commands
674
+ abort_on_failure
675
+
676
+ def call
677
+ # But even with that option, you override it at an instance level
678
+
679
+ # Will always abort on failure
680
+ execute ThisCommand, abort_on_failure: true
681
+
682
+ # Will never abort on failure
683
+ execute ThatCommand, OtherCommand, abort_on_failure: false
684
+ end
685
+ end
686
+ ----
687
+
688
+ == Top tips and tricks
689
+
690
+ - Never forget when and when not to use a command
691
+
692
+ - Have naming conventions +
693
+ I really suggest that for commands (and commanders), you finish their names with `Command`. This
694
+ clears up what they are and maybe what they do just by looking at the name.
695
+
696
+ - Use private methods to know what your command does at first glance +
697
+
698
+ [source,ruby]
699
+ ----
700
+ class UpdateExchangeRatesCommand
701
+ include AyeCommander::Command
702
+
703
+ def call
704
+ fetch_todays_exchange_rates
705
+ save_exchange_rates
706
+ end
707
+ end
708
+ ----
709
+
710
+ - But if the logic is too complicated, split it into more commands
711
+
712
+ [source,ruby]
713
+ ----
714
+ class UpdateExchangeRatesCommand
715
+ include AyeCommander::Commander
716
+ execute FetchExchangeRatesCommand, SaveExchangeRates
717
+ end
718
+ ----
719
+
720
+ - Write code, have fun!