inertiax_rails 2.0.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 +7 -0
- data/.github/workflows/push.yml +33 -0
- data/.gitignore +22 -0
- data/.rspec +3 -0
- data/CHANGELOG.md +173 -0
- data/CODE_OF_CONDUCT.md +74 -0
- data/Gemfile +7 -0
- data/LICENSE.txt +21 -0
- data/README.md +273 -0
- data/Rakefile +6 -0
- data/app/controllers/inertia_rails/static_controller.rb +7 -0
- data/app/views/inertia.html.erb +1 -0
- data/app/views/inertia_ssr.html.erb +1 -0
- data/bin/console +14 -0
- data/bin/setup +8 -0
- data/inertiax_rails.gemspec +37 -0
- data/lib/generators/inertia_rails/install/controller.rb +7 -0
- data/lib/generators/inertia_rails/install/react/InertiaExample.jsx +9 -0
- data/lib/generators/inertia_rails/install/react/inertia.jsx +17 -0
- data/lib/generators/inertia_rails/install/svelte/InertiaExample.svelte +11 -0
- data/lib/generators/inertia_rails/install/svelte/inertia.js +14 -0
- data/lib/generators/inertia_rails/install/vue/InertiaExample.vue +11 -0
- data/lib/generators/inertia_rails/install/vue/inertia.js +20 -0
- data/lib/generators/inertia_rails/install_generator.rb +84 -0
- data/lib/inertia_rails/controller.rb +110 -0
- data/lib/inertia_rails/engine.rb +16 -0
- data/lib/inertia_rails/inertia_rails.rb +52 -0
- data/lib/inertia_rails/lazy.rb +28 -0
- data/lib/inertia_rails/middleware.rb +97 -0
- data/lib/inertia_rails/renderer.rb +92 -0
- data/lib/inertia_rails/rspec.rb +125 -0
- data/lib/inertia_rails/version.rb +3 -0
- data/lib/inertia_rails.rb +24 -0
- data/lib/patches/better_errors.rb +17 -0
- data/lib/patches/debug_exceptions/patch-5-0.rb +23 -0
- data/lib/patches/debug_exceptions/patch-5-1.rb +26 -0
- data/lib/patches/debug_exceptions.rb +17 -0
- data/lib/patches/mapper.rb +8 -0
- data/lib/patches/request.rb +9 -0
- data/lib/tasks/inertia_rails.rake +16 -0
- metadata +203 -0
@@ -0,0 +1,37 @@
|
|
1
|
+
lib = File.expand_path("lib", __dir__)
|
2
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
3
|
+
require "inertia_rails/version"
|
4
|
+
|
5
|
+
Gem::Specification.new do |spec|
|
6
|
+
spec.name = "inertiax_rails"
|
7
|
+
spec.version = InertiaRails::VERSION
|
8
|
+
spec.authors = ["Brian Knoles", "Brandon Shar", "Eugene Granovsky", "Stefan Buhrmester"]
|
9
|
+
spec.email = ["brain@bellawatt.com", "brandon@bellawatt.com", "eugene@bellawatt.com", "stefan@buhrmi.de"]
|
10
|
+
|
11
|
+
spec.summary = %q{Inertia adapter for Rails}
|
12
|
+
spec.homepage = "https://github.com/buhrmi/inertia-rails"
|
13
|
+
spec.license = "MIT"
|
14
|
+
|
15
|
+
spec.metadata["homepage_uri"] = spec.homepage
|
16
|
+
spec.metadata["source_code_uri"] = spec.homepage
|
17
|
+
spec.metadata["changelog_uri"] = "#{spec.homepage}/blob/master/CHANGELOG.md"
|
18
|
+
|
19
|
+
# Specify which files should be added to the gem when it is released.
|
20
|
+
# The `git ls-files -z` loads the files in the RubyGem that have been added into git.
|
21
|
+
spec.files = Dir.chdir(File.expand_path('..', __FILE__)) do
|
22
|
+
`git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
|
23
|
+
end
|
24
|
+
spec.bindir = "exe"
|
25
|
+
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
26
|
+
spec.require_paths = ["lib"]
|
27
|
+
|
28
|
+
spec.add_runtime_dependency "railties", '>= 5'
|
29
|
+
|
30
|
+
spec.add_development_dependency "bundler", "~> 2.0"
|
31
|
+
spec.add_development_dependency "rake", "~> 13.0"
|
32
|
+
spec.add_development_dependency "rspec-rails", "~> 4.0"
|
33
|
+
spec.add_development_dependency "rails-controller-testing"
|
34
|
+
spec.add_development_dependency "sqlite3"
|
35
|
+
spec.add_development_dependency "responders"
|
36
|
+
spec.add_development_dependency "debug"
|
37
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
import { App } from '@inertiajs/inertia-react';
|
2
|
+
import React from 'react';
|
3
|
+
import { render } from 'react-dom';
|
4
|
+
import { InertiaProgress } from '@inertiajs/progress';
|
5
|
+
|
6
|
+
document.addEventListener('DOMContentLoaded', () => {
|
7
|
+
InertiaProgress.init();
|
8
|
+
const el = document.getElementById('app')
|
9
|
+
|
10
|
+
render(
|
11
|
+
<App
|
12
|
+
initialPage={JSON.parse(el.dataset.page)}
|
13
|
+
resolveComponent={name => require(`../Pages/${name}`).default}
|
14
|
+
/>,
|
15
|
+
el
|
16
|
+
)
|
17
|
+
});
|
@@ -0,0 +1,14 @@
|
|
1
|
+
import { createInertiaApp } from '@inertiajs/inertia-svelte'
|
2
|
+
import { InertiaProgress } from '@inertiajs/progress'
|
3
|
+
|
4
|
+
document.addEventListener('DOMContentLoaded', () => {
|
5
|
+
InertiaProgress.init()
|
6
|
+
|
7
|
+
createInertiaApp({
|
8
|
+
id: 'app',
|
9
|
+
resolve: name => import(`../Pages/${name}.svelte`),
|
10
|
+
setup({ el, App, props }) {
|
11
|
+
new App({ target: el, props })
|
12
|
+
},
|
13
|
+
})
|
14
|
+
})
|
@@ -0,0 +1,20 @@
|
|
1
|
+
import Vue from 'vue'
|
2
|
+
|
3
|
+
import { app, plugin } from '@inertiajs/inertia-vue'
|
4
|
+
import { InertiaProgress } from '@inertiajs/progress'
|
5
|
+
|
6
|
+
document.addEventListener('DOMContentLoaded', () => {
|
7
|
+
InertiaProgress.init();
|
8
|
+
const el = document.getElementById('app')
|
9
|
+
|
10
|
+
Vue.use(plugin)
|
11
|
+
|
12
|
+
new Vue({
|
13
|
+
render: h => h(app, {
|
14
|
+
props: {
|
15
|
+
initialPage: JSON.parse(el.dataset.page),
|
16
|
+
resolveComponent: name => require(`../Pages/${name}`).default,
|
17
|
+
},
|
18
|
+
}),
|
19
|
+
}).$mount(el)
|
20
|
+
})
|
@@ -0,0 +1,84 @@
|
|
1
|
+
module InertiaRails
|
2
|
+
class InstallGenerator < Rails::Generators::Base
|
3
|
+
source_root File.expand_path('./install', __dir__)
|
4
|
+
class_option :front_end, type: :string, default: 'react'
|
5
|
+
|
6
|
+
FRONT_END_INSTALLERS = [
|
7
|
+
'react',
|
8
|
+
'vue',
|
9
|
+
'svelte',
|
10
|
+
]
|
11
|
+
|
12
|
+
def install
|
13
|
+
exit! unless installable?
|
14
|
+
|
15
|
+
install_base!
|
16
|
+
|
17
|
+
send "install_#{options[:front_end]}!"
|
18
|
+
|
19
|
+
say "You're all set! Run rails s and checkout localhost:3000/inertia-example", :green
|
20
|
+
end
|
21
|
+
|
22
|
+
protected
|
23
|
+
|
24
|
+
def installable?
|
25
|
+
unless run("./bin/rails webpacker:verify_install")
|
26
|
+
say "Sorry, you need to have webpacker installed for inertia_rails default setup.", :red
|
27
|
+
return false
|
28
|
+
end
|
29
|
+
|
30
|
+
unless options[:front_end].in? FRONT_END_INSTALLERS
|
31
|
+
say "Sorry, there is no generator for #{options[:front_end]}!\n\n", :red
|
32
|
+
say "If you are a #{options[:front_end]} developer, please help us improve inertia_rails by contributing an installer.\n\n"
|
33
|
+
say "https://github.com/inertiajs/inertia-rails/\n\n"
|
34
|
+
|
35
|
+
return false
|
36
|
+
end
|
37
|
+
|
38
|
+
true
|
39
|
+
end
|
40
|
+
|
41
|
+
def install_base!
|
42
|
+
say "Adding inertia pack tag to application layout", :blue
|
43
|
+
insert_into_file Rails.root.join("app/views/layouts/application.html.erb").to_s, after: "<%= javascript_pack_tag 'application' %>\n" do
|
44
|
+
"\t\t<%= javascript_pack_tag 'inertia' %>\n"
|
45
|
+
end
|
46
|
+
|
47
|
+
say "Installing inertia client packages", :blue
|
48
|
+
run "yarn add @inertiajs/inertia @inertiajs/progress"
|
49
|
+
|
50
|
+
say "Copying example files", :blue
|
51
|
+
template "controller.rb", Rails.root.join("app/controllers/inertia_example_controller.rb").to_s
|
52
|
+
|
53
|
+
say "Adding a route for the example inertia controller...", :blue
|
54
|
+
route "get 'inertia-example', to: 'inertia_example#index'"
|
55
|
+
end
|
56
|
+
|
57
|
+
def install_react!
|
58
|
+
say "Creating a React page component...", :blue
|
59
|
+
run 'yarn add @inertiajs/inertia-react'
|
60
|
+
template "react/InertiaExample.jsx", Rails.root.join("app/javascript/Pages/InertiaExample.js").to_s
|
61
|
+
say "Copying inertia.jsx into webpacker's packs folder...", :blue
|
62
|
+
template "react/inertia.jsx", Rails.root.join("app/javascript/packs/inertia.jsx").to_s
|
63
|
+
say "done!", :green
|
64
|
+
end
|
65
|
+
|
66
|
+
def install_vue!
|
67
|
+
say "Creating a Vue page component...", :blue
|
68
|
+
run 'yarn add @inertiajs/inertia-vue'
|
69
|
+
template "vue/InertiaExample.vue", Rails.root.join("app/javascript/Pages/InertiaExample.vue").to_s
|
70
|
+
say "Copying inertia.js into webpacker's packs folder...", :blue
|
71
|
+
template "vue/inertia.js", Rails.root.join("app/javascript/packs/inertia.js").to_s
|
72
|
+
say "done!", :green
|
73
|
+
end
|
74
|
+
|
75
|
+
def install_svelte!
|
76
|
+
say "Creating a Svelte page component...", :blue
|
77
|
+
run 'yarn add @inertiajs/inertia-svelte'
|
78
|
+
template "svelte/InertiaExample.svelte", Rails.root.join("app/javascript/Pages/InertiaExample.svelte").to_s
|
79
|
+
say "Copying inertia.js into webpacker's packs folder...", :blue
|
80
|
+
template "svelte/inertia.js", Rails.root.join("app/javascript/packs/inertia.js").to_s
|
81
|
+
say "done!", :green
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
@@ -0,0 +1,110 @@
|
|
1
|
+
require_relative "inertia_rails"
|
2
|
+
|
3
|
+
module InertiaRails
|
4
|
+
module Controller
|
5
|
+
extend ActiveSupport::Concern
|
6
|
+
|
7
|
+
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
|
24
|
+
|
25
|
+
after_action do
|
26
|
+
cookies['XSRF-TOKEN'] = form_authenticity_token unless !protect_against_forgery?
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
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?
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
def use_inertia_instance_props
|
39
|
+
before_action do
|
40
|
+
@_inertia_instance_props = true
|
41
|
+
@_inertia_skip_props = view_assigns.keys + ['_inertia_skip_props']
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
def inertia_headers
|
47
|
+
@_inertia_html_headers.join.html_safe
|
48
|
+
end
|
49
|
+
|
50
|
+
def inertia_headers=(value)
|
51
|
+
@_inertia_html_headers = value
|
52
|
+
end
|
53
|
+
|
54
|
+
def default_render
|
55
|
+
if InertiaRails.default_render?
|
56
|
+
render(inertia: true)
|
57
|
+
else
|
58
|
+
super
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
def shared_data
|
63
|
+
(@_inertia_shared_plain_data || {}).merge(evaluated_blocks)
|
64
|
+
end
|
65
|
+
|
66
|
+
def redirect_to(options = {}, response_options = {})
|
67
|
+
capture_inertia_errors(response_options)
|
68
|
+
super(options, response_options)
|
69
|
+
end
|
70
|
+
|
71
|
+
def redirect_back(fallback_location:, allow_other_host: true, **options)
|
72
|
+
capture_inertia_errors(options)
|
73
|
+
super(
|
74
|
+
fallback_location: fallback_location,
|
75
|
+
allow_other_host: allow_other_host,
|
76
|
+
**options,
|
77
|
+
)
|
78
|
+
end
|
79
|
+
|
80
|
+
def inertia_view_assigns
|
81
|
+
return {} unless @_inertia_instance_props
|
82
|
+
view_assigns.except(*@_inertia_skip_props)
|
83
|
+
end
|
84
|
+
|
85
|
+
private
|
86
|
+
|
87
|
+
def inertia_layout
|
88
|
+
layout = ::InertiaRails.layout
|
89
|
+
|
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
|
93
|
+
end
|
94
|
+
|
95
|
+
def inertia_location(url)
|
96
|
+
headers['X-Inertia-Location'] = url
|
97
|
+
head :conflict
|
98
|
+
end
|
99
|
+
|
100
|
+
def capture_inertia_errors(options)
|
101
|
+
if (inertia_errors = options.dig(:inertia, :errors))
|
102
|
+
session[:inertia_errors] = inertia_errors
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
def evaluated_blocks
|
107
|
+
(@_inertia_shared_blocks || []).map { |block| instance_exec(&block) }.reduce(&:merge) || {}
|
108
|
+
end
|
109
|
+
end
|
110
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
require_relative "middleware"
|
2
|
+
require_relative "controller"
|
3
|
+
|
4
|
+
module InertiaRails
|
5
|
+
class Engine < ::Rails::Engine
|
6
|
+
initializer "inertia_rails.configure_rails_initialization" do |app|
|
7
|
+
app.middleware.use ::InertiaRails::Middleware
|
8
|
+
end
|
9
|
+
|
10
|
+
initializer "inertia_rails.action_controller" do
|
11
|
+
ActiveSupport.on_load(:action_controller_base) do
|
12
|
+
include ::InertiaRails::Controller
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,52 @@
|
|
1
|
+
# Needed for `thread_mattr_accessor`
|
2
|
+
require 'active_support/core_ext/module/attribute_accessors_per_thread'
|
3
|
+
require 'inertia_rails/lazy'
|
4
|
+
|
5
|
+
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
|
21
|
+
|
22
|
+
def self.ssr_url
|
23
|
+
Configuration.ssr_url
|
24
|
+
end
|
25
|
+
|
26
|
+
def self.default_render?
|
27
|
+
Configuration.default_render
|
28
|
+
end
|
29
|
+
|
30
|
+
def self.deep_merge_shared_data?
|
31
|
+
Configuration.deep_merge_shared_data
|
32
|
+
end
|
33
|
+
|
34
|
+
def self.lazy(value = nil, &block)
|
35
|
+
InertiaRails::Lazy.new(value, &block)
|
36
|
+
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
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
module InertiaRails
|
2
|
+
class Lazy
|
3
|
+
def initialize(value = nil, &block)
|
4
|
+
@value = value
|
5
|
+
@block = block
|
6
|
+
end
|
7
|
+
|
8
|
+
def call
|
9
|
+
to_proc.call
|
10
|
+
end
|
11
|
+
|
12
|
+
def to_proc
|
13
|
+
# This is called by controller.instance_exec, which changes self to the
|
14
|
+
# controller instance. That makes the instance variables unavailable to the
|
15
|
+
# proc via closure. Copying the instance variables to local variables before
|
16
|
+
# the proc is returned keeps them in scope for the returned proc.
|
17
|
+
value = @value
|
18
|
+
block = @block
|
19
|
+
if value.respond_to?(:call)
|
20
|
+
value
|
21
|
+
elsif value
|
22
|
+
Proc.new { value }
|
23
|
+
else
|
24
|
+
block
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,97 @@
|
|
1
|
+
module InertiaRails
|
2
|
+
class Middleware
|
3
|
+
def initialize(app)
|
4
|
+
@app = app
|
5
|
+
end
|
6
|
+
|
7
|
+
def call(env)
|
8
|
+
InertiaRailsRequest.new(@app, env)
|
9
|
+
.response
|
10
|
+
end
|
11
|
+
|
12
|
+
class InertiaRailsRequest
|
13
|
+
def initialize(app, env)
|
14
|
+
@app = app
|
15
|
+
@env = env
|
16
|
+
end
|
17
|
+
|
18
|
+
def response
|
19
|
+
copy_xsrf_to_csrf!
|
20
|
+
status, headers, body = @app.call(@env)
|
21
|
+
request = ActionDispatch::Request.new(@env)
|
22
|
+
|
23
|
+
# Inertia errors are added to the session via redirect_to
|
24
|
+
request.session.delete(:inertia_errors) unless keep_inertia_errors?(status)
|
25
|
+
|
26
|
+
status = 303 if inertia_non_post_redirect?(status)
|
27
|
+
|
28
|
+
stale_inertia_get? ? force_refresh(request) : [status, headers, body]
|
29
|
+
end
|
30
|
+
|
31
|
+
private
|
32
|
+
|
33
|
+
def keep_inertia_errors?(status)
|
34
|
+
redirect_status?(status) || stale_inertia_request?
|
35
|
+
end
|
36
|
+
|
37
|
+
def stale_inertia_request?
|
38
|
+
inertia_request? && version_stale?
|
39
|
+
end
|
40
|
+
|
41
|
+
def redirect_status?(status)
|
42
|
+
[301, 302].include? status
|
43
|
+
end
|
44
|
+
|
45
|
+
def non_get_redirectable_method?
|
46
|
+
['PUT', 'PATCH', 'DELETE'].include? request_method
|
47
|
+
end
|
48
|
+
|
49
|
+
def inertia_non_post_redirect?(status)
|
50
|
+
inertia_request? && redirect_status?(status) && non_get_redirectable_method?
|
51
|
+
end
|
52
|
+
|
53
|
+
def stale_inertia_get?
|
54
|
+
get? && stale_inertia_request?
|
55
|
+
end
|
56
|
+
|
57
|
+
def get?
|
58
|
+
request_method == 'GET'
|
59
|
+
end
|
60
|
+
|
61
|
+
def request_method
|
62
|
+
@env['REQUEST_METHOD']
|
63
|
+
end
|
64
|
+
|
65
|
+
def inertia_version
|
66
|
+
@env['HTTP_X_INERTIA_VERSION']
|
67
|
+
end
|
68
|
+
|
69
|
+
def inertia_request?
|
70
|
+
@env['HTTP_X_INERTIA'].present?
|
71
|
+
end
|
72
|
+
|
73
|
+
def version_stale?
|
74
|
+
sent_version != saved_version
|
75
|
+
end
|
76
|
+
|
77
|
+
def sent_version
|
78
|
+
return nil if inertia_version.nil?
|
79
|
+
|
80
|
+
InertiaRails.version.is_a?(Numeric) ? inertia_version.to_f : inertia_version
|
81
|
+
end
|
82
|
+
|
83
|
+
def saved_version
|
84
|
+
InertiaRails.version.is_a?(Numeric) ? InertiaRails.version.to_f : InertiaRails.version
|
85
|
+
end
|
86
|
+
|
87
|
+
def force_refresh(request)
|
88
|
+
request.flash.keep
|
89
|
+
Rack::Response.new('', 409, {'X-Inertia-Location' => request.original_url}).finish
|
90
|
+
end
|
91
|
+
|
92
|
+
def copy_xsrf_to_csrf!
|
93
|
+
@env['HTTP_X_CSRF_TOKEN'] = @env['HTTP_X_XSRF_TOKEN'] if @env['HTTP_X_XSRF_TOKEN'] && inertia_request?
|
94
|
+
end
|
95
|
+
end
|
96
|
+
end
|
97
|
+
end
|
@@ -0,0 +1,92 @@
|
|
1
|
+
require_relative "inertia_rails"
|
2
|
+
|
3
|
+
module InertiaRails
|
4
|
+
class Renderer
|
5
|
+
attr_reader :component, :view_data
|
6
|
+
|
7
|
+
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
|
+
@controller = controller
|
10
|
+
@request = request
|
11
|
+
@response = response
|
12
|
+
@render_method = render_method
|
13
|
+
@props = props ? props : controller.inertia_view_assigns
|
14
|
+
@view_data = view_data || {}
|
15
|
+
@deep_merge = !deep_merge.nil? ? deep_merge : InertiaRails.deep_merge_shared_data?
|
16
|
+
end
|
17
|
+
|
18
|
+
def render
|
19
|
+
if @request.headers['X-Inertia']
|
20
|
+
@response.set_header('Vary', 'X-Inertia')
|
21
|
+
@response.set_header('X-Inertia', 'true')
|
22
|
+
@render_method.call json: page, status: @response.status, content_type: Mime[:json]
|
23
|
+
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})
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
private
|
30
|
+
|
31
|
+
def render_ssr
|
32
|
+
uri = URI("#{::InertiaRails.ssr_url}/render")
|
33
|
+
res = JSON.parse(Net::HTTP.post(uri, page.to_json, 'Content-Type' => 'application/json').body)
|
34
|
+
|
35
|
+
@controller.inertia_headers = res['head']
|
36
|
+
@render_method.call template: 'inertia_ssr', layout: layout, locals: (view_data).merge({page: page, html: res['body'].html_safe,})
|
37
|
+
end
|
38
|
+
|
39
|
+
def layout
|
40
|
+
@controller.send(:inertia_layout)
|
41
|
+
end
|
42
|
+
|
43
|
+
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|
|
50
|
+
if rendering_partial_component?
|
51
|
+
key.in? partial_keys
|
52
|
+
else
|
53
|
+
!prop.is_a?(InertiaRails::Lazy)
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
deep_transform_values(
|
58
|
+
_props,
|
59
|
+
lambda do |prop|
|
60
|
+
prop.respond_to?(:call) ? @controller.instance_exec(&prop) : prop
|
61
|
+
end
|
62
|
+
)
|
63
|
+
end
|
64
|
+
|
65
|
+
def page
|
66
|
+
{
|
67
|
+
component: component,
|
68
|
+
props: computed_props,
|
69
|
+
url: @request.original_fullpath,
|
70
|
+
version: ::InertiaRails.version,
|
71
|
+
}
|
72
|
+
end
|
73
|
+
|
74
|
+
def deep_transform_values(hash, proc)
|
75
|
+
return proc.call(hash) unless hash.is_a? Hash
|
76
|
+
|
77
|
+
hash.transform_values {|value| deep_transform_values(value, proc)}
|
78
|
+
end
|
79
|
+
|
80
|
+
def partial_keys
|
81
|
+
(@request.headers['X-Inertia-Partial-Data'] || '').split(',').compact.map(&:to_sym)
|
82
|
+
end
|
83
|
+
|
84
|
+
def rendering_partial_component?
|
85
|
+
@request.inertia_partial? && @request.headers['X-Inertia-Partial-Component'] == component
|
86
|
+
end
|
87
|
+
|
88
|
+
def prop_merge_method
|
89
|
+
@deep_merge ? :deep_merge : :merge
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|