receptacle 0.3.1 → 2.0.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.
@@ -1,136 +0,0 @@
1
- # frozen_string_literal: true
2
- require 'receptacle/method_cache'
3
- require 'receptacle/registration'
4
- require 'receptacle/errors'
5
-
6
- module Receptacle
7
- # module which enables a repository to mediate methods dynamically to wrappers and strategy
8
- # @api private
9
- module MethodDelegation
10
- # dynamically build mediation method on first invocation if the method is registered
11
- def method_missing(method_name, *arguments, &block)
12
- if Registration.repositories[self].methods.include?(method_name)
13
- public_send(__build_method(method_name), *arguments, &block)
14
- else
15
- super
16
- end
17
- end
18
-
19
- def respond_to_missing?(method_name, include_private = false)
20
- Registration.repositories[self].methods.include?(method_name) || super
21
- end
22
-
23
- # @param method_name [#to_sym]
24
- # @return [void]
25
- def __build_method(method_name)
26
- method_cache = __build_method_call_cache(method_name)
27
- if method_cache.wrappers.nil? || method_cache.wrappers.empty?
28
- __define_shortcut_method(method_cache)
29
- elsif method_cache.arity.abs > 1
30
- __define_full_method_high_arity(method_cache)
31
- else
32
- __define_full_method(method_cache)
33
- end
34
- end
35
-
36
- # build method cache for given method name
37
- # @param method_name [#to_sym]
38
- # @return [MethodCache]
39
- def __build_method_call_cache(method_name)
40
- config = Registration.repositories[self]
41
-
42
- raise Errors::NotConfigured, repo: self if config.strategy.nil?
43
- MethodCache.new(
44
- strategy: config.strategy,
45
- wrappers: config.wrappers,
46
- method_name: method_name
47
- )
48
- end
49
-
50
- # build lightweight method to mediate method calls to strategy without wrappers
51
- # @param method_cache [MethodCache] method_cache of the method to be build
52
- # @return [void]
53
- def __define_shortcut_method(method_cache)
54
- define_singleton_method(method_cache.method_name) do |*args, &inner_block|
55
- method_cache.strategy.new.public_send(method_cache.method_name, *args, &inner_block)
56
- end
57
- end
58
-
59
- # build method to mediate method calls of arity 1 to strategy with full wrapper support
60
- # @param method_cache [MethodCache] method_cache of the method to be build
61
- # @return [void]
62
- def __define_full_method(method_cache)
63
- define_singleton_method(method_cache.method_name) do |*args, &inner_block|
64
- __run_wrappers(method_cache, *args) do |*call_args|
65
- method_cache.strategy.new.public_send(method_cache.method_name, *call_args, &inner_block)
66
- end
67
- end
68
- end
69
-
70
- # build method to mediate method calls of higher arity to strategy with full wrapper support
71
- # @param method_cache [MethodCache] method_cache of the method to be build
72
- # @return [void]
73
- def __define_full_method_high_arity(method_cache)
74
- define_singleton_method(method_cache.method_name) do |*args, &inner_block|
75
- __run_wrappers(method_cache, args, true) do |*call_args|
76
- method_cache.strategy.new.public_send(method_cache.method_name, *call_args, &inner_block)
77
- end
78
- end
79
- end
80
-
81
- # runtime method to call before and after wrapper in correct order
82
- # @param method_cache [MethodCache] method_cache for the current method
83
- # @param input_args input parameter of the repository method call
84
- # @param high_arity [Boolean] if are intended for a higher arity method
85
- # @return strategy method return value after all wrappers where applied
86
- def __run_wrappers(method_cache, input_args, high_arity = false)
87
- wrappers = method_cache.wrappers.map(&:new)
88
- args =
89
- if method_cache.skip_before_wrappers?
90
- input_args
91
- else
92
- __run_before_wrappers(wrappers, method_cache.before_method_name, input_args, high_arity)
93
- end
94
- ret = high_arity ? yield(*args) : yield(args)
95
- return ret if method_cache.skip_after_wrappers?
96
- __run_after_wrappers(wrappers, method_cache.after_method_name, args, ret, high_arity)
97
- end
98
-
99
- # runtime method to execute all before wrappers
100
- # @param wrappers [Array] all wrapper instances to be executed
101
- # @param method_name [Symbol] name of method to be executed on wrappers
102
- # @param args input args of the repository method
103
- # @param high_arity [Boolean] if are intended for a higher arity method
104
- # @return processed method args by before wrappers
105
- def __run_before_wrappers(wrappers, method_name, args, high_arity = false)
106
- wrappers.each do |wrapper|
107
- next unless wrapper.respond_to?(method_name)
108
- args = if high_arity
109
- wrapper.public_send(method_name, *args)
110
- else
111
- wrapper.public_send(method_name, args)
112
- end
113
- end
114
- args
115
- end
116
-
117
- # runtime method to execute all after wrappers
118
- # @param wrappers [Array] all wrapper instances to be executed
119
- # @param method_name [Symbol] name of method to be executed on wrappers
120
- # @param args input args to the strategy method (after processing in before wrappers)
121
- # @param return_value return value of strategy method
122
- # @param high_arity [Boolean] if are intended for a higher arity method
123
- # @return processed return value by all after wrappers
124
- def __run_after_wrappers(wrappers, method_name, args, return_value, high_arity = false)
125
- wrappers.reverse_each do |wrapper|
126
- next unless wrapper.respond_to?(method_name)
127
- return_value = if high_arity
128
- wrapper.public_send(method_name, return_value, *args)
129
- else
130
- wrapper.public_send(method_name, return_value, args)
131
- end
132
- end
133
- return_value
134
- end
135
- end
136
- end
@@ -1,34 +0,0 @@
1
- # frozen_string_literal: true
2
- require 'singleton'
3
- require 'set'
4
- module Receptacle
5
- # keeps global state of repositories, the defined strategy, set wrappers and methods to mediate
6
- class Registration
7
- include Singleton
8
- Tuple = Struct.new(:strategy, :wrappers, :methods)
9
-
10
- attr_reader :repositories
11
-
12
- def initialize
13
- @repositories = Hash.new do |h, k|
14
- h[k] = Tuple.new(nil, [], Set.new)
15
- end
16
- end
17
-
18
- def self.repositories
19
- instance.repositories
20
- end
21
-
22
- # {clear_method_cache} removes dynamically defined methods
23
- # this is needed to make strategy and wrappers changes inside the codebase possible
24
- def self.clear_method_cache(receptacle)
25
- instance.repositories[receptacle].methods.each do |method_name|
26
- begin
27
- receptacle.singleton_class.send(:remove_method, method_name)
28
- rescue NameError
29
- nil
30
- end
31
- end
32
- end
33
- end
34
- end
@@ -1,44 +0,0 @@
1
- # frozen_string_literal: true
2
- require 'bundler/inline'
3
-
4
- gemfile false do
5
- source 'https://rubygems.org'
6
- gem 'benchmark-ips'
7
- gem 'receptacle', path: './..'
8
- end
9
-
10
- require_relative 'speed_receptacle'
11
-
12
- Speed.strategy(Speed::Strategy::One)
13
- Speed.wrappers [Speed::Wrappers::W1,
14
- Speed::Wrappers::W2,
15
- Speed::Wrappers::W3,
16
- Speed::Wrappers::W4,
17
- Speed::Wrappers::W5,
18
- Speed::Wrappers::W6]
19
-
20
- print 'w/ wrappers'
21
- Benchmark.ips do |x|
22
- x.warmup = 10 if RUBY_ENGINE == 'jruby'
23
- x.report('a: 2x around, 1x before, 1x after') { Speed.a(1) }
24
- x.report('b: 1x around, 1x before, 1x after') { Speed.b(1) }
25
- x.report('c: 1x before, 1x after') { Speed.c(1) }
26
- x.report('d: 1x after') { Speed.d(1) }
27
- x.report('e: 1x before') { Speed.e(1) }
28
- x.report('f: 1x around') { Speed.f(1) }
29
- x.report('g: no wrappers') { Speed.g(1) }
30
- end
31
-
32
- Speed.wrappers []
33
- print 'method dispatching w/ wrappers'
34
- Benchmark.ips do |x|
35
- x.warmup = 10 if RUBY_ENGINE == 'jruby'
36
- x.report('via receptacle') { Speed.a(:foo) }
37
- x.report('direct via public_send') { Speed::Strategy::One.new.public_send(:a, :foo) }
38
- x.report('direct via method-method') do
39
- m = Speed::Strategy::One.new.method(:a)
40
- m.call(:foo)
41
- end
42
- x.report('direct method-call') { Speed::Strategy::One.new.a(:foo) }
43
- x.compare!
44
- end
@@ -1,39 +0,0 @@
1
- #!/usr/bin/env ruby
2
- # frozen_string_literal: true
3
- # run with --profile.api in JRUBY_OPTS
4
- require 'bundler/inline'
5
- require 'jruby/profiler'
6
- PROFILE_NAME = 'receptacle'.freeze
7
-
8
- gemfile false do
9
- source 'https://rubygems.org'
10
- gem 'receptacle', path: './..'
11
- end
12
- require_relative 'speed_receptacle'
13
-
14
- Speed.strategy(Speed::Strategy::One)
15
- Speed.wrappers [Speed::Wrappers::W1,
16
- Speed::Wrappers::W2,
17
- Speed::Wrappers::W3,
18
- Speed::Wrappers::W4,
19
- Speed::Wrappers::W5,
20
- Speed::Wrappers::W6]
21
- Speed.a(1)
22
- Speed.b(1)
23
- Speed.c(1)
24
- Speed.d(1)
25
- Speed.e(1)
26
- Speed.f(1)
27
- Speed.g(1)
28
-
29
- GC.disable
30
- profile_data = JRuby::Profiler.profile do
31
- 100_000.times { Speed.a(1) }
32
- end
33
-
34
- profile_printer = JRuby::Profiler::GraphProfilePrinter.new(profile_data)
35
- profile_printer.printProfile(File.open("#{PROFILE_NAME}.graph.profile", 'w+'))
36
- profile_printer.printProfile(STDOUT)
37
-
38
- profile_printer = JRuby::Profiler::FlatProfilePrinter.new(profile_data)
39
- profile_printer.printProfile(File.open("#{PROFILE_NAME}.flat.profile", 'w+'))
@@ -1,104 +0,0 @@
1
- # frozen_string_literal: true
2
- require 'receptacle'
3
- module Speed
4
- include Receptacle::Repo
5
- mediate :a
6
- mediate :b
7
- mediate :c
8
- mediate :d
9
- mediate :e
10
- mediate :f
11
- mediate :g
12
- module Strategy
13
- class One
14
- def a(arg)
15
- arg
16
- end
17
- alias b a
18
- alias c a
19
- alias d a
20
- alias e a
21
- alias f a
22
- alias g a
23
- end
24
- end
25
-
26
- module Wrappers
27
- class W1
28
- def before_a(args)
29
- args
30
- end
31
-
32
- def after_a(return_values, _)
33
- return_values
34
- end
35
-
36
- def before_f(args)
37
- args
38
- end
39
-
40
- def after_f(return_values, _)
41
- return_values
42
- end
43
- end
44
-
45
- class W2
46
- # :a
47
- def before_a(args)
48
- args
49
- end
50
-
51
- def after_a(return_values, _)
52
- return_values
53
- end
54
-
55
- # :b
56
- def before_b(args)
57
- args
58
- end
59
-
60
- def after_b(return_values, _)
61
- return_values
62
- end
63
- end
64
- class W3
65
- def before_a(args)
66
- args
67
- end
68
-
69
- def before_c(args)
70
- args
71
- end
72
- end
73
-
74
- class W4
75
- def after_a(return_values, _)
76
- return_values
77
- end
78
-
79
- def after_d(return_value, _)
80
- return_value
81
- end
82
- end
83
-
84
- class W5
85
- def before_b(args)
86
- args
87
- end
88
-
89
- def after_c(return_value, _)
90
- return_value
91
- end
92
- end
93
-
94
- class W6
95
- def after_b(return_value, _)
96
- return_value
97
- end
98
-
99
- def before_e(args)
100
- args
101
- end
102
- end
103
- end
104
- end