C0 code coverage information
Generated on Sun Oct 26 11:17:55 -0500 2008 with rcov 0.8.1.2
Code reported as executed by Ruby looks like this...
and this: this line is also marked as covered.
Lines considered as run by rcov, but not reported by Ruby, look like this,
and this: these lines were inferred by rcov (using simple heuristics).
Finally, here's a line marked as not executed.
1 require 'logger'
2 require 'aquarium/utils/default_logger'
3
4 module Aquarium
5 module Utils
6
7 # == OptionsUtils
8 # Support parsing and processing of key-value pairs of options, where the values are always converted
9 # to Sets.
10 # Types including this module should have their <tt>initialize</tt> methods call this module's
11 # #init_specification to do the options processing. See its documentation for more details.
12 #
13 # Several <i>class</i> methods are included for defining convenience <i>instance</i> methods.
14 # For example, for options <tt>:foo</tt> and <tt>:bar</tt>, calling:
15 #
16 # canonical_options_given_methods :foo, :bar
17 #
18 # will define several methods for each option specified, e.g.,:
19 #
20 # foo_given # => returns the value of @specification[:foo]
21 # foo_given? # => returns true "foo_given" is not nil or empty.
22 # bar_given # etc...
23 # bar_given?
24 #
25 # If you would like corresponding reader and writer methods, pass a list of the keys for which you want these
26 # methods defined to one of the following methods:
27 #
28 # canonical_option_reader :foo, :bar # analogous to attr_reader
29 # canonical_option_writer :foo, :bar # analogous to attr_writer
30 # canonical_option_accessor :foo, :bar # analogous to attr_accessor
31 #
32 # For all of these methods, you can also pass <tt>CANONICAL_OPTIONS</tt> (discussed below) to define methods
33 # for all of the "canonical" options, <i>e.g.,</i>
34 #
35 # canonical_option_accessor CANONICAL_OPTIONS
36 #
37 # These methods are not defined by default to prevent accidentally overriding other methods that you might
38 # have defined with the same names. Also, note that the writer methods will convert the inputs to sets,
39 # following the conventions for the options and the readers will return the sets. If you want different handling,
40 # you'll have to provide custom implementations.
41 #
42 # Note that special-case accessor methods are already defined for the <tt>:noop</tt> and <tt>:logger</tt>
43 # options (discussed below) where the writers expect single values, not sets, and the
44 # readers return the single values. (Yea, it's a bit inconsistent...)
45 #
46 # Finally, these <tt>canonical_option_*</tt> methods should only be called with the *keys* for the +CANONICAL_OPTIONS+.
47 # The keys are considered the "canonical options", while the values for the keys are synonyms that can be used instead.
48 #
49 # This module also defines several universal options that will be available to all types that include this module:
50 # <tt>:logger</tt>::
51 # A Ruby standard library Logger used for any messages. A default system-wide logger is used otherwise.
52 # The corresponding <tt>logger</tt> and <tt>logger=</tt> accessors are defined.
53 #
54 # <tt>:logger_stream</tt>::
55 # An an alternative to defining the logger, you can define just the output stream where log output will be written.
56 # If this option is specified, a new logger will be created for the instance with this output stream.
57 # There are no corresponding accessors; use the appropriate methods on the <tt>logger</tt> object instead.
58 #
59 # <tt>:severity</tt>::
60 # The logging severity level, one of the Logger::Severity values or a corresponding integer value.
61 # If this option is specified, a new logger will be created for the instance with this output stream.
62 # There are no corresponding accessors; use the corresponding methods on the <tt>logger</tt> object instead.
63 #
64 # <tt>:noop => options_hash[:noop] || false</tt>::
65 # If true, don't do "anything", the interpretation of which will vary with the type receiving the option.
66 # For example, a type might go through some initialization, such as parsng its options, but
67 # do nothing after that. Primarily useful for debugging and testing.
68 # The value can be accessed through the <tt>noop</tt> and <tt>noop=</tt> accessors.
69 #
70 module OptionsUtils
71 include SetUtils
72 include ArrayUtils
73
74 def self.universal_options
75 [:logger_stream, :logger, :severity, :noop]
76 end
77
78 def self.universal_prepositions
79 [:for, :on, :in, :within]
80 end
81
82 attr_reader :specification
83
84 # Class #initialize methods call this method to process the input options.
85 # Pass an optional block to the method that takes no parameters if you want
86 # to do additional processing of the options before init_specification validates
87 # the options. The block will have access to the @specification hash built up by
88 # init_specification and to a new attribute @original_options, which will be a
89 # copy of the original options passed to init_specification (it will be either a
90 # hash or an array).
91 # Finally, if the block returns a value or an array of values, they will be
92 # treated as keys to ignore in the options when they are validated. This is a
93 # way of dynamically treating an option as valid that can't be known in advance.
94 # (See Aspect and Pointcut for examples of this feature in use.)
95 def init_specification options, canonical_options, additional_allowed_options = []
96 @canonical_options = canonical_options
97 @additional_allowed_options = additional_allowed_options.map{|x| x.respond_to?(:intern) ? x.intern : x}
98 @original_options = options.nil? ? {} : options.dup
99 @specification = {}
100 options ||= {}
101 options_hash = hashify options
102 @canonical_options.keys.each do |key|
103 all_related_options = make_array(options_hash[key.intern]) || []
104 @canonical_options[key].inject(all_related_options) do |ary, o|
105 ary << options_hash[o.intern] if options_hash[o.intern]
106 ary
107 end
108 @specification[key.intern] = Set.new(all_related_options.flatten)
109 end
110
111 OptionsUtils::universal_options.each do |uopt|
112 @specification[uopt] = Set.new(make_array(options_hash[uopt])) unless options_hash[uopt].nil?
113 end
114 @specification[:noop] ||= Set.new([false])
115 set_logger_if_stream_specified
116 set_logger_severity_if_specified
117 set_default_logger_if_not_specified
118
119 ignorables = yield if block_given?
120 ignorables = [] if ignorables.nil?
121 ignorables = [ignorables] unless ignorables.kind_of? Array
122 validate_options(options_hash.reject {|k,v| ignorables.include?(k)})
123 end
124
125 def hashify options
126 return options if options.kind_of?(Hash)
127 new_options = {}
128 options.each do |x|
129 if x.kind_of?(Hash)
130 new_options.merge!(x)
131 else
132 new_options[x] = Set.new([])
133 end
134 end
135 new_options
136 end
137
138 def validate_options options
139 unknowns = options.keys - all_allowed_option_symbols - OptionsUtils::universal_options
140 raise Aquarium::Utils::InvalidOptions.new("Unknown options specified: #{unknowns.inspect}") if unknowns.size > 0
141 end
142
143 [:logger, :noop].each do |name|
144 module_eval(<<-EOF, __FILE__, __LINE__)
145 def #{name}
146 @specification[:#{name}].kind_of?(Set) ? @specification[:#{name}].to_a.first : @specification[:#{name}]
147 end
148 def #{name}= value
149 @specification[:#{name}] = make_set(make_array(value))
150 end
151 EOF
152 end
153
154 module ClassMethods
155 def canonical_option_reader *canonical_option_key_list
156 return if canonical_option_key_list.nil? or canonical_option_key_list.empty?
157 keys = determine_options_for_accessors canonical_option_key_list
158 keys.each do |name|
159 define_method(name) do
160 @specification[name]
161 end
162 end
163 end
164 def canonical_option_writer *canonical_option_key_list
165 return if canonical_option_key_list.nil? or canonical_option_key_list.empty?
166 keys = determine_options_for_accessors canonical_option_key_list
167 keys.each do |name|
168 define_method("#{name}=") do |value|
169 @specification[name] = make_set(make_array(value))
170 end
171 end
172 end
173 def canonical_option_accessor *canonical_option_key_list
174 canonical_option_reader *canonical_option_key_list
175 canonical_option_writer *canonical_option_key_list
176 end
177
178 def canonical_options_given_methods canonical_options
179 keys = canonical_options.respond_to?(:keys) ? canonical_options.keys : canonical_options
180 (keys + OptionsUtils::universal_options).each do |name|
181 module_eval(<<-EOF, __FILE__, __LINE__)
182 def #{name}_given
183 @specification[:#{name}]
184 end
185
186 def #{name}_given?
187 not (#{name}_given.nil? or #{name}_given.empty?)
188 end
189 EOF
190 end
191 end
192
193 # Service method that adds a new canonical option and corresponding array with
194 # "exclude_" prepended to all values. The new options are added to the input hash.
195 def add_exclude_options_for option, options_hash
196 all_variants = options_hash[option].dup
197 options_hash["exclude_#{option}"] = all_variants.map {|x| "exclude_#{x}"}
198 end
199
200 # Service method that adds a new canonical option and corresponding array with
201 # "preposition" prefixes, e.g., "on_", "for_", etc. prepended to all values.
202 # The new options are added to the input hash.
203 def add_prepositional_option_variants_for option, options_hash
204 all_variants = options_hash[option].dup + [option]
205 OptionsUtils.universal_prepositions.each do |prefix|
206 all_variants.each do |variant|
207 options_hash[option] << "#{prefix}_#{variant}"
208 end
209 end
210 end
211
212 private
213
214 def determine_options_for_accessors canonical_option_key_list
215 keys = canonical_option_key_list
216 if canonical_option_key_list.kind_of?(Array) and canonical_option_key_list.size == 1
217 keys = canonical_option_key_list[0]
218 end
219 if keys.respond_to? :keys
220 keys = keys.keys
221 end
222 keys
223 end
224 end
225
226 def self.append_features clazz
227 super
228 ClassMethods.send :append_features, (class << clazz; self; end)
229 end
230
231 private
232
233 def all_allowed_option_symbols
234 @canonical_options.to_a.flatten.map {|o| o.intern} + @additional_allowed_options
235 end
236
237 # While it's tempting to use the #logger_stream_given?, etc. methods, they will only exist if the
238 # including class called canonical_options_given_methods!
239 def set_logger_if_stream_specified
240 return if @specification[:logger_stream].nil? or @specification[:logger_stream].empty?
241 self.logger = Logger.new @specification[:logger_stream].to_a.first
242 self.logger.level = DefaultLogger::DEFAULT_SEVERITY_LEVEL
243 end
244
245 def set_logger_severity_if_specified
246 return if @specification[:severity].nil? or @specification[:severity].empty?
247 if self.logger.nil?
248 self.logger = Logger.new STDERR
249 end
250 self.logger.level = @specification[:severity].to_a.first
251 end
252
253 def set_default_logger_if_not_specified
254 self.logger ||= DefaultLogger.logger
255 end
256 end
257 end
258 end
Generated using the rcov code coverage analysis tool for Ruby version 0.8.1.2.