tiny_cache 0.0.1 → 0.0.3

Sign up to get free protection for your applications and to get access to all the features.
Files changed (77) hide show
  1. data/MIT-LICENSE +1 -1
  2. data/README.md +32 -0
  3. data/Rakefile +15 -30
  4. data/lib/tiny_cache.rb +14 -1
  5. data/lib/tiny_cache/active_record.rb +18 -0
  6. data/lib/tiny_cache/active_record/base.rb +133 -0
  7. data/lib/tiny_cache/active_record/belongs_to_association.rb +37 -0
  8. data/lib/tiny_cache/active_record/fetch_by_uniq_key.rb +39 -0
  9. data/lib/tiny_cache/active_record/finder_methods.rb +132 -0
  10. data/lib/tiny_cache/active_record/has_many_association.rb +37 -0
  11. data/lib/tiny_cache/active_record/has_one_association.rb +36 -0
  12. data/lib/tiny_cache/active_record/persistence.rb +28 -0
  13. data/lib/tiny_cache/arel/wheres.rb +36 -0
  14. data/lib/tiny_cache/config.rb +23 -0
  15. data/lib/tiny_cache/record_marshal.rb +27 -0
  16. data/lib/tiny_cache/version.rb +2 -1
  17. data/spec/dummy/README.rdoc +28 -0
  18. data/{test → spec}/dummy/Rakefile +0 -1
  19. data/{test → spec}/dummy/app/assets/javascripts/application.js +3 -5
  20. data/{app/assets/stylesheets/tiny_cache → spec/dummy/app/assets/stylesheets}/application.css +0 -0
  21. data/spec/dummy/app/controllers/application_controller.rb +6 -0
  22. data/{test → spec}/dummy/app/helpers/application_helper.rb +1 -0
  23. data/spec/dummy/app/views/layouts/application.html.erb +14 -0
  24. data/spec/dummy/bin/bundle +3 -0
  25. data/spec/dummy/bin/rails +4 -0
  26. data/spec/dummy/bin/rake +4 -0
  27. data/{test → spec}/dummy/config.ru +1 -1
  28. data/spec/dummy/config/application.rb +24 -0
  29. data/spec/dummy/config/boot.rb +6 -0
  30. data/{test → spec}/dummy/config/database.yml +0 -0
  31. data/spec/dummy/config/environment.rb +6 -0
  32. data/spec/dummy/config/environments/development.rb +30 -0
  33. data/spec/dummy/config/environments/production.rb +81 -0
  34. data/{test → spec}/dummy/config/environments/test.rb +14 -14
  35. data/{test → spec}/dummy/config/initializers/backtrace_silencers.rb +1 -0
  36. data/spec/dummy/config/initializers/filter_parameter_logging.rb +5 -0
  37. data/spec/dummy/config/initializers/inflections.rb +17 -0
  38. data/{test → spec}/dummy/config/initializers/mime_types.rb +1 -0
  39. data/spec/dummy/config/initializers/secret_token.rb +13 -0
  40. data/spec/dummy/config/initializers/session_store.rb +4 -0
  41. data/{test → spec}/dummy/config/initializers/wrap_parameters.rb +7 -6
  42. data/spec/dummy/config/locales/en.yml +23 -0
  43. data/spec/dummy/config/routes.rb +57 -0
  44. data/spec/dummy/public/404.html +58 -0
  45. data/spec/dummy/public/422.html +58 -0
  46. data/spec/dummy/public/500.html +57 -0
  47. data/{test → spec}/dummy/public/favicon.ico +0 -0
  48. data/spec/spec_helper.rb +41 -0
  49. metadata +95 -84
  50. data/README.rdoc +0 -3
  51. data/app/assets/javascripts/tiny_cache/application.js +0 -15
  52. data/app/controllers/tiny_cache/application_controller.rb +0 -4
  53. data/app/helpers/tiny_cache/application_helper.rb +0 -4
  54. data/app/views/layouts/tiny_cache/application.html.erb +0 -14
  55. data/config/routes.rb +0 -2
  56. data/lib/tiny_cache/engine.rb +0 -5
  57. data/test/dummy/README.rdoc +0 -261
  58. data/test/dummy/app/assets/stylesheets/application.css +0 -13
  59. data/test/dummy/app/controllers/application_controller.rb +0 -3
  60. data/test/dummy/app/views/layouts/application.html.erb +0 -14
  61. data/test/dummy/config/application.rb +0 -59
  62. data/test/dummy/config/boot.rb +0 -10
  63. data/test/dummy/config/environment.rb +0 -5
  64. data/test/dummy/config/environments/development.rb +0 -37
  65. data/test/dummy/config/environments/production.rb +0 -67
  66. data/test/dummy/config/initializers/inflections.rb +0 -15
  67. data/test/dummy/config/initializers/secret_token.rb +0 -7
  68. data/test/dummy/config/initializers/session_store.rb +0 -8
  69. data/test/dummy/config/locales/en.yml +0 -5
  70. data/test/dummy/config/routes.rb +0 -4
  71. data/test/dummy/public/404.html +0 -26
  72. data/test/dummy/public/422.html +0 -26
  73. data/test/dummy/public/500.html +0 -25
  74. data/test/dummy/script/rails +0 -6
  75. data/test/integration/navigation_test.rb +0 -10
  76. data/test/test_helper.rb +0 -15
  77. data/test/tiny_cache_test.rb +0 -7
@@ -1,4 +1,4 @@
1
- Copyright 2012 YOURNAME
1
+ Copyright 2013 YOURNAME
2
2
 
3
3
  Permission is hereby granted, free of charge, to any person obtaining
4
4
  a copy of this software and associated documentation files (the
@@ -0,0 +1,32 @@
1
+ # Tiny Cache
2
+
3
+ ## for Rails3+ model cache
4
+
5
+ ## 使用方法 usage:
6
+
7
+ * config
8
+ config/initializers 下新建 tiny_cache.rb
9
+
10
+ ```
11
+ # -*- encoding : utf-8 -*-
12
+ TinyCache.configure do |config|
13
+ config.cache_store = ::Rails.cache
14
+ end
15
+ ```
16
+
17
+ * 在model 中调用
18
+ ```
19
+ module Ruby800
20
+ class Board < ActiveRecord::Base
21
+
22
+ self.acts_as_tiny_cached
23
+
24
+ # self.acts_as_tiny_cached :version => 2, :expires_in => 2.weeks
25
+
26
+ end
27
+ end
28
+ ```
29
+
30
+ * 生成的实例方法
31
+ ...
32
+
data/Rakefile CHANGED
@@ -4,37 +4,22 @@ begin
4
4
  rescue LoadError
5
5
  puts 'You must `gem install bundler` and `bundle install` to run rake tasks'
6
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 = 'TinyCache'
18
- rdoc.options << '--line-numbers'
19
- rdoc.rdoc_files.include('README.rdoc')
20
- rdoc.rdoc_files.include('lib/**/*.rb')
21
- end
22
-
23
- APP_RAKEFILE = File.expand_path("../test/dummy/Rakefile", __FILE__)
24
- load 'rails/tasks/engine.rake'
25
-
26
7
 
8
+ # begin
9
+ # require 'rdoc/task'
10
+ # rescue LoadError
11
+ # require 'rdoc/rdoc'
12
+ # require 'rake/rdoctask'
13
+ # RDoc::Task = Rake::RDocTask
14
+ # end
15
+ #
16
+ # RDoc::Task.new(:rdoc) do |rdoc|
17
+ # rdoc.rdoc_dir = 'rdoc'
18
+ # rdoc.title = 'TinyCache'
19
+ # rdoc.options << '--line-numbers'
20
+ # rdoc.rdoc_files.include('README.rdoc')
21
+ # rdoc.rdoc_files.include('lib/**/*.rb')
22
+ # end
27
23
 
28
24
  Bundler::GemHelper.install_tasks
29
25
 
30
- require 'rake/testtask'
31
-
32
- Rake::TestTask.new(:test) do |t|
33
- t.libs << 'lib'
34
- t.libs << 'test'
35
- t.pattern = 'test/**/*_test.rb'
36
- t.verbose = false
37
- end
38
-
39
-
40
- task :default => :test
@@ -1,4 +1,17 @@
1
- require "tiny_cache/engine"
1
+ # -*- encoding : utf-8 -*-
2
+ require 'active_support/all'
3
+ require 'tiny_cache/config'
4
+ require 'tiny_cache/record_marshal'
2
5
 
3
6
  module TinyCache
7
+ def self.configure
8
+ block_given? ? yield(Config) : Config
9
+ end
10
+
11
+ class << self
12
+ delegate :logger, :cache_store, :cache_key_prefix, :to => Config
13
+ end
14
+
4
15
  end
16
+
17
+ require 'tiny_cache/active_record' if defined?(::ActiveRecord)
@@ -0,0 +1,18 @@
1
+ # -*- encoding : utf-8 -*-
2
+ require "#{File.dirname(__FILE__)}/active_record/base"
3
+ require "#{File.dirname(__FILE__)}/active_record/fetch_by_uniq_key"
4
+ require "#{File.dirname(__FILE__)}/active_record/finder_methods"
5
+ require "#{File.dirname(__FILE__)}/active_record/persistence"
6
+ require "#{File.dirname(__FILE__)}/active_record/belongs_to_association"
7
+ require "#{File.dirname(__FILE__)}/active_record/has_one_association"
8
+ require "#{File.dirname(__FILE__)}/active_record/has_many_association"
9
+
10
+ if defined?(ActiveRecord::Base)
11
+ ::ActiveRecord::Base.send(:include, ::TinyCache::ActiveRecord::Base)
12
+ ::ActiveRecord::Base.send(:extend, ::TinyCache::ActiveRecord::FetchByUniqKey)
13
+ ::ActiveRecord::Relation.send(:include, ::TinyCache::ActiveRecord::FinderMethods)
14
+ ::ActiveRecord::Base.send(:include, ::TinyCache::ActiveRecord::Persistence)
15
+ ::ActiveRecord::Associations::BelongsToAssociation.send(:include, ::TinyCache::ActiveRecord::Associations::BelongsToAssociation)
16
+ ::ActiveRecord::Associations::HasOneAssociation.send(:include, ::TinyCache::ActiveRecord::Associations::HasOneAssociation)
17
+ ::ActiveRecord::Associations::HasManyAssociation.send(:include, ::TinyCache::ActiveRecord::Associations::HasManyAssociation)
18
+ end
@@ -0,0 +1,133 @@
1
+ # -*- encoding : utf-8 -*-
2
+ module TinyCache
3
+ module ActiveRecord
4
+ module Base
5
+
6
+ def self.included base
7
+ base.class_eval do
8
+ include ::ActiveSupport::Concern
9
+
10
+ extend ClassMethods
11
+ include InstanceMethods
12
+
13
+ class << self
14
+ alias_method_chain :update_counters, :tiny_cache
15
+ end
16
+ end
17
+ end
18
+
19
+ module ClassMethods
20
+ attr_reader :tiny_cache_options
21
+
22
+ def acts_as_tiny_cached(*args)
23
+ options = args.extract_options!
24
+
25
+ @tiny_cache_enabled = true
26
+ @tiny_cache_options = options
27
+ @tiny_cache_options[:expires_in] ||= 1.week
28
+ @tiny_cache_options[:version] ||= 0
29
+
30
+ after_commit :expire_tiny_cache, :on => :destroy
31
+ after_commit :update_tiny_cache, :on => :update
32
+ after_commit :write_tiny_cache, :on => :create
33
+ end
34
+
35
+ def update_counters_with_tiny_cache(id, counters)
36
+ update_counters_without_tiny_cache(id, counters).tap do
37
+ Array(id).each{|i| expire_tiny_cache(i)}
38
+ end
39
+ end
40
+
41
+ # 是否启用cache
42
+ def tiny_cache_enabled?
43
+ !!@tiny_cache_enabled
44
+ end
45
+
46
+ # 不启用cache
47
+ def without_tiny_cache
48
+ old, @tiny_cache_enabled = @tiny_cache_enabled, false
49
+
50
+ yield if block_given?
51
+ ensure
52
+ @tiny_cache_enabled = old
53
+ end
54
+
55
+ def cache_store
56
+ ::TinyCache::Config.cache_store
57
+ end
58
+
59
+ def logger
60
+ ::TinyCache::Config.logger
61
+ end
62
+
63
+ def tiny_cache_key_prefix
64
+ ::TinyCache::Config.cache_key_prefix
65
+ end
66
+
67
+ def tiny_cache_version
68
+ tiny_cache_options[:version]
69
+ end
70
+
71
+ def tiny_cache_key(id)
72
+ "#{tiny_cache_key_prefix}/models/#{self.name}/#{id}/#{tiny_cache_version}"
73
+ end
74
+
75
+ def read_tiny_cache(id)
76
+ RecordMarshal.load(TinyCache.cache_store.read(tiny_cache_key(id))) if self.tiny_cache_enabled?
77
+ end
78
+
79
+ def expire_tiny_cache(id)
80
+ TinyCache.cache_store.delete(tiny_cache_key(id)) if self.tiny_cache_enabled?
81
+ end
82
+
83
+ end
84
+
85
+ module InstanceMethods
86
+ def try_load_from_tiny_cache
87
+ self.class.read_tiny_cache(self.id) || self
88
+ end
89
+
90
+ # 缓存的key
91
+ def tiny_cache_key
92
+ self.class.tiny_cache_key(self.id)
93
+ end
94
+
95
+ def expire_tiny_cache
96
+ self.class.expire_tiny_cache(self.id)
97
+ end
98
+
99
+ def tiny_cache_method_fetch *args, &block
100
+ options = args.extract_options!
101
+
102
+ self.class.cache_store.fetch self.tiny_cache_method_key(args), options do
103
+ block.call
104
+ end
105
+ end
106
+
107
+ def tiny_cache_method_expire *args
108
+ options = args.extract_options!
109
+
110
+ self.class.cache_store.delete self.tiny_cache_method_key(args)
111
+ end
112
+
113
+ def tiny_cache_method_key args
114
+ "#{self.tiny_cache_key}/method_fetch/#{args.join('/')}"
115
+ end
116
+
117
+ def refresh_tiny_cache_updated_at
118
+ self.updated_at = Time.now
119
+ end
120
+
121
+ def write_tiny_cache
122
+ if self.class.tiny_cache_enabled?
123
+ ::TinyCache.cache_store.write(
124
+ self.tiny_cache_key, RecordMarshal.dump(self), :expires_in => self.class.tiny_cache_options[:expires_in]
125
+ )
126
+ end
127
+ end
128
+
129
+ alias update_tiny_cache write_tiny_cache
130
+ end
131
+ end
132
+ end
133
+ end
@@ -0,0 +1,37 @@
1
+ # -*- encoding : utf-8 -*-
2
+ module TinyCache
3
+ module ActiveRecord
4
+ module Associations
5
+ module BelongsToAssociation
6
+ extend ActiveSupport::Concern
7
+ included do
8
+ class_eval do
9
+ alias_method_chain :find_target, :tiny_cache
10
+ end
11
+ end
12
+
13
+ def association_class
14
+ @reflection.klass
15
+ end
16
+
17
+ def find_target_with_tiny_cache
18
+ return find_target_without_tiny_cache unless klass.tiny_cache_enabled?
19
+ cache_record = klass.read_tiny_cache(tiny_cache_key)
20
+ return cache_record.tap{|record| set_inverse_instance(record)} if cache_record
21
+ record = find_target_without_tiny_cache
22
+
23
+ record.tap do |r|
24
+ set_inverse_instance(r)
25
+ r.write_tiny_cache
26
+ end if record
27
+ end
28
+
29
+ private
30
+
31
+ def tiny_cache_key
32
+ owner[reflection.foreign_key]
33
+ end
34
+ end
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,39 @@
1
+ # -*- encoding : utf-8 -*-
2
+ module TinyCache
3
+ module ActiveRecord
4
+ module FetchByUniqKey
5
+ #
6
+ # 用来根据属性进行查询缓存, 一般适合是唯一不变的条件
7
+ # eg:
8
+ # 1. topic.tiny_cache_find_by :uid => 1, :category_id => 2
9
+ #
10
+ def tiny_cache_find_by conditions={}
11
+ uniq_cache_key = tiny_cache_uniq_key(conditions)
12
+
13
+ # 根据cache_key获取id
14
+ if iid = TinyCache.cache_store.read(uniq_cache_key)
15
+ begin
16
+ self.find iid
17
+ ensure ::ActiveRecord::RecordNotFound
18
+ nil
19
+ end
20
+ else
21
+ record = self.where(conditions).first
22
+ record.tap do |record|
23
+ TinyCache.cache_store.write(uniq_cache_key, record.id)
24
+ end if record
25
+ end
26
+ end
27
+
28
+ def tiny_cache_find_by! conditions={}
29
+ tiny_cache_find_by(conditions) || raise(::ActiveRecord::RecordNotFound)
30
+ end
31
+
32
+ private
33
+
34
+ def tiny_cache_uniq_key conditions={}
35
+ "#{::TinyCache::Config.cache_key_prefix}/model_uniq_keys/#{self.name}/#{conditions.sort.inspect}"
36
+ end
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,132 @@
1
+ # -*- encoding : utf-8 -*-
2
+ require "#{File.dirname(__FILE__)}/../arel/wheres"
3
+
4
+ module TinyCache
5
+ module ActiveRecord
6
+ module FinderMethods
7
+ extend ActiveSupport::Concern
8
+
9
+ included do
10
+ class_eval do
11
+ alias_method_chain :find_one, :tiny_cache
12
+ alias_method_chain :find_by_attributes, :tiny_cache
13
+ end
14
+ end
15
+
16
+ # TODO:
17
+ def find_by_attributes(match, attributes, *args)
18
+ conditions = Hash[attributes.map {|a| [a, args[attributes.index(a)]]}]
19
+ result = where(conditions).send(match.finder)
20
+
21
+ if match.bang? && result.nil?
22
+ raise RecordNotFound, "Couldn't find #{@klass.name} with #{conditions.to_a.collect {|p| p.join(' = ')}.join(', ')}"
23
+ else
24
+ yield(result) if block_given?
25
+ result
26
+ end
27
+ end
28
+
29
+ # TODO fetch multi ids
30
+ #
31
+ # Cacheable:
32
+ #
33
+ # current_user.articles.where(:status => 1).visiable.find(params[:id])
34
+ #
35
+ # Uncacheable:
36
+ #
37
+ # Article.where("user_id = '1'").find(params[:id])
38
+ # Article.where("user_id > 1").find(params[:id])
39
+ # Article.where("articles.user_id = 1").find(prams[:id])
40
+ # Article.where("user_id = 1 AND ...").find(params[:id])
41
+ def find_one_with_tiny_cache(id)
42
+ return find_one_without_tiny_cache(id) unless tiny_cache_enabled?
43
+ return find_one_without_tiny_cache(id) unless select_all_column?
44
+
45
+ id = id.id if ActiveRecord::Base === id
46
+
47
+ # if ::ActiveRecord::IdentityMap.enabled? && cachable? && record = from_identity_map(id)
48
+ # return record
49
+ # end
50
+
51
+ if cachable?
52
+ if record = @klass.read_tiny_cache(id)
53
+ return record
54
+ end
55
+ end
56
+
57
+ if cachable_without_conditions?
58
+ if record = @klass.read_tiny_cache(id)
59
+ return record if where_match_with_cache?(where_values, record)
60
+ end
61
+ end
62
+
63
+ record = find_one_without_tiny_cache(id)
64
+ record.write_tiny_cache
65
+ record
66
+ end
67
+
68
+ # TODO cache find_or_create_by_id
69
+ def find_by_attributes_with_tiny_cache(match, attributes, *args)
70
+ return find_by_attributes_without_tiny_cache(match, attributes, *args) unless tiny_cache_enabled?
71
+ return find_by_attributes_without_tiny_cache(match, attributes, *args) unless select_all_column?
72
+
73
+ conditions = Hash[attributes.map {|a| [a, args[attributes.index(a)]]}]
74
+
75
+ if conditions.has_key?("id")
76
+ result = wrap_bang(match.bang?) do
77
+ if conditions.size == 1
78
+ find_one_with_tiny_cache(conditions["id"])
79
+ else
80
+ where(conditions.except("id")).find_one_with_tiny_cache(conditions["id"])
81
+ end
82
+ end
83
+
84
+ yield(result) if block_given? #edge rails do this bug rails3.1.0 not
85
+
86
+ return result
87
+ end
88
+
89
+ find_by_attributes_without_tiny_cache(match, attributes, *args)
90
+ end
91
+
92
+ private
93
+
94
+ def wrap_bang(bang)
95
+ bang ? yield : (yield rescue nil)
96
+ end
97
+
98
+ def cachable?
99
+ where_values.blank? &&
100
+ limit_one? && order_values.blank? &&
101
+ includes_values.blank? && preload_values.blank? &&
102
+ readonly_value.nil? && joins_values.blank? && !@klass.locking_enabled?
103
+ end
104
+
105
+ def cachable_without_conditions?
106
+ limit_one? && order_values.blank? &&
107
+ includes_values.blank? && preload_values.blank? &&
108
+ readonly_value.nil? && joins_values.blank? && !@klass.locking_enabled?
109
+ end
110
+
111
+ def where_match_with_cache?(where_values, cache_record)
112
+ condition = TinyCache::Arel::Wheres.new(where_values)
113
+ return false unless condition.all_equality?
114
+ condition.extract_pairs.all? do |pair|
115
+ cache_record.read_attribute(pair[:left]) == pair[:right]
116
+ end
117
+ end
118
+
119
+ def limit_one?
120
+ limit_value.blank? || limit_value == 1
121
+ end
122
+
123
+ def select_all_column?
124
+ select_values.blank?
125
+ end
126
+
127
+ # def from_identity_map(id)
128
+ # ::ActiveRecord::IdentityMap.get(@klass, id)
129
+ # end
130
+ end
131
+ end
132
+ end