lightio 0.3.2 → 0.4.0.pre
Sign up to get free protection for your applications and to get access to all the features.
- 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
|