sqreen 1.17.0 → 1.17.2.beta1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +418 -0
- data/README.md +5 -5
- data/lib/sqreen.rb +2 -72
- data/lib/sqreen/agent.rb +37 -0
- data/lib/sqreen/call_countable.rb +6 -6
- data/lib/sqreen/conditionable.rb +6 -6
- data/lib/sqreen/dependency.rb +18 -0
- data/lib/sqreen/dependency/callback.rb +34 -0
- data/lib/sqreen/dependency/detector.rb +97 -0
- data/lib/sqreen/dependency/hook.rb +102 -0
- data/lib/sqreen/dependency/hook_point.rb +219 -0
- data/lib/sqreen/dependency/new_relic.rb +17 -0
- data/lib/sqreen/dependency/rack.rb +36 -0
- data/lib/sqreen/dependency/rails.rb +30 -0
- data/lib/sqreen/dependency/sentry.rb +17 -0
- data/lib/sqreen/exception.rb +3 -0
- data/lib/sqreen/frameworks/generic.rb +2 -97
- data/lib/sqreen/frameworks/rails.rb +4 -13
- data/lib/sqreen/frameworks/sinatra.rb +0 -25
- data/lib/sqreen/instrumentation.rb +5 -4
- data/lib/sqreen/rules_callbacks/execjs.rb +3 -0
- data/lib/sqreen/rules_callbacks/record_request_context.rb +4 -1
- data/lib/sqreen/runner.rb +0 -3
- data/lib/sqreen/session.rb +40 -45
- data/lib/sqreen/version.rb +1 -1
- data/lib/sqreen/web_server.rb +54 -0
- data/lib/sqreen/web_server/generic.rb +24 -0
- data/lib/sqreen/web_server/passenger.rb +33 -0
- data/lib/sqreen/web_server/puma.rb +62 -0
- data/lib/sqreen/web_server/rainbows.rb +14 -0
- data/lib/sqreen/web_server/thin.rb +14 -0
- data/lib/sqreen/web_server/unicorn.rb +52 -0
- data/lib/sqreen/web_server/webrick.rb +14 -0
- data/lib/sqreen/worker.rb +68 -0
- metadata +28 -6
data/lib/sqreen/agent.rb
ADDED
@@ -0,0 +1,37 @@
|
|
1
|
+
# Copyright (c) 2015 Sqreen. All Rights Reserved.
|
2
|
+
# Please refer to our terms for more information: https://www.sqreen.io/terms.html
|
3
|
+
|
4
|
+
require 'sqreen/version'
|
5
|
+
require 'sqreen/instrumentation'
|
6
|
+
require 'sqreen/session'
|
7
|
+
require 'sqreen/runner'
|
8
|
+
require 'sqreen/callbacks'
|
9
|
+
require 'sqreen/log'
|
10
|
+
require 'sqreen/exception'
|
11
|
+
require 'sqreen/configuration'
|
12
|
+
require 'sqreen/events/attack'
|
13
|
+
require 'sqreen/sdk'
|
14
|
+
require 'sqreen/dependency/detector'
|
15
|
+
require 'sqreen/worker'
|
16
|
+
require 'sqreen/web_server'
|
17
|
+
|
18
|
+
module Sqreen
|
19
|
+
module Agent
|
20
|
+
module_function
|
21
|
+
|
22
|
+
def start
|
23
|
+
return if Sqreen.to_bool(ENV['SQREEN_DISABLE'])
|
24
|
+
|
25
|
+
Sqreen::Dependency::Detector.hook do
|
26
|
+
Sqreen.log.debug "[#{Process.pid}] Attaching to webserver"
|
27
|
+
Sqreen::WebServer.attach do
|
28
|
+
Sqreen.log.debug "[#{Process.pid}] Attached to webserver"
|
29
|
+
# TODO: maybe in dependency, not here, to separate concerns?
|
30
|
+
Sqreen::Dependency::Sentry.ignore_sqreen_exceptions
|
31
|
+
Sqreen::Dependency::NewRelic.ignore_sqreen_exceptions
|
32
|
+
Sqreen::Worker.start(Sqreen.framework)
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
@@ -33,20 +33,20 @@ module Sqreen
|
|
33
33
|
FAILING = 'failing'.freeze
|
34
34
|
COUNT_CALLS = 'sqreen_call_counts'.freeze
|
35
35
|
|
36
|
-
def pre_with_count(inst,
|
37
|
-
ret = pre_without_count(inst,
|
36
|
+
def pre_with_count(inst, args, budget = nil, &block)
|
37
|
+
ret = pre_without_count(inst, args, budget, &block)
|
38
38
|
count_calls('pre')
|
39
39
|
ret
|
40
40
|
end
|
41
41
|
|
42
|
-
def post_with_count(rv, inst,
|
43
|
-
ret = post_without_count(rv, inst,
|
42
|
+
def post_with_count(rv, inst, args, budget = nil, &block)
|
43
|
+
ret = post_without_count(rv, inst, args, budget, &block)
|
44
44
|
count_calls('post')
|
45
45
|
ret
|
46
46
|
end
|
47
47
|
|
48
|
-
def failing_with_count(rv, inst,
|
49
|
-
ret = failing_without_count(rv, inst,
|
48
|
+
def failing_with_count(rv, inst, args, budget = nil, &block)
|
49
|
+
ret = failing_without_count(rv, inst, args, budget, &block)
|
50
50
|
count_calls('failing')
|
51
51
|
ret
|
52
52
|
end
|
data/lib/sqreen/conditionable.rb
CHANGED
@@ -25,22 +25,22 @@ module Sqreen
|
|
25
25
|
end
|
26
26
|
end
|
27
27
|
|
28
|
-
def pre_with_conditions(inst,
|
28
|
+
def pre_with_conditions(inst, args, budget = nil, &block)
|
29
29
|
eargs = [nil, framework, inst, args, @data, nil]
|
30
30
|
return nil if !pre_conditions.nil? && !pre_conditions.evaluate(*eargs)
|
31
|
-
pre_without_conditions(inst,
|
31
|
+
pre_without_conditions(inst, args, budget, &block)
|
32
32
|
end
|
33
33
|
|
34
|
-
def post_with_conditions(rv, inst,
|
34
|
+
def post_with_conditions(rv, inst, args, budget = nil, &block)
|
35
35
|
eargs = [nil, framework, inst, args, @data, rv]
|
36
36
|
return nil if !post_conditions.nil? && !post_conditions.evaluate(*eargs)
|
37
|
-
post_without_conditions(rv, inst,
|
37
|
+
post_without_conditions(rv, inst, args, budget, &block)
|
38
38
|
end
|
39
39
|
|
40
|
-
def failing_with_conditions(rv, inst,
|
40
|
+
def failing_with_conditions(rv, inst, args, budget = nil, &block)
|
41
41
|
eargs = [nil, framework, inst, args, @data, rv]
|
42
42
|
return nil if !failing_conditions.nil? && !failing_conditions.evaluate(*eargs)
|
43
|
-
failing_without_conditions(rv, inst,
|
43
|
+
failing_without_conditions(rv, inst, args, budget, &block)
|
44
44
|
end
|
45
45
|
|
46
46
|
protected
|
@@ -0,0 +1,18 @@
|
|
1
|
+
# Copyright (c) 2015 Sqreen. All Rights Reserved.
|
2
|
+
# Please refer to our terms for more information: https://www.sqreen.io/terms.html
|
3
|
+
|
4
|
+
module Sqreen
|
5
|
+
module Dependency
|
6
|
+
def self.const_exist?(name)
|
7
|
+
resolve_const(name) && true
|
8
|
+
rescue NameError, ArgumentError
|
9
|
+
false
|
10
|
+
end
|
11
|
+
|
12
|
+
def self.resolve_const(name)
|
13
|
+
raise ArgumentError if name.nil? || name.empty?
|
14
|
+
|
15
|
+
name.to_s.split('::').inject(Object) { |a, e| a.const_get(e) }
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
# Copyright (c) 2015 Sqreen. All Rights Reserved.
|
2
|
+
# Please refer to our terms for more information: https://www.sqreen.io/terms.html
|
3
|
+
|
4
|
+
module Sqreen
|
5
|
+
module Dependency
|
6
|
+
class Callback
|
7
|
+
attr_reader :name
|
8
|
+
|
9
|
+
def initialize(name = nil, &block)
|
10
|
+
@name = name
|
11
|
+
@block = block
|
12
|
+
@disabled = false
|
13
|
+
end
|
14
|
+
|
15
|
+
def call(*args, &block)
|
16
|
+
Sqreen.log.debug "[#{Process.pid}] Callback #{@name} disabled:#{disabled?}"
|
17
|
+
return if @disabled
|
18
|
+
@block.call(*args, &block)
|
19
|
+
end
|
20
|
+
|
21
|
+
def disable
|
22
|
+
@disabled = true
|
23
|
+
end
|
24
|
+
|
25
|
+
def enable
|
26
|
+
@disabled = false
|
27
|
+
end
|
28
|
+
|
29
|
+
def disabled?
|
30
|
+
@disabled
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,97 @@
|
|
1
|
+
# Copyright (c) 2015 Sqreen. All Rights Reserved.
|
2
|
+
# Please refer to our terms for more information: https://www.sqreen.io/terms.html
|
3
|
+
|
4
|
+
require 'sqreen/dependency/hook'
|
5
|
+
require 'sqreen/dependency/rails'
|
6
|
+
require 'sqreen/dependency/rack'
|
7
|
+
require 'sqreen/dependency/sentry'
|
8
|
+
require 'sqreen/dependency/new_relic'
|
9
|
+
|
10
|
+
module Sqreen
|
11
|
+
module Dependency
|
12
|
+
module Detector
|
13
|
+
module_function
|
14
|
+
|
15
|
+
def start_mode
|
16
|
+
if Sqreen::Dependency::Rails.server?
|
17
|
+
:rails
|
18
|
+
elsif Sqreen::Dependency::Rack.rackup?
|
19
|
+
:rackup
|
20
|
+
else
|
21
|
+
:default
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
def hook(&block)
|
26
|
+
Sqreen.log.debug "[#{Process.pid}] Startup command: #{$0}"
|
27
|
+
|
28
|
+
# ensure middleware presence
|
29
|
+
|
30
|
+
ActiveSupport.on_load(:before_initialize, :yield => true) do
|
31
|
+
Sqreen::Dependency::Rails.insert_sqreen_middlewares
|
32
|
+
end if Sqreen::Dependency::Rails.required?
|
33
|
+
|
34
|
+
Sqreen::Dependency::Hook.add('Rack::Builder#to_app') do
|
35
|
+
after do
|
36
|
+
Sqreen::Dependency::Rails.inspect_middlewares
|
37
|
+
end
|
38
|
+
end if Sqreen::Dependency::Rails.required?
|
39
|
+
|
40
|
+
# ensure startup of thread in request handling processes
|
41
|
+
|
42
|
+
Sqreen::Dependency::Hook.add('Rack::Builder#to_app') do
|
43
|
+
after do |callback, *|
|
44
|
+
Sqreen.log.debug "[#{Process.pid}] Start mode #{Sqreen::Dependency::Detector.start_mode}"
|
45
|
+
if Sqreen::Dependency::Detector.start_mode == :rails || Sqreen::Dependency::Detector.start_mode == :rackup
|
46
|
+
|
47
|
+
Sqreen::Dependency::Rack.find_handler do |handler|
|
48
|
+
Sqreen::Dependency::Rack.on_run(handler) do
|
49
|
+
case handler.name
|
50
|
+
when 'Rack::Handler::Puma'
|
51
|
+
Sqreen::Dependency::Hook.add('Puma::Launcher#run') do
|
52
|
+
before do
|
53
|
+
# HACK: Puma master? hack falls apart when not preloading
|
54
|
+
# it would think master is not, triggering startup
|
55
|
+
Sqreen::WebServer.instance_eval { @master_pid = Process.pid }
|
56
|
+
block.call
|
57
|
+
# HACK: because to_app callback is disabled, so won't run again in fork
|
58
|
+
if Sqreen::WebServer.forking? && !Sqreen::WebServer.preload_app?
|
59
|
+
Sqreen::WebServer.after_fork { block.call }
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
Sqreen::Dependency::Hook['Puma::Launcher#run'].install
|
64
|
+
when 'Rack::Handler::PhusionPassenger'
|
65
|
+
# noop, passenger will start his own separate process
|
66
|
+
Sqreen.log.debug "[#{Process.pid}] Passenger will start in standalone process"
|
67
|
+
when 'Rack::Handler::Unicorn' # unicorn-rails
|
68
|
+
Sqreen::Dependency::Hook.add('Unicorn::HttpServer.new') do
|
69
|
+
before do
|
70
|
+
# BUG: detects single process...
|
71
|
+
end
|
72
|
+
end.install
|
73
|
+
else
|
74
|
+
block.call
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
78
|
+
else
|
79
|
+
block.call
|
80
|
+
end
|
81
|
+
|
82
|
+
# #to_app can be called multiple times, run callback once only
|
83
|
+
callback.disable
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
Sqreen::Dependency::Hook['Rack::Builder#to_app'].install
|
88
|
+
|
89
|
+
# Sqreen::Dependency::Hook.add('Rails::Server#start') do
|
90
|
+
# before { }
|
91
|
+
# end
|
92
|
+
# Sqreen::Dependency::Hook['Rails::Server#start'].install
|
93
|
+
# /!\ double instrument Rails < Rack => Rails.start_with -> Rails.start_without -> super -> Rack.start_with -> Rails.start_without
|
94
|
+
end
|
95
|
+
end
|
96
|
+
end
|
97
|
+
end
|
@@ -0,0 +1,102 @@
|
|
1
|
+
# Copyright (c) 2015 Sqreen. All Rights Reserved.
|
2
|
+
# Please refer to our terms for more information: https://www.sqreen.io/terms.html
|
3
|
+
|
4
|
+
require 'sqreen/dependency/callback'
|
5
|
+
require 'sqreen/dependency/hook_point'
|
6
|
+
|
7
|
+
module Sqreen
|
8
|
+
module Dependency
|
9
|
+
class Hook
|
10
|
+
@hooks = {}
|
11
|
+
|
12
|
+
def self.[](hook_point)
|
13
|
+
@hooks[hook_point] ||= new(hook_point)
|
14
|
+
end
|
15
|
+
|
16
|
+
def self.add(hook_point, &block)
|
17
|
+
self[hook_point].add(&block)
|
18
|
+
end
|
19
|
+
|
20
|
+
attr_reader :point
|
21
|
+
|
22
|
+
def initialize(hook_point, dependency_test = nil)
|
23
|
+
@disabled = false
|
24
|
+
@point = hook_point.is_a?(HookPoint) ? hook_point : HookPoint.new(hook_point)
|
25
|
+
@before = []
|
26
|
+
@after = []
|
27
|
+
@raised = []
|
28
|
+
@dependency_test = dependency_test || Proc.new { point.exist? }
|
29
|
+
end
|
30
|
+
|
31
|
+
def dependency?
|
32
|
+
@dependency_test.call if @dependency_test
|
33
|
+
end
|
34
|
+
|
35
|
+
def add(&block)
|
36
|
+
tap { instance_eval(&block) }
|
37
|
+
end
|
38
|
+
|
39
|
+
def callback_name(whence, tag = nil)
|
40
|
+
"#{point}@#{whence}" << (tag ? ":#{tag}" : "")
|
41
|
+
end
|
42
|
+
|
43
|
+
def before(tag = nil, &block)
|
44
|
+
return @before if block.nil?
|
45
|
+
|
46
|
+
@before << Callback.new(callback_name(:before, tag), &block)
|
47
|
+
end
|
48
|
+
|
49
|
+
def after(tag = nil, &block)
|
50
|
+
return @after if block.nil?
|
51
|
+
|
52
|
+
@after << Callback.new(callback_name(:after, tag), &block)
|
53
|
+
end
|
54
|
+
|
55
|
+
def raised(tag = nil, &block)
|
56
|
+
return @raised if block.nil?
|
57
|
+
|
58
|
+
@raised << Callback.new(callback_name(:raised, tag), &block)
|
59
|
+
end
|
60
|
+
|
61
|
+
def depends_on(&block)
|
62
|
+
@dependency_test = block
|
63
|
+
end
|
64
|
+
|
65
|
+
def enable
|
66
|
+
@disabled = false
|
67
|
+
end
|
68
|
+
|
69
|
+
def disable
|
70
|
+
@disabled = true
|
71
|
+
end
|
72
|
+
|
73
|
+
def disabled?
|
74
|
+
@disabled
|
75
|
+
end
|
76
|
+
|
77
|
+
def install
|
78
|
+
unless point.exist?
|
79
|
+
Sqreen.log.debug "[#{Process.pid}] #{point} not found"
|
80
|
+
return
|
81
|
+
end
|
82
|
+
Sqreen.log.debug "[#{Process.pid}] Hook #{point}: installing"
|
83
|
+
|
84
|
+
point.install('sqreen_hook', &Sqreen::Dependency::Hook.wrapper(self))
|
85
|
+
end
|
86
|
+
|
87
|
+
def self.wrapper(hook)
|
88
|
+
# pass self to cbs
|
89
|
+
Proc.new do |*args, &block|
|
90
|
+
Sqreen.log.debug "[#{Process.pid}] Hook #{hook.point} disabled:#{hook.disabled?} caller:#{Kernel.caller[1].inspect}"
|
91
|
+
hook.before.each { |c| c.call(c, self, args) } unless hook.disabled?
|
92
|
+
begin
|
93
|
+
hook.point.apply(self, 'sqreen_hook', *args, &block)
|
94
|
+
rescue ::Exception => e # rubocop:disable Lint/RescueException
|
95
|
+
hook.raised.each { |c| c.call(c, self, e, args) } unless hook.disabled?
|
96
|
+
raise
|
97
|
+
end.tap { |v| hook.after.each { |c| c.call(c, self, v, args) } unless hook.disabled? }
|
98
|
+
end
|
99
|
+
end
|
100
|
+
end
|
101
|
+
end
|
102
|
+
end
|
@@ -0,0 +1,219 @@
|
|
1
|
+
# Copyright (c) 2015 Sqreen. All Rights Reserved.
|
2
|
+
# Please refer to our terms for more information: https://www.sqreen.io/terms.html
|
3
|
+
|
4
|
+
require 'sqreen/dependency'
|
5
|
+
|
6
|
+
module Sqreen
|
7
|
+
module Dependency
|
8
|
+
class HookPointError < StandardError; end
|
9
|
+
|
10
|
+
class HookPoint
|
11
|
+
def self.parse(hook_point)
|
12
|
+
klass_name, separator, method_name = hook_point.split(/(\#|\.)/, 2)
|
13
|
+
|
14
|
+
raise ArgumentError, hook_point if klass_name.nil? || separator.nil? || method_name.nil?
|
15
|
+
raise ArgumentError, hook_point unless ['.', '#'].include?(separator)
|
16
|
+
|
17
|
+
method_kind = separator == '.' ? :klass_method : :instance_method
|
18
|
+
|
19
|
+
[klass_name.to_sym, method_kind, method_name.to_sym]
|
20
|
+
end
|
21
|
+
|
22
|
+
attr_reader :klass_name, :method_kind, :method_name
|
23
|
+
|
24
|
+
def initialize(hook_point)
|
25
|
+
@klass_name, @method_kind, @method_name = Sqreen::Dependency::HookPoint.parse(hook_point)
|
26
|
+
end
|
27
|
+
|
28
|
+
def to_s
|
29
|
+
"#{@klass_name}#{@method_kind == :instance_method ? '#' : '.'}#{@method_name}"
|
30
|
+
end
|
31
|
+
|
32
|
+
def exist?
|
33
|
+
return false unless Sqreen::Dependency.const_exist?(@klass_name)
|
34
|
+
|
35
|
+
if klass_method?
|
36
|
+
(klass.methods + klass.protected_methods + klass.private_methods).include?(@method_name)
|
37
|
+
elsif instance_method?
|
38
|
+
(klass.instance_methods + klass.protected_instance_methods + klass.private_instance_methods).include?(@method_name)
|
39
|
+
else
|
40
|
+
raise HookPointError, 'unknown hook point kind'
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
def klass
|
45
|
+
Sqreen::Dependency.resolve_const(@klass_name)
|
46
|
+
end
|
47
|
+
|
48
|
+
def klass_method?
|
49
|
+
@method_kind == :klass_method
|
50
|
+
end
|
51
|
+
|
52
|
+
def private_method?
|
53
|
+
if klass_method?
|
54
|
+
klass.private_methods.include?(@method_name)
|
55
|
+
elsif instance_method?
|
56
|
+
klass.private_instance_methods.include?(@method_name)
|
57
|
+
else
|
58
|
+
raise HookPointError, 'unknown hook point kind'
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
def protected_method?
|
63
|
+
if klass_method?
|
64
|
+
klass.protected_methods.include?(@method_name)
|
65
|
+
elsif instance_method?
|
66
|
+
klass.protected_instance_methods.include?(@method_name)
|
67
|
+
else
|
68
|
+
raise HookPointError, 'unknown hook point kind'
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
def instance_method?
|
73
|
+
@method_kind == :instance_method
|
74
|
+
end
|
75
|
+
|
76
|
+
def installed?(suffix)
|
77
|
+
if klass_method?
|
78
|
+
(klass.methods + klass.protected_methods + klass.private_methods).include?(:"#{method_name}_with_#{suffix}")
|
79
|
+
elsif instance_method?
|
80
|
+
(klass.instance_methods + klass.protected_instance_methods + klass.private_instance_methods).include?(:"#{method_name}_with_#{suffix}")
|
81
|
+
else
|
82
|
+
raise HookPointError, 'unknown hook point kind'
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
def apply(obj, suffix, *args, &block)
|
87
|
+
obj.send("#{method_name}_without_#{suffix}", *args, &block)
|
88
|
+
end
|
89
|
+
|
90
|
+
def install(suffix, &block)
|
91
|
+
if installed?(suffix)
|
92
|
+
Sqreen.log.debug "[#{Process.pid}] #{self} already installed"
|
93
|
+
end
|
94
|
+
unless exist?
|
95
|
+
Sqreen.log.debug "[#{Process.pid}] #{self} hook point not found"
|
96
|
+
end
|
97
|
+
|
98
|
+
define(suffix, &block)
|
99
|
+
enable(suffix)
|
100
|
+
end
|
101
|
+
|
102
|
+
def uninstall(suffix)
|
103
|
+
disable(suffix)
|
104
|
+
remove(suffix)
|
105
|
+
end
|
106
|
+
|
107
|
+
def enable(suffix)
|
108
|
+
chain(suffix)
|
109
|
+
end
|
110
|
+
|
111
|
+
def disable(suffix)
|
112
|
+
unchain(suffix)
|
113
|
+
end
|
114
|
+
|
115
|
+
def disabled?(suffix)
|
116
|
+
!chained?(suffix)
|
117
|
+
end
|
118
|
+
|
119
|
+
private
|
120
|
+
|
121
|
+
def define(suffix, &block)
|
122
|
+
hook_point = self
|
123
|
+
method_name = @method_name
|
124
|
+
|
125
|
+
if klass_method?
|
126
|
+
klass.singleton_class.instance_eval do
|
127
|
+
if hook_point.protected_method?
|
128
|
+
private
|
129
|
+
elsif hook_point.protected_method?
|
130
|
+
protected
|
131
|
+
else
|
132
|
+
public
|
133
|
+
end
|
134
|
+
|
135
|
+
define_method(:"#{method_name}_with_#{suffix}", &block)
|
136
|
+
end
|
137
|
+
elsif instance_method?
|
138
|
+
klass.class_eval do
|
139
|
+
if hook_point.protected_method?
|
140
|
+
private
|
141
|
+
elsif hook_point.protected_method?
|
142
|
+
protected
|
143
|
+
else
|
144
|
+
public
|
145
|
+
end
|
146
|
+
|
147
|
+
define_method(:"#{method_name}_with_#{suffix}", &block)
|
148
|
+
end
|
149
|
+
else
|
150
|
+
raise HookPointError, 'unknown hook point kind'
|
151
|
+
end
|
152
|
+
end
|
153
|
+
|
154
|
+
def remove(suffix)
|
155
|
+
method_name = @method_name
|
156
|
+
|
157
|
+
if klass_method?
|
158
|
+
klass.singleton_class.instance_eval do
|
159
|
+
remove_method(:"#{method_name}_with_#{suffix}")
|
160
|
+
end
|
161
|
+
elsif instance_method?
|
162
|
+
klass.class_eval do
|
163
|
+
remove_method(:"#{method_name}_with_#{suffix}")
|
164
|
+
end
|
165
|
+
else
|
166
|
+
raise HookPointError, 'unknown hook point kind'
|
167
|
+
end
|
168
|
+
end
|
169
|
+
|
170
|
+
def chained?(suffix)
|
171
|
+
method_name = @method_name
|
172
|
+
|
173
|
+
if klass_method?
|
174
|
+
klass.singleton_class.instance_eval do
|
175
|
+
instance_method(:"#{method_name}").original_name == :"#{method_name}_with_#{suffix}"
|
176
|
+
end
|
177
|
+
elsif instance_method?
|
178
|
+
klass.class_eval do
|
179
|
+
instance_method(:"#{method_name}").original_name == :"#{method_name}_with_#{suffix}"
|
180
|
+
end
|
181
|
+
else
|
182
|
+
raise HookPointError, 'unknown hook point kind'
|
183
|
+
end
|
184
|
+
end
|
185
|
+
|
186
|
+
def chain(suffix)
|
187
|
+
method_name = @method_name
|
188
|
+
|
189
|
+
if klass_method?
|
190
|
+
klass.singleton_class.instance_eval do
|
191
|
+
alias_method :"#{method_name}_without_#{suffix}", :"#{method_name}"
|
192
|
+
alias_method :"#{method_name}", :"#{method_name}_with_#{suffix}"
|
193
|
+
end
|
194
|
+
elsif instance_method?
|
195
|
+
klass.class_eval do
|
196
|
+
alias_method :"#{method_name}_without_#{suffix}", :"#{method_name}"
|
197
|
+
alias_method :"#{method_name}", :"#{method_name}_with_#{suffix}"
|
198
|
+
end
|
199
|
+
else
|
200
|
+
raise HookPointError, 'unknown hook point kind'
|
201
|
+
end
|
202
|
+
end
|
203
|
+
|
204
|
+
def unchain(suffix)
|
205
|
+
method_name = @method_name
|
206
|
+
|
207
|
+
if klass_method?
|
208
|
+
klass.singleton_class.instance_eval do
|
209
|
+
alias_method :"#{method_name}", :"#{method_name}_without_#{suffix}"
|
210
|
+
end
|
211
|
+
elsif instance_method?
|
212
|
+
klass.class_eval do
|
213
|
+
alias_method :"#{method_name}", :"#{method_name}_without_#{suffix}"
|
214
|
+
end
|
215
|
+
end
|
216
|
+
end
|
217
|
+
end
|
218
|
+
end
|
219
|
+
end
|