diff --git a/app/models/theme.rb b/app/models/theme.rb
index 23cb45fedf8..347a3a609fb 100644
--- a/app/models/theme.rb
+++ b/app/models/theme.rb
@@ -350,7 +350,12 @@ class Theme < ActiveRecord::Base
return "" if theme_id.blank?
theme_ids = !skip_transformation ? transform_ids(theme_id) : [theme_id]
- (resolve_baked_field(theme_ids, target.to_sym, field) || "").html_safe
+ cache_key = "#{theme_ids.join(",")}:#{target}:#{field}:#{Theme.compiler_version}"
+
+ get_set_cache(cache_key) do
+ target = target.to_sym
+ resolve_baked_field(theme_ids, target, field) || ""
+ end.html_safe
end
def self.lookup_modifier(theme_ids, modifier_name)
@@ -429,71 +434,25 @@ class Theme < ActiveRecord::Base
end
def self.resolve_baked_field(theme_ids, target, name)
- target = target.to_sym
- name = name&.to_sym
-
- target = :mobile if target == :mobile_theme
- target = :desktop if target == :desktop_theme
-
- case target
- when :extra_js
- get_set_cache("#{theme_ids.join(",")}:extra_js:#{Theme.compiler_version}") do
- require_rebake =
- ThemeField.where(theme_id: theme_ids, target_id: Theme.targets[:extra_js]).where(
- "compiler_version <> ?",
- Theme.compiler_version,
- )
-
- ActiveRecord::Base.transaction do
- require_rebake.each { |tf| tf.ensure_baked! }
-
- Theme.where(id: require_rebake.map(&:theme_id)).each(&:update_javascript_cache!)
- end
-
- caches =
- JavascriptCache
- .where(theme_id: theme_ids)
- .index_by(&:theme_id)
- .values_at(*theme_ids)
- .compact
-
- caches.map { |c| <<~HTML.html_safe }.join("\n")
+ if target == :extra_js
+ require_rebake =
+ ThemeField.where(theme_id: theme_ids, target_id: Theme.targets[:extra_js]).where(
+ "compiler_version <> ?",
+ Theme.compiler_version,
+ )
+ require_rebake.each { |tf| tf.ensure_baked! }
+ require_rebake
+ .map(&:theme_id)
+ .uniq
+ .each { |theme_id| Theme.find(theme_id).update_javascript_cache! }
+ caches = JavascriptCache.where(theme_id: theme_ids)
+ caches = caches.sort_by { |cache| theme_ids.index(cache.theme_id) }
+ return caches.map { |c| <<~HTML.html_safe }.join("\n")
HTML
- end
- when :translations
- theme_field_values(theme_ids, :translations, I18n.fallbacks[name])
- .to_a
- .select(&:second)
- .uniq { |((theme_id, _, _), _)| theme_id }
- .flat_map(&:second)
- .join("\n")
- else
- theme_field_values(theme_ids, [:common, target], name).values.compact.flatten.join("\n")
- end
- end
-
- def self.theme_field_values(theme_ids, targets, names)
- cache.defer_get_set_bulk(
- Array(theme_ids).product(Array(targets), Array(names)),
- lambda do |(theme_id, target, name)|
- "#{theme_id}:#{target}:#{name}:#{Theme.compiler_version}"
- end,
- ) do |keys|
- keys = keys.map { |theme_id, target, name| [theme_id, Theme.targets[target], name.to_s] }
-
- keys
- .map do |theme_id, target_id, name|
- ThemeField.where(theme_id: theme_id, target_id: target_id, name: name)
- end
- .inject { |a, b| a.or(b) }
- .each(&:ensure_baked!)
- .map { |tf| [[tf.theme_id, tf.target_id, tf.name], tf.value_baked || tf.value] }
- .group_by(&:first)
- .transform_values { |x| x.map(&:second) }
- .values_at(*keys)
end
+ list_baked_fields(theme_ids, target, name).map { |f| f.value_baked || f.value }.join("\n")
end
def self.list_baked_fields(theme_ids, target, name)
diff --git a/lib/distributed_cache.rb b/lib/distributed_cache.rb
index bc710f26564..b041b39d1d4 100644
--- a/lib/distributed_cache.rb
+++ b/lib/distributed_cache.rb
@@ -20,24 +20,6 @@ class DistributedCache < MessageBus::DistributedCache
value
end
- def defer_get_set_bulk(ks, key_blk, &blk)
- found_keys, missing_keys = ks.partition { |k| hash.key?(key_blk.call(k)) }
- found_hash = found_keys.map { |key| [key, self[key_blk.call(key)]] }.to_h
-
- if missing_keys.present?
- missing_values = blk.call(missing_keys.freeze)
- missing_hash = missing_keys.zip(missing_values).to_h
-
- Scheduler::Defer.later("#{@key}_bulk_set") do
- missing_hash.each { |key, value| self[key_blk.call(key)] = value }
- end
-
- ks.zip(missing_hash.merge(found_hash).values_at(*ks)).to_h
- else
- found_hash
- end
- end
-
def clear(after_commit: true)
if after_commit && !GlobalSetting.skip_db?
DB.after_commit { super() }
diff --git a/spec/models/theme_spec.rb b/spec/models/theme_spec.rb
index 6ffa42b4fcf..f34d7ac0d9a 100644
--- a/spec/models/theme_spec.rb
+++ b/spec/models/theme_spec.rb
@@ -1119,104 +1119,4 @@ HTML
expect(messages).to include(["development-mode-theme-changed"])
end
end
-
- describe "#lookup_field when a theme component is used in multiple themes" do
- fab!(:theme_1) { Fabricate(:theme, user: user) }
- fab!(:theme_2) { Fabricate(:theme, user: user) }
- fab!(:child) { Fabricate(:theme, user: user, component: true) }
-
- before_all do
- theme_1.add_relative_theme!(:child, child)
- theme_2.add_relative_theme!(:child, child)
- end
-
- it "efficiently caches fields of theme component by only caching the fields once across multiple themes" do
- child.set_field(target: :common, name: "header", value: "World")
- child.save!
-
- expect(Theme.lookup_field(theme_1.id, :desktop, "header")).to eq("World")
- expect(Theme.lookup_field(theme_2.id, :desktop, "header")).to eq("World")
-
- expect(
- Theme.cache.defer_get_set("#{child.id}:common:header:#{Theme.compiler_version}") { raise },
- ).to eq(["World"])
- expect(
- Theme.cache.defer_get_set("#{child.id}:desktop:header:#{Theme.compiler_version}") { raise },
- ).to eq(nil)
-
- expect(
- Theme
- .cache
- .defer_get_set("#{theme_1.id}:common:header:#{Theme.compiler_version}") { raise },
- ).to eq(nil)
- expect(
- Theme
- .cache
- .defer_get_set("#{theme_1.id}:desktop:header:#{Theme.compiler_version}") { raise },
- ).to eq(nil)
-
- expect(
- Theme
- .cache
- .defer_get_set("#{theme_2.id}:common:header:#{Theme.compiler_version}") { raise },
- ).to eq(nil)
- expect(
- Theme
- .cache
- .defer_get_set("#{theme_2.id}:desktop:header:#{Theme.compiler_version}") { raise },
- ).to eq(nil)
- end
-
- it "puts the parent value ahead of the child" do
- theme_1.set_field(target: :common, name: "header", value: "theme_1")
- theme_1.save!
-
- child.set_field(target: :common, name: "header", value: "child")
- child.save!
-
- expect(Theme.lookup_field(theme_1.id, :desktop, "header")).to eq("theme_1\nchild")
- end
-
- it "puts parent translations ahead of child translations" do
- theme_1.set_field(target: :translations, name: "en", value: <<~YAML)
- en:
- theme_1: "test"
- YAML
- theme_1.save!
- theme_field = ThemeField.order(:id).last
-
- child.set_field(target: :translations, name: "en", value: <<~YAML)
- en:
- child: "test"
- YAML
- child.save!
- child_field = ThemeField.order(:id).last
-
- expect(theme_field.value_baked).not_to eq(child_field.value_baked)
- expect(Theme.lookup_field(theme_1.id, :translations, :en)).to eq(
- [theme_field, child_field].map(&:value_baked).join("\n"),
- )
- end
-
- it "prioritizes a locale over its fallback" do
- theme_1.set_field(target: :translations, name: "en", value: <<~YAML)
- en:
- theme_1: "hello"
- YAML
- theme_1.save!
- en_field = ThemeField.order(:id).last
-
- theme_1.set_field(target: :translations, name: "es", value: <<~YAML)
- es:
- theme_1: "hola"
- YAML
- theme_1.save!
- es_field = ThemeField.order(:id).last
-
- expect(es_field.value_baked).not_to eq(en_field.value_baked)
- expect(Theme.lookup_field(theme_1.id, :translations, :en)).to eq(en_field.value_baked)
- expect(Theme.lookup_field(theme_1.id, :translations, :es)).to eq(es_field.value_baked)
- expect(Theme.lookup_field(theme_1.id, :translations, :fr)).to eq(en_field.value_baked)
- end
- end
end