{"id":15293306,"url":"https://github.com/agilefreaks/aquarium","last_synced_at":"2026-06-08T20:04:11.750Z","repository":{"id":673454,"uuid":"316913","full_name":"Agilefreaks/Aquarium","owner":"Agilefreaks","description":"An AOP library for Ruby","archived":false,"fork":false,"pushed_at":"2023-06-01T05:59:24.000Z","size":3852,"stargazers_count":149,"open_issues_count":40,"forks_count":23,"subscribers_count":14,"default_branch":"master","last_synced_at":"2025-05-30T06:29:25.909Z","etag":null,"topics":["aspect-oriented-programming","ruby"],"latest_commit_sha":null,"homepage":"https://rubygems.org/gems/aquarium/","language":"Ruby","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/Agilefreaks.png","metadata":{"files":{"readme":"README.markdown","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null}},"created_at":"2009-09-24T23:36:03.000Z","updated_at":"2024-10-08T07:11:53.000Z","dependencies_parsed_at":"2023-10-20T16:24:51.374Z","dependency_job_id":null,"html_url":"https://github.com/Agilefreaks/Aquarium","commit_stats":{"total_commits":220,"total_committers":7,"mean_commits":"31.428571428571427","dds":"0.24090909090909096","last_synced_commit":"bd4ae8113bdb819120ddc542624dcf9c74a2dc46"},"previous_names":["deanwampler/aquarium"],"tags_count":15,"template":false,"template_full_name":null,"purl":"pkg:github/Agilefreaks/Aquarium","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Agilefreaks%2FAquarium","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Agilefreaks%2FAquarium/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Agilefreaks%2FAquarium/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Agilefreaks%2FAquarium/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/Agilefreaks","download_url":"https://codeload.github.com/Agilefreaks/Aquarium/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Agilefreaks%2FAquarium/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":259763091,"owners_count":22907411,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["aspect-oriented-programming","ruby"],"created_at":"2024-09-30T16:46:09.638Z","updated_at":"2026-06-08T20:04:11.705Z","avatar_url":"https://github.com/Agilefreaks.png","language":"Ruby","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Aquarium README\n\n* [Home Page](https://deanwampler.github.io/open-source/aquarium/index.html)\n* [Rubygems](https://rubygems.org/gems/aquarium)\n\nAquarium is a toolkit for Aspect-Oriented Programming (AOP) whose goals include:\n\n* A powerful *pointcut* language for specifying where to apply aspects, comparable to the pointcut language in AspectJ for Java.\n* Management of concurrent aspects (i.e., those acting on the same *join points*).\n* Adding and removing aspects dynamically.\n* A user-friendly DSL.\n* Support for advising Java types through JRuby.\n\n## SOMEBODY ADOPT ME!!\n\nWhile 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.\n\nSo, if you like this project, _please_ consider adopting it. Thanks!\n\n## Why Is an AOP Framework Useful in Ruby?\n\nRuby'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.\n\nHowever, what is missing in Ruby is an expressive language for describing systemic modifications, a so-called *pointcut language*. If you have simple needs for method interception and wrapping, then Aquarium will be overkill. However, if you have system-wide concerns that cross the boundaries of many objects, then an AOP tookit like Aquarium can help you implement these concerns in a more modular way.\n\nSo, if you are designing with aspects, wouldn't you like to write your code using the same language? Without AOP support, you have to map your aspect designs to metaprogramming idioms, which will often be slower to implement and harder to maintain. Imagine writing objects without native support for OOP!\n\n## Supported Ruby Versions (and Caveats)\n\nAquarium 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.\n\nAquarium 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.\n\nIf you need support for even earlier versions of Ruby and JRuby, use Aquarium v0.5.0.\n\n## Terminology\n\nSeveral terms are used in the AOP community.\n\n* **Join Point** - A point of execution in a program where *advice* might be invoked.\n* **Pointcut** - (yes, one word...) A set of join points of particular interest, like a query over all join points in the system.\n* **Advice** - The behavior invoked at a join point. There are several kinds of advice:\n  * **Before advice** - Advice invoked before the actual join point is invoked.\n  * **After returning advice** - Advice invoked after the join point executes successfully.\n  * **After raising advice** - Advice invoked only after the join point raises an exception.\n  * **After advice** - Advice invoked after the join point executes successfully or raises an exception.\n  * **Around advice** - Advice invoked instead of the join point. The around advice must choose whether or not to invoke the join point by calling a special `proceed` method. Otherwise, the join point is NOT executed.\n\nOnly around advice can prevent execution of the join point, except for the special case where before advice raises an exception.\n\n## Installation\n\nAquarium is available from [RubyGems.org](https://rubygems.org/gems/aquarium). The source code is available on [GitHub](http://github.com/deanwampler/Aquarium).\n\nThe simplest approach is to install the gem:\n\n    gem install aquarium    # sudo may be required on non-Windows systems\n\n## Examples\n\nSeveral complete examples are provided in the *examples* directory.\n\nIn most cases, you can either declare the appropriate classes or use the optional DSL, which adds convenience methods to classes, objects, or even `Object` itself. The API also supports many synonyms for things like types, objects, and methods. The best place to see the full list of synonyms is the output of `pointcut_spec.rb`.\n\nHere is an example that traces invocations of all public instance methods (included inherited ones) of the classes or modules `Foo` and `Bar`.\n\n```ruby\nrequire 'aquarium'\nAspect.new :around, :calls_to =\u003e :all_methods, :on_types =\u003e [Foo, Bar] do |join_point, object, *args|\n\tp \"Entering: #{join_point.target_type.name}##{join_point.method_name} for object #{object}\"\n\tresult = join_point.proceed\n\tp \"Leaving: #{join_point.target_type.name}##{join_point.method_name} for object #{object}\"\n\tresult  # block needs to return the result of the \"proceed\"!\nend\n```\n\nThe advice to execute at each join point is the block. The pointcut is the set of all public instance methods in `Foo` and `Bar`. (There are additional options available for specifying class methods, protected methods, excluding inherited (ancestor) methods, etc.) Here is the same example using the convenience DSL that adds aspect methods to `Object` (available only if you require aquarium/dsl/object_dsl', since other toolkits, like Rails, define similar methods on `Object`!).\n\n```ruby\nrequire 'aquarium/dsl/object_dsl'\naround :calls_to =\u003e :all_methods, :on_types =\u003e [Foo, Bar] do |join_point, object, *args|\n\tp \"Entering: #{join_point.target_type.name}##{join_point.method_name} for object #{object}\"\n\tresult = join_point.proceed\n\tp \"Leaving: #{join_point.target_type.name}##{join_point.method_name} for object #{object}\"\n\tresult  # block needs to return the result of the \"proceed\"!\nend\n```\n\nSee `examples/method_tracing_example.rb` for a more detailed version of this example.\n\nIf you don't want to add these methods to `Object`, you can also add them individually to modules, classes, or objects:\n\n```ruby\nrequire 'aquarium'\n...\nmodule MyModule\n\tinclude Aquarium::DSL\nend\n\nclass MyClass\n\tinclude Aquarium::DSL\nend\n\nmy_object = MyOtherClass.new\nmy_object.extend (Aquarium::DSL)\n```\n\nIf you use the DSL inside a class and omit the `:type`, `:types`, `:object` `:objects` options, `self` is assumed.\n\n```ruby\nclass Foo\n\tinclude Aquarium::DSL\n\t...\n\tdef critical_operation *args\n\t\t...\n\tend\nend\n...\nclass Foo\n\taround :critical_operation do |join_point, object, *args|\n\t\tp \"Entering: Foo#critical_operation\"\n\t\tresult = join_point.proceed\n\t\tp \"Leaving: Foo#critical_operation\"\n\t\tresult\n\tend\nend\n```\n\nIt 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.\n\nA 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!\n\nHere are some more succinct examples, illustrating the API (using the DSL methods) and some of the various synonyms for methods, types, etc.\n\nYou can pass in pointcuts defined elsewhere:\n\n```ruby\nmy_pointcut = Pointcut.new :invocations_of =\u003e /^do_/, :within_types =\u003e /Foo::Bar::/\naround :pointcuts =\u003e my_pointcut do |jp, obj, *args| ...         # Pass in a pointcut\naround :pointcuts =\u003e [my_pointcut, ...] do |jp, obj, *args| ...  # Pass in a pointcut array\n```\n\nAs a convenience, since a `JoinPoint` is a `Pointcut` with one element, you can pass a `JoinPoint` object where `Pointcut` objects are expected:\n\n```ruby\nmy_join_point1 = JoinPoint.new :type =\u003e Foo::Bar, :method =\u003e do_this\nmy_join_point2 = JoinPoint.new :type =\u003e Foo::Bar, :method =\u003e do_that\naround :pointcuts =\u003e my_join_point1 do |jp, obj, *args| ...\naround :pointcuts =\u003e [my_join_point1, my_join_point2, ...] do |jp, obj, *args| ...\n```\n\nYou 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.\n\n```ruby\naround :type =\u003e A, ...\naround :type =\u003e \"A\", ...\naround :types =\u003e [A, B, ...], ...\naround :types =\u003e %w[A, B, ...], ...\naround :types =\u003e /A::.*Helper$/, ...\naround :types =\u003e [/A::.*Helper$/, /B::Foo.*/], ...\n```\n\nEverywhere `:type` is used, you can substitute `:class`, `:classes`, `:module`, or `:modules`. Note that they are treated as synonyms; there is currently no enforcement that the values passed with `:class`, for example, are actually classes, not modules.\n\nThere are also several prepositional prefixes allowed for any of the synonyms. E.g.,\n\n```ruby\naround :for_types =\u003e A, ...\naround :on_types =\u003e A, ...\naround :in_types =\u003e A, ...\naround :within_types =\u003e A, ...\n```\n\nUsing the plural versions of the synonyms with method specifications sometimes read better:\n\n```ruby\naround :calls_to =\u003e :all_methods, :on_types =\u003e [A, B, ...], ...\naround :calls_to =\u003e :all_methods, :in_types =\u003e [A, B, ...], ...\naround :calls_to =\u003e :all_methods, :within_types =\u003e [A, B, ...], ...\n```\n\nYou can specify types and their descendents (subclasses or included modules) or ancestors. The same synonym prefixes for `:types` and `:type` also apply.\n\n```ruby\naround :type_and_ancestors =\u003e A, ...\naround :types_and_ancestors =\u003e A, ...\naround :type_and_descendents =\u003e A, ...\naround :types_and_descendents =\u003e A, ...\naround :classes_and_descendents =\u003e A, ...\naround :modules_and_descendents =\u003e A, ...\n```\n\nSome of the synonyms:\n\n```ruby\naround :calls_to =\u003e :all_methods, :on_types_and_ancestors =\u003e A, ...\naround :calls_to =\u003e :all_methods, :in_types_and_ancestors =\u003e A, ...\naround :calls_to =\u003e :all_methods, :within_types_and_ancestors =\u003e A, ...\nand similarly for descendents\n```\n\nYou 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.\n\n```ruby\na1 = A.new\na2 = A.new\naround :object =\u003e a1, ...\naround :objects =\u003e [a1, a2], ...\n```\n\nSome of the synonyms:\n\n```ruby\naround :calls_to =\u003e :all_methods, :on_objects =\u003e [a1, a2], ...\naround :calls_to =\u003e :all_methods, :in_objects =\u003e [a1, a2], ...\naround :calls_to =\u003e :all_methods, :within_objects =\u003e [a1, a2], ...\n```\n\nIf no types or objects are specified, the object defaults to `self`. However, this default is only supported when using the DSL to create an aspect, e.g.,\n\n```ruby\nclass MyClass\n\tinclude Aquarium::DSL\n\tdef doit; ...; end\n\n\taround :method =\u003e doit, ...   # Implicit :object =\u003e self, i.e., MyClass\nend\n```\n\nYou can specify a single method symbol (name), a regular expression, or an array of the same. The synonyms for `:methods` include `:method`, `:calls_to`, `:invoking`, `:invocations_of`, and `:sending_messages_to`. The special keywords `:all` and `:all_methods` mean match all methods, subject to the `:method_options` discussed next.\n\n```ruby\naround :method =\u003e :all_methods, ...\naround :method =\u003e :foo, ...\naround :methods =\u003e [:foo, :bar, :baz], ...\naround :methods =\u003e /^foo/, ...\naround :methods =\u003e [/^foo/, /bar$/], ...\n```\n\nUsing the synonyms:\n\n```ruby\naround :calls_to =\u003e :all_methods, ...\nafter  :invoking =\u003e :all_methods, ...\nafter  :invocations_of =\u003e :all_methods, ...\nafter  :sending_messages_to =\u003e :all_methods, ...\nafter  :within_methods =\u003e :all_methods, ...\n```\n\nYou can specify method options. By default, public instance methods only are matched. Note that `:methods =\u003e :all` or `:all_methods` with no method options matches all public instance methods, including ancestor (inherited and included module) methods. For all the method options (except for `:exclude_ancestor_methods`), you can append the suffix `_methods`. You can also use the `:restrict_methods_to` synonym for `:method_options`.\n\n```ruby\naround :methods =\u003e /foo/, :method_options =\u003e [:instance], ...  # match instance methods (default)\naround :methods =\u003e /foo/, :method_options =\u003e [:class], ...     # match class methods\naround :methods =\u003e /foo/, :method_options =\u003e [:public, :protected, :private], ...\n# match public, protected, and private instance methods\naround :methods =\u003e /foo/, :method_options =\u003e [:singleton], ... # match singleton methods\naround :methods =\u003e /foo/, :method_options =\u003e [:exclude_ancestor_methods], ...\n# ignore methods defined in ancestors, inherited classes and included modules\n```\n\nWith synonyms:\n\n```ruby\naround :calls_to =\u003e /foo/, :restricting_methods_to =\u003e [:singleton_methods], ...\n```\n\nYou 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.\n\n```ruby\naround :attribute  =\u003e :foo, ...                                 # defaults to methods #foo and #foo=\naround :attributes =\u003e :foo, ...                                 # the same\naround :accessing  =\u003e :foo, ...                                 # the same\n\naround :attribute =\u003e :foo, :attribute_options =\u003e [:readers]...  # only matches #foo\naround :reading   =\u003e :foo                                       # the same\n\n\naround :attribute =\u003e :foo, :attribute_options =\u003e [:writers]...  # only matches #foo=\naround :writing   =\u003e :foo                                       # the same\n\naround :attributes =\u003e [:foo, :bar, :baz], ...\naround :attributes =\u003e /^foo/, ...\naround :attributes =\u003e [/^foo/, /bar$/], ...\n```\n\nAgain, it's important to remember that actually advising the attribute accesses is not done; it's the public accessor methods that are advised! This may change in a future release.\n\nYou can specify a `Pointcut` that encapsulates one or more pre-defined `Pointcuts` or `JoinPoints`.\n\n```ruby\naround :pointcut =\u003e pc, ...                                     # for pre-defined pointcut \"pc\"\naround :pointcuts =\u003e [pc, ...], ...                             # for pre-defined pointcut list\naround :pointcut =\u003e jp, ...                                     # for pre-defined join point \"jp\"\naround :pointcuts =\u003e [jp, ...], ...                             # for pre-defined join point list\naround :pointcut =\u003e {:type =\u003e T, :method =\u003e :m}, ...            # same as around :type =\u003e T, :method =\u003e :m, ..\n```\n\nUsing the plural versions of the synonyms, with method specifications so they read better:\n\n```ruby\naround :for_pointcuts =\u003e [pc1, pc2, ...], ...\naround :on_pointcuts =\u003e [pc1, pc2, ...], ...\naround :in_pointcuts =\u003e [pc1, pc2, ...], ...\naround :within_pointcuts =\u003e [pc1, pc2, ...], ...\n```\n\nSince V0.4.2, you can also specify *named* pointcuts, which are searched for just like methods in types (as discussed below). For example, if several classes in module `App` define class constant pointcuts named STATE_CHANGE, the following expression in an around advice aspect will match all of them:\n\n```ruby\naround :named_pointcuts =\u003e { :constants_matching =\u003e :STATE_CHANGE, :in_types =\u003e /App::.*/ } ...\n```\n\nFor the type specification, which is required, any valid option for the `TypeFinder` class is allowed.\n\nYou can also match on class variables, using `:class_variables_matching`. To match on either kind of definition, use just `:matching`. If no `:*matching` is specified, then any class constant or variable `Pointcut` found will be matched.\n\nHere are the various `:*matching` options and their synonyms:\n\n```ruby\naround :named_pointcuts =\u003e { :constants_matching            =\u003e :STATE_CHANGE, ... } ...   # class constants only\naround :named_pointcuts =\u003e { :constants_named               =\u003e :STATE_CHANGE, ... } ...\naround :named_pointcuts =\u003e { :constants_with_names_matching =\u003e :STATE_CHANGE, ... } ...\n\naround :named_pointcuts =\u003e { :class_variables_matching            =\u003e :STATE_CHANGE, ... } ...   # class variables only\naround :named_pointcuts =\u003e { :class_variables_named               =\u003e :STATE_CHANGE, ... } ...\naround :named_pointcuts =\u003e { :class_variables_with_names_matching =\u003e :STATE_CHANGE, ... } ...\n\naround :named_pointcuts =\u003e { :matching            =\u003e :STATE_CHANGE, ... } ...   # class constants and variables\naround :named_pointcuts =\u003e { :named               =\u003e :STATE_CHANGE, ... } ...\naround :named_pointcuts =\u003e { :with_names_matching =\u003e :STATE_CHANGE, ... } ...\n```\n\nThe `:*matching` options take a name, regular expression or array of the same (you can mix names and regular expressions).\n\nYou can also use the following synonyms for `:named_pointcuts`:\n\n```ruby\naround :named_pointcut =\u003e {...}\naround :for_named_pointcut =\u003e {...}\naround :on_named_pointcut =\u003e {...}\naround :in_named_pointcut =\u003e {...}\naround :within_named_pointcut =\u003e {...}\naround :for_named_pointcuts =\u003e {...}\naround :on_named_pointcuts =\u003e {...}\naround :in_named_pointcuts =\u003e {...}\naround :within_named_pointcuts =\u003e {...}\n```\n\nYou 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.\n\n\u003e **NOTE:** 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.)\n\n```ruby\naround ..., :exclude_pointcut =\u003e pc, ...\naround ..., :exclude_pointcuts =\u003e [pc, ...]\naround ..., :exclude_named_pointcut =\u003e {...}\naround ..., :exclude_named_pointcuts =\u003e {...}\naround ..., :exclude_join_point =\u003e jp, ...\naround ..., :exclude_join_points =\u003e [jp, ...]\naround ..., :exclude_type =\u003e t, ...\naround ..., :exclude_types =\u003e [t, ...]\naround ..., :exclude_type_and_ancestors =\u003e t, ...\naround ..., :exclude_types_and_ancestors =\u003e [t, ...]\naround ..., :exclude_type_and_descendents =\u003e t, ...\naround ..., :exclude_types_and_descendents =\u003e [t, ...]\naround ..., :exclude_object =\u003e o, ...\naround ..., :exclude_objects =\u003e [o, ...]\naround ..., :exclude_method =\u003e m, ...\naround ..., :exclude_methods =\u003e [m, ...]\naround ..., :exclude_attribute =\u003e a, ...\naround ..., :exclude_attributes =\u003e [a, ...]\n```\n\nAll the same synonyms for `:pointcuts`, `:named_pointcuts`, `:types`, `:objects`, and `:methods` apply here as well (after the `exclude_` prefix).\n\nYou can advice methods before execution:\n\n```ruby\nbefore :types =\u003e ...\n```\n\nYou can advice methods after returning successfully (i.e., no exceptions were raised):\n\n```ruby\nafter_returning :types =\u003e ...\nafter_returning_from :types =\u003e ...\t# synonym\n```\n\nYou can advice methods after raising exceptions:\n\n```ruby\nafter_raising :types =\u003e ...              # After any exception is thrown\nafter_raising_within :types =\u003e ...       # synonym\nafter_raising =\u003e MyError, :types =\u003e ...  # Only invoke advice if \"MyError\" is raised.\nafter_raising =\u003e [MyError1, MyError2], :types =\u003e ...\n# Only invoke advice if \"MyError1\" or \"MyError2\" is raised.\n```\n\nYou can advice methods after returning successfully or raising exceptions. (You can't specify a set of exceptions in this case.):\n\n```ruby\nafter :types =\u003e ...\nafter_raising_within_or_returning_from :types =\u003e\t# synonym\n```\n\nYou 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.\n\n```ruby\nbefore_and_after :types =\u003e, ...\nbefore_and_after_returning :types =\u003e, ...\nbefore_and_after_returning_from :types =\u003e, ...\t# synonym\nbefore_and_after_raising :types =\u003e, ...\nbefore_and_after_raising_within :types =\u003e, ...\t# synonym\nbefore_and_after_raising_within_or_returning_from :types =\u003e, ...\t# synonym\n```\n\nIf you pass a block to `Aspect.new`, it will be the advice. When invoked, the advice will be passed the following three arguments:\n\n. The `JoinPoint`, which will contain a `JoinPoint::Context` object with useful context information\n. The object being sent the current message\n. The parameters passed with the original message\n\nRecall 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!\n\n```ruby\naround :type =\u003e [...], :methods =\u003e :all do |join_point, object, *args|\n  advice_to_execute_before_the_jp\n  result = join_point.proceed\t# Invoke the join point, passing *args implicitly (you can override...)\n  advice_to_execute_after_the_jp\n  result     # return the result of the \"proceed\", unless you override the value.\nend\naround(:type =\u003e [...], :methods =\u003e :all) {|join_point, object, *args| ...}  # (...) necessary for precedence...\n```\n\nIn 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.\n\nRather than passing a block as the advice, you can pass a previously-created `Proc`:\n\n```ruby\naround :type =\u003e [...], :methods =\u003e :all, :advice =\u003e advice\naround :type =\u003e [...], :methods =\u003e :all, :advise_with =\u003e advice  # synonym for advice. Note the \"s\"!\naround :type =\u003e [...], :methods =\u003e :all, :call =\u003e advice         # synonym for advice.\naround :type =\u003e [...], :methods =\u003e :all, :invoke =\u003e advice       # synonym for advice.\n```\nFinally, when running in *JRuby*, you can advise Java types! See the examples in the separate RSpec suite in the `jruby` directory and the discussion above concerning known limitations.\n\n## Building Aquarium ##\n\nThe gem is ready to go, but if you want to rebuild it, run the tests, etc., here's what to do.\n\n### Building the Aquarium gem\n\nIf you prefer to build the gem locally, check out source from GitHub\n\n    git clone git@github.com:deanwampler/Aquarium.git\n\n(or fork it)\n\nWARNING: The older RubyForge SVN repository (`svn://rubyforge.org/var/svn/aquarium/trunk`) is now obsolete!!\n\nUse the following commands to build everything:\n\n    rake install\n\nThis builds the gem file, `pkg/aquarium-0.7.0.gem`, and installs it locally.\n\nIf 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.\n\n    gem push pkg/aquarium-0.7.0.gem\n\nAt this time, with the demise of RubyForge, the docs and home page are temporarily hosted [here](https://deanwampler.github.io/open-source/aquarium/index.html). I'll move them to a better location soon.\n\n### Running Aquarium's RSpecs\n\nIn order to run Aquarium's full suite of specs (`rake pre_commit`) you must install the following gems and tools:\n\n* **rake** - Runs the build script\n* **rspec** - Used instead of Test::Unit for TDD\n* **rcov** - Verifies that the code is 100% covered by specs\n* **webgen** - Generates the static HTML website\n* **RedCloth** - Required by webgen\n* **syntax** - Required by RSpec's custom webgen extension to highlight ruby code\n* **diff-lcs** - Required if you use the --diff switch\n* **win32console** - Required by the --colour switch if you're on Windows\n* **meta_project** - Required in order to make releases at RubyForge\n* **heckle** - Required if you use the --heckle switch\n* **jruby** - Required if run the separate spec suite in the \"jruby\" directory\n\nOnce those are all installed, you should be able to run the suite with the following steps:\n\n    git clone git@github.com:deanwampler/Aquarium.git\n    cd aquarium\n    rake spec\n\nNote that once built, Aquarium itself doesn't have any dependencies outside the Ruby core and `stdlib`.\n\nIf you want to run the tests for the JRuby support, you must also have a supported version of JRuby installed (see above). To run the specs for JRuby, use the command\n\n    rake verify_jruby\n\nThis 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.\n\n\u003e **WARNING:** Many of the JRuby-specific specs, which test advising of Java methods, currently do not pass. See the notes above and in `UPGRADE`.\n\n### Internals: Package Structure\n\n`Aquarium::Aspects` contains the `Aspect` class and supporting classes `Pointcut`, `JoinPoint`, etc.\n\n`Aquarium::Finders` provides tools for locating types, objects, and methods in the runtime, using names, symbols, or regular expressions.\n\n`Aquarium::Extensions` provides extensions to several Ruby core library routines.\n\n`Aquarium::Utils` provides general-purpose utilities for manipulating `Strings`, `Sets`, `Hashes`, etc. as well as some generic types.\n\n`Aquarium::Extras` provides add-ons for Aquarium, such as a *Design by Contract* implementation. These extras are NOT included when you require the general `aquarium.rb` file. You have to explicitly include `aquarium/extras` or one of the `aquarium/extras/*` if you want to use them.\n\n## Miscellania\n\nA few other topics that might be of interest.\n\n### Differences With Other Ruby AOP Toolkits\n\nThere are several other AOP toolkits for Ruby that precede Aquarium. The most notable are AspectR and the aspect capabilities in the Facets toolkit. There are also Ruby 2.0 proposals to add method wrappers for `before`, `after` and `wrap` behavior.\n\nThe goal of Aquarium is to provide a superset of the functionality provided by these other toolkits. Aquarium is suitable for non-trivial and large-scale aspect-oriented components in systems. Aquarium will be most valuable for systems where aspects might be added and removed dynamically at runtime and systems where nontrivial pointcut descriptions are needed, requiring a full-featured pointcut language (as discussed above...). For less demanding needs, the alternatives are lighter weight and hence may be more appropriate.\n\n### Differences With AspectJ Behavior\n\nMany of AspectJ's features are not currently supported by Aquarium, but some of them are planned for future releases.\n\n* 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.\n* 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.\n* 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.\n* 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.\n\nHowever, Aquarium does have a few advantages over AspectJ, especially when advising Java types when running in JRuby.\n\n* Aquarium can add and remove advice dynamically, at runtime.\n* Aquarium can advise individual objects, not just classes.\n* Aquarium can advise JDK classes. AspectJ can also do this, but not by default.\n* Aquarium supports named advice that can be defined separately from the aspects that use the advice.\n* Aquarium can advise ancestor (parent) types, not just derived (descendent) types of specified types.\n\nSee also the next section, **Known Limitations**.\n\n### Known Limitations\n\n* You cannot advice `String`, `Symbol` or instances there of, because trying to specify either one will be confused with naming a type.\n* Concurrent advice on type AND advice on objects of the type can't be removed cleanly.\n* The pointcut language is still limited, compared to AspectJ's. See also the comparison with AspectJ behavior elsewhere in this document.\n* The API and wrapper DSL will probably evolve until the 1.0.0 release, although no such changes have been made since v0.4.0. 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.\n* 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:\n    * Aquarium advice on a method in a Java type will _only be invoked_ when the method is called _directly_ from Ruby.\n    * 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.\n    * BUG #18325: If you have Ruby subclasses of Java types *and* you advise a Java method in the hierarchy using @:types_and_descendents =\u003e MyJavaBaseClassOrInterface@ *and* you call unadvise on the aspect, the advice \"infrastructure\" is not correctly removed from the Ruby types. Workaround: Only advise methods in Ruby subclasses of Java types where the method is explicitly overridden in the Ruby class. (The spec and the \"Rubyforge bug report\":http://rubyforge.org/tracker/index.php?func=detail\u0026aid=18325\u0026group_id=4281\u0026atid=16494 provide examples.)\n    * BUG #18326: Normally, you can use either Java- or Ruby-style method names (e.g., `doSomething` vs. `do_something`), for Java types. However, if you write an aspect using the Java-style for a method name and a Ruby subclass of the Java type where the method is actually defined (i.e., the Ruby class doesn't override the method), it appears that the `JoinPoint` was advised, but the advice is never called. Workaround: Use the Ruby-style name in this scenario.\n\n### Acknowledgments\n\nMy colleagues in the AOSD community, in particular those who developed *AspectJ*, have been a big inspiration.\n\nThe *RSpec* team, in particular David Chelimsky, have really inspired my thinking about what's possible in Ruby, especially in the realm of DSLs. I also cribbed parts of the RSpec Rake process ;)\n\nKeita 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 finally release version 0.5.0. Thank you!\n\nFinally, a number of users have contributed valuable feedback. In particular, thanks to Brendan L., Matthew F., and Mark V.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fagilefreaks%2Faquarium","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fagilefreaks%2Faquarium","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fagilefreaks%2Faquarium/lists"}