act_as_cached 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
data/MIT-LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright 2013 ddl1st
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,68 @@
1
+ # ActAsCached
2
+
3
+ [![Gem Version](https://badge.fury.io/rb/act_as_cached.png)](http://badge.fury.io/rb/act_as_cached)
4
+ [![Dependency Status](https://gemnasium.com/ddl1st/act_as_cached.png)](https://gemnasium.com/ddl1st/act_as_cached)
5
+ [![Build Status](https://travis-ci.org/ddl1st/act_as_cached.png?branch=master)](https://travis-ci.org/ddl1st/act_as_cached)
6
+ [![Code Climate](https://codeclimate.com/github/ddl1st/act_as_cached.png)](https://codeclimate.com/github/ddl1st/act_as_cached)
7
+
8
+
9
+ <tt>ActAsCached</tt> provides a lightweight seconds cached.
10
+
11
+ ----------------------
12
+
13
+ ## Install
14
+
15
+ gem "act_as_cached", "0.0.1"
16
+
17
+ ----------------------
18
+
19
+ ### Options:
20
+
21
+ * <tt>:prefix</tt> - Default is +aac+ .
22
+ * <tt>:cache_store</tt> - Customize cache store.
23
+ * <tt>:expires_time</tt> - Set cache expiration time.
24
+ * <tt>:logger</tt> - Set outout file with log, default is +Rails.logger+ .
25
+
26
+ ----------------------
27
+
28
+ ## Usage
29
+
30
+ ```ruby
31
+ class Account < ActiveRecord::Base
32
+ act_as_cached
33
+ has_many :users
34
+ end
35
+
36
+ Account.find 1 # Account Load (0.5ms) SELECT `accounts`.* FROM `accounts` WHERE `accounts`.`id` = 1 LIMIT 1
37
+ Account.find 1 # NO SQL OUTPUT
38
+
39
+ Account.find(1).users # Account Load (0.5ms) SELECT `users`.* FROM `users` INNER JOIN `accounts_users` ON `users`.`id` = `accounts_users`.`user_id` WHERE `accounts_users`.`account_id` = 1
40
+ Account.find(1).users # NO SQL OUTPUT
41
+
42
+ ```
43
+
44
+ ----------------------
45
+
46
+
47
+ ## Noties
48
+
49
+ * Currently not supported <tt>ActiveRecord::Relation#find</tt> , If you wrote the +default_scope+ in your class, cache won't be enable.
50
+
51
+ ----------------------
52
+
53
+ ## TODO LIST
54
+
55
+ * Expires cache
56
+ * Add cache key rules
57
+
58
+ ----------------------
59
+
60
+ ## Contributors
61
+
62
+ * [ddl1st](https://github.com/ddl1st)
63
+
64
+ ----------------------
65
+
66
+ ## License
67
+
68
+ MIT License
data/Rakefile ADDED
@@ -0,0 +1,38 @@
1
+ #!/usr/bin/env rake
2
+ begin
3
+ require 'bundler/setup'
4
+ rescue LoadError
5
+ puts 'You must `gem install bundler` and `bundle install` to run rake tasks'
6
+ end
7
+ begin
8
+ require 'rdoc/task'
9
+ rescue LoadError
10
+ require 'rdoc/rdoc'
11
+ require 'rake/rdoctask'
12
+ RDoc::Task = Rake::RDocTask
13
+ end
14
+
15
+ RDoc::Task.new(:rdoc) do |rdoc|
16
+ rdoc.rdoc_dir = 'rdoc'
17
+ rdoc.title = 'ActAsCached'
18
+ rdoc.options << '--line-numbers'
19
+ rdoc.rdoc_files.include('README.rdoc')
20
+ rdoc.rdoc_files.include('lib/**/*.rb')
21
+ end
22
+
23
+
24
+
25
+
26
+ Bundler::GemHelper.install_tasks
27
+
28
+ require 'rake/testtask'
29
+
30
+ Rake::TestTask.new(:test) do |t|
31
+ t.libs << 'lib'
32
+ t.libs << 'test'
33
+ t.pattern = 'test/**/*_test.rb'
34
+ t.verbose = false
35
+ end
36
+
37
+
38
+ task :default => :test
@@ -0,0 +1,22 @@
1
+ require 'active_record/observer'
2
+ module ActAsCached
3
+ module ActiveRecord
4
+ class Actobserver < ::ActiveRecord::Observer
5
+ cattr_accessor :observed_classes
6
+ @@observed_classes = []
7
+
8
+ def after_commit(object)
9
+ expire_cache(object)
10
+ end
11
+
12
+ private
13
+ def expire_cache(object)
14
+ object.cache_store.delete_matched(%r(#{match_keys(object)}),namespace: object.cache_prefix)
15
+ end
16
+
17
+ def match_keys(object)
18
+ ["all","first","last",nil].collect{|x| [object.cache_name,x].compact.join("/")}.join("|") << "$"
19
+ end
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,15 @@
1
+ module ActAsCached
2
+ module ActiveRecord
3
+ module Association
4
+ def self.included(klass)
5
+ klass.class_eval do
6
+ delegate :write_cache,:read_cache,:fetch_cache,:enabled_cache?,:cache_mod,to: :owner
7
+ end
8
+ end
9
+
10
+ def cache_path
11
+ [cache_mod.name,owner.to_param,reflection.class_name].join('/')
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,13 @@
1
+ module ActAsCached
2
+ module ActiveRecord
3
+ module AssociationReflection
4
+ def path
5
+ klass.reflections
6
+ end
7
+
8
+ def reflections_path
9
+ reflections.map(&:path)
10
+ end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,18 @@
1
+ module ActAsCached
2
+ module ActiveRecord
3
+ module CollectionAssociation
4
+
5
+ def self.included(klass)
6
+ klass.class_eval do
7
+ alias_method_chain :find_target,:act_as_cache
8
+ end
9
+ end
10
+
11
+ def find_target_with_act_as_cache
12
+ enabled_cache? ?
13
+ fetch_cache(cache_path) { find_target_without_act_as_cache } :
14
+ find_target_without_act_as_cache
15
+ end
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,28 @@
1
+ module ActAsCached
2
+ module ActiveRecord
3
+ module FinderMethods
4
+ def self.included(base)
5
+ base.class_eval do
6
+ alias_method_chain :find,:act_as_cached
7
+ end
8
+ end
9
+
10
+ # Only support find with primary_key are currently supported.
11
+ def find_with_act_as_cached(*args)
12
+ options = args.extract_options!
13
+ if args.length == 1 && enabled_cache? && where_values.blank?
14
+ # TODO
15
+ path = [cache_mod.name,args[0]].join('/')
16
+ cache_mod.fetch_cache(path) { find_without_act_as_cached(args[0]) }
17
+ else
18
+ args << options
19
+ find_without_act_as_cached(*args)
20
+ end
21
+ end
22
+
23
+ def cache_mod
24
+ klass.cache_mod
25
+ end
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,17 @@
1
+ module ActAsCached
2
+ module ActiveRecord
3
+ module HasManyThroughAssociation
4
+ def self.included(klass)
5
+ klass.class_eval do
6
+ alias_method_chain :find_target,:act_as_cache
7
+ end
8
+ end
9
+
10
+ def find_target_with_act_as_cache
11
+ enabled_cache? ?
12
+ fetch_cache(cache_path) { find_target_without_act_as_cache } :
13
+ find_target_without_act_as_cache
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,17 @@
1
+ module ActAsCached
2
+ module ActiveRecord
3
+ module SingularAssociation
4
+ def self.included(klass)
5
+ klass.class_eval do
6
+ alias_method_chain :find_target,:act_as_cache
7
+ end
8
+ end
9
+
10
+ def find_target_with_act_as_cache
11
+ enabled_cache? ?
12
+ fetch_cache(cache_path) { find_target_without_act_as_cache } :
13
+ find_target_without_act_as_cache
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,15 @@
1
+ module ActAsCached
2
+ module ActiveRecord
3
+ extend ActiveSupport::Autoload
4
+ autoload :Association
5
+ autoload :CollectionAssociation
6
+ autoload :HasManyThroughAssociation
7
+ autoload :SingularAssociation
8
+ autoload :FinderMethods
9
+ end
10
+ end
11
+ ActiveRecord::Associations::CollectionAssociation.send(:include,ActAsCached::ActiveRecord::CollectionAssociation)
12
+ ActiveRecord::Associations::HasManyThroughAssociation.send(:include,ActAsCached::ActiveRecord::HasManyThroughAssociation)
13
+ ActiveRecord::Associations::SingularAssociation.send(:include,ActAsCached::ActiveRecord::SingularAssociation)
14
+ ActiveRecord::Associations::Association.send(:include,ActAsCached::ActiveRecord::Association)
15
+ ActiveRecord::FinderMethods.send(:include,ActAsCached::ActiveRecord::FinderMethods)
@@ -0,0 +1,33 @@
1
+ module ActAsCached
2
+ module CacheOptions
3
+ def self.included(klass)
4
+ klass.send(:extend,ClassMethods)
5
+ end
6
+
7
+ module ClassMethods
8
+ def options
9
+ @options ||= cache_options.extract_options!
10
+ end
11
+
12
+ def name
13
+ @name ||= klass.name
14
+ end
15
+
16
+ def prefix
17
+ options[:prefix] || ActAsCached.prefix
18
+ end
19
+
20
+ def store
21
+ options[:cache_store] || ActAsCached.cache_store
22
+ end
23
+
24
+ def logger
25
+ options[:logger] || ActAsCached.logger
26
+ end
27
+
28
+ def time
29
+ options[:expires_time] || ActAsCached.expires_time
30
+ end
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,15 @@
1
+ module ActAsCached
2
+ module Configurable
3
+ mattr_accessor :logger,:expires_time,:cache_store,:prefix
4
+
5
+ def configure(&block)
6
+ yield config
7
+ end
8
+
9
+ def config
10
+ self
11
+ end
12
+
13
+ module_function :config
14
+ end
15
+ end
@@ -0,0 +1,20 @@
1
+ module ActAsCached
2
+ module Helpers
3
+ def write_cache(name)
4
+ store.write(name,yield,expires_in: time,namespace: prefix)
5
+ read_cache(name)
6
+ end
7
+
8
+ def read_cache(name)
9
+ store.read(name,namespace: prefix)
10
+ end
11
+
12
+ def fetch_cache(name,&block)
13
+ read_cache(name) || write_cache(name) { yield }
14
+ end
15
+
16
+ def enabled_cache?
17
+ true
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,78 @@
1
+ module ActAsCached
2
+ module Mixin
3
+ def self.included(klass)
4
+ klass.send(:extend,ClassMethods)
5
+ end
6
+
7
+ module ClassMethods
8
+ # Enable cache, in the current class.
9
+ #
10
+ # === Example
11
+ #
12
+ # class Blog < ActiveRecord::Base
13
+ # act_as_cached expires_time: 1.day
14
+ # has_many :comments
15
+ # belongs_to :user
16
+ # end
17
+ # Topic.find(1)
18
+ # Topic Load (0.5ms) SELECT `topics`.* FROM `topics` WHERE `topics`.`id` = 1 LIMIT 1
19
+ # => #<Topic id: 1 title: 'foo'>
20
+ # Topic.find(1)
21
+ # => #<Topic id: 1 title: 'foo'>
22
+ # Topic.find(1).comments
23
+ # Comment Load (1.1ms) SELECT `comments`.* FROM `comments` WHERE `comments`.`topic_id` = 1
24
+ # => [#<Comment id: 1, content: 'xxxx'>,#<Comment id: 2, content: "ooooooo">]
25
+ # Topic.find(1).comments
26
+ # => [#<Comment id: 1, content: 'xxxx'>,#<Comment id: 2, content: "ooooooo">]
27
+ # Topic.find(1).save
28
+ # Topic.find(1)
29
+ # Topic Load (0.5ms) SELECT `topics`.* FROM `topics` WHERE `topics`.`id` = 1 LIMIT 1
30
+ # => #<Topic id: 1 title: 'foo'>
31
+ # Topic.find(1)
32
+ # => #<Topic id: 1 title: 'foo'>
33
+ #
34
+ # === Options
35
+ # [:prefix]
36
+ # Namespace with act_as_cached, default is <tt>aac</tt>
37
+ # [:cache_store]
38
+ # Storage engine with act_as_cached
39
+ # [:logger]
40
+ # Logger with act_as_cached
41
+ # [:expires_time]
42
+ # Set cache expires time with activerecord associations and <tt>ActiveRecord::Query#find</tt> of current class.
43
+ def act_as_cached(*args)
44
+ observer.send(:add_observer!,klass = self)
45
+ @cache_mod = Module.new do
46
+ mattr_accessor :cache_options,:klass
47
+ self.cache_options = args
48
+ self.klass = klass
49
+ extend ActAsCached::Helpers
50
+ end
51
+ @cache_mod.send(:include,ActAsCached::CacheOptions)
52
+
53
+ delegate :options,:name,:prefix,:store,:logger,:time, to: :cache_mod , prefix: "cache"
54
+ delegate :write_cache,:read_cache,:fetch_cache,to: :cache_mod
55
+ end
56
+
57
+ def cache_mod
58
+ @cache_mod
59
+ end
60
+
61
+ def enabled_cache?
62
+ !cache_mod.nil?
63
+ end
64
+
65
+ def observer
66
+ ActAsCached::ActiveRecord::Actobserver.instance
67
+ end
68
+ end
69
+
70
+ def cache_mod
71
+ self.class.cache_mod
72
+ end
73
+
74
+ def enabled_cache?
75
+ self.class.enabled_cache?
76
+ end
77
+ end
78
+ end
@@ -0,0 +1,28 @@
1
+ module ActAsCached
2
+ def self.hook_config!
3
+ ActAsCached.configure do
4
+ config.logger ||= ::Rails.logger
5
+ config.cache_store ||= ::Rails.cache
6
+ config.expires_time ||= 1.week
7
+ config.prefix ||= 'aac'
8
+ end
9
+ end
10
+
11
+ def self.hook_rails!
12
+ if defined?(::ActiveRecord)
13
+ require 'act_as_cached/active_record'
14
+ ::ActiveRecord::Base.send(:include,Mixin)
15
+ end
16
+ end
17
+
18
+ class Rails < ::Rails::Engine
19
+ config.active_record.observers = "act_as_cached/active_record/actobserver"
20
+ config.act_as_cache = ActAsCached
21
+
22
+ config.after_initialize do
23
+ ActAsCached.hook_config!
24
+ ActAsCached.hook_rails!
25
+ end
26
+
27
+ end if defined?(::Rails)
28
+ end
@@ -0,0 +1,3 @@
1
+ module ActAsCached
2
+ VERSION = "0.0.1"
3
+ end
@@ -0,0 +1,15 @@
1
+ require 'act_as_cached/configurable'
2
+ module ActAsCached
3
+ extend Configurable
4
+
5
+ extend ActiveSupport::Autoload
6
+ autoload :CacheOptions
7
+ autoload :Mixin
8
+ autoload :Helpers
9
+ autoload :ActiveRecord
10
+
11
+ module ActiveRecord
12
+ autoload :Actobserver
13
+ end
14
+ end
15
+ require 'act_as_cached/rails' if defined?(::Rails::Engine)
@@ -0,0 +1,4 @@
1
+ # desc "Explaining what the task does"
2
+ # task :act_as_cached do
3
+ # # Task goes here
4
+ # end
@@ -0,0 +1,7 @@
1
+ require 'test_helper'
2
+
3
+ class ActAsCachedTest < ActiveSupport::TestCase
4
+ test "truth" do
5
+ assert_kind_of Module, ActAsCached
6
+ end
7
+ end
@@ -0,0 +1,13 @@
1
+ # Configure Rails Environment
2
+ ENV["RAILS_ENV"] = "test"
3
+ require "rails/test_help"
4
+
5
+ Rails.backtrace_cleaner.remove_silencers!
6
+
7
+ # Load support files
8
+ Dir["#{File.dirname(__FILE__)}/support/**/*.rb"].each { |f| require f }
9
+
10
+ # Load fixtures from the engine
11
+ if ActiveSupport::TestCase.method_defined?(:fixture_path=)
12
+ ActiveSupport::TestCase.fixture_path = File.expand_path("../fixtures", __FILE__)
13
+ end
metadata ADDED
@@ -0,0 +1,102 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: act_as_cached
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - ddl1st
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2013-10-25 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: activesupport
16
+ requirement: !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ! '>='
20
+ - !ruby/object:Gem::Version
21
+ version: 3.0.0
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: 3.0.0
30
+ - !ruby/object:Gem::Dependency
31
+ name: activerecord
32
+ requirement: !ruby/object:Gem::Requirement
33
+ none: false
34
+ requirements:
35
+ - - ! '>='
36
+ - !ruby/object:Gem::Version
37
+ version: 3.0.0
38
+ type: :development
39
+ prerelease: false
40
+ version_requirements: !ruby/object:Gem::Requirement
41
+ none: false
42
+ requirements:
43
+ - - ! '>='
44
+ - !ruby/object:Gem::Version
45
+ version: 3.0.0
46
+ description: Friendly cache with ActiveRecord.
47
+ email:
48
+ - looooooooooooooooooooooooooops@gmail.com
49
+ executables: []
50
+ extensions: []
51
+ extra_rdoc_files: []
52
+ files:
53
+ - lib/act_as_cached/active_record/actobserver.rb
54
+ - lib/act_as_cached/active_record/association.rb
55
+ - lib/act_as_cached/active_record/association_reflection.rb
56
+ - lib/act_as_cached/active_record/collection_association.rb
57
+ - lib/act_as_cached/active_record/finder_methods.rb
58
+ - lib/act_as_cached/active_record/has_many_through_association.rb
59
+ - lib/act_as_cached/active_record/singular_association.rb
60
+ - lib/act_as_cached/active_record.rb
61
+ - lib/act_as_cached/cache_options.rb
62
+ - lib/act_as_cached/configurable.rb
63
+ - lib/act_as_cached/helpers.rb
64
+ - lib/act_as_cached/mixin.rb
65
+ - lib/act_as_cached/rails.rb
66
+ - lib/act_as_cached/version.rb
67
+ - lib/act_as_cached.rb
68
+ - lib/tasks/act_as_cached_tasks.rake
69
+ - MIT-LICENSE
70
+ - Rakefile
71
+ - README.md
72
+ - test/act_as_cached_test.rb
73
+ - test/test_helper.rb
74
+ homepage: https://github.com/ddl1st/act_as_cached
75
+ licenses:
76
+ - MIT
77
+ post_install_message:
78
+ rdoc_options: []
79
+ require_paths:
80
+ - lib
81
+ required_ruby_version: !ruby/object:Gem::Requirement
82
+ none: false
83
+ requirements:
84
+ - - ! '>='
85
+ - !ruby/object:Gem::Version
86
+ version: '0'
87
+ required_rubygems_version: !ruby/object:Gem::Requirement
88
+ none: false
89
+ requirements:
90
+ - - ! '>='
91
+ - !ruby/object:Gem::Version
92
+ version: '0'
93
+ requirements: []
94
+ rubyforge_project:
95
+ rubygems_version: 1.8.25
96
+ signing_key:
97
+ specification_version: 3
98
+ summary: Friendly cache with ActiveRecord.
99
+ test_files:
100
+ - test/act_as_cached_test.rb
101
+ - test/test_helper.rb
102
+ has_rdoc: