ruby_ui_scaffold 0.1.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.
- checksums.yaml +7 -0
- data/CHANGELOG.md +343 -0
- data/LICENSE.txt +21 -0
- data/README.md +530 -0
- data/lib/generators/ruby_ui_scaffold/install/install_generator.rb +188 -0
- data/lib/generators/ruby_ui_scaffold/ruby_ui_scaffold_generator.rb +119 -0
- data/lib/generators/ruby_ui_scaffold/scaffold/scaffold_generator.rb +252 -0
- data/lib/generators/ruby_ui_scaffold/scaffold/templates/edit.rb.tt +34 -0
- data/lib/generators/ruby_ui_scaffold/scaffold/templates/form.rb.tt +50 -0
- data/lib/generators/ruby_ui_scaffold/scaffold/templates/index.rb.tt +108 -0
- data/lib/generators/ruby_ui_scaffold/scaffold/templates/index_data_table.rb.tt +187 -0
- data/lib/generators/ruby_ui_scaffold/scaffold/templates/new.rb.tt +34 -0
- data/lib/generators/ruby_ui_scaffold/scaffold/templates/show.rb.tt +55 -0
- data/lib/generators/ruby_ui_scaffold/scaffold_controller/scaffold_controller_generator.rb +43 -0
- data/lib/generators/ruby_ui_scaffold/scaffold_controller/templates/controller.rb.tt +75 -0
- data/lib/generators/ruby_ui_scaffold/scaffold_controller/templates/controller_data_table.rb.tt +110 -0
- data/lib/rails/commands/ruby_ui_scaffold/seed_command.rb +62 -0
- data/lib/ruby_ui_scaffold/attribute_helpers.rb +38 -0
- data/lib/ruby_ui_scaffold/component_installer.rb +24 -0
- data/lib/ruby_ui_scaffold/component_resolver.rb +74 -0
- data/lib/ruby_ui_scaffold/field_type_mapper.rb +164 -0
- data/lib/ruby_ui_scaffold/railtie.rb +25 -0
- data/lib/ruby_ui_scaffold/seeder.rb +115 -0
- data/lib/ruby_ui_scaffold/value_generator.rb +168 -0
- data/lib/ruby_ui_scaffold/version.rb +5 -0
- data/lib/ruby_ui_scaffold.rb +22 -0
- metadata +197 -0
checksums.yaml
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
---
|
|
2
|
+
SHA256:
|
|
3
|
+
metadata.gz: ea2c9998050e2208005c500148ed961b083d93530e699992e5d8c2cd2463df05
|
|
4
|
+
data.tar.gz: 2331070feb2defcd9763082d11b8dd8c38a3523e2e3f1333eca359dd1b042d09
|
|
5
|
+
SHA512:
|
|
6
|
+
metadata.gz: b1965a5ee407847b93b9c01410ac939d9383143f864d7b9d96e2691304ff3f9e7034e7ed61910aa82421b8164760b1273dbdc9685906c083a1d3f1bed79bc692
|
|
7
|
+
data.tar.gz: fdde9d0a8f65ad928ece8044b07ec1dda306576d48e49bbed7eda5bfdb5ca3822278c9823109f7c977d5f383e2aa57b0130b53e41da3343d9b3c0de690177579
|
data/CHANGELOG.md
ADDED
|
@@ -0,0 +1,343 @@
|
|
|
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
|
+
## [0.1.0] - 2026-06-09
|
|
11
|
+
|
|
12
|
+
### Added
|
|
13
|
+
|
|
14
|
+
- **Lucide icons on the action buttons.** The index "New" link gets a `plus`
|
|
15
|
+
icon, the show "Edit" link a `pencil`, and the form's submit a `plus`
|
|
16
|
+
(create) / `check` (update). Each button gets `gap-2` for spacing and the
|
|
17
|
+
icon `size-4`, since ruby_ui's Button/Link don't auto-size or space SVGs.
|
|
18
|
+
Reuses the already-registered `lucide_icon` helper — no new dependency.
|
|
19
|
+
- **"Back" link in the new/edit form.** The shared form now renders a `Back`
|
|
20
|
+
link next to the submit button, pointing at `request.referer` and falling
|
|
21
|
+
back to the index path when there's no referer. Requires `request` in the
|
|
22
|
+
views, so the post-scaffold injection now also adds
|
|
23
|
+
`include Phlex::Rails::Helpers::Request` to `Components::Base` (idempotent).
|
|
24
|
+
- **`--skip-model` flag** for the scaffold generator — skips model/migration/
|
|
25
|
+
model-test/fixtures generation (the whole `:orm` hook) and only
|
|
26
|
+
(re)generates the controller, views, and route. Intended for re-runs
|
|
27
|
+
against an existing model: refresh the views after a template change, or
|
|
28
|
+
add `--datatable`, without Rails creating a duplicate migration or
|
|
29
|
+
clobbering the model. Implies `--force` (overwrites the regenerated files
|
|
30
|
+
and bypasses the class-collision check, which a re-run would otherwise
|
|
31
|
+
abort on). The model is never touched — put custom code there; the
|
|
32
|
+
controller/views are treated as regenerable.
|
|
33
|
+
- **Installer generator** — `bin/rails g ruby_ui_scaffold:install` automates
|
|
34
|
+
the first-time setup on a fresh Rails app. Runs `phlex:install`,
|
|
35
|
+
`ruby_ui:install`, `ruby_ui:component:all`, and backfills the 12
|
|
36
|
+
components the scaffold uses but that `:component:all` currently misses
|
|
37
|
+
(`link`, `table`, `alert_dialog`, `badge`, `select`, `combobox`,
|
|
38
|
+
`checkbox`, `textarea`, `data_table`, `native_select`, `calendar`,
|
|
39
|
+
`date_picker`). Every step is idempotent — re-running only touches what's
|
|
40
|
+
missing.
|
|
41
|
+
- **Automatic `ruby_ui` gem bootstrap** — when the `ruby_ui` gem isn't
|
|
42
|
+
loadable, the installer now adds
|
|
43
|
+
`gem "ruby_ui", github: "ruby-ui/ruby_ui", branch: "main", require: false`
|
|
44
|
+
to the Gemfile and runs `bundle install`, instead of aborting with manual
|
|
45
|
+
instructions. Idempotent: skips the Gemfile edit when an entry already
|
|
46
|
+
exists, and does nothing once the gem loads. Works even when the install
|
|
47
|
+
runs as a subprocess of the scaffold generator — the later sub-generator
|
|
48
|
+
steps spawn fresh subprocesses that boot with the updated bundle.
|
|
49
|
+
`phlex-rails` (a declared runtime dependency) keeps the abort-with-
|
|
50
|
+
instructions behavior. No-op when no Gemfile is present.
|
|
51
|
+
- **Auto-install on scaffold** — `bin/rails g ruby_ui_scaffold ...` now
|
|
52
|
+
auto-runs the idempotent `ruby_ui_scaffold:install` when phlex/ruby_ui
|
|
53
|
+
aren't detected, so generated views work out of the box. Falls back to a
|
|
54
|
+
non-blocking warning when there's no app `bin/rails` to drive it, or when
|
|
55
|
+
opted out with the new `--skip-install` flag.
|
|
56
|
+
- **`--literal` flag** for the scaffold generator — emits Phlex views using
|
|
57
|
+
[`literal`](https://literal.fun)'s `prop` macros instead of explicit
|
|
58
|
+
`def initialize` + `@ivar` assignments. Less boilerplate per view;
|
|
59
|
+
runtime type-checking included. On first use, also injects
|
|
60
|
+
`extend Literal::Properties` into `app/components/base.rb` (idempotent).
|
|
61
|
+
Combines with `--datatable` — the DataTable index gets the full typed
|
|
62
|
+
prop set (`_Any` collection, `Integer` pagination, `_Nilable(String)`
|
|
63
|
+
search/sort/direction). Controllers don't change: `render Views::*.new(...)`
|
|
64
|
+
still works since Literal generates a compatible `initialize`. `literal
|
|
65
|
+
(>= 1.0)` is now a runtime dependency.
|
|
66
|
+
- `phlex-rails (>= 2.0)` is now a declared runtime dependency, so Bundler
|
|
67
|
+
pulls it in automatically. `lib/ruby_ui_scaffold.rb` requires it eagerly
|
|
68
|
+
to ensure phlex-rails' railtie fires under `Bundler.require`, which is
|
|
69
|
+
what makes `bin/rails g phlex:install` discoverable.
|
|
70
|
+
|
|
71
|
+
### Changed
|
|
72
|
+
|
|
73
|
+
- **Components are now installed on demand instead of all upfront.** The
|
|
74
|
+
installer no longer runs `ruby_ui:component:all` + a `MISSING_COMPONENTS`
|
|
75
|
+
backfill; it installs only the `ComponentResolver::BASE` shell every
|
|
76
|
+
scaffold uses (`table`, `link`, `button`, `card`, `typography`,
|
|
77
|
+
`dropdown_menu`, `alert_dialog`, `form`, `input`). Each `rails g
|
|
78
|
+
ruby_ui_scaffold` then installs just the column/flag-specific components
|
|
79
|
+
that scaffold references — `badge`/`checkbox` (boolean), `textarea`
|
|
80
|
+
(text), `combobox`/`select` (references), `date_picker` (date),
|
|
81
|
+
`data_table` (`--datatable`) — right after writing the views, skipping any
|
|
82
|
+
already present. Net result: apps carry only the components they use, and
|
|
83
|
+
total component installs are always ≤ the old install-everything approach.
|
|
84
|
+
New `ComponentResolver` (pure mapping logic) and `ComponentInstaller`
|
|
85
|
+
(shared install/skip helpers) modules back this. `--skip-install` opts out
|
|
86
|
+
of on-demand installs too. Relies on `ruby_ui:component` resolving
|
|
87
|
+
transitive dependencies (it does, via ruby_ui's `dependencies.yml`).
|
|
88
|
+
- `date` columns now render the ruby_ui `DatePicker` (a Popover + Calendar
|
|
89
|
+
over a submittable input) instead of a native `Input(type: "date")`. The
|
|
90
|
+
generated form passes the record's date straight to `selected_date:` (the
|
|
91
|
+
component derives the input's ISO `yyyy-MM-dd` value from it) and
|
|
92
|
+
`label: nil` to avoid duplicating the form's `FormFieldLabel`. `datetime`
|
|
93
|
+
and `time` columns are unchanged (still native inputs — `DatePicker` has
|
|
94
|
+
no time component). The installer's component backfill now includes
|
|
95
|
+
`calendar` and `date_picker` to cover the new dependency.
|
|
96
|
+
- The post-scaffold `Components::Base` injection now adds four more Phlex
|
|
97
|
+
helpers in addition to `lucide_icon` — `Phlex::Rails::Helpers::FormWith`,
|
|
98
|
+
`LinkTo`, `ButtonTo`, and `Request`. They're required by the
|
|
99
|
+
scaffold-generated views (`form_with`, `link_to`, `button_to`, and
|
|
100
|
+
`request.referer` for the form's Back link) but aren't included by
|
|
101
|
+
`phlex:install` / `ruby_ui:install` by default. Each line is added only
|
|
102
|
+
if absent, so re-running is safe.
|
|
103
|
+
- The installer now invokes sub-generators via `system` (instead of Thor's
|
|
104
|
+
`generate` action) and aborts with a clear message on the first
|
|
105
|
+
subprocess failure. Thor's `abort_on_failure` doesn't reliably propagate
|
|
106
|
+
when the failure originates inside a nested Rails command (e.g. Bundler
|
|
107
|
+
bootstrap errors swallow the exit code). Without this, one bad step would
|
|
108
|
+
cascade into dozens of follow-up failures with the same root cause —
|
|
109
|
+
drowning the actual error in noise.
|
|
110
|
+
- The installer now injects `@source "../../views/**/*.rb"` and
|
|
111
|
+
`@source "../../components/**/*.rb"` into `app/assets/tailwind/application.css`.
|
|
112
|
+
Tailwind v4's automatic content detection skips `.rb` files, so without
|
|
113
|
+
this every Phlex class name (`mx-auto`, `max-w-3xl`, `max-w-prose`, etc.)
|
|
114
|
+
would silently fail to compile — making the scaffold's centered layout
|
|
115
|
+
render as left-aligned and unstyled. Idempotent; skipped if the file
|
|
116
|
+
isn't present (non-Tailwind setups).
|
|
117
|
+
- Form and Card layouts now use `w-full max-w-prose mx-auto` so they're
|
|
118
|
+
visually centered within the wrapper instead of sitting at the left edge.
|
|
119
|
+
- The outer `h-dvh overflow-y-auto` wrapper on every scaffolded view
|
|
120
|
+
(index, show, new, edit) now also has `w-full`. Without it, when the
|
|
121
|
+
host layout's container uses `display: flex` (common pattern: e.g.
|
|
122
|
+
`<main class="container mx-auto flex">...</main>`), the wrapper becomes
|
|
123
|
+
a flex item and shrinks to its content width — collapsing the
|
|
124
|
+
`mx-auto max-w-3xl` inner wrapper from 768px down to ~256px and making
|
|
125
|
+
the whole page render left-aligned instead of centered. `w-full` forces
|
|
126
|
+
the flex item to fill the parent regardless of flex/block context.
|
|
127
|
+
|
|
128
|
+
## [0.1.0] - 2026-05-22
|
|
129
|
+
|
|
130
|
+
First public-ready release. Pre-1.0 — the API may still evolve before the
|
|
131
|
+
first stable cut.
|
|
132
|
+
|
|
133
|
+
### Added
|
|
134
|
+
|
|
135
|
+
#### Scaffold generator (`rails g ruby_ui_scaffold MODEL field:type ...`)
|
|
136
|
+
|
|
137
|
+
- Inherits the full Rails scaffold pipeline (model, migration, resource route,
|
|
138
|
+
helper, tests) and replaces the controller + view layer with Phlex classes
|
|
139
|
+
wired to [ruby_ui](https://github.com/ruby-ui/ruby_ui) components.
|
|
140
|
+
- Three internal generators:
|
|
141
|
+
- `RubyUiScaffold::Generators::RubyUiScaffoldGenerator` — entry point,
|
|
142
|
+
namespaced flat as `ruby_ui_scaffold` (not `ruby_ui_scaffold:ruby_ui_scaffold`)
|
|
143
|
+
to avoid `find_by_namespace` shadowing.
|
|
144
|
+
- `RubyUiScaffold::Generators::ScaffoldControllerGenerator` — overrides
|
|
145
|
+
Rails' scaffold_controller template to render Phlex view classes via
|
|
146
|
+
`render ::Views::ModelName::Index.new(...)`.
|
|
147
|
+
- `RubyUiScaffold::Generators::ScaffoldGenerator` — the "template engine"
|
|
148
|
+
that emits the Phlex view files.
|
|
149
|
+
- Generated views live under the `Views::` module
|
|
150
|
+
(e.g. `Views::Buddies::Index < Views::Base`), matching the convention
|
|
151
|
+
installed by `phlex:install`. No bespoke initializer or autoload_paths
|
|
152
|
+
tweak required — `phlex:install` already wires `app/views/` as the
|
|
153
|
+
`Views::` namespace root in `config/initializers/phlex.rb`.
|
|
154
|
+
- On first run, `register_output_helper :lucide_icon` is injected into
|
|
155
|
+
`app/components/base.rb` (created by `phlex:install` / `ruby_ui:install`)
|
|
156
|
+
so the index dropdown trigger works out of the box. The injection is
|
|
157
|
+
idempotent — re-running the generator never duplicates the line.
|
|
158
|
+
- Field-type → ruby_ui component mapping (`RubyUiScaffold::FieldTypeMapper`),
|
|
159
|
+
covering `string`, `text`, `integer`, `float`, `decimal`, `boolean`, `date`,
|
|
160
|
+
`time`, `datetime`/`timestamp`, `password_digest`, `attachment(s)`,
|
|
161
|
+
`references`/`belongs_to`. Date/datetime values are formatted via `iso8601`
|
|
162
|
+
/ `strftime` so the HTML5 inputs accept them.
|
|
163
|
+
|
|
164
|
+
#### Index view: plain `Table` (default) or `DataTable` (`--datatable`)
|
|
165
|
+
|
|
166
|
+
- Default index is a plain ruby_ui `Table` with header + body. Controller's
|
|
167
|
+
`index` is the bare minimum (`Model.all` plus `.includes(:assoc)` when
|
|
168
|
+
`belongs_to` is present); the view takes a single `models:` kwarg.
|
|
169
|
+
- Pass `--datatable` to opt into the full ruby_ui `DataTable` instead — adds
|
|
170
|
+
`DataTableToolbar` (`DataTableSearch` + `DataTablePerPageSelect`),
|
|
171
|
+
`DataTableSortHead` for sortable columns, and `DataTablePaginationBar` +
|
|
172
|
+
`DataTablePagination` (manual `page`/`per_page`/`total_count` adapter —
|
|
173
|
+
no pagy/kaminari dependency). The controller is upgraded with
|
|
174
|
+
`SORTABLE_COLUMNS` allowlist, `params[:search]` LIKE clause across
|
|
175
|
+
string columns, `params[:sort]`/`[:direction]` allowlist, and
|
|
176
|
+
`params[:page]`/`[:per_page]` clamp.
|
|
177
|
+
- Wrapped in `Table(class: "table-fixed")` so column widths stay equal
|
|
178
|
+
and the table never overflows its container. Text and reference cells
|
|
179
|
+
are wrapped in `div(class: "truncate", title: value)` — long content
|
|
180
|
+
is clipped with an ellipsis and revealed on hover, eliminating
|
|
181
|
+
horizontal scroll. The action column is locked to `w-16` (with
|
|
182
|
+
`whitespace-nowrap`) so the `•••` dropdown trigger is always visible.
|
|
183
|
+
- Search is rendered only when at least one `:string` column exists.
|
|
184
|
+
- Sortable columns exclude `:text`, `:rich_text`, `:json`/`:jsonb`, `:binary`,
|
|
185
|
+
and attachments. Non-sortable columns render as plain `TableHead`.
|
|
186
|
+
- The actions column is a `DropdownMenu` (`options: { strategy: "fixed" }`)
|
|
187
|
+
triggered by a Lucide `more-horizontal` icon (`lucide_icon("more-horizontal",
|
|
188
|
+
class: "size-5 cursor-pointer text-muted-foreground hover:text-foreground")`)
|
|
189
|
+
— matches Linkana's production pattern (no extra `<span>` wrapper so the
|
|
190
|
+
`data-action="click->ruby-ui--dropdown-menu#toggle"` reliably fires).
|
|
191
|
+
Menu items: Show, Edit, Separator, Delete.
|
|
192
|
+
- The entire view_template body is wrapped in
|
|
193
|
+
`div(class: "h-dvh overflow-y-auto")` so vertical scrolling works
|
|
194
|
+
regardless of how the host layout handles overflow — critical for
|
|
195
|
+
dashboards that ship with `body { overflow: hidden }` (e.g. Linkana)
|
|
196
|
+
where natural page scroll is suppressed in favor of inner-scroll
|
|
197
|
+
containers.
|
|
198
|
+
|
|
199
|
+
#### `--phlex-layout` flag
|
|
200
|
+
|
|
201
|
+
- `--phlex-layout=ClassName` makes every generated view wrap its
|
|
202
|
+
`view_template` body in `render(ClassName) do ... end` and emits
|
|
203
|
+
`layout false` in the controller. Use this when your app has a
|
|
204
|
+
Phlex layout class (with `include Phlex::Rails::Layout`) that's
|
|
205
|
+
responsible for the full HTML shell (including the `<script>`
|
|
206
|
+
tags). Without it, scaffolded pages would inherit the default
|
|
207
|
+
Rails layout — which in dual-layout apps (one ERB layout for
|
|
208
|
+
guest pages, one Phlex layout for the dashboard) can mean
|
|
209
|
+
loading the wrong JS bundle and breaking Stimulus controllers
|
|
210
|
+
on the page.
|
|
211
|
+
|
|
212
|
+
#### Destroy confirmation — ruby_ui `AlertDialog` (no JS confirm)
|
|
213
|
+
|
|
214
|
+
- The Delete dropdown item is **always** wrapped in a ruby_ui
|
|
215
|
+
`AlertDialog` with Title + Description + Cancel + Delete form
|
|
216
|
+
(submitted via `form_with(method: :delete)`). No JS browser
|
|
217
|
+
`confirm()` dialog — the confirmation lives inside the dropdown.
|
|
218
|
+
- The trigger inside the AlertDialog is a `DropdownMenuItem(href: nil)`
|
|
219
|
+
styled with `text-destructive`, matching Linkana's production pattern.
|
|
220
|
+
|
|
221
|
+
#### Centered, max-width layout
|
|
222
|
+
|
|
223
|
+
- Index: `mx-auto max-w-7xl px-4 sm:px-6 lg:px-8 py-8` (wide for tables).
|
|
224
|
+
- Show / New / Edit: `mx-auto max-w-3xl ...` (focused on a single record /
|
|
225
|
+
form).
|
|
226
|
+
|
|
227
|
+
#### Real controller behavior (not just shell)
|
|
228
|
+
|
|
229
|
+
- Generated controller's `index` action:
|
|
230
|
+
- `SORTABLE_COLUMNS` allowlist baked at generator time.
|
|
231
|
+
- `params[:per_page]` clamped to `[1, MAX_PER_PAGE = 100]`, defaulting to
|
|
232
|
+
`DEFAULT_PER_PAGE = 10`.
|
|
233
|
+
- Case-insensitive `LOWER(col) LIKE :q` search across string columns (works
|
|
234
|
+
on PostgreSQL **and** SQLite).
|
|
235
|
+
- `scope.includes(:assoc)` auto-added when the model has non-polymorphic
|
|
236
|
+
`belongs_to` references — avoids N+1 on the index.
|
|
237
|
+
- `render ::Views::Model::Index.new(...)` with all pagination/search/sort kwargs.
|
|
238
|
+
|
|
239
|
+
#### `belongs_to` / `references` first-class support
|
|
240
|
+
|
|
241
|
+
- Forms switch dynamically between a searchable ruby_ui `Combobox`
|
|
242
|
+
(when the parent table has more than `COMBOBOX_THRESHOLD = 100`
|
|
243
|
+
records) and a plain `Select` (below the threshold) — no
|
|
244
|
+
`Input(type: "number")` placeholder. The threshold is baked as a
|
|
245
|
+
per-form class constant so users can tune per-resource.
|
|
246
|
+
- Both branches share the same option-label fallback chain:
|
|
247
|
+
`record.try(:name) → :title → :display_name → "Class #id"`,
|
|
248
|
+
so it works with any parent model out of the box.
|
|
249
|
+
- Index and Show display the friendly assoc label
|
|
250
|
+
(`book.author&.try(:name) || ...`) instead of the raw foreign key.
|
|
251
|
+
- Polymorphic references (`commentable:references{polymorphic}`) fall back
|
|
252
|
+
to `Input(type: "number")` + TODO comment.
|
|
253
|
+
|
|
254
|
+
### Added
|
|
255
|
+
|
|
256
|
+
#### Seed command (`rails ruby_ui_scaffold:seed MODEL [COUNT]`)
|
|
257
|
+
|
|
258
|
+
- Implemented as a `Rails::Command::Base` subclass at
|
|
259
|
+
`lib/rails/commands/ruby_ui_scaffold/seed_command.rb` — auto-discovered
|
|
260
|
+
via Rails' `$LOAD_PATH`-based command lookup. Native flag syntax
|
|
261
|
+
(`rails ruby_ui_scaffold:seed Buddy --count 50`), no rake bracket
|
|
262
|
+
workarounds.
|
|
263
|
+
- Three options:
|
|
264
|
+
- `--count N` / `-c N` — number of records to create (defaults to 10
|
|
265
|
+
when omitted).
|
|
266
|
+
- `--reset` — `Model.destroy_all` before seeding.
|
|
267
|
+
- `--dry-run` — print one sample attribute hash without persisting.
|
|
268
|
+
- Output: per-10-record progress line + final summary with elapsed time,
|
|
269
|
+
created/skipped counts, and first 3 unique validation errors.
|
|
270
|
+
|
|
271
|
+
#### Inference chain (`RubyUiScaffold::ValueGenerator`)
|
|
272
|
+
|
|
273
|
+
For each column, the value comes from the first source that matches:
|
|
274
|
+
|
|
275
|
+
1. **`belongs_to` foreign key** — samples an existing parent record's id
|
|
276
|
+
(`Parent.unscoped.ids.sample`).
|
|
277
|
+
2. **`ActiveRecord::Enum`** — samples a key from `Model.defined_enums[col]`.
|
|
278
|
+
3. **`validates :col, inclusion: { in: [...] }`** — samples from the list.
|
|
279
|
+
4. **`validates :col, numericality: { greater_than: X, less_than: Y }`** —
|
|
280
|
+
respects the range.
|
|
281
|
+
5. **Column name heuristics** — `email`, `first_name`, `last_name`, `name`,
|
|
282
|
+
`username`, `phone`, `address`, `city`, `state`, `country`, `zip`,
|
|
283
|
+
`url`, `title`, `body`/`content`/`description`/`bio`/`summary`/`notes`,
|
|
284
|
+
`company`, `slug`, `uuid`, `birthdate`/`birthday`/`dob`/`date_of_birth`,
|
|
285
|
+
`age`, `color`, `latitude`/`longitude`, `price`/`amount`, `quantity`/`qty`,
|
|
286
|
+
`password`/`password_digest`. With Faker installed these yield realistic
|
|
287
|
+
values; without, sensible `SecureRandom`-based fallbacks.
|
|
288
|
+
6. **Column type fallback** — `:integer`, `:bigint`, `:float`/`:decimal`,
|
|
289
|
+
`:boolean`, `:date`, `:datetime`/`:timestamp`, `:time`, `:json`/`:jsonb`,
|
|
290
|
+
`:uuid`, `:string`, `:text`.
|
|
291
|
+
|
|
292
|
+
#### Seeder orchestration (`RubyUiScaffold::Seeder`)
|
|
293
|
+
|
|
294
|
+
- Preflight check: aborts with a helpful message if a `belongs_to` parent
|
|
295
|
+
has no records (`"Post requires Author records first. Run …"`).
|
|
296
|
+
- Per-record retry on validation failure (up to 3 attempts with newly
|
|
297
|
+
generated attributes).
|
|
298
|
+
- Skips columns automatically: `id`, `created_at`, `updated_at`, `*_count`
|
|
299
|
+
(counter caches), STI `inheritance_column`, and polymorphic `*_type`
|
|
300
|
+
columns.
|
|
301
|
+
- `Faker` is a **runtime dependency** of the gem, so realistic fake data
|
|
302
|
+
(names, emails, addresses, paragraphs) is always available — no extra
|
|
303
|
+
setup needed.
|
|
304
|
+
|
|
305
|
+
### Dependencies
|
|
306
|
+
|
|
307
|
+
- Runtime: `railties >= 7.1`, `faker >= 2.0`, `lucide-rails >= 0.7`
|
|
308
|
+
- Development (tests): `rails >= 7.1`, `minitest ~> 5.0`, `rake ~> 13.0`,
|
|
309
|
+
`sqlite3 >= 2.0`
|
|
310
|
+
|
|
311
|
+
`lucide_icon` is registered as a Phlex output helper on `Components::Base`
|
|
312
|
+
(`register_output_helper :lucide_icon`, injected on first scaffold), so
|
|
313
|
+
it's callable from any scaffold-generated view. The injection is a no-op
|
|
314
|
+
if your `Components::Base` already declares it.
|
|
315
|
+
|
|
316
|
+
### Tests
|
|
317
|
+
|
|
318
|
+
68 runs, 442 assertions, 0 failures. Coverage:
|
|
319
|
+
|
|
320
|
+
- `FieldTypeMapper` — every type mapping plus Select (references) and
|
|
321
|
+
polymorphic fallback.
|
|
322
|
+
- `ValueGenerator` — type-based fallbacks (no Faker required) and the
|
|
323
|
+
full name-based heuristic catalog via mock columns.
|
|
324
|
+
- `Seeder` — happy path, retry on validations, enums, inclusion,
|
|
325
|
+
numericality range, `belongs_to` preflight, `--reset`, `--dry-run`,
|
|
326
|
+
using a real `sqlite3` in-memory connection with inline AR models.
|
|
327
|
+
- Scaffold generator — full template surface: DataTable structure,
|
|
328
|
+
centered layout, DropdownMenu trigger, AlertDialog destroy, references →
|
|
329
|
+
Combobox/Select switch in form, references → friendly label in
|
|
330
|
+
index/show, `--phlex-layout` wrapping.
|
|
331
|
+
|
|
332
|
+
### Internal notes
|
|
333
|
+
|
|
334
|
+
- Rails::Generators option propagation: the railtie sets
|
|
335
|
+
`Rails::Generators.options[:ruby_ui_scaffold][:template_engine] =
|
|
336
|
+
"ruby_ui_scaffold"` **before** loading generator classes, because
|
|
337
|
+
`class_option` defaults are frozen at class-definition time.
|
|
338
|
+
- The entry generator's namespace is overridden to flat `"ruby_ui_scaffold"`
|
|
339
|
+
(not `"ruby_ui_scaffold:ruby_ui_scaffold"`) to avoid `find_by_namespace`
|
|
340
|
+
shadowing of the sub-generators when hooks fire.
|
|
341
|
+
|
|
342
|
+
[Unreleased]: https://github.com/jacksonpires/ruby_ui_scaffold/compare/v0.1.0...HEAD
|
|
343
|
+
[0.1.0]: https://github.com/jacksonpires/ruby_ui_scaffold/releases/tag/v0.1.0
|
data/LICENSE.txt
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Jackson Pires
|
|
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.
|