option_list 1.1.0 → 1.1.1

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,15 @@
1
1
  ---
2
2
  !binary "U0hBMQ==":
3
3
  metadata.gz: !binary |-
4
- OWQxY2I0ZTM2NjM1OTBiZDU2MWNmZDk4MjcyMDQ1ZGUzYWQzNDcyMA==
4
+ NDlkMWJhNDU5YmJmODU0N2IyMmEyZjNiYjcxZTc4MTI4NmJkY2RlMA==
5
5
  data.tar.gz: !binary |-
6
- NDUyZWQxNmQ5MzMwZTkzZThhZTFjNzhjMTNmN2Y4ZmY1NDU2N2RmNQ==
6
+ MTZiNTIxY2M0MzcyM2Q4NGNiN2E5NDcyNDY2ZWVjYjI3MjM3NDE0Zg==
7
7
  SHA512:
8
8
  metadata.gz: !binary |-
9
- ZGNhMjA2NjExMzNjNDAzNzdjNmZmODJkODg1NmQwZDYzOTg2MTkxNTFiMDUx
10
- MmU1NGFkMTMzZGE1MjI1MzU3MWE2M2U3MzEwMDEwYWYyNDI0YmIzYjk4MGQx
11
- NDM3NjEwNTEwYzkxY2YxNjY5MWJmZjc0NDY3NmU3MmMyZmE4NGU=
9
+ ZmUzZDYyM2MyYTBjYzVhMDQ4MjFkNDZjOWE4MmFlN2ZmMTQyNmU0MjQ3MWJm
10
+ YWE1ZjE0N2YyZmNmZWRhNGNlNTJmMmIxYjBlODAyZWQ2NmQwMjUzZDE4Yjk2
11
+ YjJlNWY4M2JjY2EyMTcwMmFiZTA1MDY2MTkxNzZjMTZkMTVmNjI=
12
12
  data.tar.gz: !binary |-
13
- ZDViOTVkN2U5NjMyZDNlYjliZDdlODk2NTM2MzFiZTJmMDExOThjNzhhOTMw
14
- MTM1Yjk5ZTEwNjBiYTE2NmQzNzRkYWI4OWIyM2JhNGIwMGY2NzRiNGE2Zjli
15
- MGJmMjMyYjliNDAzNmVmYmY2MGM1MzBmMzg1ODE1NGEwMDgzZTM=
13
+ Yzk2OTZjNDBhYWMzNDFhNzFlZWQ5OTAxOTUwNzcxZDFhZDI3NjhiMjIzYjFh
14
+ MGIxZmFjY2M3OGNlYjdmMGYzMmMxZDRjZjIzYjFmOWU2YTUyYzYwYWI1MDI4
15
+ YTllOGEzYTI5ZWQzZDM0YjIyYWVlZDE1ZTJmOTBhMGFkZGYzZWU=
data/README ADDED
@@ -0,0 +1,3 @@
1
+ This project contains the Ruby OptionList gem. A gem used to facilitate the
2
+ passing of complex parameter options with validation and checking and
3
+ minimal effort on the part of the caller and the callee.
@@ -4,40 +4,12 @@ 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
14
  #Added a default value of false to signify that no default exists for a
43
15
  #mandatory parameter.
@@ -46,7 +18,7 @@ class OptionList
46
18
 
47
19
  #The option list code version.
48
20
  def self.version
49
- '1.1.0'
21
+ '1.1.1'
50
22
  end
51
23
 
52
24
  #The option list code version. This is a redirect to the class method.
@@ -54,29 +26,7 @@ class OptionList
54
26
  self.class.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
31
  #* option_specs - The comma separated option specifications, made into an
82
32
  # array by the splat operator.
@@ -84,19 +34,10 @@ class OptionList
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
@@ -107,7 +48,7 @@ class OptionList
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
 
@@ -115,47 +56,7 @@ class OptionList
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,
@@ -168,30 +69,36 @@ class OptionList
168
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.
174
84
 
85
+ #Process a list of option selections.
86
+ def process_selections(selections)
87
+ selected, dup = @default.clone, Set.new
88
+
175
89
  selections.each do |opt|
176
90
  if opt.is_a?(Symbol)
177
91
  symbolic_selection(opt, selected, dup)
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
98
 
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?
190
99
  selected
191
100
  end
192
101
 
193
- private #Private stuff follows.
194
-
195
102
  #Return a internal category marker constant for value entries.
196
103
  def value_entry
197
104
  'A value entry.'
@@ -200,58 +107,66 @@ class OptionList
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.
@@ -259,23 +174,54 @@ class OptionList
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
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
276
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
@@ -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
@@ -5,7 +5,7 @@ require 'rdoc/task'
5
5
  RDoc::Task.new do |rdoc|
6
6
  rdoc.rdoc_dir = "rdoc"
7
7
  #rdoc.main = "option_list.rb"
8
- rdoc.rdoc_files = ['lib/option_list.rb', 'tests/option_list_test.rb', 'license.txt']
8
+ rdoc.rdoc_files = ['lib/option_list.rb', 'license.txt']
9
9
  rdoc.options << '--visibility' << 'private'
10
10
  end
11
11
 
@@ -13,3 +13,7 @@ Rake::TestTask.new do |t|
13
13
  t.test_files = ['tests/option_list_test.rb']
14
14
  t.verbose = false
15
15
  end
16
+
17
+ task :reek do |t|
18
+ `reek lib\\*.rb > reek.txt`
19
+ end
@@ -0,0 +1 @@
1
+ lib/option_list.rb -- 0 warnings
@@ -211,6 +211,12 @@ 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
metadata CHANGED
@@ -1,14 +1,14 @@
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.1
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: 2014-01-10 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rake
@@ -24,6 +24,20 @@ dependencies:
24
24
  - - ! '>='
25
25
  - !ruby/object:Gem::Version
26
26
  version: '0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: reek
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ! '>='
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ! '>='
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
27
41
  - !ruby/object:Gem::Dependency
28
42
  name: minitest
29
43
  requirement: !ruby/object:Gem::Requirement
@@ -38,7 +52,21 @@ dependencies:
38
52
  - - ! '>='
39
53
  - !ruby/object:Gem::Version
40
54
  version: '0'
41
- description: ! 'A unified handler for flexible function option parameters. '
55
+ - !ruby/object:Gem::Dependency
56
+ name: awesome_print
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ! '>='
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ! '>='
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
69
+ description: ! 'Flexible, Easy Function Parameters with Validation. '
42
70
  email: peter.c.camilleri@gmail.com
43
71
  executables: []
44
72
  extensions: []
@@ -49,6 +77,8 @@ files:
49
77
  - tests/option_list_test.rb
50
78
  - rakefile.rb
51
79
  - license.txt
80
+ - README
81
+ - reek.txt
52
82
  homepage: http://teuthida-technologies.com/
53
83
  licenses:
54
84
  - MIT
@@ -72,6 +102,6 @@ rubyforge_project:
72
102
  rubygems_version: 2.1.4
73
103
  signing_key:
74
104
  specification_version: 4
75
- summary: A unified handler for flexible function option parameters.
105
+ summary: Flexible, Easy Function Parameters with Validation.
76
106
  test_files:
77
107
  - tests/option_list_test.rb