kojak 0.0.1

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.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: e1ccedf06853a170a84c39986ea3baad7ea38347
4
+ data.tar.gz: 7393b032fc39c06f0e4b311aead7b719165ae23c
5
+ SHA512:
6
+ metadata.gz: 2a4452b4d5f0bcd9e2e7a10fd787383beb3a4bf58cebe5ffa573d8bba70a958e551df4e41e39b807b9283a60706e024a4237268b9538dd7cb3880f6cf6f7b644
7
+ data.tar.gz: d18cefa933631004fcb2dd5393970655f670016ce388f46d53cb8099d2a46453bb56f298eeddc66b7bba7625a798b79793ebdfe649dd23a1ad2218c821b73741
data/.gitignore ADDED
@@ -0,0 +1,16 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /Gemfile.lock
4
+ /_yardoc/
5
+ /coverage/
6
+ /doc/
7
+ /pkg/
8
+ /spec/reports/
9
+ /tmp/
10
+ *.bundle
11
+ *.so
12
+ *.o
13
+ *.a
14
+ mkmf.log
15
+ \#*
16
+ .\#*
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in kojak.gemspec
4
+ gemspec
data/LICENSE.txt ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2014 Kris Kovalik
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,157 @@
1
+ # Kojak
2
+
3
+ Kojak is a bald and bold inspector and debug logger for Ruby apps. It aims to
4
+ provide debug (a.k.a. verbose) logging without writing actual any single line
5
+ of logging and printing junk. It also aims to add no overhead to the execution
6
+ whatsoever.
7
+
8
+ ## Introduction
9
+
10
+ Take a look at the following code...
11
+
12
+ ```ruby
13
+ class SomeFancyTransport
14
+ # *snip *
15
+
16
+ def receive
17
+ begin
18
+ log "Waiting for messages ..."
19
+ line = readline
20
+ log "Message received, decoding ..."
21
+ payload = JSON.parse(line)
22
+ log "Decoded: #{payload.inspect}"
23
+ log "Acknowledging ..."
24
+ send(:_ack => true, :_id => payload['_id'])
25
+ return payload
26
+ rescue Timeout::Error
27
+ log "Pinging ..."
28
+ send(:_ping => true)
29
+ rescue EOFError
30
+ log "Connection closed!"
31
+ @eof = true
32
+ rescue => err
33
+ log "Receive error: #{err.to_s}"
34
+ send(:_err => err.to_h) rescue nil
35
+ end
36
+
37
+ nil
38
+ end
39
+ end
40
+ ```
41
+
42
+ Uhh, let's face it, this sucks... It looks ugly, getting throuhg and figuring
43
+ out what that function does is complicated. It has 2x much code than it should,
44
+ and even though `log` can be only a stub in non-verbose mode, it'll still takes
45
+ execution time and adds overhead. I heard some wisdom from my friend Jens some
46
+ time ago:
47
+
48
+ *Your goal toward every single line of code in your app is go get rid of her,
49
+ less code == less bugs.*
50
+
51
+ So let's get rid of junk...
52
+
53
+ ```ruby
54
+ class SomeFancyTransport
55
+ # *snip *
56
+
57
+ def decode(s)
58
+ JSON.parse(s)
59
+ end
60
+
61
+ def ack(id)
62
+ send(:_ack => true, :_id => payload['_id'])
63
+ end
64
+
65
+ def ping
66
+ send(:_ping => true)
67
+ end
68
+
69
+ def receive_err(err)
70
+ send(:_err => err.to_h)
71
+ end
72
+
73
+ def eof!
74
+ @eof = true
75
+ end
76
+
77
+ def receive
78
+ begin
79
+ payload = decode(readline)
80
+ ack(payload['_id'])
81
+ return payload
82
+ rescue Timeout::Error
83
+ ping
84
+ rescue EOFError
85
+ eof!
86
+ rescue => err
87
+ receive_err(err) rescue nil
88
+ end
89
+
90
+ nil
91
+ end
92
+ end
93
+ ```
94
+
95
+ Um, but that's more code! Well, yes and no... Let's see, actual code to be
96
+ executed stays the same, rest are just method names and definitions. Though,
97
+ now the code is self documented and it's not sliced through and through
98
+ with junk logging lines. Easier to read, easier to test. But hey... where's the
99
+ logging? Well, here it is:
100
+
101
+ ```ruby
102
+ class SomeFancyTransport
103
+ extend Kojak::Inspector
104
+ investigate :readline, :decode, :ack, :ping, ...
105
+ end
106
+
107
+ Kojak.investigate_all!
108
+ ```
109
+
110
+ Hell yeah, magic! But not so much... `investigate` declares all methods that
111
+ should be inspected. Then `investigate_all!` from `Kojak` goes through all
112
+ these methods and redeclares them - it wraps original method with debugging
113
+ stuff. Now when you call that method, our inspector will gather informatin
114
+ about the execution (arguments, returns, thrown errors, execution time, etc.)
115
+ and nicely print it out for you.
116
+
117
+ Now answer youself to one very important question. When looking at verbose
118
+ log, what's more important for you? This...
119
+
120
+ ```
121
+ Waiting for messages ...
122
+ Message Received! Decoding ...
123
+ Acknowledging ...
124
+ Receive error: couldn't acknowledge
125
+ ```
126
+
127
+ Or maybe this:
128
+
129
+ ```
130
+ SomeFancyTransport#readline (in ./lib/foo/some_fancy_transport.rb:15):
131
+ | realtime = 10.12388s
132
+ | pid = 38711
133
+ | input = []
134
+ | output = "{\"_id\":\"1\"}"
135
+ SomeFancyTransport#decode (in ./lib/foo/some_fancy_transport.rb:20):
136
+ | realtime = 0.01425s
137
+ | pid = 38711
138
+ | input = ["{\"_id\":\"1\"}"]
139
+ | output = {:id => 1}
140
+ SomeFancyTransport#ack (in ./lib/foo/some_fancy_transport.rb:30):
141
+ | realtime = 0.03331s
142
+ | pid = 38711
143
+ | input = [1]
144
+ | error = #<StandardError: couldn't acknowledge>
145
+ SomeFancyTransport#receive_err (in ./lib/foo/some_fancy_transport.rb:40):
146
+ | realtime = 0.00010s
147
+ | pid = 38711
148
+ | input = #<StandardError: couldn't acknowledge>
149
+ | output = [true, {:_err => {:kind => "standard_error", :message => "couldn't acknowledge"}}]
150
+ ```
151
+
152
+ So here we swapped bunch of crappy logging code that produces a bit of useless
153
+ text messages into self-documented code, with no logging that produces this
154
+ full debug info including source location, input arguments, output and even
155
+ real execution time or pid of the process.
156
+
157
+ TODO: continue ...
data/Rakefile ADDED
@@ -0,0 +1,9 @@
1
+ require "bundler/gem_tasks"
2
+ require "rake/testtask"
3
+
4
+ Rake::TestTask.new(:test) do |t|
5
+ t.libs << "test"
6
+ t.pattern = "test/{,test_*}/test_*.rb"
7
+ end
8
+
9
+ task :default => :test
data/examples/hello.rb ADDED
@@ -0,0 +1,21 @@
1
+ require 'kojak'
2
+
3
+ class Hello
4
+ extend Kojak::Inspector
5
+ investigate :greet, :this_one_does_nothing
6
+
7
+ def greet(attrs = {})
8
+ raise ArgumentError.new("missing name") unless attrs[:name]
9
+ "Hello #{attrs[:name]}!"
10
+ end
11
+
12
+ def this_one_does_nothing
13
+ end
14
+ end
15
+
16
+ Kojak.investigate!
17
+
18
+ h = Hello.new
19
+ h.greet(:name => "Jon Snow")
20
+ h.greet(:a => 1, :b => 2, :c => 3) rescue nil
21
+ h.this_one_does_nothing
@@ -0,0 +1,39 @@
1
+ require 'kojak'
2
+
3
+ class Hello
4
+ extend Kojak::Inspector
5
+ investigate :greet, :this_one_does_nothing
6
+
7
+ def greet(attrs = {})
8
+ raise ArgumentError.new("missing name") unless attrs[:name]
9
+ "Hello #{attrs[:name]}!"
10
+ end
11
+
12
+ def this_one_does_nothing
13
+ end
14
+ end
15
+
16
+ class HelloTyrion < Hello
17
+ extend Kojak::Inspector
18
+ investigate :greet, :this_one_takes_a_while
19
+
20
+ def greet(attrs = {})
21
+ super(:name => "Tyrion Lannister")
22
+ end
23
+
24
+ def this_one_takes_a_while
25
+ sleep(1.2)
26
+ end
27
+ end
28
+
29
+ Kojak.investigate!
30
+
31
+ h1 = Hello.new
32
+ h1.greet(:name => "Jon Snow")
33
+ h1.greet(:a => 1, :b => 2, :c => 3) rescue nil
34
+ h1.this_one_does_nothing
35
+
36
+ h2 = HelloTyrion.new
37
+ h2.greet(:foo => :bar)
38
+ h2.this_one_does_nothing
39
+ h2.this_one_takes_a_while
data/kojak.gemspec ADDED
@@ -0,0 +1,27 @@
1
+ # -*- mode: ruby -*-
2
+ # -*- coding: utf-8 -*-
3
+ lib = File.expand_path('../lib', __FILE__)
4
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
5
+ require 'kojak/version'
6
+
7
+ Gem::Specification.new do |spec|
8
+ spec.name = "kojak"
9
+ spec.version = Kojak::VERSION
10
+ spec.authors = ["Kris Kovalik", "NIMBOOST Team"]
11
+ spec.email = ["dev@nimboost.com"]
12
+ spec.summary = %q{Kojak is a neat and bald tracer and debug logger.}
13
+ spec.description = %q{Kojak allows you to trace and debug method calls.}
14
+ spec.homepage = "http://docs.nimboost.com/lib/ruby/kojak"
15
+ spec.license = "MIT"
16
+
17
+ spec.files = `git ls-files -z`.split("\x0")
18
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
19
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
20
+ spec.require_paths = ["lib"]
21
+
22
+ spec.add_dependency "json", "~> 1.8"
23
+ spec.add_dependency "rainbow", "~> 2.0"
24
+ spec.add_development_dependency "bundler", "~> 1.6"
25
+ spec.add_development_dependency "rake", "~> 10.0"
26
+ spec.add_development_dependency "mocha"
27
+ end
data/lib/kojak.rb ADDED
@@ -0,0 +1,12 @@
1
+ require "benchmark"
2
+
3
+ module Kojak
4
+ require "kojak/version"
5
+ require "kojak/printer"
6
+ require "kojak/caller"
7
+ require "kojak/inspector"
8
+ require "kojak/output"
9
+ require "kojak/central"
10
+
11
+ extend Central
12
+ end
@@ -0,0 +1,58 @@
1
+ module Kojak
2
+ # Internal: Caller wraps a method with inspection mechanism.
3
+ class Caller < Hash
4
+ # Public: Constructor. Wraps given method with caller. Gathers initial
5
+ # information about this call.
6
+ #
7
+ # method - The Method to be wrapped.
8
+ #
9
+ # Returns nothing.
10
+ def initialize(method)
11
+ @method = method
12
+
13
+ replace({
14
+ :name => "#{method.owner.name}##{method.name}",
15
+ :method => method,
16
+ :location => method.source_location,
17
+ :pid => Process.pid,
18
+ :thread => Thread.current,
19
+ :thread_id => Thread.object_id,
20
+ })
21
+ end
22
+
23
+ # Public: Calls wrapped method with given arguments and block, and
24
+ # collects all information about this particular call.
25
+ #
26
+ # args - The Array with arguments to pass.
27
+ # block - The Proc with block to pass.
28
+ #
29
+ # Returns stuff returned by called method. Raises exceptions
30
+ # by called method if any.
31
+ def call(*args, &block)
32
+ merge!({
33
+ :block_given => block_given?,
34
+ :block => block,
35
+ :args => args
36
+ })
37
+
38
+ res, err = nil, nil
39
+ rt = Benchmark.realtime do
40
+ begin
41
+ res = @method.call(*args, &block)
42
+ rescue Exception => e
43
+ err = e
44
+ end
45
+ end
46
+
47
+ merge!({
48
+ :receiver => @method.receiver.class.name,
49
+ :realtime => rt,
50
+ :result => res,
51
+ :err => err
52
+ })
53
+
54
+ raise err if err
55
+ res
56
+ end
57
+ end
58
+ end
@@ -0,0 +1,54 @@
1
+ require "thread"
2
+
3
+ module Kojak
4
+ # Internal: An extension with central utilities. This module extends top
5
+ # level Kojak module, don't use it directly.
6
+ module Central
7
+ # Global mutex.
8
+ @@mtx = Mutex.new
9
+
10
+ # Slow log of investigated methods. Contains list of called names in
11
+ # order of calling.
12
+ @@log = Array.new
13
+
14
+ # Global printer.
15
+ @@printer = Printer::RandomlyColorized.new($stderr)
16
+
17
+ # Investigated classes.
18
+ @@investigated = {}
19
+
20
+ # Internal: Returns slow log array.
21
+ def slowlog
22
+ @@mtx.synchronize { @@log }
23
+ end
24
+
25
+ # Internal: List of classes marked for investigation.
26
+ def investigated
27
+ @@mtx.synchronize { @@investigated.values }
28
+ end
29
+
30
+ # Internal: Marks given class as investigated.
31
+ #
32
+ # cls - The Class to register.
33
+ #
34
+ # Returns list of investigated classes.
35
+ def mark_for_investigation(cls)
36
+ @@mtx.synchronize { @@investigated[cls.object_id] = cls }
37
+ end
38
+
39
+ # Internal: Prints investigation results and writes to slow log.
40
+ #
41
+ # caller - The Caller to print.
42
+ #
43
+ # Returns nothing.
44
+ def print(caller)
45
+ @@mtx.synchronize { @@log << caller[:name] }
46
+ @@printer.puts(Output.new(caller))
47
+ end
48
+
49
+ # Internal: Enables investigation of all registered classes.
50
+ def investigate!
51
+ @@mtx.synchronize { @@investigated.values.each(&:investigate!) }
52
+ end
53
+ end
54
+ end
@@ -0,0 +1,62 @@
1
+ module Kojak
2
+ # Internal: Registers investigator within given class for specified method.
3
+ #
4
+ # cls - The Class that will be affected.
5
+ # m - The Method to override with investigation support.
6
+ #
7
+ # Returns nothing.
8
+ def self.register_investigator_for!(cls, m)
9
+ cls.send(:define_method, m.name) do |*args,&block|
10
+ begin
11
+ caller = Caller.new(m.bind(self))
12
+ caller.call(*args,&block)
13
+ ensure
14
+ Kojak.print(caller) if caller
15
+ end
16
+ end
17
+ end
18
+
19
+ # Public: Extend your class with this inspector module to get
20
+ # investigation functionality.
21
+ #
22
+ # Example
23
+ #
24
+ # class Dummy
25
+ # investigate :foo, :bar
26
+ #
27
+ # def foo
28
+ # *snip*...
29
+ # end
30
+ #
31
+ # def bar
32
+ # *snip*...
33
+ # end
34
+ # end
35
+ #
36
+ # Dummy.investigate! # ... or Kojak.investigate_all!
37
+ #
38
+ module Inspector
39
+ def self.extended(cls)
40
+ Kojak.mark_for_investigation(cls)
41
+ end
42
+
43
+ # Public: Marks given methods for investigation.
44
+ #
45
+ # names - The Array of method names to investigate.
46
+ #
47
+ # Returns Array with names of all methods being under investigation.
48
+ def investigate(*names)
49
+ Kojak.mark_for_investigation(self)
50
+ @__kojak_investigate ||= []
51
+ @__kojak_investigate.concat(names).uniq!
52
+ end
53
+
54
+ # Public: Enables investigation of caller class.
55
+ def investigate!
56
+ @__kojak_investigate.each do |name|
57
+ m = instance_method(name)
58
+ Kojak.register_investigator_for!(self, m)
59
+ end
60
+ end
61
+ end
62
+ end
@@ -0,0 +1,66 @@
1
+ module Kojak
2
+ # Internal: Pretty output string from given caller information.
3
+ class Output < String
4
+ # Public: Constructor. Generates output from given caller.
5
+ def initialize(caller)
6
+ @c = caller
7
+ super("\n" + all.flatten.compact.join("\n") + "\n")
8
+ end
9
+
10
+ def all
11
+ [
12
+ header, receiver, location, pid, realtime, arguments,
13
+ result, err, footer
14
+ ]
15
+ end
16
+
17
+ def header
18
+ "=== #{@c[:name]} ".ljust(80, "=")
19
+ end
20
+
21
+ def receiver
22
+ "receiver_class=#{@c[:receiver]}"
23
+ end
24
+
25
+ def location
26
+ "source_location=#{@c[:location].join(":")}"
27
+ end
28
+
29
+ def pid
30
+ "pid=#{@c[:pid]}"
31
+ end
32
+
33
+ def realtime
34
+ "real_time=%.5f" % @c[:realtime]
35
+ end
36
+
37
+ def arguments
38
+ args = @c[:args]
39
+ return if !args || args.empty?
40
+ ["--- arguments ".ljust(80, '-'), pp(args)]
41
+ end
42
+
43
+ def result
44
+ res = @c[:result]
45
+ return if res.nil?
46
+ ["--- result ".ljust(80, '-'), pp(res)]
47
+ end
48
+
49
+ def err
50
+ err = @c[:err]
51
+ return if err.nil?
52
+ errname = err.class.name + ": " + err.to_s
53
+ ["--- exception ".ljust(80, '-'), errname]
54
+ end
55
+
56
+ def footer
57
+ "=" * 80
58
+ end
59
+
60
+ protected
61
+
62
+ def pp(x)
63
+ JSON.pretty_generate(x) rescue x.inspect
64
+ end
65
+ end
66
+ end
@@ -0,0 +1,34 @@
1
+ require 'rainbow'
2
+
3
+ module Kojak
4
+ # Internal: Abstract design of a printing class.
5
+ #
6
+ # This is just a stub with documentation, implement actual printers
7
+ # in inherited classes.
8
+ class Printer
9
+ # Public: Writes given string and a newline.
10
+ #
11
+ # s - The String to print.
12
+ # args - The Array with interpolation arguments.
13
+ #
14
+ # Should return number of writen characters.
15
+ def puts(s, *args)
16
+ write("#{s}\n", *args)
17
+ end
18
+
19
+ # Public: Writes given string to the output.
20
+ #
21
+ # s - The String to print.
22
+ # args - The Array with interpolation arguments.
23
+ #
24
+ # Should return number of written characters.
25
+ def write(s, *args)
26
+ end
27
+ end
28
+
29
+ require 'kojak/printer/nulled'
30
+ require 'kojak/printer/basic'
31
+ require 'kojak/printer/synchronized'
32
+ require 'kojak/printer/colorized'
33
+ require 'kojak/printer/randomly_colorized'
34
+ end
@@ -0,0 +1,20 @@
1
+ require 'json'
2
+
3
+ module Kojak
4
+ # Internal: Basic printer. It prints stuff to configured output stream.
5
+ class Printer::Basic < Printer
6
+ # Public: Constructor. Configures printer to write to given output stream.
7
+ #
8
+ # out - The Stream to write data to.
9
+ #
10
+ # Returns nothing.
11
+ def initialize(out)
12
+ @out = out
13
+ end
14
+
15
+ def write(s, *args)
16
+ args = args.map { |x| JSON.pretty_generate(x) rescue x.inspect }
17
+ @out.write(s.to_s % args)
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,28 @@
1
+ require 'rainbow'
2
+
3
+ module Kojak
4
+ # Internal: Special version of thread-safe printer that additionally colorizes
5
+ # the output with one preconfigured color.
6
+ class Printer::Colorized < Printer::Synchronized
7
+ # Public: The String or Symbol with name of the assigned color.
8
+ attr_accessor :color
9
+
10
+ # Public: Constructor. Configures printer to write to given output in
11
+ # specified color.
12
+ #
13
+ # out - The Stream to write data to.
14
+ # color - The String or Symbol with name of the color to use.
15
+ #
16
+ # Returns nothing.
17
+ def initialize(out, color = :white)
18
+ super(out)
19
+
20
+ @color = color
21
+ @rainbow = Rainbow.new
22
+ end
23
+
24
+ def write(s, *args)
25
+ super(@rainbow.wrap(s.to_s).color(color), *args)
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,7 @@
1
+ module Kojak
2
+ # Internal: Nulled printer.
3
+ class Printer::Nulled < Printer
4
+ def puts(s, *args)
5
+ end
6
+ end
7
+ end
@@ -0,0 +1,30 @@
1
+ require 'rainbow'
2
+
3
+ module Kojak
4
+ # Internal: Colorized logger that uses random color from the palette.
5
+ class Printer::RandomlyColorized < Printer::Colorized
6
+ # Internal: List of colors to pick from. This list is always
7
+ # randomized on start.
8
+ PALETTE = %w[
9
+ #af8700 #af875f #af8787 #af87af #af87d7 #af87ff #afaf00 #afaf5f
10
+ #afafaf #afafd7 #afafff #afd700 #afd75f #afd787 #afd7af #afd7d7
11
+ #afd7ff #afff00 #afff5f #afff87 #afffaf #afffd7 #afffff #878787
12
+ #8787af #8787d7 #8787ff #87af00 #87af5f #87af87 #87afaf #87afd7
13
+ #87afff #87d700 #87d75f #87d787 #87d7af #87d7d7 #87d7ff #87ff00
14
+ #87ff5f #87ff87 #87ffaf #87ffd7 #87ffff #d78700 #d7875f #d78787
15
+ #d787af #d787d7 #d787ff #d7af00 #d7af5f #d7af87 #d7afaf #d7afd7
16
+ ].compact.shuffle
17
+
18
+ def initialize(out)
19
+ super(out, nil)
20
+ @current = -1
21
+ end
22
+
23
+ def color
24
+ @@lock.synchronize do
25
+ @current += 1
26
+ PALETTE[@current % PALETTE.size]
27
+ end
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,10 @@
1
+ module Kojak
2
+ # Internal: Thread-safe version of Printer::Basic.
3
+ class Printer::Synchronized < Printer::Basic
4
+ @@lock = Mutex.new
5
+
6
+ def write(s, *args)
7
+ @@lock.synchronize { super }
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,3 @@
1
+ module Kojak
2
+ VERSION = "0.0.1"
3
+ end
@@ -0,0 +1,9 @@
1
+ class Dummy
2
+ def dummy1(opts = {})
3
+ 1
4
+ end
5
+
6
+ def dummy2(a, b)
7
+ 2
8
+ end
9
+ end
@@ -0,0 +1,9 @@
1
+ $LOAD_PATH.unshift File.expand_path('../../lib', __FILE__)
2
+
3
+ require 'kojak'
4
+ require 'minitest/autorun'
5
+ require 'mocha/mini_test'
6
+
7
+ Dir[File.expand_path("../dummies/*.rb", __FILE__)].each do |f|
8
+ require f
9
+ end
@@ -0,0 +1,73 @@
1
+ require 'minitest_helper'
2
+
3
+ describe Kojak::Caller do
4
+ let(:obj) do
5
+ Dummy.new
6
+ end
7
+
8
+ let(:method) do
9
+ :dummy1
10
+ end
11
+
12
+ let(:caller) do
13
+ Kojak::Caller.new(obj.method(method))
14
+ end
15
+
16
+ describe "initialize" do
17
+ it "should gather base information about the method" do
18
+ caller[:name].must_equal "#{obj.class.name}##{method}"
19
+ caller[:method].must_equal obj.method(method)
20
+ caller[:location].must_equal obj.method(method).source_location
21
+ caller[:pid].must_equal Process.pid
22
+ caller[:thread_id].must_equal Thread.object_id
23
+ caller[:thread].must_equal Thread.current
24
+ end
25
+ end
26
+
27
+ describe "call" do
28
+ describe "when normal call" do
29
+ it "should store args" do
30
+ args = {:foo => 1}
31
+ caller.call(args)
32
+ caller[:args].must_equal [args]
33
+ caller[:block].must_be_nil
34
+ caller[:block_given].must_equal false
35
+ end
36
+
37
+ it "should return same result as method would" do
38
+ res = caller.call
39
+ res.must_equal 1
40
+ caller[:result].must_equal res
41
+ end
42
+
43
+ it "should measure execution time" do
44
+ res = caller.call
45
+ caller[:realtime].must_be ">", 0
46
+ end
47
+ end
48
+
49
+ describe "when block given" do
50
+ it "should store info about the block" do
51
+ block = proc {}
52
+ caller.call(&block)
53
+ assert_equal caller[:block], block
54
+ caller[:block_given].must_equal true
55
+ end
56
+ end
57
+
58
+ describe "when error raised from method" do
59
+ let(:method) do
60
+ :dummy2
61
+ end
62
+
63
+ it "should capture it and re raise" do
64
+ res = nil
65
+ err = assert_raises ArgumentError do
66
+ res = caller.call(1,2,3,4)
67
+ end
68
+ caller[:err].must_equal err
69
+ caller[:result].must_be_nil
70
+ end
71
+ end
72
+ end
73
+ end
@@ -0,0 +1,35 @@
1
+ require 'minitest_helper'
2
+
3
+ describe Kojak::Inspector do
4
+ let(:cls) do
5
+ Class.new(Dummy) do
6
+ extend Kojak::Inspector
7
+ investigate :dummy1
8
+ end
9
+ end
10
+
11
+ let(:obj) do
12
+ cls.new
13
+ end
14
+
15
+ before do
16
+ cls.extend(Kojak::Inspector)
17
+ end
18
+
19
+ describe "investigation" do
20
+ it "should register class in top level" do
21
+ assert Kojak.investigated.include?(cls)
22
+ end
23
+
24
+ it "should redefine investigated methods" do
25
+ cls.investigate!
26
+ res = nil
27
+ out, err = capture_subprocess_io do
28
+ res = obj.dummy1(:foo => 1)
29
+ end
30
+ res.must_equal(1)
31
+ err.must_match(/Dummy#dummy1/)
32
+ Kojak.slowlog.last.must_equal("Dummy#dummy1")
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,51 @@
1
+ require 'minitest_helper'
2
+ require 'stringio'
3
+
4
+ describe Kojak::Printer::Basic do
5
+ let(:out) do
6
+ StringIO.new
7
+ end
8
+
9
+ let(:printer) do
10
+ Kojak::Printer::Basic.new(out)
11
+ end
12
+
13
+ let(:text) do
14
+ "foo"
15
+ end
16
+
17
+ describe "write" do
18
+ it "should write given string to the output" do
19
+ printer.write(text)
20
+ out.seek(0)
21
+ out.read.must_equal(text)
22
+ end
23
+
24
+ describe "interpolation" do
25
+ it "should interpolate text" do
26
+ printer.write("%s", text)
27
+ out.seek(0)
28
+ out.read.must_equal(text.inspect)
29
+ end
30
+
31
+ describe "with objects" do
32
+ let(:obj) do
33
+ {:foo => 1}
34
+ end
35
+
36
+ it "should pretty print interpolated objects" do
37
+ printer.write("%s", obj)
38
+ out.seek(0)
39
+ out.read.must_equal(JSON.pretty_generate(obj))
40
+ end
41
+ end
42
+ end
43
+ end
44
+
45
+ describe "puts" do
46
+ it "should write given string and newline to the output" do
47
+ printer.expects(:write).with(text + "\n")
48
+ printer.puts(text)
49
+ end
50
+ end
51
+ end
@@ -0,0 +1,31 @@
1
+ require 'minitest_helper'
2
+ require 'stringio'
3
+
4
+ describe Kojak::Printer::Colorized do
5
+ let(:out) do
6
+ StringIO.new
7
+ end
8
+
9
+ let(:printer) do
10
+ Kojak::Printer::Colorized.new(out, :red)
11
+ end
12
+
13
+ let(:text) do
14
+ "foo"
15
+ end
16
+
17
+ describe "write" do
18
+ it "should write given string to the output" do
19
+ printer.write(text)
20
+ out.seek(0)
21
+ out.read.must_equal("\e[31m#{text}\e[0m")
22
+ end
23
+ end
24
+
25
+ describe "puts" do
26
+ it "should write given string and newline to the output" do
27
+ printer.expects(:write).with(text + "\n")
28
+ printer.puts(text)
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,24 @@
1
+ require 'minitest_helper'
2
+ require 'stringio'
3
+
4
+ describe Kojak::Printer::Nulled do
5
+ let(:printer) do
6
+ Kojak::Printer::Nulled.new
7
+ end
8
+
9
+ let(:text) do
10
+ "foo"
11
+ end
12
+
13
+ describe "write" do
14
+ it "should do nothing" do
15
+ printer.write(text)
16
+ end
17
+ end
18
+
19
+ describe "puts" do
20
+ it "should do nothing" do
21
+ printer.puts(text)
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,25 @@
1
+ require 'minitest_helper'
2
+ require 'stringio'
3
+
4
+ describe Kojak::Printer::RandomlyColorized do
5
+ let(:out) do
6
+ StringIO.new
7
+ end
8
+
9
+ let(:printer) do
10
+ Kojak::Printer::RandomlyColorized.new(out)
11
+ end
12
+
13
+ it "should be colorized" do
14
+ printer.must_be_kind_of Kojak::Printer::Colorized
15
+ end
16
+
17
+ it "should always pick different color" do
18
+ prev = nil
19
+ 100.times do
20
+ current = printer.color
21
+ current.wont_equal prev
22
+ prev = current
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,31 @@
1
+ require 'minitest_helper'
2
+ require 'stringio'
3
+
4
+ describe Kojak::Printer::Synchronized do
5
+ let(:out) do
6
+ StringIO.new
7
+ end
8
+
9
+ let(:printer) do
10
+ Kojak::Printer::Synchronized.new(out)
11
+ end
12
+
13
+ let(:text) do
14
+ "foo"
15
+ end
16
+
17
+ describe "write" do
18
+ it "should write given string to the output" do
19
+ printer.write(text)
20
+ out.seek(0)
21
+ out.read.must_equal(text)
22
+ end
23
+ end
24
+
25
+ describe "puts" do
26
+ it "should write given string and newline to the output" do
27
+ printer.expects(:write).with(text + "\n")
28
+ printer.puts(text)
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,17 @@
1
+ require 'minitest_helper'
2
+
3
+ describe Kojak do
4
+ it "should have correct version number" do
5
+ Kojak::VERSION.must_equal "0.0.1"
6
+ end
7
+
8
+ describe "investigate!" do
9
+ it "should run investigation on all registered classes" do
10
+ dummy = Class.new { extend Kojak::Inspector }
11
+ dummy.expects(:investigate!)
12
+ Kojak.investigated.clear
13
+ Kojak.mark_for_investigation(dummy)
14
+ Kojak.investigate!
15
+ end
16
+ end
17
+ end
metadata ADDED
@@ -0,0 +1,155 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: kojak
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Kris Kovalik
8
+ - NIMBOOST Team
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2014-09-02 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: json
16
+ requirement: !ruby/object:Gem::Requirement
17
+ requirements:
18
+ - - ~>
19
+ - !ruby/object:Gem::Version
20
+ version: '1.8'
21
+ type: :runtime
22
+ prerelease: false
23
+ version_requirements: !ruby/object:Gem::Requirement
24
+ requirements:
25
+ - - ~>
26
+ - !ruby/object:Gem::Version
27
+ version: '1.8'
28
+ - !ruby/object:Gem::Dependency
29
+ name: rainbow
30
+ requirement: !ruby/object:Gem::Requirement
31
+ requirements:
32
+ - - ~>
33
+ - !ruby/object:Gem::Version
34
+ version: '2.0'
35
+ type: :runtime
36
+ prerelease: false
37
+ version_requirements: !ruby/object:Gem::Requirement
38
+ requirements:
39
+ - - ~>
40
+ - !ruby/object:Gem::Version
41
+ version: '2.0'
42
+ - !ruby/object:Gem::Dependency
43
+ name: bundler
44
+ requirement: !ruby/object:Gem::Requirement
45
+ requirements:
46
+ - - ~>
47
+ - !ruby/object:Gem::Version
48
+ version: '1.6'
49
+ type: :development
50
+ prerelease: false
51
+ version_requirements: !ruby/object:Gem::Requirement
52
+ requirements:
53
+ - - ~>
54
+ - !ruby/object:Gem::Version
55
+ version: '1.6'
56
+ - !ruby/object:Gem::Dependency
57
+ name: rake
58
+ requirement: !ruby/object:Gem::Requirement
59
+ requirements:
60
+ - - ~>
61
+ - !ruby/object:Gem::Version
62
+ version: '10.0'
63
+ type: :development
64
+ prerelease: false
65
+ version_requirements: !ruby/object:Gem::Requirement
66
+ requirements:
67
+ - - ~>
68
+ - !ruby/object:Gem::Version
69
+ version: '10.0'
70
+ - !ruby/object:Gem::Dependency
71
+ name: mocha
72
+ requirement: !ruby/object:Gem::Requirement
73
+ requirements:
74
+ - - '>='
75
+ - !ruby/object:Gem::Version
76
+ version: '0'
77
+ type: :development
78
+ prerelease: false
79
+ version_requirements: !ruby/object:Gem::Requirement
80
+ requirements:
81
+ - - '>='
82
+ - !ruby/object:Gem::Version
83
+ version: '0'
84
+ description: Kojak allows you to trace and debug method calls.
85
+ email:
86
+ - dev@nimboost.com
87
+ executables: []
88
+ extensions: []
89
+ extra_rdoc_files: []
90
+ files:
91
+ - .gitignore
92
+ - Gemfile
93
+ - LICENSE.txt
94
+ - README.md
95
+ - Rakefile
96
+ - examples/hello.rb
97
+ - examples/more_complex.rb
98
+ - kojak.gemspec
99
+ - lib/kojak.rb
100
+ - lib/kojak/caller.rb
101
+ - lib/kojak/central.rb
102
+ - lib/kojak/inspector.rb
103
+ - lib/kojak/output.rb
104
+ - lib/kojak/printer.rb
105
+ - lib/kojak/printer/basic.rb
106
+ - lib/kojak/printer/colorized.rb
107
+ - lib/kojak/printer/nulled.rb
108
+ - lib/kojak/printer/randomly_colorized.rb
109
+ - lib/kojak/printer/synchronized.rb
110
+ - lib/kojak/version.rb
111
+ - test/dummies/dummy.rb
112
+ - test/minitest_helper.rb
113
+ - test/test_caller.rb
114
+ - test/test_inspector.rb
115
+ - test/test_printer/test_basic.rb
116
+ - test/test_printer/test_colorized.rb
117
+ - test/test_printer/test_nulled.rb
118
+ - test/test_printer/test_randomly_colorized.rb
119
+ - test/test_printer/test_synchronized.rb
120
+ - test/test_top_level.rb
121
+ homepage: http://docs.nimboost.com/lib/ruby/kojak
122
+ licenses:
123
+ - MIT
124
+ metadata: {}
125
+ post_install_message:
126
+ rdoc_options: []
127
+ require_paths:
128
+ - lib
129
+ required_ruby_version: !ruby/object:Gem::Requirement
130
+ requirements:
131
+ - - '>='
132
+ - !ruby/object:Gem::Version
133
+ version: '0'
134
+ required_rubygems_version: !ruby/object:Gem::Requirement
135
+ requirements:
136
+ - - '>='
137
+ - !ruby/object:Gem::Version
138
+ version: '0'
139
+ requirements: []
140
+ rubyforge_project:
141
+ rubygems_version: 2.0.6
142
+ signing_key:
143
+ specification_version: 4
144
+ summary: Kojak is a neat and bald tracer and debug logger.
145
+ test_files:
146
+ - test/dummies/dummy.rb
147
+ - test/minitest_helper.rb
148
+ - test/test_caller.rb
149
+ - test/test_inspector.rb
150
+ - test/test_printer/test_basic.rb
151
+ - test/test_printer/test_colorized.rb
152
+ - test/test_printer/test_nulled.rb
153
+ - test/test_printer/test_randomly_colorized.rb
154
+ - test/test_printer/test_synchronized.rb
155
+ - test/test_top_level.rb