aye_commander 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
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!