Skip to content

Commit e124497

Browse files
committed
Make deep merging shared data optional, either with global config or per action via the renderer
1 parent d5f43e3 commit e124497

File tree

9 files changed

+110
-17
lines changed

9 files changed

+110
-17
lines changed

lib/inertia_rails.rb

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
method(:render),
1616
props: options[:props],
1717
view_data: options[:view_data],
18+
deep_merge: options[:deep_merge],
1819
).render
1920
end
2021

lib/inertia_rails/inertia_rails.rb

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,9 @@ def self.configure
1313

1414
# "Getters"
1515
def self.shared_data(controller)
16-
shared_plain_data.merge!(evaluated_blocks(controller, shared_blocks))
16+
shared_plain_data.
17+
merge!(evaluated_blocks(controller, shared_blocks)).
18+
with_indifferent_access
1719
end
1820

1921
def self.version
@@ -40,6 +42,10 @@ def self.html_headers
4042
self.threadsafe_html_headers || []
4143
end
4244

45+
def self.deep_merge_shared_data?
46+
Configuration.deep_merge_shared_data
47+
end
48+
4349
# "Setters"
4450
def self.share(**args)
4551
self.shared_plain_data = self.shared_plain_data.merge(args)
@@ -71,6 +77,7 @@ module Configuration
7177
mattr_accessor(:ssr_enabled) { false }
7278
mattr_accessor(:ssr_url) { 'http://localhost:13714' }
7379
mattr_accessor(:default_render) { false }
80+
mattr_accessor(:deep_merge_shared_data) { false }
7481

7582
def self.evaluated_version
7683
self.version.respond_to?(:call) ? self.version.call : self.version

lib/inertia_rails/renderer.rb

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,14 +6,15 @@ module InertiaRails
66
class Renderer
77
attr_reader :component, :view_data
88

9-
def initialize(component, controller, request, response, render_method, props:, view_data:)
9+
def initialize(component, controller, request, response, render_method, props:, view_data:, deep_merge:)
1010
@component = component.is_a?(TrueClass) ? "#{controller.controller_path}/#{controller.action_name}" : component
1111
@controller = controller
1212
@request = request
1313
@response = response
1414
@render_method = render_method
15-
@props = props || controller.inertia_view_assigns
15+
@props = props ? props.with_indifferent_access : controller.inertia_view_assigns.with_indifferent_access
1616
@view_data = view_data || {}
17+
@deep_merge = !deep_merge.nil? ? deep_merge : InertiaRails.deep_merge_shared_data?
1718
end
1819

1920
def render
@@ -42,15 +43,15 @@ def layout
4243
end
4344

4445
def props
45-
_props = ::InertiaRails.shared_data(@controller).deep_merge(@props).select do |key, prop|
46+
_props = ::InertiaRails.shared_data(@controller).send(prop_merge_method, @props).select do |key, prop|
4647
if rendering_partial_component?
4748
key.in? partial_keys
4849
else
4950
!prop.is_a?(InertiaRails::Lazy)
5051
end
5152
end
5253

53-
deep_transform_values(_props, lambda {|prop| prop.respond_to?(:call) ? @controller.instance_exec(&prop) : prop })
54+
deep_transform_values(_props, lambda {|prop| prop.respond_to?(:call) ? @controller.instance_exec(&prop) : prop }).with_indifferent_access
5455
end
5556

5657
def page
@@ -75,5 +76,9 @@ def partial_keys
7576
def rendering_partial_component?
7677
@request.inertia_partial? && @request.headers['X-Inertia-Partial-Component'] == component
7778
end
79+
80+
def prop_merge_method
81+
@deep_merge ? :deep_merge : :merge
82+
end
7883
end
7984
end

lib/inertia_rails/rspec.rb

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,7 @@ def inertia_tests_setup?
7474

7575
RSpec::Matchers.define :have_exact_props do |expected_props|
7676
match do |inertia|
77-
expect(inertia.props).to eq expected_props
77+
expect(inertia.props).to eq expected_props.with_indifferent_access
7878
end
7979

8080
failure_message do |inertia|
@@ -84,7 +84,7 @@ def inertia_tests_setup?
8484

8585
RSpec::Matchers.define :include_props do |expected_props|
8686
match do |inertia|
87-
expect(inertia.props).to include expected_props
87+
expect(inertia.props).to include expected_props.with_indifferent_access
8888
end
8989

9090
failure_message do |inertia|
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
class InertiaMergeInstancePropsController < ApplicationController
2+
use_inertia_instance_props
3+
inertia_share do
4+
{
5+
nested: {
6+
points: 55,
7+
rebounds: 10,
8+
}
9+
}
10+
end
11+
12+
def merge_instance_props
13+
@nested = {
14+
points: 100,
15+
}
16+
17+
render inertia: 'InertiaTestComponent', deep_merge: true
18+
end
19+
end

spec/dummy/app/controllers/inertia_merge_shared_controller.rb

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,4 +15,20 @@ def merge_shared
1515
}
1616
}
1717
end
18+
19+
def deep_merge_shared
20+
render inertia: 'ShareTestComponent', props: {
21+
nested: {
22+
assists: 300,
23+
}
24+
}, deep_merge: true
25+
end
26+
27+
def shallow_merge_shared
28+
render inertia: 'ShareTestComponent', props: {
29+
nested: {
30+
assists: 200,
31+
}
32+
}, deep_merge: false
33+
end
1834
end

spec/dummy/config/routes.rb

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,4 +36,7 @@
3636
inertia 'inertia_route' => 'TestComponent'
3737

3838
get 'merge_shared' => 'inertia_merge_shared#merge_shared'
39+
get 'deep_merge_shared' => 'inertia_merge_shared#deep_merge_shared'
40+
get 'shallow_merge_shared' => 'inertia_merge_shared#shallow_merge_shared'
41+
get 'merge_instance_props' => 'inertia_merge_instance_props#merge_instance_props'
3942
end

spec/inertia/rendering_spec.rb

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,10 @@
44
let(:controller) { double('Controller', inertia_view_assigns: {})}
55

66
context 'first load' do
7-
let(:page) { InertiaRails::Renderer.new('TestComponent', controller, request, response, '', props: nil, view_data: nil).send(:page) }
7+
let(:page) { InertiaRails::Renderer.new('TestComponent', controller, request, response, '', props: nil, view_data: nil, deep_merge: nil).send(:page) }
88

99
context 'with props' do
10-
let(:page) { InertiaRails::Renderer.new('TestComponent', controller, request, response, '', props: {name: 'Brandon', sport: 'hockey'}, view_data: nil).send(:page) }
10+
let(:page) { InertiaRails::Renderer.new('TestComponent', controller, request, response, '', props: {name: 'Brandon', sport: 'hockey'}, view_data: nil, deep_merge: nil).send(:page) }
1111
before { get props_path }
1212

1313
it { is_expected.to include inertia_div(page) }
@@ -39,7 +39,7 @@
3939
end
4040

4141
context 'subsequent requests' do
42-
let(:page) { InertiaRails::Renderer.new('TestComponent', controller, request, response, '', props: {name: 'Brandon', sport: 'hockey'}, view_data: nil).send(:page) }
42+
let(:page) { InertiaRails::Renderer.new('TestComponent', controller, request, response, '', props: {name: 'Brandon', sport: 'hockey'}, view_data: nil, deep_merge: nil).send(:page) }
4343
let(:headers) { {'X-Inertia' => true} }
4444

4545
before { get props_path, headers: headers }
@@ -64,7 +64,7 @@
6464

6565
context 'partial rendering' do
6666
let (:page) {
67-
InertiaRails::Renderer.new('TestComponent', controller, request, response, '', props: { sport: 'hockey'}, view_data: nil).send(:page)
67+
InertiaRails::Renderer.new('TestComponent', controller, request, response, '', props: { sport: 'hockey'}, view_data: nil, deep_merge: nil).send(:page)
6868
}
6969
let(:headers) {{
7070
'X-Inertia' => true,
@@ -94,7 +94,7 @@
9494
context 'lazy prop rendering' do
9595
context 'on first load' do
9696
let (:page) {
97-
InertiaRails::Renderer.new('TestComponent', controller, request, response, '', props: { name: 'Brian'}, view_data: nil).send(:page)
97+
InertiaRails::Renderer.new('TestComponent', controller, request, response, '', props: { name: 'Brian'}, view_data: nil, deep_merge: nil).send(:page)
9898
}
9999
before { get lazy_props_path }
100100

@@ -103,7 +103,7 @@
103103

104104
context 'with a partial reload' do
105105
let (:page) {
106-
InertiaRails::Renderer.new('TestComponent', controller, request, response, '', props: { sport: 'basketball', level: 'worse than he believes', grit: 'intense'}, view_data: nil).send(:page)
106+
InertiaRails::Renderer.new('TestComponent', controller, request, response, '', props: { sport: 'basketball', level: 'worse than he believes', grit: 'intense'}, view_data: nil, deep_merge: nil).send(:page)
107107
}
108108
let(:headers) {{
109109
'X-Inertia' => true,

spec/inertia/sharing_spec.rb

Lines changed: 46 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -100,9 +100,51 @@
100100
end
101101
end
102102

103-
describe 'deep merging with shared data' do
104-
let(:props) { { nested: { goals: 100, assists: 200 } } }
105-
before { get merge_shared_path, headers: {'X-Inertia' => true} }
106-
it { is_expected.to eq props }
103+
# Tests:
104+
# 1. By default, merging is shallow
105+
# 2. You can concifgure InertiaRails to deep merge by default
106+
# 3. You can override shallow merging in a specific action
107+
# 4. You can override deep merging in a specific action
108+
# 5. Make sure that the shorthand @thing-goes-to-props works
109+
describe 'deep or shallow merging shared data' do
110+
context 'with default settings (shallow merge)' do
111+
describe 'shallow merging by default' do
112+
let(:props) { { nested: { assists: 200 } } }
113+
before { get merge_shared_path, headers: {'X-Inertia' => true} }
114+
it { is_expected.to eq props }
115+
end
116+
117+
context 'with deep merge added to the renderer' do
118+
let(:props) { { nested: { goals: 100, assists: 300 } } }
119+
before { get deep_merge_shared_path, headers: {'X-Inertia' => true} }
120+
it { is_expected.to eq props }
121+
end
122+
end
123+
124+
context 'with deep merge configured as the default' do
125+
before {
126+
InertiaRails.configure { |config| config.deep_merge_shared_data = true }
127+
}
128+
after {
129+
InertiaRails.configure { |config| config.deep_merge_shared_data = false }
130+
}
131+
describe 'deep merging by default' do
132+
let(:props) { { nested: { goals: 100, assists: 200 } } }
133+
before { get merge_shared_path, headers: {'X-Inertia' => true} }
134+
it { is_expected.to eq props }
135+
end
136+
137+
describe 'overriding deep merge in a specific action' do
138+
let(:props) { { nested: { assists: 200 } } }
139+
before { get shallow_merge_shared_path, headers: {'X-Inertia' => true} }
140+
it { is_expected.to eq props }
141+
end
142+
end
143+
144+
context 'merging with instance props' do
145+
let(:props) { { nested: { points: 100, rebounds: 10 } } }
146+
before { get merge_instance_props_path, headers: {'X-Inertia' => true} }
147+
it { is_expected.to eq props }
148+
end
107149
end
108150
end

0 commit comments

Comments
 (0)