activerecord-cached_at 5.2.1.0 → 6.0.0

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,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