inertia_builder 0.1.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/lib/inertia_builder/controller.rb +29 -0
- data/lib/inertia_builder/handler.rb +13 -0
- data/lib/inertia_builder/railtie.rb +17 -0
- data/lib/inertia_builder/renderer.rb +31 -0
- data/lib/inertia_builder/version.rb +5 -0
- data/lib/inertia_builder.rb +25 -0
- data/test/controller_test.rb +82 -0
- data/test/inertia_builder_test.rb +161 -0
- data/test/test_helper.rb +20 -0
- metadata +124 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 671869c4438679ee4343c013de503e09f283cbdc46c4335426b14a1902e006b3
|
4
|
+
data.tar.gz: bdb6b02a5cf77582a76d9b924775afa58a33aff02bdc677aab23b0c21ba124da
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 6729a9608eeaf81447b9d38350d25080b4856de2150f9e3a6b1a556af4dd73012c5ddf4a34d3e29e0d6ae220a93f156c0b616b4abcdebd2246858df5718645c6
|
7
|
+
data.tar.gz: 55b3c90f175f655aefb21912ab84e34e99e481b61580af7baa87f2da23d40ff24ac945eb0bdee4a3e0d72fe276262f12b1c6ba7193db6c75c632643729a83fd0
|
@@ -0,0 +1,29 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module InertiaBuilder
|
4
|
+
module Controller
|
5
|
+
extend ActiveSupport::Concern
|
6
|
+
|
7
|
+
included do
|
8
|
+
layout -> { inertia_json_request? ? false : 'application' }
|
9
|
+
|
10
|
+
before_action :force_json_response_with_html_template, if: -> { inertia_json_request? }
|
11
|
+
end
|
12
|
+
|
13
|
+
private
|
14
|
+
|
15
|
+
def inertia_json_request?
|
16
|
+
request.headers['X-Inertia'].present?
|
17
|
+
end
|
18
|
+
|
19
|
+
def force_json_response_with_html_template
|
20
|
+
response.content_type = Mime[:json]
|
21
|
+
response.headers['Vary'] = if response.headers['Vary'].blank?
|
22
|
+
'X-Inertia'
|
23
|
+
else
|
24
|
+
"#{response.headers['Vary']}, X-Inertia"
|
25
|
+
end
|
26
|
+
response.set_header('X-Inertia', 'true')
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module InertiaBuilder
|
4
|
+
class Handler
|
5
|
+
def self.call(template, source = nil)
|
6
|
+
source ||= template.source
|
7
|
+
|
8
|
+
# Keep line numbers right in the error.
|
9
|
+
%{__already_defined = defined?(prop); prop||=InertiaBuilder::PropBuilder.new(self);component=true;#{source};
|
10
|
+
InertiaBuilder::Renderer.new(self, prop.props, component).render unless __already_defined}
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'rails/railtie'
|
4
|
+
|
5
|
+
module InertiaBuilder
|
6
|
+
class Railtie < ::Rails::Railtie
|
7
|
+
initializer :inertia_builder do
|
8
|
+
ActiveSupport.on_load(:action_controller) do
|
9
|
+
include InertiaBuilder::Controller
|
10
|
+
end
|
11
|
+
|
12
|
+
ActiveSupport.on_load(:action_view) do
|
13
|
+
ActionView::Template.register_template_handler :inertia, InertiaBuilder::Handler
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module InertiaBuilder
|
4
|
+
class Renderer
|
5
|
+
def initialize(view_context, props, component)
|
6
|
+
@view_context = view_context
|
7
|
+
@inertia_renderer = ::InertiaRails::Renderer.new(
|
8
|
+
component,
|
9
|
+
view_context.controller,
|
10
|
+
view_context.request,
|
11
|
+
view_context.response,
|
12
|
+
view_context.controller.method(:render),
|
13
|
+
props: props
|
14
|
+
)
|
15
|
+
end
|
16
|
+
|
17
|
+
def render
|
18
|
+
page = @inertia_renderer.send(:page)
|
19
|
+
|
20
|
+
if @view_context.request.headers['X-Inertia']
|
21
|
+
page.to_json
|
22
|
+
else
|
23
|
+
@view_context.controller.render_to_string(
|
24
|
+
template: 'inertia',
|
25
|
+
layout: false,
|
26
|
+
locals: @inertia_renderer.send(:view_data).merge(page: page)
|
27
|
+
)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'jbuilder/jbuilder_template'
|
4
|
+
require 'inertia_builder/handler'
|
5
|
+
require 'inertia_builder/controller'
|
6
|
+
require 'inertia_builder/renderer'
|
7
|
+
require 'inertia_builder/railtie' if defined?(Rails)
|
8
|
+
|
9
|
+
module InertiaBuilder
|
10
|
+
class PropBuilder < JbuilderTemplate
|
11
|
+
alias props attributes!
|
12
|
+
|
13
|
+
private
|
14
|
+
|
15
|
+
def _render_partial(options)
|
16
|
+
options[:handlers] = [:inertia]
|
17
|
+
options[:locals][:prop] = self
|
18
|
+
@context.render options
|
19
|
+
end
|
20
|
+
|
21
|
+
def _render_active_model_partial(object)
|
22
|
+
@context.render object, prop: self, handlers: [:inertia]
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,82 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'test_helper'
|
4
|
+
require 'action_view/testing/resolvers'
|
5
|
+
require 'rails/controller/testing'
|
6
|
+
|
7
|
+
class TestController < ActionController::Base
|
8
|
+
include InertiaBuilder::Controller
|
9
|
+
|
10
|
+
def index; end
|
11
|
+
end
|
12
|
+
|
13
|
+
class ControllerTest < ActionController::TestCase
|
14
|
+
include Rails::Controller::Testing::TemplateAssertions
|
15
|
+
|
16
|
+
tests TestController
|
17
|
+
|
18
|
+
def setup
|
19
|
+
super
|
20
|
+
|
21
|
+
@routes = ActionDispatch::Routing::RouteSet.new
|
22
|
+
@routes.draw { get 'index' => 'test#index' }
|
23
|
+
|
24
|
+
resolver = ActionView::FixtureResolver.new(
|
25
|
+
'layouts/application.html.erb' => '<html><body><%= yield %></body></html>',
|
26
|
+
'test/index.html.inertia' => 'prop.content "content"'
|
27
|
+
)
|
28
|
+
|
29
|
+
@controller.prepend_view_path(resolver)
|
30
|
+
end
|
31
|
+
|
32
|
+
def teardown
|
33
|
+
super
|
34
|
+
@routes.clear!
|
35
|
+
end
|
36
|
+
|
37
|
+
def test_renders_index_html_inertia_for_a_standard_non_inertia_request
|
38
|
+
get :index
|
39
|
+
|
40
|
+
assert_response :success
|
41
|
+
assert_template 'test/index'
|
42
|
+
|
43
|
+
assert_equal 'text/html; charset=utf-8', response.content_type
|
44
|
+
assert_nil response.headers['X-Inertia']
|
45
|
+
|
46
|
+
assert_equal response.body, inertia_html_with_props(content: 'content')
|
47
|
+
end
|
48
|
+
|
49
|
+
def test_renders_index_html_inertia_for_an_inertia_request_with_json_headers
|
50
|
+
@request.headers['X-Inertia'] = 'true'
|
51
|
+
get :index
|
52
|
+
|
53
|
+
assert_response :success
|
54
|
+
assert_template 'test/index'
|
55
|
+
|
56
|
+
assert_equal 'application/json; charset=utf-8', response.content_type
|
57
|
+
assert_equal 'X-Inertia', response.headers['Vary']
|
58
|
+
assert_equal 'true', response.headers['X-Inertia']
|
59
|
+
|
60
|
+
assert_equal response.body, inertia_json_with_props(content: 'content')
|
61
|
+
end
|
62
|
+
|
63
|
+
private
|
64
|
+
|
65
|
+
def inertia_html_with_props(props)
|
66
|
+
<<~HTML.chomp
|
67
|
+
<html><body><div id="app" data-page="#{ERB::Util.html_escape(inertia_json_with_props(props))}"></div>
|
68
|
+
</body></html>
|
69
|
+
HTML
|
70
|
+
end
|
71
|
+
|
72
|
+
def inertia_json_with_props(props)
|
73
|
+
{
|
74
|
+
component: 'test/index',
|
75
|
+
props:,
|
76
|
+
url: '/index',
|
77
|
+
version: nil,
|
78
|
+
encryptHistory: false,
|
79
|
+
clearHistory: false,
|
80
|
+
}.to_json
|
81
|
+
end
|
82
|
+
end
|
@@ -0,0 +1,161 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
require 'action_view'
|
3
|
+
require "active_model"
|
4
|
+
require 'action_view/testing/resolvers'
|
5
|
+
|
6
|
+
class User < Struct.new(:id, :first_name, :last_name, :email)
|
7
|
+
include ActiveModel::Conversion
|
8
|
+
end
|
9
|
+
|
10
|
+
class InertiaBuilderTest < Minitest::Test
|
11
|
+
USER_PARTIAL = <<~INERTIA
|
12
|
+
prop.id user.id
|
13
|
+
prop.first_name user.first_name
|
14
|
+
prop.last_name user.last_name
|
15
|
+
prop.email user.email
|
16
|
+
INERTIA
|
17
|
+
|
18
|
+
PARTIALS = {
|
19
|
+
"users/_user.html.inertia" => USER_PARTIAL,
|
20
|
+
}
|
21
|
+
|
22
|
+
def test_basic_html_rendering
|
23
|
+
result = render_view('prop.hello "World"')
|
24
|
+
assert_equal inertia_html_with_props({ hello: 'World' }), result
|
25
|
+
end
|
26
|
+
|
27
|
+
def test_basic_json_rendering
|
28
|
+
result = render_view('prop.hello "World"', json: true)
|
29
|
+
assert_equal inertia_json_with_props({ hello: 'World' }), result
|
30
|
+
end
|
31
|
+
|
32
|
+
def test_nested_prop
|
33
|
+
template = <<~INERTIA
|
34
|
+
prop.product do
|
35
|
+
prop.id 10
|
36
|
+
prop.name 'Keyboard'
|
37
|
+
prop.price 29.99
|
38
|
+
end
|
39
|
+
INERTIA
|
40
|
+
|
41
|
+
expected_props = {
|
42
|
+
product: {
|
43
|
+
id: 10,
|
44
|
+
name: 'Keyboard',
|
45
|
+
price: 29.99
|
46
|
+
}
|
47
|
+
}
|
48
|
+
|
49
|
+
assert_equal inertia_html_with_props(expected_props), render_view(template)
|
50
|
+
assert_equal inertia_json_with_props(expected_props), render_view(template, json: true)
|
51
|
+
end
|
52
|
+
|
53
|
+
def test_collection_prop
|
54
|
+
products = [
|
55
|
+
{
|
56
|
+
id: 10,
|
57
|
+
name: 'Keyboard',
|
58
|
+
price: 29.99
|
59
|
+
},
|
60
|
+
{
|
61
|
+
id: 11,
|
62
|
+
name: 'Monitor',
|
63
|
+
price: 599.00
|
64
|
+
}
|
65
|
+
]
|
66
|
+
|
67
|
+
template = <<~INERTIA
|
68
|
+
prop.products @products do |product|
|
69
|
+
prop.id product.fetch(:id)
|
70
|
+
prop.name product.fetch(:name)
|
71
|
+
prop.price product.fetch(:price)
|
72
|
+
end
|
73
|
+
INERTIA
|
74
|
+
|
75
|
+
expected_props = { products: }
|
76
|
+
|
77
|
+
assert_equal inertia_html_with_props(expected_props), render_view(template, assigns: { products: })
|
78
|
+
assert_equal inertia_json_with_props(expected_props), render_view(template, assigns: { products: }, json: true)
|
79
|
+
end
|
80
|
+
|
81
|
+
def test_basic_partial_prop
|
82
|
+
user = User.new({ id: 42, first_name: 'John', last_name: 'Doe', email: 'john@email.com' })
|
83
|
+
|
84
|
+
template = <<~INERTIA
|
85
|
+
prop.user do
|
86
|
+
prop.partial! 'users/user', user: @user
|
87
|
+
end
|
88
|
+
INERTIA
|
89
|
+
|
90
|
+
expected_props = { user: }
|
91
|
+
|
92
|
+
assert_equal inertia_html_with_props(expected_props), render_view(template, assigns: { user: })
|
93
|
+
assert_equal inertia_json_with_props(expected_props), render_view(template, assigns: { user: }, json: true)
|
94
|
+
end
|
95
|
+
|
96
|
+
def test_collection_partial_prop
|
97
|
+
users = [
|
98
|
+
User.new({ id: 42, first_name: 'John', last_name: 'Doe', email: 'john@email.com' }),
|
99
|
+
User.new({ id: 43, first_name: 'Jane', last_name: 'Smith', email: 'jane@email.com' }),
|
100
|
+
]
|
101
|
+
|
102
|
+
template = <<~INERTIA
|
103
|
+
prop.users @users do |user|
|
104
|
+
prop.partial! user
|
105
|
+
end
|
106
|
+
INERTIA
|
107
|
+
|
108
|
+
expected_props = { users: }
|
109
|
+
|
110
|
+
assert_equal inertia_html_with_props(expected_props), render_view(template, assigns: { users: })
|
111
|
+
assert_equal inertia_json_with_props(expected_props), render_view(template, assigns: { users: }, json: true)
|
112
|
+
end
|
113
|
+
|
114
|
+
def test_nil_prop_block
|
115
|
+
template = <<~INERTIA
|
116
|
+
prop.current_user do
|
117
|
+
prop.nil!
|
118
|
+
end
|
119
|
+
INERTIA
|
120
|
+
|
121
|
+
|
122
|
+
expected_props = { current_user: nil }
|
123
|
+
|
124
|
+
assert_equal inertia_html_with_props(expected_props), render_view(template)
|
125
|
+
assert_equal inertia_json_with_props(expected_props), render_view(template, json: true)
|
126
|
+
end
|
127
|
+
|
128
|
+
private
|
129
|
+
|
130
|
+
def render_view(source, assigns: {}, json: false)
|
131
|
+
req = ActionDispatch::TestRequest.create
|
132
|
+
req.headers['X-Inertia'] = 'true' if json
|
133
|
+
|
134
|
+
controller = ActionView::TestCase::TestController.new
|
135
|
+
controller.request = req
|
136
|
+
|
137
|
+
resolver = ActionView::FixtureResolver.new(PARTIALS.merge('source.html.inertia' => source))
|
138
|
+
lookup = ActionView::LookupContext.new([resolver], {}, [''])
|
139
|
+
|
140
|
+
view = ActionView::Base.with_empty_template_cache.new(lookup, assigns, controller)
|
141
|
+
|
142
|
+
view.render(template: 'source')
|
143
|
+
end
|
144
|
+
|
145
|
+
def inertia_html_with_props(props)
|
146
|
+
<<~HTML
|
147
|
+
<div id="app" data-page="#{ERB::Util.html_escape(inertia_json_with_props(props))}"></div>
|
148
|
+
HTML
|
149
|
+
end
|
150
|
+
|
151
|
+
def inertia_json_with_props(props)
|
152
|
+
{
|
153
|
+
component: '/',
|
154
|
+
props:,
|
155
|
+
url: '/',
|
156
|
+
version: nil,
|
157
|
+
encryptHistory: false,
|
158
|
+
clearHistory: false,
|
159
|
+
}.to_json
|
160
|
+
end
|
161
|
+
end
|
data/test/test_helper.rb
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'rails'
|
4
|
+
require 'action_controller/railtie'
|
5
|
+
|
6
|
+
require 'jbuilder'
|
7
|
+
require 'inertia_rails'
|
8
|
+
require 'inertia_builder'
|
9
|
+
require 'active_support/testing/autorun'
|
10
|
+
|
11
|
+
ActiveSupport.test_order = :random
|
12
|
+
|
13
|
+
# Instantiate an Application in order to trigger the initializers
|
14
|
+
Class.new(Rails::Application) do
|
15
|
+
config.secret_key_base = 'secret'
|
16
|
+
config.eager_load = false
|
17
|
+
end.initialize!
|
18
|
+
|
19
|
+
# Touch AV::Base in order to trigger :action_view on_load hook before running the tests
|
20
|
+
ActionView::Base.inspect
|
metadata
ADDED
@@ -0,0 +1,124 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: inertia_builder
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Rodrigo Lima
|
8
|
+
bindir: bin
|
9
|
+
cert_chain: []
|
10
|
+
date: 1980-01-02 00:00:00.000000000 Z
|
11
|
+
dependencies:
|
12
|
+
- !ruby/object:Gem::Dependency
|
13
|
+
name: inertia_rails
|
14
|
+
requirement: !ruby/object:Gem::Requirement
|
15
|
+
requirements:
|
16
|
+
- - "~>"
|
17
|
+
- !ruby/object:Gem::Version
|
18
|
+
version: '3.5'
|
19
|
+
type: :runtime
|
20
|
+
prerelease: false
|
21
|
+
version_requirements: !ruby/object:Gem::Requirement
|
22
|
+
requirements:
|
23
|
+
- - "~>"
|
24
|
+
- !ruby/object:Gem::Version
|
25
|
+
version: '3.5'
|
26
|
+
- !ruby/object:Gem::Dependency
|
27
|
+
name: jbuilder
|
28
|
+
requirement: !ruby/object:Gem::Requirement
|
29
|
+
requirements:
|
30
|
+
- - "~>"
|
31
|
+
- !ruby/object:Gem::Version
|
32
|
+
version: '2.0'
|
33
|
+
type: :runtime
|
34
|
+
prerelease: false
|
35
|
+
version_requirements: !ruby/object:Gem::Requirement
|
36
|
+
requirements:
|
37
|
+
- - "~>"
|
38
|
+
- !ruby/object:Gem::Version
|
39
|
+
version: '2.0'
|
40
|
+
- !ruby/object:Gem::Dependency
|
41
|
+
name: rails
|
42
|
+
requirement: !ruby/object:Gem::Requirement
|
43
|
+
requirements:
|
44
|
+
- - "~>"
|
45
|
+
- !ruby/object:Gem::Version
|
46
|
+
version: '7.0'
|
47
|
+
type: :development
|
48
|
+
prerelease: false
|
49
|
+
version_requirements: !ruby/object:Gem::Requirement
|
50
|
+
requirements:
|
51
|
+
- - "~>"
|
52
|
+
- !ruby/object:Gem::Version
|
53
|
+
version: '7.0'
|
54
|
+
- !ruby/object:Gem::Dependency
|
55
|
+
name: rails-controller-testing
|
56
|
+
requirement: !ruby/object:Gem::Requirement
|
57
|
+
requirements:
|
58
|
+
- - "~>"
|
59
|
+
- !ruby/object:Gem::Version
|
60
|
+
version: '1.0'
|
61
|
+
type: :development
|
62
|
+
prerelease: false
|
63
|
+
version_requirements: !ruby/object:Gem::Requirement
|
64
|
+
requirements:
|
65
|
+
- - "~>"
|
66
|
+
- !ruby/object:Gem::Version
|
67
|
+
version: '1.0'
|
68
|
+
- !ruby/object:Gem::Dependency
|
69
|
+
name: rake
|
70
|
+
requirement: !ruby/object:Gem::Requirement
|
71
|
+
requirements:
|
72
|
+
- - "~>"
|
73
|
+
- !ruby/object:Gem::Version
|
74
|
+
version: '13.3'
|
75
|
+
type: :development
|
76
|
+
prerelease: false
|
77
|
+
version_requirements: !ruby/object:Gem::Requirement
|
78
|
+
requirements:
|
79
|
+
- - "~>"
|
80
|
+
- !ruby/object:Gem::Version
|
81
|
+
version: '13.3'
|
82
|
+
email: rodrigotavio91@gmail.com
|
83
|
+
executables: []
|
84
|
+
extensions: []
|
85
|
+
extra_rdoc_files: []
|
86
|
+
files:
|
87
|
+
- lib/inertia_builder.rb
|
88
|
+
- lib/inertia_builder/controller.rb
|
89
|
+
- lib/inertia_builder/handler.rb
|
90
|
+
- lib/inertia_builder/railtie.rb
|
91
|
+
- lib/inertia_builder/renderer.rb
|
92
|
+
- lib/inertia_builder/version.rb
|
93
|
+
- test/controller_test.rb
|
94
|
+
- test/inertia_builder_test.rb
|
95
|
+
- test/test_helper.rb
|
96
|
+
homepage: https://github.com/rodrigotavio91/inertia-builder
|
97
|
+
licenses:
|
98
|
+
- MIT
|
99
|
+
metadata:
|
100
|
+
bug_tracker_uri: https://github.com/rodrigotavio91/inertia-builder/issues
|
101
|
+
changelog_uri: https://github.com/rodrigotavio91/inertia-builder/releases/tag/v0.1.0
|
102
|
+
source_code_uri: https://github.com/rodrigotavio91/inertia-builder/tree/v0.1.0
|
103
|
+
rubygems_mfa_required: 'true'
|
104
|
+
rdoc_options: []
|
105
|
+
require_paths:
|
106
|
+
- lib
|
107
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
108
|
+
requirements:
|
109
|
+
- - ">="
|
110
|
+
- !ruby/object:Gem::Version
|
111
|
+
version: 3.0.0
|
112
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
113
|
+
requirements:
|
114
|
+
- - ">="
|
115
|
+
- !ruby/object:Gem::Version
|
116
|
+
version: '0'
|
117
|
+
requirements: []
|
118
|
+
rubygems_version: 3.6.9
|
119
|
+
specification_version: 4
|
120
|
+
summary: Create Inertia.js props via a Jbuilder-style DSL
|
121
|
+
test_files:
|
122
|
+
- test/controller_test.rb
|
123
|
+
- test/inertia_builder_test.rb
|
124
|
+
- test/test_helper.rb
|