@@ -48,7 +48,7 @@ def render
4848 else
4949 "#{ @response . headers [ 'Vary' ] } , X-Inertia"
5050 end
51- if @request . headers [ 'X-Inertia' ]
51+ if @request . inertia?
5252 @response . set_header ( 'X-Inertia' , 'true' )
5353 @render_method . call json : page . to_json , status : @response . status , content_type : Mime [ :json ]
5454 else
@@ -128,6 +128,9 @@ def page
128128 @page [ :scrollProps ] = scroll_props if scroll_props . present?
129129 @page . merge! ( resolve_merge_props )
130130
131+ once_props = resolve_once_props
132+ @page [ :onceProps ] = once_props if once_props . present?
133+
131134 @page
132135 end
133136
@@ -173,6 +176,17 @@ def resolve_merge_props
173176 } . delete_if { |_ , v | v . blank? }
174177 end
175178
179+ def resolve_once_props
180+ @props . each_with_object ( { } ) do |( key , prop ) , result |
181+ next unless prop . try ( :once? )
182+ next if excluded_by_partial_request? ( [ key . to_s ] )
183+
184+ once_key = ( prop . once_key || key ) . to_s
185+
186+ result [ once_key ] = { prop : key . to_s , expiresAt : prop . expires_at } . compact
187+ end
188+ end
189+
176190 def resolve_match_on_props
177191 all_merge_props . filter_map do |key , prop |
178192 prop . match_on . map! { |ms | "#{ key } .#{ ms } " } if prop . match_on . present?
@@ -251,6 +265,10 @@ def partial_except_keys
251265 @partial_except_keys ||= ( @request . headers [ 'X-Inertia-Partial-Except' ] || '' ) . split ( ',' ) . compact_blank!
252266 end
253267
268+ def except_once_keys
269+ @except_once_keys ||= ( @request . headers [ 'X-Inertia-Except-Once-Props' ] || '' ) . split ( ',' ) . compact_blank!
270+ end
271+
254272 def rendering_partial_component?
255273 @request . headers [ 'X-Inertia-Partial-Component' ] == @component
256274 end
@@ -265,19 +283,30 @@ def resolve_component(component)
265283
266284 def keep_prop? ( prop , path )
267285 return true if prop . is_a? ( AlwaysProp )
268-
269- if rendering_partial_component? && ( partial_keys . present? || partial_except_keys . present? )
270- path_with_prefixes = path_prefixes ( path )
271- return false if excluded_by_only_partial_keys? ( path_with_prefixes )
272- return false if excluded_by_except_partial_keys? ( path_with_prefixes )
273- end
286+ return false if excluded_by_once_cache? ( prop , path )
287+ return false if excluded_by_partial_request? ( path )
274288
275289 # Precedence: Evaluate IgnoreOnFirstLoadProp only after partial keys have been checked
276290 return false if prop . is_a? ( IgnoreOnFirstLoadProp ) && !rendering_partial_component?
277291
278292 true
279293 end
280294
295+ def excluded_by_once_cache? ( prop , path )
296+ return false unless prop . try ( :once? )
297+ return false if prop . try ( :fresh? )
298+
299+ once_key = ( prop . once_key || path . join ( '.' ) ) . to_s
300+ except_once_keys . include? ( once_key )
301+ end
302+
303+ def excluded_by_partial_request? ( path )
304+ return false unless rendering_partial_component? && ( partial_keys . present? || partial_except_keys . present? )
305+
306+ path_with_prefixes = path_prefixes ( path )
307+ excluded_by_only_partial_keys? ( path_with_prefixes ) || excluded_by_except_partial_keys? ( path_with_prefixes )
308+ end
309+
281310 def path_prefixes ( parts )
282311 ( 0 ...parts . length ) . map do |i |
283312 parts [ 0 ..i ] . join ( '.' )
0 commit comments