inertia_rails 3.1.4 → 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: 6b3768cf7f6b1a5551c999666848f0df4357d13ccb7a961025c390aac4c67756
4
- data.tar.gz: 4f01d96c927dc9f81bcd519954681d9192ec9606fa3884d26e4860c8ea3999cd
3
+ metadata.gz: 617a4b404988a934d8bfb0ce176965bec8f09d99dcb167322579152597c75261
4
+ data.tar.gz: 739e59619439d641ef16dd0816eaab093beb28990624397509a4f1c3b0fc40c6
5
5
  SHA512:
6
- metadata.gz: 74b32a38b4e9f4bd10f6619467b60dd0b176226f5b83f5ed924ed095523bdbb61cdd6eb2679320f44b087fc286c32e594c49ffb9cf48506236127ea887e0db93
7
- data.tar.gz: c92169d183122b11dac0d4ea6b66b22072d0c586080f424d172561c18db2c3d1a6f2939c4a22cbadab33a23346d9c4c246de67b663929ba1bd8df76177572796
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,19 @@ 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
+
14
+ ## [3.2.0] - 2024-06-19
15
+
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!
17
+ * Change the Inertia response to set the `Vary` header to `X-Inertia` instead of `Accept`, Thanks @osbre!
18
+ * Always set the `XSRF-TOKEN` in an `after_action` request instead of only on non-Inertia requests. This fixes a bug where logging out (and resetting the session) via Inertia would create a CSRF token mismatch on a subsequent Inertia request (until you manually hard refreshed the page). Thanks @jordanhiltunen!
19
+
7
20
  ## [3.1.4] - 2024-04-28
8
21
 
9
22
  * Reset Inertia shared data after each RSpec example where `inertia: true` is used. Thanks @coreyaus!
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
@@ -6,22 +6,27 @@ module InertiaRails
6
6
  extend ActiveSupport::Concern
7
7
 
8
8
  included do
9
- before_action do
10
- # :inertia_errors are deleted from the session by the middleware
11
- InertiaRails.share(errors: session[:inertia_errors]) if session[:inertia_errors].present?
12
- end
13
9
  helper ::InertiaRails::Helper
14
10
 
15
11
  after_action do
16
- cookies['XSRF-TOKEN'] = form_authenticity_token unless request.inertia? || !protect_against_forgery?
12
+ cookies['XSRF-TOKEN'] = form_authenticity_token if protect_against_forgery?
17
13
  end
18
14
  end
19
15
 
20
16
  module ClassMethods
21
- def inertia_share(**args, &block)
22
- before_action do
23
- InertiaRails.share(**args) if args
24
- InertiaRails.share_block(block) if block
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
25
30
  end
26
31
  end
27
32
 
@@ -31,10 +36,29 @@ module InertiaRails
31
36
  @_inertia_skip_props = view_assigns.keys + ['_inertia_skip_props']
32
37
  end
33
38
  end
39
+
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
46
+
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
34
58
  end
35
59
 
36
60
  def default_render
37
- if InertiaRails.default_render?
61
+ if inertia_configuration.default_render
38
62
  render(inertia: true)
39
63
  else
40
64
  super
@@ -55,19 +79,27 @@ module InertiaRails
55
79
  )
56
80
  end
57
81
 
82
+ private
83
+
58
84
  def inertia_view_assigns
59
85
  return {} unless @_inertia_instance_props
60
86
  view_assigns.except(*@_inertia_skip_props)
61
87
  end
62
88
 
63
- private
89
+ def inertia_configuration
90
+ self.class._inertia_configuration.bind_controller(self)
91
+ end
64
92
 
65
- def inertia_layout
66
- layout = ::InertiaRails.layout
93
+ def inertia_shared_data
94
+ initial_data = session[:inertia_errors].present? ? {errors: session[:inertia_errors]} : {}
67
95
 
68
- # When the global configuration is not set, let Rails decide which layout
69
- # should be used based on the controller configuration.
70
- 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)
71
103
  end
72
104
 
73
105
  def inertia_location(url)
@@ -1,7 +1,14 @@
1
1
  require_relative 'inertia_rails'
2
2
 
3
3
  module InertiaRails::Helper
4
+ def inertia_ssr_head
5
+ controller.instance_variable_get("@_inertia_ssr_head")
6
+ end
7
+
4
8
  def inertia_headers
5
- ::InertiaRails.html_headers.join.html_safe
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
6
13
  end
7
14
  end
@@ -1,106 +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
- thread_mattr_accessor :threadsafe_shared_plain_data
7
- thread_mattr_accessor :threadsafe_shared_blocks
8
- thread_mattr_accessor :threadsafe_html_headers
7
+ CONFIGURATION = Configuration.default
9
8
 
10
9
  def self.configure
11
- yield(Configuration)
10
+ yield(CONFIGURATION)
12
11
  end
13
12
 
14
- # "Getters"
15
- def self.shared_data(controller)
16
- shared_plain_data.
17
- merge!(evaluated_blocks(controller, shared_blocks))
18
- end
19
-
20
- def self.version
21
- Configuration.evaluated_version
22
- end
23
-
24
- def self.layout
25
- Configuration.layout
26
- end
27
-
28
- def self.ssr_enabled?
29
- Configuration.ssr_enabled
30
- end
31
-
32
- def self.ssr_url
33
- Configuration.ssr_url
34
- end
35
-
36
- def self.default_render?
37
- Configuration.default_render
38
- end
39
-
40
- def self.html_headers
41
- self.threadsafe_html_headers || []
42
- end
43
-
44
- def self.deep_merge_shared_data?
45
- Configuration.deep_merge_shared_data
46
- end
47
-
48
- # "Setters"
49
- def self.share(**args)
50
- self.shared_plain_data = self.shared_plain_data.merge(args)
51
- end
52
-
53
- def self.share_block(block)
54
- self.shared_blocks = self.shared_blocks + [block]
55
- end
56
-
57
- def self.html_headers=(headers)
58
- self.threadsafe_html_headers = headers
59
- end
60
-
61
- def self.reset!
62
- self.shared_plain_data = {}
63
- self.shared_blocks = []
64
- self.html_headers = []
13
+ def self.configuration
14
+ CONFIGURATION
65
15
  end
66
16
 
67
17
  def self.lazy(value = nil, &block)
68
18
  InertiaRails::Lazy.new(value, &block)
69
19
  end
70
-
71
- private
72
-
73
- module Configuration
74
- mattr_accessor(:layout) { nil }
75
- mattr_accessor(:version) { nil }
76
- mattr_accessor(:ssr_enabled) { false }
77
- mattr_accessor(:ssr_url) { 'http://localhost:13714' }
78
- mattr_accessor(:default_render) { false }
79
- mattr_accessor(:deep_merge_shared_data) { false }
80
-
81
- def self.evaluated_version
82
- self.version.respond_to?(:call) ? self.version.call : self.version
83
- end
84
- end
85
-
86
- # Getters and setters to provide default values for the threadsafe attributes
87
- def self.shared_plain_data
88
- self.threadsafe_shared_plain_data || {}
89
- end
90
-
91
- def self.shared_plain_data=(val)
92
- self.threadsafe_shared_plain_data = val
93
- end
94
-
95
- def self.shared_blocks
96
- self.threadsafe_shared_blocks || []
97
- end
98
-
99
- def self.shared_blocks=(val)
100
- self.threadsafe_shared_blocks = val
101
- end
102
-
103
- def self.evaluated_blocks(controller, blocks)
104
- blocks.flat_map { |block| controller.instance_exec(&block) }.reduce(&:merge) || {}
105
- end
106
20
  end
@@ -5,10 +5,7 @@ module InertiaRails
5
5
  end
6
6
 
7
7
  def call(env)
8
- InertiaRailsRequest.new(@app, env)
9
- .response
10
- ensure
11
- ::InertiaRails.reset!
8
+ InertiaRailsRequest.new(@app, env).response
12
9
  end
13
10
 
14
11
  class InertiaRailsRequest
@@ -22,7 +19,7 @@ module InertiaRails
22
19
  status, headers, body = @app.call(@env)
23
20
  request = ActionDispatch::Request.new(@env)
24
21
 
25
- # Inertia errors are added to the session via redirect_to
22
+ # Inertia errors are added to the session via redirect_to
26
23
  request.session.delete(:inertia_errors) unless keep_inertia_errors?(status)
27
24
 
28
25
  status = 303 if inertia_non_post_redirect?(status)
@@ -60,11 +57,15 @@ module InertiaRails
60
57
  request_method == 'GET'
61
58
  end
62
59
 
60
+ def controller
61
+ @env["action_controller.instance"]
62
+ end
63
+
63
64
  def request_method
64
65
  @env['REQUEST_METHOD']
65
66
  end
66
67
 
67
- def inertia_version
68
+ def client_version
68
69
  @env['HTTP_X_INERTIA_VERSION']
69
70
  end
70
71
 
@@ -73,17 +74,15 @@ module InertiaRails
73
74
  end
74
75
 
75
76
  def version_stale?
76
- sent_version != saved_version
77
+ coerce_version(client_version) != coerce_version(server_version)
77
78
  end
78
79
 
79
- def sent_version
80
- return nil if inertia_version.nil?
81
-
82
- InertiaRails.version.is_a?(Numeric) ? inertia_version.to_f : inertia_version
80
+ def server_version
81
+ controller&.send(:inertia_configuration)&.version
83
82
  end
84
83
 
85
- def saved_version
86
- 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
87
86
  end
88
87
 
89
88
  def force_refresh(request)
@@ -97,4 +96,3 @@ module InertiaRails
97
96
  end
98
97
  end
99
98
  end
100
-
@@ -4,51 +4,71 @@ require_relative "inertia_rails"
4
4
 
5
5
  module InertiaRails
6
6
  class Renderer
7
- attr_reader :component, :view_data
7
+ attr_reader(
8
+ :component,
9
+ :configuration,
10
+ :controller,
11
+ :props,
12
+ :view_data,
13
+ )
8
14
 
9
15
  def initialize(component, controller, request, response, render_method, props: nil, view_data: nil, deep_merge: nil)
10
- @component = component.is_a?(TrueClass) ? "#{controller.controller_path}/#{controller.action_name}" : component
11
16
  @controller = controller
17
+ @configuration = controller.__send__(:inertia_configuration)
18
+ @component = resolve_component(component)
12
19
  @request = request
13
20
  @response = response
14
21
  @render_method = render_method
15
- @props = props ? props : controller.inertia_view_assigns
22
+ @props = props || controller.__send__(:inertia_view_assigns)
16
23
  @view_data = view_data || {}
17
- @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
18
25
  end
19
26
 
20
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
21
33
  if @request.headers['X-Inertia']
22
- @response.set_header('Vary', 'Accept')
23
34
  @response.set_header('X-Inertia', 'true')
24
35
  @render_method.call json: page, status: @response.status, content_type: Mime[:json]
25
36
  else
26
- return render_ssr if ::InertiaRails.ssr_enabled? rescue nil
27
- @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)
28
39
  end
29
40
  end
30
41
 
31
42
  private
32
43
 
33
44
  def render_ssr
34
- uri = URI("#{::InertiaRails.ssr_url}/render")
45
+ uri = URI("#{configuration.ssr_url}/render")
35
46
  res = JSON.parse(Net::HTTP.post(uri, page.to_json, 'Content-Type' => 'application/json').body)
36
-
37
- ::InertiaRails.html_headers = res['head']
38
- @render_method.call html: res['body'].html_safe, layout: layout, locals: (view_data).merge({page: page})
47
+
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)
39
50
  end
40
51
 
41
52
  def layout
42
- @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)
43
68
  end
44
69
 
45
70
  def computed_props
46
- # Cast props to symbol keyed hash before merging so that we have a consistent data structure and
47
- # avoid duplicate keys after merging.
48
- #
49
- # Functionally, this permits using either string or symbol keys in the controller. Since the results
50
- # is cast to json, we should treat string/symbol keys as identical.
51
- _props = ::InertiaRails.shared_data(@controller).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|
52
72
  if rendering_partial_component?
53
73
  key.in? partial_keys
54
74
  else
@@ -59,7 +79,7 @@ module InertiaRails
59
79
  deep_transform_values(
60
80
  _props,
61
81
  lambda do |prop|
62
- prop.respond_to?(:call) ? @controller.instance_exec(&prop) : prop
82
+ prop.respond_to?(:call) ? controller.instance_exec(&prop) : prop
63
83
  end
64
84
  )
65
85
  end
@@ -69,7 +89,7 @@ module InertiaRails
69
89
  component: component,
70
90
  props: computed_props,
71
91
  url: @request.original_fullpath,
72
- version: ::InertiaRails.version,
92
+ version: configuration.version,
73
93
  }
74
94
  end
75
95
 
@@ -87,8 +107,10 @@ module InertiaRails
87
107
  @request.inertia_partial? && @request.headers['X-Inertia-Partial-Component'] == component
88
108
  end
89
109
 
90
- def prop_merge_method
91
- @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)
92
114
  end
93
115
  end
94
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
 
@@ -65,7 +72,6 @@ RSpec.configure do |config|
65
72
  }
66
73
 
67
74
  config.before(:each, inertia: true) do
68
- ::InertiaRails.reset!
69
75
  new_renderer = InertiaRails::Renderer.method(:new)
70
76
  allow(InertiaRails::Renderer).to receive(:new) do |component, controller, request, response, render, named_args|
71
77
  new_renderer.call(component, controller, request, response, inertia_wrap_render(render), **named_args)
@@ -1,3 +1,3 @@
1
1
  module InertiaRails
2
- VERSION = "3.1.4"
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.1.4
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-04-28 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,6 +156,7 @@ 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
161
162
  - lib/inertia_rails/helper.rb
@@ -194,7 +195,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
194
195
  - !ruby/object:Gem::Version
195
196
  version: '0'
196
197
  requirements: []
197
- rubygems_version: 3.5.3
198
+ rubygems_version: 3.5.10
198
199
  signing_key:
199
200
  specification_version: 4
200
201
  summary: Inertia adapter for Rails