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
@@ -0,0 +1,44 @@
1
+ class RakeCommander
2
+ module Patcher
3
+ # Helpers to patch
4
+ module Helpers
5
+ # For a given method `meth` it gives the index of the parameter `arg_name`
6
+ # @return [Integer, NilClass] the position of `arg_name` in parameters.
7
+ def method_argument_idx(meth, arg_name)
8
+ arg_name = arg_name.to_sym
9
+ meth.parameters.each_with_index do |(_type, name), i|
10
+ return i if name == arg_name
11
+ end
12
+ end
13
+
14
+ # Its usage only makes sense if you extended an existing method you are patching.
15
+ # Therefore it is expected that `super` exists, so the original parameters definition
16
+ # of the method can be accessed.
17
+ # @note although the signature of a method can change through different versions
18
+ # the name of the parameters is generally preserved (specially when they are core parameters).
19
+ # @example
20
+ # module Rake
21
+ # class Application
22
+ # def init(*args)
23
+ # args = RakeCommander::Patcher.change_method_argument(:argv, method: method(__method__), args: args) do |value|
24
+ # RakeCommander.argv_rake_native_arguments(value)
25
+ # end
26
+ # super(*args)
27
+ # end
28
+ # end
29
+ # end
30
+ #
31
+ # @param arg_name [Symbol, String] the name of the parameter as it reads in the original method.
32
+ # @param method [Method] the extended method (not its `super` method)
33
+ # @return [Array] the original arguments where `arg_name` has been changed.
34
+ def change_method_argument(arg_name, method:, args:)
35
+ raise ArgumentError, 'Expecting block' unless block_given?
36
+ raise ArgumentError, "Expecting Method. Given #{method.class}" unless method.is_a?(Method)
37
+ if idx = method_argument_idx(method.super_method, arg_name.to_sym)
38
+ args[idx] = yield(args[idx])
39
+ end
40
+ args
41
+ end
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,26 @@
1
+ require_relative 'patcher/helpers'
2
+ require_relative 'patcher/debug'
3
+ require_relative 'patcher/base'
4
+ require_relative 'patcher/application'
5
+
6
+ class RakeCommander
7
+ module Patcher
8
+ extend RakeCommander::Patcher::Helpers
9
+ include RakeCommander::Patcher::Base
10
+
11
+ class << self
12
+ def patch_include(base)
13
+ base.send :include, Application
14
+ end
15
+
16
+ def debug=(value)
17
+ @debug = !!value
18
+ end
19
+
20
+ def debug?
21
+ @debug = false unless instance_variable_defined?(:@debug)
22
+ @debug
23
+ end
24
+ end
25
+ end
26
+ end
@@ -1,6 +1,8 @@
1
1
  class RakeCommander
2
2
  module RakeContext
3
3
  class Wrapper
4
+ include Rake::DSL
5
+
4
6
  # Allows to interact with rake
5
7
  # @note this prevents subclass overlap methods to be used
6
8
  def context(object = global_instance, &block)
@@ -3,66 +3,52 @@ require_relative 'rake_context/wrapper'
3
3
  class RakeCommander
4
4
  module RakeTask
5
5
  NAMESPACE_DELIMITER = /:/.freeze
6
- RAKE_END_COMMAND = '--'.freeze
7
6
  INHERITABLE_ATTRS = [:namespace].freeze
8
7
 
9
8
  class << self
10
9
  def included(base)
11
10
  super(base)
11
+ base.extend RakeCommander::Base::ClassHelpers
12
+ base.extend RakeCommander::Base::ClassInheritable
12
13
  base.extend ClassMethods
13
- base.inheritable_attrs(*INHERITABLE_ATTRS)
14
+ base.attr_inheritable(*INHERITABLE_ATTRS)
14
15
  end
15
16
  end
16
17
 
17
18
  module ClassMethods
18
- include RakeCommander::Base::ClassHelpers
19
-
20
19
  # The rake context wrapper (to invoke rake commands)
21
20
  def rake
22
21
  @rake ||= RakeCommander::RakeContext::Wrapper.new
23
22
  end
24
23
 
25
- # Does the final rake `task` definition
26
- def install_task(&task_method)
27
- raise "Expected task_block." unless task_method
28
-
29
- # ensure options are parsed before calling task
30
- # and that ARGV is only parsed after `--`
31
- # do an `exit(0)` right at the end
32
- task_method = task_context(&task_method) if options?
33
-
34
- if namespaced?
35
- namespaced do
36
- rake.desc desc
37
- rake.task task, &task_method
38
- end
39
- else
40
- rake.desc desc
41
- rake.task task, &task_method
42
- end
24
+ # Give a description to the task
25
+ # @return [String] the description of the task
26
+ def desc(str = :not_used)
27
+ return @desc if str == :not_used
28
+ @desc = str.to_s
43
29
  end
44
30
 
45
31
  # Give a name to the task
46
32
  # @return [Symbol] the task name
47
- def task(name = nil)
48
- return @task if name.nil?
33
+ def task(name = :not_used)
34
+ return @task if name == :not_used
49
35
  @task = name.to_sym
50
36
  end
51
37
 
52
- # Give a description to the task
53
- # @return [String] the description of the task
54
- def desc(str = nil)
55
- return @desc if str.nil?
56
- @desc = str.to_s
57
- end
58
-
59
38
  # It can be hierarchical by using `NAMESPACE_DELIMITER`
60
39
  # @return [String] the namespace defined for this `RakeCommander` class.
61
- def namespace(name = nil)
62
- return @namespace if name.nil?
40
+ def namespace(name = :not_used)
41
+ return @namespace if name == :not_used
63
42
  @namespace = namespace_str(name)
64
43
  end
65
44
 
45
+ # It gives the task full name (including namespacing)
46
+ # @return [String]
47
+ def task_fullname
48
+ "#{namespace}:#{task}"
49
+ end
50
+ alias_method :name, :task_fullname
51
+
66
52
  # Is this rake context namespaced?
67
53
  # @note Rake allows to namespace tasks (i.e. `task :"run:this"`)
68
54
  # Although supported by this integration, namespace detection
@@ -82,6 +68,28 @@ class RakeCommander
82
68
  rake.namespace top, &block
83
69
  end
84
70
 
71
+ # Does the final rake `task` definition
72
+ # @note although it will exist, the task won't be listed with `rake -T`
73
+ # unless it has a description (`desc`).
74
+ # @note this method is extended by some modules
75
+ # 1. `RakeCommander::Options::Result`: Ensure options are parsed before calling task
76
+ # 2. `RakeCommander::Options::Arguments`: `exit(0)` when `Rake` interprets the full `ARGV`
77
+ # rather than stopping at the delimiter (`--`)
78
+ # @return [@see Rake::Task] same results as if you used `task :name {}` or `namespace :space {}`
79
+ def install_task(&task_method)
80
+ raise "Expected task_block." unless task_method
81
+
82
+ if namespaced?
83
+ namespaced do
84
+ rake.desc desc
85
+ rake.task task, &task_method
86
+ end
87
+ else
88
+ rake.desc desc
89
+ rake.task task, &task_method
90
+ end
91
+ end
92
+
85
93
  protected
86
94
 
87
95
  # Converstion of `namespace` name to string
@@ -99,6 +107,14 @@ class RakeCommander
99
107
  respond_to?(:task) ? "Usage: #{str_space}#{task} -- [options]" : nil
100
108
  end
101
109
 
110
+ # Offer a wrapper to build the task conext througout all inheritance chain.
111
+ # @note
112
+ # - This method offers children classes a way to add their on middleware.
113
+ # @return [Proc] our wrapped task block.
114
+ def task_context(&task_method)
115
+ task_method
116
+ end
117
+
102
118
  private
103
119
 
104
120
  # Split into `Array` the namespace based on `NAMESPACE_DELIMITER`
@@ -114,27 +130,6 @@ class RakeCommander
114
130
  proc { namespace name, &block }
115
131
  end
116
132
  end
117
-
118
- # Rake command ends at `--` (`RAKE_END_COMMAND`).
119
- # We only want to parse the options that come afterwards
120
- # @note
121
- # 1. Without `ARGV` cut, it will throw `OptionParser::InvalidOption` error
122
- # 2. **Work-around**: We also add an `exit(0)` at the end to prevent `Rake` chaining
123
- # option arguments as if they were actual tasks.
124
- # @return [Proc]
125
- def task_context(&task_method)
126
- object = eval('self', task_method.binding, __FILE__, __LINE__)
127
- return task_method unless object.is_a?(self)
128
- proc do |*args|
129
- argv = ARGV
130
- if idx = argv.index(RAKE_END_COMMAND)
131
- argv = argv[idx+1..-1]
132
- end
133
- object.options(argv)
134
- task_method.call(*args)
135
- exit(0)
136
- end
137
- end
138
133
  end
139
134
 
140
135
  def initialize
@@ -1,3 +1,3 @@
1
1
  class RakeCommander
2
- VERSION = '0.1.4'.freeze
2
+ VERSION = '0.2.0'.freeze
3
3
  end
@@ -1,9 +1,13 @@
1
1
  require 'rake'
2
2
  require_relative 'rake-commander/base'
3
+ require_relative 'rake-commander/patcher'
3
4
 
4
5
  class RakeCommander
5
6
  include RakeCommander::Base
7
+ include RakeCommander::Patcher
6
8
  end
7
9
 
8
10
  require_relative 'rake-commander/version'
9
11
  require_relative 'rake-commander/custom'
12
+
13
+ RakeCommander.self_load
@@ -8,7 +8,7 @@ Gem::Specification.new do |spec|
8
8
  spec.authors = ["Oscar Segura Samper"]
9
9
  spec.email = ["oscar@ecoportal.co.nz"]
10
10
 
11
- spec.summary = 'The classed version of rake with task options. Create re-usable tasks and samples.'
11
+ spec.summary = 'Classing rake tasks with options. Creating re-usable tasks, options and samples thereof.'
12
12
  spec.homepage = "https://github.com/rellampec/rake-commander"
13
13
  spec.licenses = %w[MIT]
14
14
 
@@ -22,8 +22,11 @@ Gem::Specification.new do |spec|
22
22
  spec.required_ruby_version = '>= 2.7.2'
23
23
 
24
24
  spec.add_development_dependency "bundler", ">= 2.4.9", "< 3"
25
+ spec.add_development_dependency 'dotenv', '>= 2.7.6', '< 3'
25
26
  spec.add_development_dependency "rake", ">= 13.0.6", "< 14"
26
27
  spec.add_development_dependency "redcarpet", ">= 3.6.0", "< 4"
27
28
  spec.add_development_dependency "rspec", ">= 3.10.0", "< 4"
28
29
  spec.add_development_dependency "yard", ">= 0.9.34", "< 1"
30
+
31
+ spec.add_dependency "rake", ">= 13.0.6", "< 14"
29
32
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rake-commander
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.4
4
+ version: 0.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Oscar Segura Samper
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2023-04-19 00:00:00.000000000 Z
11
+ date: 2023-04-27 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -30,6 +30,26 @@ dependencies:
30
30
  - - "<"
31
31
  - !ruby/object:Gem::Version
32
32
  version: '3'
33
+ - !ruby/object:Gem::Dependency
34
+ name: dotenv
35
+ requirement: !ruby/object:Gem::Requirement
36
+ requirements:
37
+ - - ">="
38
+ - !ruby/object:Gem::Version
39
+ version: 2.7.6
40
+ - - "<"
41
+ - !ruby/object:Gem::Version
42
+ version: '3'
43
+ type: :development
44
+ prerelease: false
45
+ version_requirements: !ruby/object:Gem::Requirement
46
+ requirements:
47
+ - - ">="
48
+ - !ruby/object:Gem::Version
49
+ version: 2.7.6
50
+ - - "<"
51
+ - !ruby/object:Gem::Version
52
+ version: '3'
33
53
  - !ruby/object:Gem::Dependency
34
54
  name: rake
35
55
  requirement: !ruby/object:Gem::Requirement
@@ -110,6 +130,26 @@ dependencies:
110
130
  - - "<"
111
131
  - !ruby/object:Gem::Version
112
132
  version: '1'
133
+ - !ruby/object:Gem::Dependency
134
+ name: rake
135
+ requirement: !ruby/object:Gem::Requirement
136
+ requirements:
137
+ - - ">="
138
+ - !ruby/object:Gem::Version
139
+ version: 13.0.6
140
+ - - "<"
141
+ - !ruby/object:Gem::Version
142
+ version: '14'
143
+ type: :runtime
144
+ prerelease: false
145
+ version_requirements: !ruby/object:Gem::Requirement
146
+ requirements:
147
+ - - ">="
148
+ - !ruby/object:Gem::Version
149
+ version: 13.0.6
150
+ - - "<"
151
+ - !ruby/object:Gem::Version
152
+ version: '14'
113
153
  description:
114
154
  email:
115
155
  - oscar@ecoportal.co.nz
@@ -123,21 +163,49 @@ files:
123
163
  - ".yardopts"
124
164
  - CHANGELOG.md
125
165
  - Gemfile
166
+ - LICENSE
126
167
  - README.md
127
168
  - Rakefile
128
- - examples/basic.rb
169
+ - examples/01_basic_example.rb
170
+ - examples/02_a_chainer_example.rb
171
+ - examples/02_a_chainer_options_set.rb
172
+ - examples/02_b_chained_example.rb
173
+ - examples/03_a_chainer_plus_example.rb
174
+ - examples/03_b_chained_plus_example.rb
175
+ - examples/Examples.rake
176
+ - examples/README.md
177
+ - examples/libs/shell_helpers.rb
129
178
  - lib/rake-commander.rb
130
179
  - lib/rake-commander/base.rb
131
180
  - lib/rake-commander/base/class_auto_loader.rb
132
181
  - lib/rake-commander/base/class_helpers.rb
182
+ - lib/rake-commander/base/class_inheritable.rb
183
+ - lib/rake-commander/base/custom_error.rb
184
+ - lib/rake-commander/base/object_helpers.rb
133
185
  - lib/rake-commander/custom.rb
134
186
  - lib/rake-commander/option.rb
135
187
  - lib/rake-commander/options.rb
136
188
  - lib/rake-commander/options/arguments.rb
189
+ - lib/rake-commander/options/description.rb
137
190
  - lib/rake-commander/options/error.rb
138
- - lib/rake-commander/options/error_rely.rb
191
+ - lib/rake-commander/options/error/base.rb
192
+ - lib/rake-commander/options/error/handling.rb
193
+ - lib/rake-commander/options/error/invalid_argument.rb
194
+ - lib/rake-commander/options/error/invalid_option.rb
195
+ - lib/rake-commander/options/error/missing_argument.rb
196
+ - lib/rake-commander/options/error/missing_option.rb
197
+ - lib/rake-commander/options/error/unknown_argument.rb
139
198
  - lib/rake-commander/options/name.rb
199
+ - lib/rake-commander/options/result.rb
140
200
  - lib/rake-commander/options/set.rb
201
+ - lib/rake-commander/patcher.rb
202
+ - lib/rake-commander/patcher/README.md
203
+ - lib/rake-commander/patcher/application.rb
204
+ - lib/rake-commander/patcher/application/run_method.rb
205
+ - lib/rake-commander/patcher/application/top_level_method.rb
206
+ - lib/rake-commander/patcher/base.rb
207
+ - lib/rake-commander/patcher/debug.rb
208
+ - lib/rake-commander/patcher/helpers.rb
141
209
  - lib/rake-commander/rake_context/wrapper.rb
142
210
  - lib/rake-commander/rake_task.rb
143
211
  - lib/rake-commander/version.rb
@@ -164,6 +232,6 @@ requirements: []
164
232
  rubygems_version: 3.1.4
165
233
  signing_key:
166
234
  specification_version: 4
167
- summary: The classed version of rake with task options. Create re-usable tasks and
168
- samples.
235
+ summary: Classing rake tasks with options. Creating re-usable tasks, options and samples
236
+ thereof.
169
237
  test_files: []
data/examples/basic.rb DELETED
@@ -1,30 +0,0 @@
1
- require_relative File.join(__dir__, '../lib/rake-commander')
2
- class RakeCommander::Custom::Basic < RakeCommander
3
- namespace :examples
4
-
5
- desc 'A simple example to get started'
6
- task :basic
7
-
8
- #banner "Usage: basic:example -- [options]"
9
- option '-s', '--say [SOMETHING]', "It says 'something'", default: %q(I don't know what to "say"...)
10
- option :d, '--folder NAME', default: '.', desc: 'Source local folder', required: true
11
- option '-e', '--enviro ENV', 'The target environment to run this task', required: true
12
- option '-t', :show_time, TrueClass, desc: 'Displays the local time'
13
- option :v, :debug, TrueClass, 'Shows the parsed options'
14
-
15
- def task(*_args)
16
- if options[:v]
17
- puts 'We got these options:'
18
- pp options
19
- end
20
- puts Time.now.strftime('%d %b at %H:%M') if options[:t]
21
- puts options[:s] if options.key?(:s)
22
- end
23
- end
24
-
25
- RakeCommander.self_load
26
- Rake::Task[:'examples:basic'].invoke
27
- # ruby basic.rb -- -v -d /some/folder -t
28
-
29
- #RakeCommander::Custom::Basic.parse_options %w[--help]
30
- #RakeCommander::Custom::Basic.parse_options %w[-d]
@@ -1,58 +0,0 @@
1
- class RakeCommander
2
- module Options
3
- # Relies between OptionParser and RakeCommander errors
4
- class ErrorRely < StandardError
5
- extend RakeCommander::Options::Name
6
-
7
- OPTION_REGEX = /(?:argument|option): (?<option>.+)/i.freeze
8
-
9
- def initialize(value)
10
- case value
11
- when OptionParser::MissingArgument, OptionParser::InvalidArgument
12
- super(value.message)
13
- when String
14
- super(value)
15
- else
16
- raise ArgumentError, "Expecting String or OptionParser error. Given: #{value.class}"
17
- end
18
- end
19
-
20
- def name?
21
- option_sym.to_s.length > 1
22
- end
23
-
24
- def short?
25
- option_sym.to_s.length == 1
26
- end
27
-
28
- def option_sym
29
- return @option_sym if @option_sym
30
- return nil unless match = message.match(self.class::OPTION_REGEX)
31
- option = match[:option]
32
- @option_sym = \
33
- if option.length > 1
34
- self.class.name_word_sym(option)
35
- else
36
- self.class.short_sym(option)
37
- end
38
- end
39
-
40
- private
41
-
42
- def to_description(value)
43
- case value
44
- when Hash
45
- to_description(value.values.uniq)
46
- when Array
47
- value.map do |v|
48
- to_description(v)
49
- end.join(', ')
50
- when RakeCommander::Option
51
- "#{value.name_hyphen} (#{value.short_hyphen})"
52
- else
53
- value
54
- end
55
- end
56
- end
57
- end
58
- end