patella 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +17 -0
- data/Gemfile +4 -0
- data/LICENSE +22 -0
- data/README.md +37 -0
- data/Rakefile +11 -0
- data/lib/patella.rb +8 -0
- data/lib/patella/patella.rb +130 -0
- data/lib/patella/send_later.rb +26 -0
- data/lib/patella/send_later_worker.rb +18 -0
- data/lib/patella/version.rb +3 -0
- data/patella.gemspec +25 -0
- data/test/patella_test.rb +144 -0
- data/test/test_helper.rb +34 -0
- metadata +189 -0
data/.gitignore
ADDED
data/Gemfile
ADDED
data/LICENSE
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2012 Keenan Brock
|
2
|
+
|
3
|
+
MIT License
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
6
|
+
a copy of this software and associated documentation files (the
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
11
|
+
the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be
|
14
|
+
included in all copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,37 @@
|
|
1
|
+
# Patella
|
2
|
+
|
3
|
+
Gem for the RailsConf 2012 talk Patella: It's Memoization into Memcached calculated in the background with Resque.
|
4
|
+
[www.slideshare.net/jdwyah/patella-railsconf-2012](http://www.slideshare.net/jdwyah/patella-railsconf-2012)
|
5
|
+
|
6
|
+
## Installation
|
7
|
+
|
8
|
+
Add this line to your application's Gemfile:
|
9
|
+
|
10
|
+
gem 'patella'
|
11
|
+
|
12
|
+
And then execute:
|
13
|
+
|
14
|
+
$ bundle
|
15
|
+
|
16
|
+
Or install it yourself as:
|
17
|
+
|
18
|
+
$ gem install patella
|
19
|
+
|
20
|
+
## Usage
|
21
|
+
|
22
|
+
```ruby
|
23
|
+
def self.my_slow_method(user_id)
|
24
|
+
all_notifications_for(User.find(user_id))
|
25
|
+
end
|
26
|
+
patella_reflex :notification_unread_count, :expires_in => 3.minutes, :class_method => true
|
27
|
+
```
|
28
|
+
|
29
|
+
See the tests for more [examples](https://github.com/kbrock/patella/blob/master/test/patella_test.rb)
|
30
|
+
|
31
|
+
## Contributing
|
32
|
+
|
33
|
+
1. Fork it
|
34
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
35
|
+
3. Commit your changes (`git commit -am 'Added some feature'`)
|
36
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
37
|
+
5. Create new Pull Request
|
data/Rakefile
ADDED
data/lib/patella.rb
ADDED
@@ -0,0 +1,130 @@
|
|
1
|
+
require 'active_support'
|
2
|
+
require 'json'
|
3
|
+
|
4
|
+
module Patella::Patella
|
5
|
+
|
6
|
+
def self.included(base)
|
7
|
+
base.extend ClassMethods
|
8
|
+
base.send :include, Patella::SendLater
|
9
|
+
end
|
10
|
+
|
11
|
+
def patella_key(symbol, args)
|
12
|
+
id_param = respond_to?(:id) ? self.id : nil
|
13
|
+
"patella/#{self.class.to_s}/#{id_param}/#{symbol}/#{Digest::MD5.hexdigest(args.to_json)}"
|
14
|
+
end
|
15
|
+
|
16
|
+
module ClassMethods
|
17
|
+
def patella_key(symbol, args)
|
18
|
+
"patella/#{self.to_s}//#{symbol}/#{Digest::MD5.hexdigest(args.to_json)}"
|
19
|
+
end
|
20
|
+
|
21
|
+
def patella_reflex(symbol, options = {})
|
22
|
+
options[:expires_in] ||= 30*60
|
23
|
+
options[:soft_expiration] ||= 0
|
24
|
+
options[:no_backgrounding] ||= false
|
25
|
+
is_class = options[:class_method]
|
26
|
+
|
27
|
+
original_method = :"_unpatellaed_#{symbol}"
|
28
|
+
|
29
|
+
method_definitions = <<-EOS, __FILE__, __LINE__ + 1
|
30
|
+
|
31
|
+
if method_defined?(:#{original_method})
|
32
|
+
raise "Already patella'd #{symbol}"
|
33
|
+
end
|
34
|
+
alias #{original_method} #{symbol}
|
35
|
+
|
36
|
+
def caching_#{symbol}(args)
|
37
|
+
cache_key = self.patella_key('#{symbol}',args)
|
38
|
+
result = args.any? ? #{original_method}(*args) : #{original_method}()
|
39
|
+
json = {'result' => result, 'soft_expiration' => Time.now + #{options[:expires_in]} - #{ options[:soft_expiration]}}.to_json
|
40
|
+
Rails.cache.write(cache_key, json, :expires_in => #{options[:expires_in]})
|
41
|
+
result
|
42
|
+
end
|
43
|
+
|
44
|
+
def clear_#{symbol}(*args)
|
45
|
+
cache_key = self.patella_key('#{symbol}',args)
|
46
|
+
Rails.cache.delete(cache_key)
|
47
|
+
end
|
48
|
+
|
49
|
+
def #{symbol}(*args)
|
50
|
+
patella_#{symbol}(args, {:no_backgrounding => #{options[:no_backgrounding]}})
|
51
|
+
end
|
52
|
+
|
53
|
+
def patella_#{symbol}(args, opts)
|
54
|
+
cache_key = self.patella_key('#{symbol}',args)
|
55
|
+
promise = { 'promise' => true }
|
56
|
+
|
57
|
+
json = Rails.cache.fetch(cache_key, :expires_in => #{options[:expires_in]}, :force => !Rails.caching?) do
|
58
|
+
if opts[:no_backgrounding]
|
59
|
+
promise['result'] = self.send(:caching_#{symbol}, args)
|
60
|
+
promise.delete('promise')
|
61
|
+
else
|
62
|
+
promise['result'] = self.send_later(:caching_#{symbol}, args) #send_later sends_later when Rails.caching? otherwise sends_now
|
63
|
+
promise.delete('promise') unless Rails.caching?
|
64
|
+
end
|
65
|
+
promise.to_json
|
66
|
+
end
|
67
|
+
|
68
|
+
if promise['promise'] && opts[:no_backgrounding]
|
69
|
+
promise['result'] = self.send(:caching_#{symbol}, args)
|
70
|
+
promise.delete('promise')
|
71
|
+
json = promise.to_json
|
72
|
+
end
|
73
|
+
|
74
|
+
val = JSON.parse(json)
|
75
|
+
if val and !val['promise']
|
76
|
+
loading = false
|
77
|
+
soft_expiration = Time.parse(val['soft_expiration']) rescue nil
|
78
|
+
json_val = val
|
79
|
+
val = val['result']
|
80
|
+
else
|
81
|
+
val = promise
|
82
|
+
loading = true
|
83
|
+
end
|
84
|
+
|
85
|
+
if !loading and soft_expiration and Time.now > soft_expiration
|
86
|
+
expires = #{options[:soft_expiration]} + 10*60
|
87
|
+
json_val['soft_expiration'] = (Time.now - expires).to_s
|
88
|
+
Rails.cache.write(cache_key, json_val, :expires_in => expires)
|
89
|
+
self.send_later(:caching_#{symbol}, args)
|
90
|
+
end
|
91
|
+
|
92
|
+
PatellaResult.new val, loading
|
93
|
+
end
|
94
|
+
|
95
|
+
if private_method_defined?(#{original_method.inspect}) # if private_method_defined?(:_unmemoized_mime_type)
|
96
|
+
private #{symbol.inspect} # private :mime_type
|
97
|
+
elsif protected_method_defined?(#{original_method.inspect}) # elsif protected_method_defined?(:_unmemoized_mime_type)
|
98
|
+
protected #{symbol.inspect} # protected :mime_type
|
99
|
+
end # end
|
100
|
+
EOS
|
101
|
+
|
102
|
+
if is_class
|
103
|
+
(class << self; self; end).class_eval *method_definitions
|
104
|
+
else
|
105
|
+
class_eval *method_definitions
|
106
|
+
end
|
107
|
+
end
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
class PatellaResult < ActiveSupport::BasicObject
|
112
|
+
|
113
|
+
def initialize(target=nil, loading=false)
|
114
|
+
@target = target
|
115
|
+
@loading = loading
|
116
|
+
end
|
117
|
+
|
118
|
+
def loading?
|
119
|
+
@loading
|
120
|
+
end
|
121
|
+
|
122
|
+
def loaded?
|
123
|
+
!@loading
|
124
|
+
end
|
125
|
+
|
126
|
+
def method_missing(method, *args, &block)
|
127
|
+
@target.send(method, *args, &block)
|
128
|
+
end
|
129
|
+
end
|
130
|
+
|
@@ -0,0 +1,26 @@
|
|
1
|
+
module Patella::SendLater
|
2
|
+
|
3
|
+
def self.included(base)
|
4
|
+
base.extend ClassMethods
|
5
|
+
end
|
6
|
+
|
7
|
+
def send_later method_name, *args
|
8
|
+
#in dev mode, run the command in process
|
9
|
+
if Rails.caching?
|
10
|
+
Patella::SendLaterWorker.perform_later self.class.to_s, self.id, method_name, *args
|
11
|
+
else
|
12
|
+
self.send method_name, *args
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
module ClassMethods
|
17
|
+
def send_later method_name, *args
|
18
|
+
if Rails.caching?
|
19
|
+
Patella::SendLaterWorker.perform_later self.to_s, nil, method_name, *args
|
20
|
+
else
|
21
|
+
self.send method_name, *args
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
module Patella
|
2
|
+
class SendLaterWorker
|
3
|
+
@queue = :send_later
|
4
|
+
|
5
|
+
def self.perform_later *args
|
6
|
+
self.enqueue *args
|
7
|
+
end
|
8
|
+
|
9
|
+
def self.perform(class_name, instance_id, method_name, *args)
|
10
|
+
o = class_name.constantize
|
11
|
+
o = o.find_by_id instance_id if instance_id
|
12
|
+
|
13
|
+
o.send(method_name, *args)
|
14
|
+
rescue => e
|
15
|
+
raise e, "#{e.message} \nWith SendLater: #{class_name}#{':'+instance_id.to_s if instance_id}##{method_name}(#{args.map(&:to_s).join(',')})", e.backtrace
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
data/patella.gemspec
ADDED
@@ -0,0 +1,25 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
require File.expand_path('../lib/patella/version', __FILE__)
|
3
|
+
|
4
|
+
Gem::Specification.new do |gem|
|
5
|
+
gem.authors = ["Jeff Dwyer"]
|
6
|
+
gem.email = ["jdwyer@patientslikeme.com"]
|
7
|
+
gem.description = "It's Memoization into Memcached calculated in the background with Resque."
|
8
|
+
gem.summary = "DSL to add memoization on any method into Memcached calculated in the background with Resque."
|
9
|
+
gem.homepage = "https://github.com/patientslikeme/patella"
|
10
|
+
|
11
|
+
gem.files = `git ls-files`.split($\)
|
12
|
+
gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
|
13
|
+
gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
|
14
|
+
gem.name = "patella"
|
15
|
+
gem.require_paths = ["lib"]
|
16
|
+
gem.version = Patella::VERSION
|
17
|
+
gem.add_dependency 'resque', '~>1.16'
|
18
|
+
gem.add_dependency 'activesupport', '~>2.3' #, :require => 'active_support'
|
19
|
+
gem.add_dependency 'rails', '~>2.3' #, :require => 'active_support'
|
20
|
+
gem.add_dependency 'json'
|
21
|
+
gem.add_development_dependency 'bundler'
|
22
|
+
gem.add_development_dependency 'rake'
|
23
|
+
gem.add_development_dependency 'mocha', "~>0.9.8"
|
24
|
+
gem.add_development_dependency 'bourne'
|
25
|
+
end
|
@@ -0,0 +1,144 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
|
3
|
+
class Dummy
|
4
|
+
include Patella::Patella
|
5
|
+
include Patella::SendLater
|
6
|
+
|
7
|
+
attr_accessor :id
|
8
|
+
def initialize id
|
9
|
+
self.id = id
|
10
|
+
end
|
11
|
+
|
12
|
+
def self.find_by_id id
|
13
|
+
new id
|
14
|
+
end
|
15
|
+
|
16
|
+
def foo
|
17
|
+
5
|
18
|
+
end
|
19
|
+
patella_reflex :foo
|
20
|
+
|
21
|
+
def bar(a,b)
|
22
|
+
a+b
|
23
|
+
end
|
24
|
+
patella_reflex :bar
|
25
|
+
|
26
|
+
def baz(add)
|
27
|
+
self.id + add
|
28
|
+
end
|
29
|
+
patella_reflex :baz
|
30
|
+
|
31
|
+
def self.bing
|
32
|
+
3
|
33
|
+
end
|
34
|
+
patella_reflex :bing, :class_method => true
|
35
|
+
|
36
|
+
def no_background_add(a,b)
|
37
|
+
a + b
|
38
|
+
end
|
39
|
+
patella_reflex :no_background_add, :no_backgrounding => true
|
40
|
+
end
|
41
|
+
|
42
|
+
class PatellaTest < ActiveSupport::TestCase
|
43
|
+
|
44
|
+
def test_patella_basics
|
45
|
+
# foreground
|
46
|
+
f = Dummy.new 6
|
47
|
+
f.stubs :caching_foo => 5
|
48
|
+
assert_equal 5, f.foo
|
49
|
+
assert_received(f, :caching_foo)
|
50
|
+
|
51
|
+
#background
|
52
|
+
with_caching do
|
53
|
+
Patella::SendLaterWorker.stubs :perform_later => 'loading'
|
54
|
+
f1 = Dummy.new 5
|
55
|
+
assert f1.foo.loading?
|
56
|
+
assert_received(Patella::SendLaterWorker, :perform_later) do |ex|
|
57
|
+
ex.once
|
58
|
+
ex.with 'Dummy', f1.id, :caching_foo, []
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
def test_turning_off_background
|
64
|
+
#background
|
65
|
+
with_caching do
|
66
|
+
Patella::SendLaterWorker.expects(:perform_later).never
|
67
|
+
Dummy.any_instance.expects('caching_no_background_add').once.returns(9)
|
68
|
+
d = Dummy.new(1)
|
69
|
+
result = d.no_background_add(4,5)
|
70
|
+
assert result.loaded?
|
71
|
+
assert_equal 9, result
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
def cache_clearing
|
76
|
+
d = Dummy.new(1)
|
77
|
+
result = d.bar(4,5) #load it by turning caching off
|
78
|
+
assert result.loaded?
|
79
|
+
assert_equal 9, result
|
80
|
+
|
81
|
+
with_caching do
|
82
|
+
result = d.bar(4,5) #still here
|
83
|
+
assert result.loaded?
|
84
|
+
assert_equal 9, result
|
85
|
+
|
86
|
+
d.clear_bar(4,5)
|
87
|
+
result = d.bar(4,5) #cleared
|
88
|
+
assert result.loading?
|
89
|
+
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
def test_patella_for_instance_objs
|
94
|
+
four = Dummy.new 4
|
95
|
+
assert_equal(8, four.baz(4))
|
96
|
+
assert_equal(13, four.baz(9))
|
97
|
+
end
|
98
|
+
|
99
|
+
def test_patella_for_class_methods
|
100
|
+
assert Dummy.bing.loaded?
|
101
|
+
assert_equal(3, Dummy.bing)
|
102
|
+
end
|
103
|
+
|
104
|
+
def test_keys
|
105
|
+
d = Dummy.new 2
|
106
|
+
assert_equal "patella/Dummy/2/foo/#{md5 [].to_json}", d.patella_key(:foo,[])
|
107
|
+
assert_equal "patella/Dummy/2/foo/#{md5 [1].to_json}", d.patella_key(:foo,[1])
|
108
|
+
assert_equal "patella/Dummy/2/foo/#{md5 [1,3].to_json}", d.patella_key(:foo,[1,3])
|
109
|
+
assert_equal "patella/Dummy/2/foo/#{md5 [1,"asdf"].to_json}", d.patella_key(:foo,[1,"asdf"])
|
110
|
+
assert_equal "patella/Dummy/2/foo/#{md5 [1,"asdf"*1000].to_json}", d.patella_key(:foo,[1, ("asdf"*1000)])
|
111
|
+
|
112
|
+
d3 = Dummy.new 3
|
113
|
+
assert_equal "patella/Dummy/3/foo/#{md5 [].to_json}", d3.patella_key(:foo,[])
|
114
|
+
assert_equal "patella/Dummy/3/foo/#{md5 [1].to_json}", d3.patella_key(:foo,[1])
|
115
|
+
assert_equal "patella/Dummy/3/foo/#{md5 [1,3].to_json}", d3.patella_key(:foo,[1,3])
|
116
|
+
assert_equal "patella/Dummy/3/foo/#{md5 [1,"asdf" * 1000].to_json}", d3.patella_key(:foo,[1,"asdf" * 1000])
|
117
|
+
end
|
118
|
+
|
119
|
+
def test_soft_expiration
|
120
|
+
dummy = Dummy.new 1
|
121
|
+
::Rails.cache.stubs( :fetch => { 'result' => "loads of data", 'soft_expiration' => Time.now-1.minute }.to_json )
|
122
|
+
::Rails.cache.stubs( :write => true )
|
123
|
+
dummy.stubs :send_later => 'loading'
|
124
|
+
dummy.foo
|
125
|
+
assert_received(Rails.cache, :write) { |ex| ex.once }
|
126
|
+
assert_received(dummy, :send_later) { |ex| ex.once }
|
127
|
+
end
|
128
|
+
|
129
|
+
private
|
130
|
+
def md5(str)
|
131
|
+
Digest::MD5.hexdigest(str)
|
132
|
+
end
|
133
|
+
|
134
|
+
def with_caching(&block)
|
135
|
+
#previous_caching = ActionController::Base.perform_caching
|
136
|
+
begin
|
137
|
+
Rails.stubs(:caching? => true)
|
138
|
+
#ActionController::Base.perform_caching = true
|
139
|
+
yield
|
140
|
+
ensure
|
141
|
+
#ActionController::Base.perform_caching = previous_caching
|
142
|
+
end
|
143
|
+
end
|
144
|
+
end
|
data/test/test_helper.rb
ADDED
@@ -0,0 +1,34 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'bundler/setup'
|
3
|
+
|
4
|
+
Bundler.require
|
5
|
+
require 'patella'
|
6
|
+
require 'test/unit'
|
7
|
+
require 'active_support'
|
8
|
+
require 'active_support/test_case'
|
9
|
+
require 'json'
|
10
|
+
require 'mocha'
|
11
|
+
require 'bourne'
|
12
|
+
|
13
|
+
unless defined?(Rails)
|
14
|
+
module Rails
|
15
|
+
class MockCache
|
16
|
+
def fetch(*args)
|
17
|
+
yield
|
18
|
+
end
|
19
|
+
def write(*args)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
def self.caching=(value)
|
24
|
+
@caching=value
|
25
|
+
end
|
26
|
+
|
27
|
+
def self.caching?
|
28
|
+
@caching
|
29
|
+
end
|
30
|
+
def self.cache
|
31
|
+
@cache ||= MockCache.new
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
metadata
ADDED
@@ -0,0 +1,189 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: patella
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Jeff Dwyer
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2012-06-19 00:00:00.000000000 Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: resque
|
16
|
+
requirement: !ruby/object:Gem::Requirement
|
17
|
+
none: false
|
18
|
+
requirements:
|
19
|
+
- - ~>
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: '1.16'
|
22
|
+
type: :runtime
|
23
|
+
prerelease: false
|
24
|
+
version_requirements: !ruby/object:Gem::Requirement
|
25
|
+
none: false
|
26
|
+
requirements:
|
27
|
+
- - ~>
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
version: '1.16'
|
30
|
+
- !ruby/object:Gem::Dependency
|
31
|
+
name: activesupport
|
32
|
+
requirement: !ruby/object:Gem::Requirement
|
33
|
+
none: false
|
34
|
+
requirements:
|
35
|
+
- - ~>
|
36
|
+
- !ruby/object:Gem::Version
|
37
|
+
version: '2.3'
|
38
|
+
type: :runtime
|
39
|
+
prerelease: false
|
40
|
+
version_requirements: !ruby/object:Gem::Requirement
|
41
|
+
none: false
|
42
|
+
requirements:
|
43
|
+
- - ~>
|
44
|
+
- !ruby/object:Gem::Version
|
45
|
+
version: '2.3'
|
46
|
+
- !ruby/object:Gem::Dependency
|
47
|
+
name: rails
|
48
|
+
requirement: !ruby/object:Gem::Requirement
|
49
|
+
none: false
|
50
|
+
requirements:
|
51
|
+
- - ~>
|
52
|
+
- !ruby/object:Gem::Version
|
53
|
+
version: '2.3'
|
54
|
+
type: :runtime
|
55
|
+
prerelease: false
|
56
|
+
version_requirements: !ruby/object:Gem::Requirement
|
57
|
+
none: false
|
58
|
+
requirements:
|
59
|
+
- - ~>
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '2.3'
|
62
|
+
- !ruby/object:Gem::Dependency
|
63
|
+
name: json
|
64
|
+
requirement: !ruby/object:Gem::Requirement
|
65
|
+
none: false
|
66
|
+
requirements:
|
67
|
+
- - ! '>='
|
68
|
+
- !ruby/object:Gem::Version
|
69
|
+
version: '0'
|
70
|
+
type: :runtime
|
71
|
+
prerelease: false
|
72
|
+
version_requirements: !ruby/object:Gem::Requirement
|
73
|
+
none: false
|
74
|
+
requirements:
|
75
|
+
- - ! '>='
|
76
|
+
- !ruby/object:Gem::Version
|
77
|
+
version: '0'
|
78
|
+
- !ruby/object:Gem::Dependency
|
79
|
+
name: bundler
|
80
|
+
requirement: !ruby/object:Gem::Requirement
|
81
|
+
none: false
|
82
|
+
requirements:
|
83
|
+
- - ! '>='
|
84
|
+
- !ruby/object:Gem::Version
|
85
|
+
version: '0'
|
86
|
+
type: :development
|
87
|
+
prerelease: false
|
88
|
+
version_requirements: !ruby/object:Gem::Requirement
|
89
|
+
none: false
|
90
|
+
requirements:
|
91
|
+
- - ! '>='
|
92
|
+
- !ruby/object:Gem::Version
|
93
|
+
version: '0'
|
94
|
+
- !ruby/object:Gem::Dependency
|
95
|
+
name: rake
|
96
|
+
requirement: !ruby/object:Gem::Requirement
|
97
|
+
none: false
|
98
|
+
requirements:
|
99
|
+
- - ! '>='
|
100
|
+
- !ruby/object:Gem::Version
|
101
|
+
version: '0'
|
102
|
+
type: :development
|
103
|
+
prerelease: false
|
104
|
+
version_requirements: !ruby/object:Gem::Requirement
|
105
|
+
none: false
|
106
|
+
requirements:
|
107
|
+
- - ! '>='
|
108
|
+
- !ruby/object:Gem::Version
|
109
|
+
version: '0'
|
110
|
+
- !ruby/object:Gem::Dependency
|
111
|
+
name: mocha
|
112
|
+
requirement: !ruby/object:Gem::Requirement
|
113
|
+
none: false
|
114
|
+
requirements:
|
115
|
+
- - ~>
|
116
|
+
- !ruby/object:Gem::Version
|
117
|
+
version: 0.9.8
|
118
|
+
type: :development
|
119
|
+
prerelease: false
|
120
|
+
version_requirements: !ruby/object:Gem::Requirement
|
121
|
+
none: false
|
122
|
+
requirements:
|
123
|
+
- - ~>
|
124
|
+
- !ruby/object:Gem::Version
|
125
|
+
version: 0.9.8
|
126
|
+
- !ruby/object:Gem::Dependency
|
127
|
+
name: bourne
|
128
|
+
requirement: !ruby/object:Gem::Requirement
|
129
|
+
none: false
|
130
|
+
requirements:
|
131
|
+
- - ! '>='
|
132
|
+
- !ruby/object:Gem::Version
|
133
|
+
version: '0'
|
134
|
+
type: :development
|
135
|
+
prerelease: false
|
136
|
+
version_requirements: !ruby/object:Gem::Requirement
|
137
|
+
none: false
|
138
|
+
requirements:
|
139
|
+
- - ! '>='
|
140
|
+
- !ruby/object:Gem::Version
|
141
|
+
version: '0'
|
142
|
+
description: It's Memoization into Memcached calculated in the background with Resque.
|
143
|
+
email:
|
144
|
+
- jdwyer@patientslikeme.com
|
145
|
+
executables: []
|
146
|
+
extensions: []
|
147
|
+
extra_rdoc_files: []
|
148
|
+
files:
|
149
|
+
- .gitignore
|
150
|
+
- Gemfile
|
151
|
+
- LICENSE
|
152
|
+
- README.md
|
153
|
+
- Rakefile
|
154
|
+
- lib/patella.rb
|
155
|
+
- lib/patella/patella.rb
|
156
|
+
- lib/patella/send_later.rb
|
157
|
+
- lib/patella/send_later_worker.rb
|
158
|
+
- lib/patella/version.rb
|
159
|
+
- patella.gemspec
|
160
|
+
- test/patella_test.rb
|
161
|
+
- test/test_helper.rb
|
162
|
+
homepage: https://github.com/patientslikeme/patella
|
163
|
+
licenses: []
|
164
|
+
post_install_message:
|
165
|
+
rdoc_options: []
|
166
|
+
require_paths:
|
167
|
+
- lib
|
168
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
169
|
+
none: false
|
170
|
+
requirements:
|
171
|
+
- - ! '>='
|
172
|
+
- !ruby/object:Gem::Version
|
173
|
+
version: '0'
|
174
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
175
|
+
none: false
|
176
|
+
requirements:
|
177
|
+
- - ! '>='
|
178
|
+
- !ruby/object:Gem::Version
|
179
|
+
version: '0'
|
180
|
+
requirements: []
|
181
|
+
rubyforge_project:
|
182
|
+
rubygems_version: 1.8.24
|
183
|
+
signing_key:
|
184
|
+
specification_version: 3
|
185
|
+
summary: DSL to add memoization on any method into Memcached calculated in the background
|
186
|
+
with Resque.
|
187
|
+
test_files:
|
188
|
+
- test/patella_test.rb
|
189
|
+
- test/test_helper.rb
|