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 +4 -4
- data/.gitignore +3 -0
- data/CHANGELOG.md +13 -0
- data/README.md +105 -33
- data/bin/console +7 -8
- data/inertia_rails.gemspec +1 -1
- data/lib/inertia_rails/configuration.rb +90 -0
- data/lib/inertia_rails/controller.rb +48 -16
- data/lib/inertia_rails/helper.rb +8 -1
- data/lib/inertia_rails/inertia_rails.rb +5 -91
- data/lib/inertia_rails/middleware.rb +12 -14
- data/lib/inertia_rails/renderer.rb +44 -22
- data/lib/inertia_rails/rspec.rb +10 -4
- data/lib/inertia_rails/version.rb +1 -1
- data/lib/inertia_rails.rb +4 -0
- metadata +5 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 617a4b404988a934d8bfb0ce176965bec8f09d99dcb167322579152597c75261
|
4
|
+
data.tar.gz: 739e59619439d641ef16dd0816eaab093beb28990624397509a4f1c3b0fc40c6
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: cc2dcfbdc024ea9a3e83bbd7f0d5afb1be1d14e753d776417f4dbfd7f6bd6e3cdc50e307b73d5495a67b8419ed29b4d2863f467bbf858383504bc7e62920e1ea
|
7
|
+
data.tar.gz: 825b82f4daf26c050c7fe703da03834143a866b2db9728cbb56dface555f4453e984b82038d61bef5e5ce40aa80ef295ef1def1b29794cdc0ef93f6f53a48267
|
data/.gitignore
CHANGED
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
|
-
|
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
|
-
|
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.
|
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
|
160
|
+
Deep merging can be configured using the [`deep_merge_shared_data`](#deep_merge_shared_data) configuration option.
|
137
161
|
|
138
|
-
|
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
|
-
#
|
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
|
-
|
217
|
+
## Configuration ⚙️
|
193
218
|
|
194
|
-
|
219
|
+
Inertia Rails can be configured globally or in a specific controller (and subclasses).
|
195
220
|
|
196
|
-
|
221
|
+
### Global Configuration
|
197
222
|
|
198
|
-
|
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
|
-
|
205
|
-
|
206
|
-
|
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
|
-
|
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 "
|
4
|
-
|
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
|
-
|
10
|
-
|
11
|
-
|
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__)
|
data/inertia_rails.gemspec
CHANGED
@@ -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 = ["
|
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
|
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(
|
22
|
-
|
23
|
-
|
24
|
-
|
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
|
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
|
-
|
89
|
+
def inertia_configuration
|
90
|
+
self.class._inertia_configuration.bind_controller(self)
|
91
|
+
end
|
64
92
|
|
65
|
-
def
|
66
|
-
|
93
|
+
def inertia_shared_data
|
94
|
+
initial_data = session[:inertia_errors].present? ? {errors: session[:inertia_errors]} : {}
|
67
95
|
|
68
|
-
|
69
|
-
|
70
|
-
|
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)
|
data/lib/inertia_rails/helper.rb
CHANGED
@@ -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
|
-
|
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
|
-
|
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(
|
10
|
+
yield(CONFIGURATION)
|
12
11
|
end
|
13
12
|
|
14
|
-
|
15
|
-
|
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
|
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
|
-
|
77
|
+
coerce_version(client_version) != coerce_version(server_version)
|
77
78
|
end
|
78
79
|
|
79
|
-
def
|
80
|
-
|
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
|
86
|
-
|
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
|
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
|
22
|
+
@props = props || controller.__send__(:inertia_view_assigns)
|
16
23
|
@view_data = view_data || {}
|
17
|
-
@deep_merge = !deep_merge.nil? ? deep_merge :
|
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
|
27
|
-
@render_method.call template: 'inertia', layout: layout, locals:
|
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("#{
|
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
|
-
|
38
|
-
@render_method.call html: res['body'].html_safe, layout: layout, locals:
|
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
|
-
|
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
|
-
|
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) ?
|
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:
|
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
|
91
|
-
|
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
|
data/lib/inertia_rails/rspec.rb
CHANGED
@@ -25,9 +25,16 @@ module InertiaRails
|
|
25
25
|
protected
|
26
26
|
|
27
27
|
def set_values(params)
|
28
|
-
|
29
|
-
|
30
|
-
|
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)
|
data/lib/inertia_rails.rb
CHANGED
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.
|
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-
|
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
|
-
-
|
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.
|
198
|
+
rubygems_version: 3.5.10
|
198
199
|
signing_key:
|
199
200
|
specification_version: 4
|
200
201
|
summary: Inertia adapter for Rails
|