turbo_turbo 0.2.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 (48) hide show
  1. checksums.yaml +7 -0
  2. data/.claude/settings.local.json +14 -0
  3. data/.rspec +3 -0
  4. data/.rubocop.yml +96 -0
  5. data/CHANGELOG.md +50 -0
  6. data/LICENSE +21 -0
  7. data/README.md +498 -0
  8. data/Rakefile +12 -0
  9. data/app/assets/stylesheets/turbo_turbo/alerts.css +7 -0
  10. data/app/assets/stylesheets/turbo_turbo/base.css +4 -0
  11. data/app/assets/stylesheets/turbo_turbo/button.css +24 -0
  12. data/app/assets/stylesheets/turbo_turbo/modal.css +81 -0
  13. data/app/components/turbo_turbo/alerts/alert_component.html.erb +36 -0
  14. data/app/components/turbo_turbo/alerts/alert_component.rb +12 -0
  15. data/app/components/turbo_turbo/alerts/error_component.html.erb +36 -0
  16. data/app/components/turbo_turbo/alerts/error_component.rb +12 -0
  17. data/app/components/turbo_turbo/alerts/info_component.html.erb +36 -0
  18. data/app/components/turbo_turbo/alerts/info_component.rb +12 -0
  19. data/app/components/turbo_turbo/alerts/success_component.html.erb +47 -0
  20. data/app/components/turbo_turbo/alerts/success_component.rb +12 -0
  21. data/app/components/turbo_turbo/alerts/warning_component.html.erb +36 -0
  22. data/app/components/turbo_turbo/alerts/warning_component.rb +12 -0
  23. data/app/components/turbo_turbo/modal_component.html.erb +20 -0
  24. data/app/components/turbo_turbo/modal_component.rb +9 -0
  25. data/app/components/turbo_turbo/modal_footer_component.html.erb +6 -0
  26. data/app/components/turbo_turbo/modal_footer_component.rb +10 -0
  27. data/config/locales/en.yml +15 -0
  28. data/config/routes/turbo_turbo_routes.rb +20 -0
  29. data/lib/generators/turbo_turbo/install_generator.rb +351 -0
  30. data/lib/generators/turbo_turbo/layout_generator.rb +221 -0
  31. data/lib/generators/turbo_turbo/templates/config/routes/turbo_turbo_routes.rb +20 -0
  32. data/lib/generators/turbo_turbo/templates/turbo_turbo/_error_message.html.erb +15 -0
  33. data/lib/generators/turbo_turbo/templates/turbo_turbo/_flashes.html.erb +8 -0
  34. data/lib/generators/turbo_turbo/templates/turbo_turbo/_modal_background.html.erb +2 -0
  35. data/lib/generators/turbo_turbo/templates/turbo_turbo/flash_controller.js +28 -0
  36. data/lib/generators/turbo_turbo/templates/turbo_turbo/modal_controller.js +114 -0
  37. data/lib/generators/turbo_turbo/views_generator.rb +57 -0
  38. data/lib/turbo_turbo/controller_helpers.rb +157 -0
  39. data/lib/turbo_turbo/engine.rb +19 -0
  40. data/lib/turbo_turbo/form_helper.rb +135 -0
  41. data/lib/turbo_turbo/parameter_sanitizer.rb +69 -0
  42. data/lib/turbo_turbo/standard_actions.rb +96 -0
  43. data/lib/turbo_turbo/test_helpers.rb +64 -0
  44. data/lib/turbo_turbo/version.rb +5 -0
  45. data/lib/turbo_turbo.rb +15 -0
  46. data/sig/turbo_turbo.rbs +4 -0
  47. data/turbo_turbo.gemspec +42 -0
  48. metadata +136 -0
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 54f42ce2e6f7ed159b2d7aec601352045eecc86b5619f528fc85c8e091f47d76
4
+ data.tar.gz: ea4af57484afb31fe80efc194f48db5104b8edd487378244848799ef7a30542e
5
+ SHA512:
6
+ metadata.gz: '08e33ca5a2884173f62eaad731be0a9a0db1f95e7478ceb22c5dbaf008ef929b6070e91e75009176e3a932196e9394985a476edecd65bca587f9c44c79a7a1f7'
7
+ data.tar.gz: 80fcff79f36604c58899de2433d3b88315565b4b4f081c446c56f698c37235d1554a2296b99495c82b6fb1965f173b287301c019037586ac1554a02df554f9fb
@@ -0,0 +1,14 @@
1
+ {
2
+ "permissions": {
3
+ "allow": [
4
+ "Bash(git tag:*)",
5
+ "Bash(gem build:*)",
6
+ "Bash(gem push:*)",
7
+ "Bash(mv:*)",
8
+ "Bash(ls:*)",
9
+ "Bash(bundle exec:*)",
10
+ "Bash(git add:*)"
11
+ ],
12
+ "deny": []
13
+ }
14
+ }
data/.rspec ADDED
@@ -0,0 +1,3 @@
1
+ --format documentation
2
+ --color
3
+ --require spec_helper
data/.rubocop.yml ADDED
@@ -0,0 +1,96 @@
1
+ AllCops:
2
+ NewCops: enable
3
+ TargetRubyVersion: 3.3.6
4
+ Exclude:
5
+ - 'bin/**/*'
6
+ - 'log/**/*'
7
+ - 'tmp/**/*'
8
+ - 'vendor/**/*'
9
+ - 'node_modules/**/*'
10
+
11
+ # Exclude generator files from complex metrics since they are one-time setup code
12
+ Metrics/ClassLength:
13
+ Exclude:
14
+ - 'lib/generators/**/*'
15
+ - 'spec/**/*'
16
+
17
+ Metrics/MethodLength:
18
+ Exclude:
19
+ - 'lib/generators/**/*'
20
+ - 'spec/**/*'
21
+ - 'lib/turbo_turbo/form_helper.rb'
22
+ - 'lib/turbo_turbo/controller_helpers.rb'
23
+
24
+ Metrics/AbcSize:
25
+ Exclude:
26
+ - 'lib/generators/**/*'
27
+ - 'spec/**/*'
28
+ - 'lib/turbo_turbo/form_helper.rb'
29
+
30
+ Metrics/CyclomaticComplexity:
31
+ Exclude:
32
+ - 'lib/generators/**/*'
33
+ - 'spec/**/*'
34
+ - 'lib/turbo_turbo/form_helper.rb'
35
+
36
+ Metrics/PerceivedComplexity:
37
+ Exclude:
38
+ - 'lib/generators/**/*'
39
+ - 'spec/**/*'
40
+ - 'lib/turbo_turbo/form_helper.rb'
41
+
42
+ Metrics/BlockLength:
43
+ Exclude:
44
+ - 'spec/**/*'
45
+ - '*.gemspec'
46
+ - 'lib/turbo_turbo/standard_actions.rb'
47
+
48
+ Metrics/ModuleLength:
49
+ Exclude:
50
+ - 'spec/**/*'
51
+ - 'lib/turbo_turbo/controller_helpers.rb'
52
+
53
+ Layout/LineLength:
54
+ Max: 120
55
+ Exclude:
56
+ - 'lib/generators/**/*'
57
+ - 'spec/**/*'
58
+ - 'lib/turbo_turbo/form_helper.rb'
59
+
60
+ # ViewComponent `initialize` methods don't need to call super
61
+ # This is a false positive - ViewComponent handles initialization differently
62
+ Lint/MissingSuper:
63
+ Exclude:
64
+ - 'app/components/**/*'
65
+
66
+ # Allow constants in blocks for specs
67
+ Lint/ConstantDefinitionInBlock:
68
+ Exclude:
69
+ - 'spec/**/*'
70
+
71
+ # Specs can use predicate methods without ?
72
+ Naming/PredicateMethod:
73
+ Exclude:
74
+ - 'spec/**/*'
75
+
76
+ # Allow getter/setter methods in specs
77
+ Naming/AccessorMethodName:
78
+ Exclude:
79
+ - 'spec/**/*'
80
+
81
+ Style/StringLiterals:
82
+ EnforcedStyle: double_quotes
83
+
84
+ # Allow boolean parameters in specs
85
+ Style/OptionalBooleanParameter:
86
+ Exclude:
87
+ - 'spec/**/*'
88
+
89
+ # Allow string concatenation in specs
90
+ Style/StringConcatenation:
91
+ Exclude:
92
+ - 'spec/**/*'
93
+
94
+ # Documentation is handled in README for this gem
95
+ Style/Documentation:
96
+ Enabled: false
data/CHANGELOG.md ADDED
@@ -0,0 +1,50 @@
1
+ # Changelog
2
+
3
+ All notable changes to this project will be documented in this file.
4
+
5
+ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
6
+ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
+
8
+ ## [0.2.0] - 2025-01-23
9
+
10
+ ### Fixed
11
+ - Renamed routes file from `turbo_turbo.rb` to `turbo_turbo_routes.rb` to prevent loading issues
12
+ - Updated install generator to use the new routes filename
13
+ - Fixed routes configuration to use `draw(:turbo_turbo_routes)` instead of `draw(:turbo_turbo)`
14
+
15
+ ### Added
16
+ - Routes spec to ensure routes file loads properly
17
+
18
+ ## [0.1.0] - 2025-01-15
19
+
20
+ ### Added
21
+ - Initial release of TurboTurbo gem
22
+ - Controller helpers for standardized Turbo Stream responses
23
+ - Modal management system with JavaScript controller
24
+ - Flash message handling with auto-dismiss functionality
25
+ - ViewComponent alert components (Success, Error, Warning, Info, Alert)
26
+ - Rails generator for easy installation with automatic configuration
27
+ - Complete test suite with RSpec
28
+ - Comprehensive documentation and usage examples
29
+ - Enhanced install generator with automatic ApplicationController setup
30
+ - Automatic CSS import handling
31
+ - Custom layout generator for multi-layout applications
32
+ - Form helper for standardized modal forms with builder support
33
+ - Parameter sanitization with `sanitize_for_model` method
34
+ - `turbo_actions` DSL for convention-based CRUD operations
35
+ - Test helpers for system and integration testing
36
+
37
+ ### Features
38
+ - `process_turbo_response` for handling CRUD operations
39
+ - `render_turbo_modal` for modal workflows
40
+ - `turbo_success_response` and `turbo_error_response` for custom responses
41
+ - `turbo_form_for` form helper with SimpleForm, form_with, and form_for support
42
+ - `turbo_actions` DSL supporting :create, :update, :destroy, :show, :new, :edit
43
+ - Automatic flash message generation with proper grammar and I18n support
44
+ - Modal controller with remote content loading
45
+ - Flash controller with fade-out animations
46
+ - Parameter sanitization with boolean conversion and string trimming
47
+ - Smart install generator that configures ApplicationController and imports CSS
48
+ - Rails 7+ compatibility
49
+ - Turbo-rails integration
50
+ - ViewComponent integration
data/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 Dan Brown
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,498 @@
1
+ # TurboTurbo
2
+
3
+ A simplified DSL for responding in common ways to common controller actions so you can write as little code in CRUD routes as possible. TurboTurbo provides a set of controller helpers that streamline Turbo Stream responses, modal management, and flash message handling in Rails applications.
4
+
5
+ ## Features
6
+
7
+ - **Convention-based DSL** - Eliminate CRUD boilerplate with `turbo_actions :create, :update, :destroy`
8
+ - **Modal form abstraction** - Transform 10+ lines of form boilerplate into 3 lines
9
+ - **Smart parameter sanitization** - Automatic boolean conversion and string trimming with `sanitize_for_model`
10
+ - **Simplified Turbo Stream responses** - Standardized patterns for all CRUD operations
11
+ - **Modal management** - Complete modal system with namespaced Stimulus controllers
12
+ - **Flash message handling** - Automatic flash message generation with I18n support
13
+ - **ViewComponent integration** - Pre-built alert and modal components
14
+ - **Configurable action groups** - Customize Turbo Stream behaviors by action type
15
+ - **Rails 7+ compatible** - Built for modern Rails applications
16
+ - **Zero dependencies** - No external template engines or SVG processors required
17
+ - **Test helpers included** - Built-in helpers for system/integration testing
18
+
19
+ ## Quick Start
20
+
21
+ ### 1. Installation
22
+
23
+ Add this line to your application's Gemfile:
24
+
25
+ ```ruby
26
+ gem 'turbo_turbo'
27
+ ```
28
+
29
+ And then execute:
30
+
31
+ ```bash
32
+ bundle install
33
+ rails generate turbo_turbo:install
34
+ ```
35
+
36
+ **⚠️ Important: Restart your Rails server after installation for the gem's helpers to be available.**
37
+
38
+ ### 2. Start using the DSL
39
+
40
+ ```ruby
41
+ class MessagesController < ApplicationController
42
+ turbo_actions :create, :update, :destroy, :show, :new, :edit
43
+
44
+ private
45
+
46
+ def message_params
47
+ params.require(:message)
48
+ .permit(:content, :title)
49
+ .sanitize_for_model(:message)
50
+ end
51
+ end
52
+ ```
53
+
54
+ ## What the Installer Does
55
+
56
+ The installer automatically:
57
+ - **Copies namespaced JavaScript controllers** to `app/javascript/controllers/turbo_turbo/`
58
+ - **Registers controllers** (`turbo-turbo--modal`, `turbo-turbo--flash`) in Stimulus index.js
59
+ - **Configures ApplicationController** with `TurboTurbo::ControllerHelpers` and flash types
60
+ - **Copies TurboTurbo CSS** to `app/assets/stylesheets/turbo_turbo/` (includes component CSS files and base.css, which just imports the component CSS files)
61
+ - **Imports TurboTurbo CSS** into your main stylesheet
62
+ - **Copies layout partials** for modal background and flashes to `app/views/turbo_turbo/`
63
+ - **Intelligently modifies your layout file** to include required components
64
+ - **ViewComponents work directly from the gem** (no copying required)
65
+
66
+ ### Automatic Layout Setup
67
+
68
+ The installer automatically modifies your `app/views/layouts/application.html.erb` or `application.html.slim` to:
69
+
70
+ ✅ **Add `turbo-turbo--modal` controller** to existing `data-controller` attribute, or create it if missing
71
+ ✅ **Insert flash messages render** after the body tag
72
+ ✅ **Insert modal background render** after flashes
73
+ ✅ **Append TurboTurbo::ModalComponent** at the end of the body
74
+ ✅ **Configure ApplicationController** with TurboTurbo helpers and flash types
75
+ ✅ **Copy TurboTurbo CSS** to `app/assets/stylesheets/turbo_turbo/` (all CSS files)
76
+ ✅ **Import TurboTurbo CSS** into your main stylesheet
77
+
78
+ **Before:**
79
+ ```erb
80
+ <body data-controller="navigation search">
81
+ <%= yield %>
82
+ </body>
83
+ ```
84
+
85
+ **After installation:**
86
+ ```erb
87
+ <body data-controller="navigation search turbo-turbo--modal">
88
+ <%= render 'turbo_turbo/flashes' %>
89
+ <%= render 'turbo_turbo/modal_background' %>
90
+ <%= yield %>
91
+ <%= render TurboTurbo::ModalComponent.new %>
92
+ </body>
93
+ ```
94
+
95
+ ### Custom Layout Installation
96
+
97
+ For custom layouts (admin, dashboard, etc.):
98
+
99
+ ```bash
100
+ # Basic usage - adds all components to admin layout
101
+ rails generate turbo_turbo:layout admin
102
+
103
+ # Skip flash-related components only
104
+ rails generate turbo_turbo:layout admin --skip-flash
105
+
106
+ # Skip modal-related components only
107
+ rails generate turbo_turbo:layout admin --skip-modal
108
+
109
+ # Skip everything - prints warning and exits
110
+ rails generate turbo_turbo:layout admin --skip-flash --skip-modal
111
+ ```
112
+
113
+ ## Core Features
114
+
115
+ ### 1. Convention-Based DSL
116
+
117
+ **Before (traditional approach):**
118
+ ```ruby
119
+ class MessagesController < ApplicationController
120
+ def create
121
+ @message = Message.new(message_params)
122
+ process_turbo_response(@message, @message.save)
123
+ end
124
+
125
+ def update
126
+ @message = Message.find(params[:id])
127
+ process_turbo_response(@message, @message.update(message_params))
128
+ end
129
+
130
+ def destroy
131
+ @message = Message.find(params[:id])
132
+ process_turbo_response(@message, @message.destroy)
133
+ end
134
+
135
+ # ... more CRUD actions
136
+ end
137
+ ```
138
+
139
+ **After (with TurboTurbo DSL):**
140
+ ```ruby
141
+ class MessagesController < ApplicationController
142
+ turbo_actions :create, :update, :destroy, :show, :new, :edit
143
+
144
+ private
145
+
146
+ def message_params
147
+ params.require(:message).permit(:content, :title)
148
+ end
149
+ end
150
+ ```
151
+
152
+ **Supported Actions:**
153
+ - `:create` - Creates new model instance and calls `process_turbo_response`
154
+ - `:update` - Finds and updates model instance
155
+ - `:destroy` - Finds and destroys model instance
156
+ - `:show` - Finds model instance and renders modal
157
+ - `:new` - Creates new model instance and renders modal
158
+ - `:edit` - Finds model instance and renders modal
159
+
160
+ **Requirements:**
161
+ - Controller must follow Rails naming conventions (`MessagesController` → `Message` model)
162
+ - Must define a params method (e.g., `message_params`)
163
+ - Works with any model that responds to `save`, `update`, and `destroy`
164
+
165
+ **Advanced Customization:**
166
+ ```ruby
167
+ class MessagesController < ApplicationController
168
+ turbo_actions :create, :update, :destroy
169
+
170
+ private
171
+
172
+ # Custom params for create vs update
173
+ def create_params
174
+ params.require(:message).permit(:content, :title, :author_id)
175
+ end
176
+
177
+ def message_params
178
+ params.require(:message).permit(:content, :title)
179
+ end
180
+
181
+ # Custom eager loading per action
182
+ def model_instance_for_show
183
+ Message.includes(:author, :comments).find(params[:id])
184
+ end
185
+ end
186
+ ```
187
+
188
+ ### 2. Modal Form Abstraction
189
+
190
+ **Before (what you had to write):**
191
+ ```erb
192
+ <%= simple_form_for service_provider, url: [:admin, service_provider],
193
+ data: {
194
+ turbo_turbo__modal_target: "form",
195
+ action: "turbo:submit-end->turbo-turbo--modal#closeOnSuccess"
196
+ } do |form| %>
197
+ <div class="ModalContent-turbo-turbo">
198
+ <div id="error_message_turbo_turbo"></div>
199
+ <!-- your form fields -->
200
+ </div>
201
+ <% end %>
202
+ ```
203
+
204
+ **After (with TurboTurbo form helper):**
205
+ ```erb
206
+ <%= turbo_form_for(service_provider, url: [:admin, service_provider]) do |form| %>
207
+ <!-- just your form fields -->
208
+ <% end %>
209
+ ```
210
+
211
+ **Form Builder Support:**
212
+ ```erb
213
+ <!-- SimpleForm (default) -->
214
+ <%= turbo_form_for(object) do |form| %>
215
+ <%= form.input :name %>
216
+ <% end %>
217
+
218
+ <!-- Rails form_with -->
219
+ <%= turbo_form_for(object, builder: :form_with) do |form| %>
220
+ <%= form.text_field :name %>
221
+ <% end %>
222
+
223
+ <!-- Rails form_for -->
224
+ <%= turbo_form_for(object, builder: :form_for) do |form| %>
225
+ <%= form.text_field :name %>
226
+ <% end %>
227
+ ```
228
+
229
+ **Customization Options:**
230
+ ```erb
231
+ <%= turbo_form_for(object,
232
+ url: custom_path, # Custom form URL
233
+ data: { custom: "attribute" } # Additional data attributes
234
+ ) do |form| %>
235
+ <!-- fields -->
236
+ <% end %>
237
+ ```
238
+
239
+ **The form helper automatically:**
240
+ - Wraps your form in the proper modal structure
241
+ - Sets up Turbo Stream integration for modal closing
242
+ - Handles error message containers
243
+ - Integrates with all major Rails form builders
244
+
245
+ ### 3. Parameter Sanitization
246
+
247
+ TurboTurbo extends `ActionController::Parameters` with a powerful `sanitize_for_model` method that automatically:
248
+
249
+ - **Converts boolean fields** from strings (\"true\"/\"false\", \"1\"/\"0\") to actual booleans based on your model schema
250
+ - **Trims whitespace** from all string inputs
251
+ - **Cleans arrays** by trimming strings and removing empty values
252
+
253
+ ```ruby
254
+ class MessagesController < ApplicationController
255
+ private
256
+
257
+ def message_params
258
+ params.require(:message)
259
+ .permit(:title, :content, :active, :priority, tags: [])
260
+ .sanitize_for_model(:message)
261
+ end
262
+ end
263
+ ```
264
+
265
+ **Before sanitization:**
266
+ ```ruby
267
+ params = {
268
+ title: " My Message ",
269
+ content: " Hello world ",
270
+ active: "true", # String from HTML form
271
+ priority: "1", # String from HTML form
272
+ tags: [" ruby ", "", " rails ", nil]
273
+ }
274
+ ```
275
+
276
+ **After sanitization:**
277
+ ```ruby
278
+ {
279
+ title: "My Message", # Trimmed
280
+ content: "Hello world", # Trimmed
281
+ active: true, # Converted to boolean
282
+ priority: true, # Converted to boolean (if priority is boolean column)
283
+ tags: ["ruby", "rails"] # Trimmed and cleaned
284
+ }
285
+ ```
286
+
287
+ The method is **model-aware** - it only converts fields that are actually boolean columns in your database schema, leaving other fields (like integer `priority`) unchanged.
288
+
289
+ ### 4. Modal Workflows
290
+
291
+ Create modal forms that integrate seamlessly with Turbo:
292
+
293
+ ```erb
294
+ <!-- Trigger modal with remote content -->
295
+ <%= link_to "New Message",
296
+ new_message_path,
297
+ data: {
298
+ action: "turbo-turbo--modal#setRemoteSource",
299
+ url: new_message_path,
300
+ title: "Create New Message",
301
+ subtitle: "Fill out the form below"
302
+ },
303
+ class: "btn btn-primary" %>
304
+
305
+ <!-- Modal will automatically open and load the form -->
306
+ ```
307
+
308
+ For custom behavior, you can still use the helper methods directly:
309
+
310
+ ```ruby
311
+ class MessagesController < ApplicationController
312
+ def create
313
+ message = Message.new(message_params)
314
+ process_turbo_response(message, message.save)
315
+ end
316
+
317
+ def update
318
+ message = Message.find(params[:id])
319
+ process_turbo_response(message, message.update(message_params))
320
+ end
321
+
322
+ def destroy
323
+ message = Message.find(params[:id])
324
+ process_turbo_response(message, message.destroy)
325
+ end
326
+ end
327
+ ```
328
+
329
+ ## ViewComponents
330
+
331
+ TurboTurbo includes several pre-built ViewComponents that work directly from the gem, all properly namespaced under the `TurboTurbo` module:
332
+
333
+ ### Modal Components
334
+
335
+ **TurboTurbo::ModalComponent**
336
+ - Main modal wrapper component
337
+ - Options: `show_close: true/false` - Controls visibility of close button
338
+ - Usage: `<%= render TurboTurbo::ModalComponent.new(show_close: true) %>`
339
+
340
+ **TurboTurbo::ModalFooterComponent**
341
+ - Modal footer with cancel button and content area
342
+ - Options: `skip_close: true/false`, `close_label: "Custom Cancel Text"`
343
+ - Usage: `<%= render TurboTurbo::ModalFooterComponent.new(close_label: "Close") { content } %>`
344
+
345
+ ### Alert Components
346
+
347
+ TurboTurbo includes pre-styled alert components, all namespaced under `TurboTurbo::Alerts`:
348
+
349
+ - `TurboTurbo::Alerts::SuccessComponent`
350
+ - `TurboTurbo::Alerts::ErrorComponent`
351
+ - `TurboTurbo::Alerts::WarningComponent`
352
+ - `TurboTurbo::Alerts::InfoComponent`
353
+ - `TurboTurbo::Alerts::AlertComponent` (base component)
354
+
355
+ All alert components support:
356
+ - Custom headers and messages
357
+ - Array of messages for lists
358
+ - Dismissible functionality
359
+ - Consistent styling
360
+
361
+ Example usage:
362
+ ```erb
363
+ <%= render TurboTurbo::Alerts::SuccessComponent.new({ header: "Success!", message: "Operation completed" }) %>
364
+ <%= render TurboTurbo::Alerts::ErrorComponent.new({ message: ["Error 1", "Error 2"] }) %>
365
+ ```
366
+
367
+ ## Customization
368
+
369
+ ### ViewComponents Customization
370
+
371
+ To customize ViewComponents, copy them to your application:
372
+
373
+ ```bash
374
+ rails generate turbo_turbo:views
375
+ ```
376
+
377
+ This copies all ViewComponent classes and templates to your `app/components/turbo_turbo/` directory for customization. Components in your app directory will override the gem versions.
378
+
379
+ ### I18n Flash Messages
380
+
381
+ TurboTurbo supports I18n for flash messages. Add to your locale files:
382
+
383
+ ```yaml
384
+ # config/locales/en.yml
385
+ en:
386
+ turbo_turbo:
387
+ flash:
388
+ success:
389
+ create: "%{resource} created successfully!"
390
+ destroy: "%{resource} deleted!"
391
+ archive: "%{resource} archived!"
392
+ # Add custom actions...
393
+ error:
394
+ default: "We encountered an error trying to %{action} %{resource}."
395
+ ```
396
+
397
+ ### Action Groups Configuration
398
+
399
+ Customize which actions trigger different Turbo Stream behaviors:
400
+
401
+ ```ruby
402
+ # In an initializer or controller
403
+ TurboTurbo::ControllerHelpers.turbo_response_actions[:remove] << "archive"
404
+ TurboTurbo::ControllerHelpers.error_response_actions[:validation_errors] << "upload"
405
+ ```
406
+
407
+ Available action groups:
408
+ - **turbo_response_actions**: `:prepend`, `:replace`, `:remove`
409
+ - **error_response_actions**: `:validation_errors`, `:flash_errors`
410
+
411
+ ## Architecture
412
+
413
+ ### Namespacing
414
+
415
+ TurboTurbo uses namespaced controllers and CSS to prevent conflicts:
416
+
417
+ - **Stimulus Controllers**: `turbo-turbo--modal`, `turbo-turbo--flash` (installed to `app/javascript/controllers/turbo_turbo/`)
418
+ - **CSS Files**: Served from gem at `turbo_turbo/*.css`
419
+ - **ViewComponents**: All namespaced under `TurboTurbo::` and `TurboTurbo::Alerts::`
420
+ - **Layout Partials**: Installed to `app/views/turbo_turbo/`
421
+ - **No conflicts** with your existing controllers or styles
422
+
423
+ ### Component Architecture
424
+
425
+ - **Modal Controller**: Added to body tag, manages global modal behavior
426
+ - **Flash Controllers**: Added to individual flash messages for auto-fade and dismiss
427
+ - **ViewComponents**: Served directly from gem, optionally copyable for customization
428
+ - **CSS**: Served from gem asset pipeline, automatically imported
429
+
430
+ ## Development
431
+
432
+ After checking out the repo, run:
433
+
434
+ ```bash
435
+ bundle install
436
+ rspec
437
+ ```
438
+
439
+ ## Test Helpers
440
+
441
+ TurboTurbo includes test helpers for system/integration testing:
442
+
443
+ ```ruby
444
+ # In your system specs
445
+ RSpec.describe "Modal functionality", type: :system, js: true do
446
+ include TurboTurbo::TestHelpers
447
+
448
+ it "creates a record via modal" do
449
+ visit some_path
450
+
451
+ click_new_button("user") # Clicks #new_user button
452
+ fill_in "Name", with: "John"
453
+ submit_modal # Submits TurboTurbo modal form
454
+
455
+ expect(page).to have_content("John")
456
+ expect(modal_closed?).to be true
457
+ end
458
+
459
+ it "edits a record via modal" do
460
+ user = create(:user)
461
+ visit users_path
462
+
463
+ click_on_row_link(user, "edit") # Clicks edit button in user's table row
464
+ fill_in "Name", with: "Jane"
465
+ submit_modal
466
+
467
+ user.reload
468
+ expect(user.name).to eq("Jane")
469
+ end
470
+ end
471
+ ```
472
+
473
+ **Available Helpers:**
474
+ - `submit_modal` - Submit a TurboTurbo modal form
475
+ - `click_new_button(resource)` - Click new button for resource type
476
+ - `click_on_row_link(resource, action, confirm: false)` - Click action link in table row
477
+ - `close_modal` - Close modal and verify it's closed
478
+ - `modal_open?` - Check if modal is currently open
479
+ - `modal_closed?` - Check if modal is currently closed
480
+ - `table_row(resource)` - Get DOM selector for resource's table row
481
+
482
+ To install this gem onto your local machine:
483
+
484
+ ```bash
485
+ bundle exec rake install
486
+ ```
487
+
488
+ ## Contributing
489
+
490
+ 1. Fork it
491
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
492
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
493
+ 4. Push to the branch (`git push origin my-new-feature`)
494
+ 5. Create new Pull Request
495
+
496
+ ## License
497
+
498
+ The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
data/Rakefile ADDED
@@ -0,0 +1,12 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "bundler/gem_tasks"
4
+ require "rspec/core/rake_task"
5
+
6
+ RSpec::Core::RakeTask.new(:spec)
7
+
8
+ require "rubocop/rake_task"
9
+
10
+ RuboCop::RakeTask.new
11
+
12
+ task default: %i[spec rubocop]