acts_as_method_cacheable 0.1.0 → 0.1.2

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,5 @@
1
+ language: ruby
2
+ rvm:
3
+ - "1.8.7"
4
+ - "1.9.3"
5
+ script: bundle exec rspec spec
data/Gemfile CHANGED
@@ -1,5 +1,5 @@
1
- #source 'https://rubygems.org'
2
- source 'http://ruby.taobao.org'
1
+ source 'https://rubygems.org'
2
+ #source 'http://ruby.taobao.org'
3
3
 
4
4
  # Specify your gem's dependencies in acts_as_method_cacheable.gemspec
5
5
  gemspec
data/README.md CHANGED
@@ -1,46 +1,64 @@
1
1
  ## acts_as_method_cacheable
2
2
 
3
- Instead of writing def expensive { @cached_expensive ||= original_expensive }, now you can write instance.cache_method(:expensive) instead. Also support nested cache method for associations.
4
- *This gem depend on ActiveSupport/ActiveRecord*
3
+ [![Gem Version](https://badge.fury.io/rb/acts_as_method_cacheable.png)](http://badge.fury.io/rb/acts_as_method_cacheable)
4
+ [![Build Status](https://travis-ci.org/bencao/acts_as_method_cacheable.png)](https://travis-ci.org/bencao/acts_as_method_cacheable)
5
+ [![Code Climate](https://codeclimate.com/repos/520482e556b10239110072c6/badges/5d7096d9ed502ba874ac/gpa.png)](https://codeclimate.com/repos/520482e556b10239110072c6/feed)
5
6
 
6
- ## Currernt Limitation
7
- *ONLY support method with no params*
8
-
9
- ## Installation
10
-
11
- Add this line to your application's Gemfile:
12
-
13
- gem 'acts_as_method_cacheable'
14
-
15
- And then execute:
7
+ Instead of writing
8
+ ```ruby
9
+ class Post < ActiveRecord::Base
10
+ def expensive_method
11
+ @cached_expensive ||= _expensive_method
12
+ end
16
13
 
17
- $ bundle
14
+ def _expensive_method
15
+ sleep 10
16
+ # blablabla
17
+ end
18
+ end
19
+ ```
20
+ now you can write
21
+ ```ruby
22
+ class Post < ActiveRecord::Base
23
+ def expensive_method
24
+ sleep 10
25
+ # blablabla
26
+ end
18
27
 
19
- Or install it yourself as:
28
+ acts_as_method_cacheable :methods => [:expensive_method]
29
+ end
30
+ ```
31
+ or cache method for an instance of Post only
32
+ ```ruby
33
+ post = Post.find xxx
34
+ post.cache_method(:expensive_method)
35
+ # post has_many comments
36
+ post.cache_method(:comments => :comment_expensive_method)
37
+ ```
20
38
 
21
- $ gem install acts_as_method_cacheable
39
+ *This gem depends on ActiveSupport/ActiveRecord*
22
40
 
23
41
  ## Usage
24
42
 
25
43
  ### cache method in class level, all instances will have the method cached
26
- *NOTE!! MUST put acts_as_method_cacheable in the last of the class file*
44
+ **NOTE!! MUST put acts_as_method_cacheable in the last of the class file**
27
45
  ```ruby
28
46
  class Post < ActiveRecord::Base
29
47
  def expensive_method
30
48
  # balababa
31
49
  end
32
- acts_as_method_cacheable :methods => :expensive_method
50
+ acts_as_method_cacheable :methods => [:expensive_method]
33
51
  end
34
52
  ```
35
53
 
36
- ### cache methods for a instance only
54
+ ### cache methods for an instance only
37
55
  ```ruby
38
56
  post = Post.find(xxx)
39
57
  post.cache_method(:expensive_method)
40
58
  post.cache_method([:expensive_method1, :expensive_method2])
41
59
  ```
42
60
 
43
- ### cache methods for a instance and its associations
61
+ ### cache methods for an instance and its associations
44
62
  ```ruby
45
63
  post = Post.find(xxx)
46
64
  post.cache_method([:expensive_method3, :comments => :comment_expensive_method])
@@ -65,3 +83,44 @@ post.expensive_method # cheap!
65
83
  post.reset_cache(:expensive_method)
66
84
  post.expensive_method # expensive
67
85
  ```
86
+
87
+ ## Installation
88
+
89
+ Add this line to your application's Gemfile:
90
+
91
+ gem 'acts_as_method_cacheable'
92
+
93
+ And then execute:
94
+
95
+ $ bundle
96
+
97
+ Or install it yourself as:
98
+
99
+ $ gem install acts_as_method_cacheable
100
+
101
+ ## Current Limitation
102
+ **ONLY support method with no params**
103
+
104
+ ## Contribute
105
+
106
+ You're highly welcome to improve this gem.
107
+
108
+ ### Checkout source code to local
109
+ say you git clone the source code to /tmp/acts_as_method_cacheable
110
+
111
+ ### Install dev bundle
112
+ ```bash
113
+ $ cd /tmp/acts_as_method_cacheable
114
+ $ bundle install
115
+ ```
116
+
117
+ ### Do some changes
118
+ ```bash
119
+ $ vi lib/acts_as_method_cacheable.rb
120
+ ```
121
+
122
+ ### Run test
123
+ ```bash
124
+ $ bundle exec rspec spec
125
+ ```
126
+
@@ -5,21 +5,12 @@ module ActsAsMethodCacheable
5
5
 
6
6
  included do
7
7
  def self.acts_as_method_cacheable(opts = {})
8
- unless class_variable_defined?(:@@cached_methods)
9
- cattr_accessor :cached_methods
10
- self.cached_methods = []
11
-
12
- attr_accessor :instance_cache_methods
13
-
14
- define_method "reload_with_cacheable" do |*params|
15
- cached_methods.each { |method| reset_cache(method) }
16
- self.instance_cache_methods && self.instance_cache_methods.each { |method| reset_cache(method) }
17
- reload_without_cacheable(params)
18
- end
19
-
20
- alias_method_chain :reload, :cacheable
21
-
8
+ unless class_variable_defined?(:@@class_level_cached_methods)
22
9
  include Internal
10
+ cattr_accessor :class_level_cached_methods
11
+ self.class_level_cached_methods = []
12
+ attr_accessor :instance_level_cached_methods
13
+ extend_reload_with_cacheable_support
23
14
  end
24
15
  [opts[:methods]].compact.flatten.each do |method|
25
16
  cache_method(method)
@@ -31,89 +22,71 @@ module ActsAsMethodCacheable
31
22
  extend ActiveSupport::Concern
32
23
 
33
24
  module ClassMethods
34
- # cache_method :expensive_method
35
- #
36
25
  # currently don't support method with params, it's complex to serialize params as a key
37
26
  # no param method covers most of our use cases
38
27
  private
39
28
  def cache_method(method)
40
- raise "#{method} not defined in class #{self.to_s}" unless method_defined?(method)
41
- raise "method with params is not supported by acts_as_method_cacheable yet!" unless self.new.method(method.to_sym).arity === 0
42
-
43
- return if self.cached_methods.include?(method)
44
-
45
- self.cached_methods.push(method)
46
-
29
+ return unless self.new.before_cache_method(method, true)
47
30
  define_method "#{method}_with_cacheable" do
48
- unless cache_var_defined?(method)
49
- cache_var_set(method, send("#{method}_without_cacheable".to_sym))
50
- end
31
+ cache_var_set(method, send("#{method}_without_cacheable".to_sym)) unless cache_var_defined?(method)
51
32
  cache_var_get(method)
52
33
  end
53
34
  alias_method_chain method, :cacheable
54
35
  end
55
36
 
37
+ def extend_reload_with_cacheable_support
38
+ define_method "reload_with_cacheable" do |*params|
39
+ class_level_cached_methods.each { |method| reset_cache(method) }
40
+ self.instance_level_cached_methods && self.instance_level_cached_methods.each { |method| reset_cache(method) }
41
+ reload_without_cacheable(params)
42
+ end
43
+ alias_method_chain :reload, :cacheable
44
+ end
56
45
  end
57
46
 
58
47
  def reset_cache(method)
59
48
  remove_instance_variable(cache_var_name(method)) if cache_var_defined?(method)
60
49
  end
61
50
 
62
- # instance version cache_method
63
- # cache_method(:expensive_method)
64
- # cache_method([:expensive_method, :expensive_method2])
65
- # cache_method([:expensive_method, :expensive_method2])
66
- # cache_method([:expensive_method, :sub_associations => :expensive_method2])
67
- def cache_method(args)
68
- normalized_args = args.is_a?(Array) ? args : [args]
69
-
70
- self_items = normalized_args.select{ |item| item.is_a?(Symbol) }
71
- self_items.each { |self_item| _cache_method(self_item) }
72
-
73
- children_items = normalized_args.select{ |item| item.is_a?(Hash) }
74
- children_items.each do |child_item|
75
- child, child_args = child_item.keys.first, child_item.values.first
76
- child_instances = send(child)
77
- child_instances = [child_instances] unless child_instances.respond_to?(:each)
78
- child_instances.each do |instance|
79
- instance.cache_method(child_args)
80
- end
51
+ def cache_method(params)
52
+ normalize_cachable_params(params) do |self_methods, association_methods|
53
+ self_methods.each { |method| _cache_method(method) }
54
+ association_methods.each{ |pair| cache_association_method(*pair.shift) }
81
55
  end
82
56
  self
83
57
  end
84
58
 
85
- private
86
-
87
- def cache_var_name(method)
88
- "@cached_var_for_method_#{method}".to_sym
59
+ def before_cache_method(method, class_level=false)
60
+ raise "#{method} not defined in class #{self.class.to_s}" unless self.class.method_defined?(method)
61
+ raise "method with params is not supported by acts_as_method_cacheable yet!" unless method(method.to_sym).arity === 0
62
+ self.instance_level_cached_methods ||= []
63
+ return false if class_level_cached_methods.include?(method)
64
+ return false if self.instance_level_cached_methods.include?(method)
65
+ (class_level ? class_level_cached_methods : self.instance_level_cached_methods).push(method)
66
+ true
89
67
  end
90
68
 
91
- def cache_var_set(method, value)
92
- instance_variable_set(cache_var_name(method), value)
93
- end
69
+ private
94
70
 
95
- def cache_var_get(method)
96
- instance_variable_get(cache_var_name(method))
71
+ def normalize_cachable_params(params, &block)
72
+ normalized_params = params.is_a?(Array) ? params : [params]
73
+ self_methods = normalized_params.select{ |item| item.is_a?(Symbol) }
74
+ association_methods = normalized_params.select{ |item| item.is_a?(Hash) }
75
+ yield self_methods, association_methods
97
76
  end
98
77
 
99
- def cache_var_defined?(method)
100
- instance_variable_defined?(cache_var_name(method))
78
+ def cache_association_method(association, association_params)
79
+ instances = send(association)
80
+ (instances.respond_to?(:each) ? instances : [instances]).each do |instance|
81
+ instance.cache_method(association_params)
82
+ end
101
83
  end
102
84
 
103
85
  # method which only accept a symbol as parameter
104
86
  def _cache_method(method)
105
- raise "#{method} not defined in class #{self.class.to_s}" unless self.class.method_defined?(method)
106
- raise "method with params is not supported by acts_as_method_cacheable yet!" unless method(method.to_sym).arity === 0
87
+ return unless before_cache_method(method)
107
88
 
108
- return if cached_methods.include?(method)
109
-
110
- self.instance_cache_methods ||= []
111
-
112
- return if self.instance_cache_methods.include?(method)
113
-
114
- self.instance_cache_methods.push(method)
115
-
116
- self.instance_eval <<-CACHEEND, __FILE__, __LINE__ + 1
89
+ instance_eval <<-CACHEEND, __FILE__, __LINE__ + 1
117
90
  class << self
118
91
  def #{method}
119
92
  unless cache_var_defined?(:#{method})
@@ -124,8 +97,23 @@ module ActsAsMethodCacheable
124
97
  end
125
98
  CACHEEND
126
99
  end
127
- end
128
100
 
101
+ def cache_var_name(method)
102
+ "@cached_var_for_method_#{method}".to_sym
103
+ end
104
+
105
+ def cache_var_set(method, value)
106
+ instance_variable_set(cache_var_name(method), value)
107
+ end
108
+
109
+ def cache_var_get(method)
110
+ instance_variable_get(cache_var_name(method))
111
+ end
112
+
113
+ def cache_var_defined?(method)
114
+ instance_variable_defined?(cache_var_name(method))
115
+ end
116
+ end
129
117
  end
130
118
 
131
119
  ActiveRecord::Base.send(:include, ActsAsMethodCacheable)
@@ -1,3 +1,3 @@
1
1
  module ActsAsMethodCacheable
2
- VERSION = "0.1.0"
2
+ VERSION = "0.1.2"
3
3
  end
@@ -14,10 +14,9 @@ class Schema < ActiveRecord::Migration
14
14
  t.references :post
15
15
  end
16
16
  end
17
+
17
18
  end
18
- schema = Schema.new
19
- schema.down
20
- schema.up
19
+ Schema.new.change
21
20
 
22
21
  require 'acts_as_method_cacheable'
23
22
 
@@ -194,6 +193,24 @@ describe ActsAsMethodCacheable do
194
193
  post.comment_signatures
195
194
  end
196
195
 
196
+ it "should clear cached after reset cache" do
197
+ post = Post.find(@post.id)
198
+ post.cache_method([:comment_dates, {:comments => :signature}])
199
+
200
+ comment1, comment2 = post.comments.to_a
201
+
202
+ comment1.expects(:date).once
203
+ post.comment_dates
204
+
205
+ comment1.expects(:date).never
206
+ post.comment_dates
207
+
208
+ post.reset_cache(:comment_dates)
209
+
210
+ comment1.expects(:date).once
211
+ post.comment_dates
212
+ end
213
+
197
214
  it "should raise exception when trying to cache a non-existing method" do
198
215
  expect {
199
216
  post = Post.find(@post.id)
@@ -3,7 +3,10 @@ require 'active_record'
3
3
  require 'sqlite3'
4
4
  require 'pry'
5
5
 
6
- ActiveRecord::Base.configurations = YAML::load(IO.read('db/database.yml'))
6
+ db_config = YAML::load(IO.read('db/database.yml'))
7
+ db_file = db_config['development']['database']
8
+ File.delete(db_file) if File.exists?(db_file)
9
+ ActiveRecord::Base.configurations = db_config
7
10
  ActiveRecord::Base.establish_connection('development')
8
11
 
9
12
  RSpec.configure do |config|
metadata CHANGED
@@ -1,18 +1,20 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: acts_as_method_cacheable
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.1.2
5
+ prerelease:
5
6
  platform: ruby
6
7
  authors:
7
8
  - Ben Cao
8
9
  autorequire:
9
10
  bindir: bin
10
11
  cert_chain: []
11
- date: 2013-04-09 00:00:00.000000000 Z
12
+ date: 2013-08-09 00:00:00.000000000 Z
12
13
  dependencies:
13
14
  - !ruby/object:Gem::Dependency
14
15
  name: bundler
15
16
  requirement: !ruby/object:Gem::Requirement
17
+ none: false
16
18
  requirements:
17
19
  - - ~>
18
20
  - !ruby/object:Gem::Version
@@ -20,6 +22,7 @@ dependencies:
20
22
  type: :development
21
23
  prerelease: false
22
24
  version_requirements: !ruby/object:Gem::Requirement
25
+ none: false
23
26
  requirements:
24
27
  - - ~>
25
28
  - !ruby/object:Gem::Version
@@ -27,6 +30,7 @@ dependencies:
27
30
  - !ruby/object:Gem::Dependency
28
31
  name: rake
29
32
  requirement: !ruby/object:Gem::Requirement
33
+ none: false
30
34
  requirements:
31
35
  - - ~>
32
36
  - !ruby/object:Gem::Version
@@ -34,6 +38,7 @@ dependencies:
34
38
  type: :development
35
39
  prerelease: false
36
40
  version_requirements: !ruby/object:Gem::Requirement
41
+ none: false
37
42
  requirements:
38
43
  - - ~>
39
44
  - !ruby/object:Gem::Version
@@ -41,6 +46,7 @@ dependencies:
41
46
  - !ruby/object:Gem::Dependency
42
47
  name: rspec
43
48
  requirement: !ruby/object:Gem::Requirement
49
+ none: false
44
50
  requirements:
45
51
  - - ~>
46
52
  - !ruby/object:Gem::Version
@@ -48,6 +54,7 @@ dependencies:
48
54
  type: :development
49
55
  prerelease: false
50
56
  version_requirements: !ruby/object:Gem::Requirement
57
+ none: false
51
58
  requirements:
52
59
  - - ~>
53
60
  - !ruby/object:Gem::Version
@@ -55,6 +62,7 @@ dependencies:
55
62
  - !ruby/object:Gem::Dependency
56
63
  name: mocha
57
64
  requirement: !ruby/object:Gem::Requirement
65
+ none: false
58
66
  requirements:
59
67
  - - ~>
60
68
  - !ruby/object:Gem::Version
@@ -62,6 +70,7 @@ dependencies:
62
70
  type: :development
63
71
  prerelease: false
64
72
  version_requirements: !ruby/object:Gem::Requirement
73
+ none: false
65
74
  requirements:
66
75
  - - ~>
67
76
  - !ruby/object:Gem::Version
@@ -69,6 +78,7 @@ dependencies:
69
78
  - !ruby/object:Gem::Dependency
70
79
  name: sqlite3
71
80
  requirement: !ruby/object:Gem::Requirement
81
+ none: false
72
82
  requirements:
73
83
  - - ~>
74
84
  - !ruby/object:Gem::Version
@@ -76,6 +86,7 @@ dependencies:
76
86
  type: :development
77
87
  prerelease: false
78
88
  version_requirements: !ruby/object:Gem::Requirement
89
+ none: false
79
90
  requirements:
80
91
  - - ~>
81
92
  - !ruby/object:Gem::Version
@@ -83,48 +94,55 @@ dependencies:
83
94
  - !ruby/object:Gem::Dependency
84
95
  name: pry
85
96
  requirement: !ruby/object:Gem::Requirement
97
+ none: false
86
98
  requirements:
87
- - - '>='
99
+ - - ! '>='
88
100
  - !ruby/object:Gem::Version
89
101
  version: '0'
90
102
  type: :development
91
103
  prerelease: false
92
104
  version_requirements: !ruby/object:Gem::Requirement
105
+ none: false
93
106
  requirements:
94
- - - '>='
107
+ - - ! '>='
95
108
  - !ruby/object:Gem::Version
96
109
  version: '0'
97
110
  - !ruby/object:Gem::Dependency
98
111
  name: pry-theme
99
112
  requirement: !ruby/object:Gem::Requirement
113
+ none: false
100
114
  requirements:
101
- - - '>='
115
+ - - ! '>='
102
116
  - !ruby/object:Gem::Version
103
117
  version: '0'
104
118
  type: :development
105
119
  prerelease: false
106
120
  version_requirements: !ruby/object:Gem::Requirement
121
+ none: false
107
122
  requirements:
108
- - - '>='
123
+ - - ! '>='
109
124
  - !ruby/object:Gem::Version
110
125
  version: '0'
111
126
  - !ruby/object:Gem::Dependency
112
127
  name: pry-nav
113
128
  requirement: !ruby/object:Gem::Requirement
129
+ none: false
114
130
  requirements:
115
- - - '>='
131
+ - - ! '>='
116
132
  - !ruby/object:Gem::Version
117
133
  version: '0'
118
134
  type: :development
119
135
  prerelease: false
120
136
  version_requirements: !ruby/object:Gem::Requirement
137
+ none: false
121
138
  requirements:
122
- - - '>='
139
+ - - ! '>='
123
140
  - !ruby/object:Gem::Version
124
141
  version: '0'
125
142
  - !ruby/object:Gem::Dependency
126
143
  name: activesupport
127
144
  requirement: !ruby/object:Gem::Requirement
145
+ none: false
128
146
  requirements:
129
147
  - - ~>
130
148
  - !ruby/object:Gem::Version
@@ -132,6 +150,7 @@ dependencies:
132
150
  type: :runtime
133
151
  prerelease: false
134
152
  version_requirements: !ruby/object:Gem::Requirement
153
+ none: false
135
154
  requirements:
136
155
  - - ~>
137
156
  - !ruby/object:Gem::Version
@@ -139,6 +158,7 @@ dependencies:
139
158
  - !ruby/object:Gem::Dependency
140
159
  name: activerecord
141
160
  requirement: !ruby/object:Gem::Requirement
161
+ none: false
142
162
  requirements:
143
163
  - - ~>
144
164
  - !ruby/object:Gem::Version
@@ -146,6 +166,7 @@ dependencies:
146
166
  type: :runtime
147
167
  prerelease: false
148
168
  version_requirements: !ruby/object:Gem::Requirement
169
+ none: false
149
170
  requirements:
150
171
  - - ~>
151
172
  - !ruby/object:Gem::Version
@@ -158,6 +179,7 @@ extensions: []
158
179
  extra_rdoc_files: []
159
180
  files:
160
181
  - .gitignore
182
+ - .travis.yml
161
183
  - Gemfile
162
184
  - LICENSE.txt
163
185
  - README.md
@@ -171,26 +193,27 @@ files:
171
193
  homepage: https://github.com/bencao/acts_as_method_cacheable
172
194
  licenses:
173
195
  - MIT
174
- metadata: {}
175
196
  post_install_message:
176
197
  rdoc_options: []
177
198
  require_paths:
178
199
  - lib
179
200
  required_ruby_version: !ruby/object:Gem::Requirement
201
+ none: false
180
202
  requirements:
181
- - - '>='
203
+ - - ! '>='
182
204
  - !ruby/object:Gem::Version
183
205
  version: '0'
184
206
  required_rubygems_version: !ruby/object:Gem::Requirement
207
+ none: false
185
208
  requirements:
186
- - - '>='
209
+ - - ! '>='
187
210
  - !ruby/object:Gem::Version
188
211
  version: '0'
189
212
  requirements: []
190
213
  rubyforge_project:
191
- rubygems_version: 2.0.0.rc.2
214
+ rubygems_version: 1.8.25
192
215
  signing_key:
193
- specification_version: 4
216
+ specification_version: 3
194
217
  summary: Instead of writing def expensive { @cached_expensive ||= original_expensive
195
218
  }, now you can write instance.cache_method(:expensive) instead. Also support nested
196
219
  cache method for associations.
checksums.yaml DELETED
@@ -1,7 +0,0 @@
1
- ---
2
- SHA1:
3
- metadata.gz: f01e9ffdf60462d98a0e9d5455058695747e9868
4
- data.tar.gz: 0aeeb3f23e738298155f413a5db56f35f6a027d6
5
- SHA512:
6
- metadata.gz: 450aa54f8f59e259f089ae21b5284efde4190ad6113df9f2f6f6a4514e4eba18c0df5f0e41631c181ea32924c00672303694772c2d429a87985d7ff93e69fcfc
7
- data.tar.gz: dd58518b62a70508fe5512b1281112533e467df3abba958d553143dd2769862fcfdcad20775ad086e5b0a1c09dcd6c59d32cab2871136dc353488d8343f8af45