rake_options 0.1.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
+ SHA256:
3
+ metadata.gz: 6d64b8c9debd762394889c921fc5a3c52f23618f3e1d33448c5f6337ab942edc
4
+ data.tar.gz: d24b0d7525cbb407047c86b61abca35ed3646a4f414272f1521900267a15673d
5
+ SHA512:
6
+ metadata.gz: b896b635679be32725e5f480441fed874e4a853d3af99ce45b234d4dc5c42ed68ac1bc866879aa434fca4b04fad33b76db825ad9303580d997c8a58c8184fa19
7
+ data.tar.gz: 2d89ac0871af10c498ebfc23d2bc73c7644923fe14e8e2de2005c3195ff9ad995848e22e496da5f632e165a674b07bcf604b94cec14e5a808923a6c67c23d3d6
data/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 RakeCommander Contributors
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 ADDED
@@ -0,0 +1,277 @@
1
+ # RakeOptions
2
+
3
+ RakeOptions is a Ruby gem that simplifies command line argument handling for rake tasks. It provides an intuitive API for parsing arguments with support for multiple notation styles and automatic help documentation generation.
4
+
5
+ ## Installation
6
+
7
+ Add this line to your application's Gemfile:
8
+
9
+ ```ruby
10
+ gem 'rake_options'
11
+ ```
12
+
13
+ And then execute:
14
+
15
+ ```bash
16
+ bundle install
17
+ ```
18
+
19
+ Or install it yourself as:
20
+
21
+ ```bash
22
+ gem install rake_options
23
+ ```
24
+
25
+ ## Usage
26
+
27
+ ### Basic CLI-Style Arguments
28
+
29
+ ```ruby
30
+ require 'rake_options'
31
+
32
+ desc "Build with custom options"
33
+ task :build do
34
+ config = {
35
+ "with-mysql-lib" => "--with-mysql-lib $path",
36
+ "enable-feature" => "--enable-feature $name"
37
+ }
38
+
39
+ options = RakeOptions.command_line_args(config)
40
+
41
+ puts "MySQL lib path: #{options['with-mysql-lib']}"
42
+ puts "Feature: #{options['enable-feature']}"
43
+ end
44
+ ```
45
+
46
+ Run with:
47
+ ```bash
48
+ rake build -- --with-mysql-lib "/usr/local/mysql/lib" --enable-feature "caching"
49
+ ```
50
+
51
+ ### Bracket-Style Arguments
52
+
53
+ ```ruby
54
+ require 'rake_options'
55
+
56
+ desc "Deploy with bracket notation"
57
+ task :deploy do
58
+ config = {
59
+ "environment" => "[environment=$env]",
60
+ "region" => "[region=$region]"
61
+ }
62
+
63
+ options = RakeOptions.command_line_args(config, notation: :bracket)
64
+
65
+ puts "Deploying to #{options['environment']} in #{options['region']}"
66
+ end
67
+ ```
68
+
69
+ Run with:
70
+ ```bash
71
+ rake deploy [environment=production] [region=us-west-2]
72
+ ```
73
+
74
+ ### Multiple Variables in One Template
75
+
76
+ ```ruby
77
+ config = {
78
+ "database" => "--config $file --env $environment"
79
+ }
80
+
81
+ options = RakeOptions.command_line_args(config)
82
+ # Extracts both file and environment from: --config database.yml --env production
83
+ ```
84
+
85
+ ### Automatic Help Documentation
86
+
87
+ ```ruby
88
+ require 'rake_options'
89
+
90
+ readme_content = <<~HELP
91
+ Build Task Help
92
+
93
+ Available options:
94
+ --with-mysql-lib PATH Specify MySQL library path
95
+ --enable-feature NAME Enable a specific feature
96
+ HELP
97
+
98
+ RakeOptions.initialize_readme(readme_content)
99
+
100
+ desc "Build with help support"
101
+ task :build do
102
+ config = {
103
+ "with-mysql-lib" => "--with-mysql-lib $path",
104
+ "enable-feature" => "--enable-feature $name"
105
+ }
106
+
107
+ options = RakeOptions.command_line_args(config)
108
+ # Your build logic here
109
+ end
110
+ ```
111
+
112
+ Run with:
113
+ ```bash
114
+ rake build -- --help
115
+ ```
116
+
117
+ ### Hash Access with Strings or Symbols
118
+
119
+ The returned options hash supports both string and symbol key access:
120
+
121
+ ```ruby
122
+ options = RakeOptions.command_line_args(config)
123
+
124
+ # Both work:
125
+ options['with-mysql-lib']
126
+ options[:with_mysql_lib]
127
+ ```
128
+
129
+ ## Configuration Format
130
+
131
+ The configuration hash maps symbolic names to template patterns:
132
+
133
+ ```ruby
134
+ {
135
+ "key-name" => "template pattern with $variables"
136
+ }
137
+ ```
138
+
139
+ - **Key**: The name you'll use to access the parsed value
140
+ - **Value**: A template string that defines how to extract the argument
141
+ - **Variables**: Prefixed with `$` to indicate where values should be extracted
142
+
143
+ ### Template Examples
144
+
145
+ ```ruby
146
+ # Single variable
147
+ "mysql-path" => "--with-mysql-lib $path"
148
+
149
+ # Multiple variables
150
+ "database" => "--config $file --env $environment"
151
+
152
+ # Bracket notation
153
+ "region" => "[region=$value]"
154
+ ```
155
+
156
+ ## Notation Styles
157
+
158
+ ### CLI Notation (default)
159
+
160
+ ```ruby
161
+ options = RakeOptions.command_line_args(config, notation: :cli)
162
+ ```
163
+
164
+ Supports:
165
+ - `--flag value`
166
+ - `--flag "value with spaces"`
167
+ - Multiple flags in one command
168
+
169
+ ### Bracket Notation
170
+
171
+ ```ruby
172
+ options = RakeOptions.command_line_args(config, notation: :bracket)
173
+ ```
174
+
175
+ Supports:
176
+ - `[key=value]`
177
+ - `[key="value with spaces"]`
178
+ - Multiple bracket arguments
179
+
180
+ ## Error Handling
181
+
182
+ RakeOptions provides clear error messages:
183
+
184
+ ```ruby
185
+ # Invalid notation
186
+ RakeOptions.command_line_args(config, notation: :invalid)
187
+ # => InvalidNotationError: Invalid notation ':invalid'. Supported notations: :cli, :bracket
188
+
189
+ # Missing optional arguments return nil
190
+ options['missing-arg'] # => nil
191
+ ```
192
+
193
+ ## Troubleshooting
194
+
195
+ ### Arguments not being parsed
196
+
197
+ **Problem**: Your arguments aren't being recognized.
198
+
199
+ **Solution**: Make sure you're using `--` to separate rake arguments from your custom arguments:
200
+ ```bash
201
+ rake task -- --your-flag value
202
+ ```
203
+
204
+ ### Symbol vs String keys
205
+
206
+ **Problem**: Can't access values with symbol keys.
207
+
208
+ **Solution**: RakeOptions returns a `HashWithIndifferentAccess` that supports both:
209
+ ```ruby
210
+ options[:key] # Works
211
+ options['key'] # Also works
212
+ options[:key_name] # Converts underscores to dashes automatically
213
+ options['key-name'] # Same value
214
+ ```
215
+
216
+ ### Help not displaying
217
+
218
+ **Problem**: `--help` flag doesn't show help.
219
+
220
+ **Solution**: Ensure `--help` is in ARGV before calling `command_line_args`:
221
+ ```bash
222
+ rake task -- --help
223
+ ```
224
+
225
+ ### Quoted values not working
226
+
227
+ **Problem**: Values with spaces aren't being captured correctly.
228
+
229
+ **Solution**: Use quotes around values with spaces:
230
+ ```bash
231
+ rake task -- --path "/path/with spaces"
232
+ ```
233
+
234
+ ### Template not matching
235
+
236
+ **Problem**: Template variables aren't extracting values.
237
+
238
+ **Solution**: Ensure your template matches the command line format exactly:
239
+ ```ruby
240
+ # Template
241
+ "option" => "--option $value"
242
+
243
+ # Command line must match
244
+ rake task -- --option myvalue
245
+ ```
246
+
247
+ ## Requirements
248
+
249
+ - Ruby 2.7 or higher
250
+
251
+ ## Development
252
+
253
+ After checking out the repo, run:
254
+
255
+ ```bash
256
+ bundle install
257
+ ```
258
+
259
+ Run tests:
260
+
261
+ ```bash
262
+ rake spec
263
+ ```
264
+
265
+ Build the gem:
266
+
267
+ ```bash
268
+ gem build rake_options.gemspec
269
+ ```
270
+
271
+ ## Contributing
272
+
273
+ Bug reports and pull requests are welcome on GitHub.
274
+
275
+ ## License
276
+
277
+ The gem is available as open source under the terms of the [MIT License](LICENSE).
data/Rakefile ADDED
@@ -0,0 +1,8 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "bundler/gem_tasks"
4
+ require "rspec/core/rake_task"
5
+
6
+ RSpec::Core::RakeTask.new(:spec)
7
+
8
+ task default: :spec
@@ -0,0 +1,46 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "cli_parser"
4
+ require_relative "bracket_parser"
5
+ require_relative "errors"
6
+
7
+ module RakeOptions
8
+ class ArgumentParser
9
+ VALID_NOTATIONS = [:cli, :bracket].freeze
10
+
11
+ def initialize(config, notation)
12
+ @config = config
13
+ @notation = notation
14
+ validate_notation
15
+ end
16
+
17
+ # Parse ARGV and return structured options
18
+ # @return [Hash] Parsed options
19
+ def parse
20
+ parser = select_parser
21
+ parser.parse
22
+ end
23
+
24
+ private
25
+
26
+ # Select appropriate parser based on notation
27
+ # @return [CLIParser, BracketParser] Parser instance
28
+ def select_parser
29
+ case @notation
30
+ when :cli
31
+ CLIParser.new(@config)
32
+ when :bracket
33
+ BracketParser.new(@config)
34
+ end
35
+ end
36
+
37
+ # Validate notation parameter
38
+ # @raise [InvalidNotationError] if notation is invalid
39
+ def validate_notation
40
+ return if VALID_NOTATIONS.include?(@notation)
41
+
42
+ raise InvalidNotationError,
43
+ "Invalid notation ':#{@notation}'. Supported notations: #{VALID_NOTATIONS.map { |n| ":#{n}" }.join(', ')}"
44
+ end
45
+ end
46
+ end
@@ -0,0 +1,64 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RakeOptions
4
+ class BracketParser
5
+ def initialize(config)
6
+ @config = config
7
+ end
8
+
9
+ # Parse bracket-style arguments from ARGV
10
+ # @param argv [Array] Command line arguments (defaults to ARGV)
11
+ # @return [Hash] Parsed values
12
+ def parse(argv = ARGV)
13
+ result = {}
14
+
15
+ # Extract all bracket arguments from argv
16
+ bracket_args = extract_bracket_args(argv)
17
+
18
+ # Match bracket args to config keys
19
+ @config.each do |key, _template|
20
+ # Look for matching bracket key
21
+ if bracket_args.key?(key)
22
+ result[key] = bracket_args[key]
23
+ else
24
+ result[key] = nil
25
+ end
26
+ end
27
+
28
+ result
29
+ end
30
+
31
+ private
32
+
33
+ # Find all [key=value] patterns in argv
34
+ # @param argv [Array] Command line arguments
35
+ # @return [Hash] Extracted key-value pairs
36
+ def extract_bracket_args(argv)
37
+ result = {}
38
+
39
+ argv.each do |arg|
40
+ # Match [key=value] or [key="value"]
41
+ match = arg.match(/^\[([^=]+)=(.+)\]$/)
42
+ next unless match
43
+
44
+ key = match[1]
45
+ value = parse_bracket_value(match[2])
46
+ result[key] = value
47
+ end
48
+
49
+ result
50
+ end
51
+
52
+ # Handle quoted and unquoted values in brackets
53
+ # @param value [String] Value from bracket
54
+ # @return [String] Parsed value
55
+ def parse_bracket_value(value)
56
+ # Remove surrounding quotes if present
57
+ if value.start_with?('"') && value.end_with?('"')
58
+ value[1..-2]
59
+ else
60
+ value
61
+ end
62
+ end
63
+ end
64
+ end
@@ -0,0 +1,43 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "template_engine"
4
+
5
+ module RakeOptions
6
+ class CLIParser
7
+ def initialize(config)
8
+ @config = config
9
+ @parsed_templates = {}
10
+
11
+ # Pre-parse all templates
12
+ @config.each do |key, template_str|
13
+ @parsed_templates[key] = TemplateEngine.parse_template(template_str)
14
+ end
15
+ end
16
+
17
+ # Parse CLI-style arguments from ARGV
18
+ # @param argv [Array] Command line arguments (defaults to ARGV)
19
+ # @return [Hash] Parsed values
20
+ def parse(argv = ARGV)
21
+ result = {}
22
+
23
+ @config.each do |key, _template_str|
24
+ parsed_template = @parsed_templates[key]
25
+ extracted = TemplateEngine.extract_values(argv, parsed_template)
26
+
27
+ if extracted && !extracted.empty?
28
+ # For single variable templates, store the value directly
29
+ if extracted.size == 1
30
+ result[key] = extracted.values.first
31
+ else
32
+ # For multiple variables, store the hash
33
+ result[key] = extracted
34
+ end
35
+ else
36
+ result[key] = nil
37
+ end
38
+ end
39
+
40
+ result
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,8 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RakeOptions
4
+ class Error < StandardError; end
5
+ class InvalidNotationError < Error; end
6
+ class TemplateParseError < Error; end
7
+ class ArgumentParseError < Error; end
8
+ end
@@ -0,0 +1,55 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RakeOptions
4
+ class HashWithIndifferentAccess < Hash
5
+ def initialize(hash = {})
6
+ super()
7
+ hash.each do |key, value|
8
+ self[key] = value
9
+ end
10
+ end
11
+
12
+ # Override [] to handle both string and symbol keys
13
+ def [](key)
14
+ super(convert_key(key))
15
+ end
16
+
17
+ # Override []= to store with string keys
18
+ def []=(key, value)
19
+ super(convert_key(key), value)
20
+ end
21
+
22
+ # Override fetch to handle both string and symbol keys
23
+ def fetch(key, *args, &block)
24
+ super(convert_key(key), *args, &block)
25
+ end
26
+
27
+ # Override key? to handle both string and symbol keys
28
+ def key?(key)
29
+ super(convert_key(key))
30
+ end
31
+
32
+ alias_method :has_key?, :key?
33
+ alias_method :include?, :key?
34
+
35
+ private
36
+
37
+ # Convert key to string and normalize dashes/underscores
38
+ def convert_key(key)
39
+ key_str = key.to_s
40
+ # Try exact match first
41
+ return key_str if Hash.instance_method(:key?).bind(self).call(key_str)
42
+
43
+ # Try with underscores converted to dashes
44
+ dashed = key_str.tr("_", "-")
45
+ return dashed if Hash.instance_method(:key?).bind(self).call(dashed)
46
+
47
+ # Try with dashes converted to underscores
48
+ underscored = key_str.tr("-", "_")
49
+ return underscored if Hash.instance_method(:key?).bind(self).call(underscored)
50
+
51
+ # Return original if no match
52
+ key_str
53
+ end
54
+ end
55
+ end
@@ -0,0 +1,49 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RakeOptions
4
+ class HelpGenerator
5
+ def initialize(config, readme_content = nil)
6
+ @config = config
7
+ @readme_content = readme_content
8
+ end
9
+
10
+ # Display help and exit
11
+ # @return [void]
12
+ def display_and_exit
13
+ puts generate_help_text
14
+ exit(0)
15
+ end
16
+
17
+ private
18
+
19
+ # Create formatted help text
20
+ # @return [String] Formatted help text
21
+ def generate_help_text
22
+ if @readme_content
23
+ @readme_content
24
+ else
25
+ auto_generate_help
26
+ end
27
+ end
28
+
29
+ # Auto-generate help from config
30
+ # @return [String] Auto-generated help text
31
+ def auto_generate_help
32
+ help_text = "Available Options:\n\n"
33
+
34
+ @config.each do |key, template|
35
+ help_text += format_option(key, template)
36
+ end
37
+
38
+ help_text
39
+ end
40
+
41
+ # Format individual option for display
42
+ # @param key [String] Option key
43
+ # @param template [String] Template string
44
+ # @return [String] Formatted option line
45
+ def format_option(key, template)
46
+ " #{template.ljust(40)} (#{key})\n"
47
+ end
48
+ end
49
+ end
@@ -0,0 +1,70 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RakeOptions
4
+ class TemplateEngine
5
+ # Parse a template string into components
6
+ # @param template [String] Template like "--flag $variable"
7
+ # @return [Hash] Template structure with flag, variables, and pattern
8
+ def self.parse_template(template)
9
+ variables = identify_variables(template)
10
+ pattern = build_pattern(template, variables)
11
+
12
+ {
13
+ template: template,
14
+ variables: variables,
15
+ pattern: pattern
16
+ }
17
+ end
18
+
19
+ # Extract values from input based on template
20
+ # @param input [Array] Input tokens (ARGV)
21
+ # @param parsed_template [Hash] Parsed template structure
22
+ # @return [Hash, nil] Extracted variable values or nil if no match
23
+ def self.extract_values(input, parsed_template)
24
+ input_str = input.join(" ")
25
+ match = parsed_template[:pattern].match(input_str)
26
+
27
+ return nil unless match
28
+
29
+ result = {}
30
+ # Each variable creates 2 capture groups (quoted and unquoted)
31
+ # We need to pick the non-nil one
32
+ capture_index = 1
33
+ parsed_template[:variables].each do |var|
34
+ # Get both possible captures (quoted or unquoted)
35
+ quoted_value = match[capture_index]
36
+ unquoted_value = match[capture_index + 1]
37
+
38
+ result[var] = quoted_value || unquoted_value
39
+ capture_index += 2
40
+ end
41
+
42
+ result
43
+ end
44
+
45
+ private
46
+
47
+ # Find $variable placeholders in template
48
+ # @param template [String] Template string
49
+ # @return [Array<String>] List of variable names
50
+ def self.identify_variables(template)
51
+ template.scan(/\$(\w+)/).flatten
52
+ end
53
+
54
+ # Create regex pattern from template
55
+ # @param template [String] Template string
56
+ # @param variables [Array<String>] Variable names
57
+ # @return [Regexp] Regex pattern for matching
58
+ def self.build_pattern(template, variables)
59
+ # Replace $variables with capture groups that match:
60
+ # - Quoted strings: "value with spaces"
61
+ # - Unquoted values: value (non-whitespace)
62
+ pattern_str = template.gsub(/\$\w+/, '(?:"([^"]+)"|(\S+))')
63
+
64
+ # Escape special regex characters except our capture groups
65
+ pattern_str = pattern_str.gsub(/\s+/, '\s+')
66
+
67
+ Regexp.new(pattern_str)
68
+ end
69
+ end
70
+ end
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RakeOptions
4
+ VERSION = "0.1.0"
5
+ end
@@ -0,0 +1,39 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "rake_options/version"
4
+ require_relative "rake_options/errors"
5
+ require_relative "rake_options/argument_parser"
6
+ require_relative "rake_options/help_generator"
7
+ require_relative "rake_options/hash_with_indifferent_access"
8
+
9
+ module RakeOptions
10
+ class << self
11
+ attr_accessor :readme_content
12
+
13
+ # Parse command line arguments based on configuration
14
+ # @param config [Hash] Argument configuration mapping
15
+ # @param notation [Symbol] Notation style (:cli or :bracket)
16
+ # @return [HashWithIndifferentAccess] Parsed arguments with indifferent access
17
+ def command_line_args(config, notation: :cli)
18
+ # Check for --help flag
19
+ if ARGV.include?("--help")
20
+ help_generator = HelpGenerator.new(config, readme_content)
21
+ help_generator.display_and_exit
22
+ end
23
+
24
+ # Parse arguments
25
+ parser = ArgumentParser.new(config, notation)
26
+ result = parser.parse
27
+
28
+ # Wrap in HashWithIndifferentAccess
29
+ HashWithIndifferentAccess.new(result)
30
+ end
31
+
32
+ # Initialize help documentation
33
+ # @param readme_content [String] Help text or README content
34
+ # @return [void]
35
+ def initialize_readme(content)
36
+ self.readme_content = content
37
+ end
38
+ end
39
+ end