aquarium 0.1.8 → 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGES +59 -2
- data/README +33 -16
- data/RELEASE-PLAN +28 -5
- data/UPGRADE +11 -0
- data/examples/aspect_design_example.rb +2 -2
- data/examples/aspect_design_example_spec.rb +2 -2
- data/examples/design_by_contract_example.rb +4 -4
- data/examples/design_by_contract_example_spec.rb +4 -4
- data/examples/method_missing_example.rb +4 -1
- data/examples/method_missing_example_spec.rb +4 -1
- data/examples/method_tracing_example.rb +2 -2
- data/examples/method_tracing_example_spec.rb +16 -16
- data/lib/aquarium/aspects/advice.rb +47 -25
- data/lib/aquarium/aspects/aspect.rb +81 -39
- data/lib/aquarium/aspects/dsl/aspect_dsl.rb +1 -1
- data/lib/aquarium/aspects/exclusion_handler.rb +2 -2
- data/lib/aquarium/aspects/join_point.rb +28 -28
- data/lib/aquarium/aspects/pointcut.rb +61 -15
- data/lib/aquarium/extras/design_by_contract.rb +7 -7
- data/lib/aquarium/finders.rb +0 -1
- data/lib/aquarium/finders/method_finder.rb +10 -20
- data/lib/aquarium/finders/type_finder.rb +141 -75
- data/lib/aquarium/utils.rb +1 -0
- data/lib/aquarium/utils/logic_error.rb +9 -0
- data/lib/aquarium/utils/method_utils.rb +4 -3
- data/lib/aquarium/utils/nil_object.rb +1 -0
- data/lib/aquarium/utils/type_utils.rb +19 -0
- data/lib/aquarium/version.rb +2 -2
- data/spec/aquarium/aspects/advice_chain_node_spec.rb +2 -2
- data/spec/aquarium/aspects/advice_spec.rb +28 -5
- data/spec/aquarium/aspects/aspect_invocation_spec.rb +522 -289
- data/spec/aquarium/aspects/aspect_spec.rb +59 -41
- data/spec/aquarium/aspects/aspect_with_nested_types_spec.rb +7 -7
- data/spec/aquarium/aspects/aspect_with_subtypes_spec.rb +2 -2
- data/spec/aquarium/aspects/concurrent_aspects_spec.rb +1 -2
- data/spec/aquarium/aspects/concurrent_aspects_with_objects_and_types_spec.rb +1 -1
- data/spec/aquarium/aspects/dsl/aspect_dsl_spec.rb +34 -34
- data/spec/aquarium/aspects/join_point_spec.rb +79 -0
- data/spec/aquarium/aspects/pointcut_or_composition_spec.rb +13 -3
- data/spec/aquarium/aspects/pointcut_spec.rb +310 -63
- data/spec/aquarium/extras/design_by_contract_spec.rb +4 -4
- data/spec/aquarium/finders/method_finder_spec.rb +208 -54
- data/spec/aquarium/finders/type_finder_spec.rb +24 -88
- data/spec/aquarium/finders/type_finder_with_descendents_and_ancestors_spec.rb +206 -0
- data/spec/aquarium/spec_example_classes.rb +75 -12
- data/spec/aquarium/utils/logic_error_spec.rb +10 -0
- data/spec/aquarium/utils/type_utils_sample_classes.rb +203 -0
- data/spec/aquarium/utils/type_utils_spec.rb +47 -1
- metadata +48 -39
- data/lib/aquarium/finders/object_finder.rb +0 -75
- data/spec/aquarium/finders/object_finder_spec.rb +0 -231
@@ -36,6 +36,13 @@ module Aquarium
|
|
36
36
|
# <tt>:type => type || [type_list]</tt>::
|
37
37
|
# One or an array of types, type names and/or type regular expessions to match.
|
38
38
|
#
|
39
|
+
# <tt>:types_and_descendents => type || [type_list]</tt>::
|
40
|
+
# <tt>:type_and_descendents => type || [type_list]</tt>::
|
41
|
+
# <tt>:types_and_ancestors => type || [type_list]</tt>::
|
42
|
+
# <tt>:type_and_ancestors => type || [type_list]</tt>::
|
43
|
+
# One or an array of types and either their descendents or ancestors.
|
44
|
+
# If you want both the descendents _and_ ancestors, use both options.
|
45
|
+
#
|
39
46
|
# <tt>:objects => object || [object_list]</tt>::
|
40
47
|
# <tt>:object => object || [object_list]</tt>::
|
41
48
|
# Objects to match.
|
@@ -86,6 +93,13 @@ module Aquarium
|
|
86
93
|
# excluded, they should be subsets of the matched pointcuts. Otherwise, the
|
87
94
|
# resulting pointcut will be empty!
|
88
95
|
#
|
96
|
+
# <tt>:exclude_types_and_descendents => type || [type_list]</tt>::
|
97
|
+
# <tt>:exclude_type_and_descendents => type || [type_list]</tt>::
|
98
|
+
# <tt>:exclude_types_and_ancestors => type || [type_list]</tt>::
|
99
|
+
# <tt>:exclude_type_and_ancestors => type || [type_list]</tt>::
|
100
|
+
# Exclude the specified types and their descendents, ancestors.
|
101
|
+
# If you want to exclude both the descendents _and_ ancestors, use both options.
|
102
|
+
#
|
89
103
|
def initialize options = {}
|
90
104
|
init_specification options
|
91
105
|
init_candidate_types
|
@@ -94,7 +108,7 @@ module Aquarium
|
|
94
108
|
init_join_points
|
95
109
|
end
|
96
110
|
|
97
|
-
attr_reader :join_points_matched, :join_points_not_matched, :specification, :candidate_types, :candidate_objects, :candidate_join_points
|
111
|
+
attr_reader :join_points_matched, :join_points_not_matched, :specification, :candidate_types, :candidate_types_excluded, :candidate_objects, :candidate_join_points
|
98
112
|
|
99
113
|
# Two Considered equivalent only if the same join points matched and not_matched sets are equal,
|
100
114
|
# the specifications are equal, and the candidate types and candidate objects are equal.
|
@@ -103,6 +117,7 @@ module Aquarium
|
|
103
117
|
object_id == other.object_id ||
|
104
118
|
(specification == other.specification &&
|
105
119
|
candidate_types == other.candidate_types &&
|
120
|
+
candidate_types_excluded == other.candidate_types_excluded &&
|
106
121
|
candidate_objects == other.candidate_objects &&
|
107
122
|
join_points_matched == other.join_points_matched &&
|
108
123
|
join_points_not_matched == other.join_points_not_matched)
|
@@ -115,7 +130,7 @@ module Aquarium
|
|
115
130
|
end
|
116
131
|
|
117
132
|
def inspect
|
118
|
-
"Pointcut: {specification: #{specification.inspect}, candidate_types: #{candidate_types.inspect}, candidate_objects: #{candidate_objects.inspect}, join_points_matched: #{join_points_matched.inspect}, join_points_not_matched: #{join_points_not_matched.inspect}}"
|
133
|
+
"Pointcut: {specification: #{specification.inspect}, candidate_types: #{candidate_types.inspect}, candidate_types_excluded: #{candidate_types_excluded.inspect}, candidate_objects: #{candidate_objects.inspect}, join_points_matched: #{join_points_matched.inspect}, join_points_not_matched: #{join_points_not_matched.inspect}}"
|
119
134
|
end
|
120
135
|
|
121
136
|
alias to_s inspect
|
@@ -131,11 +146,23 @@ module Aquarium
|
|
131
146
|
|
132
147
|
protected
|
133
148
|
|
134
|
-
attr_writer :join_points_matched, :join_points_not_matched, :specification, :candidate_types, :candidate_objects, :candidate_join_points
|
149
|
+
attr_writer :join_points_matched, :join_points_not_matched, :specification, :candidate_types, :candidate_types_excluded, :candidate_objects, :candidate_join_points
|
135
150
|
|
136
|
-
ALLOWED_OPTIONS_SINGULAR = %w[
|
137
|
-
|
151
|
+
ALLOWED_OPTIONS_SINGULAR = %w[
|
152
|
+
type object join_point method
|
153
|
+
exclude_type exclude_object exclude_join_point exclude_pointcut exclude_method
|
154
|
+
default_object attribute method_option attribute_option]
|
138
155
|
|
156
|
+
OTHER_ALLOWED_OPTIONS = %w[
|
157
|
+
type_and_descendents types_and_descendents type_and_ancestors types_and_ancestors
|
158
|
+
exclude_type_and_descendents exclude_types_and_descendents exclude_type_and_ancestors exclude_types_and_ancestors]
|
159
|
+
|
160
|
+
OTHER_ALLOWED_SPEC_KEYS = {
|
161
|
+
"types_and_descendents" => "type_and_descendents",
|
162
|
+
"types_and_ancestors" => "type_and_ancestors",
|
163
|
+
"exclude_types_and_descendents" => "exclude_type_and_descendents",
|
164
|
+
"exclude_types_and_ancestors" => "exclude_type_and_ancestors" }
|
165
|
+
|
139
166
|
def init_specification options
|
140
167
|
@specification = {}
|
141
168
|
options ||= {}
|
@@ -145,6 +172,12 @@ module Aquarium
|
|
145
172
|
@specification[:#{option}s]= Set.new(make_array(options[:#{option}], options[:#{option}s]))
|
146
173
|
EOF
|
147
174
|
end
|
175
|
+
OTHER_ALLOWED_SPEC_KEYS.keys.each do |option|
|
176
|
+
self.instance_eval(<<-EOF, __FILE__, __LINE__)
|
177
|
+
@specification[:#{option}]= Set.new(make_array(options[:#{option}], options[:#{OTHER_ALLOWED_SPEC_KEYS[option]}]))
|
178
|
+
EOF
|
179
|
+
end
|
180
|
+
|
148
181
|
use_default_object_if_defined unless (types_given? || objects_given?)
|
149
182
|
|
150
183
|
raise Aquarium::Utils::InvalidOptions.new(":all is not yet supported for :attributes.") if @specification[:attributes] == Set.new([:all])
|
@@ -162,6 +195,9 @@ module Aquarium
|
|
162
195
|
knowns << x.intern
|
163
196
|
knowns << "#{x}s".intern
|
164
197
|
end
|
198
|
+
OTHER_ALLOWED_OPTIONS.each do |x|
|
199
|
+
knowns << x.intern
|
200
|
+
end
|
165
201
|
unknowns = options.keys - knowns
|
166
202
|
raise Aquarium::Utils::InvalidOptions.new("Unknown options specified: #{unknowns.inspect}") if unknowns.size > 0
|
167
203
|
end
|
@@ -209,16 +245,26 @@ module Aquarium
|
|
209
245
|
private
|
210
246
|
|
211
247
|
def init_candidate_types
|
212
|
-
|
213
|
-
|
214
|
-
|
215
|
-
|
216
|
-
|
248
|
+
finder_options = {}
|
249
|
+
exclude_finder_options = {}
|
250
|
+
['', 'exclude_'].each do |prefix|
|
251
|
+
['', '_and_ancestors', '_and_descendents'].each do |suffix|
|
252
|
+
# Because the user might be asking for descendents and/or ancestors, we convert explicitly-specified
|
253
|
+
# types into names, then "refind" them. While less efficient, it makes the code more uniform.
|
254
|
+
eval <<-EOF
|
255
|
+
#{prefix}type_regexps_or_names#{suffix} = @specification[:#{prefix}types#{suffix}].map do |t|
|
256
|
+
Aquarium::Utils::TypeUtils.is_type?(t) ? t.name : t
|
257
|
+
end
|
258
|
+
unless #{prefix}type_regexps_or_names#{suffix}.nil?
|
259
|
+
finder_options[:"#{prefix}types#{suffix}"] = #{prefix}type_regexps_or_names#{suffix}
|
260
|
+
exclude_finder_options[:"types#{suffix}"] = #{prefix}type_regexps_or_names#{suffix} if "#{prefix}".length > 0
|
261
|
+
end
|
262
|
+
EOF
|
263
|
+
end
|
217
264
|
end
|
218
|
-
|
219
|
-
|
220
|
-
@
|
221
|
-
@candidate_types.matched.delete_if {|type, value| excluded_explicit_types.include? type}
|
265
|
+
@candidate_types = Aquarium::Finders::TypeFinder.new.find finder_options
|
266
|
+
@candidate_types_excluded = Aquarium::Finders::TypeFinder.new.find exclude_finder_options
|
267
|
+
@specification[:exclude_types_calculated] = Set.new(@candidate_types_excluded.matched.keys)
|
222
268
|
end
|
223
269
|
|
224
270
|
def init_candidate_objects
|
@@ -243,7 +289,7 @@ module Aquarium
|
|
243
289
|
def init_join_points
|
244
290
|
@join_points_matched = Set.new
|
245
291
|
@join_points_not_matched = Set.new
|
246
|
-
find_join_points_for :type, candidate_types, make_all_method_names
|
292
|
+
find_join_points_for :type, (candidate_types - candidate_types_excluded), make_all_method_names
|
247
293
|
find_join_points_for :object, candidate_objects, make_all_method_names
|
248
294
|
add_join_points_for_candidate_join_points
|
249
295
|
remove_excluded_join_points
|
@@ -28,25 +28,25 @@ module Aquarium
|
|
28
28
|
|
29
29
|
def invariant *args, &contract_block
|
30
30
|
message = handle_message_arg args
|
31
|
-
Aspect.new make_args(:around, *args) do |jp, *params|
|
32
|
-
DesignByContract.test_condition "invariant failure (before invocation): #{message}", jp, *params, &contract_block
|
31
|
+
Aspect.new make_args(:around, *args) do |jp, obj, *params|
|
32
|
+
DesignByContract.test_condition "invariant failure (before invocation): #{message}", jp, obj, *params, &contract_block
|
33
33
|
result = jp.proceed
|
34
|
-
DesignByContract.test_condition "invariant failure (after invocation): #{message}", jp, *params, &contract_block
|
34
|
+
DesignByContract.test_condition "invariant failure (after invocation): #{message}", jp, obj, *params, &contract_block
|
35
35
|
result
|
36
36
|
end
|
37
37
|
end
|
38
38
|
|
39
39
|
private
|
40
40
|
|
41
|
-
def self.test_condition message, jp, *args
|
42
|
-
unless yield(jp, *args)
|
41
|
+
def self.test_condition message, jp, obj, *args
|
42
|
+
unless yield(jp, obj, *args)
|
43
43
|
raise ContractError.new(message)
|
44
44
|
end
|
45
45
|
end
|
46
46
|
|
47
47
|
def add_advice kind, test_kind, message, *args, &contract_block
|
48
|
-
Aspect.new make_args(kind, *args) do |jp, *params|
|
49
|
-
DesignByContract.test_condition "#{test_kind} failure: #{message}", jp, *params, &contract_block
|
48
|
+
Aspect.new make_args(kind, *args) do |jp, obj, *params|
|
49
|
+
DesignByContract.test_condition "#{test_kind} failure: #{message}", jp, obj, *params, &contract_block
|
50
50
|
end
|
51
51
|
end
|
52
52
|
|
data/lib/aquarium/finders.rb
CHANGED
@@ -75,16 +75,17 @@ module Aquarium
|
|
75
75
|
result
|
76
76
|
end
|
77
77
|
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
do_find_all_by types_and_objects, method_names_or_regexps
|
78
|
+
NIL_OBJECT = MethodFinder.new unless const_defined?(:NIL_OBJECT)
|
79
|
+
|
80
|
+
RECOGNIZED_METHOD_OPTIONS = %w[public private protected
|
81
|
+
instance class exclude_ancestor_methods exclude_ancestor_methods]
|
82
|
+
|
83
|
+
def self.is_recognized_method_option string_or_symbol
|
84
|
+
RECOGNIZED_METHOD_OPTIONS.include? string_or_symbol.to_s
|
86
85
|
end
|
87
|
-
|
86
|
+
|
87
|
+
protected
|
88
|
+
|
88
89
|
def do_find_all_by types_and_objects, method_names_or_regexps
|
89
90
|
types_and_objects = make_array types_and_objects
|
90
91
|
names_or_regexps = make_methods_array method_names_or_regexps
|
@@ -112,17 +113,6 @@ module Aquarium
|
|
112
113
|
Aquarium::Finders::FinderResult.new types_and_objects_to_matched_methods.merge(:not_matched => types_and_objects_not_matched)
|
113
114
|
end
|
114
115
|
|
115
|
-
NIL_OBJECT = MethodFinder.new unless const_defined?(:NIL_OBJECT)
|
116
|
-
|
117
|
-
RECOGNIZED_METHOD_OPTIONS = %w[public private protected
|
118
|
-
instance class exclude_ancestor_methods exclude_ancestor_methods]
|
119
|
-
|
120
|
-
def self.is_recognized_method_option string_or_symbol
|
121
|
-
RECOGNIZED_METHOD_OPTIONS.include? string_or_symbol.to_s
|
122
|
-
end
|
123
|
-
|
124
|
-
protected
|
125
|
-
|
126
116
|
def init_specification options
|
127
117
|
options[:options] = init_method_options(options[:options])
|
128
118
|
validate options
|
@@ -1,5 +1,6 @@
|
|
1
1
|
require 'set'
|
2
2
|
require File.dirname(__FILE__) + '/../utils/array_utils'
|
3
|
+
require File.dirname(__FILE__) + '/../utils/type_utils'
|
3
4
|
require File.dirname(__FILE__) + '/../utils/invalid_options'
|
4
5
|
require File.dirname(__FILE__) + '/../extensions/hash'
|
5
6
|
require File.dirname(__FILE__) + '/../extensions/regexp'
|
@@ -11,32 +12,59 @@ module Aquarium
|
|
11
12
|
module Finders
|
12
13
|
class TypeFinder
|
13
14
|
include Aquarium::Utils::ArrayUtils
|
15
|
+
include Aquarium::Utils::TypeUtils
|
14
16
|
|
15
17
|
# Usage:
|
16
|
-
# finder_result = TypeFinder.new.find [
|
18
|
+
# finder_result = TypeFinder.new.find [options => [...] ]
|
17
19
|
# where
|
18
20
|
# <tt>:types => types_and_type_names_and_regexps</tt>::
|
19
|
-
# The types or type names/regular expessions to match.
|
20
|
-
# Specify one or an array of values.
|
21
|
-
#
|
22
21
|
# <tt>:names => types_and_type_names_and_regexps</tt>::
|
23
|
-
#
|
22
|
+
# <tt>:type => types_and_type_names_and_regexps</tt>::
|
23
|
+
# <tt>:name => types_and_type_names_and_regexps</tt>::
|
24
|
+
# A single type or array of types, specified using any combination of the type
|
25
|
+
# name strings, the type "constants" and/or regular expessions. The four different
|
26
|
+
# flags are just "sugar" for each other.
|
27
|
+
#
|
28
|
+
# <tt>:types_and_descendents => types_and_type_names_and_regexps</tt>::
|
29
|
+
# <tt>:names_and_descendents => types_and_type_names_and_regexps</tt>::
|
30
|
+
# <tt>:type_and_descendents => types_and_type_names_and_regexps</tt>::
|
31
|
+
# <tt>:name_and_descendents => types_and_type_names_and_regexps</tt>::
|
32
|
+
#
|
33
|
+
# Same as for <tt>:types</tt> <i>etc.</i>, but also match their descendents.
|
34
|
+
#
|
35
|
+
# <tt>:types_and_ancestors => types_and_type_names_and_regexps</tt>::
|
36
|
+
# <tt>:names_and_ancestors => types_and_type_names_and_regexps</tt>::
|
37
|
+
# <tt>:type_and_ancestors => types_and_type_names_and_regexps</tt>::
|
38
|
+
# <tt>:name_and_ancestors => types_and_type_names_and_regexps</tt>::
|
39
|
+
#
|
40
|
+
# Same as for <tt>:types</tt> <i>etc.</i>, but also match their ancestors.
|
41
|
+
# This option will also match <tt>Class</tt>, <tt>Module</tt>, <i>etc.</>,
|
42
|
+
# so use with caution!
|
24
43
|
#
|
25
|
-
#
|
26
|
-
#
|
44
|
+
# To get both descendents and ancestors, use both options with the same type
|
45
|
+
# specification.
|
27
46
|
#
|
28
|
-
#
|
29
|
-
# Sugar for specifying one type name.
|
47
|
+
# The "other options" include the following:
|
30
48
|
#
|
31
|
-
# <tt>:exclude_type => type_or_type_name_or_regexp</tt>::
|
32
|
-
# <tt>:exclude_types => type_or_type_name_or_regexp</tt>::
|
33
|
-
# <tt>:exclude_name => type_or_type_name_or_regexp</tt>::
|
34
|
-
# <tt>:exclude_names => type_or_type_name_or_regexp</tt>::
|
35
|
-
# Exclude matching types from the list. These excluded types <b>won't</b> appear
|
36
|
-
# in the FinderResult#not_matched.
|
37
49
|
#
|
38
|
-
#
|
39
|
-
# <tt>:
|
50
|
+
# <tt>:exclude_type => types_and_type_names_and_regexps</tt>::
|
51
|
+
# <tt>:exclude_types => types_and_type_names_and_regexps</tt>::
|
52
|
+
# <tt>:exclude_name => types_and_type_names_and_regexps</tt>::
|
53
|
+
# <tt>:exclude_names => types_and_type_names_and_regexps</tt>::
|
54
|
+
# Exclude the specified type or list of types from the list of matched types.
|
55
|
+
# These excluded types <b>won't</b> appear in the FinderResult#not_matched.
|
56
|
+
#
|
57
|
+
# <tt>:exclude_types_and_descendents => types_and_type_names_and_regexps</tt>::
|
58
|
+
# <tt>:exclude_names_and_descendents => types_and_type_names_and_regexps</tt>::
|
59
|
+
# <tt>:exclude_type_and_descendents => types_and_type_names_and_regexps</tt>::
|
60
|
+
# <tt>:exclude_name_and_descendents => types_and_type_names_and_regexps</tt>::
|
61
|
+
#
|
62
|
+
# <tt>:exclude_types_and_ancestors => types_and_type_names_and_regexps</tt>::
|
63
|
+
# <tt>:exclude_names_and_ancestors => types_and_type_names_and_regexps</tt>::
|
64
|
+
# <tt>:exclude_type_and_ancestors => types_and_type_names_and_regexps</tt>::
|
65
|
+
# <tt>:exclude_name_and_ancestors => types_and_type_names_and_regexps</tt>::
|
66
|
+
#
|
67
|
+
# Exclude the descendents or ancestors, as well.
|
40
68
|
#
|
41
69
|
# Because of the special sigificance of the module ("namespace") separator "::", the rules
|
42
70
|
# for the regular expressions are as follows. Assume that "subexp" is a "sub regular
|
@@ -57,79 +85,67 @@ module Aquarium
|
|
57
85
|
# It behaves as <tt>/...::#{subexp}::.../</tt>, meaning that the subexp must match
|
58
86
|
# the whole name between the "::" exactly.
|
59
87
|
#
|
88
|
+
# Note: a common idiom in aspects is to include descendents of a type, but not the type
|
89
|
+
# itself. You can do as in the following example:
|
90
|
+
# <tt>... :type_and_descendents => "Foo", :exclude_type => "Foo"
|
60
91
|
def find options = {}
|
61
92
|
result = Aquarium::Finders::FinderResult.new
|
62
93
|
excluded = Aquarium::Finders::FinderResult.new
|
63
94
|
unknown_options = []
|
95
|
+
input_type_nil = false
|
64
96
|
options.each do |option, value|
|
65
97
|
unless TypeFinder.is_recognized_option option
|
66
98
|
unknown_options << option
|
67
99
|
next
|
68
100
|
end
|
69
|
-
|
101
|
+
if value.nil?
|
102
|
+
input_type_nil = true
|
103
|
+
next
|
104
|
+
end
|
70
105
|
if option.to_s =~ /^exclude_/
|
71
|
-
excluded <<
|
106
|
+
excluded << find_matching(value, option)
|
72
107
|
else
|
73
|
-
result <<
|
108
|
+
result << find_matching(value, option)
|
74
109
|
end
|
75
110
|
end
|
76
|
-
|
111
|
+
handle_errors unknown_options, input_type_nil
|
77
112
|
result - excluded
|
78
113
|
end
|
79
114
|
|
80
|
-
|
81
|
-
|
82
|
-
def
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
begin
|
88
|
-
found << name.split("::").inject(Object) { |parent, const| parent.const_get(const) }
|
89
|
-
Aquarium::Finders::FinderResult.new(make_return_hash(found, []))
|
90
|
-
rescue NameError => ne
|
91
|
-
Aquarium::Finders::FinderResult.new(make_return_hash([], [type_name]))
|
92
|
-
end
|
93
|
-
end
|
94
|
-
|
95
|
-
alias :find_by_type :find_by_name
|
96
|
-
|
97
|
-
def find_all_by regexpes_or_names
|
98
|
-
raise Aquarium::Utils::InvalidOptions.new("Input type(s) can't be nil!") if regexpes_or_names.nil?
|
99
|
-
find_matching regexpes_or_names
|
115
|
+
protected
|
116
|
+
|
117
|
+
def handle_errors unknown_options, input_type_nil
|
118
|
+
message = ""
|
119
|
+
message += "Unknown options: #{unknown_options.inspect}. " unless unknown_options.empty?
|
120
|
+
message += "Input type specification can't be nil! " if input_type_nil
|
121
|
+
raise Aquarium::Utils::InvalidOptions.new(message) unless message.empty?
|
100
122
|
end
|
101
123
|
|
102
124
|
def self.is_recognized_option option_or_symbol
|
103
|
-
%w[name names type types
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
return nil if expression.nil?
|
110
|
-
expression.respond_to?(:strip) ? expression.strip : expression
|
111
|
-
end
|
112
|
-
|
113
|
-
def empty expression
|
114
|
-
expression.nil? || (expression.respond_to?(:empty?) && expression.empty?)
|
125
|
+
%w[name names type types].each do |t|
|
126
|
+
['', "exclude_"].each do |excl|
|
127
|
+
return true if ["#{excl}#{t}", "#{excl}#{t}_and_descendents", "#{excl}#{t}_and_ancestors"].include?(option_or_symbol.to_s)
|
128
|
+
end
|
129
|
+
end
|
130
|
+
false
|
115
131
|
end
|
116
132
|
|
117
|
-
def find_matching regexpes_or_names
|
133
|
+
def find_matching regexpes_or_names, option
|
118
134
|
result = Aquarium::Finders::FinderResult.new
|
119
135
|
expressions = make_array regexpes_or_names
|
120
136
|
expressions.each do |expression|
|
121
137
|
expr = strip expression
|
122
138
|
next if empty expr
|
123
139
|
if expr.kind_of? Regexp
|
124
|
-
result << find_namespace_matched(expr)
|
140
|
+
result << find_namespace_matched(expr, option)
|
125
141
|
else
|
126
|
-
result << find_by_name(expr)
|
142
|
+
result << find_by_name(expr, option)
|
127
143
|
end
|
128
144
|
end
|
129
145
|
result
|
130
146
|
end
|
131
147
|
|
132
|
-
def find_namespace_matched expression
|
148
|
+
def find_namespace_matched expression, option
|
133
149
|
expr = expression.kind_of?(Regexp) ? expression.source : expression.to_s
|
134
150
|
return nil if expr.empty?
|
135
151
|
found_types = [Module]
|
@@ -140,12 +156,26 @@ module Aquarium
|
|
140
156
|
break if found_types.size == 0
|
141
157
|
end
|
142
158
|
if found_types.size > 0
|
143
|
-
|
159
|
+
finish_and_make_successful_result found_types, option
|
144
160
|
else
|
145
|
-
|
161
|
+
make_failed_result expression
|
146
162
|
end
|
147
163
|
end
|
148
164
|
|
165
|
+
# For a name (not a regular expression), return the corresponding type.
|
166
|
+
# (Adapted from the RubyQuiz #113 solution by James Edward Gray II)
|
167
|
+
def find_by_name type_name, option
|
168
|
+
name = type_name.to_s # in case it's a symbol...
|
169
|
+
return nil if name.nil? || name.strip.empty?
|
170
|
+
name.strip!
|
171
|
+
begin
|
172
|
+
found = [name.split("::").inject(Object) { |parent, const| parent.const_get(const) }]
|
173
|
+
finish_and_make_successful_result found, option
|
174
|
+
rescue NameError
|
175
|
+
make_failed_result type_name
|
176
|
+
end
|
177
|
+
end
|
178
|
+
|
149
179
|
def find_next_types enclosing_types, subexp, suppress_lh_ctrl_a, suppress_rh_ctrl_z
|
150
180
|
# grep <parent>.constants because "subexp" may be a regexp string!.
|
151
181
|
# Then use const_get to get the type itself.
|
@@ -154,30 +184,66 @@ module Aquarium
|
|
154
184
|
rhs = suppress_rh_ctrl_z ? "" : "\\Z"
|
155
185
|
regexp = /#{lhs}#{subexp}#{rhs}/
|
156
186
|
enclosing_types.each do |parent|
|
157
|
-
parent.constants
|
158
|
-
|
187
|
+
next unless parent.respond_to?(:constants)
|
188
|
+
parent.constants.grep(regexp) do |name|
|
189
|
+
begin
|
190
|
+
found_types << parent.const_get(name)
|
191
|
+
rescue NameError # ignore
|
192
|
+
end
|
159
193
|
end
|
160
194
|
end
|
161
195
|
found_types
|
162
196
|
end
|
163
197
|
|
164
|
-
def
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
198
|
+
def finish_and_make_successful_result found, option
|
199
|
+
all = prettify(found + handle_ancestors_and_descendents(found, option))
|
200
|
+
hash = make_return_hash(all)
|
201
|
+
Aquarium::Finders::FinderResult.new hash
|
202
|
+
end
|
203
|
+
|
204
|
+
def make_failed_result name
|
205
|
+
Aquarium::Finders::FinderResult.new :not_matched => {name => Set.new([])}
|
206
|
+
end
|
207
|
+
|
208
|
+
def handle_ancestors_and_descendents types, option
|
209
|
+
result = []
|
210
|
+
result << add_descendents(types) if should_find_descendents(option)
|
211
|
+
result << add_ancestors(types) if should_find_ancestors(option)
|
212
|
+
result
|
213
|
+
end
|
214
|
+
|
215
|
+
def add_descendents types
|
216
|
+
types.inject([]) { |memo, t| memo << Aquarium::Utils::TypeUtils.descendents(t) }
|
217
|
+
end
|
218
|
+
def add_ancestors types
|
219
|
+
types.inject([]) { |memo, t| memo << t.ancestors }
|
169
220
|
end
|
170
221
|
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
|
178
|
-
|
179
|
-
|
222
|
+
def should_find_descendents option
|
223
|
+
option.to_s.include? "_descendents"
|
224
|
+
end
|
225
|
+
def should_find_ancestors option
|
226
|
+
option.to_s.include? "_ancestors"
|
227
|
+
end
|
228
|
+
|
229
|
+
|
230
|
+
def make_return_hash found
|
231
|
+
prettify(found).inject({}) {|hash, x| hash[x] = Set.new([]); hash}
|
232
|
+
end
|
233
|
+
|
234
|
+
def prettify array
|
235
|
+
array.flatten.uniq.inject([]) {|memo, x| memo << x unless empty(x); memo}
|
180
236
|
end
|
237
|
+
|
238
|
+
def strip expression
|
239
|
+
return nil if expression.nil?
|
240
|
+
expression.respond_to?(:strip) ? expression.strip : expression
|
241
|
+
end
|
242
|
+
|
243
|
+
def empty thing
|
244
|
+
thing.nil? || (thing.respond_to?(:empty?) && thing.empty?)
|
245
|
+
end
|
246
|
+
|
181
247
|
end
|
182
248
|
end
|
183
249
|
end
|