clasp-ruby 0.20.1 → 0.22.0.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
- SHA256:
3
- metadata.gz: f92b789f37af02b75b9644ff0d68c2f8dc1fb37bed7372f85a2e3076bda3318c
4
- data.tar.gz: 915b038fa26a675ff5adef533286ed89dd1637cab521d9d187eb2cc28eb60405
2
+ SHA1:
3
+ metadata.gz: bdf2add70e2d8e134e6759718fabde89ecb5c158
4
+ data.tar.gz: dcf9e41a17a28f53406daef64bdba1f614f2a080
5
5
  SHA512:
6
- metadata.gz: 14b4ed81618a5564af21068ed04f8443cb0e699763f3489d91deae285bf3e7b2e5a8b77e0331185cec751659772ddd6d54a3a93ac1ee98aaf05f0a11c12d78b4
7
- data.tar.gz: 90b82b5a6b1553c540f8904dbc4c2b3fe7257a0c177fbb4cacc4004af9e46470f5f0af8e119a635cf25062ab1b41a7bee54c9c4a512e74a48807b9cda4da4779
6
+ metadata.gz: d329ce03d0057446f877425faec12da111253f0bb51dbcd0d1e619d0f3e7f5dcc781b20b18ffbb65eb796011b3021d208e1d391881754ec3ecd61e6efad49186
7
+ data.tar.gz: 63873b0a897f4d854d6f4c80e5605cd68ebecdb4591361662cfa075292ac9c4a69ae8aa8f09ab97446db675fe5611cca41bc7ea83f01853bafb55722b5093fee
data/README.md CHANGED
@@ -1,9 +1,27 @@
1
1
  # CLASP.Ruby
2
- Command-Line Argument Sorting and Parsing for Ruby
2
+ Command-Line Argument Sorting and Parsing, for Ruby
3
3
 
4
4
  [![Gem Version](https://badge.fury.io/rb/clasp-ruby.svg)](https://badge.fury.io/rb/clasp-ruby)
5
5
 
6
- ## Installation & usage
6
+ ## Table of Contents
7
+
8
+ 1. [Introduction](#introduction)
9
+ 2. [Installation](#installation)
10
+ 3. [Components](#components)
11
+ 4. [Examples](#examples)
12
+ 5. [Project Information](#project-information)
13
+
14
+ ## Introduction
15
+
16
+ **CLASP** stands for Command-Line Argument Sorting and
17
+ Parsing. The first CLASP library was a C library with a C++ wrapper. There
18
+ have been several implementations in other languages. **CLASP.Ruby** is the
19
+ Ruby version.
20
+
21
+ All CLASP libraries provide the facilities to **C**ommand **L**ine
22
+ **I**nterface (**CLI**) programs as described in detail below.
23
+
24
+ ## Installation
7
25
 
8
26
  Install via **gem** as in:
9
27
 
@@ -19,19 +37,7 @@ Use via **require**, as in:
19
37
  require 'clasp'
20
38
  ```
21
39
 
22
- ## Examples
23
-
24
- Examples are provided in the ```examples``` directory, along with a markdown description for each. A detailed list TOC of them is provided in [EXAMPLES.md](./EXAMPLES.md).
25
-
26
- ## Description
27
-
28
- **CLASP** stands for **C**ommand-**L**ine **A**rgument **S**orting and
29
- **P**arsing. The first CLASP library was a C library with a C++ wrapper. There
30
- have been several implementations in other languages. **CLASP.Ruby** is the
31
- Ruby version.
32
-
33
- All CLASP libraries provide the following facilities to **C**ommand **L**ine
34
- **I**nterface (**CLI**) programs:
40
+ ## Components
35
41
 
36
42
  ### Command-line parsing
37
43
 
@@ -101,7 +107,7 @@ Specifications = [
101
107
  CLASP.Flag('--all', alias: '-a', help: 'processes all item types'),
102
108
  CLASP.Flag('-c', help: 'count the processed items'),
103
109
  CLASP.Option('--opt1', alias: '-o', help: 'an option of some kind', values_range: %w{ val1, val2 }),
104
- CLASP.Option('--opt1=val1', alias: '-v'),
110
+ CLASP.Flag('--opt1=val1', alias: '-v'),
105
111
 
106
112
  # see next section for why these two are here
107
113
  CLASP::Flag.Help,
@@ -195,15 +201,21 @@ then the program will output the following
195
201
  cr-example.rb 0.1.2
196
202
  ```
197
203
 
198
- ## Where to get help
204
+ ## Examples
205
+
206
+ Examples are provided in the ```examples``` directory, along with a markdown description for each. A detailed list TOC of them is provided in [EXAMPLES.md](./EXAMPLES.md).
207
+
208
+ ## Project Information
209
+
210
+ ### Where to get help
199
211
 
200
212
  [GitHub Page](https://github.com/synesissoftware/CLASP.Ruby "GitHub Page")
201
213
 
202
- ## Contribution guidelines
214
+ ### Contribution guidelines
203
215
 
204
216
  Defect reports, feature requests, and pull requests are welcome on https://github.com/synesissoftware/CLASP.Ruby.
205
217
 
206
- ## Related projects
218
+ ### Related projects
207
219
 
208
220
  **CLASP.Ruby** is inspired by the [C/C++ CLASP library](https://github.com/synesissoftware/CLASP), which is documented in the articles:
209
221
 
@@ -211,9 +223,19 @@ Defect reports, feature requests, and pull requests are welcome on https://githu
211
223
  * _[Anatomy of a CLI Program written in C](http://synesis.com.au/publishing/software-anatomies/anatomy-of-a-cli-program-written-in-c.html)_, Matthew Wilson, [CVu](http://accu.org/index.php/journals/c77/), September 2012; and
212
224
  * _[Anatomy of a CLI Program written in C++](http://synesis.com.au/publishing/software-anatomies/anatomy-of-a-cli-program-written-in-c++.html)_, Matthew Wilson, [CVu](http://accu.org/index.php/journals/c77/), September 2015.
213
225
 
214
- **CLASP.Ruby** is used in the **[libCLImate.Ruby](https://github.com/synesissoftware/libCLImate.Ruby)** library.
226
+ Other CLASP libraries include:
227
+
228
+ * [**CLASP**](https://github.com/synesissoftware/CLASP/)
229
+ * [**CLASP.Go**](https://github.com/synesissoftware/CLASP.Go/)
230
+ * [**CLASP.js**](https://github.com/synesissoftware/CLASP.js/)
231
+ * [**CLASP.NET**](https://github.com/synesissoftware/CLASP.NET/)
232
+ * [**CLASP.Python**](https://github.com/synesissoftware/CLASP.Python/)
233
+
234
+ Projects in which **CLASP.Ruby** is used include:
235
+
236
+ * [**libCLImate.Ruby**](https://github.com/synesissoftware/libCLImate.Ruby)
215
237
 
216
- ## License
238
+ ### License
217
239
 
218
240
  **CLASP.Ruby** is released under the 3-clause BSD license. See LICENSE for details.
219
241
 
@@ -1,16 +1,7 @@
1
1
  #!/usr/bin/ruby
2
2
 
3
- #############################################################################
4
- # File: examples/cr-example.rb
3
+ # examples/cr-example.rb
5
4
  #
6
- # Purpose: COMPLETE_ME
7
- #
8
- # Created: 11 06 2016
9
- # Updated: 11 06 2016
10
- #
11
- # Author: Matthew Wilson
12
- #
13
- #############################################################################
14
5
 
15
6
  $:.unshift File.join(File.dirname(__FILE__), '..', 'lib')
16
7
 
@@ -23,7 +14,7 @@ Specifications = [
23
14
  CLASP.Flag('--all', alias: '-a', help: 'processes all item types'),
24
15
  CLASP.Flag('-c', help: 'count the processed items'),
25
16
  CLASP.Option('--opt1', alias: '-o', help: 'an option of some kind', values_range: %w{ val1, val2 }),
26
- CLASP.Option('--opt1=val1', alias: '-v'),
17
+ CLASP.Flag('--opt1=val1', alias: '-v'),
27
18
 
28
19
  # see next section for why these two are here
29
20
  CLASP::Flag.Help,
@@ -201,4 +201,19 @@ verbosity is specified as: chatty
201
201
  Debug mode is specified
202
202
  ```
203
203
 
204
+ ### Utilise the default value for verbosity
205
+
206
+ If executed with the arguments
207
+
208
+ ```
209
+ ruby examples/flag_and_option_specifications.rb -d --verbosity=
210
+ ```
211
+
212
+ it gives the output:
213
+
214
+ ```
215
+ verbosity is specified as: terse
216
+ Debug mode is specified
217
+ ```
218
+
204
219
 
@@ -6,7 +6,7 @@
6
6
  # CLASP.Ruby
7
7
  #
8
8
  # Created: 14th February 2014
9
- # Updated: 19th April 2019
9
+ # Updated: 20th April 2019
10
10
  #
11
11
  # Home: http://github.com/synesissoftware/CLASP.Ruby
12
12
  #
@@ -46,7 +46,8 @@
46
46
 
47
47
 
48
48
 
49
- require File.join(File.dirname(__FILE__), 'specifications.rb')
49
+ require File.join(File.dirname(__FILE__), 'specifications')
50
+ require File.join(File.dirname(__FILE__), 'util', 'value_parser')
50
51
 
51
52
  require 'yaml'
52
53
 
@@ -104,6 +105,7 @@ class Arguments
104
105
  @name
105
106
  end
106
107
 
108
+ # @!visibility private
107
109
  def eql?(rhs) # :nodoc:
108
110
 
109
111
  return false if rhs.nil?
@@ -114,6 +116,7 @@ class Arguments
114
116
  false
115
117
  end
116
118
 
119
+ # @!visibility private
117
120
  def ==(rhs) # :nodoc:
118
121
 
119
122
  return false if rhs.nil?
@@ -134,17 +137,40 @@ class Arguments
134
137
  # Class that represents a parsed option
135
138
  class OptionArgument
136
139
 
140
+ include ::CLASP::Util::ValueParser
141
+
137
142
  # @!visibility private
138
143
  #
139
144
  # [PRIVATE] This method is subject to changed between versions and
140
145
  # should not be called directly from application code
141
146
  def initialize(arg, given_index, given_name, resolved_name, argument_spec, given_hyphens, given_label, value, extras) # :nodoc:
142
147
 
143
- actual_value = value
148
+ resolved_value = nil
149
+
150
+ if argument_spec
151
+
152
+ case constraint = (argument_spec.constraint || {})
153
+ =begin
154
+ when Proc
155
+
156
+ resolved_value = value_from_Proc(constraint, value, arg, given_index, given_name, argument_spec, extras)
157
+ =end
158
+ when Hash
159
+
160
+ if constraint.empty?
161
+
162
+ resolved_value = (value || '').empty? ? argument_spec.default_value : value
163
+ else
164
+
165
+ resolved_value = value_from_Hash(constraint, value, arg, given_index, given_name, argument_spec, extras)
166
+ end
167
+ else
144
168
 
145
- if value.nil? && argument_spec
169
+ warn "unexpected constraint on argument specification #{argument_spec} when parsing argument '#{arg}'"
170
+ end
171
+ else
146
172
 
147
- actual_value = argument_spec.default_value
173
+ resolved_value = value
148
174
  end
149
175
 
150
176
  @arg = arg
@@ -153,7 +179,8 @@ class Arguments
153
179
  @argument_specification = argument_spec
154
180
  @given_hyphens = given_hyphens
155
181
  @given_label = given_label
156
- @value = actual_value
182
+ @given_value = value
183
+ @value = resolved_value
157
184
  @name = resolved_name || given_name
158
185
  @extras = extras.nil? ? {} : extras
159
186
  end
@@ -170,7 +197,9 @@ class Arguments
170
197
  attr_reader :given_label
171
198
  # (String) The resolved name of the argument
172
199
  attr_reader :name
173
- # (String) The value of the option
200
+ # (String) The given value of the option
201
+ attr_reader :given_value
202
+ # (????) The value of the option, which may be of a type other than string subject to the option specification's constraint
174
203
  attr_reader :value
175
204
  # (Object, Hash) The extras associated with the argument
176
205
  attr_reader :extras
@@ -178,6 +207,7 @@ class Arguments
178
207
  # [DEPRECATED] Use +argument_specification+
179
208
  def argument_alias; @argument_specification; end
180
209
 
210
+ # @!visibility private
181
211
  def eql?(rhs) # :nodoc:
182
212
 
183
213
  return false if rhs.nil?
@@ -188,6 +218,7 @@ class Arguments
188
218
  false
189
219
  end
190
220
 
221
+ # @!visibility private
191
222
  def ==(rhs) # :nodoc:
192
223
 
193
224
  return false if rhs.nil?
@@ -454,7 +485,7 @@ class Arguments
454
485
  values = []
455
486
 
456
487
  forced_value = false
457
- want_option_value = false
488
+ pending_option = nil
458
489
 
459
490
  argv.each_with_index do |arg, index|
460
491
 
@@ -559,15 +590,22 @@ class Arguments
559
590
 
560
591
  if argument_spec and argument_spec.is_a? CLASP::OptionSpecification and not value
561
592
 
562
- want_option_value = true
563
- options << OptionArgument.new(arg, index, given_name, resolved_name, argument_spec, hyphens.size, given_label, nil, argument_spec ? argument_spec.extras : nil)
593
+ pending_option = {
594
+
595
+ arg: arg,
596
+ index: index,
597
+ given_name: given_name,
598
+ resolved_name: resolved_name,
599
+ argument_spec: argument_spec,
600
+ hyphens_size: hyphens.size,
601
+ given_label: given_label,
602
+ extras: argument_spec ? argument_spec.extras : nil,
603
+ }
564
604
  elsif value
565
605
 
566
- want_option_value = false
567
606
  options << OptionArgument.new(arg, index, given_name, resolved_name, argument_spec, hyphens.size, given_label, value, argument_spec ? argument_spec.extras : nil)
568
607
  else
569
608
 
570
- want_option_value = false
571
609
  flags << FlagArgument.new(arg, index, given_name, resolved_name, argument_spec, hyphens.size, given_label, argument_spec ? argument_spec.extras : nil)
572
610
  end
573
611
 
@@ -575,20 +613,31 @@ class Arguments
575
613
  end
576
614
  end
577
615
 
578
- if want_option_value and not forced_value
616
+ if pending_option
579
617
 
580
- option = options[-1]
581
- option.instance_eval("@value='#{arg}'")
582
- want_option_value = false
583
- else
618
+ value = forced_value ? nil : arg
584
619
 
585
- arg = arg.dup
586
- arg_ix = ::Integer === index ? index : index.dup
620
+ options << OptionArgument.new(pending_option[:arg], pending_option[:index], pending_option[:given_name], pending_option[:resolved_name], pending_option[:argument_spec], pending_option[:hyphens_size], pending_option[:given_label], value, pending_option[:extras])
587
621
 
588
- arg.define_singleton_method(:given_index) { arg_ix }
622
+ pending_option = nil
589
623
 
590
- values << arg
624
+ next unless forced_value
591
625
  end
626
+
627
+ arg = arg.dup
628
+ arg_ix = ::Integer === index ? index : index.dup
629
+
630
+ arg.define_singleton_method(:given_index) { arg_ix }
631
+
632
+ values << arg
633
+ end
634
+
635
+ if pending_option
636
+
637
+ value = nil
638
+
639
+ options << OptionArgument.new(pending_option[:arg], pending_option[:index], pending_option[:given_name], pending_option[:resolved_name], pending_option[:argument_spec], pending_option[:hyphens_size], pending_option[:given_label], value, pending_option[:extras])
640
+
592
641
  end
593
642
 
594
643
  return flags, options, values
@@ -50,6 +50,15 @@ require 'clasp/specifications'
50
50
  require 'clasp/cli'
51
51
  require 'clasp/version'
52
52
 
53
+ module CLASP
54
+
55
+ # TBC (but is a shorthand for calling +Arguments.new()+
56
+ def self.parse(argv = ARGV, specifications = nil, options = {})
57
+
58
+ return Arguments.new(argv, specifications, options)
59
+ end
60
+ end # module CLASP
61
+
53
62
  # ############################## end of file ############################# #
54
63
 
55
64
 
@@ -60,45 +60,49 @@ module CLASP
60
60
  =end
61
61
 
62
62
  # :stopdoc:
63
- module CLI_helpers_
64
63
 
65
- module Constants
64
+ # @!visibility private
65
+ module CLI_helpers_ # :nodoc: all
66
66
 
67
+ # @!visibility private
68
+ module Constants # :nodoc: all
69
+
70
+ # @!visibility private
67
71
  VALID_ALIAS_TYPES = [ FlagSpecification, OptionSpecification, AliasSpecification ]
72
+ # @!visibility private
68
73
  VALID_ALIAS_TYPES_STRING = VALID_ALIAS_TYPES[0...-1].join(', ') + ', or ' + VALID_ALIAS_TYPES[-1].to_s
69
74
  end # module Constants
70
75
 
71
- # :nodoc:
72
- def self.generate_version_string_ options
76
+ # @!visibility private
77
+ def self.generate_version_string_ options # :nodoc:
73
78
 
74
- program_name = options[:program_name] || File.basename($0)
79
+ program_name = options[:program_name] || File.basename($0)
75
80
 
76
- version_prefix = options[:version_prefix]
81
+ version_prefix = options[:version_prefix]
77
82
 
78
- if options[:version]
83
+ if options[:version]
79
84
 
80
- case options[:version]
81
- when ::Array
82
- version = options[:version].join('.')
85
+ case options[:version]
86
+ when ::Array
87
+ version = options[:version].join('.')
88
+ else
89
+ version = options[:version]
90
+ end
83
91
  else
84
- version = options[:version]
85
- end
86
- else
87
92
 
88
- version_major = options[:version_major] or raise ArgumentError, "options must specify :version or :version_major [ + :version_minor [ + :version_revision [ + :version_build ]]]"
89
- version_minor = options[:version_minor]
90
- version_rev = options[:version_revision]
91
- version_build = options[:version_build]
93
+ version_major = options[:version_major] or raise ArgumentError, "options must specify :version or :version_major [ + :version_minor [ + :version_revision [ + :version_build ]]]"
94
+ version_minor = options[:version_minor]
95
+ version_rev = options[:version_revision]
96
+ version_build = options[:version_build]
92
97
 
93
- version = version_major.to_s
94
- version += ".#{version_minor}" if version_minor
95
- version += ".#{version_rev}" if version_rev
96
- version += ".#{version_build}" if version_build
97
- end
98
-
99
- "#{program_name} #{version_prefix}#{version}"
100
- end
98
+ version = version_major.to_s
99
+ version += ".#{version_minor}" if version_minor
100
+ version += ".#{version_rev}" if version_rev
101
+ version += ".#{version_build}" if version_build
102
+ end
101
103
 
104
+ "#{program_name} #{version_prefix}#{version}"
105
+ end
102
106
  end # module CLI_helpers_
103
107
 
104
108
  # ######################################################################## #
@@ -5,7 +5,7 @@
5
5
  # Purpose: Argument specification classes
6
6
  #
7
7
  # Created: 25th October 2014
8
- # Updated: 19th April 2019
8
+ # Updated: 29th April 2019
9
9
  #
10
10
  # Home: http://github.com/synesissoftware/CLASP.Ruby
11
11
  #
@@ -57,8 +57,40 @@ module CLASP
57
57
  # ######################################################################## #
58
58
  # classes
59
59
 
60
+ # @!visibility private
61
+ class SpecificationBase # :nodoc: all
62
+
63
+ private
64
+ # @!visibility private
65
+ def check_arity_(blk, range, label) # :nodoc:
66
+
67
+ raise ArgumentError, "block must be a #{Proc}; #{blk.class} given" unless blk.nil? || Proc === blk
68
+
69
+ if blk
70
+
71
+ case blk.arity
72
+ when range
73
+
74
+ ;
75
+ else
76
+
77
+ msg = "wrong arity for #{label}"
78
+
79
+ if $DEBUG
80
+
81
+ raise ArgumentError, msg
82
+ else
83
+
84
+ warn msg
85
+ end
86
+ end
87
+ end
88
+ end
89
+ public
90
+ end
91
+
60
92
  # A class that represents the specification for a command-line flag
61
- class FlagSpecification
93
+ class FlagSpecification < SpecificationBase
62
94
 
63
95
  # Creates a FlagSpecification instance from the given name, aliases, and help
64
96
  #
@@ -69,12 +101,19 @@ class FlagSpecification
69
101
  # - +aliases+ (+Array+) 0 or more strings specifying short-form or option-value aliases
70
102
  # - +help+ (+String+) The help string, which may be +nil+
71
103
  # - +extras+ An application-defined additional parameter. If +nil+, it is assigned an empty +Hash+
72
- def initialize(name, aliases, help, extras = nil)
104
+ #
105
+ # * *Block* An optional block that is called when a matching flag argument is found
106
+ #
107
+ # *NOTE:* Users should prefer the +CLASP::Flag()+ method
108
+ def initialize(name, aliases, help, extras = nil, &blk)
109
+
110
+ check_arity_(blk, 0..3, "flag")
73
111
 
74
112
  @name = name
75
113
  @aliases = (aliases || []).select { |a| a and not a.empty? }
76
114
  @help = help
77
115
  @extras = extras || {}
116
+ @action = blk
78
117
  end
79
118
 
80
119
  # The flag's name string
@@ -86,12 +125,24 @@ class FlagSpecification
86
125
  # The flag's extras
87
126
  attr_reader :extras
88
127
 
128
+ # (Proc) The procedure
129
+ attr_reader :action
130
+
131
+ # @!visibility private
132
+ def action=(blk) # :nodoc: all
133
+
134
+ check_arity_(blk, 0..3, "flag")
135
+
136
+ @action = blk
137
+ end
138
+
89
139
  # String form of the flag
90
140
  def to_s
91
141
 
92
142
  "{#{name}; aliases=#{aliases.join(', ')}; help='#{help}'; extras=#{extras}}"
93
143
  end
94
144
 
145
+ # @!visibility private
95
146
  def eql? rhs # :nodoc:
96
147
 
97
148
  case rhs
@@ -132,28 +183,38 @@ class FlagSpecification
132
183
  @@Version_ = self.new('--version', [], 'shows version and terminates')
133
184
  public
134
185
  # An instance of FlagSpecification that provides default '--help' information
135
- def self.Help(extras = nil)
186
+ #
187
+ # If you wish to specify +extras+ or attach a block, you may do so
188
+ def self.Help(extras = nil, &blk)
136
189
 
137
190
  h = @@Help_
138
191
 
139
- return self.new(h.name, h.aliases, h.help, extras) if extras
192
+ if extras || blk
193
+
194
+ return self.new(h.name, h.aliases, h.help, extras, &blk)
195
+ end
140
196
 
141
197
  h
142
198
  end
143
199
 
144
200
  # An instance of FlagSpecification that provides default '--version' information
145
- def self.Version(extras = nil)
201
+ #
202
+ # If you wish to specify +extras+ or attach a block, you may do so
203
+ def self.Version(extras = nil, &blk)
146
204
 
147
205
  h = @@Version_
148
206
 
149
- return self.new(h.name, h.aliases, h.help, extras) if extras
207
+ if extras || blk
208
+
209
+ return self.new(h.name, h.aliases, h.help, extras, &blk)
210
+ end
150
211
 
151
212
  h
152
213
  end
153
214
  end
154
215
 
155
216
  # A class that represents the specification for a command-line option
156
- class OptionSpecification
217
+ class OptionSpecification < SpecificationBase
157
218
 
158
219
  # Creates an OptionSpecification instance from the given name, aliases, help,
159
220
  # values_range, and default_value
@@ -168,8 +229,15 @@ class OptionSpecification
168
229
  # - +default_value+ (+String+) The default value of the option, which will be used in the case where an option is specified without a value. May be +nil+
169
230
  # - +required+ (boolean) Whether the option is required. May be +nil+
170
231
  # - +required_message+ (::String) Message to be used when reporting that a required option is missing. May be +nil+ in which case a message of the form "<option-name> not specified; use --help for usage". If begins with the nul character ("\0"), then is used in the place of the <option-name> and placed into the rest of the standard form message
232
+ # - +constraint+ (Hash) Constraint to be applied to the parsed values of options matching this specification. NOTE: only integer constraints are supported in the current version
171
233
  # - +extras+ An application-defined additional parameter. If +nil+, it is assigned an empty +Hash+
172
- def initialize(name, aliases, help, values_range, default_value, required, required_message, extras = nil)
234
+ #
235
+ # * *Block* An optional block that is called when a matching option argument is found
236
+ #
237
+ # *NOTE:* Users should prefer the +CLASP::Option()+ method
238
+ def initialize(name, aliases, help, values_range, default_value, required, required_message, constraint, extras = nil, &blk)
239
+
240
+ check_arity_(blk, 0..3, "option")
173
241
 
174
242
  @name = name
175
243
  @aliases = (aliases || []).select { |a| a and not a.empty? }
@@ -178,7 +246,9 @@ class OptionSpecification
178
246
  @default_value = default_value
179
247
  @required = required
180
248
  @required_message = nil
249
+ @constraint = constraint || {}
181
250
  @extras = extras || {}
251
+ @action = blk
182
252
 
183
253
  rm_name = nil
184
254
 
@@ -213,18 +283,31 @@ class OptionSpecification
213
283
  attr_reader :default_value
214
284
  # Indicates whether the option is required
215
285
  def required?; @required; end
216
- # The message to be used when reporting that a required option is
217
- # missing
286
+ # The message to be used when reporting that a required option is missing
218
287
  attr_reader :required_message
288
+ # The value constraint
289
+ attr_reader :constraint
219
290
  # The option's extras
220
291
  attr_reader :extras
221
292
 
293
+ # (Proc) The procedure
294
+ attr_reader :action
295
+
296
+ # @!visibility private
297
+ def action=(blk) # :nodoc: all
298
+
299
+ check_arity_(blk, 0..3, "flag")
300
+
301
+ @action = blk
302
+ end
303
+
222
304
  # String form of the option
223
305
  def to_s
224
306
 
225
- "{#{name}; aliases=#{aliases.join(', ')}; values_range=[ #{values_range.join(', ')} ]; default_value='#{default_value}'; help='#{help}'; required?=#{required?}; extras=#{extras}}"
307
+ "{#{name}; aliases=#{aliases.join(', ')}; values_range=[ #{values_range.join(', ')} ]; default_value='#{default_value}'; help='#{help}'; required?=#{required?}; required_message=#{required_message}; constraint=#{constraint}; extras=#{extras}}"
226
308
  end
227
309
 
310
+ # @!visibility private
228
311
  def eql? rhs # :nodoc:
229
312
 
230
313
  case rhs
@@ -309,7 +392,9 @@ end
309
392
  # - +:aliases+ (::Array) An array of aliases, e.g. [ '-v', '-verb' ]. Ignored if +:alias+ is specified
310
393
  # - +:extras+ An application-defined object, usually a hash of custom attributes
311
394
  # - +:help+ (::String) A help string
312
- def CLASP.Flag(name, options = {})
395
+ #
396
+ # * *Block* An optional block that is called when a matching flag argument is found
397
+ def CLASP.Flag(name, options = {}, &blk)
313
398
 
314
399
  aliases = nil
315
400
  help = nil
@@ -342,7 +427,7 @@ def CLASP.Flag(name, options = {})
342
427
  end
343
428
  end
344
429
 
345
- CLASP::FlagSpecification.new(name, aliases, help, extras)
430
+ CLASP::FlagSpecification.new(name, aliases, help, extras, &blk)
346
431
  end
347
432
 
348
433
  # Generator method that obtains a CLASP::OptionSpecification according to the given
@@ -364,9 +449,12 @@ end
364
449
  # - +required+ (boolean) Whether the option is required. May be +nil+
365
450
  # - +required_message+ (::String) Message to be used when reporting that a required option is missing. May be +nil+ in which case a message of the form "<option-name> not specified; use --help for usage". If begins with the nul character ("\0"), then is used in the place of the <option-name> and placed into the rest of the standard form message
366
451
  # - +extras+ An application-defined additional parameter. If +nil+, it is assigned an empty +Hash+.
452
+ # - +constraint+ (Hash) Constraint to be applied to the parsed values of options matching this specification. NOTE: only integer constraints are supported in the current version
367
453
  # - +:values_range+ (::Array) An array defining the accepted values for the option
368
454
  # - +:values+ [DEPRECATED] Alternative to +:values_range+
369
- def CLASP.Option(name, options = {})
455
+ #
456
+ # * *Block* An optional block that is called when a matching option argument is found
457
+ def CLASP.Option(name, options = {}, &blk)
370
458
 
371
459
  aliases = nil
372
460
  help = nil
@@ -374,6 +462,7 @@ def CLASP.Option(name, options = {})
374
462
  default_value = nil
375
463
  required = false
376
464
  require_message = nil
465
+ constraint = nil
377
466
  extras = nil
378
467
 
379
468
  options.each do |k, v|
@@ -405,6 +494,9 @@ def CLASP.Option(name, options = {})
405
494
  when :extras
406
495
 
407
496
  extras = v
497
+ when :constraint
498
+
499
+ constraint = v
408
500
  else
409
501
 
410
502
  raise ArgumentError, "invalid option for option: '#{k}' => '#{v}'"
@@ -415,7 +507,7 @@ def CLASP.Option(name, options = {})
415
507
  end
416
508
  end
417
509
 
418
- CLASP::OptionSpecification.new(name, aliases, help, values_range, default_value, required, require_message, extras)
510
+ CLASP::OptionSpecification.new(name, aliases, help, values_range, default_value, required, require_message, constraint, extras, &blk)
419
511
  end
420
512
 
421
513
  def CLASP.Alias(name, *args)