cache_method 0.2.3 → 0.2.4

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/CHANGELOG CHANGED
@@ -1,3 +1,9 @@
1
+ 0.2.4 / 2012-04-17
2
+
3
+ * Enhancements
4
+
5
+ * Refactored to simplify and make thread-safe
6
+
1
7
  0.2.3 / 2012-04-06
2
8
 
3
9
  * Enhancements
data/Gemfile CHANGED
@@ -7,3 +7,5 @@ gemspec
7
7
  gem 'rake'
8
8
  gem 'dalli'
9
9
  gem 'yard'
10
+ gem 'minitest'
11
+ gem 'minitest-reporters'
data/README.markdown CHANGED
@@ -6,28 +6,35 @@ Lets you cache the results of calling methods given their arguments. Like memoiz
6
6
 
7
7
  ## Real-world usage
8
8
 
9
- In production use at [impact.brighterplanet.com](http://impact.brighterplanet.com) and [data.brighterplanet.com](http://data.brighterplanet.com).
9
+ <p><a href="http://brighterplanet.com"><img src="https://s3.amazonaws.com/static.brighterplanet.com/assets/logos/flush-left/inline/green/rasterized/brighter_planet-160-transparent.png" alt="Brighter Planet logo"/></a></p>
10
+
11
+ We use `cache_method` for [data science at Brighter Planet](http://brighterplanet.com/research) and in production at
12
+
13
+ * [Brighter Planet's impact estimate web service](http://impact.brighterplanet.com)
14
+ * [Brighter Planet's reference data web service](http://data.brighterplanet.com)
15
+
16
+ ## Rationale
17
+
18
+ * It should be easy to cache instance methods
19
+ * It should be easy to cache methods that depend on object state
20
+ * It should be easy to uncache a method without clearing the whole cache
21
+ * It should be easy to do all that using a default in-process store, memcached, dalli (if you're on heroku), redis, etc. (all supported by this gem through the [cache gem](https://rubygems.org/gems/cache))
10
22
 
11
23
  ## Example
12
24
 
13
25
  require 'cache_method'
14
26
  class Blog
15
- attr_reader :name
16
- attr_reader :url
17
-
27
+ attr_reader :name, :url
18
28
  def initialize(name, url)
19
29
  @name = name
20
30
  @url = url
21
31
  end
22
-
23
- def get_latest_entries
32
+ # The slow method that you want to speed up
33
+ def entries(date)
24
34
  # ...
25
35
  end
26
- cache_method :get_latest_entries
27
-
28
- # By default, cache_method derives the cache key for an instance by getting the SHA1 hash of the Marshal.dump
29
- # If you need to customize how an instance is recognized, you can define #as_cache_key.
30
- # Marshal.load will be called on the result.
36
+ cache_method :entries
37
+ # Not always required
31
38
  def as_cache_key
32
39
  { :name => name, :url => url }
33
40
  end
@@ -35,37 +42,20 @@ In production use at [impact.brighterplanet.com](http://impact.brighterplanet.co
35
42
 
36
43
  Then you can do
37
44
 
38
- my_blog.get_latest_entries => first time won't be cached
39
- my_blog.get_latest_entries => second time will come from cache
45
+ my_blog.entries(Date.today) => first time won't be cached
46
+ my_blog.entries(Date.today) => second time will come from cache
40
47
 
41
48
  And clear them too
42
49
 
43
- my_blog.cache_method_clear :get_latest_entries
50
+ my_blog.cache_method_clear :entries
44
51
 
45
52
  (which doesn't delete the rest of your cache)
46
53
 
47
- ## ActiveRecord
48
-
49
- If you're caching methods ActiveRecord objects (aka instances of `ActiveRecord::Base`), then you should probably define something like:
50
-
51
- class ActiveRecord::Base
52
- def as_cache_key
53
- attributes
54
- end
55
- end
56
-
57
- ## Debug
58
-
59
- CacheMethod can warn you if your obj or args cache keys are suspiciously long.
60
-
61
- require 'cache_method'
62
- require 'cache_method/debug'
63
-
64
- Then watch your logs.
65
-
66
54
  ## Configuration (and supported cache clients)
67
55
 
68
- You need to set where the cache will be stored:
56
+ By default, an in-process, non-shared cache is used.
57
+
58
+ You can set where the cache will be stored:
69
59
 
70
60
  CacheMethod.config.storage = Memcached.new '127.0.0.1:11211'
71
61
 
@@ -79,13 +69,30 @@ or this might even work...
79
69
 
80
70
  See `Config` for the full list of supported caches.
81
71
 
82
- ## Defining a #as_cache_key method
72
+ ## Cache keys
73
+
74
+ Caching a method involves getting cache keys for
75
+
76
+ 1. the object where the method is defined - for example, `my_blog.as_cache_key`
77
+ 2. the arguments passed to the method - for example, `Marshal.dump(Date.today)`, because `Date#as_cache_key` is not defined
78
+
79
+ Then the cache keys are SHA-1 hashed and combined for an overall key:
80
+
81
+ method signature + obj digest + args digest
82
+ Blog#entries + SHA1(Marshal.dump({:name="Seamus's blog",:url=>"http://numbers.brighterplanet.com"}) + SHA1(Marshal.dump(Date.today))
83
+
84
+ Technically the full cache key is
85
+
86
+ # when caching class methods
87
+ method signature + generation + args digest
88
+ # when caching instance methods
89
+ method signature + obj digest + generation + args digest
83
90
 
84
- Since we're not pure functional programmers, sometimes cache hits depend on object state in addition to method arguments. To illustrate:
91
+ (see "Generational caching" below for an explanation of the generation part)
85
92
 
86
- my_blog.get_latest_entries
93
+ ### #as_cache_key
87
94
 
88
- get_latest_entries doesn't take any arguments, so it must depend on my_blog.url or something. This works because we define:
95
+ As above, you can define a custom cache key for an object:
89
96
 
90
97
  class Blog
91
98
  # [...]
@@ -95,26 +102,97 @@ get_latest_entries doesn't take any arguments, so it must depend on my_blog.url
95
102
  # [...]
96
103
  end
97
104
 
98
- If you don't define `#as_cache_key`, then `cache_method` will `Marshal.dump` an instance.
105
+ If you don't define `#as_cache_key`, the default is to `Marshal.dump` the whole object. (That's not as terrible as it seems - marshalling is fast!)
99
106
 
100
- ## Danger: #to_cache_key
107
+ ### #to_cache_key (danger!)
101
108
 
102
- Let's say you want need cache keys from classes that undefine the `#class` method (how annoying). You can define `#to_cache_key`, but **make sure it identifies the class too!** (in addition to the instance.)
109
+ There's another way to define a cache key, but it should be used with caution because it gives you total control.
103
110
 
104
- For example, if you find yourself passing association proxies as arguments to cached methods, this might be helpful:
111
+ The key is to make sure your `#to_cache_key` method identifies both the class and the instance!
105
112
 
106
- user = User.first
107
- Foo.bar(user.groups) # you're passing an association as an argument
113
+ ### Comparison
108
114
 
115
+ <table>
116
+ <tr>
117
+ <th>Method</th>
118
+ <th>Must uniquely identify class</th>
119
+ <th>Must uniquely identify instance</th>
120
+ </tr>
121
+ <tr>
122
+ <td><code>#as_cache_key</code></td>
123
+ <td>N&dagger;</td>
124
+ <td>Y</td>
125
+ </tr>
126
+ <tr>
127
+ <td><code>#to_cache_key</code></td>
128
+ <td>Y</td>
129
+ <td>Y</td>
130
+ </tr>
131
+ </table>
132
+
133
+ &dagger; The class name is automatically inserted for you by calling `object.class.name`, which is what causes all the trouble with `ActiveRecord::Associations::CollectionProxy`, etc.
134
+
135
+ ## ActiveRecord
136
+
137
+ If you're caching methods on instances of `ActiveRecord::Base`, and/or using them as arguments to other cached methods, then you should probably define something like:
138
+
139
+ class ActiveRecord::Base
140
+ def as_cache_key
141
+ attributes
142
+ end
143
+ end
144
+
145
+ If you find yourself passing association proxies as arguments to cached methods, this might be helpful:
146
+
147
+ # For use in ActiveRecord 3.0.x
109
148
  class ActiveRecord::Associations::AssociationCollection
110
- # danger! this is a special case... try to use #as_cache_key instead
111
- # also note that this example is based on ActiveRecord 3.0.10 ... YMMV
149
+ # rare use of to_cache_key
112
150
  def to_cache_key
113
- [ proxy_owner.class.name, proxy_owner.id, proxy_reflection.name, conditions ].join('/')
151
+ [
152
+ 'ActiveRecord::Associations::AssociationCollection',
153
+ proxy_owner.class.name,
154
+ proxy_owner.id,
155
+ proxy_reflection.name,
156
+ conditions
157
+ ].join('/')
114
158
  end
115
159
  end
116
160
 
117
- Otherwise, `cache_method` will try to run `user.groups.class` which causes the proxy to be loaded. You probably don't want to load 1000 AR objects just to generate a cache key.
161
+ # For use in ActiveRecord 3.2.x
162
+ class ActiveRecord::Associations::CollectionProxy
163
+ # rare use of to_cache_key
164
+ def to_cache_key
165
+ owner = proxy_association.owner
166
+ [
167
+ 'ActiveRecord::Associations::CollectionProxy', # [included because we're using #to_cache_key instead of #as_cache_key ]
168
+ owner.class.name, # User
169
+ owner.id, # 'seamusabshere'
170
+ proxy_association.reflection.name, # :comments
171
+ scoped.where_sql # "WHERE `comments`.`user_id` = 'seamusabshere'" [maybe a little bit redundant, but play it safe]
172
+ ].join('/')
173
+ end
174
+ end
175
+
176
+ Otherwise, `cache_method` will call `user.comments.class.name` which causes the proxy to load the target, i.e. load all the Comment objects into memory. You probably don't want to load 1000 AR objects just to generate a cache key.
177
+
178
+ ## Generational caching
179
+
180
+ Generational caching allows clearing the cached results for only one method, for example
181
+
182
+ my_blog.cache_method_clear :entries
183
+
184
+ You can disable it to get a little speed boost
185
+
186
+ CacheMethod.config.generational = false
187
+
188
+ ## Debug
189
+
190
+ CacheMethod can warn you if your obj or args cache keys are suspiciously long.
191
+
192
+ require 'cache_method'
193
+ require 'cache_method/debug'
194
+
195
+ Then watch your logs.
118
196
 
119
197
  ## Module methods
120
198
 
@@ -143,13 +221,6 @@ Rest assured that `Tiger.my_module_method` and `Lion.my_module_method` will be c
143
221
  # cache_method :my_module_method
144
222
  end
145
223
 
146
- ## Rationale
147
-
148
- * It should be easy to cache a method using memcached, dalli (if you're on heroku), redis, etc. (that's why I made the [cache gem](https://rubygems.org/gems/cache))
149
- * It should be easy to uncache a method without clearing the whole cache
150
- * It should be easy to cache instance methods
151
- * It should be easy to cache methods that depend on object state (hence `#as_cache_key`)
152
-
153
224
  ## Copyright
154
225
 
155
226
  Copyright 2012 Seamus Abshere
@@ -1,91 +1,66 @@
1
- require 'digest/sha1'
2
1
  module CacheMethod
3
2
  class CachedResult #:nodoc: all
4
- class << self
5
- def resolve_cache_key(obj)
6
- case obj
7
- when ::Array
8
- obj.map do |v|
9
- resolve_cache_key v
10
- end
11
- when ::Hash
12
- obj.inject({}) do |memo, (k, v)|
13
- kk = resolve_cache_key k
14
- vv = resolve_cache_key v
15
- memo[kk] = vv
16
- memo
17
- end
18
- else
19
- if obj.respond_to?(:to_cache_key)
20
- # this is meant to be used sparingly, usually when a proxy class is involved
21
- obj.to_cache_key
22
- elsif obj.respond_to?(:as_cache_key)
23
- [obj.class.name, obj.as_cache_key]
24
- else
25
- obj
26
- end
27
- end
28
- end
29
- end
30
-
31
- CACHE_KEY_JOINER = ','
32
-
33
- def initialize(obj, method_id, original_method_id, ttl, args)
3
+ def initialize(obj, method_id, original_method_id, ttl, args, &blk)
34
4
  @obj = obj
35
5
  @method_id = method_id
6
+ @method_signature = CacheMethod.method_signature obj, method_id
36
7
  @original_method_id = original_method_id
37
- @ttl = ttl
8
+ @ttl = ttl || CacheMethod.config.default_ttl
38
9
  @args = args
10
+ @args_digest = args.empty? ? 'empty' : CacheMethod.digest(args)
11
+ @blk = blk
12
+ @fetch_mutex = ::Mutex.new
39
13
  end
40
14
 
41
15
  attr_reader :obj
42
16
  attr_reader :method_id
17
+ attr_reader :method_signature
43
18
  attr_reader :original_method_id
44
19
  attr_reader :args
20
+ attr_reader :args_digest
21
+ attr_reader :blk
22
+ attr_reader :ttl
45
23
 
46
24
  # Store things wrapped in an Array so that nil is accepted
47
25
  def fetch
48
- if v = Config.instance.storage.get(cache_key) and v.is_a?(::Array)
49
- v.first
26
+ if wrapped_v = get_wrapped
27
+ wrapped_v.first
50
28
  else
51
- v = obj.send original_method_id, *args
52
- Config.instance.storage.set cache_key, [v], ttl
53
- v
29
+ @fetch_mutex.synchronize do
30
+ (get_wrapped || set_wrapped).first
31
+ end
54
32
  end
55
33
  end
56
34
 
57
35
  def exist?
58
- Config.instance.storage.exist?(cache_key)
36
+ CacheMethod.config.storage.exist?(cache_key)
59
37
  end
60
-
61
- def ttl
62
- @ttl ||= Config.instance.default_ttl
63
- end
64
-
38
+
39
+ private
40
+
65
41
  def cache_key
66
42
  if obj.is_a?(::Class) or obj.is_a?(::Module)
67
43
  [ 'CacheMethod', 'CachedResult', method_signature, current_generation, args_digest ].compact.join CACHE_KEY_JOINER
68
44
  else
69
- [ 'CacheMethod', 'CachedResult', method_signature, obj_digest, current_generation, args_digest ].compact.join CACHE_KEY_JOINER
45
+ [ 'CacheMethod', 'CachedResult', method_signature, CacheMethod.digest(obj), current_generation, args_digest ].compact.join CACHE_KEY_JOINER
70
46
  end
71
47
  end
72
-
73
- def method_signature
74
- @method_signature ||= ::CacheMethod.method_signature(obj, method_id)
75
- end
76
-
77
- def obj_digest
78
- @obj_digest ||= ::Digest::SHA1.hexdigest(::Marshal.dump(CachedResult.resolve_cache_key(obj)))
79
- end
80
-
81
- def args_digest
82
- @args_digest ||= args.empty? ? 'empty' : ::Digest::SHA1.hexdigest(::Marshal.dump(CachedResult.resolve_cache_key(args)))
83
- end
84
-
48
+
85
49
  def current_generation
86
- if Config.instance.generational?
87
- @current_generation ||= Generation.new(obj, method_id).current
50
+ if CacheMethod.config.generational?
51
+ Generation.new(obj, method_id).fetch
88
52
  end
89
53
  end
54
+
55
+ def get_wrapped
56
+ CacheMethod.config.storage.get cache_key
57
+ end
58
+
59
+ def set_wrapped
60
+ v = obj.send(*([original_method_id]+args), &blk)
61
+ wrapped_v = [v]
62
+ CacheMethod.config.storage.set cache_key, wrapped_v, ttl
63
+ wrapped_v
64
+ end
90
65
  end
91
66
  end
@@ -1,5 +1,5 @@
1
1
  require 'cache'
2
- require 'singleton'
2
+
3
3
  module CacheMethod
4
4
  # Here's where you set config options.
5
5
  #
@@ -9,7 +9,10 @@ module CacheMethod
9
9
  #
10
10
  # You'd probably put this in your Rails config/initializers, for example.
11
11
  class Config
12
- include ::Singleton
12
+
13
+ def initialize
14
+ @mutex = ::Mutex.new
15
+ end
13
16
 
14
17
  # Whether to use "generational" caching. Default is true.
15
18
  #
@@ -44,7 +47,9 @@ module CacheMethod
44
47
  end
45
48
 
46
49
  def storage #:nodoc:
47
- @storage ||= ::Cache.new
50
+ @storage || @mutex.synchronize do
51
+ @storage ||= ::Cache.new
52
+ end
48
53
  end
49
54
 
50
55
  # TTL for method caches. Defaults to 24 hours or 86,400 seconds.
@@ -1,17 +1,8 @@
1
1
  module CacheMethod
2
- class CachedResult #:nodoc: all
3
- def obj_digest
4
- if (l = ::Marshal.dump(CachedResult.resolve_cache_key(obj)).length) > 500
5
- $stderr.puts "Warn: #{l} long obj digest. #{obj.class} -> #{CachedResult.resolve_cache_key(obj).inspect}"
6
- end
7
- @obj_digest ||= ::Digest::SHA1.hexdigest(::Marshal.dump(CachedResult.resolve_cache_key(obj)))
8
- end
9
-
10
- def args_digest
11
- if (l = ::Marshal.dump(CachedResult.resolve_cache_key(args)).length) > 500
12
- $stderr.puts "Warn: #{l} long args digest. #{method_signature}(#{CachedResult.resolve_cache_key(args).inspect})"
13
- end
14
- @args_digest ||= args.empty? ? 'empty' : ::Digest::SHA1.hexdigest(::Marshal.dump(CachedResult.resolve_cache_key(args)))
2
+ def CacheMethod.digest(obj)
3
+ if (l = ::Marshal.dump(resolve_cache_key(obj)).length) > 500
4
+ $stderr.puts "[cache_method] Warn: #{l} long digest. #{obj.class} -> #{resolve_cache_key(obj).inspect}"
15
5
  end
6
+ ::Digest::SHA1.hexdigest ::Marshal.dump(resolve_cache_key(obj))
16
7
  end
17
8
  end
@@ -1,49 +1,49 @@
1
- require 'digest/sha1'
2
1
  module CacheMethod
3
2
  class Generation #:nodoc: all
4
- class << self
5
- def random_name
6
- rand(100_000_000).to_s
7
- end
8
- end
9
-
10
3
  def initialize(obj, method_id)
11
4
  @obj = obj
12
5
  @method_id = method_id
6
+ @method_signature = CacheMethod.method_signature obj, method_id
7
+ @fetch_mutex = ::Mutex.new
13
8
  end
14
9
 
15
10
  attr_reader :obj
16
11
  attr_reader :method_id
17
-
18
- def method_signature
19
- @method_signature ||= ::CacheMethod.method_signature(obj, method_id)
12
+ attr_reader :method_signature
13
+
14
+ def fetch
15
+ if existing = get
16
+ existing
17
+ else
18
+ @fetch_mutex.synchronize do
19
+ get || set
20
+ end
21
+ end
20
22
  end
21
23
 
22
- def obj_digest
23
- @obj_digest ||= ::Digest::SHA1.hexdigest(::Marshal.dump(obj.respond_to?(:as_cache_key) ? obj.as_cache_key : obj))
24
+ def mark_passing
25
+ CacheMethod.config.storage.delete cache_key
24
26
  end
25
-
27
+
28
+ private
29
+
26
30
  def cache_key
27
31
  if obj.is_a?(::Class) or obj.is_a?(::Module)
28
- [ 'CacheMethod', 'Generation', method_signature ].join ','
32
+ [ 'CacheMethod', 'Generation', method_signature ].join CACHE_KEY_JOINER
29
33
  else
30
- [ 'CacheMethod', 'Generation', method_signature, obj_digest ].join ','
34
+ [ 'CacheMethod', 'Generation', method_signature, CacheMethod.digest(obj) ].join CACHE_KEY_JOINER
31
35
  end
32
36
  end
33
-
34
- def current
35
- if cached_v = Config.instance.storage.get(cache_key)
36
- cached_v
37
- else
38
- v = Generation.random_name
39
- # never expire!
40
- Config.instance.storage.set cache_key, v, 0
41
- v
42
- end
37
+
38
+ def get
39
+ CacheMethod.config.storage.get cache_key
43
40
  end
44
-
45
- def mark_passing
46
- Config.instance.storage.delete cache_key
41
+
42
+ def set
43
+ random_name = ::Kernel.rand(1e11).to_s
44
+ # never expire!
45
+ CacheMethod.config.storage.set cache_key, random_name, 0
46
+ random_name
47
47
  end
48
48
  end
49
49
  end
@@ -1,3 +1,3 @@
1
1
  module CacheMethod
2
- VERSION = "0.2.3"
2
+ VERSION = "0.2.4"
3
3
  end
data/lib/cache_method.rb CHANGED
@@ -1,24 +1,60 @@
1
+ require 'thread'
2
+ require 'digest/sha1'
3
+
1
4
  require 'cache_method/config'
2
5
  require 'cache_method/cached_result'
3
6
  require 'cache_method/generation'
4
7
 
5
- # See the README.rdoc for more info!
6
8
  module CacheMethod
7
- def self.config #:nodoc:
8
- Config.instance
9
+ MUTEX = ::Mutex.new
10
+ CACHE_KEY_JOINER = ','
11
+
12
+ def CacheMethod.config #:nodoc:
13
+ @config || MUTEX.synchronize do
14
+ @config ||= Config.new
15
+ end
9
16
  end
10
17
 
11
- def self.klass_name(obj) #:nodoc:
18
+ def CacheMethod.klass_name(obj) #:nodoc:
12
19
  (obj.is_a?(::Class) or obj.is_a?(::Module)) ? obj.to_s : obj.class.to_s
13
20
  end
14
21
 
15
- def self.method_delimiter(obj) #:nodoc:
22
+ def CacheMethod.method_delimiter(obj) #:nodoc:
16
23
  (obj.is_a?(::Class) or obj.is_a?(::Module)) ? '.' : '#'
17
24
  end
18
25
 
19
- def self.method_signature(obj, method_id) #:nodoc:
26
+ def CacheMethod.method_signature(obj, method_id) #:nodoc:
20
27
  [ klass_name(obj), method_id ].join method_delimiter(obj)
21
28
  end
29
+
30
+ def CacheMethod.resolve_cache_key(obj)
31
+ case obj
32
+ when ::Array
33
+ obj.map do |v|
34
+ resolve_cache_key v
35
+ end
36
+ when ::Hash
37
+ obj.inject({}) do |memo, (k, v)|
38
+ kk = resolve_cache_key k
39
+ vv = resolve_cache_key v
40
+ memo[kk] = vv
41
+ memo
42
+ end
43
+ else
44
+ if obj.respond_to?(:to_cache_key)
45
+ # this is meant to be used sparingly, usually when a proxy class is involved
46
+ obj.to_cache_key
47
+ elsif obj.respond_to?(:as_cache_key)
48
+ [obj.class.name, obj.as_cache_key]
49
+ else
50
+ obj
51
+ end
52
+ end
53
+ end
54
+
55
+ def CacheMethod.digest(obj)
56
+ ::Digest::SHA1.hexdigest ::Marshal.dump(resolve_cache_key(obj))
57
+ end
22
58
 
23
59
  # All Objects, including instances and Classes, get the <tt>#cache_method_clear</tt> method.
24
60
  module InstanceMethods
@@ -59,15 +95,13 @@ module CacheMethod
59
95
  def cache_method(method_id, ttl = nil)
60
96
  original_method_id = "_uncached_#{method_id}"
61
97
  alias_method original_method_id, method_id
62
- define_method method_id do |*args|
63
- ::CacheMethod::CachedResult.new(self, method_id, original_method_id, ttl, args).fetch
98
+ define_method method_id do |*args, &blk|
99
+ ::CacheMethod::CachedResult.new(self, method_id, original_method_id, ttl, args, &blk).fetch
64
100
  end
65
101
  end
66
102
  end
67
103
  end
68
104
 
69
- unless ::Object.method_defined? :cache_method
70
- ::Object.send :include, ::CacheMethod::InstanceMethods
71
- ::Class.send :include, ::CacheMethod::ClassMethods
72
- ::Module.send :include, ::CacheMethod::ClassMethods
73
- end
105
+ ::Object.send :include, ::CacheMethod::InstanceMethods
106
+ ::Class.send :include, ::CacheMethod::ClassMethods
107
+ ::Module.send :include, ::CacheMethod::ClassMethods
data/test/helper.rb CHANGED
@@ -1,19 +1,17 @@
1
1
  require 'rubygems'
2
- require 'bundler'
3
- Bundler.setup
4
- require 'test/unit'
2
+ require 'bundler/setup'
3
+ require 'minitest/spec'
4
+ require 'minitest/autorun'
5
+ require 'minitest/reporters'
6
+ MiniTest::Unit.runner = MiniTest::SuiteRunner.new
7
+ MiniTest::Unit.runner.reporters << MiniTest::Reporters::SpecReporter.new
5
8
 
6
9
  if ::Bundler.definition.specs['ruby-debug19'].first or ::Bundler.definition.specs['ruby-debug'].first
7
10
  require 'ruby-debug'
8
11
  end
9
12
 
10
- $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
11
- $LOAD_PATH.unshift(File.dirname(__FILE__))
12
13
  require 'cache_method'
13
14
 
14
- class Test::Unit::TestCase
15
- end
16
-
17
15
  class CopyCat1
18
16
  attr_reader :name
19
17
  def initialize(name)