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
@@ -1,12 +1,13 @@
1
1
  require 'aquarium/utils/array_utils'
2
+ require 'set'
2
3
 
3
4
  module Aquarium
4
5
  module Aspects
5
- # Some classes and modules support a :default_object flag and use it if no type or
6
+ # Some classes and modules support a :default_objects flag and use it if no type or
6
7
  # object is specified. For "convenience", requires that classes and modules including
7
- # this module have a hash @specification defined with keys :default_object, :types,
8
+ # this module have a hash @specification defined with keys :default_objects, :types,
8
9
  # and :objects.
9
- module DefaultObjectHandler
10
+ module DefaultObjectsHandler
10
11
  include Aquarium::Utils::ArrayUtils
11
12
 
12
13
  def default_objects_given
@@ -22,15 +23,15 @@ module Aquarium
22
23
  not default_objects_given.empty?
23
24
  end
24
25
 
25
- def use_default_object_if_defined
26
+ def use_default_objects_if_defined
26
27
  return unless default_objects_given?
27
28
  default_objects_given.each do |object|
28
29
  if (object.kind_of?(Class) || object.kind_of?(Module))
29
30
  @specification[:types] ||= []
30
- @specification[:types] << default_objects_given
31
+ @specification[:types] << object
31
32
  else
32
33
  @specification[:objects] ||= []
33
- @specification[:objects] << default_objects_given
34
+ @specification[:objects] << object
34
35
  end
35
36
  end
36
37
  end
@@ -52,9 +52,9 @@ module Aquarium
52
52
  def append_implicit_self options
53
53
  opts = options.dup
54
54
  if (!opts.empty?) && opts.last.kind_of?(Hash)
55
- opts.last[:default_object] = self
55
+ opts.last[:default_objects] = self
56
56
  else
57
- opts << {:default_object => self}
57
+ opts << {:default_objects => self}
58
58
  end
59
59
  opts
60
60
  end
@@ -6,7 +6,7 @@ require 'aquarium/extensions'
6
6
  require 'aquarium/finders/finder_result'
7
7
  require 'aquarium/finders/type_finder'
8
8
  require 'aquarium/finders/method_finder'
9
- require 'aquarium/aspects/default_object_handler'
9
+ require 'aquarium/aspects/default_objects_handler'
10
10
 
11
11
  module Aquarium
12
12
  module Aspects
@@ -18,43 +18,83 @@ module Aquarium
18
18
  class Pointcut
19
19
  include Aquarium::Utils::ArrayUtils
20
20
  include Aquarium::Utils::HashUtils
21
+ include Aquarium::Utils::OptionsUtils
21
22
  include Aquarium::Utils::SetUtils
22
23
  include ExclusionHandler
23
- include DefaultObjectHandler
24
+ include DefaultObjectsHandler
24
25
 
25
26
  attr_reader :specification
26
27
 
27
28
  # Construct a Pointcut for methods in types or objects.
28
- # Pointcut.new :type{s} => [...] | :object{s} => [...] \
29
+ # Pointcut.new :join_points => [...] | :type{s} => [...] | :object{s} => [...] \
29
30
  # {, :method{s} => [], :method_options => [...], \
30
31
  # :attribute{s} => [...], :attribute_options[...]}
31
- # where
32
- # the "{}" indicate optional elements. For example, you can use
33
- # :types or :type.
32
+ # where the "{}" indicate optional elements. Most of the arguments have many
33
+ # synonyms, shown below, to promote an English-like DSL.
34
+ #
35
+ # <tt>:join_points => join_point || [join_point_list]</tt>::
36
+ # <tt>:join_point => join_point || [join_point_list]</tt>::
37
+ # <tt>:for_join_points => join_point || [join_point_list]</tt>::
38
+ # <tt>:for_join_point => join_point || [join_point_list]</tt>::
39
+ # <tt>:on_join_points => join_point || [join_point_list]</tt>::
40
+ # <tt>:on_join_point => join_point || [join_point_list]</tt>::
41
+ # <tt>:within_join_points => join_point || [join_point_list]</tt>::
42
+ # <tt>:within_join_point => join_point || [join_point_list]</tt>::
43
+ # One or an array of join_points.
34
44
  #
35
45
  # <tt>:types => type || [type_list]</tt>::
36
46
  # <tt>:type => type || [type_list]</tt>::
47
+ # <tt>:for_types => type || [type_list]</tt>::
48
+ # <tt>:for_type => type || [type_list]</tt>::
49
+ # <tt>:on_types => type || [type_list]</tt>::
50
+ # <tt>:on_type => type || [type_list]</tt>::
51
+ # <tt>:within_types => type || [type_list]</tt>::
52
+ # <tt>:within_type => type || [type_list]</tt>::
37
53
  # One or an array of types, type names and/or type regular expessions to match.
38
54
  #
39
55
  # <tt>:types_and_descendents => type || [type_list]</tt>::
40
56
  # <tt>:type_and_descendents => type || [type_list]</tt>::
41
57
  # <tt>:types_and_ancestors => type || [type_list]</tt>::
42
58
  # <tt>:type_and_ancestors => type || [type_list]</tt>::
59
+ # <tt>:for_types_and_ancestors => type || [type_list]</tt>::
60
+ # <tt>:for_type_and_ancestors => type || [type_list]</tt>::
61
+ # <tt>:on_types_and_descendents => type || [type_list]</tt>::
62
+ # <tt>:on_type_and_descendents => type || [type_list]</tt>::
63
+ # <tt>:on_types_and_ancestors => type || [type_list]</tt>::
64
+ # <tt>:on_type_and_ancestors => type || [type_list]</tt>::
65
+ # <tt>:within_types_and_descendents => type || [type_list]</tt>::
66
+ # <tt>:within_type_and_descendents => type || [type_list]</tt>::
67
+ # <tt>:within_types_and_ancestors => type || [type_list]</tt>::
68
+ # <tt>:within_type_and_ancestors => type || [type_list]</tt>::
43
69
  # One or an array of types and either their descendents or ancestors.
44
70
  # If you want both the descendents _and_ ancestors, use both options.
45
71
  #
46
72
  # <tt>:objects => object || [object_list]</tt>::
47
73
  # <tt>:object => object || [object_list]</tt>::
74
+ # <tt>:for_objects => object || [object_list]</tt>::
75
+ # <tt>:for_object => object || [object_list]</tt>::
76
+ # <tt>:on_objects => object || [object_list]</tt>::
77
+ # <tt>:on_object => object || [object_list]</tt>::
78
+ # <tt>:within_objects => object || [object_list]</tt>::
79
+ # <tt>:within_object => object || [object_list]</tt>::
48
80
  # Objects to match.
49
81
  #
50
- # <tt>:default_object => object</tt>::
82
+ # <tt>:default_objects => object || [object_list]</tt>::
83
+ # <tt>:default_object => object || [object_list]</tt>::
51
84
  # An "internal" flag used by AspectDSL#pointcut when no object or type is specified,
52
- # the value of :default_object will be used, if defined. AspectDSL#pointcut sets the
85
+ # the value of :default_objects will be used, if defined. AspectDSL#pointcut sets the
53
86
  # value to self, so that the user doesn't have to in the appropriate contexts.
54
87
  # This flag is subject to change, so don't use it explicitly!
55
88
  #
56
89
  # <tt>:methods => method || [method_list]</tt>::
57
90
  # <tt>:method => method || [method_list]</tt>::
91
+ # <tt>:within_methods => method || [method_list]</tt>::
92
+ # <tt>:within_method => method || [method_list]</tt>::
93
+ # <tt>:calling => method || [method_list]</tt>::
94
+ # <tt>:calls_to => method || [method_list]</tt>::
95
+ # <tt>:invoking => method || [method_list]</tt>::
96
+ # <tt>:invocations_of => method || [method_list]</tt>::
97
+ # <tt>:sending_message_to => method || [method_list]</tt>::
58
98
  # One or an array of methods, method names and/or method regular expessions to match.
59
99
  # By default, unless :attributes are specified, searches for public instance methods
60
100
  # with the method option :exclude_ancestor_methods implied, unless explicit method
@@ -64,17 +104,34 @@ module Aquarium
64
104
  # One or more options supported by Aquarium::Finders::MethodFinder. The :exclude_ancestor_methods
65
105
  # option is most useful.
66
106
  #
107
+ # <tt>:reading => attribute || [attribute_list]</tt>::
108
+ # <tt>:writing => attribute || [attribute_list]</tt>::
109
+ # <tt>:changing => attribute || [attribute_list]</tt>::
110
+ # <tt>:accessing => attribute || [attribute_list]</tt>::
111
+ # One or an array of attribute names and/or regular expessions to match.
112
+ # This is syntactic sugar for the corresponding attribute readers and/or writers
113
+ # methods.
114
+ # If <tt>:reading</tt> is specified, just attribute readers are matched.
115
+ # If <tt>:writing</tt> is specified, just attribute writers are matched.
116
+ # If <tt>:accessing</tt> is specified, both readers and writers are matched.
117
+ # Any matches will be joined with the matched <tt>:methods.</tt>.
118
+ #
67
119
  # <tt>:attributes => attribute || [attribute_list]</tt>::
68
120
  # <tt>:attribute => attribute || [attribute_list]</tt>::
69
121
  # One or an array of attribute names and/or regular expessions to match.
70
122
  # This is syntactic sugar for the corresponding attribute readers and/or writers
71
123
  # methods, as specified using the <tt>:attrbute_options</tt>. Any matches will be
72
- # joined with the matched :methods.</tt>.
124
+ # joined with the matched <tt>:methods.</tt>.
73
125
  #
74
126
  # <tt>:attribute_options => [options]</tt>::
75
127
  # One or more of <tt>:readers</tt>, <tt>:reader</tt> (synonymous),
76
128
  # <tt>:writers</tt>, and/or <tt>:writer</tt> (synonymous). By default, both
77
- # readers and writers are matched.
129
+ # readers and writers are matched.
130
+ # <tt>:reading => ...</tt> is synonymous with <tt>:attributes => ...,
131
+ # :attribute_options => [:readers]</tt>.
132
+ # <tt>:writing => ...</tt> and <tt>:changing => ...</tt> are synonymous with <tt>:attributes => ...,
133
+ # :attribute_options => [:writers]</tt>.
134
+ # <tt>:accessing => ...</tt> is synonymous with <tt>:attributes => ...</tt>.
78
135
  #
79
136
  # <tt>:exclude_pointcuts => pc || [pc_list]</tt>::
80
137
  # <tt>:exclude_pointcut => pc || [pc_list]</tt>::
@@ -89,6 +146,7 @@ module Aquarium
89
146
  # <tt>:exclude_method => method || [method_list]</tt>::
90
147
  # <tt>:exclude_attributes => attribute || [attribute_list]</tt>::
91
148
  # <tt>:exclude_attribute => attribute || [attribute_list]</tt>::
149
+ # Also <tt>exclude_{synonyms}</tt> of the same options...
92
150
  # Exclude the specified "things" from the matched join points. If pointcuts are
93
151
  # excluded, they should be subsets of the matched pointcuts. Otherwise, the
94
152
  # resulting pointcut will be empty!
@@ -101,7 +159,7 @@ module Aquarium
101
159
  # If you want to exclude both the descendents _and_ ancestors, use both options.
102
160
  #
103
161
  def initialize options = {}
104
- init_specification options
162
+ init_specification options, canonical_options
105
163
  init_candidate_types
106
164
  init_candidate_objects
107
165
  init_candidate_join_points
@@ -135,91 +193,42 @@ module Aquarium
135
193
 
136
194
  alias to_s inspect
137
195
 
138
- def self.make_attribute_method_names attribute_name_regexps_or_names, attribute_options = []
139
- readers = make_attribute_readers attribute_name_regexps_or_names
140
- return readers if read_only attribute_options
141
-
142
- writers = make_attribute_writers readers
143
- return writers if write_only attribute_options
144
- return readers + writers
196
+ CANONICAL_OPTIONS = {
197
+ "types" => %w[type for_type for_types on_type on_types in_type in_types within_type within_types],
198
+ "objects" => %w[object for_object for_objects on_object on_objects in_object in_objects within_object within_objects],
199
+ "join_points" => %w[join_point for_join_point for_join_points on_join_point on_join_points within_join_point within_join_points],
200
+ "methods" => %w[method within_method within_methods calling invoking calls_to invocations_of sending_message_to sending_messages_to],
201
+ "attributes" => %w[attribute accessing],
202
+ "method_options" => %w[method_option restricting_methods_to],
203
+ "attribute_options" => %w[attribute_option],
204
+ "types_and_descendents" => %w[type_and_descendents on_type_and_descendents on_types_and_descendents within_type_and_descendents within_types_and_descendents],
205
+ "types_and_ancestors" => %w[type_and_ancestors on_type_and_ancestors on_types_and_ancestors within_type_and_ancestors within_types_and_ancestors],
206
+ "default_objects" => %w[default_object]
207
+ }
208
+ %w[types objects join_points methods types_and_descendents types_and_ancestors].each do |key|
209
+ CANONICAL_OPTIONS["exclude_#{key}"] = CANONICAL_OPTIONS[key].map {|x| "exclude_#{x}"}
145
210
  end
146
-
147
- protected
148
-
149
- attr_writer :join_points_matched, :join_points_not_matched, :specification, :candidate_types, :candidate_types_excluded, :candidate_objects, :candidate_join_points
150
-
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]
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
-
166
- def init_specification options
167
- @specification = {}
168
- options ||= {}
169
- validate_options options
170
- ALLOWED_OPTIONS_SINGULAR.each do |option|
171
- self.instance_eval(<<-EOF, __FILE__, __LINE__)
172
- @specification[:#{option}s]= Set.new(make_array(options[:#{option}], options[:#{option}s]))
173
- EOF
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
-
181
- use_default_object_if_defined unless (types_given? || objects_given?)
182
-
183
- raise Aquarium::Utils::InvalidOptions.new(":all is not yet supported for :attributes.") if @specification[:attributes] == Set.new([:all])
184
- init_methods_specification options
185
- end
186
-
187
- def init_methods_specification options
188
- @specification[:methods] = Set.new(make_array(options[:methods], options[:method]))
189
- @specification[:methods].add(:all) if @specification[:methods].empty? and @specification[:attributes].empty?
211
+ CANONICAL_OPTIONS["methods"].dup.each do |synonym|
212
+ CANONICAL_OPTIONS["methods"] << "#{synonym}_methods_matching"
190
213
  end
214
+ CANONICAL_OPTIONS["exclude_pointcuts"] = %w[exclude_pointcut exclude_on_pointcut exclude_on_pointcuts exclude_within_pointcut exclude_within_pointcuts]
191
215
 
192
- def validate_options options
193
- knowns = []
194
- ALLOWED_OPTIONS_SINGULAR.each do |x|
195
- knowns << x.intern
196
- knowns << "#{x}s".intern
197
- end
198
- OTHER_ALLOWED_OPTIONS.each do |x|
199
- knowns << x.intern
200
- end
201
- unknowns = options.keys - knowns
202
- raise Aquarium::Utils::InvalidOptions.new("Unknown options specified: #{unknowns.inspect}") if unknowns.size > 0
203
- end
204
-
205
- def self.read_only attribute_options
206
- read_option(attribute_options) && !write_option(attribute_options)
207
- end
208
-
209
- def self.write_only attribute_options
210
- write_option(attribute_options) && !read_option(attribute_options)
211
- end
212
-
213
- def self.read_option attribute_options
214
- attribute_options.include?(:readers) || attribute_options.include?(:reader)
216
+ ATTRIBUTE_OPTIONS = %w[reading writing changing]
217
+
218
+ ALL_ALLOWED_OPTIONS = ATTRIBUTE_OPTIONS +
219
+ CANONICAL_OPTIONS.keys.inject([]) {|ary,i| ary << i << CANONICAL_OPTIONS[i]}.flatten
220
+
221
+ ALL_ALLOWED_OPTION_SYMBOLS = ALL_ALLOWED_OPTIONS.map {|o| o.intern}
222
+
223
+ def canonical_options
224
+ CANONICAL_OPTIONS
215
225
  end
216
-
217
- def self.write_option attribute_options
218
- attribute_options.include?(:writers) || attribute_options.include?(:writer)
226
+ def all_allowed_option_symbols
227
+ ALL_ALLOWED_OPTION_SYMBOLS
219
228
  end
220
-
221
- %w[types objects join_points methods attributes method_options attribute_options].each do |name|
222
- class_eval(<<-EOF, __FILE__, __LINE__)
229
+
230
+ CANONICAL_OPTIONS.keys.each do |name|
231
+ module_eval(<<-EOF, __FILE__, __LINE__)
223
232
  def #{name}_given
224
233
  @specification[:#{name}]
225
234
  end
@@ -230,18 +239,60 @@ module Aquarium
230
239
  EOF
231
240
  end
232
241
 
233
- %w[types objects join_points pointcuts methods].each do |name|
234
- class_eval(<<-EOF, __FILE__, __LINE__)
235
- def exclude_#{name}_given
236
- @specification[:exclude_#{name}]
242
+ def self.make_attribute_reading_writing_options options_hash
243
+ result = {}
244
+ [:writing, :changing, :reading].each do |attr_key|
245
+ unless options_hash[attr_key].nil? or options_hash[attr_key].empty?
246
+ result[:attributes] ||= Set.new([])
247
+ result[:attribute_options] ||= Set.new([])
248
+ result[:attributes].merge(Aquarium::Utils::ArrayUtils.make_array(options_hash[attr_key]))
249
+ attr_opt = attr_key == :reading ? :readers : :writers
250
+ result[:attribute_options] << attr_opt
237
251
  end
252
+ end
253
+ result
254
+ end
255
+
256
+ protected
238
257
 
239
- def exclude_#{name}_given?
240
- not (exclude_#{name}_given.nil? or exclude_#{name}_given.empty?)
258
+ attr_writer :join_points_matched, :join_points_not_matched, :specification, :candidate_types, :candidate_types_excluded, :candidate_objects, :candidate_join_points
259
+
260
+ def init_type_specific_specification original_options, options_hash
261
+ @specification.merge! Pointcut.make_attribute_reading_writing_options(options_hash)
262
+ # Map the method options to their canonical values:
263
+ @specification[:method_options] = Aquarium::Finders::MethodFinder.init_method_options(@specification[:method_options])
264
+ use_default_objects_if_defined unless (types_given? || objects_given?)
265
+
266
+ raise Aquarium::Utils::InvalidOptions.new(":all is not yet supported for :attributes.") if @specification[:attributes] == Set.new([:all])
267
+ if options_hash[:reading] and (options_hash[:writing] or options_hash[:changing])
268
+ unless options_hash[:reading].eql?(options_hash[:writing]) or options_hash[:reading].eql?(options_hash[:changing])
269
+ raise Aquarium::Utils::InvalidOptions.new(":reading and :writing/:changing can only be used together if they refer to the same set of attributes.")
241
270
  end
242
- EOF
271
+ end
272
+ init_methods_specification options_hash
273
+ end
274
+
275
+ def init_methods_specification options
276
+ match_all_methods if ((no_methods_specified and no_attributes_specified) or all_methods_specified)
243
277
  end
244
278
 
279
+ def match_all_methods
280
+ @specification[:methods] = Set.new([:all])
281
+ end
282
+
283
+ def no_methods_specified
284
+ @specification[:methods].nil? or @specification[:methods].empty?
285
+ end
286
+
287
+ def all_methods_specified
288
+ methods_spec = @specification[:methods].to_a
289
+ methods_spec.include?(:all) or methods_spec.include?(:all_methods)
290
+ end
291
+
292
+ def no_attributes_specified
293
+ @specification[:attributes].nil? or @specification[:attributes].empty?
294
+ end
295
+
245
296
  private
246
297
 
247
298
  def init_candidate_types
@@ -316,7 +367,7 @@ module Aquarium
316
367
  Aquarium::Finders::MethodFinder.new.find type_or_object_sym => candidates.matched_keys,
317
368
  :methods => which_methods,
318
369
  :exclude_methods => @specification[:exclude_methods],
319
- :options => @specification[:method_options].to_a
370
+ :options => method_options
320
371
  end
321
372
 
322
373
  def add_join_points search_results, type_or_object_sym
@@ -325,24 +376,36 @@ module Aquarium
325
376
  end
326
377
 
327
378
  def add_join_points_to which_join_points_list, results_hash, type_or_object_sym
328
- instance_method = @specification[:method_options].include?(:class) ? false : true
329
379
  results_hash.each_pair do |type_or_object, method_name_list|
330
380
  method_name_list.each do |method_name|
331
381
  which_join_points_list << Aquarium::Aspects::JoinPoint.new(
332
382
  type_or_object_sym => type_or_object,
333
383
  :method_name => method_name,
334
- :instance_method => instance_method)
384
+ :instance_method => is_instance_methods?)
335
385
  end
336
386
  end
337
387
  end
338
388
 
389
+ def is_instance_methods?
390
+ not @specification[:method_options].include? :class
391
+ end
392
+
339
393
  def make_all_method_names
340
394
  @specification[:methods] +
341
- Pointcut.make_attribute_method_names(@specification[:attributes], @specification[:attribute_options]) -
342
- @specification[:exclude_methods]
395
+ make_attribute_method_names(@specification[:attributes], @specification[:attribute_options]) -
396
+ @specification[:exclude_methods]
343
397
  end
344
398
 
345
- def self.make_attribute_readers attributes
399
+ def make_attribute_method_names attribute_name_regexps_or_names, attribute_options = []
400
+ readers = make_attribute_readers attribute_name_regexps_or_names
401
+ return readers if read_only attribute_options
402
+
403
+ writers = make_attribute_writers readers
404
+ return writers if write_only attribute_options
405
+ return readers + writers
406
+ end
407
+
408
+ def make_attribute_readers attributes
346
409
  readers = attributes.map do |regexp_or_name|
347
410
  if regexp_or_name.kind_of? Regexp
348
411
  exp = remove_trailing_equals_and_or_dollar regexp_or_name.source
@@ -355,7 +418,7 @@ module Aquarium
355
418
  Set.new(readers.sort_by {|exp| exp.to_s})
356
419
  end
357
420
 
358
- def self.make_attribute_writers attributes
421
+ def make_attribute_writers attributes
359
422
  writers = attributes.map do |regexp_or_name|
360
423
  if regexp_or_name.kind_of? Regexp
361
424
  # remove the "\b$" from the end of the reader expression, if present.
@@ -367,11 +430,31 @@ module Aquarium
367
430
  Set.new(writers.sort_by {|exp| exp.to_s})
368
431
  end
369
432
 
370
- def self.remove_trailing_equals_and_or_dollar exp
433
+ def read_only attribute_options
434
+ read_option(attribute_options) && !write_option(attribute_options)
435
+ end
436
+
437
+ def write_only attribute_options
438
+ write_option(attribute_options) && !read_option(attribute_options)
439
+ end
440
+
441
+ def read_option attribute_options
442
+ attribute_options.include?(:readers) or attribute_options.include?(:reader)
443
+ end
444
+
445
+ def write_option attribute_options
446
+ attribute_options.include?(:writers) or attribute_options.include?(:writer)
447
+ end
448
+
449
+ def method_options
450
+ @specification[:method_options].to_a.map {|mo| mo == :all_methods ? :all : mo }
451
+ end
452
+
453
+ def remove_trailing_equals_and_or_dollar exp
371
454
  exp.gsub(/\=?\$?$/, '')
372
455
  end
373
456
 
374
- def self.remove_leading_colon_or_at_sign exp
457
+ def remove_leading_colon_or_at_sign exp
375
458
  exp.gsub(/^\^?(@|:)/, '')
376
459
  end
377
460
  end