card 1.107.0 → 1.108.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/VERSION +1 -1
- data/config/environments/development.rb +1 -9
- data/config/environments/test.rb +2 -2
- data/config/initializers/01_core_extensions/integer.rb +13 -0
- data/config/initializers/01_core_extensions/{persistent_identifiers.rb → persistent_identifier.rb} +0 -5
- data/config/initializers/01_core_extensions/symbol.rb +11 -0
- data/config/initializers/core_extensions.rb +2 -2
- data/db/schema.rb +8 -4
- data/lib/card/auth/token.rb +1 -1
- data/lib/card/cache/all.rb +30 -7
- data/lib/card/cache/card_class.rb +12 -6
- data/lib/card/cache/class_methods.rb +122 -0
- data/lib/card/cache/populate.rb +64 -0
- data/lib/card/cache/{persistent.rb → shared.rb} +22 -16
- data/lib/card/cache/{persistent_class.rb → shared_class.rb} +2 -2
- data/lib/card/cache/temporary.rb +40 -8
- data/lib/card/cache.rb +65 -130
- data/lib/card/codename.rb +66 -54
- data/lib/card/director/class_methods.rb +13 -13
- data/lib/card/director/stages.rb +1 -1
- data/lib/card/error.rb +8 -0
- data/lib/card/fetch/card_class.rb +5 -24
- data/lib/card/fetch/retrieve.rb +1 -1
- data/lib/card/fetch/store.rb +4 -18
- data/lib/card/format/nesting.rb +1 -1
- data/lib/card/format/registration.rb +15 -7
- data/lib/card/format/render.rb +16 -15
- data/lib/card/format.rb +11 -4
- data/lib/card/lexicon.rb +37 -23
- data/lib/card/mark.rb +2 -34
- data/lib/card/name/all.rb +3 -7
- data/lib/card/name/card_class.rb +2 -2
- data/lib/card/name/name_variants.rb +7 -1
- data/lib/card/name.rb +60 -12
- data/lib/card/query/card_class.rb +1 -1
- data/lib/card/query/card_query/match_attributes.rb +14 -4
- data/lib/card/query/card_query/relational_attributes.rb +2 -0
- data/lib/card/query/card_query/run.rb +9 -3
- data/lib/card/query/sql_statement.rb +1 -1
- data/lib/card/reference/all.rb +8 -3
- data/lib/card/reference.rb +4 -9
- data/lib/card/rule/cache.rb +2 -0
- data/lib/card/rule/read_rule_cache.rb +2 -0
- data/lib/card/set/event.rb +1 -4
- data/lib/card/set/format/abstract_format/view_opts.rb +1 -1
- data/lib/card/set/format/abstract_format.rb +20 -3
- data/lib/card/set/inheritance.rb +2 -3
- data/lib/card/set/pattern/all.rb +12 -0
- data/lib/card/subcards/add.rb +1 -1
- data/lib/card/subcards/remove.rb +1 -1
- data/lib/card/view/cache/cache_action.rb +24 -7
- data/lib/card/view/cache.rb +34 -6
- data/lib/card/view/options/voo_api.rb +1 -1
- data/lib/card/view.rb +1 -0
- data/lib/cardio/cli.rb +1 -1
- data/lib/cardio/command/rake_command.rb +10 -2
- data/lib/cardio/command/rspec_command/parser.rb +9 -11
- data/lib/cardio/command/rspec_command.rb +7 -3
- data/lib/cardio/generators/deck_helper.rb +7 -7
- data/lib/cardio/migration/port.rb +37 -0
- data/lib/cardio/migration.rb +5 -36
- data/lib/cardio/mod/class_methods.rb +1 -0
- data/lib/cardio/mod.rb +1 -1
- data/lib/cardio/railtie.rb +5 -4
- data/lib/cardio/utils.rb +1 -1
- data/lib/generators/deck/deck_generator.rb +5 -11
- data/lib/generators/deck/templates/Gemfile.erb +2 -2
- data/lib/generators/deck/templates/config/puma.rb +0 -6
- data/lib/generators/mod/mod_generator.rb +7 -0
- data/lib/generators/set/set_generator.rb +2 -1
- data/mod/core/config/locales/en.yml +1 -0
- data/mod/core/data/schema/20200805200729_add_unique_pair_indices.rb +1 -1
- data/mod/core/data/schema/20240628212556_add_trash_index.rb +11 -0
- data/mod/core/data/schema/20241017160402_unique_codename.rb +13 -0
- data/mod/core/data/transform/20140317035504_account_requests_to_signups.rb +4 -6
- data/mod/core/data/transform/20150724123438_update_file_and_image_cards.rb +1 -1
- data/mod/core/data/transform/20190320091257_upgrade_recaptcha_to_v3.rb +3 -3
- data/mod/core/data/transform/20190502130029_add_shark_and_help_desk_role.rb +1 -1
- data/mod/core/data/transform/20190822093633_move_help_text_to_code.rb +2 -2
- data/mod/core/data/transform/20190829205148_remove_add_help.rb +1 -1
- data/mod/core/lib/tasks/card/migrate.rake +6 -5
- data/mod/core/lib/tasks/card/mod.rake +3 -0
- data/mod/core/lib/tasks/card.rake +6 -2
- data/mod/core/set/all/autoname.rb +1 -1
- data/mod/core/set/all/codename.rb +6 -8
- data/mod/core/set/all/name_events.rb +7 -5
- data/mod/core/set/all/result.rb +1 -1
- data/mod/core/set/all/trash.rb +2 -4
- data/mod/core/set/all/type.rb +1 -1
- data/mod/core/set/self/mod.rb +1 -1
- data/mod/core/set/self/trash.rb +1 -1
- data/mod/core/spec/set/all/trash_spec.rb +3 -3
- metadata +30 -24
- data/lib/card/cache/prepopulate.rb +0 -48
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 2f065584dd5d9ec2de2f500efa89435c0270d1ed25178cb88dfa77432573068b
|
4
|
+
data.tar.gz: 10507be87eed39bbddb2db5414d6746f72f2637a912a8762834340718c3539f2
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: d25bdb2406dd6d40700ecd945954b3fe9eb2e1a311e92677c446ba622b6f64e6c01c7547ce9ea91df52676fa1ac978b868a0f5840f6aa9bdb46360fe01eeadd0
|
7
|
+
data.tar.gz: 78333d7e0400f187119d593e68d05ed9481e3e194dfdeb7b1c2e431a71af07e58124027536c2a236e27b6b7dcf84ae36066b7e737e53828ff0e9565236674fa2
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.
|
1
|
+
0.18.0
|
@@ -4,7 +4,7 @@ Cardio.application.class.configure do
|
|
4
4
|
# Settings specified here will take precedence over those in config/application.rb
|
5
5
|
|
6
6
|
config.view_cache = false
|
7
|
-
|
7
|
+
# config.cache_log_level = :debug
|
8
8
|
config.eager_load = false
|
9
9
|
|
10
10
|
config.asset_refresh = :eager
|
@@ -38,14 +38,6 @@ Cardio.application.class.configure do
|
|
38
38
|
# in the nest where the error occurred
|
39
39
|
config.raise_all_rendering_errors = true
|
40
40
|
|
41
|
-
# config.performance_logger = {
|
42
|
-
# methods: [:event, :search, :fetch, :view], # choose methods to log
|
43
|
-
# min_time: 100, # show only method calls that are slower than 100ms
|
44
|
-
# max_depth: 3, # show nested method calls only up to depth 3
|
45
|
-
# details: true # show method arguments and sql
|
46
|
-
# log_level: :info
|
47
|
-
# }
|
48
|
-
|
49
41
|
# Only use best-standards-support built into browsers
|
50
42
|
config.action_dispatch.best_standards_support = :builtin
|
51
43
|
|
data/config/environments/test.rb
CHANGED
@@ -16,8 +16,8 @@ Cardio.application.class.configure do
|
|
16
16
|
|
17
17
|
config.assets.enabled = true if Object.const_defined?(:JasmineRails)
|
18
18
|
|
19
|
-
config.
|
20
|
-
config.
|
19
|
+
config.shared_cache = false
|
20
|
+
config.seed_cache_from_stash = true
|
21
21
|
|
22
22
|
# Configure static asset server for tests with Cache-Control for performance
|
23
23
|
config.serve_static_files = true
|
@@ -9,8 +9,8 @@ module CoreExtensions
|
|
9
9
|
::String.include String
|
10
10
|
::Array.include Array
|
11
11
|
::Hash.include Hash::Merging
|
12
|
-
::Symbol.include
|
13
|
-
::Integer.include
|
12
|
+
::Symbol.include Symbol
|
13
|
+
::Integer.include Integer
|
14
14
|
::Hash.extend Hash::ClassMethods::Nesting
|
15
15
|
::MatchData.include MatchData
|
16
16
|
end
|
data/db/schema.rb
CHANGED
@@ -9,6 +9,10 @@
|
|
9
9
|
# migrations use external dependencies or application code.
|
10
10
|
#
|
11
11
|
# It's strongly recommended that you check this file into your version control system.
|
12
|
+
#
|
13
|
+
# Remark: We used to use size: medium for text columns. This option is supported by mysql but not by postgres.
|
14
|
+
# limit: 16.megabytes - 1 translates to the same size in mysql databases.
|
15
|
+
|
12
16
|
|
13
17
|
ActiveRecord::Schema.define(version: 2022_10_31_182227) do
|
14
18
|
|
@@ -36,7 +40,7 @@ ActiveRecord::Schema.define(version: 2022_10_31_182227) do
|
|
36
40
|
create_table "card_changes", id: :integer, charset: "utf8mb3", force: :cascade do |t|
|
37
41
|
t.integer "card_action_id"
|
38
42
|
t.integer "field"
|
39
|
-
t.text "value",
|
43
|
+
t.text "value", limit: 16.megabytes - 1
|
40
44
|
t.index ["card_action_id"], name: "card_changes_card_action_id_index"
|
41
45
|
end
|
42
46
|
|
@@ -65,7 +69,7 @@ ActiveRecord::Schema.define(version: 2022_10_31_182227) do
|
|
65
69
|
t.integer "left_id"
|
66
70
|
t.integer "right_id"
|
67
71
|
t.string "left_key"
|
68
|
-
t.text "content",
|
72
|
+
t.text "content", limit: 16.megabytes - 1
|
69
73
|
t.datetime "updated_at"
|
70
74
|
t.index ["left_id"], name: "right_id_index"
|
71
75
|
t.index ["right_id"], name: "left_id_index"
|
@@ -87,7 +91,7 @@ ActiveRecord::Schema.define(version: 2022_10_31_182227) do
|
|
87
91
|
t.integer "references_expired"
|
88
92
|
t.boolean "trash", null: false
|
89
93
|
t.integer "type_id", null: false
|
90
|
-
t.text "db_content",
|
94
|
+
t.text "db_content", limit: 16.megabytes - 1
|
91
95
|
t.index ["codename"], name: "cards_codename_index"
|
92
96
|
t.index ["created_at"], name: "cards_created_at_index"
|
93
97
|
t.index ["key"], name: "cards_key_index", unique: true
|
@@ -102,7 +106,7 @@ ActiveRecord::Schema.define(version: 2022_10_31_182227) do
|
|
102
106
|
create_table "delayed_jobs", charset: "utf8mb3", force: :cascade do |t|
|
103
107
|
t.integer "priority", default: 0, null: false
|
104
108
|
t.integer "attempts", default: 0, null: false
|
105
|
-
t.text "handler",
|
109
|
+
t.text "handler", limit: 16.megabytes - 1, null: false
|
106
110
|
t.text "last_error"
|
107
111
|
t.datetime "run_at"
|
108
112
|
t.datetime "locked_at"
|
data/lib/card/auth/token.rb
CHANGED
data/lib/card/cache/all.rb
CHANGED
@@ -2,12 +2,25 @@ class Card
|
|
2
2
|
class Cache
|
3
3
|
# cache-related instance methods available to all Cards
|
4
4
|
module All
|
5
|
+
def write_lexicon
|
6
|
+
Lexicon.write_to_temp_cache id, name, lex
|
7
|
+
end
|
8
|
+
|
9
|
+
def lex
|
10
|
+
if simple?
|
11
|
+
name
|
12
|
+
elsif left_id && right_id
|
13
|
+
[left_id, right_id]
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
5
17
|
def expire cache_type=nil
|
6
18
|
return unless (cache_class = cache_class_from_type cache_type)
|
7
19
|
|
8
20
|
expire_views
|
9
21
|
expire_names cache_class
|
10
22
|
expire_id cache_class
|
23
|
+
expire_left cache_type
|
11
24
|
end
|
12
25
|
|
13
26
|
def view_cache_clean?
|
@@ -18,18 +31,28 @@ class Card
|
|
18
31
|
return if view_cache_keys.include? cache_key
|
19
32
|
|
20
33
|
view_cache_keys << cache_key
|
21
|
-
|
34
|
+
shared_write_view_cache_keys
|
22
35
|
end
|
23
36
|
|
24
37
|
private
|
25
38
|
|
26
|
-
def
|
27
|
-
|
39
|
+
def expire_left cache_type
|
40
|
+
return unless name.compound? && expire_left?
|
41
|
+
|
42
|
+
Card.cache.read(name.left_name.key)&.expire cache_type
|
43
|
+
end
|
44
|
+
|
45
|
+
def expire_left?
|
46
|
+
true
|
47
|
+
end
|
48
|
+
|
49
|
+
def shared_read_view_cache_keys key_root=key
|
50
|
+
Card.cache.shared&.read_attribute key_root, :view_cache_keys
|
28
51
|
end
|
29
52
|
|
30
|
-
def
|
53
|
+
def shared_write_view_cache_keys
|
31
54
|
# puts "WRITE VIEW CACHE KEYS (#{name}): #{view_cache_keys}"
|
32
|
-
Card.cache.
|
55
|
+
Card.cache.shared&.write_attribute key, :view_cache_keys, view_cache_keys
|
33
56
|
end
|
34
57
|
|
35
58
|
def cache_class_from_type cache_type
|
@@ -37,7 +60,7 @@ class Card
|
|
37
60
|
end
|
38
61
|
|
39
62
|
def view_cache_keys
|
40
|
-
@view_cache_keys ||=
|
63
|
+
@view_cache_keys ||= shared_read_view_cache_keys(key) || []
|
41
64
|
end
|
42
65
|
|
43
66
|
def expire_names cache
|
@@ -58,7 +81,7 @@ class Card
|
|
58
81
|
def expire_views
|
59
82
|
each_key_version do |key|
|
60
83
|
# puts "EXPIRE VIEW CACHE (#{name}): #{view_cache_keys}"
|
61
|
-
view_keys =
|
84
|
+
view_keys = shared_read_view_cache_keys key
|
62
85
|
next unless view_keys.present?
|
63
86
|
|
64
87
|
expire_view_cache_keys view_keys
|
@@ -2,10 +2,16 @@ class Card
|
|
2
2
|
class Cache
|
3
3
|
# cache-related class methods
|
4
4
|
module CardClass
|
5
|
-
def
|
6
|
-
|
5
|
+
def cache
|
6
|
+
Card::Cache[Card]
|
7
|
+
end
|
7
8
|
|
8
|
-
|
9
|
+
def after_write_to_temp_cache card
|
10
|
+
card.write_lexicon if card.is_a? Card
|
11
|
+
end
|
12
|
+
|
13
|
+
def retrieve_from_cache cache_key, local_only=false
|
14
|
+
local_only ? cache.temp.read(cache_key) : cache.read(cache_key)
|
9
15
|
end
|
10
16
|
|
11
17
|
def retrieve_from_cache_by_id id, local_only=false
|
@@ -21,16 +27,16 @@ class Card
|
|
21
27
|
|
22
28
|
def write_to_cache card, local_only=false
|
23
29
|
if local_only
|
24
|
-
|
30
|
+
write_to_temp_cache card
|
25
31
|
elsif cache
|
26
32
|
cache.write card.key, card
|
27
33
|
end
|
28
34
|
end
|
29
35
|
|
30
|
-
def
|
36
|
+
def write_to_temp_cache card
|
31
37
|
return unless cache
|
32
38
|
|
33
|
-
cache.
|
39
|
+
cache.temp.write card.key, card, callback: false
|
34
40
|
end
|
35
41
|
|
36
42
|
def expire name
|
@@ -0,0 +1,122 @@
|
|
1
|
+
|
2
|
+
class Card
|
3
|
+
class Cache
|
4
|
+
# class methods for Card::Cache
|
5
|
+
module ClassMethods
|
6
|
+
include Populate
|
7
|
+
|
8
|
+
attr_accessor :no_renewal
|
9
|
+
attr_accessor :counter
|
10
|
+
|
11
|
+
# create a new cache for the ruby class provided
|
12
|
+
# @param klass [Class]
|
13
|
+
# @return [{Card::Cache}]
|
14
|
+
def [] klass
|
15
|
+
raise "nil klass" if klass.nil?
|
16
|
+
|
17
|
+
cache_by_class[klass] ||= new class: klass, store: (shared_cache || nil)
|
18
|
+
end
|
19
|
+
|
20
|
+
# clear the temporary caches and ensure we're using the latest stamp
|
21
|
+
# on the shared caches.
|
22
|
+
def renew
|
23
|
+
# Cardio.config.cache_log_level = :debug
|
24
|
+
# Cardio.config.view_cache = true
|
25
|
+
# Cardio.config.asset_refresh = :cautious
|
26
|
+
# Cache.reset_all
|
27
|
+
|
28
|
+
Card::Cache.counter = nil
|
29
|
+
return if no_renewal
|
30
|
+
|
31
|
+
renew_shared
|
32
|
+
cache_by_class.each_value do |cache|
|
33
|
+
cache.temp.reset
|
34
|
+
cache.shared&.renew
|
35
|
+
end
|
36
|
+
|
37
|
+
populate_temp_cache
|
38
|
+
end
|
39
|
+
|
40
|
+
def renew_shared
|
41
|
+
Card::Cache::Shared.renew if shared_cache
|
42
|
+
end
|
43
|
+
|
44
|
+
# reset standard cached for all classes
|
45
|
+
def reset
|
46
|
+
reset_shared
|
47
|
+
reset_temp
|
48
|
+
end
|
49
|
+
|
50
|
+
# reset all caches for all classes
|
51
|
+
def reset_all
|
52
|
+
reset_shared
|
53
|
+
reset_temp
|
54
|
+
reset_other
|
55
|
+
end
|
56
|
+
|
57
|
+
# completely wipe out all caches, often including the Shared cache of
|
58
|
+
# other decks using the same mechanism.
|
59
|
+
# Generally prefer {.reset_all}
|
60
|
+
# @see .reset_all
|
61
|
+
def reset_global
|
62
|
+
cache_by_class.each_value do |cache|
|
63
|
+
cache.temp.reset
|
64
|
+
cache.shared&.annihilate
|
65
|
+
end
|
66
|
+
reset_other
|
67
|
+
end
|
68
|
+
|
69
|
+
# reset the Shared cache for all classes
|
70
|
+
def reset_shared
|
71
|
+
Card::Cache::Shared.reset if shared_cache
|
72
|
+
cache_by_class.each_value do |cache|
|
73
|
+
cache.shared&.reset
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
# reset the Temporary cache for all classes
|
78
|
+
def reset_temp
|
79
|
+
cache_by_class.each_value { |cache| cache.temp.reset }
|
80
|
+
end
|
81
|
+
|
82
|
+
# reset Codename cache and delete tmp files
|
83
|
+
# (the non-standard caches)
|
84
|
+
def reset_other
|
85
|
+
Card::Codename.reset_cache
|
86
|
+
Cardio::Utils.delete_tmp_files!
|
87
|
+
end
|
88
|
+
|
89
|
+
def restore
|
90
|
+
reset_temp
|
91
|
+
seed_from_stash
|
92
|
+
end
|
93
|
+
|
94
|
+
def shared_on!
|
95
|
+
return if @shared_cache
|
96
|
+
|
97
|
+
@cache_by_class = {}
|
98
|
+
@shared_cache = Cardio.config.shared_cache && Cardio.cache
|
99
|
+
end
|
100
|
+
|
101
|
+
def cache_by_class
|
102
|
+
@cache_by_class ||= {}
|
103
|
+
end
|
104
|
+
|
105
|
+
def shared_cache
|
106
|
+
return @shared_cache unless @shared_cache.nil?
|
107
|
+
|
108
|
+
@shared_cache = (ENV["NO_RAILS_CACHE"] != "true") && shared_on!
|
109
|
+
end
|
110
|
+
|
111
|
+
def tallies
|
112
|
+
"#{tally_total} Cache calls (" + counter.map { |k, v| "#{k}=#{v} " }.join + ")"
|
113
|
+
end
|
114
|
+
|
115
|
+
private
|
116
|
+
|
117
|
+
def tally_total
|
118
|
+
counter.values.map(&:values).flatten.sum
|
119
|
+
end
|
120
|
+
end
|
121
|
+
end
|
122
|
+
end
|
@@ -0,0 +1,64 @@
|
|
1
|
+
class Card
|
2
|
+
class Cache
|
3
|
+
# population-related class methods for Card::Cache
|
4
|
+
module Populate
|
5
|
+
def populate_ids ids
|
6
|
+
# use ids to look up names
|
7
|
+
results = Lexicon.cache.read_multi(ids.map(&:to_s)).values
|
8
|
+
names = []
|
9
|
+
pairs = []
|
10
|
+
results.each do |result|
|
11
|
+
result.is_a?(String) ? (names << result) : (pairs << result)
|
12
|
+
end
|
13
|
+
|
14
|
+
if pairs.any?
|
15
|
+
populate_ids pairs.flatten
|
16
|
+
names += pairs.map(&:cardname)
|
17
|
+
end
|
18
|
+
|
19
|
+
# use keys to look up
|
20
|
+
populate_names names
|
21
|
+
end
|
22
|
+
|
23
|
+
def populate_names names
|
24
|
+
keys = names.map { |n| n.to_name.key }
|
25
|
+
Card.cache.read_multi keys
|
26
|
+
end
|
27
|
+
|
28
|
+
def populate_fields list, *fields
|
29
|
+
name_arrays = list.each_with_object([]) do |item, arrays|
|
30
|
+
fields.flatten.each do |field|
|
31
|
+
arrays << [item, field]
|
32
|
+
end
|
33
|
+
end
|
34
|
+
populate_names name_arrays
|
35
|
+
end
|
36
|
+
|
37
|
+
private
|
38
|
+
|
39
|
+
def populate_temp_cache
|
40
|
+
return unless shared_cache
|
41
|
+
|
42
|
+
populate_ids Codename.ids
|
43
|
+
# Codename.process_codenames if result.blank?
|
44
|
+
Card.cache.read_multi Set.basket[:cache_seed_strings]
|
45
|
+
populate_names Set.basket[:cache_seed_names]
|
46
|
+
end
|
47
|
+
|
48
|
+
# for testing, stash rules in variable and use that to re-seed cache
|
49
|
+
def seed_from_stash
|
50
|
+
return unless Cardio.config.seed_cache_from_stash
|
51
|
+
|
52
|
+
stash_to_cache("RULES") { Card::Rule.rule_cache }
|
53
|
+
stash_to_cache("READRULES") { Card::Rule.read_rule_cache }
|
54
|
+
stash_to_cache("PREFERENCES") { Card::Rule.preference_cache }
|
55
|
+
end
|
56
|
+
|
57
|
+
def stash_to_cache variable
|
58
|
+
@stash ||= {}
|
59
|
+
value = @stash[variable] ||= yield
|
60
|
+
Card.cache.temp.write variable, value.clone
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
@@ -2,23 +2,20 @@
|
|
2
2
|
|
3
3
|
class Card
|
4
4
|
class Cache
|
5
|
-
#
|
5
|
+
# _Shared_ caches closely mirror the database and are
|
6
6
|
# intended to be altered only upon database alterations.
|
7
7
|
#
|
8
|
-
# Unlike the database, the
|
8
|
+
# Unlike the database, the shared cache stores records of records that
|
9
9
|
# have been requested but are missing or, in the case of some {Card cards},
|
10
10
|
# "virtual", meaning that they follow known patterns but do not exist in the
|
11
11
|
# database.
|
12
12
|
#
|
13
|
-
# Most
|
13
|
+
# Most shared cache implementations cannot store objects with singleton
|
14
14
|
# classes, therefore {Card cards} generally must have set_modules
|
15
|
-
# re-included after retrieval from the
|
15
|
+
# re-included after retrieval from the shared cache.
|
16
16
|
#
|
17
|
-
class
|
18
|
-
extend
|
19
|
-
|
20
|
-
attr_accessor :prefix
|
21
|
-
|
17
|
+
class Shared
|
18
|
+
extend SharedClass
|
22
19
|
# @param opts [Hash]
|
23
20
|
# @option opts [Rails::Cache] :store
|
24
21
|
# @option opts [ruby Class] :class, typically ActiveRecord descendant
|
@@ -82,27 +79,36 @@ class Card
|
|
82
79
|
@store.read full_key(key)
|
83
80
|
end
|
84
81
|
|
82
|
+
def read_multi keys
|
83
|
+
map = keys.each_with_object({}) { |k, h| h[full_key k] = k }
|
84
|
+
raw = @store.read_multi(*map.keys)
|
85
|
+
raw.each_with_object({}) { |(k, v), h| h[map[k]] = v }
|
86
|
+
end
|
87
|
+
|
85
88
|
# update an attribute of an object already in the cache
|
86
89
|
# @param key [String]
|
87
90
|
# @param attribute [String, Symbol]
|
88
91
|
def write_attribute key, attribute, value
|
89
92
|
return value unless @store
|
90
93
|
|
91
|
-
if (object = deep_read key)
|
94
|
+
# if (object = deep_read key)
|
95
|
+
if (object = read key)
|
92
96
|
object.instance_variable_set "@#{attribute}", value
|
93
97
|
write key, object
|
94
98
|
end
|
95
99
|
value
|
96
100
|
end
|
97
101
|
|
98
|
-
def deep_read key
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
102
|
+
# def deep_read key
|
103
|
+
# binding.pry
|
104
|
+
# # local_cache = @store.send :local_cache
|
105
|
+
# # local_cache&.clear
|
106
|
+
# read key
|
107
|
+
# end
|
103
108
|
|
104
109
|
def read_attribute key, attribute
|
105
|
-
object = deep_read key
|
110
|
+
# object = deep_read key
|
111
|
+
object = read key
|
106
112
|
object.instance_variable_get "@#{attribute}"
|
107
113
|
end
|
108
114
|
|
data/lib/card/cache/temporary.rb
CHANGED
@@ -8,30 +8,47 @@ class Card
|
|
8
8
|
# In practice, it's a set of Cache-like methods for using a
|
9
9
|
# simple Hash.
|
10
10
|
#
|
11
|
-
# Unlike the
|
11
|
+
# Unlike the Shared cache, the Temporary cache can handle objects with
|
12
12
|
# singleton classes.
|
13
13
|
class Temporary
|
14
|
+
MAX_KEYS = 10_000
|
14
15
|
attr_reader :store
|
15
16
|
|
16
|
-
def initialize
|
17
|
+
def initialize klass
|
18
|
+
@klass = klass
|
17
19
|
@store = {}
|
20
|
+
@reps = 0
|
18
21
|
end
|
19
22
|
|
20
23
|
# @param key [String]
|
21
24
|
def read key
|
22
|
-
return unless @store.key? key
|
23
|
-
|
24
25
|
@store[key]
|
25
26
|
end
|
26
27
|
|
27
28
|
# @param key [String]
|
28
|
-
def write key, value
|
29
|
-
|
29
|
+
def write key, value, callback: true
|
30
|
+
within_key_counts do
|
31
|
+
@store[key] = value.tap do
|
32
|
+
@reps += 1
|
33
|
+
@klass.try :after_write_to_temp_cache, value if callback
|
34
|
+
end
|
35
|
+
end
|
30
36
|
end
|
31
37
|
|
32
38
|
# @param key [String]
|
33
|
-
def fetch key
|
34
|
-
read(key) || write(key, yield)
|
39
|
+
def fetch key
|
40
|
+
# read(key) || write(key, yield)
|
41
|
+
exist?(key) ? read(key) : write(key, yield)
|
42
|
+
end
|
43
|
+
|
44
|
+
def fetch_multi keys
|
45
|
+
@store.slice(*keys).tap do |found|
|
46
|
+
missing = keys - found.keys
|
47
|
+
if (newfound = missing.present? && yield(missing))
|
48
|
+
found.merge! newfound
|
49
|
+
newfound.each { |key, value| write key, value }
|
50
|
+
end
|
51
|
+
end
|
35
52
|
end
|
36
53
|
|
37
54
|
# @param key [String]
|
@@ -46,6 +63,7 @@ class Card
|
|
46
63
|
end
|
47
64
|
|
48
65
|
def reset
|
66
|
+
@reps = 0
|
49
67
|
@store = {}
|
50
68
|
end
|
51
69
|
|
@@ -53,6 +71,20 @@ class Card
|
|
53
71
|
def exist? key
|
54
72
|
@store.key? key
|
55
73
|
end
|
74
|
+
|
75
|
+
private
|
76
|
+
|
77
|
+
# enforces MAX_KEYS. The @reps count increments with each write but may
|
78
|
+
# overestimate the store size, because of multiple writes to the same key.
|
79
|
+
# (but this approach avoids recounting each time)
|
80
|
+
def within_key_counts
|
81
|
+
if @reps >= MAX_KEYS && (@reps = @store.size) > MAX_KEYS
|
82
|
+
Rails.logger.info "RESETTING temporary #{@klass} cache. " \
|
83
|
+
"MAX_KEYS (#{MAX_KEYS}) exceeded"
|
84
|
+
reset
|
85
|
+
end
|
86
|
+
yield
|
87
|
+
end
|
56
88
|
end
|
57
89
|
end
|
58
90
|
end
|