inertia_builder 0.1.0 → 0.2.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 671869c4438679ee4343c013de503e09f283cbdc46c4335426b14a1902e006b3
4
- data.tar.gz: bdb6b02a5cf77582a76d9b924775afa58a33aff02bdc677aab23b0c21ba124da
3
+ metadata.gz: d326ccb567aaf8b60dd19a3ec57358008e620e575e725e0827cf9378e64bf8e7
4
+ data.tar.gz: 4876abe3f8910c981e628417876b88598b643564dd471f6b8bdb66bd9064e799
5
5
  SHA512:
6
- metadata.gz: 6729a9608eeaf81447b9d38350d25080b4856de2150f9e3a6b1a556af4dd73012c5ddf4a34d3e29e0d6ae220a93f156c0b616b4abcdebd2246858df5718645c6
7
- data.tar.gz: 55b3c90f175f655aefb21912ab84e34e99e481b61580af7baa87f2da23d40ff24ac945eb0bdee4a3e0d72fe276262f12b1c6ba7193db6c75c632643729a83fd0
6
+ metadata.gz: 64a1c8f5f14eefd54908e53b6e88f7b199e4e30a9b9ce7d8fa25932508ccc1727481d89f8b40f4f638eace0a1609bf270e98ead3931fe46a86b4969860bca38f
7
+ data.tar.gz: 3f4026b1fbc067905de4a5eae7f8289bfe3987d6464be4622d254b9efabf8acac319c92dc6cd6c1cf8a147ff94b9a625c5fe60c4a6b40e1446c0470cd2a2159b
@@ -5,19 +5,12 @@ module InertiaBuilder
5
5
  extend ActiveSupport::Concern
6
6
 
7
7
  included do
8
- layout -> { inertia_json_request? ? false : 'application' }
9
-
10
- before_action :force_json_response_with_html_template, if: -> { inertia_json_request? }
8
+ before_action :format_inertia_json_response, if: -> { inertia_json_request? }
11
9
  end
12
10
 
13
11
  private
14
12
 
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]
13
+ def format_inertia_json_response
21
14
  response.headers['Vary'] = if response.headers['Vary'].blank?
22
15
  'X-Inertia'
23
16
  else
@@ -25,5 +18,13 @@ module InertiaBuilder
25
18
  end
26
19
  response.set_header('X-Inertia', 'true')
27
20
  end
21
+
22
+ def action_has_layout?
23
+ !inertia_json_request? && super
24
+ end
25
+
26
+ def inertia_json_request?
27
+ request.headers['X-Inertia'] == 'true'
28
+ end
28
29
  end
29
30
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module InertiaBuilder
4
- VERSION = '0.1.0'
4
+ VERSION = '0.2.0'
5
5
  end
@@ -4,22 +4,81 @@ require 'jbuilder/jbuilder_template'
4
4
  require 'inertia_builder/handler'
5
5
  require 'inertia_builder/controller'
6
6
  require 'inertia_builder/renderer'
7
- require 'inertia_builder/railtie' if defined?(Rails)
7
+ require 'inertia_builder/railtie'
8
+
9
+ class JbuilderTemplate
10
+ self.template_lookup_options = { handlers: %i[inertia jbuilder] }
11
+ end
8
12
 
9
13
  module InertiaBuilder
10
14
  class PropBuilder < JbuilderTemplate
11
15
  alias props attributes!
12
16
 
17
+ def optional!(&block)
18
+ _call_inertia_block(:optional, &block)
19
+ end
20
+
21
+ def always!(&block)
22
+ _call_inertia_block(:always, &block)
23
+ end
24
+
25
+ def defer!(**opts, &block)
26
+ _call_inertia_block(:defer, **opts, &block)
27
+ end
28
+
29
+ def method_missing(name, *args, &block)
30
+ prop = self
31
+
32
+ if @inertia_block
33
+ method, opts = @inertia_block
34
+ _set_value(name, ::InertiaRails.send(method, **opts) { prop.set!(name, *args, &block) })
35
+ elsif !@in_scope
36
+ # Lazy evaluate outermost properties.
37
+ _set_value(name, -> { prop.set!(name, *args, &block); })
38
+ else
39
+ super
40
+ end
41
+ end
42
+
13
43
  private
14
44
 
45
+ def _call_inertia_block(method, **opts)
46
+ ::Kernel.raise "Nesting #{method}! in a #{@inertia_block[0]}! block is not allowed" if @inertia_block
47
+
48
+ @inertia_block = [method, opts]
49
+ yield
50
+ @inertia_block = nil
51
+ end
52
+
53
+ def _render_partial_with_options(options)
54
+ options[:locals] ||= options.except(:partial, :as, :collection, :cached)
55
+ options[:locals][:prop] = self
56
+ super(options)
57
+ end
58
+
15
59
  def _render_partial(options)
16
- options[:handlers] = [:inertia]
17
60
  options[:locals][:prop] = self
18
61
  @context.render options
19
62
  end
20
63
 
21
64
  def _render_active_model_partial(object)
22
- @context.render object, prop: self, handlers: [:inertia]
65
+ @context.render object, prop: self
66
+ end
67
+
68
+ def _scope
69
+ @in_scope = true
70
+ super
71
+ ensure
72
+ @in_scope = false
73
+ end
74
+
75
+ def _merge_values(current_value, updates)
76
+ # Always override lazy evaluation procs.
77
+ if current_value.is_a?(::Proc)
78
+ updates
79
+ else
80
+ super
81
+ end
23
82
  end
24
83
  end
25
84
  end
@@ -2,28 +2,39 @@
2
2
 
3
3
  require 'test_helper'
4
4
  require 'action_view/testing/resolvers'
5
- require 'rails/controller/testing'
6
5
 
7
6
  class TestController < ActionController::Base
8
7
  include InertiaBuilder::Controller
9
8
 
9
+ layout 'application'
10
+
10
11
  def index; end
12
+
13
+ def create
14
+ respond_to do |format|
15
+ format.html { render :create, status: :created }
16
+ format.json { render :create, status: :created }
17
+ end
18
+ end
11
19
  end
12
20
 
13
21
  class ControllerTest < ActionController::TestCase
14
- include Rails::Controller::Testing::TemplateAssertions
15
-
16
22
  tests TestController
17
23
 
18
24
  def setup
19
25
  super
20
26
 
21
27
  @routes = ActionDispatch::Routing::RouteSet.new
22
- @routes.draw { get 'index' => 'test#index' }
28
+ @routes.draw do
29
+ get 'index' => 'test#index'
30
+ post 'create' => 'test#create'
31
+ end
23
32
 
24
33
  resolver = ActionView::FixtureResolver.new(
25
34
  'layouts/application.html.erb' => '<html><body><%= yield %></body></html>',
26
- 'test/index.html.inertia' => 'prop.content "content"'
35
+ 'test/index.html.inertia' => 'prop.content "content"',
36
+ 'test/create.html.inertia' => 'prop.content "This is Inertia content"',
37
+ 'test/create.json.jbuilder' => 'json.content "This is JSON content"'
27
38
  )
28
39
 
29
40
  @controller.prepend_view_path(resolver)
@@ -35,10 +46,10 @@ class ControllerTest < ActionController::TestCase
35
46
  end
36
47
 
37
48
  def test_renders_index_html_inertia_for_a_standard_non_inertia_request
49
+ @request.headers['X-Inertia'] = nil
38
50
  get :index
39
51
 
40
52
  assert_response :success
41
- assert_template 'test/index'
42
53
 
43
54
  assert_equal 'text/html; charset=utf-8', response.content_type
44
55
  assert_nil response.headers['X-Inertia']
@@ -51,15 +62,31 @@ class ControllerTest < ActionController::TestCase
51
62
  get :index
52
63
 
53
64
  assert_response :success
54
- assert_template 'test/index'
55
65
 
56
- assert_equal 'application/json; charset=utf-8', response.content_type
66
+ # assert_equal 'application/json; charset=utf-8', response.content_type
57
67
  assert_equal 'X-Inertia', response.headers['Vary']
58
68
  assert_equal 'true', response.headers['X-Inertia']
59
69
 
60
70
  assert_equal response.body, inertia_json_with_props(content: 'content')
61
71
  end
62
72
 
73
+ def test_respects_respond_to_block
74
+ @request.headers['X-Inertia'] = 'true'
75
+ post :create
76
+
77
+ assert_response :created
78
+ assert_equal response.body, inertia_json_with_props(
79
+ { content: 'This is Inertia content' },
80
+ component: 'test/create',
81
+ url: '/create'
82
+ )
83
+
84
+ post :create, format: :json
85
+
86
+ assert_response :created
87
+ assert_equal response.body, { content: 'This is JSON content' }.to_json
88
+ end
89
+
63
90
  private
64
91
 
65
92
  def inertia_html_with_props(props)
@@ -69,14 +96,14 @@ class ControllerTest < ActionController::TestCase
69
96
  HTML
70
97
  end
71
98
 
72
- def inertia_json_with_props(props)
99
+ def inertia_json_with_props(props, opts = {})
73
100
  {
74
- component: 'test/index',
75
- props:,
76
- url: '/index',
101
+ component: opts.fetch(:component, 'test/index'),
102
+ props: { errors: {} }.merge(props),
103
+ url: opts.fetch(:url, '/index'),
77
104
  version: nil,
78
105
  encryptHistory: false,
79
- clearHistory: false,
106
+ clearHistory: false
80
107
  }.to_json
81
108
  end
82
109
  end
@@ -1,9 +1,10 @@
1
1
  require 'test_helper'
2
2
  require 'action_view'
3
- require "active_model"
3
+ require 'active_model'
4
4
  require 'action_view/testing/resolvers'
5
5
 
6
6
  class User < Struct.new(:id, :first_name, :last_name, :email)
7
+ extend ActiveModel::Naming
7
8
  include ActiveModel::Conversion
8
9
  end
9
10
 
@@ -16,7 +17,7 @@ class InertiaBuilderTest < Minitest::Test
16
17
  INERTIA
17
18
 
18
19
  PARTIALS = {
19
- "users/_user.html.inertia" => USER_PARTIAL,
20
+ 'users/_user.html.inertia' => USER_PARTIAL
20
21
  }
21
22
 
22
23
  def test_basic_html_rendering
@@ -46,8 +47,8 @@ class InertiaBuilderTest < Minitest::Test
46
47
  }
47
48
  }
48
49
 
49
- assert_equal inertia_html_with_props(expected_props), render_view(template)
50
50
  assert_equal inertia_json_with_props(expected_props), render_view(template, json: true)
51
+ assert_equal inertia_html_with_props(expected_props), render_view(template)
51
52
  end
52
53
 
53
54
  def test_collection_prop
@@ -72,10 +73,11 @@ class InertiaBuilderTest < Minitest::Test
72
73
  end
73
74
  INERTIA
74
75
 
75
- expected_props = { products: }
76
+ expected_props = { products: products }
76
77
 
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)
78
+ assert_equal inertia_json_with_props(expected_props),
79
+ render_view(template, assigns: { products: products }, json: true)
80
+ assert_equal inertia_html_with_props(expected_props), render_view(template, assigns: { products: products })
79
81
  end
80
82
 
81
83
  def test_basic_partial_prop
@@ -87,16 +89,16 @@ class InertiaBuilderTest < Minitest::Test
87
89
  end
88
90
  INERTIA
89
91
 
90
- expected_props = { user: }
92
+ expected_props = { user: user }
91
93
 
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
+ assert_equal inertia_json_with_props(expected_props), render_view(template, assigns: { user: user }, json: true)
95
+ assert_equal inertia_html_with_props(expected_props), render_view(template, assigns: { user: user })
94
96
  end
95
97
 
96
98
  def test_collection_partial_prop
97
99
  users = [
98
100
  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' }),
101
+ User.new({ id: 43, first_name: 'Jane', last_name: 'Smith', email: 'jane@email.com' })
100
102
  ]
101
103
 
102
104
  template = <<~INERTIA
@@ -105,10 +107,24 @@ class InertiaBuilderTest < Minitest::Test
105
107
  end
106
108
  INERTIA
107
109
 
108
- expected_props = { users: }
110
+ expected_props = { users: users }
111
+
112
+ assert_equal inertia_html_with_props(expected_props), render_view(template, assigns: { users: users })
113
+ assert_equal inertia_json_with_props(expected_props), render_view(template, assigns: { users: users }, json: true)
114
+ end
115
+
116
+ def test_shorthand_collection_partial_prop
117
+ users = [
118
+ User.new({ id: 42, first_name: 'John', last_name: 'Doe', email: 'john@email.com' }),
119
+ User.new({ id: 43, first_name: 'Jane', last_name: 'Smith', email: 'jane@email.com' })
120
+ ]
121
+
122
+ template = "prop.users @users, partial: 'users/user', as: :user"
109
123
 
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)
124
+ expected_props = { users: users }
125
+
126
+ assert_equal inertia_html_with_props(expected_props), render_view(template, assigns: { users: users })
127
+ assert_equal inertia_json_with_props(expected_props), render_view(template, assigns: { users: users }, json: true)
112
128
  end
113
129
 
114
130
  def test_nil_prop_block
@@ -118,18 +134,102 @@ class InertiaBuilderTest < Minitest::Test
118
134
  end
119
135
  INERTIA
120
136
 
121
-
122
137
  expected_props = { current_user: nil }
123
138
 
124
139
  assert_equal inertia_html_with_props(expected_props), render_view(template)
125
140
  assert_equal inertia_json_with_props(expected_props), render_view(template, json: true)
126
141
  end
127
142
 
143
+ def test_optional_block
144
+ template = <<~INERTIA
145
+ prop.id 1
146
+ prop.optional! do
147
+ prop.user 'User'
148
+ prop.calculation 'Calculation'
149
+ end
150
+ INERTIA
151
+
152
+ partial_headers = {
153
+ 'X-Inertia-Partial-Data' => 'user,calculation',
154
+ 'X-Inertia-Partial-Component' => '/'
155
+ }
156
+ assert_equal inertia_json_with_props({ id: 1 }), render_view(template, json: true)
157
+ assert_equal inertia_json_with_props({ user: 'User', calculation: 'Calculation' }),
158
+ render_view(template, json: true, headers: partial_headers)
159
+ end
160
+
161
+ def test_always_block
162
+ template = <<~INERTIA
163
+ prop.id 1
164
+ prop.always! do
165
+ prop.user 'User'
166
+ end
167
+ prop.optional! do
168
+ prop.calculation 'Calculation'
169
+ end
170
+ INERTIA
171
+
172
+ partial_headers = {
173
+ 'X-Inertia-Partial-Data' => 'calculation',
174
+ 'X-Inertia-Partial-Component' => '/'
175
+ }
176
+ assert_equal inertia_json_with_props({ id: 1, user: 'User' }), render_view(template, json: true)
177
+ assert_equal inertia_json_with_props({ user: 'User', calculation: 'Calculation' }),
178
+ render_view(template, json: true, headers: partial_headers)
179
+ end
180
+
181
+ def test_defer_block
182
+ template = <<~INERTIA
183
+ prop.id 1
184
+ prop.defer! do
185
+ prop.user 'User'
186
+ prop.calculation 'Calculation'
187
+ end
188
+ INERTIA
189
+
190
+ assert_equal inertia_json_with_props({ id: 1 }, deferredProps: { default: %w[user calculation] }),
191
+ render_view(template, json: true)
192
+ end
193
+
194
+ def test_defer_block_grouping
195
+ template = <<~INERTIA
196
+ prop.id 1
197
+ prop.defer! group: 'group1' do
198
+ prop.user 'User'
199
+ end
200
+ prop.defer! group: 'group2' do
201
+ prop.calculation 'Calculation'
202
+ end
203
+ INERTIA
204
+
205
+ assert_equal inertia_json_with_props({ id: 1 }, deferredProps: { group1: ['user'], group2: ['calculation'] }),
206
+ render_view(template, json: true)
207
+ end
208
+
209
+ def test_defer_block_fetching
210
+ template = <<~INERTIA
211
+ prop.id 1
212
+ prop.defer! do
213
+ prop.user 'User'
214
+ prop.calculation 'Calculation'
215
+ end
216
+ INERTIA
217
+
218
+ partial_headers = {
219
+ 'X-Inertia-Partial-Data' => 'user,calculation',
220
+ 'X-Inertia-Partial-Component' => '/'
221
+ }
222
+
223
+ assert_equal inertia_json_with_props({ user: 'User', calculation: 'Calculation' }),
224
+ render_view(template, json: true, headers: partial_headers)
225
+ end
226
+
128
227
  private
129
228
 
130
- def render_view(source, assigns: {}, json: false)
229
+ def render_view(source, **opts)
131
230
  req = ActionDispatch::TestRequest.create
132
- req.headers['X-Inertia'] = 'true' if json
231
+ req.headers.merge!(opts[:headers] || {})
232
+ req.headers['X-Inertia'] = 'true' if opts[:json]
133
233
 
134
234
  controller = ActionView::TestCase::TestController.new
135
235
  controller.request = req
@@ -137,7 +237,7 @@ class InertiaBuilderTest < Minitest::Test
137
237
  resolver = ActionView::FixtureResolver.new(PARTIALS.merge('source.html.inertia' => source))
138
238
  lookup = ActionView::LookupContext.new([resolver], {}, [''])
139
239
 
140
- view = ActionView::Base.with_empty_template_cache.new(lookup, assigns, controller)
240
+ view = ActionView::Base.with_empty_template_cache.new(lookup, opts[:assigns] || [], controller)
141
241
 
142
242
  view.render(template: 'source')
143
243
  end
@@ -148,14 +248,14 @@ class InertiaBuilderTest < Minitest::Test
148
248
  HTML
149
249
  end
150
250
 
151
- def inertia_json_with_props(props)
251
+ def inertia_json_with_props(props, **extra_fields)
152
252
  {
153
253
  component: '/',
154
- props:,
254
+ props: { errors: {} }.merge(props),
155
255
  url: '/',
156
256
  version: nil,
157
257
  encryptHistory: false,
158
- clearHistory: false,
159
- }.to_json
258
+ clearHistory: false
259
+ }.merge(extra_fields).to_json
160
260
  end
161
261
  end
data/test/test_helper.rb CHANGED
@@ -16,5 +16,9 @@ Class.new(Rails::Application) do
16
16
  config.eager_load = false
17
17
  end.initialize!
18
18
 
19
+ InertiaRails.configure do |c|
20
+ c.always_include_errors_hash = true # Fix deprecated warning in tests.
21
+ end
22
+
19
23
  # Touch AV::Base in order to trigger :action_view on_load hook before running the tests
20
24
  ActionView::Base.inspect
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: inertia_builder
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Rodrigo Lima
@@ -38,47 +38,19 @@ dependencies:
38
38
  - !ruby/object:Gem::Version
39
39
  version: '2.0'
40
40
  - !ruby/object:Gem::Dependency
41
- name: rails
41
+ name: appraisal
42
42
  requirement: !ruby/object:Gem::Requirement
43
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
- - - "~>"
44
+ - - ">="
59
45
  - !ruby/object:Gem::Version
60
- version: '1.0'
46
+ version: '0'
61
47
  type: :development
62
48
  prerelease: false
63
49
  version_requirements: !ruby/object:Gem::Requirement
64
50
  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
- - - "~>"
51
+ - - ">="
80
52
  - !ruby/object:Gem::Version
81
- version: '13.3'
53
+ version: '0'
82
54
  email: rodrigotavio91@gmail.com
83
55
  executables: []
84
56
  extensions: []
@@ -98,8 +70,8 @@ licenses:
98
70
  - MIT
99
71
  metadata:
100
72
  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
73
+ changelog_uri: https://github.com/rodrigotavio91/inertia-builder/releases/tag/v0.2.0
74
+ source_code_uri: https://github.com/rodrigotavio91/inertia-builder/tree/v0.2.0
103
75
  rubygems_mfa_required: 'true'
104
76
  rdoc_options: []
105
77
  require_paths: