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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 265bbfbbde5fedca853374f381977f960186c174
4
- data.tar.gz: 5ba07e066d01dfb9647f8294f29b1508604fe2ea
3
+ metadata.gz: ab47f56fd5a02a5de555ceb2ace3ffeed0300db0
4
+ data.tar.gz: 8657d57606125d660e8cbbd544054ddfb7988381
5
5
  SHA512:
6
- metadata.gz: 4e762bcad98bbbf72d5f867d22bb20e1add43d02a66e0f63f1da21c1804b335b539bee2eea2c2956c0a7900153931285052a4ee21413bc78e620575c0caefb79
7
- data.tar.gz: aa177e07c23669b04a02ac5de5e4ca9bedb2dacc7e92dd8124419b63fd5d5924fa0d236fee9dc01471ec32c8a3454e20cb19f59eb1c9e3b8c77cf1cb969092df
6
+ metadata.gz: fcfc3efd8608afcc4890a712d7b57468ef795b22e4dae6405827105ab95a8c84c1f4c62f0f2c50855a1cbd5508504b263e7d0ca203b571db1661783730e5e06e
7
+ data.tar.gz: 77bef4440f0080fda36afd8399c5cdebf9502b0aebe9db42c31353e36d051cf7c1b1d7f6bbd704d1143584ea4e749483bb5dd8469fe015e251c4603d9c1e1935
data/Gemfile CHANGED
@@ -5,4 +5,9 @@ gemspec
5
5
 
6
6
  group :test do
7
7
  gem 'rspec'
8
+ end
9
+
10
+ group :development do
11
+ gem "method_profiler"
12
+ gem "ruby-prof"
8
13
  end
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
@@ -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 { |name| Object.const_get(name) }
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
@@ -1,3 +1,3 @@
1
1
  module Aop
2
- VERSION = "0.1.0"
2
+ VERSION = "1.0.0"
3
3
  end
@@ -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: 0.1.0
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-08 00:00:00.000000000 Z
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