focal_point 0.0.1

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.
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