memcacheable 0.0.2 → 0.1.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: 8ada7260a91b22ee55210e38fced1ee123e23c2e
4
- data.tar.gz: 86e4b7113bc21f837d3b17261b6bf00f589db6b0
3
+ metadata.gz: 83bbf521df93a4a60b8a30d553026ad529de5a4a
4
+ data.tar.gz: b4c06414324517d18d7baeb121340b190784d418
5
5
  SHA512:
6
- metadata.gz: 64ab2b3303b5f27c66eef23cfe426ccc98d925995cdcc20ba631e4348d6bad53319aca7c8b311cc85a82b2fa60f76582f368579d08f2f2241e75a5a25538ea51
7
- data.tar.gz: 15c657ebc23dc4b4807423d5e0c317709cf9005ad675c9030e601f0bd86e52d834eeaa37804c1fd4c158749d49b676366ca24833b14f0a82a012ae6e61e7fab7
6
+ metadata.gz: 70da4d0ccb557357110e24298fc01b41995b3da99ead6ebd23d85101ea45cecfdeb26e7476d303a2e2894d5154ca3fc1cd6ea0d5900b1e4cecc3aa6376a36bd6
7
+ data.tar.gz: 0365dced8c66ecd9f3a9e0577a187ef7e62d9de21dc88816c7f17737943be9f7a706c5678ace823b50ae9837b17bc60c4466c046ef811e3daff5d9750d7e314b
data/README.md CHANGED
@@ -26,7 +26,7 @@ class Person < ActiveRecord::Base
26
26
  end
27
27
  ```
28
28
 
29
- **Boom!** Now you can `fetch` a person by their id, like below. When the person gets updated or touched, it will flush the cache, and the person will be reloaded on the next `fetch`.
29
+ Boom! Now you can `fetch` a person by their id, like below. When the person gets updated or touched, it will flush the cache, and the person will be reloaded on the next `fetch`.
30
30
 
31
31
  ```ruby
32
32
  person = Person.fetch id # caches the person
@@ -62,7 +62,7 @@ person = Person.fetch_by weight: 175, height: 71 # fetched and cached with new
62
62
  Like noise in your life? Try `fetch_by!` (hard to say: "fetch-by-bang!").
63
63
 
64
64
  ```ruby
65
- person = Person.fetch_by! name: 'Mork' # => raises ActiveRecord::RecordNotFound
65
+ person = Person.fetch_by! name: 'Mork' # => ActiveRecord::RecordNotFound
66
66
  ```
67
67
 
68
68
  While `fetch_by` just pulls back just one record, you can fetch a collection with `fetch_where`:
@@ -88,7 +88,7 @@ Btw, don't do something stupid like trying to call scope methods on the result o
88
88
  Person.fetch_where(height: 60).limit(5)
89
89
  ```
90
90
 
91
- I may fix this later, though, because I like scopes.
91
+ If you want something very similar to scopes, keep reading to learn about [caching methods](https://github.com/flintinatux/memcacheable#cache-methods).
92
92
 
93
93
  ### Cache associations
94
94
 
@@ -138,6 +138,31 @@ person.touch # association cache is flushed, but not the fetch_where
138
138
  person.fetch_kittens # reloads the kittens from the cache, and caches as an association
139
139
  ```
140
140
 
141
+ ### Cache methods
142
+
143
+ Does your model have a method that eats up lots of calculation time, or perhaps a scope-like method that requires a database query? Cache that bad boy!
144
+
145
+ ```ruby
146
+ class Person < ActiveRecord::Base
147
+ has_many :kittens
148
+
149
+ include Memcacheable
150
+ cache_method :random_kitten
151
+
152
+ def random_kitten(seed=Random.new_seed)
153
+ kittens.sample(Random.new seed)
154
+ end
155
+ end
156
+ ```
157
+
158
+ **Voila!** Now you get a nice fetch method to cache the results:
159
+
160
+ ```ruby
161
+ person.fetch_random_kitten(12345) # => gets your random kitty, and then caches it
162
+ ```
163
+
164
+ Notice that the fetch method accepts the same args as the original. **Caveat:** blocks are not accepted, unfortunately. I love blocks, but they don't have a consistent identifier to include in a cache key. So feel free to get creative with args, but not blocks.
165
+
141
166
  ## Inspiration
142
167
 
143
168
  None of the caching options out there really satisfied my needs, so I wrote this gem. But I was not without inspiration. I learned the basics of Rails caching from the [RailsCasts](http://railscasts.com/) episode on [Model Caching](http://railscasts.com/episodes/115-model-caching-revised), and I borrowed a lot of syntax from the very popular [IdentityCache gem](https://github.com/Shopify/identity_cache) from our friends at [Shopify](http://www.shopify.com/).
data/lib/memcacheable.rb CHANGED
@@ -10,6 +10,7 @@ module Memcacheable
10
10
  autoload :FetchByCriteria
11
11
  autoload :FetchHasMany
12
12
  autoload :FetchHasOne
13
+ autoload :FetchMethod
13
14
  autoload :FetchOne
14
15
  autoload :FetchWhere
15
16
  autoload :Fetcher
@@ -52,6 +53,14 @@ module Memcacheable
52
53
  self.cached_indexes << fields.map(&:to_sym).sort
53
54
  end
54
55
 
56
+ def cache_method(*methods)
57
+ methods.each do |method|
58
+ define_method "fetch_#{method}" do |*args|
59
+ FetchMethod.new(self, method, args).fetch
60
+ end
61
+ end
62
+ end
63
+
55
64
  def fetch(id)
56
65
  FetchOne.new(self, id).fetch
57
66
  end
@@ -1,11 +1,16 @@
1
1
  module Memcacheable
2
2
  class FetchBelongsTo < FetchAssociation
3
+ def fetchable?
4
+ klass.respond_to?(:fetch)
5
+ end
3
6
 
4
7
  def find_on_cache_miss
5
- klass = association.to_s.camelize.constantize
6
8
  id = object.send "#{association}_id"
7
- klass.respond_to?(:fetch) ? klass.fetch(id) : klass.find(id) rescue nil
9
+ fetchable? ? klass.fetch(id) : object.send(association) rescue nil
10
+ end
11
+
12
+ def klass
13
+ @klass ||= association.to_s.camelize.constantize
8
14
  end
9
-
10
15
  end
11
16
  end
@@ -6,7 +6,7 @@ module Memcacheable
6
6
 
7
7
  def find_on_cache_miss
8
8
  criteria = { "#{class_name}_id" => object.id }
9
- fetchable? ? klass.fetch_where(criteria) : klass.where(criteria).to_a
9
+ fetchable? ? klass.fetch_where(criteria) : object.send(association).to_a
10
10
  end
11
11
 
12
12
  def klass
@@ -6,7 +6,7 @@ module Memcacheable
6
6
 
7
7
  def find_on_cache_miss
8
8
  criteria = { "#{class_name}_id" => object.id }
9
- fetchable? ? klass.fetch_by(criteria) : klass.find_by(criteria)
9
+ fetchable? ? klass.fetch_by(criteria) : object.send(association)
10
10
  end
11
11
 
12
12
  def klass
@@ -0,0 +1,23 @@
1
+ module Memcacheable
2
+ class FetchMethod < Fetcher
3
+ attr_accessor :object, :method, :args
4
+
5
+ def initialize(object, method, args)
6
+ self.object = object
7
+ self.method = method
8
+ self.args = args
9
+ end
10
+
11
+ def cache_key
12
+ [object, method, args]
13
+ end
14
+
15
+ def description
16
+ "method #{cache_key.to_param}"
17
+ end
18
+
19
+ def find_on_cache_miss
20
+ object.send method, *args
21
+ end
22
+ end
23
+ end
@@ -1,3 +1,3 @@
1
1
  module Memcacheable
2
- VERSION = "0.0.2"
2
+ VERSION = "0.1.0"
3
3
  end
@@ -20,6 +20,11 @@ class Kitten < FakeModel
20
20
  include Memcacheable
21
21
  cache_index :person_id
22
22
  cache_belongs_to :person
23
+ cache_method :meow
24
+
25
+ def meow(how_many=1)
26
+ how_many.times.map{ 'meow' }.join ' '
27
+ end
23
28
  end
24
29
 
25
30
  describe Memcacheable do
@@ -175,6 +180,7 @@ describe Memcacheable do
175
180
  describe '::cache_has_one' do
176
181
  let(:dog) { Dog.new person_id: id }
177
182
  before do
183
+ person.stub(:dog).and_return dog
178
184
  Dog.stub(:find_by) do |criteria|
179
185
  criteria.all?{ |k,v| dog.send(k) == v } ? dog : nil
180
186
  end
@@ -189,7 +195,7 @@ describe Memcacheable do
189
195
  end
190
196
 
191
197
  it "only queries once and then caches" do
192
- Dog.should_receive(:find_by).once
198
+ person.should_receive(:dog).once
193
199
  person.fetch_dog
194
200
  person.fetch_dog
195
201
  end
@@ -197,7 +203,7 @@ describe Memcacheable do
197
203
  it "flushes when touched by association" do
198
204
  person.fetch_dog
199
205
  person.touch
200
- Dog.should_receive(:find_by).once
206
+ person.should_receive(:dog).once
201
207
  person.fetch_dog
202
208
  end
203
209
  end
@@ -269,4 +275,29 @@ describe Memcacheable do
269
275
  person.fetch_kittens
270
276
  end
271
277
  end
278
+
279
+ describe '::cache_method' do
280
+ let(:kitten) { Kitten.new }
281
+
282
+ it "defines a new fetch method" do
283
+ kitten.should.respond_to?(:fetch_meow)
284
+ end
285
+
286
+ it "calls down to the method correctly on cache miss" do
287
+ kitten.fetch_meow(3) { |sound| sound.upcase }.should eq 'meow meow meow'
288
+ end
289
+
290
+ it "only queries once and then caches" do
291
+ kitten.should_receive(:meow).once
292
+ kitten.fetch_meow
293
+ kitten.fetch_meow
294
+ end
295
+
296
+ it "flushed when touched by an association" do
297
+ kitten.fetch_meow
298
+ kitten.touch
299
+ kitten.should_receive(:meow).once
300
+ kitten.fetch_meow
301
+ end
302
+ end
272
303
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: memcacheable
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.2
4
+ version: 0.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Scott McCormack
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2013-06-30 00:00:00.000000000 Z
11
+ date: 2013-07-01 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activerecord
@@ -116,6 +116,7 @@ files:
116
116
  - lib/memcacheable/fetch_by_criteria.rb
117
117
  - lib/memcacheable/fetch_has_many.rb
118
118
  - lib/memcacheable/fetch_has_one.rb
119
+ - lib/memcacheable/fetch_method.rb
119
120
  - lib/memcacheable/fetch_one.rb
120
121
  - lib/memcacheable/fetch_where.rb
121
122
  - lib/memcacheable/fetcher.rb