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
@@ -1,63 +1,36 @@
1
1
  module Aspector
2
- class Logger
3
-
2
+ # Default logger for Aspector
3
+ # @note It uses ::Logger features - providing basic logging
4
+ class Logger < ::Logger
4
5
  attr_reader :context
5
- attr_accessor :level
6
6
 
7
- def initialize context
7
+ def initialize(context)
8
+ super(STDOUT)
8
9
  @context = context
9
-
10
- if (level_string = ENV['ASPECTOR_LOG_LEVEL'])
11
- @level = string_to_level(level_string)
12
- else
13
- @level = Logging::DEFAULT_VISIBLE_LEVEL
14
- end
10
+ @level = (ENV['ASPECTOR_LOG_LEVEL'] || ::Logger::ERROR).to_i
15
11
  end
16
12
 
17
- def log level, *args
18
- return if self.level > level
19
-
20
- puts log_prefix(level) << args.join(" | ")
21
- end
22
-
23
- def visible? level
24
- self.level <= level
13
+ %i( debug info warn error fatal ).each do |level|
14
+ define_method level do |*args|
15
+ super(nil) { postfix(*args) }
16
+ end
25
17
  end
26
18
 
27
19
  private
28
20
 
29
- def log_prefix level
30
- s = "#{Time.now} | Aspector | " << level_to_string(level) << " | "
21
+ def postfix(*args)
22
+ msg = []
23
+
31
24
  if context.is_a? Aspector::Base
32
- s << context.class.to_s << " | " << context.target.to_s << " | "
25
+ msg << context.class.to_s
26
+ msg << context.target.to_s
33
27
  else
34
- s << context.to_s << " | "
28
+ msg << context.to_s
35
29
  end
36
- end
37
30
 
38
- def level_to_string level
39
- case level
40
- when Logging::ERROR then "ERROR"
41
- when Logging::WARN then "WARN "
42
- when Logging::INFO then "INFO "
43
- when Logging::DEBUG then "DEBUG"
44
- when Logging::TRACE then "TRACE"
45
- else level.to_s
46
- end
47
- end
31
+ msg += args
48
32
 
49
- def string_to_level level_string
50
- return Logging::DEFAULT_VISIBLE_LEVEL if level_string.nil? or level_string.strip == ''
51
-
52
- case level_string.downcase
53
- when 'error' then Logging::ERROR
54
- when 'warn' then Logging::WARN
55
- when 'info' then Logging::INFO
56
- when 'debug' then Logging::DEBUG
57
- when 'trace' then Logging::TRACE
58
- when 'none' then Logging::NONE
59
- end
33
+ msg.join(' | ')
60
34
  end
61
35
  end
62
36
  end
63
-
@@ -1,38 +1,19 @@
1
1
  module Aspector
2
+ # Class used as a wrapper to get logging instances
2
3
  module Logging
3
- # Log levels
4
- NONE = 10000
5
- ERROR = 50
6
- WARN = 40
7
- INFO = 30
8
- DEBUG = 20
9
- TRACE = 10
10
-
11
- DEFAULT_VISIBLE_LEVEL = INFO
12
-
13
- def self.get_logger context
14
- if logger_class_name = ENV["ASPECTOR_LOGGER"]
15
- begin
16
- logger_class = constanize(logger_class_name)
17
- logger_class.new(context)
18
- rescue => e
19
- $stderr.puts e.message
20
-
21
- Logger.new(context)
22
- end
23
- else
24
- Logger.new(context)
4
+ class << self
5
+ def get_logger(context)
6
+ (deconstantize(ENV['ASPECTOR_LOGGER'] || 'Aspector::Logger')).new(context)
25
7
  end
26
- end
27
8
 
28
- private
9
+ private
29
10
 
30
- def self.constanize class_name
31
- unless /\A(?:::)?([A-Z]\w*(?:::[A-Z]\w*)+)\z/ =~ class_name
32
- raise NameError, "#{class_name} is not a valid constant name!"
11
+ def deconstantize(klass_name)
12
+ Object.const_get(klass_name.to_s)
13
+ rescue NameError
14
+ $stderr.puts "#{klass_name} is not a valid constant name!"
15
+ Aspector::Logger
33
16
  end
34
-
35
- Object.module_eval("::#{$1}", __FILE__, __LINE__)
36
17
  end
37
18
  end
38
19
  end
@@ -1,11 +1,11 @@
1
1
  module Aspector
2
2
  class MethodMatcher
3
- def initialize *match_data
3
+ def initialize(*match_data)
4
4
  @match_data = match_data
5
5
  @match_data.flatten!
6
6
  end
7
7
 
8
- def match? method, aspect = nil
8
+ def match?(method, aspect = nil)
9
9
  @match_data.detect do |item|
10
10
  case item
11
11
  when String
@@ -29,16 +29,15 @@ module Aspector
29
29
  end
30
30
  end
31
31
  end
32
-
33
- def use_deferred_logic? logic
32
+
33
+ def use_deferred_logic?(logic)
34
34
  @match_data.detect do |item|
35
35
  logic == item
36
36
  end
37
37
  end
38
38
 
39
39
  def to_s
40
- @match_data.map {|item| item.inspect }.join ", "
40
+ @match_data.map(&:inspect).join(', ')
41
41
  end
42
42
  end
43
43
  end
44
-
@@ -1,33 +1,25 @@
1
1
  module Aspector
2
2
  module ObjectExtension
3
-
4
3
  private
5
4
 
6
- def aspector *args, &block
5
+ def aspector(*args, &block)
7
6
  options = args.last.is_a?(Hash) ? args.pop : {}
8
7
 
9
8
  aspect = Aspector(options, &block)
10
9
 
11
10
  aspect.apply(self) if self.is_a? Module
12
- args.each {|target| aspect.apply(target) }
11
+ args.each { |target| aspect.apply(target) }
13
12
 
14
13
  aspect
15
14
  end
16
15
 
17
- def Aspector options = {}, &block
16
+ def Aspector(options = {}, &block)
18
17
  klass = Class.new(Aspector::Base)
19
18
  klass.class_eval { default options }
20
- klass.class_eval &block if block_given?
19
+ klass.class_eval(&block) if block_given?
21
20
  klass
22
21
  end
23
-
24
- def returns value = nil
25
- throw :returns, value
26
- end
27
- alias :returns :returns
28
-
29
22
  end
30
23
  end
31
24
 
32
25
  Object.send(:include, Aspector::ObjectExtension)
33
-
@@ -0,0 +1,3 @@
1
+ module Aspector
2
+ VERSION = '0.14.0'
3
+ end
@@ -0,0 +1,59 @@
1
+ # This spec will execute all the examples to check if they are working
2
+ # By default all of them should work just fine
3
+ # Note that it won't check if the aspector works in examples as it should
4
+ # This is covered in unit and functional specs - here we check that all examples run
5
+ # without problems
6
+
7
+ require 'spec_helper'
8
+ require 'stringio'
9
+
10
+ # Kernel
11
+ module Kernel
12
+ # Method used to catch the STDIO from all the examples
13
+ # Examples by default print some stuff to the output - we don't want that
14
+ # in our specs, thats why we catch it
15
+ # We're only interested if all examples work as they should (without any errors)
16
+ def capture_stdout
17
+ out = StringIO.new
18
+ $stdout = out
19
+ yield
20
+ out
21
+ ensure
22
+ $stdout = STDOUT
23
+ end
24
+
25
+ # Same as capture_stdout but to silence stderr
26
+ def capture_stderr
27
+ out = StringIO.new
28
+ $stderr = out
29
+ yield
30
+ out
31
+ ensure
32
+ $stderr = STDOUT
33
+ end
34
+ end
35
+
36
+ RSpec.describe 'Aspector examples' do
37
+ Dir.glob(
38
+ File.join(
39
+ File.dirname(__FILE__),
40
+ '..',
41
+ 'examples/*.rb'
42
+ )
43
+ ).each do |example_file|
44
+ context "examples file: #{example_file}" do
45
+ it 'should run without any errors' do
46
+ capture_stdout do
47
+ capture_stderr do
48
+ Process.fork do
49
+ require example_file
50
+ end
51
+ end
52
+ end
53
+
54
+ _pid, status = Process.wait2
55
+ expect(status.exitstatus).to eq 0
56
+ end
57
+ end
58
+ end
59
+ end
@@ -0,0 +1,54 @@
1
+ require 'spec_helper'
2
+
3
+ RSpec.describe Aspector do
4
+ let(:klass1) do
5
+ ClassBuilder.build do
6
+ def exec1
7
+ values << 'exec1'
8
+ end
9
+ end
10
+ end
11
+
12
+ let(:klass2) do
13
+ ClassBuilder.build do
14
+ def exec2
15
+ values << 'exec2'
16
+ end
17
+ end
18
+ end
19
+
20
+ let(:instance1) { klass1.new }
21
+ let(:instance2) { klass2.new }
22
+
23
+ let(:aspect_klass) do
24
+ ClassBuilder.inherit(Aspector::Base) do
25
+ attr_accessor :call_count
26
+
27
+ def initialize
28
+ @call_count = 0
29
+ end
30
+
31
+ before interception_arg: true do |interception|
32
+ interception.aspect.call_count += 1
33
+ end
34
+ end
35
+ end
36
+
37
+ let(:aspect) { aspect_klass.new }
38
+
39
+ context 'binding single aspect to multiple different targets' do
40
+ before do
41
+ aspect.apply klass1, method: :exec1
42
+ aspect.apply klass2, methods: %w( exec2 )
43
+ end
44
+
45
+ it 'should work' do
46
+ instance1.exec1
47
+ instance2.exec2
48
+
49
+ expect(aspect.call_count).to eq 2
50
+ expect(instance1.values).to eq %w( exec1 )
51
+ expect(instance2.values).to eq %w( exec2 )
52
+ end
53
+ end
54
+ end
@@ -0,0 +1,112 @@
1
+ require 'spec_helper'
2
+
3
+ RSpec.describe 'Aspector options access' do
4
+ subject { klass.new }
5
+ let(:interception_options) { { rand => rand } }
6
+
7
+ context 'accessing interception options' do
8
+ let(:klass) do
9
+ ClassBuilder.build do
10
+ attr_accessor :interception_options
11
+ end
12
+ end
13
+
14
+ before do
15
+ aspect.apply klass, interception_options.merge(method: :exec)
16
+ end
17
+
18
+ context 'before aspect' do
19
+ let(:aspect) do
20
+ ClassBuilder.inherit(Aspector::Base) do
21
+ default aspect_field: 60
22
+
23
+ before interception_arg: true do |interception|
24
+ self.interception_options = interception.options
25
+ end
26
+ end
27
+ end
28
+
29
+ it 'should be able to reach interception options that were merged with aspect options' do
30
+ subject.exec
31
+ expected = interception_options.merge(method: :exec).merge(aspect_field: 60)
32
+ expect(subject.interception_options).to eq expected
33
+ end
34
+
35
+ it 'still should exec original method' do
36
+ subject.exec
37
+ expect(subject.values).to eq %w( exec-result )
38
+ end
39
+ end
40
+
41
+ context 'before_filter aspect' do
42
+ let(:aspect) do
43
+ ClassBuilder.inherit(Aspector::Base) do
44
+ default aspect_field: 60
45
+
46
+ before_filter interception_arg: true do |interception|
47
+ self.interception_options = interception.options
48
+ end
49
+ end
50
+ end
51
+
52
+ it 'should be able to reach interception options that were merged with aspect options' do
53
+ subject.exec
54
+ expected = interception_options.merge(method: :exec).merge(aspect_field: 60)
55
+ expect(subject.interception_options).to eq expected
56
+ end
57
+
58
+ it 'still should exec original method' do
59
+ subject.exec
60
+ expect(subject.values).to eq %w( exec-result )
61
+ end
62
+ end
63
+
64
+ context 'around aspect' do
65
+ let(:aspect) do
66
+ ClassBuilder.inherit(Aspector::Base) do
67
+ default aspect_field: 60
68
+
69
+ around interception_arg: true do |interception, proxy, &block|
70
+ self.interception_options = interception.options
71
+ proxy.call(&block)
72
+ end
73
+ end
74
+ end
75
+
76
+ it 'should be able to reach interception options that were merged with aspect options' do
77
+ subject.exec
78
+ expected = interception_options.merge(method: :exec).merge(aspect_field: 60)
79
+ expect(subject.interception_options).to eq expected
80
+ end
81
+
82
+ it 'still should exec original method' do
83
+ subject.exec
84
+ expect(subject.values).to eq %w( exec-result )
85
+ end
86
+ end
87
+
88
+ context 'after aspect' do
89
+ let(:aspect) do
90
+ ClassBuilder.inherit(Aspector::Base) do
91
+ default aspect_field: 60
92
+
93
+ after interception_arg: true do |interception, result|
94
+ self.interception_options = interception.options
95
+ result
96
+ end
97
+ end
98
+ end
99
+
100
+ it 'should be able to reach interception options that were merged with aspect options' do
101
+ subject.exec
102
+ expected = interception_options.merge(method: :exec).merge(aspect_field: 60)
103
+ expect(subject.interception_options).to eq expected
104
+ end
105
+
106
+ it 'still should exec original method' do
107
+ subject.exec
108
+ expect(subject.values).to eq %w( exec-result )
109
+ end
110
+ end
111
+ end
112
+ end
@@ -0,0 +1,159 @@
1
+ require 'spec_helper'
2
+
3
+ RSpec.describe 'Aspector for a class' do
4
+ context 'public class methods' do
5
+ subject do
6
+ ClassBuilder.build do
7
+ class << self
8
+ def values
9
+ @values ||= []
10
+ end
11
+
12
+ def exec
13
+ values << 'class-exec-result'
14
+ end
15
+ end
16
+ end
17
+ end
18
+
19
+ context 'before aspect' do
20
+ before do
21
+ aspector(subject, class_methods: true) do
22
+ before :exec do
23
+ values << 'class-exec-before'
24
+ end
25
+ end
26
+ end
27
+
28
+ it 'should work' do
29
+ subject.exec
30
+ expect(subject.values).to eq %w( class-exec-before class-exec-result )
31
+ end
32
+ end
33
+
34
+ context 'around aspect' do
35
+ before do
36
+ aspector(subject, class_methods: true) do
37
+ around :exec do |proxy, &block|
38
+ values << 'class-exec-around-before'
39
+ result = proxy.call(&block)
40
+ values << 'class-exec-around-after'
41
+ result
42
+ end
43
+ end
44
+ end
45
+
46
+ it 'should work' do
47
+ expected = %w( class-exec-around-before class-exec-result class-exec-around-after )
48
+ subject.exec
49
+ expect(subject.values).to eq expected
50
+ end
51
+ end
52
+
53
+ context 'after aspect' do
54
+ before do
55
+ aspector(subject, class_methods: true) do
56
+ after :exec do |result|
57
+ values << 'class-exec-after'
58
+ result
59
+ end
60
+ end
61
+ end
62
+
63
+ it 'should work' do
64
+ subject.exec
65
+ expect(subject.values).to eq %w( class-exec-result class-exec-after )
66
+ end
67
+ end
68
+ end
69
+
70
+ context 'private class methods' do
71
+ subject do
72
+ ClassBuilder.build do
73
+ class << self
74
+ def values
75
+ @values ||= []
76
+ end
77
+
78
+ private
79
+
80
+ def exec
81
+ values << 'class-exec-result'
82
+ end
83
+ end
84
+ end
85
+ end
86
+
87
+ context 'before aspect' do
88
+ before do
89
+ aspector(subject, class_methods: true) do
90
+ before :exec do
91
+ values << 'class-exec-before(public_methods_only)'
92
+ end
93
+ end
94
+
95
+ aspector(subject, class_methods: true, private_methods: true) do
96
+ before :exec do
97
+ values << 'class-exec-before'
98
+ end
99
+ end
100
+ end
101
+
102
+ it 'should work' do
103
+ subject.send(:exec)
104
+ expect(subject.values).to eq %w( class-exec-before class-exec-result )
105
+ end
106
+ end
107
+
108
+ context 'around aspect' do
109
+ before do
110
+ aspector(subject, class_methods: true) do
111
+ around :exec do |proxy, &block|
112
+ values << 'class-exec-around-before(public_methods_only)'
113
+ result = proxy.call(&block)
114
+ values << 'class-exec-around-after(public_methods_only)'
115
+ result
116
+ end
117
+ end
118
+
119
+ aspector(subject, class_methods: true, private_methods: true) do
120
+ around :exec do |proxy, &block|
121
+ values << 'class-exec-around-before'
122
+ result = proxy.call(&block)
123
+ values << 'class-exec-around-after'
124
+ result
125
+ end
126
+ end
127
+ end
128
+
129
+ it 'should work' do
130
+ expected = %w( class-exec-around-before class-exec-result class-exec-around-after )
131
+ subject.send(:exec)
132
+ expect(subject.values).to eq expected
133
+ end
134
+ end
135
+
136
+ context 'after aspect' do
137
+ before do
138
+ aspector(subject, class_methods: true) do
139
+ after :exec do |result|
140
+ values << 'class-exec-after(public_methods_only)'
141
+ result
142
+ end
143
+ end
144
+
145
+ aspector(subject, class_methods: true, private_methods: true) do
146
+ after :exec do |result|
147
+ values << 'class-exec-after'
148
+ result
149
+ end
150
+ end
151
+ end
152
+
153
+ it 'should work' do
154
+ subject.send(:exec)
155
+ expect(subject.values).to eq %w( class-exec-result class-exec-after )
156
+ end
157
+ end
158
+ end
159
+ end