rollbar 2.22.1 → 3.4.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.
Files changed (92) hide show
  1. checksums.yaml +4 -4
  2. data/.github/pull_request_template.md +34 -0
  3. data/.github/workflows/ci.yml +104 -0
  4. data/.rubocop.yml +185 -33
  5. data/Gemfile +26 -28
  6. data/README.md +32 -8
  7. data/data/rollbar.snippet.js +1 -1
  8. data/docs/configuration.md +8 -0
  9. data/gemfiles/rails30.gemfile +17 -35
  10. data/gemfiles/rails31.gemfile +20 -37
  11. data/gemfiles/rails32.gemfile +13 -31
  12. data/gemfiles/rails40.gemfile +12 -32
  13. data/gemfiles/rails41.gemfile +11 -31
  14. data/gemfiles/rails42.gemfile +12 -32
  15. data/gemfiles/rails50.gemfile +16 -30
  16. data/gemfiles/rails51.gemfile +16 -30
  17. data/gemfiles/rails52.gemfile +10 -19
  18. data/gemfiles/rails60.gemfile +10 -25
  19. data/gemfiles/rails61.gemfile +52 -0
  20. data/gemfiles/rails70.gemfile +52 -0
  21. data/lib/generators/rollbar/rollbar_generator.rb +18 -14
  22. data/lib/rails/rollbar_runner.rb +11 -20
  23. data/lib/rollbar/capistrano.rb +17 -9
  24. data/lib/rollbar/capistrano3.rb +8 -2
  25. data/lib/rollbar/capistrano_tasks.rb +44 -8
  26. data/lib/rollbar/configuration.rb +138 -84
  27. data/lib/rollbar/delay/girl_friday.rb +3 -7
  28. data/lib/rollbar/delay/resque.rb +2 -3
  29. data/lib/rollbar/delay/shoryuken.rb +4 -3
  30. data/lib/rollbar/delay/sidekiq.rb +5 -5
  31. data/lib/rollbar/delay/sucker_punch.rb +4 -6
  32. data/lib/rollbar/delay/thread.rb +17 -2
  33. data/lib/rollbar/deploy.rb +8 -7
  34. data/lib/rollbar/encoding/encoder.rb +17 -6
  35. data/lib/rollbar/encoding.rb +2 -7
  36. data/lib/rollbar/exception_reporter.rb +17 -8
  37. data/lib/rollbar/item/backtrace.rb +22 -10
  38. data/lib/rollbar/item/frame.rb +8 -5
  39. data/lib/rollbar/item/locals.rb +49 -2
  40. data/lib/rollbar/item.rb +80 -50
  41. data/lib/rollbar/json.rb +2 -1
  42. data/lib/rollbar/language_support.rb +0 -6
  43. data/lib/rollbar/lazy_store.rb +3 -7
  44. data/lib/rollbar/logger.rb +2 -0
  45. data/lib/rollbar/logger_proxy.rb +3 -1
  46. data/lib/rollbar/middleware/js/json_value.rb +15 -5
  47. data/lib/rollbar/middleware/js.rb +70 -38
  48. data/lib/rollbar/middleware/rack/builder.rb +3 -3
  49. data/lib/rollbar/middleware/rack/test_session.rb +3 -3
  50. data/lib/rollbar/middleware/rack.rb +4 -4
  51. data/lib/rollbar/middleware/rails/rollbar.rb +9 -6
  52. data/lib/rollbar/middleware/rails/show_exceptions.rb +8 -4
  53. data/lib/rollbar/notifier/trace_with_bindings.rb +13 -3
  54. data/lib/rollbar/notifier.rb +309 -172
  55. data/lib/rollbar/plugin.rb +8 -8
  56. data/lib/rollbar/plugins/active_job.rb +20 -3
  57. data/lib/rollbar/plugins/delayed_job/plugin.rb +19 -2
  58. data/lib/rollbar/plugins/error_context.rb +11 -0
  59. data/lib/rollbar/plugins/goalie.rb +27 -16
  60. data/lib/rollbar/plugins/rails/controller_methods.rb +18 -14
  61. data/lib/rollbar/plugins/rails/railtie30.rb +2 -1
  62. data/lib/rollbar/plugins/rails/railtie32.rb +2 -1
  63. data/lib/rollbar/plugins/rails/railtie_mixin.rb +2 -2
  64. data/lib/rollbar/plugins/rails.rb +5 -2
  65. data/lib/rollbar/plugins/rake.rb +2 -1
  66. data/lib/rollbar/plugins/sidekiq/plugin.rb +39 -21
  67. data/lib/rollbar/plugins/sidekiq.rb +1 -1
  68. data/lib/rollbar/plugins/thread.rb +8 -7
  69. data/lib/rollbar/plugins/validations.rb +3 -1
  70. data/lib/rollbar/rake_tasks.rb +1 -2
  71. data/lib/rollbar/request_data_extractor.rb +53 -19
  72. data/lib/rollbar/rollbar_test.rb +9 -118
  73. data/lib/rollbar/scrubbers/params.rb +13 -7
  74. data/lib/rollbar/scrubbers/url.rb +56 -17
  75. data/lib/rollbar/scrubbers.rb +2 -6
  76. data/lib/rollbar/truncation/frames_strategy.rb +1 -1
  77. data/lib/rollbar/truncation/mixin.rb +1 -1
  78. data/lib/rollbar/truncation/remove_any_key_strategy.rb +4 -1
  79. data/lib/rollbar/truncation/remove_extra_strategy.rb +3 -1
  80. data/lib/rollbar/truncation/strings_strategy.rb +4 -2
  81. data/lib/rollbar/util/hash.rb +14 -7
  82. data/lib/rollbar/util/ip_anonymizer.rb +1 -1
  83. data/lib/rollbar/util.rb +23 -13
  84. data/lib/rollbar/version.rb +1 -1
  85. data/lib/rollbar.rb +12 -7
  86. data/lib/tasks/benchmark.rake +2 -1
  87. data/rollbar.gemspec +6 -3
  88. data/spec/support/rollbar_api.rb +67 -0
  89. metadata +19 -12
  90. data/.travis.yml +0 -281
  91. data/lib/rollbar/encoding/legacy_encoder.rb +0 -20
  92. /data/lib/generators/rollbar/templates/{initializer.rb → initializer.erb} +0 -0
@@ -3,11 +3,7 @@ require 'rollbar/item/frame'
3
3
  module Rollbar
4
4
  class Item
5
5
  class Backtrace
6
- attr_reader :exception
7
- attr_reader :message
8
- attr_reader :extra
9
- attr_reader :configuration
10
- attr_reader :files
6
+ attr_reader :exception, :message, :extra, :configuration, :files
11
7
 
12
8
  private :files
13
9
 
@@ -54,7 +50,10 @@ module Rollbar
54
50
 
55
51
  current_exception = exception
56
52
 
57
- while current_exception.respond_to?(:cause) && (cause = current_exception.cause) && cause.is_a?(Exception) && !visited.include?(cause)
53
+ while current_exception.respond_to?(:cause) &&
54
+ (cause = current_exception.cause) &&
55
+ cause.is_a?(Exception) &&
56
+ !visited.include?(cause)
58
57
  traces << trace_data(cause)
59
58
  visited << cause
60
59
  current_exception = cause
@@ -74,10 +73,20 @@ module Rollbar
74
73
  end
75
74
 
76
75
  def map_frames(current_exception)
77
- exception_backtrace(current_exception).map do |frame|
76
+ frames = cleaned_backtrace(current_exception).map do |frame|
78
77
  Rollbar::Item::Frame.new(self, frame,
79
78
  :configuration => configuration).to_h
80
- end.reverse
79
+ end
80
+ frames.reverse!
81
+ end
82
+
83
+ def cleaned_backtrace(current_exception)
84
+ normalized_backtrace = exception_backtrace(current_exception)
85
+ if configuration.backtrace_cleaner
86
+ configuration.backtrace_cleaner.clean(normalized_backtrace)
87
+ else
88
+ normalized_backtrace
89
+ end
81
90
  end
82
91
 
83
92
  # Returns the backtrace to be sent to our API. There are 3 options:
@@ -90,7 +99,9 @@ module Rollbar
90
99
  # are those from the user's Rollbar.error line until this method. We want
91
100
  # to remove those lines.
92
101
  def exception_backtrace(current_exception)
93
- return current_exception.backtrace if current_exception.backtrace.respond_to?(:map)
102
+ if current_exception.backtrace.respond_to?(:map)
103
+ return current_exception.backtrace
104
+ end
94
105
  return [] unless configuration.populate_empty_backtraces
95
106
 
96
107
  caller_backtrace = caller
@@ -99,7 +110,8 @@ module Rollbar
99
110
  end
100
111
 
101
112
  def rollbar_lib_gem_dir
102
- Gem::Specification.find_by_name('rollbar').gem_dir + '/lib'
113
+ gem_dir = Gem::Specification.find_by_name('rollbar').gem_dir
114
+ "#{gem_dir}/lib"
103
115
  end
104
116
  end
105
117
  end
@@ -6,9 +6,7 @@ module Rollbar
6
6
  class Item
7
7
  # Representation of the trace data per frame in the payload
8
8
  class Frame
9
- attr_reader :backtrace
10
- attr_reader :frame
11
- attr_reader :configuration
9
+ attr_reader :backtrace, :frame, :configuration
12
10
 
13
11
  MAX_CONTEXT_LENGTH = 4
14
12
 
@@ -97,12 +95,15 @@ module Rollbar
97
95
  end
98
96
 
99
97
  def locals_data(filename, lineno)
98
+ return unless configuration.locals[:enabled]
99
+
100
100
  ::Rollbar::Item::Locals.locals_for_location(filename, lineno)
101
101
  end
102
102
 
103
103
  def post_data(file_lines, lineno)
104
104
  from_line = lineno
105
- number_of_lines = [from_line + MAX_CONTEXT_LENGTH, file_lines.size].min - from_line
105
+ number_of_lines = [from_line + MAX_CONTEXT_LENGTH,
106
+ file_lines.size].min - from_line
106
107
 
107
108
  file_lines[from_line, number_of_lines]
108
109
  end
@@ -111,7 +112,9 @@ module Rollbar
111
112
  to_line = lineno - 2
112
113
  from_line = [to_line - MAX_CONTEXT_LENGTH + 1, 0].max
113
114
 
114
- file_lines[from_line, (to_line - from_line + 1)].select { |line| line && !line.empty? }
115
+ file_lines[from_line, (to_line - from_line + 1)].select do |line|
116
+ line && !line.empty?
117
+ end
115
118
  end
116
119
  end
117
120
  end
@@ -1,5 +1,5 @@
1
- require 'rollbar/notifier'
2
1
  require 'rollbar/scrubbers/params'
2
+ require 'rollbar/util'
3
3
 
4
4
  module Rollbar
5
5
  class Item
@@ -32,6 +32,8 @@ module Rollbar
32
32
  end
33
33
 
34
34
  def locals_for(frame)
35
+ return {} unless frame
36
+
35
37
  {}.tap do |hash|
36
38
  frame.local_variables.map do |var|
37
39
  hash[var] = prepare_value(frame.local_variable_get(var))
@@ -39,8 +41,53 @@ module Rollbar
39
41
  end
40
42
  end
41
43
 
44
+ # Prepare objects to be handled by the payload serializer.
45
+ #
46
+ # Hashes and Arrays are traversed. Then all types execpt strings and
47
+ # immediates are exported using #inspect. Sending the object itself to the
48
+ # serializer can result in large recursive expansions, especially in Rails
49
+ # environments with ActiveRecord, ActiveSupport, etc. on the stack.
50
+ # Other export options could be #to_s, #to_h, and #as_json. Several of these
51
+ # will omit the class name, or are not implemented for many types.
52
+ #
53
+ # #inspect has the advantage that it is specifically intended for debugging
54
+ # output. If the user wants more or different information in the payload
55
+ # about a specific type, #inspect is the correct place to implement it.
56
+ # Likewise the default implementation should be oriented toward usefulness
57
+ # in debugging.
58
+ #
59
+ # Because #inspect outputs a string, it can be handled well by the string
60
+ # truncation strategy for large payloads.
61
+ #
42
62
  def prepare_value(value)
43
- value.to_s
63
+ unless value.is_a?(Hash) || value.is_a?(Array)
64
+ return simple_value?(value) ? value : value.inspect
65
+ end
66
+
67
+ cloned_value = ::Rollbar::Util.deep_copy(value)
68
+ ::Rollbar::Util.iterate_and_update_with_block(cloned_value) do |v|
69
+ simple_value?(v) ? v : v.inspect
70
+ end
71
+
72
+ cloned_value
73
+ end
74
+
75
+ def simple_classes
76
+ if Gem::Version.new(RUBY_VERSION) >= Gem::Version.new('2.4.0')
77
+ [String, Symbol, Integer, Float, TrueClass, FalseClass, NilClass]
78
+ else
79
+ [String, Symbol, Fixnum, Bignum, Float, TrueClass, FalseClass, NilClass] # rubocop:disable Lint/UnifiedInteger
80
+ end
81
+ end
82
+
83
+ def simple_value?(value)
84
+ simple_classes.each do |klass|
85
+ # Use instance_of? so that subclasses and module containers will
86
+ # be treated like complex object types, not simple values.
87
+ return true if value.instance_of?(klass)
88
+ end
89
+
90
+ false
44
91
  end
45
92
 
46
93
  def scrub(hash)
data/lib/rollbar/item.rb CHANGED
@@ -23,24 +23,15 @@ module Rollbar
23
23
 
24
24
  attr_writer :payload
25
25
 
26
- attr_reader :level
27
- attr_reader :message
28
- attr_reader :exception
29
- attr_reader :extra
30
-
31
- attr_reader :configuration
32
- attr_reader :scope
33
- attr_reader :logger
34
- attr_reader :notifier
35
-
36
- attr_reader :context
26
+ attr_reader :level, :message, :exception, :extra, :configuration, :scope, :logger,
27
+ :notifier, :context
37
28
 
38
29
  def_delegators :payload, :[]
39
30
 
40
31
  class << self
41
32
  def build_with(payload, options = {})
42
33
  new(options).tap do |item|
43
- item.payload = payload
34
+ item.payload = item.add_access_token_to_payload(payload)
44
35
  end
45
36
  end
46
37
  end
@@ -64,10 +55,7 @@ module Rollbar
64
55
 
65
56
  def build
66
57
  data = build_data
67
- self.payload = {
68
- 'access_token' => configuration.access_token,
69
- 'data' => data
70
- }
58
+ self.payload = add_access_token_to_payload({ 'data' => data })
71
59
 
72
60
  enforce_valid_utf8
73
61
  transform
@@ -75,7 +63,22 @@ module Rollbar
75
63
  end
76
64
 
77
65
  def build_data
78
- data = {
66
+ data = initial_data
67
+
68
+ build_optional_data(data)
69
+
70
+ Util.deep_merge(data, configuration.payload_options)
71
+ Util.deep_merge(data, scope)
72
+
73
+ # Our API doesn't allow null context values, so just delete
74
+ # the key if value is nil.
75
+ data.delete(:context) unless data[:context]
76
+
77
+ data
78
+ end
79
+
80
+ def initial_data
81
+ {
79
82
  :timestamp => Time.now.to_i,
80
83
  :environment => build_environment,
81
84
  :level => level,
@@ -89,35 +92,33 @@ module Rollbar
89
92
  },
90
93
  :body => build_body
91
94
  }
92
- data[:project_package_paths] = configuration.project_gem_paths if configuration.project_gem_paths.any?
93
- data[:code_version] = configuration.code_version if configuration.code_version
94
- data[:uuid] = SecureRandom.uuid if defined?(SecureRandom) && SecureRandom.respond_to?(:uuid)
95
+ end
95
96
 
96
- Util.deep_merge(data, configuration.payload_options)
97
- Util.deep_merge(data, scope)
97
+ def build_optional_data(data)
98
+ if configuration.project_gem_paths.any?
99
+ data[:project_package_paths] = configuration.project_gem_paths
100
+ end
98
101
 
99
- # Our API doesn't allow null context values, so just delete
100
- # the key if value is nil.
101
- data.delete(:context) unless data[:context]
102
+ data[:code_version] = configuration.code_version if configuration.code_version
102
103
 
103
- data
104
+ return unless defined?(SecureRandom) && SecureRandom.respond_to?(:uuid)
105
+
106
+ data[:uuid] = SecureRandom.uuid
104
107
  end
105
108
 
106
109
  def configured_options
107
- if Gem.loaded_specs['activesupport'] && Gem.loaded_specs['activesupport'].version < Gem::Version.new('4.1')
108
- # There are too many types that crash ActiveSupport JSON serialization, and not worth
109
- # the risk just to send this diagnostic object. In versions < 4.1, ActiveSupport hooks
110
- # Ruby's JSON.generate so deeply there's no workaround.
110
+ if Gem.loaded_specs['activesupport'] &&
111
+ Gem.loaded_specs['activesupport'].version < Gem::Version.new('4.1')
112
+ # There are too many types that crash ActiveSupport JSON serialization,
113
+ # and not worth the risk just to send this diagnostic object.
114
+ # In versions < 4.1, ActiveSupport hooks Ruby's JSON.generate so deeply
115
+ # that there's no workaround.
111
116
  'not serialized in ActiveSupport < 4.1'
112
- elsif configuration.use_async
113
- # Currently serialization is performed by each handler, and this invariably
114
- # means it is actually performed by ActiveSupport.
115
- #
116
- # TODO: Since serialization must be done prior to scheduling the job,
117
- # it should at least be done by rollbar-gem itself. Much work has been done
118
- # to avoid the bugs in ActiveSupport JSON. The async handlers are currently
119
- # still subject to all those knnown issues.
120
- 'not serialized for async/delayed handlers'
117
+ elsif configuration.use_async && !configuration.async_json_payload
118
+ # The setting allows serialization to be performed by each handler,
119
+ # and this usually means it is actually performed by ActiveSupport,
120
+ # which cannot safely serialize this key.
121
+ 'not serialized when async_json_payload is not set'
121
122
  else
122
123
  scrub(configuration.configured_options.configured)
123
124
  end
@@ -137,22 +138,28 @@ module Rollbar
137
138
  nil
138
139
  end
139
140
 
140
- def handle_too_large_payload(stringified_payload, final_payload, attempts)
141
+ def handle_too_large_payload(stringified_payload, _final_payload, attempts)
141
142
  uuid = stringified_payload['data']['uuid']
142
143
  host = stringified_payload['data'].fetch('server', {})['host']
143
144
 
144
- notifier.send_failsafe(
145
- too_large_payload_string(attempts),
146
- nil,
147
- uuid,
148
- host
149
- )
150
- logger.error("[Rollbar] Payload too large to be sent for UUID #{uuid}: #{Rollbar::JSON.dump(payload)}")
145
+ original_error = {
146
+ :message => message,
147
+ :exception => exception,
148
+ :configuration => configuration,
149
+ :uuid => uuid,
150
+ :host => host
151
+ }
152
+
153
+ notifier.send_failsafe(too_large_payload_string(attempts), nil, original_error)
154
+
155
+ logger.error('[Rollbar] Payload too large to be sent for UUID ' \
156
+ "#{uuid}: #{Rollbar::JSON.dump(payload)}")
151
157
  end
152
158
 
153
159
  def too_large_payload_string(attempts)
154
160
  'Could not send payload due to it being too large after truncating attempts. ' \
155
- "Original size: #{attempts.first} Attempts: #{attempts.join(', ')} Final size: #{attempts.last}"
161
+ "Original size: #{attempts.first} Attempts: #{attempts.join(', ')} " \
162
+ "Final size: #{attempts.last}"
156
163
  end
157
164
 
158
165
  def ignored?
@@ -164,6 +171,22 @@ module Rollbar
164
171
  configuration.ignored_person_ids.include?(person_id)
165
172
  end
166
173
 
174
+ def add_access_token_to_payload(payload)
175
+ # Some use cases remain where the token is needed in the payload. For example:
176
+ #
177
+ # When using async senders, if the access token is changed dynamically in
178
+ # the main process config, the sender process won't see that change.
179
+ #
180
+ # Until the delayed sender interface is changed to allow passing dynamic
181
+ # config options, this workaround allows the main process to set the token
182
+ # by adding it to the payload.
183
+ if configuration && configuration.use_payload_access_token
184
+ payload['access_token'] ||= configuration.access_token
185
+ end
186
+
187
+ payload
188
+ end
189
+
167
190
  private
168
191
 
169
192
  def build_environment
@@ -187,13 +210,20 @@ module Rollbar
187
210
  end
188
211
 
189
212
  def build_extra
213
+ merged_extra = Util.deep_merge(scrub(extra), scrub(error_context))
214
+
190
215
  if custom_data_method? && !Rollbar::Util.method_in_stack(:custom_data, __FILE__)
191
- Util.deep_merge(scrub(custom_data), scrub(extra) || {})
216
+ Util.deep_merge(scrub(custom_data), merged_extra)
192
217
  else
193
- scrub(extra)
218
+ # avoid putting an empty {} in the payload.
219
+ merged_extra.empty? ? nil : merged_extra
194
220
  end
195
221
  end
196
222
 
223
+ def error_context
224
+ exception.respond_to?(:rollbar_context) && exception.rollbar_context
225
+ end
226
+
197
227
  def scrub(data)
198
228
  return data unless data.is_a? Hash
199
229
 
data/lib/rollbar/json.rb CHANGED
@@ -1,8 +1,9 @@
1
1
  require 'rollbar/language_support'
2
+ require 'json'
2
3
 
3
4
  module Rollbar
4
5
  module JSON # :nodoc:
5
- extend self
6
+ module_function
6
7
 
7
8
  attr_writer :options_module
8
9
 
@@ -10,10 +10,6 @@ module Rollbar
10
10
  mod.const_get(target, inherit)
11
11
  end
12
12
 
13
- def ruby_19?
14
- version?('1.9')
15
- end
16
-
17
13
  def version?(version)
18
14
  numbers = version.split('.')
19
15
 
@@ -21,8 +17,6 @@ module Rollbar
21
17
  end
22
18
 
23
19
  def timeout_exceptions
24
- return [] if ruby_19?
25
-
26
20
  [Net::ReadTimeout, Net::OpenTimeout]
27
21
  end
28
22
  end
@@ -1,10 +1,8 @@
1
1
  module Rollbar
2
2
  class LazyStore
3
- attr_reader :loaded_data
3
+ attr_reader :loaded_data, :raw
4
4
  private :loaded_data
5
5
 
6
- attr_reader :raw
7
-
8
6
  def initialize(initial_data)
9
7
  initial_data ||= {}
10
8
 
@@ -41,8 +39,6 @@ module Rollbar
41
39
  raw[key] = value
42
40
 
43
41
  loaded_data.delete(key)
44
-
45
- value
46
42
  end
47
43
 
48
44
  def data
@@ -76,8 +72,8 @@ module Rollbar
76
72
  super
77
73
  end
78
74
 
79
- def respond_to?(method_sym)
80
- super || raw.respond_to?(method_sym)
75
+ def respond_to_missing?(method_sym, include_all)
76
+ raw.respond_to?(method_sym, include_all)
81
77
  end
82
78
  end
83
79
  end
@@ -16,7 +16,9 @@ module Rollbar
16
16
  # Rails.logger.extend(ActiveSupport::Logger.broadcast(Rollbar::Logger.new))
17
17
  class Logger < ::Logger
18
18
  class Error < RuntimeError; end
19
+
19
20
  class DatetimeFormatNotSupported < Error; end
21
+
20
22
  class FormatterNotSupported < Error; end
21
23
 
22
24
  def initialize
@@ -23,7 +23,9 @@ module Rollbar
23
23
  end
24
24
 
25
25
  def log(level, message)
26
- return unless Rollbar.configuration.enabled && acceptable_levels.include?(level.to_sym)
26
+ unless Rollbar.configuration.enabled && acceptable_levels.include?(level.to_sym)
27
+ return
28
+ end
27
29
 
28
30
  @object.send(level, message)
29
31
  rescue StandardError
@@ -1,8 +1,12 @@
1
- # Allows a Ruby String to be used to create native Javascript objects
2
- # when calling JSON#generate.
1
+ # Allows a Ruby String to be used to pass native Javascript objects/functions
2
+ # when calling JSON#generate with a Rollbar::JSON::JsOptionsState instance.
3
3
  #
4
4
  # Example:
5
- # JSON.generate({ foo: Rollbar::JSON::Value.new('function(){ alert("bar") }') })
5
+ # JSON.generate(
6
+ # { foo: Rollbar::JSON::Value.new('function(){ alert("bar") }') },
7
+ # Rollbar::JSON::JsOptionsState.new
8
+ # )
9
+ #
6
10
  # => '{"foo":function(){ alert(\"bar\") }}'
7
11
  #
8
12
  # MUST use the Ruby JSON encoder, as in the example. The ActiveSupport encoder,
@@ -11,6 +15,8 @@
11
15
  #
12
16
  module Rollbar
13
17
  module JSON
18
+ class JsOptionsState < ::JSON::State; end
19
+
14
20
  class Value # :nodoc:
15
21
  attr_accessor :value
16
22
 
@@ -18,8 +24,12 @@ module Rollbar
18
24
  @value = value
19
25
  end
20
26
 
21
- def to_json(*_args)
22
- value
27
+ def to_json(opts = {})
28
+ # Return the raw value if this is from the js middleware
29
+ return value if opts.class == Rollbar::JSON::JsOptionsState
30
+
31
+ # Otherwise convert to a string
32
+ %Q["#{value}"]
23
33
  end
24
34
  end
25
35
  end