covercache 0.1.0 → 0.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.
checksums.yaml CHANGED
@@ -1,15 +1,15 @@
1
1
  ---
2
2
  !binary "U0hBMQ==":
3
3
  metadata.gz: !binary |-
4
- YzU2NzM4MzljZGIzOTkyMzU2ZGYwMDBkN2Q2YzNjODQ4YmQxNDE4NQ==
4
+ YjlhOGViNGViNzYwZDEyNjg3ZjQ1N2ZhNjFiYWY3OGJmMTY5ODVmNw==
5
5
  data.tar.gz: !binary |-
6
- NDNhOTk2NmVhMjExZjcxNDhhNDJhOGY1MDA1YWE1Nzg0YmQ5N2YwNA==
6
+ MmE3MDFlNmU0ZDQ4NDVjNjEwN2EwOWQxMjI5NDVmY2FiODA3ZjVmYQ==
7
7
  !binary "U0hBNTEy":
8
8
  metadata.gz: !binary |-
9
- YThkMDcwYzE1Y2YzZWY2MWM4MTU4M2ZmMWY5NTFmNmZjMWEwYzVlNjJjNGEy
10
- ZjYzZjU0ZmM1Zjk5ZTlmMWUzOGEwMWNlOThlNWFkZjAyMzVhMGFiYzQ2NTlk
11
- M2EwNWViMDRjNTNhZjQ2OWJiODA5YWM5YmIyNDJiM2QwNzZmYzQ=
9
+ NjM3YmJmN2NjMzI2NzBlYjU5MDQ5ZmI5YjBhY2E4MDllY2I1MTM4YzdhMWE0
10
+ YjQ4M2EzYTk3ZjJjODZkMGYwYTYwMDIxY2Y0Yzk3OGI0ZTczOWI5NWE3N2Vj
11
+ ZTJjZDEyZThjMDNmOTY1MGMyYzE2YTY3ZGJkMjllMDlmZmEwZWY=
12
12
  data.tar.gz: !binary |-
13
- ODE1MTRlYzMzZjc4MTdkZjE1MzhmZDc4MGJmYmRkODdiZTZkYTBlMTUzZmVk
14
- ZjFhZjkyNmUyNjFjNjgyMGFlY2JlZWI0MzU0MDVlNGE1ODZiZWRmODQ1ZGMz
15
- ZGIwNzIxMWU0YzgwOWE3MmYzZDQ2MGZjNGMzMTFjOGE5MDE1Yzk=
13
+ MmQ5OTllYzMwZmUyMmZlYzk2MmUyNzMyOGEyMmI1ZWY2OTg3ZDkxNjBiYzBk
14
+ NmNjMTE4ZDA1ZGMxMjU1MGFhZGE0ZDIwZjZiZDY1ZGQ3Zjc4OTEzM2EzMzlk
15
+ N2YxOTRkYjY2ZjE3ZTFkMjhiMjEyYjE3MjcyYjRhMmM2OWNiOTY=
data/README.md CHANGED
@@ -1,5 +1,7 @@
1
1
  # Covercache
2
2
 
3
+ [![Code Climate](https://codeclimate.com/github/estum/covercache.png)](https://codeclimate.com/github/estum/covercache)
4
+
3
5
  Covercache is a helper for Rails.cache, based on [PackRat](https://github.com/cpuguy83/pack_rat) gem, and, as it says: <br />
4
6
  > When included in your class it makes caching trivial.
5
7
 
@@ -59,7 +61,7 @@ You can easily wrap methods with the <tt>define_cached</tt> helper.<br />
59
61
  Note, that Relation objects couldn't be cached, so wrapped method or block
60
62
  should return an array or single instance of your model.
61
63
 
62
- To wrap instance methods you should use a name of already defined method:
64
+ To wrap instance methods you should use a name of already defined method or set block with the first argument as record:
63
65
 
64
66
  class Post < ActiveRecord::Base
65
67
  def all_comments
@@ -72,7 +74,10 @@ To wrap instance methods you should use a name of already defined method:
72
74
  # You can add arguments like for covercache:
73
75
  # [keys], debug: true, expires_in: 10.minutes
74
76
  define_cached :all_comments, expires_in: 1.minute
75
-
77
+
78
+ define_cached :all_comments_authors, debug: true, do |record|
79
+ record.author
80
+ end
76
81
  # ...
77
82
  end
78
83
 
data/covercache.gemspec CHANGED
@@ -10,7 +10,7 @@ Gem::Specification.new do |spec|
10
10
  spec.email = ["anton.estum@gmail.com"]
11
11
  spec.description = %q{Helper method to simplify Rails caching, based on PackRat}
12
12
  spec.summary = %q{Rails cache helper based on PackRat gem}
13
- spec.homepage = "http://github.com/tonissimo/covercache"
13
+ spec.homepage = "http://github.com/estum/covercache"
14
14
  spec.license = "MIT"
15
15
 
16
16
  spec.files = `git ls-files`.split($/)
@@ -1,3 +1,3 @@
1
1
  module Covercache
2
- VERSION = "0.1.0"
2
+ VERSION = "0.2"
3
3
  end
data/lib/covercache.rb CHANGED
@@ -4,8 +4,6 @@ require 'active_support/core_ext'
4
4
  require 'active_support/concern'
5
5
  require 'active_record'
6
6
 
7
- require "where"
8
-
9
7
  # == Motivation
10
8
  #
11
9
  # * Quickly use <tt>Rails.cache</tt> for some tasks without rubbish code in models.
@@ -22,7 +20,7 @@ require "where"
22
20
  # Note, that Relation objects couldn't be cached, so wrapped method or block
23
21
  # should return an array or single instance of your model.
24
22
  #
25
- # To wrap instance methods you should use a name of already defined method:
23
+ # To wrap instance methods you should use a name of already defined method or set block with the first argument as record:
26
24
  #
27
25
  # class Post < ActiveRecord::Base
28
26
  # def all_comments
@@ -35,13 +33,16 @@ require "where"
35
33
  # # You can add arguments like for covercache: [keys], debug: true, expires_in: 10.minutes
36
34
  # define_cached :all_comments, expires_in: 1.minute
37
35
  #
36
+ # define_cached :all_comments_authors, expires_in: 1.minute do |record|
37
+ # record.author
38
+ # end
38
39
  # # ...
39
40
  # end
40
41
  #
41
42
  # post = Post.find(1)
42
43
  # post.cached_all_comments
43
44
  #
44
- # To wrap class methods you can also use use a block:
45
+ # To wrap class methods:
45
46
  #
46
47
  # class_define_cached :for_post_ids, debug: true, expires_in: 10.minutes do |post_ids|
47
48
  # where(post_id: post_ids).all
@@ -52,85 +53,119 @@ require "where"
52
53
  #
53
54
  module Covercache
54
55
  # General helper method (ex <tt>cache</tt> helper in PackRat)
55
- module Base
56
- private # ?
57
- def covercache(*args, &block)
58
- options = args.extract_options!
59
- key = []
60
- should_debug = options[:debug].present? and options[:debug]
61
-
62
- klass = covercaching_class_method? ? self : self.class
63
-
64
- # Remove helper related options so we can pass to Rails.cache
65
- filtered_options = options.except(:debug, :overwrite_key)
56
+ module Base
57
+ # Arguments:
58
+ #
59
+ # [1..] any count of custom *keys
60
+ # :debug #=>
61
+ # :without_auto_key #=> don't generate cache_key
62
+ # any option for Rails.cache.fetch
63
+ #
64
+ # Example:
65
+ #
66
+ # scope :published, -> { where published: true }
67
+ # has_many :comments
68
+ #
69
+ # def cached_comments
70
+ # covercache do
71
+ # comments.all
72
+ # end
73
+ # end
74
+ #
75
+ # def self.cached_published
76
+ # covercache debug:true do
77
+ # published.all
78
+ # end
79
+ # end
80
+ #
81
+ private
82
+ def covercache(*keys, &block)
83
+ klass = class_or_instance_class
84
+ options = keys.extract_options!
85
+ cover_opts = options.extract! :debug, :without_auto_key
66
86
 
67
- # if overwrite_key was set, we skip creating our own key
68
- unless options[:overwrite_key]
69
- model_digest = covercaching_get_model_digest
70
- # Hack to get the method that called cache
71
- calling_method = caller[0][/`([^']*)'/, 1]
72
-
73
- key << klass.name
74
- key << [model_digest, calling_method].compact.join('/')
75
- key << cache_key if self.respond_to?(:cache_key)
76
- key += args
77
- end
78
- # Output the generated cache key to the console if debug is set
79
- puts key.inspect if should_debug
80
- # Make the actual Rails.cache call
81
- Rails.cache.fetch key, filtered_options do
82
- klass.covercache_keys << key unless key.in?(klass.covercache_keys)
87
+ # if :no_auto_cache_keys was set, we skip creating our own key
88
+ keys.prepend get_auto_cache_key(klass.name, caller) unless cover_opts[:without_auto_key]
89
+ keys.flatten!
90
+ puts keys.inspect if !!cover_opts[:debug]
91
+ # puts caller.inspect if !!cover_opts[:debug],
92
+
93
+ Rails.cache.fetch keys, options do
94
+ klass.covercache_keys |= [ keys ]
83
95
  block.call
84
96
  end
85
97
  end
86
-
87
- def covercaching_get_model_digest
88
- self.covercache_model_digest.presence
98
+
99
+ def get_auto_cache_key(class_name, _caller)
100
+ caller_method = _caller.map {|c| c[/`([^']*)'/, 1] }.detect {|m| !m.start_with?('block') }
101
+ puts caller_method.inspect
102
+ [ class_name, covercache_model_digest, caller_method, (cache_key if self.respond_to?(:cache_key?)) ].compact
103
+ end
104
+
105
+ def class_or_instance_class
106
+ self.is_a?(Class) ? self : self.class
89
107
  end
90
108
 
91
- def covercaching_class_method?
92
- self.is_a? Class
109
+ def extract_cache_key_from(*args)
110
+ Array.wrap((args.last.delete(:cache_key) if args.last.is_a?(Hash)))
93
111
  end
94
112
  end
95
113
 
96
114
  # == Defining Helper
97
115
  #
98
- # TODO: share logic
99
- #
100
116
  module DefiningHelper
101
117
  # Define and wrap methods or blocks
102
- def define_cached(*args)
103
- method_name = args.shift
104
- opts = args.extract_options!
105
- # method definition
106
- define_method :"cached_#{method_name}" do |*method_args|
107
- if method_args.last.is_a?(Hash) and method_args.last.has_key?(:cache_key)
108
- add_to_args = method_args.last.delete(:cache_key)
109
- args += [add_to_args].flatten if add_to_args.present?
110
- end
111
- covercache(*args,opts){ self.send method_name, *method_args }
112
- end
118
+ def define_cached(method, *args, &block)
119
+ options = args.extract_options!
120
+ is_class_method = !!options.delete(:is_class_method)
121
+
122
+ file, line = caller[is_class_method ? 1 : 0].split ':', 2
123
+ line = line.to_i
124
+
125
+ covercache_method_arguments method, args, options, &block
126
+ covercache_define_wrapper method, file, line, block_given?, is_class_method
127
+ end
128
+
129
+ def class_define_cached(method, *args, &block)
130
+ options = args.extract_options!
131
+ options[:is_class_method] = true
132
+ self.send :define_cached, method, *args, options, &block
113
133
  end
114
134
 
115
- # TODO: not good
116
- def class_define_cached(*args)
117
- (class << self; self; end).instance_eval do
118
- method_name = args.shift
119
- opts = args.extract_options!
120
- # method definition
121
- define_method :"cached_#{method_name}" do |*method_args|
122
- if method_args.last.is_a?(Hash) and method_args.last.has_key?(:cache_key)
123
- add_to_args = method_args.last.delete :cache_key
124
- args += [add_to_args].flatten if add_to_args.present?
125
- end
126
- covercache(*args, opts) do
127
- block_given? ? yield(*method_args) : self.send(method_name, *method_args)
135
+ private
136
+ def covercache_define_wrapper(method, file, line, with_block=false, is_class_method=false)
137
+ cached_method = "cached_#{method}"
138
+
139
+ class_eval(<<-EOS, file, line - 2)
140
+ def #{'self.' if is_class_method}#{cached_method}(*args, &block)
141
+ settings = Array.wrap(#{cached_method}_settings[:args]) + extract_cache_key_from(*args)
142
+ covercache(*settings, #{cached_method}_settings[:opts]) do
143
+ if #{with_block}
144
+ #{cached_method}_settings[:block].call(#{'self,' unless is_class_method}*args)
145
+ else
146
+ self.__send__ :#{method}, *args, &block
147
+ end
128
148
  end
129
149
  end
130
- end
150
+ EOS
151
+ end
152
+
153
+ def covercache_method_arguments(method, *args, &block)
154
+ settings = collect_method_args_to_hash(*args, &block)
155
+ puts settings.inspect
156
+ class_attribute :"cached_#{method}_settings"
157
+ self.send(:"cached_#{method}_settings=", settings)
158
+ end
159
+
160
+ def collect_method_args_to_hash(*args, &block)
161
+ puts args.inspect
162
+ Hash[ %w{args opts block}.map do |x|
163
+ [ x, (args.shift || block) ]
164
+ end].to_options
131
165
  end
132
166
  end
133
167
 
168
+
134
169
  # Extend and Include to model Base helper so cache method is available in all contexts.
135
170
  # <em>(yes, it is form PackRat too)</em>
136
171
  module ModelConcern
@@ -143,7 +178,7 @@ module Covercache
143
178
  end
144
179
 
145
180
  self.covercache_keys ||= []
146
- self.covercache_model_source = Where.is_class self, of: 'app/models'
181
+ self.covercache_model_source ||= @covercache_caller_source #Where.is_class self, of: 'app/models'
147
182
 
148
183
  generate_model_digest!
149
184
 
@@ -165,14 +200,12 @@ module Covercache
165
200
 
166
201
  # Generates and sets file_digest attribute
167
202
  def generate_model_digest!
168
- self.covercache_model_digest = self.generate_model_digest
203
+ self.covercache_model_digest = generate_model_digest
169
204
  end
170
205
 
171
206
  def covercache_flush!
172
- self.covercache_keys.each do |key|
173
- Rails.cache.delete(key) # if Rails.cache.exist?(key)
174
- end.clear
175
- self.covercache_keys.empty?
207
+ self.covercache_keys.each { |key| Rails.cache.delete(key) }.clear # if Rails.cache.exist?(key)
208
+ covercache_keys.empty?
176
209
  end
177
210
  end
178
211
 
@@ -185,7 +218,10 @@ module Covercache
185
218
  # module CoversWithCache
186
219
  # add Covercache supporting to model
187
220
  def covers_with_cache
221
+ caller_source = caller.first[/[^:]+/]
222
+
188
223
  class_eval do
224
+ @covercache_caller_source = caller_source
189
225
  include Covercache::ModelConcern
190
226
  extend Covercache::DefiningHelper
191
227
  end
Binary file
@@ -5,10 +5,6 @@ describe "covercache" do
5
5
  it 'should respond to covers_with_cache' do
6
6
  Post.should respond_to(:covers_with_cache)
7
7
  end
8
- it 'should hove class keys storage' do
9
- pp Post.covercache_keys
10
- Post.covercache_keys.should be_an(Array)
11
- end
12
8
  it 'should have covercache_model_source attribute' do
13
9
  pp Post.covercache_model_source
14
10
  Post.covercache_model_source.should be_an(String)
@@ -36,4 +32,12 @@ describe "covercache" do
36
32
  pp test.cached_comments.inspect
37
33
  test.cached_comments.count.should == test.comments.count
38
34
  end
35
+ it 'post should have class keys storage' do
36
+ pp Post.covercache_keys
37
+ Post.covercache_keys.should be_an(Array)
38
+ end
39
+ it 'comments should have class keys storage' do
40
+ pp Comment.covercache_keys
41
+ Comment.covercache_keys.should be_an(Array)
42
+ end
39
43
  end
data/spec/spec_helper.rb CHANGED
@@ -62,17 +62,20 @@ end
62
62
  class Post < ActiveRecord::Base
63
63
  has_many :comments
64
64
  covers_with_cache
65
- def cached_comments
66
- covercache debug: true do
67
- comments.all
68
- end
69
- end
65
+ define_cached :comments, debug: true
66
+ # def cached_comments
67
+ # covercache debug: true do
68
+ # comments.all
69
+ # end
70
+ # end
70
71
  end
71
72
 
72
73
  class Comment < ActiveRecord::Base
73
74
  belongs_to :post
74
75
  covers_with_cache
75
- define_cached :post, debug: true
76
+ define_cached :post, debug: true do |record|
77
+ record.post
78
+ end
76
79
  class_define_cached :for_post, debug: true do |post_id|
77
80
  where(post_id: post_id).all
78
81
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: covercache
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: '0.2'
5
5
  platform: ruby
6
6
  authors:
7
7
  - Tõnis Simo
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2013-05-20 00:00:00.000000000 Z
12
+ date: 2013-05-21 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: bundler
@@ -139,11 +139,10 @@ files:
139
139
  - covercache.gemspec
140
140
  - lib/covercache.rb
141
141
  - lib/covercache/version.rb
142
- - lib/where.rb
143
142
  - spec/covercache.sqlite3
144
143
  - spec/libs/covercache_spec.rb
145
144
  - spec/spec_helper.rb
146
- homepage: http://github.com/tonissimo/covercache
145
+ homepage: http://github.com/estum/covercache
147
146
  licenses:
148
147
  - MIT
149
148
  metadata: {}
data/lib/where.rb DELETED
@@ -1,69 +0,0 @@
1
- # A little Ruby module for finding the source location where class and methods are defined.
2
- # https://gist.github.com/wtaysom/1236979
3
-
4
- module Where
5
- #== Example
6
- #
7
- # Where.is_class Post, of: 'app/models'
8
- #
9
- class <<self
10
- def is_class(klass, opts={})
11
- methods = defined_methods(klass)
12
- file_groups = methods.group_by{|sl| sl[0]}
13
- file_counts = file_groups.map do |file, sls|
14
- lines = sls.map{|sl| sl[1]}
15
- count = lines.size
16
- line = lines.min
17
- {file: file, count: count, line: line}
18
- end
19
- file_counts.sort_by!{|fc| fc[:count]}
20
-
21
- if opts[:of].present?
22
- path_expand = joins_path(opts[:of])
23
- return File.expand_path("../../spec/spec_helper.rb", __FILE__) unless !!path_expand
24
- pattern = Regexp.new "^#{path_expand}/.*"
25
- matches = file_counts.select do |fc|
26
- !!fc[:file][pattern]
27
- end
28
- return matches.first[:file] if matches.first.present?
29
- end
30
-
31
- source_locations = file_counts.map{|fc| [fc[:file], fc[:line]]}
32
- source_locations
33
- end
34
-
35
- private
36
- def joins_path(of)
37
- Rails.root.join(of).to_s
38
- rescue NameError
39
- return false
40
- end
41
-
42
- def source_location(method)
43
- method.source_location || (
44
- method.to_s =~ /: (.*)>/
45
- $1
46
- )
47
- end
48
-
49
- def defined_methods(klass)
50
- methods = klass.methods(false).map{|m| klass.method(m)} +
51
- klass.instance_methods(false).map{|m| klass.instance_method(m)}
52
- methods.map!(&:source_location)
53
- methods.compact!
54
- methods
55
- end
56
- end
57
- end
58
-
59
- def where_is(klass, method = nil)
60
- Where.edit(if method
61
- begin
62
- Where.is_instance_method(klass, method)
63
- rescue NameError
64
- Where.is_method(klass, method)
65
- end
66
- else
67
- Where.is_class_primarily(klass)
68
- end)
69
- end