focal_point 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
data/.document ADDED
@@ -0,0 +1,5 @@
1
+ README.rdoc
2
+ lib/**/*.rb
3
+ bin/*
4
+ features/**/*.feature
5
+ LICENSE
data/.gitignore ADDED
@@ -0,0 +1,21 @@
1
+ ## MAC OS
2
+ .DS_Store
3
+
4
+ ## TEXTMATE
5
+ *.tmproj
6
+ tmtags
7
+
8
+ ## EMACS
9
+ *~
10
+ \#*
11
+ .\#*
12
+
13
+ ## VIM
14
+ *.swp
15
+
16
+ ## PROJECT::GENERAL
17
+ coverage
18
+ rdoc
19
+ pkg
20
+
21
+ ## PROJECT::SPECIFIC
data/LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2009 levicook@gmail.com
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.rdoc ADDED
@@ -0,0 +1,18 @@
1
+ = focal_point
2
+
3
+
4
+
5
+
6
+ == Note on Patches/Pull Requests
7
+
8
+ * Fork the project.
9
+ * Make your feature addition or bug fix.
10
+ * Add tests for it. This is important so I don't break it in a
11
+ future version unintentionally.
12
+ * Commit, do not mess with rakefile, version, or history.
13
+ (if you want to have your own version, that is fine but bump version in a commit by itself I can ignore when I pull)
14
+ * Send me a pull request. Bonus points for topic branches.
15
+
16
+ == Copyright
17
+
18
+ Copyright (c) 2010 levicook@gmail.com. See LICENSE for details.
data/Rakefile ADDED
@@ -0,0 +1,54 @@
1
+ require 'rubygems'
2
+ require 'rake'
3
+
4
+ begin
5
+ require 'jeweler'
6
+ Jeweler::Tasks.new do |gem|
7
+ gem.name = "focal_point"
8
+ gem.summary = %Q{Focused, unobtrusive execution timing reports for ruby.}
9
+ gem.description = %Q{instruments methods with hitimes}
10
+ gem.email = "levicook@gmail.com"
11
+ gem.homepage = "http://github.com/levicook/focal_point"
12
+ gem.authors = ["levicook@gmail.com"]
13
+ gem.add_dependency "hitimes", ">= 1.0.0"
14
+ gem.add_development_dependency "riot", ">= 0"
15
+ gem.add_development_dependency "yard", ">= 0"
16
+ # gem is a Gem::Specification... see http://www.rubygems.org/read/chapter/20 for additional settings
17
+ end
18
+ Jeweler::GemcutterTasks.new
19
+ rescue LoadError
20
+ puts "Jeweler (or a dependency) not available. Install it with: gem install jeweler"
21
+ end
22
+
23
+ require 'rake/testtask'
24
+ Rake::TestTask.new(:test) do |test|
25
+ test.libs << 'lib' << 'test'
26
+ test.pattern = 'test/**/*_test.rb'
27
+ test.verbose = true
28
+ end
29
+
30
+ begin
31
+ require 'rcov/rcovtask'
32
+ Rcov::RcovTask.new do |test|
33
+ test.libs << 'test'
34
+ test.pattern = 'test/**/*_test.rb'
35
+ test.verbose = true
36
+ end
37
+ rescue LoadError
38
+ task :rcov do
39
+ abort "RCov is not available. In order to run rcov, you must: sudo gem install spicycode-rcov"
40
+ end
41
+ end
42
+
43
+ task :test => :check_dependencies
44
+
45
+ task :default => :test
46
+
47
+ begin
48
+ require 'yard'
49
+ YARD::Rake::YardocTask.new
50
+ rescue LoadError
51
+ task :yardoc do
52
+ abort "YARD is not available. In order to run yardoc, you must: sudo gem install yard"
53
+ end
54
+ end
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 0.0.1
@@ -0,0 +1,91 @@
1
+ require 'rubygems'
2
+ require 'hitimes'
3
+ require 'pp'
4
+
5
+ require File.expand_path(File.dirname(__FILE__)) + "/multiton"
6
+
7
+ class FocalPoint
8
+ include Multiton
9
+
10
+ attr_accessor :timer
11
+
12
+ def self.[] target
13
+ new(target)
14
+ end
15
+
16
+ def initialize(target)
17
+ @target = String(target)
18
+ @timer = Hitimes::TimedMetric.new(@target)
19
+
20
+ if @target =~ /^([^#.]+)(#|\.)(.*)$/
21
+ @method, @scope = case $2
22
+ when '#' ## we're instrumenting an instance method
23
+ [$3, eval($1)]
24
+ when '.' ## we're instrumenting a module method
25
+ [$3, (class << eval($1); self; end)]
26
+ end
27
+ else
28
+ $stdout.puts "FocalPoint::Error: Not sure how to instrument #{@target}"
29
+ end
30
+ @scope.module_eval(source)
31
+ end
32
+
33
+ def source
34
+ <<-CODE
35
+ alias :real_#{@method} :#{@method}
36
+ def #{@method}(*args)
37
+ FocalPoint[#{@target.inspect}].timer.measure do
38
+ real_#{@method}(*args)
39
+ end
40
+ end
41
+ CODE
42
+ end
43
+
44
+
45
+ def self.print_timers
46
+ timers = []
47
+ ObjectSpace.each_object(FocalPoint) { |fp| timers << fp.timer }
48
+ timers.compact.sort_by(&:sum).each do |timer|
49
+ puts '-'*80
50
+ pp timer.to_hash
51
+ puts
52
+ end
53
+ end
54
+
55
+ end
56
+
57
+ # call-seq:
58
+ #
59
+ # focal_point("ClassName#instance_method", ...)
60
+ # focal_point("ClassName.class_method", ...)
61
+ #
62
+ #
63
+ def focal_point(*targets)
64
+ targets.each { |t| FocalPoint[t] }
65
+ end
66
+ alias :focal_points :focal_point
67
+
68
+
69
+ =begin
70
+
71
+ at_exit do
72
+ FocalPoint.print_timers
73
+ end
74
+
75
+ if $0 == __FILE__
76
+ module Quux
77
+ class Foo
78
+ def self.bar
79
+ 5.times { sleep(1) }
80
+ end
81
+ def bar
82
+ 5.times { sleep(1) }
83
+ end
84
+ end
85
+ end
86
+ focal_points('Quux::Foo.bar', 'Quux::Foo#bar')
87
+ Quux::Foo.bar
88
+ Quux::Foo.new.bar
89
+ end
90
+
91
+ =end
data/lib/multiton.rb ADDED
@@ -0,0 +1,272 @@
1
+ # = Multiton
2
+ #
3
+ # == Synopsis
4
+ #
5
+ # Multiton design pattern ensures only one object is allocated for a given state.
6
+ #
7
+ # The 'multiton' pattern is similar to a singleton, but instead of only one
8
+ # instance, there are several similar instances. It is useful when you want to
9
+ # avoid constructing objects many times because of some huge expense (connecting
10
+ # to a database for example), require a set of similar but not identical
11
+ # objects, and cannot easily control how many times a contructor may be called.
12
+ #
13
+ # class SomeMultitonClass
14
+ # include Multiton
15
+ # attr :arg
16
+ # def initialize(arg)
17
+ # @arg = arg
18
+ # end
19
+ # end
20
+ #
21
+ # a = SomeMultitonClass.new(4)
22
+ # b = SomeMultitonClass.new(4) # a and b are same object
23
+ # c = SomeMultitonClass.new(2) # c is a different object
24
+ #
25
+ # == Previous Behavior
26
+ #
27
+ # In previous versions of Multiton the #new method was made
28
+ # private and #instance had to be used in its stay --just like Singleton.
29
+ # But this is less desirable for Multiton since Multitions can
30
+ # have multiple instances, not just one.
31
+ #
32
+ # So instead Multiton now defines #create as a private alias of
33
+ # the original #new method (just in case it is needed) and then
34
+ # defines #new to handle the multiton; #instance is provided
35
+ # as an alias for it.
36
+ #
37
+ #--
38
+ # So if you must have the old behavior, all you need do is re-alias
39
+ # #new to #create and privatize it.
40
+ #
41
+ # class SomeMultitonClass
42
+ # include Multiton
43
+ # alias_method :new, :create
44
+ # private :new
45
+ # ...
46
+ # end
47
+ #
48
+ # Then only #instance will be available for creating the Multiton.
49
+ #++
50
+ #
51
+ # == How It Works
52
+ #
53
+ # A pool of objects is searched for a previously cached object,
54
+ # if one is not found we construct one and cache it in the pool
55
+ # based on class and the args given to the contructor.
56
+ #
57
+ # A limitation of this approach is that it is impossible to
58
+ # detect if different blocks were given to a contructor (if it takes a
59
+ # block). So it is the constructor arguments _only_ which determine
60
+ # the uniqueness of an object. To workaround this, define the _class_
61
+ # method ::multiton_id.
62
+ #
63
+ # def Klass.multiton_id(*args, &block)
64
+ # # ...
65
+ # end
66
+ #
67
+ # Which should return a hash key used to identify the object being
68
+ # constructed as (not) unique.
69
+ #
70
+ # == Authors
71
+ #
72
+ # * Christoph Rippel
73
+ # * Thomas Sawyer
74
+ #
75
+ # = Copying
76
+ #
77
+ # Copyright (c) 2007 Christoph Rippel, Thomas Sawyer
78
+ #
79
+ # Ruby License
80
+ #
81
+ # This module is free software. You may use, modify, and/or redistribute this
82
+ # software under the same terms as Ruby.
83
+ #
84
+ # This program is distributed in the hope that it will be useful, but WITHOUT
85
+ # ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
86
+ # FOR A PARTICULAR PURPOSE.
87
+
88
+ require 'thread'
89
+
90
+ # = Multiton
91
+ #
92
+ # Multiton design pattern ensures only one object is allocated for a given state.
93
+ #
94
+ # The 'multiton' pattern is similar to a singleton, but instead of only one
95
+ # instance, there are several similar instances. It is useful when you want to
96
+ # avoid constructing objects many times because of some huge expense (connecting
97
+ # to a database for example), require a set of similar but not identical
98
+ # objects, and cannot easily control how many times a contructor may be called.
99
+ #
100
+ # class SomeMultitonClass
101
+ # include Multiton
102
+ # attr :arg
103
+ # def initialize(arg)
104
+ # @arg = arg
105
+ # end
106
+ # end
107
+ #
108
+ # a = SomeMultitonClass.new(4)
109
+ # b = SomeMultitonClass.new(4) # a and b are same object
110
+ # c = SomeMultitonClass.new(2) # c is a different object
111
+ #
112
+ # == How It Works
113
+ #
114
+ # A pool of objects is searched for a previously cached object,
115
+ # if one is not found we construct one and cache it in the pool
116
+ # based on class and the args given to the contructor.
117
+ #
118
+ # A limitation of this approach is that it is impossible to
119
+ # detect if different blocks were given to a contructor (if it takes a
120
+ # block). So it is the constructor arguments _only_ which determine
121
+ # the uniqueness of an object. To workaround this, define the _class_
122
+ # method ::multiton_id.
123
+ #
124
+ # def Klass.multiton_id(*args, &block)
125
+ # # ...
126
+ # end
127
+ #
128
+ # Which should return a hash key used to identify the object being
129
+ # constructed as (not) unique.
130
+
131
+ module Multiton
132
+
133
+ # disable build-in copying methods
134
+
135
+ def clone
136
+ raise TypeError, "can't clone Multiton #{self}"
137
+ #self
138
+ end
139
+
140
+ def dup
141
+ raise TypeError, "can't dup Multiton #{self}"
142
+ #self
143
+ end
144
+
145
+ # default marshalling strategy
146
+
147
+ protected
148
+
149
+ def _dump(depth=-1)
150
+ Marshal.dump(@multiton_initializer)
151
+ end
152
+
153
+ # Mutex to safely store multiton instances.
154
+
155
+ class InstanceMutex < Hash #:nodoc:
156
+ def initialize
157
+ @global = Mutex.new
158
+ end
159
+
160
+ def initialized(arg)
161
+ store(arg, DummyMutex)
162
+ end
163
+
164
+ def (DummyMutex = Object.new).synchronize
165
+ yield
166
+ end
167
+
168
+ def default(arg)
169
+ @global.synchronize{ fetch(arg){ store(arg, Mutex.new) } }
170
+ end
171
+ end
172
+
173
+ # Multiton can be included in another module, in which case that module effectively becomes
174
+ # a multiton behavior distributor too. This is why we propogate #included to the base module.
175
+ # by putting it in another module.
176
+ #
177
+ #--
178
+ # def append_features(mod)
179
+ # # help out people counting on transitive mixins
180
+ # unless mod.instance_of?(Class)
181
+ # raise TypeError, "Inclusion of Multiton in module #{mod}"
182
+ # end
183
+ # super
184
+ # end
185
+ #++
186
+
187
+ module Inclusive
188
+ private
189
+ def included(base)
190
+ class << base
191
+ #alias_method(:new!, :new) unless method_defined?(:new!)
192
+ # gracefully handle multiple inclusions of Multiton
193
+ unless include?(Multiton::MetaMethods)
194
+ alias_method :new!, :new
195
+ private :allocate #, :new
196
+ include Multiton::MetaMethods
197
+
198
+ if method_defined?(:marshal_dump)
199
+ undef_method :marshal_dump
200
+ warn "warning: marshal_dump was undefined since it is incompatible with the Multiton pattern"
201
+ end
202
+ end
203
+ end
204
+ end
205
+ end
206
+
207
+ extend Inclusive
208
+
209
+ #
210
+
211
+ module MetaMethods
212
+
213
+ include Inclusive
214
+
215
+ def instance(*e, &b)
216
+ arg = multiton_id(*e, &b)
217
+ multiton_instance.fetch(arg) do
218
+ multiton_mutex[arg].synchronize do
219
+ multiton_instance.fetch(arg) do
220
+ val = multiton_instance[arg] = new!(*e, &b) #new(*e, &b)
221
+ val.instance_variable_set(:@multiton_initializer, e, &b)
222
+ multiton_mutex.initialized(arg)
223
+ val
224
+ end
225
+ end
226
+ end
227
+ end
228
+ alias_method :new, :instance
229
+
230
+ def initialized?(*e, &b)
231
+ multiton_instance.key?(multiton_id(*e, &b))
232
+ end
233
+
234
+ protected
235
+
236
+ def multiton_instance
237
+ @multiton_instance ||= Hash.new
238
+ end
239
+
240
+ def multiton_mutex
241
+ @multiton_mutex ||= InstanceMutex.new
242
+ end
243
+
244
+ def reinitialize
245
+ multiton_instance.clear
246
+ multiton_mutex.clear
247
+ end
248
+
249
+ def _load(str)
250
+ instance(*Marshal.load(str))
251
+ end
252
+
253
+ private
254
+
255
+ # Default method to to create a key to cache already constructed
256
+ # instances. In the use case MultitonClass.new(e), MultiClass.new(f)
257
+ # must be semantically equal if multiton_id(e).eql?(multiton_id(f))
258
+ # evaluates to true.
259
+ def multiton_id(*e, &b)
260
+ e
261
+ end
262
+
263
+ def singleton_method_added(sym)
264
+ super
265
+ if (sym == :marshal_dump) & singleton_methods.include?('marshal_dump')
266
+ raise TypeError, "Don't use marshal_dump - rely on _dump and _load instead"
267
+ end
268
+ end
269
+
270
+ end
271
+
272
+ end
@@ -0,0 +1,14 @@
1
+ require 'teststrap'
2
+
3
+ context "focal_point" do
4
+
5
+ setup do
6
+ false
7
+ end
8
+
9
+ asserts "i'm a failure :(" do
10
+ topic
11
+ fail
12
+ end
13
+
14
+ end
data/test/teststrap.rb ADDED
@@ -0,0 +1,3 @@
1
+ require 'rubygems'
2
+ require 'riot'
3
+ require 'focal_point'
metadata ADDED
@@ -0,0 +1,95 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: focal_point
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - levicook@gmail.com
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+
12
+ date: 2010-01-03 00:00:00 -06:00
13
+ default_executable:
14
+ dependencies:
15
+ - !ruby/object:Gem::Dependency
16
+ name: hitimes
17
+ type: :runtime
18
+ version_requirement:
19
+ version_requirements: !ruby/object:Gem::Requirement
20
+ requirements:
21
+ - - ">="
22
+ - !ruby/object:Gem::Version
23
+ version: 1.0.0
24
+ version:
25
+ - !ruby/object:Gem::Dependency
26
+ name: riot
27
+ type: :development
28
+ version_requirement:
29
+ version_requirements: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: "0"
34
+ version:
35
+ - !ruby/object:Gem::Dependency
36
+ name: yard
37
+ type: :development
38
+ version_requirement:
39
+ version_requirements: !ruby/object:Gem::Requirement
40
+ requirements:
41
+ - - ">="
42
+ - !ruby/object:Gem::Version
43
+ version: "0"
44
+ version:
45
+ description: instruments methods with hitimes
46
+ email: levicook@gmail.com
47
+ executables: []
48
+
49
+ extensions: []
50
+
51
+ extra_rdoc_files:
52
+ - LICENSE
53
+ - README.rdoc
54
+ files:
55
+ - .document
56
+ - .gitignore
57
+ - LICENSE
58
+ - README.rdoc
59
+ - Rakefile
60
+ - VERSION
61
+ - lib/focal_point.rb
62
+ - lib/multiton.rb
63
+ - test/focal_point_test.rb
64
+ - test/teststrap.rb
65
+ has_rdoc: true
66
+ homepage: http://github.com/levicook/focal_point
67
+ licenses: []
68
+
69
+ post_install_message:
70
+ rdoc_options:
71
+ - --charset=UTF-8
72
+ require_paths:
73
+ - lib
74
+ required_ruby_version: !ruby/object:Gem::Requirement
75
+ requirements:
76
+ - - ">="
77
+ - !ruby/object:Gem::Version
78
+ version: "0"
79
+ version:
80
+ required_rubygems_version: !ruby/object:Gem::Requirement
81
+ requirements:
82
+ - - ">="
83
+ - !ruby/object:Gem::Version
84
+ version: "0"
85
+ version:
86
+ requirements: []
87
+
88
+ rubyforge_project:
89
+ rubygems_version: 1.3.5
90
+ signing_key:
91
+ specification_version: 3
92
+ summary: Focused, unobtrusive execution timing reports for ruby.
93
+ test_files:
94
+ - test/focal_point_test.rb
95
+ - test/teststrap.rb