pask 0.1.0

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.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 0efd58f652add7f0a2601ca79c2bd14cd1a332c2
4
+ data.tar.gz: f2a1dd7ec682b6723e74c93089b379ec9bfaab84
5
+ SHA512:
6
+ metadata.gz: f05dcb5e9c2dbe6c913a0f6a7454560ce1ba45e9a278efdecdfccf5b8697371426755d786cf354a8cd42b2f9df9a3e03e6b5bc11b96c49c4740cfb7196f16f15
7
+ data.tar.gz: 3d5b48e49f330ca4899ae8daf6af9b764f3fac1e34a1e9a18f624e2d51340542155b95207ff35639c822f7e0de4064bef5b7b97682bd89f49aa83473fb2afa2b
@@ -0,0 +1 @@
1
+ Gemfile.lock
data/.pryrc ADDED
@@ -0,0 +1,3 @@
1
+ require "bundler"
2
+ Bundler.require :default
3
+ Pry.plugins.values.map(&:disable!)
@@ -0,0 +1 @@
1
+ 2
@@ -0,0 +1,9 @@
1
+ rvm:
2
+ - 1.9.2
3
+ - 1.9.3
4
+ - 2.0.0
5
+ - 2.1.0
6
+ - ruby-head
7
+
8
+ notifications:
9
+ email: true
@@ -0,0 +1,3 @@
1
+ == v0.1.0
2
+ - release!
3
+ it works. quiet release.
data/Gemfile ADDED
@@ -0,0 +1,13 @@
1
+ source 'https://rubygems.org'
2
+ gemspec
3
+ gem "binding.repl"
4
+
5
+ group :development do
6
+ gem "rake"
7
+ end
8
+
9
+ group :development, :test do
10
+ gem "pry"
11
+ gem "pry-doc"
12
+ gem "minitest", require: "minitest/autorun"
13
+ end
@@ -0,0 +1,74 @@
1
+ __OVERVIEW__
2
+
3
+ | Project | pask
4
+ |:----------------|:--------------------------------------------------
5
+ | Homepage | https://github.com/robgleeson/pask
6
+ | Build | [![Build Status](https://travis-ci.org/robgleeson/pask.png?branch=master)](https://travis-ci.org/robgleeson/pask)
7
+
8
+ __DESCRIPTION__
9
+
10
+ pask is a concurrent tracer written with the power of `Thread#set_trace_func`.
11
+
12
+ __EXAMPLES__
13
+
14
+ ```ruby
15
+ tracer = Pask.new
16
+ tracer.trace { Person.call }
17
+ event1 = tracer.start # the event for "Person.call called"
18
+ event2 = tracer.resume # the event for "Person.call returned"
19
+ event3 = tracer.resume # returns nil (no more code to trace)
20
+ ```
21
+
22
+ __REPL IN YO TRACER__
23
+
24
+ pask produces instance's of `Pask::Event` when it traces code. an event is returned to
25
+ the caller and opens up some exciting possibilities(due to the wonders of `Binding`):
26
+
27
+ ```ruby
28
+ def call
29
+ x = 1
30
+ p x
31
+ end
32
+
33
+ tracer = Pask.new
34
+ tracer.trace { Person.call }
35
+ event1 = tracer.start
36
+ event.binding.pry # start pry inside 'call', before assignment of x.
37
+ ```
38
+
39
+ __RUBIES__
40
+
41
+ - MRI
42
+ - 1.9.2
43
+ - 1.9.3
44
+ - 2.0.0
45
+ - 2.1.0
46
+ - 2.1.0+
47
+
48
+ __CONTRIBUTE!__
49
+
50
+ [fork it](https://github.com/robgleeson/binding.repl/fork), clone it, change! <br>
51
+ open a pull request :)
52
+
53
+ some tips for working on the project:
54
+ ```
55
+ cd $CLONED_DIR
56
+ bundle install
57
+ rake test # run tests
58
+ ```
59
+
60
+ __INSTALL__
61
+
62
+ $ gem install pask
63
+
64
+ __SEE ALSO__
65
+
66
+ - [machine.repl](https://github.com/robgleeson/machine.repl)<br>
67
+ the webmachine-ruby repl/debugger.
68
+
69
+ - [examples/ directory](https://github.com/robgleeson/pask/tree/master/examples)<br>
70
+ examples in the `examples/` directory. `$ rake examples` runs em all.
71
+
72
+ __LICENSE__
73
+
74
+ see UNLICENSE.txt. public domain.
@@ -0,0 +1,24 @@
1
+ require "bundler/gem_tasks"
2
+
3
+ desc "run the tests"
4
+ task :test do
5
+ Dir["test/*_test.rb"].each do |file|
6
+ require_relative(file)
7
+ end
8
+ end
9
+ task :default => :test
10
+
11
+ desc "run the examples in examples/*.rb"
12
+ task :examples do
13
+ Dir["examples/*.rb"].each do |example|
14
+ sh "ruby %s" % [example]
15
+ puts
16
+ end
17
+ end
18
+
19
+ desc "repeat test runs twenty times to look for race conditions"
20
+ task :race do
21
+ 20.times do |index|
22
+ Rake::Task["test"].execute
23
+ end
24
+ end
@@ -0,0 +1,24 @@
1
+ This is free and unencumbered software released into the public domain.
2
+
3
+ Anyone is free to copy, modify, publish, use, compile, sell, or
4
+ distribute this software, either in source code form or as a compiled
5
+ binary, for any purpose, commercial or non-commercial, and by any
6
+ means.
7
+
8
+ In jurisdictions that recognize copyright laws, the author or authors
9
+ of this software dedicate any and all copyright interest in the
10
+ software to the public domain. We make this dedication for the benefit
11
+ of the public at large and to the detriment of our heirs and
12
+ successors. We intend this dedication to be an overt act of
13
+ relinquishment in perpetuity of all present and future rights to this
14
+ software under copyright law.
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 NONINFRINGEMENT.
19
+ IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
20
+ OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
21
+ ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
22
+ OTHER DEALINGS IN THE SOFTWARE.
23
+
24
+ For more information, please refer to <http://unlicense.org/>
@@ -0,0 +1,15 @@
1
+ require "bundler/setup"
2
+ Bundler.require :default
3
+
4
+ def call
5
+ 2+2
6
+ x = 2
7
+ end
8
+
9
+ tracer = Pask.new
10
+ tracer.interested? { |e| e.call? or e.return? }
11
+ tracer.trace { call }
12
+ event1 = tracer.start
13
+ puts "[-] call() called. value of x is #{event1.binding.eval('x').inspect}."
14
+ event2 = tracer.resume
15
+ puts "[-] call() returned. value of x is now #{event2.binding.eval('x')}."
@@ -0,0 +1,11 @@
1
+ require "bundler"
2
+ Bundler.require :default
3
+ def call
4
+ end
5
+
6
+ puts "[-] counting 11 method calls. 10 ruby method calls, one C method call."
7
+ tracer = Pask.new
8
+ tracer.interested? { |e| e.call? or e.c_call? }
9
+ tracer.trace { 10.times { call } }
10
+ events = tracer.consume!
11
+ puts "[-] code traced. count done! a total of: #{events.size}"
@@ -0,0 +1,13 @@
1
+ require "bundler"
2
+ Bundler.require :default
3
+
4
+ def call
5
+ sleep(1)
6
+ end
7
+
8
+ puts "[-] going to sleep for a second, hold tight!"
9
+ tracer = Pask.new
10
+ tracer.interested? { |e| e.call? or e.return? }
11
+ tracer.trace { call }
12
+ events = tracer.consume!
13
+ puts "[-] #{events.at(0).signature} took #{events.at(1) - events.at(0)} second(s) to finish"
@@ -0,0 +1,114 @@
1
+ class Pask
2
+ require "thread"
3
+ require_relative "pask/event"
4
+
5
+ NotStartedError = Class.new(RuntimeError)
6
+ NotFinishedError = Class.new(RuntimeError)
7
+
8
+ VERSION = "0.1.0".freeze
9
+ RUN_STATE = "run".freeze
10
+ SLEEP_STATE = "sleep".freeze
11
+ TERMINATED_STATE = [nil, false].freeze
12
+ CATCH_ALL = Proc.new { true }
13
+
14
+ def self.version
15
+ VERSION
16
+ end
17
+
18
+ def initialize
19
+ @thread = nil
20
+ @queue = nil
21
+ @predicate = CATCH_ALL
22
+ @predicate_copy = nil
23
+ @block = nil
24
+ @block_copy = nil
25
+ end
26
+
27
+ def interested?(callable = nil, &block)
28
+ predicate = callable || block
29
+ unless predicate
30
+ raise ArgumentError, "no predicate given to #{__method__}()"
31
+ end
32
+ @predicate = predicate
33
+ end
34
+
35
+ def started?
36
+ if @queue and @thread
37
+ true
38
+ else
39
+ false
40
+ end
41
+ end
42
+
43
+ def running?
44
+ if @thread and @thread.status == RUN_STATE
45
+ true
46
+ else
47
+ false
48
+ end
49
+ end
50
+
51
+ def finished?
52
+ if @thread and TERMINATED_STATE.include?(@thread.status)
53
+ true
54
+ else
55
+ false
56
+ end
57
+ end
58
+
59
+ def sleeping?
60
+ if @thread and @thread.status == SLEEP_STATE
61
+ true
62
+ else
63
+ false
64
+ end
65
+ end
66
+
67
+ def resume
68
+ unless started?
69
+ raise NotStartedError, "tracer has not been started"
70
+ end
71
+ if sleeping?
72
+ @thread.wakeup
73
+ @queue.deq
74
+ end
75
+ end
76
+
77
+ def start
78
+ if started? and not finished?
79
+ raise NotFinishedError, "tracer has not finished"
80
+ end
81
+ @queue = Queue.new
82
+ @thread = Thread.new do
83
+ @predicate_copy, @block_copy = @predicate, @block
84
+ Thread.current.set_trace_func method(:on_event).to_proc
85
+ @block_copy.call
86
+ Thread.current.set_trace_func(nil)
87
+ @queue.enq nil
88
+ end
89
+ @queue.deq
90
+ end
91
+
92
+ def trace(&block)
93
+ @block = block
94
+ end
95
+
96
+ def consume!
97
+ events = [start]
98
+ current_event = nil
99
+ events.push(current_event) while current_event = resume
100
+ events
101
+ end
102
+
103
+ private
104
+ def on_event(name, file, lineno, method, binding, _)
105
+ event = Event.new name, file: file, method: method, lineno: lineno, binding: binding
106
+ if event.file != __FILE__ and @predicate_copy.call(event)
107
+ @queue.enq(event)
108
+ Thread.stop
109
+ end
110
+ rescue Exception => e
111
+ warn "TRACER CRASHED"
112
+ Thread.current.set_trace_func(nil)
113
+ end
114
+ end
@@ -0,0 +1,96 @@
1
+ class Pask::Event
2
+ CALL = "call"
3
+ C_CALL = "c-call"
4
+ RETURN = "return"
5
+ C_RETURN = "c-return"
6
+ KNOWN_NAMES = [CALL, C_CALL, RETURN, C_RETURN].map!(&:freeze)
7
+
8
+ def initialize(name, other)
9
+ @name = name
10
+ @other = other
11
+ @created_at = Time.now
12
+ @signature = nil
13
+ @to_f = nil
14
+ @emit_by = binding.eval "self"
15
+ end
16
+
17
+ def emit_by
18
+ # FIXME: messed up for C calls and returns. maybe return nil for them?
19
+ @emit_by
20
+ end
21
+
22
+ def to_f
23
+ @to_f ||= @created_at.to_f
24
+ end
25
+
26
+ def created_at
27
+ @created_at
28
+ end
29
+
30
+ def file
31
+ @other[:file]
32
+ end
33
+
34
+ def lineno
35
+ @other[:lineno]
36
+ end
37
+
38
+ def emit_by_method
39
+ @other[:method]
40
+ end
41
+
42
+ def binding
43
+ @other[:binding]
44
+ end
45
+
46
+ def __binding__
47
+ Kernel.binding
48
+ end
49
+
50
+ def any_call?
51
+ call? or c_call?
52
+ end
53
+
54
+ def return?
55
+ @name == RETURN
56
+ end
57
+
58
+ def c_return?
59
+ @name == C_RETURN
60
+ end
61
+
62
+ def call?
63
+ @name == CALL
64
+ end
65
+
66
+ def c_call?
67
+ @name == C_CALL
68
+ end
69
+
70
+ def any_return?
71
+ return? or c_return?
72
+ end
73
+
74
+ def origin?(mod)
75
+ if emit_by.respond_to?(:ancestors)
76
+ emit_by.ancestors.include?(mod)
77
+ else
78
+ mod === emit_by
79
+ end
80
+ end
81
+
82
+ def signature
83
+ @signature ||= if emit_by.kind_of?(Module)
84
+ [emit_by, @method].join(".")
85
+ else
86
+ [emit_by.class, @method].join("#")
87
+ end
88
+ end
89
+
90
+ def -(other)
91
+ unless other.respond_to?(:to_f)
92
+ raise TypeError, "cannot coerce argument to a float"
93
+ end
94
+ to_f - other.to_f
95
+ end
96
+ end
@@ -0,0 +1,15 @@
1
+ require File.expand_path('../lib/pask', __FILE__)
2
+ Gem::Specification.new do |gem|
3
+ gem.name = "pask"
4
+ gem.authors = ["Public Domain"]
5
+ gem.email = ["robert@flowof.info"]
6
+ gem.description = "concurrent tracer implemented on top of Thread#set_trace_func"
7
+ gem.summary = gem.description
8
+ gem.homepage = "https://github.com/robgleeson/pask"
9
+ gem.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
10
+ gem.files = `git ls-files`.split("\n")
11
+ gem.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
12
+ gem.require_paths = ["lib"]
13
+ gem.license = "Public Domain"
14
+ gem.version = Pask.version
15
+ end
@@ -0,0 +1,12 @@
1
+ require_relative "setup"
2
+ class TracerEventTest < MiniTest::Test
3
+ def test_return_value_of_binding
4
+ event = Pask::Event.new "call", {binding: TOPLEVEL_BINDING}
5
+ assert_equal TOPLEVEL_BINDING, event.binding
6
+ end
7
+
8
+ def test_return_value_of___binding__
9
+ event = Pask::Event.new "call", {binding: TOPLEVEL_BINDING}
10
+ assert_equal event, event.__binding__.eval("self")
11
+ end
12
+ end
@@ -0,0 +1,133 @@
1
+ require_relative 'setup'
2
+ class PaskTest < MiniTest::Test
3
+ def setup
4
+ @code = Code.new
5
+ end
6
+
7
+ class Code
8
+ def call
9
+ 2+2
10
+ x = 5
11
+ end
12
+ end
13
+
14
+ def test_start
15
+ tracer = Pask.new
16
+ tracer.interested? { |e| e.return? }
17
+ tracer.trace { @code.call }
18
+ assert_instance_of Pask::Event, tracer.start
19
+ end
20
+
21
+ def test_start_without_a_match
22
+ tracer = Pask.new
23
+ tracer.interested? { false }
24
+ tracer.trace { @code.call }
25
+ assert_equal nil, tracer.start
26
+ end
27
+
28
+ def test_start_on_started_tracer
29
+ tracer = Pask.new
30
+ tracer.trace { @code.call }
31
+ tracer.start
32
+ assert_raises(Pask::NotFinishedError) { tracer.start }
33
+ end
34
+
35
+ def test_consume!
36
+ tracer = Pask.new
37
+ tracer.interested? { |e| e.call? or e.return? }
38
+ tracer.trace { @code.call }
39
+ events = tracer.consume!
40
+ assert_equal 2, events.size
41
+ end
42
+
43
+ def test_sleeping_predicate_on_started_tracer
44
+ tracer = Pask.new
45
+ tracer.interested? { |event| event.return? }
46
+ tracer.trace { @code.call }
47
+ tracer.start
48
+ assert_equal true, tracer.sleeping?
49
+ end
50
+
51
+ def test_sleeping_predicate_on_finished_tracer
52
+ tracer = Pask.new
53
+ tracer.interested? { |event| event.return? }
54
+ tracer.trace { @code.call }
55
+ tracer.start
56
+ tracer.resume while tracer.resume
57
+ assert_equal false, tracer.sleeping?
58
+ end
59
+
60
+ def test_started_predicate_on_started_tracer
61
+ tracer = Pask.new
62
+ tracer.interested? { |event| event.return? }
63
+ tracer.trace { @code.call }
64
+ tracer.start
65
+ assert_equal true, tracer.started?
66
+ end
67
+
68
+ def test_started_predicate_on_unstarted_tracer
69
+ tracer = Pask.new
70
+ assert_equal false, tracer.started?
71
+ end
72
+
73
+ def test_resume_on_unstarted_tracer
74
+ tracer = Pask.new
75
+ assert_raises(Pask::NotStartedError) { tracer.resume }
76
+ end
77
+
78
+ def test_interested_predicate_without_block
79
+ tracer = Pask.new
80
+ assert_raises(ArgumentError) { tracer.interested? }
81
+ end
82
+
83
+ def test_interested_predicate_with_callable
84
+ obj = Proc.new {}
85
+ tracer = Pask.new
86
+ assert_equal obj, tracer.interested?(obj)
87
+ end
88
+
89
+ def test_finished_predicate_on_finished_tracer
90
+ tracer = Pask.new
91
+ tracer.trace { @code.call }
92
+ tracer.start
93
+ tracer.resume while tracer.resume
94
+ assert_equal true, tracer.finished?
95
+ end
96
+
97
+ def test_finished_predicate_on_unstarted_tracer
98
+ tracer = Pask.new
99
+ assert_equal false, tracer.finished?
100
+ end
101
+
102
+ def test_finished_predicate_on_started_tracer
103
+ tracer = Pask.new
104
+ tracer.interested? { |event| event.return? }
105
+ tracer.trace { @code.call }
106
+ tracer.start
107
+ assert_equal false, tracer.finished?
108
+ end
109
+
110
+ def test_running_predicate_on_dead_tracer
111
+ tracer = Pask.new
112
+ tracer.interested? { |event| event.return? }
113
+ tracer.trace { @code.call }
114
+ tracer.start
115
+ tracer.resume
116
+ assert_equal false, tracer.running?
117
+ end
118
+
119
+ def test_running_predicate_on_unstarted_tracer
120
+ tracer = Pask.new
121
+ assert_equal false, tracer.running?
122
+ end
123
+
124
+ def test_concurrent_trace_with_binding_and_local_variable
125
+ tracer = Pask.new
126
+ tracer.interested? { |e| e.call? or e.return? }
127
+ tracer.trace { @code.call }
128
+ event = tracer.start
129
+ assert_equal nil, event.binding.eval("x")
130
+ event = tracer.resume
131
+ assert_equal 5, event.binding.eval("x")
132
+ end
133
+ end
@@ -0,0 +1,2 @@
1
+ require "bundler/setup"
2
+ Bundler.require :default, :test
metadata ADDED
@@ -0,0 +1,66 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: pask
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Public Domain
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2014-02-14 00:00:00.000000000 Z
12
+ dependencies: []
13
+ description: concurrent tracer implemented on top of Thread#set_trace_func
14
+ email:
15
+ - robert@flowof.info
16
+ executables: []
17
+ extensions: []
18
+ extra_rdoc_files: []
19
+ files:
20
+ - ".gitignore"
21
+ - ".pryrc"
22
+ - ".ruby-version"
23
+ - ".travis.yml"
24
+ - ChangeLog.txt
25
+ - Gemfile
26
+ - README.md
27
+ - Rakefile
28
+ - UNLICENSE.txt
29
+ - examples/concurrent_binding_trace.rb
30
+ - examples/count_method_calls_trace.rb
31
+ - examples/time_trace.rb
32
+ - lib/pask.rb
33
+ - lib/pask/event.rb
34
+ - pask.gemspec
35
+ - test/pask_event_test.rb
36
+ - test/pask_test.rb
37
+ - test/setup.rb
38
+ homepage: https://github.com/robgleeson/pask
39
+ licenses:
40
+ - Public Domain
41
+ metadata: {}
42
+ post_install_message:
43
+ rdoc_options: []
44
+ require_paths:
45
+ - lib
46
+ required_ruby_version: !ruby/object:Gem::Requirement
47
+ requirements:
48
+ - - ">="
49
+ - !ruby/object:Gem::Version
50
+ version: '0'
51
+ required_rubygems_version: !ruby/object:Gem::Requirement
52
+ requirements:
53
+ - - ">="
54
+ - !ruby/object:Gem::Version
55
+ version: '0'
56
+ requirements: []
57
+ rubyforge_project:
58
+ rubygems_version: 2.2.2
59
+ signing_key:
60
+ specification_version: 4
61
+ summary: concurrent tracer implemented on top of Thread#set_trace_func
62
+ test_files:
63
+ - test/pask_event_test.rb
64
+ - test/pask_test.rb
65
+ - test/setup.rb
66
+ has_rdoc: