aquarium 0.1.8 → 0.2.0
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 +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
|