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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 138e3de444e2c7c0364c252e37a411f6139257d3
4
- data.tar.gz: 82ae1b18916e8845d8a625501194b7eeee20d95c
3
+ metadata.gz: ad8a4766d74b2ef331b68288522bdcd94bb3bd2b
4
+ data.tar.gz: 26cd517beece08a45ce5047b0d601812133b645a
5
5
  SHA512:
6
- metadata.gz: 75a8cb0ac1dcbb0bfb720845fc8cbe9c6de45dc562651229a0b4a84a595af900bec66f7e045fa66c4ca221616202549f56639131c093af23f0849b5565eaa130
7
- data.tar.gz: ae6b198008255b5a3a88bf5035368249f7eaa1b2c2c929b03c9dbd305b00bdf5a4461f801c4e1565950e565ef049f1771d1f3e24cfa0136e4e508d1f633b43a0
6
+ metadata.gz: 6a6666c297ec7089fc96fee85f0deed49952dfa9b58d0604daf4aeffa3626b6ef11240b609df089794a210c5eb46ede866dd1c9602c3e3e45e297b02cd19b9dc
7
+ data.tar.gz: 8253b5cbb7f6a95423274dfad3621e2fdf9659a6d7c4af8df89ffabcfabfeaa8ae590d3cae84e18730c4a535d54517ed5a0350b56289654c3051c9db414342ef
data/.travis.yml ADDED
@@ -0,0 +1,8 @@
1
+ language: ruby
2
+ rvm:
3
+ - 2.1.2
4
+ - 2.0.0
5
+ - 1.9.3
6
+ - jruby-19mode
7
+ - jruby-head
8
+ - rbx-2.2.7
data/README.md CHANGED
@@ -1,9 +1,16 @@
1
1
  # Hometown
2
+ [![Gem Version](https://badge.fury.io/rb/hometown.png)](http://badge.fury.io/rb/hometown)
3
+ [![Build Status](https://api.travis-ci.org/jasonrclark/hometown.png)](https://travis-ci.org/jasonrclark/hometown)
4
+ [![Code Climate](https://codeclimate.com/github/jasonrclark/hometown.png)](https://codeclimate.com/github/jasonrclark/hometown)
5
+ [![Coverage Status](https://coveralls.io/repos/jasonrclark/hometown/badge.png?branch=master)](https://coveralls.io/r/jasonrclark/hometown)
2
6
 
3
7
  Track object creation to stamp out pesky leaks.
4
8
 
5
9
  ## Requirements
6
- Currently I'm testing against Ruby 2.1, but will be extending that in the future. I expect to allow back to 1.9.x, JRuby and Rubinius.
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
- Find where an object was created:
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
- Track disposal of an object:
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/[my-github-username]/hometown/fork )
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", "~> 1.6"
22
- spec.add_development_dependency "rake", "~> 10.2"
23
- spec.add_development_dependency "minitest","~> 5.3"
24
- spec.add_development_dependency "pry", "~> 0.9"
25
- spec.add_development_dependency "pry-nav", "~> 0.2"
26
- spec.add_development_dependency "guard", "~> 2.6"
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/hooks"
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
- HOMETOWN_TRACE_ON_INSTANCE = :@__hometown_creation_backtrace
7
+ @creation_tracer = Hometown::CreationTracer.new
8
+ @disposal_tracer = Hometown::DisposalTracer.new
9
9
 
10
- @undisposed = {}
11
- @watch_patches = {}
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.already_patched?(clazz)
31
- @patched.include?(clazz)
14
+ def self.disposal_tracer
15
+ @disposal_tracer
32
16
  end
33
17
 
34
- def self.for(instance)
35
- instance.instance_variable_get(HOMETOWN_TRACE_ON_INSTANCE)
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.create_trace(clazz, backtrace)
43
- Trace.new(clazz, backtrace)
22
+ def self.watch_for_disposal(clazz, disposal_method)
23
+ @disposal_tracer.patch(clazz, disposal_method)
44
24
  end
45
25
 
46
- def self.mark_for_disposal(instance)
47
- trace = Hometown.for(instance)
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.mark_disposed(instance)
54
- trace = Hometown.for(instance)
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
@@ -1,3 +1,3 @@
1
1
  module Hometown
2
- VERSION = "0.0.1"
2
+ VERSION = "0.2.0"
3
3
  end
@@ -1,4 +1,4 @@
1
- require 'minitest/autorun'
1
+ require File.expand_path(File.join(File.dirname(__FILE__), "..", "test_helper"))
2
2
  require 'hometown/trace'
3
3
 
4
4
  module Hometown
@@ -1,4 +1,4 @@
1
- require 'minitest/autorun'
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
- assert_nil result[trace]
83
+ assert_equal 0, result[trace]
84
84
  end
85
85
 
86
86
  def test_marks_for_disposal
@@ -0,0 +1,7 @@
1
+ if ENV["CI"]
2
+ require 'coveralls'
3
+ Coveralls.wear!
4
+ end
5
+
6
+ gem 'minitest'
7
+ require 'minitest/autorun'
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.1
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-03 00:00:00.000000000 Z
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/hooks.rb
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:
@@ -1,17 +0,0 @@
1
- module Hometown
2
- class Hooks
3
- def initialize
4
- @hooks = []
5
- end
6
-
7
- def <<(hook)
8
- @hooks << hook
9
- end
10
-
11
- def run_on(instance)
12
- @hooks.each do |hook|
13
- hook.call(instance)
14
- end
15
- end
16
- end
17
- end
@@ -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