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,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