receptacle 0.3.1 → 2.0.0

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