achilles 1.0.0 → 1.1.1

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 97bbd095ec5feda0679a77b020f80ad70ec59b202757af4d84e1c318643a4632
4
- data.tar.gz: 40bdce2ca5420c29a4be44260e2655cfbca3e6357d294aa017454c0f90b9a642
3
+ metadata.gz: d860cd0be6faa8af39117c4dc6feadb2445f48ccfd687963a3a8eeddbe1c3b95
4
+ data.tar.gz: '09cdfabdfa0895b1a3c7a92726baf49cb234a9a716b8a1e3a65c7eb6e578904e'
5
5
  SHA512:
6
- metadata.gz: d21a4690c44bfeb356544370465319b6b6d969731a07aa4f4a9e462e32038a57c53f63f2e6ee05b5b3959e86d1fe7f517b48acce6ab8d08266d9dd7de8589285
7
- data.tar.gz: b1a8de0b8ed2beb3d564df46e35568958989b93b1ca5f8b2d4d89a93dadb40640b1d81c5ef0a0316a7c4016f7ed29da1382ef10b86bdf1103756075a6ec11293
6
+ metadata.gz: 7899f56529ad2a867a289b546241023e235a1d848d3a22ceb1a396fb92a515c9edeb3b50decdec7d37e952f9c5fc524326c8d1fc3456e78f5e1c7db5be53c10d
7
+ data.tar.gz: 7d43d1c196f608be65a27d42840cdd0a3f3bac7bb86870dec876b89bdbfdf63408465dabd265986322638300ab0d8287c3a747e2b2830ccc3c824bf74217318e
data/CHANGELOG.md CHANGED
@@ -2,6 +2,42 @@
2
2
 
3
3
  All notable changes to Achilles will be documented in this file.
4
4
 
5
+ ## 1.1.1
6
+
7
+ ### Fixed
8
+
9
+ - Fixed Turbo browser history restoration so components restored from cached DOM
10
+ are registered and set up again when their registry entries were already
11
+ removed.
12
+ - Fixed component subtree deregistration so child components are removed with
13
+ their parent and cannot block later re-registration.
14
+ - Added browser system coverage for back, forward, restored nested components,
15
+ restored form-field listeners, Turbo-replaced form restoration, and duplicate
16
+ listener prevention.
17
+
18
+ ## 1.1.0
19
+
20
+ ### Added
21
+
22
+ - Added explicit `Application#start` and `Application#stop` lifecycle methods.
23
+ - Added opt-in `Application#strictLifecycleErrors` handling for tests and
24
+ development.
25
+ - Added browser system coverage for nested components inside Turbo form
26
+ replacement and Turbo Drive navigation.
27
+
28
+ ### Changed
29
+
30
+ - `Application` no longer starts automatically from the constructor. Applications
31
+ should register component classes, then call `achilles.start()`. See
32
+ [docs/upgrading-to-1.1.0.md](docs/upgrading-to-1.1.0.md).
33
+ - Elements with `data-component-class` must now have a non-empty `id`; invalid
34
+ component roots are skipped with a console error.
35
+ - Component teardown now runs from child components to parent components.
36
+ - Component parentage now follows DOM ancestry under the synthetic `Page` root.
37
+ - Turbo lifecycle hooks are now attached by `start()` and removed by `stop()`.
38
+ - Achilles now requires `turbo-rails` so Turbo importmap assets are available to
39
+ host applications.
40
+
5
41
  ## 1.0.0
6
42
 
7
43
  ### Added
@@ -0,0 +1,46 @@
1
+ # Code Of Conduct
2
+
3
+ ## Our Pledge
4
+
5
+ We want Achilles to be a respectful, useful, and practical open source project.
6
+
7
+ Everyone participating in the project is expected to communicate with clarity,
8
+ patience, and respect. Disagreement is fine. Harassment, personal attacks, and
9
+ abusive behavior are not.
10
+
11
+ ## Expected Behavior
12
+
13
+ - Be respectful of differing experience levels and viewpoints.
14
+ - Keep technical criticism focused on the code, design, or documentation.
15
+ - Assume good intent, but be direct when something is unclear or risky.
16
+ - Help keep issues and pull requests actionable.
17
+ - Respect maintainers' time and project scope.
18
+
19
+ ## Unacceptable Behavior
20
+
21
+ - Harassment, threats, or personal attacks.
22
+ - Insults, slurs, or discriminatory language.
23
+ - Public or private abuse of contributors or maintainers.
24
+ - Publishing someone's private information without permission.
25
+ - Repeated off-topic disruption after maintainers ask for the discussion to
26
+ stop or move elsewhere.
27
+
28
+ ## Enforcement
29
+
30
+ Maintainers may remove comments, close issues, reject pull requests, or block
31
+ participants who violate this code of conduct.
32
+
33
+ To report a concern, email:
34
+
35
+ ```text
36
+ jey@jeygeethan.com
37
+ ```
38
+
39
+ Reports will be reviewed privately. Please include relevant links, screenshots,
40
+ or context when possible.
41
+
42
+ ## Scope
43
+
44
+ This code of conduct applies to Achilles project spaces, including GitHub
45
+ issues, pull requests, discussions, release comments, and private project
46
+ communication with maintainers.
data/CONTRIBUTING.md ADDED
@@ -0,0 +1,122 @@
1
+ # Contributing
2
+
3
+ Thanks for taking the time to improve Achilles.
4
+
5
+ Achilles is intentionally small. Contributions should keep the public API clear,
6
+ the Rails + Turbo integration reliable, and the documentation practical for
7
+ applications that already use the gem.
8
+
9
+ ## Local Setup
10
+
11
+ Install dependencies:
12
+
13
+ ```bash
14
+ bundle install
15
+ ```
16
+
17
+ Run the dummy Rails app:
18
+
19
+ ```bash
20
+ bin/rails server
21
+ ```
22
+
23
+ Open:
24
+
25
+ ```text
26
+ http://localhost:3000/
27
+ ```
28
+
29
+ The dummy app includes a working counter component that exercises the basic
30
+ Achilles flow.
31
+
32
+ ## Test Commands
33
+
34
+ Run the Rails and JavaScript lifecycle tests:
35
+
36
+ ```bash
37
+ bin/rails test
38
+ ```
39
+
40
+ Run the browser system test:
41
+
42
+ ```bash
43
+ bin/rails test:system
44
+ ```
45
+
46
+ Run JavaScript syntax checks:
47
+
48
+ ```bash
49
+ for file in $(find app/javascript/achilles test/dummy/app/javascript -name '*.js' -print); do node --input-type=module --check < "$file" || exit 1; done
50
+ ```
51
+
52
+ Run the dummy app asset precompile check:
53
+
54
+ ```bash
55
+ RAILS_ENV=test bin/rails app:assets:precompile
56
+ ```
57
+
58
+ Run the full local verification set:
59
+
60
+ ```bash
61
+ bin/rails test
62
+ bin/rails test:system
63
+ for file in $(find app/javascript/achilles test/dummy/app/javascript -name '*.js' -print); do node --input-type=module --check < "$file" || exit 1; done
64
+ RAILS_ENV=test bin/rails app:assets:precompile
65
+ ```
66
+
67
+ ## System Test Browser Requirements
68
+
69
+ System tests use Selenium with headless Chrome.
70
+
71
+ On CI, GitHub's Ubuntu runner provides Chrome. Locally, set these environment
72
+ variables if Chrome or chromedriver are installed in non-standard locations:
73
+
74
+ ```bash
75
+ CHROME_BIN=/path/to/chrome CHROMEDRIVER_PATH=/path/to/chromedriver bin/rails test:system
76
+ ```
77
+
78
+ If Chrome is not available, the system test base skips browser tests cleanly.
79
+
80
+ ## Pull Request Guidelines
81
+
82
+ - Keep changes scoped.
83
+ - Add or update tests for behavior changes.
84
+ - Update the README or docs for public API changes.
85
+ - Add or update an upgrade guide for changes existing applications must make.
86
+ - Mention breaking changes clearly in the pull request.
87
+ - Do not introduce a new runtime dependency without explaining why it belongs in
88
+ a small lifecycle gem.
89
+ - Prefer browser DOM APIs in Achilles internals.
90
+ - Keep jQuery usage out of Achilles internals. Applications may still use jQuery
91
+ explicitly in their own components.
92
+
93
+ ## Public API Expectations
94
+
95
+ Treat these as public API:
96
+
97
+ - `Application`
98
+ - `ComponentBase`
99
+ - `ComponentsClassMapper`
100
+ - `data-component-class`
101
+ - `setup()`
102
+ - `teardown()`
103
+ - `rootElement()`
104
+ - `rootNode()`
105
+ - `rootElementSelector()`
106
+
107
+ Changes to these APIs need tests, documentation, and changelog notes.
108
+
109
+ ## Release Process
110
+
111
+ Use [docs/release-checklist.md](docs/release-checklist.md) for prereleases and
112
+ final releases.
113
+
114
+ Maintainer release ownership and project decision guidelines are documented in
115
+ [MAINTAINERS.md](MAINTAINERS.md).
116
+
117
+ Before releasing:
118
+
119
+ - confirm CI is green
120
+ - run the full local verification set
121
+ - build the gem with `gem build achilles.gemspec`
122
+ - test release candidates in at least one real application
data/MAINTAINERS.md ADDED
@@ -0,0 +1,53 @@
1
+ # Maintainers
2
+
3
+ Achilles is maintained as a small, stable Rails + Turbo lifecycle gem.
4
+
5
+ The maintainer goal is to keep the public API easy to understand, the runtime
6
+ surface small, and releases safe for applications that already depend on the
7
+ gem.
8
+
9
+ ## Decision Making
10
+
11
+ Maintainers should prefer changes that:
12
+
13
+ - preserve the small public API
14
+ - improve reliability across Rails, Turbo, and importmap applications
15
+ - reduce hidden runtime dependencies
16
+ - make migration or debugging easier for existing applications
17
+ - include tests for changed behavior
18
+
19
+ Breaking changes are acceptable when they make the project simpler, safer, or
20
+ more predictable. They should be documented in `CHANGELOG.md`, covered by tests,
21
+ and called out in migration notes when existing applications need code changes.
22
+
23
+ ## Release Ownership
24
+
25
+ Before publishing a release, a maintainer should:
26
+
27
+ - confirm CI is green
28
+ - run the local verification set in `CONTRIBUTING.md`
29
+ - follow `docs/release-checklist.md`
30
+ - test release candidates in at least one real Rails application when behavior
31
+ changes affect runtime integration
32
+
33
+ Patch releases should be narrow and low risk. Minor releases can add compatible
34
+ APIs or documentation improvements. Major releases may remove deprecated
35
+ behavior or tighten public contracts.
36
+
37
+ ## Security Reports
38
+
39
+ Security reports should follow `SECURITY.md`. Reports are handled privately
40
+ until there is a fix or a clear public response.
41
+
42
+ ## Community Standards
43
+
44
+ Project participation is covered by `CODE_OF_CONDUCT.md`. Maintainers may close
45
+ issues, reject pull requests, edit discussions, or block participants when
46
+ needed to keep the project focused and respectful.
47
+
48
+ ## Adding Maintainers
49
+
50
+ New maintainers should already have a history of useful issues, documentation,
51
+ or code contributions. They should understand the project scope, support the
52
+ public API expectations in `CONTRIBUTING.md`, and be comfortable reviewing
53
+ changes conservatively.
data/README.md CHANGED
@@ -1,12 +1,53 @@
1
1
  # Achilles
2
2
 
3
3
  Achilles is a small JavaScript lifecycle layer for Rails + Turbo applications.
4
- It is positioned as a simpler alternative to Stimulus for apps that prefer
5
- explicit component classes mapped to DOM nodes.
4
+ It is an explicit component-class alternative to Stimulus for teams that prefer
5
+ plain JavaScript classes mapped directly to DOM nodes.
6
6
 
7
7
  Achilles scans the page for elements with `data-component-class`, instantiates
8
8
  the matching JavaScript class, and calls `setup` and `teardown` as Turbo renders
9
- new pages.
9
+ new pages or new component markup is inserted.
10
+
11
+ Current applications should use Achilles `1.1.1`. If you are upgrading an
12
+ existing app, start with the [1.1.0 upgrade guide](docs/upgrading-to-1.1.0.md)
13
+ and then read the [1.1.1 release note](docs/releases/v1.1.1.md).
14
+
15
+ ## Why Achilles?
16
+
17
+ Rails and Turbo make server-rendered interfaces productive, but many apps still
18
+ need page-specific JavaScript for menus, forms, filters, widgets, charts, and
19
+ small interaction islands.
20
+
21
+ Achilles keeps that JavaScript close to the DOM without requiring controller
22
+ naming conventions or a build step. You register classes explicitly, mark the
23
+ HTML root node, and implement lifecycle methods.
24
+
25
+ Use Achilles when you want:
26
+
27
+ - explicit JavaScript classes instead of controller naming conventions
28
+ - lifecycle hooks that match Turbo navigation
29
+ - no internal jQuery dependency
30
+ - no JavaScript build step
31
+ - predictable setup and teardown for server-rendered UI
32
+ - simple dynamic DOM registration through `MutationObserver`
33
+
34
+ ## Achilles vs Stimulus
35
+
36
+ Stimulus is a strong default for many Rails apps. Achilles is intentionally
37
+ smaller and more explicit.
38
+
39
+ | Concern | Achilles | Stimulus |
40
+ | --- | --- | --- |
41
+ | Class registration | Explicit mapper registration | File and identifier conventions |
42
+ | DOM marker | `data-component-class` | `data-controller` |
43
+ | Root element API | `this.rootElement()` | `this.element` |
44
+ | Lifecycle | `setup()` and `teardown()` | `connect()` and `disconnect()` |
45
+ | Targets/actions | Use standard DOM APIs | Built-in targets/actions |
46
+ | Dependency | Rails, Turbo, Importmap | Hotwire Stimulus |
47
+
48
+ Choose Achilles if you want a tiny lifecycle layer with explicit class mapping.
49
+ Choose Stimulus if you want its controller ecosystem, targets, values, actions,
50
+ and conventions.
10
51
 
11
52
  ## Requirements
12
53
 
@@ -39,8 +80,12 @@ import { CounterComponent } from "components/counter_component";
39
80
 
40
81
  const achilles = new Application();
41
82
  achilles.componentsClassMapper.addComponentClass("CounterComponent", CounterComponent);
83
+ achilles.start();
42
84
  ```
43
85
 
86
+ Call `start()` after registering component classes. Achilles parses the current
87
+ page and attaches Turbo lifecycle hooks when the application starts.
88
+
44
89
  Create components by extending `ComponentBase`:
45
90
 
46
91
  ```js
@@ -70,17 +115,151 @@ Mark the component root in your view:
70
115
  <button id="counter" data-component-class="CounterComponent">0</button>
71
116
  ```
72
117
 
73
- Every component root must have a unique `id`. Achilles uses that id to register
74
- the component, find its root element, and avoid running setup twice for the same
75
- DOM node.
118
+ Every component root must have a non-empty unique `id`. Achilles uses that id to
119
+ register the component, find its root element, and avoid running setup twice for
120
+ the same DOM node. Components without ids are skipped and reported in the browser
121
+ console.
122
+
123
+ ## Dynamic Components
124
+
125
+ Achilles watches the document for inserted component markup. If Turbo Streams,
126
+ custom JavaScript, or another UI flow adds a matching element, Achilles parses
127
+ and sets it up automatically.
128
+
129
+ ```erb
130
+ <div id="notification-42" data-component-class="NotificationComponent">
131
+ Saved successfully
132
+ </div>
133
+ ```
134
+
135
+ ```js
136
+ import { ComponentBase } from "achilles/components/component_base";
137
+
138
+ class NotificationComponent extends ComponentBase {
139
+ setup() {
140
+ this.timeout = window.setTimeout(() => {
141
+ this.rootElement().remove();
142
+ }, 3000);
143
+ }
144
+
145
+ teardown() {
146
+ window.clearTimeout(this.timeout);
147
+ }
148
+ }
149
+
150
+ export { NotificationComponent };
151
+ ```
152
+
153
+ Register the component class once:
154
+
155
+ ```js
156
+ achilles.componentsClassMapper.addComponentClass("NotificationComponent", NotificationComponent);
157
+ ```
158
+
159
+ ## Nested Components
160
+
161
+ Achilles keeps a component tree rooted at a single synthetic `Page` component.
162
+ A component's parent is its nearest ancestor element with `data-component-class`.
163
+ If there is no component ancestor, its parent is `Page`.
164
+
165
+ ```erb
166
+ <div id="dashboard" data-component-class="DashboardComponent">
167
+ <div id="filters" data-component-class="FiltersComponent"></div>
168
+ </div>
169
+ ```
170
+
171
+ This creates the following component tree:
172
+
173
+ ```text
174
+ Page
175
+ dashboard
176
+ filters
177
+ ```
178
+
179
+ ## Example App
180
+
181
+ The dummy Rails app includes a working counter component. See
182
+ [examples/README.md](examples/README.md) for the files and local run command.
76
183
 
77
184
  ## Lifecycle
78
185
 
79
- - `setup` runs after `turbo:load` and after new matching DOM nodes are inserted.
80
- - `teardown` runs before Turbo renders a new page.
81
- - `rootElement()` returns the DOM element for the component id.
82
- - `rootNode()` is an alias for `rootElement()`.
83
- - `rootElementSelector()` returns a CSS selector for the component id.
186
+ - `setup()` runs after `turbo:load` and after new matching DOM nodes are inserted.
187
+ - `teardown()` runs before Turbo renders a new page.
188
+ - `setup()` and `teardown()` are called once per registered component instance.
189
+ - Parent components are set up before their children.
190
+ - Child components are torn down before their parents.
191
+ - Components that attach listeners, timers, observers, subscriptions, or widgets
192
+ should clean them up in `teardown()`.
193
+ - Lifecycle errors are logged and swallowed by default so one broken component
194
+ does not stop the page. Enable strict mode in tests or development when errors
195
+ should be re-raised:
196
+
197
+ ```js
198
+ achilles.strictLifecycleErrors = true;
199
+ ```
200
+
201
+ ## API Reference
202
+
203
+ ### `Application`
204
+
205
+ The top-level Achilles application object.
206
+
207
+ ```js
208
+ import { Application } from "achilles/application/application";
209
+
210
+ const achilles = new Application();
211
+ achilles.start();
212
+ ```
213
+
214
+ Useful properties:
215
+
216
+ - `componentsClassMapper`: register component classes by name.
217
+ - `componentRegistry`: inspect or manage registered component instances.
218
+ - `strictLifecycleErrors`: re-raise lifecycle errors after logging them.
219
+ - `timezone`: access the configured app timezone.
220
+
221
+ Call `start()` after registering component classes. Call `stop()` when an
222
+ application instance should remove its Turbo hooks and stop observing the DOM.
223
+
224
+ ### `ComponentsClassMapper`
225
+
226
+ Maps `data-component-class` values to JavaScript classes.
227
+
228
+ ```js
229
+ achilles.componentsClassMapper.addComponentClass("MenuComponent", MenuComponent);
230
+ ```
231
+
232
+ ### `ComponentBase`
233
+
234
+ Base class for application components.
235
+
236
+ ```js
237
+ import { ComponentBase } from "achilles/components/component_base";
238
+
239
+ class MenuComponent extends ComponentBase {
240
+ setup() {}
241
+ teardown() {}
242
+ }
243
+ ```
244
+
245
+ Useful methods:
246
+
247
+ - `setup()`: override to initialize behavior.
248
+ - `teardown()`: override to clean up behavior before Turbo renders a new page.
249
+ - `rootElement()`: returns the component root DOM element.
250
+ - `rootNode()`: alias for `rootElement()`.
251
+ - `rootElementSelector()`: returns a CSS selector for the component id.
252
+
253
+ ### Component Markup
254
+
255
+ ```erb
256
+ <div id="account-menu" data-component-class="MenuComponent"></div>
257
+ ```
258
+
259
+ The `data-component-class` value must match a class registered with
260
+ `componentsClassMapper`. The element must also have a non-empty unique `id`.
261
+ Nested components are parented by DOM ancestry, with top-level components
262
+ parented by the synthetic `Page` component.
84
263
 
85
264
  ## Timezone
86
265
 
@@ -93,18 +272,73 @@ value through `achilles.timezone.timezoneString`.
93
272
 
94
273
  If no timezone is present, Achilles falls back to `Etc/UTC`.
95
274
 
275
+ ## Upgrading
276
+
277
+ Applications upgrading to `1.1.0` should read the
278
+ [1.1.0 upgrade guide](docs/upgrading-to-1.1.0.md). The complete upgrade index
279
+ lives in [docs/upgrading.md](docs/upgrading.md), and the GitHub release draft is
280
+ available at [docs/releases/v1.1.1.md](docs/releases/v1.1.1.md).
281
+
282
+ ## Upgrading From 0.1.3
283
+
284
+ Achilles `1.0.0` changes `rootElement()` to return a DOM element. It no longer
285
+ returns a jQuery object when `window.$` is present.
286
+
287
+ Old jQuery-style code:
288
+
289
+ ```js
290
+ this.rootElement().addClass("is-open");
291
+ ```
292
+
293
+ Use DOM APIs:
294
+
295
+ ```js
296
+ this.rootElement().classList.add("is-open");
297
+ ```
298
+
299
+ Or wrap explicitly if the application still uses jQuery:
300
+
301
+ ```js
302
+ $(this.rootElement()).addClass("is-open");
303
+ ```
304
+
305
+ Applications upgrading from `0.1.3` should also read the
306
+ [v1 migration guide](docs/migrating-from-0.1.3-to-v1.md).
307
+
96
308
  ## Contributing
97
309
 
310
+ See [CONTRIBUTING.md](CONTRIBUTING.md) for setup, test commands, pull request
311
+ guidelines, and release notes. Project participation is covered by the
312
+ [code of conduct](CODE_OF_CONDUCT.md). Maintainer responsibilities are described
313
+ in [MAINTAINERS.md](MAINTAINERS.md).
314
+
98
315
  Run the test suite with:
99
316
 
100
317
  ```bash
101
318
  bin/rails test
102
319
  ```
103
320
 
104
- ## Migration
321
+ Run the browser system test with:
105
322
 
106
- Applications upgrading from `0.1.3` should read the
107
- [v1 migration guide](docs/migrating-from-0.1.3-to-v1.md).
323
+ ```bash
324
+ bin/rails test:system
325
+ ```
326
+
327
+ Run the JavaScript syntax check with:
328
+
329
+ ```bash
330
+ for file in $(find app/javascript/achilles -name '*.js' -print); do node --input-type=module --check < "$file" || exit 1; done
331
+ ```
332
+
333
+ Run the dummy app asset precompile check with:
334
+
335
+ ```bash
336
+ RAILS_ENV=test bin/rails app:assets:precompile
337
+ ```
338
+
339
+ ## Security
340
+
341
+ Report security issues privately. See [SECURITY.md](SECURITY.md).
108
342
 
109
343
  ## License
110
344
 
data/SECURITY.md ADDED
@@ -0,0 +1,50 @@
1
+ # Security Policy
2
+
3
+ ## Supported Versions
4
+
5
+ Only the latest minor release of Achilles `1.x` receives security fixes.
6
+
7
+ | Version | Supported |
8
+ | --- | --- |
9
+ | `1.x` | Yes |
10
+ | `< 1.0` | No |
11
+
12
+ ## Reporting A Vulnerability
13
+
14
+ Please do not open a public GitHub issue for security vulnerabilities.
15
+
16
+ Report security issues by email:
17
+
18
+ ```text
19
+ jey@jeygeethan.com
20
+ ```
21
+
22
+ Include:
23
+
24
+ - affected Achilles version
25
+ - Rails version
26
+ - Ruby version
27
+ - a description of the vulnerability
28
+ - reproduction steps or proof of concept
29
+ - whether the issue is already public
30
+
31
+ ## Response Expectations
32
+
33
+ You should receive an acknowledgement within 7 days.
34
+
35
+ If the issue is confirmed, the fix will be prepared privately when practical and
36
+ released with a changelog entry that gives users enough information to upgrade
37
+ without exposing unnecessary exploit details before a fix is available.
38
+
39
+ ## Scope
40
+
41
+ Security reports are most useful when they involve Achilles behavior directly,
42
+ including:
43
+
44
+ - unsafe DOM lifecycle behavior
45
+ - asset/importmap packaging issues
46
+ - Rails engine integration issues
47
+ - behavior that could cause applications to execute unintended JavaScript
48
+
49
+ General application security issues in apps that use Achilles should be reported
50
+ to those applications instead.