hometown 0.0.1 → 0.2.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.
- checksums.yaml +4 -4
- data/.travis.yml +8 -0
- data/README.md +27 -4
- data/hometown.gemspec +7 -6
- data/lib/hometown.rb +16 -43
- data/lib/hometown/creation_tracer.rb +67 -0
- data/lib/hometown/disposal_tracer.rb +47 -0
- data/lib/hometown/version.rb +1 -1
- data/test/hometown/trace_test.rb +1 -1
- data/test/hometown_test.rb +2 -2
- data/test/test_helper.rb +7 -0
- metadata +21 -5
- data/lib/hometown/hooks.rb +0 -17
- data/lib/hometown/watch.rb +0 -25
- data/lib/hometown/watch_for_disposal.rb +0 -30
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: ad8a4766d74b2ef331b68288522bdcd94bb3bd2b
|
4
|
+
data.tar.gz: 26cd517beece08a45ce5047b0d601812133b645a
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 6a6666c297ec7089fc96fee85f0deed49952dfa9b58d0604daf4aeffa3626b6ef11240b609df089794a210c5eb46ede866dd1c9602c3e3e45e297b02cd19b9dc
|
7
|
+
data.tar.gz: 8253b5cbb7f6a95423274dfad3621e2fdf9659a6d7c4af8df89ffabcfabfeaa8ae590d3cae84e18730c4a535d54517ed5a0350b56289654c3051c9db414342ef
|
data/.travis.yml
ADDED
data/README.md
CHANGED
@@ -1,9 +1,16 @@
|
|
1
1
|
# Hometown
|
2
|
+
[](http://badge.fury.io/rb/hometown)
|
3
|
+
[](https://travis-ci.org/jasonrclark/hometown)
|
4
|
+
[](https://codeclimate.com/github/jasonrclark/hometown)
|
5
|
+
[](https://coveralls.io/r/jasonrclark/hometown)
|
2
6
|
|
3
7
|
Track object creation to stamp out pesky leaks.
|
4
8
|
|
5
9
|
## Requirements
|
6
|
-
|
10
|
+
Tests are run against MRI 1.9.3 through 2.1.2, JRuby 1.7 (latest) and head, and
|
11
|
+
Rubinius 2.x (latest).
|
12
|
+
|
13
|
+
Ruby 1.8.7 and REE are not supported. Sorry retro-Ruby fans!
|
7
14
|
|
8
15
|
## Installation
|
9
16
|
|
@@ -11,7 +18,13 @@ Currently I'm testing against Ruby 2.1, but will be extending that in the future
|
|
11
18
|
|
12
19
|
## Usage
|
13
20
|
|
14
|
-
|
21
|
+
### Object Creation
|
22
|
+
Hometown's primary use is finding where objects were instantiated. In
|
23
|
+
sufficiently complicated applications, this can be a real help debugging issues
|
24
|
+
like testing side-effects (i.e. where did that thread get started from?)
|
25
|
+
|
26
|
+
To find where an object was created, `Hometown.watch` its class, and then ask
|
27
|
+
`Hometown.for` on an instance of that class to see where it started out.
|
15
28
|
|
16
29
|
```
|
17
30
|
# examples/example.rb
|
@@ -32,7 +45,17 @@ $ ruby examples/example.rb
|
|
32
45
|
```
|
33
46
|
|
34
47
|
|
35
|
-
|
48
|
+
### Resource Disposal
|
49
|
+
Though not hugely common in the Ruby world, some libraries (such as [swt]
|
50
|
+
(https://github.com/danlucraft/swt)) require you to explicitly dispose of
|
51
|
+
objects you create. Most often this happens when it's an interface library to
|
52
|
+
some other system that holds OS resources until you release them back. Leaking
|
53
|
+
is a bad idea.
|
54
|
+
|
55
|
+
Hometown can help track down these leaks. To watch a class of objects to ensure
|
56
|
+
created instances are disposed, call `Hometown.watch_for_disposal` on the
|
57
|
+
class. `Hometown.undisposed` returns you objects indicating--with stack traces
|
58
|
+
--all the locations where an object was created but not released.
|
36
59
|
|
37
60
|
```
|
38
61
|
# dispose.rb
|
@@ -69,7 +92,7 @@ Properly disposed!
|
|
69
92
|
|
70
93
|
## Contributing
|
71
94
|
|
72
|
-
1. Fork it ( https://github.com/
|
95
|
+
1. Fork it ( https://github.com/jasonrclark/hometown/fork )
|
73
96
|
2. Create your feature branch (`git checkout -b my-new-feature`)
|
74
97
|
3. Commit your changes (`git commit -am 'Add some feature'`)
|
75
98
|
4. Push to the branch (`git push origin my-new-feature`)
|
data/hometown.gemspec
CHANGED
@@ -18,11 +18,12 @@ Gem::Specification.new do |spec|
|
|
18
18
|
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
19
19
|
spec.require_paths = ["lib"]
|
20
20
|
|
21
|
-
spec.add_development_dependency "bundler",
|
22
|
-
spec.add_development_dependency "
|
23
|
-
spec.add_development_dependency "
|
24
|
-
spec.add_development_dependency "
|
25
|
-
spec.add_development_dependency "pry
|
26
|
-
spec.add_development_dependency "
|
21
|
+
spec.add_development_dependency "bundler", "~> 1.6"
|
22
|
+
spec.add_development_dependency "coveralls", "~> 0.7"
|
23
|
+
spec.add_development_dependency "rake", "~> 10.2"
|
24
|
+
spec.add_development_dependency "minitest", "~> 5.3"
|
25
|
+
spec.add_development_dependency "pry", "~> 0.9"
|
26
|
+
spec.add_development_dependency "pry-nav", "~> 0.2"
|
27
|
+
spec.add_development_dependency "guard", "~> 2.6"
|
27
28
|
spec.add_development_dependency "guard-minitest", "~> 1.3"
|
28
29
|
end
|
data/lib/hometown.rb
CHANGED
@@ -1,60 +1,33 @@
|
|
1
|
-
require "hometown/
|
1
|
+
require "hometown/creation_tracer"
|
2
|
+
require "hometown/disposal_tracer"
|
2
3
|
require "hometown/trace"
|
3
4
|
require "hometown/version"
|
4
|
-
require "hometown/watch"
|
5
|
-
require "hometown/watch_for_disposal"
|
6
5
|
|
7
6
|
module Hometown
|
8
|
-
|
7
|
+
@creation_tracer = Hometown::CreationTracer.new
|
8
|
+
@disposal_tracer = Hometown::DisposalTracer.new
|
9
9
|
|
10
|
-
|
11
|
-
|
12
|
-
@dispose_patches = {}
|
13
|
-
|
14
|
-
def self.watch(clazz)
|
15
|
-
return if @watch_patches.include?(clazz)
|
16
|
-
|
17
|
-
@watch_patches[clazz] = true
|
18
|
-
Watch.patch(clazz)
|
19
|
-
end
|
20
|
-
|
21
|
-
def self.watch_for_disposal(clazz, disposal_method)
|
22
|
-
return if @dispose_patches.include?(clazz)
|
23
|
-
|
24
|
-
watch(clazz)
|
25
|
-
|
26
|
-
@dispose_patches[clazz] = true
|
27
|
-
WatchForDisposal.patch(clazz, disposal_method)
|
10
|
+
def self.creation_tracer
|
11
|
+
@creation_tracer
|
28
12
|
end
|
29
13
|
|
30
|
-
def self.
|
31
|
-
@
|
14
|
+
def self.disposal_tracer
|
15
|
+
@disposal_tracer
|
32
16
|
end
|
33
17
|
|
34
|
-
def self.
|
35
|
-
|
36
|
-
end
|
37
|
-
|
38
|
-
def self.undisposed()
|
39
|
-
@undisposed
|
18
|
+
def self.watch(clazz)
|
19
|
+
@creation_tracer.patch(clazz)
|
40
20
|
end
|
41
21
|
|
42
|
-
def self.
|
43
|
-
|
22
|
+
def self.watch_for_disposal(clazz, disposal_method)
|
23
|
+
@disposal_tracer.patch(clazz, disposal_method)
|
44
24
|
end
|
45
25
|
|
46
|
-
def self.
|
47
|
-
|
48
|
-
|
49
|
-
@undisposed[trace] ||= 0
|
50
|
-
@undisposed[trace] += 1
|
26
|
+
def self.for(instance)
|
27
|
+
@creation_tracer.find_trace_for(instance)
|
51
28
|
end
|
52
29
|
|
53
|
-
def self.
|
54
|
-
|
55
|
-
|
56
|
-
if @undisposed[trace]
|
57
|
-
@undisposed[trace] -= 1
|
58
|
-
end
|
30
|
+
def self.undisposed
|
31
|
+
@disposal_tracer.undisposed()
|
59
32
|
end
|
60
33
|
end
|
@@ -0,0 +1,67 @@
|
|
1
|
+
module Hometown
|
2
|
+
class CreationTracer
|
3
|
+
def initialize
|
4
|
+
@tracing_classes = {}
|
5
|
+
end
|
6
|
+
|
7
|
+
def patch(clazz, other_instance_hook=nil)
|
8
|
+
if !patched?(clazz)
|
9
|
+
remember_patched(clazz)
|
10
|
+
on_creation_add_trace_for_instance(clazz)
|
11
|
+
install_traced_new(clazz)
|
12
|
+
end
|
13
|
+
|
14
|
+
# Critical that we only add other instance hooks after our primary
|
15
|
+
# creation hook is registered above!
|
16
|
+
update_on_instance_created(clazz, other_instance_hook)
|
17
|
+
end
|
18
|
+
|
19
|
+
def patched?(clazz)
|
20
|
+
@tracing_classes.include?(clazz)
|
21
|
+
end
|
22
|
+
|
23
|
+
def remember_patched(clazz)
|
24
|
+
@tracing_classes[clazz] = true
|
25
|
+
end
|
26
|
+
|
27
|
+
def on_creation_add_trace_for_instance(clazz)
|
28
|
+
update_on_instance_created(clazz, method(:add_trace_for))
|
29
|
+
end
|
30
|
+
|
31
|
+
def install_traced_new(clazz)
|
32
|
+
clazz.instance_eval do
|
33
|
+
class << self
|
34
|
+
def new_traced(*args, &blk)
|
35
|
+
instance = new_untraced(*args, &blk)
|
36
|
+
@instance_hooks.each { |hook| hook.call(instance) }
|
37
|
+
instance
|
38
|
+
end
|
39
|
+
|
40
|
+
alias_method :new_untraced, :new
|
41
|
+
alias_method :new, :new_traced
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
HOMETOWN_TRACE_ON_INSTANCE = :@__hometown_creation_backtrace
|
47
|
+
|
48
|
+
def add_trace_for(instance)
|
49
|
+
trace = Hometown::Trace.new(instance.class, caller[4..-1])
|
50
|
+
instance.instance_variable_set(HOMETOWN_TRACE_ON_INSTANCE, trace)
|
51
|
+
end
|
52
|
+
|
53
|
+
def find_trace_for(instance)
|
54
|
+
instance.instance_variable_get(HOMETOWN_TRACE_ON_INSTANCE)
|
55
|
+
end
|
56
|
+
|
57
|
+
# This hook allows other tracing in Hometown to get a whack at an object
|
58
|
+
# after it's been created without forcing them to patch new themselves
|
59
|
+
def update_on_instance_created(clazz, on_instance_created)
|
60
|
+
return unless on_instance_created
|
61
|
+
clazz.instance_eval do
|
62
|
+
@instance_hooks ||= []
|
63
|
+
@instance_hooks << on_instance_created
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
@@ -0,0 +1,47 @@
|
|
1
|
+
module Hometown
|
2
|
+
class DisposalTracer
|
3
|
+
attr_reader :undisposed
|
4
|
+
|
5
|
+
def initialize
|
6
|
+
@undisposed = Hash.new(0)
|
7
|
+
@tracing_classes = {}
|
8
|
+
end
|
9
|
+
|
10
|
+
def patch(clazz, disposal_method)
|
11
|
+
return if @tracing_classes.include?(clazz)
|
12
|
+
@tracing_classes[clazz] = true
|
13
|
+
|
14
|
+
trace_creation(clazz)
|
15
|
+
patch_disposal_method(clazz, disposal_method)
|
16
|
+
end
|
17
|
+
|
18
|
+
def trace_creation(clazz)
|
19
|
+
Hometown.creation_tracer.patch(clazz, method(:mark_for_disposal))
|
20
|
+
end
|
21
|
+
|
22
|
+
def patch_disposal_method(clazz, disposal_method)
|
23
|
+
traced = "#{disposal_method}_traced"
|
24
|
+
untraced = "#{disposal_method}_untraced"
|
25
|
+
|
26
|
+
clazz.class_eval do
|
27
|
+
define_method(traced) do |*args, &blk|
|
28
|
+
Hometown.disposal_tracer.notice_disposed(self)
|
29
|
+
self.send(untraced, *args, &blk)
|
30
|
+
end
|
31
|
+
|
32
|
+
alias_method untraced, disposal_method
|
33
|
+
alias_method disposal_method, traced
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
def mark_for_disposal(instance)
|
38
|
+
trace = Hometown.for(instance)
|
39
|
+
@undisposed[trace] += 1
|
40
|
+
end
|
41
|
+
|
42
|
+
def notice_disposed(instance)
|
43
|
+
trace = Hometown.for(instance)
|
44
|
+
@undisposed[trace] -= 1 if @undisposed[trace]
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
data/lib/hometown/version.rb
CHANGED
data/test/hometown/trace_test.rb
CHANGED
data/test/hometown_test.rb
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
require
|
1
|
+
require File.expand_path(File.join(File.dirname(__FILE__), "test_helper"))
|
2
2
|
require 'hometown'
|
3
3
|
|
4
4
|
class HometownTest < Minitest::Test
|
@@ -80,7 +80,7 @@ class HometownTest < Minitest::Test
|
|
80
80
|
|
81
81
|
result = Hometown.undisposed
|
82
82
|
trace = Hometown.for(traced_object)
|
83
|
-
|
83
|
+
assert_equal 0, result[trace]
|
84
84
|
end
|
85
85
|
|
86
86
|
def test_marks_for_disposal
|
data/test/test_helper.rb
ADDED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: hometown
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0
|
4
|
+
version: 0.2.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Jason R. Clark
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2014-06-
|
11
|
+
date: 2014-06-18 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -24,6 +24,20 @@ dependencies:
|
|
24
24
|
- - "~>"
|
25
25
|
- !ruby/object:Gem::Version
|
26
26
|
version: '1.6'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: coveralls
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '0.7'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '0.7'
|
27
41
|
- !ruby/object:Gem::Dependency
|
28
42
|
name: rake
|
29
43
|
requirement: !ruby/object:Gem::Requirement
|
@@ -116,6 +130,7 @@ extensions: []
|
|
116
130
|
extra_rdoc_files: []
|
117
131
|
files:
|
118
132
|
- ".gitignore"
|
133
|
+
- ".travis.yml"
|
119
134
|
- Gemfile
|
120
135
|
- Guardfile
|
121
136
|
- LICENSE.txt
|
@@ -125,13 +140,13 @@ files:
|
|
125
140
|
- examples/example.rb
|
126
141
|
- hometown.gemspec
|
127
142
|
- lib/hometown.rb
|
128
|
-
- lib/hometown/
|
143
|
+
- lib/hometown/creation_tracer.rb
|
144
|
+
- lib/hometown/disposal_tracer.rb
|
129
145
|
- lib/hometown/trace.rb
|
130
146
|
- lib/hometown/version.rb
|
131
|
-
- lib/hometown/watch.rb
|
132
|
-
- lib/hometown/watch_for_disposal.rb
|
133
147
|
- test/hometown/trace_test.rb
|
134
148
|
- test/hometown_test.rb
|
149
|
+
- test/test_helper.rb
|
135
150
|
homepage: http://github.com/jasonrclark/hometown
|
136
151
|
licenses:
|
137
152
|
- MIT
|
@@ -159,4 +174,5 @@ summary: Track object creation
|
|
159
174
|
test_files:
|
160
175
|
- test/hometown/trace_test.rb
|
161
176
|
- test/hometown_test.rb
|
177
|
+
- test/test_helper.rb
|
162
178
|
has_rdoc:
|
data/lib/hometown/hooks.rb
DELETED
data/lib/hometown/watch.rb
DELETED
@@ -1,25 +0,0 @@
|
|
1
|
-
module Hometown
|
2
|
-
module Watch
|
3
|
-
def self.patch(clazz)
|
4
|
-
clazz.instance_eval do
|
5
|
-
@hooks = Hooks.new
|
6
|
-
|
7
|
-
class << self
|
8
|
-
def new_traced(*args, &blk)
|
9
|
-
trace = Hometown.create_trace(self, caller)
|
10
|
-
|
11
|
-
instance = new_original(*args, &blk)
|
12
|
-
instance.instance_variable_set(HOMETOWN_TRACE_ON_INSTANCE, trace)
|
13
|
-
instance.class.instance_variable_get(:@hooks).run_on(instance)
|
14
|
-
|
15
|
-
instance
|
16
|
-
end
|
17
|
-
|
18
|
-
alias_method :new_original, :new
|
19
|
-
alias_method :new, :new_traced
|
20
|
-
end
|
21
|
-
|
22
|
-
end
|
23
|
-
end
|
24
|
-
end
|
25
|
-
end
|
@@ -1,30 +0,0 @@
|
|
1
|
-
module Hometown
|
2
|
-
module WatchForDisposal
|
3
|
-
def self.patch(clazz, disposal_method)
|
4
|
-
add_disposal_hooks(clazz)
|
5
|
-
patch_disposal_method(clazz, disposal_method)
|
6
|
-
end
|
7
|
-
|
8
|
-
def self.add_disposal_hooks(clazz)
|
9
|
-
hooks = clazz.instance_variable_get(:@hooks)
|
10
|
-
hooks << Proc.new do |instance|
|
11
|
-
Hometown.mark_for_disposal(instance)
|
12
|
-
end
|
13
|
-
end
|
14
|
-
|
15
|
-
def self.patch_disposal_method(clazz, disposal_method)
|
16
|
-
traced = "#{disposal_method}_traced"
|
17
|
-
original = "#{disposal_method}_original"
|
18
|
-
|
19
|
-
clazz.class_eval do
|
20
|
-
define_method(traced) do |*args, &blk|
|
21
|
-
Hometown.mark_disposed(self)
|
22
|
-
self.send(original, *args, &blk)
|
23
|
-
end
|
24
|
-
|
25
|
-
alias_method original, disposal_method
|
26
|
-
alias_method disposal_method, traced
|
27
|
-
end
|
28
|
-
end
|
29
|
-
end
|
30
|
-
end
|