aspector 0.13.1 → 0.14.0

Sign up to get free protection for your applications and to get access to all the features.
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