rubybreaker 0.0.2 → 0.0.3

Sign up to get free protection for your applications and to get access to all the features.
Files changed (77) hide show
  1. data/NEWS +6 -46
  2. data/README.md +30 -19
  3. data/TODO +22 -20
  4. data/VERSION +1 -1
  5. data/bin/rubybreaker +11 -2
  6. data/lib/rubybreaker.rb +31 -11
  7. data/lib/rubybreaker/debug.rb +5 -49
  8. data/lib/rubybreaker/{context.rb → debug/context.rb} +0 -0
  9. data/lib/rubybreaker/debug/debug.rb +61 -0
  10. data/lib/rubybreaker/{error.rb → debug/error.rb} +1 -1
  11. data/lib/rubybreaker/runtime.rb +3 -2
  12. data/lib/rubybreaker/runtime/inspector.rb +20 -13
  13. data/lib/rubybreaker/runtime/monitor.rb +21 -13
  14. data/lib/rubybreaker/runtime/object_wrapper.rb +3 -1
  15. data/lib/rubybreaker/runtime/type_system.rb +3 -2
  16. data/lib/rubybreaker/test/testcase.rb +0 -1
  17. data/lib/rubybreaker/type/type.rb +1 -1
  18. data/lib/rubybreaker/type/type_grammar.treetop +1 -1
  19. data/lib/rubybreaker/type/type_unparser.rb +1 -1
  20. data/lib/rubybreaker/util.rb +3 -11
  21. data/test/integrated/tc_both_broken_breakable.rb +27 -0
  22. data/test/ts_integrated.rb +1 -0
  23. data/webpage/index.html +30 -19
  24. data/webpage/rdoc/RubyBreaker.html +127 -10
  25. data/webpage/rdoc/RubyBreaker/Breakable.html +1 -5
  26. data/webpage/rdoc/RubyBreaker/Broken.html +2 -6
  27. data/webpage/rdoc/RubyBreaker/Broken/BrokenEigen.html +3 -6
  28. data/webpage/rdoc/RubyBreaker/Context.html +6 -10
  29. data/webpage/rdoc/RubyBreaker/Errors.html +2 -6
  30. data/webpage/rdoc/RubyBreaker/Errors/InternalError.html +3 -7
  31. data/webpage/rdoc/RubyBreaker/Errors/InvalidSubtypeCheck.html +3 -7
  32. data/webpage/rdoc/RubyBreaker/Errors/InvalidTypeConstruction.html +2 -6
  33. data/webpage/rdoc/RubyBreaker/Errors/SubtypeFailure.html +2 -6
  34. data/webpage/rdoc/RubyBreaker/Errors/TypeError.html +3 -7
  35. data/webpage/rdoc/RubyBreaker/Errors/UserError.html +3 -7
  36. data/webpage/rdoc/RubyBreaker/Main.html +33 -20
  37. data/webpage/rdoc/RubyBreaker/ObjectPosition.html +4 -8
  38. data/webpage/rdoc/RubyBreaker/Position.html +7 -11
  39. data/webpage/rdoc/RubyBreaker/RubyTypeUtils.html +1 -5
  40. data/webpage/rdoc/RubyBreaker/Runtime.html +1 -5
  41. data/webpage/rdoc/RubyBreaker/Runtime/Inspector.html +25 -18
  42. data/webpage/rdoc/RubyBreaker/Runtime/MethodInfo.html +1 -5
  43. data/webpage/rdoc/RubyBreaker/Runtime/Monitor.html +4 -8
  44. data/webpage/rdoc/RubyBreaker/Runtime/MonitorInstaller.html +20 -43
  45. data/webpage/rdoc/RubyBreaker/Runtime/MonitorSwitch.html +7 -11
  46. data/webpage/rdoc/RubyBreaker/Runtime/MonitorUtils.html +6 -10
  47. data/webpage/rdoc/RubyBreaker/Runtime/ObjectWrapper.html +7 -11
  48. data/webpage/rdoc/RubyBreaker/Runtime/Pluggable.html +1 -5
  49. data/webpage/rdoc/RubyBreaker/Runtime/TypePlaceholder.html +1 -5
  50. data/webpage/rdoc/RubyBreaker/Runtime/TypeSigParser.html +1 -5
  51. data/webpage/rdoc/RubyBreaker/Runtime/TypeSystem.html +8 -12
  52. data/webpage/rdoc/RubyBreaker/TestCase.html +1 -6
  53. data/webpage/rdoc/RubyBreaker/TypeComparer.html +1 -5
  54. data/webpage/rdoc/RubyBreaker/TypeDefs.html +1 -5
  55. data/webpage/rdoc/RubyBreaker/TypeDefs/AnyType.html +1 -5
  56. data/webpage/rdoc/RubyBreaker/TypeDefs/BlockType.html +1 -5
  57. data/webpage/rdoc/RubyBreaker/TypeDefs/DuckType.html +1 -5
  58. data/webpage/rdoc/RubyBreaker/TypeDefs/FusionType.html +1 -5
  59. data/webpage/rdoc/RubyBreaker/TypeDefs/MethodListType.html +1 -5
  60. data/webpage/rdoc/RubyBreaker/TypeDefs/MethodType.html +1 -5
  61. data/webpage/rdoc/RubyBreaker/TypeDefs/NilType.html +1 -5
  62. data/webpage/rdoc/RubyBreaker/TypeDefs/NominalType.html +1 -5
  63. data/webpage/rdoc/RubyBreaker/TypeDefs/OptionalType.html +1 -5
  64. data/webpage/rdoc/RubyBreaker/TypeDefs/OrType.html +1 -5
  65. data/webpage/rdoc/RubyBreaker/TypeDefs/SelfType.html +1 -5
  66. data/webpage/rdoc/RubyBreaker/TypeDefs/Type.html +1 -5
  67. data/webpage/rdoc/RubyBreaker/TypeDefs/VarLengthType.html +1 -5
  68. data/webpage/rdoc/RubyBreaker/TypeUnparser.html +1 -5
  69. data/webpage/rdoc/RubyBreaker/Typing.html +1 -5
  70. data/webpage/rdoc/RubyBreaker/{Utilities.html → Util.html} +8 -9
  71. data/webpage/rdoc/created.rid +15 -14
  72. data/webpage/rdoc/index.html +1 -5
  73. data/webpage/rdoc/js/search_index.js +1 -1
  74. data/webpage/rdoc/table_of_contents.html +22 -36
  75. metadata +9 -7
  76. data/webpage/rdoc/RubyBreaker/Debug.html +0 -411
  77. data/webpage/rdoc/RubyBreaker/Kernel.html +0 -259
data/NEWS CHANGED
@@ -1,48 +1,8 @@
1
- # VERSION 0.0.2
2
-
3
- ## Class Method Support
4
-
5
- RubyBreaker now supports class methods. Auto-documentation of class methods
6
- is done in the eigen class scope of the target module. For example, the
7
- consider the following:
8
-
9
- class A
10
- include RubyBreaker::Breakable
11
- class << self
12
- def foo(x); x.to_s end
13
- end
14
- end
15
- RubyBreaker.monitor()
16
- A.foo(1)
17
-
18
- When `foo` method is run, RubyBreaker will auto-document the type of the
19
- method and output as the following:
20
-
21
- class A
22
- include RubyBreaker::Broken
23
- class << self
24
- typesig("foo(fixnum[to_s]) -> string")
25
- end
26
- end
1
+ # VERSION 0.0.3
2
+ * A class or module can be both Breakable and Broken at the same time.
3
+ * Better logging/debugging output
27
4
 
28
- ## Variable-Length Argument Type
29
-
30
- RubyBreaker can now auto-document variable-length argument types if the last
31
- argument was declared with `*` prefix. For instance,
32
-
33
- class A
34
- include RubyBreaker::Breakable
35
- def foo(*args)
36
- args[0].to_s
37
- end
38
- end
39
- RubyBreaker.monitor()
40
- A.new.foo("1")
41
-
42
- Will generate the following output:
43
-
44
- class A
45
- include RubyBreaker::Broken
46
- typesig("foo(string[to_s]*) -> string")
47
- end
5
+ # VERSION 0.0.2
6
+ * Class methods can be auto-documented or manually documented.
7
+ * Variable-length argument type can be auto-documented.
48
8
 
data/README.md CHANGED
@@ -171,10 +171,20 @@ signature `bar(fixnum[to_s]) -> string`, which means it takes an object that
171
171
  has `Fixnum`'s `to_s` method and returns a string. More detail on the type
172
172
  annotation language will be explained in later section.
173
173
 
174
- It is possible to include either `Breakable` or `Broken` in an eigen-class
175
- to document class methods. To make this easier, RubyBreaker will
176
- automatically support auto-documentation and manual documentation of class
177
- methods if the original module is declared as `Breakable` or `Broken`,
174
+ ### Hybrid of Breakable and Broken
175
+
176
+ Starting from version 0.0.3, RubyBreaker allows a module to be declared as
177
+ `Breakable` and `Broken` at the same time. If a method has a corresponding
178
+ type signature (manually documented) somewhere, then RubyBreaker will not
179
+ monitor it during runtime. All other methods will be instrumented and
180
+ monitored by RubyBreaker.
181
+
182
+ ### Class Methods
183
+
184
+ It is possible to include `Breakable` and/or `Broken` in an eigen-class to
185
+ document class methods. To make this easier, RubyBreaker will automatically
186
+ support auto-documentation and manual documentation of class methods if the
187
+ original module is declared as `Breakable` and/or `Broken`,
178
188
  respectively--that is, up to immediate eigen class level of a "nominal"
179
189
  module. The following example shows how class methods can be
180
190
  auto-documented and manually documented, respectively.
@@ -194,11 +204,6 @@ auto-documented and manually documented, respectively.
194
204
  end
195
205
  end
196
206
 
197
- Keep in mind that `Broken` module always wins against `Breakable`. In other
198
- words, if a module is declared as both `Broken` and `Breakable`, it is
199
- treated as `Broken`. Future versions of RubyBreaker will support a hybrid of
200
- the two modules, but it remains as a limitation in the current version.
201
-
202
207
  ### Program Entry Point
203
208
 
204
209
  In Ruby, as soon as a file is `require`d, the execution of that file begins.
@@ -271,8 +276,8 @@ and returns a `String` object. Note that these types are in lowercase,
271
276
  indicating they are objects and not modules or classes themselves.
272
277
 
273
278
  There are several types that represent an object: nominal, duck, fusion,
274
- nil, 'any', and block. Each type signature itself represents a method type
275
- or a method list type (explained below).
279
+ nil, 'any', 'or', optional, variable-length, and block. Each type signature
280
+ itself represents a method type or a method list type (explained below).
276
281
 
277
282
  ### Nominal Type
278
283
 
@@ -325,6 +330,20 @@ other type is not a subtype of `?`. This becomes a bit complicated for
325
330
  method or block argument types because of their contra-variance
326
331
  characteristic. Please refer to the section *Subtyping*.
327
332
 
333
+ ### Or Type
334
+
335
+ Any above types can be "or"ed together, using `||`, to represent an object
336
+ that can be either one or the other. It _does_ not represent an object that
337
+ has to be both (which is not supported by RubyBreaker).
338
+
339
+ ### Optional Argument Type and Variable-Length Argument Type
340
+
341
+ Another useful features of Ruby are the optional argument type and the
342
+ variable-length argument type. The former represents an argument that has a
343
+ default value (and therefore does not have to be provided). The latter
344
+ represents zero or more arguments of the same type. These are denoted by
345
+ suffices, `?` and `*`, respectively.
346
+
328
347
  ### Block Type
329
348
 
330
349
  One of the Ruby's prominent features is the block argument. It allows
@@ -340,14 +359,6 @@ However, *keep in mind* that RubyBreaker *cannot* automatically document the
340
359
  block types due to `yield` being a language construct rather than a method,
341
360
  which means it cannot be captured by meta-programming!
342
361
 
343
- ### Optional Argument Type and Variable-Length Argument Type
344
-
345
- Another useful features of Ruby are the optional argument type and the
346
- variable-length argument type. The former represents an argument that has a
347
- default value (and therefore does not have to be provided). The latter
348
- represents zero or more arguments of the same type. These are denoted by
349
- suffices, `?` and `*`, respectively.
350
-
351
362
  ### Method Type and Method List Types
352
363
 
353
364
  Method type is similar to the block type, but it represents an actual method
data/TODO CHANGED
@@ -1,30 +1,32 @@
1
- # Dynamic Type Check
1
+ # Dynamic Type Checker
2
2
 
3
- Although type documentation is the main purpose of RubyBreaker, this
4
- documentation can be used to enforce correct types at runtime. It is often
5
- helpful to find type errors earlier on during execution to narrow down the
6
- root cause of the problem.
3
+ Type documentation generated by RubyBreaker can be more useful if the types
4
+ are actually enforced during runtime. In order to do this, each method has
5
+ to be dynamically instrumented (as "breakable" methods are) and inspected
6
+ for each argument type and return type. Although this is still a dynamic
7
+ type checking, it finds type errors earlier in the program execution.
7
8
 
8
- # Hybrid of Breakable and Broken
9
+ # More Testing Frameworks
9
10
 
10
- A module or class cannot be both Breakable and Broken. Once it is declared
11
- to be Broken, it is Broken no matter what. In theory, it is possible to
12
- selectively auto-document methods that are not yet documented.
11
+ The Ruby community has other testing frameworks such as RSpec and Cucumber.
12
+ RubyBreaker should be able to support these frameworks so that those test
13
+ suites can be used for RubyBreaker.
13
14
 
14
- # RDoc Documentation
15
+ # RDoc and YARD Documentation Support
15
16
 
16
- The output of RubyBreaker is an executable Ruby code (for the convenience of
17
- the author). But, for most Ruby programmers, this documentation may be more
18
- useful if the output was somehow incorporated with the RDoc output.
17
+ It is cool (and actually useful in a way) to output the type documentation
18
+ in an executable Ruby code. However, for documetation purpose, it would be
19
+ better to have the information in RDoc or YARD format.
19
20
 
20
- # Ruby on Rails Support
21
+ # Core Library Documentation
21
22
 
22
- Duh...
23
+ The core idea behind RubyBreaker is the incremental type documentation,
24
+ starting from the core library to the application level modules.
25
+ Unfortunately, due to technical difficulties, it is not intuitive to
26
+ auto-document the core library code. Thus, it might be necessary to document
27
+ it manually and import this information for upper-level modules.
23
28
 
24
- # PERMANENT LIMITATIONS
29
+ # Ruby on Rails Support
25
30
 
26
- * Block argument cannot be auto-documented.
27
- * Manual modification (minimal) of code is required.
28
- * No parametric polymorphic types are supported. The lack of this type is
29
- not a bug but a feature! Polymorphic types are just headaches!
31
+ What more can I say?
30
32
 
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.0.2
1
+ 0.0.3
data/bin/rubybreaker CHANGED
@@ -13,6 +13,10 @@ module RubyBreaker
13
13
 
14
14
  opts.banner = "Usage: #{File.basename(__FILE__)} [options] in_file[.rb]"
15
15
 
16
+ opts.on("--debug", "Run in debug mode") do
17
+ OPTIONS[:debug] = true
18
+ end
19
+
16
20
  opts.on("-f","--io-file FILE","Specify an input/output file") do |f|
17
21
  OPTIONS[:output] = f
18
22
  end
@@ -45,15 +49,20 @@ module RubyBreaker
45
49
  OPTIONS[:mode] = :bin # indicate that RubyBreaker is being run as a
46
50
  # binary (program).
47
51
 
48
- puts COPYRIGHT
49
- puts
52
+ if OPTIONS[:verbose] # Show copyright info only when verbose
53
+ puts COPYRIGHT
54
+ puts
55
+ end
50
56
 
51
57
  # There has to be an input file
52
58
  if ARGV.length < 1 then
59
+ puts "Specify a Ruby program"
53
60
  puts option_parser.banner
54
61
  exit(1)
55
62
  end
56
63
 
64
+ OPTIONS[:file] = ARGV[0]
65
+
57
66
  Main.run()
58
67
 
59
68
  end
data/lib/rubybreaker.rb CHANGED
@@ -3,6 +3,7 @@
3
3
  # observed at runtime and generates type annotation at the end. It can be
4
4
  # run as either a stand-alone script or as a Ruby library.
5
5
 
6
+ require_relative "rubybreaker/debug"
6
7
  require_relative "rubybreaker/runtime"
7
8
  require_relative "rubybreaker/test"
8
9
 
@@ -16,12 +17,13 @@ module RubyBreaker
16
17
  # Options for RubyBreaker
17
18
  OPTIONS = {
18
19
  :debug => false, # in debug mode?
19
- :verbose => true, # in verbose mode?
20
+ :verbose => false, # in RubyBreaker.verbose mode?
20
21
  :mode => :lib, # bin or lib?
21
22
  :io_file => nil, # generate input/output other than default?
22
23
  :append => true, # append to the input file (if there is)?
23
24
  :stdout => true, # also display on the screen?
24
25
  :rubylib => true, # include core ruby library documentation?
26
+ :file => nil, # the input Ruby program (as typed by the user)
25
27
  }
26
28
 
27
29
  # This array lists modules/classes that are actually instrumented with a
@@ -48,10 +50,11 @@ module RubyBreaker
48
50
  def self.setup()
49
51
 
50
52
  BREAKABLE.each do |mod|
51
- # Avoid already installed module or now Broken module. Remember,
52
- # once a module is a declared to be Broken, it wins. Broken modules
53
- # cannot be Breakable!
54
- unless INSTALLED.include?(mod) || BROKEN.include?(mod)
53
+
54
+ # Remember, RubyBreaker now supports a hybrid of Breakable and
55
+ # Broken module. Just check if the module has already been
56
+ # instrumented.
57
+ unless INSTALLED.include?(mod)
55
58
  MonitorInstaller.install_module_monitor(mod)
56
59
  INSTALLED << mod
57
60
  end
@@ -68,6 +71,7 @@ module RubyBreaker
68
71
  # Reads the input file if specified or exists
69
72
  def self.input()
70
73
  return unless OPTIONS[:io_file] && File.exist?(OPTIONS[:io_file])
74
+ RubyBreaker.verbose("RubyBreaker input file exists...loading")
71
75
  eval "load \"#{OPTIONS[:io_file]}\"", TOPLEVEL_BINDING
72
76
  end
73
77
 
@@ -136,9 +140,11 @@ module RubyBreaker
136
140
  pp.breakable()
137
141
  end
138
142
 
139
- # This method will generate the output
143
+ # This method will generate the output.
140
144
  def self.output()
141
145
 
146
+ RubyBreaker.verbose("Generating type documentation")
147
+
142
148
  io_exist = OPTIONS[:io_file] && File.exist?(OPTIONS[:io_file])
143
149
 
144
150
  str = ""
@@ -162,14 +168,24 @@ module RubyBreaker
162
168
  end
163
169
  f.puts str
164
170
  end
171
+
172
+ RubyBreaker.verbose("Done generating type documentation")
165
173
  end
166
174
 
167
- # This method will run the input file
175
+ # This method will run do things in the following order:
176
+ #
177
+ # * Checks to see if the user program and an input file exists
178
+ # * Loads the documentation for Ruby Core Library (TODO)
179
+ # * Reads the input type documentation if any
180
+ # * Reads (require's) the user program
181
+ #
168
182
  def self.run()
169
183
 
170
- # First, take care of the program file.
184
+ RubyBreaker.setup_logger()
185
+ RubyBreaker.verbose("Running RubyBreaker")
171
186
 
172
- argv0 = ARGV[0]
187
+ # First, take care of the program file.
188
+ argv0 = OPTIONS[:file]
173
189
  prog_file = argv0
174
190
  prog_file = File.expand_path(prog_file)
175
191
 
@@ -179,7 +195,7 @@ module RubyBreaker
179
195
  end
180
196
 
181
197
  if !File.exist?(prog_file)
182
- puts "ERROR: '#{argv0}' is an invalid file."
198
+ fatal("#{argv0} is an invalid file.")
183
199
  exit(1)
184
200
  end
185
201
 
@@ -190,6 +206,7 @@ module RubyBreaker
190
206
  OPTIONS[:io_file] = File.absolute_path(OPTIONS[:io_file])
191
207
 
192
208
  if OPTIONS[:rubylib]
209
+ RubyBreaker.verbose("Loading RubyBreaker's Ruby Core Library documentation")
193
210
  # Load the core library type documentation
194
211
  eval("require \"rubybreaker/rubylib\"", TOPLEVEL_BINDING)
195
212
  end
@@ -201,11 +218,14 @@ module RubyBreaker
201
218
  # Finally, require the program file! Let it run! Wheeee!
202
219
  eval("require '#{prog_file}'", TOPLEVEL_BINDING)
203
220
 
221
+ RubyBreaker.verbose("Done running the input program")
222
+
204
223
  end
205
224
 
206
225
  end
207
226
 
208
- # Just redirecting
227
+ # This is the manual indicator for the program entry point. It simply
228
+ # redirects to the monitor setup code.
209
229
  def self.monitor()
210
230
  Main.setup()
211
231
  end
@@ -1,52 +1,8 @@
1
1
  #--
2
- # This file is for debugging RubyBreaker.
2
+ # This file imports all necessary modules for both debugging RubyBreaker
3
+ # internally and debugging the user program.
3
4
 
4
- require "prettyprint"
5
- require_relative "context"
5
+ require_relative "debug/context"
6
+ require_relative "debug/error"
7
+ require_relative "debug/debug"
6
8
 
7
- module RubyBreaker
8
-
9
- # This module is for internal purpose only - to help ourselves find bugs
10
- # and fix them with more informative error messages.
11
- module Debug
12
-
13
- OUTPUT = ""
14
-
15
- def self.debug_mode?()
16
- return defined?(RubyBreaker::OPTIONS) && RubyBreaker::OPTIONS[:debug]
17
- end
18
-
19
- def self.msg(text,context=nil)
20
- return unless self.debug_mode?
21
- pp = PrettyPrint.new(OUTPUT)
22
- msg = "[DEBUG] #{text}"
23
- if context
24
- context.format_with_msg(pp,msg)
25
- else
26
- pp.text(msg,79)
27
- pp.breakable()
28
- end
29
- pp.flush
30
- puts OUTPUT
31
- OUTPUT.replace("")
32
- end
33
-
34
- def self.short_msg(text)
35
- return unless self.debug_mode?
36
- msg = "[DEBUG] #{text}"
37
- print msg
38
- end
39
-
40
- def self.token(msg)
41
- return unless self.debug_mode?
42
- print msg
43
- end
44
-
45
- def self.feed_line()
46
- return unless self.debug_mode?
47
- puts ""
48
- end
49
-
50
- end
51
-
52
- end
File without changes
@@ -0,0 +1,61 @@
1
+ #-
2
+ # This file contains the debug module which is exclusively used by
3
+ # RubyBreaker internally.
4
+ #
5
+ require "prettyprint"
6
+ require "logger"
7
+ require_relative "context"
8
+
9
+ module RubyBreaker
10
+
11
+ # This sets up the logger for debugging RubyBreaker
12
+ def self.setup_logger #:nodoc:
13
+ return if defined?(LOGGER)
14
+ out = if (defined?(OPTIONS) && !OPTIONS[:file].empty?)
15
+ then "#{OPTIONS[:file]}.log"
16
+ else STDOUT
17
+ end
18
+ const_set(:LOGGER, Logger.new(out))
19
+ LOGGER.level = Logger::DEBUG
20
+ end
21
+
22
+ # This method will display verbose message. It is not for debugging but to
23
+ # inform users of each stage in the analysis.
24
+ def self.verbose(str, &blk)
25
+ return unless defined?(OPTIONS) && (OPTIONS[:verbose] || OPTIONS[:debug])
26
+ if blk
27
+ msg = yield
28
+ msg = "#{str} : #{msg}" if str
29
+ else
30
+ msg = str
31
+ end
32
+ STDOUT.puts msg if OPTIONS[:verbose]
33
+ LOGGER.info msg if OPTIONS[:debug]
34
+ end
35
+
36
+ # This method is for reporting an error to the user. It will immediately
37
+ # show the error message but also log it.
38
+ def self.error(err, level=:error, &blk)
39
+ msg = err.to_s
40
+ msg = "#{msg} : #{yield}" if blk
41
+ STDOUT.puts "[#{level.to_s.upcase}] #{msg}"
42
+ LOGGER.send(level, msg) if defined?(OPTIONS) && OPTIONS[:debug]
43
+ end
44
+
45
+ # This method logs a non-error (or error) message but with the provided
46
+ # context.
47
+ def self.log(str, level=:debug, context=nil, &blk)
48
+ return unless defined?(OPTIONS) && OPTIONS[:debug]
49
+ msg = str.to_s
50
+ msg = "#{msg} : #{yield}" if blk
51
+ if context
52
+ output = ""
53
+ pp = PrettyPrint.new(output)
54
+ context.format_with_msg(pp,msg)
55
+ pp.flush
56
+ msg = output
57
+ end
58
+ LOGGER.send(level, msg)
59
+ end
60
+
61
+ end