aquarium 0.4.0 → 0.4.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.
- data/CHANGES +26 -5
- data/README +8 -8
- data/RELEASE-PLAN +20 -2
- data/TODO.rb +26 -0
- data/UPGRADE +5 -5
- data/examples/aspect_design_example.rb +1 -1
- data/examples/aspect_design_example_spec.rb +1 -1
- data/examples/design_by_contract_example.rb +4 -9
- data/examples/design_by_contract_example_spec.rb +7 -9
- data/examples/exception_wrapping_example.rb +48 -0
- data/examples/exception_wrapping_example_spec.rb +49 -0
- data/examples/reusable_aspect_hack_example.rb +56 -0
- data/examples/reusable_aspect_hack_example_spec.rb +80 -0
- data/lib/aquarium.rb +1 -0
- data/lib/aquarium/aspects.rb +1 -1
- data/lib/aquarium/aspects/advice.rb +16 -13
- data/lib/aquarium/aspects/aspect.rb +81 -56
- data/lib/aquarium/aspects/join_point.rb +4 -4
- data/lib/aquarium/aspects/pointcut.rb +49 -73
- data/lib/aquarium/dsl.rb +2 -0
- data/lib/aquarium/dsl/aspect_dsl.rb +77 -0
- data/lib/aquarium/{aspects/dsl → dsl}/object_dsl.rb +2 -2
- data/lib/aquarium/extras/design_by_contract.rb +1 -1
- data/lib/aquarium/finders.rb +1 -1
- data/lib/aquarium/finders/method_finder.rb +26 -26
- data/lib/aquarium/finders/type_finder.rb +45 -39
- data/lib/aquarium/utils/array_utils.rb +6 -5
- data/lib/aquarium/utils/default_logger.rb +2 -1
- data/lib/aquarium/utils/options_utils.rb +178 -67
- data/lib/aquarium/utils/set_utils.rb +8 -3
- data/lib/aquarium/version.rb +1 -1
- data/spec/aquarium/aspects/aspect_invocation_spec.rb +111 -14
- data/spec/aquarium/aspects/aspect_spec.rb +91 -7
- data/spec/aquarium/aspects/pointcut_spec.rb +61 -0
- data/spec/aquarium/{aspects/dsl → dsl}/aspect_dsl_spec.rb +76 -32
- data/spec/aquarium/finders/method_finder_spec.rb +80 -80
- data/spec/aquarium/finders/type_finder_spec.rb +57 -52
- data/spec/aquarium/finders/type_finder_with_descendents_and_ancestors_spec.rb +12 -12
- data/spec/aquarium/spec_example_types.rb +4 -3
- data/spec/aquarium/utils/array_utils_spec.rb +9 -7
- data/spec/aquarium/utils/options_utils_spec.rb +106 -5
- data/spec/aquarium/utils/set_utils_spec.rb +14 -0
- metadata +12 -7
- data/lib/aquarium/aspects/dsl.rb +0 -2
- data/lib/aquarium/aspects/dsl/aspect_dsl.rb +0 -64
data/lib/aquarium/dsl.rb
ADDED
@@ -0,0 +1,77 @@
|
|
1
|
+
require 'aquarium/aspects/aspect'
|
2
|
+
require 'aquarium/utils/type_utils'
|
3
|
+
|
4
|
+
# Convenience methods added to the current type to provide a low-level AOP DSL.
|
5
|
+
# If you don't want these methods added to a type, then only require aspect.rb
|
6
|
+
# and create instances of Aspect.
|
7
|
+
|
8
|
+
module Aquarium
|
9
|
+
module DSLMethods
|
10
|
+
|
11
|
+
def advise *options, &block
|
12
|
+
o = append_implicit_self options
|
13
|
+
Aquarium::Aspects::Aspect.new *o, &block
|
14
|
+
end
|
15
|
+
|
16
|
+
%w[before after after_returning after_raising around].each do |advice_kind|
|
17
|
+
module_eval(<<-ADVICE_METHODS, __FILE__, __LINE__)
|
18
|
+
def #{advice_kind} *options, &block
|
19
|
+
advise :#{advice_kind}, *options, &block
|
20
|
+
end
|
21
|
+
ADVICE_METHODS
|
22
|
+
end
|
23
|
+
|
24
|
+
%w[after after_returning after_raising].each do |after_kind|
|
25
|
+
module_eval(<<-AFTER, __FILE__, __LINE__)
|
26
|
+
def before_and_#{after_kind} *options, &block
|
27
|
+
advise :before, :#{after_kind}, *options, &block
|
28
|
+
end
|
29
|
+
AFTER
|
30
|
+
end
|
31
|
+
|
32
|
+
alias :after_returning_from :after_returning
|
33
|
+
alias :after_raising_within :after_raising
|
34
|
+
alias :after_raising_within_or_returning_from :after
|
35
|
+
|
36
|
+
alias :before_and_after_returning_from :before_and_after_returning
|
37
|
+
alias :before_and_after_raising_within :before_and_after_raising
|
38
|
+
alias :before_and_after_raising_within_or_returning_from :before_and_after
|
39
|
+
|
40
|
+
def pointcut *options, &block
|
41
|
+
o = append_implicit_self options
|
42
|
+
Aquarium::Aspects::Pointcut.new *o, &block
|
43
|
+
end
|
44
|
+
|
45
|
+
private
|
46
|
+
def append_implicit_self options
|
47
|
+
opts = options.dup
|
48
|
+
if (!opts.empty?) && opts.last.kind_of?(Hash)
|
49
|
+
opts.last[:default_objects] = self
|
50
|
+
else
|
51
|
+
opts << {:default_objects => self}
|
52
|
+
end
|
53
|
+
opts
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
module DSL
|
58
|
+
include Aquarium::DSLMethods
|
59
|
+
# Add the methods as class, not instance, methods.
|
60
|
+
def self.append_features clazz
|
61
|
+
super(class << clazz; self; end)
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
# Backwards compatibility with old name.
|
66
|
+
module Aspects
|
67
|
+
module DSL
|
68
|
+
module AspectDSL
|
69
|
+
include Aquarium::DSLMethods
|
70
|
+
# Add the methods as class, not instance, methods.
|
71
|
+
def self.append_features clazz
|
72
|
+
super(class << clazz; self; end)
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
@@ -1,8 +1,8 @@
|
|
1
|
-
require 'aquarium/
|
1
|
+
require 'aquarium/dsl/aspect_dsl'
|
2
2
|
|
3
3
|
# Add aspect convenience methods to Object. Only require this
|
4
4
|
# file if you really want these methods on all objects in your runtime!
|
5
5
|
class Object
|
6
|
-
include Aquarium::
|
6
|
+
include Aquarium::DSL
|
7
7
|
end
|
8
8
|
|
@@ -1,7 +1,7 @@
|
|
1
1
|
# A simple Design by Contract module. Adds advice to test that the contract, which is specified with
|
2
2
|
# a block passes. Note that it doesn't attempt to handle the correct behavior under contract
|
3
3
|
# inheritance.
|
4
|
-
# Warning: This module automatically includes Aquarium::
|
4
|
+
# Warning: This module automatically includes Aquarium::DSL into the class with
|
5
5
|
# the contract and it adds the :precondition, :postcondition, and the :invariant methods to Object!
|
6
6
|
require 'aquarium'
|
7
7
|
|
data/lib/aquarium/finders.rb
CHANGED
@@ -1,6 +1,7 @@
|
|
1
1
|
require 'set'
|
2
2
|
require File.dirname(__FILE__) + '/../utils/array_utils'
|
3
3
|
require File.dirname(__FILE__) + '/../utils/invalid_options'
|
4
|
+
require File.dirname(__FILE__) + '/../utils/set_utils'
|
4
5
|
require File.dirname(__FILE__) + '/../utils/type_utils'
|
5
6
|
require File.dirname(__FILE__) + '/../utils/options_utils'
|
6
7
|
require File.dirname(__FILE__) + '/finder_result'
|
@@ -17,7 +18,7 @@ module Aquarium
|
|
17
18
|
# Method names, not method objects, are always returned, because we can only get
|
18
19
|
# method objects for instance methods if we have an instance!
|
19
20
|
#
|
20
|
-
# finder_result = MethodFinder.new.find :types => ... {, :methods => ..., :
|
21
|
+
# finder_result = MethodFinder.new.find :types => ... {, :methods => ..., :method_options => [...]}
|
21
22
|
# where
|
22
23
|
# "{}" indicate optional arguments
|
23
24
|
#
|
@@ -68,10 +69,10 @@ module Aquarium
|
|
68
69
|
# One or more method names and regular expressions to exclude from the match.
|
69
70
|
# Specify one or an array of values.
|
70
71
|
#
|
71
|
-
# <tt>:
|
72
|
-
# <tt>:
|
73
|
-
# <tt>:
|
74
|
-
# <tt>:restricting_methods_to =>
|
72
|
+
# <tt>:method_options => options</tt>::
|
73
|
+
# <tt>:method_option => options</tt>::
|
74
|
+
# <tt>:options => options</tt>::
|
75
|
+
# <tt>:restricting_methods_to => options</tt>::
|
75
76
|
# By default, searches for public instance methods. Specify one or more
|
76
77
|
# of the following options for alternatives. You can combine any of the
|
77
78
|
# <tt>:public</tt>, <tt>:protected</tt>, and <tt>:private</tt>, as well as
|
@@ -90,7 +91,9 @@ module Aquarium
|
|
90
91
|
# +Derived+ class that is defined in the +Base+ class, you won't find it!
|
91
92
|
#
|
92
93
|
def find options = {}
|
93
|
-
init_specification options, CANONICAL_OPTIONS
|
94
|
+
init_specification options, CANONICAL_OPTIONS do
|
95
|
+
finish_specification_initialization
|
96
|
+
end
|
94
97
|
return Aquarium::Finders::FinderResult.new if nothing_to_find?
|
95
98
|
types_and_objects = input_types + input_objects
|
96
99
|
method_names_or_regexps = input_methods
|
@@ -108,28 +111,25 @@ module Aquarium
|
|
108
111
|
|
109
112
|
NIL_OBJECT = MethodFinder.new unless const_defined?(:NIL_OBJECT)
|
110
113
|
|
111
|
-
# TODO
|
112
|
-
|
113
|
-
"types" => %w[type for_type for_types on_type on_types in_type in_types within_type within_types],
|
114
|
+
# TODO remove (or deprecate) the "options" option!
|
115
|
+
METHOD_FINDER_CANONICAL_OPTIONS = {
|
114
116
|
"objects" => %w[object for_object for_objects on_object on_objects in_object in_objects within_object within_objects],
|
115
117
|
"methods" => %w[method within_method within_methods calling invoking invocations_of calls_to sending_message_to sending_messages_to],
|
116
|
-
"
|
118
|
+
"method_options" => %w[options method_option restricting_methods_to]
|
117
119
|
}
|
118
120
|
|
119
|
-
%w[
|
120
|
-
|
121
|
+
%w[objects methods].each do |key|
|
122
|
+
METHOD_FINDER_CANONICAL_OPTIONS["exclude_#{key}"] = METHOD_FINDER_CANONICAL_OPTIONS[key].map {|x| "exclude_#{x}"}
|
121
123
|
end
|
122
|
-
|
123
|
-
|
124
|
+
METHOD_FINDER_CANONICAL_OPTIONS["methods"].dup.each do |synonym|
|
125
|
+
if synonym =~ /methods?$/
|
126
|
+
METHOD_FINDER_CANONICAL_OPTIONS["methods"] << "#{synonym}_matching"
|
127
|
+
else
|
128
|
+
METHOD_FINDER_CANONICAL_OPTIONS["methods"] << "#{synonym}_methods_matching"
|
129
|
+
end
|
124
130
|
end
|
125
131
|
|
126
|
-
|
127
|
-
|
128
|
-
ALL_ALLOWED_OPTION_SYMBOLS = ALL_ALLOWED_OPTIONS.map {|o| o.intern}
|
129
|
-
|
130
|
-
def all_allowed_option_symbols
|
131
|
-
ALL_ALLOWED_OPTION_SYMBOLS
|
132
|
-
end
|
132
|
+
CANONICAL_OPTIONS = METHOD_FINDER_CANONICAL_OPTIONS.merge(Aquarium::Finders::TypeFinder::TYPE_FINDER_CANONICAL_OPTIONS)
|
133
133
|
|
134
134
|
RECOGNIZED_METHOD_OPTIONS = {
|
135
135
|
"all" => %w[all_methods],
|
@@ -200,8 +200,8 @@ module Aquarium
|
|
200
200
|
Aquarium::Finders::FinderResult.new types_and_objects_to_matched_methods.merge(:not_matched => types_and_objects_not_matched)
|
201
201
|
end
|
202
202
|
|
203
|
-
def
|
204
|
-
@specification[:
|
203
|
+
def finish_specification_initialization
|
204
|
+
@specification[:method_options] = MethodFinder.init_method_options(@specification[:method_options]) if @specification[:method_options]
|
205
205
|
extra_validation
|
206
206
|
end
|
207
207
|
|
@@ -231,7 +231,7 @@ module Aquarium
|
|
231
231
|
end
|
232
232
|
|
233
233
|
def exclude_ancestor_methods?
|
234
|
-
@specification[:
|
234
|
+
@specification[:method_options].include?(:exclude_ancestor_methods)
|
235
235
|
end
|
236
236
|
|
237
237
|
private
|
@@ -273,7 +273,7 @@ module Aquarium
|
|
273
273
|
is_type = Aquarium::Utils::TypeUtils.is_type?(type_or_object)
|
274
274
|
scope_prefixes = []
|
275
275
|
class_instance_prefixes = []
|
276
|
-
@specification[:
|
276
|
+
@specification[:method_options].each do |opt, value|
|
277
277
|
opt_string = opt.to_s
|
278
278
|
case opt_string
|
279
279
|
when "public", "private", "protected"
|
@@ -314,7 +314,7 @@ module Aquarium
|
|
314
314
|
end
|
315
315
|
|
316
316
|
def extra_validation
|
317
|
-
method_options = @specification[:
|
317
|
+
method_options = @specification[:method_options]
|
318
318
|
return if method_options.nil?
|
319
319
|
if method_options.include?(:singleton) &&
|
320
320
|
(method_options.include?(:class) || method_options.include?(:public) ||
|
@@ -13,8 +13,31 @@ module Aquarium
|
|
13
13
|
class TypeFinder
|
14
14
|
include Aquarium::Utils::ArrayUtils
|
15
15
|
include Aquarium::Utils::TypeUtils
|
16
|
+
include Aquarium::Utils::OptionsUtils
|
16
17
|
|
17
|
-
|
18
|
+
def self.add_ancestors_and_descendents_option_variants_for option, options_hash
|
19
|
+
all_variants = options_hash[option].dup
|
20
|
+
options_hash["#{option}_and_descendents"] = all_variants.map {|x| "#{x}_and_descendents"}
|
21
|
+
options_hash["#{option}_and_ancestors"] = all_variants.map {|x| "#{x}_and_ancestors"}
|
22
|
+
end
|
23
|
+
|
24
|
+
TYPE_FINDER_CANONICAL_OPTIONS = {
|
25
|
+
"types" => %w[type class classes module modules name names],
|
26
|
+
}
|
27
|
+
# Add the ancestors and descendents first, then add all the preposition and exclude variants, so the latter
|
28
|
+
# are added to the former...
|
29
|
+
TYPE_FINDER_CANONICAL_OPTIONS.keys.dup.each do |type_option|
|
30
|
+
add_ancestors_and_descendents_option_variants_for type_option, TYPE_FINDER_CANONICAL_OPTIONS
|
31
|
+
end
|
32
|
+
TYPE_FINDER_CANONICAL_OPTIONS.keys.dup.each do |type_option|
|
33
|
+
add_prepositional_option_variants_for type_option, TYPE_FINDER_CANONICAL_OPTIONS
|
34
|
+
add_exclude_options_for type_option, TYPE_FINDER_CANONICAL_OPTIONS
|
35
|
+
end
|
36
|
+
|
37
|
+
CANONICAL_OPTIONS = TYPE_FINDER_CANONICAL_OPTIONS.dup
|
38
|
+
|
39
|
+
canonical_options_given_methods CANONICAL_OPTIONS
|
40
|
+
canonical_option_accessor CANONICAL_OPTIONS
|
18
41
|
|
19
42
|
# Usage:
|
20
43
|
# finder_result = TypeFinder.new.find [options => [...] ]
|
@@ -90,54 +113,37 @@ module Aquarium
|
|
90
113
|
# Note: a common idiom in aspects is to include descendents of a type, but not the type
|
91
114
|
# itself. You can do as in the following example:
|
92
115
|
# <tt>... :type_and_descendents => "Foo", :exclude_type => "Foo"
|
93
|
-
#
|
116
|
+
#
|
94
117
|
def find options = {}
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
noop = false
|
100
|
-
options.each do |option, value|
|
101
|
-
unless TypeFinder.is_recognized_option option
|
102
|
-
unknown_options << option
|
103
|
-
next
|
104
|
-
end
|
105
|
-
if value.nil?
|
106
|
-
input_type_nil = true
|
107
|
-
next
|
108
|
-
end
|
109
|
-
noop = value if option == :noop
|
110
|
-
next if noop
|
111
|
-
if option.to_s =~ /^exclude_/
|
112
|
-
excluded << find_matching(value, option)
|
113
|
-
else
|
114
|
-
result << find_matching(value, option)
|
115
|
-
end
|
116
|
-
end
|
117
|
-
handle_errors unknown_options, input_type_nil
|
118
|
-
result - excluded
|
118
|
+
init_specification options, CANONICAL_OPTIONS
|
119
|
+
result = do_find_types
|
120
|
+
unset_specification
|
121
|
+
result
|
119
122
|
end
|
120
123
|
|
121
124
|
|
122
125
|
protected
|
123
126
|
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
message += "Input type specification can't be nil! " if input_type_nil
|
128
|
-
raise Aquarium::Utils::InvalidOptions.new(message) unless message.empty?
|
127
|
+
# Hack. Since the finder could be reused, unset the specification created by #find.
|
128
|
+
def unset_specification
|
129
|
+
@specification = {}
|
129
130
|
end
|
130
|
-
|
131
|
-
def
|
132
|
-
|
133
|
-
|
134
|
-
|
131
|
+
|
132
|
+
def do_find_types
|
133
|
+
result = Aquarium::Finders::FinderResult.new
|
134
|
+
excluded = Aquarium::Finders::FinderResult.new
|
135
|
+
return result if noop
|
136
|
+
@specification.each do |option, types|
|
137
|
+
next unless TYPE_FINDER_CANONICAL_OPTIONS.keys.include?(option.to_s)
|
138
|
+
next if types.nil? or types.empty?
|
139
|
+
target_result = option.to_s =~ /^exclude_/ ? excluded : result
|
140
|
+
types.each do |value|
|
141
|
+
target_result << find_matching(value, option)
|
135
142
|
end
|
136
143
|
end
|
137
|
-
|
138
|
-
false
|
144
|
+
result - excluded
|
139
145
|
end
|
140
|
-
|
146
|
+
|
141
147
|
def find_matching regexpes_or_names, option
|
142
148
|
result = Aquarium::Finders::FinderResult.new
|
143
149
|
expressions = make_array regexpes_or_names
|
@@ -7,22 +7,23 @@ module Aquarium
|
|
7
7
|
|
8
8
|
# Return an array containing the input item or list of items. If the input
|
9
9
|
# is an array, it is returned. In all cases, the constructed array is a
|
10
|
-
# flattened version of the input and any nil elements are removed by #
|
10
|
+
# flattened version of the input and any nil elements are removed by #strip_array_nils.
|
11
11
|
# Note that this behavior effectively converts +nil+ to +[]+.
|
12
12
|
def make_array *value_or_enum
|
13
13
|
ArrayUtils.make_array value_or_enum
|
14
14
|
end
|
15
15
|
|
16
16
|
def self.make_array *value_or_enum
|
17
|
-
|
17
|
+
strip_array_nils do_make_array(value_or_enum)
|
18
18
|
end
|
19
19
|
|
20
20
|
# Return a copy of the input array with all nils removed.
|
21
|
-
def
|
22
|
-
ArrayUtils.
|
21
|
+
def strip_array_nils array
|
22
|
+
ArrayUtils.strip_array_nils array
|
23
23
|
end
|
24
24
|
|
25
|
-
|
25
|
+
# Return a copy of the input array with all nils removed.
|
26
|
+
def self.strip_array_nils array
|
26
27
|
array.to_a.compact
|
27
28
|
end
|
28
29
|
|
@@ -6,8 +6,9 @@ module Aquarium
|
|
6
6
|
# Individual objects may chose to create their own loggers.
|
7
7
|
module DefaultLogger
|
8
8
|
|
9
|
+
DEFAULT_SEVERITY_LEVEL = Logger::Severity::WARN
|
9
10
|
@@default_logger = Logger.new STDERR
|
10
|
-
@@default_logger.level =
|
11
|
+
@@default_logger.level = DEFAULT_SEVERITY_LEVEL
|
11
12
|
|
12
13
|
def self.logger
|
13
14
|
@@default_logger
|
@@ -4,44 +4,88 @@ require 'aquarium/utils/default_logger'
|
|
4
4
|
module Aquarium
|
5
5
|
module Utils
|
6
6
|
|
7
|
-
# Support parsing and processing of key-value pairs of options
|
8
|
-
#
|
9
|
-
#
|
10
|
-
#
|
11
|
-
#
|
12
|
-
#
|
13
|
-
#
|
14
|
-
#
|
7
|
+
# Support parsing and processing of key-value pairs of options, where the values are always converted
|
8
|
+
# to sets.
|
9
|
+
# Types including this module should have their <tt>initialize</tt> methods call this module's
|
10
|
+
# <tt>init_specification</tt>
|
11
|
+
# to do the options processing. See its documentation for more details.
|
12
|
+
#
|
13
|
+
# Several class methods are included in including types for defining convenience instance methods.
|
14
|
+
# for options +:foo+ and +:bar+, calling:
|
15
|
+
# <tt>canonical_options_given_methods :foo, :bar</tt>
|
16
|
+
# will define several methods for each option specified, e.g.,:
|
17
|
+
# <tt>foo_given? # => returns true if a value was specified for the :foo option</tt>
|
18
|
+
# <tt>foo_given # => returns the value of @specification[:foo]</tt>
|
19
|
+
# <tt>bar_given? # etc.
|
20
|
+
# <tt>bar_given
|
21
|
+
# If you would like corresponding reader and writer methods, pass a list of the keys for which you want these
|
22
|
+
# methods defined to
|
23
|
+
# <tt>canonical_option_reader :foo, :bar # analogous to attr_reader
|
24
|
+
# <tt>canonical_option_writer :foo, :bar # analogous to attr_writer
|
25
|
+
# <tt>canonical_option_accessor :foo, :bar # analogous to attr_accessor
|
26
|
+
# For all of these methods, you can also pass CANONICAL_OPTIONS (discussed below) to define methods
|
27
|
+
# for all of the "canonical" options. _E.g.,_
|
28
|
+
# <tt>canonical_option_accessor CANONICAL_OPTIONS
|
29
|
+
#
|
30
|
+
# These methods are not defined by default to prevent accidentally overriding other methods that you might
|
31
|
+
# have defined with the same names. Also, note that the writer methods will convert the inputs to sets,
|
32
|
+
# following the conventions for the options and the readers will return the sets. If you want different handling,
|
33
|
+
# you'll have to provide custom implementations. Note that special-case accessor methods are already defined
|
34
|
+
# for the :noop and :logger options (discussed below) where the writers expect single values, not sets, and the
|
35
|
+
# readers return the single values.
|
36
|
+
# Finally, these +canonical_option_*+ methods should only be called with the *keys* for the +CANONICAL_OPTIONS+.
|
37
|
+
# The keys are considered the "canonical options", while the values for the keys are synonyms that can be used instead.
|
15
38
|
#
|
16
39
|
# This module also defines several universal options that will be available to all types that include this module:
|
17
|
-
# <tt>:logger
|
18
|
-
# A standard library Logger used for any messages. A default system-wide logger is used otherwise.
|
40
|
+
# <tt>:logger</tt>
|
41
|
+
# A Ruby standard library Logger used for any messages. A default system-wide logger is used otherwise.
|
19
42
|
# The corresponding <tt>logger</tt> and <tt>logger=</tt> accessors are defined.
|
20
43
|
#
|
21
|
-
# <tt>:logger_stream
|
44
|
+
# <tt>:logger_stream</tt>
|
22
45
|
# An an alternative to defining the logger, you can define just the output stream where log output will be written.
|
23
46
|
# If this option is specified, a new logger will be created for the instance with this output stream.
|
24
|
-
# There
|
47
|
+
# There are no corresponding accessors; use the appropriate methods on the <tt>logger</tt> object instead.
|
25
48
|
#
|
26
|
-
# <tt>:severity
|
27
|
-
# The logging severity level, one of the Logger::Severity values or
|
49
|
+
# <tt>:severity</tt>
|
50
|
+
# The logging severity level, one of the Logger::Severity values or a corresponding integer value.
|
28
51
|
# If this option is specified, a new logger will be created for the instance with this output stream.
|
29
|
-
# There
|
52
|
+
# There are no corresponding accessors; use the corresponding methods on the <tt>logger</tt> object instead.
|
30
53
|
#
|
31
54
|
# <tt>:noop => options_hash[:noop] || false</tt>
|
32
55
|
# If true, don't do "anything", the interpretation of which will vary with the type receiving the option.
|
33
|
-
# For example, a type might go through some initialization, such as parsng its
|
34
|
-
# do nothing after that. Primarily useful for debugging.
|
56
|
+
# For example, a type might go through some initialization, such as parsng its options, but
|
57
|
+
# do nothing after that. Primarily useful for debugging and testing.
|
35
58
|
# The value can be accessed through the <tt>noop</tt> and <tt>noop=</tt> accessors.
|
59
|
+
#
|
36
60
|
module OptionsUtils
|
61
|
+
include SetUtils
|
62
|
+
include ArrayUtils
|
37
63
|
|
38
64
|
def self.universal_options
|
39
65
|
[:logger_stream, :logger, :severity, :noop]
|
40
66
|
end
|
41
67
|
|
42
|
-
def
|
68
|
+
def self.universal_prepositions
|
69
|
+
[:for, :on, :in, :within]
|
70
|
+
end
|
71
|
+
|
72
|
+
attr_reader :specification
|
73
|
+
|
74
|
+
# Class #initialize methods call this method to process the input options.
|
75
|
+
# Pass an optional block to the method that takes no parameters if you want
|
76
|
+
# to do additional processing of the options before init_specification validates
|
77
|
+
# the options. The block will have access to the @specification hash built up by
|
78
|
+
# init_specification and to a new attribute @original_options, which will be a
|
79
|
+
# copy of the original options passed to init_specification (it will be either a
|
80
|
+
# hash or an array).
|
81
|
+
# Finally, if the block returns a value or an array of values, they will be
|
82
|
+
# treated as keys to ignore in the options when they are validated. This is a
|
83
|
+
# way of dynamically treating an option as valid that can't be known in advance.
|
84
|
+
# (See Aspect and Pointcut for examples of this feature in use.)
|
85
|
+
def init_specification options, canonical_options, additional_allowed_options = []
|
43
86
|
@canonical_options = canonical_options
|
44
|
-
@
|
87
|
+
@additional_allowed_options = additional_allowed_options.map{|x| x.respond_to?(:intern) ? x.intern : x}
|
88
|
+
@original_options = options.nil? ? {} : options.dup
|
45
89
|
@specification = {}
|
46
90
|
options ||= {}
|
47
91
|
options_hash = hashify options
|
@@ -51,39 +95,21 @@ module Aquarium
|
|
51
95
|
ary << options_hash[o.intern] if options_hash[o.intern]
|
52
96
|
ary
|
53
97
|
end
|
54
|
-
@specification[key.intern] = Set.new(
|
98
|
+
@specification[key.intern] = Set.new(all_related_options.flatten)
|
55
99
|
end
|
56
|
-
|
57
|
-
universal_options = {
|
58
|
-
:logger_stream => options_hash[:logger_stream],
|
59
|
-
:severity => options_hash[:severity],
|
60
|
-
:noop => options_hash[:noop] || false
|
61
|
-
}
|
62
|
-
|
63
|
-
set_logger_if_logger_or_stream_specified universal_options, options_hash
|
64
|
-
set_logger_severity_if_specified universal_options, options_hash
|
65
|
-
set_logger_if_not_specified universal_options, options_hash
|
66
100
|
|
67
101
|
OptionsUtils::universal_options.each do |uopt|
|
68
|
-
@specification[uopt] = Set.new([
|
102
|
+
@specification[uopt] = Set.new(make_array(options_hash[uopt])) unless options_hash[uopt].nil?
|
69
103
|
end
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
def #{name}= value
|
80
|
-
@specification[:#{name}] = Set.new([value])
|
81
|
-
end
|
82
|
-
EOF
|
83
|
-
end
|
84
|
-
|
85
|
-
# Override for type-specific initialization
|
86
|
-
def init_type_specific_specification original_options, options_hash, &optional_block
|
104
|
+
@specification[:noop] ||= Set.new([false])
|
105
|
+
set_logger_if_stream_specified
|
106
|
+
set_logger_severity_if_specified
|
107
|
+
set_default_logger_if_not_specified
|
108
|
+
|
109
|
+
ignorables = yield if block_given?
|
110
|
+
ignorables = [] if ignorables.nil?
|
111
|
+
ignorables = [ignorables] unless ignorables.kind_of? Array
|
112
|
+
validate_options(options_hash.reject {|k,v| ignorables.include?(k)})
|
87
113
|
end
|
88
114
|
|
89
115
|
def hashify options
|
@@ -104,33 +130,118 @@ module Aquarium
|
|
104
130
|
raise Aquarium::Utils::InvalidOptions.new("Unknown options specified: #{unknowns.inspect}") if unknowns.size > 0
|
105
131
|
end
|
106
132
|
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
133
|
+
[:logger, :noop].each do |name|
|
134
|
+
module_eval(<<-EOF, __FILE__, __LINE__)
|
135
|
+
def #{name}
|
136
|
+
@specification[:#{name}].kind_of?(Set) ? @specification[:#{name}].to_a.first : @specification[:#{name}]
|
137
|
+
end
|
138
|
+
def #{name}= value
|
139
|
+
@specification[:#{name}] = make_set(make_array(value))
|
140
|
+
end
|
141
|
+
EOF
|
115
142
|
end
|
116
143
|
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
144
|
+
module ClassMethods
|
145
|
+
def canonical_option_reader *canonical_option_key_list
|
146
|
+
return if canonical_option_key_list.nil? or canonical_option_key_list.empty?
|
147
|
+
keys = determine_options_for_accessors canonical_option_key_list
|
148
|
+
keys.each do |name|
|
149
|
+
define_method(name) do
|
150
|
+
@specification[name]
|
151
|
+
end
|
152
|
+
end
|
153
|
+
end
|
154
|
+
def canonical_option_writer *canonical_option_key_list
|
155
|
+
return if canonical_option_key_list.nil? or canonical_option_key_list.empty?
|
156
|
+
keys = determine_options_for_accessors canonical_option_key_list
|
157
|
+
keys.each do |name|
|
158
|
+
define_method("#{name}=") do |value|
|
159
|
+
@specification[name] = make_set(make_array(value))
|
160
|
+
end
|
161
|
+
end
|
162
|
+
end
|
163
|
+
def canonical_option_accessor *canonical_option_key_list
|
164
|
+
canonical_option_reader *canonical_option_key_list
|
165
|
+
canonical_option_writer *canonical_option_key_list
|
166
|
+
end
|
167
|
+
|
168
|
+
def canonical_options_given_methods canonical_options
|
169
|
+
keys = canonical_options.respond_to?(:keys) ? canonical_options.keys : canonical_options
|
170
|
+
(keys + OptionsUtils::universal_options).each do |name|
|
171
|
+
module_eval(<<-EOF, __FILE__, __LINE__)
|
172
|
+
def #{name}_given
|
173
|
+
@specification[:#{name}]
|
174
|
+
end
|
175
|
+
|
176
|
+
def #{name}_given?
|
177
|
+
not (#{name}_given.nil? or #{name}_given.empty?)
|
178
|
+
end
|
179
|
+
EOF
|
180
|
+
end
|
181
|
+
end
|
182
|
+
|
183
|
+
# Service method that adds a new canonical option and corresponding array with
|
184
|
+
# "exclude_" prepended to all values. The new options are added to the input hash.
|
185
|
+
def add_exclude_options_for option, options_hash
|
186
|
+
all_variants = options_hash[option].dup
|
187
|
+
options_hash["exclude_#{option}"] = all_variants.map {|x| "exclude_#{x}"}
|
188
|
+
end
|
189
|
+
|
190
|
+
# Service method that adds a new canonical option and corresponding array with
|
191
|
+
# "preposition" prefixes, e.g., "on_", "for_", etc. prepended to all values.
|
192
|
+
# The new options are added to the input hash.
|
193
|
+
def add_prepositional_option_variants_for option, options_hash
|
194
|
+
all_variants = options_hash[option].dup + [option]
|
195
|
+
OptionsUtils.universal_prepositions.each do |prefix|
|
196
|
+
all_variants.each do |variant|
|
197
|
+
options_hash[option] << "#{prefix}_#{variant}"
|
198
|
+
end
|
124
199
|
end
|
125
200
|
end
|
201
|
+
|
202
|
+
protected
|
203
|
+
def determine_options_for_accessors canonical_option_key_list
|
204
|
+
keys = canonical_option_key_list
|
205
|
+
if canonical_option_key_list.kind_of?(Array) and canonical_option_key_list.size == 1
|
206
|
+
keys = canonical_option_key_list[0]
|
207
|
+
end
|
208
|
+
if keys.respond_to? :keys
|
209
|
+
keys = keys.keys
|
210
|
+
end
|
211
|
+
keys
|
212
|
+
end
|
213
|
+
end
|
214
|
+
|
215
|
+
def self.append_features clazz
|
216
|
+
super
|
217
|
+
ClassMethods.send :append_features, (class << clazz; self; end)
|
218
|
+
end
|
219
|
+
|
220
|
+
protected
|
221
|
+
|
222
|
+
def all_allowed_option_symbols
|
223
|
+
@canonical_options.to_a.flatten.map {|o| o.intern} + @additional_allowed_options
|
224
|
+
end
|
225
|
+
|
226
|
+
# While it's tempting to use the #logger_stream_given?, etc. methods, they will only exist if the
|
227
|
+
# including class called canonical_options_given_methods!
|
228
|
+
def set_logger_if_stream_specified
|
229
|
+
return if @specification[:logger_stream].nil? or @specification[:logger_stream].empty?
|
230
|
+
self.logger = Logger.new @specification[:logger_stream].to_a.first
|
231
|
+
self.logger.level = DefaultLogger::DEFAULT_SEVERITY_LEVEL
|
126
232
|
end
|
127
233
|
|
128
|
-
def
|
129
|
-
if
|
130
|
-
|
131
|
-
|
234
|
+
def set_logger_severity_if_specified
|
235
|
+
return if @specification[:severity].nil? or @specification[:severity].empty?
|
236
|
+
if self.logger.nil?
|
237
|
+
self.logger = Logger.new STDERR
|
238
|
+
end
|
239
|
+
self.logger.level = @specification[:severity].to_a.first
|
132
240
|
end
|
133
241
|
|
242
|
+
def set_default_logger_if_not_specified
|
243
|
+
self.logger ||= DefaultLogger.logger
|
244
|
+
end
|
134
245
|
end
|
135
246
|
end
|
136
247
|
end
|