aop 0.0.1 → 0.0.2
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/.rspec +2 -0
- data/.travis.yml +13 -0
- data/Gemfile +4 -0
- data/README.md +6 -8
- data/lib/aop.rb +1 -1
- data/lib/aop/pointcut.rb +106 -0
- data/lib/aop/version.rb +1 -1
- data/spec/aop/after_spec.rb +24 -0
- data/spec/aop/before_spec.rb +24 -0
- data/spec/fixtures/bank_account.rb +6 -0
- data/spec/spec_helper.rb +29 -0
- data/spec/support/loader.rb +23 -0
- metadata +16 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: af9f44c8cac54ec4197e54a135d3c6829b8badd6
|
4
|
+
data.tar.gz: f9ffd78f10555e9ef4f09c2d14231d7248f4c82f
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: f24dd40907b9bd09ac43e491651cfb64cedaa4044363d99fba028e3b23c85afe2a5d1a12baa6d2c440a6e7dec0aec8f65413048753584183cb63efb8a8451b2a
|
7
|
+
data.tar.gz: af8f883d3f73072302b052afd3e69cca24d4d5d0517bb404cb30c68bc9f9be906e987aa3e1a596c9d3e625a96e028a28c5f7166708f405e66aa184b23282ca71
|
data/.rspec
ADDED
data/.travis.yml
ADDED
data/Gemfile
CHANGED
data/README.md
CHANGED
@@ -1,5 +1,7 @@
|
|
1
1
|
# Aop
|
2
2
|
|
3
|
+
[](https://travis-ci.org/waterlink/aop)
|
4
|
+
|
3
5
|
Very thin AOP gem for Ruby.
|
4
6
|
|
5
7
|
Thin and fast framework for Aspect Oriented Programming in Ruby.
|
@@ -24,11 +26,9 @@ Or install it yourself as:
|
|
24
26
|
|
25
27
|
### Before advice
|
26
28
|
|
27
|
-
*Not implemented*
|
28
|
-
|
29
29
|
```ruby
|
30
30
|
module Authentication
|
31
|
-
Aop["BankAccount#transfer:before"] do |account, *args, &blk|
|
31
|
+
Aop["BankAccount#transfer:before"].advice do |account, *args, &blk|
|
32
32
|
can!(:transfer, account)
|
33
33
|
end
|
34
34
|
|
@@ -40,11 +40,9 @@ end
|
|
40
40
|
|
41
41
|
### After advice
|
42
42
|
|
43
|
-
*Not implemented*
|
44
|
-
|
45
43
|
```ruby
|
46
44
|
module Analytics
|
47
|
-
Aop["User#sign_in:after"] do |target, *args, &blk|
|
45
|
+
Aop["User#sign_in:after"].advice do |target, *args, &blk|
|
48
46
|
report("sign_in", user.id)
|
49
47
|
end
|
50
48
|
end
|
@@ -56,7 +54,7 @@ end
|
|
56
54
|
|
57
55
|
```ruby
|
58
56
|
module Transactional
|
59
|
-
Aop["BankAccount#transfer:around"] do |joint_point, account, *args, &blk|
|
57
|
+
Aop["BankAccount#transfer:around"].advice do |joint_point, account, *args, &blk|
|
60
58
|
start_transaction
|
61
59
|
joint_point.call
|
62
60
|
finish_transaction
|
@@ -66,7 +64,7 @@ end
|
|
66
64
|
|
67
65
|
## TODO (to specify)
|
68
66
|
|
69
|
-
- multiple classes, methods and types of advices
|
67
|
+
- multiple classes, methods and types of advices at once
|
70
68
|
|
71
69
|
## Contributing
|
72
70
|
|
data/lib/aop.rb
CHANGED
data/lib/aop/pointcut.rb
ADDED
@@ -0,0 +1,106 @@
|
|
1
|
+
require "securerandom"
|
2
|
+
|
3
|
+
module Aop
|
4
|
+
def self.[](pointcut_spec)
|
5
|
+
Pointcut.new(pointcut_spec)
|
6
|
+
end
|
7
|
+
|
8
|
+
class Pointcut
|
9
|
+
def initialize(spec)
|
10
|
+
@spec = spec
|
11
|
+
|
12
|
+
@class_spec = spec.scan(/^[^#\.]+/).first || ""
|
13
|
+
@class_names = @class_spec.split(",")
|
14
|
+
@classes = @class_names.map { |name| Object.const_get(name) }
|
15
|
+
|
16
|
+
@method_spec = spec.scan(/[#\.][^#\.:]+/)
|
17
|
+
@methods = @method_spec.map { |m| MethodReference.from(m) }
|
18
|
+
|
19
|
+
@advices = spec.scan(/[^:]:([^:]+)/).flatten
|
20
|
+
end
|
21
|
+
|
22
|
+
def advice(&advised)
|
23
|
+
advices = @advices
|
24
|
+
advices.each do |advice|
|
25
|
+
_advice(advice, advised)
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
private
|
30
|
+
|
31
|
+
def _advice(advice, advised)
|
32
|
+
return before_advice(advised) if advice == "before"
|
33
|
+
after_advice(advised)
|
34
|
+
end
|
35
|
+
|
36
|
+
def generic_advice(advised, &body)
|
37
|
+
methods = @methods
|
38
|
+
@classes.each do |klass|
|
39
|
+
klass.class_eval do
|
40
|
+
methods.each do |method_ref|
|
41
|
+
method_ref.decorate(klass, &body[method_ref])
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
def before_advice(advised)
|
48
|
+
generic_advice(advised) do |method_ref|
|
49
|
+
lambda do |*args, &blk|
|
50
|
+
advised.call(self, *args, &blk)
|
51
|
+
method_ref.call(self, *args, &blk)
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
def after_advice(advised)
|
57
|
+
generic_advice(advised) do |method_ref|
|
58
|
+
lambda do |*args, &blk|
|
59
|
+
result = method_ref.call(self, *args, &blk)
|
60
|
+
advised.call(self, *args, &blk)
|
61
|
+
result
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
class MethodReference
|
67
|
+
def self.from(m)
|
68
|
+
return new(m) if m[0...1] == "#"
|
69
|
+
Singleton.new(m)
|
70
|
+
end
|
71
|
+
|
72
|
+
def initialize(m)
|
73
|
+
@name = m[1..-1]
|
74
|
+
end
|
75
|
+
|
76
|
+
def decorate(target, &with)
|
77
|
+
name = @name
|
78
|
+
new_name = alias_name
|
79
|
+
alias_target(target).class_eval do
|
80
|
+
alias_method(new_name, name)
|
81
|
+
define_method(name, &with)
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
def call(target, *args, &blk)
|
86
|
+
alias_target(target).send(alias_name, *args, &blk)
|
87
|
+
end
|
88
|
+
|
89
|
+
private
|
90
|
+
|
91
|
+
def alias_target(target)
|
92
|
+
target
|
93
|
+
end
|
94
|
+
|
95
|
+
def alias_name
|
96
|
+
@_alias_name ||= "__aop_#{SecureRandom.hex(10)}"
|
97
|
+
end
|
98
|
+
|
99
|
+
class Singleton < self
|
100
|
+
def alias_target(target)
|
101
|
+
class << target; self; end
|
102
|
+
end
|
103
|
+
end
|
104
|
+
end
|
105
|
+
end
|
106
|
+
end
|
data/lib/aop/version.rb
CHANGED
@@ -0,0 +1,24 @@
|
|
1
|
+
RSpec.describe "After advice" do
|
2
|
+
include FixtureLoader
|
3
|
+
|
4
|
+
let(:spy) { double("Spy") }
|
5
|
+
|
6
|
+
before do
|
7
|
+
load_fixture("BankAccount", "bank_account")
|
8
|
+
|
9
|
+
Aop["BankAccount#transfer:after"].advice do |account, *args, &blk|
|
10
|
+
spy.after(account, *args, &blk)
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
it "fires after #transfer" do
|
15
|
+
account = BankAccount.new(spy)
|
16
|
+
other = BankAccount.new(spy)
|
17
|
+
amount = 55
|
18
|
+
|
19
|
+
expect(spy).to receive(:inside).with(other, amount).ordered.once
|
20
|
+
expect(spy).to receive(:after).with(account, other, amount).ordered.once
|
21
|
+
|
22
|
+
expect(account.transfer(other, amount)).to eq(:a_result)
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
RSpec.describe "Before advice" do
|
2
|
+
include FixtureLoader
|
3
|
+
|
4
|
+
let(:spy) { double("Spy") }
|
5
|
+
|
6
|
+
before do
|
7
|
+
load_fixture("BankAccount", "bank_account")
|
8
|
+
|
9
|
+
Aop["BankAccount#transfer:before"].advice do |account, *args, &blk|
|
10
|
+
spy.before(account, *args, &blk)
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
it "fires before #transfer" do
|
15
|
+
account = BankAccount.new(spy)
|
16
|
+
other = BankAccount.new(spy)
|
17
|
+
amount = 55
|
18
|
+
|
19
|
+
expect(spy).to receive(:before).with(account, other, amount).ordered.once
|
20
|
+
expect(spy).to receive(:inside).with(other, amount).ordered.once
|
21
|
+
|
22
|
+
expect(account.transfer(other, amount)).to eq(:a_result)
|
23
|
+
end
|
24
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,29 @@
|
|
1
|
+
require "aop"
|
2
|
+
require "./spec/support/loader"
|
3
|
+
|
4
|
+
RSpec.configure do |config|
|
5
|
+
config.expect_with :rspec do |expectations|
|
6
|
+
expectations.include_chain_clauses_in_custom_matcher_descriptions = true
|
7
|
+
end
|
8
|
+
|
9
|
+
config.mock_with :rspec do |mocks|
|
10
|
+
mocks.verify_partial_doubles = true
|
11
|
+
end
|
12
|
+
|
13
|
+
config.filter_run :focus
|
14
|
+
config.run_all_when_everything_filtered = true
|
15
|
+
|
16
|
+
config.disable_monkey_patching!
|
17
|
+
|
18
|
+
config.warnings = true
|
19
|
+
|
20
|
+
if config.files_to_run.one?
|
21
|
+
config.default_formatter = 'doc'
|
22
|
+
end
|
23
|
+
|
24
|
+
config.profile_examples = 10
|
25
|
+
|
26
|
+
config.order = :random
|
27
|
+
|
28
|
+
Kernel.srand config.seed
|
29
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
module FixtureLoader
|
2
|
+
def self.included(base)
|
3
|
+
base.after { reset_fixtures }
|
4
|
+
end
|
5
|
+
|
6
|
+
def load_fixture(name, filename)
|
7
|
+
return if loaded[name]
|
8
|
+
load "./spec/fixtures/#{filename}.rb"
|
9
|
+
loaded[name] = 1
|
10
|
+
end
|
11
|
+
|
12
|
+
def reset_fixtures
|
13
|
+
loaded.each do |name, _|
|
14
|
+
Object.send(:remove_const, name)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
private
|
19
|
+
|
20
|
+
def loaded
|
21
|
+
@_loaded ||= {}
|
22
|
+
end
|
23
|
+
end
|
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.0.
|
4
|
+
version: 0.0.2
|
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-
|
11
|
+
date: 2015-02-07 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -46,13 +46,21 @@ extensions: []
|
|
46
46
|
extra_rdoc_files: []
|
47
47
|
files:
|
48
48
|
- ".gitignore"
|
49
|
+
- ".rspec"
|
50
|
+
- ".travis.yml"
|
49
51
|
- Gemfile
|
50
52
|
- LICENSE.txt
|
51
53
|
- README.md
|
52
54
|
- Rakefile
|
53
55
|
- aop.gemspec
|
54
56
|
- lib/aop.rb
|
57
|
+
- lib/aop/pointcut.rb
|
55
58
|
- lib/aop/version.rb
|
59
|
+
- spec/aop/after_spec.rb
|
60
|
+
- spec/aop/before_spec.rb
|
61
|
+
- spec/fixtures/bank_account.rb
|
62
|
+
- spec/spec_helper.rb
|
63
|
+
- spec/support/loader.rb
|
56
64
|
homepage: https://github.com/waterlink/aop
|
57
65
|
licenses:
|
58
66
|
- MIT
|
@@ -77,4 +85,9 @@ rubygems_version: 2.2.2
|
|
77
85
|
signing_key:
|
78
86
|
specification_version: 4
|
79
87
|
summary: Very thin AOP gem for Ruby
|
80
|
-
test_files:
|
88
|
+
test_files:
|
89
|
+
- spec/aop/after_spec.rb
|
90
|
+
- spec/aop/before_spec.rb
|
91
|
+
- spec/fixtures/bank_account.rb
|
92
|
+
- spec/spec_helper.rb
|
93
|
+
- spec/support/loader.rb
|