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
@@ -0,0 +1,66 @@
1
+ require 'spec_helper'
2
+
3
+ RSpec.describe 'Advices on private methods' do
4
+ subject { klass.new }
5
+
6
+ let(:klass) do
7
+ ClassBuilder.build
8
+ end
9
+
10
+ let(:instance1) { klass.new }
11
+ let(:instance2) { klass.new }
12
+
13
+ context 'before aspect' do
14
+ before do
15
+ aspector(instance1) do
16
+ before :exec do
17
+ values << 'exec-before'
18
+ end
19
+ end
20
+ end
21
+
22
+ it 'should bind only to one instance to which we want to bind' do
23
+ instance1.exec
24
+ instance2.exec
25
+ expect(instance1.values).to eq %w( exec-before exec-result )
26
+ expect(instance2.values).to eq %w( exec-result )
27
+ end
28
+ end
29
+
30
+ context 'around aspect' do
31
+ before do
32
+ aspector(instance1) do
33
+ around :exec do |proxy, &block|
34
+ values << 'exec-around-before'
35
+ result = proxy.call(&block)
36
+ values << 'exec-around-after'
37
+ result
38
+ end
39
+ end
40
+ end
41
+
42
+ it 'should bind only to one instance to which we want to bind' do
43
+ instance1.exec
44
+ instance2.exec
45
+ expect(instance1.values).to eq %w( exec-around-before exec-result exec-around-after )
46
+ expect(instance2.values).to eq %w( exec-result )
47
+ end
48
+ end
49
+
50
+ context 'after aspect' do
51
+ before do
52
+ aspector(instance1) do
53
+ after :exec do |_result|
54
+ values << 'exec-after'
55
+ end
56
+ end
57
+ end
58
+
59
+ it 'should bind only the aspect that binds to private methods' do
60
+ instance1.exec
61
+ instance2.exec
62
+ expect(instance1.values).to eq %w( exec-result exec-after )
63
+ expect(instance2.values).to eq %w( exec-result )
64
+ end
65
+ end
66
+ end
@@ -0,0 +1,138 @@
1
+ require 'spec_helper'
2
+
3
+ RSpec.describe Aspector do
4
+ let(:klass) { ClassBuilder.build }
5
+ subject { klass.new }
6
+
7
+ context 'binding multiple aspects in a single place' do
8
+ before do
9
+ aspector(klass) do
10
+ before(:exec) { values << 'first-aspect' }
11
+ end
12
+
13
+ aspector(klass) do
14
+ before(:exec) { values << 'second-aspect' }
15
+ end
16
+ end
17
+
18
+ it 'should work' do
19
+ subject.exec
20
+ expect(subject.values).to eq %w( second-aspect first-aspect exec-result )
21
+ end
22
+ end
23
+
24
+ context 'when we try to treat Aspect as a regular class' do
25
+ let(:aspect_klass) do
26
+ ClassBuilder.inherit(Aspector::Base) do
27
+ before(:exec) { values << 'before-exec' }
28
+ end
29
+ end
30
+
31
+ before do
32
+ aspect_klass.apply(klass)
33
+ end
34
+
35
+ it 'should work' do
36
+ subject.exec
37
+ expect(subject.values).to eq %w( before-exec exec-result )
38
+ end
39
+ end
40
+
41
+ context 'when we want to apply aspect multiple times' do
42
+ let(:aspect_klass) do
43
+ Aspector do
44
+ before(:exec) { values << 'before-exec' }
45
+ end
46
+ end
47
+
48
+ before do
49
+ aspect_klass.apply(klass)
50
+ aspect_klass.apply(klass)
51
+ end
52
+
53
+ it 'should apply aspect multiple times' do
54
+ subject.exec
55
+ expect(subject.values).to eq %w( before-exec before-exec exec-result )
56
+ end
57
+ end
58
+
59
+ context 'when we set new_methods_only to true' do
60
+ let(:aspect_klass) do
61
+ Aspector do
62
+ before(:exec) { values << 'before-exec' }
63
+ end
64
+ end
65
+
66
+ before do
67
+ aspect_klass.apply(klass, new_methods_only: true)
68
+ end
69
+
70
+ context 'and we try to apply it to already existing method' do
71
+ it 'should not apply to existing methods' do
72
+ subject.exec
73
+ expect(subject.values).to eq %w( exec-result )
74
+ end
75
+ end
76
+
77
+ context 'and we try to apply it to a new method' do
78
+ let(:klass) do
79
+ ClassBuilder.raw do
80
+ def values
81
+ @values ||= []
82
+ end
83
+ end
84
+ end
85
+
86
+ before do
87
+ klass.send :define_method, :exec do
88
+ values << 'exec-result'
89
+ end
90
+ end
91
+
92
+ it 'should apply to new methods' do
93
+ subject.exec
94
+ expect(subject.values).to eq %w( before-exec exec-result )
95
+ end
96
+ end
97
+ end
98
+
99
+ context 'when we set existing_methods_only to true' do
100
+ let(:aspect_klass) do
101
+ Aspector do
102
+ before(:exec) { values << 'before-exec' }
103
+ end
104
+ end
105
+
106
+ before do
107
+ aspect_klass.apply(klass, existing_methods_only: true)
108
+ end
109
+
110
+ context 'and we try to apply it to already existing method' do
111
+ it 'should apply to existing methods' do
112
+ subject.exec
113
+ expect(subject.values).to eq %w( before-exec exec-result )
114
+ end
115
+ end
116
+
117
+ context 'and we try to apply it to a new method' do
118
+ let(:klass) do
119
+ ClassBuilder.raw do
120
+ def values
121
+ @values ||= []
122
+ end
123
+ end
124
+ end
125
+
126
+ before do
127
+ klass.send :define_method, :exec do
128
+ values << 'exec-result'
129
+ end
130
+ end
131
+
132
+ it 'should not apply to new methods' do
133
+ subject.exec
134
+ expect(subject.values).to eq %w( exec-result )
135
+ end
136
+ end
137
+ end
138
+ end
@@ -0,0 +1,37 @@
1
+ require 'spec_helper'
2
+
3
+ RSpec.describe 'Aspects combined' do
4
+ let(:klass) { ClassBuilder.build }
5
+ subject { klass.new }
6
+
7
+ context 'when we want to combine multiple different aspects' do
8
+ before do
9
+ aspector(klass) do
10
+ before :exec do
11
+ values << 'exec-before'
12
+ end
13
+
14
+ after :exec do |result|
15
+ values << 'exec-after'
16
+ result
17
+ end
18
+
19
+ around :exec do |proxy, &block|
20
+ values << 'exec-around-before'
21
+ result = proxy.call(&block)
22
+ values << 'exec-around-after'
23
+ result
24
+ end
25
+ end
26
+ end
27
+
28
+ it 'should work' do
29
+ expected = %w(
30
+ exec-before exec-around-before exec-result exec-around-after exec-after
31
+ )
32
+
33
+ subject.exec
34
+ expect(subject.values).to eq expected
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,61 @@
1
+ require 'spec_helper'
2
+
3
+ RSpec.describe 'Aspect execution order' do
4
+ let(:klass) { ClassBuilder.build }
5
+ subject { klass.new }
6
+
7
+ context 'when we apply aspects in certain order' do
8
+ before do
9
+ aspector(klass) do
10
+ before :exec do
11
+ values << 'exec-before1'
12
+ end
13
+
14
+ after :exec do |result|
15
+ values << 'exec-after1'
16
+ result
17
+ end
18
+
19
+ around :exec do |proxy, &block|
20
+ values << 'exec-around-before1'
21
+ result = proxy.call(&block)
22
+ values << 'exec-around-after1'
23
+ result
24
+ end
25
+
26
+ before :exec do
27
+ values << 'exec-before2'
28
+ end
29
+
30
+ after :exec do |result|
31
+ values << 'exec-after2'
32
+ result
33
+ end
34
+
35
+ around :exec do |proxy, &block|
36
+ values << 'exec-around-before2'
37
+ result = proxy.call(&block)
38
+ values << 'exec-around-after2'
39
+ result
40
+ end
41
+ end
42
+ end
43
+
44
+ it 'should use this order' do
45
+ expected = %w(
46
+ exec-before1
47
+ exec-before2
48
+ exec-around-before1
49
+ exec-around-before2
50
+ exec-result
51
+ exec-around-after2
52
+ exec-around-after1
53
+ exec-after1
54
+ exec-after2
55
+ )
56
+
57
+ subject.exec
58
+ expect(subject.values).to eq expected
59
+ end
60
+ end
61
+ end
@@ -0,0 +1,82 @@
1
+ require 'spec_helper'
2
+
3
+ RSpec.describe 'Aspects on private methods' do
4
+ subject { klass.new }
5
+
6
+ let(:klass) do
7
+ ClassBuilder.build do
8
+ private :exec
9
+ end
10
+ end
11
+
12
+ context 'before aspect' do
13
+ before do
14
+ aspector(klass) do
15
+ before :exec do
16
+ values << 'exec-before(public_methods_only)'
17
+ end
18
+ end
19
+
20
+ aspector(klass, private_methods: true) do
21
+ before :exec do
22
+ values << 'exec-before'
23
+ end
24
+ end
25
+ end
26
+
27
+ it 'should bind only the aspect that binds to private methods' do
28
+ subject.send :exec
29
+ expect(subject.values).to eq %w( exec-before exec-result )
30
+ end
31
+ end
32
+
33
+ context 'around aspect' do
34
+ before do
35
+ aspector(klass) do
36
+ around :exec do |proxy, &block|
37
+ values << 'exec-around-before(public_methods_only)'
38
+ result = proxy.call(&block)
39
+ values << 'exec-around-after(public_methods_only)'
40
+ result
41
+ end
42
+ end
43
+
44
+ aspector(klass, private_methods: true) do
45
+ around :exec do |proxy, &block|
46
+ values << 'exec-around-before'
47
+ result = proxy.call(&block)
48
+ values << 'exec-around-after'
49
+ result
50
+ end
51
+ end
52
+ end
53
+
54
+ it 'should bind only the aspect that binds to private methods' do
55
+ subject.send :exec
56
+ expect(subject.values).to eq %w( exec-around-before exec-result exec-around-after )
57
+ end
58
+ end
59
+
60
+ context 'after aspect' do
61
+ before do
62
+ aspector(klass) do
63
+ after :exec do |result|
64
+ values << 'exec-after(public_methods_only)'
65
+ result
66
+ end
67
+ end
68
+
69
+ aspector(klass, private_methods: true) do
70
+ after :exec do |result|
71
+ values << 'exec-after'
72
+ result
73
+ end
74
+ end
75
+ end
76
+
77
+ it 'should bind only the aspect that binds to private methods' do
78
+ subject.send :exec
79
+ expect(subject.values).to eq %w( exec-result exec-after )
80
+ end
81
+ end
82
+ end
@@ -1,31 +1,30 @@
1
- ENV["ASPECTOR_LOG_LEVEL"] ||= "warn"
2
-
3
- $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
4
- $LOAD_PATH.unshift(File.dirname(__FILE__))
5
1
  require 'rubygems'
6
2
  require 'rspec'
7
- require 'rspec/autorun'
8
- require 'aspector'
3
+ require 'simplecov'
4
+ require 'pry'
9
5
 
10
- # Requires supporting files with custom matchers and macros, etc,
11
- # in ./support/ and its subdirectories.
12
- Dir["#{File.dirname(__FILE__)}/support/**/*.rb"].each {|f| require f}
6
+ $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
7
+ $LOAD_PATH.unshift(File.dirname(__FILE__))
13
8
 
14
- RSpec.configure do |config|
9
+ # Don't include unnecessary stuff into rcov
10
+ SimpleCov.start do
11
+ add_filter '/vendor/'
12
+ add_filter '/gems/'
13
+ add_filter '/.bundle/'
14
+ add_filter '/spec/'
15
+ merge_timeout 600
15
16
  end
16
17
 
17
- def create_test_class &block
18
- klass = Class.new do
19
- def value
20
- @value ||= []
21
- end
18
+ Dir["#{File.dirname(__FILE__)}/support/**/*.rb"].each { |f| require f }
22
19
 
23
- def test
24
- value << "test"
25
- end
26
- end
20
+ RSpec.configure do |config|
21
+ config.disable_monkey_patching!
27
22
 
28
- klass.class_eval &block if block_given?
29
- klass
23
+ config.expect_with :rspec do |expectations|
24
+ expectations.include_chain_clauses_in_custom_matcher_descriptions = true
25
+ end
30
26
  end
31
27
 
28
+ require 'aspector'
29
+
30
+ ENV['ASPECTOR_LOG_LEVEL'] ||= ::Logger::WARN.to_s
@@ -0,0 +1,44 @@
1
+ # rubocop:disable NestedMethodDefinition
2
+ # Class builder helps creating anonymous classes that we can use to spec our aspector
3
+ # We need co create new class instances to have an "empty" and "clear" class for each spec
4
+ # This module acts as an interface to create classes
5
+ module ClassBuilder
6
+ class << self
7
+ # Builds a new anonymous class with a predefined methods
8
+ # @param block [Proc, nil] block that should be evaluated (if given)
9
+ # @return [Class] created anonymous class
10
+ def build(&block)
11
+ klass = raw do
12
+ def values
13
+ @values ||= []
14
+ end
15
+
16
+ # This is a method that we use as a place to bind to
17
+ # @note This method accepts dummy not used parameter but we keep it
18
+ # so we can exec with parameter if we need
19
+ def exec(_param = nil)
20
+ values << 'exec-result'
21
+ end
22
+ end
23
+
24
+ klass.class_eval(&block) if block_given?
25
+ klass
26
+ end
27
+
28
+ # Creates an empty class without any predefined methods
29
+ # @param block [Proc, nil] block that should be evaluated (if given)
30
+ # @return [Class] created anonymous class
31
+ def raw(&block)
32
+ Class.new(&block)
33
+ end
34
+
35
+ # This method allows us to create a class that inherits from any other
36
+ # @param klass [Class] any class from which we want to inherit in our anonymous class
37
+ # @param block [Proc] a block of code that should be evaluated in a new anonymous class body
38
+ # @return [Class] new anonymous class
39
+ def inherit(klass, &block)
40
+ Class.new(klass, &block)
41
+ end
42
+ end
43
+ end
44
+ # rubocop:enable NestedMethodDefinition