Skip to content

Commit

Permalink
Add install, scaffold & controller generators
Browse files Browse the repository at this point in the history
  • Loading branch information
skryukov committed Jun 11, 2024
1 parent 37e3d7f commit a5c6176
Show file tree
Hide file tree
Showing 71 changed files with 2,924 additions and 0 deletions.
46 changes: 46 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,52 @@ foreman start -f Procfile.dev
And navigate to `http://127.0.0.1:5100/inertia-example` to see the example Inertia page.
### Scaffold generator
`InertiaRailsContrib` also comes with a scaffold generator that generates a scaffold for a model with Inertia. To use it, execute the following command in the terminal:
```bash
bin/rails generate inertia:scaffold ModelName field1:type field2:type
```
Example output:
```bash
$ bin/rails generate inertia:scaffold Post title:string body:text
invoke active_record
create db/migrate/20240611123952_create_posts.rb
create app/models/post.rb
invoke test_unit
create test/models/post_test.rb
create test/fixtures/posts.yml
invoke resource_route
route resources :posts
invoke scaffold_controller
create app/controllers/posts_controller.rb
invoke inertia_templates
create app/frontend/pages/Post
create app/frontend/pages/Post/Index.svelte
create app/frontend/pages/Post/Edit.svelte
create app/frontend/pages/Post/Show.svelte
create app/frontend/pages/Post/New.svelte
create app/frontend/pages/Post/Form.svelte
create app/frontend/pages/Post/Post.svelte
invoke resource_route
invoke test_unit
create test/controllers/posts_controller_test.rb
create test/system/posts_test.rb
invoke helper
create app/helpers/posts_helper.rb
invoke test_unit
```
#### Tailwind CSS integration
`InertiaRailsContrib` tries to detect the presence of Tailwind CSS in the application and generate the templates accordingly. If you want to specify templates type, use the `--inertia-templates` option:
- `inertia_templates` - default
- `inertia_tw_templates` - Tailwind CSS
## Development
After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
Expand Down
16 changes: 16 additions & 0 deletions lib/generators/inertia/controller/controller_generator.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
require "rails/generators/rails/controller/controller_generator"
require "inertia_rails_contrib/generators_helper"

module Inertia
module Generators
class ControllerGenerator < Rails::Generators::ControllerGenerator
include InertiaRailsContrib::GeneratorsHelper

source_root File.expand_path("./templates", __dir__)

remove_hook_for :template_engine

hook_for :inertia_templates, required: true, default: InertiaRailsContrib::GeneratorsHelper.guess_inertia_template
end
end
end
10 changes: 10 additions & 0 deletions lib/generators/inertia/controller/templates/controller.rb.tt
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
<% module_namespacing do -%>
class <%= class_name %>Controller < <%= parent_class_name.classify %>
<% actions.each do |action| -%>
def <%= action %>
render inertia: '<%= "#{inertia_base_path}/#{action.camelize}" %>'
end
<%= "\n" unless action == actions.last -%>
<% end -%>
end
<% end -%>
151 changes: 151 additions & 0 deletions lib/generators/inertia/install/install_generator.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,151 @@
module Inertia
module Generators
class InstallGenerator < Rails::Generators::Base
source_root File.expand_path("./templates", __dir__)

APPLICATION_LAYOUT = Rails.root.join("app/views/layouts/application.html.erb")

FRAMEWORKS = {
"react" => {
packages: %w[@inertiajs/react react react-dom],
dev_packages: %w[@vitejs/plugin-react],
vite_plugin_import: "import react from '@vitejs/plugin-react'",
vite_plugin_call: "react()",
copy_files: {
"InertiaExample.jsx" => "app/frontend/pages/InertiaExample.jsx",
"InertiaExample.module.css" => "app/frontend/pages/InertiaExample.module.css",
"../assets/react.svg" => "app/frontend/assets/react.svg",
"../assets/inertia.svg" => "app/frontend/assets/inertia.svg",
"../assets/vite_ruby.svg" => "app/frontend/assets/vite_ruby.svg"
}
},
"vue" => {
packages: %w[@inertiajs/vue3 vue],
dev_packages: %w[@vitejs/plugin-vue],
vite_plugin_import: "import vue from '@vitejs/plugin-vue'",
vite_plugin_call: "vue()",
copy_files: {
"InertiaExample.vue" => "app/frontend/pages/InertiaExample.vue",
"../assets/vue.svg" => "app/frontend/assets/vue.svg",
"../assets/inertia.svg" => "app/frontend/assets/inertia.svg",
"../assets/vite_ruby.svg" => "app/frontend/assets/vite_ruby.svg"
}
},
"svelte" => {
packages: %w[@inertiajs/svelte svelte @sveltejs/vite-plugin-svelte],
dev_packages: %w[@vitejs/plugin-vue],
vite_plugin_import: "import { svelte } from '@sveltejs/vite-plugin-svelte'",
vite_plugin_call: "svelte()",
copy_files: {
"svelte.config.js" => "svelte.config.js",
"InertiaExample.svelte" => "app/frontend/pages/InertiaExample.svelte",
"../assets/svelte.svg" => "app/frontend/assets/svelte.svg",
"../assets/inertia.svg" => "app/frontend/assets/inertia.svg",
"../assets/vite_ruby.svg" => "app/frontend/assets/vite_ruby.svg"
}
}
}

def install
say "Installing Inertia's Rails adapter"

if package_manager.nil?
say "Could not find a package.json file to install Inertia to.", :red
exit!
end

unless ruby_vite?
say "Could not find a Vite configuration file `config/vite.json`. This generator only supports Ruby on Rails with Vite.", :red
exit!
end

install_inertia

say "Inertia's Rails adapter successfully installed", :green
end

private

def install_inertia
say "Adding Inertia's Rails adapter initializer"
template "initializer.rb", Rails.root.join("config/initializers/inertia_rails.rb").to_s

say "Installing Inertia npm packages"
add_packages(*FRAMEWORKS[framework][:packages])
add_packages("--save-dev", *FRAMEWORKS[framework][:dev_packages])

unless File.read(vite_config_path).include?(FRAMEWORKS[framework][:vite_plugin_import])
say "Adding Vite plugin for #{framework}"
insert_into_file vite_config_path, "\n #{FRAMEWORKS[framework][:vite_plugin_call]},", after: "plugins: ["
prepend_file vite_config_path, "#{FRAMEWORKS[framework][:vite_plugin_import]}\n"
end

unless Rails.root.join("package.json").read.include?('"type": "module"')
say 'Add "type": "module", to the package.json file'
gsub_file Rails.root.join("package.json").to_s, /\A\s*\{/, "{\n \"type\": \"module\","
end

say "Copying inertia.js into Vite entrypoints", :blue
template "#{framework}/inertia.js", Rails.root.join("app/frontend/entrypoints/inertia.js").to_s

say "Adding inertia.js script tag to the application layout"
headers = <<-ERB
<%= vite_javascript_tag 'inertia' %>
<%= inertia_headers %>
ERB
insert_into_file APPLICATION_LAYOUT.to_s, headers, after: "<%= vite_client_tag %>\n"

if framework == "react" && !APPLICATION_LAYOUT.read.include?("vite_react_refresh_tag")
say "Adding Vite React Refresh tag to the application layout"
insert_into_file APPLICATION_LAYOUT.to_s, "<%= vite_react_refresh_tag %>\n ", before: "<%= vite_client_tag %>"
end

say "Copying example Inertia controller"
template "controller.rb", Rails.root.join("app/controllers/inertia_example_controller.rb").to_s

say "Adding a route for the example Inertia controller"
route "get 'inertia-example', to: 'inertia_example#index'"

say "Copying framework related files"
FRAMEWORKS[framework][:copy_files].each do |source, destination|
template "#{framework}/#{source}", Rails.root.join(destination).to_s
end
end

def ruby_vite?
Rails.root.join("config/vite.json").exist? && vite_config_path
end

def package_manager
return @package_manager if defined?(@package_manager)

@package_manager = detect_package_manager
end

def add_packages(*packages)
run "#{package_manager} add #{packages.join(" ")}"
end

def detect_package_manager
return nil unless Rails.root.join("package.json").exist?

if Rails.root.join("package-lock.json").exist?
"npm"
elsif Rails.root.join("bun.config.js").exist?
"bun"
else
"yarn"
end
end

def vite_config_path
@vite_config_path ||= Dir.glob(Rails.root.join("vite.config.{ts,js,mjs,cjs}")).first
end

def framework
@framework ||= ask("What framework do you want to use with Turbo Mount?", limited_to: FRAMEWORKS.keys, default: "react")
end
end
end
end
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions lib/generators/inertia/install/templates/assets/react.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions lib/generators/inertia/install/templates/assets/svelte.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions lib/generators/inertia/install/templates/assets/vue.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
7 changes: 7 additions & 0 deletions lib/generators/inertia/install/templates/controller.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
class InertiaExampleController < ApplicationController
def index
render inertia: "InertiaExample", props: {
name: params.fetch(:name, "World")
}
end
end
4 changes: 4 additions & 0 deletions lib/generators/inertia/install/templates/initializer.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
InertiaRails.configure do |config|
config.ssr_enabled = ViteRuby.config.ssr_build_enabled
config.version = ViteRuby.digest
end
60 changes: 60 additions & 0 deletions lib/generators/inertia/install/templates/react/InertiaExample.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
import { Head } from '@inertiajs/react'
import { useState } from 'react'

import reactSvg from '/assets/react.svg'
import inertiaSvg from '/assets/inertia.svg'
import viteRubySvg from '/assets/vite_ruby.svg'

import cs from './InertiaExample.module.css'

export default function InertiaExample({ name }) {
const [count, setCount] = useState(0)

return (
<>
<Head title="Inertia + Vite Ruby + React Example" />

<div className={cs.root}>
<h1 className={cs.h1}>Hello {name}!</h1>

<div>
<a href="https://inertia-rails.netlify.app" target="_blank">
<img className={cs.logo} src={inertiaSvg} alt="Inertia logo" />
</a>
<a href="https://vite-ruby.netlify.app" target="_blank">
<img
className={`${cs.logo} ${cs.vite}`}
src={viteRubySvg}
alt="Vite Ruby logo"
/>
</a>
<a href="https://react.dev" target="_blank">
<img
className={`${cs.logo} ${cs.react}`}
src={reactSvg}
alt="React logo"
/>
</a>
</div>

<h2 className={cs.h2}>Inertia + Vite Ruby + React</h2>

<div className="card">
<button
className={cs.button}
onClick={() => setCount((count) => count + 1)}
>
count is {count}
</button>
<p>
Edit <code>app/frontend/pages/InertiaExample.jsx</code> and save to
test HMR
</p>
</div>
<p className={cs.readTheDocs}>
Click on the Inertia, Vite Ruby, and React logos to learn more
</p>
</div>
</>
)
}
Loading

0 comments on commit a5c6176

Please sign in to comment.