hometown 0.0.1 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
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