Skip to content

Commit fac9eba

Browse files
authored
Merge pull request #121 from ElMassimo/refactor/shared
feat: add `inertia_config` to enable controller-specific config
2 parents 13a452c + c5dc7a4 commit fac9eba

20 files changed

+364
-232
lines changed

.gitignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,3 +20,6 @@
2020

2121
# Appraisal
2222
gemfiles/*.gemfile.lock
23+
24+
# Local files, such as .env.development.local
25+
*.local

README.md

Lines changed: 88 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -7,16 +7,26 @@
77

88
### Backend
99

10-
Just add the inertia rails gem to your Gemfile
10+
Add the `inertia_rails` gem to your Gemfile.
11+
1112
```ruby
1213
gem 'inertia_rails'
1314
```
1415

16+
For more instructions, see [Server-side setup](https://inertia-rails.netlify.app/guide/server-side-setup.html).
17+
1518
### Frontend
1619

17-
Rails 7 specific frontend docs coming soon. For now, check out the [official Inertia docs](https://inertiajs.com) or these project examples:
20+
We are discussing on bringing official docs for Inertia Rails to this repo, as
21+
the [official docs](https://inertiajs.com/client-side-setup) are specific to Laravel.
22+
23+
In the meantime, you can refer to the community-maintained [Client-side setup](https://inertia-rails.netlify.app/guide/client-side-setup.html).
24+
25+
Examples:
26+
27+
- [React/Vite](https://github.com/BrandonShar/inertia-rails-template)
28+
- [React/Vite + SSR](https://github.com/ElMassimo/inertia-rails-ssr-template)
1829
- [PingCRM with Vue and Vite](https://github.com/ledermann/pingcrm)
19-
- [Starter template with React and Vite](https://github.com/BrandonShar/inertia-rails-template)
2030

2131
## Usage
2232

@@ -65,12 +75,11 @@ end
6575
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
6676
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`.
6777

68-
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.
78+
Automatic component name is also opt in, you must set the [`default_render`](#default_render) config value to `true`. Otherwise, you can simply `render inertia: true` for the same behavior explicitly.
6979

7080
### Layout
7181

72-
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
73-
of using rails layouts.
82+
Inertia layouts use the rails layout convention and can be set or changed in the same way.
7483

7584
```ruby
7685
class EventsController < ApplicationController
@@ -135,20 +144,14 @@ end
135144
}
136145
```
137146

138-
Deep merging can be set as the project wide default via the InertiaRails configuration:
139-
140-
```ruby
141-
# config/initializers/some_initializer.rb
142-
InertiaRails.configure do |config|
143-
config.deep_merge_shared_data = true
144-
end
145-
146-
```
147+
Deep merging can be configured using the [`deep_merge_shared_data`](#deep_merge_shared_data) configuration option.
147148

148-
If deep merging is enabled by default, it's possible to opt out within the action:
149+
If deep merging is enabled, you can still opt-out within the action:
149150

150151
```ruby
151152
class CrazyScorersController < ApplicationController
153+
inertia_config(deep_merge_shared_data: true)
154+
152155
inertia_share do
153156
{
154157
basketball_data: {
@@ -165,7 +168,7 @@ class CrazyScorersController < ApplicationController
165168
end
166169
end
167170

168-
# Even if deep merging is set by default, since the renderer has `deep_merge: false`, it will send a shallow merge to the frontend:
171+
# `deep_merge: false` overrides the default:
169172
{
170173
basketball_data: {
171174
points: 100,
@@ -189,34 +192,85 @@ If you don't need a controller to handle a static component, you can route direc
189192
inertia 'about' => 'AboutComponent'
190193
```
191194

192-
### SSR
195+
### SSR _(experimental)_
196+
197+
Enable SSR via the configuration options for [`ssr_enabled`](#ssr_enabled-experimental) and [`ssr_url`](#ssr_url-experimental).
198+
199+
When using SSR, don't forget to add `<%= inertia_ssr_head %>` to the `<head>` of your layout (i.e. `application.html.erb`).
193200

194-
Enable SSR via the config settings for `ssr_enabled` and `ssr_url`.
201+
## Configuration ⚙️
195202

196-
When using SSR, don't forget to add `<%= inertia_headers %>` to the `<head>` of your `application.html.erb`.
203+
Inertia Rails can be configured globally or in a specific controller (and subclasses).
197204

198-
## Configuration
205+
### Global Configuration
199206

200-
Inertia Rails has a few different configuration options that can be set anywhere, but the most common location is from within an initializer.
207+
If using global configuration, we recommend you place the code inside an initializer:
201208

202-
The default config is shown below
203209
```ruby
210+
# config/initializers/inertia.rb
211+
204212
InertiaRails.configure do |config|
205-
206-
# set the current version for automatic asset refreshing. A string value should be used if any.
207-
config.version = nil
208-
# enable default inertia rendering (warning! this will override rails default rendering behavior)
209-
config.default_render = true
210-
211-
# ssr specific options
212-
config.ssr_enabled = false
213-
config.ssr_url = 'http://localhost:13714'
213+
# Example: force a full-reload if the deployed assets change.
214+
config.version = ViteRuby.digest
215+
end
216+
```
214217

215-
config.deep_merge_shared_data = false
216-
218+
The default configuration can be found [here](https://github.com/inertiajs/inertia-rails/blob/master/lib/inertia_rails/configuration.rb#L5-L22).
219+
220+
### Local Configuration
221+
222+
Use `inertia_config` in your controllers to override global settings:
223+
224+
```ruby
225+
class EventsController < ApplicationController
226+
inertia_config(
227+
version: "events-#{InertiaRails.configuration.version}",
228+
ssr_enabled: -> { action_name == "index" },
229+
)
217230
end
218231
```
219232

233+
### Configuration Options
234+
235+
#### `version` _(recommended)_
236+
237+
This allows Inertia to detect if the app running in the client is oudated,
238+
forcing a full page visit instead of an XHR visit on the next request.
239+
240+
See [assets versioning](https://inertiajs.com/asset-versioning).
241+
242+
__Default__: `nil`
243+
244+
#### `deep_merge_shared_data`
245+
246+
When enabled, props will be deep merged with shared data, combining hashes
247+
with the same keys instead of replacing them.
248+
249+
__Default__: `false`
250+
251+
#### `default_render`
252+
253+
Overrides Rails default rendering behavior to render using Inertia by default.
254+
255+
__Default__: `false`
256+
257+
#### `ssr_enabled` _(experimental)_
258+
259+
Whether to use a JavaScript server to pre-render your JavaScript pages,
260+
allowing your visitors to receive fully rendered HTML when they first visit
261+
your application.
262+
263+
Requires a JS server to be available at `ssr_url`. [_Example_](https://github.com/ElMassimo/inertia-rails-ssr-template)
264+
265+
__Default__: `false`
266+
267+
#### `ssr_url` _(experimental)_
268+
269+
The URL of the JS server that will pre-render the app using the specified
270+
component and props.
271+
272+
__Default__: `"http://localhost:13714"`
273+
220274
## Testing
221275

222276
If you're using Rspec, Inertia Rails comes with some nice test helpers to make things simple.

bin/console

Lines changed: 7 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,13 @@
11
#!/usr/bin/env ruby
22

3-
require "bundler/setup"
4-
require "inertia_rails/rails"
5-
6-
# You can add fixtures and/or initialization code here to make experimenting
7-
# with your gem easier. You can also use a different console, if you like.
3+
require "pathname"
4+
ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../../Gemfile",
5+
Pathname.new(__FILE__).realpath)
86

9-
# (If you use this, don't forget to add pry to your Gemfile!)
10-
# require "pry"
11-
# Pry.start
7+
require "rubygems"
8+
require "bundler/setup"
9+
require "rails/all"
10+
require "inertia_rails"
1211

1312
require "irb"
1413
IRB.start(__FILE__)

lib/inertia_rails.rb

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,4 +21,8 @@
2121

2222
module InertiaRails
2323
class Error < StandardError; end
24+
25+
def self.deprecator # :nodoc:
26+
@deprecator ||= ActiveSupport::Deprecation.new
27+
end
2428
end

lib/inertia_rails/configuration.rb

Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
# frozen_string_literal: true
2+
3+
module InertiaRails
4+
class Configuration
5+
DEFAULTS = {
6+
# Whether to combine hashes with the same keys instead of replacing them.
7+
deep_merge_shared_data: false,
8+
9+
# Overrides Rails default rendering behavior to render using Inertia by default.
10+
default_render: false,
11+
12+
# DEPRECATED: Let Rails decide which layout should be used based on the
13+
# controller configuration.
14+
layout: true,
15+
16+
# SSR options.
17+
ssr_enabled: false,
18+
ssr_url: 'http://localhost:13714',
19+
20+
# Used to detect version drift between server and client.
21+
version: nil,
22+
}.freeze
23+
24+
OPTION_NAMES = DEFAULTS.keys.freeze
25+
26+
protected attr_reader :controller
27+
protected attr_reader :options
28+
29+
def initialize(controller: nil, **attrs)
30+
@controller = controller
31+
@options = attrs.extract!(*OPTION_NAMES)
32+
33+
unless attrs.empty?
34+
raise ArgumentError, "Unknown options for #{self.class}: #{attrs.keys}"
35+
end
36+
end
37+
38+
def bind_controller(controller)
39+
Configuration.new(**@options, controller: controller)
40+
end
41+
42+
def freeze
43+
@options.freeze
44+
super
45+
end
46+
47+
def merge!(config)
48+
@options.merge!(config.options)
49+
self
50+
end
51+
52+
def merge(config)
53+
Configuration.new(**@options.merge(config.options))
54+
end
55+
56+
# Internal: Finalizes the configuration for a specific controller.
57+
def with_defaults(config)
58+
@options = config.options.merge(@options)
59+
freeze
60+
end
61+
62+
OPTION_NAMES.each do |option|
63+
define_method(option) {
64+
evaluate_option @options[option]
65+
}
66+
define_method("#{option}=") { |value|
67+
@options[option] = value
68+
}
69+
end
70+
71+
def self.default
72+
new(**DEFAULTS)
73+
end
74+
75+
private
76+
77+
def evaluate_option(value)
78+
return value unless value.respond_to?(:call)
79+
return value.call unless controller
80+
controller.instance_exec(&value)
81+
end
82+
end
83+
end

0 commit comments

Comments
 (0)