activerecord-cached_at 5.2.1.0 → 6.0.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 239749f64d6cfea60e5980c0f47fb9bbaa96eca7fa416bed82ba40d0f17fa284
4
- data.tar.gz: 161a47ae7ba00f3bb112b24bf26f69f5c9604a0bbf2558f6b34e269a62e524ea
3
+ metadata.gz: d0d9e3d63dbd18d8baab8017ad304e44f55b73ebe42060e0723438b252dc4cfe
4
+ data.tar.gz: 1c6cbb926a39f8c2cabe19a44d918e063df2f6278d443ea3d13ffae37b28e608
5
5
  SHA512:
6
- metadata.gz: e4076c01c2720e82212537c1575b6e6d5866b6be0818d6bbbdf0be80ec034613130861fe23e5b5f2e53ad84153ce9b5376df15feb7f734d0e15b466b62763f69
7
- data.tar.gz: eec3533e19885cffc9830ce8a6ce9e012e675ffb172aad80bb681554e8320479bc02f3f58438d7a4e75744cefd73d177ff7ac167f6f81c70da8603eeb0fee20a
6
+ metadata.gz: bfafa9a17b95d329a28b0b7b3cc2c570b5cb206b9e9ab51a653f263b971fa59e5d56b4ecc87db02c1a0a526e98ff2ef3012ccc49b31b73da3f7c65cec9d7fc5b
7
+ data.tar.gz: 1311efa1ca1b808d636739c417ceb5561976e9d6565096ba871a51bed9e2faa40110de75a000c48d93e51e63e3022f523cdfd7fb05e956e9a82e734c81c704fa
data/README.md CHANGED
@@ -1,15 +1,16 @@
1
- # ActiveRecord - CachedAt
1
+ # ActiveRecord - CachedAt [![Travis CI](https://travis-ci.org/malomalo/activerecord-cached_at.svg?branch=master)](https://travis-ci.org/malomalo/activerecord-cached_at)
2
2
 
3
3
  This gem causes ActiveRecord to update a `cached_at` column if present, like the
4
4
  `updated_at` column.
5
5
 
6
6
  When calculating a `cache_key` for a model it will also consider the `cached_at`
7
- column to determin the key of a model.
7
+ column to determine the key of a model.
8
8
 
9
9
  Any `ActiveRecord::Migration` that calls `timestamps` will include a `cached_at`
10
10
  column.
11
11
 
12
- TODO: Document about relationship caches
12
+ Call to [`ActiveRecord::Persistence::touch`](https://api.rubyonrails.org/classes/ActiveRecord/Persistence.html#method-i-touch)
13
+ will also touch the `cached_at` column.
13
14
 
14
15
  ## Installation
15
16
 
@@ -22,12 +23,94 @@ aren't updating the models you can just require the helpers:
22
23
 
23
24
  gem 'activerecord-cached_at', require: 'cached_at/helpers'
24
25
 
26
+ ## Configuration
25
27
 
28
+ By default updates to the `cached_at`, `updated_at`, and `created_at` columns
29
+ will not trigger and update to the `cached_at` column. You can add aditional
30
+ fields to ignore:
31
+
32
+ ```ruby
33
+ class User
34
+ cached_at ignore: :my_column
35
+ end
36
+
37
+ class Photo
38
+ cached_at ignore: :column_a, :column_b
39
+ end
40
+ ```
41
+ ## Relationship Cache Keys
42
+
43
+ CachedAt also allows you to keep cache keys for relationships. This allows you
44
+ to use the record to determine if a cache is valid for a relationship instead
45
+ of doing another database query.
46
+
47
+ For example:
48
+
49
+ ```ruby
50
+ class User < ActiveRecord::Base
51
+ has_many :photos
52
+ end
53
+
54
+ class Photo
55
+ belongs_to :user, cached_at: true
56
+ end
57
+
58
+ bob_ross = User.create(name: 'Bob Ross')
59
+ # => INSERT INTO "users"
60
+ # ("name", "cached_at", "updated_at_", "created_at")
61
+ # VALUES
62
+ # ("Bob Ross", "2020-07-19 20:22:03", "2020-07-19 20:22:03", "2020-07-19 20:22:03")
63
+
64
+ photo = Photo.create(user: bob_ross, file: ...)
65
+ # =>INSERT INTO "photos" ("user_id", "cached_at", "updated_at_", "created_at") VALUES (1, "Bob Ross", "2020-07-19 20:22:04", "2020-07-19 20:22:04", "2020-07-19 20:22:04")
66
+ # => UPDATE "users" SET "photos_cached_at" = "2020-07-19 20:22:04" WHERE "users"."id" = 1
67
+
68
+ photo.update(file: ...)
69
+ # =>UPDATE "photos" (..., "cached_at", "updated_at_") VALUES (..., "2020-07-19 20:22:05", "2020-07-19 20:22:05", "2020-07-19 20:22:05")
70
+ # => UPDATE "users" SET "photos_cached_at" = "2020-07-19 20:22:05" WHERE "users"."id" = 1
71
+
72
+ photo.update(user: not_bob_ross)
73
+ # =>UPDATE "photos" ("user_id", "cached_at", "updated_at_") VALUES (2, "2020-07-19 20:22:06", "2020-07-19 20:22:06", "2020-07-19 20:22:06")
74
+ # => UPDATE "users" SET "photos_cached_at" = "2020-07-19 20:22:06" WHERE "users"."id" IN (1, 2)
75
+
76
+ photo.destroy
77
+ # => UPDATE "users" SET "photos_cached_at" = "2020-07-19 20:22:07" WHERE "users"."id" = 2
78
+ # => DELETE FROM "users" WHERE WHERE "users"."id" = 2
79
+ ```
80
+
81
+ # Usage
82
+
83
+ `cached_at` will automatically be used for determining the cache key in Rails.
84
+
85
+ However if you need to calculate the cache key based on relationship cache keys
86
+ you will need to manually compute the cache key. Examples are below:
87
+
88
+ The cache key here is the maxium of the following keys: `cached_at`,
89
+ `listings_cached_at`, and `photos_cached_at`
90
+
91
+ ```erb
92
+ <%= render partial: 'row', collection: @properties, as: :property, cached: Proc.new { |item|
93
+ [item.cache_key_with_version(:listings, :photos), current_account.id ]
94
+ } %>
95
+
96
+ <% cache @property.cache_key_with_version(:listings, :photos) do %>
97
+ <b>All the info on this property</b>
98
+ <%= @property.name %>
99
+ <% @property.listings.each do |listing| %>
100
+ <%= listing.info %>
101
+ <% end %>
102
+ <% @property.photos.each do |photo| %>
103
+ <%= image_tag(photo.url) %>
104
+ <% end %>
105
+ <% end %>
106
+
107
+ ```
26
108
  ## TODO:
27
109
 
28
- Add a `cache_key` method to the Model class that gets `MAX(cached_at)`
110
+ * Document going more than one level with cached_at keys
29
111
 
112
+ * Add a `cache_key` method to the Model class that gets `MAX(cached_at)`
30
113
 
114
+ * change option to cache: true
31
115
 
32
- change option to cache: true
33
- add cache_association helper
116
+ * add cache_association helper
@@ -13,20 +13,34 @@ module CachedAt
13
13
  extend ActiveSupport::Concern
14
14
 
15
15
  included do
16
+ class_attribute :cached_at_settings, default: {ignore: ['cached_at', 'updated_at', 'created_at']}
17
+ before_save :set_cached_at
16
18
  before_save :update_belongs_to_cached_at_keys
17
19
  before_destroy { update_relations_cached_at(method: :destroy) }
18
20
 
19
21
  after_touch { update_relations_cached_at_from_cached_at(method: :touch) }
20
- after_save :update_relations_cached_at_from_cached_at
22
+ after_save :update_relations_cached_at_from_cached_at
23
+ end
24
+
25
+ class_methods do
26
+ def cached_at(ignore: [])
27
+ ignore = [ignore] if !ignore.is_a?(Array)
28
+ self.cached_at_settings[:ignore].push(*ignore.map(&:to_s))
29
+ end
30
+ end
31
+
32
+ def touch(*names, time: nil)
33
+ names.push('cached_at')
34
+ super(*names, time: time)
21
35
  end
22
36
 
23
37
  private
24
38
 
25
39
  def update_relations_cached_at_from_cached_at(method: nil)
26
- update_relations_cached_at({
40
+ update_relations_cached_at(
27
41
  timestamp: (self.class.column_names.include?('cached_at') ? cached_at : nil),
28
42
  method: method
29
- })
43
+ )
30
44
  end
31
45
 
32
46
  def update_relations_cached_at(timestamp: nil, method: nil)
@@ -49,6 +63,14 @@ module CachedAt
49
63
  end
50
64
  end
51
65
 
66
+ def set_cached_at
67
+ return if !self.class.column_names.include?('cached_at')
68
+ diff = changes.transform_values(&:first)
69
+ return if diff.keys.all? { |k| cached_at_settings[:ignore].include?(k) }
70
+
71
+ self.cached_at = current_time_from_proper_timezone
72
+ end
73
+
52
74
  def update_belongs_to_cached_at_keys
53
75
  self.class.reflect_on_all_associations.each do |reflection|
54
76
  next unless reflection.is_a?(ActiveRecord::Reflection::BelongsToReflection)
@@ -64,7 +86,7 @@ module CachedAt
64
86
 
65
87
  end
66
88
  end
67
-
89
+
68
90
  end
69
91
  end
70
92
 
@@ -2,14 +2,12 @@ module ActiveRecord
2
2
  module ConnectionAdapters #:nodoc:
3
3
 
4
4
  class TableDefinition
5
- def timestamps(*args)
6
- options = args.extract_options!
7
-
5
+ def timestamps(**options)
8
6
  options[:null] = false if options[:null].nil?
9
7
 
10
8
  column(:created_at, :datetime, options)
11
9
  column(:updated_at, :datetime, options)
12
- column(:cached_at, :datetime, options)
10
+ column(:cached_at, :datetime, options)
13
11
  end
14
12
  end
15
13
 
@@ -7,7 +7,7 @@ module ActiveRecord
7
7
 
8
8
  add_column table_name, :created_at, :datetime, options
9
9
  add_column table_name, :updated_at, :datetime, options
10
- add_column table_name, :cached_at, :datetime, options
10
+ add_column table_name, :cached_at, :datetime, options
11
11
  end
12
12
 
13
13
  def remove_timestamps(table_name, options = {})
@@ -29,16 +29,40 @@ module CachedAt
29
29
 
30
30
  end
31
31
 
32
- def cache_key(includes = nil)
32
+ def cache_key(*includes)
33
33
  if includes.nil? || includes.empty?
34
- super
34
+ if cache_versioning
35
+ "#{model_name.cache_key}/#{id}"
36
+ else
37
+ "#{model_name.cache_key}/#{id}@#{cache_version}"
38
+ end
39
+ else
40
+ digest = Digest::MD5.hexdigest(paramaterize_cache_includes(includes))
41
+ if cache_versioning
42
+ "#{model_name.cache_key}/#{id}+#{digest}"
43
+ else
44
+ "#{model_name.cache_key}/#{id}+#{digest}@#{cache_version(includes)}"
45
+ end
46
+ end
47
+ end
48
+
49
+ def cache_key_with_version(*includes)
50
+ if version = cache_version(*includes)
51
+ "#{cache_key(*includes)}-#{version}"
52
+ else
53
+ cache_key(*includes)
54
+ end
55
+ end
56
+
57
+ def cache_version(*includes)
58
+ timestamp = if includes.empty?
59
+ try(:cached_at) || try(:cached_at)
35
60
  else
36
61
  timestamp_keys = ['cached_at'] + self.class.cached_at_columns_for_includes(includes)
37
- timestamp = max_updated_column_timestamp(timestamp_keys).utc.to_s(cache_timestamp_format)
38
- digest ||= Digest::MD5.new()
39
- digest << paramaterize_cache_includes(includes)
40
- "#{model_name.cache_key}/#{id}+#{digest.hexdigest}@#{timestamp}"
62
+ timestamp = timestamp_keys.map { |attr| self[attr]&.to_time }.compact.max
41
63
  end
64
+
65
+ timestamp.utc.to_s(:usec)
42
66
  end
43
67
 
44
68
  # TODO
@@ -84,4 +108,4 @@ module CachedAt
84
108
  end
85
109
  end
86
110
 
87
- ActiveRecord::Base.include(CachedAt::Base::Helpers)
111
+ ActiveRecord::Base.include(CachedAt::Base::Helpers)
@@ -7,7 +7,7 @@ module ActiveRecord
7
7
  private
8
8
 
9
9
  def timestamp_attributes_for_update
10
- ['updated_at', 'cached_at']
10
+ ['updated_at']
11
11
  end
12
12
 
13
13
  def timestamp_attributes_for_create
@@ -1,3 +1,3 @@
1
1
  module CachedAt
2
- VERSION = '5.2.1.0'
2
+ VERSION = '6.0.0'
3
3
  end
metadata CHANGED
@@ -1,27 +1,27 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: activerecord-cached_at
3
3
  version: !ruby/object:Gem::Version
4
- version: 5.2.1.0
4
+ version: 6.0.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Jon Bracy
8
- autorequire:
8
+ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2018-10-08 00:00:00.000000000 Z
11
+ date: 2020-07-19 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activerecord
15
15
  requirement: !ruby/object:Gem::Requirement
16
16
  requirements:
17
- - - "~>"
17
+ - - ">="
18
18
  - !ruby/object:Gem::Version
19
19
  version: 5.2.1
20
20
  type: :runtime
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
- - - "~>"
24
+ - - ">="
25
25
  - !ruby/object:Gem::Version
26
26
  version: 5.2.1
27
27
  - !ruby/object:Gem::Dependency
@@ -153,7 +153,7 @@ homepage: https://github.com/malomalo/activerecord-cached_at
153
153
  licenses:
154
154
  - MIT
155
155
  metadata: {}
156
- post_install_message:
156
+ post_install_message:
157
157
  rdoc_options:
158
158
  - "--main"
159
159
  - README.md
@@ -170,9 +170,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
170
170
  - !ruby/object:Gem::Version
171
171
  version: '0'
172
172
  requirements: []
173
- rubyforge_project:
174
- rubygems_version: 2.7.4
175
- signing_key:
173
+ rubygems_version: 3.1.2
174
+ signing_key:
176
175
  specification_version: 4
177
176
  summary: Allows ActiveRecord and Rails to use a `cached_at` column for the `cache_key`
178
177
  if available