inertiax_rails 2.0.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|