option_list 1.1.0 → 1.1.5

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 CHANGED
@@ -1,15 +1,7 @@
1
1
  ---
2
- !binary "U0hBMQ==":
3
- metadata.gz: !binary |-
4
- OWQxY2I0ZTM2NjM1OTBiZDU2MWNmZDk4MjcyMDQ1ZGUzYWQzNDcyMA==
5
- data.tar.gz: !binary |-
6
- NDUyZWQxNmQ5MzMwZTkzZThhZTFjNzhjMTNmN2Y4ZmY1NDU2N2RmNQ==
2
+ SHA256:
3
+ metadata.gz: 3fc80941391b0ec0003d6410fbacab0e5f54726d7ead3e0a30b85b2a1302f865
4
+ data.tar.gz: 8c13ce0b7069b663805c86f267996794d4a53261c2cf7cce9922284eed3dadaf
7
5
  SHA512:
8
- metadata.gz: !binary |-
9
- ZGNhMjA2NjExMzNjNDAzNzdjNmZmODJkODg1NmQwZDYzOTg2MTkxNTFiMDUx
10
- MmU1NGFkMTMzZGE1MjI1MzU3MWE2M2U3MzEwMDEwYWYyNDI0YmIzYjk4MGQx
11
- NDM3NjEwNTEwYzkxY2YxNjY5MWJmZjc0NDY3NmU3MmMyZmE4NGU=
12
- data.tar.gz: !binary |-
13
- ZDViOTVkN2U5NjMyZDNlYjliZDdlODk2NTM2MzFiZTJmMDExOThjNzhhOTMw
14
- MTM1Yjk5ZTEwNjBiYTE2NmQzNzRkYWI4OWIyM2JhNGIwMGY2NzRiNGE2Zjli
15
- MGJmMjMyYjliNDAzNmVmYmY2MGM1MzBmMzg1ODE1NGEwMDgzZTM=
6
+ metadata.gz: 19e4ce85288187faa5326495572e6b33df72b30792c174e8922aabbcb9e10cd329f8f88e612a704a052f349bd066ae0196f3522b500ddde7bb7bb375000e1c9f
7
+ data.tar.gz: bac17c293cd014626a1e03f069f89315be0615aea23c3c2943846d3698c0cafda9bf138e8da37e7b555a234e029a80983f9bc52e70e2901ca4012f7171c7e48b
data/.gitignore ADDED
@@ -0,0 +1,20 @@
1
+ *.bat
2
+ *.zip
3
+ *.tmp
4
+ *.gem
5
+ *.rbc
6
+ .bundle
7
+ .config
8
+ .yardoc
9
+ Gemfile.lock
10
+ InstalledFiles
11
+ _yardoc
12
+ coverage
13
+ doc/
14
+ lib/bundler/man
15
+ pkg
16
+ rdoc
17
+ spec/reports
18
+ test/tmp
19
+ test/version_tmp
20
+ tmp
@@ -0,0 +1,49 @@
1
+ # Contributor Code of Conduct
2
+
3
+ As contributors and maintainers of this project, and in the interest of
4
+ fostering an open and welcoming community, we pledge to respect all people who
5
+ contribute through reporting issues, posting feature requests, updating
6
+ documentation, submitting pull requests or patches, and other activities.
7
+
8
+ We are committed to making participation in this project a harassment-free
9
+ experience for everyone, regardless of level of experience, gender, gender
10
+ identity and expression, sexual orientation, disability, personal appearance,
11
+ body size, race, ethnicity, age, religion, or nationality.
12
+
13
+ Examples of unacceptable behavior by participants include:
14
+
15
+ * The use of sexualized language or imagery
16
+ * Personal attacks
17
+ * Trolling or insulting/derogatory comments
18
+ * Public or private harassment
19
+ * Publishing other's private information, such as physical or electronic
20
+ addresses, without explicit permission
21
+ * Other unethical or unprofessional conduct
22
+
23
+ Project maintainers have the right and responsibility to remove, edit, or
24
+ reject comments, commits, code, wiki edits, issues, and other contributions
25
+ that are not aligned to this Code of Conduct, or to ban temporarily or
26
+ permanently any contributor for other behaviors that they deem inappropriate,
27
+ threatening, offensive, or harmful.
28
+
29
+ By adopting this Code of Conduct, project maintainers commit themselves to
30
+ fairly and consistently applying these principles to every aspect of managing
31
+ this project. Project maintainers who do not follow or enforce the Code of
32
+ Conduct may be permanently removed from the project team.
33
+
34
+ This code of conduct applies both within project spaces and in public spaces
35
+ when an individual is representing the project or its community.
36
+
37
+ Instances of abusive, harassing, or otherwise unacceptable behavior may be
38
+ reported by contacting a project maintainer at peter.c.camilleri@gmail.com. All
39
+ complaints will be reviewed and investigated and will result in a response that
40
+ is deemed necessary and appropriate to the circumstances. Maintainers are
41
+ obligated to maintain confidentiality with regard to the reporter of an
42
+ incident.
43
+
44
+ This Code of Conduct is adapted from the [Contributor Covenant][homepage],
45
+ version 1.3.0, available at
46
+ [http://contributor-covenant.org/version/1/3/0/][version]
47
+
48
+ [homepage]: http://contributor-covenant.org
49
+ [version]: http://contributor-covenant.org/version/1/3/0/
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in option_list.gemspec
4
+ gemspec
data/README.md ADDED
@@ -0,0 +1,95 @@
1
+ # OptionList
2
+
3
+ This gem addresses the fact that parameter validation is long and
4
+ tedious and something needs to be done about that. This gem implements
5
+ the idea that parameters be described separately and validated in a
6
+ single line of client method code.
7
+
8
+ Most of what this gem does has been subsumed by the Ruby Language itself,
9
+ starting with version 1.9 and further with versions 2.0 and beyond.
10
+
11
+ Finally, I'd like to add a personal note about this code. This was my first
12
+ attempt at creating a gem. As such there is very much a newbie vibe to the
13
+ code. I hope you can chalk this up to just a part of the learning process.
14
+ None the less, if there are improvements that you (the reader) could suggest,
15
+ I'd really appreciate hearing about them.
16
+
17
+ Thanks in advance, Peter.
18
+
19
+
20
+ ## Installation
21
+
22
+ Add this line to your application's Gemfile:
23
+
24
+ gem 'option_list'
25
+
26
+ And then execute:
27
+
28
+ $ bundle
29
+
30
+ Or install it yourself as:
31
+
32
+ $ gem install option_list
33
+
34
+ The options_list gem is at: ( https://rubygems.org/gems/options_list )
35
+
36
+ ## Usage
37
+
38
+ The use of option_list occurs in three phases: Describing the Parameters,
39
+ Passing in Parameters and Validating/Accessing the Parameters. This can be
40
+ seen in the following example:
41
+ ```ruby
42
+ module ReadLine
43
+ #Create the parameter specification (simplified for brevity)
44
+ @spec = OptionList.new([:buffer, :history, :no_history], {:depth => 50}) do |options|
45
+ fail "Depth must be an integer" unless options.depth.is_a(Integer)
46
+ fail "Depth must be positive" if options.depth < 1
47
+ end
48
+
49
+ class << self
50
+ attr_reader :spec
51
+ end
52
+
53
+ def read_line(prompt, *options)
54
+ @options = ReadLine.spec.select(options)
55
+ #Further code deleted for brevity.
56
+ #Somewhere along the line it records the last line.
57
+ buffer_line(current_line)
58
+ current_line
59
+ end
60
+
61
+ def buffer_line(line)
62
+ @line_buffer << line if @options.history?
63
+ @line_buffer.delete_at(0) if @line_buffer.length > @options.depth
64
+ end
65
+ end
66
+ ```
67
+ The option_list gem is described in the The option_list User's Guide
68
+ which covers version 1.1.1 which has no material change from 1.1.3
69
+
70
+ ## Contributing
71
+
72
+ #### Plan A
73
+
74
+ 1. Fork it ( https://github.com/PeterCamilleri/option_list/fork )
75
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
76
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
77
+ 4. Push to the branch (`git push origin my-new-feature`)
78
+ 5. Create new Pull Request
79
+
80
+ #### Plan B
81
+
82
+ Go to the GitHub repository and raise an issue calling attention to some
83
+ aspect that could use some TLC or a suggestion or an idea.
84
+
85
+ ## License
86
+
87
+ The gem is available as open source under the terms of the
88
+ [MIT License](./LICENSE.txt).
89
+
90
+ ## Code of Conduct
91
+
92
+ Everyone interacting in the fully_freeze project’s codebases, issue trackers,
93
+ chat rooms and mailing lists is expected to follow the
94
+ [code of conduct](./CODE_OF_CONDUCT.md).
95
+
data/docs/OL_UG.odt ADDED
Binary file
data/docs/OL_UG.pdf ADDED
Binary file
Binary file
data/lib/option_list.rb CHANGED
@@ -4,158 +4,59 @@ require 'set'
4
4
  #This file contains the code for the \OptionList class that implements smart
5
5
  #and easy options for functions. It has the virtue of defining function
6
6
  #options clearly in a single place instead of spread out in complex code.
7
- #A classic but unclear sequence in many functions is the use of boolean values
8
- #to control or switch on or off some option. For example consider the well
9
- #known readline function:
10
- # cmd = readline(">", false)
11
- #See the boolean at the end? It says "false". What is "false"? Who knows? You
12
- #need to dig deeper or guess or just cut and paste without understanding. What
13
- #if, instead, the code looked like:
14
- # cmd = readline(">", :nohistory)
15
- #The boolean has been replaced by something a lot clearer. The problem with
16
- #such clarity is that it involves a lot more effort than the lazy boolean. The
17
- #\OptionList class aims to solve that issue by making useful, flexible options
18
- #just about as easy to use as the lazy way.
19
- #==== Warning: This code is most opinionated! ;-)
20
- #The philosophy and opinion expressed through this code is that errors should
21
- #be detected as close to the point of error as possible and that an error is
22
- #preferable to running with potentially incorrect results. As a result, it
23
- #will be noted that the programmer does not hesitate to use the fAE, a wrapper
24
- #of the "fail" keyword, liberally throughout the code to achieve this end.
25
- #Complimentary to this is the idea that the library (gem) writer should do as
26
- #much of the "heavy lifting" as possible, with clear, detailed documentation so
27
- #that the library (gem) user does not have to work hard or be lost or confused.
28
- #Only time will tell to what extent these lofty goals have been achieved.
29
- #Hopefully, with good feedback, this and other code libraries will improve.
30
- #=== Version 1.0.1
31
- #This version represents a major overhaul of the \OptionList class. In
32
- #particular, the selected option data and the dynamic methods used to access
33
- #that data are no longer contained in the option list object but instead in
34
- #a singleton subclass of Hash that is returned by the select method. There
35
- #are several advantages to this, but the main one is that since \OptionList
36
- #objects now only contain specification and default values, they are much more
37
- #thread safe than before. While there is nothing multi-threaded about the
38
- #\OptionList class itself, it is reasonable to assume that a function option
39
- #handler could very easily end up embedded in such an environment. In fact, my
40
- #first major test of the class ran into this exact issue.
7
+ #==== User's Guide
8
+ #Most of the documentation for this gem has been moved to
9
+ #{Option List User's Guide}[http://teuthida-technologies.com/guides/OL_UG_Version_1_1_1.pdf]
10
+ #====Version 1.1.1
11
+ #Embraced reek and git and created the user's guide to pair down rdoc mark up
12
+ #to be less obtrusive to the code.
41
13
  #=== Version 1.1.0
42
- #Added a default value of false to signify that no default exists for a
14
+ #Added a default value of false to signify that no default exists for a
43
15
  #mandatory parameter.
44
16
  #Modified processing of array specs to avoid side effects in the parameters.
45
17
  class OptionList
46
18
 
47
19
  #The option list code version.
48
20
  def self.version
49
- '1.1.0'
21
+ OptionList::VERSION
50
22
  end
51
-
23
+
52
24
  #The option list code version. This is a redirect to the class method.
53
25
  def version
54
- self.class.version
26
+ OptionList::VERSION
55
27
  end
56
28
 
57
- #Create an option list from an array of option specifications. These
58
- #specifications consist of a number of array specifications and hash
59
- #specifications. These are described below:
60
- #==== Array Specification
61
- #Array specifications are used in cases where an option category may be one
62
- #of a fixed number of distinct symbol values or optionally nil to represent
63
- #no selection.
64
- #In this form of specification, the first element of the array is the
65
- #category of the option, and the second and following entries are the
66
- #allowed values for that category. The second element has a special role. It
67
- #is the default value for the category. This value may be one of:
68
- #* A symbol - The default symbolic value for this option.
69
- #* nil - The default value is nil and this setting is optional.
70
- #* false - There is no default value and this setting is mandatory.
71
- #The symbols used in each category must be unique across
72
- #all of the categories in the option list.
73
- #==== Hash Specification
74
- #Hash specifications are used to specify options that do not have a fixed set
75
- #of possible values. In this form of specification, the hash key symbol is
76
- #the category and the hash value is the default value for the category. It
77
- #may be nil or any other type or value. Allowed values are not listed.
78
- #==== Example:
79
- # @opt_list = OptionList.new([:history, :history, :nohistory], {:page_len => 42})
29
+ #Create an option list from an array of option specifications.
80
30
  #==== Parameters:
81
- #* option_specs - The comma separated option specifications, made into an
31
+ #* option_specs - The comma separated option specifications, made into an
82
32
  # array by the splat operator.
83
33
  #* select_block - An optional block of code that is called when selections
84
34
  # have been made. This allows for custom validations to be applied at that
85
35
  # point. This block should accept one argument, a reference to the option
86
36
  # list value object that is calling it for validation.
87
- #==== Dynamic Methods:
88
- #As option specifications are added, new methods are created in the value
89
- #object to allow for easy access to the option information. If the above
90
- #example were processed, the following methods would be added to the value
91
- #returned by the select method:
92
- #* history - would return the value of the :history category.
93
- #* history? - would return true if the :history option where active.
94
- #* nohistory? - would return true if the :nohistory option were active.
95
- #* page_len - would return the value of the :page_len category.
96
37
  #==== Exceptions:
97
38
  #* ArgumentError for a number of invalid argument conditions.
98
39
  def initialize(*option_specs, &select_block)
99
- fAE "Missing option specifications." if option_specs.empty?
40
+ error "Missing option specifications." if option_specs.empty?
100
41
  @mandatory = Array.new
101
42
  @categories = Hash.new
102
43
  @default = Hash.new
103
-
44
+
104
45
  option_specs.each do |spec|
105
46
  if spec.is_a?(Hash)
106
47
  hash_spec(spec)
107
48
  elsif spec.is_a?(Array)
108
49
  array_spec(spec)
109
50
  else
110
- fAE "Found #{spec.class} instead of Hash or Array."
51
+ error "Found #{spec.class} instead of Hash or Array."
111
52
  end
112
53
  end
113
-
54
+
114
55
  @select_block = select_block
115
56
  end
116
57
 
117
58
  #From the possible options, select the actual, in force, options and return
118
- #an access object for use in the code. These options may take several forms:
119
- #=== Types of option data:
120
- #* A symbol - For categories with a specified symbol list, simply list one
121
- # of the allowed symbols.
122
- #* A hash - For any type of category, a hash may be used in the the form
123
- # {category => value}. Note that for categories with a specified symbol list
124
- # the value must be a symbol in the list.
125
- #* Mixed - Symbols and hashes can be mixed in an array (see below on how this
126
- # is done) Note: Option order does not matter.
127
- #Some examples:
128
- # o = foo(:sedan, :turbo)
129
- # o = foo({:style=>:sedan})
130
- # o = foo(page_len: 60)
131
- # o = foo(:sedan, :turbo, page_len: 60)
132
- # o = foo(style: :sedan, page_len: 60)
133
- #=== Passing in option data:
134
- #The caller of this method may pass these options in a number of ways:
135
- #==== Array (via a splat)
136
- #Given the example shown in the new method, consider:
137
- # def test(count, *opt)
138
- # @opt_list.select(opt)
139
- # #etc, etc, etc...
140
- # end
141
- #With the caller now looking like:
142
- # test(43, :history)
143
- #==== Array (explicit)
144
- #An alternative strategy, where the use of splat is not desired, is to simply
145
- #have an array argument, as below:
146
- # def test(*arg1, opt)
147
- # @opt_list.select(opt)
148
- # #etc, etc, etc...
149
- # end
150
- #With the caller now looking like:
151
- # test(34, 53, 76, 'hike!', [:history])
152
- #==== Hash
153
- #Options may also be passed via a hash.
154
- # test(34, 53, 76, 'hike!', {:history=>:history, :page_len=>55})
155
- # test(34, 53, 76, 'hike!', history: :history, page_len: 55)
156
- #==== Symbol
157
- #If only a single option is required, it can be passed simply as:
158
- # test(34, 53, 76, 'hike!', :history)
59
+ #an access object for use in the code.
159
60
  #==== Parameters:
160
61
  #* selections - An array of the options passed into the client function, usually
161
62
  # with the splat operator. Note that if select is called with no arguments,
@@ -165,12 +66,25 @@ class OptionList
165
66
  #==== Exceptions:
166
67
  #* ArgumentError for a number of invalid argument conditions.
167
68
  #==== Notes:
168
- #After processing the selections, the selection validation block is called
69
+ #After processing the selections, the selection validation block is called
169
70
  #if one was defined for the constructor.
170
71
  def select(selections=[])
171
- selected = @default.clone
172
72
  selections = [selections] unless selections.is_a?(Array)
173
- dup = Set.new
73
+ selected = process_selections(selections)
74
+
75
+ @mandatory.each do |cat|
76
+ error "Missing mandatory setting #{cat}" unless selected[cat]
77
+ end
78
+
79
+ @select_block.call(selected) if @select_block
80
+ selected
81
+ end
82
+
83
+ private #Private stuff follows.
84
+
85
+ #Process a list of option selections.
86
+ def process_selections(selections)
87
+ selected, dup = @default.clone, Set.new
174
88
 
175
89
  selections.each do |opt|
176
90
  if opt.is_a?(Symbol)
@@ -178,104 +92,136 @@ class OptionList
178
92
  elsif opt.is_a?(Hash)
179
93
  hash_selections(opt, selected, dup)
180
94
  else
181
- fAE "Found #{opt.class} instead of Hash or Symbol."
95
+ error "Found #{opt.class} instead of Hash or Symbol."
182
96
  end
183
97
  end
184
-
185
- @mandatory.each do |cat|
186
- fAE "Missing mandatory setting #{cat}" unless selected[cat]
187
- end
188
-
189
- @select_block.call(selected) unless @select_block.nil?
98
+
190
99
  selected
191
100
  end
192
-
193
- private #Private stuff follows.
194
101
 
195
102
  #Return a internal category marker constant for value entries.
196
103
  def value_entry
197
104
  'A value entry.'
198
105
  end
199
-
106
+
200
107
  #Process an array spec that lists all the valid values for an option. See
201
108
  #the new method for more information on these specs.
202
109
  def array_spec(spec)
203
- cat = spec[0]
204
- spec = spec[1...spec.length]
205
-
206
- fAE "Found #{cat.class}, expected Symbol." unless cat.is_a?(Symbol)
207
- fAE "Duplicate category: #{cat}" if @default.has_key?(cat)
208
- fAE "Invalid number of entries for #{cat}." unless spec.length > 1
209
- @default.define_singleton_method(cat) { self[cat] }
210
-
211
- spec.each_with_index do |opt, index|
110
+ category, default, spec_len = spec[0], spec[1], spec.length
111
+ error "Invalid number of entries for #{category}." unless spec_len > 2
112
+ add_option_reader(category)
113
+ array_spec_default(category, default)
114
+ array_spec_tail_rest(category, spec[2...spec_len])
115
+ end
116
+
117
+ #Process the first element of the array spec tail.
118
+ def array_spec_default(category, opt)
119
+ opt && array_spec_single(category, opt)
120
+ @default[category] = opt
121
+ @mandatory << category if opt == false
122
+ end
123
+
124
+ #Process the rest of the array spec tail.
125
+ def array_spec_tail_rest(category, spec_tail_rest)
126
+ spec_tail_rest.each do |opt|
212
127
  if opt
213
- fAE "Found #{opt.class}, expected Symbol." unless opt.is_a?(Symbol)
214
- fAE "Duplicate option: #{opt}" if @categories.has_key?(opt)
215
-
216
- @categories[opt] = cat
217
- qry = (opt.to_s + '?').to_sym
218
- @default.define_singleton_method(qry) { self[cat] == opt }
219
- @default[cat] = opt if index == 0
220
- elsif index == 0
221
- @default[cat] = opt
222
- @mandatory << cat if opt == false
128
+ array_spec_single(category, opt)
223
129
  else
224
- fAE "The values nil/false are only allowed as the default option."
130
+ error "The values nil/false are only allowed as the default option."
225
131
  end
226
132
  end
227
133
  end
228
-
134
+
135
+ #Process a single array spec option
136
+ def array_spec_single(category, opt)
137
+ duplicate_entry_check(@categories, opt, 'option')
138
+ @categories[opt] = category
139
+ add_option_tester(category, opt)
140
+ end
141
+
229
142
  #Process a hash spec that lists only the default value for an option. See
230
143
  #the new method for more information on these specs.
231
144
  def hash_spec(spec)
232
- fAE "Hash contains no specs." unless spec.length > 0
145
+ error "Hash contains no specs." unless spec.length > 0
233
146
 
234
- spec.each do |cat, value|
235
- fAE "Found #{cat.class}, expected Symbol." unless cat.is_a?(Symbol)
236
- fAE "Duplicate category: #{cat}" if @default.has_key?(cat)
237
-
238
- @default.define_singleton_method(cat) { self[cat] }
239
- @categories[cat] = value_entry
240
- @default[cat] = value
147
+ spec.each do |category, value|
148
+ add_option_reader(category)
149
+ set_default_option_value(category, value)
150
+ @mandatory << category if value == false
241
151
  end
242
152
  end
243
-
153
+
154
+ #Set the default value of a value entry.
155
+ def set_default_option_value(category, value)
156
+ @categories[category] = value_entry
157
+ @default[category] = value
158
+ end
159
+
244
160
  #Process a symbolic option selection.
245
161
  #==== Parameters:
246
162
  #* option - a symbol to process.
247
163
  #* selected - a hash of selected data.
248
164
  #* dup - a set of categories that have been set. Used to detect duplicates.
249
- def symbolic_selection(option, selected, dup)
250
- fAE "Unknown option: #{option}." unless @categories.has_key?(option)
251
- cat = @categories[option]
252
- fAE "Category #{cat} has multiple values." if dup.include?(cat)
253
- dup.add(cat)
254
- selected[cat] = option
165
+ def symbolic_selection(symbol_option, selected, dup)
166
+ missing_entry_check(@categories, symbol_option, 'option')
167
+ category = @categories[symbol_option]
168
+ hash_option_dup_check(category, dup)
169
+ selected[category] = symbol_option
255
170
  end
256
-
171
+
257
172
  #Process a hash of option selection values.
258
173
  #==== Parameters:
259
174
  #* options - a hash of options to process.
260
175
  #* selected - a hash of selected data.
261
176
  #* dup - a set of categories that have been set. Used to detect duplicates.
262
- def hash_selections(options, selected, dup)
263
- options.each do |cat, value|
264
- fAE "Not a category: #{cat}." unless @default.has_key?(cat)
265
- fAE "Category #{cat} has multiple values." if dup.include?(cat)
266
-
267
- unless (@categories[cat] == value_entry) || value.nil?
268
- fAE "Found #{opt.class}, expected Symbol." unless value.is_a?(Symbol)
269
- fAE "Invalid option: #{value}." unless @categories[value] == cat
270
- end
271
-
272
- dup.add(cat)
273
- selected[cat] = value
177
+ def hash_selections(hash_options, selected, dup)
178
+ hash_options.each do |category, value|
179
+ missing_entry_check(@default, category, 'category')
180
+
181
+ hash_option_value_check(category, value)
182
+ hash_option_dup_check(category, dup)
183
+ selected[category] = value
184
+ end
185
+ end
186
+
187
+ #Validate a hash option value.
188
+ def hash_option_value_check(value_category, value)
189
+ if (@categories[value_category] != value_entry) && value
190
+ error "Found #{value.class}, expected Symbol." unless value.is_a?(Symbol)
191
+ error "Invalid option: #{value}." unless @categories[value] == value_category
274
192
  end
275
193
  end
276
194
 
195
+ #Add to set with no duplicates allowed.
196
+ def hash_option_dup_check(category, dup)
197
+ error "Category #{category} has multiple values." unless dup.add?(category)
198
+ end
199
+
200
+ #Add query method for the selected category.
201
+ def add_option_reader(name)
202
+ duplicate_entry_check(@default, name, 'category')
203
+ @default.define_singleton_method(name) { self[name] }
204
+ end
205
+
206
+ #Add a query method (eg: has_stuff? ) for the selected option.
207
+ def add_option_tester(target, value)
208
+ qry = (value.to_s + '?').to_sym
209
+ @default.define_singleton_method(qry) { self[target] == value}
210
+ end
211
+
212
+ #Flag any duplicate entry errors.
213
+ def duplicate_entry_check(target, entry, detail)
214
+ error "Found #{entry.class}, expected Symbol." unless entry.is_a?(Symbol)
215
+ error "Duplicate #{detail}: #{entry}" if target.has_key?(entry)
216
+ end
217
+
218
+ #Flag any missing entry errors.
219
+ def missing_entry_check(target, entry, detail)
220
+ error "Unknown #{detail}: #{entry}" unless target.has_key?(entry)
221
+ end
222
+
277
223
  #Fail with an argument error.
278
- def fAE(msg)
279
- fail(ArgumentError, msg, caller)
224
+ def error(messsage)
225
+ fail(ArgumentError, messsage, caller)
280
226
  end
281
227
  end
@@ -0,0 +1,3 @@
1
+ class OptionList
2
+ VERSION = '1.1.5'
3
+ end
data/license.txt CHANGED
@@ -1,6 +1,6 @@
1
1
  === The MIT License (MIT).
2
2
 
3
- Copyright (c) 2013 Peter Camilleri
3
+ Copyright (c) 2013, 2014 Peter Camilleri
4
4
 
5
5
  Permission is hereby granted, free of charge, to any person obtaining a copy
6
6
  of this software and associated documentation files (the "Software"), to deal
@@ -0,0 +1,34 @@
1
+ # coding: utf-8
2
+
3
+ #Specify the building of the option_list gem.
4
+
5
+ lib = File.expand_path('../lib', __FILE__)
6
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
7
+ require 'option_list/version'
8
+
9
+ Gem::Specification.new do |s|
10
+ s.name = "option_list"
11
+ s.summary = "Flexible, Easy Function Parameters with Validation."
12
+ s.description = '[Deprecated] Flexible, Easy Function Parameters with Validation. '
13
+ s.version = OptionList::VERSION
14
+ s.author = ["Peter Camilleri"]
15
+ s.email = "peter.c.camilleri@gmail.com"
16
+ s.homepage = "http://teuthida-technologies.com/"
17
+ s.platform = Gem::Platform::RUBY
18
+ s.required_ruby_version = '>=1.9.3'
19
+
20
+ s.add_development_dependency "bundler", ">= 2.1.0"
21
+ s.add_development_dependency 'rake'
22
+ s.add_development_dependency 'reek', "~> 1.3.8"
23
+ s.add_development_dependency 'minitest', "~> 4.7.5"
24
+ s.add_development_dependency 'rdoc', "~> 4.0.1"
25
+ s.add_development_dependency 'awesome_print'
26
+
27
+ s.files = `git ls-files`.split($/)
28
+ s.test_files = s.files.grep(%r{^(test|spec|features)/})
29
+ s.extra_rdoc_files = ['license.txt']
30
+
31
+ s.license = 'MIT'
32
+ s.require_path = 'lib'
33
+ end
34
+
data/rakefile.rb CHANGED
@@ -1,11 +1,16 @@
1
1
  #!/usr/bin/env rake
2
+ # coding: utf-8
3
+
2
4
  require 'rake/testtask'
3
5
  require 'rdoc/task'
6
+ require "bundler/gem_tasks"
4
7
 
5
8
  RDoc::Task.new do |rdoc|
6
9
  rdoc.rdoc_dir = "rdoc"
7
- #rdoc.main = "option_list.rb"
8
- rdoc.rdoc_files = ['lib/option_list.rb', 'tests/option_list_test.rb', 'license.txt']
10
+
11
+ #List out all the files to be documented.
12
+ rdoc.rdoc_files.include("lib/**/*.rb", "license.txt", "README.md")
13
+
9
14
  rdoc.options << '--visibility' << 'private'
10
15
  end
11
16
 
@@ -13,3 +18,14 @@ Rake::TestTask.new do |t|
13
18
  t.test_files = ['tests/option_list_test.rb']
14
19
  t.verbose = false
15
20
  end
21
+
22
+ desc "Run a scan for smelly code!"
23
+ task :reek do |t|
24
+ `reek lib\\*.rb > reek.txt`
25
+ end
26
+
27
+ desc "What version of option_list is this?"
28
+ task :vers do |t|
29
+ puts
30
+ puts "option_list version = #{OptionList::VERSION}"
31
+ end
data/reek.txt ADDED
@@ -0,0 +1 @@
1
+ lib/option_list.rb -- 0 warnings
data/sire.rb ADDED
@@ -0,0 +1,51 @@
1
+ # Really simple program # 3
2
+ # A Simple Interactive Ruby Environment
3
+ # SIRE Version 0.2.6
4
+
5
+ require 'readline'
6
+ require 'awesome_print'
7
+ require_relative 'lib/option_list'
8
+ include Readline
9
+
10
+ class Object
11
+ def classes
12
+ begin
13
+ klass = self
14
+
15
+ begin
16
+ klass = klass.class unless klass.instance_of?(Class)
17
+ print klass
18
+ klass = klass.superclass
19
+ print " < " if klass
20
+ end while klass
21
+
22
+ puts
23
+ end
24
+ end
25
+ end
26
+
27
+ def show_args(*args)
28
+ args
29
+ end
30
+
31
+ puts "Welcome to SIRE Version 0.2.6"
32
+ puts "Simple Interactive Ruby Environment"
33
+ done = false
34
+
35
+ until done
36
+ begin
37
+ line = readline('SIRE>', true)
38
+ result = eval line
39
+ ap result unless result.nil?
40
+ rescue Interrupt
41
+ done = true
42
+ rescue Exception => e
43
+ puts "#{e.class} detected: #{e}"
44
+ puts e.backtrace
45
+ puts
46
+ end
47
+ end
48
+
49
+ puts
50
+ puts "Bye bye for now!"
51
+
@@ -8,73 +8,73 @@ class OptionTest
8
8
  def initialize(opt)
9
9
  @opt = opt
10
10
  end
11
-
11
+
12
12
  def test(*args)
13
- o = @opt.select(args)
13
+ @opt.select(args)
14
14
  end
15
15
  end
16
16
 
17
- class OptionListTester < MiniTest::Unit::TestCase
17
+ class OptionListTester < Minitest::Test
18
18
  $do_this_only_one_time = ""
19
-
19
+
20
20
  def initialize(*all)
21
21
  if $do_this_only_one_time != __FILE__
22
22
  puts
23
- puts "Running test file: #{File.split(__FILE__)[1]}"
23
+ puts "Running test file: #{File.split(__FILE__)[1]}"
24
24
  $do_this_only_one_time = __FILE__
25
25
  end
26
-
26
+
27
27
  super(*all)
28
28
  end
29
29
 
30
30
  def test_that_it_rejects_bad_specs
31
31
  #Reject empty argument lists.
32
- assert_raises(ArgumentError) { @x = OptionList.new }
32
+ assert_raises(ArgumentError) { @x = OptionList.new }
33
33
  assert_raises(ArgumentError) { @x = OptionList.new([])}
34
34
  assert_raises(ArgumentError) { @x = OptionList.new({})}
35
-
35
+
36
36
  #Reject if not an array or a hash.
37
37
  assert_raises(ArgumentError) { @x = OptionList.new(4) }
38
- assert_raises(ArgumentError) { @x = OptionList.new('foobar') }
38
+ assert_raises(ArgumentError) { @x = OptionList.new('foobar') }
39
39
 
40
40
  #Reject for too few arguments.
41
41
  assert_raises(ArgumentError) { @x = OptionList.new([]) }
42
42
  assert_raises(ArgumentError) { @x = OptionList.new([:foo]) }
43
43
  assert_raises(ArgumentError) { @x = OptionList.new([:foo, :bar]) }
44
-
44
+
45
45
  #Reject for the wrong types of arguments.
46
46
  assert_raises(ArgumentError) { @x = OptionList.new(['foo', :foo, :bar] )}
47
47
  assert_raises(ArgumentError) { @x = OptionList.new([:foo, 'foo', :bar] )}
48
48
  assert_raises(ArgumentError) { @x = OptionList.new([:foo, :foo, 'bar'])}
49
49
  assert_raises(ArgumentError) { @x = OptionList.new({'foo' => 42})}
50
-
50
+
51
51
  #Reject for duplicate categories.
52
52
  assert_raises(ArgumentError) { @x = OptionList.new([:foo, :lala, :bar], [:foo, :kung, :east]) }
53
53
  assert_raises(ArgumentError) { @x = OptionList.new([:foo, :lala, :bar], {:foo => :kung}) }
54
54
  assert_raises(ArgumentError) { @x = OptionList.new({:foo => :lala}, {:foo => :kung}) }
55
55
  #The following is not detectable since :foo => :kung overwrites :foo => :lala
56
56
  #assert_raises(ArgumentError) { @x = OptionList.new({:foo => :lala, :foo => :kung}) }
57
-
57
+
58
58
  #Reject for duplicate options.
59
59
  assert_raises(ArgumentError) { @x = OptionList.new([:foo, :bar, :bar]) }
60
- assert_raises(ArgumentError) { @x = OptionList.new([:foo, :foo, :bar], [:bla, :food, :bar]) }
60
+ assert_raises(ArgumentError) { @x = OptionList.new([:foo, :foo, :bar], [:bla, :food, :bar]) }
61
61
 
62
- #Reject for nil in the wrong position.
62
+ #Reject for nil in the wrong position.
63
63
  assert_raises(ArgumentError) { @x = OptionList.new([:foo, :bar, nil]) }
64
64
  end
65
-
65
+
66
66
  def test_that_the_methods_were_added
67
67
  ol1 = OptionList.new([:history, :history, :nohistory], {:pg_len => 42})
68
68
  o = ol1.select
69
-
69
+
70
70
  assert_respond_to(o, :history)
71
71
  assert_respond_to(o, :history? )
72
72
  assert_respond_to(o, :nohistory? )
73
73
  assert_respond_to(o, :pg_len)
74
74
 
75
- ol2 = OptionList.new([:history, nil, :history, :nohistory])
75
+ ol2 = OptionList.new([:history, nil, :history, :nohistory])
76
76
  o = ol2.select
77
-
77
+
78
78
  assert_respond_to(o, :history)
79
79
  assert_respond_to(o, :history? )
80
80
  assert_respond_to(o, :nohistory? )
@@ -83,76 +83,76 @@ class OptionListTester < MiniTest::Unit::TestCase
83
83
 
84
84
  def test_that_it_rejects_bad_selections
85
85
  ol1 = OptionList.new([:history, :history, :nohistory], {:pg_len => 42})
86
-
86
+
87
87
  #Reject if options are not an array or a hash.
88
88
  assert_raises(ArgumentError) { ol1.select(45) }
89
-
89
+
90
90
  #Reject if the option is not a symbol.
91
91
  assert_raises(ArgumentError) { ol1.select([34]) }
92
-
92
+
93
93
  #Reject if the category is not a symbol.
94
94
  assert_raises(ArgumentError) { ol1.select({'page_len'=>77}) }
95
-
95
+
96
96
  #Reject if the symbol is not one defined.
97
97
  assert_raises(ArgumentError) { ol1.select([:foobar]) }
98
98
  assert_raises(ArgumentError) { ol1.select({:history=>:foobar}) }
99
-
99
+
100
100
  #Reject on duplicate symbol from the same category.
101
101
  assert_raises(ArgumentError) { ol1.select([:history, :history]) }
102
- assert_raises(ArgumentError) { ol1.select([:history, :nohistory]) }
102
+ assert_raises(ArgumentError) { ol1.select([:history, :nohistory]) }
103
103
  assert_raises(ArgumentError) { ol1.select([:history, {:history=>:nohistory}]) }
104
104
  assert_raises(ArgumentError) { ol1.select([{:history=>:history}, {:history=>:nohistory}]) }
105
-
105
+
106
106
  #Reject on an undefined category.
107
107
  assert_raises(ArgumentError) { ol1.select({:zoo => 999})}
108
108
  end
109
-
109
+
110
110
  def test_that_it_handles_good_options
111
111
  #ol1 test series.
112
112
  ol1 = OptionList.new([:history, :history, :nohistory], {:pg_len => 42})
113
-
113
+
114
114
  o = ol1.select
115
115
  assert(o.history?)
116
116
  refute(o.nohistory?)
117
117
  assert_equal(o.history, :history)
118
- assert_equal(o.pg_len, 42)
119
-
118
+ assert_equal(o.pg_len, 42)
119
+
120
120
  o = ol1.select([])
121
121
  assert(o.history?)
122
122
  refute(o.nohistory?)
123
123
  assert_equal(o.history, :history)
124
- assert_equal(o.pg_len, 42)
125
-
124
+ assert_equal(o.pg_len, 42)
125
+
126
126
  o = ol1.select([:history])
127
127
  assert(o.history?)
128
128
  refute(o.nohistory?)
129
129
  assert_equal(o.history, :history)
130
- assert_equal(o.pg_len, 42)
131
-
130
+ assert_equal(o.pg_len, 42)
131
+
132
132
  o = ol1.select([:nohistory])
133
133
  refute(o.history?)
134
134
  assert(o.nohistory?)
135
135
  assert_equal(o.history, :nohistory)
136
- assert_equal(o.pg_len, 42)
136
+ assert_equal(o.pg_len, 42)
137
137
 
138
138
  o = ol1.select({:history=>:history})
139
139
  assert(o.history?)
140
140
  refute(o.nohistory?)
141
141
  assert_equal(o.history, :history)
142
- assert_equal(o.pg_len, 42)
143
-
142
+ assert_equal(o.pg_len, 42)
143
+
144
144
  o = ol1.select({:history=>:nohistory})
145
145
  refute(o.history?)
146
146
  assert(o.nohistory?)
147
147
  assert_equal(o.history, :nohistory)
148
- assert_equal(o.pg_len, 42)
148
+ assert_equal(o.pg_len, 42)
149
149
 
150
150
  o = ol1.select({:pg_len=>55})
151
151
  assert(o.history?)
152
152
  refute(o.nohistory?)
153
153
  assert_equal(o.history, :history)
154
154
  assert_equal(o.pg_len, 55)
155
-
155
+
156
156
  o = ol1.select({:history=>:history, :pg_len=>55})
157
157
  assert(o.history?)
158
158
  refute(o.nohistory?)
@@ -177,10 +177,10 @@ class OptionListTester < MiniTest::Unit::TestCase
177
177
  assert_equal(o.history, :nohistory)
178
178
  assert_equal(o.pg_len, 55)
179
179
 
180
-
180
+
181
181
  #ol2 test series.
182
- ol2 = OptionList.new([:history, nil, :history, :nohistory])
183
-
182
+ ol2 = OptionList.new([:history, nil, :history, :nohistory])
183
+
184
184
  o = ol2.select([:history])
185
185
  assert(o.history?)
186
186
  refute(o.nohistory?)
@@ -196,9 +196,9 @@ class OptionListTester < MiniTest::Unit::TestCase
196
196
  refute(o.nohistory?)
197
197
  assert(o.history.nil?)
198
198
  end
199
-
199
+
200
200
  def test_that_mandatory_parms_work
201
- ol2 = OptionList.new([:history, false, :history, :nohistory])
201
+ ol2 = OptionList.new([:history, false, :history, :nohistory])
202
202
 
203
203
  o = ol2.select([:history])
204
204
  assert(o.history?)
@@ -211,24 +211,30 @@ class OptionListTester < MiniTest::Unit::TestCase
211
211
  assert_equal(o.history, :nohistory)
212
212
 
213
213
  assert_raises(ArgumentError) { ol2.select() }
214
+
215
+ ol3 = OptionList.new(page_len: false)
216
+ o = ol3.select(page_len: 42)
217
+ assert_equal(o.page_len, 42)
218
+
219
+ assert_raises(ArgumentError) { ol3.select() }
214
220
  end
215
-
221
+
216
222
  def test_that_it_does_not_munge_parms
217
223
  parm1 = [:history, false, :history, :nohistory]
218
224
  parm2 = parm1.clone
219
- ol2 = OptionList.new(parm1)
225
+ OptionList.new(parm1)
220
226
  assert_equal(parm1, parm2)
221
227
  end
222
-
228
+
223
229
  def test_that_the_select_block_works
224
230
  ol3 = OptionList.new([:history, nil, :history, :nohistory],
225
231
  fuel1: :matter, fuel2: :antimatter) do |opt|
226
232
  fail "The :history option must be set." if opt.history.nil?
227
233
  fail "Improper fuel mix." unless opt.fuel1 == :matter && opt.fuel2 == :antimatter
228
234
  end
229
-
235
+
230
236
  t = OptionTest.new(ol3)
231
-
237
+
232
238
  assert_raises(RuntimeError) { t.test() }
233
239
  assert_raises(RuntimeError) { t.test(:nohistory, fuel2: :income_tax) }
234
240
  #Really though, this should work! Both anti-matter and income tax
metadata CHANGED
@@ -1,54 +1,121 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: option_list
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.1.0
4
+ version: 1.1.5
5
5
  platform: ruby
6
6
  authors:
7
7
  - Peter Camilleri
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2013-11-20 00:00:00.000000000 Z
11
+ date: 2021-05-19 00:00:00.000000000 Z
12
12
  dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: bundler
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: 2.1.0
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: 2.1.0
13
27
  - !ruby/object:Gem::Dependency
14
28
  name: rake
15
29
  requirement: !ruby/object:Gem::Requirement
16
30
  requirements:
17
- - - ! '>='
31
+ - - ">="
18
32
  - !ruby/object:Gem::Version
19
33
  version: '0'
20
34
  type: :development
21
35
  prerelease: false
22
36
  version_requirements: !ruby/object:Gem::Requirement
23
37
  requirements:
24
- - - ! '>='
38
+ - - ">="
25
39
  - !ruby/object:Gem::Version
26
40
  version: '0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: reek
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: 1.3.8
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: 1.3.8
27
55
  - !ruby/object:Gem::Dependency
28
56
  name: minitest
29
57
  requirement: !ruby/object:Gem::Requirement
30
58
  requirements:
31
- - - ! '>='
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: 4.7.5
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: 4.7.5
69
+ - !ruby/object:Gem::Dependency
70
+ name: rdoc
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - "~>"
74
+ - !ruby/object:Gem::Version
75
+ version: 4.0.1
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - "~>"
81
+ - !ruby/object:Gem::Version
82
+ version: 4.0.1
83
+ - !ruby/object:Gem::Dependency
84
+ name: awesome_print
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - ">="
32
88
  - !ruby/object:Gem::Version
33
89
  version: '0'
34
90
  type: :development
35
91
  prerelease: false
36
92
  version_requirements: !ruby/object:Gem::Requirement
37
93
  requirements:
38
- - - ! '>='
94
+ - - ">="
39
95
  - !ruby/object:Gem::Version
40
96
  version: '0'
41
- description: ! 'A unified handler for flexible function option parameters. '
97
+ description: "[Deprecated] Flexible, Easy Function Parameters with Validation. "
42
98
  email: peter.c.camilleri@gmail.com
43
99
  executables: []
44
100
  extensions: []
45
101
  extra_rdoc_files:
46
102
  - license.txt
47
103
  files:
104
+ - ".gitignore"
105
+ - CODE_OF_CONDUCT.md
106
+ - Gemfile
107
+ - README.md
108
+ - docs/OL_UG.odt
109
+ - docs/OL_UG.pdf
110
+ - docs/OL_UG_Version_1_1_1.pdf
48
111
  - lib/option_list.rb
49
- - tests/option_list_test.rb
50
- - rakefile.rb
112
+ - lib/option_list/version.rb
51
113
  - license.txt
114
+ - option_list.gemspec
115
+ - rakefile.rb
116
+ - reek.txt
117
+ - sire.rb
118
+ - tests/option_list_test.rb
52
119
  homepage: http://teuthida-technologies.com/
53
120
  licenses:
54
121
  - MIT
@@ -59,19 +126,17 @@ require_paths:
59
126
  - lib
60
127
  required_ruby_version: !ruby/object:Gem::Requirement
61
128
  requirements:
62
- - - ! '>='
129
+ - - ">="
63
130
  - !ruby/object:Gem::Version
64
131
  version: 1.9.3
65
132
  required_rubygems_version: !ruby/object:Gem::Requirement
66
133
  requirements:
67
- - - ! '>='
134
+ - - ">="
68
135
  - !ruby/object:Gem::Version
69
136
  version: '0'
70
137
  requirements: []
71
- rubyforge_project:
72
- rubygems_version: 2.1.4
138
+ rubygems_version: 3.2.17
73
139
  signing_key:
74
140
  specification_version: 4
75
- summary: A unified handler for flexible function option parameters.
76
- test_files:
77
- - tests/option_list_test.rb
141
+ summary: Flexible, Easy Function Parameters with Validation.
142
+ test_files: []