inertia_rails 3.2.0 → 3.3.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 76a7152d32edba5a140ed16b25efe25a6766a7d53c955f39f6b5efb2b5fc0d12
4
- data.tar.gz: c1dc0d2bf09b2f3c71e4729a26dc6c24c78dc0fe4c5b8d09484bacee18543953
3
+ metadata.gz: 617a4b404988a934d8bfb0ce176965bec8f09d99dcb167322579152597c75261
4
+ data.tar.gz: 739e59619439d641ef16dd0816eaab093beb28990624397509a4f1c3b0fc40c6
5
5
  SHA512:
6
- metadata.gz: 70ff7787dd839a58c652ffe5fa402be2baa2036ea54b86b2b033ecaf2062304a4f5e8433316eea192ab25009d6c5e8b1cf45dfeba71dc6f8775b361cb3055f50
7
- data.tar.gz: 9e9072c293cd62385df9e8be3f464c7b688acc665ad4ce40daea5fd29c0414c18ba5c9f72967edfc3c7963974867d2791486e78714cea98ac212c64f38144ccb
6
+ metadata.gz: cc2dcfbdc024ea9a3e83bbd7f0d5afb1be1d14e753d776417f4dbfd7f6bd6e3cdc50e307b73d5495a67b8419ed29b4d2863f467bbf858383504bc7e62920e1ea
7
+ data.tar.gz: 825b82f4daf26c050c7fe703da03834143a866b2db9728cbb56dface555f4453e984b82038d61bef5e5ce40aa80ef295ef1def1b29794cdc0ef93f6f53a48267
data/.gitignore CHANGED
@@ -20,3 +20,6 @@
20
20
 
21
21
  # Appraisal
22
22
  gemfiles/*.gemfile.lock
23
+
24
+ # Local files, such as .env.development.local
25
+ *.local
data/CHANGELOG.md CHANGED
@@ -4,6 +4,13 @@ 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.3.0] - 2024-10-27
8
+
9
+ * Refactor Inertia configuration into a controller class method. Thanks @ElMassimo!
10
+ * Documentation updates. Thanks @osbre and @austenmadden!
11
+ * Further fixes to the `Vary` header. Thanks @skryukov!
12
+ * Add configuration option for the component path in the renderer.
13
+
7
14
  ## [3.2.0] - 2024-06-19
8
15
 
9
16
  * Refactor the internals of shared Inertia data to use controller instance variables instead of module level variables that run a higher risk of being leaked between requests. Big thanks to @ledermann for the initial work many years ago and to @PedroAugustoRamalhoDuarte for finishing it up!
data/README.md CHANGED
@@ -7,14 +7,26 @@
7
7
 
8
8
  ### Backend
9
9
 
10
- Just add the inertia rails gem to your Gemfile
10
+ Add the `inertia_rails` gem to your Gemfile.
11
+
11
12
  ```ruby
12
13
  gem 'inertia_rails'
13
14
  ```
14
15
 
16
+ For more instructions, see [Server-side setup](https://inertia-rails.netlify.app/guide/server-side-setup.html).
17
+
15
18
  ### Frontend
16
19
 
17
- Rails 7 specific frontend docs coming soon. For now, check out the official Inertia docs at https://inertiajs.com/ or see an example using React/Vite [here](https://github.com/BrandonShar/inertia-rails-template)
20
+ We are discussing on bringing official docs for Inertia Rails to this repo, as
21
+ the [official docs](https://inertiajs.com/client-side-setup) are specific to Laravel.
22
+
23
+ In the meantime, you can refer to the community-maintained [Client-side setup](https://inertia-rails.netlify.app/guide/client-side-setup.html).
24
+
25
+ Examples:
26
+
27
+ - [React/Vite](https://github.com/BrandonShar/inertia-rails-template)
28
+ - [React/Vite + SSR](https://github.com/ElMassimo/inertia-rails-ssr-template)
29
+ - [PingCRM with Vue and Vite](https://github.com/ledermann/pingcrm)
18
30
 
19
31
  ## Usage
20
32
 
@@ -63,12 +75,24 @@ end
63
75
  In order to use instance props, you must call `use_inertia_instance_props` on the controller (or a base controller it inherits from). If any props are provided manually, instance props
64
76
  are automatically disabled for that response. Instance props are only included if they are defined after the before filter is set from `use_inertia_instance_props`.
65
77
 
66
- Automatic component name is also opt in, you must set the `default_render` config value to `true`. Otherwise, you can simply `render inertia: true` for the same behavior explicitly.
78
+ Automatic component name is also opt in, you must set the [`default_render`](#default_render) config value to `true`. Otherwise, you can simply `render inertia: true` for the same behavior explicitly.
79
+
80
+ If the default component path doesn't match your convention, you can define a method to resolve it however you like via the `component_path_resolver` config value. The value of this should be callable and will receive the path and action and should return a string component path.
81
+
82
+ ```ruby
83
+ inertia_config(
84
+ component_path_resolver: ->(path:, action:) do
85
+ "Storefront/#{path.camelize}/#{action.camelize}"
86
+ end
87
+ )
88
+
89
+ ```
90
+
91
+
67
92
 
68
93
  ### Layout
69
94
 
70
- Inertia layouts use the rails layout convention and can be set or changed in the same way. The original `layout` config option is still functional, but will likely be deprecated in the future in favor
71
- of using rails layouts.
95
+ Inertia layouts use the rails layout convention and can be set or changed in the same way.
72
96
 
73
97
  ```ruby
74
98
  class EventsController < ApplicationController
@@ -133,20 +157,14 @@ end
133
157
  }
134
158
  ```
135
159
 
136
- Deep merging can be set as the project wide default via the InertiaRails configuration:
160
+ Deep merging can be configured using the [`deep_merge_shared_data`](#deep_merge_shared_data) configuration option.
137
161
 
138
- ```ruby
139
- # config/initializers/some_initializer.rb
140
- InertiaRails.configure do |config|
141
- config.deep_merge_shared_data = true
142
- end
143
-
144
- ```
145
-
146
- If deep merging is enabled by default, it's possible to opt out within the action:
162
+ If deep merging is enabled, you can still opt-out within the action:
147
163
 
148
164
  ```ruby
149
165
  class CrazyScorersController < ApplicationController
166
+ inertia_config(deep_merge_shared_data: true)
167
+
150
168
  inertia_share do
151
169
  {
152
170
  basketball_data: {
@@ -163,7 +181,7 @@ class CrazyScorersController < ApplicationController
163
181
  end
164
182
  end
165
183
 
166
- # Even if deep merging is set by default, since the renderer has `deep_merge: false`, it will send a shallow merge to the frontend:
184
+ # `deep_merge: false` overrides the default:
167
185
  {
168
186
  basketball_data: {
169
187
  points: 100,
@@ -177,6 +195,9 @@ On the front end, Inertia supports the concept of "partial reloads" where only t
177
195
 
178
196
  ```ruby
179
197
  inertia_share some_data: InertiaRails.lazy(lambda { some_very_slow_method })
198
+
199
+ # Using a Ruby block syntax
200
+ inertia_share some_data: InertiaRails.lazy { some_very_slow_method }
180
201
  ```
181
202
 
182
203
  ### Routing
@@ -187,34 +208,85 @@ If you don't need a controller to handle a static component, you can route direc
187
208
  inertia 'about' => 'AboutComponent'
188
209
  ```
189
210
 
190
- ### SSR
211
+ ### SSR _(experimental)_
212
+
213
+ Enable SSR via the configuration options for [`ssr_enabled`](#ssr_enabled-experimental) and [`ssr_url`](#ssr_url-experimental).
214
+
215
+ When using SSR, don't forget to add `<%= inertia_ssr_head %>` to the `<head>` of your layout (i.e. `application.html.erb`).
191
216
 
192
- Enable SSR via the config settings for `ssr_enabled` and `ssr_url`.
217
+ ## Configuration ⚙️
193
218
 
194
- When using SSR, don't forget to add `<%= inertia_headers %>` to the `<head>` of your `application.html.erb`.
219
+ Inertia Rails can be configured globally or in a specific controller (and subclasses).
195
220
 
196
- ## Configuration
221
+ ### Global Configuration
197
222
 
198
- Inertia Rails has a few different configuration options that can be set anywhere, but the most common location is from within an initializer.
223
+ If using global configuration, we recommend you place the code inside an initializer:
199
224
 
200
- The default config is shown below
201
225
  ```ruby
226
+ # config/initializers/inertia.rb
227
+
202
228
  InertiaRails.configure do |config|
203
-
204
- # set the current version for automatic asset refreshing. A string value should be used if any.
205
- config.version = nil
206
- # enable default inertia rendering (warning! this will override rails default rendering behavior)
207
- config.default_render = true
208
-
209
- # ssr specific options
210
- config.ssr_enabled = false
211
- config.ssr_url = 'http://localhost:13714'
229
+ # Example: force a full-reload if the deployed assets change.
230
+ config.version = ViteRuby.digest
231
+ end
232
+ ```
212
233
 
213
- config.deep_merge_shared_data = false
214
-
234
+ The default configuration can be found [here](https://github.com/inertiajs/inertia-rails/blob/master/lib/inertia_rails/configuration.rb#L5-L22).
235
+
236
+ ### Local Configuration
237
+
238
+ Use `inertia_config` in your controllers to override global settings:
239
+
240
+ ```ruby
241
+ class EventsController < ApplicationController
242
+ inertia_config(
243
+ version: "events-#{InertiaRails.configuration.version}",
244
+ ssr_enabled: -> { action_name == "index" },
245
+ )
215
246
  end
216
247
  ```
217
248
 
249
+ ### Configuration Options
250
+
251
+ #### `version` _(recommended)_
252
+
253
+ This allows Inertia to detect if the app running in the client is oudated,
254
+ forcing a full page visit instead of an XHR visit on the next request.
255
+
256
+ See [assets versioning](https://inertiajs.com/asset-versioning).
257
+
258
+ __Default__: `nil`
259
+
260
+ #### `deep_merge_shared_data`
261
+
262
+ When enabled, props will be deep merged with shared data, combining hashes
263
+ with the same keys instead of replacing them.
264
+
265
+ __Default__: `false`
266
+
267
+ #### `default_render`
268
+
269
+ Overrides Rails default rendering behavior to render using Inertia by default.
270
+
271
+ __Default__: `false`
272
+
273
+ #### `ssr_enabled` _(experimental)_
274
+
275
+ Whether to use a JavaScript server to pre-render your JavaScript pages,
276
+ allowing your visitors to receive fully rendered HTML when they first visit
277
+ your application.
278
+
279
+ Requires a JS server to be available at `ssr_url`. [_Example_](https://github.com/ElMassimo/inertia-rails-ssr-template)
280
+
281
+ __Default__: `false`
282
+
283
+ #### `ssr_url` _(experimental)_
284
+
285
+ The URL of the JS server that will pre-render the app using the specified
286
+ component and props.
287
+
288
+ __Default__: `"http://localhost:13714"`
289
+
218
290
  ## Testing
219
291
 
220
292
  If you're using Rspec, Inertia Rails comes with some nice test helpers to make things simple.
data/bin/console CHANGED
@@ -1,14 +1,13 @@
1
1
  #!/usr/bin/env ruby
2
2
 
3
- require "bundler/setup"
4
- require "inertia_rails/rails"
5
-
6
- # You can add fixtures and/or initialization code here to make experimenting
7
- # with your gem easier. You can also use a different console, if you like.
3
+ require "pathname"
4
+ ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../../Gemfile",
5
+ Pathname.new(__FILE__).realpath)
8
6
 
9
- # (If you use this, don't forget to add pry to your Gemfile!)
10
- # require "pry"
11
- # Pry.start
7
+ require "rubygems"
8
+ require "bundler/setup"
9
+ require "rails/all"
10
+ require "inertia_rails"
12
11
 
13
12
  require "irb"
14
13
  IRB.start(__FILE__)
@@ -6,7 +6,7 @@ Gem::Specification.new do |spec|
6
6
  spec.name = "inertia_rails"
7
7
  spec.version = InertiaRails::VERSION
8
8
  spec.authors = ["Brian Knoles", "Brandon Shar", "Eugene Granovsky"]
9
- spec.email = ["brain@bellawatt.com", "brandon@bellawatt.com", "eugene@bellawatt.com"]
9
+ spec.email = ["brian@bellawatt.com", "brandon@bellawatt.com", "eugene@bellawatt.com"]
10
10
 
11
11
  spec.summary = %q{Inertia adapter for Rails}
12
12
  spec.homepage = "https://github.com/inertiajs/inertia-rails"
@@ -0,0 +1,90 @@
1
+ # frozen_string_literal: true
2
+
3
+ module InertiaRails
4
+ class Configuration
5
+ DEFAULTS = {
6
+ # Whether to combine hashes with the same keys instead of replacing them.
7
+ deep_merge_shared_data: false,
8
+
9
+ # Overrides Rails default rendering behavior to render using Inertia by default.
10
+ default_render: false,
11
+
12
+ # Allows the user to hook into the default rendering behavior and change it to fit their needs
13
+ component_path_resolver: ->(path:, action:) { "#{path}/#{action}" },
14
+
15
+ # DEPRECATED: Let Rails decide which layout should be used based on the
16
+ # controller configuration.
17
+ layout: true,
18
+
19
+ # SSR options.
20
+ ssr_enabled: false,
21
+ ssr_url: 'http://localhost:13714',
22
+
23
+ # Used to detect version drift between server and client.
24
+ version: nil,
25
+ }.freeze
26
+
27
+ OPTION_NAMES = DEFAULTS.keys.freeze
28
+
29
+ protected attr_reader :controller
30
+ protected attr_reader :options
31
+
32
+ def initialize(controller: nil, **attrs)
33
+ @controller = controller
34
+ @options = attrs.extract!(*OPTION_NAMES)
35
+
36
+ unless attrs.empty?
37
+ raise ArgumentError, "Unknown options for #{self.class}: #{attrs.keys}"
38
+ end
39
+ end
40
+
41
+ def bind_controller(controller)
42
+ Configuration.new(**@options, controller: controller)
43
+ end
44
+
45
+ def freeze
46
+ @options.freeze
47
+ super
48
+ end
49
+
50
+ def merge!(config)
51
+ @options.merge!(config.options)
52
+ self
53
+ end
54
+
55
+ def merge(config)
56
+ Configuration.new(**@options.merge(config.options))
57
+ end
58
+
59
+ # Internal: Finalizes the configuration for a specific controller.
60
+ def with_defaults(config)
61
+ @options = config.options.merge(@options)
62
+ freeze
63
+ end
64
+
65
+ def component_path_resolver(path:, action:)
66
+ @options[:component_path_resolver].call(path:, action:)
67
+ end
68
+
69
+ OPTION_NAMES.each do |option|
70
+ define_method(option) {
71
+ evaluate_option @options[option]
72
+ } unless method_defined?(option)
73
+ define_method("#{option}=") { |value|
74
+ @options[option] = value
75
+ }
76
+ end
77
+
78
+ def self.default
79
+ new(**DEFAULTS)
80
+ end
81
+
82
+ private
83
+
84
+ def evaluate_option(value)
85
+ return value unless value.respond_to?(:call)
86
+ return value.call unless controller
87
+ controller.instance_exec(&value)
88
+ end
89
+ end
90
+ end
@@ -1,37 +1,32 @@
1
1
  require_relative "inertia_rails"
2
+ require_relative "helper"
2
3
 
3
4
  module InertiaRails
4
5
  module Controller
5
6
  extend ActiveSupport::Concern
6
7
 
7
8
  included do
8
- helper_method :inertia_headers
9
-
10
- before_action do
11
- error_sharing = proc do
12
- # :inertia_errors are deleted from the session by the middleware
13
- if @_request && session[:inertia_errors].present?
14
- { errors: session[:inertia_errors] }
15
- else
16
- {}
17
- end
18
- end
19
-
20
- @_inertia_shared_plain_data ||= {}
21
- @_inertia_shared_blocks ||= [error_sharing]
22
- @_inertia_html_headers ||= []
23
- end
9
+ helper ::InertiaRails::Helper
24
10
 
25
11
  after_action do
26
- cookies['XSRF-TOKEN'] = form_authenticity_token unless !protect_against_forgery?
12
+ cookies['XSRF-TOKEN'] = form_authenticity_token if protect_against_forgery?
27
13
  end
28
14
  end
29
15
 
30
16
  module ClassMethods
31
- def inertia_share(hash = nil, &block)
32
- before_action do
33
- @_inertia_shared_plain_data = @_inertia_shared_plain_data.merge(hash) if hash
34
- @_inertia_shared_blocks = @_inertia_shared_blocks + [block] if block_given?
17
+ def inertia_share(attrs = {}, &block)
18
+ @inertia_share ||= []
19
+ @inertia_share << attrs.freeze unless attrs.empty?
20
+ @inertia_share << block if block
21
+ end
22
+
23
+ def inertia_config(**attrs)
24
+ config = InertiaRails::Configuration.new(**attrs)
25
+
26
+ if @inertia_config
27
+ @inertia_config.merge!(config)
28
+ else
29
+ @inertia_config = config
35
30
  end
36
31
  end
37
32
 
@@ -41,28 +36,35 @@ module InertiaRails
41
36
  @_inertia_skip_props = view_assigns.keys + ['_inertia_skip_props']
42
37
  end
43
38
  end
44
- end
45
39
 
46
- def inertia_headers
47
- @_inertia_html_headers.join.html_safe
48
- end
40
+ def _inertia_configuration
41
+ @_inertia_configuration ||= begin
42
+ config = superclass.try(:_inertia_configuration) || ::InertiaRails.configuration
43
+ @inertia_config&.with_defaults(config) || config
44
+ end
45
+ end
49
46
 
50
- def inertia_headers=(value)
51
- @_inertia_html_headers = value
47
+ def _inertia_shared_data
48
+ @_inertia_shared_data ||= begin
49
+ shared_data = superclass.try(:_inertia_shared_data)
50
+
51
+ if @inertia_share && shared_data.present?
52
+ shared_data + @inertia_share.freeze
53
+ else
54
+ @inertia_share || shared_data || []
55
+ end.freeze
56
+ end
57
+ end
52
58
  end
53
59
 
54
60
  def default_render
55
- if InertiaRails.default_render?
61
+ if inertia_configuration.default_render
56
62
  render(inertia: true)
57
63
  else
58
64
  super
59
65
  end
60
66
  end
61
67
 
62
- def shared_data
63
- (@_inertia_shared_plain_data || {}).merge(evaluated_blocks)
64
- end
65
-
66
68
  def redirect_to(options = {}, response_options = {})
67
69
  capture_inertia_errors(response_options)
68
70
  super(options, response_options)
@@ -77,19 +79,27 @@ module InertiaRails
77
79
  )
78
80
  end
79
81
 
82
+ private
83
+
80
84
  def inertia_view_assigns
81
85
  return {} unless @_inertia_instance_props
82
86
  view_assigns.except(*@_inertia_skip_props)
83
87
  end
84
88
 
85
- private
89
+ def inertia_configuration
90
+ self.class._inertia_configuration.bind_controller(self)
91
+ end
86
92
 
87
- def inertia_layout
88
- layout = ::InertiaRails.layout
93
+ def inertia_shared_data
94
+ initial_data = session[:inertia_errors].present? ? {errors: session[:inertia_errors]} : {}
89
95
 
90
- # When the global configuration is not set, let Rails decide which layout
91
- # should be used based on the controller configuration.
92
- layout.nil? ? true : layout
96
+ self.class._inertia_shared_data.filter_map { |shared_data|
97
+ if shared_data.respond_to?(:call)
98
+ instance_exec(&shared_data)
99
+ else
100
+ shared_data
101
+ end
102
+ }.reduce(initial_data, &:merge)
93
103
  end
94
104
 
95
105
  def inertia_location(url)
@@ -102,9 +112,5 @@ module InertiaRails
102
112
  session[:inertia_errors] = inertia_errors
103
113
  end
104
114
  end
105
-
106
- def evaluated_blocks
107
- (@_inertia_shared_blocks || []).map { |block| instance_exec(&block) }.reduce(&:merge) || {}
108
- end
109
115
  end
110
116
  end
@@ -0,0 +1,14 @@
1
+ require_relative 'inertia_rails'
2
+
3
+ module InertiaRails::Helper
4
+ def inertia_ssr_head
5
+ controller.instance_variable_get("@_inertia_ssr_head")
6
+ end
7
+
8
+ def inertia_headers
9
+ InertiaRails.deprecator.warn(
10
+ "`inertia_headers` is deprecated and will be removed in InertiaRails 4.0, use `inertia_ssr_head` instead."
11
+ )
12
+ inertia_ssr_head
13
+ end
14
+ end
@@ -1,52 +1,20 @@
1
1
  # Needed for `thread_mattr_accessor`
2
2
  require 'active_support/core_ext/module/attribute_accessors_per_thread'
3
3
  require 'inertia_rails/lazy'
4
+ require 'inertia_rails/configuration'
4
5
 
5
6
  module InertiaRails
6
- def self.configure
7
- yield(Configuration)
8
- end
9
-
10
- def self.version
11
- Configuration.evaluated_version
12
- end
13
-
14
- def self.layout
15
- Configuration.layout
16
- end
17
-
18
- def self.ssr_enabled?
19
- Configuration.ssr_enabled
20
- end
7
+ CONFIGURATION = Configuration.default
21
8
 
22
- def self.ssr_url
23
- Configuration.ssr_url
24
- end
25
-
26
- def self.default_render?
27
- Configuration.default_render
9
+ def self.configure
10
+ yield(CONFIGURATION)
28
11
  end
29
12
 
30
- def self.deep_merge_shared_data?
31
- Configuration.deep_merge_shared_data
13
+ def self.configuration
14
+ CONFIGURATION
32
15
  end
33
16
 
34
17
  def self.lazy(value = nil, &block)
35
18
  InertiaRails::Lazy.new(value, &block)
36
19
  end
37
-
38
- private
39
-
40
- module Configuration
41
- mattr_accessor(:layout) { nil }
42
- mattr_accessor(:version) { nil }
43
- mattr_accessor(:ssr_enabled) { false }
44
- mattr_accessor(:ssr_url) { 'http://localhost:13714' }
45
- mattr_accessor(:default_render) { false }
46
- mattr_accessor(:deep_merge_shared_data) { false }
47
-
48
- def self.evaluated_version
49
- self.version.respond_to?(:call) ? self.version.call : self.version
50
- end
51
- end
52
20
  end
@@ -5,8 +5,7 @@ module InertiaRails
5
5
  end
6
6
 
7
7
  def call(env)
8
- InertiaRailsRequest.new(@app, env)
9
- .response
8
+ InertiaRailsRequest.new(@app, env).response
10
9
  end
11
10
 
12
11
  class InertiaRailsRequest
@@ -58,11 +57,15 @@ module InertiaRails
58
57
  request_method == 'GET'
59
58
  end
60
59
 
60
+ def controller
61
+ @env["action_controller.instance"]
62
+ end
63
+
61
64
  def request_method
62
65
  @env['REQUEST_METHOD']
63
66
  end
64
67
 
65
- def inertia_version
68
+ def client_version
66
69
  @env['HTTP_X_INERTIA_VERSION']
67
70
  end
68
71
 
@@ -71,17 +74,15 @@ module InertiaRails
71
74
  end
72
75
 
73
76
  def version_stale?
74
- sent_version != saved_version
77
+ coerce_version(client_version) != coerce_version(server_version)
75
78
  end
76
79
 
77
- def sent_version
78
- return nil if inertia_version.nil?
79
-
80
- InertiaRails.version.is_a?(Numeric) ? inertia_version.to_f : inertia_version
80
+ def server_version
81
+ controller&.send(:inertia_configuration)&.version
81
82
  end
82
83
 
83
- def saved_version
84
- InertiaRails.version.is_a?(Numeric) ? InertiaRails.version.to_f : InertiaRails.version
84
+ def coerce_version(version)
85
+ server_version.is_a?(Numeric) ? version.to_f : version
85
86
  end
86
87
 
87
88
  def force_refresh(request)
@@ -1,52 +1,74 @@
1
+ require 'net/http'
2
+ require 'json'
1
3
  require_relative "inertia_rails"
2
4
 
3
5
  module InertiaRails
4
6
  class Renderer
5
- attr_reader :component, :view_data
7
+ attr_reader(
8
+ :component,
9
+ :configuration,
10
+ :controller,
11
+ :props,
12
+ :view_data,
13
+ )
6
14
 
7
15
  def initialize(component, controller, request, response, render_method, props: nil, view_data: nil, deep_merge: nil)
8
- @component = component.is_a?(TrueClass) ? "#{controller.controller_path}/#{controller.action_name}" : component
9
16
  @controller = controller
17
+ @configuration = controller.__send__(:inertia_configuration)
18
+ @component = resolve_component(component)
10
19
  @request = request
11
20
  @response = response
12
21
  @render_method = render_method
13
- @props = props ? props : controller.inertia_view_assigns
22
+ @props = props || controller.__send__(:inertia_view_assigns)
14
23
  @view_data = view_data || {}
15
- @deep_merge = !deep_merge.nil? ? deep_merge : InertiaRails.deep_merge_shared_data?
24
+ @deep_merge = !deep_merge.nil? ? deep_merge : configuration.deep_merge_shared_data
16
25
  end
17
26
 
18
27
  def render
28
+ if @response.headers["Vary"].blank?
29
+ @response.headers["Vary"] = 'X-Inertia'
30
+ else
31
+ @response.headers["Vary"] = "#{@response.headers["Vary"]}, X-Inertia"
32
+ end
19
33
  if @request.headers['X-Inertia']
20
- @response.set_header('Vary', 'X-Inertia')
21
34
  @response.set_header('X-Inertia', 'true')
22
35
  @render_method.call json: page, status: @response.status, content_type: Mime[:json]
23
36
  else
24
- return render_ssr if ::InertiaRails.ssr_enabled? rescue nil
25
- @render_method.call template: 'inertia', layout: layout, locals: (view_data).merge({page: page})
37
+ return render_ssr if configuration.ssr_enabled rescue nil
38
+ @render_method.call template: 'inertia', layout: layout, locals: view_data.merge(page: page)
26
39
  end
27
40
  end
28
41
 
29
42
  private
30
43
 
31
44
  def render_ssr
32
- uri = URI("#{::InertiaRails.ssr_url}/render")
45
+ uri = URI("#{configuration.ssr_url}/render")
33
46
  res = JSON.parse(Net::HTTP.post(uri, page.to_json, 'Content-Type' => 'application/json').body)
34
47
 
35
- @controller.inertia_headers = res['head']
36
- @render_method.call html: res['body'].html_safe, layout: layout, locals: (view_data).merge({page: page})
48
+ controller.instance_variable_set("@_inertia_ssr_head", res['head'].join.html_safe)
49
+ @render_method.call html: res['body'].html_safe, layout: layout, locals: view_data.merge(page: page)
37
50
  end
38
51
 
39
52
  def layout
40
- @controller.send(:inertia_layout)
53
+ layout = configuration.layout
54
+ layout.nil? ? true : layout
55
+ end
56
+
57
+ def shared_data
58
+ controller.__send__(:inertia_shared_data)
59
+ end
60
+
61
+ # Cast props to symbol keyed hash before merging so that we have a consistent data structure and
62
+ # avoid duplicate keys after merging.
63
+ #
64
+ # Functionally, this permits using either string or symbol keys in the controller. Since the results
65
+ # is cast to json, we should treat string/symbol keys as identical.
66
+ def merge_props(shared_data, props)
67
+ shared_data.deep_symbolize_keys.send(@deep_merge ? :deep_merge : :merge, props.deep_symbolize_keys)
41
68
  end
42
69
 
43
70
  def computed_props
44
- # Cast props to symbol keyed hash before merging so that we have a consistent data structure and
45
- # avoid duplicate keys after merging.
46
- #
47
- # Functionally, this permits using either string or symbol keys in the controller. Since the results
48
- # is cast to json, we should treat string/symbol keys as identical.
49
- _props = @controller.shared_data.merge.deep_symbolize_keys.send(prop_merge_method, @props.deep_symbolize_keys).select do |key, prop|
71
+ _props = merge_props(shared_data, props).select do |key, prop|
50
72
  if rendering_partial_component?
51
73
  key.in? partial_keys
52
74
  else
@@ -57,7 +79,7 @@ module InertiaRails
57
79
  deep_transform_values(
58
80
  _props,
59
81
  lambda do |prop|
60
- prop.respond_to?(:call) ? @controller.instance_exec(&prop) : prop
82
+ prop.respond_to?(:call) ? controller.instance_exec(&prop) : prop
61
83
  end
62
84
  )
63
85
  end
@@ -67,7 +89,7 @@ module InertiaRails
67
89
  component: component,
68
90
  props: computed_props,
69
91
  url: @request.original_fullpath,
70
- version: ::InertiaRails.version,
92
+ version: configuration.version,
71
93
  }
72
94
  end
73
95
 
@@ -85,8 +107,10 @@ module InertiaRails
85
107
  @request.inertia_partial? && @request.headers['X-Inertia-Partial-Component'] == component
86
108
  end
87
109
 
88
- def prop_merge_method
89
- @deep_merge ? :deep_merge : :merge
110
+ def resolve_component(component)
111
+ return component unless component.is_a? TrueClass
112
+
113
+ configuration.component_path_resolver(path: controller.controller_path, action: controller.action_name)
90
114
  end
91
115
  end
92
116
  end
@@ -25,9 +25,16 @@ module InertiaRails
25
25
  protected
26
26
 
27
27
  def set_values(params)
28
- @view_data = params[:locals].except(:page)
29
- @props = params[:locals][:page][:props]
30
- @component = params[:locals][:page][:component]
28
+ if params[:locals].present?
29
+ @view_data = params[:locals].except(:page)
30
+ @props = params[:locals][:page][:props]
31
+ @component = params[:locals][:page][:component]
32
+ else
33
+ # Sequential Inertia request
34
+ @view_data = {}
35
+ @props = params[:json][:props]
36
+ @component = params[:json][:component]
37
+ end
31
38
  end
32
39
  end
33
40
 
@@ -1,3 +1,3 @@
1
1
  module InertiaRails
2
- VERSION = "3.2.0"
2
+ VERSION = "3.3.0"
3
3
  end
data/lib/inertia_rails.rb CHANGED
@@ -21,4 +21,8 @@ end
21
21
 
22
22
  module InertiaRails
23
23
  class Error < StandardError; end
24
+
25
+ def self.deprecator # :nodoc:
26
+ @deprecator ||= ActiveSupport::Deprecation.new
27
+ end
24
28
  end
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.2.0
4
+ version: 3.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Brian Knoles
@@ -10,7 +10,7 @@ authors:
10
10
  autorequire:
11
11
  bindir: exe
12
12
  cert_chain: []
13
- date: 2024-06-19 00:00:00.000000000 Z
13
+ date: 2024-10-27 00:00:00.000000000 Z
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
16
16
  name: railties
@@ -126,7 +126,7 @@ dependencies:
126
126
  version: '0'
127
127
  description:
128
128
  email:
129
- - brain@bellawatt.com
129
+ - brian@bellawatt.com
130
130
  - brandon@bellawatt.com
131
131
  - eugene@bellawatt.com
132
132
  executables: []
@@ -156,8 +156,10 @@ files:
156
156
  - lib/generators/inertia_rails/install/vue/inertia.js
157
157
  - lib/generators/inertia_rails/install_generator.rb
158
158
  - lib/inertia_rails.rb
159
+ - lib/inertia_rails/configuration.rb
159
160
  - lib/inertia_rails/controller.rb
160
161
  - lib/inertia_rails/engine.rb
162
+ - lib/inertia_rails/helper.rb
161
163
  - lib/inertia_rails/inertia_rails.rb
162
164
  - lib/inertia_rails/lazy.rb
163
165
  - lib/inertia_rails/middleware.rb