aquarium 0.2.0 → 0.3.0
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGES +35 -0
- data/MIT-LICENSE +1 -1
- data/README +66 -20
- data/Rakefile +1 -1
- data/UPGRADE +5 -0
- data/examples/aspect_design_example.rb +5 -4
- data/examples/aspect_design_example_spec.rb +6 -5
- data/examples/design_by_contract_example.rb +3 -3
- data/examples/design_by_contract_example_spec.rb +4 -4
- data/examples/method_missing_example.rb +1 -1
- data/examples/method_missing_example_spec.rb +2 -2
- data/examples/method_tracing_example.rb +3 -3
- data/examples/method_tracing_example_spec.rb +6 -6
- data/lib/aquarium/aspects/advice.rb +2 -3
- data/lib/aquarium/aspects/aspect.rb +100 -246
- data/lib/aquarium/aspects/{default_object_handler.rb → default_objects_handler.rb} +7 -6
- data/lib/aquarium/aspects/dsl/aspect_dsl.rb +2 -2
- data/lib/aquarium/aspects/pointcut.rb +190 -107
- data/lib/aquarium/finders/method_finder.rb +120 -34
- data/lib/aquarium/finders/type_finder.rb +2 -5
- data/lib/aquarium/utils.rb +1 -0
- data/lib/aquarium/utils/array_utils.rb +11 -3
- data/lib/aquarium/utils/options_utils.rb +74 -0
- data/lib/aquarium/utils/type_utils.rb +25 -11
- data/lib/aquarium/version.rb +1 -1
- data/spec/aquarium/aspects/advice_chain_node_spec.rb +1 -1
- data/spec/aquarium/aspects/advice_spec.rb +1 -1
- data/spec/aquarium/aspects/aspect_invocation_spec.rb +179 -145
- data/spec/aquarium/aspects/aspect_spec.rb +1 -1
- data/spec/aquarium/aspects/aspect_with_nested_types_spec.rb +1 -1
- data/spec/aquarium/aspects/aspect_with_subtypes_spec.rb +1 -1
- data/spec/aquarium/aspects/concurrent_aspects_spec.rb +1 -1
- data/spec/aquarium/aspects/concurrent_aspects_with_objects_and_types_spec.rb +1 -1
- data/spec/aquarium/aspects/default_objects_handler_spec.rb +147 -0
- data/spec/aquarium/aspects/dsl/aspect_dsl_spec.rb +72 -121
- data/spec/aquarium/aspects/join_point_spec.rb +1 -1
- data/spec/aquarium/aspects/pointcut_and_composition_spec.rb +1 -1
- data/spec/aquarium/aspects/pointcut_or_composition_spec.rb +48 -47
- data/spec/aquarium/aspects/pointcut_spec.rb +727 -410
- data/spec/aquarium/extensions/hash_spec.rb +1 -1
- data/spec/aquarium/extensions/regex_spec.rb +1 -1
- data/spec/aquarium/extensions/set_spec.rb +1 -1
- data/spec/aquarium/extensions/string_spec.rb +1 -1
- data/spec/aquarium/extensions/symbol_spec.rb +1 -1
- data/spec/aquarium/extras/design_by_contract_spec.rb +1 -1
- data/spec/aquarium/finders/finder_result_spec.rb +1 -1
- data/spec/aquarium/finders/method_finder_spec.rb +49 -16
- data/spec/aquarium/finders/type_finder_spec.rb +1 -1
- data/spec/aquarium/finders/type_finder_with_descendents_and_ancestors_spec.rb +16 -1
- data/spec/aquarium/utils/array_utils_spec.rb +31 -6
- data/spec/aquarium/utils/hash_utils_spec.rb +1 -1
- data/spec/aquarium/utils/html_escaper_spec.rb +1 -1
- data/spec/aquarium/utils/logic_error_spec.rb +1 -1
- data/spec/aquarium/utils/method_utils_spec.rb +1 -1
- data/spec/aquarium/utils/name_utils_spec.rb +1 -1
- data/spec/aquarium/utils/nil_object_spec.rb +1 -1
- data/spec/aquarium/utils/set_utils_spec.rb +1 -1
- data/spec/aquarium/utils/type_utils_spec.rb +1 -1
- metadata +9 -7
@@ -2,6 +2,7 @@ require 'set'
|
|
2
2
|
require File.dirname(__FILE__) + '/../utils/array_utils'
|
3
3
|
require File.dirname(__FILE__) + '/../utils/invalid_options'
|
4
4
|
require File.dirname(__FILE__) + '/../utils/type_utils'
|
5
|
+
require File.dirname(__FILE__) + '/../utils/options_utils'
|
5
6
|
require File.dirname(__FILE__) + '/finder_result'
|
6
7
|
|
7
8
|
# Find methods and types and objects.
|
@@ -9,6 +10,7 @@ module Aquarium
|
|
9
10
|
module Finders
|
10
11
|
class MethodFinder
|
11
12
|
include Aquarium::Utils::ArrayUtils
|
13
|
+
include Aquarium::Utils::OptionsUtils
|
12
14
|
|
13
15
|
# Returns a Aquarium::Finders::FinderResult for the hash of types, type names, and/or regular expressions
|
14
16
|
# and the corresponding method name <b>symbols</b> found.
|
@@ -21,37 +23,66 @@ module Aquarium
|
|
21
23
|
#
|
22
24
|
# <tt>:types => types_and_type_names_and_regexps</tt>::
|
23
25
|
# <tt>:type => types_and_type_names_and_regexps</tt>::
|
26
|
+
# <tt>:for_types => types_and_type_names_and_regexps</tt>::
|
27
|
+
# <tt>:for_type => types_and_type_names_and_regexps</tt>::
|
28
|
+
# <tt>:on_types => types_and_type_names_and_regexps</tt>::
|
29
|
+
# <tt>:on_type => types_and_type_names_and_regexps</tt>::
|
30
|
+
# <tt>:in_types => types_and_type_names_and_regexps</tt>::
|
31
|
+
# <tt>:in_type => types_and_type_names_and_regexps</tt>::
|
32
|
+
# <tt>:within_types => types_and_type_names_and_regexps</tt>::
|
33
|
+
# <tt>:within_type => types_and_type_names_and_regexps</tt>::
|
24
34
|
# One or more types, type names and/or regular expessions to match.
|
25
35
|
# Specify one or an array of values.
|
26
36
|
#
|
27
37
|
# <tt>:objects => objects</tt>::
|
28
38
|
# <tt>:object => objects</tt>::
|
39
|
+
# <tt>:for_objects => objects</tt>::
|
40
|
+
# <tt>:for_object => objects</tt>::
|
41
|
+
# <tt>:on_objects => objects</tt>::
|
42
|
+
# <tt>:on_object => objects</tt>::
|
43
|
+
# <tt>:in_objects => objects</tt>::
|
44
|
+
# <tt>:in_object => objects</tt>::
|
45
|
+
# <tt>:within_objects => objects</tt>::
|
46
|
+
# <tt>:within_object => objects</tt>::
|
29
47
|
# One or more objects to match.
|
30
48
|
# Specify one or an array of values.
|
31
49
|
# Note: Currently, string or symbol objects will be misinterpreted as type names!
|
32
50
|
#
|
33
51
|
# <tt>:methods => method_names_and_regexps</tt>::
|
34
52
|
# <tt>:method => method_names_and_regexps</tt>::
|
53
|
+
# <tt>:within_methods => method_names_and_regexps</tt>::
|
54
|
+
# <tt>:within_method => method_names_and_regexps</tt>::
|
55
|
+
# <tt>:calling => method_names_and_regexps</tt>::
|
56
|
+
# <tt>:invoking => method_names_and_regexps</tt>::
|
57
|
+
# <tt>:calls_to => method_names_and_regexps</tt>::
|
58
|
+
# <tt>:sending_message_to => method_names_and_regexps</tt>::
|
59
|
+
# <tt>:sending_messages_to => method_names_and_regexps</tt>::
|
35
60
|
# One or more method names and regular expressions to match.
|
36
|
-
# Specify one or an array of values.
|
61
|
+
# Specify one or an array of values. If :all or :all_methods is specified, all
|
62
|
+
# methods will be matched. That is, these special values are equivalent to the
|
63
|
+
# the regular expression /.+/.
|
37
64
|
#
|
38
65
|
# <tt>:exclude_methods => method_names_and_regexps</tt>::
|
39
66
|
# <tt>:exclude_method => method_names_and_regexps</tt>::
|
67
|
+
# <tt>:exclude_<other_method_synonyms> => method_names_and_regexps</tt>::
|
40
68
|
# One or more method names and regular expressions to exclude from the match.
|
41
69
|
# Specify one or an array of values.
|
42
70
|
#
|
43
71
|
# <tt>:options => method_options</tt>::
|
72
|
+
# <tt>:method_options => method_options</tt>::
|
73
|
+
# <tt>:method_option => method_options</tt>::
|
74
|
+
# <tt>:restricting_methods_to => method_options</tt>::
|
44
75
|
# By default, searches for public instance methods. Specify one or more
|
45
76
|
# of the following options for alternatives. You can combine any of the
|
46
77
|
# <tt>:public</tt>, <tt>:protected</tt>, and <tt>:private</tt>, as well as
|
47
78
|
# <tt>:instance</tt> and <tt>:class</tt>.
|
48
79
|
#
|
49
|
-
# <tt>:public</tt>:: Search for public methods (default).
|
50
|
-
# <tt>:private</tt>:: Search for private methods.
|
51
|
-
# <tt>:protected</tt>:: Search for protected methods.
|
52
|
-
# <tt>:instance</tt>:: Search for instance methods.
|
53
|
-
# <tt>:class</tt>:: Search for class methods.
|
54
|
-
# <tt>:singleton</tt>:: Search for singleton methods. (Using :class for objects
|
80
|
+
# <tt>:public</tt> or <tt>:public_methods</tt>:: Search for public methods (default).
|
81
|
+
# <tt>:private</tt> or <tt>:private_methods</tt>:: Search for private methods.
|
82
|
+
# <tt>:protected</tt> or <tt>:protected_methods</tt>:: Search for protected methods.
|
83
|
+
# <tt>:instance</tt> or <tt>:instance_methods</tt>:: Search for instance methods.
|
84
|
+
# <tt>:class</tt> or <tt>:class_methods</tt>:: Search for class methods.
|
85
|
+
# <tt>:singleton</tt> or <tt>:singleton_methods</tt>:: Search for singleton methods. (Using :class for objects
|
55
86
|
# won't work and :class, :public, :protected, and :private are ignored when
|
56
87
|
# looking for singleton methods.)
|
57
88
|
# <tt>:exclude_ancestor_methods</tt>:: Suppress "ancestor" methods. This
|
@@ -59,7 +90,7 @@ module Aquarium
|
|
59
90
|
# +Derived+ class that is defined in the +Base+ class, you won't find it!
|
60
91
|
#
|
61
92
|
def find options = {}
|
62
|
-
init_specification options
|
93
|
+
init_specification options, canonical_options
|
63
94
|
return Aquarium::Finders::FinderResult.new if nothing_to_find?
|
64
95
|
types_and_objects = input_types + input_objects
|
65
96
|
method_names_or_regexps = input_methods
|
@@ -77,11 +108,69 @@ module Aquarium
|
|
77
108
|
|
78
109
|
NIL_OBJECT = MethodFinder.new unless const_defined?(:NIL_OBJECT)
|
79
110
|
|
80
|
-
|
81
|
-
|
111
|
+
CANONICAL_OPTIONS = {
|
112
|
+
"types" => %w[type for_type for_types on_type on_types in_type in_types within_type within_types],
|
113
|
+
"objects" => %w[object for_object for_objects on_object on_objects in_object in_objects within_object within_objects],
|
114
|
+
"methods" => %w[method within_method within_methods calling invoking invocations_of calls_to sending_message_to sending_messages_to],
|
115
|
+
"options" => %w[method_options method_option restricting_methods_to]
|
116
|
+
}
|
117
|
+
|
118
|
+
%w[types objects methods].each do |key|
|
119
|
+
CANONICAL_OPTIONS["exclude_#{key}"] = CANONICAL_OPTIONS[key].map {|x| "exclude_#{x}"}
|
120
|
+
end
|
121
|
+
CANONICAL_OPTIONS["methods"].dup.each do |synonym|
|
122
|
+
CANONICAL_OPTIONS["methods"] << "#{synonym}_methods_matching"
|
123
|
+
end
|
124
|
+
|
125
|
+
ALL_ALLOWED_OPTIONS = CANONICAL_OPTIONS.keys.inject([]) {|ary,i| ary << i << CANONICAL_OPTIONS[i]}.flatten
|
126
|
+
|
127
|
+
ALL_ALLOWED_OPTION_SYMBOLS = ALL_ALLOWED_OPTIONS.map {|o| o.intern}
|
128
|
+
|
129
|
+
def canonical_options
|
130
|
+
CANONICAL_OPTIONS
|
131
|
+
end
|
132
|
+
def all_allowed_option_symbols
|
133
|
+
ALL_ALLOWED_OPTION_SYMBOLS
|
134
|
+
end
|
135
|
+
|
136
|
+
RECOGNIZED_METHOD_OPTIONS = {
|
137
|
+
"all" => %w[all_methods],
|
138
|
+
"public" => %w[public_methods],
|
139
|
+
"private" => %w[private_methods],
|
140
|
+
"protected" => %w[protected_methods],
|
141
|
+
"instance" => %w[instance_methods],
|
142
|
+
"class" => %w[class_methods],
|
143
|
+
"singleton" => %w[singleton_methods],
|
144
|
+
"exclude_ancestor_methods" => %w[exclude_ancestors exclude_ancestors_methods suppress_ancestors suppress_ancestor_methods suppress_ancestors_methods]
|
145
|
+
}
|
146
|
+
|
147
|
+
def self.init_method_options scope_options_set
|
148
|
+
return Set.new([]) if scope_options_set.nil?
|
149
|
+
options = []
|
150
|
+
scope_options_set.each do |opt|
|
151
|
+
if RECOGNIZED_METHOD_OPTIONS.keys.include?(opt.to_s)
|
152
|
+
options << opt
|
153
|
+
else
|
154
|
+
RECOGNIZED_METHOD_OPTIONS.keys.each do |key|
|
155
|
+
options << key.intern if RECOGNIZED_METHOD_OPTIONS[key].include?(opt.to_s)
|
156
|
+
end
|
157
|
+
end
|
158
|
+
end
|
159
|
+
options << :instance unless (options.include?(:class) or options.include?(:singleton))
|
160
|
+
Set.new(options.sort.uniq)
|
161
|
+
end
|
82
162
|
|
163
|
+
def self.all_recognized_method_option_symbols
|
164
|
+
all = RECOGNIZED_METHOD_OPTIONS.keys.map {|key| key.intern}
|
165
|
+
RECOGNIZED_METHOD_OPTIONS.keys.inject(all) do |all, key|
|
166
|
+
all += RECOGNIZED_METHOD_OPTIONS[key].map {|value| value.intern}
|
167
|
+
all
|
168
|
+
end
|
169
|
+
end
|
170
|
+
|
83
171
|
def self.is_recognized_method_option string_or_symbol
|
84
|
-
|
172
|
+
sym = string_or_symbol.respond_to?(:intern) ? string_or_symbol.intern : string_or_symbol
|
173
|
+
all_recognized_method_option_symbols.include? sym
|
85
174
|
end
|
86
175
|
|
87
176
|
protected
|
@@ -113,44 +202,51 @@ module Aquarium
|
|
113
202
|
Aquarium::Finders::FinderResult.new types_and_objects_to_matched_methods.merge(:not_matched => types_and_objects_not_matched)
|
114
203
|
end
|
115
204
|
|
116
|
-
def
|
117
|
-
|
118
|
-
|
119
|
-
@specification = options
|
205
|
+
def init_type_specific_specification original_options, options_hash
|
206
|
+
@specification[:options] = MethodFinder.init_method_options(@specification[:options]) if @specification[:options]
|
207
|
+
extra_validation
|
120
208
|
end
|
121
209
|
|
122
210
|
def nothing_to_find?
|
123
211
|
types_and_objects = input_types + input_objects
|
124
|
-
types_and_objects.nil? or types_and_objects.empty? or
|
212
|
+
types_and_objects.nil? or types_and_objects.empty? or all_exclude_all_methods?
|
125
213
|
end
|
126
214
|
|
127
215
|
def input_types
|
128
|
-
|
216
|
+
@specification[:types]
|
129
217
|
end
|
130
218
|
|
131
219
|
def input_objects
|
132
|
-
|
220
|
+
@specification[:objects]
|
133
221
|
end
|
134
222
|
|
135
223
|
def input_methods
|
136
|
-
|
224
|
+
@specification[:methods]
|
137
225
|
end
|
138
226
|
|
139
227
|
def input_exclude_methods
|
140
|
-
|
228
|
+
@specification[:exclude_methods]
|
229
|
+
end
|
230
|
+
|
231
|
+
def all_exclude_all_methods?
|
232
|
+
input_exclude_methods.include?(:all) or input_exclude_methods.include?(:all_methods)
|
141
233
|
end
|
142
234
|
|
143
235
|
def exclude_ancestor_methods?
|
144
|
-
@specification[:options].include?(:exclude_ancestor_methods)
|
236
|
+
@specification[:options].include?(:exclude_ancestor_methods)
|
145
237
|
end
|
146
238
|
|
147
239
|
private
|
148
240
|
|
149
241
|
def make_methods_array *array_or_single_item
|
150
242
|
ary = make_array(*array_or_single_item).reject {|m| m.to_s.strip.empty?}
|
151
|
-
ary = [/^.+$/] if
|
243
|
+
ary = [/^.+$/] if include_all_methods?(ary)
|
152
244
|
ary
|
153
245
|
end
|
246
|
+
|
247
|
+
def include_all_methods? array
|
248
|
+
array.include?(:all) or array.include?(:all_methods)
|
249
|
+
end
|
154
250
|
|
155
251
|
def make_regexp name_or_regexp
|
156
252
|
name_or_regexp.kind_of?(Regexp) ? name_or_regexp : /^#{Regexp.escape(name_or_regexp.to_s)}$/
|
@@ -175,13 +271,6 @@ module Aquarium
|
|
175
271
|
method_array
|
176
272
|
end
|
177
273
|
|
178
|
-
def init_method_options *scope_options
|
179
|
-
return [] if scope_options.nil?
|
180
|
-
options = scope_options.flatten
|
181
|
-
options << :instance unless options.include?(:class) or options.include?(:instance) or options.include?(:singleton)
|
182
|
-
options
|
183
|
-
end
|
184
|
-
|
185
274
|
def make_methods_reflection_method_names type_or_object, root_method_name
|
186
275
|
is_type = Aquarium::Utils::TypeUtils.is_type?(type_or_object)
|
187
276
|
scope_prefixes = []
|
@@ -226,11 +315,8 @@ module Aquarium
|
|
226
315
|
end
|
227
316
|
end
|
228
317
|
|
229
|
-
def
|
230
|
-
|
231
|
-
okay, bad = options.keys.partition {|x| allowed.include?(x)}
|
232
|
-
raise Aquarium::Utils::InvalidOptions.new("Unrecognized option(s): #{bad.inspect}") unless bad.empty?
|
233
|
-
method_options = options[:options]
|
318
|
+
def extra_validation
|
319
|
+
method_options = @specification[:options]
|
234
320
|
return if method_options.nil?
|
235
321
|
if method_options.include?(:singleton) &&
|
236
322
|
(method_options.include?(:class) || method_options.include?(:public) ||
|
@@ -148,7 +148,7 @@ module Aquarium
|
|
148
148
|
def find_namespace_matched expression, option
|
149
149
|
expr = expression.kind_of?(Regexp) ? expression.source : expression.to_s
|
150
150
|
return nil if expr.empty?
|
151
|
-
found_types = [
|
151
|
+
found_types = [Object]
|
152
152
|
split_expr = expr.split("::")
|
153
153
|
split_expr.each_with_index do |subexp, index|
|
154
154
|
next if subexp.size == 0
|
@@ -186,10 +186,7 @@ module Aquarium
|
|
186
186
|
enclosing_types.each do |parent|
|
187
187
|
next unless parent.respond_to?(:constants)
|
188
188
|
parent.constants.grep(regexp) do |name|
|
189
|
-
|
190
|
-
found_types << parent.const_get(name)
|
191
|
-
rescue NameError # ignore
|
192
|
-
end
|
189
|
+
found_types << parent.const_get(name)
|
193
190
|
end
|
194
191
|
end
|
195
192
|
found_types
|
data/lib/aquarium/utils.rb
CHANGED
@@ -10,16 +10,24 @@ module Aquarium
|
|
10
10
|
# flattened version of the input and any nil elements are removed by #strip_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
|
+
def self.make_array *value_or_enum
|
17
|
+
strip_nils do_make_array(value_or_enum)
|
18
|
+
end
|
19
|
+
|
16
20
|
# Return a copy of the input array with all nils removed.
|
17
21
|
def strip_nils array
|
18
|
-
array
|
22
|
+
ArrayUtils.strip_nils array
|
23
|
+
end
|
24
|
+
|
25
|
+
def self.strip_nils array
|
26
|
+
array.to_a.compact
|
19
27
|
end
|
20
28
|
|
21
29
|
private
|
22
|
-
def do_make_array value_or_enum
|
30
|
+
def self.do_make_array value_or_enum
|
23
31
|
v = value_or_enum.flatten
|
24
32
|
v = v[0].to_a if (v.empty? == false && v[0].kind_of?(Set))
|
25
33
|
v
|
@@ -0,0 +1,74 @@
|
|
1
|
+
module Aquarium
|
2
|
+
module Utils
|
3
|
+
|
4
|
+
# Support parsing and processing of key-value pairs of options.
|
5
|
+
# Including types must define the following methods (see Pointcut for an example):
|
6
|
+
# canonical_options # Return the hash of canonical options
|
7
|
+
# all_allowed_option_symbols # Returns an array of all allowed options as symbols.
|
8
|
+
# init_type_specific_specification(original_options, options_hash) for any unique options handling (optional).
|
9
|
+
module OptionsUtils
|
10
|
+
|
11
|
+
def self.universal_options
|
12
|
+
[:verbose, :log, :noop]
|
13
|
+
end
|
14
|
+
|
15
|
+
def init_specification options, canonical_options, &optional_block
|
16
|
+
@canonical_options = canonical_options
|
17
|
+
@original_options = options.dup unless options.nil?
|
18
|
+
@specification = {}
|
19
|
+
options ||= {}
|
20
|
+
options_hash = hashify options
|
21
|
+
@canonical_options.keys.each do |key|
|
22
|
+
all_related_options = make_array(options_hash[key.intern]) || []
|
23
|
+
@canonical_options[key].inject(all_related_options) do |ary, o|
|
24
|
+
ary << options_hash[o.intern] if options_hash[o.intern]
|
25
|
+
ary
|
26
|
+
end
|
27
|
+
@specification[key.intern] = Set.new(make_array(all_related_options))
|
28
|
+
end
|
29
|
+
uopts = {
|
30
|
+
:log => options_hash[:log] || "",
|
31
|
+
:verbose => options_hash[:verbose] || 0,
|
32
|
+
:noop => options_hash[:noop] || false
|
33
|
+
}
|
34
|
+
OptionsUtils::universal_options.each { |uopt| @specification[uopt] = Set.new [uopts[uopt]] }
|
35
|
+
init_type_specific_specification @original_options, options_hash, &optional_block
|
36
|
+
validate_options options_hash
|
37
|
+
end
|
38
|
+
|
39
|
+
OptionsUtils::universal_options.each do |name|
|
40
|
+
module_eval(<<-EOF, __FILE__, __LINE__)
|
41
|
+
def #{name}
|
42
|
+
@specification[:#{name}].to_a.first
|
43
|
+
end
|
44
|
+
def #{name}= value
|
45
|
+
@specification[:#{name}] = Set.new([value])
|
46
|
+
end
|
47
|
+
EOF
|
48
|
+
end
|
49
|
+
|
50
|
+
# Override for type-specific initialization
|
51
|
+
def init_type_specific_specification options, &optional_block
|
52
|
+
end
|
53
|
+
|
54
|
+
def hashify options
|
55
|
+
return options if options.kind_of?(Hash)
|
56
|
+
new_options = {}
|
57
|
+
options.each do |x|
|
58
|
+
if x.kind_of?(Hash)
|
59
|
+
new_options.merge!(x)
|
60
|
+
else
|
61
|
+
new_options[x] = Set.new([])
|
62
|
+
end
|
63
|
+
end
|
64
|
+
new_options
|
65
|
+
end
|
66
|
+
|
67
|
+
def validate_options options
|
68
|
+
unknowns = options.keys - all_allowed_option_symbols - OptionsUtils::universal_options
|
69
|
+
raise Aquarium::Utils::InvalidOptions.new("Unknown options specified: #{unknowns.inspect}") if unknowns.size > 0
|
70
|
+
end
|
71
|
+
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
@@ -6,22 +6,36 @@ module Aquarium
|
|
6
6
|
end
|
7
7
|
|
8
8
|
def self.descendents clazz
|
9
|
-
|
9
|
+
visited_types = [Class, Object, Module, clazz]
|
10
|
+
result = Module.constants.inject([clazz]) do |result, const|
|
11
|
+
mod = Module.class_eval(const)
|
12
|
+
if mod.respond_to?(:ancestors)
|
13
|
+
result << mod if mod.ancestors.include?(clazz)
|
14
|
+
do_descendents clazz, mod, visited_types, result
|
15
|
+
end
|
16
|
+
result
|
17
|
+
end
|
18
|
+
result.uniq
|
10
19
|
end
|
11
20
|
|
12
21
|
protected
|
13
22
|
|
14
|
-
def self.do_descendents clazz,
|
15
|
-
|
16
|
-
visited <<
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
next unless
|
21
|
-
|
22
|
-
|
23
|
+
def self.do_descendents clazz, visiting_module, visited, result
|
24
|
+
# p "#{clazz}, #{visiting_module}<br/>"
|
25
|
+
visited << visiting_module
|
26
|
+
# TODO Odd ruby behavior; a class is constant on the class, yet if it is nested in a module
|
27
|
+
# it comes up undefined! However, doing class_eval works around it. Is there a better way??
|
28
|
+
visiting_module.constants.each do |const|
|
29
|
+
next unless visiting_module.const_defined?(const)
|
30
|
+
clazz2 = visiting_module.const_get(const)
|
31
|
+
# clazz2 = visiting_module.const_defined?(const) ?
|
32
|
+
# visiting_module.const_get(const) :
|
33
|
+
# visiting_module.class_eval(const)
|
34
|
+
next if visited.include?(clazz2) or not clazz2.respond_to?(:ancestors)
|
35
|
+
visited << clazz2
|
36
|
+
result << clazz2 if clazz2.ancestors.include?(clazz)
|
37
|
+
do_descendents clazz, clazz2, visited, result
|
23
38
|
end
|
24
|
-
result.flatten.uniq
|
25
39
|
end
|
26
40
|
end
|
27
41
|
end
|