module RubyBreaker
RubyBreaker
is a dynamic instrumentation and monitoring tool that generates type documentation automatically for Ruby programs.
-
This file contains utility functions that are useful for the Runtime Library.
Constants
- COPYRIGHT
This constant contains the copyright information.
- IO_EXTENSION
Extension used for files that contain type information in Ruby format
- OPTIONS
Options for
RubyBreaker
- OPTION_PARSER
This option parser may be used for the command-line mode or for the library mode when used with Rakefile. See rubybreaker/task.rb for how this can be used in the latter.
- TASK_EXTENSION
Extension used for files that contain
RubyBreaker
task information- YAML_EXTENSION
Extension used for files that contain type information in YAML format
Public Class Methods
This method just redirects to Runtime’s method.
# File lib/rubybreaker/runtime.rb, line 93 def self.break(*mods) Runtime.break(*mods) end
This method just redirects to Runtime’s method. DEPRECATED: Use +RubyBreaker.break()+ to indicate the point of entry.
# File lib/rubybreaker/runtime.rb, line 88 def self.breakable(*mods) Runtime.breakable(*mods) end
# File lib/rubybreaker/runtime.rb, line 97 def self.check(*mods) Runtime.check(*mods) end
This method returns true if the logger is already created and false otherwise.
# File lib/rubybreaker/debug/debug.rb, line 13 def self.defined_logger?() return defined?(LOGGER) end
This method is for reporting an error to the user. It will immediately show the error message but also log it.
# File lib/rubybreaker/debug/debug.rb, line 44 def self.error(err, level=:error, &blk) msg = err.to_s msg = "#{msg} : #{yield}" if blk STDOUT.puts "[#{level.to_s.upcase}] #{msg}" LOGGER.send(level, msg) if defined?(OPTIONS) && OPTIONS[:debug] end
This method logs a non-error (or error) message but with the provided context.
# File lib/rubybreaker/debug/debug.rb, line 53 def self.log(str, level=:debug, context=nil, &blk) return unless defined?(OPTIONS) && OPTIONS[:debug] msg = str.to_s msg = "#{msg} : #{yield}" if blk if context output = "" pp = PrettyPrint.new(output) context.format_with_msg(pp,msg) pp.flush msg = output end LOGGER.send(level, msg) end
DEPRECATED: Use +RubyBreaker.run()+ to indicate the point of entry.
# File lib/rubybreaker/runtime.rb, line 83 def self.monitor() end
This method runs RubyBreaker
for a particular test case (class). This is a bit different from running RubyBreaker
as a shell program.
# File lib/rubybreaker.rb, line 175 def self.run(*mods) RubyBreaker.setup_logger() unless RubyBreaker.defined_logger?() # Task based run should use the rubybreaker options same as in shell # mode. So, parse the options first. if self.running_as_task?() # running in task mode RubyBreaker.verbose("Running RubyBreaker within a testcase") task = self.task OPTION_PARSER.parse(*task[:rubybreaker_opts]) Runtime.break(*task[:break]) Runtime.check(*task[:check]) task_name = task[:name] RubyBreaker.verbose("Done reading task information") io_file = self.io_file(task_name) elsif OPTIONS[:prog] # running in shell mode Runtime.break(*mods) # should not happen but for backward-compatibility Runtime.break(*OPTIONS[:break]) Runtime.check(*OPTIONS[:check]) io_file = self.io_file(OPTIONS[:prog_file]) else # Otherwise, assume there are no explicit IO files. end self.load_input(io_file) # The following is deprecated but doing this for backward compatibility Runtime.instrument() # At the end, we WILL generate an output of the type information. at_exit { self.output(io_file) } end
This method will display verbose message. It is not for debugging but to inform users of each stage in the analysis.
# File lib/rubybreaker/debug/debug.rb, line 30 def self.verbose(str, &blk) return unless defined?(OPTIONS) && (OPTIONS[:verbose] || OPTIONS[:debug]) if blk msg = yield msg = "#{str} : #{msg}" if str else msg = str end STDOUT.puts msg if OPTIONS[:verbose] LOGGER.info msg if OPTIONS[:debug] end
Private Class Methods
This method finds the IO file for this run. It is either specified in io-file option or using the program name or the task name.
# File lib/rubybreaker.rb, line 160 def self.io_file(prog_or_task) if OPTIONS[:io_file] fname = OPTIONS[:io_file] elsif prog_or_task fname = "#{File.basename(prog_or_task.to_s, ".rb")}.#{IO_EXTENSION}" end return nil unless fname fname = File.absolute_path(fname) return fname end
This method loads the IO file by loading it.
# File lib/rubybreaker.rb, line 113 def self.load_input(fname) return fname eval "load \"#{fname}\"", TOPLEVEL_BINDING RubyBreaker.verbose("RubyBreaker input file #{fname} is loaded") end
This method will generate the output to the given filename.
# File lib/rubybreaker.rb, line 120 def self.output(fname) RubyBreaker.verbose("Generating type documentation") code = "" # Document each module that was monitored. MONITOR_MAP.each_key { |mod| str = Runtime::TypeSigUnparser.unparse(mod) code << str print str if OPTIONS[:stdout] # display on the screen if requested } if fname && OPTIONS[:save_output] # Check if the file already exists--that is, if it was used for input io_exist = File.exists?(fname) if File.writable?(fname) RubyBreaker.verbose("Saving it to #{fname}") # Append the result to the input file (or create a new file) fmode = OPTIONS[:append] ? "a" : "w" open(fname, fmode) do |f| # When append, do not write the header unless OPTIONS[:append] f.puts "# This file is auto-generated by RubyBreaker" end # But time stamp always f.puts "# Last modified: #{Time.now}" f.puts "require \"rubybreaker\"" unless OPTIONS[:append] f.print code end else RubyBreaker.verbose("Cannot write to #{fname}.") end end RubyBreaker.verbose("Done generating type documentation") end
This method determines if RubyBreaker
is running as a task.
# File lib/rubybreaker.rb, line 107 def self.running_as_task?(); return $__rubybreaker_task != nil end
This method returns the task currently being run.
# File lib/rubybreaker.rb, line 110 def self.task(); return $__rubybreaker_task end