Skip to content

Commit

Permalink
Merge branch 'master' into fix-workflows
Browse files Browse the repository at this point in the history
  • Loading branch information
PedroAugustoRamalhoDuarte authored Apr 15, 2024
2 parents 4b0e8ec + 0d3bc00 commit d395eb3
Show file tree
Hide file tree
Showing 37 changed files with 876 additions and 42 deletions.
8 changes: 4 additions & 4 deletions Appraisals
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
appraise "rails-7.0" do
gem "rails", "~> 7.0"
end

appraise "rails-6.1" do
gem "rails", "~> 6.1.0"
end
Expand All @@ -13,7 +17,3 @@ end
appraise "rails-5.1" do
gem "rails", "~> 5.1.7"
end

appraise "rails-5.0" do
gem "rails", "~> 5.0.7", '>= 5.0.7.2'
end
47 changes: 47 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,53 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [3.1.3] - 2023-11-03

* Depend on railties instead of rails so that applications which only use pieces of Rails can avoid a full Rails installation. Thanks @BenMorganMY!

## [3.1.2] - 2023-09-26

* Fix `have_exact_props` RSpec matcher in the situation where shared props are defined in a lambda that outputs a hash with symbolized keys

## [3.1.1] - 2023-08-21

* Fix broken partial reloads caused by comparing a list of symbolized keys with string keys from HashWithIndifferentAccess

## [3.1.0] - 2023-08-21

### Features

* CSRF protection works without additional configuration now.
* Optional deep merging of shared props.

### Fixes

* Document Inertia headers. @buhrmi
* Documentation typo fix. @lujanfernaud
* Changelog URI fix. @PedroAugustoRamalhoDuarte

## [3.0.0] - 2022-09-22

* Allow rails layout to set inertia layout. Thanks @ElMassimo!
* Add the ability to set inertia props and components via rails conventions (see readme)

## [2.0.1] - 2022-07-12

* Fix for a middleware issue where global state could be polluted if an exception occurs in a request. Thanks @ElMassimo!

## [2.0.0] - 2022-06-20

* Fix an issue with Rails 7.0. Thanks @0xDing and @aviemet!
* Drop support for Rails 5.0 (and mentally, though not literally drop support for Rails < 6)

## [1.12.1] - 2022-05-09

* Allow inertia to take over after initial pageload when using ssr. Thanks @99monkey!

## [1.12.0] - 2022-05-04

* SSR!

## [1.11.1] - 2021-06-27

* Fixed thread safety in the middleware. Thanks @caifara!
Expand Down
263 changes: 262 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,268 @@

# Inertia.js Rails Adapter

Visit [inertiajs.com](https://inertiajs.com/) for installation instructions, documentation, and to learn more.
## Installation

### Backend

Just add the inertia rails gem to your Gemfile
```ruby
gem 'inertia_rails'
```

### Frontend

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)

## Usage

### Responses

Render Inertia responses is simple, just use the inertia renderer in your controller methods. The renderer accepts two arguments, the first is the name of the component you want to render from within your pages directory (without extension). The second argument is an options hash where you can provide `props` to your components. This options hash also allows you to pass `view_data` to your layout, but this is much less common.

```ruby
def index
render inertia: 'Event/Index', props: {
events: Event.all,
}
end
```

#### Rails Component and Instance Props

Starting in version 3.0, Inertia Rails allows you to provide your component name and props via common rails conventions.

```ruby
class EventsController < ApplicationController
use_inertia_instance_props

def index
@events = Event.all
end

end
```

is the same as


```ruby
class EventsController < ApplicationController
def index
render inertia: 'events/index', props: {
events: Event.all
}
end
end
```

#### Instance Props and Default Render Notes

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
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`.

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.

### Layout

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
of using rails layouts.

```ruby
class EventsController < ApplicationController
layout 'inertia_application'
end
```


### Shared Data

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.

```ruby
class EventsController < ApplicationController
# share syncronously
inertia_share app_name: env['app.name']

# share lazily, evaluated at render time
inertia_share do
if logged_in?
{
user: logged_in_user,
}
end
end

# share lazily alternate syntax
inertia_share user_count: lambda { User.count }

end
```

#### Deep Merging Shared Data

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.

```ruby
class ApplicationController
inertia_share do
{ basketball_data: { points: 50, rebounds: 100 } }
end
end
```

Let's say we want a particular action to change only part of that data structure. The renderer accepts a `deep_merge` option:

```ruby
class CrazyScorersController < ApplicationController
def index
render inertia: 'CrazyScorersComponent',
props: { basketball_data: { points: 100 } },
deep_merge: true
end
end

# The renderer will send this to the frontend:
{
basketball_data: {
points: 100,
rebounds: 100,
}
}
```

Deep merging can be set as the project wide default via the InertiaRails configuration:

```ruby
# config/initializers/some_initializer.rb
InertiaRails.configure do |config|
config.deep_merge_shared_data = true
end

```

If deep merging is enabled by default, it's possible to opt out within the action:

```ruby
class CrazyScorersController < ApplicationController
inertia_share do
{
basketball_data: {
points: 50,
rebounds: 10,
}
}
end

def index
render inertia: 'CrazyScorersComponent',
props: { basketball_data: { points: 100 } },
deep_merge: false
end
end

# Even if deep merging is set by default, since the renderer has `deep_merge: false`, it will send a shallow merge to the frontend:
{
basketball_data: {
points: 100,
}
}
```

### Lazy Props

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.

```ruby
inertia_share some_data: InertiaRails.lazy(lambda { some_very_slow_method })
```

### Routing

If you don't need a controller to handle a static component, you can route directly to a component with the inertia route helper

```ruby
inertia 'about' => 'AboutComponent'
```

### SSR

Enable SSR via the config settings for `ssr_enabled` and `ssr_url`.

When using SSR, don't forget to add `<%= inertia_headers %>` to the `<head>` of your `application.html.erb`.

## Configuration

Inertia Rails has a few different configuration options that can be set anywhere, but the most common location is from within an initializer.

The default config is shown below
```ruby
InertiaRails.configure do |config|

# set the current version for automatic asset refreshing. A string value should be used if any.
config.version = nil
# enable default inertia rendering (warning! this will override rails default rendering behavior)
config.default_render = true

# ssr specific options
config.ssr_enabled = false
config.ssr_url = 'http://localhost:13714'

config.deep_merge_shared_data = false

end
```

## Testing

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

To use these helpers, just add the following require statement to your `spec/rails_helper.rb`

```ruby
require 'inertia_rails/rspec'
```

And in any test you want to use the inertia helpers, add the inertia flag to the describe block

```ruby
RSpec.describe EventController, type: :request do
describe '#index', inertia: true do
# ...
end
end
```

### Assertions

```ruby
RSpec.describe EventController, type: :request do
describe '#index', inertia: true do

# check the component
expect_inertia.to render_component 'Event/Index'

# access the component name
expect(inertia.component).to eq 'TestComponent'

# props (including shared props)
expect_inertia.to have_exact_props({name: 'Brandon', sport: 'hockey'})
expect_inertia.to include_props({sport: 'hockey'})

# access props
expect(inertia.props[:name]).to eq 'Brandon'

# view data
expect_inertia.to have_exact_view_data({name: 'Brian', sport: 'basketball'})
expect_inertia.to include_view_data({sport: 'basketball'})

# access view data
expect(inertia.view_data[:name]).to eq 'Brian'

end
end

```

*Maintained and sponsored by the team at [bellaWatt](https://bellawatt.com/)*

Expand Down
2 changes: 1 addition & 1 deletion gemfiles/rails_5.0.gemfile → gemfiles/rails_7.0.gemfile
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,6 @@

source "https://rubygems.org"

gem "rails", "~> 5.0.7", ">= 5.0.7.2"
gem "rails", "~> 7.0"

gemspec path: "../"
5 changes: 3 additions & 2 deletions inertia_rails.gemspec
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ Gem::Specification.new do |spec|

spec.metadata["homepage_uri"] = spec.homepage
spec.metadata["source_code_uri"] = spec.homepage
spec.metadata["changelog_uri"] = "#{spec.homepage}/CHANGELOG.md"
spec.metadata["changelog_uri"] = "#{spec.homepage}/blob/master/CHANGELOG.md"

# Specify which files should be added to the gem when it is released.
# The `git ls-files -z` loads the files in the RubyGem that have been added into git.
Expand All @@ -25,7 +25,7 @@ Gem::Specification.new do |spec|
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
spec.require_paths = ["lib"]

spec.add_runtime_dependency "rails", '>= 5'
spec.add_runtime_dependency "railties", '>= 5'

spec.add_development_dependency "bundler", "~> 2.0"
spec.add_development_dependency "rake", "~> 13.0"
Expand All @@ -34,4 +34,5 @@ Gem::Specification.new do |spec|
spec.add_development_dependency "sqlite3"
spec.add_development_dependency "appraisal"
spec.add_development_dependency "responders"
spec.add_development_dependency "debug"
end
6 changes: 1 addition & 5 deletions lib/generators/inertia_rails/install/react/inertia.jsx
Original file line number Diff line number Diff line change
@@ -1,21 +1,17 @@
import { App } from '@inertiajs/inertia-react';
import React from 'react';
import { render } from 'react-dom';
import axios from 'axios';
import { InertiaProgress } from '@inertiajs/progress';

document.addEventListener('DOMContentLoaded', () => {
InertiaProgress.init();
const el = document.getElementById('app')

const csrfToken = document.querySelector('meta[name=csrf-token]').content;
axios.defaults.headers.common['X-CSRF-Token'] = csrfToken;

render(
<App
initialPage={JSON.parse(el.dataset.page)}
resolveComponent={name => require(`../Pages/${name}`).default}
/>,
el
)
});
});
Loading

0 comments on commit d395eb3

Please sign in to comment.