agilefreaks / aquarium Goto Github PK
View Code? Open in Web Editor NEWAn AOP library for Ruby
Home Page: https://rubygems.org/gems/aquarium/
An AOP library for Ruby
Home Page: https://rubygems.org/gems/aquarium/
Hello, colleagues.
I'm trying execute next code from the basic example
require 'aquarium'
include Aquarium::Aspects
class Foo
def foo
'foo'
end
end
Aspect.new :around, :calls_to => :all_methods, :on_types => [Foo] do |joint_point, object, *args|
p "Entering: #{join_point.target_type.name}##{join_point.method_name} for object #{object}"
result = join_point.proceed
p "Leaving: #{join_point.target_type.name}##{join_point.method_name} for object #{object}"
result # block needs to return the result of the "proceed"!
end
foo = Foo.new
foo.foo
and have next console output
d:/apps/Ruby193/lib/ruby/gems/1.9.1/gems/aquarium-0.5.1/lib/aquarium/aspects/aspect.rb:402: warning: redefining `object_id' may cause serious problems
d:/apps/Ruby193/lib/ruby/gems/1.9.1/gems/aquarium-0.5.1/lib/aquarium/aspects/aspect.rb:403: stack level too deep (SystemStackError)
It possible to get worked aspectr code for Ruby 1.9.3p545?
require 'aquarium'
class LoggingAspect
include Aquarium::Aspects
def initialize(logger)
@logger = logger
end
def apply(auth_usecase)
Aspect.new :after, methods: [:run], objects: [auth_usecase] do |jp|
if !jp.context.raised_exception
project = jp.context.returned_value
@logger.info("Project authenticated: #{project}")
else
auth_key = jp.context.parameters[0]
@logger.warn("Project failed to authenticate: #{auth_key}")
@logger.warn("Error: #{jp.context.raised_exception}")
end
end
end
end
class AuthUsecase
def run(auth_key)
auth_key
end
end
a = AuthUsecase.new
l = Logger.new("output.log")
LoggingAspect.new(l).apply(a)
Thread.new do
loop do
result = a.run("123")
msg = result == "123" ? "OK" : "WTF"
l.info msg
end
end
Thread.new do
loop do
result = a.run("ABC")
msg = result == "ABC" ? "OK" : "WTF"
l.info msg
end
end
> grep "WTF" output.log | wc -l
20118
> grep "OK" output.log | wc -l
29891
Compare it to unaspected version:
class AuthUsecase
def run(auth_key)
auth_key
end
end
a = AuthUsecase.new
l = Logger.new("output.log")
Thread.new do
loop do
result = a.run("123")
msg = result == "123" ? "OK" : "WTF"
l.info msg
end
end
Thread.new do
loop do
result = a.run("ABC")
msg = result == "ABC" ? "OK" : "WTF"
l.info msg
end
end
> grep "OK" output2.log | wc -l
113326
> grep "WTF" output2.log | wc -l
0
Date:2007-08-26 19:37
Priority:2
Submitted By:Dean Wampler (deanwampler)
Assigned To:Dean Wampler (deanwampler)
Category:Optimizations
State:Open
Summary:Use a lighter-weight static join point by default.
Detailed description
Should the join points be a lighter-weight, static join point, by default? Ideally, the runtime context object should
only be created if the advice actually uses it! Determining whether or not this is true won't be easy, however. ParseTree
might be usable to figure out if the context is used by the compiled proc. (TBD)
Date:2007-08-26 20:13
Priority:1
Submitted By:Dean Wampler (deanwampler)
Assigned To:Dean Wampler (deanwampler)
Category:Advice handling
State:Open
Summary:Support unadvising anonymous aspects
Detailed description
Currently, to "unadvise" advice (remove it), you have to have the Aspect
instance that created it. I.e., Aspect#unadvise
is an instance method.
One possibility is to implement an AspectManager that tracks existing aspects.
Hello :)
Describe the bug
Today we encountered a situation where two gems are patching the same core class with the same method. badum-ching
The adyen-ruby-api-library gem and the aquarium gem are both patching the method to_camel_case
into String.
While both seem to have the same intention, the behavior varies slightly regarding the first character.
One gem wants it as upper-case, the other one as lower-case.
We end up with the current exception: aquarium/aspects/advice.rb:281:in
const_get': wrong constant name noAdviceChainNode (NameError)`
Notice the lower-case 'n' in the name of a const.
Expected behavior
The expected behavior would be to not have the method to_camel_case
in the core String class at all. It would be best not to patch core classes at all within a gem. You never know who else might come up with the same idea for a method name.
Desktop (please complete the following information):
Additional context
A quick search revealed only one usage of to_camel_case
inside aquarium/aspects/advice.rb
. Is that correct? Is it possible to move the definition of to_camel_case
in to that same file? Or in to some kind of aquarium/utils.rb
maybe?
I'll also open an issue at the adyen gem.
Best regards,
Eric
Date: 2007-08-26 19:49
Priority: 2
Submitted By: Dean Wampler (deanwampler)
Assigned To: Dean Wampler (deanwampler)
Category: Pointcut Handling
State: Open
Summary: Can't advice string or symbol objects.
Detailed description
If you specify a string or symbol object with :object[s] => ....
specifiers, when you actually want to match on that object, they are misinterpreted as the names of types.
The fix is to better disambiguate the handling of :type[s]
vs. :object[s]
.
Date:2008-07-21 11:30
Priority:3
Submitted By:Dean Wampler (deanwampler)
Assigned To:Dean Wampler (deanwampler)
Category:Refactorings
State:Open
Summary:Eliminate most "require File.dirname(FILE) + ..."
Detailed description
As Micah Martin blogged, http://blog.8thlight.com/articles/2007/10/08/micahs-general-guidelines-on-ruby-require, you can avoid most of these by setting your path properly. Refactor Aquarium to use these idioms.
Date:2007-08-26 19:55
Priority:4
Submitted By:Dean Wampler (deanwampler)
Assigned To:Dean Wampler (deanwampler)
Category:Pointcut language
State:Open
Summary:Add conditional constructs to the pointcut language
Detailed description
Add conditional constructs, such as if, unless, etc. to the pointcut language.
Very important for approaching the functionality of AspectJ.
Date:2009-10-03 18:45
Priority:3
Submitted By:Dean Wampler (deanwampler)
Assigned To:Dean Wampler (deanwampler)
Category:JRuby support
State:Open
Summary:Support RSpec 1.2.8 (latest) and and JRuby 1.3.1 (latest)
Detailed description
Some small API changes, etc. cause some specs to break with these two updated versions of RSpec and JRuby. Fix.
Date:2008-12-02 13:13
Priority:3
Submitted By:Nobody
Assigned To:Dean Wampler (deanwampler)
Category:Miscellaneous
State:Open
Summary:No read file permissions after gem install
Detailed description
I've just installed aquarium via rubygems and I can't actually include it because all the permissions are wrong.
-rwx------ 1 root admin 315 Dec 2 12:59 aquarium.rb
aquarium:
total 56
drwxr-xr-x 15 root admin 510 Dec 2 12:52 .
drwxr-xr-x 4 root admin 136 Dec 2 12:52 ..
drwxr-xr-x 9 root admin 306 Dec 2 12:52 aspects
-rw------- 1 root admin 213 Dec 2 12:59 aspects.rb
drwxr-xr-x 4 root admin 136 Dec 2 12:52 dsl
-rw------- 1 root admin 83 Dec 2 12:59 dsl.rb
drwxr-xr-x 7 root admin 238 Dec 2 12:52 extensions
-rw------- 1 root admin 180 Dec 2 12:59 extensions.rb
drwxr-xr-x 3 root admin 102 Dec 2 12:52 extras
-rw------- 1 root admin 184 Dec 2 12:59 extras.rb
drwxr-xr-x 6 root admin 204 Dec 2 12:52 finders
-rw------- 1 root admin 164 Dec 2 12:59 finders.rb
drwxr-xr-x 14 root admin 476 Dec 2 12:52 utils
-rw------- 1 root admin 415 Dec 2 12:59 utils.rb
-rw------- 1 root admin 795 Dec 2 12:59 version.rb
One can see the problem when you compare this with a different gem:
-rw-r--r-- 1 root admin 1K Apr 30 2008 COPYING
-rw-r--r-- 1 root admin 886B Apr 30 2008 ChangeLog
-rw-r--r-- 1 root admin 12K Apr 30 2008 README
-rw-r--r-- 1 root admin 1K Apr 30 2008 wirble.gemspec
-rw-r--r-- 1 root admin 12K Apr 30 2008 wirble.rb
Date:2007-08-26 19:56
Priority:4
Submitted By:Dean Wampler (deanwampler)
Assigned To:Dean Wampler (deanwampler)
Category:Pointcut language
State:Open
Summary:Add "context flow" constructs to the pointcut language
Detailed description
Add "context flow" constructs to the pointcut language, like "cflow" and "within" constructs
in AspectJ's pointcut language.
I am not sure this application can be accomodated but I thought to bring this use case to your attention.
Context: Exploring using Aquarium to write RSpec examples of async_sinatra behavior.
In sinatra/base.rb you'll see something like this:
define_method "#{verb} #{path}", &block if block_given? unbound_method = instance_method("#{verb} #{path}") block = if block.arity != 0 proc { unbound_method.bind(self).call(*@block_params) } else proc { unbound_method.bind(self).call } end
I see this error when I try to replicate the method_tracing_example_spec.rb
`@@_aspect_class_advice_chain_ASSpec_HEAD _slash_foo' is not allowed as a class variable name
The spec helper file:
$stdout.sync=true require 'eventmachine' dir = "/usr/src/" require dir + 'em-spec/lib/em-spec/rspec' # or 'bacon' or 'test' require dir + 'em-spec/lib/ext/fiber18' require dir + 'sinatra/lib/sinatra/base' require dir + 'async_sinatra/lib/sinatra/async' # require dir + 'async_sinatra/spec/as_server' require 'aquarium' include Aquarium::Aspects module Sinatra module Async module SpecHelper # include ::Sinatra::Async::SpecServer include ::EM::SpecHelper end end end Spec::Runner.configure do |config| include ::Sinatra::Async::SpecHelper end
The spec file:
dir = File.expand_path(File.dirname(__FILE__)) require dir + '/as_spec_helper' module Sinatra describe Async, "routes when testing with EM::SpecHelper" do default_timeout 400 class ::ASSpec < ::Sinatra::Base register ::Sinatra::Async disable :raise_errors, :show_exceptions def log message @log ||= [] @log << message end def logged_messages @log end aget '/foo' do body "hello from aget" end end before(:all) do # @server=HTTPServer.new(ASSpec) # @wr_session=Webrat::Session.new # @wr_session gets clobbered somewhere in asinatra, possibly em. end it "should still work as usual" do aspect = ::Aquarium::Aspects::Aspect.new :around, :invocations_of => :all_methods, :for_type => ::ASSpec, :restricting_methods_to => :exclude_ancestor_methods do |execution_point, obj, *args| begin obj.log "Entering: #{execution_point.target_type.name}##{execution_point.method_name}: args = #{args.inspect}" execution_point.proceed ensure obj.log "Leaving: #{execution_point.target_type.name}##{execution_point.method_name}: args = #{args.inspect}" end end ::Sinatra.new(::ASSpec) end # it "should still work as usual" do # as_app = asinatra(ASSpec) do # visit 'http://0.0.0.0:4000/foo' # end # #resp.body.should == "hello from aget" # end # # it "should not require a call to done when #em is not used" do # 1.should == 1 # end # # it "should have timers" do # em do # start = Time.now # # EM.add_timer(0.5){ # (Time.now-start).should be_close( 0.5, 0.1 ) # done # } # end # end end end
0.4.4 doesn't work with Ruby 1.9.
At the moment if I wanted to try out the 1.9.1-port branch I had to put the following into my Gemfile:
gem 'aquarium', "0.0.0", :path => 'vendor/gems/Aquarium/aquarium'
and manually change the branch in my vendored aquarium directory.
The problem is that the project structure doesn't look like a gem (without building it first).
Ideally, it could work with:
gem 'aquarium', "0.4.4", :git => "....", :branch => "1.9.1-port"
Date:2007-08-26 20:03
Priority:3
Submitted By:Dean Wampler (deanwampler)
Assigned To:Dean Wampler (deanwampler)
Category:Advice handling
State:Open
Summary:Support user-defined advice precedence
Detailed description
This is a useful feature in AspectJ and will be important for teams using lots of aspects, where concurrent advice (i.e., more than one at the same join point), will be more common.
The release is unplanned because it's probably less important than other bugs and enhancements for the 0.2.0 release.
With V0.7.0, I moved them to https://deanwampler.github.io/open-source/aquarium/index.html. Need a better location...
Date:2007-09-18 18:24
Priority:2
Submitted By:Dean Wampler (deanwampler)
Assigned To:Dean Wampler (deanwampler)
Category:Advice handling
State:Open
Summary:Allow join points in an aspect to be "re-evaluated"
Detailed description
When an aspect is created, it finds the matching join points based on the state of the runtime at that moment. Types or objects are that are loaded or created later, which might also match, won't be advised.
This FR is to add the capability to have an aspect check again for possible join points. The behavior should be as follows:
Note, the last point may never happen, as a reference to the join point in the aspect might keep it alive, even if all other references are gone. It would be nice to find a way to detect such a situation or use the equivalent of Java "weak references" so that aspect's reference to the object is ignored when the garbage collector is used.
A more sophisticated implementation of this feature (and one that is probably preferred) would be to observe the runtime for newly-loaded classes and newly-created objects and apply advise dynamically.
Date:2008-11-24 22:07
Priority:4
Submitted By:Dean Wampler (deanwampler)
Assigned To:Dean Wampler (deanwampler)
Category:Internals
State:Open
Summary:Verbose and confusing stack trace when exception thrown
Detailed description
If a method being advised throws an exception, the stack trace is loaded with excessive to_s
information about the context. It would also be nice to strip out the stack frames from Aquarium internals.
Date:2007-08-26 20:29
Priority:2
Submitted By:Dean Wampler (deanwampler)
Assigned To:Dean Wampler (deanwampler)
Category:Refactorings
State:Open
Summary:Refactor eql? implementations to more closely follow conventions in the Ruby std libs.
Detailed description
They probably work fine now (they are tested!), but they could probably behave more like typical Ruby libs behave.
Date:2008-04-20 11:58
Priority:4
Submitted By:Dean Wampler (deanwampler)
Assigned To:Dean Wampler (deanwampler)
Category:Pointcut Handling
State:Open
Summary:No JoinPoints matched error for pointcut constants
Detailed description
When running rake website
, some of the specs. that used :named_pointcuts
that specified types with class constant :pointcuts
sometimes return no join points matched. This doesn't happen when running the specs in TextMate.
I suspect this is a strange interaction between the logic in the PointcutFinderTestClasses
that checks to see if the constant pointcut is already defined before defining it, so we don't get errors about redefining a constant. This can happen because several spec files load these test classes.
This problem doesn't happen when class variable pointcuts are used.
A test case demonstrating the problem is TBD.
Date:2008-10-25 22:04
Priority:3
Submitted By:Dean Wampler (deanwampler)
Assigned To:Dean Wampler (deanwampler)
Category:Refactorings
State:Open
Summary:Support Ruby 1.9
Detailed description
Make specific enhancements and add specific tests to verify correct execution on Ruby 1.9.
Date:2008-04-05 11:29
Priority:4
Submitted By:Dean Wampler (deanwampler)
Assigned To:Dean Wampler (deanwampler)
Category:Optimizations
State:Open
Summary:Advice overhead is too high
Detailed description
A method call takes 20-40 times longer when advised.
Method aliasing takes about 2 times longer.
Some possible bottlenecks:
alias_method
. We could start with this as the default, then replace it with the fullerDate:2008-02-28 10:47
Priority:4
Submitted By:Dean Wampler (deanwampler)
Assigned To:Dean Wampler (deanwampler)
Category:Pointcut language
State:Open
Summary:Warn when an explicitly specified type or object does not result in a join point match
Detailed description
You are warned if no join points are matched by the aspect, but not if you specify several types and/or objects and only some of them result in matches.
When running Rake v10.0.3, I get the following error:
$ rake
rake aborted!
uninitialized constant Rake::GemPackageTask
Hello guys,
I would like to ask if it is possible to use AOP programming in Rails to display Notifications to the users.
For example:
Before or after saving a user in the db show a notification popup that a new account is created
or
Before or after updating a user in the db show a notification popup that a new account is update
Can someone provide an example if that is posible?
Thanks in advance.
Date:2008-02-23 21:21
Priority:4
Submitted By:Dean Wampler (deanwampler)
Assigned To:Dean Wampler (deanwampler)
Category:JRuby Integration
State:Open
Summary:Can't cleanly remove advice from Ruby classes that extend Java classes, when using :types_and_descendents=>Java_type
Detailed description
Consider a Java class Base
and a Ruby class Derived
that subclasses Base
:
package com.foo;
public class Base {
public void doit() {}
}
class Derived < Java::ComFoo::Base
end
Then apply an aspect like
my_aspect = aspect :before, :types_and_descendents => Base, :calls_to => :doit do; ...; end
Then call my_aspect.unadvise
later, Aquarium does not properly clean up the advice infrastructure from Derived
which it creates for the doit
method (even though doit
isn't defined in Derived
).
It appears to work fine if you advise a Java hierarchy only or you advise the Ruby class and only the methods it explicitly defines, including overrides of Java-class methods. The following changes to my_aspect
work fine.
my_aspect = aspect :before, :types => Base, :calls_to => :doit do;
...
end
my_aspect = aspect :before, :types_and_descendents => Base, :calls_to => :doit, :restrict_methods_to => exclude_ancestor_methods do
...
end
The 2nd variation works because doit
is only defined in Base
, so only that version will be advised.
As described here: ruby/set#2 SortedSet has been removed from Ruby and is now an external dependency.
Due to dynamic nature of Aquarium specs now fail due to a:
RuntimeError:
The `SortedSet` class has been extracted from the `set` library. You must use the `sorted_set` gem or other alternatives.
while iterating over Module.contants
We can either rescue this error or include SortedSet as a dependency.
The current documentation is hosted here
Will need to move it to some other place.
I would like to be able to configure Aquarium once so that I can set :method_options to a particular default set of options. (Say :public, :private, :protected). It would be nice if I were able to just configure Aquarium in one location rather than re-declaring the method_options for every Advice.
Date:2008-03-26 12:49
Priority:3
Submitted By:Dean Wampler (deanwampler)
Assigned To:Dean Wampler (deanwampler)
Category:None
State:Open
Summary:Reusable aspects: deferred definition of pointcuts and advising of join points
Detailed description
There is currently no easy way to define a reusable aspect, where the actual pointcuts are specified and evaluated later, because the Aspect is evaluated as soon as it is declared!
It would not be difficult to extend Aspect to defer evaluation (See #19 (RubyForge-14054) for a related feature request). It should also be possible to allow deferred specification of pointcuts.
For now, there is a "module_eval" hack documented in
examples/reusable_aspect_hack_example.rb
I am not sure what the purpose of the empty? part of this empty method is, but it may cause an issue when creating aspects on types that use that method for other purposes than what I believe is what is intended here. For example. MongoMapper document types use it to query the document collection, which actually raises a NameError when the connection has not been set yet (which is likely when aspect code interpreted). Consider removal of the empty? half of this condition?
Could you add an example that adds a join point upon entering an assignment operator that does a typecheck? This would be very useful even if it only worked on specific classes like String and Fixnum.
Date:2008-02-15 18:22
Priority:3
Submitted By:Dean Wampler (deanwampler)
Assigned To:Dean Wampler (deanwampler)
Category:None
State:Open
Summary:Need a mechanism to suspend advice execution, especially during advice itself
Detailed description
A common problem in aspects is infinite recursion when the advice for a method calls the method, which invokes the advice for the method, ad infinitum. AspectJ handles this with several pointcut designators, e.g., !within(MyAspect) or !cflowbelow(adviceexecution()), etc.
While I want to add these sorts of designators eventually, it might be nice to add something more "rubyish" such as the following:
aspect :before, :calls_to => doit, :on_type => Foo do |jp, object, *args|
jp.while_suspending_advice do
value = object.foo(args)
...
end
end
JoinPoint#while_suspending_advice
might take an optional list of JoinPoints, where if blank, all advices for all JoinPoints are bypassed, but if JPs are specified, only those are suspended within the block.
Note that this facility is only required for the general case where advice wants to call arbitrary methods that may have advice. If the advice just wants to call the same method that it is advising, there is already a workaround;
call jp.invoke_original_join_point.
Date:2008-02-23 21:36
Priority:4
Submitted By:Dean Wampler (deanwampler)
Assigned To:Dean Wampler (deanwampler)
Category:JRuby Integration
State:Open
Summary:Advise on a Java method, using the Java name (e.g., doIt
), on a Ruby subclass, is not invoked
Detailed description
For example:
package com.foo;
public class Foo {
public void doIt() {}
}
class RubyFoo < Java::ComFoo::Foo
end
Now
aspect1 = aspect :before, :calls_to => :doIt, :on_type => RubyFoo do
...
end
Even though aspect1.matched_join_points
will contain the expected JoinPoint
,
the advise will never be called. However, if you use
aspect2 = aspect :before, :calls_to => :do_it, :on_type => RubyFoo do
...
end
The advice will be invoked if Foo#doIt
is invoked.
Date:2008-02-28 15:02
Priority:1
Submitted By:Dean Wampler (deanwampler)
Assigned To:Dean Wampler (deanwampler)
Category:Advice handling
State:Open
Summary:Add :around_and_return
advice?
Detailed description
It's a common mistake to forget that around advice needs to return something appropriate. For example, the following method-tracing advice block probably doesn't do what you intended:
p "entering #{join_point.inspect}"
join_point.proceed
p "leaving #{join_point.inspect}"
It looks simple enough, but the block will return the result of p
, which is nil, rather than the result
of calling proceed
.
This new advice type would capture internally whatever proceed returned and use that value as the result after the block finishes. It would allow the above advice to work as intended.
I'm not sure this is the best solution to this common problem, but I've made this mistake too, so it's easy to do!
Date:2008-04-27 14:35
Priority:3
Submitted By:Dean Wampler (deanwampler)
Assigned To:Dean Wampler (deanwampler)
Category:None
State:Open
Summary:Removed any deprecated features before 1.0 release
Detailed description
Just a reminder that anything marked deprecated before 1.0 should be removed as we approach that release.
When I create a pointcut like this:
my_pointcuts = Pointcut.new :invocations_of => :all_methods, :on_types => MyClass
It keep giving me this error:
uninitialized constant MyClass (NameError)
Can you help me with that, please?
Date:2009-10-03 18:47
Priority:3
Submitted By:Dean Wampler (deanwampler)
Assigned To:Dean Wampler (deanwampler)
Category:Documentation
State:Open
Summary:Upgrade website generation to webgen 5.X
Detailed description
The web site doesn't build with Webgen 5.X.
Date:2007-08-26 19:31
Priority:3
Submitted By:Dean Wampler (deanwampler)
Assigned To:Dean Wampler (deanwampler)
Category:Examples
State:Open
Summary:Provide example refactorings of ActiveRecord, other Rails libs.
Detailed description
Use Aquarium to refactor some of the metaprogramming code in Rails and perhaps other libraries to demonstrate when AOP is and isn't appropriate.
and before/after advices
Date:2007-08-26 20:01
Priority:1
Submitted By:Dean Wampler (deanwampler)
Assigned To:Dean Wampler (deanwampler)
Category:Advice handling
State:Open
Summary:Refine, as appropriate, the default advice precedence of concurrent around and before/after advices
Detailed description
Currently, it's just ordered by when it's defined. Should the behavior have a default precedence independent of when the advice is applied?
Date:2008-04-20 20:11
Priority:3
Submitted By:Dean Wampler (deanwampler)
Assigned To:Dean Wampler (deanwampler)
Category:Documentation
State:Open
Summary:Figure out how to get RDocs generated for methods generated using metaprogramming
Detailed description
For example, in some places, I iterated through an array of strings and use eval to create accessor methods (when the bodies need customization...). These methods don't show up in the rdocs.
JoinPoint
is a good example.
This means that the rdocs aren't complete for the reader!
Date:2008-02-28 10:48
Priority:4
Submitted By:Dean Wampler (deanwampler)
Assigned To:Dean Wampler (deanwampler)
Category:Pointcut language
State:Open
Summary:Warn if an :objects
flag is used with a type value
Detailed description
While types are technically objects, it is potentially confusing to use types with the :objects
flag. Warn the user.
Date:2007-08-26 20:18
Priority:1
Submitted By:Dean Wampler (deanwampler)
Assigned To:Dean Wampler (deanwampler)
Category:Pointcut language
State:Open
Summary:Support "MyClass#my_method" and "MyClass.my_method" as convenience options
Detailed description
I.e.,
after "MyClass#my_method" ...
as a convenient alternative for instance methods instead of
after :type => MyClass, :method => :my_method
Simiarly,
after "MyClass.my_method" ...
as a convenient alternative for class methods instead of
after :type => MyClass, :method => :my_method, :method_options => [:class]
However, the distinction between '#' and '.' is probably too subtle and both should either be interpreted as instance methods (the most common case) or should be allowed to match either instance or class methods, whatever matches.
A declarative, efficient, and flexible JavaScript library for building user interfaces.
๐ Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. ๐๐๐
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google โค๏ธ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.