inertia_rails 2.0.1 → 3.1.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 3e99464a3b4534b1ccb484be532f15a11cf6883a13ea3e25643e6546a6934cc9
4
- data.tar.gz: da8e75d1927615db2b2421cffeec4b34ce4a01649756294be11520a910baeb54
3
+ metadata.gz: 27e7436cecef40102f1e1ca1971c75b182195e3291f55f94981ef8c87359d9d4
4
+ data.tar.gz: 50c9634e36e040732e9bf42e7770a3d759e4930513b4fb54d2761e905689bc24
5
5
  SHA512:
6
- metadata.gz: da00b4ce0a9652fdc6b68988fcae4d6ad70caafd39574e6dd369e4e71f24c9a283323910aaeaef55fe17c50521d933bac3198e999806fd6629719795228ce5a6
7
- data.tar.gz: '08b1a351fb8314ae370323dd9f374f59c507a4f8d9e9504e1bab8904d633bcfa4b9dc4f92c4ee2facd1d469deded86923067f315ca7b4cce184162d783151e21'
6
+ metadata.gz: 2653faf355cc672ebe5faec59fffdfe928cddd8b045ba114d5e8fafe01e3c5eb74ed47ac63336e62e1080c5e616a1ce3ac68fa8475644a1caf9f70e0206ce327
7
+ data.tar.gz: f7ae1c4be29f75140a42a08cd31c8ca946b1b5bb39a681c8bd2b0c8db71c716bcf3368bc0f37b573ffe84906686d57b191ead1e1143de3d19bf4441bab06c09f
data/CHANGELOG.md CHANGED
@@ -4,6 +4,24 @@ 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.1.0] - 2023-08-21
8
+
9
+ ### Features
10
+
11
+ * CSRF protection works without additional configuration now.
12
+ * Optional deep merging of shared props.
13
+
14
+ ### Fixes
15
+
16
+ * Document Inertia headers. @buhrmi
17
+ * Documentation typo fix. @lujanfernaud
18
+ * Changelog URI fix. @PedroAugustoRamalhoDuarte
19
+
20
+ ## [3.0.0] - 2022-09-22
21
+
22
+ * Allow rails layout to set inertia layout. Thanks @ElMassimo!
23
+ * Add the ability to set inertia props and components via rails conventions (see readme)
24
+
7
25
  ## [2.0.1] - 2022-07-12
8
26
 
9
27
  * Fix for a middleware issue where global state could be polluted if an exception occurs in a request. Thanks @ElMassimo!
data/README.md CHANGED
@@ -14,7 +14,7 @@ gem 'inertia_rails'
14
14
 
15
15
  ### Frontend
16
16
 
17
- Rails 7 specific frontend docs coming soon. For now, check out the official Inertia docs at https://inertiajs.com/
17
+ Rails 7 specific frontend docs coming soon. For now, check out the official Inertia docs at https://inertiajs.com/ or see an example using React/Vite [here](https://github.com/BrandonShar/inertia-rails-template)
18
18
 
19
19
  ## Usage
20
20
 
@@ -30,9 +30,56 @@ def index
30
30
  end
31
31
  ```
32
32
 
33
+ #### Rails Component and Instance Props
34
+
35
+ Starting in version 3.0, Inertia Rails allows you to provide your component name and props via common rails conventions.
36
+
37
+ ```ruby
38
+ class EventsController < ApplicationController
39
+ use_inertia_instance_props
40
+
41
+ def index
42
+ @events = Event.all
43
+ end
44
+
45
+ end
46
+ ```
47
+
48
+ is the same as
49
+
50
+
51
+ ```ruby
52
+ class EventsController < ApplicationController
53
+ def index
54
+ render inertia: 'events/index', props: {
55
+ events: Event.all
56
+ }
57
+ end
58
+ end
59
+ ```
60
+
61
+ #### Instance Props and Default Render Notes
62
+
63
+ 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
+ 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
+
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.
67
+
68
+ ### Layout
69
+
70
+ Inertia layouts use the rails layout convention and can be set or changed in the same way. The original `layout` config option is still functional, but will likely be deprecated in the future in favor
71
+ of using rails layouts.
72
+
73
+ ```ruby
74
+ class EventsController < ApplicationController
75
+ layout 'inertia_application'
76
+ end
77
+ ```
78
+
79
+
33
80
  ### Shared Data
34
81
 
35
- If you have data that you want to be provided as a prop to every component (a common use-case is informationa about the authenticated user) you can use the `shared_data` controller method.
82
+ If you have data that you want to be provided as a prop to every component (a common use-case is information about the authenticated user) you can use the `shared_data` controller method.
36
83
 
37
84
  ```ruby
38
85
  class EventsController < ApplicationController
@@ -54,6 +101,84 @@ class EventsController < ApplicationController
54
101
  end
55
102
  ```
56
103
 
104
+ #### Deep Merging Shared Data
105
+
106
+ By default, Inertia will shallow merge data defined in an action with the shared data. You might want a deep merge. Imagine using shared data to represent defaults you'll override sometimes.
107
+
108
+ ```ruby
109
+ class ApplicationController
110
+ inertia_share do
111
+ { basketball_data: { points: 50, rebounds: 100 } }
112
+ end
113
+ end
114
+ ```
115
+
116
+ Let's say we want a particular action to change only part of that data structure. The renderer accepts a `deep_merge` option:
117
+
118
+ ```ruby
119
+ class CrazyScorersController < ApplicationController
120
+ def index
121
+ render inertia: 'CrazyScorersComponent',
122
+ props: { basketball_data: { points: 100 } },
123
+ deep_merge: true
124
+ end
125
+ end
126
+
127
+ # The renderer will send this to the frontend:
128
+ {
129
+ basketball_data: {
130
+ points: 100,
131
+ rebounds: 100,
132
+ }
133
+ }
134
+ ```
135
+
136
+ Deep merging can be set as the project wide default via the InertiaRails configuration:
137
+
138
+ ```ruby
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:
147
+
148
+ ```ruby
149
+ class CrazyScorersController < ApplicationController
150
+ inertia_share do
151
+ {
152
+ basketball_data: {
153
+ points: 50,
154
+ rebounds: 10,
155
+ }
156
+ }
157
+ end
158
+
159
+ def index
160
+ render inertia: 'CrazyScorersComponent',
161
+ props: { basketball_data: { points: 100 } },
162
+ deep_merge: false
163
+ end
164
+ end
165
+
166
+ # Even if deep merging is set by default, since the renderer has `deep_merge: false`, it will send a shallow merge to the frontend:
167
+ {
168
+ basketball_data: {
169
+ points: 100,
170
+ }
171
+ }
172
+ ```
173
+
174
+ ### Lazy Props
175
+
176
+ On the front end, Inertia supports the concept of "partial reloads" where only the props requested are returned by the server. Sometimes, you may want to use this flow to avoid processing a particularly slow prop on the intial load. In this case, you can use Lazy props. Lazy props aren't evaluated unless they're specifically requested by name in a partial reload.
177
+
178
+ ```ruby
179
+ inertia_share some_data: InertiaRails.lazy(lambda { some_very_slow_method })
180
+ ```
181
+
57
182
  ### Routing
58
183
 
59
184
  If you don't need a controller to handle a static component, you can route directly to a component with the inertia route helper
@@ -62,6 +187,12 @@ If you don't need a controller to handle a static component, you can route direc
62
187
  inertia 'about' => 'AboutComponent'
63
188
  ```
64
189
 
190
+ ### SSR
191
+
192
+ Enable SSR via the config settings for `ssr_enabled` and `ssr_url`.
193
+
194
+ When using SSR, don't forget to add `<%= inertia_headers %>` to the `<head>` of your `application.html.erb`.
195
+
65
196
  ## Configuration
66
197
 
67
198
  Inertia Rails has a few different configuration options that can be set anywhere, but the most common location is from within an initializer.
@@ -72,13 +203,14 @@ InertiaRails.configure do |config|
72
203
 
73
204
  # set the current version for automatic asset refreshing. A string value should be used if any.
74
205
  config.version = nil
206
+ # enable default inertia rendering (warning! this will override rails default rendering behavior)
207
+ config.default_render = true
75
208
 
76
- # set the layout you want inertia components to be rendered within. This layout must include any required inertia javascript.
77
- config.layout = 'application'
78
-
79
209
  # ssr specific options
80
210
  config.ssr_enabled = false
81
211
  config.ssr_url = 'http://localhost:13714'
212
+
213
+ config.deep_merge_shared_data = false
82
214
 
83
215
  end
84
216
  ```
@@ -14,7 +14,7 @@ Gem::Specification.new do |spec|
14
14
 
15
15
  spec.metadata["homepage_uri"] = spec.homepage
16
16
  spec.metadata["source_code_uri"] = spec.homepage
17
- spec.metadata["changelog_uri"] = "#{spec.homepage}/CHANGELOG.md"
17
+ spec.metadata["changelog_uri"] = "#{spec.homepage}/blob/master/CHANGELOG.md"
18
18
 
19
19
  # Specify which files should be added to the gem when it is released.
20
20
  # The `git ls-files -z` loads the files in the RubyGem that have been added into git.
@@ -34,4 +34,5 @@ Gem::Specification.new do |spec|
34
34
  spec.add_development_dependency "sqlite3"
35
35
  spec.add_development_dependency "appraisal"
36
36
  spec.add_development_dependency "responders"
37
+ spec.add_development_dependency "debug"
37
38
  end
@@ -1,16 +1,12 @@
1
1
  import { App } from '@inertiajs/inertia-react';
2
2
  import React from 'react';
3
3
  import { render } from 'react-dom';
4
- import axios from 'axios';
5
4
  import { InertiaProgress } from '@inertiajs/progress';
6
5
 
7
6
  document.addEventListener('DOMContentLoaded', () => {
8
7
  InertiaProgress.init();
9
8
  const el = document.getElementById('app')
10
9
 
11
- const csrfToken = document.querySelector('meta[name=csrf-token]').content;
12
- axios.defaults.headers.common['X-CSRF-Token'] = csrfToken;
13
-
14
10
  render(
15
11
  <App
16
12
  initialPage={JSON.parse(el.dataset.page)}
@@ -18,4 +14,4 @@ document.addEventListener('DOMContentLoaded', () => {
18
14
  />,
19
15
  el
20
16
  )
21
- });
17
+ });
@@ -1,12 +1,7 @@
1
- import axios from 'axios'
2
-
3
1
  import { createInertiaApp } from '@inertiajs/inertia-svelte'
4
2
  import { InertiaProgress } from '@inertiajs/progress'
5
3
 
6
4
  document.addEventListener('DOMContentLoaded', () => {
7
- const csrfToken = document.querySelector('meta[name=csrf-token]').content
8
- axios.defaults.headers.common['X-CSRF-Token'] = csrfToken
9
-
10
5
  InertiaProgress.init()
11
6
 
12
7
  createInertiaApp({
@@ -16,4 +11,4 @@ document.addEventListener('DOMContentLoaded', () => {
16
11
  new App({ target: el, props })
17
12
  },
18
13
  })
19
- })
14
+ })
@@ -1,13 +1,9 @@
1
- import axios from 'axios'
2
1
  import Vue from 'vue'
3
2
 
4
3
  import { app, plugin } from '@inertiajs/inertia-vue'
5
4
  import { InertiaProgress } from '@inertiajs/progress'
6
5
 
7
6
  document.addEventListener('DOMContentLoaded', () => {
8
- const csrfToken = document.querySelector('meta[name=csrf-token]').content
9
- axios.defaults.headers.common['X-CSRF-Token'] = csrfToken
10
-
11
7
  InertiaProgress.init();
12
8
  const el = document.getElementById('app')
13
9
 
@@ -11,6 +11,10 @@ module InertiaRails
11
11
  InertiaRails.share(errors: session[:inertia_errors]) if session[:inertia_errors].present?
12
12
  end
13
13
  helper ::InertiaRails::Helper
14
+
15
+ after_action do
16
+ cookies['XSRF-TOKEN'] = form_authenticity_token unless request.inertia? || !protect_against_forgery?
17
+ end
14
18
  end
15
19
 
16
20
  module ClassMethods
@@ -20,6 +24,21 @@ module InertiaRails
20
24
  InertiaRails.share_block(block) if block
21
25
  end
22
26
  end
27
+
28
+ def use_inertia_instance_props
29
+ before_action do
30
+ @_inertia_instance_props = true
31
+ @_inertia_skip_props = view_assigns.keys + ['_inertia_skip_props']
32
+ end
33
+ end
34
+ end
35
+
36
+ def default_render
37
+ if InertiaRails.default_render?
38
+ render(inertia: true)
39
+ else
40
+ super
41
+ end
23
42
  end
24
43
 
25
44
  def redirect_to(options = {}, response_options = {})
@@ -36,8 +55,21 @@ module InertiaRails
36
55
  )
37
56
  end
38
57
 
58
+ def inertia_view_assigns
59
+ return {} unless @_inertia_instance_props
60
+ view_assigns.except(*@_inertia_skip_props)
61
+ end
62
+
39
63
  private
40
64
 
65
+ def inertia_layout
66
+ layout = ::InertiaRails.layout
67
+
68
+ # When the global configuration is not set, let Rails decide which layout
69
+ # should be used based on the controller configuration.
70
+ layout.nil? ? true : layout
71
+ end
72
+
41
73
  def inertia_location(url)
42
74
  headers['X-Inertia-Location'] = url
43
75
  head :conflict
@@ -13,7 +13,9 @@ module InertiaRails
13
13
 
14
14
  # "Getters"
15
15
  def self.shared_data(controller)
16
- shared_plain_data.merge!(evaluated_blocks(controller, shared_blocks))
16
+ shared_plain_data.
17
+ merge!(evaluated_blocks(controller, shared_blocks)).
18
+ with_indifferent_access
17
19
  end
18
20
 
19
21
  def self.version
@@ -32,10 +34,18 @@ module InertiaRails
32
34
  Configuration.ssr_url
33
35
  end
34
36
 
37
+ def self.default_render?
38
+ Configuration.default_render
39
+ end
40
+
35
41
  def self.html_headers
36
42
  self.threadsafe_html_headers || []
37
43
  end
38
44
 
45
+ def self.deep_merge_shared_data?
46
+ Configuration.deep_merge_shared_data
47
+ end
48
+
39
49
  # "Setters"
40
50
  def self.share(**args)
41
51
  self.shared_plain_data = self.shared_plain_data.merge(args)
@@ -62,10 +72,12 @@ module InertiaRails
62
72
  private
63
73
 
64
74
  module Configuration
65
- mattr_accessor(:layout) { 'application' }
75
+ mattr_accessor(:layout) { nil }
66
76
  mattr_accessor(:version) { nil }
67
77
  mattr_accessor(:ssr_enabled) { false }
68
78
  mattr_accessor(:ssr_url) { 'http://localhost:13714' }
79
+ mattr_accessor(:default_render) { false }
80
+ mattr_accessor(:deep_merge_shared_data) { false }
69
81
 
70
82
  def self.evaluated_version
71
83
  self.version.respond_to?(:call) ? self.version.call : self.version
@@ -18,6 +18,7 @@ module InertiaRails
18
18
  end
19
19
 
20
20
  def response
21
+ copy_xsrf_to_csrf!
21
22
  status, headers, body = @app.call(@env)
22
23
  request = ActionDispatch::Request.new(@env)
23
24
 
@@ -89,6 +90,10 @@ module InertiaRails
89
90
  request.flash.keep
90
91
  Rack::Response.new('', 409, {'X-Inertia-Location' => request.original_url}).finish
91
92
  end
93
+
94
+ def copy_xsrf_to_csrf!
95
+ @env['HTTP_X_CSRF_TOKEN'] = @env['HTTP_X_XSRF_TOKEN'] if @env['HTTP_X_XSRF_TOKEN'] && inertia_request?
96
+ end
92
97
  end
93
98
  end
94
99
  end
@@ -6,14 +6,15 @@ module InertiaRails
6
6
  class Renderer
7
7
  attr_reader :component, :view_data
8
8
 
9
- def initialize(component, controller, request, response, render_method, props:, view_data:)
10
- @component = component
9
+ 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
11
  @controller = controller
12
12
  @request = request
13
13
  @response = response
14
14
  @render_method = render_method
15
- @props = props || {}
15
+ @props = props ? props.with_indifferent_access : controller.inertia_view_assigns.with_indifferent_access
16
16
  @view_data = view_data || {}
17
+ @deep_merge = !deep_merge.nil? ? deep_merge : InertiaRails.deep_merge_shared_data?
17
18
  end
18
19
 
19
20
  def render
@@ -23,7 +24,7 @@ module InertiaRails
23
24
  @render_method.call json: page, status: @response.status, content_type: Mime[:json]
24
25
  else
25
26
  return render_ssr if ::InertiaRails.ssr_enabled? rescue nil
26
- @render_method.call template: 'inertia', layout: ::InertiaRails.layout, locals: (view_data).merge({page: page})
27
+ @render_method.call template: 'inertia', layout: layout, locals: (view_data).merge({page: page})
27
28
  end
28
29
  end
29
30
 
@@ -34,11 +35,15 @@ module InertiaRails
34
35
  res = JSON.parse(Net::HTTP.post(uri, page.to_json, 'Content-Type' => 'application/json').body)
35
36
 
36
37
  ::InertiaRails.html_headers = res['head']
37
- @render_method.call html: res['body'].html_safe, layout: ::InertiaRails.layout, locals: (view_data).merge({page: page})
38
+ @render_method.call html: res['body'].html_safe, layout: layout, locals: (view_data).merge({page: page})
38
39
  end
39
40
 
40
- def props
41
- _props = ::InertiaRails.shared_data(@controller).merge(@props).select do |key, prop|
41
+ def layout
42
+ @controller.send(:inertia_layout)
43
+ end
44
+
45
+ def computed_props
46
+ _props = ::InertiaRails.shared_data(@controller).send(prop_merge_method, @props).select do |key, prop|
42
47
  if rendering_partial_component?
43
48
  key.in? partial_keys
44
49
  else
@@ -46,13 +51,13 @@ module InertiaRails
46
51
  end
47
52
  end
48
53
 
49
- deep_transform_values(_props, lambda {|prop| prop.respond_to?(:call) ? @controller.instance_exec(&prop) : prop })
54
+ deep_transform_values(_props, lambda {|prop| prop.respond_to?(:call) ? @controller.instance_exec(&prop) : prop }).with_indifferent_access
50
55
  end
51
56
 
52
57
  def page
53
58
  {
54
59
  component: component,
55
- props: props,
60
+ props: computed_props,
56
61
  url: @request.original_fullpath,
57
62
  version: ::InertiaRails.version,
58
63
  }
@@ -71,5 +76,9 @@ module InertiaRails
71
76
  def rendering_partial_component?
72
77
  @request.inertia_partial? && @request.headers['X-Inertia-Partial-Component'] == component
73
78
  end
79
+
80
+ def prop_merge_method
81
+ @deep_merge ? :deep_merge : :merge
82
+ end
74
83
  end
75
84
  end
@@ -74,7 +74,7 @@ end
74
74
 
75
75
  RSpec::Matchers.define :have_exact_props do |expected_props|
76
76
  match do |inertia|
77
- expect(inertia.props).to eq expected_props
77
+ expect(inertia.props).to eq expected_props.with_indifferent_access
78
78
  end
79
79
 
80
80
  failure_message do |inertia|
@@ -84,7 +84,7 @@ end
84
84
 
85
85
  RSpec::Matchers.define :include_props do |expected_props|
86
86
  match do |inertia|
87
- expect(inertia.props).to include expected_props
87
+ expect(inertia.props).to include expected_props.with_indifferent_access
88
88
  end
89
89
 
90
90
  failure_message do |inertia|
@@ -1,3 +1,3 @@
1
1
  module InertiaRails
2
- VERSION = "2.0.1"
2
+ VERSION = "3.1.0"
3
3
  end
data/lib/inertia_rails.rb CHANGED
@@ -15,6 +15,7 @@ ActionController::Renderers.add :inertia do |component, options|
15
15
  method(:render),
16
16
  props: options[:props],
17
17
  view_data: options[:view_data],
18
+ deep_merge: options[:deep_merge],
18
19
  ).render
19
20
  end
20
21
 
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: 2.0.1
4
+ version: 3.1.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: 2022-07-12 00:00:00.000000000 Z
13
+ date: 2023-08-21 00:00:00.000000000 Z
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
16
16
  name: rails
@@ -124,6 +124,20 @@ dependencies:
124
124
  - - ">="
125
125
  - !ruby/object:Gem::Version
126
126
  version: '0'
127
+ - !ruby/object:Gem::Dependency
128
+ name: debug
129
+ requirement: !ruby/object:Gem::Requirement
130
+ requirements:
131
+ - - ">="
132
+ - !ruby/object:Gem::Version
133
+ version: '0'
134
+ type: :development
135
+ prerelease: false
136
+ version_requirements: !ruby/object:Gem::Requirement
137
+ requirements:
138
+ - - ">="
139
+ - !ruby/object:Gem::Version
140
+ version: '0'
127
141
  description:
128
142
  email:
129
143
  - brain@bellawatt.com
@@ -184,7 +198,7 @@ licenses:
184
198
  metadata:
185
199
  homepage_uri: https://github.com/inertiajs/inertia-rails
186
200
  source_code_uri: https://github.com/inertiajs/inertia-rails
187
- changelog_uri: https://github.com/inertiajs/inertia-rails/CHANGELOG.md
201
+ changelog_uri: https://github.com/inertiajs/inertia-rails/blob/master/CHANGELOG.md
188
202
  post_install_message:
189
203
  rdoc_options: []
190
204
  require_paths:
@@ -200,7 +214,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
200
214
  - !ruby/object:Gem::Version
201
215
  version: '0'
202
216
  requirements: []
203
- rubygems_version: 3.3.3
217
+ rubygems_version: 3.4.10
204
218
  signing_key:
205
219
  specification_version: 4
206
220
  summary: Inertia adapter for Rails