Skip to content

Commit

Permalink
Support dot notation for :only keys in partial reloads
Browse files Browse the repository at this point in the history
  • Loading branch information
bknoles committed Nov 14, 2024
1 parent bbb2717 commit a42ea77
Show file tree
Hide file tree
Showing 4 changed files with 103 additions and 10 deletions.
62 changes: 52 additions & 10 deletions lib/inertia_rails/renderer.rb
Original file line number Diff line number Diff line change
Expand Up @@ -74,25 +74,21 @@ def merge_props(shared_data, props)
end

def computed_props
_props = merge_props(shared_data, props).select do |key, prop|
if rendering_partial_component?
partial_keys.none? || key.in?(partial_keys) || prop.is_a?(AlwaysProp)
else
!prop.is_a?(LazyProp)
end
end
_props = merge_props(shared_data, props)

drop_partial_except_keys(_props) if rendering_partial_component?
deep_transform_props _props do |prop, path|
next [:dont_keep] unless keep_prop?(prop, path)

deep_transform_values _props do |prop|
case prop
transformed_prop = case prop
when BaseProp
prop.call(controller)
when Proc
controller.instance_exec(&prop)
else
prop
end

[:keep, transformed_prop]
end
end

Expand All @@ -105,6 +101,22 @@ def page
}
end

def deep_transform_props(props, parent_path = '', &block)
props.reduce({}) do |transformed_props, (key, prop)|
current_path = [parent_path, key].reject(&:empty?).join('.')

if prop.is_a?(Hash)
nested = deep_transform_props(prop, current_path, &block)
transformed_props.merge!(key => nested) unless nested.empty?
else
action, transformed_prop = block.call(prop, current_path)
transformed_props.merge!(key => transformed_prop) if action == :keep
end

transformed_props
end
end

def deep_transform_values(hash, &block)
return block.call(hash) unless hash.is_a? Hash

Expand Down Expand Up @@ -138,5 +150,35 @@ def resolve_component(component)

configuration.component_path_resolver(path: controller.controller_path, action: controller.action_name)
end

def keep_prop?(prop, path)
return true if prop.is_a?(AlwaysProp)

if rendering_partial_component?
path_with_prefixes = path_prefixes(path)
return false if excluded_by_only_partial_keys?(path_with_prefixes)
return false if excluded_by_except_partial_keys?(path_with_prefixes)
end

# Precedence: Evaluate LazyProp only after partial keys have been checked
return false if prop.is_a?(LazyProp) && !rendering_partial_component?

true
end

def path_prefixes(path)
parts = path.split('.')
(0...parts.length).map do |i|
parts[0..i].join('.')
end
end

def excluded_by_only_partial_keys?(path_with_prefixes)
partial_keys.present? && (path_with_prefixes & partial_keys.map(&:to_s)).empty?
end

def excluded_by_except_partial_keys?(path_with_prefixes)
partial_except_keys.present? && (path_with_prefixes & partial_except_keys.map(&:to_s)).any?
end
end
end
24 changes: 24 additions & 0 deletions spec/dummy/app/controllers/inertia_render_test_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,30 @@ def except_props
}
end

def deeply_nested_props
render inertia: 'TestComponent', props: {
flat: 'flat param',
lazy: InertiaRails.lazy('lazy param'),
nested_lazy: InertiaRails.lazy do
{
first: 'first nested lazy param',
}
end,
nested: {
first: 'first nested param',
second: 'second nested param',
deeply_nested: {
first: 'first deeply nested param',
second: false,
what_about_nil: nil,
deeply_nested_always: InertiaRails.always { 'deeply nested always prop' },
deeply_nested_lazy: InertiaRails.lazy { 'deeply nested lazy prop' }
}
},
always: InertiaRails.always { 'always prop' }
}
end

def view_data
render inertia: 'TestComponent', view_data: {
name: 'Brian',
Expand Down
1 change: 1 addition & 0 deletions spec/dummy/config/routes.rb
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@
get 'always_props' => 'inertia_render_test#always_props'
get 'except_props' => 'inertia_render_test#except_props'
get 'non_inertiafied' => 'inertia_test#non_inertiafied'
get 'deeply_nested_props' => 'inertia_render_test#deeply_nested_props'

get 'instance_props_test' => 'inertia_rails_mimic#instance_props_test'
get 'default_render_test' => 'inertia_rails_mimic#default_render_test'
Expand Down
26 changes: 26 additions & 0 deletions spec/inertia/rendering_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,32 @@
is_expected.to include('Brandon')
end
end

context 'with dot notation' do
let(:headers) do
{
'X-Inertia' => true,
'X-Inertia-Partial-Data' => 'nested.first,nested.deeply_nested.second,nested.deeply_nested.what_about_nil',
'X-Inertia-Partial-Component' => 'TestComponent',
}
end

before { get deeply_nested_props_path, headers: headers }

it 'only renders the dot notated props' do
expect(response.parsed_body['props']).to eq(
'always' => 'always prop',
'nested' => {
'first' => 'first nested param',
'deeply_nested' => {
'second' => false,
'what_about_nil' => nil,
'deeply_nested_always' => 'deeply nested always prop',
},
},
)
end
end
end

context 'partial except rendering' do
Expand Down

0 comments on commit a42ea77

Please sign in to comment.