turbo_crud 0.4.4

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 (61) hide show
  1. checksums.yaml +7 -0
  2. data/MIT-LICENSE +21 -0
  3. data/README.md +277 -0
  4. data/Rakefile +11 -0
  5. data/app/assets/stylesheets/turbo_crud.css +22 -0
  6. data/app/assets/stylesheets/turbo_crud.css:Zone.Identifier +0 -0
  7. data/app/assets/stylesheets/turbo_crud_drawer.css +59 -0
  8. data/app/assets/stylesheets/turbo_crud_drawer.css:Zone.Identifier +0 -0
  9. data/app/assets/stylesheets/turbo_crud_modal.css +51 -0
  10. data/app/assets/stylesheets/turbo_crud_modal.css:Zone.Identifier +0 -0
  11. data/app/views/turbo_crud/shared/_container_drawer.html.erb +21 -0
  12. data/app/views/turbo_crud/shared/_container_drawer.html.erb:Zone.Identifier +0 -0
  13. data/app/views/turbo_crud/shared/_container_modal.html.erb +21 -0
  14. data/app/views/turbo_crud/shared/_container_modal.html.erb:Zone.Identifier +0 -0
  15. data/app/views/turbo_crud/shared/_flash.html.erb +16 -0
  16. data/app/views/turbo_crud/shared/_flash.html.erb:Zone.Identifier +0 -0
  17. data/config/routes.rb +5 -0
  18. data/config/routes.rb:Zone.Identifier +0 -0
  19. data/lib/generators/turbo_crud/full_scaffold_generator.rb +100 -0
  20. data/lib/generators/turbo_crud/full_scaffold_generator.rb:Zone.Identifier +0 -0
  21. data/lib/generators/turbo_crud/scaffold_generator.rb +264 -0
  22. data/lib/generators/turbo_crud/scaffold_generator.rb:Zone.Identifier +0 -0
  23. data/lib/generators/turbo_crud/templates/controller.rb.tt +41 -0
  24. data/lib/generators/turbo_crud/templates/controller.rb.tt:Zone.Identifier +0 -0
  25. data/lib/generators/turbo_crud/templates/views/_form.html.erb.tt +22 -0
  26. data/lib/generators/turbo_crud/templates/views/_form.html.erb.tt:Zone.Identifier +0 -0
  27. data/lib/generators/turbo_crud/templates/views/_row.html.erb.tt +20 -0
  28. data/lib/generators/turbo_crud/templates/views/_row.html.erb.tt:Zone.Identifier +0 -0
  29. data/lib/generators/turbo_crud/templates/views/edit.drawer.html.erb.tt +16 -0
  30. data/lib/generators/turbo_crud/templates/views/edit.drawer.html.erb.tt:Zone.Identifier +0 -0
  31. data/lib/generators/turbo_crud/templates/views/edit.html.erb.tt +14 -0
  32. data/lib/generators/turbo_crud/templates/views/edit.html.erb.tt:Zone.Identifier +0 -0
  33. data/lib/generators/turbo_crud/templates/views/index.html.erb.tt +15 -0
  34. data/lib/generators/turbo_crud/templates/views/index.html.erb.tt:Zone.Identifier +0 -0
  35. data/lib/generators/turbo_crud/templates/views/new.drawer.html.erb.tt +16 -0
  36. data/lib/generators/turbo_crud/templates/views/new.drawer.html.erb.tt:Zone.Identifier +0 -0
  37. data/lib/generators/turbo_crud/templates/views/new.html.erb.tt +14 -0
  38. data/lib/generators/turbo_crud/templates/views/new.html.erb.tt:Zone.Identifier +0 -0
  39. data/lib/turbo_crud/controller.rb +288 -0
  40. data/lib/turbo_crud/controller.rb:Zone.Identifier +0 -0
  41. data/lib/turbo_crud/engine.rb +14 -0
  42. data/lib/turbo_crud/engine.rb:Zone.Identifier +0 -0
  43. data/lib/turbo_crud/helpers.rb +88 -0
  44. data/lib/turbo_crud/helpers.rb:Zone.Identifier +0 -0
  45. data/lib/turbo_crud/version.rb +6 -0
  46. data/lib/turbo_crud/version.rb:Zone.Identifier +0 -0
  47. data/lib/turbo_crud.rb +52 -0
  48. data/lib/turbo_crud.rb:Zone.Identifier +0 -0
  49. data/test/README_TESTS.md +13 -0
  50. data/test/README_TESTS.md:Zone.Identifier +0 -0
  51. data/test/test_helper.rb +84 -0
  52. data/test/test_helper.rb:Zone.Identifier +0 -0
  53. data/test/tmp/full_scaffold_generator/config/routes.rb +2 -0
  54. data/test/tmp/scaffold_generator/app/assets/stylesheets/application.css +3 -0
  55. data/test/tmp/scaffold_generator/app/views/layouts/application.html.erb +2 -0
  56. data/test/tmp/scaffold_generator/config/routes.rb +2 -0
  57. data/test/turbo_crud_controller_test.rb +64 -0
  58. data/test/turbo_crud_controller_test.rb:Zone.Identifier +0 -0
  59. data/test/turbo_crud_generators_test.rb +73 -0
  60. data/test/views/posts/_row.html.erb +3 -0
  61. metadata +130 -0
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 5680c4f123c59d578d9bc79c132fed6d29251f848eec94be37366bf87f2fb8f0
4
+ data.tar.gz: 5432fb82ac1a8e8c5a537494955c52dd0e21f57bd1f940c7ed98dad34c2071b2
5
+ SHA512:
6
+ metadata.gz: 25baeec7a6f6acf23c6294a9b0c107e576deda265349459bd1ac83aeef221a9af2bb4ea013a8387c4010ee77784a45743a4f1183e218d51e79ab60ff11300524
7
+ data.tar.gz: 381dbfae5cda6cdd2fa0b4088a5ecaf5ac16b0f69f80f9c9696d6bc6979a93995aa2f12f2002f85278aad1ce65fefa60b18d9213584e63d9a4ab292d5809253f
data/MIT-LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 TurboCrud
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,277 @@
1
+ # TurboCrud (v0.4.4)
2
+
3
+ TurboCrud is a small, opinionated helper layer for Rails + Turbo that makes CRUD feel like you're speedrunning.
4
+
5
+ ## What you get
6
+ - Consistent **Turbo Stream** responses for create/update/destroy
7
+ - Drop-in **modal frame** + **drawer frame** + **flash frame**
8
+ - `turbo_save` helper (create/update with one method)
9
+ - Generator scaffold that **auto-builds form fields from attributes**
10
+ - Test skeleton + a tiny dummy app (so you can extend it later)
11
+
12
+ ---
13
+
14
+ ## Install
15
+
16
+ Add to your Gemfile:
17
+
18
+ ```ruby
19
+ gem "turbo_crud", path: "../turbo_crud"
20
+ ```
21
+
22
+ Then:
23
+
24
+ ```bash
25
+ bundle install
26
+ ```
27
+
28
+ ---
29
+
30
+ ## Layout setup (required)
31
+
32
+ Put these in `app/views/layouts/application.html.erb`:
33
+
34
+ ```erb
35
+ <%= turbo_crud_flash_frame %>
36
+
37
+ <%= turbo_crud_modal_frame %>
38
+ <%= turbo_crud_drawer_frame %>
39
+ ```
40
+
41
+ Put modal/drawer frames near the end of `<body>`.
42
+
43
+ ---
44
+
45
+ ## CSS (Sprockets)
46
+ Add:
47
+
48
+ ```css
49
+ /*
50
+ *= require turbo_crud
51
+ *= require turbo_crud_modal
52
+ *= require turbo_crud_drawer
53
+ */
54
+ ```
55
+
56
+ (If you're on cssbundling/importmap, copy these CSS files into your app or import them.)
57
+
58
+ ---
59
+
60
+ ## Controller usage
61
+
62
+ ```ruby
63
+ class PostsController < ApplicationController
64
+ include TurboCrud::Controller
65
+
66
+ def create
67
+ @post = Post.new(post_params)
68
+ turbo_create(@post, list: Post, success_message: "Post created!")
69
+ end
70
+
71
+ def update
72
+ @post = Post.find(params[:id])
73
+ @post.assign_attributes(post_params)
74
+ turbo_update(@post, success_message: "Post updated!")
75
+ end
76
+
77
+ def destroy
78
+ @post = Post.find(params[:id])
79
+ turbo_destroy(@post, list: Post, success_message: "Post deleted.")
80
+ end
81
+ end
82
+ ```
83
+
84
+ ### One-liner save (create OR update)
85
+
86
+ ```ruby
87
+ turbo_save(@post, list: Post, success_message: "Saved!")
88
+ ```
89
+
90
+ TurboCrud will decide whether to insert (create) or replace (update).
91
+
92
+ ---
93
+
94
+ ## Modal vs Drawer
95
+
96
+ Links:
97
+ - `turbo_crud_modal_link "New", new_post_path`
98
+ - `turbo_crud_drawer_link "New", new_post_path`
99
+
100
+ Forms:
101
+ - `turbo_crud_form_with ...` (defaults to your configured container)
102
+ - or explicitly: `turbo_crud_form_with ..., frame: TurboCrud.config.drawer_frame_id`
103
+
104
+ ---
105
+
106
+ ## Generator
107
+
108
+ ```bash
109
+ rails g turbo_crud:scaffold Post title body:text published:boolean views:integer
110
+ ```
111
+
112
+ It generates:
113
+ - controller wired to TurboCrud
114
+ - views: index/new/edit/_row/_form
115
+ - `_form` will contain inputs for each attribute you passed.
116
+
117
+ ---
118
+
119
+ ## Notes
120
+ This gem stays small on purpose.
121
+ Big gems become “frameworks”.
122
+ Frameworks become “why is this broken?”. 😄
123
+
124
+ ## Generator options
125
+
126
+ By default the scaffold generates **modal** `new/edit` views.
127
+
128
+ You can switch to **drawer** views:
129
+
130
+ ```bash
131
+ rails g turbo_crud:scaffold Post title body:text --container=drawer
132
+ ```
133
+
134
+ Or generate **both** (modal files + extra drawer files as `new.drawer.html.erb` / `edit.drawer.html.erb`):
135
+
136
+ ```bash
137
+ rails g turbo_crud:scaffold Post title body:text --container=both
138
+ ```
139
+
140
+ Tip: if you want the whole app to prefer drawers, set:
141
+
142
+ ```ruby
143
+ TurboCrud.configure do |c|
144
+ c.default_container = :drawer
145
+ end
146
+ ```
147
+
148
+ ## Using TurboCrud with existing apps (existing forms, existing views)
149
+
150
+ You do **not** have to rewrite your forms.
151
+
152
+ ### Step 1: open new/edit in a frame (modal or drawer)
153
+
154
+ ```erb
155
+ <%= turbo_crud_modal_link "New", new_post_path %>
156
+ <%= turbo_crud_modal_link "Edit", edit_post_path(@post) %>
157
+ ```
158
+
159
+ Or drawer:
160
+
161
+ ```erb
162
+ <%= turbo_crud_drawer_link "New", new_post_path %>
163
+ ```
164
+
165
+ ### Step 2: wrap your current new/edit views (keep your form partial!)
166
+
167
+ In your existing `new.html.erb`:
168
+
169
+ ```erb
170
+ <%= turbo_crud_container title: "New Post" do %>
171
+ <%= render "form" %>
172
+ <% end %>
173
+ ```
174
+
175
+ In your existing `edit.html.erb`:
176
+
177
+ ```erb
178
+ <%= turbo_crud_container title: "Edit Post" do %>
179
+ <%= render "form" %>
180
+ <% end %>
181
+ ```
182
+
183
+ ### Step 3: use `turbo_respond` in your controller
184
+
185
+ ```ruby
186
+ def create
187
+ @post = Post.new(post_params)
188
+ turbo_respond(@post, list: Post, success_message: "Created!")
189
+ end
190
+
191
+ def update
192
+ @post = Post.find(params[:id])
193
+ @post.assign_attributes(post_params)
194
+ turbo_respond(@post, list: Post, success_message: "Updated!")
195
+ end
196
+ ```
197
+
198
+ ### Row partial auto-detection
199
+
200
+ TurboCrud will try:
201
+ 1) `posts/_row.html.erb`
202
+ 2) `posts/_post.html.erb` (common existing Rails partial)
203
+
204
+ You can override globally:
205
+
206
+ ```ruby
207
+ TurboCrud.configure do |c|
208
+ c.row_partial = "posts/post" # or "shared/post_row"
209
+ end
210
+ ```
211
+
212
+ Or per-call:
213
+
214
+ ```ruby
215
+ turbo_respond(@post, list: Post, row_partial: "posts/post")
216
+ ```
217
+
218
+ ## Full scaffold generator (model + migration + routes + TurboCrud views)
219
+
220
+ TurboCrud now includes a “batteries included” generator that creates:
221
+
222
+ - Model + migration (like `rails g model ...`)
223
+ - Routes (`resources :things`)
224
+ - TurboCrud controller + views (modal/drawer/both)
225
+
226
+ Run it like:
227
+
228
+ ```bash
229
+ bin/rails g turbo_crud:full_scaffold Post title body:text published:boolean --container=both
230
+ bin/rails db:migrate
231
+ ```
232
+
233
+ Notes:
234
+ - Rails generators don’t run migrations automatically (Rails is polite like that).
235
+ - If routes already exist, TurboCrud won’t double-inject them.
236
+
237
+ ## One generator to remember: `turbo_crud:scaffold`
238
+
239
+ By default it generates **controller + views** (no model, no routes):
240
+
241
+ ```bash
242
+ bin/rails g turbo_crud:scaffold Post title body:text published:boolean --container=both
243
+ ```
244
+
245
+ If you want **FULL scaffold** (model + migration + routes + controller + views), add `--full`:
246
+
247
+ ```bash
248
+ bin/rails g turbo_crud:scaffold Post title body:text published:boolean --container=both --full
249
+ bin/rails db:migrate
250
+ ```
251
+
252
+ You can control parts:
253
+
254
+ ```bash
255
+ bin/rails g turbo_crud:scaffold Post title body:text --full --skip-model
256
+ bin/rails g turbo_crud:scaffold Post title body:text --full --skip-routes
257
+ ```
258
+
259
+ ## Install helper (`--install`)
260
+
261
+ You can ask the scaffold generator to also wire up your app layout + CSS.
262
+
263
+ ```bash
264
+ bin/rails g turbo_crud:scaffold Post title body:text --container=both --install
265
+ ```
266
+
267
+ What `--install` does (idempotent):
268
+ - injects these frames near the end of `app/views/layouts/application.html.erb` (before `</body>`):
269
+ - `turbo_crud_flash_frame`
270
+ - `turbo_crud_modal_frame`
271
+ - `turbo_crud_drawer_frame`
272
+ - tries to add Sprockets requires to `app/assets/stylesheets/application.css`:
273
+ - `*= require turbo_crud`
274
+ - `*= require turbo_crud_modal`
275
+ - `*= require turbo_crud_drawer`
276
+
277
+ If it can’t find those files, it will print a warning with what to add manually.
data/Rakefile ADDED
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Tiny Rakefile so `bundle exec rake test` feels at home. 🏠
4
+ require "rake/testtask"
5
+
6
+ Rake::TestTask.new do |t|
7
+ t.libs << "test"
8
+ t.pattern = "test/**/*_test.rb"
9
+ end
10
+
11
+ task default: :test
@@ -0,0 +1,22 @@
1
+ /* TurboCrud minimal flash styles (override in your app). */
2
+ /* Goal: good defaults, no extra JavaScript, no drama. */
3
+
4
+ .turbo-crud__flash{
5
+ position: fixed;
6
+ right: 1rem;
7
+ top: 1rem;
8
+ z-index: 9999;
9
+ max-width: min(92vw, 480px);
10
+ padding: 0.75rem 1rem;
11
+ border-radius: 1rem;
12
+ box-shadow: 0 18px 40px rgba(15, 23, 42, 0.18);
13
+ backdrop-filter: blur(8px);
14
+ border: 1px solid rgba(148, 163, 184, 0.35);
15
+ font-size: 0.9rem;
16
+ }
17
+
18
+ /* Green-ish “nice job” */
19
+ .turbo-crud__flash--notice{ background: rgba(16, 185, 129, 0.12); }
20
+
21
+ /* Red-ish “uh oh” */
22
+ .turbo-crud__flash--alert{ background: rgba(244, 63, 94, 0.12); }
@@ -0,0 +1,59 @@
1
+ /* TurboCrud JS-free drawer shell.
2
+ Drawer is just a modal that decided to live on the right side. 😄 */
3
+
4
+ .turbo-crud__drawer-shell{
5
+ position: fixed;
6
+ inset: 0;
7
+ z-index: 9998;
8
+ display: grid;
9
+ grid-template-columns: 1fr min(92vw, 520px);
10
+ background: rgba(2,6,23,0.55);
11
+ backdrop-filter: blur(8px);
12
+ }
13
+
14
+ .turbo-crud__drawer-backdrop{
15
+ /* Clicks will go through unless you add a close link in your drawer head.
16
+ Keeping it JS-free means: we don't auto-close on backdrop click. */
17
+ }
18
+
19
+ .turbo-crud__drawer-card{
20
+ height: 100%;
21
+ background: white;
22
+ border-left: 1px solid rgba(148,163,184,0.35);
23
+ box-shadow: -20px 0 80px rgba(15,23,42,0.25);
24
+ overflow: auto;
25
+ }
26
+
27
+ .turbo-crud__drawer-head{
28
+ position: sticky;
29
+ top: 0;
30
+ z-index: 1;
31
+ display: flex;
32
+ align-items: center;
33
+ justify-content: space-between;
34
+ padding: 1rem 1.25rem;
35
+ border-bottom: 1px solid rgba(148,163,184,0.25);
36
+ background: rgba(255,255,255,0.92);
37
+ backdrop-filter: blur(10px);
38
+ }
39
+
40
+ .turbo-crud__drawer-title{
41
+ font-size: 1rem;
42
+ font-weight: 800;
43
+ color: #0f172a;
44
+ }
45
+
46
+ .turbo-crud__drawer-close{
47
+ display: inline-flex;
48
+ align-items: center;
49
+ justify-content: center;
50
+ width: 2.25rem;
51
+ height: 2.25rem;
52
+ border-radius: 0.9rem;
53
+ border: 1px solid rgba(148,163,184,0.35);
54
+ background: white;
55
+ color: #0f172a;
56
+ text-decoration: none;
57
+ }
58
+
59
+ .turbo-crud__drawer-body{ padding: 1.25rem; }
@@ -0,0 +1,51 @@
1
+ /* TurboCrud JS-free modal shell.
2
+ Turbo swaps modal content into a frame. Simple & stable. 🌙 */
3
+
4
+ .turbo-crud__modal-shell{
5
+ position: fixed;
6
+ inset: 0;
7
+ z-index: 9998;
8
+ display: grid;
9
+ place-items: center;
10
+ padding: 1.25rem;
11
+ background: rgba(2,6,23,0.55);
12
+ backdrop-filter: blur(8px);
13
+ }
14
+
15
+ .turbo-crud__modal-card{
16
+ width: min(92vw, 720px);
17
+ border-radius: 1.25rem;
18
+ background: white;
19
+ border: 1px solid rgba(148,163,184,0.35);
20
+ box-shadow: 0 30px 80px rgba(15,23,42,0.30);
21
+ overflow: hidden;
22
+ }
23
+
24
+ .turbo-crud__modal-head{
25
+ display: flex;
26
+ align-items: center;
27
+ justify-content: space-between;
28
+ padding: 1rem 1.25rem;
29
+ border-bottom: 1px solid rgba(148,163,184,0.25);
30
+ }
31
+
32
+ .turbo-crud__modal-title{
33
+ font-size: 1rem;
34
+ font-weight: 700;
35
+ color: #0f172a;
36
+ }
37
+
38
+ .turbo-crud__modal-close{
39
+ display: inline-flex;
40
+ align-items: center;
41
+ justify-content: center;
42
+ width: 2.25rem;
43
+ height: 2.25rem;
44
+ border-radius: 0.9rem;
45
+ border: 1px solid rgba(148,163,184,0.35);
46
+ background: white;
47
+ color: #0f172a;
48
+ text-decoration: none;
49
+ }
50
+
51
+ .turbo-crud__modal-body{ padding: 1.25rem; }
@@ -0,0 +1,21 @@
1
+ <%# Drawer container shell (JS-free). Turbo swaps this into the drawer frame.
2
+ body is pre-captured HTML from your existing view. %>
3
+
4
+ <div class="turbo-crud__drawer-shell">
5
+ <div class="turbo-crud__drawer-backdrop"></div>
6
+
7
+ <div class="turbo-crud__drawer-card">
8
+ <div class="turbo-crud__drawer-head">
9
+ <h2 class="turbo-crud__drawer-title"><%= title %></h2>
10
+
11
+ <%= link_to "✕", "#",
12
+ data: { turbo_frame: (close_to_top ? "_top" : TurboCrud.config.drawer_frame_id) },
13
+ class: "turbo-crud__drawer-close",
14
+ aria: { label: "Close" } %>
15
+ </div>
16
+
17
+ <div class="turbo-crud__drawer-body">
18
+ <%= body %>
19
+ </div>
20
+ </div>
21
+ </div>
@@ -0,0 +1,21 @@
1
+ <%# Modal container shell (JS-free). Turbo swaps this into the modal frame.
2
+ body is pre-captured HTML from your existing view. %>
3
+
4
+ <div class="turbo-crud__modal-shell">
5
+ <div class="turbo-crud__modal-card">
6
+ <div class="turbo-crud__modal-head">
7
+ <h2 class="turbo-crud__modal-title"><%= title %></h2>
8
+
9
+ <%# close_to_top: if true, we target _top so the frame clears.
10
+ (works great for modal/drawer content) %>
11
+ <%= link_to "✕", "#",
12
+ data: { turbo_frame: (close_to_top ? "_top" : TurboCrud.config.modal_frame_id) },
13
+ class: "turbo-crud__modal-close",
14
+ aria: { label: "Close" } %>
15
+ </div>
16
+
17
+ <div class="turbo-crud__modal-body">
18
+ <%= body %>
19
+ </div>
20
+ </div>
21
+ </div>
@@ -0,0 +1,16 @@
1
+ <%# Flash partial. Tiny. Friendly. Like a snack. 🍪 %>
2
+
3
+ <% notice_message = local_assigns[:notice] || flash[:notice] %>
4
+ <% alert_message = local_assigns[:alert] || flash[:alert] %>
5
+
6
+ <% if notice_message.present? %>
7
+ <div class="turbo-crud__flash turbo-crud__flash--notice" role="status">
8
+ <span><%= notice_message %></span>
9
+ </div>
10
+ <% end %>
11
+
12
+ <% if alert_message.present? %>
13
+ <div class="turbo-crud__flash turbo-crud__flash--alert" role="alert">
14
+ <span><%= alert_message %></span>
15
+ </div>
16
+ <% end %>
data/config/routes.rb ADDED
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ # No engine routes (yet). We're a helper gem, not an empire. 👑🚫
4
+ TurboCrud::Engine.routes.draw do
5
+ end
Binary file
@@ -0,0 +1,100 @@
1
+ # frozen_string_literal: true
2
+
3
+ # FullScaffoldGenerator:
4
+ # Think of this as "rails scaffold" + TurboCrud UI sugar. 🍬
5
+ #
6
+ # It will generate:
7
+ # - model + migration
8
+ # - routes (resources :plural)
9
+ # - TurboCrud controller + views (modal/drawer/both)
10
+ #
11
+ # It does NOT run db:migrate (Rails generators don't do that by default).
12
+ require "rails/generators"
13
+ require "rails/generators/named_base"
14
+
15
+ module TurboCrud
16
+ module Generators
17
+ class FullScaffoldGenerator < Rails::Generators::NamedBase
18
+ VALID_CONTAINERS = %w[modal drawer both].freeze
19
+
20
+ source_root File.expand_path("templates", __dir__)
21
+
22
+ argument :attributes, type: :array, default: [], banner: "field:type field:type"
23
+
24
+ class_option :container,
25
+ type: :string,
26
+ default: "modal",
27
+ desc: "Choose where new/edit renders: modal, drawer, or both"
28
+
29
+ # If you already made the model, you can skip it.
30
+ class_option :skip_model,
31
+ type: :boolean,
32
+ default: false,
33
+ desc: "Skip generating the model/migration"
34
+
35
+ # If you already have routes, you can skip route injection.
36
+ class_option :skip_routes,
37
+ type: :boolean,
38
+ default: false,
39
+ desc: "Skip injecting resources routes"
40
+
41
+ def validate_options!
42
+ normalized = options[:container].to_s.strip.downcase
43
+ return if VALID_CONTAINERS.include?(normalized)
44
+
45
+ raise Thor::Error,
46
+ "Invalid --container=#{options[:container].inspect}. Expected one of: #{VALID_CONTAINERS.join(', ')}."
47
+ end
48
+
49
+ def generate_model
50
+ return if options[:skip_model]
51
+
52
+ # Build "title:string body:text" etc.
53
+ model_attrs = attributes.map do |a|
54
+ a.type ? "#{a.name}:#{a.type}" : a.name
55
+ end
56
+
57
+ say_status :invoke, "rails g model #{class_name} #{model_attrs.join(' ')}", :green
58
+ # Invoke the Rails model generator.
59
+ invoke "active_record:model", [class_name], attributes: attributes
60
+ end
61
+
62
+ def inject_routes
63
+ return if options[:skip_routes]
64
+
65
+ route_line = "resources :#{plural_name}"
66
+ routes_path = File.join(destination_root, "config/routes.rb")
67
+
68
+ unless File.exist?(routes_path)
69
+ say_status :warning, "config/routes.rb not found. Skipping routes injection.", :yellow
70
+ return
71
+ end
72
+
73
+ routes_content = File.read(routes_path)
74
+
75
+ if routes_content.include?(route_line)
76
+ say_status :identical, "routes already include #{route_line}", :blue
77
+ return
78
+ end
79
+
80
+ # Put routes near the top of the draw block.
81
+ # If we can't find it, we just append at the end.
82
+ if routes_content.match?(/Rails\.application\.routes\.draw do\n/m)
83
+ inject_into_file "config/routes.rb", " #{route_line}\n", after: "Rails.application.routes.draw do\n"
84
+ say_status :insert, "added #{route_line} to routes.rb", :green
85
+ else
86
+ append_to_file "config/routes.rb", "\n#{route_line}\n"
87
+ say_status :append, "appended #{route_line} to routes.rb", :green
88
+ end
89
+ end
90
+
91
+ def generate_turbo_crud_scaffold
92
+ say_status :invoke, "rails g turbo_crud:scaffold #{class_name} ...", :green
93
+
94
+ # Invoke our own scaffold generator (the one that makes controller/views)
95
+ invoke "turbo_crud:scaffold", [class_name] + attributes.map { |a| a.type ? "#{a.name}:#{a.type}" : a.name },
96
+ container: options[:container]
97
+ end
98
+ end
99
+ end
100
+ end