sentry-raven 0.10.1 → 0.11.1

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of sentry-raven might be problematic. Click here for more details.

checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 057d8d70d5fbf847fa8c41a33a8dabc5f4bf1f4d
4
- data.tar.gz: 2042e4bbae25824369c260746f21c31b64d3c4f4
3
+ metadata.gz: 6edaa8907b593e814f5fac83ed1fcedd5f48138d
4
+ data.tar.gz: a5ba9906fdee7ae55db2245962b605d194c8bc78
5
5
  SHA512:
6
- metadata.gz: a74a58c59287a2d4fde07886f26d9634e08f1b3b5bcf9c5c793facaec632094a83e047f426953a88b193911b71e4364fa541a0276b851f2dc86c1ea1a1aa7913
7
- data.tar.gz: de5656fd29ce7aff1ad06c919179363f3330ff05cd4a7ab0ad17a733fb300d4468c2e3b487b4c9480abf9841d8ed28a928336d31d8df0d7d9a979c1f0cf9938b
6
+ metadata.gz: a8755f16a1d1ee91c7195d9cca81fc4257acf6f9913e4d0452772d282efaece4448cb7ea701175f0768102fae2e09454325cdb238ef505df600fcf6a7067139b
7
+ data.tar.gz: 7a993be651953b5d57dd582baef3eecf4d9d791b8465b5dce2eee901610f38ba452e68f09c07c178cb410d03c77cdb0281627ea5c32d827ba6b51032d6c8656a
data/README.md CHANGED
@@ -8,59 +8,42 @@ A client and integration layer for the [Sentry](https://github.com/getsentry/sen
8
8
 
9
9
  ## Requirements
10
10
 
11
- We test on Ruby MRI 1.8.7, 1.9.3 and 2.0.0. Rubinius and JRuby support is experimental.
11
+ We test on Ruby MRI 1.8.7/REE, 1.9.3, 2.0 and 2.1. JRuby support is experimental - check TravisCI to see if the build is passing or failing.
12
12
 
13
13
  ## Installation
14
14
 
15
15
  ```ruby
16
- gem "sentry-raven" #, :github => "getsentry/raven-ruby"
16
+ gem "sentry-raven", :require => 'raven' #, :github => "getsentry/raven-ruby"
17
17
  ```
18
18
 
19
19
  ## Usage
20
20
 
21
- The minimum configuration require setting the ``SENTRY_DSN`` with value found on your Sentry project settings page. It should resemble something like ```https://public:secret@app.getsentry.com/9999```. See [Configuration](#configuration) for configuration methods, and other options.
21
+ Set the ``SENTRY_DSN`` environment variable with the value found on your Sentry project settings page. It should resemble something like ```https://public:secret@app.getsentry.com/9999```.
22
22
 
23
23
  Many implementations will automatically capture uncaught exceptions (such as Rails, Sidekiq or by using
24
24
  the Rack middleware). If you catch those exceptions yourself, but still want to report on them, see section [Capturing Events](#capturing-events).
25
25
 
26
- ### Rails 3 or 4
26
+ ### Rails
27
27
 
28
- In Rails 3 or 4 all uncaught exceptions will be automatically reported under most situations.
28
+ In Rails, all uncaught exceptions will be automatically reported.
29
29
 
30
- You'll still want to ensure you've disabled anything that would prevent errors from being propagated to the ```Raven::Rack``` middleware:
31
-
32
- Disable ```ActionDispatch::ShowExceptions```:
30
+ You'll still want to ensure you've disabled anything that would prevent errors from being propagated to the ```Raven::Rack``` middleware, like ```ActionDispatch::ShowExceptions```:
33
31
 
34
32
  ```ruby
35
- config.action_dispatch.show_exceptions = false
33
+ config.action_dispatch.show_exceptions = false # this is the default setting in production
36
34
  ```
37
35
 
38
- #### Delayed::Job
39
-
40
- No extra configuration required. Usage of [delayed-plugins-raven](https://github.com/qiushihe/delayed-plugins-raven) gem is deprecated.
41
-
42
- ### Rails 2
43
-
44
- No support for Rails 2 yet, but it is being worked on.
45
-
46
36
  ### Rack
47
37
 
48
- Add ```use Raven::Rack``` to your ```config.ru``` (or other rackup file).
38
+ Add ```use Raven::Rack``` to your ```config.ru``` or other rackup file (this is automatically inserted in Rails).
49
39
 
50
40
  ### Sinatra
51
41
 
52
42
  Like any other Rack middleware, add ```use Raven::Rack``` to your Sinatra app.
53
43
 
54
- ### Sidekiq
55
-
56
- Raven includes [Sidekiq middleware](https://github.com/mperham/sidekiq/wiki/Middleware) which takes
57
- care of reporting errors that occur in Sidekiq jobs. To use it, just require the middleware by doing
58
-
59
- ```ruby
60
- require 'raven/sidekiq'
61
- ```
62
- after you require Sidekiq. If you are using Sidekiq with Rails, just put this require somewhere in the initializers.
44
+ ### Sidekiq, Delayed::Job and Rake
63
45
 
46
+ Raven works out-of-the-box with all these tools!
64
47
 
65
48
  ## Capturing Events
66
49
 
@@ -166,7 +149,6 @@ class UserSession < Authlogic::Session::Base
166
149
  end
167
150
  ```
168
151
 
169
-
170
152
  ## Configuration
171
153
 
172
154
  ### SENTRY_DSN
@@ -176,6 +158,7 @@ After you complete setting up a project, you'll be given a value which we call a
176
158
  With Raven, you may either set the ```SENTRY_DSN``` environment variable (recommended), or set your DSN manually in a config block:
177
159
 
178
160
  ```ruby
161
+ # in Rails, this might be in config/initializers/sentry.rb
179
162
  Raven.configure do |config|
180
163
  config.dsn = 'http://public:secret@example.com/project-id'
181
164
  end
@@ -183,7 +166,7 @@ end
183
166
 
184
167
  ### Environments
185
168
 
186
- By default, events will be sent to Sentry in all environments. If you do not wish
169
+ As of [v0.10.0](https://github.com/getsentry/raven-ruby/blob/21cb3164e0d0ab91394ba98b78195c4f6342b4bb/changelog.md#0100), events will be sent to Sentry in all environments. If you do not wish
187
170
  to send events in an environment, we suggest you unset the ```SENTRY_DSN```
188
171
  variable in that environment.
189
172
 
@@ -215,7 +198,17 @@ Raven.configure do |config|
215
198
  end
216
199
  ```
217
200
 
218
- You can find the list of exceptions that are excluded by default in [Raven::Configuration::IGNORE_DEFAULT](https://github.com/getsentry/raven-ruby/blob/master/lib/raven/configuration.rb#L74-L80). Remember you'll be overriding those defaults by setting this configuration.
201
+ You can find the list of exceptions that are excluded by default in [Raven::Configuration::IGNORE_DEFAULT](https://github.com/getsentry/raven-ruby/blob/master/lib/raven/configuration.rb). Remember you'll be overriding those defaults by setting this configuration.
202
+
203
+ You can also use a configuration option to determine if an individual event should
204
+ be sent to Sentry. Events are passed to the Proc or lambda you provide - returning
205
+ `false` will stop the event from sending to Sentry:
206
+
207
+ ```ruby
208
+ Raven.configure do |config|
209
+ config.should_send = Proc.new { |e| true unless e.contains_sensitive_info? }
210
+ end
211
+ ```
219
212
 
220
213
  ### Tags
221
214
 
@@ -10,7 +10,10 @@ require 'raven/interfaces/message'
10
10
  require 'raven/interfaces/exception'
11
11
  require 'raven/interfaces/stack_trace'
12
12
  require 'raven/interfaces/http'
13
- require 'raven/processors/sanitizedata'
13
+ require 'raven/processor'
14
+ require 'raven/processor/sanitizedata'
15
+ require 'raven/processor/removecircularreferences'
16
+ require 'raven/processor/utf8conversion'
14
17
 
15
18
  module Raven
16
19
  class << self
@@ -98,7 +101,7 @@ module Raven
98
101
  end
99
102
 
100
103
  def capture_exception(exception, options = {})
101
- send_or_skip do
104
+ send_or_skip(exception) do
102
105
  if evt = Event.from_exception(exception, options)
103
106
  yield evt if block_given?
104
107
  if configuration.async?
@@ -111,7 +114,7 @@ module Raven
111
114
  end
112
115
 
113
116
  def capture_message(message, options = {})
114
- send_or_skip do
117
+ send_or_skip(message) do
115
118
  if evt = Event.from_message(message, options)
116
119
  yield evt if block_given?
117
120
  if configuration.async?
@@ -123,8 +126,14 @@ module Raven
123
126
  end
124
127
  end
125
128
 
126
- def send_or_skip
127
- if configuration.send_in_current_environment?
129
+ def send_or_skip(exc)
130
+ should_send = if configuration.should_send
131
+ configuration.should_send.call(*[exc])
132
+ else
133
+ true
134
+ end
135
+
136
+ if configuration.send_in_current_environment? && should_send
128
137
  yield if block_given?
129
138
  else
130
139
  configuration.log_excluded_environment_message
@@ -198,11 +207,13 @@ module Raven
198
207
 
199
208
  # Injects various integrations
200
209
  def inject
201
- # TODO(dcramer): integrations should have a way to opt-out
202
210
  require 'raven/integrations/delayed_job' if defined?(::Delayed::Plugin)
203
211
  require 'raven/railtie' if defined?(::Rails::Railtie)
204
212
  require 'raven/sidekiq' if defined?(Sidekiq)
205
- require 'raven/tasks' if defined?(Rake)
213
+ if defined?(Rake)
214
+ require 'raven/rake'
215
+ require 'raven/tasks'
216
+ end
206
217
  end
207
218
 
208
219
  # For cross-language compat
@@ -46,8 +46,10 @@ module Raven
46
46
  evt = Raven.capture_exception(exception)
47
47
  end
48
48
 
49
- if evt
49
+ if evt && !(evt.is_a? Thread)
50
50
  puts "-> event ID: #{evt.id}"
51
+ elsif evt #async configuration
52
+ puts "-> event ID: #{evt.value.id}"
51
53
  else
52
54
  puts ""
53
55
  puts "An error occurred while attempting to send the event."
@@ -80,6 +80,9 @@ module Raven
80
80
  # ActionDispatch::ShowExceptions or ActionDispatch::DebugExceptions
81
81
  attr_accessor :catch_debugged_exceptions
82
82
 
83
+ # Provide a configurable callback to block or send events
84
+ attr_accessor :should_send
85
+
83
86
  IGNORE_DEFAULT = ['ActiveRecord::RecordNotFound',
84
87
  'ActionController::RoutingError',
85
88
  'ActionController::InvalidAuthenticityToken',
@@ -94,7 +97,7 @@ module Raven
94
97
  self.current_environment = ENV['RAILS_ENV'] || ENV['RACK_ENV'] || 'default'
95
98
  self.send_modules = true
96
99
  self.excluded_exceptions = IGNORE_DEFAULT
97
- self.processors = [Raven::Processor::SanitizeData]
100
+ self.processors = [Raven::Processor::RemoveCircularReferences, Raven::Processor::UTF8Conversion, Raven::Processor::SanitizeData]
98
101
  self.ssl_verification = false
99
102
  self.encoding = 'gzip'
100
103
  self.timeout = 1
@@ -1,13 +1,25 @@
1
+ require 'json'
2
+
1
3
  module Raven
2
- module Processor
3
- class Processor
4
- def initialize(client)
5
- @client = client
6
- end
4
+ class Processor
5
+ def initialize(client)
6
+ @client = client
7
+ end
7
8
 
8
- def process(data)
9
- data
9
+ def process(data)
10
+ data
11
+ end
12
+
13
+ private
14
+
15
+ def parse_json_or_nil(string)
16
+ begin
17
+ result = OkJson.decode(string)
18
+ result.is_a?(String) ? nil : result
19
+ rescue Raven::OkJson::Error
20
+ nil
10
21
  end
11
22
  end
23
+
12
24
  end
13
25
  end
@@ -0,0 +1,17 @@
1
+ module Raven
2
+ class Processor::RemoveCircularReferences < Processor
3
+
4
+ def process(v, visited = [])
5
+ return "(...)" if visited.include?(v.__id__)
6
+ visited += [v.__id__]
7
+ if v.is_a?(Hash)
8
+ v.reduce({}) { |memo, (k, v_)| memo[k] = process(v_, visited); memo }
9
+ elsif v.is_a?(Array)
10
+ v.map { |v_| process(v_, visited) }
11
+ else
12
+ v
13
+ end
14
+ end
15
+
16
+ end
17
+ end
@@ -0,0 +1,26 @@
1
+ module Raven
2
+ class Processor::SanitizeData < Processor
3
+ STRING_MASK = '********'
4
+ INT_MASK = 0
5
+ FIELDS_RE = /(authorization|password|passwd|secret|ssn|social(.*)?sec)/i
6
+ VALUES_RE = /^\d{16}$/
7
+
8
+ def process(value)
9
+ value.merge(value) do |k, v|
10
+ if v.is_a?(Hash)
11
+ process(v)
12
+ elsif v.is_a?(String) && (json_hash = parse_json_or_nil(v))
13
+ #if this string is actually a json obj, convert and sanitize
14
+ process(json_hash).to_json
15
+ elsif v.is_a?(Integer) && (VALUES_RE.match(v.to_s) || FIELDS_RE.match(k))
16
+ INT_MASK
17
+ elsif VALUES_RE.match(v.to_s) || FIELDS_RE.match(k)
18
+ STRING_MASK
19
+ else
20
+ v
21
+ end
22
+ end
23
+ end
24
+ end
25
+ end
26
+
@@ -0,0 +1,26 @@
1
+ module Raven
2
+ class Processor::UTF8Conversion < Processor
3
+
4
+ def process(value)
5
+ if value.is_a? Array
6
+ value.map { |v_| process v_ }
7
+ elsif value.is_a? Hash
8
+ value.merge(value) { |k, v_| process v_ }
9
+ else
10
+ clean_invalid_utf8_bytes(value)
11
+ end
12
+ end
13
+
14
+ private
15
+
16
+ def clean_invalid_utf8_bytes(obj)
17
+ if obj.respond_to?(:to_utf8)
18
+ obj.to_utf8
19
+ elsif obj.respond_to?(:encoding)
20
+ obj.encode('UTF-16', :invalid => :replace, :undef => :replace, :replace => '').encode('UTF-8')
21
+ else
22
+ obj
23
+ end
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,13 @@
1
+ require 'raven'
2
+ require 'rake/task'
3
+
4
+ module Rake
5
+ class Application
6
+ alias :orig_display_error_messsage :display_error_message
7
+ def display_error_message(ex)
8
+ Raven.capture_exception ex, :logger => 'rake', :tags => { 'rake_task' => @name }
9
+ orig_display_error_messsage(ex)
10
+ end
11
+ end
12
+ end
13
+
@@ -1,3 +1,3 @@
1
1
  module Raven
2
- VERSION = "0.10.1"
2
+ VERSION = "0.11.1"
3
3
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: sentry-raven
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.10.1
4
+ version: 0.11.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Noah Kantrowitz
@@ -9,104 +9,104 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2014-09-11 00:00:00.000000000 Z
12
+ date: 2014-11-02 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: faraday
16
16
  requirement: !ruby/object:Gem::Requirement
17
17
  requirements:
18
- - - '>='
18
+ - - ">="
19
19
  - !ruby/object:Gem::Version
20
20
  version: 0.7.6
21
21
  type: :runtime
22
22
  prerelease: false
23
23
  version_requirements: !ruby/object:Gem::Requirement
24
24
  requirements:
25
- - - '>='
25
+ - - ">="
26
26
  - !ruby/object:Gem::Version
27
27
  version: 0.7.6
28
28
  - !ruby/object:Gem::Dependency
29
29
  name: uuidtools
30
30
  requirement: !ruby/object:Gem::Requirement
31
31
  requirements:
32
- - - '>='
32
+ - - ">="
33
33
  - !ruby/object:Gem::Version
34
34
  version: '0'
35
35
  type: :runtime
36
36
  prerelease: false
37
37
  version_requirements: !ruby/object:Gem::Requirement
38
38
  requirements:
39
- - - '>='
39
+ - - ">="
40
40
  - !ruby/object:Gem::Version
41
41
  version: '0'
42
42
  - !ruby/object:Gem::Dependency
43
43
  name: rake
44
44
  requirement: !ruby/object:Gem::Requirement
45
45
  requirements:
46
- - - '>='
46
+ - - ">="
47
47
  - !ruby/object:Gem::Version
48
48
  version: '0'
49
49
  type: :development
50
50
  prerelease: false
51
51
  version_requirements: !ruby/object:Gem::Requirement
52
52
  requirements:
53
- - - '>='
53
+ - - ">="
54
54
  - !ruby/object:Gem::Version
55
55
  version: '0'
56
56
  - !ruby/object:Gem::Dependency
57
57
  name: rspec
58
58
  requirement: !ruby/object:Gem::Requirement
59
59
  requirements:
60
- - - ~>
60
+ - - "~>"
61
61
  - !ruby/object:Gem::Version
62
62
  version: '3.0'
63
63
  type: :development
64
64
  prerelease: false
65
65
  version_requirements: !ruby/object:Gem::Requirement
66
66
  requirements:
67
- - - ~>
67
+ - - "~>"
68
68
  - !ruby/object:Gem::Version
69
69
  version: '3.0'
70
70
  - !ruby/object:Gem::Dependency
71
71
  name: mime-types
72
72
  requirement: !ruby/object:Gem::Requirement
73
73
  requirements:
74
- - - ~>
74
+ - - "~>"
75
75
  - !ruby/object:Gem::Version
76
76
  version: '1.16'
77
77
  type: :development
78
78
  prerelease: false
79
79
  version_requirements: !ruby/object:Gem::Requirement
80
80
  requirements:
81
- - - ~>
81
+ - - "~>"
82
82
  - !ruby/object:Gem::Version
83
83
  version: '1.16'
84
84
  - !ruby/object:Gem::Dependency
85
85
  name: coveralls
86
86
  requirement: !ruby/object:Gem::Requirement
87
87
  requirements:
88
- - - '>='
88
+ - - ">="
89
89
  - !ruby/object:Gem::Version
90
90
  version: '0'
91
91
  type: :development
92
92
  prerelease: false
93
93
  version_requirements: !ruby/object:Gem::Requirement
94
94
  requirements:
95
- - - '>='
95
+ - - ">="
96
96
  - !ruby/object:Gem::Version
97
97
  version: '0'
98
98
  - !ruby/object:Gem::Dependency
99
99
  name: rest-client
100
100
  requirement: !ruby/object:Gem::Requirement
101
101
  requirements:
102
- - - '>='
102
+ - - ">="
103
103
  - !ruby/object:Gem::Version
104
104
  version: '0'
105
105
  type: :development
106
106
  prerelease: false
107
107
  version_requirements: !ruby/object:Gem::Requirement
108
108
  requirements:
109
- - - '>='
109
+ - - ">="
110
110
  - !ruby/object:Gem::Version
111
111
  version: '0'
112
112
  description:
@@ -118,6 +118,10 @@ extra_rdoc_files:
118
118
  - README.md
119
119
  - LICENSE
120
120
  files:
121
+ - LICENSE
122
+ - README.md
123
+ - bin/raven
124
+ - lib/raven.rb
121
125
  - lib/raven/backtrace.rb
122
126
  - lib/raven/base.rb
123
127
  - lib/raven/better_attr_accessor.rb
@@ -128,31 +132,30 @@ files:
128
132
  - lib/raven/error.rb
129
133
  - lib/raven/event.rb
130
134
  - lib/raven/integrations/delayed_job.rb
135
+ - lib/raven/interfaces.rb
131
136
  - lib/raven/interfaces/exception.rb
132
137
  - lib/raven/interfaces/http.rb
133
138
  - lib/raven/interfaces/message.rb
134
139
  - lib/raven/interfaces/stack_trace.rb
135
- - lib/raven/interfaces.rb
136
140
  - lib/raven/linecache.rb
137
141
  - lib/raven/logger.rb
138
142
  - lib/raven/okjson.rb
139
143
  - lib/raven/processor.rb
140
- - lib/raven/processors/sanitizedata.rb
144
+ - lib/raven/processor/removecircularreferences.rb
145
+ - lib/raven/processor/sanitizedata.rb
146
+ - lib/raven/processor/utf8conversion.rb
141
147
  - lib/raven/rack.rb
142
148
  - lib/raven/rails/controller_methods.rb
143
149
  - lib/raven/rails/middleware/debug_exceptions_catcher.rb
144
150
  - lib/raven/railtie.rb
151
+ - lib/raven/rake.rb
145
152
  - lib/raven/sidekiq.rb
146
153
  - lib/raven/tasks.rb
154
+ - lib/raven/transports.rb
147
155
  - lib/raven/transports/http.rb
148
156
  - lib/raven/transports/udp.rb
149
- - lib/raven/transports.rb
150
157
  - lib/raven/version.rb
151
- - lib/raven.rb
152
158
  - lib/sentry-raven.rb
153
- - README.md
154
- - LICENSE
155
- - bin/raven
156
159
  homepage: http://github.com/getsentry/raven-ruby
157
160
  licenses:
158
161
  - Apache-2.0
@@ -163,17 +166,17 @@ require_paths:
163
166
  - lib
164
167
  required_ruby_version: !ruby/object:Gem::Requirement
165
168
  requirements:
166
- - - '>='
169
+ - - ">="
167
170
  - !ruby/object:Gem::Version
168
171
  version: '0'
169
172
  required_rubygems_version: !ruby/object:Gem::Requirement
170
173
  requirements:
171
- - - '>='
174
+ - - ">="
172
175
  - !ruby/object:Gem::Version
173
176
  version: '0'
174
177
  requirements: []
175
178
  rubyforge_project:
176
- rubygems_version: 2.0.3
179
+ rubygems_version: 2.2.2
177
180
  signing_key:
178
181
  specification_version: 4
179
182
  summary: A gem that provides a client interface for the Sentry error logger
@@ -1,76 +0,0 @@
1
- require 'raven/processor'
2
- require 'json'
3
-
4
- module Raven
5
- module Processor
6
- class SanitizeData < Processor
7
-
8
- MASK = '********'
9
- FIELDS_RE = /(authorization|password|passwd|secret)/i
10
- VALUES_RE = /^\d{16}$/
11
-
12
- def apply(value, key = nil, visited = [], &block)
13
- if value.is_a?(Hash)
14
- return "{...}" if visited.include?(value.__id__)
15
- visited += [value.__id__]
16
-
17
- value.each.reduce({}) do |memo, (k, v)|
18
- memo[k] = apply(v, k, visited, &block)
19
- memo
20
- end
21
- elsif value.is_a?(Array)
22
- return "[...]" if visited.include?(value.__id__)
23
- visited += [value.__id__]
24
-
25
- value.map do |value_|
26
- apply(value_, key, visited, &block)
27
- end
28
- elsif value.is_a?(String) && json_hash = JSON.parse(value) rescue nil
29
- return "[...]" if visited.include?(value.__id__)
30
- visited += [value.__id__]
31
-
32
- json_hash = json_hash.each.reduce({}) do |memo, (k, v)|
33
- memo[k] = apply(v, k, visited, &block)
34
- memo
35
- end
36
-
37
- json_hash.to_json
38
- else
39
- block.call(key, value)
40
- end
41
- end
42
-
43
- def sanitize(key, value)
44
- if !value.is_a?(String) || value.empty?
45
- value
46
- elsif VALUES_RE.match(clean_invalid_utf8_bytes(value)) || FIELDS_RE.match(key)
47
- MASK
48
- else
49
- clean_invalid_utf8_bytes(value)
50
- end
51
- end
52
-
53
- def process(data)
54
- apply(data) do |key, value|
55
- sanitize(key, value)
56
- end
57
- end
58
-
59
- private
60
-
61
- def clean_invalid_utf8_bytes(text)
62
- if RUBY_VERSION <= '1.8.7'
63
- text
64
- else
65
- text.encode(
66
- 'UTF-8',
67
- 'binary',
68
- :invalid => :replace,
69
- :undef => :replace,
70
- :replace => ''
71
- )
72
- end
73
- end
74
- end
75
- end
76
- end