Skip to content

Commit

Permalink
Add support for :if, :unless, :only, :except options in inertia_share
Browse files Browse the repository at this point in the history
  • Loading branch information
skryukov committed Oct 21, 2024
1 parent fac9eba commit b998284
Show file tree
Hide file tree
Showing 4 changed files with 118 additions and 11 deletions.
64 changes: 60 additions & 4 deletions lib/inertia_rails/controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,19 @@ module Controller
end

module ClassMethods
def inertia_share(attrs = {}, &block)
@inertia_share ||= []
@inertia_share << attrs.freeze unless attrs.empty?
@inertia_share << block if block
def inertia_share(hash = nil, **props, &block)
options = extract_inertia_share_options(props)
return push_to_inertia_share(**(hash || props), &block) if options.empty?

push_to_inertia_share do
next unless options[:if].all? { |filter| instance_exec(&filter) } if options[:if]
next unless options[:unless].none? { |filter| instance_exec(&filter) } if options[:unless]

next hash unless block

res = instance_exec(&block)
hash ? hash.merge(res) : res
end
end

def inertia_config(**attrs)
Expand Down Expand Up @@ -55,6 +64,53 @@ def _inertia_shared_data
end.freeze
end
end

private

def push_to_inertia_share(**attrs, &block)
@inertia_share ||= []
@inertia_share << attrs.freeze unless attrs.empty?
@inertia_share << block if block
end

def extract_inertia_share_options(props)
options = props.slice(:if, :unless, :only, :except)

return options if options.empty?

if props.except(:if, :unless, :only, :except).any?
raise ArgumentError, "You must not mix shared data and [:if, :unless, :only, :except] options, pass data as a hash or a block."
end

extract_inertia_share_option(options, :only, :if)
extract_inertia_share_option(options, :except, :unless)

options.transform_values! do |filters|
Array(filters).map!(&method(:filter_to_proc))
end

options
end

def extract_inertia_share_option(options, from, to)
if (from_value = options.delete(from))
filter = AbstractController::Callbacks::ActionFilter.new([:inertia_share], from, from_value)
options[to] = Array(options[to]).unshift(filter)
end
end

def filter_to_proc(filter)
case filter
when Symbol
-> { send(filter) }
when Proc
filter
when AbstractController::Callbacks::ActionFilter
-> { filter.match?(self) }
else
raise ArgumentError, "You must pass a symbol or a proc as a filter."
end
end
end

def default_render
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,32 @@ class InertiaConditionalSharingController < ApplicationController
{conditionally_shared_show_prop: 1} if action_name == "show"
end

inertia_share only: :edit do
{only_block_prop: 1}
end

inertia_share except: [:show, :index] do
{except_block_prop: 1}
end

inertia_share if: -> { is_edit? } do
{if_proc_prop: 1}
end

inertia_share unless: -> { !is_edit? } do
{unless_proc_prop: 1}
end

inertia_share({only_prop: 1}, only: :edit)

inertia_share({if_prop: 1}, if: [:is_edit?, -> { true }])

inertia_share({unless_prop: 1}, unless: :not_edit?)

inertia_share({only_if_prop: 1}, only: :edit, if: -> { true })

inertia_share({except_if_prop: 1}, except: [:index, :show], if: -> { true })

def index
render inertia: 'EmptyTestComponent', props: {
index_only_prop: 1,
Expand All @@ -16,4 +42,18 @@ def show
show_only_prop: 1,
}
end

def edit
render inertia: 'EmptyTestComponent', props: {
edit_only_prop: 1,
}
end

def not_edit?
!is_edit?
end

def is_edit?
action_name == "edit"
end
end
1 change: 1 addition & 0 deletions spec/dummy/config/routes.rb
Original file line number Diff line number Diff line change
Expand Up @@ -50,4 +50,5 @@

get 'conditional_share_index' => 'inertia_conditional_sharing#index'
get 'conditional_share_show' => 'inertia_conditional_sharing#show'
get 'conditional_share_edit' => 'inertia_conditional_sharing#edit'
end
24 changes: 17 additions & 7 deletions spec/inertia/conditional_sharing_spec.rb
Original file line number Diff line number Diff line change
@@ -1,28 +1,38 @@
RSpec.describe "conditionally shared data in a controller", type: :request do
context "when there is conditional data inside inertia_share" do
it "does not leak data between requests" do
get conditional_share_index_path, headers: {'X-Inertia' => true}
get conditional_share_index_path, headers: { 'X-Inertia' => true }
expect(JSON.parse(response.body)['props'].deep_symbolize_keys).to eq({
index_only_prop: 1,
normal_shared_prop: 1,
})

# NOTE: we actually have to run the show action twice since the new implementation
# sets up a before_action within a before_action to share the data.
# In effect, that means that the shared data isn't rendered until the second time the action is run.
get conditional_share_show_path, headers: {'X-Inertia' => true}
get conditional_share_show_path, headers: {'X-Inertia' => true}
get conditional_share_show_path, headers: { 'X-Inertia' => true }
expect(JSON.parse(response.body)['props'].deep_symbolize_keys).to eq({
normal_shared_prop: 1,
show_only_prop: 1,
conditionally_shared_show_prop: 1,
})

get conditional_share_index_path, headers: {'X-Inertia' => true}
get conditional_share_index_path, headers: { 'X-Inertia' => true }
expect(JSON.parse(response.body)['props'].deep_symbolize_keys).to eq({
index_only_prop: 1,
normal_shared_prop: 1,
})
get conditional_share_edit_path, headers: { 'X-Inertia' => true }
expect(JSON.parse(response.body)['props'].deep_symbolize_keys).to eq({
normal_shared_prop: 1,
only_block_prop: 1,
except_block_prop: 1,
if_proc_prop: 1,
unless_proc_prop: 1,
only_prop: 1,
if_prop: 1,
unless_prop: 1,
only_if_prop: 1,
except_if_prop: 1,
edit_only_prop: 1,
})
end
end
end

0 comments on commit b998284

Please sign in to comment.