aquarium 0.2.0 → 0.3.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.
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