inertia_rails 3.9.0 → 3.11.0

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 4b7d385cab4b5bdd84cc50c1c9e22d838ce8f77fc680542ba1b0a11d17c95674
4
- data.tar.gz: 472113f1732205c105f10a069070fb1c6b226ffdf176e57fb15fc66cf7a0bdd2
3
+ metadata.gz: 416af24e523dd32daacb768be83fc88bf5465a87adbf49e8105cb630409e45f9
4
+ data.tar.gz: 4c1d7c883d6ffc3e3239777bd1b1f400f5a0f69c22093a307225d696551b2da5
5
5
  SHA512:
6
- metadata.gz: b18d7f00080810908560268ad97da13079b3c89d7fee8aee3b4edcb45d011aae7d16fdff8f9697b953fa7e4e76034eb9d9e9afa1cf79ca6f946cb7810222b83e
7
- data.tar.gz: 80805ac8753cdc1469de52a6f92fceb186e735070d1cc81b44ce93fc62218bea6fc7e515a2950a06f5fb1951aaa6dc2fc6fd40c6b958e5b453be6a2470c3103d
6
+ metadata.gz: 23f7eeab79b5e54cc12408f6196f946f5cb5ea78086d55ac56d06827ea0243ba96c90e432e7c69583f7531ae7cd2611a026fdce15039219c36f7ef9034888fa6
7
+ data.tar.gz: c29f0e2d3c88ebc7a4cc6a405219152a3ae5f4428b23b8ec9a82de8e38e98a52c035d15389c51858b1a6534aae47adef1a26ebd179c4892c7a7b0b76427b88b0
data/CHANGELOG.md CHANGED
@@ -4,6 +4,21 @@ All notable changes to this project will be documented in this file.
4
4
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
5
5
  and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
6
6
 
7
+ ## [3.11.0] - 2025-08-29
8
+
9
+ * Fix Svelte generator (@skryukov)
10
+ * Docs updates for SSR and 2.1.2 (@skryukov)
11
+ * Devcontainers for local dev (@kieraneglin)
12
+ * Add configurable prop transformation (@kieraneglin)
13
+ * Gradual deprecation of null errors because Inertis.js expects an empty object (@skryukov)
14
+ * Allow the more helpful UnknownFormat exception to raise when a static intertia route is requested with a non-HTML format (@skryukov)
15
+
16
+ ## [3.10.0] - 2025-07-30
17
+
18
+ * llms.txt in docs (@brandonshar and @skryukov)
19
+ * Add support for deep merging merge props (@skryukov)
20
+ * Server managed meta tags (@bknoles and @skryukov)
21
+
7
22
  ## [3.9.0] - 2025-06-18
8
23
 
9
24
  * Docs updates
@@ -1,7 +1,9 @@
1
1
  module InertiaRails
2
2
  class StaticController < InertiaRails.configuration.parent_controller.constantize
3
3
  def static
4
- render inertia: params[:component]
4
+ respond_to do |format|
5
+ format.html { render inertia: params[:component] }
6
+ end
5
7
  end
6
8
  end
7
9
  end
@@ -4,6 +4,7 @@ react:
4
4
  - "@vitejs/plugin-react"
5
5
  - "react"
6
6
  - "react-dom"
7
+ - "vite@latest"
7
8
  packages_ts:
8
9
  - "@types/react"
9
10
  - "@types/react-dom"
@@ -29,6 +30,7 @@ vue:
29
30
  packages:
30
31
  - "vue"
31
32
  - "@vitejs/plugin-vue"
33
+ - "vite@latest"
32
34
  packages_ts:
33
35
  - "typescript@~5.6.2"
34
36
  - "vue-tsc"
@@ -52,6 +54,7 @@ svelte4:
52
54
  packages:
53
55
  - "svelte@4"
54
56
  - "@sveltejs/vite-plugin-svelte@3"
57
+ - "vite@5"
55
58
  packages_ts:
56
59
  - "@tsconfig/svelte@4"
57
60
  - "svelte-check"
@@ -76,7 +79,8 @@ svelte:
76
79
  inertia_package: "@inertiajs/svelte"
77
80
  packages:
78
81
  - "svelte@5"
79
- - "@sveltejs/vite-plugin-svelte@4"
82
+ - "@sveltejs/vite-plugin-svelte"
83
+ - "vite@latest"
80
84
  packages_ts:
81
85
  - "@tsconfig/svelte@5"
82
86
  - "svelte-check"
@@ -1,6 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  InertiaRails.configure do |config|
4
- config.ssr_enabled = ViteRuby.config.ssr_build_enabled
5
4
  config.version = ViteRuby.digest
5
+ config.encrypt_history = true
6
+ config.always_include_errors_hash = true
6
7
  end
@@ -2,11 +2,6 @@ import { createInertiaApp } from '@inertiajs/svelte'
2
2
  import { mount } from 'svelte';
3
3
 
4
4
  createInertiaApp({
5
- // Set default page title
6
- // see https://inertia-rails.dev/guide/title-and-meta
7
- //
8
- // title: title => title ? `${title} - App` : 'App',
9
-
10
5
  // Disable progress bar
11
6
  //
12
7
  // see https://inertia-rails.dev/guide/progress-indicators
@@ -2,11 +2,6 @@ import { createInertiaApp, type ResolvedComponent } from '@inertiajs/svelte'
2
2
  import { mount } from 'svelte'
3
3
 
4
4
  createInertiaApp({
5
- // Set default page title
6
- // see https://inertia-rails.dev/guide/title-and-meta
7
- //
8
- // title: title => title ? `${title} - App` : 'App',
9
-
10
5
  // Disable progress bar
11
6
  //
12
7
  // see https://inertia-rails.dev/guide/progress-indicators
@@ -25,7 +20,7 @@ createInertiaApp({
25
20
  // and use the following line.
26
21
  // see https://inertia-rails.dev/guide/pages#default-layouts
27
22
  //
28
- // return { default: page.default, layout: page.layout || Layout }
23
+ // return { default: page.default, layout: page.layout || Layout } as ResolvedComponent
29
24
 
30
25
  return page
31
26
  },
@@ -12,6 +12,9 @@ module InertiaRails
12
12
  # Allows the user to hook into the default rendering behavior and change it to fit their needs
13
13
  component_path_resolver: ->(path:, action:) { "#{path}/#{action}" },
14
14
 
15
+ # A function that transforms the props before they are sent to the client.
16
+ prop_transformer: ->(props:) { props },
17
+
15
18
  # DEPRECATED: Let Rails decide which layout should be used based on the
16
19
  # controller configuration.
17
20
  layout: true,
@@ -26,8 +29,11 @@ module InertiaRails
26
29
  # Used to detect version drift between server and client.
27
30
  version: nil,
28
31
 
29
- # Allows configuring the base controller for StaticController
32
+ # Allows configuring the base controller for StaticController.
30
33
  parent_controller: '::ApplicationController',
34
+
35
+ # Whether to include empty `errors` hash to the props when no errors are present.
36
+ always_include_errors_hash: nil,
31
37
  }.freeze
32
38
 
33
39
  OPTION_NAMES = DEFAULTS.keys.freeze
@@ -89,6 +95,10 @@ module InertiaRails
89
95
  @options[:component_path_resolver].call(path: path, action: action)
90
96
  end
91
97
 
98
+ def prop_transformer(props:)
99
+ @options[:prop_transformer].call(props: props)
100
+ end
101
+
92
102
  OPTION_NAMES.each do |option|
93
103
  unless method_defined?(option)
94
104
  define_method(option) do
@@ -1,6 +1,7 @@
1
1
  require_relative "inertia_rails"
2
2
  require_relative "helper"
3
3
  require_relative "action_filter"
4
+ require_relative "meta_tag_builder"
4
5
 
5
6
  module InertiaRails
6
7
  module Controller
@@ -127,6 +128,10 @@ module InertiaRails
127
128
  super
128
129
  end
129
130
 
131
+ def inertia_meta
132
+ @inertia_meta ||= InertiaRails::MetaTagBuilder.new(self)
133
+ end
134
+
130
135
  private
131
136
 
132
137
  def inertia_view_assigns
@@ -139,7 +144,22 @@ module InertiaRails
139
144
  end
140
145
 
141
146
  def inertia_shared_data
142
- initial_data = session[:inertia_errors].present? ? {errors: session[:inertia_errors]} : {}
147
+ initial_data =
148
+ if session[:inertia_errors].present?
149
+ { errors: session[:inertia_errors] }
150
+ elsif inertia_configuration.always_include_errors_hash
151
+ { errors: {} }
152
+ else
153
+ if inertia_configuration.always_include_errors_hash.nil?
154
+ InertiaRails.deprecator.warn(
155
+ "To comply with the Inertia protocol, an empty errors hash `{errors: {}}` " \
156
+ "will be included to all responses by default starting with InertiaRails 4.0. " \
157
+ "To opt-in now, set `config.always_include_errors_hash = true`. " \
158
+ "To disable this warning, set it to `false`."
159
+ )
160
+ end
161
+ {}
162
+ end
143
163
 
144
164
  self.class._inertia_shared_data.filter_map { |shared_data|
145
165
  if shared_data.respond_to?(:call)
@@ -4,9 +4,9 @@ module InertiaRails
4
4
  class DeferProp < IgnoreOnFirstLoadProp
5
5
  DEFAULT_GROUP = 'default'
6
6
 
7
- attr_reader :group
7
+ attr_reader :group, :match_on
8
8
 
9
- def initialize(group: nil, merge: nil, deep_merge: nil, &block)
9
+ def initialize(group: nil, merge: nil, deep_merge: nil, match_on: nil, &block)
10
10
  raise ArgumentError, 'Cannot set both `deep_merge` and `merge` to true' if deep_merge && merge
11
11
 
12
12
  super(&block)
@@ -14,6 +14,7 @@ module InertiaRails
14
14
  @group = group || DEFAULT_GROUP
15
15
  @merge = merge || deep_merge
16
16
  @deep_merge = deep_merge
17
+ @match_on = match_on.nil? ? nil : Array(match_on)
17
18
  end
18
19
 
19
20
  def merge?
@@ -7,11 +7,12 @@ module InertiaRails
7
7
  module Generators
8
8
  class ControllerTemplateBase < Rails::Generators::NamedBase
9
9
  include Helper
10
+
10
11
  class_option :frontend_framework, required: true, desc: 'Frontend framework to generate the views for.',
11
12
  default: Helper.guess_the_default_framework
12
13
 
13
14
  class_option :typescript, type: :boolean, desc: 'Whether to use TypeScript',
14
- default: Helper.guess_typescript
15
+ default: Helper.uses_typescript?
15
16
 
16
17
  argument :actions, type: :array, default: [], banner: 'action action'
17
18
 
@@ -23,7 +23,7 @@ module InertiaRails
23
23
  end
24
24
  end
25
25
 
26
- def guess_typescript
26
+ def uses_typescript?
27
27
  Rails.root.join('tsconfig.json').exist?
28
28
  end
29
29
 
@@ -15,4 +15,18 @@ module InertiaRails::Helper
15
15
  def inertia_rendering?
16
16
  controller.instance_variable_get("@_inertia_rendering")
17
17
  end
18
+
19
+ def inertia_page
20
+ controller.instance_variable_get("@_inertia_page")
21
+ end
22
+
23
+ def inertia_meta_tags
24
+ meta_tag_data = (inertia_page || {}).dig(:props, :_inertia_meta) || []
25
+
26
+ meta_tags = meta_tag_data.map do |inertia_meta_tag|
27
+ inertia_meta_tag.to_tag(tag)
28
+ end
29
+
30
+ safe_join(meta_tags, "\n")
31
+ end
18
32
  end
@@ -8,6 +8,7 @@ require 'inertia_rails/optional_prop'
8
8
  require 'inertia_rails/defer_prop'
9
9
  require 'inertia_rails/merge_prop'
10
10
  require 'inertia_rails/configuration'
11
+ require 'inertia_rails/meta_tag'
11
12
 
12
13
  module InertiaRails
13
14
  class << self
@@ -33,16 +34,16 @@ module InertiaRails
33
34
  AlwaysProp.new(&block)
34
35
  end
35
36
 
36
- def merge(&block)
37
- MergeProp.new(&block)
37
+ def merge(match_on: nil, &block)
38
+ MergeProp.new(match_on: match_on, &block)
38
39
  end
39
40
 
40
- def deep_merge(&block)
41
- MergeProp.new(deep_merge: true, &block)
41
+ def deep_merge(match_on: nil, &block)
42
+ MergeProp.new(deep_merge: true, match_on: match_on, &block)
42
43
  end
43
44
 
44
- def defer(group: nil, merge: nil, deep_merge: nil, &block)
45
- DeferProp.new(group: group, merge: merge, deep_merge: deep_merge, &block)
45
+ def defer(group: nil, merge: nil, deep_merge: nil, match_on: nil, &block)
46
+ DeferProp.new(group: group, merge: merge, deep_merge: deep_merge, match_on: match_on, &block)
46
47
  end
47
48
  end
48
49
  end
@@ -2,9 +2,12 @@
2
2
 
3
3
  module InertiaRails
4
4
  class MergeProp < BaseProp
5
- def initialize(deep_merge: false, &block)
5
+ attr_reader :match_on
6
+
7
+ def initialize(deep_merge: false, match_on: nil, &block)
6
8
  super(&block)
7
9
  @deep_merge = deep_merge
10
+ @match_on = match_on.nil? ? nil : Array(match_on)
8
11
  end
9
12
 
10
13
  def merge?
@@ -0,0 +1,113 @@
1
+ # frozen_string_literal: true
2
+
3
+ module InertiaRails
4
+ class MetaTag
5
+ # See https://github.com/rails/rails/blob/v8.0.0/actionview/lib/action_view/helpers/tag_helper.rb#L84-L97
6
+ UNARY_TAGS = %i[
7
+ area base br col embed hr img input keygen link meta source track wbr
8
+ ].freeze
9
+
10
+ LD_JSON_TYPE = 'application/ld+json'
11
+ DEFAULT_SCRIPT_TYPE = 'text/plain'
12
+
13
+ GENERATABLE_HEAD_KEY_PROPERTIES = %i[name property http_equiv].freeze
14
+
15
+ def initialize(tag_name: nil, head_key: nil, allow_duplicates: false, type: nil, **tag_data)
16
+ if shortened_title_tag?(tag_name, tag_data)
17
+ @tag_name = :title
18
+ @tag_data = { inner_content: tag_data[:title] }
19
+ else
20
+ @tag_name = tag_name.nil? ? :meta : tag_name.to_sym
21
+ @tag_data = tag_data.symbolize_keys
22
+ end
23
+ @tag_type = determine_tag_type(type)
24
+ @allow_duplicates = allow_duplicates
25
+ @head_key = @tag_name == :title ? 'title' : (head_key || generate_head_key)
26
+ end
27
+
28
+ def as_json(_options = nil)
29
+ {
30
+ tagName: @tag_name,
31
+ headKey: @head_key,
32
+ type: @tag_type,
33
+ }.tap do |result|
34
+ result.merge!(@tag_data.transform_keys { |k| k.to_s.camelize(:lower).to_sym })
35
+ result.compact_blank!
36
+ end
37
+ end
38
+
39
+ def to_tag(tag_helper)
40
+ data = @tag_data.merge(type: @tag_type, inertia: @head_key)
41
+
42
+ inner_content =
43
+ if @tag_name == :script
44
+ tag_script_inner_content(data.delete(:inner_content))
45
+ else
46
+ data.delete(:inner_content)
47
+ end
48
+
49
+ if UNARY_TAGS.include? @tag_name
50
+ tag_helper.public_send(@tag_name, **data.transform_keys { |k| k.to_s.tr('_', '-').to_sym })
51
+ else
52
+ tag_helper.public_send(@tag_name, inner_content, **data.transform_keys { |k| k.to_s.tr('_', '-').to_sym })
53
+ end
54
+ end
55
+
56
+ def [](key)
57
+ key = key.to_sym
58
+ return @tag_name if key == :tag_name
59
+ return @head_key if key == :head_key
60
+ return @tag_type if key == :type
61
+
62
+ @tag_data[key]
63
+ end
64
+
65
+ private
66
+
67
+ def tag_script_inner_content(content)
68
+ case content
69
+ when Hash, Array
70
+ ERB::Util.json_escape(content.to_json).html_safe
71
+ else
72
+ content
73
+ end
74
+ end
75
+
76
+ def shortened_title_tag?(tag_name, tag_data)
77
+ tag_name.nil? && tag_data.keys == [:title]
78
+ end
79
+
80
+ def determine_tag_type(type)
81
+ return type unless @tag_name == :script
82
+
83
+ type == LD_JSON_TYPE ? LD_JSON_TYPE : DEFAULT_SCRIPT_TYPE
84
+ end
85
+
86
+ def generate_head_key
87
+ generate_meta_head_key || "#{@tag_name}-#{tag_digest}"
88
+ end
89
+
90
+ def tag_digest
91
+ signature = @tag_data.sort.map { |k, v| "#{k}=#{v}" }.join('&')
92
+ Digest::MD5.hexdigest(signature)[0, 8]
93
+ end
94
+
95
+ def generate_meta_head_key
96
+ return unless @tag_name == :meta
97
+ return 'meta-charset' if @tag_data.key?(:charset)
98
+
99
+ GENERATABLE_HEAD_KEY_PROPERTIES.each do |key|
100
+ next unless @tag_data.key?(key)
101
+
102
+ return [
103
+ 'meta',
104
+ key,
105
+ @tag_data[key].parameterize,
106
+ @allow_duplicates ? tag_digest : nil
107
+ ].compact.join('-')
108
+ end
109
+
110
+ nil
111
+ end
112
+ end
113
+ end
@@ -0,0 +1,52 @@
1
+ # frozen_string_literal: true
2
+
3
+ module InertiaRails
4
+ class MetaTagBuilder
5
+ def initialize(controller)
6
+ @controller = controller
7
+ @meta_tags = {}
8
+ end
9
+
10
+ def meta_tags
11
+ @meta_tags.values
12
+ end
13
+
14
+ def add(meta_tag)
15
+ if meta_tag.is_a?(Array)
16
+ meta_tag.each { |tag| add(tag) }
17
+ elsif meta_tag.is_a?(Hash)
18
+ add_new_tag(meta_tag)
19
+ else
20
+ raise ArgumentError, 'Meta tag must be a Hash or Array of Hashes'
21
+ end
22
+
23
+ self
24
+ end
25
+
26
+ def remove(head_key = nil, &block)
27
+ raise ArgumentError, 'Cannot provide both head_key and a block' if head_key && block_given?
28
+ raise ArgumentError, 'Must provide either head_key or a block' if head_key.nil? && !block_given?
29
+
30
+ if head_key
31
+ @meta_tags.delete(head_key)
32
+ else
33
+ @meta_tags.reject! { |_, tag| block.call(tag) }
34
+ end
35
+
36
+ self
37
+ end
38
+
39
+ def clear
40
+ @meta_tags.clear
41
+
42
+ self
43
+ end
44
+
45
+ private
46
+
47
+ def add_new_tag(new_tag_data)
48
+ new_tag = InertiaRails::MetaTag.new(**new_tag_data)
49
+ @meta_tags[new_tag[:head_key]] = new_tag
50
+ end
51
+ end
52
+ end
@@ -16,9 +16,8 @@ module InertiaRails
16
16
  :clear_history
17
17
  )
18
18
 
19
- def initialize(component, controller, request, response, render_method, props: nil, view_data: nil,
20
- deep_merge: nil, encrypt_history: nil, clear_history: nil)
21
- if component.is_a?(Hash) && !props.nil?
19
+ def initialize(component, controller, request, response, render_method, **options)
20
+ if component.is_a?(Hash) && options.key?(:props)
22
21
  raise ArgumentError,
23
22
  'Parameter `props` is not allowed when passing a Hash as the first argument'
24
23
  end
@@ -29,12 +28,13 @@ module InertiaRails
29
28
  @request = request
30
29
  @response = response
31
30
  @render_method = render_method
32
- @props = props || (component.is_a?(Hash) ? component : controller.__send__(:inertia_view_assigns))
33
- @view_data = view_data || {}
34
- @deep_merge = deep_merge.nil? ? configuration.deep_merge_shared_data : deep_merge
35
- @encrypt_history = encrypt_history.nil? ? configuration.encrypt_history : encrypt_history
36
- @clear_history = clear_history || controller.session[:inertia_clear_history] || false
31
+ @props = options.fetch(:props, component.is_a?(Hash) ? component : controller.__send__(:inertia_view_assigns))
32
+ @view_data = options.fetch(:view_data, {})
33
+ @deep_merge = options.fetch(:deep_merge, configuration.deep_merge_shared_data)
34
+ @encrypt_history = options.fetch(:encrypt_history, configuration.encrypt_history)
35
+ @clear_history = options.fetch(:clear_history, controller.session[:inertia_clear_history] || false)
37
36
  @controller.instance_variable_set('@_inertia_rendering', true)
37
+ controller.inertia_meta.add(options[:meta]) if options[:meta]
38
38
  end
39
39
 
40
40
  def render
@@ -52,6 +52,7 @@ module InertiaRails
52
52
  rescue StandardError
53
53
  nil
54
54
  end
55
+ controller.instance_variable_set('@_inertia_page', page)
55
56
  @render_method.call template: 'inertia', layout: layout, locals: view_data.merge(page: page)
56
57
  end
57
58
  end
@@ -89,8 +90,20 @@ module InertiaRails
89
90
  end
90
91
 
91
92
  def computed_props
92
- merged_props = merge_props(shared_data, props)
93
- deep_transform_props(merged_props)
93
+ # rubocop:disable Style/MultilineBlockChain
94
+ merge_props(shared_data, props)
95
+ .then do |merged_props| # Always keep errors in the props
96
+ if merged_props.key?(:errors) && !merged_props[:errors].is_a?(BaseProp)
97
+ errors = merged_props[:errors]
98
+ merged_props[:errors] = InertiaRails.always { errors }
99
+ end
100
+ merged_props
101
+ end
102
+ .then { |props| deep_transform_props(props) } # Internal hydration/filtering
103
+ .then { |props| configuration.prop_transformer(props: props) } # Apply user-defined prop transformer
104
+ .tap { |props| props[:_inertia_meta] = meta_tags if meta_tags.present? } # Add meta tags last (never transformed)
105
+
106
+ # rubocop:enable Style/MultilineBlockChain
94
107
  end
95
108
 
96
109
  def page
@@ -106,14 +119,17 @@ module InertiaRails
106
119
  deferred_props = deferred_props_keys
107
120
  default_page[:deferredProps] = deferred_props if deferred_props.present?
108
121
 
109
- all_merge_props = merge_props_keys
110
-
111
- deep_merge_props, merge_props = all_merge_props.partition do |key|
112
- @props[key].deep_merge?
122
+ deep_merge_props, merge_props = all_merge_props.partition do |_key, prop|
123
+ prop.deep_merge?
113
124
  end
114
125
 
115
- default_page[:mergeProps] = merge_props if merge_props.present?
116
- default_page[:deepMergeProps] = deep_merge_props if deep_merge_props.present?
126
+ match_props_on = all_merge_props.filter_map do |key, prop|
127
+ prop.match_on.map { |ms| "#{key}.#{ms}" } if prop.match_on.present?
128
+ end.flatten
129
+
130
+ default_page[:mergeProps] = merge_props.map(&:first) if merge_props.present?
131
+ default_page[:deepMergeProps] = deep_merge_props.map(&:first) if deep_merge_props.present?
132
+ default_page[:matchPropsOn] = match_props_on if match_props_on.present?
117
133
 
118
134
  default_page
119
135
  end
@@ -147,9 +163,16 @@ module InertiaRails
147
163
  end
148
164
  end
149
165
 
150
- def merge_props_keys
151
- @props.each_with_object([]) do |(key, prop), result|
152
- result << key if prop.try(:merge?) && reset_keys.exclude?(key)
166
+ def all_merge_props
167
+ @all_merge_props ||= @props.select do |key, prop|
168
+ next unless prop.try(:merge?)
169
+ next if reset_keys.include?(key)
170
+ next if rendering_partial_component? && (
171
+ (partial_keys.present? && partial_keys.exclude?(key.name)) ||
172
+ (partial_except_keys.present? && partial_except_keys.include?(key.name))
173
+ )
174
+
175
+ true
153
176
  end
154
177
  end
155
178
 
@@ -180,7 +203,7 @@ module InertiaRails
180
203
  def keep_prop?(prop, path)
181
204
  return true if prop.is_a?(AlwaysProp)
182
205
 
183
- if rendering_partial_component?
206
+ if rendering_partial_component? && (partial_keys.present? || partial_except_keys.present?)
184
207
  path_with_prefixes = path_prefixes(path)
185
208
  return false if excluded_by_only_partial_keys?(path_with_prefixes)
186
209
  return false if excluded_by_except_partial_keys?(path_with_prefixes)
@@ -205,5 +228,9 @@ module InertiaRails
205
228
  def excluded_by_except_partial_keys?(path_with_prefixes)
206
229
  partial_except_keys.present? && (path_with_prefixes & partial_except_keys).any?
207
230
  end
231
+
232
+ def meta_tags
233
+ controller.inertia_meta.meta_tags
234
+ end
208
235
  end
209
236
  end
@@ -75,7 +75,7 @@ RSpec.configure do |config|
75
75
  config.before(:each, inertia: true) do
76
76
  new_renderer = InertiaRails::Renderer.method(:new)
77
77
  allow(InertiaRails::Renderer).to receive(:new) do |component, controller, request, response, render, named_args|
78
- new_renderer.call(component, controller, request, response, inertia_wrap_render(render), **named_args)
78
+ new_renderer.call(component, controller, request, response, inertia_wrap_render(render), **(named_args || {}))
79
79
  end
80
80
  end
81
81
  end
@@ -1,3 +1,3 @@
1
1
  module InertiaRails
2
- VERSION = "3.9.0"
2
+ VERSION = "3.11.0"
3
3
  end
data/lib/inertia_rails.rb CHANGED
@@ -15,11 +15,7 @@ ActionController::Renderers.add :inertia do |component, options|
15
15
  request,
16
16
  response,
17
17
  method(:render),
18
- props: options[:props],
19
- view_data: options[:view_data],
20
- deep_merge: options[:deep_merge],
21
- encrypt_history: options[:encrypt_history],
22
- clear_history: options[:clear_history]
18
+ **options
23
19
  ).render
24
20
  end
25
21
 
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: inertia_rails
3
3
  version: !ruby/object:Gem::Version
4
- version: 3.9.0
4
+ version: 3.11.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Brian Knoles
@@ -9,7 +9,7 @@ authors:
9
9
  - Eugene Granovsky
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2025-06-18 00:00:00.000000000 Z
12
+ date: 2025-08-29 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: railties
@@ -225,6 +225,8 @@ files:
225
225
  - lib/inertia_rails/inertia_rails.rb
226
226
  - lib/inertia_rails/lazy_prop.rb
227
227
  - lib/inertia_rails/merge_prop.rb
228
+ - lib/inertia_rails/meta_tag.rb
229
+ - lib/inertia_rails/meta_tag_builder.rb
228
230
  - lib/inertia_rails/middleware.rb
229
231
  - lib/inertia_rails/optional_prop.rb
230
232
  - lib/inertia_rails/renderer.rb