acts_as_method_cacheable 0.1.0 → 0.1.2

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.
@@ -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