receptacle 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.dir-locals.el +1 -0
- data/.gitignore +11 -0
- data/.rubocop.yml +27 -0
- data/.travis.yml +27 -0
- data/CODE_OF_CONDUCT.md +74 -0
- data/Gemfile +4 -0
- data/Guardfile +30 -0
- data/LICENSE.txt +21 -0
- data/README.md +355 -0
- data/Rakefile +25 -0
- data/bin/console +11 -0
- data/bin/setup +8 -0
- data/examples/simple_repo.rb +103 -0
- data/lib/receptacle/errors.rb +13 -0
- data/lib/receptacle/interface_methods.rb +49 -0
- data/lib/receptacle/method_cache.rb +39 -0
- data/lib/receptacle/method_delegation.rb +114 -0
- data/lib/receptacle/registration.rb +34 -0
- data/lib/receptacle/test_support.rb +47 -0
- data/lib/receptacle/version.rb +4 -0
- data/lib/receptacle.rb +13 -0
- data/performance/benchmark.rb +44 -0
- data/performance/profile.rb +39 -0
- data/performance/speed_receptacle.rb +104 -0
- data/receptacle.gemspec +38 -0
- metadata +223 -0
@@ -0,0 +1,103 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# frozen_string_literal: true
|
3
|
+
require 'bundler/inline'
|
4
|
+
|
5
|
+
gemfile true do
|
6
|
+
source 'https://rubygems.org'
|
7
|
+
gem 'receptacle', '../'
|
8
|
+
gem 'mongo'
|
9
|
+
end
|
10
|
+
|
11
|
+
User = Struct.new(:id, :name)
|
12
|
+
|
13
|
+
# define our Repository
|
14
|
+
module Repository
|
15
|
+
module User
|
16
|
+
include Receptacle::Repo
|
17
|
+
mediate :find
|
18
|
+
mediate :create
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
# we should have a global mongo connection which can be easily reused
|
23
|
+
module Connection
|
24
|
+
class Mongo
|
25
|
+
include Singleton
|
26
|
+
|
27
|
+
def initialize
|
28
|
+
@client = ::Mongo::Client.new
|
29
|
+
end
|
30
|
+
attr_reader :client
|
31
|
+
def self.client
|
32
|
+
instance.client
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
# some strategies
|
38
|
+
module Repository
|
39
|
+
module User
|
40
|
+
module Strategy
|
41
|
+
class Mongo
|
42
|
+
def find(id:)
|
43
|
+
mongo_to_model(collection.find(_id: id))
|
44
|
+
rescue
|
45
|
+
nil
|
46
|
+
end
|
47
|
+
|
48
|
+
def create(name:)
|
49
|
+
ret = collection.insert_one(name: name)
|
50
|
+
find(id: ret['_id']) # TODO: check this
|
51
|
+
end
|
52
|
+
|
53
|
+
private
|
54
|
+
|
55
|
+
def mongo_to_model(doc)
|
56
|
+
::User.new(doc['_id'], doc['name'])
|
57
|
+
end
|
58
|
+
|
59
|
+
def collection
|
60
|
+
Connection::Mongo.client[:users]
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
# in memory is using a simple class instance variable as internal storage
|
65
|
+
|
66
|
+
class InMemory
|
67
|
+
class << self; attr_accessor :store end
|
68
|
+
@store = {}
|
69
|
+
def find(id:)
|
70
|
+
store[id]
|
71
|
+
end
|
72
|
+
|
73
|
+
def create(name:)
|
74
|
+
id = BSON::ObjectId.new
|
75
|
+
store[id] = User.new(id, name)
|
76
|
+
end
|
77
|
+
|
78
|
+
private
|
79
|
+
|
80
|
+
def store
|
81
|
+
self.class.store
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
# configure the repository and use it
|
89
|
+
Repository::User.strategy Repository::User::Strategy::InMemory
|
90
|
+
|
91
|
+
user = Repository::User.create(name: 'foo')
|
92
|
+
p user
|
93
|
+
p Repository::User.find(id: user.id)
|
94
|
+
|
95
|
+
# switching to mongo and we see it's using a different store but keeps the same interface
|
96
|
+
Repository::User.strategy Repository::User::Strategy::Mongo
|
97
|
+
|
98
|
+
p Repository::User.find(id: user.id)
|
99
|
+
#-> nil
|
100
|
+
|
101
|
+
user = Repository::User.create(name: 'foo')
|
102
|
+
p user
|
103
|
+
p Repository::User.find(id: user.id)
|
@@ -0,0 +1,13 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
module Receptacle
|
3
|
+
module Errors
|
4
|
+
class NotConfigured < StandardError
|
5
|
+
attr_reader :repo
|
6
|
+
def initialize(repo:)
|
7
|
+
@repo = repo
|
8
|
+
super("Missing Configuration for repository: <#{repo}>")
|
9
|
+
end
|
10
|
+
end
|
11
|
+
class ReservedMethodName < StandardError; end
|
12
|
+
end
|
13
|
+
end
|
@@ -0,0 +1,49 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
require 'receptacle/registration'
|
3
|
+
require 'receptacle/errors'
|
4
|
+
|
5
|
+
module Receptacle
|
6
|
+
module InterfaceMethods
|
7
|
+
RESERVED_METHOD_NAMES = Set.new(%i(wrappers mediate strategy delegate_to_strategy))
|
8
|
+
private_constant :RESERVED_METHOD_NAMES
|
9
|
+
|
10
|
+
# registers a method_name for the to be mediated or forwarded to the configured strategy
|
11
|
+
#
|
12
|
+
# @param method_name [String] name of method to register
|
13
|
+
def mediate(method_name)
|
14
|
+
raise Errors::ReservedMethodName if RESERVED_METHOD_NAMES.include?(method_name)
|
15
|
+
Registration.repositories[self].methods << method_name
|
16
|
+
end
|
17
|
+
alias delegate_to_strategy mediate
|
18
|
+
|
19
|
+
# get or sets the strategy
|
20
|
+
#
|
21
|
+
# @note will set the strategy for this receptacle if passed in; will only
|
22
|
+
# return the current strategy if nil or no parameter passed include
|
23
|
+
# @param value [Class,nil]
|
24
|
+
# @return [Class] current configured strategy class
|
25
|
+
def strategy(value = nil)
|
26
|
+
if value
|
27
|
+
Registration.repositories[self].strategy = value
|
28
|
+
Registration.clear_method_cache(self)
|
29
|
+
else
|
30
|
+
Registration.repositories[self].strategy
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
# get or sets the wrappers
|
35
|
+
#
|
36
|
+
# @note will set the wrappers for this receptacle if passed in; will only
|
37
|
+
# return the current wrappers if nil or no parameter passed include
|
38
|
+
# @param value [Class,Array(Class),nil] wrappers
|
39
|
+
# @return [Array(Class)] current configured wrappers
|
40
|
+
def wrappers(value = nil)
|
41
|
+
if value
|
42
|
+
Registration.repositories[self].wrappers = Array(value)
|
43
|
+
Registration.clear_method_cache(self)
|
44
|
+
else
|
45
|
+
Registration.repositories[self].wrappers
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
module Receptacle
|
3
|
+
# Cache describing which strategy and wrappers need to be applied for this method
|
4
|
+
# @api private
|
5
|
+
class MethodCache
|
6
|
+
# @return [Symbol] name of the method this cache belongs to
|
7
|
+
attr_reader :method_name
|
8
|
+
# @return [Class] strategy class currently setup
|
9
|
+
attr_reader :strategy
|
10
|
+
# @return [Array(Class)] Array of wrapper classes which implement a wrapper for this method
|
11
|
+
attr_reader :wrappers
|
12
|
+
# @return [Symbol] name of the before action method
|
13
|
+
attr_reader :before_method_name
|
14
|
+
# @return [Symbol] name of the after action method
|
15
|
+
attr_reader :after_method_name
|
16
|
+
|
17
|
+
def initialize(method_name:, strategy:, before_wrappers:, after_wrappers:)
|
18
|
+
@strategy = strategy
|
19
|
+
@before_method_name = :"before_#{method_name}"
|
20
|
+
@after_method_name = :"after_#{method_name}"
|
21
|
+
@method_name = method_name.to_sym
|
22
|
+
before_wrappers ||= []
|
23
|
+
after_wrappers ||= []
|
24
|
+
@wrappers = before_wrappers | after_wrappers
|
25
|
+
@skip_before_wrappers = before_wrappers.empty?
|
26
|
+
@skip_after_wrappers = after_wrappers.empty?
|
27
|
+
end
|
28
|
+
|
29
|
+
# @return [Boolean] true if no before wrappers need to be applied for this method
|
30
|
+
def skip_before_wrappers?
|
31
|
+
@skip_before_wrappers
|
32
|
+
end
|
33
|
+
|
34
|
+
# @return [Boolean] true if no after wrappers need to be applied for this method
|
35
|
+
def skip_after_wrappers?
|
36
|
+
@skip_after_wrappers
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
@@ -0,0 +1,114 @@
|
|
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
|
+
else
|
30
|
+
__define_full_method(method_cache)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
# build method cache for given method name
|
35
|
+
# @param method_name [#to_sym]
|
36
|
+
# @return [MethodCache]
|
37
|
+
def __build_method_call_cache(method_name)
|
38
|
+
config = Registration.repositories[self]
|
39
|
+
before_method_name = :"before_#{method_name}"
|
40
|
+
after_method_name = :"after_#{method_name}"
|
41
|
+
|
42
|
+
raise Errors::NotConfigured, repo: self if config.strategy.nil?
|
43
|
+
MethodCache.new(
|
44
|
+
strategy: config.strategy,
|
45
|
+
before_wrappers: config.wrappers.select { |w| w.method_defined?(before_method_name) },
|
46
|
+
after_wrappers: config.wrappers.select { |w| w.method_defined?(after_method_name) },
|
47
|
+
method_name: method_name
|
48
|
+
)
|
49
|
+
end
|
50
|
+
|
51
|
+
# build lightweight method to mediate method calls to strategy without wrappers
|
52
|
+
# @param method_cache [MethodCache] method_cache of the method to be build
|
53
|
+
# @return [void]
|
54
|
+
def __define_shortcut_method(method_cache)
|
55
|
+
define_singleton_method(method_cache.method_name) do |*args, &inner_block|
|
56
|
+
method_cache.strategy.new.public_send(method_cache.method_name, *args, &inner_block)
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
# build method to mediate method calls to strategy with full wrapper support
|
61
|
+
# @param method_cache [MethodCache] method_cache of the method to be build
|
62
|
+
# @return [void]
|
63
|
+
def __define_full_method(method_cache)
|
64
|
+
define_singleton_method(method_cache.method_name) do |*args, &inner_block|
|
65
|
+
__run_wrappers(method_cache, *args) do |*call_args|
|
66
|
+
method_cache.strategy.new.public_send(method_cache.method_name, *call_args, &inner_block)
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
# runtime method to call before and after wrapper in correct order
|
72
|
+
# @param method_cache [MethodCache] method_cache for the current method
|
73
|
+
# @param input_args input parameter of the repository method call
|
74
|
+
# @return strategy method return value after all wrappers where applied
|
75
|
+
def __run_wrappers(method_cache, input_args)
|
76
|
+
wrappers = method_cache.wrappers.map(&:new)
|
77
|
+
args = if method_cache.skip_before_wrappers?
|
78
|
+
input_args
|
79
|
+
else
|
80
|
+
__run_before_wrappers(wrappers, method_cache.before_method_name, input_args)
|
81
|
+
end
|
82
|
+
ret = yield(args)
|
83
|
+
return ret if method_cache.skip_after_wrappers?
|
84
|
+
__run_after_wrappers(wrappers, method_cache.after_method_name, args, ret)
|
85
|
+
end
|
86
|
+
|
87
|
+
# runtime method to execute all before wrappers
|
88
|
+
# @param wrappers [Array] all wrapper instances to be executed
|
89
|
+
# @param method_name [Symbol] name of method to be executed on wrappers
|
90
|
+
# @param args input args of the repository method
|
91
|
+
# @return processed method args by before wrappers
|
92
|
+
def __run_before_wrappers(wrappers, method_name, args)
|
93
|
+
wrappers.each do |wrapper|
|
94
|
+
next unless wrapper.respond_to?(method_name)
|
95
|
+
args = wrapper.public_send(method_name, args)
|
96
|
+
end
|
97
|
+
args
|
98
|
+
end
|
99
|
+
|
100
|
+
# runtime method to execute all after wrappers
|
101
|
+
# @param wrappers [Array] all wrapper instances to be executed
|
102
|
+
# @param method_name [Symbol] name of method to be executed on wrappers
|
103
|
+
# @param args input args to the strategy method (after processing in before wrappers)
|
104
|
+
# @param return_value return value of strategy method
|
105
|
+
# @return processed return value by all after wrappers
|
106
|
+
def __run_after_wrappers(wrappers, method_name, args, return_value)
|
107
|
+
wrappers.reverse_each do |wrapper|
|
108
|
+
next unless wrapper.respond_to?(method_name)
|
109
|
+
return_value = wrapper.public_send(method_name, return_value, args)
|
110
|
+
end
|
111
|
+
return_value
|
112
|
+
end
|
113
|
+
end
|
114
|
+
end
|
@@ -0,0 +1,34 @@
|
|
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
|
@@ -0,0 +1,47 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
module Receptacle
|
3
|
+
module TestSupport
|
4
|
+
# provides helpful method to toggle strategies during testing
|
5
|
+
#
|
6
|
+
# can be used in rspec like this
|
7
|
+
#
|
8
|
+
# require 'receptacle/test_support'y
|
9
|
+
# RSpec.configure do |c|
|
10
|
+
# c.include Receptacle::TestSupport
|
11
|
+
# end
|
12
|
+
#
|
13
|
+
# RSpec.describe(UserRepository) do
|
14
|
+
# around do |example|
|
15
|
+
# with_strategy(strategy){example.run}
|
16
|
+
# end
|
17
|
+
# let(:strategy) { UserRepository::Strategy::MySQL }
|
18
|
+
# ...
|
19
|
+
# end
|
20
|
+
#
|
21
|
+
# or similar with minitest
|
22
|
+
#
|
23
|
+
# require 'receptacle/test_support'
|
24
|
+
# class UserRepositoryTest < Minitest::Test
|
25
|
+
# def described_class
|
26
|
+
# UserRepository
|
27
|
+
# end
|
28
|
+
# def repo_exists(strategy)
|
29
|
+
# with_strategy(strategy) do
|
30
|
+
# assert described_class.exists(id: 5)
|
31
|
+
# end
|
32
|
+
# end
|
33
|
+
# def test_mysql
|
34
|
+
# repo_exists(UserRepository::Strategy::MySQL)
|
35
|
+
# end
|
36
|
+
# def test_fake
|
37
|
+
# repo_exists(UserRepository::Strategy::Fake)
|
38
|
+
# end
|
39
|
+
# end
|
40
|
+
def with_strategy(strategy, repo = described_class)
|
41
|
+
original_strategy = repo.strategy
|
42
|
+
repo.strategy strategy
|
43
|
+
yield
|
44
|
+
repo.strategy original_strategy
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
data/lib/receptacle.rb
ADDED
@@ -0,0 +1,13 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
require 'receptacle/version'
|
3
|
+
require 'receptacle/interface_methods'
|
4
|
+
require 'receptacle/method_delegation'
|
5
|
+
|
6
|
+
module Receptacle
|
7
|
+
module Repo
|
8
|
+
def self.included(base)
|
9
|
+
base.extend(InterfaceMethods)
|
10
|
+
base.extend(MethodDelegation)
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
@@ -0,0 +1,44 @@
|
|
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
|
@@ -0,0 +1,39 @@
|
|
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'
|
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+'))
|
@@ -0,0 +1,104 @@
|
|
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
|
data/receptacle.gemspec
ADDED
@@ -0,0 +1,38 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
# frozen_string_literal: true
|
3
|
+
lib = File.expand_path('../lib', __FILE__)
|
4
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
5
|
+
require 'receptacle/version'
|
6
|
+
|
7
|
+
Gem::Specification.new do |spec|
|
8
|
+
spec.name = 'receptacle'
|
9
|
+
spec.version = Receptacle::VERSION
|
10
|
+
spec.authors = ['Andreas Eger']
|
11
|
+
spec.email = ['dev@eger-andreas.de']
|
12
|
+
|
13
|
+
spec.summary = 'repository pattern'
|
14
|
+
spec.description = 'provides functionality for the repository or strategy pattern'
|
15
|
+
spec.homepage = 'https://github.com/andreaseger/receptacle'
|
16
|
+
spec.license = 'MIT'
|
17
|
+
|
18
|
+
spec.required_ruby_version = '~> 2.2'
|
19
|
+
|
20
|
+
spec.files = `git ls-files -z`.split("\x0").reject do |f|
|
21
|
+
f.match(%r{^(test|spec|features)/})
|
22
|
+
end
|
23
|
+
spec.bindir = 'exe'
|
24
|
+
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
25
|
+
spec.require_paths = ['lib']
|
26
|
+
|
27
|
+
spec.add_development_dependency 'bundler', '~> 1.13'
|
28
|
+
spec.add_development_dependency 'rake', '~> 10.0'
|
29
|
+
spec.add_development_dependency 'minitest', '~> 5.0'
|
30
|
+
spec.add_development_dependency 'pry'
|
31
|
+
spec.add_development_dependency 'rubocop_runner', '~> 2.0'
|
32
|
+
spec.add_development_dependency 'rubocop', '~> 0.46.0'
|
33
|
+
spec.add_development_dependency 'simplecov', '~> 0.13'
|
34
|
+
spec.add_development_dependency 'codecov'
|
35
|
+
spec.add_development_dependency 'guard'
|
36
|
+
spec.add_development_dependency 'guard-minitest'
|
37
|
+
spec.add_development_dependency 'guard-rubocop'
|
38
|
+
end
|