inertia_rails 3.3.0 → 3.5.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.
Files changed (209) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +21 -0
  3. data/README.md +6 -7
  4. data/lib/generators/inertia/controller/controller_generator.rb +18 -0
  5. data/lib/generators/inertia/controller/templates/controller.rb.tt +10 -0
  6. data/lib/generators/inertia/install/frameworks.yml +98 -0
  7. data/lib/generators/inertia/install/helpers.rb +51 -0
  8. data/lib/generators/inertia/install/install_generator.rb +291 -0
  9. data/lib/generators/inertia/install/js_package_manager.rb +50 -0
  10. data/lib/generators/inertia/install/templates/assets/inertia.svg +1 -0
  11. data/lib/generators/inertia/install/templates/assets/react.svg +1 -0
  12. data/lib/generators/inertia/install/templates/assets/svelte.svg +1 -0
  13. data/lib/generators/inertia/install/templates/assets/vite_ruby.svg +1 -0
  14. data/lib/generators/inertia/install/templates/assets/vue.svg +1 -0
  15. data/lib/generators/{inertia_rails/install → inertia/install/templates}/controller.rb +3 -1
  16. data/lib/generators/inertia/install/templates/dev +23 -0
  17. data/lib/generators/inertia/install/templates/initializer.rb +6 -0
  18. data/lib/generators/inertia/install/templates/react/InertiaExample.jsx +60 -0
  19. data/lib/generators/inertia/install/templates/react/InertiaExample.module.css +80 -0
  20. data/lib/generators/inertia/install/templates/react/InertiaExample.tsx +60 -0
  21. data/lib/generators/inertia/install/templates/react/inertia.js +45 -0
  22. data/lib/generators/inertia/install/templates/react/inertia.ts +51 -0
  23. data/lib/generators/inertia/install/templates/react/tsconfig.app.json +27 -0
  24. data/lib/generators/inertia/install/templates/react/tsconfig.json +11 -0
  25. data/lib/generators/inertia/install/templates/react/tsconfig.node.json +13 -0
  26. data/lib/generators/inertia/install/templates/react/vite-env.d.ts +1 -0
  27. data/lib/generators/inertia/install/templates/svelte/InertiaExample.svelte +112 -0
  28. data/lib/generators/inertia/install/templates/svelte/InertiaExample.ts.svelte +112 -0
  29. data/lib/generators/inertia/install/templates/svelte/inertia.js +44 -0
  30. data/lib/generators/inertia/install/templates/svelte/inertia.ts.tt +45 -0
  31. data/lib/generators/inertia/install/templates/svelte/svelte.config.js +7 -0
  32. data/lib/generators/inertia/install/templates/svelte/tsconfig.json +21 -0
  33. data/lib/generators/inertia/install/templates/svelte/tsconfig.node.json +12 -0
  34. data/lib/generators/inertia/install/templates/svelte/vite-env.d.ts +2 -0
  35. data/lib/generators/inertia/install/templates/svelte4/InertiaExample.svelte +116 -0
  36. data/lib/generators/inertia/install/templates/svelte4/InertiaExample.ts.svelte +116 -0
  37. data/lib/generators/inertia/install/templates/svelte4/inertia.js +43 -0
  38. data/lib/generators/inertia/install/templates/svelte4/inertia.ts.tt +44 -0
  39. data/lib/generators/inertia/install/templates/svelte4/svelte.config.js +7 -0
  40. data/lib/generators/inertia/install/templates/svelte4/tsconfig.json +21 -0
  41. data/lib/generators/inertia/install/templates/svelte4/tsconfig.node.json +12 -0
  42. data/lib/generators/inertia/install/templates/svelte4/vite-env.d.ts +2 -0
  43. data/lib/generators/inertia/install/templates/tailwind/application.css +13 -0
  44. data/lib/generators/inertia/install/templates/tailwind/postcss.config.js +6 -0
  45. data/lib/generators/inertia/install/templates/tailwind/tailwind.config.js.tt +18 -0
  46. data/lib/generators/inertia/install/templates/vue/InertiaExample.ts.vue +117 -0
  47. data/lib/generators/inertia/install/templates/vue/InertiaExample.vue +117 -0
  48. data/lib/generators/inertia/install/templates/vue/inertia.js +35 -0
  49. data/lib/generators/inertia/install/templates/vue/inertia.ts +35 -0
  50. data/lib/generators/inertia/install/templates/vue/tsconfig.app.json +24 -0
  51. data/lib/generators/inertia/install/templates/vue/tsconfig.json +11 -0
  52. data/lib/generators/inertia/install/templates/vue/tsconfig.node.json +22 -0
  53. data/lib/generators/inertia/install/templates/vue/vite-env.d.ts +1 -0
  54. data/lib/generators/inertia/scaffold/scaffold_generator.rb +16 -0
  55. data/lib/generators/inertia/scaffold_controller/scaffold_controller_generator.rb +60 -0
  56. data/lib/generators/inertia/scaffold_controller/templates/controller.rb.tt +100 -0
  57. data/lib/generators/inertia_templates/controller/controller_generator.rb +12 -0
  58. data/lib/generators/inertia_templates/controller/templates/react/view.jsx.tt +8 -0
  59. data/lib/generators/inertia_templates/controller/templates/react/view.tsx.tt +8 -0
  60. data/lib/generators/inertia_templates/controller/templates/svelte/view.svelte.tt +2 -0
  61. data/lib/generators/inertia_templates/controller/templates/svelte4/view.svelte.tt +2 -0
  62. data/lib/generators/inertia_templates/controller/templates/vue/view.vue.tt +4 -0
  63. data/lib/generators/inertia_templates/scaffold/scaffold_generator.rb +12 -0
  64. data/lib/generators/inertia_templates/scaffold/templates/react/Edit.jsx.tt +35 -0
  65. data/lib/generators/inertia_templates/scaffold/templates/react/Edit.tsx.tt +40 -0
  66. data/lib/generators/inertia_templates/scaffold/templates/react/Form.jsx.tt +111 -0
  67. data/lib/generators/inertia_templates/scaffold/templates/react/Form.tsx.tt +130 -0
  68. data/lib/generators/inertia_templates/scaffold/templates/react/Index.jsx.tt +26 -0
  69. data/lib/generators/inertia_templates/scaffold/templates/react/Index.tsx.tt +32 -0
  70. data/lib/generators/inertia_templates/scaffold/templates/react/New.jsx.tt +27 -0
  71. data/lib/generators/inertia_templates/scaffold/templates/react/New.tsx.tt +32 -0
  72. data/lib/generators/inertia_templates/scaffold/templates/react/One.jsx.tt +26 -0
  73. data/lib/generators/inertia_templates/scaffold/templates/react/One.tsx.tt +32 -0
  74. data/lib/generators/inertia_templates/scaffold/templates/react/Show.jsx.tt +32 -0
  75. data/lib/generators/inertia_templates/scaffold/templates/react/Show.tsx.tt +38 -0
  76. data/lib/generators/inertia_templates/scaffold/templates/react/types.ts.tt +19 -0
  77. data/lib/generators/inertia_templates/scaffold/templates/svelte/Edit.svelte.tt +36 -0
  78. data/lib/generators/inertia_templates/scaffold/templates/svelte/Edit.ts.svelte.tt +37 -0
  79. data/lib/generators/inertia_templates/scaffold/templates/svelte/Form.svelte.tt +97 -0
  80. data/lib/generators/inertia_templates/scaffold/templates/svelte/Form.ts.svelte.tt +102 -0
  81. data/lib/generators/inertia_templates/scaffold/templates/svelte/Index.svelte.tt +35 -0
  82. data/lib/generators/inertia_templates/scaffold/templates/svelte/Index.ts.svelte.tt +39 -0
  83. data/lib/generators/inertia_templates/scaffold/templates/svelte/New.svelte.tt +29 -0
  84. data/lib/generators/inertia_templates/scaffold/templates/svelte/New.ts.svelte.tt +30 -0
  85. data/lib/generators/inertia_templates/scaffold/templates/svelte/One.svelte.tt +28 -0
  86. data/lib/generators/inertia_templates/scaffold/templates/svelte/One.ts.svelte.tt +30 -0
  87. data/lib/generators/inertia_templates/scaffold/templates/svelte/Show.svelte.tt +35 -0
  88. data/lib/generators/inertia_templates/scaffold/templates/svelte/Show.ts.svelte.tt +39 -0
  89. data/lib/generators/inertia_templates/scaffold/templates/svelte/types.ts.tt +19 -0
  90. data/lib/generators/inertia_templates/scaffold/templates/svelte4/Edit.svelte.tt +37 -0
  91. data/lib/generators/inertia_templates/scaffold/templates/svelte4/Edit.ts.svelte.tt +38 -0
  92. data/lib/generators/inertia_templates/scaffold/templates/svelte4/Form.svelte.tt +96 -0
  93. data/lib/generators/inertia_templates/scaffold/templates/svelte4/Form.ts.svelte.tt +106 -0
  94. data/lib/generators/inertia_templates/scaffold/templates/svelte4/Index.svelte.tt +36 -0
  95. data/lib/generators/inertia_templates/scaffold/templates/svelte4/Index.ts.svelte.tt +37 -0
  96. data/lib/generators/inertia_templates/scaffold/templates/svelte4/New.svelte.tt +30 -0
  97. data/lib/generators/inertia_templates/scaffold/templates/svelte4/New.ts.svelte.tt +31 -0
  98. data/lib/generators/inertia_templates/scaffold/templates/svelte4/One.svelte.tt +28 -0
  99. data/lib/generators/inertia_templates/scaffold/templates/svelte4/One.ts.svelte.tt +30 -0
  100. data/lib/generators/inertia_templates/scaffold/templates/svelte4/Show.svelte.tt +39 -0
  101. data/lib/generators/inertia_templates/scaffold/templates/svelte4/Show.ts.svelte.tt +40 -0
  102. data/lib/generators/inertia_templates/scaffold/templates/svelte4/types.ts.tt +19 -0
  103. data/lib/generators/inertia_templates/scaffold/templates/vue/Edit.ts.vue.tt +37 -0
  104. data/lib/generators/inertia_templates/scaffold/templates/vue/Edit.vue.tt +36 -0
  105. data/lib/generators/inertia_templates/scaffold/templates/vue/Form.ts.vue.tt +101 -0
  106. data/lib/generators/inertia_templates/scaffold/templates/vue/Form.vue.tt +94 -0
  107. data/lib/generators/inertia_templates/scaffold/templates/vue/Index.ts.vue.tt +35 -0
  108. data/lib/generators/inertia_templates/scaffold/templates/vue/Index.vue.tt +31 -0
  109. data/lib/generators/inertia_templates/scaffold/templates/vue/New.ts.vue.tt +30 -0
  110. data/lib/generators/inertia_templates/scaffold/templates/vue/New.vue.tt +29 -0
  111. data/lib/generators/inertia_templates/scaffold/templates/vue/One.ts.vue.tt +28 -0
  112. data/lib/generators/inertia_templates/scaffold/templates/vue/One.vue.tt +26 -0
  113. data/lib/generators/inertia_templates/scaffold/templates/vue/Show.ts.vue.tt +41 -0
  114. data/lib/generators/inertia_templates/scaffold/templates/vue/Show.vue.tt +37 -0
  115. data/lib/generators/inertia_templates/scaffold/templates/vue/types.ts.tt +19 -0
  116. data/lib/generators/inertia_tw_templates/controller/controller_generator.rb +12 -0
  117. data/lib/generators/inertia_tw_templates/controller/templates/react/view.jsx.tt +8 -0
  118. data/lib/generators/inertia_tw_templates/controller/templates/react/view.tsx.tt +8 -0
  119. data/lib/generators/inertia_tw_templates/controller/templates/svelte/view.svelte.tt +2 -0
  120. data/lib/generators/inertia_tw_templates/controller/templates/svelte4/view.svelte.tt +2 -0
  121. data/lib/generators/inertia_tw_templates/controller/templates/vue/view.vue.tt +4 -0
  122. data/lib/generators/inertia_tw_templates/scaffold/scaffold_generator.rb +12 -0
  123. data/lib/generators/inertia_tw_templates/scaffold/templates/react/Edit.jsx.tt +42 -0
  124. data/lib/generators/inertia_tw_templates/scaffold/templates/react/Edit.tsx.tt +47 -0
  125. data/lib/generators/inertia_tw_templates/scaffold/templates/react/Form.jsx.tt +122 -0
  126. data/lib/generators/inertia_tw_templates/scaffold/templates/react/Form.tsx.tt +142 -0
  127. data/lib/generators/inertia_tw_templates/scaffold/templates/react/Index.jsx.tt +43 -0
  128. data/lib/generators/inertia_tw_templates/scaffold/templates/react/Index.tsx.tt +49 -0
  129. data/lib/generators/inertia_tw_templates/scaffold/templates/react/New.jsx.tt +30 -0
  130. data/lib/generators/inertia_tw_templates/scaffold/templates/react/New.tsx.tt +35 -0
  131. data/lib/generators/inertia_tw_templates/scaffold/templates/react/One.jsx.tt +26 -0
  132. data/lib/generators/inertia_tw_templates/scaffold/templates/react/One.tsx.tt +32 -0
  133. data/lib/generators/inertia_tw_templates/scaffold/templates/react/Show.jsx.tt +47 -0
  134. data/lib/generators/inertia_tw_templates/scaffold/templates/react/Show.tsx.tt +53 -0
  135. data/lib/generators/inertia_tw_templates/scaffold/templates/react/types.ts.tt +19 -0
  136. data/lib/generators/inertia_tw_templates/scaffold/templates/svelte/Edit.svelte.tt +44 -0
  137. data/lib/generators/inertia_tw_templates/scaffold/templates/svelte/Edit.ts.svelte.tt +45 -0
  138. data/lib/generators/inertia_tw_templates/scaffold/templates/svelte/Form.svelte.tt +118 -0
  139. data/lib/generators/inertia_tw_templates/scaffold/templates/svelte/Form.ts.svelte.tt +123 -0
  140. data/lib/generators/inertia_tw_templates/scaffold/templates/svelte/Index.svelte.tt +42 -0
  141. data/lib/generators/inertia_tw_templates/scaffold/templates/svelte/Index.ts.svelte.tt +46 -0
  142. data/lib/generators/inertia_tw_templates/scaffold/templates/svelte/New.svelte.tt +32 -0
  143. data/lib/generators/inertia_tw_templates/scaffold/templates/svelte/New.ts.svelte.tt +33 -0
  144. data/lib/generators/inertia_tw_templates/scaffold/templates/svelte/One.svelte.tt +28 -0
  145. data/lib/generators/inertia_tw_templates/scaffold/templates/svelte/One.ts.svelte.tt +30 -0
  146. data/lib/generators/inertia_tw_templates/scaffold/templates/svelte/Show.svelte.tt +50 -0
  147. data/lib/generators/inertia_tw_templates/scaffold/templates/svelte/Show.ts.svelte.tt +54 -0
  148. data/lib/generators/inertia_tw_templates/scaffold/templates/svelte/types.ts.tt +19 -0
  149. data/lib/generators/inertia_tw_templates/scaffold/templates/svelte4/Edit.svelte.tt +45 -0
  150. data/lib/generators/inertia_tw_templates/scaffold/templates/svelte4/Edit.ts.svelte.tt +46 -0
  151. data/lib/generators/inertia_tw_templates/scaffold/templates/svelte4/Form.svelte.tt +120 -0
  152. data/lib/generators/inertia_tw_templates/scaffold/templates/svelte4/Form.ts.svelte.tt +130 -0
  153. data/lib/generators/inertia_tw_templates/scaffold/templates/svelte4/Index.svelte.tt +43 -0
  154. data/lib/generators/inertia_tw_templates/scaffold/templates/svelte4/Index.ts.svelte.tt +44 -0
  155. data/lib/generators/inertia_tw_templates/scaffold/templates/svelte4/New.svelte.tt +33 -0
  156. data/lib/generators/inertia_tw_templates/scaffold/templates/svelte4/New.ts.svelte.tt +34 -0
  157. data/lib/generators/inertia_tw_templates/scaffold/templates/svelte4/One.svelte.tt +28 -0
  158. data/lib/generators/inertia_tw_templates/scaffold/templates/svelte4/One.ts.svelte.tt +30 -0
  159. data/lib/generators/inertia_tw_templates/scaffold/templates/svelte4/Show.svelte.tt +51 -0
  160. data/lib/generators/inertia_tw_templates/scaffold/templates/svelte4/Show.ts.svelte.tt +52 -0
  161. data/lib/generators/inertia_tw_templates/scaffold/templates/svelte4/types.ts.tt +19 -0
  162. data/lib/generators/inertia_tw_templates/scaffold/templates/vue/Edit.ts.vue.tt +45 -0
  163. data/lib/generators/inertia_tw_templates/scaffold/templates/vue/Edit.vue.tt +44 -0
  164. data/lib/generators/inertia_tw_templates/scaffold/templates/vue/Form.ts.vue.tt +134 -0
  165. data/lib/generators/inertia_tw_templates/scaffold/templates/vue/Form.vue.tt +127 -0
  166. data/lib/generators/inertia_tw_templates/scaffold/templates/vue/Index.ts.vue.tt +47 -0
  167. data/lib/generators/inertia_tw_templates/scaffold/templates/vue/Index.vue.tt +43 -0
  168. data/lib/generators/inertia_tw_templates/scaffold/templates/vue/New.ts.vue.tt +33 -0
  169. data/lib/generators/inertia_tw_templates/scaffold/templates/vue/New.vue.tt +32 -0
  170. data/lib/generators/inertia_tw_templates/scaffold/templates/vue/One.ts.vue.tt +28 -0
  171. data/lib/generators/inertia_tw_templates/scaffold/templates/vue/One.vue.tt +26 -0
  172. data/lib/generators/inertia_tw_templates/scaffold/templates/vue/Show.ts.vue.tt +53 -0
  173. data/lib/generators/inertia_tw_templates/scaffold/templates/vue/Show.vue.tt +49 -0
  174. data/lib/generators/inertia_tw_templates/scaffold/templates/vue/types.ts.tt +19 -0
  175. data/lib/inertia_rails/action_filter.rb +27 -0
  176. data/lib/inertia_rails/always_prop.rb +6 -0
  177. data/lib/inertia_rails/base_prop.rb +14 -0
  178. data/lib/inertia_rails/configuration.rb +1 -1
  179. data/lib/inertia_rails/controller.rb +70 -15
  180. data/lib/inertia_rails/generators/controller_template_base.rb +63 -0
  181. data/lib/inertia_rails/generators/helper.rb +139 -0
  182. data/lib/inertia_rails/generators/scaffold_template_base.rb +45 -0
  183. data/lib/inertia_rails/inertia_rails.rb +18 -12
  184. data/lib/inertia_rails/lazy_prop.rb +20 -0
  185. data/lib/inertia_rails/renderer.rb +71 -17
  186. data/lib/inertia_rails/version.rb +1 -1
  187. data/lib/patches/better_errors.rb +10 -8
  188. data/lib/patches/debug_exceptions/patch-5-0.rb +7 -3
  189. data/lib/patches/debug_exceptions/patch-5-1.rb +7 -3
  190. data/lib/patches/mapper.rb +9 -5
  191. data/lib/patches/request.rb +10 -6
  192. metadata +192 -126
  193. data/.github/workflows/push.yml +0 -33
  194. data/.gitignore +0 -25
  195. data/.rspec +0 -3
  196. data/CODE_OF_CONDUCT.md +0 -74
  197. data/Gemfile +0 -7
  198. data/Rakefile +0 -6
  199. data/bin/console +0 -13
  200. data/bin/setup +0 -8
  201. data/inertia_rails.gemspec +0 -37
  202. data/lib/generators/inertia_rails/install/react/InertiaExample.jsx +0 -9
  203. data/lib/generators/inertia_rails/install/react/inertia.jsx +0 -17
  204. data/lib/generators/inertia_rails/install/svelte/InertiaExample.svelte +0 -11
  205. data/lib/generators/inertia_rails/install/svelte/inertia.js +0 -14
  206. data/lib/generators/inertia_rails/install/vue/InertiaExample.vue +0 -11
  207. data/lib/generators/inertia_rails/install/vue/inertia.js +0 -20
  208. data/lib/generators/inertia_rails/install_generator.rb +0 -84
  209. data/lib/inertia_rails/lazy.rb +0 -28
@@ -0,0 +1,43 @@
1
+ <template>
2
+ <Head title="<%= human_name.pluralize %>" />
3
+
4
+ <div className="mx-auto md:w-2/3 w-full px-8 pt-8">
5
+ <p
6
+ v-if="flash.notice"
7
+ class="py-2 px-3 bg-green-50 mb-5 text-green-500 font-medium rounded-lg inline-block"
8
+ >
9
+ {{ flash.notice }}
10
+ </p>
11
+
12
+ <div class="flex justify-between items-center">
13
+ <h1 class="font-bold text-4xl"><%= human_name.pluralize %></h1>
14
+ <Link
15
+ href="<%= js_new_resource_path %>"
16
+ class="rounded-lg py-3 px-5 bg-blue-600 text-white block font-medium"
17
+ >
18
+ New <%= human_name.downcase %>
19
+ </Link>
20
+ </div>
21
+
22
+ <div class="min-w-full">
23
+ <template v-for="<%= singular_table_name %> in <%= plural_table_name %>" :key="<%= singular_table_name %>.id">
24
+ <<%= inertia_component_name %> :<%= singular_table_name %>="<%= singular_table_name %>" />
25
+ <p>
26
+ <Link
27
+ :href="`<%= js_resource_path %>`"
28
+ class="ml-2 rounded-lg py-3 px-5 bg-gray-100 inline-block font-medium"
29
+ >
30
+ Show this <%= human_name.downcase %>
31
+ </Link>
32
+ </p>
33
+ </template>
34
+ </div>
35
+ </div>
36
+ </template>
37
+
38
+ <script setup>
39
+ import { Head, Link } from '@inertiajs/vue3'
40
+ import <%= inertia_component_name %> from './<%= inertia_component_name %>.vue'
41
+
42
+ const { <%= plural_table_name %>, flash } = defineProps(['<%= plural_table_name %>', 'flash'])
43
+ </script>
@@ -0,0 +1,33 @@
1
+ <template>
2
+ <Head title="New <%= human_name.downcase %>" />
3
+
4
+ <div className="mx-auto md:w-2/3 w-full px-8 pt-8">
5
+ <h1 class="font-bold text-4xl">New <%= human_name.downcase %></h1>
6
+
7
+ <Form
8
+ :<%= singular_table_name %>="<%= singular_table_name %>"
9
+ submitText="Create <%= human_name %>"
10
+ @onSubmit="handleSubmit"
11
+ />
12
+
13
+ <Link
14
+ href="<%= js_resources_path %>"
15
+ class="ml-2 rounded-lg py-3 px-5 bg-gray-100 inline-block font-medium"
16
+ >
17
+ Back to <%= human_name.pluralize.downcase %>
18
+ </Link>
19
+ </div>
20
+ </template>
21
+
22
+ <script setup lang="ts">
23
+ import { Head, InertiaForm, Link } from '@inertiajs/vue3'
24
+ import Form from './Form.vue'
25
+ import { <%= inertia_model_form_type %>, <%= inertia_model_type %> } from './types'
26
+
27
+ const { <%= singular_table_name %> } = defineProps<{ <%= singular_table_name %>: <%= inertia_model_type %> }>()
28
+
29
+ const handleSubmit = (form: InertiaForm<<%= inertia_model_form_type %>>) => {
30
+ form.transform((data) => ({ <%= singular_table_name %>: data }))
31
+ form.post('<%= js_resources_path %>')
32
+ }
33
+ </script>
@@ -0,0 +1,32 @@
1
+ <template>
2
+ <Head title="New <%= human_name.downcase %>" />
3
+
4
+ <div className="mx-auto md:w-2/3 w-full px-8 pt-8">
5
+ <h1 class="font-bold text-4xl">New <%= human_name.downcase %></h1>
6
+
7
+ <Form
8
+ :<%= singular_table_name %>="<%= singular_table_name %>"
9
+ submitText="Create <%= human_name %>"
10
+ @onSubmit="handleSubmit"
11
+ />
12
+
13
+ <Link
14
+ href="<%= js_resources_path %>"
15
+ class="ml-2 rounded-lg py-3 px-5 bg-gray-100 inline-block font-medium"
16
+ >
17
+ Back to <%= human_name.pluralize.downcase %>
18
+ </Link>
19
+ </div>
20
+ </template>
21
+
22
+ <script setup>
23
+ import { Head, Link } from '@inertiajs/vue3'
24
+ import Form from './Form.vue'
25
+
26
+ const { <%= singular_table_name %> } = defineProps(['<%= singular_table_name %>'])
27
+
28
+ const handleSubmit = (form) => {
29
+ form.transform((data) => ({ <%= singular_table_name %>: data }))
30
+ form.post('<%= js_resources_path %>')
31
+ }
32
+ </script>
@@ -0,0 +1,28 @@
1
+ <template>
2
+ <div>
3
+ <% attributes.reject(&:password_digest?).each do |attribute| -%>
4
+ <p class="my-5">
5
+ <strong class="block font-medium mb-1"><%= attribute.human_name %>:</strong>
6
+ <% if attribute.attachment? -%>
7
+ <a v-if="<%= singular_table_name %>.<%= attribute.column_name %>" :href="<%= singular_table_name %>.<%= attribute.column_name %>.url">
8
+ {{ <%= singular_table_name %>.<%= attribute.column_name %>.filename }}
9
+ </a>
10
+ </p>
11
+ <% elsif attribute.attachments? -%>
12
+ </p>
13
+ <div v-for="file in <%= singular_table_name %>.<%= attribute.column_name %>">
14
+ <a :href="file.url">{{ file.filename }}</a>
15
+ </div>
16
+ <% else -%>
17
+ {{ <%= singular_table_name %>.<%= attribute.column_name %> }}
18
+ </p>
19
+ <% end -%>
20
+ <% end -%>
21
+ </div>
22
+ </template>
23
+
24
+ <script setup lang="ts">
25
+ import { <%= inertia_model_type %> } from './types'
26
+
27
+ const { <%= singular_table_name %> } = defineProps<{ <%= singular_table_name %>: <%= inertia_model_type %> }>()
28
+ </script>
@@ -0,0 +1,26 @@
1
+ <template>
2
+ <div>
3
+ <% attributes.reject(&:password_digest?).each do |attribute| -%>
4
+ <p class="my-5">
5
+ <strong class="block font-medium mb-1"><%= attribute.human_name %>:</strong>
6
+ <% if attribute.attachment? -%>
7
+ <a v-if="<%= singular_table_name %>.<%= attribute.column_name %>" :href="<%= singular_table_name %>.<%= attribute.column_name %>.url">
8
+ {{ <%= singular_table_name %>.<%= attribute.column_name %>.filename }}
9
+ </a>
10
+ </p>
11
+ <% elsif attribute.attachments? -%>
12
+ </p>
13
+ <div v-for="file in <%= singular_table_name %>.<%= attribute.column_name %>">
14
+ <a :href="file.url">{{ file.filename }}</a>
15
+ </div>
16
+ <% else -%>
17
+ {{ <%= singular_table_name %>.<%= attribute.column_name %> }}
18
+ </p>
19
+ <% end -%>
20
+ <% end -%>
21
+ </div>
22
+ </template>
23
+
24
+ <script setup>
25
+ const { <%= singular_table_name %> } = defineProps(['<%= singular_table_name %>'])
26
+ </script>
@@ -0,0 +1,53 @@
1
+ <template>
2
+ <Head :title="`<%= human_name %> #${<%= singular_table_name %>.id}`" />
3
+
4
+ <div className="mx-auto md:w-2/3 w-full px-8 pt-8">
5
+ <div class="mx-auto">
6
+ <p
7
+ v-if="flash.notice"
8
+ class="py-2 px-3 bg-green-50 mb-5 text-green-500 font-medium rounded-lg inline-block"
9
+ >
10
+ {{ flash.notice }}
11
+ </p>
12
+
13
+ <h1 class="font-bold text-4xl"><%= human_name %> #{{ <%= singular_table_name %>.id }}</h1>
14
+
15
+ <<%= inertia_component_name %> :<%= singular_table_name %>="<%= singular_table_name %>" />
16
+
17
+ <Link
18
+ :href="`<%= js_edit_resource_path %>`"
19
+ class="ml-2 rounded-lg py-3 px-5 bg-gray-100 inline-block font-medium"
20
+ >
21
+ Edit this <%= human_name.downcase %>
22
+ </Link>
23
+ <Link
24
+ href="<%= js_resources_path %>"
25
+ class="ml-2 rounded-lg py-3 px-5 bg-gray-100 inline-block font-medium"
26
+ >
27
+ Back to <%= human_name.pluralize.downcase %>
28
+ </Link>
29
+
30
+ <div class="inline-block ml-2">
31
+ <Link
32
+ :href="`<%= js_resource_path %>`"
33
+ as="button"
34
+ method="delete"
35
+ class="mt-2 rounded-lg py-3 px-5 bg-gray-100 font-medium"
36
+ >
37
+ Destroy this <%= human_name.downcase %>
38
+ </Link>
39
+ </div>
40
+ </div>
41
+ </div>
42
+ </template>
43
+
44
+ <script setup lang="ts">
45
+ import { Head, Link } from '@inertiajs/vue3'
46
+ import <%= inertia_component_name %> from './<%= inertia_component_name %>.vue'
47
+ import { <%= inertia_model_type %> } from './types'
48
+
49
+ const { <%= singular_table_name %>, flash } = defineProps<{
50
+ <%= singular_table_name %>: <%= inertia_model_type %>
51
+ flash: { notice?: string }
52
+ }>()
53
+ </script>
@@ -0,0 +1,49 @@
1
+ <template>
2
+ <Head :title="`<%= human_name %> #${<%= singular_table_name %>.id}`" />
3
+
4
+ <div className="mx-auto md:w-2/3 w-full px-8 pt-8">
5
+ <div class="mx-auto">
6
+ <p
7
+ v-if="flash.notice"
8
+ class="py-2 px-3 bg-green-50 mb-5 text-green-500 font-medium rounded-lg inline-block"
9
+ >
10
+ {{ flash.notice }}
11
+ </p>
12
+
13
+ <h1 class="font-bold text-4xl"><%= human_name %> #{{ <%= singular_table_name %>.id }}</h1>
14
+
15
+ <<%= inertia_component_name %> :<%= singular_table_name %>="<%= singular_table_name %>" />
16
+
17
+ <Link
18
+ :href="`<%= js_edit_resource_path %>`"
19
+ class="ml-2 rounded-lg py-3 px-5 bg-gray-100 inline-block font-medium"
20
+ >
21
+ Edit this <%= human_name.downcase %>
22
+ </Link>
23
+ <Link
24
+ href="<%= js_resources_path %>"
25
+ class="ml-2 rounded-lg py-3 px-5 bg-gray-100 inline-block font-medium"
26
+ >
27
+ Back to <%= human_name.pluralize.downcase %>
28
+ </Link>
29
+
30
+ <div class="inline-block ml-2">
31
+ <Link
32
+ :href="`<%= js_resource_path %>`"
33
+ as="button"
34
+ method="delete"
35
+ class="mt-2 rounded-lg py-3 px-5 bg-gray-100 font-medium"
36
+ >
37
+ Destroy this <%= human_name.downcase %>
38
+ </Link>
39
+ </div>
40
+ </div>
41
+ </div>
42
+ </template>
43
+
44
+ <script setup>
45
+ import { Head, Link } from '@inertiajs/vue3'
46
+ import <%= inertia_component_name %> from './<%= inertia_component_name %>.vue'
47
+
48
+ const { <%= singular_table_name %>, flash } = defineProps(['<%= singular_table_name %>', 'flash'])
49
+ </script>
@@ -0,0 +1,19 @@
1
+ export interface <%= inertia_model_type %> {
2
+ id: number
3
+ <% attributes.reject(&:password_digest?).each do |attribute| -%>
4
+ <%= attribute.column_name %>: <%= ts_type(attribute) %>
5
+ <% end -%>
6
+ }
7
+
8
+ export type <%= inertia_model_form_type %> = Omit<<%= inertia_model_type %>, <%= omit_input_attributes.map { |a| "'#{a}'" }.join(' | ') %>><% if custom_form_attributes.any? -%> & {
9
+ <% custom_form_attributes.map do |attribute| -%>
10
+ <% if attribute.password_digest? -%>
11
+ password: string
12
+ password_confirmation: string
13
+ <% elsif attribute.attachment? -%>
14
+ <%= attribute.column_name %>?: File
15
+ <% elsif attribute.attachments? -%>
16
+ <%= attribute.column_name %>?: File[]
17
+ <% end -%>
18
+ <% end -%>
19
+ }<% end %>
@@ -0,0 +1,27 @@
1
+ # frozen_string_literal: true
2
+ #
3
+ # Based on AbstractController::Callbacks::ActionFilter
4
+ # https://github.com/rails/rails/blob/v7.2.0/actionpack/lib/abstract_controller/callbacks.rb#L39
5
+ module InertiaRails
6
+ class ActionFilter
7
+ def initialize(conditional_key, actions)
8
+ @conditional_key = conditional_key
9
+ @actions = Array(actions).map(&:to_s).to_set
10
+ end
11
+
12
+ def match?(controller)
13
+ missing_action = @actions.find { |action| !controller.available_action?(action) }
14
+ if missing_action
15
+ message = <<~MSG
16
+ The #{missing_action} action could not be found for the :inertia_share
17
+ callback on #{controller.class.name}, but it is listed in the controller's
18
+ #{@conditional_key.inspect} option.
19
+ MSG
20
+
21
+ raise AbstractController::ActionNotFound.new(message, controller, missing_action)
22
+ end
23
+
24
+ @actions.include?(controller.action_name)
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,6 @@
1
+ # frozen_string_literal: true
2
+
3
+ module InertiaRails
4
+ class AlwaysProp < BaseProp
5
+ end
6
+ end
@@ -0,0 +1,14 @@
1
+ # frozen_string_literal: true
2
+
3
+ module InertiaRails
4
+ # Base class for all props.
5
+ class BaseProp
6
+ def initialize(&block)
7
+ @block = block
8
+ end
9
+
10
+ def call(controller)
11
+ controller.instance_exec(&@block)
12
+ end
13
+ end
14
+ end
@@ -63,7 +63,7 @@ module InertiaRails
63
63
  end
64
64
 
65
65
  def component_path_resolver(path:, action:)
66
- @options[:component_path_resolver].call(path:, action:)
66
+ @options[:component_path_resolver].call(path: path, action: action)
67
67
  end
68
68
 
69
69
  OPTION_NAMES.each do |option|
@@ -1,5 +1,6 @@
1
1
  require_relative "inertia_rails"
2
2
  require_relative "helper"
3
+ require_relative "action_filter"
3
4
 
4
5
  module InertiaRails
5
6
  module Controller
@@ -14,10 +15,19 @@ module InertiaRails
14
15
  end
15
16
 
16
17
  module ClassMethods
17
- def inertia_share(attrs = {}, &block)
18
- @inertia_share ||= []
19
- @inertia_share << attrs.freeze unless attrs.empty?
20
- @inertia_share << block if block
18
+ def inertia_share(hash = nil, **props, &block)
19
+ options = extract_inertia_share_options(props)
20
+ return push_to_inertia_share(**(hash || props), &block) if options.empty?
21
+
22
+ push_to_inertia_share do
23
+ next unless options[:if].all? { |filter| instance_exec(&filter) } if options[:if]
24
+ next unless options[:unless].none? { |filter| instance_exec(&filter) } if options[:unless]
25
+
26
+ next hash unless block
27
+
28
+ res = instance_exec(&block)
29
+ hash ? hash.merge(res) : res
30
+ end
21
31
  end
22
32
 
23
33
  def inertia_config(**attrs)
@@ -55,6 +65,53 @@ module InertiaRails
55
65
  end.freeze
56
66
  end
57
67
  end
68
+
69
+ private
70
+
71
+ def push_to_inertia_share(**attrs, &block)
72
+ @inertia_share ||= []
73
+ @inertia_share << attrs.freeze unless attrs.empty?
74
+ @inertia_share << block if block
75
+ end
76
+
77
+ def extract_inertia_share_options(props)
78
+ options = props.slice(:if, :unless, :only, :except)
79
+
80
+ return options if options.empty?
81
+
82
+ if props.except(:if, :unless, :only, :except).any?
83
+ raise ArgumentError, "You must not mix shared data and [:if, :unless, :only, :except] options, pass data as a hash or a block."
84
+ end
85
+
86
+ transform_inertia_share_option(options, :only, :if)
87
+ transform_inertia_share_option(options, :except, :unless)
88
+
89
+ options.transform_values! do |filters|
90
+ Array(filters).map!(&method(:filter_to_proc))
91
+ end
92
+
93
+ options
94
+ end
95
+
96
+ def transform_inertia_share_option(options, from, to)
97
+ if (from_value = options.delete(from))
98
+ filter = InertiaRails::ActionFilter.new(from, from_value)
99
+ options[to] = Array(options[to]).unshift(filter)
100
+ end
101
+ end
102
+
103
+ def filter_to_proc(filter)
104
+ case filter
105
+ when Symbol
106
+ -> { send(filter) }
107
+ when Proc
108
+ filter
109
+ when InertiaRails::ActionFilter
110
+ -> { filter.match?(self) }
111
+ else
112
+ raise ArgumentError, "You must pass a symbol or a proc as a filter."
113
+ end
114
+ end
58
115
  end
59
116
 
60
117
  def default_render
@@ -67,16 +124,7 @@ module InertiaRails
67
124
 
68
125
  def redirect_to(options = {}, response_options = {})
69
126
  capture_inertia_errors(response_options)
70
- super(options, response_options)
71
- end
72
-
73
- def redirect_back(fallback_location:, allow_other_host: true, **options)
74
- capture_inertia_errors(options)
75
- super(
76
- fallback_location: fallback_location,
77
- allow_other_host: allow_other_host,
78
- **options,
79
- )
127
+ super
80
128
  end
81
129
 
82
130
  private
@@ -109,7 +157,14 @@ module InertiaRails
109
157
 
110
158
  def capture_inertia_errors(options)
111
159
  if (inertia_errors = options.dig(:inertia, :errors))
112
- session[:inertia_errors] = inertia_errors
160
+ if inertia_errors.respond_to?(:to_hash)
161
+ session[:inertia_errors] = inertia_errors.to_hash
162
+ else
163
+ InertiaRails.deprecator.warn(
164
+ "Object passed to `inertia: { errors: ... }` must respond to `to_hash`. Pass a hash-like object instead."
165
+ )
166
+ session[:inertia_errors] = inertia_errors
167
+ end
113
168
  end
114
169
  end
115
170
  end
@@ -0,0 +1,63 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'rails/generators/named_base'
4
+ require 'inertia_rails/generators/helper'
5
+
6
+ module InertiaRails
7
+ module Generators
8
+ class ControllerTemplateBase < Rails::Generators::NamedBase
9
+ include Helper
10
+ class_option :frontend_framework, required: true, desc: 'Frontend framework to generate the views for.',
11
+ default: Helper.guess_the_default_framework
12
+
13
+ class_option :typescript, type: :boolean, desc: 'Whether to use TypeScript',
14
+ default: Helper.guess_typescript
15
+
16
+ argument :actions, type: :array, default: [], banner: 'action action'
17
+
18
+ def empty_views_dir
19
+ empty_directory base_path
20
+ end
21
+
22
+ def copy_view_files
23
+ actions.each do |action|
24
+ @action = action
25
+ @path = File.join(base_path, "#{action.camelize}.#{extension}")
26
+ template "#{options.frontend_framework}/#{template_filename}.#{extension}", @path
27
+ end
28
+ end
29
+
30
+ private
31
+
32
+ def base_path
33
+ File.join(pages_path, inertia_base_path)
34
+ end
35
+
36
+ def template_filename
37
+ 'view'
38
+ end
39
+
40
+ def pages_path
41
+ "#{root_path}/pages"
42
+ end
43
+
44
+ def root_path
45
+ (defined?(ViteRuby) ? ViteRuby.config.source_code_dir : 'app/frontend')
46
+ end
47
+
48
+ def extension
49
+ case options.frontend_framework
50
+ when 'react' then typescript? ? 'tsx' : 'jsx'
51
+ when 'vue' then 'vue'
52
+ when 'svelte', 'svelte4' then 'svelte'
53
+ else
54
+ raise ArgumentError, "Unknown frontend framework: #{options.frontend_framework}"
55
+ end
56
+ end
57
+
58
+ def typescript?
59
+ options.typescript
60
+ end
61
+ end
62
+ end
63
+ end
@@ -0,0 +1,139 @@
1
+ # frozen_string_literal: true
2
+
3
+ module InertiaRails
4
+ module Generators
5
+ module Helper
6
+ class << self
7
+ def guess_the_default_framework
8
+ package = Rails.root.join('package.json').read
9
+ case package
10
+ when %r{@inertiajs/react}
11
+ 'react'
12
+ when %r{@inertiajs/svelte}
13
+ package.match?(/"svelte": "\^5/) ? 'svelte' : 'svelte4'
14
+ when %r{@inertiajs/vue3}
15
+ 'vue'
16
+ else
17
+ Thor::Shell::Basic.new.say_error 'Could not determine the Inertia.js framework you are using.'
18
+ exit 1
19
+ end
20
+ end
21
+
22
+ def guess_typescript
23
+ Rails.root.join('tsconfig.json').exist?
24
+ end
25
+
26
+ def guess_inertia_template
27
+ if Rails.root.join('tailwind.config.js').exist? || Rails.root.join('tailwind.config.ts').exist?
28
+ 'inertia_tw_templates'
29
+ else
30
+ 'inertia_templates'
31
+ end
32
+ end
33
+ end
34
+
35
+ def inertia_base_path
36
+ (class_path + [file_name]).map(&:camelize).join('/')
37
+ end
38
+
39
+ def inertia_component_name
40
+ singular_name.camelize
41
+ end
42
+
43
+ def inertia_model_type
44
+ "#{inertia_component_name}Type"
45
+ end
46
+
47
+ def inertia_model_form_type
48
+ "#{inertia_component_name}FormType"
49
+ end
50
+
51
+ def attributes_to_serialize
52
+ [:id] + attributes.reject do |attribute|
53
+ attribute.password_digest? ||
54
+ attribute.attachment? ||
55
+ attribute.attachments?
56
+ end.map(&:column_name)
57
+ end
58
+
59
+ def custom_form_attributes
60
+ attributes.select do |attribute|
61
+ attribute.password_digest? ||
62
+ attribute.attachment? ||
63
+ attribute.attachments?
64
+ end
65
+ end
66
+
67
+ def omit_input_attributes
68
+ ['id'] + attributes.select { |attribute| attribute.attachment? || attribute.attachments? }.map(&:column_name)
69
+ end
70
+
71
+ def js_resource_path
72
+ "#{route_url}/${#{singular_table_name}.id}"
73
+ end
74
+
75
+ def js_edit_resource_path
76
+ "#{route_url}/${#{singular_table_name}.id}/edit"
77
+ end
78
+
79
+ def js_new_resource_path
80
+ "#{route_url}/new"
81
+ end
82
+
83
+ def js_resources_path
84
+ route_url
85
+ end
86
+
87
+ def inertia_js_version
88
+ @inertia_js_version ||= Gem::Version.new(
89
+ JSON.parse(`npm ls @inertiajs/core --json`).then do |json|
90
+ json['dependencies'].values.first['version']
91
+ end
92
+ )
93
+ end
94
+
95
+ def ts_type(attribute)
96
+ case attribute.type
97
+ when :float, :decimal, :integer
98
+ 'number'
99
+ when :boolean
100
+ 'boolean'
101
+ when :attachment
102
+ '{ filename: string; url: string }'
103
+ when :attachments
104
+ '{ filename: string; url: string }[]'
105
+ else
106
+ 'string'
107
+ end
108
+ end
109
+
110
+ def input_type(attribute)
111
+ case attribute.type
112
+ when :text, :rich_text
113
+ 'text_area'
114
+ when :integer, :float, :decimal
115
+ 'number'
116
+ when :datetime, :timestamp, :time
117
+ 'datetime-local'
118
+ when :date
119
+ 'date'
120
+ when :boolean
121
+ 'checkbox'
122
+ when :attachments, :attachment
123
+ 'file'
124
+ else
125
+ 'text'
126
+ end
127
+ end
128
+
129
+ def default_value(attribute)
130
+ case attribute.type
131
+ when :boolean
132
+ 'false'
133
+ else
134
+ "''"
135
+ end
136
+ end
137
+ end
138
+ end
139
+ end