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.
Files changed (102) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +14 -0
  3. data/.rubocop.yml +26 -0
  4. data/.ruby-gemset +1 -0
  5. data/.ruby-version +1 -0
  6. data/.travis.yml +8 -11
  7. data/Changelog.md +59 -0
  8. data/Gemfile +9 -14
  9. data/Gemfile.lock +84 -50
  10. data/README.md +118 -0
  11. data/Rakefile +6 -22
  12. data/aspector.gemspec +15 -127
  13. data/benchmarks/after_benchmark.rb +28 -0
  14. data/benchmarks/around_advice_benchmark.rb +35 -0
  15. data/benchmarks/around_benchmark.rb +32 -0
  16. data/benchmarks/before_benchmark.rb +28 -0
  17. data/benchmarks/benchmark_helper.rb +17 -0
  18. data/benchmarks/combined_benchmark.rb +36 -0
  19. data/benchmarks/method_invocation_benchmark.rb +30 -0
  20. data/benchmarks/raw_benchmark.rb +39 -0
  21. data/examples/activerecord_hooks.rb +10 -15
  22. data/examples/around_example.rb +20 -31
  23. data/examples/aspector_apply_example.rb +10 -17
  24. data/examples/aspector_example.rb +7 -16
  25. data/examples/cache_aspect.rb +20 -30
  26. data/examples/design_by_contract.rb +20 -44
  27. data/examples/exception_handler.rb +12 -20
  28. data/examples/exception_handler2.rb +16 -24
  29. data/examples/implicit_method_option_test.rb +8 -16
  30. data/examples/interception_options_example.rb +71 -0
  31. data/examples/logging_aspect.rb +16 -24
  32. data/examples/process_aspector.rb +13 -0
  33. data/examples/retry_aspect.rb +20 -20
  34. data/lib/aspector.rb +17 -15
  35. data/lib/aspector/advice.rb +44 -57
  36. data/lib/aspector/advice_metadata.rb +10 -11
  37. data/lib/aspector/aspect_instances.rb +2 -3
  38. data/lib/aspector/base.rb +6 -368
  39. data/lib/aspector/base_class_methods.rb +24 -55
  40. data/lib/aspector/deferred_logic.rb +3 -4
  41. data/lib/aspector/deferred_option.rb +5 -10
  42. data/lib/aspector/interception.rb +356 -0
  43. data/lib/aspector/logger.rb +18 -45
  44. data/lib/aspector/logging.rb +10 -29
  45. data/lib/aspector/method_matcher.rb +5 -6
  46. data/lib/aspector/object_extension.rb +4 -12
  47. data/lib/aspector/version.rb +3 -0
  48. data/spec/examples_spec.rb +59 -0
  49. data/spec/functionals/aspect_for_multiple_targets_spec.rb +54 -0
  50. data/spec/functionals/aspect_interception_options_accessing_spec.rb +112 -0
  51. data/spec/functionals/aspect_on_a_class_spec.rb +159 -0
  52. data/spec/functionals/aspect_on_an_instance_spec.rb +66 -0
  53. data/spec/functionals/aspector_spec.rb +138 -0
  54. data/spec/functionals/aspects_combined_spec.rb +37 -0
  55. data/spec/functionals/aspects_execution_order_spec.rb +61 -0
  56. data/spec/functionals/aspects_on_private_methods_spec.rb +82 -0
  57. data/spec/spec_helper.rb +20 -21
  58. data/spec/support/class_builder.rb +44 -0
  59. data/spec/units/advice_spec.rb +49 -0
  60. data/spec/units/advices/after_spec.rb +328 -0
  61. data/spec/units/advices/around_spec.rb +336 -0
  62. data/spec/units/advices/before_filter_spec.rb +287 -0
  63. data/spec/units/advices/before_spec.rb +237 -0
  64. data/spec/units/advices/raw_spec.rb +67 -0
  65. data/spec/units/base_class_methods_spec.rb +262 -0
  66. data/spec/units/base_spec.rb +133 -0
  67. data/spec/units/deferred_logic_spec.rb +35 -0
  68. data/spec/units/logger_spec.rb +20 -0
  69. data/spec/units/logging_spec.rb +85 -0
  70. data/spec/units/method_matcher_spec.rb +95 -0
  71. data/spec/units/object_extension_spec.rb +11 -0
  72. data/spec/units/special_chars_spec.rb +128 -0
  73. metadata +98 -246
  74. data/.document +0 -5
  75. data/.rvmrc +0 -8
  76. data/README.rdoc +0 -80
  77. data/VERSION +0 -1
  78. data/performance-tests/after_test.rb +0 -25
  79. data/performance-tests/around_advice_benchmark.rb +0 -66
  80. data/performance-tests/around_test.rb +0 -27
  81. data/performance-tests/before_test.rb +0 -25
  82. data/performance-tests/combined_test.rb +0 -33
  83. data/performance-tests/method_invocation_test.rb +0 -25
  84. data/performance-tests/raw_test.rb +0 -37
  85. data/performance-tests/test_helper.rb +0 -9
  86. data/run_all_examples.sh +0 -12
  87. data/spec/functional/advices_on_private_methods_spec.rb +0 -21
  88. data/spec/functional/aspect_on_eigen_class_spec.rb +0 -72
  89. data/spec/functional/aspect_on_object_spec.rb +0 -20
  90. data/spec/functional/aspector_spec.rb +0 -140
  91. data/spec/functional/aspects_combined_spec.rb +0 -48
  92. data/spec/functional/execution_order_spec.rb +0 -42
  93. data/spec/unit/advice_spec.rb +0 -4
  94. data/spec/unit/after_spec.rb +0 -88
  95. data/spec/unit/around_spec.rb +0 -76
  96. data/spec/unit/base_class_methods_spec.rb +0 -28
  97. data/spec/unit/base_spec.rb +0 -112
  98. data/spec/unit/before_spec.rb +0 -125
  99. data/spec/unit/deferred_logic_spec.rb +0 -23
  100. data/spec/unit/method_matcher_spec.rb +0 -43
  101. data/spec/unit/raw_spec.rb +0 -53
  102. data/spec/unit/special_chars_spec.rb +0 -122
@@ -1,15 +1,13 @@
1
- class A
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
- aspect.apply(A)
30
-
31
- A.new.test
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
- class A
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
@@ -1,5 +1,8 @@
1
- class A
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 key, ttl
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 :ttl => 60
41
+ default ttl: 60
48
42
 
49
- around :name => 'cache', :aspect_arg => true, :method_arg => true do |aspect, method, proxy, &block|
43
+ around interception_arg: true, method_arg: true do |interception, method, proxy, &block|
50
44
  key = method
51
- ttl = aspect.options[:ttl]
45
+ ttl = interception.options[:ttl]
52
46
 
53
47
  SimpleCache.cache key, ttl do
54
- proxy.call &block
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
- a = A.new
56
+ instance = ExampleClass.new
66
57
 
67
58
  # Will store value in cache
68
- a.test
69
- a.test2
59
+ instance.test
60
+ instance.test2
70
61
 
71
62
  # Will get value from cache
72
- a.test
73
- a.test2
63
+ instance.test
64
+ instance.test2
74
65
 
75
66
  sleep 3
76
67
 
77
- a.test # Cache expired
78
- a.test2 # Cache is still valid
79
-
68
+ instance.test # Cache expired
69
+ instance.test2 # Cache is still valid
@@ -1,73 +1,49 @@
1
- # Design by contract example
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 price
11
+ def buy(price)
11
12
  @transactions << price
12
13
  @total += price
13
14
  end
14
15
 
15
- def sell price
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 bool, message = 'Assertion failure'
30
- unless bool
31
- $stderr.puts message
32
- $stderr.puts caller
33
- end
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 :result_arg => false do |*_, &block|
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
- class A
1
+ $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
2
+ require 'aspector'
2
3
 
3
- def test input
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 proxy, *args, &block
19
- proxy.call *args, &block
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
- class A
1
+ $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
2
+ require 'aspector'
2
3
 
3
- def self.test input
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 input
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 proxy, *args, &block
23
- proxy.call *args, &block
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
- ExceptionHandler.apply A, :method => "test"
30
+ ExampleClass.test('good')
31
+ ExampleClass.test(nil)
41
32
 
42
- a = A.new
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
- class A
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 A, :method => "test"
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