rake-commander 0.1.4 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (53) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +8 -0
  3. data/.rubocop.yml +12 -8
  4. data/CHANGELOG.md +69 -4
  5. data/LICENSE +21 -0
  6. data/README.md +94 -2
  7. data/Rakefile +11 -13
  8. data/examples/01_basic_example.rb +28 -0
  9. data/examples/02_a_chainer_example.rb +66 -0
  10. data/examples/02_a_chainer_options_set.rb +8 -0
  11. data/examples/02_b_chained_example.rb +13 -0
  12. data/examples/03_a_chainer_plus_example.rb +34 -0
  13. data/examples/03_b_chained_plus_example.rb +17 -0
  14. data/examples/Examples.rake +7 -0
  15. data/examples/README.md +79 -0
  16. data/examples/libs/shell_helpers.rb +81 -0
  17. data/lib/rake-commander/base/class_auto_loader.rb +45 -7
  18. data/lib/rake-commander/base/class_helpers.rb +16 -61
  19. data/lib/rake-commander/base/class_inheritable.rb +122 -0
  20. data/lib/rake-commander/base/custom_error.rb +52 -0
  21. data/lib/rake-commander/base/object_helpers.rb +42 -0
  22. data/lib/rake-commander/base.rb +16 -2
  23. data/lib/rake-commander/option.rb +115 -25
  24. data/lib/rake-commander/options/arguments.rb +206 -94
  25. data/lib/rake-commander/options/description.rb +17 -0
  26. data/lib/rake-commander/options/error/base.rb +86 -0
  27. data/lib/rake-commander/options/error/handling.rb +106 -0
  28. data/lib/rake-commander/options/error/invalid_argument.rb +21 -0
  29. data/lib/rake-commander/options/error/invalid_option.rb +9 -0
  30. data/lib/rake-commander/options/error/missing_argument.rb +10 -0
  31. data/lib/rake-commander/options/error/missing_option.rb +48 -0
  32. data/lib/rake-commander/options/error/unknown_argument.rb +32 -0
  33. data/lib/rake-commander/options/error.rb +75 -10
  34. data/lib/rake-commander/options/name.rb +67 -23
  35. data/lib/rake-commander/options/result.rb +107 -0
  36. data/lib/rake-commander/options/set.rb +7 -1
  37. data/lib/rake-commander/options.rb +175 -98
  38. data/lib/rake-commander/patcher/README.md +79 -0
  39. data/lib/rake-commander/patcher/application/run_method.rb +46 -0
  40. data/lib/rake-commander/patcher/application/top_level_method.rb +74 -0
  41. data/lib/rake-commander/patcher/application.rb +16 -0
  42. data/lib/rake-commander/patcher/base.rb +45 -0
  43. data/lib/rake-commander/patcher/debug.rb +32 -0
  44. data/lib/rake-commander/patcher/helpers.rb +44 -0
  45. data/lib/rake-commander/patcher.rb +26 -0
  46. data/lib/rake-commander/rake_context/wrapper.rb +2 -0
  47. data/lib/rake-commander/rake_task.rb +49 -54
  48. data/lib/rake-commander/version.rb +1 -1
  49. data/lib/rake-commander.rb +4 -0
  50. data/rake-commander.gemspec +4 -1
  51. metadata +74 -6
  52. data/examples/basic.rb +0 -30
  53. data/lib/rake-commander/options/error_rely.rb +0 -58
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 72eaeab3395a3cb86ee6740a4dc90669f673af8ee30f6ba6dc40572a245d3b88
4
- data.tar.gz: c75023ec7f58039781627a052e870982af0e546beefc91d2e8a54df2e58383e9
3
+ metadata.gz: ce1bfcde79ef608b7e13c440c626ee3ace47ec5adf3088e6fc969bf85502cc59
4
+ data.tar.gz: f785888cff61578247dfb1fdfed6c4e44fd076180d8be0dd1e00bf14ac0f1700
5
5
  SHA512:
6
- metadata.gz: 42e24e489d42e44de71162a4faee64dba5f72555afcfcb0742a6506c238f6f0ea480f0185f926a4f65f418005b3d30eb185d3311ca4aacd89e53eea6c31577ad
7
- data.tar.gz: f8a70b443fbfd5717595d99faf4c12620a4adcb5f4cd9e4f01259622046de923fef3bc01db75382a17c272b7dbf5738163584f10cd8607aea50c3b92e8efbcca
6
+ metadata.gz: 5d998c21f52d117ff59e5eb066a43cd0c5a0939a4187af2c5ee1dc3cbaead328b65d71ad67e333adf426ab465926fd56fc5830e4e7a3a7b048602f02dea39344
7
+ data.tar.gz: 2afaaa3bb046e6de18b13fe4b0e762dead4ace9a14b3d097c2b6c37142e7e0e0d111f5eecd9846647e50a64f13cbf8491b291d7572fafaada91cdc408400d064
data/.gitignore CHANGED
@@ -1,3 +1,9 @@
1
+ # env files
2
+ .env*
3
+
4
+ # core development-only executables
5
+ /bin/raked*
6
+
1
7
  # it's a gem, ignore the lockfile
2
8
  Gemfile.lock
3
9
 
@@ -18,3 +24,5 @@ Gemfile.lock
18
24
  # rspec failure tracking
19
25
  .rspec_status
20
26
  scratch.rb
27
+
28
+ /.byebug_history
data/.rubocop.yml CHANGED
@@ -22,14 +22,16 @@ Metrics/ModuleLength:
22
22
  Metrics/AbcSize:
23
23
  Max: 50
24
24
  Metrics/CyclomaticComplexity:
25
- Max: 15
25
+ Max: 10
26
26
  Metrics/PerceivedComplexity:
27
- Max: 15
27
+ Max: 10
28
28
 
29
29
  ParameterLists:
30
30
  Max: 5
31
31
  CountKeywordArgs: false
32
32
 
33
+ Style/Alias:
34
+ EnforcedStyle: prefer_alias_method
33
35
  Style/StringLiterals:
34
36
  Enabled: false
35
37
  Style/FrozenStringLiteralComment:
@@ -51,12 +53,8 @@ Style/ClassAndModuleChildren:
51
53
  Style/OptionalBooleanParameter:
52
54
  Enabled: false
53
55
 
54
- Layout/SpaceInsideHashLiteralBraces:
55
- Enabled: false
56
- Layout/SpaceInsideBlockBraces:
57
- Enabled: false
58
- Layout/SpaceAroundOperators:
59
- Enabled: false
56
+ Layout/HashAlignment:
57
+ EnforcedColonStyle: table
60
58
  Layout/ExtraSpacing:
61
59
  AllowForAlignment: true
62
60
  Layout/AccessModifierIndentation:
@@ -65,6 +63,12 @@ Layout/DotPosition:
65
63
  EnforcedStyle: trailing
66
64
  Layout/MultilineMethodCallIndentation:
67
65
  EnforcedStyle: indented
66
+ Layout/SpaceInsideHashLiteralBraces:
67
+ Enabled: false
68
+ Layout/SpaceInsideBlockBraces:
69
+ Enabled: false
70
+ Layout/SpaceAroundOperators:
71
+ Enabled: false
68
72
  Layout/FirstHashElementIndentation:
69
73
  Enabled: false
70
74
  Layout/EmptyLineAfterGuardClause:
data/CHANGELOG.md CHANGED
@@ -2,16 +2,81 @@
2
2
  All notable changes to this project will be documented in this file.
3
3
 
4
4
  ## TO DO
5
- - Specify what options are REQUIRED in help lines.
6
- - Rake task parameters (see: https://stackoverflow.com/a/825832/4352306)
7
- - `OptionParser#parse` result (what to do with unknown ARGV `leftovers`)
5
+ - Option results
6
+ - Include the symbol name keys (configurable). Note that dash will be replaced by underscore.
7
+ - Type Coertions
8
+ - Add a way to define/redefine a set and use them.
9
+ - Add more supported type_coertions as native to the gem (i.e. `Symbol`)
10
+ - Add support [for `ActiveRecord::Enum`](https://apidock.com/rails/ActiveRecord/Enum)
11
+ - Option definitions
12
+ - Order: `where: [:tail, :top]` and `[after, before]: :option_name`
13
+ - Configuration: allow to define option override behaviour and whether it should trigger an exception
14
+ - Error handlers
15
+ - See if it would be possible to parse all the valid options so we get all the valid results before some error is raised. Although this can be achieved with `OptionParser#order!`, it destroys switches; which would require to give it two parsing shots whenever there is an error.
16
+ - It should be ensured that the parsed options results object is remains the same.
17
+ - Think about options that modify other options. Technically this should be hold only at behaviour level
18
+ - This would allow to make the order of the options irrelevant when it comes to modify error handling behaviour via options themselves.
19
+ - Rake task parameters (see: https://stackoverflow.com/a/825832/4352306 & https://stackoverflow.com/a/825832/4352306)
20
+ - Add `enhance` functionality (when a task is invoked it runs before it; declared with `task` as well)
21
+ - Add `no_short` option (which should give the result of that option with the option name key)
22
+ - Add `on_option` handler at instance level, so within a `task` definition, we can decide certain things, such as if the functionality should be active when the `self.class` does not have that option.
23
+ * This is specially useful to be able to extend through inheritance chain, where we extend `task` (rather than redefining it), but we don't want options we removed (with `option_remove`) to throw unexpected results.
24
+ * Example: `on_option(:t, defined: true) {|option| do-stuff}` <- block to be called only if the option is defined in the class (alternative: `defined: :only`)
25
+ * Example: `on_option(:t, defined: false) {|option| do-stuff}` <- block to be called regardless the option exists (alternative: `defined: :ignore`)
26
+ * Example: `on_options(:t, :s, present: true) {|options| do-stuff}` <- block to be called only when the option `:t` and `:s` are both present in the parsed `options` result.
27
+ - Once this has been done, think about it being a hash-alike object with methods for the option names (i.e. `options.debug?`)
8
28
 
9
- ## [0.1.5] - 2023-04-xx
29
+ ## DISCARDED IMPROVENTS
30
+ - Option to globally enable/disable the 2nd patch?
31
+ * That would make this gem completely useless.
32
+
33
+ ## [0.2.1] - 2023-04-xx
10
34
 
11
35
  ### Added
12
36
  ### Fixed
13
37
  ### Changed
14
38
 
39
+
40
+ ## [0.2.0] - 2023-04-28
41
+
42
+ ### Added
43
+ - Better support for `RakeCommander::Options::Set`
44
+ - `RakeCommander::Options::options_use` accepts it as a parameter.
45
+ - Added `override` parameter to specify if this should override clashed option names.
46
+ - `RakeCommander::Options::class_resolver` to define the `RakeCommander::Option` class.
47
+ - Serves the purpose to ease class extension through inheritance.
48
+ - Ability to reopen options without changing the order.
49
+ - `RakeCommander::Options::option_reopen` which upserts (adds if not existing).
50
+ - New parameter `reopen` to `RakeCommander::Options::option` (redirects to the above method).
51
+ - Automatic option shortcuts (`implicit_shorts`) show in help and only when applicable.
52
+ - These are added automatically by `OptionParser`
53
+ - `OptionParser` leftovers trigger an error by default.
54
+ - This behaviour can be disabled or modified via callback/block by using `RakeCommander::Options:Error::error_on_leftovers`.
55
+ - Description auto **multi-line**
56
+ - Currently based on `RakeCommander::Options::Description::DESC_MAX_LENGTH`
57
+ - `RakeCommander::Options#remove_option`
58
+ - `RakeCommander::Options::Error::Base` and children can be raised using different methods (see `RakeCommander::Options::Error` for examples).
59
+ - The **task** `name` that raised the error is included in the message.
60
+ - `RakeCommander::Options::Error::Handling` which provides **configurability** around actions on specific option errors with a default to a general options parsing error.
61
+
62
+ ### Fixed
63
+ - `RakeCommander::Base::ClassAutoLoader`
64
+ - Register excluded child classes (singleton classes) into their own property,
65
+ rather than in the `autoloaded_children` (they are not autoloaded).
66
+ - Missing `rake` dependency in gemspec file.
67
+ - Boolean switch detection (pre-parse arguments) and parsing
68
+ - It adds support for boolean option names such as `--[no-]verbose`
69
+ - Error messaging. There were missing cases, specially with implicit short options.
70
+ - `RakeCommander::Options`
71
+ - `#option_reopen` fixed
72
+ - **Inheritance fixed**
73
+
74
+ ### Changed
75
+ - Development: examples invokation via `Rake`
76
+ - Refactored `RakeCommander::Options` to acquire functionality through extension.
77
+ - `attr_inheritable` and `inheritable_class_var` are the new names of previous methods
78
+ - Behaviour has been changed, so you define if it should `dup` the variables, and you can pass a `block` to do the `dup` yourself. They won **NOT** `freeze` anymore, as we are mostly working at class level.
79
+
15
80
  ## [0.1.4] - 2023-04-20
16
81
 
17
82
  ### Fixed
data/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2023 Oscar Segura
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.md CHANGED
@@ -1,6 +1,20 @@
1
1
  # RakeCommander
2
2
 
3
- Another way to define re-usable rake tasks and samples.
3
+ Classing rake tasks with options. Creating re-usable tasks, options and samples thereof.
4
+
5
+ ## Introduction
6
+
7
+ Rake commander is a way to declare **rake tasks** with re-usable classes. It enhances the command line syntax, as tasks can come with their own **options**, inherit them, re-use declared options sets, modify/re-open or even remove them.
8
+
9
+ Although the `OptionParser` ruby native class is used for parsing the options, the declaration of options, additionally to the ones of `OptionParser` comes with some **opinionated improvements** and amendments:
10
+
11
+ 1. It is possible to declare options as `required`
12
+ * This is additional to required option arguments.
13
+ * Options are inheritable (they get a custom `deep_dup`)
14
+ 2. An option can have a `default` value.
15
+ * Which can optionally be automatically used when the option accepts or requires an argument.
16
+ 3. Options parsing raises specific option errors. For a given task/class, each error type can have its own handler or preferred action.
17
+ * Defined error handling is inheritable and can be redefined.
4
18
 
5
19
  ## Installation
6
20
 
@@ -20,9 +34,87 @@ Or install it yourself as:
20
34
 
21
35
  ## Usage
22
36
 
37
+ See [**the `examples`**](https://github.com/rellampec/rake-commander/tree/main/examples).
38
+
39
+ ```
40
+ rake -T examples
41
+ ```
42
+
43
+ Go through the [basic example](https://github.com/rellampec/rake-commander/blob/main/examples/01_basic_example.rb).
44
+
45
+ ```
46
+ rake examples:basic -- -h
47
+ rake examples:basic -- -z -e prod
48
+ ```
49
+ * The double dash `--` is used to tell to rake-commander where the options section starts.
50
+
51
+ At the same time the double dash delimiter seems to make rake ignore anything that comes afterwards. Without loading rake commander, you could try:
52
+
53
+ ```
54
+ $ rake --trace rspec
55
+ ** Invoke spec (first_time)
56
+ ** Execute spec
57
+ rspec logging and results
58
+ ```
59
+
60
+ And then re-try with
61
+
62
+ ```
63
+ $ rake rspec -- --trace
64
+ rspec logging and results
65
+ ```
66
+
67
+ * The `--trace` option is being natively ignored by `rake` due to the preceding double dash (` -- `).
68
+
69
+
70
+ ### Syntax
71
+
72
+ ### Declaring and using Task Options
73
+
74
+ It supports most of options syntax of the native `OptionParser` but for a couple of exceptions perhaps:
75
+ 1. It does **NOT** support definitions or parsing of shortcuts with **embedded argument** (i.e. `-nNAME`).
76
+ 2. It does **NOT** support definitions that include equal sign (i.e. `name=NAME`, `n=NAME`)
77
+
78
+ An argument should be explicitly declared in the `name` part:
79
+
80
+ ```
81
+ option :n, '--name NAME'
82
+ ```
83
+
84
+ ### Command Line
85
+
86
+ Although it is planned to extend the syntax, the current version shares the options through all tasks (declared as `RakeCommander` classes) that are invoked in the same command line.
87
+
88
+ ```
89
+ rake [rake-options] task1 task2 -- [shared-task-options]
90
+ ```
91
+
92
+ The double dash ` -- ` delimiter allows to modify the `ARGV` parsing behaviour of `rake`, giving room for **opinionated enhanced syntax**. Anything that comes before the double dash is feed to standard `rake`, and anything after `--` are parsed as option tasks via `rake commander`.
93
+
94
+ ```
95
+ <rake part> -- [tasks options part]
96
+ ```
97
+
98
+ ## `rake` full support
99
+
100
+ Work has been done with the aim of providing a full patch on `rake`, provided that the main invocation command remains as `rake`.
101
+
102
+ To preserve `rake` as invocation command, though, the patch needs to relaunch the rake application when it has already started. The reason is that `rake` has already pre-parsed `ARGV` when `rake-commander` is loaded (i.e. from a `Rakefile`) and has identified as tasks things that are part of the task options.
103
+
104
+ * For compatibility with tasks declared using `RakeCommander`, the rake application is always relaunched. Anything that does not belong to task options should not be feed to rake tasks declared with rake commander classes.
105
+
106
+ ### Patching `Rake`
107
+
108
+ The two patches:
109
+
110
+ 1. Rake commander does come with a neat patch to the [`Rake::Application#run` method](https://github.com/ruby/rake/blob/48e798484babf725b0562cc417986da513e5d0ae/lib/rake/application.rb#L79) to clean up the `ARGV` before the rake application starts. But it kicks in too late...
111
+ 2. For this reason a more arguable patch has been applied to [`Rake::Application#top_level` method](https://github.com/ruby/rake/blob/48e798484babf725b0562cc417986da513e5d0ae/lib/rake/application.rb#L131), where the rake application is relaunched.
112
+
113
+ For further details please see [`RakeCommander::Patcher`](https://github.com/rellampec/rake-commander/blob/main/lib/rake-commander/patcher).
114
+
23
115
 
24
116
  ## Development
25
117
 
26
118
  To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
27
119
 
28
- For more info on available `Rake` tasks: `rake -T`
120
+ For more info on available `Rake` tasks: `rake -T` (or `bin/raked -T`)
data/Rakefile CHANGED
@@ -1,32 +1,30 @@
1
- require "bundler/gem_tasks"
2
- require "rspec/core/rake_task"
3
- require "yard"
4
- require "redcarpet"
1
+ require 'bundler/gem_tasks'
2
+ require 'rspec/core/rake_task'
3
+ require 'yard'
4
+ require 'redcarpet'
5
5
 
6
- desc "run the specs"
6
+ # Install examples and load `rake-commander`
7
+ Rake.add_rakelib 'examples'
8
+
9
+ desc 'run the specs'
7
10
  RSpec::Core::RakeTask.new(:spec)
8
11
 
9
- desc "run rspec showing backtrace"
12
+ desc 'run rspec showing backtrace'
10
13
  RSpec::Core::RakeTask.new(:spec_trace) do |task|
11
14
  task.rspec_opts = ['--backtrace']
12
15
  end
13
16
 
14
- desc "run rspec stopping on first fail, and show backtrace"
17
+ desc 'run rspec stopping on first fail, and show backtrace'
15
18
  RSpec::Core::RakeTask.new(:spec_fast) do |task|
16
19
  task.rspec_opts = ['--fail-fast', '--backtrace']
17
20
  end
18
21
 
19
22
  # default task name is yard
20
- desc "Yard: generate all the documentation"
23
+ desc 'Yard: generate all the documentation'
21
24
  YARD::Rake::YardocTask.new(:doc) do |t|
22
25
  #t.files = ['lib/**/*.rb']
23
26
  end
24
27
 
25
- desc "Examples: Run examples (rake examples[basic] -- -h)"
26
- task :examples, [:sample] do |_t, args|
27
- require_relative "examples/#{args[:sample]}"
28
- end
29
-
30
28
  task default: [:spec]
31
29
  task rspec_trace: :spec_trace
32
30
  task rspec_fast: :spec_fast
@@ -0,0 +1,28 @@
1
+ class RakeCommander::Custom::BasicExample < RakeCommander
2
+ namespace :examples
3
+
4
+ desc 'A simple example to get started'
5
+ task :basic
6
+
7
+ #banner "Usage: basic:example -- [options]"
8
+ option '-w', :show_time, TrueClass, desc: 'Displays the local time'
9
+ option :z, '--timezone', TrueClass, default: false, required: true
10
+ option :o, '--hello NAME', String, desc: 'It greets.'
11
+ option '-s', '--say [SOMETHING]', "It says 'something'", default: %q(I don't know what to "say"...)
12
+ option :d, '--folder NAME', default: '.', desc: 'Source local folder', required: true
13
+ option '-e', :'--enviro ENV', 'The target environment to run this task', required: true
14
+ option :v, :debug, TrueClass, 'Shows the parsed options'
15
+ option :V, '[no-]verbose', 'Verbosity', TrueClass
16
+ #option :f, :folder, required: false, reopen: true
17
+
18
+ def task(*_args)
19
+ puts "Hello #{options[:o]}!!" if options[:o]
20
+ if options[:v]
21
+ puts 'We got these options:'
22
+ pp options
23
+ end
24
+ puts Time.now.strftime('%d %b at %H:%M') if options[:w]
25
+ puts Time.now.zone if options[:z]
26
+ puts options[:s] if options.key?(:s)
27
+ end
28
+ end
@@ -0,0 +1,66 @@
1
+ require_relative 'libs/shell_helpers'
2
+ require_relative '02_a_chainer_options_set'
3
+
4
+ class RakeCommander::Custom::Chainer < RakeCommander
5
+ include Examples::Libs::ShellHelpers
6
+ TARGET_TASK = 'examples:chained'.freeze
7
+
8
+ namespace :examples
9
+
10
+ task :chainer
11
+ desc "Uses rake (or raked) to invoke #{TARGET_TASK}"
12
+
13
+ # When an option as a default value defined, it is added to `options` result
14
+ # even when the option was not invoked
15
+ options_with_defaults true
16
+
17
+ # Loads the otions from a pre-defined options set
18
+ options_use RakeCommander::Custom::ChainerOptionsSet
19
+ # Redefines the description of the option `:chain`
20
+ option_reopen :chain, desc: "Calls: '< rake|raked > #{TARGET_TASK} task'"
21
+ # Adds some option of its own
22
+ str_desc = "The method used to shell the call to examples:chained."
23
+ str_desc << " Options: #{SHELL_METHODS.join(', ')}"
24
+ option '-m', 'method [METHOD]', default: 'system', desc: str_desc
25
+
26
+ def task(*_args)
27
+ if options[:c]
28
+ cmd = "#{subcommand_base} -- #{subcommand_arguments.join(' ')}"
29
+ puts "Calling --> '#{cmd}'"
30
+ shell(cmd, method: options[:m])
31
+ else
32
+ puts "Nothing to do :|"
33
+ end
34
+ end
35
+
36
+ def subcommand_base
37
+ with = options[:w] == 'raked' ? 'bin\raked' : 'rake'
38
+ "#{with} #{self.class::TARGET_TASK}"
39
+ end
40
+
41
+ def subcommand_arguments
42
+ [].tap do |args|
43
+ if options.key?(:s)
44
+ str_opt = "--say"
45
+ str_opt << " \"#{options[:s]}\"" if options[:s]
46
+ args << str_opt
47
+ end
48
+ args << "--debug" if options[:b]
49
+ end
50
+ end
51
+
52
+ private
53
+
54
+ def puts(str)
55
+ return super unless options[:b]
56
+ super "#{app_id} #{str} #{thread_id}"
57
+ end
58
+
59
+ def app_id
60
+ "(#{self.class.task_fullname}: #{Rake.application.object_id})"
61
+ end
62
+
63
+ def thread_id
64
+ "(PID: #{Process.pid} ++ Thread: #{Thread.current.object_id})"
65
+ end
66
+ end
@@ -0,0 +1,8 @@
1
+ class RakeCommander::Custom::ChainerOptionsSet < RakeCommander::Options::Set
2
+ name :chainer_options
3
+
4
+ option :c, :chain, TrueClass, desc: "Calls: '< rake|raked > chained-task task'"
5
+ option :w, '--with CALLER', default: 'rake', desc: "Specifies if should invoke with 'rake' or 'raked'"
6
+ option '-s', "It makes the chained-task say 'something'", name: '--say [SOMETHING]'
7
+ option '-b', '--debug', TrueClass, 'Whether to add additional context information to messages'
8
+ end
@@ -0,0 +1,13 @@
1
+ class RakeCommander::Custom::Chained < RakeCommander::Custom::Chainer
2
+ #namespace :examples # <-- inherited
3
+ desc 'A task you want to chain to'
4
+ task :chained
5
+
6
+ option_reopen :s, "It says 'something'", default: %q(I don't know what to "say"...)
7
+ option_remove :c, :w, :method
8
+
9
+ def task(*_args)
10
+ puts "Called !!"
11
+ puts options[:s] if options[:s]
12
+ end
13
+ end
@@ -0,0 +1,34 @@
1
+ class RakeCommander::Custom::ChainerPlus < RakeCommander::Custom::Chainer
2
+ TARGET_TASK = 'examples:chained_plus'.freeze
3
+
4
+ desc "Uses rake (or raked) to invoke #{TARGET_TASK}"
5
+ task :chainer_plus
6
+
7
+ # Disable using defaults when options are not invoked.
8
+ options_with_defaults false
9
+
10
+ # Update option description
11
+ option_reopen :chain, desc: "Calls: '< rake|raked > #{TARGET_TASK} task'"
12
+ # Extend with new options
13
+ option :e, '--exit-on-error', TrueClass, \
14
+ desc: "Whether #{TARGET_TASK} should just exit on 'missing argument' error (or raise an exception)"
15
+ option :o, '--hello NAME', String, desc: 'It greets.'
16
+
17
+ # Make it default to `exit 1` when there are errors
18
+ error_on_options false
19
+ # Let it trigger/raise the error when an unknown option is used!
20
+ error_on_options true, error: RakeCommander::Options::Error::InvalidArgument
21
+
22
+ def task(*_args)
23
+ puts "Hello #{options[:o]}!!" if options[:o]
24
+ options[:m] = :system unless options[:m]
25
+ super
26
+ end
27
+
28
+ # We add the extended arguments at the beginning
29
+ def subcommand_arguments
30
+ [].tap do |args|
31
+ args << '--exit-on-error' if options[:e]
32
+ end.concat(super)
33
+ end
34
+ end
@@ -0,0 +1,17 @@
1
+ require 'pp'
2
+ class RakeCommander::Custom::ChainedPlus < RakeCommander::Custom::Chained
3
+ desc 'A task+ you want to chain to'
4
+ task :chained_plus
5
+
6
+ option_remove :say
7
+ option :e, '--exit-on-error', TrueClass, desc: 'If it should just exit on "missing argument" error or raise an exception'
8
+ # Move option to the end, make **required** the argument (SOMETHING) as well as the option itself.
9
+ option :s, '--say SOMETHING', "It says 'something'", required: true
10
+
11
+ error_on_options error: RakeCommander::Options::Error::MissingArgument do |err, _argv, results, _leftovers|
12
+ msg = "Parsed results when 'missing argument' error was raised"
13
+ msg << "\non option '#{err.option.name_full}'" if err.option
14
+ puts "#{msg} => #{results.pretty_inspect}"
15
+ !results[:e]
16
+ end
17
+ end
@@ -0,0 +1,7 @@
1
+ require 'dotenv'
2
+ require 'dotenv/load'
3
+
4
+ require_relative '../lib/rake-commander'
5
+ RakeCommander::Patcher.debug = ENV['COMMANDER_DEBUG'] == "true"
6
+ Dir["#{__dir__}/*_example.rb"].sort.each {|file| require_relative file }
7
+ RakeCommander.self_load
@@ -0,0 +1,79 @@
1
+ ## Examples
2
+
3
+ * `RakeCommander` is loaded from the repo branch you are checked out.
4
+
5
+ The Rakefile `Examples.rake` has three lines that can serve as a guide. One were we require `rake-commander`, another where we define our `RakeCommander` classes, and one where we load them as actual `Rake` tasks.
6
+
7
+ ```ruby
8
+ require_relative '../lib/rake-commander'
9
+ RakeCommander::Patcher.debug = ENV['COMMANDER_DEBUG'] == "true"
10
+ Dir["#{__dir__}/*_example.rb"].sort.each {|file| require_relative file }
11
+ RakeCommander.self_load
12
+ ```
13
+
14
+ * To see the patches in action, you can add `COMMANDER_DEBUG=true` to a `.env` file
15
+
16
+ ```
17
+ rake -T examples
18
+ ```
19
+
20
+ ### Basic Example
21
+
22
+ ```
23
+ rake examples:basic -- -h
24
+ rake examples:basic -- -z -e prod
25
+ ```
26
+
27
+ ### Chainer && Chained Example
28
+
29
+ Two tasks where chainer calls chained through **a `shell` call to `rake`**.
30
+
31
+ * Read well the example before running it.
32
+
33
+ ```
34
+ rake examples:chainer -- -h
35
+ rake examples:chained -- -h
36
+ ```
37
+
38
+ ### Chainer Plus and Chained Plus Example
39
+
40
+ Same as the previous example but these tasks inherit from the previous example, extending their behaviour and changing the options.
41
+
42
+
43
+ ```
44
+ rake examples:chainer_plus -- -h
45
+ rake examples:chained_plus -- -h
46
+ ```
47
+
48
+ #### Error handling mixed with options
49
+
50
+ The option `--exit-on-error` allows the error handler defined in `chained_plus` to decide if it should raise the error or just do an `exit 1`
51
+
52
+ * This is possible because the order that the options have been declared. Observe that they `--say` option has been removed and redefined **after** the option `--exit-on-error` has been defined.
53
+ * `OptionParser` **switches are processed in order** and, therefore, the error on `--say` only pops up after the `--exit on-error` option has been already parsed.
54
+
55
+ While this will raise the error (with a trace):
56
+
57
+ ```
58
+ $ rake examples:chainer_plus -- --chain --say
59
+ Calling --> 'rake examples:chained_plus -- --say'
60
+ Parsed results when 'missing argument' error was raised
61
+ on option '--say SOMETHING' => {}
62
+ rake aborted!
63
+ RakeCommander::Options::Error::MissingArgument: (examples:chained_plus) missing required argument in option: --say SOMETHING (-s)
64
+ < here back trace>
65
+ Tasks: TOP => examples:chained_plus
66
+ (See full trace by running task with --trace)
67
+ * Failed to running 'rake examples:chained_plus -- --say'
68
+ ```
69
+
70
+ This will only print the error with an `exit 1`:
71
+
72
+ ```
73
+ $ rake examples:chainer_plus -- --chain --say --exit-on-error
74
+ Calling --> 'rake examples:chained_plus -- --exit-on-error --say'
75
+ Parsed results when 'missing argument' error was raised
76
+ on option '--say SOMETHING' => {:e=>true}
77
+ (examples:chained_plus) missing required argument in option: --say SOMETHING (-s)
78
+ * Failed to running 'rake examples:chained_plus -- --exit-on-error --say'
79
+ ```