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
@@ -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