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