lightio 0.3.2 → 0.4.0.pre
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 +4 -4
- data/Gemfile.lock +1 -1
- data/README.md +5 -3
- data/Rakefile +9 -1
- data/examples/echo_server.rb +2 -0
- data/examples/echo_server_with_raw_socket.rb +2 -1
- data/examples/monkey_patch.rb +26 -0
- data/lib/lightio.rb +3 -0
- data/lib/lightio/core/beam.rb +1 -1
- data/lib/lightio/core/ioloop.rb +11 -5
- data/lib/lightio/library.rb +2 -0
- data/lib/lightio/library/base.rb +96 -0
- data/lib/lightio/library/file.rb +9 -0
- data/lib/lightio/library/io.rb +14 -85
- data/lib/lightio/library/queue.rb +4 -1
- data/lib/lightio/library/sized_queue.rb +3 -0
- data/lib/lightio/library/socket.rb +93 -127
- data/lib/lightio/library/thread.rb +153 -122
- data/lib/lightio/library/threads_wait.rb +7 -8
- data/lib/lightio/module.rb +11 -0
- data/lib/lightio/module/base.rb +40 -0
- data/lib/lightio/module/file.rb +10 -0
- data/lib/lightio/module/io.rb +88 -0
- data/lib/lightio/module/socket.rb +165 -0
- data/lib/lightio/module/thread.rb +76 -0
- data/lib/lightio/module/threads_wait.rb +13 -0
- data/lib/lightio/monkey.rb +150 -0
- data/lib/lightio/raw_proxy.rb +24 -0
- data/lib/lightio/version.rb +1 -1
- data/lib/lightio/wrap.rb +16 -63
- data/lightio.gemspec +2 -0
- metadata +17 -6
- data/lib/lightio/library/mutex.rb +0 -93
@@ -5,6 +5,11 @@ module LightIO::Library
|
|
5
5
|
ErrNoWaitingThread = ::ThreadsWait::ErrNoWaitingThread
|
6
6
|
ErrNoFinishedThread = ::ThreadsWait::ErrNoFinishedThread
|
7
7
|
|
8
|
+
extend Base::MockMethods
|
9
|
+
mock ::ThreadsWait
|
10
|
+
|
11
|
+
extend LightIO::Module::ThreadsWait::ClassMethods
|
12
|
+
|
8
13
|
attr_reader :threads
|
9
14
|
|
10
15
|
def initialize(*threads)
|
@@ -36,7 +41,7 @@ module LightIO::Library
|
|
36
41
|
end
|
37
42
|
|
38
43
|
def next_wait(nonblock=nil)
|
39
|
-
raise ThreadsWait::ErrNoWaitingThread, 'No threads for waiting.' if empty?
|
44
|
+
raise ::ThreadsWait::ErrNoWaitingThread, 'No threads for waiting.' if empty?
|
40
45
|
@threads.each do |thr|
|
41
46
|
if thr.alive? && nonblock
|
42
47
|
next
|
@@ -47,13 +52,7 @@ module LightIO::Library
|
|
47
52
|
@threads.delete(thr)
|
48
53
|
return thr
|
49
54
|
end
|
50
|
-
raise ThreadsWait::ErrNoFinishedThread, 'No finished threads.'
|
51
|
-
end
|
52
|
-
|
53
|
-
class << self
|
54
|
-
def all_waits(*threads, &blk)
|
55
|
-
new(*threads).all_waits(&blk)
|
56
|
-
end
|
55
|
+
raise ::ThreadsWait::ErrNoFinishedThread, 'No finished threads.'
|
57
56
|
end
|
58
57
|
end
|
59
58
|
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
module LightIO::Module
|
2
|
+
module Base
|
3
|
+
module NewHelper
|
4
|
+
protected
|
5
|
+
def define_new_for_modules(*mods)
|
6
|
+
mods.each {|mod| define_new_for_module(mod)}
|
7
|
+
end
|
8
|
+
|
9
|
+
def define_new_for_module(mod)
|
10
|
+
LightIO::Module.send(:module_eval, <<-STR, __FILE__, __LINE__ + 1)
|
11
|
+
module #{mod}
|
12
|
+
module ClassMethods
|
13
|
+
def new(*args, &blk)
|
14
|
+
obj = LightIO::Library::#{mod}.__send__ :allocate
|
15
|
+
obj.__send__ :initialize, *args, &blk
|
16
|
+
obj
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
STR
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
module Helper
|
25
|
+
protected
|
26
|
+
def wrap_to_library(obj)
|
27
|
+
return _wrap(obj) if self.respond_to?(:_wrap)
|
28
|
+
find_library_class._wrap(obj)
|
29
|
+
end
|
30
|
+
|
31
|
+
# private
|
32
|
+
def find_library_class
|
33
|
+
name = self.name
|
34
|
+
namespace_index = name.rindex("::")
|
35
|
+
class_name = namespace_index.nil? ? name : name[(namespace_index + 2)..-1]
|
36
|
+
LightIO::Library.const_get(class_name)
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
@@ -0,0 +1,88 @@
|
|
1
|
+
module LightIO::Module
|
2
|
+
extend Base::NewHelper
|
3
|
+
|
4
|
+
define_new_for_module "IO"
|
5
|
+
|
6
|
+
module IO
|
7
|
+
include LightIO::Module::Base
|
8
|
+
|
9
|
+
class << self
|
10
|
+
# helper methods
|
11
|
+
def convert_to_io(io)
|
12
|
+
unless io.respond_to?(:to_io)
|
13
|
+
raise TypeError, "no implicit conversion of #{io.class} into IO"
|
14
|
+
end
|
15
|
+
to_io = io.to_io
|
16
|
+
unless to_io.is_a?(LightIO::Library::IO)
|
17
|
+
raise TypeError, "can't process raw IO, use LightIO::IO._wrap(obj) to wrap it" if to_io.is_a?(::IO)
|
18
|
+
raise TypeError, "can't convert #{io.class} to IO (#{io.class}#to_io gives #{to_io.class})"
|
19
|
+
end
|
20
|
+
to_io
|
21
|
+
end
|
22
|
+
|
23
|
+
def get_io_watcher(io)
|
24
|
+
unless io.is_a?(LightIO::Library::IO)
|
25
|
+
io = convert_to_io(io)
|
26
|
+
end
|
27
|
+
io.__send__(:io_watcher)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
module ClassMethods
|
32
|
+
include LightIO::Module::Base::Helper
|
33
|
+
|
34
|
+
def open(*args)
|
35
|
+
io = self.new(*args)
|
36
|
+
return io unless block_given?
|
37
|
+
begin
|
38
|
+
yield io
|
39
|
+
ensure
|
40
|
+
io.close if io.respond_to? :close
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
def pipe(*args)
|
45
|
+
r, w = origin_pipe(*args)
|
46
|
+
if block_given?
|
47
|
+
begin
|
48
|
+
return yield r, w
|
49
|
+
ensure
|
50
|
+
w.close
|
51
|
+
r.close
|
52
|
+
end
|
53
|
+
end
|
54
|
+
# [r, w]
|
55
|
+
[wrap_to_library(r), wrap_to_library(w)]
|
56
|
+
end
|
57
|
+
|
58
|
+
def select(read_fds, write_fds=nil, _except_fds=nil, timeout=nil)
|
59
|
+
timer = timeout && Time.now
|
60
|
+
read_fds ||= []
|
61
|
+
write_fds ||= []
|
62
|
+
loop do
|
63
|
+
# make sure io registered, then clear io watcher status
|
64
|
+
read_fds.each {|fd| LightIO::Module::IO.get_io_watcher(fd).tap {|io| io.readable?; io.clear_status}}
|
65
|
+
write_fds.each {|fd| LightIO::Module::IO.get_io_watcher(fd).tap {|io| io.writable?; io.clear_status}}
|
66
|
+
# run ioloop once
|
67
|
+
LightIO.sleep 0
|
68
|
+
r_fds = read_fds.select {|fd|
|
69
|
+
io = LightIO::Module::IO.convert_to_io(fd)
|
70
|
+
io.closed? ? raise(IOError, 'closed stream') : LightIO::Module::IO.get_io_watcher(io).readable?
|
71
|
+
}
|
72
|
+
w_fds = write_fds.select {|fd|
|
73
|
+
io = LightIO::Module::IO.convert_to_io(fd)
|
74
|
+
io.closed? ? raise(IOError, 'closed stream') : LightIO::Module::IO.get_io_watcher(io).writable?
|
75
|
+
}
|
76
|
+
e_fds = []
|
77
|
+
if r_fds.empty? && w_fds.empty?
|
78
|
+
if timeout && Time.now - timer > timeout
|
79
|
+
return nil
|
80
|
+
end
|
81
|
+
else
|
82
|
+
return [r_fds, w_fds, e_fds]
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
@@ -0,0 +1,165 @@
|
|
1
|
+
require 'socket'
|
2
|
+
|
3
|
+
module LightIO::Module
|
4
|
+
extend Base::NewHelper
|
5
|
+
|
6
|
+
define_new_for_modules *%w{Addrinfo Socket IPSocket TCPSocket TCPServer UDPSocket UNIXSocket UNIXServer}
|
7
|
+
|
8
|
+
module Addrinfo
|
9
|
+
include LightIO::Module::Base
|
10
|
+
|
11
|
+
module WrapperHelper
|
12
|
+
protected
|
13
|
+
include LightIO::Module::Base::Helper
|
14
|
+
|
15
|
+
def wrap_socket_method(method)
|
16
|
+
define_method method do |*args|
|
17
|
+
socket = wrap_to_library(@obj.send(method, *args))
|
18
|
+
if block_given?
|
19
|
+
begin
|
20
|
+
yield socket
|
21
|
+
ensure
|
22
|
+
socket.close
|
23
|
+
end
|
24
|
+
else
|
25
|
+
socket
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
def wrap_socket_methods(*methods)
|
31
|
+
methods.each {|m| wrap_socket_method(m)}
|
32
|
+
end
|
33
|
+
|
34
|
+
def wrap_addrinfo_return_method(method)
|
35
|
+
define_method method do |*args|
|
36
|
+
result = @obj.send(method, *args)
|
37
|
+
if result.is_a?(::Addrinfo)
|
38
|
+
wrap_to_library(result)
|
39
|
+
elsif result.respond_to?(:map)
|
40
|
+
result.map {|r| wrap_to_library(r)}
|
41
|
+
else
|
42
|
+
result
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
def wrap_addrinfo_return_methods(*methods)
|
48
|
+
methods.each {|m| wrap_addrinfo_return_method(m)}
|
49
|
+
end
|
50
|
+
|
51
|
+
def wrap_class_addrinfo_return_method(method)
|
52
|
+
define_method method do |*args|
|
53
|
+
result = __send__(:"origin_#{method}", *args)
|
54
|
+
if result.is_a?(::Addrinfo)
|
55
|
+
wrap_to_library(result)
|
56
|
+
elsif result.respond_to?(:map)
|
57
|
+
result.map {|r| wrap_to_library(r)}
|
58
|
+
else
|
59
|
+
result
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
def wrap_class_addrinfo_return_methods(*methods)
|
65
|
+
methods.each {|m| wrap_class_addrinfo_return_method(m)}
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
module ClassMethods
|
70
|
+
extend WrapperHelper
|
71
|
+
|
72
|
+
def foreach(*args, &block)
|
73
|
+
Addrinfo.getaddrinfo(*args).each(&block)
|
74
|
+
end
|
75
|
+
|
76
|
+
wrap_class_addrinfo_return_methods :getaddrinfo, :ip, :udp, :tcp, :unix
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
module BasicSocket
|
81
|
+
include LightIO::Module::Base
|
82
|
+
|
83
|
+
module ClassMethods
|
84
|
+
include LightIO::Module::Base::Helper
|
85
|
+
|
86
|
+
def for_fd(fd)
|
87
|
+
wrap_to_library(origin_for_fd(fd))
|
88
|
+
end
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
module Socket
|
93
|
+
include LightIO::Module::Base
|
94
|
+
|
95
|
+
module ClassMethods
|
96
|
+
include LightIO::Module::Base::Helper
|
97
|
+
extend LightIO::Wrap::Wrapper::HelperMethods
|
98
|
+
## implement ::Socket class methods
|
99
|
+
wrap_methods_run_in_threads_pool :getaddrinfo, :gethostbyaddr, :gethostbyname, :gethostname,
|
100
|
+
:getnameinfo, :getservbyname
|
101
|
+
|
102
|
+
def getifaddrs
|
103
|
+
origin_getifaddrs.map {|ifaddr| LightIO::Library::Socket::Ifaddr._wrap(ifaddr)}
|
104
|
+
end
|
105
|
+
|
106
|
+
def socketpair(domain, type, protocol)
|
107
|
+
origin_socketpair(domain, type, protocol).map {|s| wrap_to_library(s)}
|
108
|
+
end
|
109
|
+
|
110
|
+
alias_method :pair, :socketpair
|
111
|
+
|
112
|
+
def unix_server_socket(path)
|
113
|
+
if block_given?
|
114
|
+
origin_unix_server_socket(path) {|s| yield wrap_to_library(s)}
|
115
|
+
else
|
116
|
+
wrap_to_library(origin_unix_server_socket(path))
|
117
|
+
end
|
118
|
+
end
|
119
|
+
|
120
|
+
def ip_sockets_port0(ai_list, reuseaddr)
|
121
|
+
origin_ip_sockets_port0(ai_list, reuseaddr).map {|s| wrap_to_library(s)}
|
122
|
+
end
|
123
|
+
end
|
124
|
+
end
|
125
|
+
|
126
|
+
|
127
|
+
module IPSocket
|
128
|
+
include LightIO::Module::Base
|
129
|
+
|
130
|
+
module ClassMethods
|
131
|
+
extend LightIO::Wrap::Wrapper::HelperMethods
|
132
|
+
wrap_methods_run_in_threads_pool :getaddress
|
133
|
+
end
|
134
|
+
end
|
135
|
+
|
136
|
+
module TCPSocket
|
137
|
+
include LightIO::Module::Base
|
138
|
+
end
|
139
|
+
|
140
|
+
module TCPServer
|
141
|
+
include LightIO::Module::Base
|
142
|
+
end
|
143
|
+
|
144
|
+
module UDPSocket
|
145
|
+
include LightIO::Module::Base
|
146
|
+
end
|
147
|
+
|
148
|
+
module UNIXSocket
|
149
|
+
include LightIO::Module::Base
|
150
|
+
|
151
|
+
module ClassMethods
|
152
|
+
include LightIO::Module::Base::Helper
|
153
|
+
|
154
|
+
def socketpair(*args)
|
155
|
+
origin_socketpair(*args).map {|io| wrap_to_library(io)}
|
156
|
+
end
|
157
|
+
|
158
|
+
alias_method :pair, :socketpair
|
159
|
+
end
|
160
|
+
end
|
161
|
+
|
162
|
+
module UNIXServer
|
163
|
+
include LightIO::Module::Base
|
164
|
+
end
|
165
|
+
end
|
@@ -0,0 +1,76 @@
|
|
1
|
+
require 'thread'
|
2
|
+
|
3
|
+
module LightIO::Module
|
4
|
+
extend Base::NewHelper
|
5
|
+
|
6
|
+
define_new_for_modules *%w{ThreadGroup Mutex Queue SizedQueue ConditionVariable}
|
7
|
+
|
8
|
+
module Thread
|
9
|
+
include LightIO::Module::Base
|
10
|
+
|
11
|
+
module ClassMethods
|
12
|
+
extend Forwardable
|
13
|
+
|
14
|
+
def new(*args, &blk)
|
15
|
+
obj = LightIO::Library::Thread.__send__ :allocate
|
16
|
+
obj.__send__ :initialize, *args, &blk
|
17
|
+
obj
|
18
|
+
end
|
19
|
+
|
20
|
+
def fork(*args, &blk)
|
21
|
+
obj = LightIO::Library::Thread.__send__ :allocate
|
22
|
+
obj.send(:init_core, *args, &blk)
|
23
|
+
obj
|
24
|
+
end
|
25
|
+
|
26
|
+
alias start fork
|
27
|
+
|
28
|
+
def kill(thr)
|
29
|
+
thr.kill
|
30
|
+
end
|
31
|
+
|
32
|
+
def current
|
33
|
+
return main if LightIO::Core::LightFiber.is_root?(Fiber.current)
|
34
|
+
LightIO::Library::Thread.instance_variable_get(:@current_thread) || origin_current
|
35
|
+
end
|
36
|
+
|
37
|
+
def exclusive(&blk)
|
38
|
+
LightIO::Library::Thread.__send__(:thread_mutex).synchronize(&blk)
|
39
|
+
end
|
40
|
+
|
41
|
+
def list
|
42
|
+
thread_list = []
|
43
|
+
LightIO::Library::Thread.__send__(:threads).keys.each {|id|
|
44
|
+
begin
|
45
|
+
thr = ObjectSpace._id2ref(id)
|
46
|
+
unless thr.alive?
|
47
|
+
# manually remove thr from threads
|
48
|
+
thr.kill
|
49
|
+
next
|
50
|
+
end
|
51
|
+
thread_list << thr
|
52
|
+
rescue RangeError
|
53
|
+
# mean object is recycled
|
54
|
+
# just wait ruby GC call finalizer to remove it from threads
|
55
|
+
next
|
56
|
+
end
|
57
|
+
}
|
58
|
+
thread_list
|
59
|
+
end
|
60
|
+
|
61
|
+
def pass
|
62
|
+
LightIO::Beam.pass
|
63
|
+
end
|
64
|
+
|
65
|
+
alias stop pass
|
66
|
+
|
67
|
+
def finalizer(object_id)
|
68
|
+
proc {LightIO::Library::Thread.__send__(:threads).delete(object_id)}
|
69
|
+
end
|
70
|
+
|
71
|
+
def main
|
72
|
+
origin_main
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
@@ -0,0 +1,150 @@
|
|
1
|
+
module LightIO
|
2
|
+
module Monkey
|
3
|
+
class PatchError < StandardError
|
4
|
+
end
|
5
|
+
|
6
|
+
IO_PATCH_CONSTANTS = %w{IO File Socket Socket::Ifaddr TCPServer TCPSocket BasicSocket Addrinfo IPSocket UDPSocket UNIXSocket UNIXServer}.freeze
|
7
|
+
THREAD_PATCH_CONSTANTS = %w{Thread ThreadGroup Queue SizedQueue ConditionVariable Mutex ThreadsWait}.freeze
|
8
|
+
|
9
|
+
@patched
|
10
|
+
|
11
|
+
class << self
|
12
|
+
def patch_all!
|
13
|
+
patch_thread!
|
14
|
+
patch_io!
|
15
|
+
patch_kernel!
|
16
|
+
end
|
17
|
+
|
18
|
+
def unpatch_all!
|
19
|
+
unpatch_thread!
|
20
|
+
unpatch_io!
|
21
|
+
unpatch_kernel!
|
22
|
+
end
|
23
|
+
|
24
|
+
def patched?(obj)
|
25
|
+
patched.key?(obj) && !patched[obj]&.empty?
|
26
|
+
end
|
27
|
+
|
28
|
+
def patch_thread!
|
29
|
+
require 'thread'
|
30
|
+
THREAD_PATCH_CONSTANTS.each {|klass_name| patch!(klass_name)}
|
31
|
+
patch_method!(Timeout, :timeout, LightIO::Timeout.method(:timeout))
|
32
|
+
end
|
33
|
+
|
34
|
+
def unpatch_thread!
|
35
|
+
require 'thread'
|
36
|
+
THREAD_PATCH_CONSTANTS.each {|klass_name| unpatch!(klass_name)}
|
37
|
+
unpatch_method!(Timeout, :timeout)
|
38
|
+
end
|
39
|
+
|
40
|
+
def patch_io!
|
41
|
+
require 'socket'
|
42
|
+
IO_PATCH_CONSTANTS.each {|klass_name| patch!(klass_name)}
|
43
|
+
end
|
44
|
+
|
45
|
+
def unpatch_io!
|
46
|
+
require 'socket'
|
47
|
+
IO_PATCH_CONSTANTS.each {|klass_name| unpatch!(klass_name)}
|
48
|
+
end
|
49
|
+
|
50
|
+
def patch_kernel!
|
51
|
+
patch_method!(Kernel, :sleep, LightIO.method(:sleep))
|
52
|
+
patch_method!(Kernel, :select, LightIO::Library::IO.method(:select))
|
53
|
+
patch_instance_method!(Kernel, :sleep, LightIO.method(:sleep))
|
54
|
+
patch_instance_method!(Kernel, :select, LightIO::Library::IO.method(:select))
|
55
|
+
end
|
56
|
+
|
57
|
+
def unpatch_kernel!
|
58
|
+
unpatch_method!(Kernel, :sleep, LightIO.method(:sleep))
|
59
|
+
unpatch_method!(Kernel, :select, LightIO::Library::IO.method(:select))
|
60
|
+
unpatch_instance_method!(Kernel, :sleep, LightIO.method(:sleep))
|
61
|
+
unpatch_instance_method!(Kernel, :select, LightIO::Library::IO.method(:select))
|
62
|
+
end
|
63
|
+
|
64
|
+
private
|
65
|
+
def patch!(klass_name)
|
66
|
+
klass = Object.const_get(klass_name)
|
67
|
+
raise PatchError, "already patched constant #{klass}" if patched?(klass)
|
68
|
+
patched[klass] = {}
|
69
|
+
class_methods_module = find_class_methods_module(klass_name)
|
70
|
+
methods = class_methods_module && find_monkey_patch_class_methods(klass_name)
|
71
|
+
return unless class_methods_module && methods
|
72
|
+
methods.each do |method_name|
|
73
|
+
method = class_methods_module.instance_method(method_name)
|
74
|
+
patch_method!(klass, method_name, method)
|
75
|
+
end
|
76
|
+
rescue
|
77
|
+
patched.delete(klass)
|
78
|
+
raise
|
79
|
+
end
|
80
|
+
|
81
|
+
def unpatch!(klass_name)
|
82
|
+
klass = Object.const_get(klass_name)
|
83
|
+
raise PatchError, "can't find patched constant #{klass}" unless patched?(klass)
|
84
|
+
unpatch_method!(klass, :new)
|
85
|
+
find_monkey_patch_class_methods(klass_name).each do |method_name|
|
86
|
+
unpatch_method!(klass, method_name)
|
87
|
+
end
|
88
|
+
patched.delete(klass)
|
89
|
+
end
|
90
|
+
|
91
|
+
def find_class_methods_module(klass_name)
|
92
|
+
LightIO::Module.const_get("#{klass_name}::ClassMethods", false)
|
93
|
+
rescue NameError
|
94
|
+
nil
|
95
|
+
end
|
96
|
+
|
97
|
+
def find_monkey_patch_class_methods(klass_name)
|
98
|
+
find_class_methods_module(klass_name).instance_methods
|
99
|
+
end
|
100
|
+
|
101
|
+
def patched_method?(obj, method)
|
102
|
+
patched?(obj) && patched[obj].key?(method)
|
103
|
+
end
|
104
|
+
|
105
|
+
def patched_methods(const)
|
106
|
+
patched[const] ||= {}
|
107
|
+
end
|
108
|
+
|
109
|
+
def patch_method!(const, method, patched_method)
|
110
|
+
raise PatchError, "already patched method #{const}.#{method}" if patched_method?(const, method)
|
111
|
+
patched_methods(const)[method] = patched_method
|
112
|
+
const.send(:define_singleton_method, method, patched_method)
|
113
|
+
nil
|
114
|
+
end
|
115
|
+
|
116
|
+
def unpatch_method!(const, method)
|
117
|
+
raise PatchError, "can't find patched method #{const}.#{method}" unless patched_method?(const, method)
|
118
|
+
origin_method = patched_methods(const).delete(method)
|
119
|
+
const.send(:define_singleton_method, method, origin_method)
|
120
|
+
nil
|
121
|
+
end
|
122
|
+
|
123
|
+
def patched_instance_method?(obj, method)
|
124
|
+
patched_instance_methods(obj).key?(method)
|
125
|
+
end
|
126
|
+
|
127
|
+
def patched_instance_methods(const)
|
128
|
+
(patched[:instance_methods] ||= {})[const] ||= {}
|
129
|
+
end
|
130
|
+
|
131
|
+
def patch_instance_method!(const, method, patched_method)
|
132
|
+
raise PatchError, "already patched method #{const}.#{method}" if patched_instance_method?(const, method)
|
133
|
+
patched_instance_methods(const)[method] = patched_method
|
134
|
+
const.send(:define_method, method, patched_method)
|
135
|
+
nil
|
136
|
+
end
|
137
|
+
|
138
|
+
def unpatch_instance_method!(const, method)
|
139
|
+
raise PatchError, "can't find patched method #{const}.#{method}" unless patched_instance_method?(const, method)
|
140
|
+
origin_method = patched_instance_methods(const).delete(method)
|
141
|
+
const.send(:define_method, method, origin_method)
|
142
|
+
nil
|
143
|
+
end
|
144
|
+
|
145
|
+
def patched
|
146
|
+
@patched ||= {}
|
147
|
+
end
|
148
|
+
end
|
149
|
+
end
|
150
|
+
end
|