aquarium 0.5.1 → 0.7.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/CHANGES +240 -215
- data/README +61 -44
- data/RELEASE-PLAN +21 -23
- data/Rakefile +64 -94
- data/UPGRADE +45 -35
- data/aquarium.gemspec +50 -0
- data/examples/README.txt +6 -0
- data/examples/aspect_design_example_spec.rb +2 -2
- data/examples/design_by_contract_example_spec.rb +3 -3
- data/examples/exception_wrapping_example_spec.rb +2 -2
- data/examples/introductions_example_spec.rb +1 -1
- data/examples/method_tracing_example_spec.rb +17 -16
- data/examples/reusable_aspect_hack_example_spec.rb +5 -5
- data/jruby/Rakefile +61 -0
- data/jruby/java/example/Worker.java +9 -0
- data/jruby/java/example/sorter/StringListSorter.java +22 -0
- data/jruby/java/example/sorter/converter/StringListCaseConverterAndSorter.java +42 -0
- data/jruby/java/example/visibility/Visibility.java +13 -0
- data/jruby/spec/java_class_aspects_spec.rb +434 -0
- data/jruby/spec/java_visibility_spec.rb +122 -0
- data/jruby/spec/spec_helper.rb +5 -0
- data/lib/aquarium/aspects/aspect.rb +8 -4
- data/lib/aquarium/utils/type_utils.rb +4 -1
- data/lib/aquarium/version.rb +29 -28
- data/previous_failures.txt +0 -0
- data/rspec.watchr +60 -0
- data/spec/aquarium/aspects/advice_spec.rb +10 -10
- data/spec/aquarium/aspects/aspect_invocation_spec.rb +79 -79
- data/spec/aquarium/aspects/aspect_spec.rb +73 -73
- data/spec/aquarium/aspects/aspect_with_nested_types_spec.rb +5 -5
- data/spec/aquarium/aspects/concurrent_aspects_spec.rb +1 -1
- data/spec/aquarium/aspects/default_objects_handler_spec.rb +5 -5
- data/spec/aquarium/aspects/join_point_spec.rb +40 -40
- data/spec/aquarium/aspects/pointcut_and_composition_spec.rb +8 -8
- data/spec/aquarium/aspects/pointcut_spec.rb +25 -25
- data/spec/aquarium/extensions/regex_spec.rb +6 -6
- data/spec/aquarium/extras/design_by_contract_spec.rb +6 -6
- data/spec/aquarium/finders/finder_result_spec.rb +2 -2
- data/spec/aquarium/finders/method_finder_spec.rb +24 -24
- data/spec/aquarium/finders/pointcut_finder_spec.rb +10 -10
- data/spec/aquarium/finders/pointcut_finder_spec_test_classes.rb +4 -4
- data/spec/aquarium/finders/type_finder_spec.rb +5 -5
- data/spec/aquarium/finders/type_finder_with_descendents_and_ancestors_spec.rb +6 -5
- data/spec/aquarium/finders/type_finder_with_nested_types_spec.rb +2 -2
- data/spec/aquarium/utils/logic_error_spec.rb +1 -1
- data/spec/aquarium/utils/method_utils_spec.rb +38 -38
- data/spec/aquarium/utils/nil_object_spec.rb +11 -11
- data/spec/aquarium/utils/options_utils_spec.rb +8 -8
- data/spec/aquarium/utils/type_utils_spec.rb +3 -3
- metadata +238 -57
data/README
CHANGED
@@ -7,6 +7,18 @@ Aquarium is a toolkit for Aspect-Oriented Programming (AOP) whose goals include:
|
|
7
7
|
* A user-friendly DSL.
|
8
8
|
* Support for advising Java types through JRuby.
|
9
9
|
|
10
|
+
=== SOMEBODY ADOPT ME!!
|
11
|
+
|
12
|
+
While I've enjoyed working on Aquarium, I no longer do Ruby development. I just don't have the time to keep updating Aquarium as Ruby evolves. There are plenty of deprecation warnings now when you build Aquarium and JRuby support has been spotty since Aquarium v0.6.0.
|
13
|
+
|
14
|
+
=== Supported Ruby Versions (and Caveats)
|
15
|
+
|
16
|
+
Aquarium v0.7.0 was tested with Ruby 2.6.3p62 (2019-04-16 revision 67580) and JRuby 9.2.7.0 (Ruby 2.5.3) 2019-04-09 8a269e3. There are many deprecation warnings about constants that have been renamed, all of which appear to be in old libraries still used by Aquarium.
|
17
|
+
|
18
|
+
Aquarium v0.6.0 supports only Ruby 2.0.0p247. JRuby 1.7.4 (Ruby 1.9.3p392) does *not* currently pass all the custom Java specs in the `jruby/spec` directory, which specifically test working with Java classes. However, JRuby does pass all the Ruby specs in the `spec` directory. I didn't want to release v0.6.0 with the Java-specific specs not working, but I didn't have time to resolve the issues. Patches are welcome.
|
19
|
+
|
20
|
+
If you need support for even earlier versions of Ruby and JRuby, use Aquarium v0.5.0.
|
21
|
+
|
10
22
|
=== Why Is an AOP Framework Useful in Ruby?
|
11
23
|
|
12
24
|
Ruby's metaprogramming facilities already provide some of the capabilities for which static-language AOP toolkits like AspectJ are typically used. With Ruby, you can easily add new methods and attributes to existing classes and objects. You can alias and redefine existing methods, which provides the method interception and "wrapping" needed to extend or modify existing behavior.
|
@@ -35,7 +47,6 @@ Only around advice can prevent execution of the join point, except for the speci
|
|
35
47
|
* You cannot advice "String", "Symbol" or instances there of, because trying to specify either one will be confused with naming a type.
|
36
48
|
* Concurrent advice on type AND advice on objects of the type can't be removed cleanly.
|
37
49
|
* The pointcut language is still limited, compared to AspectJ's. See also the comparison with AspectJ behavior next.
|
38
|
-
* The API and wrapper DSL will probably evolve until the 1.0.0 release. Backwards compatibility will be maintained between releases as much as possible. When it is necessary to break backwards compatibility, translation tools will be provided, if possible.
|
39
50
|
* There are limitations when advising Java types through JRuby. The separate RSpec suite in the "jruby" directory documentations the details on how to use Aquarium with JRuby-wrapped Java types and the limitations thereof. Here we summarize what works and what doesn't:
|
40
51
|
* Aquarium advice on a method in a Java type will only be invoked when the method is called directly from Ruby.
|
41
52
|
* To have the advice invoked when the method is called from either Java or Ruby, it is necessary to create a subclass of the Java type in Ruby and an override of the method, which can just call "super". Note that it will be necessary for instances of this Ruby type to be used throughout, not instances of a Java parent type.
|
@@ -51,19 +62,19 @@ The goal of Aquarium is to provide a superset of the functionality provided by t
|
|
51
62
|
=== Differences With AspectJ Behavior
|
52
63
|
|
53
64
|
Many of AspectJ's features are not currently supported by Aquarium, but some of them are planned for future releases.
|
54
|
-
|
55
|
-
* Attribute reading and writing join points are not supported. The :attributes and :attributes_options parameters (and their synonyms) for Aspect.new are actually "syntactic sugar" for the corresponding accessor methods.
|
65
|
+
|
66
|
+
* Attribute reading and writing join points are not supported. The :attributes and :attributes_options parameters (and their synonyms) for Aspect.new are actually "syntactic sugar" for the corresponding accessor methods.
|
56
67
|
* More advanced AspectJ pointcut language features are not supported, such as the runtime pointcut designators like "if" conditionals and "cflow" (context flow) and the compile time "within" and "withincode" designators. Most of AspectJ pointcut language features are planned, however.
|
57
68
|
* While AspectJ provides "intertype declaration" capabilities (i.e., adding state and behavior to existing classes), Ruby's native metaprogramming support satisfies this need. There may be convenience hooks added in a future release, however.
|
58
|
-
* User defined advice precedence is not supported. However, advice precedence is unambiguous; the last aspects created while modules are loaded at runtime have higher precedence than earlier aspects. Ensuring a particular order is not always easy, of course.
|
69
|
+
* User defined advice precedence is not supported. However, advice precedence is unambiguous; the last aspects created while modules are loaded at runtime have higher precedence than earlier aspects. Ensuring a particular order is not always easy, of course.
|
59
70
|
|
60
71
|
However, Aquarium does have a few advantages over AspectJ, especially when advising Java types when running in JRuby.
|
61
72
|
|
62
73
|
* Aquarium can add and remove advice dynamically, at runtime.
|
63
74
|
* Aquarium can advise individual objects, not just classes.
|
64
75
|
* Aquarium can advise JDK classes. AspectJ can also do this, but not by default.
|
65
|
-
* Aquarium supports named advice that can be defined separately from the aspects that use the advice.
|
66
|
-
* Aquarium can advise ancestor (parent) types, not just derived (descendent) types of specified types.
|
76
|
+
* Aquarium supports named advice that can be defined separately from the aspects that use the advice.
|
77
|
+
* Aquarium can advise ancestor (parent) types, not just derived (descendent) types of specified types.
|
67
78
|
|
68
79
|
Note: at the time of this writing (V0.4.0 release), there is an important limitation of Aquarium when used with java code; it appears that advice is only invoked if an advised method is called directly from Ruby code. If the advised method is called by other Java code, the advice is *not* invoked. Whether or not this limitation can be removed is under investigation.
|
69
80
|
|
@@ -134,7 +145,7 @@ If you use the DSL inside a class and omit the :type(s) and :object(s) options,
|
|
134
145
|
It is important to note that aspect "instances" usually behave like class (static) variables, in terms of the lifetime of their effects. In the example shown, class Foo is permanently modified to do the print statements shown for all "critical methods", unless you save the result of calling "around" to a variable, e.g., critical_operation_logging, and you explicitly call "critical_operation_logging.unadvise" at some future time. Put another way, the effects scope just like changes made when you reopen a class or module.
|
135
146
|
|
136
147
|
A common mistake is to create an aspect in an initialize method and assign it to an attribute. This usually means that you are creating long-lived, redundant aspects every time an instance of your class is created. The aspect modifications remain in effect even when the instances themselves are garbage collected!
|
137
|
-
|
148
|
+
|
138
149
|
Here are some more succinct examples, illustrating the API (using the DSL methods) and some of the various synonyms for methods, types, etc.
|
139
150
|
|
140
151
|
You can pass in pointcuts defined elsewhere:
|
@@ -147,8 +158,8 @@ As a convenience, since a JoinPoint is a Pointcut with one element, you can pass
|
|
147
158
|
|
148
159
|
my_join_point1 = JoinPoint.new :type => Foo::Bar, :method => do_this
|
149
160
|
my_join_point2 = JoinPoint.new :type => Foo::Bar, :method => do_that
|
150
|
-
around :pointcuts => my_join_point1 do |jp, obj, *args| ...
|
151
|
-
around :pointcuts => [my_join_point1, my_join_point2, ...] do |jp, obj, *args| ...
|
161
|
+
around :pointcuts => my_join_point1 do |jp, obj, *args| ...
|
162
|
+
around :pointcuts => [my_join_point1, my_join_point2, ...] do |jp, obj, *args| ...
|
152
163
|
|
153
164
|
You can specify a single type, a type name, a type regular expression, or an array of the same. Note that :type and :types are synonymous. Use the singular form for better readability when you are specifying just one type. Other synonyms include :on_types, :within_types, and :in_types, plus the singular forms.
|
154
165
|
|
@@ -189,8 +200,8 @@ Some of the synonyms:
|
|
189
200
|
around :calls_to => :all_methods, :in_types_and_ancestors = A, ...
|
190
201
|
around :calls_to => :all_methods, :within_types_and_ancestors = A, ...
|
191
202
|
and similarly for descendents
|
192
|
-
|
193
|
-
You can specify a single object or an array of objects. As for :types, you can use :object, :objects, :on_objects, :within_object, :in_objects, and the singular forms synonymously.
|
203
|
+
|
204
|
+
You can specify a single object or an array of objects. As for :types, you can use :object, :objects, :on_objects, :within_object, :in_objects, and the singular forms synonymously.
|
194
205
|
|
195
206
|
a1 = A.new
|
196
207
|
a2 = A.new
|
@@ -208,7 +219,7 @@ If no types or objects are specified, the object defaults to "self". However, th
|
|
208
219
|
class MyClass
|
209
220
|
include Aquarium::DSL
|
210
221
|
def doit; ...; end
|
211
|
-
|
222
|
+
|
212
223
|
around :method => doit, ... # Implicit :object => self, i.e., MyClass
|
213
224
|
end
|
214
225
|
|
@@ -232,15 +243,15 @@ You can specify method options. By default, public instance methods only are mat
|
|
232
243
|
|
233
244
|
around :methods = /foo/, :method_options => [:instance], ... # match instance methods (default)
|
234
245
|
around :methods = /foo/, :method_options => [:class], ... # match class methods
|
235
|
-
around :methods = /foo/, :method_options => [:public, :protected, :private], ...
|
246
|
+
around :methods = /foo/, :method_options => [:public, :protected, :private], ...
|
236
247
|
# match public, protected, and private instance methods
|
237
248
|
around :methods = /foo/, :method_options => [:singleton], ... # match singleton methods
|
238
|
-
around :methods = /foo/, :method_options => [:exclude_ancestor_methods], ...
|
239
|
-
# ignore methods defined in ancestors, inherited classes and included modules
|
249
|
+
around :methods = /foo/, :method_options => [:exclude_ancestor_methods], ...
|
250
|
+
# ignore methods defined in ancestors, inherited classes and included modules
|
240
251
|
|
241
252
|
With synonyms:
|
242
253
|
|
243
|
-
around :calls_to = /foo/, :restricting_methods_to => [:singleton_methods], ...
|
254
|
+
around :calls_to = /foo/, :restricting_methods_to => [:singleton_methods], ...
|
244
255
|
|
245
256
|
You can specify attributes, which are actually convenience methods for the attribute accessors. They work very much like the :method options. Note that :all is NOT supported in this case. The available synonyms are slightly more complicated, as shown in these examples.
|
246
257
|
|
@@ -248,11 +259,11 @@ You can specify attributes, which are actually convenience methods for the attri
|
|
248
259
|
around :attributes = :foo, ... # the same
|
249
260
|
around :accessing = :foo, ... # the same
|
250
261
|
|
251
|
-
around :attribute = :foo, :attribute_options => [:readers]... # only matches #foo
|
262
|
+
around :attribute = :foo, :attribute_options => [:readers]... # only matches #foo
|
252
263
|
around :reading = :foo # the same
|
253
|
-
|
254
|
-
|
255
|
-
around :attribute = :foo, :attribute_options => [:writers]... # only matches #foo=
|
264
|
+
|
265
|
+
|
266
|
+
around :attribute = :foo, :attribute_options => [:writers]... # only matches #foo=
|
256
267
|
around :writing = :foo # the same
|
257
268
|
|
258
269
|
around :attributes = [:foo, :bar, :baz], ...
|
@@ -282,10 +293,10 @@ in an around advice aspect will match all of them:
|
|
282
293
|
|
283
294
|
around :named_pointcuts => { :constants_matching => :STATE_CHANGE, :in_types => /App::.*/ } ...
|
284
295
|
|
285
|
-
For the type specification, which is required, any valid option for the TypeFinder class is allowed.
|
296
|
+
For the type specification, which is required, any valid option for the TypeFinder class is allowed.
|
286
297
|
|
287
298
|
You can also match on class variables, using ":class_variables_matching". To match on either kind of definition, use just
|
288
|
-
":matching". If no :*matching is specified, then any class constant or variable Pointcut found will be matched.
|
299
|
+
":matching". If no :*matching is specified, then any class constant or variable Pointcut found will be matched.
|
289
300
|
|
290
301
|
Here are the variaus :*matching options and their synonyms:
|
291
302
|
|
@@ -314,7 +325,7 @@ You can also use the following synonyms for :named_pointcuts:
|
|
314
325
|
around :on_named_pointcuts => {...}
|
315
326
|
around :in_named_pointcuts => {...}
|
316
327
|
around :within_named_pointcuts => {...}
|
317
|
-
|
328
|
+
|
318
329
|
You can specifically exclude particular pointcuts, join points, types, objects, methods, or attributes. This is useful when you specify a list or regular expression of "items" to match and you want to exclude some of the items.
|
319
330
|
Note that there is an open bug (#15202) that appears to affect advising types, unadvising the types, then advising objects of the same types. (This is not likely to happen a lot in real applications, but it shows up when running Aquarium's specs.)
|
320
331
|
|
@@ -347,21 +358,21 @@ You can advice methods after returning successfully (i.e., no exceptions were ra
|
|
347
358
|
|
348
359
|
after_returning :types => ...
|
349
360
|
after_returning_from :types => ... # synonym
|
350
|
-
|
361
|
+
|
351
362
|
You can advice methods after raising exceptions:
|
352
363
|
|
353
364
|
after_raising :types => ... # After any exception is thrown
|
354
365
|
after_raising_within :types => ... # synonym
|
355
366
|
after_raising => MyError, :types => ... # Only invoke advice if "MyError" is raised.
|
356
|
-
after_raising => [MyError1, MyError2], :types => ...
|
367
|
+
after_raising => [MyError1, MyError2], :types => ...
|
357
368
|
# Only invoke advice if "MyError1" or "MyError2" is raised.
|
358
|
-
|
369
|
+
|
359
370
|
You can advice methods after returning successfully or raising exceptions. (You can't specify
|
360
371
|
a set of exceptions in this case.):
|
361
372
|
|
362
373
|
after :types => ...
|
363
374
|
after_raising_within_or_returning_from : types => # synonym
|
364
|
-
|
375
|
+
|
365
376
|
You can advice methods both before after. This is different from around advice, where the around advice has to explicitly invoke the join point (using JoinPoint#proceed). Instead, the before-and-after methods are convenience wrappers around the creation of separate before advice and the corresponding after advice.
|
366
377
|
|
367
378
|
before_and_after :types =>, ...
|
@@ -371,10 +382,10 @@ You can advice methods both before after. This is different from around advice,
|
|
371
382
|
before_and_after_raising_within :types =>, ... # synonym
|
372
383
|
before_and_after_raising_within_or_returning_from :types =>, ... # synonym
|
373
384
|
|
374
|
-
If you pass a block to Aspect.new, it will be the advice. When invoked, the advice will be passed the following three arguments,
|
375
|
-
1) the JoinPoint, which will contain a JoinPoint::Context object with useful context information,
|
376
|
-
2) the object being sent the current message, and
|
377
|
-
3) the parameters passed with the original message.
|
385
|
+
If you pass a block to Aspect.new, it will be the advice. When invoked, the advice will be passed the following three arguments,
|
386
|
+
1) the JoinPoint, which will contain a JoinPoint::Context object with useful context information,
|
387
|
+
2) the object being sent the current message, and
|
388
|
+
3) the parameters passed with the original message.
|
378
389
|
Recall that a Proc doesn't check the number of arguments (while lambdas do), so if you don't care about any of the trailing parameters, you can leave them out of the parameter list. Recall that the other difference between the two is that a return statement in a Proc returns from the method that contains it. As rule, do NOT use return statements in advices!
|
379
390
|
|
380
391
|
around :type => [...], :methods => :all do |join_point, object, *args|
|
@@ -388,10 +399,10 @@ Recall that a Proc doesn't check the number of arguments (while lambdas do), so
|
|
388
399
|
In the example, we show that you must be careful to return the correct value, usually the value returned by "proceed" or a value created by the block itself.
|
389
400
|
|
390
401
|
Note, prior to V0.2.0, the advice argument list was |join_point, *args|. Aquarium will look for such obsolete signatures (by looking at the arity of the proc) and raise an exception, if found. This check will be removed in a future release.
|
391
|
-
|
402
|
+
|
392
403
|
Rather than passing a block as the advice, you can pass a previously-created Proc:
|
393
|
-
|
394
|
-
around :type => [...], :methods => :all, :advice => advice
|
404
|
+
|
405
|
+
around :type => [...], :methods => :all, :advice => advice
|
395
406
|
around :type => [...], :methods => :all, :advise_with => advice # synonym for advice. Note the "s"!
|
396
407
|
around :type => [...], :methods => :all, :call => advice # synonym for advice.
|
397
408
|
around :type => [...], :methods => :all, :invoke => advice # synonym for advice.
|
@@ -420,12 +431,19 @@ The simplest approach is to install the gem:
|
|
420
431
|
|
421
432
|
If you prefer to build the gem locally, clone from GitHub,
|
422
433
|
|
423
|
-
git clone git://github.com/deanwampler/Aquarium.git
|
434
|
+
git clone git://github.com/deanwampler/Aquarium.git
|
424
435
|
|
425
436
|
Then do the following:
|
426
437
|
|
427
|
-
rake
|
428
|
-
|
438
|
+
rake install
|
439
|
+
|
440
|
+
This builds the gem file, pkg/aquarium-x.y.z.gem, and installs it locally.
|
441
|
+
|
442
|
+
If you are a maintainer and want to upload a new version to RubyGems.org, first see [this page](https://guides.rubygems.org/make-your-own-gem/) for creating a `~/.gem/credentials` file, then run this command to publish a new version.
|
443
|
+
|
444
|
+
gem push pkg/aquarium-0.7.0.gem
|
445
|
+
|
446
|
+
At this time, with the demise of RubyForge, the docs and home page are temporarily hosted at https://deanwampler.github.io/open-source/aquarium/index.html. I'll move them to a better location soon. I build them by running `rake website`, then copying the entire output of `../doc/aquarium/out/` to my GitHub site under the `open-source/Aquarium` directory. TBD - move somewhere better!
|
429
447
|
|
430
448
|
== Running Aquarium's RSpec Specs
|
431
449
|
|
@@ -433,14 +451,14 @@ In order to run Aquarium's full suite of specs (rake pre_commit) you must instal
|
|
433
451
|
|
434
452
|
* rake # Runs the build script
|
435
453
|
* rspec # Used instead of Test::Unit for TDD
|
436
|
-
* rcov #
|
454
|
+
* rcov # For running coverage checks
|
437
455
|
* webgen # Generates the static HTML website
|
456
|
+
* bundler # Gem manager
|
438
457
|
* coderay # Required by webgen
|
439
458
|
* diff-lcs # Required if you use the --diff switch
|
440
459
|
* win32console # Required by the --colour switch if you're on Windows
|
441
|
-
* meta_project # Required in order to make releases at RubyForge
|
442
460
|
* heckle # Required if you use the --heckle switch
|
443
|
-
* jruby # Required if run the separate spec suite in the "jruby" directory
|
461
|
+
* jruby # Required if run the separate spec suite in the "jruby" directory
|
444
462
|
|
445
463
|
Once those are all installed, you should be able to run the suite with the following steps:
|
446
464
|
|
@@ -452,13 +470,13 @@ or
|
|
452
470
|
|
453
471
|
Note that Aquarium itself - once built - doesn't have any dependencies outside the Ruby core and stdlib.
|
454
472
|
|
455
|
-
If you want to run the tests for the JRuby support, you must also have JRuby
|
473
|
+
If you want to run the tests for the JRuby support, you must also have JRuby installed (see version information above). To run the specs for JRuby, use the command
|
456
474
|
|
457
475
|
* rake verify_jruby
|
458
476
|
|
459
477
|
This command runs the standard Aquarium specs using JRuby instead of MRI, then runs a separate set of specs in the "jruby/spec" directory which test Aquarium with Java classes inside JRuby.
|
460
478
|
|
461
|
-
|
479
|
+
> **WARNING:** Currently, not all JRuby-specific specs pass!
|
462
480
|
|
463
481
|
=== Acknowledgments
|
464
482
|
|
@@ -468,5 +486,4 @@ The RSpec team, in particular David Chelimsky, have really inspired my thinking
|
|
468
486
|
|
469
487
|
Keita Yamaguchi contributed some key patches that enabled Ruby 1.9.3 and JRuby 1.6.7 support, in addition to the prior support for Ruby 1.8.7. These patches allowed me to final release version 0.5.X. Thank you!
|
470
488
|
|
471
|
-
Finally, a number of users have contributed valuable feedback. In particular, thanks to Brendan L., Matthew F., and Mark V.
|
472
|
-
|
489
|
+
Finally, a number of users have contributed valuable feedback. In particular, thanks to Brendan L., Matthew F., and Mark V.
|
data/RELEASE-PLAN
CHANGED
@@ -6,26 +6,26 @@ Mostly bug fixes and enhancements. Only "minor" breakages of backwards compatibi
|
|
6
6
|
|
7
7
|
=== Versions 0.2
|
8
8
|
|
9
|
-
Change the argument list for advice to |join_point, object, *args| from |join_point, *args|,
|
10
|
-
where "*args" are the arguments passed to the invoked join point (method). Since nontrivial
|
11
|
-
advice usually needs the object being advised, it became clear that having to call
|
12
|
-
"join_point.context.advised_object" is too tedious.
|
9
|
+
Change the argument list for advice to |join_point, object, *args| from |join_point, *args|,
|
10
|
+
where "*args" are the arguments passed to the invoked join point (method). Since nontrivial
|
11
|
+
advice usually needs the object being advised, it became clear that having to call
|
12
|
+
"join_point.context.advised_object" is too tedious.
|
13
13
|
|
14
14
|
This change will obvious break existing aspects.
|
15
15
|
|
16
16
|
=== Versions 0.3+
|
17
17
|
|
18
|
-
More refinements and simplifications to the API and functionality, including much-needed
|
18
|
+
More refinements and simplifications to the API and functionality, including much-needed
|
19
19
|
redundancy reduction. I haven't used mocks much in the specs, but they would help improve the
|
20
|
-
performance of the RSpec runs.
|
20
|
+
performance of the RSpec runs.
|
21
21
|
|
22
|
-
The main thrust of new feature work will be expanding the pointcut language to include
|
23
|
-
conditionals and stack context constructs, as well as more intuitive ways of expressing sets of
|
24
|
-
types, such as types nested arbitrarily deep in module "namespaces" (e.g., #13403), etc.
|
22
|
+
The main thrust of new feature work will be expanding the pointcut language to include
|
23
|
+
conditionals and stack context constructs, as well as more intuitive ways of expressing sets of
|
24
|
+
types, such as types nested arbitrarily deep in module "namespaces" (e.g., #13403), etc.
|
25
25
|
|
26
26
|
I'm also thinking about an alternative syntax for the DSL. Instead of just this:
|
27
27
|
|
28
|
-
Aspect.new :around :pointcuts => [pc1, pc2, ...] do |jp, object, *args|
|
28
|
+
Aspect.new :around :pointcuts => [pc1, pc2, ...] do |jp, object, *args|
|
29
29
|
# advise
|
30
30
|
end
|
31
31
|
|
@@ -33,12 +33,12 @@ How about something like the following?
|
|
33
33
|
|
34
34
|
around do
|
35
35
|
pointcuts pc1 or pc2
|
36
|
-
advise_with do |jp, object, *args|
|
36
|
+
advise_with do |jp, object, *args|
|
37
37
|
# advise
|
38
38
|
end
|
39
39
|
end
|
40
40
|
|
41
|
-
I'm not sure it adds much (at this stage of thinking about it...) except that it could make
|
41
|
+
I'm not sure it adds much (at this stage of thinking about it...) except that it could make
|
42
42
|
composition of complex pointcuts easier.
|
43
43
|
|
44
44
|
I also want to ensure full support for running in JRuby. In particular, you should be able to
|
@@ -47,13 +47,13 @@ advise Java types!
|
|
47
47
|
=== Version 0.5
|
48
48
|
|
49
49
|
My goals for this release include more performance improvements (#19321) and investigating some ideas
|
50
|
-
for a real DSL, meaning declarative statements in blocks, rather than method arguments. It will be
|
50
|
+
for a real DSL, meaning declarative statements in blocks, rather than method arguments. It will be
|
51
51
|
redundant somewhat with the existing "method-argument form", as it exists today, but it will set
|
52
52
|
the stage for much more complex aspect definitions than would be convenient with the current form.
|
53
53
|
|
54
54
|
Another big change is to make pointcut evaluation and advice application happen continuous at runtime,
|
55
55
|
rather than only when the aspect is defined. This would eliminate subtle problems related to the order
|
56
|
-
of loading and also makes the declarative nature of aspects more fully realized. For example, if I
|
56
|
+
of loading and also makes the declarative nature of aspects more fully realized. For example, if I
|
57
57
|
declare an aspect for all types in a module namespace, it should still apply to nested types defined
|
58
58
|
or loaded after the aspect is defined. This enhancement will also make it easier to define reusable
|
59
59
|
and "abstract" aspects, where they aren't applied immediately, but only when extended by "concrete"
|
@@ -64,16 +64,14 @@ simpler aspects.
|
|
64
64
|
|
65
65
|
Finally, this release will finally support Ruby 1.9.X and the latest JRuby at the time of the release.
|
66
66
|
|
67
|
-
=== Version 0.6
|
67
|
+
=== Version 0.6.X and 0.7.X
|
68
|
+
|
69
|
+
These releases updated to the latest with Ruby and JRuby versions without other significant changes.
|
70
|
+
|
71
|
+
=== Version 1.0+
|
68
72
|
|
69
73
|
I have been thinking about higher-order abstractions that work above the "Pointcut + Advice
|
70
|
-
Model" of Aquarium (and AspectJ...) today. I consider the pointcut + advice model to be an
|
74
|
+
Model" of Aquarium (and AspectJ...) today. I consider the pointcut + advice model to be an
|
71
75
|
important, maybe essential, building block of AOP, but if that's all AOP is, then we've probably
|
72
|
-
already hit the limit of what we can expect AOP to do. That doesn't seem right to me, but it's
|
76
|
+
already hit the limit of what we can expect AOP to do. That doesn't seem right to me, but it's
|
73
77
|
not at all clear what the higher-order abstractions should be.
|
74
|
-
|
75
|
-
=== Version 1.0.0
|
76
|
-
|
77
|
-
Reasonably stable and full-featured API and DSL. Also, to justify Aquarium's existence ;), I want
|
78
|
-
to produce some non-trivial examples of refactoring known APIs and demonstrating improved clarity,
|
79
|
-
productivity, modularity, etc., etc.
|
data/Rakefile
CHANGED
@@ -1,9 +1,9 @@
|
|
1
1
|
$:.unshift('lib')
|
2
2
|
require 'rubygems'
|
3
3
|
require 'rake'
|
4
|
-
require 'rake/gempackagetask'
|
5
|
-
require 'rake/contrib/rubyforgepublisher'
|
6
4
|
require 'rake/clean'
|
5
|
+
require "bundler/gem_tasks"
|
6
|
+
require 'rake/packagetask'
|
7
7
|
require 'rdoc/task'
|
8
8
|
require 'aquarium/version'
|
9
9
|
|
@@ -20,8 +20,8 @@ PKG_VERSION = Aquarium::VERSION::STRING
|
|
20
20
|
PKG_FILE_NAME = "#{PKG_NAME}-#{PKG_VERSION}"
|
21
21
|
PKG_FILES = FileList[
|
22
22
|
'[A-Z]*',
|
23
|
-
'lib/**/*.rb',
|
24
|
-
'spec/**/*.rb',
|
23
|
+
'lib/**/*.rb',
|
24
|
+
'spec/**/*.rb',
|
25
25
|
'examples/**/*.rb',
|
26
26
|
'rake_tasks/**/*.rake'
|
27
27
|
]
|
@@ -29,71 +29,20 @@ FileUtils.touch(File.dirname(__FILE__) + '/previous_failures.txt')
|
|
29
29
|
|
30
30
|
IS_WINDOWS = (RUBY_VERSION == "1.8.7" ? PLATFORM : RUBY_PLATFORM) =~ /mswin/
|
31
31
|
|
32
|
-
|
32
|
+
WEBSITE_ROOT = "../doc/aquarium"
|
33
|
+
|
34
|
+
task :default => :spec
|
33
35
|
|
34
36
|
desc "Run all specs"
|
35
37
|
RSpec::Core::RakeTask.new do |t|
|
36
38
|
t.rspec_opts = ['--color', '--format progress', '--profile']
|
37
39
|
end
|
38
40
|
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
desc 'Generate HTML documentation for website'
|
45
|
-
task :webgen do
|
46
|
-
Dir.chdir '../doc/aquarium' do
|
47
|
-
output = nil
|
48
|
-
IO.popen('rake webgen 2>&1') do |io|
|
49
|
-
output = io.read
|
50
|
-
end
|
51
|
-
raise "ERROR while running webgen: #{output}" if output =~ /ERROR/n || $? != 0
|
52
|
-
end
|
53
|
-
end
|
54
|
-
|
55
|
-
desc 'Generate RDoc'
|
56
|
-
rd = Rake::RDocTask.new do |rdoc|
|
57
|
-
rdoc.rdoc_dir = '../doc/aquarium/out/rdoc'
|
58
|
-
rdoc.options << '--title' << 'Aquarium' << '--line-numbers' << '--inline-source' << '--main' << 'README'
|
59
|
-
rdoc.rdoc_files.include('README', 'CHANGES', 'MIT_LICENSE', 'examples/**/*.rb', 'UPGRADE', 'lib/**/*.rb') # 'EXAMPLES.rd'
|
60
|
-
end
|
61
|
-
|
62
|
-
# desc "Generate EXAMPLES.rb"
|
63
|
-
# task :rdoc do
|
64
|
-
# core.rdoc
|
65
|
-
# end
|
66
|
-
|
67
|
-
aquarium = Gem::Specification.new do |s|
|
68
|
-
s.name = PKG_NAME
|
69
|
-
s.version = PKG_VERSION
|
70
|
-
s.summary = Aquarium::VERSION::DESCRIPTION
|
71
|
-
s.description = <<-EOF
|
72
|
-
Aquarium is a full-featured Aspect-Oriented Programming (AOP) framework for Ruby that is
|
73
|
-
designed to provide an intuitive syntax and support for large-scale, dynamic aspects.
|
74
|
-
EOF
|
75
|
-
|
76
|
-
s.files = PKG_FILES.to_a
|
77
|
-
s.require_path = 'lib'
|
78
|
-
|
79
|
-
s.has_rdoc = true
|
80
|
-
s.rdoc_options = rd.options
|
81
|
-
s.extra_rdoc_files = rd.rdoc_files.reject { |fn| fn =~ /\.rb$|^EXAMPLES.rd$/ }.to_a
|
82
|
-
|
83
|
-
s.autorequire = 'aquarium'
|
84
|
-
s.bindir = 'bin'
|
85
|
-
s.executables = []
|
86
|
-
s.default_executable = ''
|
87
|
-
s.authors = ["Aquarium Development Team"]
|
88
|
-
s.email = "aquarium-devel@rubyforge.org"
|
89
|
-
s.homepage = "http://aquarium.rubyforge.org"
|
90
|
-
s.rubyforge_project = "aquarium"
|
91
|
-
end
|
92
|
-
|
93
|
-
Rake::GemPackageTask.new(aquarium) do |pkg|
|
94
|
-
pkg.need_zip = true
|
95
|
-
pkg.need_tar = true
|
96
|
-
end
|
41
|
+
Rake::PackageTask.new("aquarium", "#{Aquarium::VERSION::STRING}") do |p|
|
42
|
+
p.need_zip = true
|
43
|
+
p.need_tar = true
|
44
|
+
p.package_files = `git ls-files`.split("\n")
|
45
|
+
end
|
97
46
|
|
98
47
|
def egrep(pattern)
|
99
48
|
Dir['**/*.rb'].each do |fn|
|
@@ -115,12 +64,15 @@ task :todo do
|
|
115
64
|
end
|
116
65
|
|
117
66
|
task :clobber do
|
118
|
-
rm_rf
|
119
|
-
rm_rf
|
67
|
+
rm_rf "#{WEBSITE_ROOT}/out"
|
68
|
+
rm_rf "#{WEBSITE_ROOT}/webgen/out"
|
69
|
+
rm_rf "#{WEBSITE_ROOT}/webgen/tmp"
|
120
70
|
rm_rf 'translated_specs'
|
121
71
|
end
|
122
72
|
|
123
|
-
task :release => [:clobber, :verify_committed, :verify_user, :spec, :
|
73
|
+
task :release => [:clobber, :verify_committed, :verify_user, :spec, :package_release]
|
74
|
+
|
75
|
+
task :package_release => [:website, :install, :package, :publish_packages, :tag, :publish_website, :publish_news]
|
124
76
|
|
125
77
|
# TODO!
|
126
78
|
desc "Verifies that there is no uncommitted code"
|
@@ -132,37 +84,55 @@ task :verify_committed do
|
|
132
84
|
end
|
133
85
|
end
|
134
86
|
|
135
|
-
# TODO!
|
136
87
|
desc "Creates a tag in git"
|
137
88
|
task :tag do
|
138
|
-
|
89
|
+
`git tag #{Aquarium::VERSION::TAG}`
|
90
|
+
end
|
91
|
+
|
92
|
+
# desc "Creates a tag in svn (obsolete)"
|
93
|
+
# task :svntag do
|
94
|
+
# from = `svn info #{File.dirname(__FILE__)}`.match(/URL: (.*)\/aquarium/n)[1]
|
95
|
+
# to = from.gsub(/trunk/, "tags/#{Aquarium::VERSION::TAG}")
|
96
|
+
# tag_cmd = "svn cp #{from} #{to} -m \"Tag release #{Aquarium::VERSION::FULL_VERSION}\""
|
97
|
+
# raise "Can't tag to the same place: #{tag_cmd}" if to == from
|
98
|
+
# puts "Creating tag in SVN"
|
99
|
+
# `#{tag_cmd}`
|
100
|
+
# raise "Tagging failed" unless $? == 0
|
101
|
+
# end
|
139
102
|
|
140
|
-
desc "
|
141
|
-
task :
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
103
|
+
desc "Build the website, but do not publish it"
|
104
|
+
task :website => [:clobber, :rdoc, :webgen, :stage_website]
|
105
|
+
|
106
|
+
desc 'Generate HTML documentation for website'
|
107
|
+
task :webgen do
|
108
|
+
puts "In #{WEBSITE_ROOT}, running 'rake webgen':"
|
109
|
+
Dir.chdir "#{WEBSITE_ROOT}" do
|
110
|
+
output = nil
|
111
|
+
IO.popen('rake webgen 2>&1') do |io|
|
112
|
+
output = io.read
|
113
|
+
end
|
114
|
+
if $? != 0
|
115
|
+
puts "ERROR while running webgen: #{output}"
|
116
|
+
raise "ERROR while running webgen"
|
117
|
+
end
|
118
|
+
end
|
149
119
|
end
|
150
120
|
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
#
|
156
|
-
|
121
|
+
desc 'Generate RDoc'
|
122
|
+
rd = Rake::RDocTask.new do |rdoc|
|
123
|
+
rdoc.rdoc_dir = "#{WEBSITE_ROOT}/out/rdoc"
|
124
|
+
rdoc.options << '--title' << 'Aquarium' << '--line-numbers' << '--main' << 'README'
|
125
|
+
rdoc.rdoc_files.include('README', 'CHANGES', 'MIT_LICENSE', 'examples/**/*.rb', 'UPGRADE', 'lib/**/*.rb') # 'EXAMPLES.rd'
|
126
|
+
end
|
157
127
|
|
158
|
-
|
159
|
-
|
128
|
+
task :stage_website do
|
129
|
+
# Hack so that cp_r works correctly:
|
130
|
+
mv "#{WEBSITE_ROOT}/out/", "#{WEBSITE_ROOT}/out-tmp/"
|
131
|
+
puts "cp_r #{WEBSITE_ROOT}/webgen/out, #{WEBSITE_ROOT}/out"
|
132
|
+
cp_r "#{WEBSITE_ROOT}/webgen/out", "#{WEBSITE_ROOT}/out"
|
133
|
+
mv "#{WEBSITE_ROOT}/out-tmp/rdoc", "#{WEBSITE_ROOT}/out"
|
134
|
+
rmdir "#{WEBSITE_ROOT}/out-tmp/"
|
160
135
|
|
161
|
-
task :rdoc_rails do
|
162
|
-
Dir.chdir '../aquarium_on_rails' do
|
163
|
-
rake = IS_WINDOWS ? "rake.cmd" : "rake"
|
164
|
-
`#{rake} rdoc`
|
165
|
-
end
|
166
136
|
end
|
167
137
|
|
168
138
|
desc "Verify that the Aquarium specs run under JRuby and that JRuby can advise Java types. If this task fails, try running 'jruby -S rake' separately."
|
@@ -173,7 +143,7 @@ task :verify_jruby do
|
|
173
143
|
sh "jruby -S #{rake}"
|
174
144
|
end
|
175
145
|
end
|
176
|
-
|
146
|
+
|
177
147
|
|
178
148
|
task :verify_user do
|
179
149
|
raise "RUBYFORGE_USER environment variable not set!" unless ENV['RUBYFORGE_USER']
|
@@ -189,7 +159,7 @@ task :do_publish_website do
|
|
189
159
|
publisher = Rake::SshDirPublisher.new(
|
190
160
|
"#{host}",
|
191
161
|
"/var/www/gforge-projects/#{PKG_NAME}",
|
192
|
-
"
|
162
|
+
"#{WEBSITE_ROOT}/out"
|
193
163
|
)
|
194
164
|
publisher.upload
|
195
165
|
else
|
@@ -204,7 +174,7 @@ task :archive_website => [:verify_user, :website] do
|
|
204
174
|
publisher = Rake::SshDirPublisher.new(
|
205
175
|
"#{host}",
|
206
176
|
"/var/www/gforge-projects/#{PKG_NAME}/#{Spec::VERSION::TAG}",
|
207
|
-
"
|
177
|
+
"#{WEBSITE_ROOT}/out"
|
208
178
|
)
|
209
179
|
publisher.upload
|
210
180
|
end
|
@@ -241,7 +211,7 @@ task :publish_packages => [:verify_user, :package] do
|
|
241
211
|
*release_files
|
242
212
|
)
|
243
213
|
publisher.upload
|
244
|
-
|
214
|
+
|
245
215
|
puts "UPLADED THE FOLLOWING FILES:"
|
246
216
|
release_files.each do |file|
|
247
217
|
name = file.match(/pkg\/(.*)/)[1]
|