aop 0.1.0 → 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/Gemfile +5 -0
- data/README.md +20 -0
- data/benchmarks/before.rb +56 -0
- data/lib/aop/pointcut.rb +39 -2
- data/lib/aop/version.rb +1 -1
- data/spec/aop/advanced_spec.rb +68 -0
- metadata +3 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: ab47f56fd5a02a5de555ceb2ace3ffeed0300db0
|
4
|
+
data.tar.gz: 8657d57606125d660e8cbbd544054ddfb7988381
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: fcfc3efd8608afcc4890a712d7b57468ef795b22e4dae6405827105ab95a8c84c1f4c62f0f2c50855a1cbd5508504b263e7d0ca203b571db1661783730e5e06e
|
7
|
+
data.tar.gz: 77bef4440f0080fda36afd8399c5cdebf9502b0aebe9db42c31353e36d051cf7c1b1d7f6bbd704d1143584ea4e749483bb5dd8469fe015e251c4603d9c1e1935
|
data/Gemfile
CHANGED
data/README.md
CHANGED
@@ -86,6 +86,26 @@ module Analytics
|
|
86
86
|
end
|
87
87
|
```
|
88
88
|
|
89
|
+
### Handling missed pointcuts
|
90
|
+
|
91
|
+
When pointcut is gone, for example when method or class gets renamed, it is a potential bug, because some code will not be run. This library tackles this problem by failing hard when pointcut can not be found.
|
92
|
+
|
93
|
+
Example:
|
94
|
+
|
95
|
+
```ruby
|
96
|
+
Aop["Admin#sign_in:after"].advice do |target, *args, &blk|
|
97
|
+
# .. do something ..
|
98
|
+
end
|
99
|
+
```
|
100
|
+
|
101
|
+
Then somebody renames `Admin#sign_in` to `Admin#logout`, and when you run the code you will get:
|
102
|
+
|
103
|
+
```
|
104
|
+
Aop::PointcutNotFound: Unable to find pointcut Admin#sign_in
|
105
|
+
Reason: #<NameError: undefined method `sign_in' for class `Admin'>
|
106
|
+
.. backtrace ..
|
107
|
+
```
|
108
|
+
|
89
109
|
## Contributing
|
90
110
|
|
91
111
|
1. Fork it ( https://github.com/waterlink/aop/fork )
|
@@ -0,0 +1,56 @@
|
|
1
|
+
require "aop"
|
2
|
+
require "benchmark"
|
3
|
+
require "method_profiler"
|
4
|
+
|
5
|
+
class Example
|
6
|
+
def normal_add(a, b)
|
7
|
+
a + b
|
8
|
+
end
|
9
|
+
|
10
|
+
def heavy_add(a, b)
|
11
|
+
a + b
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
Aop["Example#heavy_add:before"].advice do |example, a, b|
|
16
|
+
# do nothing
|
17
|
+
nil
|
18
|
+
end
|
19
|
+
|
20
|
+
def benchmark
|
21
|
+
example = Example.new
|
22
|
+
|
23
|
+
Benchmark.bm 30 do |x|
|
24
|
+
x.report 'normal add' do
|
25
|
+
1000000.times do
|
26
|
+
example.normal_add(rand(1000), rand(1000))
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
x.report 'add with :before' do
|
31
|
+
1000000.times do
|
32
|
+
example.heavy_add(rand(1000), rand(1000))
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
def profile
|
39
|
+
example = Example.new
|
40
|
+
|
41
|
+
observers = []
|
42
|
+
observers << MethodProfiler.observe(Example)
|
43
|
+
observers << MethodProfiler.observe(Aop)
|
44
|
+
observers << MethodProfiler.observe(Aop::Pointcut)
|
45
|
+
observers << MethodProfiler.observe(Aop::Pointcut::MethodReference)
|
46
|
+
observers << MethodProfiler.observe(Aop::Pointcut::MethodReference::Singleton)
|
47
|
+
|
48
|
+
10000.times do
|
49
|
+
example.heavy_add(rand(1000), rand(1000))
|
50
|
+
end
|
51
|
+
|
52
|
+
observers.each { |o| puts o.report }
|
53
|
+
end
|
54
|
+
|
55
|
+
benchmark
|
56
|
+
profile
|
data/lib/aop/pointcut.rb
CHANGED
@@ -5,13 +5,32 @@ module Aop
|
|
5
5
|
Pointcut.new(pointcut_spec)
|
6
6
|
end
|
7
7
|
|
8
|
+
class PointcutNotFound < StandardError
|
9
|
+
attr_reader :original_error
|
10
|
+
|
11
|
+
def initialize(pointcut_spec, original_error)
|
12
|
+
super("Unable to find pointcut #{pointcut_spec}")
|
13
|
+
@original_error = original_error
|
14
|
+
end
|
15
|
+
|
16
|
+
def to_s
|
17
|
+
"#{super}\n\tReason: #{original_error.inspect}"
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
8
21
|
class Pointcut
|
9
22
|
def initialize(spec)
|
10
23
|
@spec = spec
|
11
24
|
|
12
25
|
@class_spec = spec.scan(/^[^#\.]+/).first || ""
|
13
26
|
@class_names = @class_spec.split(",")
|
14
|
-
@classes = @class_names.map
|
27
|
+
@classes = @class_names.map do |name|
|
28
|
+
begin
|
29
|
+
Object.const_get(name)
|
30
|
+
rescue NameError => err
|
31
|
+
raise PointcutNotFound.new(spec, err)
|
32
|
+
end
|
33
|
+
end
|
15
34
|
|
16
35
|
@method_spec = spec.scan(/[#\.][^,#\.:]+/)
|
17
36
|
@methods = @method_spec.map { |m| MethodReference.from(m) }
|
@@ -92,6 +111,8 @@ module Aop
|
|
92
111
|
alias_method(new_name, name)
|
93
112
|
define_method(name, &with)
|
94
113
|
end
|
114
|
+
rescue NameError => err
|
115
|
+
raise PointcutNotFound.new(method_spec(target), err)
|
95
116
|
end
|
96
117
|
|
97
118
|
def call(target, *args, &blk)
|
@@ -100,15 +121,31 @@ module Aop
|
|
100
121
|
|
101
122
|
private
|
102
123
|
|
124
|
+
def method_spec(target)
|
125
|
+
"#{target_name(target)}#{method_notation}#{@name}"
|
126
|
+
end
|
127
|
+
|
128
|
+
def method_notation
|
129
|
+
"#"
|
130
|
+
end
|
131
|
+
|
103
132
|
def alias_target(target)
|
104
133
|
target
|
105
134
|
end
|
106
135
|
|
136
|
+
def target_name(target)
|
137
|
+
target.name || target.inspect
|
138
|
+
end
|
139
|
+
|
107
140
|
def alias_name
|
108
|
-
@_alias_name ||= "__aop_#{SecureRandom.hex(10)}"
|
141
|
+
@_alias_name ||= :"__aop_#{SecureRandom.hex(10)}"
|
109
142
|
end
|
110
143
|
|
111
144
|
class Singleton < self
|
145
|
+
def method_notation
|
146
|
+
"."
|
147
|
+
end
|
148
|
+
|
112
149
|
def alias_target(target)
|
113
150
|
class << target; self; end
|
114
151
|
end
|
data/lib/aop/version.rb
CHANGED
data/spec/aop/advanced_spec.rb
CHANGED
@@ -30,6 +30,74 @@ RSpec.describe "Advanced advices" do
|
|
30
30
|
end
|
31
31
|
end
|
32
32
|
|
33
|
+
describe "missed pointcuts" do
|
34
|
+
describe "missed class" do
|
35
|
+
let(:advice) {
|
36
|
+
Aop["NonexistentKlass#helloworld,.greeting:before"].advice do |target, *args, &blk|
|
37
|
+
:do_something
|
38
|
+
end
|
39
|
+
}
|
40
|
+
|
41
|
+
it "fails with proper message" do
|
42
|
+
expect { advice }.to raise_error(
|
43
|
+
Aop::PointcutNotFound,
|
44
|
+
/Unable to find pointcut NonexistentKlass#helloworld,.greeting/
|
45
|
+
)
|
46
|
+
end
|
47
|
+
|
48
|
+
it "declares proper reason" do
|
49
|
+
expect { advice }.to raise_error(
|
50
|
+
Aop::PointcutNotFound,
|
51
|
+
/Reason: #<NameError: (Missing or |)uninitialized constant(:|) NonexistentKlass>/
|
52
|
+
)
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
describe "missed instance method" do
|
57
|
+
let(:advice) {
|
58
|
+
Aop["BankAccount.transfer,#greeting:before"].advice do |target, *args, &blk|
|
59
|
+
:do_something
|
60
|
+
end
|
61
|
+
}
|
62
|
+
|
63
|
+
it "fails with proper message" do
|
64
|
+
expect { advice }.to raise_error(
|
65
|
+
Aop::PointcutNotFound,
|
66
|
+
/Unable to find pointcut BankAccount#greeting/
|
67
|
+
)
|
68
|
+
end
|
69
|
+
|
70
|
+
it "declares proper reason" do
|
71
|
+
expect { advice }.to raise_error(
|
72
|
+
Aop::PointcutNotFound,
|
73
|
+
/Reason: #<NameError: undefined method [`']greeting' for class [`']BankAccount'>/
|
74
|
+
)
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
describe "missed class method" do
|
79
|
+
let(:advice) {
|
80
|
+
Aop["BankAccount#transfer,.greeting:before"].advice do |target, *args, &blk|
|
81
|
+
:do_something
|
82
|
+
end
|
83
|
+
}
|
84
|
+
|
85
|
+
it "fails with proper message" do
|
86
|
+
expect { advice }.to raise_error(
|
87
|
+
Aop::PointcutNotFound,
|
88
|
+
/Unable to find pointcut BankAccount.greeting/
|
89
|
+
)
|
90
|
+
end
|
91
|
+
|
92
|
+
it "declares proper reason" do
|
93
|
+
expect { advice }.to raise_error(
|
94
|
+
Aop::PointcutNotFound,
|
95
|
+
/Reason: #<NameError: (undefined method|Unable to find) [`']greeting' for (class|object) ([`']Class'|[`']#<Class:0x[0-9a-f]+>'|BankAccount)>/
|
96
|
+
)
|
97
|
+
end
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
33
101
|
describe "multiple classes, methods and advices" do
|
34
102
|
before do
|
35
103
|
Aop["BankAccount,CashAccount#transfer,#withdraw,.transfer:before,:after"].advice do |*args, &blk|
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: aop
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version:
|
4
|
+
version: 1.0.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Alex Fedorov
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2015-02-
|
11
|
+
date: 2015-02-11 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -53,6 +53,7 @@ files:
|
|
53
53
|
- README.md
|
54
54
|
- Rakefile
|
55
55
|
- aop.gemspec
|
56
|
+
- benchmarks/before.rb
|
56
57
|
- lib/aop.rb
|
57
58
|
- lib/aop/pointcut.rb
|
58
59
|
- lib/aop/version.rb
|