compony 0.11.8 → 0.11.9
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 +4 -4
- data/.yardopts +36 -1
- data/CHANGELOG.md +31 -0
- data/CLAUDE.md +85 -0
- data/Gemfile.lock +1 -1
- data/README.md +13 -3
- data/VERSION +1 -1
- data/compony.gemspec +3 -3
- data/doc/ComponentGenerator.html +1 -1
- data/doc/Components.html +1 -1
- data/doc/ComponentsGenerator.html +1 -1
- data/doc/Compony/Component.html +54 -54
- data/doc/Compony/ComponentMixins/Default/Labelling.html +1 -1
- data/doc/Compony/ComponentMixins/Default/Standalone/ResourcefulVerbDsl.html +1 -1
- data/doc/Compony/ComponentMixins/Default/Standalone/StandaloneDsl.html +109 -70
- data/doc/Compony/ComponentMixins/Default/Standalone/VerbDsl.html +64 -28
- data/doc/Compony/ComponentMixins/Default/Standalone.html +1 -1
- data/doc/Compony/ComponentMixins/Default.html +1 -1
- data/doc/Compony/ComponentMixins/Resourceful.html +213 -74
- data/doc/Compony/ComponentMixins.html +1 -1
- data/doc/Compony/Components/Buttons/CssButton.html +1 -1
- data/doc/Compony/Components/Buttons/Link.html +1 -1
- data/doc/Compony/Components/Buttons.html +1 -1
- data/doc/Compony/Components/Destroy.html +83 -29
- data/doc/Compony/Components/Edit.html +110 -38
- data/doc/Compony/Components/Form.html +551 -208
- data/doc/Compony/Components/Index.html +1 -1
- data/doc/Compony/Components/List.html +3 -3
- data/doc/Compony/Components/New.html +110 -38
- data/doc/Compony/Components/Show.html +1 -1
- data/doc/Compony/Components/WithForm.html +194 -47
- data/doc/Compony/Components.html +1 -1
- data/doc/Compony/ControllerMixin.html +1 -1
- data/doc/Compony/Engine.html +1 -1
- data/doc/Compony/Intent.html +2 -2
- data/doc/Compony/ManageIntentsDsl.html +1 -1
- data/doc/Compony/MethodAccessibleHash.html +1 -1
- data/doc/Compony/ModelFields/Anchormodel.html +1 -1
- data/doc/Compony/ModelFields/Association.html +1 -1
- data/doc/Compony/ModelFields/Attachment.html +1 -1
- data/doc/Compony/ModelFields/Base.html +1 -1
- data/doc/Compony/ModelFields/Boolean.html +1 -1
- data/doc/Compony/ModelFields/Color.html +1 -1
- data/doc/Compony/ModelFields/Currency.html +1 -1
- data/doc/Compony/ModelFields/Date.html +1 -1
- data/doc/Compony/ModelFields/Datetime.html +1 -1
- data/doc/Compony/ModelFields/Decimal.html +1 -1
- data/doc/Compony/ModelFields/Email.html +1 -1
- data/doc/Compony/ModelFields/Float.html +1 -1
- data/doc/Compony/ModelFields/Integer.html +1 -1
- data/doc/Compony/ModelFields/Percentage.html +1 -1
- data/doc/Compony/ModelFields/Phone.html +1 -1
- data/doc/Compony/ModelFields/RichText.html +1 -1
- data/doc/Compony/ModelFields/String.html +1 -1
- data/doc/Compony/ModelFields/Text.html +1 -1
- data/doc/Compony/ModelFields/Time.html +1 -1
- data/doc/Compony/ModelFields/Url.html +1 -1
- data/doc/Compony/ModelFields.html +1 -1
- data/doc/Compony/ModelMixin.html +1 -1
- data/doc/Compony/NaturalOrdering.html +1 -1
- data/doc/Compony/RequestContext.html +1 -1
- data/doc/Compony/Version.html +1 -1
- data/doc/Compony/ViewHelpers.html +1 -1
- data/doc/Compony/VirtualModel.html +1 -1
- data/doc/Compony.html +1 -1
- data/doc/ComponyController.html +1 -1
- data/doc/_index.html +97 -1
- data/doc/file.CHANGELOG.html +758 -0
- data/doc/file.README.html +25 -4
- data/doc/file.basic_component.html +314 -0
- data/doc/file.cookbook.html +189 -0
- data/doc/file.destroy.html +105 -0
- data/doc/file.dsl_reference.html +672 -0
- data/doc/file.edit.html +109 -0
- data/doc/file.example.html +291 -0
- data/doc/file.example_advanced.html +257 -0
- data/doc/file.feasibility.html +115 -0
- data/doc/file.form.html +195 -0
- data/doc/file.generators.html +89 -0
- data/doc/file.glossary.html +217 -0
- data/doc/file.gotchas.html +222 -0
- data/doc/file.index.html +135 -0
- data/doc/file.inheritance.html +136 -0
- data/doc/file.installation.html +115 -0
- data/doc/file.integrations.html +218 -0
- data/doc/file.intents.html +265 -0
- data/doc/file.internal_datastructures.html +129 -0
- data/doc/file.list.html +253 -0
- data/doc/file.maintaining.html +127 -0
- data/doc/file.model_fields.html +137 -0
- data/doc/file.nesting.html +237 -0
- data/doc/file.new.html +109 -0
- data/doc/file.ownership.html +98 -0
- data/doc/file.patterns.html +669 -0
- data/doc/file.pre_built_components.html +99 -0
- data/doc/file.resourceful.html +181 -0
- data/doc/file.show.html +158 -0
- data/doc/file.standalone.html +233 -0
- data/doc/file.virtual_models.html +117 -0
- data/doc/file.with_form.html +157 -0
- data/doc/file_list.html +160 -0
- data/doc/guide/cookbook.md +41 -0
- data/doc/guide/dsl_reference.md +155 -0
- data/doc/guide/example_advanced.md +209 -0
- data/doc/guide/generators.md +1 -1
- data/doc/guide/glossary.md +42 -0
- data/doc/guide/gotchas.md +125 -0
- data/doc/guide/maintaining.md +64 -0
- data/doc/guide/patterns.md +681 -0
- data/doc/guide/pre_built_components/edit.md +1 -1
- data/doc/guide/pre_built_components/index.md +64 -1
- data/doc/guide/pre_built_components/list.md +111 -7
- data/doc/guide/pre_built_components/show.md +57 -2
- data/doc/guide/pre_built_components/with_form.md +56 -9
- data/doc/guide/pre_built_components.md +7 -2
- data/doc/guide/standalone.md +16 -1
- data/doc/index.html +25 -4
- data/doc/integrations.md +61 -0
- data/doc/llms.txt +62 -0
- data/doc/top-level-namespace.html +1 -1
- data/lib/compony/component.rb +8 -3
- data/lib/compony/component_mixins/default/standalone/standalone_dsl.rb +32 -15
- data/lib/compony/component_mixins/default/standalone/verb_dsl.rb +11 -3
- data/lib/compony/component_mixins/resourceful.rb +30 -16
- data/lib/compony/components/destroy.rb +21 -1
- data/lib/compony/components/edit.rb +25 -1
- data/lib/compony/components/form.rb +63 -21
- data/lib/compony/components/list.rb +1 -1
- data/lib/compony/components/new.rb +25 -1
- data/lib/compony/components/with_form.rb +20 -5
- data/lib/compony/intent.rb +1 -1
- metadata +43 -1
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
[Back to the guide](/README.md#guide--documentation)
|
|
2
|
+
|
|
3
|
+
# Gotchas / anti-patterns
|
|
4
|
+
|
|
5
|
+
Known footguns, each with **symptom → cause → fix**. Skim before debugging.
|
|
6
|
+
|
|
7
|
+
### 1. `standalone` without an explicit `path:`
|
|
8
|
+
|
|
9
|
+
- **Symptom:** `undefined method '..._comp_path'`, or no route generated for the component.
|
|
10
|
+
- **Cause:** `standalone` only emits a route when a `path:` is given.
|
|
11
|
+
- **Fix:** Always pass `standalone path: 'users/show/:id' do ... end`. A component with no
|
|
12
|
+
standalone is valid but must be nested in another component (has no URL).
|
|
13
|
+
|
|
14
|
+
### 2. `render_intent` / `render_sub_comp` output not appearing
|
|
15
|
+
|
|
16
|
+
- **Symptom:** The button/list silently missing from the rendered page.
|
|
17
|
+
- **Cause:** Dyny does not auto-print return values. `render_intent` returns an HTML string.
|
|
18
|
+
- **Fix:** Wrap it: `concat render_intent(:edit, user)` (Dyny's equivalent of `<%= %>`).
|
|
19
|
+
|
|
20
|
+
### 3. Overriding `respond` skips authorization
|
|
21
|
+
|
|
22
|
+
- **Symptom:** An endpoint is reachable without the expected permission check.
|
|
23
|
+
- **Cause:** `authorize` is evaluated inside the *default* `respond`. A custom `respond`
|
|
24
|
+
(especially the `nil`/all-formats one) replaces it.
|
|
25
|
+
- **Fix:** Re-check authorization yourself inside the custom `respond`, or keep a separate
|
|
26
|
+
default `respond` for the relevant format.
|
|
27
|
+
|
|
28
|
+
### 4. `schema_field` with the foreign key name
|
|
29
|
+
|
|
30
|
+
- **Symptom:** Association param rejected by Schemacop / not assigned.
|
|
31
|
+
- **Cause:** Compony adds `_id` for associations automatically.
|
|
32
|
+
- **Fix:** Use the **association name**: `schema_field :author`, not `schema_field :author_id`.
|
|
33
|
+
Same for `field :author` in `form_fields`.
|
|
34
|
+
|
|
35
|
+
### 5. Model without `label`
|
|
36
|
+
|
|
37
|
+
- **Symptom:** Errors or blank text when Compony renders titles/links for the model.
|
|
38
|
+
- **Cause:** Compony calls `model.label` for display everywhere.
|
|
39
|
+
- **Fix:** Implement `def label` on every Compony model (or have a `label` column).
|
|
40
|
+
|
|
41
|
+
### 6. Forgetting to forward args in a custom `initialize`
|
|
42
|
+
|
|
43
|
+
- **Symptom:** `parent_comp`, `@data`, comp opts mysteriously `nil`; nesting broken.
|
|
44
|
+
- **Cause:** The base initializer wires essential state. Skipping `super` drops it.
|
|
45
|
+
- **Fix:** Call `super(*args, **kwargs, &block)` first, then set your own ivars. Prefer
|
|
46
|
+
putting logic in `setup` instead of overriding `initialize`.
|
|
47
|
+
|
|
48
|
+
### 7. Reading a label without its block argument
|
|
49
|
+
|
|
50
|
+
- **Symptom:** `wrong number of arguments` from a label block.
|
|
51
|
+
- **Cause:** Resourceful components' label blocks take the model; non-resourceful take none.
|
|
52
|
+
- **Fix:** `label(User.first)` for resourceful; `label` (no arg) otherwise. At most one arg.
|
|
53
|
+
|
|
54
|
+
### 8. `content :name` inside a block tries to render another component's block
|
|
55
|
+
|
|
56
|
+
- **Symptom:** Content block not found / unexpected output.
|
|
57
|
+
- **Cause:** Nested `content :name` only renders a block defined in the **same** component.
|
|
58
|
+
- **Fix:** To embed another component use `render_sub_comp`, not nested `content`.
|
|
59
|
+
|
|
60
|
+
### 9. Multiple pages in one component
|
|
61
|
+
|
|
62
|
+
- **Symptom:** Tangled `standalone`/`verb`/`respond` tree, confusing routes.
|
|
63
|
+
- **Cause:** Extra `standalone` calls are for *companion* endpoints (AJAX tiles, autocomplete
|
|
64
|
+
JSON) of the *same* screen — not for separate pages.
|
|
65
|
+
- **Fix:** One screen = one component exposing one main route. Make another component for
|
|
66
|
+
another page.
|
|
67
|
+
|
|
68
|
+
### 10. Resourceful component on a path without `:id`
|
|
69
|
+
|
|
70
|
+
- **Symptom:** `Couldn't find <Model> without an ID` (e.g. an Index at `/users`).
|
|
71
|
+
- **Cause:** Default `load_data` does `data_class.find(params[:id])`.
|
|
72
|
+
- **Fix:** Override `load_data { @data = User.all }` (or your scope) for collection/index
|
|
73
|
+
components.
|
|
74
|
+
|
|
75
|
+
### 11. `redirect_to` with a hardcoded path
|
|
76
|
+
|
|
77
|
+
- **Symptom:** Links break after renaming/moving a component.
|
|
78
|
+
- **Cause:** Bypassing the intent system.
|
|
79
|
+
- **Fix:** Use `redirect_to Compony.path(:index, :users)` / `Compony.path(:show, @data)`.
|
|
80
|
+
|
|
81
|
+
### 12. ActiveStorage attachment on a virtual model
|
|
82
|
+
|
|
83
|
+
- **Symptom:** Uploaded file not persisted for a `Compony::VirtualModel`.
|
|
84
|
+
- **Cause:** Virtual models aren't backed by a real table; the default `store_data` save
|
|
85
|
+
doesn't persist attachments.
|
|
86
|
+
- **Fix:** Override `store_data` to only validate (`@create_succeeded = @data.validate`) and
|
|
87
|
+
perform the real mutation/attachment in `on_created_respond`. See
|
|
88
|
+
[virtual_models.md](/doc/guide/virtual_models.md).
|
|
89
|
+
|
|
90
|
+
### 13. `tailwindcss-rails` purges Compony styles
|
|
91
|
+
|
|
92
|
+
- **Symptom:** Compony component markup unstyled in production.
|
|
93
|
+
- **Cause:** Tailwind's unused-CSS purge doesn't scan component classes (their HTML isn't in
|
|
94
|
+
`app/views`).
|
|
95
|
+
- **Fix:** Safelist the classes, or don't use `tailwindcss-rails` with Compony (see
|
|
96
|
+
README "Caveats").
|
|
97
|
+
|
|
98
|
+
### 14. Public endpoint still 401/redirecting
|
|
99
|
+
|
|
100
|
+
- **Symptom:** Webhook/public action blocked by app auth or CSRF.
|
|
101
|
+
- **Cause:** `skip_authentication!` alone doesn't remove CSRF, and `authorize` is still
|
|
102
|
+
mandatory.
|
|
103
|
+
- **Fix:** In the standalone: `skip_authentication!` + `skip_forgery_protection!`, and in
|
|
104
|
+
the verb `authorize { true }` (then validate a bearer token yourself).
|
|
105
|
+
|
|
106
|
+
### 15. Hand-rolled endpoint where a pre-built CRUD component exists
|
|
107
|
+
|
|
108
|
+
- **Symptom:** A custom `Compony::Component` with manual `button_to`/standalone for
|
|
109
|
+
delete/edit/create, duplicating routing, authorization, confirmation UI, styling.
|
|
110
|
+
- **Cause:** Reaching for a bespoke component instead of the pre-built one.
|
|
111
|
+
- **Fix:** For CRUD use the pre-built parents (`Destroy`/`Edit`/`New`/`Show`/`Index`) and
|
|
112
|
+
point with `render_intent(:destroy, record)`. Reserve custom `Compony::Component`
|
|
113
|
+
standalones for genuinely non-CRUD actions (job dispatch, webhooks, multi-record ops).
|
|
114
|
+
Put a resourceful component in the family of the model it operates on, not the family
|
|
115
|
+
it is navigated from; pass parent context via path params.
|
|
116
|
+
|
|
117
|
+
### 16. `attr_accessor` on a model for form-only fields
|
|
118
|
+
|
|
119
|
+
- **Symptom:** Virtual form fields declared as `attr_accessor` on the ActiveRecord model.
|
|
120
|
+
- **Cause:** Polluting the persistent model with form-only concerns.
|
|
121
|
+
- **Fix:** Use an `ActiveType::Record[Model]` (or `Compony::VirtualModel`) inner class on
|
|
122
|
+
the component with `ar_attribute` + `field`, and set `data_class` to it. See
|
|
123
|
+
[virtual_models.md](/doc/guide/virtual_models.md).
|
|
124
|
+
|
|
125
|
+
[Guide index](/README.md#guide--documentation)
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
[Back to the guide](/README.md#guide--documentation)
|
|
2
|
+
|
|
3
|
+
# Maintaining Compony
|
|
4
|
+
|
|
5
|
+
Conventions for contributors and AI assistants working **on the gem itself** (not apps
|
|
6
|
+
using it). Keeps docs, gemspec and the rendered API reference from drifting.
|
|
7
|
+
|
|
8
|
+
## Every change
|
|
9
|
+
|
|
10
|
+
- **Behavior change → CHANGELOG.** Add a bullet under the top `# unreleased` heading in
|
|
11
|
+
[CHANGELOG.md](/CHANGELOG.md). If it breaks existing apps, add a `## Steps to take`
|
|
12
|
+
subsection with the migration (see prior entries for the format).
|
|
13
|
+
- **New/renamed DSL method → docs.** Update [dsl_reference.md](/doc/guide/dsl_reference.md)
|
|
14
|
+
(keep the `# DSL method` source marker) and add a YARD summary with
|
|
15
|
+
`@param`/`@return`/`@api` on the method.
|
|
16
|
+
- **Comments only / docs only is fine to note as such** in the CHANGELOG ("no behavior
|
|
17
|
+
change").
|
|
18
|
+
|
|
19
|
+
## Releasing
|
|
20
|
+
|
|
21
|
+
1. Bump the version in `lib/compony/version.rb`. Loading the Rakefile rewrites `VERSION`
|
|
22
|
+
from it; `.edge` marks an unreleased prerelease.
|
|
23
|
+
2. Run `rake gemspec` — regenerates `compony.gemspec` (never hand-edit it; it says so).
|
|
24
|
+
3. Move the `# unreleased` CHANGELOG block to a `# X.Y.Z` heading; start a fresh
|
|
25
|
+
`# unreleased`.
|
|
26
|
+
4. Run `yard doc` and **commit the regenerated `doc/*.html`** — the rendered API
|
|
27
|
+
reference is committed and shipped in the gem (`s.files`), so stale HTML ships
|
|
28
|
+
otherwise.
|
|
29
|
+
5. `bundle exec rubocop` clean.
|
|
30
|
+
6. Tag / push / `gem build` per your release flow (`bundler/gem_tasks`).
|
|
31
|
+
|
|
32
|
+
## Dependencies
|
|
33
|
+
|
|
34
|
+
- Hard deps and their constraints live **only** in the `:gemspec` task in
|
|
35
|
+
[`Rakefile`](/Rakefile). Change them there, run `rake gemspec`, and update the mirror
|
|
36
|
+
table in [integrations.md](/doc/integrations.md) in the same commit.
|
|
37
|
+
- Keep the README's stated Rails/Ruby support in sync with
|
|
38
|
+
`required_ruby_version` / the `rails` constraint — the gemspec is authoritative.
|
|
39
|
+
|
|
40
|
+
## Documentation rendering
|
|
41
|
+
|
|
42
|
+
- `doc/*.html` is generated by YARD from `lib/` + the extra files listed in
|
|
43
|
+
[`.yardopts`](/.yardopts).
|
|
44
|
+
- **Adding a guide page?** Add its path to `.yardopts` (after the `-` separator) or it
|
|
45
|
+
will not appear in the rendered reference, then re-run `yard doc`.
|
|
46
|
+
- Markdown changes to already-listed guide pages still need a `yard doc` re-run + commit
|
|
47
|
+
to refresh the HTML.
|
|
48
|
+
- `yard doc` logs `Cannot resolve link to … from text:` for guide-to-guide Markdown links
|
|
49
|
+
and inline `<code>` in the guide pages. This is **expected and benign** — YARD tries to
|
|
50
|
+
treat them as Ruby code-object links. The pages render correctly and the links work on
|
|
51
|
+
GitHub. Do not rewrite guide cross-links to silence YARD. (Genuine broken `{Xref}` in
|
|
52
|
+
Ruby `lib/` comments are worth fixing; guide-Markdown link warnings are not.)
|
|
53
|
+
|
|
54
|
+
## Patterns derived from real apps
|
|
55
|
+
|
|
56
|
+
- [patterns.md](/doc/guide/patterns.md) / [cookbook.md](/doc/guide/cookbook.md) are
|
|
57
|
+
**fully anonymized**: no app, business, author or domain-model names; neutral domain
|
|
58
|
+
(`Account`, `Order`, `LineItem`, `Document`); app-wrapper-only APIs excluded or
|
|
59
|
+
generalized (this is a public LGPL repo).
|
|
60
|
+
- Document the **secure** variant only. Never reproduce an insecure shape (e.g. a
|
|
61
|
+
capability token without expiry) even if that is what an app currently does — show the
|
|
62
|
+
hardened form and call out the requirement.
|
|
63
|
+
|
|
64
|
+
[Guide index](/README.md#guide--documentation)
|