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.
- checksums.yaml +5 -5
- data/.github/workflows/matrix.yml +32 -0
- data/.gitignore +1 -1
- data/.rubocop.yml +6 -8
- data/CHANGELOG.md +31 -19
- data/Gemfile +2 -7
- data/Gemfile.lock +92 -0
- data/LICENSE.txt +1 -1
- data/README.md +22 -52
- data/Rakefile +8 -18
- data/bin/console +3 -3
- data/examples/simple_repo.rb +17 -15
- data/lib/receptacle/errors.rb +2 -1
- data/lib/receptacle/repo.rb +51 -0
- data/lib/receptacle/test_support.rb +2 -17
- data/lib/receptacle/version.rb +2 -1
- data/lib/receptacle.rb +3 -9
- data/receptacle.gemspec +31 -26
- data/upgrade_notes.md +45 -0
- metadata +55 -70
- data/.dir-locals.el +0 -1
- data/.travis.yml +0 -36
- data/Dangerfile +0 -26
- data/Guardfile +0 -31
- data/lib/receptacle/interface_methods.rb +0 -49
- data/lib/receptacle/method_cache.rb +0 -42
- data/lib/receptacle/method_delegation.rb +0 -136
- data/lib/receptacle/registration.rb +0 -34
- data/performance/benchmark.rb +0 -44
- data/performance/profile.rb +0 -39
- data/performance/speed_receptacle.rb +0 -104
@@ -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
|
data/performance/benchmark.rb
DELETED
@@ -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
|
data/performance/profile.rb
DELETED
@@ -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
|