cache-object 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.
- checksums.yaml +7 -0
- data/.gitignore +22 -0
- data/.rspec +2 -0
- data/Gemfile +4 -0
- data/Guardfile +6 -0
- data/LICENSE.txt +22 -0
- data/README.md +29 -0
- data/Rakefile +2 -0
- data/bin/bundler +16 -0
- data/bin/coderay +16 -0
- data/bin/guard +16 -0
- data/bin/htmldiff +16 -0
- data/bin/ldiff +16 -0
- data/bin/listen +16 -0
- data/bin/pry +16 -0
- data/bin/rake +16 -0
- data/bin/rspec +16 -0
- data/bin/thor +16 -0
- data/cache-object.gemspec +31 -0
- data/lib/cache/object.rb +22 -0
- data/lib/cache/object/active_record.rb +57 -0
- data/lib/cache/object/adapter.rb +42 -0
- data/lib/cache/object/config.rb +16 -0
- data/lib/cache/object/instance_decorator.rb +26 -0
- data/lib/cache/object/key_generator.rb +16 -0
- data/lib/cache/object/version.rb +5 -0
- data/spec/cache/object/active_record_spec.rb +129 -0
- data/spec/cache/object/adapter_spec.rb +48 -0
- data/spec/cache/object/config_spec.rb +39 -0
- data/spec/cache/object/instance_decorator_spec.rb +19 -0
- data/spec/features/cache_object_spec.rb +77 -0
- data/spec/spec_helper.rb +15 -0
- data/spec/support/models.rb +50 -0
- metadata +182 -0
checksums.yaml
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
---
|
|
2
|
+
SHA1:
|
|
3
|
+
metadata.gz: 7c71004d93363e44c125a123d5657588af9a31da
|
|
4
|
+
data.tar.gz: 41b528c9d8fe6cf225ba47a1e68b8845936f9297
|
|
5
|
+
SHA512:
|
|
6
|
+
metadata.gz: 97ec547f05417382920b39bc0a4760d265f429e6825afc5ec0cb956bfc79ba76616d8c74937ee85072a2dc4e154bfb6e9ad48b5f46c4915a9ea902c49ee624df
|
|
7
|
+
data.tar.gz: 6d7a334cfe0d6481369553a3ee7aec038e9f00c84b2ef20cfb346891fa6e1d56d85940561eab4496e77d58b670a718d008cb9edad1ac14a9d5b6c681f84368ae
|
data/.gitignore
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
*.gem
|
|
2
|
+
*.rbc
|
|
3
|
+
.bundle
|
|
4
|
+
.config
|
|
5
|
+
.yardoc
|
|
6
|
+
Gemfile.lock
|
|
7
|
+
InstalledFiles
|
|
8
|
+
_yardoc
|
|
9
|
+
coverage
|
|
10
|
+
doc/
|
|
11
|
+
lib/bundler/man
|
|
12
|
+
pkg
|
|
13
|
+
rdoc
|
|
14
|
+
spec/reports
|
|
15
|
+
test/tmp
|
|
16
|
+
test/version_tmp
|
|
17
|
+
tmp
|
|
18
|
+
*.bundle
|
|
19
|
+
*.so
|
|
20
|
+
*.o
|
|
21
|
+
*.a
|
|
22
|
+
mkmf.log
|
data/.rspec
ADDED
data/Gemfile
ADDED
data/Guardfile
ADDED
data/LICENSE.txt
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
Copyright (c) 2014 Server Cimen & Siyamed Sinir
|
|
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,29 @@
|
|
|
1
|
+
# Cache::Object
|
|
2
|
+
|
|
3
|
+
TODO: Write a gem description
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
Add this line to your application's Gemfile:
|
|
8
|
+
|
|
9
|
+
gem 'cache-object'
|
|
10
|
+
|
|
11
|
+
And then execute:
|
|
12
|
+
|
|
13
|
+
$ bundle
|
|
14
|
+
|
|
15
|
+
Or install it yourself as:
|
|
16
|
+
|
|
17
|
+
$ gem install cache-object
|
|
18
|
+
|
|
19
|
+
## Usage
|
|
20
|
+
|
|
21
|
+
TODO: Write usage instructions here
|
|
22
|
+
|
|
23
|
+
## Contributing
|
|
24
|
+
|
|
25
|
+
1. Fork it ( https://github.com/[my-github-username]/cache-object/fork )
|
|
26
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
|
27
|
+
3. Commit your changes (`git commit -am 'Add some feature'`)
|
|
28
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
|
29
|
+
5. Create a new Pull Request
|
data/Rakefile
ADDED
data/bin/bundler
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
#!/usr/bin/env ruby
|
|
2
|
+
#
|
|
3
|
+
# This file was generated by Bundler.
|
|
4
|
+
#
|
|
5
|
+
# The application 'bundler' is installed as part of a gem, and
|
|
6
|
+
# this file is here to facilitate running it.
|
|
7
|
+
#
|
|
8
|
+
|
|
9
|
+
require 'pathname'
|
|
10
|
+
ENV['BUNDLE_GEMFILE'] ||= File.expand_path("../../Gemfile",
|
|
11
|
+
Pathname.new(__FILE__).realpath)
|
|
12
|
+
|
|
13
|
+
require 'rubygems'
|
|
14
|
+
require 'bundler/setup'
|
|
15
|
+
|
|
16
|
+
load Gem.bin_path('bundler', 'bundler')
|
data/bin/coderay
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
#!/usr/bin/env ruby
|
|
2
|
+
#
|
|
3
|
+
# This file was generated by Bundler.
|
|
4
|
+
#
|
|
5
|
+
# The application 'coderay' is installed as part of a gem, and
|
|
6
|
+
# this file is here to facilitate running it.
|
|
7
|
+
#
|
|
8
|
+
|
|
9
|
+
require 'pathname'
|
|
10
|
+
ENV['BUNDLE_GEMFILE'] ||= File.expand_path("../../Gemfile",
|
|
11
|
+
Pathname.new(__FILE__).realpath)
|
|
12
|
+
|
|
13
|
+
require 'rubygems'
|
|
14
|
+
require 'bundler/setup'
|
|
15
|
+
|
|
16
|
+
load Gem.bin_path('coderay', 'coderay')
|
data/bin/guard
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
#!/usr/bin/env ruby
|
|
2
|
+
#
|
|
3
|
+
# This file was generated by Bundler.
|
|
4
|
+
#
|
|
5
|
+
# The application 'guard' is installed as part of a gem, and
|
|
6
|
+
# this file is here to facilitate running it.
|
|
7
|
+
#
|
|
8
|
+
|
|
9
|
+
require 'pathname'
|
|
10
|
+
ENV['BUNDLE_GEMFILE'] ||= File.expand_path("../../Gemfile",
|
|
11
|
+
Pathname.new(__FILE__).realpath)
|
|
12
|
+
|
|
13
|
+
require 'rubygems'
|
|
14
|
+
require 'bundler/setup'
|
|
15
|
+
|
|
16
|
+
load Gem.bin_path('guard', 'guard')
|
data/bin/htmldiff
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
#!/usr/bin/env ruby
|
|
2
|
+
#
|
|
3
|
+
# This file was generated by Bundler.
|
|
4
|
+
#
|
|
5
|
+
# The application 'htmldiff' is installed as part of a gem, and
|
|
6
|
+
# this file is here to facilitate running it.
|
|
7
|
+
#
|
|
8
|
+
|
|
9
|
+
require 'pathname'
|
|
10
|
+
ENV['BUNDLE_GEMFILE'] ||= File.expand_path("../../Gemfile",
|
|
11
|
+
Pathname.new(__FILE__).realpath)
|
|
12
|
+
|
|
13
|
+
require 'rubygems'
|
|
14
|
+
require 'bundler/setup'
|
|
15
|
+
|
|
16
|
+
load Gem.bin_path('diff-lcs', 'htmldiff')
|
data/bin/ldiff
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
#!/usr/bin/env ruby
|
|
2
|
+
#
|
|
3
|
+
# This file was generated by Bundler.
|
|
4
|
+
#
|
|
5
|
+
# The application 'ldiff' is installed as part of a gem, and
|
|
6
|
+
# this file is here to facilitate running it.
|
|
7
|
+
#
|
|
8
|
+
|
|
9
|
+
require 'pathname'
|
|
10
|
+
ENV['BUNDLE_GEMFILE'] ||= File.expand_path("../../Gemfile",
|
|
11
|
+
Pathname.new(__FILE__).realpath)
|
|
12
|
+
|
|
13
|
+
require 'rubygems'
|
|
14
|
+
require 'bundler/setup'
|
|
15
|
+
|
|
16
|
+
load Gem.bin_path('diff-lcs', 'ldiff')
|
data/bin/listen
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
#!/usr/bin/env ruby
|
|
2
|
+
#
|
|
3
|
+
# This file was generated by Bundler.
|
|
4
|
+
#
|
|
5
|
+
# The application 'listen' is installed as part of a gem, and
|
|
6
|
+
# this file is here to facilitate running it.
|
|
7
|
+
#
|
|
8
|
+
|
|
9
|
+
require 'pathname'
|
|
10
|
+
ENV['BUNDLE_GEMFILE'] ||= File.expand_path("../../Gemfile",
|
|
11
|
+
Pathname.new(__FILE__).realpath)
|
|
12
|
+
|
|
13
|
+
require 'rubygems'
|
|
14
|
+
require 'bundler/setup'
|
|
15
|
+
|
|
16
|
+
load Gem.bin_path('listen', 'listen')
|
data/bin/pry
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
#!/usr/bin/env ruby
|
|
2
|
+
#
|
|
3
|
+
# This file was generated by Bundler.
|
|
4
|
+
#
|
|
5
|
+
# The application 'pry' is installed as part of a gem, and
|
|
6
|
+
# this file is here to facilitate running it.
|
|
7
|
+
#
|
|
8
|
+
|
|
9
|
+
require 'pathname'
|
|
10
|
+
ENV['BUNDLE_GEMFILE'] ||= File.expand_path("../../Gemfile",
|
|
11
|
+
Pathname.new(__FILE__).realpath)
|
|
12
|
+
|
|
13
|
+
require 'rubygems'
|
|
14
|
+
require 'bundler/setup'
|
|
15
|
+
|
|
16
|
+
load Gem.bin_path('pry', 'pry')
|
data/bin/rake
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
#!/usr/bin/env ruby
|
|
2
|
+
#
|
|
3
|
+
# This file was generated by Bundler.
|
|
4
|
+
#
|
|
5
|
+
# The application 'rake' is installed as part of a gem, and
|
|
6
|
+
# this file is here to facilitate running it.
|
|
7
|
+
#
|
|
8
|
+
|
|
9
|
+
require 'pathname'
|
|
10
|
+
ENV['BUNDLE_GEMFILE'] ||= File.expand_path("../../Gemfile",
|
|
11
|
+
Pathname.new(__FILE__).realpath)
|
|
12
|
+
|
|
13
|
+
require 'rubygems'
|
|
14
|
+
require 'bundler/setup'
|
|
15
|
+
|
|
16
|
+
load Gem.bin_path('rake', 'rake')
|
data/bin/rspec
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
#!/usr/bin/env ruby
|
|
2
|
+
#
|
|
3
|
+
# This file was generated by Bundler.
|
|
4
|
+
#
|
|
5
|
+
# The application 'rspec' is installed as part of a gem, and
|
|
6
|
+
# this file is here to facilitate running it.
|
|
7
|
+
#
|
|
8
|
+
|
|
9
|
+
require 'pathname'
|
|
10
|
+
ENV['BUNDLE_GEMFILE'] ||= File.expand_path("../../Gemfile",
|
|
11
|
+
Pathname.new(__FILE__).realpath)
|
|
12
|
+
|
|
13
|
+
require 'rubygems'
|
|
14
|
+
require 'bundler/setup'
|
|
15
|
+
|
|
16
|
+
load Gem.bin_path('rspec-core', 'rspec')
|
data/bin/thor
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
#!/usr/bin/env ruby
|
|
2
|
+
#
|
|
3
|
+
# This file was generated by Bundler.
|
|
4
|
+
#
|
|
5
|
+
# The application 'thor' is installed as part of a gem, and
|
|
6
|
+
# this file is here to facilitate running it.
|
|
7
|
+
#
|
|
8
|
+
|
|
9
|
+
require 'pathname'
|
|
10
|
+
ENV['BUNDLE_GEMFILE'] ||= File.expand_path("../../Gemfile",
|
|
11
|
+
Pathname.new(__FILE__).realpath)
|
|
12
|
+
|
|
13
|
+
require 'rubygems'
|
|
14
|
+
require 'bundler/setup'
|
|
15
|
+
|
|
16
|
+
load Gem.bin_path('thor', 'thor')
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
# coding: utf-8
|
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
|
4
|
+
require 'cache/object/version'
|
|
5
|
+
|
|
6
|
+
Gem::Specification.new do |spec|
|
|
7
|
+
spec.name = "cache-object"
|
|
8
|
+
spec.version = Cache::Object::VERSION
|
|
9
|
+
spec.authors = ["Matt Camuto"]
|
|
10
|
+
spec.email = ["dev@wanelo.com"]
|
|
11
|
+
spec.summary = %q{Object R/W Caching}
|
|
12
|
+
spec.description = %q{Object R/W Caching on top of ActiveRecord}
|
|
13
|
+
spec.homepage = ""
|
|
14
|
+
spec.license = "MIT"
|
|
15
|
+
|
|
16
|
+
spec.files = `git ls-files -z`.split("\x0")
|
|
17
|
+
# spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
|
18
|
+
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
|
19
|
+
spec.require_paths = ["lib"]
|
|
20
|
+
|
|
21
|
+
spec.add_dependency "activerecord", ">= 3.0"
|
|
22
|
+
|
|
23
|
+
spec.add_development_dependency "bundler", "~> 1.6"
|
|
24
|
+
spec.add_development_dependency "rake"
|
|
25
|
+
|
|
26
|
+
spec.add_development_dependency "rspec", ">= 3.0"
|
|
27
|
+
spec.add_development_dependency "guard"
|
|
28
|
+
spec.add_development_dependency "guard-rspec"
|
|
29
|
+
spec.add_development_dependency "sqlite3"
|
|
30
|
+
|
|
31
|
+
end
|
data/lib/cache/object.rb
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
require 'cache/object/version'
|
|
2
|
+
require 'cache/object/config'
|
|
3
|
+
require 'cache/object/active_record'
|
|
4
|
+
require 'cache/object/adapter'
|
|
5
|
+
require 'cache/object/key_generator'
|
|
6
|
+
|
|
7
|
+
module Cache
|
|
8
|
+
module Object
|
|
9
|
+
# Your code goes here...
|
|
10
|
+
def self.configure
|
|
11
|
+
yield configuration
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def self.configuration
|
|
15
|
+
@configuration ||= Cache::Object::Config.new
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def self.adapter
|
|
19
|
+
@adapter ||= configuration.adapter
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
end
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
require 'cache/object/instance_decorator'
|
|
2
|
+
|
|
3
|
+
module Cache
|
|
4
|
+
module Object
|
|
5
|
+
module ActiveRecord
|
|
6
|
+
def self.included(base)
|
|
7
|
+
base.instance_eval do
|
|
8
|
+
extend ClassMethods
|
|
9
|
+
|
|
10
|
+
def _object_cache_attr_mappings
|
|
11
|
+
@_object_cache_attr_mappings ||= []
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
after_destroy :expire_cache!
|
|
15
|
+
after_save :write_cache!
|
|
16
|
+
after_rollback :expire_cache!
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def write_cache!
|
|
21
|
+
Cache::Object.adapter.write(_cache_object_decorator)
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def expire_cache!
|
|
25
|
+
Cache::Object.adapter.delete(_cache_object_decorator)
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def _cache_object_decorator
|
|
29
|
+
Cache::Object::InstanceDecorator.new(self, self.class._object_cache_attr_mappings)
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
module ClassMethods
|
|
33
|
+
def find(*args)
|
|
34
|
+
Cache::Object.adapter.fetch(self, *args[0]) do
|
|
35
|
+
super(*args)
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def find_by_id(id)
|
|
40
|
+
Cache::Object.adapter.fetch(self, id) do
|
|
41
|
+
where(self.primary_key => id).first
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def object_cache_on(*attrs)
|
|
46
|
+
self._object_cache_attr_mappings << attrs
|
|
47
|
+
define_singleton_method("find_by_#{attrs.join('_and_')}") do |*args|
|
|
48
|
+
attributes = Hash[attrs.zip(args)]
|
|
49
|
+
Cache::Object.adapter.fetch_mapping(self, attributes) do
|
|
50
|
+
super(*args)
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
end
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
module Cache
|
|
2
|
+
module Object
|
|
3
|
+
class Adapter
|
|
4
|
+
attr_reader :store
|
|
5
|
+
|
|
6
|
+
def initialize(store)
|
|
7
|
+
raise "Cache Store is nil" unless store
|
|
8
|
+
@store = store
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
def write(decorator)
|
|
12
|
+
decorator.keys.each do |key|
|
|
13
|
+
store.write(key, decorator.instance, expires_in: ttl)
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def delete(decorator)
|
|
18
|
+
decorator.keys.each do |key|
|
|
19
|
+
store.delete(key)
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def fetch(klass, id, &block)
|
|
24
|
+
store.fetch(KeyGenerator.key_for_object(klass.name, id),
|
|
25
|
+
expires_in: ttl,
|
|
26
|
+
&block)
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def fetch_mapping(klass, attributes, &block)
|
|
30
|
+
store.fetch(KeyGenerator.key_for_mapping(klass.name, attributes),
|
|
31
|
+
expires_in: ttl,
|
|
32
|
+
&block)
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
private
|
|
36
|
+
|
|
37
|
+
def ttl
|
|
38
|
+
Cache::Object.configuration.ttl
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
end
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
module Cache
|
|
2
|
+
module Object
|
|
3
|
+
class Config
|
|
4
|
+
attr_accessor :enabled, :ttl, :adapter, :cache
|
|
5
|
+
|
|
6
|
+
def initialize
|
|
7
|
+
self.enabled = true
|
|
8
|
+
self.ttl = 86400
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
def adapter
|
|
12
|
+
@adapter ||= Cache::Object::Adapter.new(cache)
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
end
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
module Cache
|
|
2
|
+
module Object
|
|
3
|
+
class InstanceDecorator < Struct.new(:instance, :mappings)
|
|
4
|
+
def keys
|
|
5
|
+
[canonical_cache_key] + mapping_cache_keys
|
|
6
|
+
end
|
|
7
|
+
|
|
8
|
+
private
|
|
9
|
+
|
|
10
|
+
def mapping_cache_keys
|
|
11
|
+
mappings.map do |mapping|
|
|
12
|
+
attributes = mapping.inject({}) do |memo, attr|
|
|
13
|
+
memo[attr] = instance.send(attr)
|
|
14
|
+
memo
|
|
15
|
+
end
|
|
16
|
+
KeyGenerator.key_for_mapping(instance.class.name, attributes)
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def canonical_cache_key
|
|
21
|
+
KeyGenerator.key_for_object(instance.class.name, instance.id)
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
module Cache
|
|
2
|
+
module Object
|
|
3
|
+
class KeyGenerator
|
|
4
|
+
def self.key_for_object(class_name, id)
|
|
5
|
+
"#{class_name}-#{id}"
|
|
6
|
+
end
|
|
7
|
+
|
|
8
|
+
def self.key_for_mapping(class_name, attributes)
|
|
9
|
+
attributes_key = attributes.map do |k, v|
|
|
10
|
+
"#{k}-#{v}"
|
|
11
|
+
end.join('-')
|
|
12
|
+
"#{class_name}-#{attributes_key}"
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
end
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
require 'spec_helper'
|
|
2
|
+
|
|
3
|
+
RSpec.describe Cache::Object::ActiveRecord do
|
|
4
|
+
|
|
5
|
+
class FakeActiveRecord
|
|
6
|
+
def self.after_save(method)
|
|
7
|
+
;
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
def self.after_rollback(method)
|
|
11
|
+
;
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def self.after_destroy(method)
|
|
15
|
+
;
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def self.find(id)
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def self.find_by_name_and_age(name, age)
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
class Model < FakeActiveRecord
|
|
26
|
+
include Cache::Object::ActiveRecord
|
|
27
|
+
|
|
28
|
+
object_cache_on :name, :age
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
let(:super_clazz) { FakeActiveRecord }
|
|
32
|
+
let(:clazz) { Model }
|
|
33
|
+
|
|
34
|
+
let(:adapter) do
|
|
35
|
+
Class.new do
|
|
36
|
+
def fetch(clazz, id)
|
|
37
|
+
yield
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def fetch_mapping(klass, attributes)
|
|
41
|
+
yield
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
let(:adapter_instance) { adapter.new }
|
|
47
|
+
|
|
48
|
+
before do
|
|
49
|
+
allow(Cache::Object).to receive(:adapter).and_return(adapter_instance)
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
describe ".included" do
|
|
53
|
+
it "receives correct callbacks" do
|
|
54
|
+
expect(clazz).to receive(:after_save).with(:write_cache!).once
|
|
55
|
+
expect(clazz).to receive(:after_rollback).with(:expire_cache!).once
|
|
56
|
+
expect(clazz).to receive(:after_destroy).with(:expire_cache!).once
|
|
57
|
+
clazz.send(:include, Cache::Object::ActiveRecord)
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
describe "caching methods" do
|
|
63
|
+
describe "#write_cache" do
|
|
64
|
+
it "calls write cache" do
|
|
65
|
+
expect(adapter_instance).to receive(:write).with(an_instance_of(Cache::Object::InstanceDecorator))
|
|
66
|
+
object = clazz.new
|
|
67
|
+
object.write_cache!
|
|
68
|
+
end
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
describe "#expire_cache" do
|
|
73
|
+
it "calls write cache" do
|
|
74
|
+
expect(adapter_instance).to receive(:delete).with(an_instance_of(Cache::Object::InstanceDecorator))
|
|
75
|
+
object = clazz.new
|
|
76
|
+
object.expire_cache!
|
|
77
|
+
end
|
|
78
|
+
end
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
describe "Finder class methods" do
|
|
82
|
+
describe "#respond_to?" do
|
|
83
|
+
it "find" do
|
|
84
|
+
expect(clazz).to respond_to(:find)
|
|
85
|
+
end
|
|
86
|
+
it "find_by_id" do
|
|
87
|
+
expect(clazz).to respond_to(:find_by_id)
|
|
88
|
+
end
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
describe ".find" do
|
|
92
|
+
describe "caching interactions" do
|
|
93
|
+
it "yields to super with cache" do
|
|
94
|
+
allow(Cache::Object).to receive(:adapter).and_return(adapter.new)
|
|
95
|
+
expect(super_clazz).to receive(:find).with(12).once
|
|
96
|
+
clazz.find(12)
|
|
97
|
+
end
|
|
98
|
+
end
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
describe ".find_by_id" do
|
|
102
|
+
describe "caching interactions" do
|
|
103
|
+
it "yields to super with cache" do
|
|
104
|
+
expect(super_clazz).to receive(:primary_key) { "primary_key" }
|
|
105
|
+
expect(super_clazz).to receive(:where).with("primary_key" => 12).once { double(first: true) }
|
|
106
|
+
clazz.find_by_id(12)
|
|
107
|
+
end
|
|
108
|
+
end
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
describe "object_cache_on" do
|
|
112
|
+
|
|
113
|
+
it "creates_finder_methods" do
|
|
114
|
+
expect(clazz).to respond_to(:find_by_name_and_age)
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
it "calls fetch_mapping on the adapter" do
|
|
118
|
+
expect(adapter_instance).to receive(:fetch_mapping).with(clazz, name: "bob", age: 13).once
|
|
119
|
+
clazz.find_by_name_and_age("bob", 13)
|
|
120
|
+
end
|
|
121
|
+
|
|
122
|
+
it "calls super" do
|
|
123
|
+
expect(super_clazz).to receive(:find_by_name_and_age).with("bob", 13)
|
|
124
|
+
clazz.find_by_name_and_age("bob", 13)
|
|
125
|
+
end
|
|
126
|
+
end
|
|
127
|
+
end
|
|
128
|
+
end
|
|
129
|
+
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
require 'spec_helper'
|
|
2
|
+
|
|
3
|
+
RSpec.describe Cache::Object::Adapter do
|
|
4
|
+
|
|
5
|
+
before do
|
|
6
|
+
allow(Cache::Object.configuration).to receive(:ttl).and_return(118)
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
let(:cache_store) { double("CacheStore", write: true) }
|
|
10
|
+
let(:adapter) { Cache::Object::Adapter.new(cache_store) }
|
|
11
|
+
let(:instance) { double(class: double(name: "User")) }
|
|
12
|
+
|
|
13
|
+
describe "#delete" do
|
|
14
|
+
it "dispatches to delete" do
|
|
15
|
+
expect(cache_store).to receive(:delete).with("User-1")
|
|
16
|
+
expect(cache_store).to receive(:delete).with("User-1-blah")
|
|
17
|
+
|
|
18
|
+
adapter.delete(double(instance: instance, keys: ["User-1", "User-1-blah"]))
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
describe "#write" do
|
|
23
|
+
it "dispatches to write" do
|
|
24
|
+
expect(cache_store).to receive(:write).with("User-1", instance, expires_in: 118)
|
|
25
|
+
expect(cache_store).to receive(:write).with("User-1-blah", instance, expires_in: 118)
|
|
26
|
+
|
|
27
|
+
adapter.write(double(instance: instance, keys: ["User-1", "User-1-blah"]))
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
describe "#fetch" do
|
|
32
|
+
it "fetches the object from the cache_store" do
|
|
33
|
+
expect(cache_store).to receive(:fetch).with("User-1", expires_in: 118).and_yield
|
|
34
|
+
expect { |b|
|
|
35
|
+
adapter.fetch(instance.class, 1, &b)
|
|
36
|
+
}.to yield_control
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
describe "#fetch_mapping" do
|
|
41
|
+
it "fetches the object from the cache store based on the attributes" do
|
|
42
|
+
expect(cache_store).to receive(:fetch).with("User-user_id-1-name-bob", expires_in: 118).and_yield
|
|
43
|
+
expect { |b|
|
|
44
|
+
adapter.fetch_mapping(instance.class, { user_id: 1, name: "bob" }, &b)
|
|
45
|
+
}.to yield_control
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
end
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
require 'spec_helper'
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
RSpec.describe Cache::Object do
|
|
5
|
+
|
|
6
|
+
describe ".configure" do
|
|
7
|
+
let(:clazz) { Class.new }
|
|
8
|
+
|
|
9
|
+
describe "#cache" do
|
|
10
|
+
it "sets cache" do
|
|
11
|
+
Cache::Object.configure { |c| c.cache = clazz }
|
|
12
|
+
expect(Cache::Object.configuration.cache).to eq(clazz)
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
describe "#enabled" do
|
|
17
|
+
it "is enabled by default" do
|
|
18
|
+
expect(Cache::Object.configuration.enabled).to eq(true)
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
it "is disabled if set false" do
|
|
23
|
+
Cache::Object.configure { |c| c.enabled = false }
|
|
24
|
+
expect(Cache::Object.configuration.enabled).to eq(false)
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
describe "#ttl" do
|
|
29
|
+
it "sets to time" do
|
|
30
|
+
Cache::Object.configure { |c| c.ttl = 1234 }
|
|
31
|
+
expect(Cache::Object.configuration.ttl).to eq(1234)
|
|
32
|
+
end
|
|
33
|
+
it "is one day by default" do
|
|
34
|
+
expect(Cache::Object.configuration.ttl).to eq(86400)
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
end
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
require 'spec_helper'
|
|
2
|
+
|
|
3
|
+
RSpec.describe Cache::Object::InstanceDecorator do
|
|
4
|
+
let(:decorator) { Cache::Object::InstanceDecorator.new(instance, mappings) }
|
|
5
|
+
|
|
6
|
+
describe '#keys' do
|
|
7
|
+
let(:instance) { double(id: 1, user_id: 11, sex: 'male', class: double(name: 'User')) }
|
|
8
|
+
let(:mappings) { [[:user_id] , [:user_id, :sex]] }
|
|
9
|
+
|
|
10
|
+
it 'includes the keys based on the mappings' do
|
|
11
|
+
expect(decorator.keys).to include 'User-user_id-11'
|
|
12
|
+
expect(decorator.keys).to include 'User-user_id-11-sex-male'
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
it 'includes the canonical id key' do
|
|
16
|
+
expect(decorator.keys).to include 'User-1'
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
end
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
require 'spec_helper'
|
|
2
|
+
require 'support/models'
|
|
3
|
+
|
|
4
|
+
# Only writing the features before all the units to see the semantics of
|
|
5
|
+
# using with actual AR finder methods
|
|
6
|
+
RSpec.describe "Caching" do
|
|
7
|
+
|
|
8
|
+
before do
|
|
9
|
+
CreateModelsForTest.migrate(:up)
|
|
10
|
+
Cache::Object.configure do |c|
|
|
11
|
+
c.cache = ::ActiveSupport::Cache::MemoryStore.new
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
after do
|
|
16
|
+
CreateModelsForTest.migrate(:down)
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
let!(:user) { User.create(age: 13, name: "Bob") }
|
|
20
|
+
|
|
21
|
+
describe "#find" do
|
|
22
|
+
it "finds the object from the cache" do
|
|
23
|
+
expect {
|
|
24
|
+
expect(User.find(user.id)).to eq(user)
|
|
25
|
+
}.to change { ActiveRecord::QueryCounter.query_count }.by(0)
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
describe "#find_by_id" do
|
|
30
|
+
it "finds the object from the cache" do
|
|
31
|
+
expect {
|
|
32
|
+
expect(User.find_by_id(user.id)).to eq(user)
|
|
33
|
+
}.to change { ActiveRecord::QueryCounter.query_count }.by(0)
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
describe "#find_by_name_and_age" do
|
|
38
|
+
it "finds the object from the cache" do
|
|
39
|
+
expect {
|
|
40
|
+
expect(User.find_by_name_and_age("Bob", 13)).to eq(user)
|
|
41
|
+
}.to change { ActiveRecord::QueryCounter.query_count }.by(0)
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
describe "when the name is changed" do
|
|
45
|
+
it "writes the updated data into the cache" do
|
|
46
|
+
user.update_attributes(name: "Sally")
|
|
47
|
+
expect {
|
|
48
|
+
fetched_user = User.find_by_name_and_age("Sally", 13)
|
|
49
|
+
expect(fetched_user.name).to eq("Sally")
|
|
50
|
+
}.to change { ActiveRecord::QueryCounter.query_count }.by(0)
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
describe "when user id destroyed" do
|
|
56
|
+
it "tries to run a query" do
|
|
57
|
+
user.destroy
|
|
58
|
+
expect {
|
|
59
|
+
expect {
|
|
60
|
+
User.find(user.id)
|
|
61
|
+
}.to raise_error
|
|
62
|
+
}.to change { ActiveRecord::QueryCounter.query_count }.by(1)
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
describe "rolling back a transaction" do
|
|
67
|
+
it "expires the cache" do
|
|
68
|
+
expect {
|
|
69
|
+
user.update_attributes(name: "asplode")
|
|
70
|
+
}.to raise_error
|
|
71
|
+
|
|
72
|
+
expect {
|
|
73
|
+
User.find(user.id)
|
|
74
|
+
}.to change { ActiveRecord::QueryCounter.query_count }.by(1)
|
|
75
|
+
end
|
|
76
|
+
end
|
|
77
|
+
end
|
data/spec/spec_helper.rb
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
require 'cache/object'
|
|
2
|
+
RSpec.configure do |config|
|
|
3
|
+
|
|
4
|
+
config.before do
|
|
5
|
+
Cache::Object.instance_variable_set(:@configuration, nil)
|
|
6
|
+
end
|
|
7
|
+
|
|
8
|
+
config.after do
|
|
9
|
+
Cache::Object.instance_variable_set(:@configuration, nil)
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
config.raise_errors_for_deprecations!
|
|
13
|
+
config.disable_monkey_patching!
|
|
14
|
+
|
|
15
|
+
end
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
require 'active_record'
|
|
2
|
+
|
|
3
|
+
ActiveRecord::Migration.verbose = false
|
|
4
|
+
ActiveRecord::Base.logger = Logger.new($STDOUT)
|
|
5
|
+
ActiveRecord::Base.establish_connection(:adapter => 'sqlite3', :database => ':memory:')
|
|
6
|
+
|
|
7
|
+
module ActiveRecord
|
|
8
|
+
class QueryCounter
|
|
9
|
+
cattr_accessor :query_count do
|
|
10
|
+
0
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
IGNORED_SQL = [/^PRAGMA (?!(table_info))/, /^SELECT currval/, /^SELECT CAST/, /^SELECT @@IDENTITY/, /^SELECT @@ROWCOUNT/, /^SAVEPOINT/, /^ROLLBACK TO SAVEPOINT/, /^RELEASE SAVEPOINT/, /^SHOW max_identifier_length/]
|
|
14
|
+
|
|
15
|
+
def call(name, start, finish, message_id, values)
|
|
16
|
+
# FIXME: this seems bad. we should probably have a better way to indicate
|
|
17
|
+
# the query was cached
|
|
18
|
+
unless 'CACHE' == values[:name]
|
|
19
|
+
self.class.query_count += 1 unless IGNORED_SQL.any? { |r| values[:sql] =~ r }
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
ActiveSupport::Notifications.subscribe('sql.active_record', ActiveRecord::QueryCounter.new)
|
|
26
|
+
|
|
27
|
+
class CreateModelsForTest < ActiveRecord::Migration
|
|
28
|
+
def self.up
|
|
29
|
+
create_table :users do |t|
|
|
30
|
+
t.string :name
|
|
31
|
+
t.integer :age, :default => 12
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def self.down
|
|
36
|
+
drop_table(:users)
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
class User < ActiveRecord::Base
|
|
41
|
+
include Cache::Object::ActiveRecord
|
|
42
|
+
|
|
43
|
+
object_cache_on :name, :age
|
|
44
|
+
|
|
45
|
+
after_save :asplode_if_name_is_asplode
|
|
46
|
+
|
|
47
|
+
def asplode_if_name_is_asplode
|
|
48
|
+
raise "WOAH" if name == "asplode"
|
|
49
|
+
end
|
|
50
|
+
end
|
metadata
ADDED
|
@@ -0,0 +1,182 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: cache-object
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
version: 0.0.1
|
|
5
|
+
platform: ruby
|
|
6
|
+
authors:
|
|
7
|
+
- Matt Camuto
|
|
8
|
+
autorequire:
|
|
9
|
+
bindir: bin
|
|
10
|
+
cert_chain: []
|
|
11
|
+
date: 2014-06-23 00:00:00.000000000 Z
|
|
12
|
+
dependencies:
|
|
13
|
+
- !ruby/object:Gem::Dependency
|
|
14
|
+
name: activerecord
|
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
|
16
|
+
requirements:
|
|
17
|
+
- - ">="
|
|
18
|
+
- !ruby/object:Gem::Version
|
|
19
|
+
version: '3.0'
|
|
20
|
+
type: :runtime
|
|
21
|
+
prerelease: false
|
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
23
|
+
requirements:
|
|
24
|
+
- - ">="
|
|
25
|
+
- !ruby/object:Gem::Version
|
|
26
|
+
version: '3.0'
|
|
27
|
+
- !ruby/object:Gem::Dependency
|
|
28
|
+
name: bundler
|
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
|
30
|
+
requirements:
|
|
31
|
+
- - "~>"
|
|
32
|
+
- !ruby/object:Gem::Version
|
|
33
|
+
version: '1.6'
|
|
34
|
+
type: :development
|
|
35
|
+
prerelease: false
|
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
37
|
+
requirements:
|
|
38
|
+
- - "~>"
|
|
39
|
+
- !ruby/object:Gem::Version
|
|
40
|
+
version: '1.6'
|
|
41
|
+
- !ruby/object:Gem::Dependency
|
|
42
|
+
name: rake
|
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
|
44
|
+
requirements:
|
|
45
|
+
- - ">="
|
|
46
|
+
- !ruby/object:Gem::Version
|
|
47
|
+
version: '0'
|
|
48
|
+
type: :development
|
|
49
|
+
prerelease: false
|
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
51
|
+
requirements:
|
|
52
|
+
- - ">="
|
|
53
|
+
- !ruby/object:Gem::Version
|
|
54
|
+
version: '0'
|
|
55
|
+
- !ruby/object:Gem::Dependency
|
|
56
|
+
name: rspec
|
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
|
58
|
+
requirements:
|
|
59
|
+
- - ">="
|
|
60
|
+
- !ruby/object:Gem::Version
|
|
61
|
+
version: '3.0'
|
|
62
|
+
type: :development
|
|
63
|
+
prerelease: false
|
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
65
|
+
requirements:
|
|
66
|
+
- - ">="
|
|
67
|
+
- !ruby/object:Gem::Version
|
|
68
|
+
version: '3.0'
|
|
69
|
+
- !ruby/object:Gem::Dependency
|
|
70
|
+
name: guard
|
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
|
72
|
+
requirements:
|
|
73
|
+
- - ">="
|
|
74
|
+
- !ruby/object:Gem::Version
|
|
75
|
+
version: '0'
|
|
76
|
+
type: :development
|
|
77
|
+
prerelease: false
|
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
79
|
+
requirements:
|
|
80
|
+
- - ">="
|
|
81
|
+
- !ruby/object:Gem::Version
|
|
82
|
+
version: '0'
|
|
83
|
+
- !ruby/object:Gem::Dependency
|
|
84
|
+
name: guard-rspec
|
|
85
|
+
requirement: !ruby/object:Gem::Requirement
|
|
86
|
+
requirements:
|
|
87
|
+
- - ">="
|
|
88
|
+
- !ruby/object:Gem::Version
|
|
89
|
+
version: '0'
|
|
90
|
+
type: :development
|
|
91
|
+
prerelease: false
|
|
92
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
93
|
+
requirements:
|
|
94
|
+
- - ">="
|
|
95
|
+
- !ruby/object:Gem::Version
|
|
96
|
+
version: '0'
|
|
97
|
+
- !ruby/object:Gem::Dependency
|
|
98
|
+
name: sqlite3
|
|
99
|
+
requirement: !ruby/object:Gem::Requirement
|
|
100
|
+
requirements:
|
|
101
|
+
- - ">="
|
|
102
|
+
- !ruby/object:Gem::Version
|
|
103
|
+
version: '0'
|
|
104
|
+
type: :development
|
|
105
|
+
prerelease: false
|
|
106
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
107
|
+
requirements:
|
|
108
|
+
- - ">="
|
|
109
|
+
- !ruby/object:Gem::Version
|
|
110
|
+
version: '0'
|
|
111
|
+
description: Object R/W Caching on top of ActiveRecord
|
|
112
|
+
email:
|
|
113
|
+
- dev@wanelo.com
|
|
114
|
+
executables: []
|
|
115
|
+
extensions: []
|
|
116
|
+
extra_rdoc_files: []
|
|
117
|
+
files:
|
|
118
|
+
- ".gitignore"
|
|
119
|
+
- ".rspec"
|
|
120
|
+
- Gemfile
|
|
121
|
+
- Guardfile
|
|
122
|
+
- LICENSE.txt
|
|
123
|
+
- README.md
|
|
124
|
+
- Rakefile
|
|
125
|
+
- bin/bundler
|
|
126
|
+
- bin/coderay
|
|
127
|
+
- bin/guard
|
|
128
|
+
- bin/htmldiff
|
|
129
|
+
- bin/ldiff
|
|
130
|
+
- bin/listen
|
|
131
|
+
- bin/pry
|
|
132
|
+
- bin/rake
|
|
133
|
+
- bin/rspec
|
|
134
|
+
- bin/thor
|
|
135
|
+
- cache-object.gemspec
|
|
136
|
+
- lib/cache/object.rb
|
|
137
|
+
- lib/cache/object/active_record.rb
|
|
138
|
+
- lib/cache/object/adapter.rb
|
|
139
|
+
- lib/cache/object/config.rb
|
|
140
|
+
- lib/cache/object/instance_decorator.rb
|
|
141
|
+
- lib/cache/object/key_generator.rb
|
|
142
|
+
- lib/cache/object/version.rb
|
|
143
|
+
- spec/cache/object/active_record_spec.rb
|
|
144
|
+
- spec/cache/object/adapter_spec.rb
|
|
145
|
+
- spec/cache/object/config_spec.rb
|
|
146
|
+
- spec/cache/object/instance_decorator_spec.rb
|
|
147
|
+
- spec/features/cache_object_spec.rb
|
|
148
|
+
- spec/spec_helper.rb
|
|
149
|
+
- spec/support/models.rb
|
|
150
|
+
homepage: ''
|
|
151
|
+
licenses:
|
|
152
|
+
- MIT
|
|
153
|
+
metadata: {}
|
|
154
|
+
post_install_message:
|
|
155
|
+
rdoc_options: []
|
|
156
|
+
require_paths:
|
|
157
|
+
- lib
|
|
158
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
159
|
+
requirements:
|
|
160
|
+
- - ">="
|
|
161
|
+
- !ruby/object:Gem::Version
|
|
162
|
+
version: '0'
|
|
163
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
164
|
+
requirements:
|
|
165
|
+
- - ">="
|
|
166
|
+
- !ruby/object:Gem::Version
|
|
167
|
+
version: '0'
|
|
168
|
+
requirements: []
|
|
169
|
+
rubyforge_project:
|
|
170
|
+
rubygems_version: 2.2.2
|
|
171
|
+
signing_key:
|
|
172
|
+
specification_version: 4
|
|
173
|
+
summary: Object R/W Caching
|
|
174
|
+
test_files:
|
|
175
|
+
- spec/cache/object/active_record_spec.rb
|
|
176
|
+
- spec/cache/object/adapter_spec.rb
|
|
177
|
+
- spec/cache/object/config_spec.rb
|
|
178
|
+
- spec/cache/object/instance_decorator_spec.rb
|
|
179
|
+
- spec/features/cache_object_spec.rb
|
|
180
|
+
- spec/spec_helper.rb
|
|
181
|
+
- spec/support/models.rb
|
|
182
|
+
has_rdoc:
|