aspectual 0.1.1 → 0.9.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
- SHA1:
3
- metadata.gz: 6f66580a8019b4635fd8323ea0c6a15307d5ff56
4
- data.tar.gz: 379cbabcd70d9e26ab8dec1128450f3638c963a2
2
+ SHA256:
3
+ metadata.gz: 817c7afcb4818012c3ff63a90b9cd1d98b5c485fc9fe9970cb6b2e44a24328cd
4
+ data.tar.gz: 9bb74cc472397a9c2dbc7c9360bd5e63801ced7f54cc46b241e6911239f924ec
5
5
  SHA512:
6
- metadata.gz: 81e877cabfbcdfec9b810cbb4c1682212cd98e1c5db5a9747cdbae25f36dceed8834a68cc502318e7c71113b51d68eb642b8fdb4b8b4cd4446a762031486f982
7
- data.tar.gz: 5c8384f1dda770edced49d1eb1b8b85aeb7e19b61e178295cc8e68cd4a67673561fe61558a209da206a8ffabe39fe579653b97b3d840e0f78e2bf3410d33938b
6
+ metadata.gz: 601bf77bb2417fd3c2592c1f8774050ed233c35840ba44fcfd763f86446cbb1e9c1dc3e0808debacd505410fc19d10b57b49c81df9a42f6ed1ffec1321dbd38d
7
+ data.tar.gz: 77d715ff59e1b2773d085e5cefee6e5496521414f4a19ae70371dad02f2ba60fc69d7d43bdc4a9009a184cfca8be9f3570578d3e90f84c16fac776eb59f33199
@@ -0,0 +1,37 @@
1
+ # This workflow uses actions that are not certified by GitHub.
2
+ # They are provided by a third-party and are governed by
3
+ # separate terms of service, privacy policy, and support
4
+ # documentation.
5
+ # This workflow will download a prebuilt Ruby version, install dependencies and run tests with Rake
6
+ # For more information see: https://github.com/marketplace/actions/setup-ruby-jruby-and-truffleruby
7
+
8
+ name: Rubocop verification
9
+
10
+ on:
11
+ push:
12
+ branches: [ "main" ]
13
+ pull_request:
14
+ branches: [ "main" ]
15
+
16
+ permissions:
17
+ contents: read
18
+
19
+ jobs:
20
+ lint:
21
+ runs-on: ubuntu-latest
22
+ strategy:
23
+ matrix:
24
+ ruby-version: ['3.1', '3.2', '3.3', '3.4', '4.0']
25
+
26
+ steps:
27
+ - uses: actions/checkout@v4
28
+ - name: Set up Ruby
29
+ # To automatically get bug fixes and new Ruby versions for ruby/setup-ruby,
30
+ # change this to (see https://github.com/ruby/setup-ruby#versioning):
31
+ # uses: ruby/setup-ruby@v1
32
+ uses: ruby/setup-ruby@8e41b362d2589a22a44c1cfa214b3c83052c195b # v1.318.0
33
+ with:
34
+ ruby-version: ${{ matrix.ruby-version }}
35
+ bundler-cache: true # runs 'bundle install' and caches installed gems automatically
36
+ - name: Run rubocop
37
+ run: bundle exec rubocop
@@ -0,0 +1,37 @@
1
+ # This workflow uses actions that are not certified by GitHub.
2
+ # They are provided by a third-party and are governed by
3
+ # separate terms of service, privacy policy, and support
4
+ # documentation.
5
+ # This workflow will download a prebuilt Ruby version, install dependencies and run tests with Rake
6
+ # For more information see: https://github.com/marketplace/actions/setup-ruby-jruby-and-truffleruby
7
+
8
+ name: Ruby version compatibility
9
+
10
+ on:
11
+ push:
12
+ branches: [ "main" ]
13
+ pull_request:
14
+ branches: [ "main" ]
15
+
16
+ permissions:
17
+ contents: read
18
+
19
+ jobs:
20
+ test:
21
+ runs-on: ubuntu-latest
22
+ strategy:
23
+ matrix:
24
+ ruby-version: ['3.1', '3.2', '3.3', '3.4', '4.0']
25
+
26
+ steps:
27
+ - uses: actions/checkout@v4
28
+ - name: Set up Ruby
29
+ # To automatically get bug fixes and new Ruby versions for ruby/setup-ruby,
30
+ # change this to (see https://github.com/ruby/setup-ruby#versioning):
31
+ # uses: ruby/setup-ruby@v1
32
+ uses: ruby/setup-ruby@8e41b362d2589a22a44c1cfa214b3c83052c195b # v1.318.0
33
+ with:
34
+ ruby-version: ${{ matrix.ruby-version }}
35
+ bundler-cache: true # runs 'bundle install' and caches installed gems automatically
36
+ - name: Run tests
37
+ run: bundle exec rspec
data/.rubocop.yml ADDED
@@ -0,0 +1,39 @@
1
+ plugins:
2
+ - rubocop-rspec
3
+ AllCops:
4
+ NewCops: enable
5
+ Metrics/BlockLength:
6
+ Exclude:
7
+ # rspec blocks can get very long
8
+ - 'spec/**/*'
9
+ Metrics/ClassLength:
10
+ Exclude:
11
+ # our spec Classes can get very long
12
+ - 'spec/**/*'
13
+ Metrics/ModuleLength:
14
+ CountAsOne:
15
+ - array
16
+ - hash
17
+ - method_call
18
+ Metrics/MethodLength:
19
+ CountAsOne:
20
+ - array
21
+ - hash
22
+ - method_call
23
+ Naming/VariableNumber:
24
+ EnforcedStyle: snake_case
25
+ RSpec/ExampleLength:
26
+ CountAsOne:
27
+ - array
28
+ - hash
29
+ - method_call
30
+ RSpec/NestedGroups:
31
+ Max: 4
32
+ RSpec/RepeatedExample:
33
+ Enabled: false
34
+ Style/TrailingCommaInArrayLiteral:
35
+ EnforcedStyleForMultiline: consistent_comma
36
+ Style/TrailingCommaInArguments:
37
+ EnforcedStyleForMultiline: consistent_comma
38
+ Style/TrailingCommaInHashLiteral:
39
+ EnforcedStyleForMultiline: consistent_comma
data/Gemfile CHANGED
@@ -1,4 +1,10 @@
1
+ # frozen_string_literal: true
2
+
1
3
  source 'https://rubygems.org'
2
4
 
3
- # Specify your gem's dependencies in overload.gemspec
5
+ gem 'bundler'
6
+ gem 'rspec'
7
+ gem 'rubocop'
8
+ gem 'rubocop-rspec'
9
+
4
10
  gemspec
data/README.md CHANGED
@@ -12,11 +12,11 @@ Add this line to your application's Gemfile:
12
12
 
13
13
  And then execute:
14
14
 
15
- $ bundle
15
+ bundle install
16
16
 
17
17
  Or install it yourself as:
18
18
 
19
- $ gem install aspectual
19
+ gem install aspectual
20
20
 
21
21
  ## Usage
22
22
 
@@ -24,20 +24,80 @@ Extend the module
24
24
 
25
25
  extend Aspectual
26
26
 
27
- Create any methods you want as aspects (Aspectual will not add methods you don't have as aspects)
27
+ ### Implicit method aspects
28
28
 
29
- Then declare your aspects
29
+ Aspectual supports implicitly defining your aspects directly before a method.
30
+ This method of defining aspects will hook into the next defined method,
31
+ assigning those aspects to that method:
30
32
 
31
- aspects before: :logging, after: :more_logging
33
+ aspects before: :logging, around: fancy_logging, after: :more_logging
32
34
  def foo
33
35
  "foo"
34
36
  end
35
37
 
36
- aspects before: [:notify_user, :notify_admin], after: [:remove_temp_files, :play_sound]
38
+ aspects before: [:notify_user, :notify_admin], around: [:setup_and_teardown, :play_background_music], after: [:remove_temp_files, :play_sound]
37
39
  def bar
38
40
  "bar"
39
41
  end
40
42
 
43
+ ### Explicit method aspects
44
+
45
+ Aspectual supports defining your aspects associated with a specific method,
46
+ which will be added either immediately (if the method is defined) or when that
47
+ method is first defined on the associated class.
48
+
49
+ def foo
50
+ "foo"
51
+ end
52
+
53
+ aspects :foo, before: :logging, around: fancy_logging, after: :more_logging
54
+
55
+ aspects :bar, before: [:notify_user, :notify_admin], around: [:setup_and_teardown, :play_background_music], after: [:remove_temp_files, :play_sound]
56
+
57
+ def bar
58
+ "bar"
59
+ end
60
+
61
+ ### Inheriting aspects
62
+
63
+ Aspectual supports defining aspects in a parent class, when those methods will
64
+ be defined in a child class. Aspectual will attach the aspects to the next
65
+ definition of the named method, to support child classes inheriting aspected
66
+ behaviors.
67
+
68
+ class Foo
69
+ aspects :foo, before: :logging, around: fancy_logging, after: :more_logging
70
+ end
71
+
72
+ class Bar < Foo
73
+ def foo
74
+ "foo"
75
+ end
76
+ end
77
+
78
+ This behavior means that defining an aspect on an interface that is implemented
79
+ will require a call to `super` to invoke those aspects:
80
+
81
+ class Foo
82
+ aspects :foo, before: :logging, around: fancy_logging, after: :more_logging
83
+ def foo
84
+ "foo"
85
+ end
86
+ end
87
+
88
+ class Bar < Foo
89
+ def foo
90
+ super + "bar"
91
+ end
92
+ end
93
+
94
+ class Baz < Foo
95
+ # Does not call aspects defined in Foo
96
+ def foo
97
+ "baz"
98
+ end
99
+ end
100
+
41
101
  ## Contributing
42
102
 
43
103
  1. Fork it
data/aspectual.gemspec CHANGED
@@ -1,26 +1,27 @@
1
- # coding: utf-8
2
- lib = File.expand_path('../lib', __FILE__)
1
+ # frozen_string_literal: true
2
+
3
+ require 'English'
4
+ lib = File.expand_path('lib', __dir__)
3
5
  $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
6
  require 'aspectual/version'
5
7
 
6
8
  Gem::Specification.new do |spec|
7
- spec.name = "aspectual"
9
+ spec.name = 'aspectual'
8
10
  spec.version = Aspectual::VERSION
9
- spec.authors = ["Alex Sunderland"]
10
- spec.email = ["agentantelope+aspectual@gmail.com"]
11
- spec.description = %q{
11
+ spec.authors = ['Fell Sunderland']
12
+ spec.email = ['agentantelope+aspectual@gmail.com']
13
+ spec.description = '
12
14
  A simple gem to support minimal Aspect Oriented Programming in ruby.
13
- }
14
- spec.summary = %q{A gem to help with AOP.}
15
- spec.homepage = "https://github.com/AgentAntelope/aspectual"
16
- spec.license = "MIT"
15
+ '
16
+ spec.summary = 'A gem to help with AOP.'
17
+ spec.homepage = 'https://github.com/AgentAntelope/aspectual'
18
+ spec.license = 'MIT'
17
19
 
18
- spec.files = `git ls-files`.split($/).reject {|f| f.end_with?('gem')}
20
+ spec.files = `git ls-files`.split($INPUT_RECORD_SEPARATOR).reject { |f| f.end_with?('gem') }
19
21
  spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
20
- spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
21
- spec.require_paths = ["lib"]
22
+ spec.require_paths = ['lib']
23
+
24
+ spec.required_ruby_version = '>= 3.1'
22
25
 
23
- spec.add_development_dependency "bundler"
24
- spec.add_development_dependency "rake"
25
- spec.add_development_dependency "rspec"
26
+ spec.metadata['rubygems_mfa_required'] = 'true'
26
27
  end
@@ -0,0 +1,121 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Aspectual
4
+ # Used to build up aspect methods to replace the methods Aspectual wraps
5
+ module MethodConstruction
6
+ private
7
+
8
+ def define_aspect_method(method_name:, position:, aspect:)
9
+ # This is to prevent us from looping because we're about to define some
10
+ # methods.
11
+ @_defining_method = true
12
+
13
+ with_method_name = with_aspect_method_name(
14
+ target: method_name, position:, feature: aspect,
15
+ )
16
+
17
+ with_method_proc = build_with_method_proc(aspect:, position:, method_name:)
18
+
19
+ define_method(with_method_name, with_method_proc)
20
+
21
+ scope_method_to_parent(
22
+ scope_method_name: method_name,
23
+ new_method_name: with_method_name,
24
+ )
25
+
26
+ alias_method_chain(target: method_name, feature: aspect, position:)
27
+
28
+ @_defining_method = false
29
+ end
30
+
31
+ def build_with_method_proc(aspect:, position:, method_name:)
32
+ without_method_name = without_aspect_method_name(
33
+ target: method_name, position:, feature: aspect,
34
+ )
35
+
36
+ case position
37
+ when BEFORE_ASPECT
38
+ before_lambda(aspect:, without_method_name:)
39
+ when AROUND_ASPECT
40
+ around_lambda(aspect:, without_method_name:)
41
+ when AFTER_ASPECT
42
+ after_lambda(aspect:, without_method_name:)
43
+ end
44
+ end
45
+
46
+ def before_lambda(aspect:, without_method_name:)
47
+ lambda do |*args, **kwargs, &blk|
48
+ # first, call the aspect
49
+ send(aspect, *args, **kwargs, &blk)
50
+
51
+ # then, call the method without the aspect
52
+ send(without_method_name, *args, **kwargs, &blk)
53
+ end
54
+ end
55
+
56
+ def around_lambda(aspect:, without_method_name:)
57
+ lambda do |*args, **kwargs, &blk|
58
+ send(aspect, *args, **kwargs, &proc do
59
+ send(without_method_name, *args, **kwargs, &blk)
60
+ end)
61
+ end
62
+ end
63
+
64
+ def after_lambda(aspect:, without_method_name:)
65
+ lambda do |*args, **kwargs, &blk|
66
+ # first, call the method without the aspect
67
+ send(without_method_name, *args, **kwargs, &blk)
68
+
69
+ # then, call the aspect
70
+ send(aspect, *args, **kwargs, &blk)
71
+ end
72
+ end
73
+
74
+ def scope_method_to_parent(scope_method_name:, new_method_name:)
75
+ if public_method_defined?(scope_method_name)
76
+ public new_method_name
77
+ elsif protected_method_defined?(scope_method_name)
78
+ protected new_method_name
79
+ elsif private_method_defined?(scope_method_name)
80
+ private new_method_name
81
+ end
82
+ end
83
+
84
+ # adapted from ActiveSupport
85
+ def alias_method_chain(target:, feature:, position:)
86
+ with_method_name = with_aspect_method_name(target:, position:, feature:)
87
+ without_method_name = without_aspect_method_name(target:, position:, feature:)
88
+
89
+ alias_method(without_method_name, target)
90
+ alias_method(target, with_method_name)
91
+
92
+ scope_method_to_parent(
93
+ scope_method_name: without_method_name,
94
+ new_method_name: target,
95
+ )
96
+ end
97
+
98
+ def with_aspect_method_name(target:, position:, feature:)
99
+ target, punctuation = *safe_target(target:)
100
+
101
+ # Produce something like: :target_with_position_feature (with possible
102
+ # punctuation on the end)
103
+ :"#{target}_with_#{position}_#{feature}#{punctuation}"
104
+ end
105
+
106
+ def without_aspect_method_name(target:, position:, feature:)
107
+ target, punctuation = *safe_target(target:)
108
+
109
+ # Produce something like: :target_without_position_feature (with possible
110
+ # punctuation on the end)
111
+ :"#{target}_without_#{position}_#{feature}#{punctuation}"
112
+ end
113
+
114
+ def safe_target(target:)
115
+ # Extract punctuation on methods ending in one of !, ?, or = since e.g.
116
+ # target?_without_position_feature should properly be
117
+ # target_without_position_feature?.
118
+ target.to_s.scan(/(.*)([?!=])?$/).flatten
119
+ end
120
+ end
121
+ end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Aspectual
2
- VERSION = '0.1.1'
4
+ VERSION = '0.9.0'
3
5
  end
data/lib/aspectual.rb CHANGED
@@ -1,85 +1,125 @@
1
1
  #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
2
3
 
4
+ # Aspectual allows Aspect Oriented Programming in Ruby.
3
5
  module Aspectual
4
- def aspects(aspects)
5
- # The before aspect have to be reversed so that when multiple before
6
- # aspects are added to one method, the first declared method will be
7
- # added last.
8
- @_before_aspects = Array(aspects[:before]).reverse
9
- @_around_aspects = Array(aspects[:around]).reverse
10
- @_after_aspects = Array(aspects[:after])
11
- end
12
-
13
- def method_added(method_symbol)
14
- return unless has_aspects_declared?
15
- return if @_defining_method
16
- if method_defined?(method_symbol)
17
-
18
- %w{before around after}.each do |position|
19
- current_aspects = instance_variable_get("@_#{position}_aspects")
20
- if current_aspects
21
- current_aspects.each do |aspect|
22
- define_aspect_method(method_symbol, position, aspect)
23
- end
24
- end
25
- end
6
+ require_relative 'aspectual/method_construction'
7
+
8
+ include MethodConstruction
9
+
10
+ # This order is important to ensure we're assigning aspects in the correct
11
+ # order: Before comes before around, so needs to be added to the method after
12
+ VALID_ASPECTS = [
13
+ AROUND_ASPECT = :around,
14
+ BEFORE_ASPECT = :before,
15
+ AFTER_ASPECT = :after,
16
+ ].freeze
17
+
18
+ BLANK_ASPECT = VALID_ASPECTS.to_h { [_1, [].freeze] }.freeze
19
+
20
+ NEXT_METHOD_ADDED_KEY = :_next_method_added
21
+
22
+ # Special default value for next method added
23
+ DEFAULT_ASPECTS = { NEXT_METHOD_ADDED_KEY => BLANK_ASPECT }.freeze
24
+
25
+ def aspects(*method_names, before: [], around: [], after: [])
26
+ method_names = [NEXT_METHOD_ADDED_KEY] if method_names.empty?
27
+ method_names.each do |method_name|
28
+ merge_aspects(
29
+ method_name:,
30
+ new_aspects: {
31
+ BEFORE_ASPECT => Array(before),
32
+ AROUND_ASPECT => Array(around),
33
+ AFTER_ASPECT => Array(after),
34
+ },
35
+ )
36
+
37
+ # If we already defined the method we're adding aspects for, we need to
38
+ # process that straight away.
39
+ next unless method_defined?(method_name) || private_method_defined?(method_name)
40
+
41
+ add_aspects_to_method(method_name:)
26
42
  end
27
43
  end
28
44
 
29
- private
45
+ def method_added(method_name)
46
+ add_aspects_to_method(method_name:)
30
47
 
31
- def has_aspects_declared?
32
- @_before_aspects || @_after_aspects
48
+ super
33
49
  end
34
50
 
35
- def define_aspect_method(method_symbol, position, aspect)
36
- # This is to prevent us from looping because we're about to define some methods.
37
- @_defining_method = true
38
-
39
- with_aspect_method_name = method_symbol.to_s + "_with_#{position}_" + aspect.to_s
40
- without_aspect_method_name = method_symbol.to_s + "_without_#{position}_" + aspect.to_s
51
+ def defined_aspects
52
+ return @defined_aspects if defined?(@defined_aspects)
41
53
 
42
- case position
43
- when "before"
44
- aspect_proc = lambda {|*args| send(aspect, *args); send(without_aspect_method_name, *args)}
45
- when "around"
46
- aspect_proc = lambda {|*args| send(aspect, *args, &Proc.new{send(without_aspect_method_name, *args)})}
47
- when "after"
48
- aspect_proc = lambda {|*args| send(without_aspect_method_name, *args); send(aspect, *args)}
54
+ aspected_ancestor = ancestors[1..].detect do |klass|
55
+ klass.is_a?(Aspectual)
49
56
  end
50
57
 
51
- define_method(with_aspect_method_name, aspect_proc)
58
+ @defined_aspects = Marshal.load(
59
+ Marshal.dump(
60
+ aspected_ancestor&.defined_aspects || DEFAULT_ASPECTS,
61
+ ),
62
+ )
63
+ end
52
64
 
53
- scope_method_to_parent(method_symbol, with_aspect_method_name)
65
+ private
54
66
 
55
- alias_method_chain(method_symbol, aspect.to_sym, position)
67
+ attr_writer :defined_aspects
56
68
 
57
- @_defining_method = false
69
+ def can_define_aspect?(aspects:, method_name:)
70
+ # If there are no defined aspects we have nothing to do
71
+ aspects.values.any?(&:any?) &&
72
+ # the method we're adding aspects to needs to have been defined
73
+ (method_defined?(method_name) || private_method_defined?(method_name))
58
74
  end
59
75
 
60
- def scope_method_to_parent(without_method, target)
61
- case
62
- when public_method_defined?(without_method)
63
- public target
64
- when protected_method_defined?(without_method)
65
- protected target
66
- when private_method_defined?(without_method)
67
- private target
76
+ def add_aspects_to_method(method_name:)
77
+ return if @_defining_method
78
+
79
+ aspects = calculate_aspects(method_name:)
80
+
81
+ return unless can_define_aspect?(aspects:, method_name:)
82
+
83
+ # Remove the aspects for this method, in case we see multiple definitions
84
+ # of a single method (i.e. inheritance)
85
+ defined_aspects.delete(method_name)
86
+
87
+ aspects.each do |position, positional_aspects|
88
+ positional_aspects.each do |aspect|
89
+ define_aspect_method(method_name:, position:, aspect:)
90
+ end
68
91
  end
69
92
  end
70
93
 
71
- # adapted from active support
72
- def alias_method_chain(target, feature, position)
73
- # Strip out punctuation on predicates or bang methods since
74
- # e.g. target?_without_position_feature is not a valid method name.
75
- aliased_target, punctuation = target.to_s.sub(/([?!=])$/, ''), $1
94
+ def calculate_aspects(method_name:)
95
+ merge_aspects(
96
+ method_name:,
97
+ new_aspects: defined_aspects.fetch(NEXT_METHOD_ADDED_KEY, BLANK_ASPECT),
98
+ )
76
99
 
77
- with_method = "#{aliased_target}_with_#{position}_#{feature}#{punctuation}"
78
- without_method = "#{aliased_target}_without_#{position}_#{feature}#{punctuation}"
100
+ # Blank out the aspects for the next method added
101
+ defined_aspects[NEXT_METHOD_ADDED_KEY] = BLANK_ASPECT
79
102
 
80
- alias_method without_method, target
81
- alias_method target, with_method
103
+ defined_aspects[method_name] || BLANK_ASPECT
104
+ end
82
105
 
83
- scope_method_to_parent(without_method, target)
106
+ def merge_aspects(method_name:, new_aspects:)
107
+ return defined_aspects if new_aspects == BLANK_ASPECT
108
+
109
+ self.defined_aspects = defined_aspects.merge(
110
+ { method_name => new_aspects },
111
+ ) do |_aspected_method_name, old_config, new_config|
112
+ old_config.merge(new_config) do |aspect, old_aspects, new_aspects|
113
+ case aspect
114
+ when BEFORE_ASPECT, AROUND_ASPECT
115
+ # The before and around aspects have to be reversed so that when
116
+ # multiple aspects are added to one method, the first declared method
117
+ # will be added last.
118
+ (new_aspects.reverse + old_aspects).uniq
119
+ else
120
+ (old_aspects + new_aspects).uniq
121
+ end
122
+ end
123
+ end
84
124
  end
85
125
  end
@@ -0,0 +1,74 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative '../../lib/aspectual'
4
+
5
+ class BlockArgsTestClass
6
+ extend Aspectual
7
+
8
+ attr_reader :methods_called
9
+
10
+ def initialize
11
+ # This is to ensure that all methods are properly called within the
12
+ # context of the current instance of this object.
13
+ @methods_called = []
14
+ end
15
+
16
+ def block_aspect_method(&block)
17
+ methods_called << "block_aspect_method_block_result_#{block.call}"
18
+ self
19
+ end
20
+
21
+ def block_around_aspect_method(&block)
22
+ methods_called << 'before_block_block_around_aspect_method_block'
23
+ block.call
24
+ methods_called << 'after_block_block_around_aspect_method_block'
25
+ self
26
+ end
27
+
28
+ aspects before: :block_aspect_method
29
+ def before_block_test_method(&block)
30
+ methods_called << "before_block_test_method_block_result_#{block.call}"
31
+ self
32
+ end
33
+
34
+ aspects around: :block_around_aspect_method
35
+ def around_block_test_method(&block)
36
+ methods_called << "around_block_test_method_block_result_#{block.call}"
37
+ self
38
+ end
39
+
40
+ aspects after: :block_aspect_method
41
+ def after_block_test_method(&block)
42
+ methods_called << "after_block_test_method_block_result_#{block.call}"
43
+ self
44
+ end
45
+ end
46
+
47
+ describe Aspectual do
48
+ describe 'methods with block arguments' do
49
+ it 'can handle before aspects' do
50
+ test_instance = BlockArgsTestClass.new.before_block_test_method { 'called' }
51
+ expect(test_instance.methods_called).to eq(%w[
52
+ block_aspect_method_block_result_called
53
+ before_block_test_method_block_result_called
54
+ ])
55
+ end
56
+
57
+ it 'can handle around aspects' do
58
+ test_instance = BlockArgsTestClass.new.around_block_test_method { 'called' }
59
+ expect(test_instance.methods_called).to eq(%w[
60
+ before_block_block_around_aspect_method_block
61
+ around_block_test_method_block_result_called
62
+ after_block_block_around_aspect_method_block
63
+ ])
64
+ end
65
+
66
+ it 'can handle after aspects' do
67
+ test_instance = BlockArgsTestClass.new.after_block_test_method { 'called' }
68
+ expect(test_instance.methods_called).to eq(%w[
69
+ after_block_test_method_block_result_called
70
+ block_aspect_method_block_result_called
71
+ ])
72
+ end
73
+ end
74
+ end