aspector 0.13.1 → 0.14.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 +7 -0
- data/.gitignore +14 -0
- data/.rubocop.yml +26 -0
- data/.ruby-gemset +1 -0
- data/.ruby-version +1 -0
- data/.travis.yml +8 -11
- data/Changelog.md +59 -0
- data/Gemfile +9 -14
- data/Gemfile.lock +84 -50
- data/README.md +118 -0
- data/Rakefile +6 -22
- data/aspector.gemspec +15 -127
- data/benchmarks/after_benchmark.rb +28 -0
- data/benchmarks/around_advice_benchmark.rb +35 -0
- data/benchmarks/around_benchmark.rb +32 -0
- data/benchmarks/before_benchmark.rb +28 -0
- data/benchmarks/benchmark_helper.rb +17 -0
- data/benchmarks/combined_benchmark.rb +36 -0
- data/benchmarks/method_invocation_benchmark.rb +30 -0
- data/benchmarks/raw_benchmark.rb +39 -0
- data/examples/activerecord_hooks.rb +10 -15
- data/examples/around_example.rb +20 -31
- data/examples/aspector_apply_example.rb +10 -17
- data/examples/aspector_example.rb +7 -16
- data/examples/cache_aspect.rb +20 -30
- data/examples/design_by_contract.rb +20 -44
- data/examples/exception_handler.rb +12 -20
- data/examples/exception_handler2.rb +16 -24
- data/examples/implicit_method_option_test.rb +8 -16
- data/examples/interception_options_example.rb +71 -0
- data/examples/logging_aspect.rb +16 -24
- data/examples/process_aspector.rb +13 -0
- data/examples/retry_aspect.rb +20 -20
- data/lib/aspector.rb +17 -15
- data/lib/aspector/advice.rb +44 -57
- data/lib/aspector/advice_metadata.rb +10 -11
- data/lib/aspector/aspect_instances.rb +2 -3
- data/lib/aspector/base.rb +6 -368
- data/lib/aspector/base_class_methods.rb +24 -55
- data/lib/aspector/deferred_logic.rb +3 -4
- data/lib/aspector/deferred_option.rb +5 -10
- data/lib/aspector/interception.rb +356 -0
- data/lib/aspector/logger.rb +18 -45
- data/lib/aspector/logging.rb +10 -29
- data/lib/aspector/method_matcher.rb +5 -6
- data/lib/aspector/object_extension.rb +4 -12
- data/lib/aspector/version.rb +3 -0
- data/spec/examples_spec.rb +59 -0
- data/spec/functionals/aspect_for_multiple_targets_spec.rb +54 -0
- data/spec/functionals/aspect_interception_options_accessing_spec.rb +112 -0
- data/spec/functionals/aspect_on_a_class_spec.rb +159 -0
- data/spec/functionals/aspect_on_an_instance_spec.rb +66 -0
- data/spec/functionals/aspector_spec.rb +138 -0
- data/spec/functionals/aspects_combined_spec.rb +37 -0
- data/spec/functionals/aspects_execution_order_spec.rb +61 -0
- data/spec/functionals/aspects_on_private_methods_spec.rb +82 -0
- data/spec/spec_helper.rb +20 -21
- data/spec/support/class_builder.rb +44 -0
- data/spec/units/advice_spec.rb +49 -0
- data/spec/units/advices/after_spec.rb +328 -0
- data/spec/units/advices/around_spec.rb +336 -0
- data/spec/units/advices/before_filter_spec.rb +287 -0
- data/spec/units/advices/before_spec.rb +237 -0
- data/spec/units/advices/raw_spec.rb +67 -0
- data/spec/units/base_class_methods_spec.rb +262 -0
- data/spec/units/base_spec.rb +133 -0
- data/spec/units/deferred_logic_spec.rb +35 -0
- data/spec/units/logger_spec.rb +20 -0
- data/spec/units/logging_spec.rb +85 -0
- data/spec/units/method_matcher_spec.rb +95 -0
- data/spec/units/object_extension_spec.rb +11 -0
- data/spec/units/special_chars_spec.rb +128 -0
- metadata +98 -246
- data/.document +0 -5
- data/.rvmrc +0 -8
- data/README.rdoc +0 -80
- data/VERSION +0 -1
- data/performance-tests/after_test.rb +0 -25
- data/performance-tests/around_advice_benchmark.rb +0 -66
- data/performance-tests/around_test.rb +0 -27
- data/performance-tests/before_test.rb +0 -25
- data/performance-tests/combined_test.rb +0 -33
- data/performance-tests/method_invocation_test.rb +0 -25
- data/performance-tests/raw_test.rb +0 -37
- data/performance-tests/test_helper.rb +0 -9
- data/run_all_examples.sh +0 -12
- data/spec/functional/advices_on_private_methods_spec.rb +0 -21
- data/spec/functional/aspect_on_eigen_class_spec.rb +0 -72
- data/spec/functional/aspect_on_object_spec.rb +0 -20
- data/spec/functional/aspector_spec.rb +0 -140
- data/spec/functional/aspects_combined_spec.rb +0 -48
- data/spec/functional/execution_order_spec.rb +0 -42
- data/spec/unit/advice_spec.rb +0 -4
- data/spec/unit/after_spec.rb +0 -88
- data/spec/unit/around_spec.rb +0 -76
- data/spec/unit/base_class_methods_spec.rb +0 -28
- data/spec/unit/base_spec.rb +0 -112
- data/spec/unit/before_spec.rb +0 -125
- data/spec/unit/deferred_logic_spec.rb +0 -23
- data/spec/unit/method_matcher_spec.rb +0 -43
- data/spec/unit/raw_spec.rb +0 -53
- data/spec/unit/special_chars_spec.rb +0 -122
@@ -1,15 +1,13 @@
|
|
1
|
-
|
1
|
+
$LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
|
2
|
+
require 'aspector'
|
3
|
+
|
4
|
+
# Example class to which we will apply our aspects
|
5
|
+
class ExampleClass
|
2
6
|
def test
|
3
7
|
puts 'test'
|
4
8
|
end
|
5
9
|
end
|
6
10
|
|
7
|
-
##############################
|
8
|
-
|
9
|
-
$LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
|
10
|
-
|
11
|
-
require 'aspector'
|
12
|
-
|
13
11
|
aspect = Aspector do
|
14
12
|
target do
|
15
13
|
def do_this
|
@@ -24,13 +22,8 @@ aspect = Aspector do
|
|
24
22
|
end
|
25
23
|
end
|
26
24
|
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
# Expected output:
|
34
|
-
# do_this
|
35
|
-
# do_that
|
36
|
-
# test
|
25
|
+
aspect.apply(ExampleClass)
|
26
|
+
element = ExampleClass.new
|
27
|
+
element.test
|
28
|
+
aspect.disable
|
29
|
+
element.test
|
@@ -1,16 +1,14 @@
|
|
1
|
-
|
1
|
+
$LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
|
2
|
+
require 'aspector'
|
3
|
+
|
4
|
+
# Example class to which we will apply our aspects
|
5
|
+
class ExampleClass
|
2
6
|
def test
|
3
7
|
puts 'test'
|
4
8
|
end
|
5
9
|
end
|
6
10
|
|
7
|
-
|
8
|
-
|
9
|
-
$LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
|
10
|
-
|
11
|
-
require 'aspector'
|
12
|
-
|
13
|
-
aspector(A) do
|
11
|
+
aspector(ExampleClass) do
|
14
12
|
target do
|
15
13
|
def do_this
|
16
14
|
puts 'do_this'
|
@@ -24,11 +22,4 @@ aspector(A) do
|
|
24
22
|
end
|
25
23
|
end
|
26
24
|
|
27
|
-
|
28
|
-
|
29
|
-
A.new.test
|
30
|
-
|
31
|
-
# Expected output:
|
32
|
-
# do_this
|
33
|
-
# do_that
|
34
|
-
# test
|
25
|
+
ExampleClass.new.test
|
data/examples/cache_aspect.rb
CHANGED
@@ -1,5 +1,8 @@
|
|
1
|
-
|
1
|
+
$LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
|
2
|
+
require 'aspector'
|
2
3
|
|
4
|
+
# Example class to which we will apply our aspects
|
5
|
+
class ExampleClass
|
3
6
|
def test
|
4
7
|
puts 'test'
|
5
8
|
1
|
@@ -9,16 +12,13 @@ class A
|
|
9
12
|
puts 'test2'
|
10
13
|
2
|
11
14
|
end
|
12
|
-
|
13
15
|
end
|
14
16
|
|
15
|
-
|
16
|
-
|
17
|
+
# A simple cache engine
|
17
18
|
class SimpleCache
|
18
|
-
|
19
19
|
@data = {}
|
20
20
|
|
21
|
-
def self.cache
|
21
|
+
def self.cache(key, ttl)
|
22
22
|
found = @data[key] # found is like [time, value]
|
23
23
|
|
24
24
|
if found
|
@@ -34,46 +34,36 @@ class SimpleCache
|
|
34
34
|
@data[key] = [Time.now, value]
|
35
35
|
value
|
36
36
|
end
|
37
|
-
|
38
37
|
end
|
39
38
|
|
40
|
-
|
41
|
-
|
42
|
-
$LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
|
43
|
-
|
44
|
-
require 'aspector'
|
45
|
-
|
39
|
+
# Aspect used to wrap methods with caching logic
|
46
40
|
class CacheAspect < Aspector::Base
|
47
|
-
default :
|
41
|
+
default ttl: 60
|
48
42
|
|
49
|
-
around :
|
43
|
+
around interception_arg: true, method_arg: true do |interception, method, proxy, &block|
|
50
44
|
key = method
|
51
|
-
ttl =
|
45
|
+
ttl = interception.options[:ttl]
|
52
46
|
|
53
47
|
SimpleCache.cache key, ttl do
|
54
|
-
proxy.call
|
48
|
+
proxy.call(&block)
|
55
49
|
end
|
56
50
|
end
|
57
|
-
|
58
51
|
end
|
59
52
|
|
60
|
-
|
61
|
-
|
62
|
-
CacheAspect.apply A, :method => "test", :ttl => 2 # 2 seconds
|
63
|
-
CacheAspect.apply A, :method => "test2"
|
53
|
+
CacheAspect.apply ExampleClass, method: :test, ttl: 2
|
54
|
+
CacheAspect.apply ExampleClass, method: :test2
|
64
55
|
|
65
|
-
|
56
|
+
instance = ExampleClass.new
|
66
57
|
|
67
58
|
# Will store value in cache
|
68
|
-
|
69
|
-
|
59
|
+
instance.test
|
60
|
+
instance.test2
|
70
61
|
|
71
62
|
# Will get value from cache
|
72
|
-
|
73
|
-
|
63
|
+
instance.test
|
64
|
+
instance.test2
|
74
65
|
|
75
66
|
sleep 3
|
76
67
|
|
77
|
-
|
78
|
-
|
79
|
-
|
68
|
+
instance.test # Cache expired
|
69
|
+
instance.test2 # Cache is still valid
|
@@ -1,73 +1,49 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
class A
|
1
|
+
$LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
|
2
|
+
require 'aspector'
|
4
3
|
|
4
|
+
# Design by contract example class
|
5
|
+
class ExampleClass
|
5
6
|
def initialize
|
6
7
|
@transactions = []
|
7
8
|
@total = 0
|
8
9
|
end
|
9
10
|
|
10
|
-
def buy
|
11
|
+
def buy(price)
|
11
12
|
@transactions << price
|
12
13
|
@total += price
|
13
14
|
end
|
14
15
|
|
15
|
-
def sell
|
16
|
+
def sell(price)
|
16
17
|
@transactions << price # Wrong
|
17
18
|
@total -= price
|
18
19
|
end
|
19
|
-
|
20
20
|
end
|
21
21
|
|
22
|
-
|
23
|
-
|
24
|
-
$LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
|
25
|
-
|
26
|
-
require 'aspector'
|
27
|
-
|
22
|
+
# Object extensions
|
28
23
|
class Object
|
29
|
-
def assert
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
24
|
+
def assert(bool, message = 'Assertion failure')
|
25
|
+
return if bool
|
26
|
+
|
27
|
+
$stderr.puts message
|
28
|
+
$stderr.puts caller
|
34
29
|
end
|
35
30
|
end
|
36
31
|
|
32
|
+
# Aspect that we will apply
|
37
33
|
class ContractExample < Aspector::Base
|
38
|
-
|
39
|
-
before do |price, &block|
|
34
|
+
before do |price, &_block|
|
40
35
|
assert price > 0, "Price is #{price}, should be greater than 0"
|
41
36
|
end
|
42
37
|
|
43
|
-
after :
|
38
|
+
after result_arg: false do |*_, &_block|
|
44
39
|
sum = @transactions.reduce(&:+)
|
45
40
|
assert @total == sum, "Total(#{@total}) and sum of transactions(#{sum}) do not match"
|
46
41
|
end
|
47
|
-
|
48
42
|
end
|
49
43
|
|
50
|
-
|
51
|
-
|
52
|
-
ContractExample.apply A, :methods => %w[buy sell]
|
53
|
-
|
54
|
-
a = A.new
|
55
|
-
a.buy 10
|
56
|
-
a.sell 10
|
57
|
-
a.sell -10
|
58
|
-
|
59
|
-
##############################
|
60
|
-
|
61
|
-
=begin
|
62
|
-
class A
|
63
|
-
include DesignByContract
|
64
|
-
|
65
|
-
precond { |price| assert price < 0, "Price is less than 0" }
|
66
|
-
postcond { }
|
67
|
-
# invariant block will be executed before and after the method
|
68
|
-
invariant { assert @total != @transactions.reduce(&:sum), "Total and sum of transactions do not equal" }
|
69
|
-
def buy price
|
70
|
-
end
|
71
|
-
end
|
72
|
-
=end
|
44
|
+
ContractExample.apply ExampleClass, methods: %w( buy sell )
|
73
45
|
|
46
|
+
instance = ExampleClass.new
|
47
|
+
instance.buy(10)
|
48
|
+
instance.sell(10)
|
49
|
+
instance.sell(-10)
|
@@ -1,36 +1,28 @@
|
|
1
|
-
|
1
|
+
$LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
|
2
|
+
require 'aspector'
|
2
3
|
|
3
|
-
|
4
|
+
# Example class to which we will apply our aspects
|
5
|
+
class ExampleClass
|
6
|
+
def test(input)
|
4
7
|
puts input.upcase
|
5
8
|
end
|
6
|
-
|
7
9
|
end
|
8
10
|
|
9
|
-
|
10
|
-
|
11
|
-
$LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
|
12
|
-
|
13
|
-
require 'aspector'
|
14
|
-
|
11
|
+
# Aspect used to handle exceptions
|
15
12
|
class ExceptionHandler < Aspector::Base
|
16
|
-
|
17
13
|
target do
|
18
|
-
def handle_exception
|
19
|
-
proxy.call
|
14
|
+
def handle_exception(proxy, *args, &block)
|
15
|
+
proxy.call(*args, &block)
|
20
16
|
rescue => e
|
21
17
|
puts "Rescued: #{e}"
|
22
18
|
end
|
23
19
|
end
|
24
20
|
|
25
21
|
around :handle_exception
|
26
|
-
|
27
22
|
end
|
28
23
|
|
29
|
-
|
30
|
-
|
31
|
-
ExceptionHandler.apply A, :method => "test"
|
32
|
-
|
33
|
-
a = A.new
|
34
|
-
a.test 'good'
|
35
|
-
a.test nil
|
24
|
+
ExceptionHandler.apply(ExampleClass, method: :test)
|
36
25
|
|
26
|
+
a = ExampleClass.new
|
27
|
+
a.test('good')
|
28
|
+
a.test(nil)
|
@@ -1,45 +1,37 @@
|
|
1
|
-
|
1
|
+
$LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
|
2
|
+
require 'aspector'
|
2
3
|
|
3
|
-
|
4
|
+
# Example class to which we will apply our aspects
|
5
|
+
class ExampleClass
|
6
|
+
def self.test(input)
|
4
7
|
puts input.upcase
|
5
8
|
end
|
6
9
|
|
7
|
-
def test
|
10
|
+
def test(input)
|
8
11
|
puts input.upcase
|
9
12
|
end
|
10
|
-
|
11
13
|
end
|
12
14
|
|
13
|
-
|
14
|
-
|
15
|
-
$LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
|
16
|
-
|
17
|
-
require 'aspector'
|
18
|
-
|
15
|
+
# Aspect used to handle exceptions
|
19
16
|
class ExceptionHandler < Aspector::Base
|
20
|
-
|
21
17
|
target do
|
22
|
-
def handle_exception
|
23
|
-
proxy.call
|
18
|
+
def handle_exception(proxy, *args, &block)
|
19
|
+
proxy.call(*args, &block)
|
24
20
|
rescue => e
|
25
21
|
puts "Rescued: #{e}"
|
26
22
|
end
|
27
23
|
end
|
28
24
|
|
29
25
|
around :handle_exception
|
30
|
-
|
31
26
|
end
|
32
27
|
|
33
|
-
|
34
|
-
|
35
|
-
ExceptionHandler.apply A, :method => "test", :class_methods => true
|
36
|
-
|
37
|
-
A.test 'good'
|
38
|
-
A.test nil
|
28
|
+
ExceptionHandler.apply(ExampleClass, method: :test, class_methods: true)
|
39
29
|
|
40
|
-
|
30
|
+
ExampleClass.test('good')
|
31
|
+
ExampleClass.test(nil)
|
41
32
|
|
42
|
-
|
43
|
-
a.test 'good'
|
44
|
-
a.test nil
|
33
|
+
ExceptionHandler.apply(ExampleClass, method: :test)
|
45
34
|
|
35
|
+
instance = ExampleClass.new
|
36
|
+
instance.test('good')
|
37
|
+
instance.test(nil)
|
@@ -1,15 +1,14 @@
|
|
1
|
-
|
1
|
+
$LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
|
2
|
+
require 'aspector'
|
3
|
+
|
4
|
+
# Example class to which we will apply our aspects
|
5
|
+
class ExampleClass
|
2
6
|
def test
|
3
7
|
puts 'test'
|
4
8
|
end
|
5
9
|
end
|
6
10
|
|
7
|
-
|
8
|
-
|
9
|
-
$LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
|
10
|
-
|
11
|
-
require 'aspector'
|
12
|
-
|
11
|
+
# Aspect that we want to use
|
13
12
|
class ImplicitMethodOptionTest < Aspector::Base
|
14
13
|
# Apply advice to options[:method] and options[:methods] if no target method is given
|
15
14
|
# before options[:method], options[:methods] do
|
@@ -18,13 +17,6 @@ class ImplicitMethodOptionTest < Aspector::Base
|
|
18
17
|
end
|
19
18
|
end
|
20
19
|
|
21
|
-
ImplicitMethodOptionTest.apply
|
22
|
-
|
23
|
-
##############################
|
24
|
-
|
25
|
-
A.new.test
|
26
|
-
|
27
|
-
# Expected output:
|
28
|
-
# before
|
29
|
-
# test
|
20
|
+
ImplicitMethodOptionTest.apply(ExampleClass, method: :test)
|
30
21
|
|
22
|
+
ExampleClass.new.test
|
@@ -0,0 +1,71 @@
|
|
1
|
+
$LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
|
2
|
+
require 'aspector'
|
3
|
+
|
4
|
+
# This example shows how can we access and use interception options
|
5
|
+
# Interception options are all the options that come from the default
|
6
|
+
# aspect options and directly from aspect applying
|
7
|
+
# Note that if you apply aspect instance to the same elements and
|
8
|
+
# you don't change options - they will be the same for all the classes/instances
|
9
|
+
# to which you apply this aspect. If you change apply parameters - the interception
|
10
|
+
# parameters will differ as well.
|
11
|
+
|
12
|
+
# Example class to which we will apply our aspects
|
13
|
+
class Example1Class
|
14
|
+
def exec
|
15
|
+
puts "#{Example1Class} exec execution"
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
# Example class2 to which we will apply our aspects
|
20
|
+
class Example2Class
|
21
|
+
def exec_different
|
22
|
+
puts "#{Example2Class} exec_different execution"
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
# Aspect used to wrap methods from example classes
|
27
|
+
class ExampleAspect < Aspector::Base
|
28
|
+
default super_option: 60, key_option: 2
|
29
|
+
|
30
|
+
around interception_arg: true, method_arg: true do |interception, method, proxy, &block|
|
31
|
+
# Here we fetch options from both - interception and aspect class
|
32
|
+
# Aspect options are transfered directly to all the instances of an aspect and to all
|
33
|
+
# the interceptions. However they have a lower priority then interception direct options
|
34
|
+
# so they can be overwritten by them
|
35
|
+
method = interception.options[:method]
|
36
|
+
key_option = interception.options[:key_option]
|
37
|
+
super_option = interception.options[:super_option]
|
38
|
+
interception_option = interception.options[:interception_option]
|
39
|
+
|
40
|
+
puts "super_option value: #{super_option}"
|
41
|
+
puts "key_option value: #{key_option}"
|
42
|
+
|
43
|
+
proxy.call(&block)
|
44
|
+
|
45
|
+
puts "interception_option value: #{interception_option}"
|
46
|
+
puts "method value: #{method}"
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
ExampleAspect.apply Example1Class, method: :exec, interception_option: 2
|
51
|
+
ExampleAspect.apply Example2Class, method: :exec_different
|
52
|
+
|
53
|
+
instance1 = Example1Class.new
|
54
|
+
instance2 = Example2Class.new
|
55
|
+
|
56
|
+
instance1.exec
|
57
|
+
instance2.exec_different
|
58
|
+
|
59
|
+
# --- instance1
|
60
|
+
# Expected output
|
61
|
+
# super_option value: 60
|
62
|
+
# key_option value: 2
|
63
|
+
# Example1Class exec execution
|
64
|
+
# interception_option value: 2
|
65
|
+
# method value: exec
|
66
|
+
# --- instance2
|
67
|
+
# super_option value: 60
|
68
|
+
# key_option value: 2
|
69
|
+
# Example2Class exec_different execution
|
70
|
+
# interception_option value:
|
71
|
+
# method value: exec_different
|