appsignal 0.4.7 → 0.5.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.
- data/.ruby-version +1 -0
- data/README.md +20 -19
- data/appsignal.gemspec +2 -2
- data/lib/appsignal.rb +41 -18
- data/lib/appsignal/agent.rb +28 -54
- data/lib/appsignal/aggregator.rb +65 -0
- data/lib/appsignal/aggregator/post_processor.rb +27 -0
- data/lib/appsignal/config.rb +9 -4
- data/lib/appsignal/listener.rb +30 -0
- data/lib/appsignal/middleware.rb +4 -30
- data/lib/appsignal/middleware/action_view_sanitizer.rb +21 -0
- data/lib/appsignal/middleware/active_record_sanitizer.rb +60 -0
- data/lib/appsignal/middleware/chain.rb +99 -0
- data/lib/appsignal/middleware/delete_blanks.rb +12 -0
- data/lib/appsignal/railtie.rb +9 -1
- data/lib/appsignal/to_appsignal_hash.rb +23 -0
- data/lib/appsignal/transaction.rb +72 -16
- data/lib/appsignal/transaction/params_sanitizer.rb +91 -13
- data/lib/appsignal/transaction/transaction_formatter.rb +32 -68
- data/lib/appsignal/version.rb +1 -1
- data/spec/appsignal/agent_spec.rb +46 -156
- data/spec/appsignal/aggregator/post_processor_spec.rb +84 -0
- data/spec/appsignal/aggregator_spec.rb +182 -0
- data/spec/appsignal/inactive_railtie_spec.rb +2 -1
- data/spec/appsignal/{middleware_spec.rb → listener_spec.rb} +2 -2
- data/spec/appsignal/middleware/action_view_sanitizer_spec.rb +27 -0
- data/spec/appsignal/middleware/active_record_sanitizer_spec.rb +201 -0
- data/spec/appsignal/middleware/chain_spec.rb +168 -0
- data/spec/appsignal/middleware/delete_blanks_spec.rb +37 -0
- data/spec/appsignal/railtie_spec.rb +47 -34
- data/spec/appsignal/to_appsignal_hash_spec.rb +29 -0
- data/spec/appsignal/transaction/params_sanitizer_spec.rb +141 -36
- data/spec/appsignal/transaction/transaction_formatter_spec.rb +60 -155
- data/spec/appsignal/transaction_spec.rb +186 -53
- data/spec/appsignal/transmitter_spec.rb +11 -6
- data/spec/appsignal_spec.rb +33 -0
- data/spec/spec_helper.rb +9 -62
- data/spec/support/helpers/notification_helpers.rb +30 -0
- data/spec/support/helpers/transaction_helpers.rb +64 -0
- metadata +74 -63
- data/.rvmrc +0 -1
- data/lib/appsignal/transaction/faulty_request_formatter.rb +0 -30
- data/lib/appsignal/transaction/regular_request_formatter.rb +0 -11
- data/lib/appsignal/transaction/slow_request_formatter.rb +0 -34
- data/spec/appsignal/transaction/faulty_request_formatter_spec.rb +0 -49
- data/spec/appsignal/transaction/regular_request_formatter_spec.rb +0 -14
- data/spec/appsignal/transaction/slow_request_formatter_spec.rb +0 -76
data/lib/appsignal/middleware.rb
CHANGED
@@ -1,30 +1,4 @@
|
|
1
|
-
require '
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
def initialize(app, options = {})
|
6
|
-
@app, @options = app, options
|
7
|
-
end
|
8
|
-
|
9
|
-
def call(env)
|
10
|
-
Appsignal::Transaction.create(env['action_dispatch.request_id'], env)
|
11
|
-
@app.call(env)
|
12
|
-
rescue Exception => exception
|
13
|
-
unless in_ignored_exceptions?(exception)
|
14
|
-
Appsignal::Transaction.current.add_exception(
|
15
|
-
Appsignal::ExceptionNotification.new(env, exception)
|
16
|
-
)
|
17
|
-
end
|
18
|
-
raise exception
|
19
|
-
ensure
|
20
|
-
Appsignal::Transaction.current.complete!
|
21
|
-
end
|
22
|
-
|
23
|
-
private
|
24
|
-
|
25
|
-
def in_ignored_exceptions?(exception)
|
26
|
-
Array.wrap(Appsignal.config[:ignore_exceptions]).
|
27
|
-
include?(exception.class.name)
|
28
|
-
end
|
29
|
-
end
|
30
|
-
end
|
1
|
+
require 'appsignal/middleware/chain'
|
2
|
+
require 'appsignal/middleware/delete_blanks'
|
3
|
+
require 'appsignal/middleware/action_view_sanitizer'
|
4
|
+
require 'appsignal/middleware/active_record_sanitizer'
|
@@ -0,0 +1,21 @@
|
|
1
|
+
module Appsignal
|
2
|
+
module Middleware
|
3
|
+
class ActionViewSanitizer
|
4
|
+
TARGET_EVENT_CATEGORY = 'action_view'.freeze
|
5
|
+
|
6
|
+
def call(event)
|
7
|
+
if event.name.end_with?(TARGET_EVENT_CATEGORY)
|
8
|
+
identifier = event.payload[:identifier]
|
9
|
+
if identifier
|
10
|
+
identifier.gsub!(root_path, '')
|
11
|
+
end
|
12
|
+
end
|
13
|
+
yield
|
14
|
+
end
|
15
|
+
|
16
|
+
def root_path
|
17
|
+
@root_path ||= "#{Rails.root.to_s}/"
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,60 @@
|
|
1
|
+
require 'active_record'
|
2
|
+
|
3
|
+
module Appsignal
|
4
|
+
module Middleware
|
5
|
+
class ActiveRecordSanitizer
|
6
|
+
TARGET_EVENT_NAME = 'sql.active_record'.freeze
|
7
|
+
|
8
|
+
SINGLE_QUOTE = /\\'/.freeze
|
9
|
+
DOUBLE_QUOTE = /\\"/.freeze
|
10
|
+
QUOTED_DATA = /(?:"[^"]+"|'[^']+')/.freeze
|
11
|
+
SINGLE_QUOTED_DATA = /(?:'[^']+')/.freeze
|
12
|
+
IN_ARRAY = /(IN \()[^\)]+(\))/.freeze
|
13
|
+
NUMERIC_DATA = /\b\d+\b/.freeze
|
14
|
+
|
15
|
+
SANITIZED_VALUE = '\1?\2'.freeze
|
16
|
+
|
17
|
+
def call(event)
|
18
|
+
if event.name == TARGET_EVENT_NAME
|
19
|
+
unless schema_query?(event) || adapter_uses_prepared_statements?
|
20
|
+
query_string = event.payload[:sql]
|
21
|
+
if query_string
|
22
|
+
if adapter_uses_double_quoted_table_names?
|
23
|
+
query_string.gsub!(SINGLE_QUOTE, SANITIZED_VALUE)
|
24
|
+
query_string.gsub!(SINGLE_QUOTED_DATA, SANITIZED_VALUE)
|
25
|
+
else
|
26
|
+
query_string.gsub!(SINGLE_QUOTE, SANITIZED_VALUE)
|
27
|
+
query_string.gsub!(DOUBLE_QUOTE, SANITIZED_VALUE)
|
28
|
+
query_string.gsub!(QUOTED_DATA, SANITIZED_VALUE)
|
29
|
+
end
|
30
|
+
query_string.gsub!(IN_ARRAY, SANITIZED_VALUE)
|
31
|
+
query_string.gsub!(NUMERIC_DATA, SANITIZED_VALUE)
|
32
|
+
end
|
33
|
+
end
|
34
|
+
event.payload.delete(:connection_id)
|
35
|
+
event.payload.delete(:binds)
|
36
|
+
end
|
37
|
+
yield
|
38
|
+
end
|
39
|
+
|
40
|
+
def schema_query?(event)
|
41
|
+
event.payload[:name] == 'SCHEMA'
|
42
|
+
end
|
43
|
+
|
44
|
+
def connection_config
|
45
|
+
ActiveRecord::Base.connection_config
|
46
|
+
end
|
47
|
+
|
48
|
+
def adapter_uses_double_quoted_table_names?
|
49
|
+
adapter = connection_config[:adapter]
|
50
|
+
adapter =~ /postgres/ || adapter =~ /sqlite/
|
51
|
+
end
|
52
|
+
|
53
|
+
def adapter_uses_prepared_statements?
|
54
|
+
return false unless adapter_uses_double_quoted_table_names?
|
55
|
+
return true if connection_config[:prepared_statements].nil?
|
56
|
+
connection_config[:prepared_statements]
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
@@ -0,0 +1,99 @@
|
|
1
|
+
module Appsignal
|
2
|
+
# Middleware is code configured to run before/after a message is processed.
|
3
|
+
# It is patterned after Rack middleware.
|
4
|
+
#
|
5
|
+
# @example To add middleware:
|
6
|
+
#
|
7
|
+
# Appsignal.post_processing_middleware do |chain|
|
8
|
+
# chain.add MyPostProcessingHook
|
9
|
+
# end
|
10
|
+
#
|
11
|
+
# @example To insert immediately preceding another entry:
|
12
|
+
#
|
13
|
+
# Appsignal.post_process_middleware do |chain|
|
14
|
+
# chain.insert_before ActiveRecord, MyPostProcessingHook
|
15
|
+
# end
|
16
|
+
#
|
17
|
+
# @example To insert immediately after another entry:
|
18
|
+
#
|
19
|
+
# Appsignal.post_process_middleware do |chain|
|
20
|
+
# chain.insert_after ActiveRecord, MyPostProcessingHook
|
21
|
+
# end
|
22
|
+
#
|
23
|
+
# @example This is an example of a minimal middleware class:
|
24
|
+
#
|
25
|
+
# class MySHook
|
26
|
+
# def call(transaction)
|
27
|
+
# puts "Before post processing"
|
28
|
+
# yield
|
29
|
+
# puts "After post processing"
|
30
|
+
# end
|
31
|
+
# end
|
32
|
+
#
|
33
|
+
module Middleware
|
34
|
+
class Chain
|
35
|
+
attr_reader :entries
|
36
|
+
|
37
|
+
def initialize
|
38
|
+
@entries = []
|
39
|
+
yield self if block_given?
|
40
|
+
end
|
41
|
+
|
42
|
+
def remove(klass)
|
43
|
+
entries.delete_if { |entry| entry.klass == klass }
|
44
|
+
end
|
45
|
+
|
46
|
+
def add(klass, *args)
|
47
|
+
entries << Entry.new(klass, *args) unless exists?(klass)
|
48
|
+
end
|
49
|
+
|
50
|
+
def insert_before(oldklass, newklass, *args)
|
51
|
+
i = entries.index { |entry| entry.klass == newklass }
|
52
|
+
new_entry = i.nil? ? Entry.new(newklass, *args) : entries.delete_at(i)
|
53
|
+
i = entries.find_index { |entry| entry.klass == oldklass } || 0
|
54
|
+
entries.insert(i, new_entry)
|
55
|
+
end
|
56
|
+
|
57
|
+
def insert_after(oldklass, newklass, *args)
|
58
|
+
i = entries.index { |entry| entry.klass == newklass }
|
59
|
+
new_entry = i.nil? ? Entry.new(newklass, *args) : entries.delete_at(i)
|
60
|
+
i = entries.find_index { |entry| entry.klass == oldklass } || entries.count - 1
|
61
|
+
entries.insert(i+1, new_entry)
|
62
|
+
end
|
63
|
+
|
64
|
+
def exists?(klass)
|
65
|
+
entries.any? { |entry| entry.klass == klass }
|
66
|
+
end
|
67
|
+
|
68
|
+
def retrieve
|
69
|
+
@retrieve ||= entries.map(&:make_new)
|
70
|
+
end
|
71
|
+
|
72
|
+
def clear
|
73
|
+
entries.clear
|
74
|
+
end
|
75
|
+
|
76
|
+
def invoke(*args)
|
77
|
+
chain = retrieve.dup
|
78
|
+
traverse_chain = lambda do
|
79
|
+
unless chain.empty?
|
80
|
+
chain.shift.call(*args, &traverse_chain)
|
81
|
+
end
|
82
|
+
end
|
83
|
+
traverse_chain.call
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
class Entry
|
88
|
+
attr_reader :klass
|
89
|
+
def initialize(klass, *args)
|
90
|
+
@klass = klass
|
91
|
+
@args = args
|
92
|
+
end
|
93
|
+
|
94
|
+
def make_new
|
95
|
+
@klass.new(*@args)
|
96
|
+
end
|
97
|
+
end
|
98
|
+
end
|
99
|
+
end
|
data/lib/appsignal/railtie.rb
CHANGED
@@ -1,8 +1,14 @@
|
|
1
1
|
module Appsignal
|
2
2
|
class Railtie < Rails::Railtie
|
3
3
|
initializer "appsignal.configure_rails_initialization" do |app|
|
4
|
+
Appsignal.logger = Logger.new(Rails.root.join('log/appsignal.log')).tap do |l|
|
5
|
+
l.level = Logger::INFO
|
6
|
+
end
|
7
|
+
Appsignal.flush_in_memory_log
|
8
|
+
|
4
9
|
if Appsignal.active?
|
5
|
-
app.middleware.
|
10
|
+
app.middleware.
|
11
|
+
insert_before(ActionDispatch::RemoteIp, Appsignal::Listener)
|
6
12
|
|
7
13
|
Appsignal.subscriber = ActiveSupport::Notifications.subscribe(/^[^!]/) do |*args|
|
8
14
|
if Appsignal::Transaction.current
|
@@ -17,3 +23,5 @@ module Appsignal
|
|
17
23
|
end
|
18
24
|
end
|
19
25
|
end
|
26
|
+
|
27
|
+
require 'appsignal/to_appsignal_hash'
|
@@ -0,0 +1,23 @@
|
|
1
|
+
module Appsignal
|
2
|
+
module ToAppsignalHash
|
3
|
+
|
4
|
+
def to_appsignal_hash
|
5
|
+
{
|
6
|
+
:name => name,
|
7
|
+
:duration => duration,
|
8
|
+
:time => time.to_f,
|
9
|
+
:end => self.end.to_f,
|
10
|
+
:payload => payload
|
11
|
+
}
|
12
|
+
end
|
13
|
+
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
module ActiveSupport
|
18
|
+
module Notifications
|
19
|
+
class Event
|
20
|
+
include Appsignal::ToAppsignalHash
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -1,8 +1,20 @@
|
|
1
1
|
require 'socket'
|
2
2
|
require 'appsignal/transaction/transaction_formatter'
|
3
|
+
require 'appsignal/transaction/params_sanitizer'
|
3
4
|
|
4
5
|
module Appsignal
|
5
6
|
class Transaction
|
7
|
+
# Based on what Rails uses + some variables we'd like to show
|
8
|
+
ENV_METHODS = %w(CONTENT_LENGTH AUTH_TYPE GATEWAY_INTERFACE
|
9
|
+
PATH_TRANSLATED REMOTE_HOST REMOTE_IDENT REMOTE_USER REMOTE_ADDR
|
10
|
+
REQUEST_METHOD SERVER_NAME SERVER_PORT SERVER_PROTOCOL
|
11
|
+
|
12
|
+
HTTP_X_REQUEST_START HTTP_X_MIDDLEWARE_START HTTP_X_QUEUE_START
|
13
|
+
HTTP_X_QUEUE_TIME HTTP_X_HEROKU_QUEUE_WAIT_TIME HTTP_X_APPLICATION_START
|
14
|
+
HTTP_ACCEPT HTTP_ACCEPT_CHARSET HTTP_ACCEPT_ENCODING HTTP_ACCEPT_LANGUAGE
|
15
|
+
HTTP_CACHE_CONTROL HTTP_CONNECTION HTTP_USER_AGENT HTTP_FROM HTTP_NEGOTIATE
|
16
|
+
HTTP_PRAGMA HTTP_REFERER).freeze
|
17
|
+
|
6
18
|
def self.create(key, env)
|
7
19
|
Thread.current[:appsignal_transaction_id] = key
|
8
20
|
Appsignal.transactions[key] = Appsignal::Transaction.new(key, env)
|
@@ -12,7 +24,8 @@ module Appsignal
|
|
12
24
|
Appsignal.transactions[Thread.current[:appsignal_transaction_id]]
|
13
25
|
end
|
14
26
|
|
15
|
-
attr_reader :id, :events, :process_action_event, :action, :exception, :env
|
27
|
+
attr_reader :id, :events, :process_action_event, :action, :exception, :env,
|
28
|
+
:fullpath, :time
|
16
29
|
|
17
30
|
def initialize(id, env)
|
18
31
|
@id = id
|
@@ -22,15 +35,22 @@ module Appsignal
|
|
22
35
|
@env = env
|
23
36
|
end
|
24
37
|
|
38
|
+
def sanitized_environment
|
39
|
+
@sanitized_environment ||= {}
|
40
|
+
end
|
41
|
+
|
42
|
+
def sanitized_session_data
|
43
|
+
@sanitized_session_data ||= {}
|
44
|
+
end
|
45
|
+
|
25
46
|
def request
|
26
47
|
ActionDispatch::Request.new(@env)
|
27
48
|
end
|
28
49
|
|
29
50
|
def set_process_action_event(event)
|
30
51
|
@process_action_event = event
|
31
|
-
if
|
32
|
-
@action = "#{
|
33
|
-
"#{process_action_event.payload[:action]}"
|
52
|
+
if event && event.payload
|
53
|
+
@action = "#{event.payload[:controller]}##{event.payload[:action]}"
|
34
54
|
end
|
35
55
|
end
|
36
56
|
|
@@ -39,6 +59,7 @@ module Appsignal
|
|
39
59
|
end
|
40
60
|
|
41
61
|
def add_exception(ex)
|
62
|
+
@time = Time.now.utc.to_f
|
42
63
|
@exception = ex
|
43
64
|
end
|
44
65
|
|
@@ -48,30 +69,65 @@ module Appsignal
|
|
48
69
|
|
49
70
|
def slow_request?
|
50
71
|
return false unless process_action_event && process_action_event.payload
|
51
|
-
Appsignal.config[:slow_request_threshold] <=
|
72
|
+
Appsignal.config[:slow_request_threshold] <=
|
73
|
+
process_action_event.duration
|
52
74
|
end
|
53
75
|
|
54
|
-
def
|
55
|
-
|
56
|
-
|
76
|
+
def slower?(transaction)
|
77
|
+
process_action_event.duration > transaction.process_action_event.duration
|
78
|
+
end
|
79
|
+
|
80
|
+
def truncate!
|
81
|
+
process_action_event.payload.clear
|
82
|
+
events.clear
|
83
|
+
sanitized_environment.clear
|
84
|
+
sanitized_session_data.clear
|
85
|
+
@env = nil
|
86
|
+
end
|
87
|
+
|
88
|
+
def convert_values_to_primitives!
|
89
|
+
Appsignal::ParamsSanitizer.sanitize!(@process_action_event.payload) if @process_action_event
|
90
|
+
@events.each { |o| Appsignal::ParamsSanitizer.sanitize!(o.payload) }
|
91
|
+
add_sanitized_context!
|
92
|
+
end
|
93
|
+
|
94
|
+
def type
|
95
|
+
return :exception if exception?
|
96
|
+
return :slow_request if slow_request?
|
97
|
+
:regular_request
|
57
98
|
end
|
58
99
|
|
59
100
|
def to_hash
|
60
|
-
|
61
|
-
TransactionFormatter.faulty(self)
|
62
|
-
elsif slow_request?
|
63
|
-
TransactionFormatter.slow(self)
|
64
|
-
else
|
65
|
-
TransactionFormatter.regular(self)
|
66
|
-
end.to_hash
|
101
|
+
TransactionFormatter.new(self).to_hash
|
67
102
|
end
|
68
103
|
|
69
104
|
def complete!
|
70
105
|
Thread.current[:appsignal_transaction_id] = nil
|
71
106
|
current_transaction = Appsignal.transactions.delete(@id)
|
72
107
|
if process_action_event || exception?
|
73
|
-
Appsignal.
|
108
|
+
Appsignal.enqueue(current_transaction)
|
109
|
+
end
|
110
|
+
end
|
111
|
+
|
112
|
+
protected
|
113
|
+
|
114
|
+
def add_sanitized_context!
|
115
|
+
sanitize_environment!
|
116
|
+
sanitize_session_data!
|
117
|
+
@env = nil
|
118
|
+
end
|
119
|
+
|
120
|
+
def sanitize_environment!
|
121
|
+
env.each do |key, value|
|
122
|
+
sanitized_environment[key] = value if ENV_METHODS.include?(key)
|
74
123
|
end
|
75
124
|
end
|
125
|
+
|
126
|
+
def sanitize_session_data!
|
127
|
+
@sanitized_session_data =
|
128
|
+
Appsignal::ParamsSanitizer.sanitize(request.session)
|
129
|
+
@fullpath = request.fullpath
|
130
|
+
end
|
131
|
+
|
76
132
|
end
|
77
133
|
end
|
@@ -2,35 +2,113 @@ module Appsignal
|
|
2
2
|
class ParamsSanitizer
|
3
3
|
class << self
|
4
4
|
def sanitize(params)
|
5
|
-
|
5
|
+
ParamsSanitizerCopy.sanitize_value(params)
|
6
6
|
end
|
7
7
|
|
8
|
-
|
8
|
+
def sanitize!(params)
|
9
|
+
ParamsSanitizerDestructive.sanitize_value(params)
|
10
|
+
end
|
9
11
|
|
10
|
-
def
|
11
|
-
|
12
|
-
hash.each_pair do |key, value|
|
13
|
-
out[key] = sanitize_value(value)
|
14
|
-
end
|
15
|
-
out
|
12
|
+
def scrub(params)
|
13
|
+
ParamsSanitizerCopyScrub.sanitize_value(params)
|
16
14
|
end
|
17
15
|
|
18
|
-
def
|
19
|
-
|
16
|
+
def scrub!(params)
|
17
|
+
ParamsSanitizerDestructiveScrub.sanitize_value(params)
|
20
18
|
end
|
21
19
|
|
20
|
+
protected
|
21
|
+
|
22
22
|
def sanitize_value(value)
|
23
23
|
case value
|
24
24
|
when Hash
|
25
25
|
sanitize_hash(value)
|
26
26
|
when Array
|
27
27
|
sanitize_array(value)
|
28
|
-
when String
|
29
|
-
value
|
28
|
+
when Fixnum, String, Symbol
|
29
|
+
unmodified(value)
|
30
30
|
else
|
31
|
-
value
|
31
|
+
inspected(value)
|
32
32
|
end
|
33
33
|
end
|
34
|
+
|
35
|
+
def sanitize_hash_with_target(source_hash, target_hash)
|
36
|
+
source_hash.each_pair do |key, value|
|
37
|
+
target_hash[key] = sanitize_value(value)
|
38
|
+
end
|
39
|
+
target_hash
|
40
|
+
end
|
41
|
+
|
42
|
+
def sanitize_array_with_target(source_array, target_array)
|
43
|
+
source_array.each_with_index do |item, index|
|
44
|
+
target_array[index] = sanitize_value(item)
|
45
|
+
end
|
46
|
+
target_array
|
47
|
+
end
|
48
|
+
|
49
|
+
def unmodified(value)
|
50
|
+
value
|
51
|
+
end
|
52
|
+
|
53
|
+
def inspected(value)
|
54
|
+
value.inspect
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
class ParamsSanitizerCopy < ParamsSanitizer
|
60
|
+
class << self
|
61
|
+
protected
|
62
|
+
|
63
|
+
def sanitize_hash(hash)
|
64
|
+
sanitize_hash_with_target(hash, {})
|
65
|
+
end
|
66
|
+
|
67
|
+
def sanitize_array(array)
|
68
|
+
sanitize_array_with_target(array, [])
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
class ParamsSanitizerDestructive < ParamsSanitizer
|
74
|
+
class << self
|
75
|
+
protected
|
76
|
+
|
77
|
+
def sanitize_hash(hash)
|
78
|
+
sanitize_hash_with_target(hash, hash)
|
79
|
+
end
|
80
|
+
|
81
|
+
def sanitize_array(array)
|
82
|
+
sanitize_array_with_target(array, array)
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
class ParamsSanitizerCopyScrub < ParamsSanitizerCopy
|
88
|
+
class << self
|
89
|
+
protected
|
90
|
+
|
91
|
+
def unmodified(value)
|
92
|
+
'?'
|
93
|
+
end
|
94
|
+
|
95
|
+
def inspected(value)
|
96
|
+
'?'
|
97
|
+
end
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
class ParamsSanitizerDestructiveScrub < ParamsSanitizerDestructive
|
102
|
+
class << self
|
103
|
+
protected
|
104
|
+
|
105
|
+
def unmodified(value)
|
106
|
+
'?'
|
107
|
+
end
|
108
|
+
|
109
|
+
def inspected(value)
|
110
|
+
'?'
|
111
|
+
end
|
34
112
|
end
|
35
113
|
end
|
36
114
|
end
|