vident 1.0.0 → 1.0.2
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 +4 -4
- data/CHANGELOG.md +12 -0
- data/README.md +4 -1
- data/lib/vident/component_attribute_resolver.rb +27 -8
- data/lib/vident/component_class_lists.rb +7 -1
- data/lib/vident/stimulus_builder.rb +28 -11
- data/lib/vident/stimulus_helper.rb +4 -4
- data/lib/vident/version.rb +1 -1
- data/lib/vident2/caching.rb +93 -0
- data/lib/vident2/component.rb +538 -0
- data/lib/vident2/engine.rb +18 -0
- data/lib/vident2/error.rb +30 -0
- data/lib/vident2/internals/action_builder.rb +101 -0
- data/lib/vident2/internals/attribute_writer.rb +22 -0
- data/lib/vident2/internals/class_list_builder.rb +79 -0
- data/lib/vident2/internals/declaration.rb +17 -0
- data/lib/vident2/internals/declarations.rb +76 -0
- data/lib/vident2/internals/draft.rb +60 -0
- data/lib/vident2/internals/dsl.rb +198 -0
- data/lib/vident2/internals/plan.rb +12 -0
- data/lib/vident2/internals/registry.rb +41 -0
- data/lib/vident2/internals/resolver.rb +306 -0
- data/lib/vident2/internals/target_builder.rb +29 -0
- data/lib/vident2/phlex/html.rb +84 -0
- data/lib/vident2/phlex.rb +9 -0
- data/lib/vident2/stimulus/action.rb +140 -0
- data/lib/vident2/stimulus/class_map.rb +69 -0
- data/lib/vident2/stimulus/collection.rb +42 -0
- data/lib/vident2/stimulus/controller.rb +59 -0
- data/lib/vident2/stimulus/naming.rb +26 -0
- data/lib/vident2/stimulus/null.rb +16 -0
- data/lib/vident2/stimulus/outlet.rb +113 -0
- data/lib/vident2/stimulus/param.rb +62 -0
- data/lib/vident2/stimulus/target.rb +57 -0
- data/lib/vident2/stimulus/value.rb +77 -0
- data/lib/vident2/tailwind.rb +19 -0
- data/lib/vident2/version.rb +5 -0
- data/lib/vident2/view_component/base.rb +124 -0
- data/lib/vident2/view_component.rb +9 -0
- data/lib/vident2.rb +50 -0
- data/skills/vident/SKILL.md +11 -2
- data/skills/vident/api-reference.md +518 -0
- data/skills/vident/examples.md +492 -0
- metadata +35 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 5a79dcf9aadabd901dedc14da649f0aa2bee309caa0e17c7c10f0c3afea4c863
|
|
4
|
+
data.tar.gz: 34115cae656bdc0ef14f21f79aa0e98afa131b517cadd2aee5bf2f5fe6781074
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 9e3c2a2a1ea99972a07fe4e50b5652aed7a6971e389984d997f9f08509f78c3310f6b1e506e4aa7d18622055937a0cacdf58ef19e72599499fdf9af73a32fa7d
|
|
7
|
+
data.tar.gz: d40bb597e686af902916aebc6dc26b1b4bf803dfb3c4abcac1528140106fe601a229d19ac9f51b0c0d3d2a560548f29ade5356084328e6eaa99aa4946dfc75b9
|
data/CHANGELOG.md
CHANGED
|
@@ -6,6 +6,17 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/)
|
|
|
6
6
|
and this project adheres to [Semantic Versioning](http://semver.org/).
|
|
7
7
|
|
|
8
8
|
|
|
9
|
+
## [1.0.2] - 2026-04-21
|
|
10
|
+
|
|
11
|
+
### Changed
|
|
12
|
+
|
|
13
|
+
- Stimulus DSL procs (`values foo: -> { ... }`, `actions -> { ... }`, etc.) now resolve at **render time** — Phlex's `before_template` for `Vident::Phlex::HTML`, ViewComponent's `before_render` for `Vident::ViewComponent::Base` — instead of in `after_initialize`. Procs can now reach `helpers` / `view_context`, so they can call Rails helpers (`number_with_precision`, `t`, `l`, url helpers, etc.). Non-proc DSL entries still land in the collections at init time, so `after_component_initialize` mutators and external readers see them in the same order as before.
|
|
14
|
+
|
|
15
|
+
### Added
|
|
16
|
+
|
|
17
|
+
- `phlex_helpers :name1, :name2, ...` class macro on `Vident::Phlex::HTML` — opts the component into Phlex's per-helper Rails adapters (`Phlex::Rails::Helpers::<CamelCase>`) so DSL procs can call helpers bare (`number_with_precision(@amount, precision: 2)`) instead of via the deprecated `helpers.<method>`. Unknown helper names raise `ArgumentError` at class definition.
|
|
18
|
+
|
|
19
|
+
|
|
9
20
|
## [1.0.0] - 2026-04-19
|
|
10
21
|
|
|
11
22
|
### Breaking
|
|
@@ -36,6 +47,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
|
|
|
36
47
|
- `Vident::StimulusController`'s `implied_controller_path` / `implied_controller_name` overrides now raise the same `ArgumentError` as the base when `implied_controller` is nil, instead of a confusing `NoMethodError`.
|
|
37
48
|
- `stimulus_values:` and `stimulus_classes:` props now accept cross-controller entries. The type unions include `Array` (matching `stimulus_actions:`/`stimulus_targets:`), and the collection parsers pass through pre-built `StimulusValue`/`StimulusValueCollection` (and class equivalents) instead of re-wrapping them into the single-value constructor and raising `ArgumentError: Invalid number of arguments` (#23).
|
|
38
49
|
- `Vident::ComponentClassLists#class_list_builder` no longer memoises the `ClassListBuilder` instance. The first caller's `root_element_html_class:` was previously latched into the cached builder, which silently dropped the `class:` argument passed to a later `root_element(class: …)` whenever `class_list_for_stimulus_classes(:name)` ran first. The underlying `TailwindMerge::Merger` is still thread-cached, so the re-construction cost is negligible.
|
|
50
|
+
- `Vident::ComponentClassLists#class_list_for_stimulus_classes` no longer leaks root element classes (component name, `root_element_classes`, etc.) into its return value. It previously reused the shared `class_list_builder`, whose `ClassListBuilder` is initialised with all root-element class sources baked in; `build` always prepends those, so child elements received the full root class soup instead of just the named Stimulus class CSS. Fix: build from a fresh `ClassListBuilder` with no root-element sources.
|
|
39
51
|
|
|
40
52
|
## [1.0.0.beta2] - 2026-04-16
|
|
41
53
|
|
data/README.md
CHANGED
|
@@ -415,7 +415,10 @@ class DynamicComponent < Vident::ViewComponent::Base
|
|
|
415
415
|
end
|
|
416
416
|
```
|
|
417
417
|
|
|
418
|
-
Procs have access to instance variables
|
|
418
|
+
Procs have access to instance variables and component methods. They run at render time (Phlex `before_template` / ViewComponent `before_render`), so they can reach the view context:
|
|
419
|
+
|
|
420
|
+
- **Phlex**: `helpers` is deprecated in phlex-rails. Opt in per Rails helper by including the matching adapter — e.g. `include Phlex::Rails::Helpers::NumberWithPrecision` — and call the helper bare (`number_with_precision(@amount, precision: 2)`) inside the proc. Vident ships a `phlex_helpers :number_with_precision, :t, :l` class macro on `Vident::Phlex::HTML` that does the right include for each name. See [phlex.fun/rails/helpers](https://www.phlex.fun/rails/helpers) for the full list of adapters.
|
|
421
|
+
- **ViewComponent**: call `helpers.<method>` or `view_context.<method>` directly.
|
|
419
422
|
|
|
420
423
|
**Important**: Each proc returns a single value for its corresponding stimulus attribute. If a proc returns an array, that entire array is treated as a single value, not multiple separate values. To provide multiple values for an attribute, use multiple procs or mix procs with static values:
|
|
421
424
|
|
|
@@ -6,14 +6,16 @@ module Vident
|
|
|
6
6
|
|
|
7
7
|
private
|
|
8
8
|
|
|
9
|
-
# Prepare attributes set at initialization
|
|
9
|
+
# Prepare attributes set at initialization. The DSL's static entries are
|
|
10
|
+
# merged in here so user-land `after_component_initialize` mutators append
|
|
11
|
+
# after them (preserving DSL-first ordering). DSL procs are NOT resolved
|
|
12
|
+
# here — they run at render time via `resolve_stimulus_attributes_at_render_time`
|
|
13
|
+
# so they can reach `helpers` / `view_context`.
|
|
10
14
|
def prepare_component_attributes
|
|
11
15
|
prepare_stimulus_collections
|
|
12
16
|
|
|
13
|
-
|
|
14
|
-
add_stimulus_attributes_from_dsl
|
|
17
|
+
add_stimulus_attributes_from_dsl(phase: :static)
|
|
15
18
|
|
|
16
|
-
# Process root_element_attributes (higher precedence)
|
|
17
19
|
extra = root_element_attributes
|
|
18
20
|
@html_options = (extra[:html_options] || {}).merge(@html_options) if extra.key?(:html_options)
|
|
19
21
|
@root_element_attributes_classes = extra[:classes]
|
|
@@ -23,6 +25,19 @@ module Vident
|
|
|
23
25
|
Stimulus::PRIMITIVES.each do |primitive|
|
|
24
26
|
send(mutator_method(primitive), extra[primitive.key]) if extra.key?(primitive.key)
|
|
25
27
|
end
|
|
28
|
+
|
|
29
|
+
@stimulus_proc_attributes_resolved = false
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
# Render-phase: resolve DSL proc entries against the component instance,
|
|
33
|
+
# now that `helpers` / `view_context` are wired. Triggered by Phlex's
|
|
34
|
+
# `before_template` and ViewComponent's `before_render`. Idempotent —
|
|
35
|
+
# `stimulus_data_attributes` also calls this as a safety net.
|
|
36
|
+
def resolve_stimulus_attributes_at_render_time
|
|
37
|
+
return if @stimulus_proc_attributes_resolved
|
|
38
|
+
@stimulus_proc_attributes_resolved = true
|
|
39
|
+
|
|
40
|
+
add_stimulus_attributes_from_dsl(phase: :procs)
|
|
26
41
|
end
|
|
27
42
|
|
|
28
43
|
def resolve_root_element_attributes_before_render(root_element_html_options = nil)
|
|
@@ -51,10 +66,13 @@ module Vident
|
|
|
51
66
|
final_attributes.merge!(other_html_options.except(:data))
|
|
52
67
|
end
|
|
53
68
|
|
|
54
|
-
# Run
|
|
55
|
-
#
|
|
56
|
-
|
|
57
|
-
|
|
69
|
+
# Run DSL attributes through their `add_stimulus_*` mutators. `phase:` is
|
|
70
|
+
# forwarded to the builder: `:static` skips procs (init-time), `:procs`
|
|
71
|
+
# skips non-procs (render-time), `:all` resolves everything.
|
|
72
|
+
# `values_from_props` is a sidecar on values, resolved at instance
|
|
73
|
+
# render time (only during the static phase since it has no procs).
|
|
74
|
+
def add_stimulus_attributes_from_dsl(phase: :all)
|
|
75
|
+
dsl_attrs = self.class.stimulus_dsl_attributes(self, phase:)
|
|
58
76
|
return if dsl_attrs.empty?
|
|
59
77
|
|
|
60
78
|
Stimulus::PRIMITIVES.each do |primitive|
|
|
@@ -80,6 +98,7 @@ module Vident
|
|
|
80
98
|
end
|
|
81
99
|
|
|
82
100
|
def stimulus_data_attributes
|
|
101
|
+
resolve_stimulus_attributes_at_render_time
|
|
83
102
|
collections = Stimulus::PRIMITIVES.to_h { |primitive| [primitive.name, instance_variable_get(collection_ivar(primitive))] }
|
|
84
103
|
StimulusDataAttributeBuilder.new(**collections).build
|
|
85
104
|
end
|
|
@@ -8,7 +8,13 @@ module Vident
|
|
|
8
8
|
# Getter for a stimulus classes list so can be used in view to set initial state on SSR
|
|
9
9
|
# Returns a String of classes that can be used in a `class` attribute.
|
|
10
10
|
def class_list_for_stimulus_classes(*names)
|
|
11
|
-
|
|
11
|
+
# DSL proc entries are resolved lazily at render time; trigger them now
|
|
12
|
+
# so procs that use only instance state work from ERB/template.
|
|
13
|
+
resolve_stimulus_attributes_at_render_time if respond_to?(:resolve_stimulus_attributes_at_render_time, true)
|
|
14
|
+
ClassListBuilder.new(tailwind_merger:).build(
|
|
15
|
+
@stimulus_classes_collection&.to_a,
|
|
16
|
+
stimulus_class_names: names
|
|
17
|
+
) || ""
|
|
12
18
|
end
|
|
13
19
|
|
|
14
20
|
private
|
|
@@ -62,14 +62,23 @@ module Vident
|
|
|
62
62
|
self
|
|
63
63
|
end
|
|
64
64
|
|
|
65
|
-
|
|
65
|
+
# `phase:` controls which entries are resolved.
|
|
66
|
+
# - `:static` — resolve non-proc entries only; procs are skipped entirely
|
|
67
|
+
# (no placeholder). Used at init time, before helpers/view_context exist.
|
|
68
|
+
# - `:procs` — resolve proc entries only; non-procs are skipped. Used at
|
|
69
|
+
# render time so procs can reach `helpers` / `view_context`.
|
|
70
|
+
# - `:all` — resolve everything (legacy path, retained for safety).
|
|
71
|
+
def to_attributes(component_instance, phase: :all)
|
|
66
72
|
attrs = {}
|
|
67
73
|
DSL_PRIMITIVES.each do |primitive|
|
|
68
74
|
entries = @entries[primitive.name]
|
|
69
75
|
next if entries.empty?
|
|
70
|
-
|
|
76
|
+
resolved = resolve_entries(primitive, entries, component_instance, phase:)
|
|
77
|
+
attrs[primitive.key] = resolved unless resolved.nil? || resolved.empty?
|
|
78
|
+
end
|
|
79
|
+
if phase != :procs && !@values_from_props.empty?
|
|
80
|
+
attrs[:stimulus_values_from_props] = @values_from_props.dup
|
|
71
81
|
end
|
|
72
|
-
attrs[:stimulus_values_from_props] = @values_from_props.dup unless @values_from_props.empty?
|
|
73
82
|
attrs
|
|
74
83
|
end
|
|
75
84
|
|
|
@@ -87,19 +96,24 @@ module Vident
|
|
|
87
96
|
# Outlets don't support procs — static merge only. The other keyed kinds
|
|
88
97
|
# and the positional (Array-shaped) kinds resolve procs in the component
|
|
89
98
|
# instance and drop nil results.
|
|
90
|
-
def resolve_entries(primitive, entries, component_instance)
|
|
91
|
-
|
|
99
|
+
def resolve_entries(primitive, entries, component_instance, phase:)
|
|
100
|
+
if primitive.name == :outlets
|
|
101
|
+
return (phase == :procs) ? {} : entries.dup
|
|
102
|
+
end
|
|
92
103
|
|
|
93
104
|
if primitive.keyed?
|
|
94
|
-
resolve_hash_filtering_nil(entries, component_instance)
|
|
105
|
+
resolve_hash_filtering_nil(entries, component_instance, phase:)
|
|
95
106
|
else
|
|
96
|
-
resolve_array_filtering_nil(entries, component_instance)
|
|
107
|
+
resolve_array_filtering_nil(entries, component_instance, phase:)
|
|
97
108
|
end
|
|
98
109
|
end
|
|
99
110
|
|
|
100
|
-
def resolve_array_filtering_nil(array, component_instance)
|
|
111
|
+
def resolve_array_filtering_nil(array, component_instance, phase:)
|
|
101
112
|
array.each_with_object([]) do |value, out|
|
|
102
|
-
|
|
113
|
+
is_proc = callable?(value)
|
|
114
|
+
next if phase == :static && is_proc
|
|
115
|
+
next if phase == :procs && !is_proc
|
|
116
|
+
resolved = is_proc ? component_instance.instance_exec(&value) : value
|
|
103
117
|
out << resolved unless resolved.nil?
|
|
104
118
|
end
|
|
105
119
|
end
|
|
@@ -107,9 +121,12 @@ module Vident
|
|
|
107
121
|
# Dropping nil matters because Stimulus's Boolean value parser reads an
|
|
108
122
|
# empty data attribute as `true` — so `-> { flag? || nil }` would silently
|
|
109
123
|
# flip a Boolean value on. Omitting the entry keeps the attribute off.
|
|
110
|
-
def resolve_hash_filtering_nil(hash, component_instance)
|
|
124
|
+
def resolve_hash_filtering_nil(hash, component_instance, phase:)
|
|
111
125
|
hash.each_with_object({}) do |(key, value), out|
|
|
112
|
-
|
|
126
|
+
is_proc = callable?(value)
|
|
127
|
+
next if phase == :static && is_proc
|
|
128
|
+
next if phase == :procs && !is_proc
|
|
129
|
+
resolved = is_proc ? component_instance.instance_exec(&value) : value
|
|
113
130
|
out[key] = resolved unless resolved.nil?
|
|
114
131
|
end
|
|
115
132
|
end
|
|
@@ -19,16 +19,16 @@ module Vident
|
|
|
19
19
|
@stimulus_builder.instance_eval(&block)
|
|
20
20
|
end
|
|
21
21
|
|
|
22
|
-
def stimulus_dsl_attributes(component_instance)
|
|
22
|
+
def stimulus_dsl_attributes(component_instance, phase: :all)
|
|
23
23
|
# If no stimulus blocks have been defined on this class, check parent
|
|
24
24
|
if @stimulus_builder.nil? && superclass.respond_to?(:stimulus_dsl_attributes)
|
|
25
|
-
return superclass.stimulus_dsl_attributes(component_instance)
|
|
25
|
+
return superclass.stimulus_dsl_attributes(component_instance, phase:)
|
|
26
26
|
end
|
|
27
27
|
|
|
28
28
|
# Ensure inheritance is applied at access time
|
|
29
29
|
ensure_inheritance_merged
|
|
30
30
|
|
|
31
|
-
@stimulus_builder&.to_attributes(component_instance) || {}
|
|
31
|
+
@stimulus_builder&.to_attributes(component_instance, phase:) || {}
|
|
32
32
|
end
|
|
33
33
|
|
|
34
34
|
private
|
|
@@ -51,7 +51,7 @@ module Vident
|
|
|
51
51
|
end
|
|
52
52
|
|
|
53
53
|
# Instance method to get DSL attributes for this component instance
|
|
54
|
-
def stimulus_dsl_attributes = self.class.stimulus_dsl_attributes(self)
|
|
54
|
+
def stimulus_dsl_attributes(phase: :all) = self.class.stimulus_dsl_attributes(self, phase:)
|
|
55
55
|
|
|
56
56
|
# Instance method to resolve prop-mapped values at runtime
|
|
57
57
|
def resolve_values_from_props(prop_names)
|
data/lib/vident/version.rb
CHANGED
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "digest/sha1"
|
|
4
|
+
|
|
5
|
+
module Vident2
|
|
6
|
+
# Fragment-caching opt-in. Include into a component to get `cacheable?`,
|
|
7
|
+
# `cache_key`, and the `with_cache_key` / `depends_on` class helpers.
|
|
8
|
+
#
|
|
9
|
+
# `cache_component_modified_time` lives on the adapter base class
|
|
10
|
+
# (Phlex: `.rb` mtime; VC: sidecar template + `.rb` mtime).
|
|
11
|
+
module Caching
|
|
12
|
+
extend ActiveSupport::Concern
|
|
13
|
+
|
|
14
|
+
class_methods do
|
|
15
|
+
def inherited(subclass)
|
|
16
|
+
subclass.instance_variable_set(:@named_cache_key_attributes, @named_cache_key_attributes&.clone)
|
|
17
|
+
super
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def with_cache_key(*attrs, name: :_collection)
|
|
21
|
+
attrs << :component_modified_time
|
|
22
|
+
attrs << :to_h if respond_to?(:to_h)
|
|
23
|
+
named_cache_key_includes(name, *attrs.uniq)
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
attr_reader :named_cache_key_attributes
|
|
27
|
+
|
|
28
|
+
def depends_on(*klasses)
|
|
29
|
+
@component_dependencies ||= []
|
|
30
|
+
@component_dependencies += klasses
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
attr_reader :component_dependencies
|
|
34
|
+
|
|
35
|
+
def component_modified_time
|
|
36
|
+
return @component_modified_time if defined?(::Rails) && ::Rails.env.production? && @component_modified_time
|
|
37
|
+
|
|
38
|
+
raise StandardError, "Must implement cache_component_modified_time" unless respond_to?(:cache_component_modified_time)
|
|
39
|
+
|
|
40
|
+
deps = component_dependencies&.map(&:component_modified_time)&.join("-") || ""
|
|
41
|
+
@component_modified_time = deps + cache_component_modified_time
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
private
|
|
45
|
+
|
|
46
|
+
def named_cache_key_includes(name, *attrs)
|
|
47
|
+
define_cache_key_method unless @named_cache_key_attributes
|
|
48
|
+
@named_cache_key_attributes ||= {}
|
|
49
|
+
@named_cache_key_attributes[name] = attrs
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
def define_cache_key_method
|
|
53
|
+
define_method :cache_key do |n = :_collection|
|
|
54
|
+
@cache_key ||= {}
|
|
55
|
+
return @cache_key[n] if @cache_key.key?(n)
|
|
56
|
+
generate_cache_key(n)
|
|
57
|
+
@cache_key[n]
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
def component_modified_time = self.class.component_modified_time
|
|
63
|
+
|
|
64
|
+
def cacheable? = respond_to?(:cache_key)
|
|
65
|
+
|
|
66
|
+
def cache_key_modifier = ENV["RAILS_CACHE_ID"]
|
|
67
|
+
|
|
68
|
+
def cache_keys_for_sources(key_attributes)
|
|
69
|
+
sources = key_attributes.flat_map { |n| n.is_a?(Proc) ? instance_eval(&n) : send(n) }
|
|
70
|
+
sources.compact.filter_map { |item| generate_item_cache_key_from(item) unless item == self }
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
def generate_item_cache_key_from(item)
|
|
74
|
+
if item.respond_to? :cache_key_with_version
|
|
75
|
+
item.cache_key_with_version
|
|
76
|
+
elsif item.respond_to? :cache_key
|
|
77
|
+
item.cache_key
|
|
78
|
+
elsif item.is_a?(String)
|
|
79
|
+
Digest::SHA1.hexdigest(item)
|
|
80
|
+
else
|
|
81
|
+
Digest::SHA1.hexdigest(Marshal.dump(item))
|
|
82
|
+
end
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
def generate_cache_key(index)
|
|
86
|
+
key_attributes = self.class.named_cache_key_attributes[index]
|
|
87
|
+
return nil unless key_attributes
|
|
88
|
+
key = "#{self.class.name}/#{cache_keys_for_sources(key_attributes).join("/")}"
|
|
89
|
+
raise StandardError, "Cache key for key #{key} is blank!" if key.blank?
|
|
90
|
+
@cache_key[index] = cache_key_modifier.present? ? "#{key}/#{cache_key_modifier}" : key
|
|
91
|
+
end
|
|
92
|
+
end
|
|
93
|
+
end
|