├── lib ├── blab │ ├── version.rb │ ├── formatter.rb │ ├── config.rb │ ├── tracer.rb │ └── printer.rb └── blab.rb ├── Rakefile ├── blab.gemspec ├── test ├── test_blab.rb ├── support │ └── test.rb ├── test_formatter.rb └── test_printer.rb ├── LICENSE ├── .gitignore └── README.md /lib/blab/version.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module Blab 4 | VERSION = "0.0.1" 5 | end 6 | -------------------------------------------------------------------------------- /Rakefile: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require "bundler/gem_tasks" 4 | require "rake/testtask" 5 | 6 | Rake::TestTask.new do |t| 7 | t.test_files = FileList["test/test_*.rb"] 8 | end 9 | desc 'Run gem tests' 10 | 11 | task default: :test 12 | -------------------------------------------------------------------------------- /blab.gemspec: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | $LOAD_PATH.push File.expand_path("../lib", __FILE__) 4 | 5 | require "blab/version" 6 | 7 | Gem::Specification.new do |s| 8 | s.name = "blab" 9 | s.version = Blab::VERSION 10 | s.date = "2019-05-20" 11 | s.summary = "Blab" 12 | s.description = "A debugging tool" 13 | s.authors = ["Yulia Oletskaya"] 14 | s.email = "yulia.oletskaya@gmail.com" 15 | s.homepage = "http://rubygems.org/gems/blab" 16 | s.license = "MIT" 17 | 18 | s.add_development_dependency("minitest", "~> 5.11") 19 | 20 | s.files = Dir["lib/**/*", "LICENSE", "README.md"] 21 | s.test_files = Dir["test/*", "test/**/*"] 22 | s.require_paths = ["lib"] 23 | end 24 | -------------------------------------------------------------------------------- /test/test_blab.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require "minitest/autorun" 4 | require "blab" 5 | 6 | class SimpleTest 7 | def sum(a, b) 8 | with_blab { a + b } 9 | end 10 | 11 | blab def diff(a, b) 12 | a - b 13 | end 14 | end 15 | 16 | class TestBlab < Minitest::Test 17 | def setup 18 | @simple_test = SimpleTest.new 19 | end 20 | 21 | def test_sum 22 | out, err = capture_subprocess_io do 23 | @simple_test.sum(1, 2) 24 | end 25 | assert_equal err, "" 26 | assert_match(/with_blab { a \+ b }/, out) 27 | end 28 | 29 | def test_diff 30 | out, err = capture_subprocess_io do 31 | @simple_test.diff(2, 1) 32 | end 33 | assert_equal err, "" 34 | assert_match(/a - b/, out) 35 | end 36 | end 37 | -------------------------------------------------------------------------------- /lib/blab.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require_relative "blab/config" 4 | require_relative "blab/formatter" 5 | require_relative "blab/printer" 6 | require_relative "blab/tracer" 7 | require_relative "blab/version" 8 | 9 | module Blab 10 | def self.included(base) 11 | base.define_singleton_method(:blab) do |name| 12 | old_m = self.instance_method(name) 13 | 14 | self.send(:define_method, name) do |*args| 15 | begin 16 | set_trace_func(Blab::Tracer.trace) 17 | old_m.bind(self).call(*args) 18 | ensure 19 | set_trace_func(nil) 20 | Blab::Tracer.reset 21 | end 22 | end 23 | end 24 | 25 | def with_blab 26 | begin 27 | set_trace_func(Blab::Tracer.trace) 28 | yield 29 | ensure 30 | set_trace_func(nil) 31 | Blab::Tracer.reset 32 | end 33 | end 34 | end 35 | end 36 | 37 | BasicObject.include(Blab) 38 | -------------------------------------------------------------------------------- /test/support/test.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | require_relative "../../lib/blab" 3 | require "set" 4 | 5 | class Y 6 | blab def x(name, &block) 7 | a = 15 8 | b = 30 9 | d, c = 12, 67 10 | puts(name) 11 | yo = z(2) 12 | 10.times do |i| 13 | a += i 14 | c = 8 15 | end 16 | se = Set.new 17 | hello( 18 | a, 19 | a, 20 | b 21 | ) 22 | hsh = { 23 | xx: 1, 24 | a: 200, 25 | z: 300 26 | } 27 | end 28 | 29 | def z(n) 30 | 7 + n 31 | end 32 | 33 | def hello(a, b, c) 34 | puts(a) 35 | end 36 | end 37 | 38 | # Blab::Config.original_scope_only = true 39 | 40 | class Test 41 | def shuffle(arr) 42 | for n in 0...arr.size 43 | targ = n + rand(arr.size - n) 44 | arr[n], arr[targ] = arr[targ], arr[n] if n != targ 45 | end 46 | end 47 | 48 | def pairs(a, b) 49 | with_blab do 50 | a << "Insane" 51 | shuffle(b) 52 | end 53 | b.each { |x| shuffle(a); a.each { |y| print y, " ", x, ".\n" } } 54 | end 55 | end 56 | 57 | Test.new.pairs(["Bored", "Curious"], ["cat", "frog"]) 58 | 59 | -------------------------------------------------------------------------------- /test/test_formatter.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require "minitest/autorun" 4 | require "blab" 5 | 6 | class TestFormatter < Minitest::Test 7 | def setup 8 | @formatter = Blab::Formatter.new(15) 9 | end 10 | 11 | def test_array_formet 12 | arr1 = Array.new(3, 5) 13 | arr2 = Array.new(100, 10) 14 | 15 | assert_equal @formatter.format(arr1), "[5, 5, 5]" 16 | assert_equal @formatter.format(arr2), "[10, 10,... 10, 10]" 17 | end 18 | 19 | def test_string_format 20 | str1 = "test" 21 | str2 = str1 * 50 22 | 23 | assert_equal @formatter.format(str1), "\"test\"" 24 | assert_equal @formatter.format(str2), "\"testtes...esttest\"" 25 | end 26 | 27 | def test_hash_format 28 | hsh1 = { a: :b } 29 | hsh2 = { aaa: :bbbbb, dddd: :ccccccccccc, e: :f } 30 | 31 | assert_equal @formatter.format(hsh1), "{:a=>:b}" 32 | assert_equal @formatter.format(hsh2), "{:aaa=>:... :e=>:f}" 33 | end 34 | 35 | def test_struct_format 36 | person = Struct.new(:name, :age).new("Hanna", 30) 37 | 38 | assert_equal @formatter.format(person), "#" 39 | end 40 | end 41 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 Yulia Oletskaya 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.gem 2 | *.rbc 3 | *.log 4 | *.bundle 5 | *.o 6 | .ruby-version 7 | Makefile 8 | /.config 9 | /coverage/ 10 | /InstalledFiles 11 | /pkg/ 12 | /spec/reports/ 13 | /spec/examples.txt 14 | /test/tmp/ 15 | /test/version_tmp/ 16 | /tmp/ 17 | 18 | # Used by dotenv library to load environment variables. 19 | # .env 20 | 21 | ## Specific to RubyMotion: 22 | .dat* 23 | .repl_history 24 | build/ 25 | *.bridgesupport 26 | build-iPhoneOS/ 27 | build-iPhoneSimulator/ 28 | 29 | ## Specific to RubyMotion (use of CocoaPods): 30 | # 31 | # We recommend against adding the Pods directory to your .gitignore. However 32 | # you should judge for yourself, the pros and cons are mentioned at: 33 | # https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control 34 | # 35 | # vendor/Pods/ 36 | 37 | ## Documentation cache and generated files: 38 | /.yardoc/ 39 | /_yardoc/ 40 | /doc/ 41 | /rdoc/ 42 | 43 | ## Environment normalization: 44 | /.bundle/ 45 | /vendor/bundle 46 | /lib/bundler/man/ 47 | 48 | # for a library or gem, you might want to ignore these files since the code is 49 | # intended to run in multiple environments; otherwise, check them in: 50 | # Gemfile.lock 51 | # .ruby-version 52 | # .ruby-gemset 53 | 54 | # unless supporting rvm < 1.11.0 or doing something fancy, ignore this: 55 | .rvmrc 56 | -------------------------------------------------------------------------------- /lib/blab/formatter.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module Blab 4 | class Formatter 5 | ELLIPSIS = "..." 6 | DEFAULT_MAX_LENGTH = 100 7 | 8 | attr_reader :max_length 9 | 10 | def initialize(max_length = DEFAULT_MAX_LENGTH) 11 | @max_length = max_length 12 | end 13 | 14 | def format(object) 15 | formatted = prepare_for_inspection(object).inspect 16 | return formatted if formatted.length < max_length 17 | 18 | beginning = truncate(formatted, 0, max_length / 2) 19 | ending = truncate(formatted, -max_length / 2, -1) 20 | "#{beginning}#{ELLIPSIS}#{ending}" 21 | end 22 | 23 | def prepare_for_inspection(object) 24 | case object 25 | when Array 26 | prepare_array(object) 27 | when Hash 28 | prepare_hash(object) 29 | else 30 | object 31 | end 32 | end 33 | 34 | def prepare_array(array) 35 | array.map { |element| prepare_for_inspection(element) } 36 | end 37 | 38 | def prepare_hash(input_hash) 39 | input_hash.inject({}) do |output_hash, key_and_value| 40 | key, value = key_and_value.map { |element| prepare_for_inspection(element) } 41 | output_hash[key] = value 42 | output_hash 43 | end 44 | end 45 | 46 | private 47 | 48 | def truncate(str, start_ind, end_ind) 49 | str[start_ind..end_ind].sub(/\e\[\d+$/, '') 50 | end 51 | end 52 | end 53 | -------------------------------------------------------------------------------- /test/test_printer.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require "minitest/autorun" 4 | require "blab" 5 | 6 | class TestPrinter < Minitest::Test 7 | def setup 8 | @logger = Blab::Config.logger 9 | @config = [ 10 | [:time, 12], 11 | [:event, 6], 12 | [:file_lines, 50], 13 | [:class_name, 12], 14 | [:method_name, 12], 15 | [:code_lines, 120] 16 | ] 17 | @printer = Blab::Printer.new(@config, @logger) 18 | end 19 | 20 | def test_file_lines 21 | options = { 22 | file: "/test/file.rb", 23 | line: 10 24 | } 25 | expected = [["/test/file.rb:10"], Blab::Printer::DEFAULT_FILE_LINES_WIDTH] 26 | assert_equal @printer.file_lines(options), expected 27 | end 28 | 29 | def test_extra_file_lines 30 | options = { 31 | file: "/some/pretty/long/path/to/test/file.rb", 32 | line: 10, 33 | width: 5 34 | } 35 | expected = [ 36 | ["/some", "/pret", "ty/lo", "ng/pa", "th/to", "/test", "/file", ".rb:1", "0"], 37 | options[:width] 38 | ] 39 | assert_equal @printer.file_lines(options), expected 40 | end 41 | 42 | def test_method_name 43 | options = { 44 | method_name: "secret", 45 | width: 15 46 | } 47 | expected = [[options[:method_name]], options[:width]] 48 | assert_equal @printer.method_name(options), expected 49 | end 50 | 51 | def test_event 52 | options = { event: "line" } 53 | expected = [[options[:event]], Blab::Printer::DEFAULT_EVENT_WIDTH] 54 | assert_equal @printer.event(options), expected 55 | end 56 | end 57 | -------------------------------------------------------------------------------- /lib/blab/config.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require "logger" 4 | 5 | module Blab 6 | module Config 7 | extend self 8 | 9 | DATETIME_FORMAT = "%H:%M:%S.%L" 10 | 11 | DEFAULT_OUTPUT = [ 12 | { type: :time, order: 1, width: 12 }, 13 | { type: :event, order: 2, width: 6 }, 14 | { type: :file_lines, order: 3, width: 50 }, 15 | #{ type: :class_name, order: 4, width: 10 }, 16 | #{ type: :method_name, order: 5, width: 12 }, 17 | { type: :code_lines, order: 5, width: 120 } 18 | ].freeze 19 | 20 | attr_writer :logger, 21 | :datetime_format, 22 | :log_output, 23 | :trace_c_calls, 24 | :output_config, 25 | :output_order, 26 | :original_scope_only 27 | 28 | def logger 29 | @logger ||= begin 30 | logger = Logger.new(log_output) 31 | logger.formatter = proc { |severity, datetime, progname, msg| "#{msg}\n" } 32 | logger 33 | end 34 | end 35 | 36 | def log_output 37 | @log_output ||= STDOUT 38 | end 39 | 40 | def output_order 41 | @output_order ||= DEFAULT_OUTPUT 42 | end 43 | 44 | def output_config 45 | @output_config ||= output_order.sort_by { |h| h[:order] }.map! { |h| [h[:type], h[:width]] } 46 | end 47 | 48 | def datetime_format 49 | @datetime_format ||= DATETIME_FORMAT 50 | end 51 | 52 | def trace_c_calls? 53 | @trace_c_calls ||= false 54 | end 55 | 56 | def original_scope_only? 57 | @original_scope_only ||= false 58 | end 59 | end 60 | end 61 | -------------------------------------------------------------------------------- /lib/blab/tracer.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module Blab 4 | module Tracer 5 | extend self 6 | 7 | FILE_NAME = /.+\/blab\.rb$/ 8 | C_CALLS = ["c-call", "c-return"].freeze 9 | 10 | def reset 11 | printer.reset_files 12 | @defined_vars = {} 13 | end 14 | 15 | def trace 16 | proc do |event, file, line, method_name, context, class_name| 17 | next if file =~ FILE_NAME 18 | next if skip_c_calls? && C_CALLS.include?(event) 19 | next if original_scope_only? && !original_scope?(file, method_name, class_name) 20 | 21 | context.local_variables.each do |v| 22 | next unless context.local_variable_defined?(v) 23 | 24 | val = context.local_variable_get(v) 25 | old_v = defined_vars[v] 26 | 27 | next if val == old_v 28 | 29 | formatted_output(v, val) 30 | defined_vars[v] = val 31 | end 32 | 33 | printer.print( 34 | time: Time.now.strftime(datetime_format), 35 | event: event, 36 | file: file, 37 | line: line, 38 | method_name: method_name.to_s, 39 | class_name: class_name.to_s 40 | ) 41 | end 42 | end 43 | 44 | private 45 | 46 | def printer 47 | @printer ||= Printer.new(Blab::Config.output_config, logger) 48 | end 49 | 50 | def logger 51 | Blab::Config.logger 52 | end 53 | 54 | def datetime_format 55 | Blab::Config.datetime_format 56 | end 57 | 58 | def formatted_output(key, val) 59 | logger.info("Var......... #{key}=#{formatter.format(val)}") 60 | end 61 | 62 | def formatter 63 | @formatter ||= Blab::Formatter.new 64 | end 65 | 66 | def defined_vars 67 | @defined_vars ||= {} 68 | end 69 | 70 | def original_scope_only? 71 | Blab::Config.original_scope_only? 72 | end 73 | 74 | def skip_c_calls? 75 | !Blab::Config.trace_c_calls? 76 | end 77 | 78 | def original_scope?(file, method_name, class_name) 79 | @original_file ||= file 80 | @original_method_name ||= method_name 81 | @orinal_class_name ||= class_name 82 | 83 | @original_file == file && 84 | @original_method_name == method_name && 85 | @orinal_class_name == class_name 86 | end 87 | end 88 | end 89 | -------------------------------------------------------------------------------- /lib/blab/printer.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module Blab 4 | class Printer 5 | DEFAULT_CLASS_NAME_WIDTH = 5 6 | DEFAULT_CODE_LINES_WIDTH = 120 7 | DEFAULT_EVENT_WIDTH = 6 8 | DEFAULT_FILE_LINES_WIDTH = 60 9 | DEFAULT_RU_MAXSS_WIDTH = 50 10 | DEFAULT_METHOD_NAME_WIDTH = 10 11 | DEFAULT_TIME_WIDTH = 12 12 | 13 | PRINT_FIELDS = [ 14 | :class_name, 15 | :event, 16 | :method_name, 17 | :time 18 | ].freeze 19 | 20 | attr_reader :config, :logger 21 | 22 | def initialize(config, logger) 23 | @config = config 24 | @logger = logger 25 | end 26 | 27 | def print(options = {}) 28 | strings = config.map do |(type, width)| 29 | send(type, options.merge(width: width)) 30 | end 31 | 32 | config_length = config.length 33 | final = strings.map { |e| e.first.length }.max.times.map do |i| 34 | config_length.times.map do |j| 35 | str = strings[j][0][i] || "" 36 | # TODO: do not ljust the last element 37 | config_length == (j + 1) ? str : str.ljust(strings[j][1]) 38 | end.join(" ") 39 | end 40 | logger.info(final.join("\n")) 41 | end 42 | 43 | def file_lines(options = {}) 44 | file = options[:file] 45 | line = options[:line] 46 | width = options[:width] || DEFAULT_FILE_LINES_WIDTH 47 | ["#{file}:#{line}".scan(/.{#{width}}|.+/), width] 48 | end 49 | 50 | def code_lines(options= {}) 51 | file = options[:file] 52 | line = options[:line] 53 | width = options[:width] || DEFAULT_CODE_LINES_WIDTH 54 | [source_line(file, line).scan(/.{#{width}}|.+/), width] 55 | end 56 | 57 | PRINT_FIELDS.each do |name| 58 | define_method(name) do |options = {}| 59 | val = options[name] 60 | width = options[:width] || self.class.const_get("DEFAULT_#{name.upcase}_WIDTH") 61 | [val.scan(/.{#{width}}|.+/), width] 62 | end 63 | end 64 | 65 | def reset_files 66 | @files_map && @files_map.keys.each { |key| @files_map.delete(key) } 67 | end 68 | 69 | def files_map 70 | @files_map ||= Hash.new do |h, f| 71 | h[f] = File.readlines(f) 72 | end 73 | end 74 | 75 | # TODO: show all relevant file-lines 76 | def source_line(file, line) 77 | begin 78 | files_map[file][line - 1] 79 | rescue 80 | "source is unavailable" 81 | end 82 | end 83 | end 84 | end 85 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # blab 2 | [![Gem Version](https://badge.fury.io/rb/blab.svg)](https://badge.fury.io/rb/blab) 3 | 4 | A debugging tool. 5 | 6 | The gem allows to trace local variables and memory usage for Ruby code. \ 7 | It's intended for use in a development environment only. \ 8 | Blab is inspired by [PySnooper](https://github.com/cool-RR/PySnooper). 9 | 10 | 11 | ## Installation 12 | 13 | Put this line in your Gemfile 14 | 15 | ```ruby 16 | gem "blab", group: :development 17 | ``` 18 | 19 | Then run 20 | 21 | ``` 22 | bundle install 23 | ``` 24 | 25 | ## Usage 26 | 27 | Use the `blab` decorator in front of a method defenition. 28 | 29 | ```ruby 30 | require "blab" 31 | 32 | class Test 33 | blab def longest_rep(str) 34 | max = str.chars.chunk(&:itself).map(&:last).max_by(&:size) 35 | max ? [max[0], max.size] : ["", 0] 36 | end 37 | end 38 | 39 | Test.new.longest_rep("cbaaabb") 40 | 41 | ``` 42 | 43 | The output to STDOUT: 44 | 45 | ``` 46 | Var......... str="cbaaabb" 47 | 18:17:26.042 call test/support/test.rb:46 blab def longest_rep(str) 48 | 18:17:26.042 line test/support/test.rb:47 max = str.chars.chunk(&:itself).map(&:last).max_by(&:size) 49 | Var......... max=["a", "a", "a"] 50 | 18:17:26.043 line test/support/test.rb:48 max ? [max[0], max.size] : ["", 0] 51 | 18:17:26.043 return test/support/test.rb:49 end 52 | ``` 53 | 54 | The gem allows to wrap only a piece of code in a block: 55 | 56 | ```ruby 57 | class Test 58 | def shuffle(arr) 59 | for n in 0...arr.size 60 | targ = n + rand(arr.size - n) 61 | arr[n], arr[targ] = arr[targ], arr[n] if n != targ 62 | end 63 | end 64 | 65 | def pairs(a, b) 66 | with_blab do 67 | a << "Insane" 68 | shuffle(b) 69 | end 70 | b.each { |x| shuffle(a); a.each { |y| print y, " ", x, ".\n" } } 71 | end 72 | end 73 | 74 | Test.new.pairs(["Bored", "Curious"], ["cat", "frog"]) 75 | ``` 76 | 77 | The output: 78 | 79 | ``` 80 | Var......... a=["Bored", "Curious"] 81 | Var......... b=["cat", "frog"] 82 | 18:38:15.188 line test/support/test.rb:54 a << "Insane" 83 | 18:38:15.188 line test/support/test.rb:55 shuffle(b) 84 | Var......... arr=["cat", "frog"] 85 | 18:38:15.188 call test/support/test.rb:45 def shuffle(arr) 86 | 18:38:15.189 line test/support/test.rb:46 for n in 0...arr.size 87 | Var......... n=0 88 | 18:38:15.189 line test/support/test.rb:47 targ = n + rand(arr.size - n) 89 | Var......... targ=0 90 | 18:38:15.189 line test/support/test.rb:48 arr[n], arr[targ] = arr[targ], arr[n] if n != targ 91 | Var......... n=1 92 | 18:38:15.189 line test/support/test.rb:47 targ = n + rand(arr.size - n) 93 | Var......... targ=1 94 | 18:38:15.189 line test/support/test.rb:48 arr[n], arr[targ] = arr[targ], arr[n] if n != targ 95 | 18:38:15.189 return test/support/test.rb:50 end 96 | ``` 97 | 98 | ## Configuration 99 | 100 | Output to a file: 101 | 102 | ```ruby 103 | Blab::Config.log_output = "log/blab.log" 104 | ``` 105 | 106 | Datetime format: 107 | 108 | ```ruby 109 | Blab::Config.datetime_format = "%H:%M:%S.%L" 110 | ``` 111 | 112 | Custom logger: 113 | 114 | ```ruby 115 | Blab::Config.logger = MyCustomLogger.new 116 | ``` 117 | 118 | Trace C calls your program makes from Ruby: 119 | 120 | ```ruby 121 | Blab::Config.trace_c_calls = true 122 | ``` 123 | 124 | Trace only within the original scope. \ 125 | It means that the trace will be showed only for the current method and it will skip all external call's traces. 126 | 127 | ```ruby 128 | Blab::Config.original_scope_only = true 129 | ``` 130 | 131 | Format output. Available config is: 132 | 133 | ```ruby 134 | output_order = [ 135 | { type: :time, order: 1, width: 12 }, 136 | { type: :event, order: 2, width: 6 }, 137 | { type: :file_lines, order: 3, width: 30 }, 138 | { type: :class_name, order: 4, width: 10 }, 139 | { type: :method_name, order: 5, width: 12 }, 140 | { type: :code_lines, order: 7, width: 120 } 141 | ] 142 | 143 | Blab::Config.output_order = output_order 144 | ``` 145 | By default it doesn't show current class name and method name. You can adjust the width, change the order, skip/add the desired output info. 146 | 147 | ## Contribution 148 | 149 | Fork & Pull Request. \ 150 | Run the tests via `rake`. 151 | 152 | ## License 153 | 154 | MIT 155 | 156 | --------------------------------------------------------------------------------