aquarium 0.2.0 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (59) hide show
  1. data/CHANGES +35 -0
  2. data/MIT-LICENSE +1 -1
  3. data/README +66 -20
  4. data/Rakefile +1 -1
  5. data/UPGRADE +5 -0
  6. data/examples/aspect_design_example.rb +5 -4
  7. data/examples/aspect_design_example_spec.rb +6 -5
  8. data/examples/design_by_contract_example.rb +3 -3
  9. data/examples/design_by_contract_example_spec.rb +4 -4
  10. data/examples/method_missing_example.rb +1 -1
  11. data/examples/method_missing_example_spec.rb +2 -2
  12. data/examples/method_tracing_example.rb +3 -3
  13. data/examples/method_tracing_example_spec.rb +6 -6
  14. data/lib/aquarium/aspects/advice.rb +2 -3
  15. data/lib/aquarium/aspects/aspect.rb +100 -246
  16. data/lib/aquarium/aspects/{default_object_handler.rb → default_objects_handler.rb} +7 -6
  17. data/lib/aquarium/aspects/dsl/aspect_dsl.rb +2 -2
  18. data/lib/aquarium/aspects/pointcut.rb +190 -107
  19. data/lib/aquarium/finders/method_finder.rb +120 -34
  20. data/lib/aquarium/finders/type_finder.rb +2 -5
  21. data/lib/aquarium/utils.rb +1 -0
  22. data/lib/aquarium/utils/array_utils.rb +11 -3
  23. data/lib/aquarium/utils/options_utils.rb +74 -0
  24. data/lib/aquarium/utils/type_utils.rb +25 -11
  25. data/lib/aquarium/version.rb +1 -1
  26. data/spec/aquarium/aspects/advice_chain_node_spec.rb +1 -1
  27. data/spec/aquarium/aspects/advice_spec.rb +1 -1
  28. data/spec/aquarium/aspects/aspect_invocation_spec.rb +179 -145
  29. data/spec/aquarium/aspects/aspect_spec.rb +1 -1
  30. data/spec/aquarium/aspects/aspect_with_nested_types_spec.rb +1 -1
  31. data/spec/aquarium/aspects/aspect_with_subtypes_spec.rb +1 -1
  32. data/spec/aquarium/aspects/concurrent_aspects_spec.rb +1 -1
  33. data/spec/aquarium/aspects/concurrent_aspects_with_objects_and_types_spec.rb +1 -1
  34. data/spec/aquarium/aspects/default_objects_handler_spec.rb +147 -0
  35. data/spec/aquarium/aspects/dsl/aspect_dsl_spec.rb +72 -121
  36. data/spec/aquarium/aspects/join_point_spec.rb +1 -1
  37. data/spec/aquarium/aspects/pointcut_and_composition_spec.rb +1 -1
  38. data/spec/aquarium/aspects/pointcut_or_composition_spec.rb +48 -47
  39. data/spec/aquarium/aspects/pointcut_spec.rb +727 -410
  40. data/spec/aquarium/extensions/hash_spec.rb +1 -1
  41. data/spec/aquarium/extensions/regex_spec.rb +1 -1
  42. data/spec/aquarium/extensions/set_spec.rb +1 -1
  43. data/spec/aquarium/extensions/string_spec.rb +1 -1
  44. data/spec/aquarium/extensions/symbol_spec.rb +1 -1
  45. data/spec/aquarium/extras/design_by_contract_spec.rb +1 -1
  46. data/spec/aquarium/finders/finder_result_spec.rb +1 -1
  47. data/spec/aquarium/finders/method_finder_spec.rb +49 -16
  48. data/spec/aquarium/finders/type_finder_spec.rb +1 -1
  49. data/spec/aquarium/finders/type_finder_with_descendents_and_ancestors_spec.rb +16 -1
  50. data/spec/aquarium/utils/array_utils_spec.rb +31 -6
  51. data/spec/aquarium/utils/hash_utils_spec.rb +1 -1
  52. data/spec/aquarium/utils/html_escaper_spec.rb +1 -1
  53. data/spec/aquarium/utils/logic_error_spec.rb +1 -1
  54. data/spec/aquarium/utils/method_utils_spec.rb +1 -1
  55. data/spec/aquarium/utils/name_utils_spec.rb +1 -1
  56. data/spec/aquarium/utils/nil_object_spec.rb +1 -1
  57. data/spec/aquarium/utils/set_utils_spec.rb +1 -1
  58. data/spec/aquarium/utils/type_utils_spec.rb +1 -1
  59. 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_&lt;other_method_synonyms&gt; => 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
- RECOGNIZED_METHOD_OPTIONS = %w[public private protected
81
- instance class exclude_ancestor_methods exclude_ancestor_methods]
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
- RECOGNIZED_METHOD_OPTIONS.include? string_or_symbol.to_s
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 init_specification options
117
- options[:options] = init_method_options(options[:options])
118
- validate options
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 input_exclude_methods.include?(:all)
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
- make_array @specification[:types], @specification[:type]
216
+ @specification[:types]
129
217
  end
130
218
 
131
219
  def input_objects
132
- make_array @specification[:objects], @specification[:object]
220
+ @specification[:objects]
133
221
  end
134
222
 
135
223
  def input_methods
136
- make_array @specification[:methods], @specification[:method]
224
+ @specification[:methods]
137
225
  end
138
226
 
139
227
  def input_exclude_methods
140
- make_array @specification[:exclude_methods], @specification[:exclude_method]
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) or @specification[:options].include?(:suppress_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 ary.include?(:all)
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 validate options
230
- allowed = (%w[type types object objects method methods exclude_method exclude_methods options] + RECOGNIZED_METHOD_OPTIONS).map {|x| x.intern}
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 = [Module]
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
- begin
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
@@ -4,6 +4,7 @@ require 'aquarium/utils/set_utils'
4
4
 
5
5
  require 'aquarium/utils/method_utils'
6
6
  require 'aquarium/utils/name_utils'
7
+ require 'aquarium/utils/options_utils'
7
8
 
8
9
  require 'aquarium/utils/html_escaper'
9
10
  require 'aquarium/utils/invalid_options'
@@ -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
- strip_nils do_make_array(value_or_enum)
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.compact
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
- do_descendents clazz, Class, ["Class", "Module", "Object", clazz]
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, class_with_consts, visited
15
- result = [clazz]
16
- visited << class_with_consts.name
17
- class_with_consts.constants.each do |const|
18
- next if const == clazz.name or visited.include?(const)
19
- clazz2 = class_with_consts.class_eval "#{const}"
20
- next unless clazz2.respond_to?(:ancestors)
21
- result += [clazz2] if clazz2.ancestors.include? clazz
22
- result += do_descendents(clazz, clazz2, visited) unless visited.include?(clazz2.name)
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
@@ -8,7 +8,7 @@ module Aquarium
8
8
 
9
9
  unless defined? MAJOR
10
10
  MAJOR = 0
11
- MINOR = 2
11
+ MINOR = 3
12
12
  TINY = 0
13
13
  RELEASE_CANDIDATE = nil
14
14