Open dreyks opened 8 years ago
I can only reply to 2.) and I think it looks totally fine to me. The point of Cells is to get away from ugly view fragment caching by modelling your view as an object (with multiple states, if you want that).
Only call
with rails-cells is html_safe
ed, render really only returns the string.
Ok, got it, thanks
But what about code change? Right now I have to Rails.cache.clear
after each deploy that changes code in cell class, which is sub-optimal
Bring it on, sounds like a good feature!
This is not as straightforward to implement as I thought. Rails' cache
is a view helper, and therefore it knows from which template it was called. Cells' cache on the other hand is called without any knowledge about the template that would render.
Will look into this closer when I have free time
Yeah, but we have a better internal API where you can find the respective state template and hash it, on the class level. No problem!
I'm here if you need help - maybe join Gitter and we chat there at some point.
Yeah, that's what I thought too, but then again, someone can do
def show
render :surprise
end
and how will we know that...
How do I find the chat-room in gitter?
I would not dive into this template cache digest thing as it over-complicates things.
We rather should go for a simpler cache getter/cleaning methods in the core.
f. ex. this is some code i am using to delete the cache for specific cells:
class SomeCell
cache :show do
"#{model.id}/#{model.updated_at.strftime('%Y%m%d%H%M%S')}"
end
cache :public_show do
"#{model.id}/#{model.updated_at.strftime('%Y%m%d%H%M%S')}"
end
# Delete the cache for a single or multiple states (views)
# If states is empty deletes all defined caches
# Needed whenever a view is updated, for manual deletion of the cache:
#
# Model.select(:id, :updated_at).each{|i| SomeCell.delete_cache( i, :show)}
#
# @param [aModel] obj
# @param [Array<Symbol>, Symbol] states name of cache partial see defs on top
def self.delete_cache(obj, states)
state_keys = states.is_a?(Array) ? states : [states]
state_keys ||= version_procs.keys
state_keys.each do |i|
key = cache_key(i, obj)
#btw. this does not seem to work maybe bcs my local prefix 'cache' in rails.cache_config is not prepended
::Rails.cache.delete(key)
end
end
# Get a cache key for a given object and state
# @param [aModel] obj
# @param [Array<Symbol>, Symbol] states name of cache partial see defs on top
# @return [String]
def self.cache_key(state, obj)
cell = self.(obj)
# ??
state_cache_key(state, version_procs[state].evaluate(cell))
end
end
the specs continue to have a rather ugly lookup:
describe 'cache keys' do
it 'has keys' do
obj = FactoryGirl.create(:a_model)
cell = cell(:some, obj)
# WTF ??
key = cell.class.state_cache_key(:show, cell.class.version_procs[:show].evaluate(cell))
expect(key).to eq ("cells/some/show/#{obj.id}/#{obj.updated_at.strftime('%Y%m%d%H%M%S')}")
end
end
This could be continued with methods to destroy all caches for a cell, all caches for an object across cells, etc. I am using redis + gem readthis for caching and think about to just use http://redis.io/commands/keys command, which by the help of *-lookups would render all of the above useless.
Yeah, after spending some time fiddling with template digesting I gave up and now use a Capistrano task that clears cell caches for cells whose file were changed between deploys
Has anything changed? Recently run into this issue myself. I simply added a 'version' to the cell as part of my cache key, and I change that if I update the code. Not ideal, but it works.
Btw, great gem! I've redesigned a monstrous Rails app recently and persevered with cells for the frontend and it has proven to be much cleaner and simpler to maintain. It is especially useful to bundle assets along with the cells.
Here is my example for caching: Make cell depends on template + translations + assets
module PokemonTypeCardDefaultStyle
class Cell < ::SomeBase::Cell
# === Caching === #
cache(
:show,
:cache_key,
expires_in: :cache_valid_time_period_length,
if: :can_be_cached?,
)
def can_be_cached?
# Use following code when child cell(s) is/are used
# [
# child_cell_1,
# ].all?(&:can_be_cached?)
true
end
def cache_valid_time_period_length
# Use following code when child cell(s) is/are used
# [
# child_cell_1,
# ].map(&:cache_valid_time_period_length).min
# Long time
100.years
end
def self.cache_key
super.merge(
# Update this if cell logic updated (code update != logic update)
logic: :v2017_10_26_1054,
template: TEMPLATE_FILES_CONTENT_CACHE_KEY,
translations: TRANSLATION_FILES_CONTENT_CACHE_KEY,
assets: ASSET_FILES_CONTENT_CACHE_KEY,
)
end
def cache_key
super.merge(
current_locale: ::I18n.config.locale,
pokemon_type: pokemon_type.cache_key,
)
end
# === Caching === #
def show
render
end
private
Contract ::Pokemon::Type
attr_reader :pokemon_type
TEMPLATE_FILES_CONTENT_CACHE_KEY = begin
view_folder_path = File.expand_path("views", __dir__)
file_paths = Dir.glob(File.join(view_folder_path, "**", "*"))
file_digests = file_paths.map do |file_path|
next nil unless File.file?(file_path)
::Digest::MD5.hexdigest(File.read(file_path))
end.compact
::Digest::MD5.hexdigest(file_digests.join(""))
end
private_constant :TEMPLATE_FILES_CONTENT_CACHE_KEY
TRANSLATION_FILES_CONTENT_CACHE_KEY = begin
folder_path = ::Rails.root.join(
"config/locales/path/to/cell/translations",
)
file_paths = Dir.glob(File.join(folder_path, "**", "*"))
file_digests = file_paths.map do |file_path|
::Digest::MD5.hexdigest(File.read(file_path))
end
::Digest::MD5.hexdigest(file_digests.join(""))
end
private_constant :TRANSLATION_FILES_CONTENT_CACHE_KEY
ASSET_FILES_CONTENT_CACHE_KEY = begin
folder_path = ::Rails.root.join(
*[
"app/assets/images/path/to/cell/assets",
].join("/").split("/"),
)
file_paths = Dir.glob(File.join(folder_path, "**", "*"))
file_digests = file_paths.map do |file_path|
next nil unless File.file?(file_path)
::Digest::MD5.hexdigest(File.read(file_path))
end
::Digest::MD5.hexdigest(file_digests.join(""))
end
private_constant :ASSET_FILES_CONTENT_CACHE_KEY
end
end
If you need to a cell to depend on super cell's cache key:
module PokemonTypeCardCompactStyle
class Cell < ::PokemonTypeCardDefaultStyle::Cell
# === Caching === #
def self.cache_key
super.merge(
# Update this if cell logic updated (code update != logic update)
logic: [
super.fetch(:logic, nil),
:v2018_09_17_1426,
].compact.join("+"),
template: TEMPLATE_FILES_CONTENT_CACHE_KEY,
)
end
def cache_key
super.merge(
current_locale: ::I18n.config.locale,
pokemon_type: pokemon_type.cache_key,
)
end
# === Caching === #
def show
render
end
TEMPLATE_FILES_CONTENT_CACHE_KEY = begin
view_folder_path = File.expand_path("views", __dir__)
file_paths = Dir.glob(File.join(view_folder_path, "**", "*"))
file_digests = file_paths.map do |file_path|
next nil unless File.file?(file_path)
::Digest::MD5.hexdigest(File.read(file_path))
end.compact
::Digest::MD5.hexdigest(file_digests.join(""))
end
private_constant :TEMPLATE_FILES_CONTENT_CACHE_KEY
end
yeah, i ended up doing something similar two years ago. i'm not using cells right now though
Currently I've come up with something like this
And then I have
show.erb
which has all the html (and this state is cached), and (uncached)js.erb
that has all the access to cell instance variablesWhat d'you think?
Btw, for some weird reason I have to write
call(:html) + render(:js).html_safe
, otherwise the js template gets escaped