better_page 2.0.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 (99) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +62 -0
  3. data/MIT-LICENSE +20 -0
  4. data/README.md +357 -0
  5. data/Rakefile +3 -0
  6. data/docs/00-README.md +17 -0
  7. data/docs/01-getting-started.md +137 -0
  8. data/docs/02-component-registry.md +192 -0
  9. data/docs/03-base-pages.md +238 -0
  10. data/docs/04-schema-validation.md +180 -0
  11. data/docs/05-turbo-support.md +220 -0
  12. data/docs/06-compliance-analyzer.md +147 -0
  13. data/docs/07-configuration.md +157 -0
  14. data/guide/00-README.md +32 -0
  15. data/guide/01-quick-start.md +148 -0
  16. data/guide/02-building-index-page.md +258 -0
  17. data/guide/03-building-show-page.md +266 -0
  18. data/guide/04-building-form-page.md +309 -0
  19. data/guide/05-custom-pages.md +325 -0
  20. data/guide/06-best-practices.md +311 -0
  21. data/lib/better_page/base_page.rb +161 -0
  22. data/lib/better_page/compliance/analyzer.rb +409 -0
  23. data/lib/better_page/component_registry.rb +393 -0
  24. data/lib/better_page/config.rb +165 -0
  25. data/lib/better_page/configuration.rb +153 -0
  26. data/lib/better_page/custom_base_page.rb +85 -0
  27. data/lib/better_page/default_components.rb +200 -0
  28. data/lib/better_page/form_base_page.rb +170 -0
  29. data/lib/better_page/index_base_page.rb +69 -0
  30. data/lib/better_page/railtie.rb +34 -0
  31. data/lib/better_page/show_base_page.rb +120 -0
  32. data/lib/better_page/validation_error.rb +7 -0
  33. data/lib/better_page/version.rb +3 -0
  34. data/lib/better_page.rb +80 -0
  35. data/lib/generators/better_page/component_generator.rb +131 -0
  36. data/lib/generators/better_page/install_generator.rb +160 -0
  37. data/lib/generators/better_page/page_generator.rb +101 -0
  38. data/lib/generators/better_page/sync_generator.rb +109 -0
  39. data/lib/generators/better_page/templates/application_page.rb.tt +12 -0
  40. data/lib/generators/better_page/templates/better_page_initializer.rb.tt +53 -0
  41. data/lib/generators/better_page/templates/custom_base_page.rb.tt +83 -0
  42. data/lib/generators/better_page/templates/custom_page.rb.tt +31 -0
  43. data/lib/generators/better_page/templates/edit_page.rb.tt +46 -0
  44. data/lib/generators/better_page/templates/form_base_page.rb.tt +126 -0
  45. data/lib/generators/better_page/templates/index_base_page.rb.tt +65 -0
  46. data/lib/generators/better_page/templates/index_page.rb.tt +56 -0
  47. data/lib/generators/better_page/templates/javascript/controllers/app_nav_controller.js +57 -0
  48. data/lib/generators/better_page/templates/javascript/controllers/drawer_controller.js +99 -0
  49. data/lib/generators/better_page/templates/javascript/controllers/dropdown_controller.js +60 -0
  50. data/lib/generators/better_page/templates/javascript/controllers/index.js +36 -0
  51. data/lib/generators/better_page/templates/javascript/controllers/modal_controller.js +70 -0
  52. data/lib/generators/better_page/templates/javascript/controllers/sidebar_controller.js +152 -0
  53. data/lib/generators/better_page/templates/javascript/controllers/table_controller.js +60 -0
  54. data/lib/generators/better_page/templates/javascript/controllers/tabs_controller.js +89 -0
  55. data/lib/generators/better_page/templates/new_page.rb.tt +46 -0
  56. data/lib/generators/better_page/templates/show_base_page.rb.tt +117 -0
  57. data/lib/generators/better_page/templates/show_page.rb.tt +45 -0
  58. data/lib/generators/better_page/templates/view_components/application_view_component.rb.tt +7 -0
  59. data/lib/generators/better_page/templates/view_components/custom_view_component.html.erb.tt +21 -0
  60. data/lib/generators/better_page/templates/view_components/custom_view_component.rb.tt +21 -0
  61. data/lib/generators/better_page/templates/view_components/form_view_component.html.erb.tt +25 -0
  62. data/lib/generators/better_page/templates/view_components/form_view_component.rb.tt +23 -0
  63. data/lib/generators/better_page/templates/view_components/index_view_component.html.erb.tt +33 -0
  64. data/lib/generators/better_page/templates/view_components/index_view_component.rb.tt +29 -0
  65. data/lib/generators/better_page/templates/view_components/show_view_component.html.erb.tt +29 -0
  66. data/lib/generators/better_page/templates/view_components/show_view_component.rb.tt +25 -0
  67. data/lib/generators/better_page/templates/view_components/ui/alerts_component.html.erb.tt +47 -0
  68. data/lib/generators/better_page/templates/view_components/ui/alerts_component.rb.tt +47 -0
  69. data/lib/generators/better_page/templates/view_components/ui/content_section_component.html.erb.tt +42 -0
  70. data/lib/generators/better_page/templates/view_components/ui/content_section_component.rb.tt +34 -0
  71. data/lib/generators/better_page/templates/view_components/ui/drawer_component.html.erb.tt +73 -0
  72. data/lib/generators/better_page/templates/view_components/ui/drawer_component.rb.tt +78 -0
  73. data/lib/generators/better_page/templates/view_components/ui/errors_component.html.erb.tt +23 -0
  74. data/lib/generators/better_page/templates/view_components/ui/errors_component.rb.tt +18 -0
  75. data/lib/generators/better_page/templates/view_components/ui/field_component.html.erb.tt +65 -0
  76. data/lib/generators/better_page/templates/view_components/ui/field_component.rb.tt +91 -0
  77. data/lib/generators/better_page/templates/view_components/ui/footer_component.html.erb.tt +33 -0
  78. data/lib/generators/better_page/templates/view_components/ui/footer_component.rb.tt +32 -0
  79. data/lib/generators/better_page/templates/view_components/ui/header_component.html.erb.tt +55 -0
  80. data/lib/generators/better_page/templates/view_components/ui/header_component.rb.tt +39 -0
  81. data/lib/generators/better_page/templates/view_components/ui/modal_component.html.erb.tt +70 -0
  82. data/lib/generators/better_page/templates/view_components/ui/modal_component.rb.tt +54 -0
  83. data/lib/generators/better_page/templates/view_components/ui/overview_component.html.erb.tt +22 -0
  84. data/lib/generators/better_page/templates/view_components/ui/overview_component.rb.tt +71 -0
  85. data/lib/generators/better_page/templates/view_components/ui/pagination_component.html.erb.tt +63 -0
  86. data/lib/generators/better_page/templates/view_components/ui/pagination_component.rb.tt +69 -0
  87. data/lib/generators/better_page/templates/view_components/ui/panel_component.html.erb.tt +31 -0
  88. data/lib/generators/better_page/templates/view_components/ui/panel_component.rb.tt +23 -0
  89. data/lib/generators/better_page/templates/view_components/ui/statistics_component.html.erb.tt +33 -0
  90. data/lib/generators/better_page/templates/view_components/ui/statistics_component.rb.tt +51 -0
  91. data/lib/generators/better_page/templates/view_components/ui/table_component.html.erb.tt +112 -0
  92. data/lib/generators/better_page/templates/view_components/ui/table_component.rb.tt +88 -0
  93. data/lib/generators/better_page/templates/view_components/ui/tabs_component.html.erb.tt +52 -0
  94. data/lib/generators/better_page/templates/view_components/ui/tabs_component.rb.tt +76 -0
  95. data/lib/generators/better_page/templates/view_components/ui/widget_component.html.erb.tt +72 -0
  96. data/lib/generators/better_page/templates/view_components/ui/widget_component.rb.tt +34 -0
  97. data/lib/tasks/better_page.rake +70 -0
  98. data/lib/tasks/better_page_tasks.rake +4 -0
  99. metadata +188 -0
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: d9f95ed70f6d49b9da753f5e9475fb21e8bba223489a9199739e4dafd652fba0
4
+ data.tar.gz: acf2c99e9cef1c9e7075480c8f2098fe222751e0036acf301514a01cf7feb34e
5
+ SHA512:
6
+ metadata.gz: fe2ad9fefed3af83c594a31b3a721dc090a59ba006cd22ec1d6d17f6614ab0063d71e8c606564abb201d945c8abb78dceba9162e2eb83b4679f0397cea19ef8d
7
+ data.tar.gz: b5335b3e3eb1623a6b151f190b91f10364568455f0a9de2d601ba0de890566de3e6074e15594d349f47e05d45c9db42cb670b4753e010de13620bafb6eef43b5
data/CHANGELOG.md ADDED
@@ -0,0 +1,62 @@
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.1.0/),
6
+ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
+
8
+ ## [Unreleased]
9
+
10
+ ## [2.0.0] - 2025-12-01
11
+
12
+ ### Added
13
+
14
+ - **ViewComponents Library** - Complete set of reusable UI components
15
+ - **Layout Components**
16
+ - `AppNavComponent` - Application navigation bar with user menu, notifications, mobile support
17
+ - `SidebarComponent` - Collapsible sidebar with groups, items, badges, persist state
18
+ - `DrawerComponent` - Slide-in drawer panels (right/left position)
19
+ - **UI Components**
20
+ - `TableComponent` - Data tables with sorting, pagination footer, row actions, selection
21
+ - `DropdownController` - Stimulus controller for dropdown menus
22
+
23
+ - **Stimulus Controllers Package** (`better-page-stimulus`)
24
+ - Published on npm for easy JavaScript integration
25
+ - Controllers: `dropdown`, `table`, `drawer`, `sidebar`, `app-nav`
26
+ - Works with Rails importmap or npm/yarn
27
+
28
+ - **ApplicationViewComponent** - Base class for all ViewComponents
29
+ - All ViewComponents now inherit from `BetterPage::ApplicationViewComponent` instead of `ViewComponent::Base`
30
+ - Includes `Turbo::FramesHelper` for Turbo frame/stream support in component templates
31
+ - Generated automatically by `better_page:install` generator
32
+
33
+ - **Generator Updates**
34
+ - Installs Stimulus controllers via generator
35
+ - Creates ViewComponent previews for development
36
+
37
+ ### Changed
38
+
39
+ - Minimum Rails version updated to 8.1.1
40
+ - ViewComponent dependency updated to >= 3.0
41
+
42
+ ## [0.1.0] - 2025-01-28
43
+
44
+ ### Added
45
+
46
+ - **Component Registration DSL** - `register_component` method for declaring UI components with schema validation
47
+ - **Schema Validation** - Integration with dry-schema for automatic component data validation in development
48
+ - **Base Page Classes**
49
+ - `BetterPage::BasePage` - Foundation class with helper methods
50
+ - `BetterPage::IndexBasePage` - For list/index views with `header` and `table` components
51
+ - `BetterPage::ShowBasePage` - For detail views with `header` component
52
+ - `BetterPage::FormBasePage` - For new/edit forms with `header` and `panels` components
53
+ - `BetterPage::CustomBasePage` - For dashboards and custom views with `content` component
54
+ - **Compliance Analyzer** - Tool to verify pages follow architecture rules (no database queries, no business logic)
55
+ - **Rails Generators**
56
+ - `better_page:install` - Sets up `app/pages/` directory and `ApplicationPage` base class
57
+ - `better_page:page` - Generates page classes for specified actions
58
+ - **Documentation**
59
+ - API reference in `docs/` folder
60
+ - Step-by-step guides in `guide/` folder
61
+ - **Rake Tasks**
62
+ - `better_page:compliance:analyze` - Analyze all pages for compliance issues
data/MIT-LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright alessiobussolari
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,357 @@
1
+ # BetterPage
2
+
3
+ A Rails engine that provides a structured Page Object pattern for building UI configurations. Pages are presentation-layer classes that configure UI without business logic, making your views cleaner and more maintainable.
4
+
5
+ ## Features
6
+
7
+ - **Component Registration DSL** - Declare UI components with schema validation using dry-schema
8
+ - **Multiple Page Types** - Index, Show, Form, and Custom page base classes
9
+ - **Schema Validation** - Automatic validation of component data in development
10
+ - **Turbo Support** - Built-in support for Turbo Frames and Turbo Streams
11
+ - **Compliance Analyzer** - Ensure pages follow architecture rules
12
+ - **Rails Generators** - Quickly scaffold new pages
13
+
14
+ ## Installation
15
+
16
+ Add to your Gemfile:
17
+
18
+ ```ruby
19
+ gem "better_page"
20
+ ```
21
+
22
+ Run:
23
+
24
+ ```bash
25
+ bundle install
26
+ rails generate better_page:install
27
+ ```
28
+
29
+ ## Quick Start
30
+
31
+ ### Generate a Page
32
+
33
+ ```bash
34
+ rails generate better_page:page admin/users index show new edit
35
+ ```
36
+
37
+ ### Define a Page
38
+
39
+ ```ruby
40
+ # app/pages/admin/users/index_page.rb
41
+ class Admin::Users::IndexPage < IndexBasePage
42
+ def initialize(users, metadata = {})
43
+ @users = users
44
+ @user = metadata[:user]
45
+ super(users, metadata)
46
+ end
47
+
48
+ private
49
+
50
+ def header
51
+ {
52
+ title: "Users",
53
+ breadcrumbs: [{ label: "Admin", path: admin_root_path }],
54
+ actions: [{ label: "New User", path: new_admin_user_path, icon: "plus" }]
55
+ }
56
+ end
57
+
58
+ def table
59
+ {
60
+ items: @users,
61
+ columns: [
62
+ { key: :name, label: "Name", type: :link, path: ->(u) { admin_user_path(u) } },
63
+ { key: :email, label: "Email", type: :text }
64
+ ],
65
+ empty_state: { icon: "users", title: "No users", message: "Create your first user" }
66
+ }
67
+ end
68
+ end
69
+ ```
70
+
71
+ ### Use in Controller
72
+
73
+ ```ruby
74
+ class Admin::UsersController < ApplicationController
75
+ def index
76
+ users = User.all.order(:name)
77
+ @config = Admin::Users::IndexPage.new(users, user: current_user).index
78
+ # @config is a BetterPage::Config object
79
+ end
80
+ end
81
+ ```
82
+
83
+ ### Access in View
84
+
85
+ ```erb
86
+ <%# Direct method access %>
87
+ <h1><%= @config.header[:title] %></h1>
88
+
89
+ <%# Hash-like access (backward compatible) %>
90
+ <h1><%= @config[:header][:title] %></h1>
91
+
92
+ <% @config.table[:items].each do |user| %>
93
+ <%= user.name %>
94
+ <% end %>
95
+ ```
96
+
97
+ ## Page Types
98
+
99
+ | Type | Base Class | Required Components | Use Case |
100
+ |------|-----------|---------------------|----------|
101
+ | Index | `IndexBasePage` | `header`, `table` | List views |
102
+ | Show | `ShowBasePage` | `header` | Detail views |
103
+ | Form | `FormBasePage` | `header`, `panels` | New/Edit forms |
104
+ | Custom | `CustomBasePage` | `content` | Dashboards, reports |
105
+
106
+ ## BetterPage::Config
107
+
108
+ When you call a page action (e.g., `page.index`, `page.show`), it returns a `BetterPage::Config` object. This follows the same pattern as `BetterService::Result` and `BetterController::Result`.
109
+
110
+ ### Structure
111
+
112
+ ```ruby
113
+ config = Admin::Users::IndexPage.new(users, user: current_user).index
114
+
115
+ config.components # => Hash of all component configurations
116
+ config.meta # => { page_type: :index, klass: IndexViewComponent }
117
+ ```
118
+
119
+ ### Component Access
120
+
121
+ ```ruby
122
+ # Direct method access
123
+ config.header # => { title: "Users", breadcrumbs: [...] }
124
+ config.table # => { items: [...], columns: [...] }
125
+ config.statistics # => [{ label: "Total", value: 100 }]
126
+
127
+ # Hash-like access (backward compatible)
128
+ config[:header][:title] # => "Users"
129
+ config.dig(:header, :breadcrumbs, 0, :label) # => "Admin"
130
+ ```
131
+
132
+ ### Meta Access
133
+
134
+ ```ruby
135
+ config.page_type # => :index, :show, :form, :custom
136
+ config.klass # => ViewComponent class for rendering
137
+ ```
138
+
139
+ ### Destructuring
140
+
141
+ ```ruby
142
+ # Supports destructuring like BetterService::Result
143
+ components, meta = config
144
+
145
+ components[:header][:title] # => "Users"
146
+ meta[:page_type] # => :index
147
+ ```
148
+
149
+ ### Component Helpers
150
+
151
+ ```ruby
152
+ # Check if component is present (not nil/empty)
153
+ config.component?(:header) # => true
154
+ config.component?(:pagination) # => false if empty
155
+
156
+ # List all component names
157
+ config.component_names # => [:header, :table, :statistics, ...]
158
+
159
+ # Get only present (non-empty) components
160
+ config.present_components # => { header: {...}, table: {...} }
161
+
162
+ # Iterate over components
163
+ config.each_component do |name, value|
164
+ puts "#{name}: #{value}"
165
+ end
166
+ ```
167
+
168
+ ### Hash-like Interface
169
+
170
+ For backward compatibility, Config supports full hash-like access:
171
+
172
+ ```ruby
173
+ config[:header] # => { title: "Users", ... }
174
+ config.key?(:header) # => true
175
+ config.dig(:table, :items, 0) # => first item
176
+ ```
177
+
178
+ ## Configuration
179
+
180
+ BetterPage uses a hybrid configuration system. Default components are registered by the gem, and you can customize them in your initializer:
181
+
182
+ ```ruby
183
+ # config/initializers/better_page.rb
184
+ BetterPage.configure do |config|
185
+ # Add a custom global component
186
+ config.register_component :sidebar, default: { enabled: false }
187
+ config.allow_components :index, :sidebar
188
+
189
+ # Override a default component
190
+ config.register_component :pagination, default: { enabled: true, per_page: 25 }
191
+ end
192
+ ```
193
+
194
+ ### Check for Updates
195
+
196
+ When upgrading BetterPage, check for new components:
197
+
198
+ ```bash
199
+ rails generate better_page:sync
200
+ ```
201
+
202
+ ## Component Registration
203
+
204
+ Components can be registered at three levels:
205
+
206
+ ### 1. Global Configuration (Initializer)
207
+
208
+ ```ruby
209
+ # config/initializers/better_page.rb
210
+ BetterPage.configure do |config|
211
+ config.register_component :sidebar, default: { enabled: false } do
212
+ optional(:enabled).filled(:bool)
213
+ optional(:items).array(:hash)
214
+ end
215
+ config.allow_components :index, :sidebar
216
+ end
217
+ ```
218
+
219
+ ### 2. Base Page Classes (Local)
220
+
221
+ ```ruby
222
+ # app/pages/index_base_page.rb
223
+ class IndexBasePage < ApplicationPage
224
+ page_type :index
225
+
226
+ # Add component only for index pages
227
+ register_component :quick_filters, default: []
228
+ end
229
+ ```
230
+
231
+ ### 3. Individual Pages
232
+
233
+ ```ruby
234
+ # app/pages/admin/users/index_page.rb
235
+ class Admin::Users::IndexPage < IndexBasePage
236
+ # Component only for this specific page
237
+ register_component :user_stats, default: nil
238
+
239
+ def user_stats
240
+ { active_count: @users.active.count }
241
+ end
242
+ end
243
+ ```
244
+
245
+ ## Architecture Rules
246
+
247
+ Pages must follow these rules (enforced by compliance analyzer):
248
+
249
+ 1. **No database queries** - Data passed via constructor
250
+ 2. **No business logic** - UI configuration only
251
+ 3. **No service layer access** - No service objects
252
+ 4. **Hash-only structures** - No OpenStruct/Struct
253
+
254
+ Run compliance check:
255
+
256
+ ```bash
257
+ rake better_page:compliance:analyze
258
+ ```
259
+
260
+ ## ViewComponent Architecture
261
+
262
+ All UI components inherit from `ApplicationViewComponent`:
263
+
264
+ ```
265
+ ViewComponent::Base
266
+
267
+
268
+ BetterPage::ApplicationViewComponent (includes Turbo::FramesHelper)
269
+
270
+ ├── IndexViewComponent
271
+ ├── ShowViewComponent
272
+ ├── FormViewComponent
273
+ ├── CustomViewComponent
274
+ └── Ui::* (Header, Table, Drawer, Modal, etc.)
275
+ ```
276
+
277
+ The `ApplicationViewComponent` base class includes `Turbo::FramesHelper`, making Turbo helpers available in all component templates.
278
+
279
+ ## Turbo Support
280
+
281
+ BetterPage provides built-in support for Turbo Frames and Turbo Streams.
282
+
283
+ ### Turbo Frame (Single Component)
284
+
285
+ ```ruby
286
+ # Controller - lazy load table
287
+ def table
288
+ component = Products::IndexPage.new(@products, current_user).frame_index(:table)
289
+ render component[:klass].new(**component[:config])
290
+ end
291
+ ```
292
+
293
+ ### Turbo Stream (Multiple Components)
294
+
295
+ ```ruby
296
+ # Controller - update multiple components
297
+ def refresh
298
+ components = Products::IndexPage.new(@products, current_user).stream_index(:table, :statistics)
299
+
300
+ render turbo_stream: components.map { |c|
301
+ turbo_stream.replace(c[:target], c[:klass].new(**c[:config]))
302
+ }
303
+ end
304
+ ```
305
+
306
+ Dynamic methods are generated based on your page's main action: `frame_index`, `stream_index`, `frame_show`, `stream_show`, etc.
307
+
308
+ ## Lookbook (Component Preview)
309
+
310
+ BetterPage includes [Lookbook](https://lookbook.build/) for previewing ViewComponents in development.
311
+
312
+ ```bash
313
+ cd spec/rails_app && bin/rails server -p 3099
314
+ ```
315
+
316
+ Open http://localhost:3099/lookbook to browse component previews.
317
+
318
+ ## Documentation
319
+
320
+ - **[docs/](docs/)** - API reference and technical documentation
321
+ - **[guide/](guide/)** - Step-by-step guides and tutorials
322
+
323
+ ### Quick Links
324
+
325
+ - [Getting Started](docs/01-getting-started.md)
326
+ - [Component Registry](docs/02-component-registry.md)
327
+ - [Base Pages Reference](docs/03-base-pages.md)
328
+ - [Schema Validation](docs/04-schema-validation.md)
329
+ - [Turbo Support](docs/05-turbo-support.md)
330
+ - [Compliance Analyzer](docs/06-compliance-analyzer.md)
331
+ - [Configuration](docs/07-configuration.md)
332
+ - [Quick Start Guide](guide/01-quick-start.md)
333
+ - [Building Index Pages](guide/02-building-index-page.md)
334
+ - [Building Form Pages](guide/04-building-form-page.md)
335
+ - [Best Practices](guide/06-best-practices.md)
336
+
337
+ ## Requirements
338
+
339
+ - Ruby >= 3.0
340
+ - Rails >= 8.1
341
+ - dry-schema ~> 1.13
342
+
343
+ ## Contributing
344
+
345
+ 1. Fork the repository
346
+ 2. Create your feature branch (`git checkout -b feature/my-feature`)
347
+ 3. Commit your changes (`git commit -am 'Add my feature'`)
348
+ 4. Push to the branch (`git push origin feature/my-feature`)
349
+ 5. Create a Pull Request
350
+
351
+ ## License
352
+
353
+ The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
354
+
355
+ ## Author
356
+
357
+ Alessio Bussolari <alessio.bussolari@pandev.it>
data/Rakefile ADDED
@@ -0,0 +1,3 @@
1
+ require "bundler/setup"
2
+
3
+ require "bundler/gem_tasks"
data/docs/00-README.md ADDED
@@ -0,0 +1,17 @@
1
+ # BetterPage Documentation
2
+
3
+ API reference and technical documentation for BetterPage.
4
+
5
+ ## Contents
6
+
7
+ - [01-getting-started.md](01-getting-started.md) - Installation and basic setup
8
+ - [02-component-registry.md](02-component-registry.md) - Component registration DSL
9
+ - [03-base-pages.md](03-base-pages.md) - Base page classes reference
10
+ - [04-schema-validation.md](04-schema-validation.md) - dry-schema validation
11
+ - [05-turbo-support.md](05-turbo-support.md) - Turbo Frame and Stream support
12
+ - [06-compliance-analyzer.md](06-compliance-analyzer.md) - Compliance analyzer
13
+
14
+ ## Quick Links
15
+
16
+ - [Guide](../guide/) - Step-by-step tutorials
17
+ - [Context7](../context7/) - LLM-optimized snippets
@@ -0,0 +1,137 @@
1
+ # Getting Started
2
+
3
+ ### Install BetterPage Gem
4
+
5
+ Add BetterPage to your Rails application Gemfile.
6
+
7
+ ```ruby
8
+ gem "better_page"
9
+ ```
10
+
11
+ --------------------------------
12
+
13
+ ### Run Bundle Install
14
+
15
+ Install the gem and its dependencies.
16
+
17
+ ```bash
18
+ bundle install
19
+ ```
20
+
21
+ --------------------------------
22
+
23
+ ### Run Install Generator
24
+
25
+ Create the page infrastructure in your application.
26
+
27
+ ```bash
28
+ rails generate better_page:install
29
+ ```
30
+
31
+ This creates:
32
+ - `app/pages/application_page.rb` - Base page class
33
+ - `app/pages/index_base_page.rb` - Base for index pages
34
+ - `app/pages/show_base_page.rb` - Base for show pages
35
+ - `app/pages/form_base_page.rb` - Base for form pages
36
+ - `app/pages/custom_base_page.rb` - Base for custom pages
37
+ - `config/initializers/better_page.rb` - Configuration file
38
+ - `app/components/better_page/application_view_component.rb` - Base ViewComponent class (includes `Turbo::FramesHelper`)
39
+ - `app/components/better_page/` - ViewComponents for rendering pages
40
+
41
+ --------------------------------
42
+
43
+ ### Generate Page Classes
44
+
45
+ Scaffold page classes for a resource with multiple actions.
46
+
47
+ ```bash
48
+ rails generate better_page:page admin/users index show new edit
49
+ ```
50
+
51
+ This creates:
52
+ - `app/pages/admin/users/index_page.rb`
53
+ - `app/pages/admin/users/show_page.rb`
54
+ - `app/pages/admin/users/new_page.rb`
55
+ - `app/pages/admin/users/edit_page.rb`
56
+
57
+ --------------------------------
58
+
59
+ ### Basic Page Structure
60
+
61
+ Pages are presentation-layer classes that configure UI without business logic.
62
+
63
+ ```ruby
64
+ class Admin::Users::IndexPage < IndexBasePage
65
+ def initialize(users, metadata = {})
66
+ @users = users
67
+ @user = metadata[:user]
68
+ super(users, metadata)
69
+ end
70
+
71
+ private
72
+
73
+ def header
74
+ {
75
+ title: "Users",
76
+ breadcrumbs: [{ label: "Home", path: "/" }],
77
+ actions: [{ label: "New User", path: new_admin_user_path, icon: "plus" }]
78
+ }
79
+ end
80
+
81
+ def table
82
+ {
83
+ items: @users,
84
+ columns: [
85
+ { key: :name, label: "Name", type: :link },
86
+ { key: :email, label: "Email", type: :text }
87
+ ],
88
+ empty_state: { icon: "users", title: "No users", message: "No users found" }
89
+ }
90
+ end
91
+ end
92
+ ```
93
+
94
+ --------------------------------
95
+
96
+ ### Use Page in Controller
97
+
98
+ Instantiate the page and call the main action method.
99
+
100
+ ```ruby
101
+ class Admin::UsersController < ApplicationController
102
+ def index
103
+ users = User.all
104
+ @page = Admin::Users::IndexPage.new(users, user: current_user).index
105
+ end
106
+
107
+ def show
108
+ user = User.find(params[:id])
109
+ @page = Admin::Users::ShowPage.new(user, user: current_user).show
110
+ end
111
+ end
112
+ ```
113
+
114
+ --------------------------------
115
+
116
+ ### Access Page Data in View
117
+
118
+ Use the @page hash to render UI components.
119
+
120
+ ```erb
121
+ <h1><%= @page[:header][:title] %></h1>
122
+
123
+ <% @page[:table][:items].each do |user| %>
124
+ <%= user.name %>
125
+ <% end %>
126
+ ```
127
+
128
+ --------------------------------
129
+
130
+ ### Architecture Rules
131
+
132
+ Pages must follow these rules:
133
+
134
+ 1. **No database queries** - Data passed via constructor
135
+ 2. **No business logic** - UI configuration only
136
+ 3. **No service layer access** - No service objects
137
+ 4. **Hash-only structures** - No OpenStruct/Struct