aquarium 0.3.0 → 0.3.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (44) hide show
  1. data/Aquarium.ipr +253 -0
  2. data/Aquarium.iws +629 -0
  3. data/CHANGES +43 -0
  4. data/UPGRADE +13 -7
  5. data/examples/method_tracing_example_spec.rb +4 -1
  6. data/lib/aquarium/aspects/aspect.rb +28 -11
  7. data/lib/aquarium/aspects/exclusion_handler.rb +1 -1
  8. data/lib/aquarium/aspects/join_point.rb +58 -14
  9. data/lib/aquarium/aspects/pointcut.rb +5 -6
  10. data/lib/aquarium/extras/design_by_contract.rb +1 -1
  11. data/lib/aquarium/finders/method_finder.rb +1 -4
  12. data/lib/aquarium/finders/type_finder.rb +8 -1
  13. data/lib/aquarium/utils.rb +1 -0
  14. data/lib/aquarium/utils/default_logger.rb +20 -0
  15. data/lib/aquarium/utils/options_utils.rb +74 -12
  16. data/lib/aquarium/utils/type_utils.rb +1 -7
  17. data/lib/aquarium/version.rb +1 -1
  18. data/spec/aquarium/aspects/advice_chain_node_spec.rb +1 -1
  19. data/spec/aquarium/aspects/advice_spec.rb +1 -1
  20. data/spec/aquarium/aspects/aspect_invocation_spec.rb +1531 -1465
  21. data/spec/aquarium/aspects/aspect_spec.rb +22 -27
  22. data/spec/aquarium/aspects/aspect_with_nested_types_spec.rb +1 -1
  23. data/spec/aquarium/aspects/aspect_with_subtypes_spec.rb +1 -1
  24. data/spec/aquarium/aspects/concurrent_aspects_spec.rb +1 -1
  25. data/spec/aquarium/aspects/concurrent_aspects_with_objects_and_types_spec.rb +1 -1
  26. data/spec/aquarium/aspects/dsl/aspect_dsl_spec.rb +434 -424
  27. data/spec/aquarium/aspects/join_point_spec.rb +27 -4
  28. data/spec/aquarium/aspects/pointcut_and_composition_spec.rb +98 -102
  29. data/spec/aquarium/aspects/pointcut_or_composition_spec.rb +95 -107
  30. data/spec/aquarium/aspects/pointcut_spec.rb +1365 -1382
  31. data/spec/aquarium/extensions/hash_spec.rb +1 -1
  32. data/spec/aquarium/extensions/set_spec.rb +1 -1
  33. data/spec/aquarium/finders/finder_result_spec.rb +1 -1
  34. data/spec/aquarium/finders/method_finder_spec.rb +1 -1
  35. data/spec/aquarium/finders/type_finder_with_descendents_and_ancestors_spec.rb +63 -145
  36. data/spec/aquarium/{spec_example_classes.rb → spec_example_types.rb} +35 -0
  37. data/spec/aquarium/utils/default_logger_spec.rb +28 -0
  38. data/spec/aquarium/utils/hash_utils_spec.rb +1 -1
  39. data/spec/aquarium/utils/logic_error_spec.rb +1 -1
  40. data/spec/aquarium/utils/name_utils_spec.rb +1 -1
  41. data/spec/aquarium/utils/nil_object_spec.rb +1 -1
  42. data/spec/aquarium/utils/options_utils_spec.rb +122 -0
  43. data/spec/aquarium/utils/set_utils_spec.rb +1 -1
  44. metadata +9 -4
data/CHANGES CHANGED
@@ -1,3 +1,46 @@
1
+ == Version 0.3.1
2
+
3
+ V0.3.1 adds numerous performance improvements, especially in the RSpec suite, and some minor API additions.
4
+
5
+ Bug fixes:
6
+ N.A.
7
+
8
+ Enhancements:
9
+ 14447 Unify internal handling of reporting errors to the user
10
+ 17514 Provide an Aquarium library-wide logger with configuration parameters and instance overrides
11
+ 17515 Add an optional warning if an aspect doesn't match any join points
12
+ 17516 Remove unnecessary examples that use :types_and_descendents to shorten time to run the RSpec suite
13
+ 17565 JoinPoint.new should convert a type name, symbol, or regex to the type and only allow one type
14
+
15
+ These first two enhancements are related. There is a now an Aquarium::Utils::DefaultLogger
16
+ module with static accessors for getting and setting the "system-wide" logger.
17
+
18
+ When instance-level overrides are necessary, the Aquarium::Utils::OptionsUtils provides
19
+ "universal" options (but currently used only by Aspect and Pointcut) for specifying a logger
20
+ (with the new :logger parameter), or alternatively, specifying just the output stream
21
+ (:logger_stream) and/or the severity (:severity, one of the standard library's
22
+ Logger::Severity-defined constants). If either of the latter two options is specified, a
23
+ separate logger instance is created, rather than changing parameters for the global logger.
24
+
25
+ OptionsUtils also supports a :noop parameter, which classes interpret to mean do none (or
26
+ perhaps only some) of the processing. Useful for debugging.
27
+
28
+ #17515 adds a helpful warning to the system (or aspect-instance's) logger if an aspect
29
+ matches no join points. This warning will be suppressed if (i) the severity level for the
30
+ logger is above WARN or (ii) the aspect was created with the option
31
+ :ignore_no_matching_join_points => true.
32
+
33
+ #17516 fixes halved the long execution times for the whole RSpec suite by refactoring some examples
34
+ that used type finders with the :types_and_descendents option unnecessarily. It is a very intensive
35
+ computation! Note that I stubbed out these calls using an aspect with around advise, a useful
36
+ "development-time" aspect. See Aquarium::TypeUtilsStub (in spec_example_types.rb) and how it's used
37
+ in pointcut_spec.rb. Using this technique and other optimizations, the time to run the suite was
38
+ reduced from ~5 minutes to about 1 minute.
39
+
40
+ #17565 fixes a "hole" in JoinPoint, where it doesn't confirm that a specified type string, symbol
41
+ or regex matches a class that exists and only one class. Now it does and it stores the type, rather
42
+ than the original "specification" for it.
43
+
1
44
  == Version 0.3.0
2
45
 
3
46
  V0.3.0 adds numerous improvements to the DSL, making aspect specification more intuitive and
data/UPGRADE CHANGED
@@ -1,9 +1,15 @@
1
- == Upgrading to Aquarium-0.3.0
1
+ == Updating to Aquarium-0.3.1
2
+
3
+ There should be no upgrade issues with this release. However, the enhancement #17565 now ensures that a
4
+ JoinPoint is only specified with one type and a type that actually exists, when a string, symbol, or
5
+ regex is used to specify the type.
6
+
7
+ == Updating to Aquarium-0.3.0
2
8
 
3
9
  There are no known upgrade issues with this release. Although many new synonyms were added for API method
4
10
  parameters, all changes are backwards compatible.
5
11
 
6
- == Upgrading to Aquarium-0.2.0
12
+ == Updating to Aquarium-0.2.0
7
13
 
8
14
  This release changes the expected advice parameter list from |join_point, *method_args| to
9
15
  |join_point, object, *method_args|, where "object" is the current object receiving the message
@@ -14,22 +20,22 @@ Aquarium will raise an exception if the advice block or proc has this obsolete s
14
20
 
15
21
  Note that "JoinPoint#Context.advised_object" is still supported, even if it is now less useful.
16
22
 
17
- == Upgrading to Aquarium-0.1.8
23
+ == Updating to Aquarium-0.1.8
18
24
 
19
25
  V0.1.7 did not successfully "register" at rubyforge. This releases fixes that problem and also adds
20
26
  several feature enhancements and refactorings. There are no known upgrade issues.
21
27
 
22
- == Upgrading to Aquarium-0.1.7
28
+ == Updating to Aquarium-0.1.7
23
29
 
24
30
  This is primarily a bug-fix release, so there should be no upgrading or incompatibility issues.
25
31
 
26
- == Upgrading to Aquarium-0.1.6
32
+ == Updating to Aquarium-0.1.6
27
33
 
28
34
  As described in the CHANGES, the JoinPoint#type, JoinPoint#type=, JoinPoint#object, and JoinPoint#object=
29
35
  are now deprecated. Client code that uses these methods will still work, but warning messages will be
30
36
  written to STDOUT. See CHANGES for more details.
31
37
 
32
- == Upgrading to Aquarium-0.1.5
38
+ == Updating to Aquarium-0.1.5
33
39
 
34
40
  This is mostly a bug-fix release, but it did have to introduce one API change, as described in the
35
41
  CHANGES. In particular, the aspect "DSL" methods are no longer automatically to Object, as some of
@@ -69,6 +75,6 @@ object.extend(Aquarium::Aspects::DSL::AspectDSL)
69
75
 
70
76
  See the CHANGES for more details.
71
77
 
72
- == Upgrading existing code to Aquarium-0.1.0
78
+ == Updating existing code to Aquarium-0.1.0
73
79
 
74
80
  This is the first release of Aquarium.
@@ -79,8 +79,11 @@ end
79
79
 
80
80
  describe "An example with advice on the public instance methods (excluding ancestor methods) of Bar" do
81
81
  it "should not trace any calls to the public methods defined by the included BarModule" do
82
+ # Suppress warnings about no join points matched with :ignore_no_matching_join_points => true
82
83
  aspect = Aquarium::Aspects::Aspect.new :around,
83
- :invocations_of => :all_methods, :for_type => Aquarium::Bar, :restricting_methods_to => :exclude_ancestor_methods do |execution_point, obj, *args|
84
+ :invocations_of => :all_methods, :for_type => Aquarium::Bar,
85
+ :restricting_methods_to => :exclude_ancestor_methods,
86
+ :ignore_no_matching_join_points => true do |execution_point, obj, *args|
84
87
  begin
85
88
  obj.log "Entering: #{execution_point.target_type.name}##{execution_point.method_name}: args = #{args.inspect}"
86
89
  execution_point.proceed
@@ -35,7 +35,8 @@ module Aquarium
35
35
  CANONICAL_OPTIONS = Pointcut::CANONICAL_OPTIONS.merge({
36
36
  "advice" => %w[action do_action use_advice advise_with invoke call],
37
37
  "pointcuts" => %w[pointcut within_pointcuts within_pointcut on_pointcuts on_pointcut],
38
- "exclude_pointcuts" => %w[exclude_pointcut exclude_on_pointcut exclude_on_pointcuts exclude_within_pointcut exclude_within_pointcuts]
38
+ "exclude_pointcuts" => %w[exclude_pointcut exclude_on_pointcut exclude_on_pointcuts exclude_within_pointcut exclude_within_pointcuts],
39
+ "ignore_no_matching_join_points" => %[ignore_no_jps]
39
40
  })
40
41
 
41
42
  ALL_ALLOWED_OPTIONS = CANONICAL_OPTIONS.keys.inject([]) {|ary,i| ary << i << CANONICAL_OPTIONS[i]}.flatten +
@@ -103,15 +104,18 @@ module Aquarium
103
104
  # One or an array of Pointcut or JoinPoint objects. Mutually-exclusive with the :types, :objects,
104
105
  # :methods, :attributes, :method_options, and :attribute_options parameters.
105
106
  #
106
- # <tt>:noop</tt>::
107
- # Does not actually advise any join points. Useful for testing.
107
+ # <tt>:ignore_no_matching_join_points => true | false</tt>
108
+ # <tt>ignore_no_jps => true | false</tt>::
109
+ # Do not issue a warning if no join points are actually matched by the aspect. By default, the value
110
+ # is false, meaning that a WARN-level message will be written to the log. It is usually very helpful
111
+ # to be warned when no matches occurred, for diagnostic purposes!
108
112
  #
109
- # It also accepts all the same options that Pointcut accepts, including the synonyms for :types,
110
- # :methods, etc.
113
+ # Aspect.new also accepts all the same options that Pointcut accepts, including the synonyms for :types,
114
+ # :methods, etc. It also accepts the "universal" options documented in OptionsUtils.
111
115
  def initialize *options, &block
112
116
  @first_option_that_was_method = []
113
117
  opts = rationalize options
114
- init_specification opts, canonical_options, &block
118
+ init_specification opts, CANONICAL_OPTIONS, &block
115
119
  init_pointcuts
116
120
  validate_specification
117
121
  return if noop
@@ -126,9 +130,6 @@ module Aquarium
126
130
  get_jps :join_points_not_matched
127
131
  end
128
132
 
129
- def canonical_options
130
- CANONICAL_OPTIONS
131
- end
132
133
  def all_allowed_option_symbols
133
134
  ALL_ALLOWED_OPTION_SYMBOLS + @first_option_that_was_method
134
135
  end
@@ -213,8 +214,25 @@ module Aquarium
213
214
  pointcuts << Pointcut.new(pc_options)
214
215
  end
215
216
  @pointcuts = Set.new(remove_excluded_join_points_and_empty_pointcuts(pointcuts))
217
+ warn_if_no_join_points_matched
216
218
  end
217
219
 
220
+ def warn_if_no_join_points_matched
221
+ return unless should_warn_if_no_matching_join_points
222
+ @pointcuts.each do |pc|
223
+ if pc.join_points_matched.size > 0
224
+ return
225
+ end
226
+ end
227
+ logger.warn "Warning: No join points were matched. The options specified were #{@original_options.inspect}"
228
+ end
229
+
230
+ def should_warn_if_no_matching_join_points
231
+ @specification[:ignore_no_matching_join_points].nil? or
232
+ @specification[:ignore_no_matching_join_points].empty? or
233
+ @specification[:ignore_no_matching_join_points].to_a.first == false
234
+ end
235
+
218
236
  def remove_excluded_join_points_and_empty_pointcuts pointcuts
219
237
  pointcuts.reject do |pc|
220
238
  pc.join_points_matched.delete_if do |jp|
@@ -328,7 +346,6 @@ module Aquarium
328
346
  Aspect.is_type_join_point?(join_point) ? "" : "remove_method :#{join_point.method_name}"
329
347
  end
330
348
 
331
- # TODO Move to JoinPoint
332
349
  def self.is_type_join_point? join_point
333
350
  Aquarium::Utils::TypeUtils.is_type? join_point.type_or_object
334
351
  end
@@ -473,7 +490,7 @@ module Aquarium
473
490
  bad_options(":after can't be used with :after_raising.") if after_given_with? :after_raising
474
491
  bad_options(":after_returning can't be used with :after_raising.") if after_returning_given_with? :after_raising
475
492
  unless some_type_or_pc_option_given?
476
- bad_options("At least one of :pointcut(s), :type(s), :type(s)_with_ancestors, :type(s)_with_descendents, :object(s) is required.")
493
+ bad_options("At least one of :pointcut(s), :type(s), :type(s)_and_ancestors, :type(s)_and_descendents, :object(s) is required.")
477
494
  end
478
495
  if pointcuts_given? and (some_type_option_given? or objects_given?)
479
496
  bad_options("Can't specify both :pointcut(s) and one or more of :type(s), and/or :object(s).")
@@ -18,7 +18,7 @@ module Aquarium
18
18
  end
19
19
  end
20
20
 
21
- # Using @specification[:exclude_join_points].include?(jp) doesn't always work correctly (it probably uses equal())!
21
+ # Using @specification[:exclude_join_points].include?(jp) doesn't always work correctly (it probably uses equal?())!
22
22
  def is_excluded_join_point? jp
23
23
  return false if @specification[:exclude_join_points].nil?
24
24
  @specification[:exclude_join_points].find {|jp2| jp2 == jp || jp2.eql?(jp)}
@@ -17,6 +17,20 @@ module Aquarium
17
17
  alias :target_object :advised_object
18
18
  alias :target_object= :advised_object=
19
19
 
20
+ # Create a join point object. It must have one and only type _or_ object and one method or the special keywords <tt>:all</tt>.
21
+ # Usage:
22
+ # join_point = JoinPoint.new.find :type => ..., :method_name => ... [, (:class_method | :instance_method) => (true | false) ]
23
+ # where
24
+ # <tt>:type => type_or_type_name_or_regexp</tt>::
25
+ # A single type, type name or regular expression matching only one type. One and only one
26
+ # type _or_ object is required. An error is raised otherwise.
27
+ #
28
+ # <tt>:method_name => method_name_or_sym</tt>::
29
+ # <tt>:method => method_name_or_sym</tt>::
30
+ # A single method name or symbol. Only one is allowed, although the special flag <tt>:all</tt> is allowed.
31
+ #
32
+ # <tt>(:class_method | :instance_method) => (true | false)</tt>::
33
+ # Is the method a class or instance method? Defaults to <tt>:instance_method => true</tt>.
20
34
  def initialize options
21
35
  update options
22
36
  assert_valid options
@@ -91,7 +105,7 @@ module Aquarium
91
105
  end
92
106
 
93
107
  def initialize options = {}
94
- @target_type = options[:type]
108
+ @target_type = resolve_type options
95
109
  @target_object = options[:object]
96
110
  @method_name = options[:method_name] || options[:method]
97
111
  class_method = options[:class_method].nil? ? false : options[:class_method]
@@ -143,24 +157,26 @@ module Aquarium
143
157
  new_jp
144
158
  end
145
159
 
160
+ # Needed for comparing this field in #compare_field
161
+ def instance_method
162
+ @instance_method
163
+ end
164
+
146
165
  # We require the same object id, not just equal objects.
147
166
  def <=> other
148
167
  return 0 if object_id == other.object_id
149
168
  return 1 if other.nil?
150
- result = self.class <=> other.class
151
- return result unless result == 0
152
- result = (self.target_type.nil? and other.target_type.nil?) ? 0 : self.target_type.to_s <=> other.target_type.to_s
169
+ result = self.class <=> other.class
153
170
  return result unless result == 0
154
- result = (self.target_object.nil? and other.target_object.nil?) ? 0 : self.target_object.object_id <=> other.target_object.object_id
171
+ result = compare_field(:target_object, other) {|f1,f2| f1.object_id <=> f2.object_id}
155
172
  return result unless result == 0
156
- result = (self.method_name.nil? and other.method_name.nil?) ? 0 : self.method_name.to_s <=> other.method_name.to_s
173
+ result = compare_field(:instance_method, other) {|f1,f2| boolean_compare(f1,f2)}
157
174
  return result unless result == 0
158
- result = self.instance_method? == other.instance_method?
159
- return 1 unless result == true
160
- return 0 if self.context.nil? and other.context.nil?
161
- return -1 if self.context.nil? and !other.context.nil?
162
- return 1 if !self.context.nil? and other.context.nil?
163
- return self.context <=> other.context
175
+ [:target_type, :method_name, :context].each do |field|
176
+ result = compare_field field, other
177
+ return result unless result == 0
178
+ end
179
+ 0
164
180
  end
165
181
 
166
182
  def eql? other
@@ -179,9 +195,37 @@ module Aquarium
179
195
 
180
196
  protected
181
197
 
198
+ def compare_field field_reader, other
199
+ field1 = self.method(field_reader).call
200
+ field2 = other.method(field_reader).call
201
+ if field1.nil?
202
+ return field2.nil? ? 0 : -1
203
+ else
204
+ return 1 if field2.nil?
205
+ end
206
+ block_given? ? (yield field1, field2) : (field1 <=> field2)
207
+ end
208
+
209
+ def boolean_compare b1, b2
210
+ return 0 if b1 == b2
211
+ return b1 == true ? 1 : -1
212
+ end
213
+
214
+ def resolve_type options
215
+ type = options[:type]
216
+ return type if type.nil? # okay, if they specified an object!
217
+ return type if type.kind_of? Module
218
+ found = Aquarium::Finders::TypeFinder.new.find :type => type
219
+ if found.matched.empty?
220
+ bad_attributes("No type matched the string or regular expression: #{type}", options)
221
+ elsif found.matched.size > 1
222
+ bad_attributes("More than one type matched the string or regular expression: #{type}", options)
223
+ end
224
+ found.matched.keys.first
225
+ end
226
+
182
227
  # Since JoinPoints can be declared for non-existent methods, tolerate "nil" for the visibility.
183
- def assert_valid options
184
- error_message = ""
228
+ def assert_valid options, error_message = ""
185
229
  error_message << "Must specify a :method_name. " unless method_name
186
230
  error_message << "Must specify either a :type or :object. " unless (target_type or target_object)
187
231
  error_message << "Can't specify both a :type or :object. " if (target_type and target_object)
@@ -158,8 +158,10 @@ module Aquarium
158
158
  # Exclude the specified types and their descendents, ancestors.
159
159
  # If you want to exclude both the descendents _and_ ancestors, use both options.
160
160
  #
161
+ # Pointcut.new also accepts all the "universal" options documented in OptionsUtils.
161
162
  def initialize options = {}
162
- init_specification options, canonical_options
163
+ init_specification options, CANONICAL_OPTIONS
164
+ return if noop
163
165
  init_candidate_types
164
166
  init_candidate_objects
165
167
  init_candidate_join_points
@@ -177,8 +179,8 @@ module Aquarium
177
179
  candidate_types == other.candidate_types &&
178
180
  candidate_types_excluded == other.candidate_types_excluded &&
179
181
  candidate_objects == other.candidate_objects &&
180
- join_points_matched == other.join_points_matched &&
181
- join_points_not_matched == other.join_points_not_matched)
182
+ join_points_not_matched == other.join_points_not_matched &&
183
+ join_points_matched == other.join_points_matched) # not_matched is probably smaller, so do first.
182
184
  end
183
185
 
184
186
  alias :== :eql?
@@ -220,9 +222,6 @@ module Aquarium
220
222
 
221
223
  ALL_ALLOWED_OPTION_SYMBOLS = ALL_ALLOWED_OPTIONS.map {|o| o.intern}
222
224
 
223
- def canonical_options
224
- CANONICAL_OPTIONS
225
- end
226
225
  def all_allowed_option_symbols
227
226
  ALL_ALLOWED_OPTION_SYMBOLS
228
227
  end
@@ -1,6 +1,6 @@
1
1
  # A simple Design by Contract module. Adds advice to test that the contract, which is specified with
2
2
  # a block passes. Note that it doesn't attempt to handle the correct behavior under contract
3
- # inheritance (TODO).
3
+ # inheritance.
4
4
  # Warning: This module automatically includes Aquarium::Aspects::DSL::AspectDSL into the class with
5
5
  # the contract and it adds the :precondition, :postcondition, and the :invariant methods to Object!
6
6
  require 'aquarium'
@@ -90,7 +90,7 @@ module Aquarium
90
90
  # +Derived+ class that is defined in the +Base+ class, you won't find it!
91
91
  #
92
92
  def find options = {}
93
- init_specification options, canonical_options
93
+ init_specification options, CANONICAL_OPTIONS
94
94
  return Aquarium::Finders::FinderResult.new if nothing_to_find?
95
95
  types_and_objects = input_types + input_objects
96
96
  method_names_or_regexps = input_methods
@@ -126,9 +126,6 @@ module Aquarium
126
126
 
127
127
  ALL_ALLOWED_OPTION_SYMBOLS = ALL_ALLOWED_OPTIONS.map {|o| o.intern}
128
128
 
129
- def canonical_options
130
- CANONICAL_OPTIONS
131
- end
132
129
  def all_allowed_option_symbols
133
130
  ALL_ALLOWED_OPTION_SYMBOLS
134
131
  end
@@ -14,6 +14,8 @@ module Aquarium
14
14
  include Aquarium::Utils::ArrayUtils
15
15
  include Aquarium::Utils::TypeUtils
16
16
 
17
+ TYPES_SYNONYMS = %w[name names type types]
18
+
17
19
  # Usage:
18
20
  # finder_result = TypeFinder.new.find [options => [...] ]
19
21
  # where
@@ -88,11 +90,13 @@ module Aquarium
88
90
  # Note: a common idiom in aspects is to include descendents of a type, but not the type
89
91
  # itself. You can do as in the following example:
90
92
  # <tt>... :type_and_descendents => "Foo", :exclude_type => "Foo"
93
+ # TODO: Use the new OptionsUtils.
91
94
  def find options = {}
92
95
  result = Aquarium::Finders::FinderResult.new
93
96
  excluded = Aquarium::Finders::FinderResult.new
94
97
  unknown_options = []
95
98
  input_type_nil = false
99
+ noop = false
96
100
  options.each do |option, value|
97
101
  unless TypeFinder.is_recognized_option option
98
102
  unknown_options << option
@@ -102,6 +106,8 @@ module Aquarium
102
106
  input_type_nil = true
103
107
  next
104
108
  end
109
+ noop = value if option == :noop
110
+ next if noop
105
111
  if option.to_s =~ /^exclude_/
106
112
  excluded << find_matching(value, option)
107
113
  else
@@ -122,11 +128,12 @@ module Aquarium
122
128
  end
123
129
 
124
130
  def self.is_recognized_option option_or_symbol
125
- %w[name names type types].each do |t|
131
+ TYPES_SYNONYMS.each do |t|
126
132
  ['', "exclude_"].each do |excl|
127
133
  return true if ["#{excl}#{t}", "#{excl}#{t}_and_descendents", "#{excl}#{t}_and_ancestors"].include?(option_or_symbol.to_s)
128
134
  end
129
135
  end
136
+ return true if option_or_symbol.to_s.eql?("noop")
130
137
  false
131
138
  end
132
139
 
@@ -6,6 +6,7 @@ require 'aquarium/utils/method_utils'
6
6
  require 'aquarium/utils/name_utils'
7
7
  require 'aquarium/utils/options_utils'
8
8
 
9
+ require 'aquarium/utils/default_logger'
9
10
  require 'aquarium/utils/html_escaper'
10
11
  require 'aquarium/utils/invalid_options'
11
12
  require 'aquarium/utils/logic_error'
@@ -0,0 +1,20 @@
1
+ require 'logger'
2
+
3
+ module Aquarium
4
+ module Utils
5
+ # DefaultLogger holds the Aquarium-wide "default" Ruby standard library logger.
6
+ # Individual objects may chose to create their own loggers.
7
+ module DefaultLogger
8
+
9
+ @@default_logger = Logger.new STDERR
10
+
11
+ def self.logger
12
+ @@default_logger
13
+ end
14
+
15
+ def self.logger= logger
16
+ @@default_logger = logger
17
+ end
18
+ end
19
+ end
20
+ end