honeybadger 2.1.1 → 2.1.3

Sign up to get free protection for your applications and to get access to all the features.
Files changed (44) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +108 -164
  3. data/lib/honeybadger/agent/worker.rb +2 -2
  4. data/lib/honeybadger/backend/base.rb +15 -1
  5. data/lib/honeybadger/cli.rb +1 -1
  6. data/lib/honeybadger/cli/heroku.rb +0 -2
  7. data/lib/honeybadger/notice.rb +5 -5
  8. data/lib/honeybadger/trace.rb +3 -2
  9. data/lib/honeybadger/util/sanitizer.rb +31 -27
  10. data/lib/honeybadger/version.rb +1 -1
  11. data/vendor/cli/inifile.rb +628 -0
  12. data/vendor/cli/thor.rb +484 -0
  13. data/vendor/cli/thor/actions.rb +319 -0
  14. data/vendor/cli/thor/actions/create_file.rb +103 -0
  15. data/vendor/cli/thor/actions/create_link.rb +59 -0
  16. data/vendor/cli/thor/actions/directory.rb +118 -0
  17. data/vendor/cli/thor/actions/empty_directory.rb +135 -0
  18. data/vendor/cli/thor/actions/file_manipulation.rb +316 -0
  19. data/vendor/cli/thor/actions/inject_into_file.rb +107 -0
  20. data/vendor/cli/thor/base.rb +656 -0
  21. data/vendor/cli/thor/command.rb +133 -0
  22. data/vendor/cli/thor/core_ext/hash_with_indifferent_access.rb +77 -0
  23. data/vendor/cli/thor/core_ext/io_binary_read.rb +10 -0
  24. data/vendor/cli/thor/core_ext/ordered_hash.rb +98 -0
  25. data/vendor/cli/thor/error.rb +32 -0
  26. data/vendor/cli/thor/group.rb +281 -0
  27. data/vendor/cli/thor/invocation.rb +178 -0
  28. data/vendor/cli/thor/line_editor.rb +17 -0
  29. data/vendor/cli/thor/line_editor/basic.rb +35 -0
  30. data/vendor/cli/thor/line_editor/readline.rb +88 -0
  31. data/vendor/cli/thor/parser.rb +4 -0
  32. data/vendor/cli/thor/parser/argument.rb +73 -0
  33. data/vendor/cli/thor/parser/arguments.rb +175 -0
  34. data/vendor/cli/thor/parser/option.rb +125 -0
  35. data/vendor/cli/thor/parser/options.rb +218 -0
  36. data/vendor/cli/thor/rake_compat.rb +71 -0
  37. data/vendor/cli/thor/runner.rb +322 -0
  38. data/vendor/cli/thor/shell.rb +81 -0
  39. data/vendor/cli/thor/shell/basic.rb +421 -0
  40. data/vendor/cli/thor/shell/color.rb +149 -0
  41. data/vendor/cli/thor/shell/html.rb +126 -0
  42. data/vendor/cli/thor/util.rb +267 -0
  43. data/vendor/cli/thor/version.rb +3 -0
  44. metadata +35 -2
@@ -1,4 +1,4 @@
1
1
  module Honeybadger
2
2
  # Public: The current String Honeybadger version.
3
- VERSION = '2.1.1'.freeze
3
+ VERSION = '2.1.3'.freeze
4
4
  end
@@ -0,0 +1,628 @@
1
+ #encoding: UTF-8
2
+
3
+ # This class represents the INI file and can be used to parse, modify,
4
+ # and write INI files.
5
+ class IniFile
6
+ include Enumerable
7
+
8
+ class Error < StandardError; end
9
+ VERSION = '3.0.0'
10
+
11
+ # Public: Open an INI file and load the contents.
12
+ #
13
+ # filename - The name of the file as a String
14
+ # opts - The Hash of options (default: {})
15
+ # :comment - String containing the comment character(s)
16
+ # :parameter - String used to separate parameter and value
17
+ # :encoding - Encoding String for reading / writing
18
+ # :default - The String name of the default global section
19
+ #
20
+ # Examples
21
+ #
22
+ # IniFile.load('file.ini')
23
+ # #=> IniFile instance
24
+ #
25
+ # IniFile.load('does/not/exist.ini')
26
+ # #=> nil
27
+ #
28
+ # Returns an IniFile instance or nil if the file could not be opened.
29
+ def self.load( filename, opts = {} )
30
+ return unless File.file? filename
31
+ new(opts.merge(:filename => filename))
32
+ end
33
+
34
+ # Get and set the filename
35
+ attr_accessor :filename
36
+
37
+ # Get and set the encoding
38
+ attr_accessor :encoding
39
+
40
+ # Public: Create a new INI file from the given set of options. If :content
41
+ # is provided then it will be used to populate the INI file. If a :filename
42
+ # is provided then the contents of the file will be parsed and stored in the
43
+ # INI file. If neither the :content or :filename is provided then an empty
44
+ # INI file is created.
45
+ #
46
+ # opts - The Hash of options (default: {})
47
+ # :content - The String/Hash containing the INI contents
48
+ # :comment - String containing the comment character(s)
49
+ # :parameter - String used to separate parameter and value
50
+ # :encoding - Encoding String for reading / writing
51
+ # :default - The String name of the default global section
52
+ # :filename - The filename as a String
53
+ #
54
+ # Examples
55
+ #
56
+ # IniFile.new
57
+ # #=> an empty IniFile instance
58
+ #
59
+ # IniFile.new( :content => "[global]\nfoo=bar" )
60
+ # #=> an IniFile instance
61
+ #
62
+ # IniFile.new( :filename => 'file.ini', :encoding => 'UTF-8' )
63
+ # #=> an IniFile instance
64
+ #
65
+ # IniFile.new( :content => "[global]\nfoo=bar", :comment => '#' )
66
+ # #=> an IniFile instance
67
+ #
68
+ def initialize( opts = {} )
69
+ @comment = opts.fetch(:comment, ';#')
70
+ @param = opts.fetch(:parameter, '=')
71
+ @encoding = opts.fetch(:encoding, nil)
72
+ @default = opts.fetch(:default, 'global')
73
+ @filename = opts.fetch(:filename, nil)
74
+ content = opts.fetch(:content, nil)
75
+
76
+ @ini = Hash.new {|h,k| h[k] = Hash.new}
77
+
78
+ if content.is_a?(Hash) then merge!(content)
79
+ elsif content then parse(content)
80
+ elsif @filename then read
81
+ end
82
+ end
83
+
84
+ # Public: Write the contents of this IniFile to the file system. If left
85
+ # unspecified, the currently configured filename and encoding will be used.
86
+ # Otherwise the filename and encoding can be specified in the options hash.
87
+ #
88
+ # opts - The default options Hash
89
+ # :filename - The filename as a String
90
+ # :encoding - The encoding as a String
91
+ #
92
+ # Returns this IniFile instance.
93
+ def write( opts = {} )
94
+ filename = opts.fetch(:filename, @filename)
95
+ encoding = opts.fetch(:encoding, @encoding)
96
+ mode = encoding ? "w:#{encoding}" : "w"
97
+
98
+ File.open(filename, mode) do |f|
99
+ @ini.each do |section,hash|
100
+ f.puts "[#{section}]"
101
+ hash.each {|param,val| f.puts "#{param} #{@param} #{escape_value val}"}
102
+ f.puts
103
+ end
104
+ end
105
+
106
+ self
107
+ end
108
+ alias :save :write
109
+
110
+ # Public: Read the contents of the INI file from the file system and replace
111
+ # and set the state of this IniFile instance. If left unspecified the
112
+ # currently configured filename and encoding will be used when reading from
113
+ # the file system. Otherwise the filename and encoding can be specified in
114
+ # the options hash.
115
+ #
116
+ # opts - The default options Hash
117
+ # :filename - The filename as a String
118
+ # :encoding - The encoding as a String
119
+ #
120
+ # Returns this IniFile instance if the read was successful; nil is returned
121
+ # if the file could not be read.
122
+ def read( opts = {} )
123
+ filename = opts.fetch(:filename, @filename)
124
+ encoding = opts.fetch(:encoding, @encoding)
125
+ return unless File.file? filename
126
+
127
+ mode = encoding ? "r:#{encoding}" : "r"
128
+ File.open(filename, mode) { |fd| parse fd }
129
+ self
130
+ end
131
+ alias :restore :read
132
+
133
+ # Returns this IniFile converted to a String.
134
+ def to_s
135
+ s = []
136
+ @ini.each do |section,hash|
137
+ s << "[#{section}]"
138
+ hash.each {|param,val| s << "#{param} #{@param} #{escape_value val}"}
139
+ s << ""
140
+ end
141
+ s.join("\n")
142
+ end
143
+
144
+ # Returns this IniFile converted to a Hash.
145
+ def to_h
146
+ @ini.dup
147
+ end
148
+
149
+ # Public: Creates a copy of this inifile with the entries from the
150
+ # other_inifile merged into the copy.
151
+ #
152
+ # other - The other IniFile.
153
+ #
154
+ # Returns a new IniFile.
155
+ def merge( other )
156
+ self.dup.merge!(other)
157
+ end
158
+
159
+ # Public: Merges other_inifile into this inifile, overwriting existing
160
+ # entries. Useful for having a system inifile with user overridable settings
161
+ # elsewhere.
162
+ #
163
+ # other - The other IniFile.
164
+ #
165
+ # Returns this IniFile.
166
+ def merge!( other )
167
+ return self if other.nil?
168
+
169
+ my_keys = @ini.keys
170
+ other_keys = case other
171
+ when IniFile
172
+ other.instance_variable_get(:@ini).keys
173
+ when Hash
174
+ other.keys
175
+ else
176
+ raise Error, "cannot merge contents from '#{other.class.name}'"
177
+ end
178
+
179
+ (my_keys & other_keys).each do |key|
180
+ case other[key]
181
+ when Hash
182
+ @ini[key].merge!(other[key])
183
+ when nil
184
+ nil
185
+ else
186
+ raise Error, "cannot merge section #{key.inspect} - unsupported type: #{other[key].class.name}"
187
+ end
188
+ end
189
+
190
+ (other_keys - my_keys).each do |key|
191
+ @ini[key] = case other[key]
192
+ when Hash
193
+ other[key].dup
194
+ when nil
195
+ {}
196
+ else
197
+ raise Error, "cannot merge section #{key.inspect} - unsupported type: #{other[key].class.name}"
198
+ end
199
+ end
200
+
201
+ self
202
+ end
203
+
204
+ # Public: Yield each INI file section, parameter, and value in turn to the
205
+ # given block.
206
+ #
207
+ # block - The block that will be iterated by the each method. The block will
208
+ # be passed the current section and the parameter/value pair.
209
+ #
210
+ # Examples
211
+ #
212
+ # inifile.each do |section, parameter, value|
213
+ # puts "#{parameter} = #{value} [in section - #{section}]"
214
+ # end
215
+ #
216
+ # Returns this IniFile.
217
+ def each
218
+ return unless block_given?
219
+ @ini.each do |section,hash|
220
+ hash.each do |param,val|
221
+ yield section, param, val
222
+ end
223
+ end
224
+ self
225
+ end
226
+
227
+ # Public: Yield each section in turn to the given block.
228
+ #
229
+ # block - The block that will be iterated by the each method. The block will
230
+ # be passed the current section as a Hash.
231
+ #
232
+ # Examples
233
+ #
234
+ # inifile.each_section do |section|
235
+ # puts section.inspect
236
+ # end
237
+ #
238
+ # Returns this IniFile.
239
+ def each_section
240
+ return unless block_given?
241
+ @ini.each_key {|section| yield section}
242
+ self
243
+ end
244
+
245
+ # Public: Remove a section identified by name from the IniFile.
246
+ #
247
+ # section - The section name as a String.
248
+ #
249
+ # Returns the deleted section Hash.
250
+ def delete_section( section )
251
+ @ini.delete section.to_s
252
+ end
253
+
254
+ # Public: Get the section Hash by name. If the section does not exist, then
255
+ # it will be created.
256
+ #
257
+ # section - The section name as a String.
258
+ #
259
+ # Examples
260
+ #
261
+ # inifile['global']
262
+ # #=> global section Hash
263
+ #
264
+ # Returns the Hash of parameter/value pairs for this section.
265
+ def []( section )
266
+ return nil if section.nil?
267
+ @ini[section.to_s]
268
+ end
269
+
270
+ # Public: Set the section to a hash of parameter/value pairs.
271
+ #
272
+ # section - The section name as a String.
273
+ # value - The Hash of parameter/value pairs.
274
+ #
275
+ # Examples
276
+ #
277
+ # inifile['tenderloin'] = { 'gritty' => 'yes' }
278
+ # #=> { 'gritty' => 'yes' }
279
+ #
280
+ # Returns the value Hash.
281
+ def []=( section, value )
282
+ @ini[section.to_s] = value
283
+ end
284
+
285
+ # Public: Create a Hash containing only those INI file sections whose names
286
+ # match the given regular expression.
287
+ #
288
+ # regex - The Regexp used to match section names.
289
+ #
290
+ # Examples
291
+ #
292
+ # inifile.match(/^tree_/)
293
+ # #=> Hash of matching sections
294
+ #
295
+ # Return a Hash containing only those sections that match the given regular
296
+ # expression.
297
+ def match( regex )
298
+ @ini.dup.delete_if { |section, _| section !~ regex }
299
+ end
300
+
301
+ # Public: Check to see if the IniFile contains the section.
302
+ #
303
+ # section - The section name as a String.
304
+ #
305
+ # Returns true if the section exists in the IniFile.
306
+ def has_section?( section )
307
+ @ini.has_key? section.to_s
308
+ end
309
+
310
+ # Returns an Array of section names contained in this IniFile.
311
+ def sections
312
+ @ini.keys
313
+ end
314
+
315
+ # Public: Freeze the state of this IniFile object. Any attempts to change
316
+ # the object will raise an error.
317
+ #
318
+ # Returns this IniFile.
319
+ def freeze
320
+ super
321
+ @ini.each_value {|h| h.freeze}
322
+ @ini.freeze
323
+ self
324
+ end
325
+
326
+ # Public: Mark this IniFile as tainted -- this will traverse each section
327
+ # marking each as tainted.
328
+ #
329
+ # Returns this IniFile.
330
+ def taint
331
+ super
332
+ @ini.each_value {|h| h.taint}
333
+ @ini.taint
334
+ self
335
+ end
336
+
337
+ # Public: Produces a duplicate of this IniFile. The duplicate is independent
338
+ # of the original -- i.e. the duplicate can be modified without changing the
339
+ # original. The tainted state of the original is copied to the duplicate.
340
+ #
341
+ # Returns a new IniFile.
342
+ def dup
343
+ other = super
344
+ other.instance_variable_set(:@ini, Hash.new {|h,k| h[k] = Hash.new})
345
+ @ini.each_pair {|s,h| other[s].merge! h}
346
+ other.taint if self.tainted?
347
+ other
348
+ end
349
+
350
+ # Public: Produces a duplicate of this IniFile. The duplicate is independent
351
+ # of the original -- i.e. the duplicate can be modified without changing the
352
+ # original. The tainted state and the frozen state of the original is copied
353
+ # to the duplicate.
354
+ #
355
+ # Returns a new IniFile.
356
+ def clone
357
+ other = dup
358
+ other.freeze if self.frozen?
359
+ other
360
+ end
361
+
362
+ # Public: Compare this IniFile to some other IniFile. For two INI files to
363
+ # be equivalent, they must have the same sections with the same parameter /
364
+ # value pairs in each section.
365
+ #
366
+ # other - The other IniFile.
367
+ #
368
+ # Returns true if the INI files are equivalent and false if they differ.
369
+ def eql?( other )
370
+ return true if equal? other
371
+ return false unless other.instance_of? self.class
372
+ @ini == other.instance_variable_get(:@ini)
373
+ end
374
+ alias :== :eql?
375
+
376
+ # Escape special characters.
377
+ #
378
+ # value - The String value to escape.
379
+ #
380
+ # Returns the escaped value.
381
+ def escape_value( value )
382
+ value = value.to_s.dup
383
+ value.gsub!(%r/\\([0nrt])/, '\\\\\1')
384
+ value.gsub!(%r/\n/, '\n')
385
+ value.gsub!(%r/\r/, '\r')
386
+ value.gsub!(%r/\t/, '\t')
387
+ value.gsub!(%r/\0/, '\0')
388
+ value
389
+ end
390
+
391
+ # Parse the given content and store the information in this IniFile
392
+ # instance. All data will be cleared out and replaced with the information
393
+ # read from the content.
394
+ #
395
+ # content - A String or a file descriptor (must respond to `each_line`)
396
+ #
397
+ # Returns this IniFile.
398
+ def parse( content )
399
+ parser = Parser.new(@ini, @param, @comment, @default)
400
+ parser.parse(content)
401
+ self
402
+ end
403
+
404
+ # The IniFile::Parser has the responsibility of reading the contents of an
405
+ # .ini file and storing that information into a ruby Hash. The object being
406
+ # parsed must respond to `each_line` - this includes Strings and any IO
407
+ # object.
408
+ class Parser
409
+
410
+ attr_writer :section
411
+ attr_accessor :property
412
+ attr_accessor :value
413
+
414
+ # Create a new IniFile::Parser that can be used to parse the contents of
415
+ # an .ini file.
416
+ #
417
+ # hash - The Hash where parsed information will be stored
418
+ # param - String used to separate parameter and value
419
+ # comment - String containing the comment character(s)
420
+ # default - The String name of the default global section
421
+ #
422
+ def initialize( hash, param, comment, default )
423
+ @hash = hash
424
+ @default = default
425
+
426
+ comment = comment.to_s.empty? ? "\\z" : "\\s*(?:[#{comment}].*)?\\z"
427
+
428
+ @section_regexp = %r/\A\s*\[([^\]]+)\]#{comment}/
429
+ @ignore_regexp = %r/\A#{comment}/
430
+ @property_regexp = %r/\A(.*?)(?<!\\)#{param}(.*)\z/
431
+
432
+ @open_quote = %r/\A\s*(".*)\z/
433
+ @close_quote = %r/\A(.*(?<!\\)")#{comment}/
434
+ @full_quote = %r/\A\s*(".*(?<!\\)")#{comment}/
435
+ @trailing_slash = %r/\A(.*)(?<!\\)\\#{comment}/
436
+ @normal_value = %r/\A(.*?)#{comment}/
437
+ end
438
+
439
+ # Returns `true` if the current value starts with a leading double quote.
440
+ # Otherwise returns false.
441
+ def leading_quote?
442
+ value && value =~ %r/\A"/
443
+ end
444
+
445
+ # Given a string, attempt to parse out a value from that string. This
446
+ # value might be continued on the following line. So this method returns
447
+ # `true` if it is expecting more data.
448
+ #
449
+ # string - String to parse
450
+ #
451
+ # Returns `true` if the next line is also part of the current value.
452
+ # Returns `fase` if the string contained a complete value.
453
+ def parse_value( string )
454
+ continuation = false
455
+
456
+ # if our value starts with a double quote, then we are in a
457
+ # line continuation situation
458
+ if leading_quote?
459
+ # check for a closing quote at the end of the string
460
+ if string =~ @close_quote
461
+ value << $1
462
+
463
+ # otherwise just append the string to the value
464
+ else
465
+ value << string
466
+ continuation = true
467
+ end
468
+
469
+ # not currently processing a continuation line
470
+ else
471
+ case string
472
+ when @full_quote
473
+ self.value = $1
474
+
475
+ when @open_quote
476
+ self.value = $1
477
+ continuation = true
478
+
479
+ when @trailing_slash
480
+ self.value ? self.value << $1 : self.value = $1
481
+ continuation = true
482
+
483
+ when @normal_value
484
+ self.value ? self.value << $1 : self.value = $1
485
+
486
+ else
487
+ error
488
+ end
489
+ end
490
+
491
+ if continuation
492
+ self.value << $/ if leading_quote?
493
+ else
494
+ process_property
495
+ end
496
+
497
+ continuation
498
+ end
499
+
500
+ # Parse the ini file contents. This will clear any values currently stored
501
+ # in the ini hash.
502
+ #
503
+ # content - Any object that responds to `each_line`
504
+ #
505
+ # Returns nil.
506
+ def parse( content )
507
+ return unless content
508
+
509
+ continuation = false
510
+
511
+ @hash.clear
512
+ @line = nil
513
+ self.section = nil
514
+
515
+ content.each_line do |line|
516
+ @line = line.chomp
517
+
518
+ if continuation
519
+ continuation = parse_value @line
520
+ else
521
+ case @line
522
+ when @ignore_regexp
523
+ nil
524
+ when @section_regexp
525
+ self.section = @hash[$1]
526
+ when @property_regexp
527
+ self.property = $1.strip
528
+ error if property.empty?
529
+
530
+ continuation = parse_value $2
531
+ else
532
+ error
533
+ end
534
+ end
535
+ end
536
+
537
+ # check here if we have a dangling value ... usually means we have an
538
+ # unmatched open quote
539
+ if leading_quote?
540
+ error "Unmatched open quote"
541
+ elsif property && value
542
+ process_property
543
+ elsif value
544
+ error
545
+ end
546
+
547
+ nil
548
+ end
549
+
550
+ # Store the property/value pair in the currently active section. This
551
+ # method checks for continuation of the value to the next line.
552
+ #
553
+ # Returns nil.
554
+ def process_property
555
+ property.strip!
556
+ value.strip!
557
+
558
+ self.value = $1 if value =~ %r/\A"(.*)(?<!\\)"\z/m
559
+
560
+ section[property] = typecast(value)
561
+
562
+ self.property = nil
563
+ self.value = nil
564
+ end
565
+
566
+ # Returns the current section Hash.
567
+ def section
568
+ @section ||= @hash[@default]
569
+ end
570
+
571
+ # Raise a parse error using the given message and appending the current line
572
+ # being parsed.
573
+ #
574
+ # msg - The message String to use.
575
+ #
576
+ # Raises IniFile::Error
577
+ def error( msg = 'Could not parse line' )
578
+ raise Error, "#{msg}: #{@line.inspect}"
579
+ end
580
+
581
+ # Attempt to typecast the value string. We are looking for boolean values,
582
+ # integers, floats, and empty strings. Below is how each gets cast, but it
583
+ # is pretty logical and straightforward.
584
+ #
585
+ # "true" --> true
586
+ # "false" --> false
587
+ # "" --> nil
588
+ # "42" --> 42
589
+ # "3.14" --> 3.14
590
+ # "foo" --> "foo"
591
+ #
592
+ # Returns the typecast value.
593
+ def typecast( value )
594
+ case value
595
+ when %r/\Atrue\z/i; true
596
+ when %r/\Afalse\z/i; false
597
+ when %r/\A\s*\z/i; nil
598
+ else
599
+ Integer(value) rescue \
600
+ Float(value) rescue \
601
+ unescape_value(value)
602
+ end
603
+ end
604
+
605
+ # Unescape special characters found in the value string. This will convert
606
+ # escaped null, tab, carriage return, newline, and backslash into their
607
+ # literal equivalents.
608
+ #
609
+ # value - The String value to unescape.
610
+ #
611
+ # Returns the unescaped value.
612
+ def unescape_value( value )
613
+ value = value.to_s
614
+ value.gsub!(%r/\\[0nrt\\]/) { |char|
615
+ case char
616
+ when '\0'; "\0"
617
+ when '\n'; "\n"
618
+ when '\r'; "\r"
619
+ when '\t'; "\t"
620
+ when '\\\\'; "\\"
621
+ end
622
+ }
623
+ value
624
+ end
625
+ end
626
+
627
+ end # IniFile
628
+