achilles 0.1.2 → 1.0.0.rc1
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/CHANGELOG.md +37 -0
- data/README.md +94 -11
- data/Rakefile +0 -2
- data/app/javascript/achilles/application/dom-mutation-observer/observer.js +1 -1
- data/app/javascript/achilles/application/hooks-manager/turbo.js +4 -13
- data/app/javascript/achilles/application/timezone/timezone.js +1 -1
- data/app/javascript/achilles/components/component_base.js +9 -1
- data/app/javascript/achilles/components/component_parser.js +7 -7
- data/app/javascript/achilles/components/components_registry.js +12 -4
- data/docs/migrating-from-0.1.3-to-v1.md +120 -0
- data/docs/release-checklist.md +73 -0
- data/docs/v1-roadmap.md +68 -0
- data/lib/achilles/version.rb +1 -1
- data/lib/achilles.rb +1 -0
- metadata +40 -11
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: e7490e9d3a5cb570991cffdc696869bc4db9910ad6811125f9b6f5e50939bf8f
|
|
4
|
+
data.tar.gz: 1cadeacf7fad25b4c22d5e061276928847de011e23ceac333935531906b1fef9
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 801f4fbe6e267497ab2fda085bd36721f99cdde505616db473eef536517c9957789ed1fe956c6fa7361d94293dbd945d511aff96f772fb89f94a4966e2e27ad6
|
|
7
|
+
data.tar.gz: 0a8df7b78adb9516753e5b592551f3f76f6d0d06331d603d897e6efab627224e87ca97ed6704283979fc861102308aacb2bc389a91bc138de3c053dde7c60491
|
data/CHANGELOG.md
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to Achilles will be documented in this file.
|
|
4
|
+
|
|
5
|
+
## 1.0.0.rc1
|
|
6
|
+
|
|
7
|
+
### Added
|
|
8
|
+
|
|
9
|
+
- Added `importmap-rails` as an explicit runtime dependency.
|
|
10
|
+
- Added `rootNode()` as an alias for a component's DOM element.
|
|
11
|
+
- Added Turbo teardown support through `turbo:before-render`.
|
|
12
|
+
- Added JavaScript lifecycle behavior tests for parser, registry, Turbo hooks,
|
|
13
|
+
dynamic DOM insertion, setup idempotence, teardown idempotence, missing
|
|
14
|
+
elements, and root lookup.
|
|
15
|
+
- Added GitHub Actions CI for Rails tests, JavaScript syntax checks, and dummy
|
|
16
|
+
app asset precompile.
|
|
17
|
+
- Added v1 roadmap and migration documentation.
|
|
18
|
+
|
|
19
|
+
### Changed
|
|
20
|
+
|
|
21
|
+
- Replaced Achilles' internal jQuery usage with browser DOM APIs.
|
|
22
|
+
- Updated README with real installation, usage, lifecycle, and contribution
|
|
23
|
+
instructions.
|
|
24
|
+
- Fixed dummy app asset manifest linkage for asset precompile.
|
|
25
|
+
- Removed deprecated Rails statistics rake task loading.
|
|
26
|
+
|
|
27
|
+
### Breaking Changes
|
|
28
|
+
|
|
29
|
+
- `rootElement()` now always returns the DOM element for the component id.
|
|
30
|
+
- `rootElement()` no longer returns a jQuery object when `window.$` is present.
|
|
31
|
+
- Applications that want jQuery should wrap the DOM element explicitly with
|
|
32
|
+
`$(this.rootElement())`.
|
|
33
|
+
- Achilles internals are independent from jQuery.
|
|
34
|
+
|
|
35
|
+
## 0.1.3
|
|
36
|
+
|
|
37
|
+
Tagged as the last known pre-v1 baseline.
|
data/README.md
CHANGED
|
@@ -1,28 +1,111 @@
|
|
|
1
1
|
# Achilles
|
|
2
|
-
Short description and motivation.
|
|
3
2
|
|
|
4
|
-
|
|
5
|
-
|
|
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.
|
|
6
|
+
|
|
7
|
+
Achilles scans the page for elements with `data-component-class`, instantiates
|
|
8
|
+
the matching JavaScript class, and calls `setup` and `teardown` as Turbo renders
|
|
9
|
+
new pages.
|
|
10
|
+
|
|
11
|
+
## Requirements
|
|
12
|
+
|
|
13
|
+
- Rails 7.0.2.3 or newer
|
|
14
|
+
- Turbo Rails
|
|
15
|
+
- Importmap Rails
|
|
6
16
|
|
|
7
17
|
## Installation
|
|
8
|
-
|
|
18
|
+
|
|
19
|
+
Add Achilles to your application's Gemfile:
|
|
9
20
|
|
|
10
21
|
```ruby
|
|
11
22
|
gem "achilles"
|
|
12
23
|
```
|
|
13
24
|
|
|
14
|
-
|
|
25
|
+
Then install:
|
|
26
|
+
|
|
15
27
|
```bash
|
|
16
|
-
|
|
28
|
+
bundle install
|
|
17
29
|
```
|
|
18
30
|
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
31
|
+
## Usage
|
|
32
|
+
|
|
33
|
+
Create one Achilles application instance and register your component classes:
|
|
34
|
+
|
|
35
|
+
```js
|
|
36
|
+
// app/javascript/application.js
|
|
37
|
+
import { Application } from "achilles/application/application";
|
|
38
|
+
import { CounterComponent } from "components/counter_component";
|
|
39
|
+
|
|
40
|
+
const achilles = new Application();
|
|
41
|
+
achilles.componentsClassMapper.addComponentClass("CounterComponent", CounterComponent);
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
Create components by extending `ComponentBase`:
|
|
45
|
+
|
|
46
|
+
```js
|
|
47
|
+
// app/javascript/components/counter_component.js
|
|
48
|
+
import { ComponentBase } from "achilles/components/component_base";
|
|
49
|
+
|
|
50
|
+
class CounterComponent extends ComponentBase {
|
|
51
|
+
setup() {
|
|
52
|
+
this.rootElement().addEventListener("click", this.increment);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
teardown() {
|
|
56
|
+
this.rootElement().removeEventListener("click", this.increment);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
increment = () => {
|
|
60
|
+
this.rootElement().textContent = Number(this.rootElement().textContent) + 1;
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
export { CounterComponent };
|
|
22
65
|
```
|
|
23
66
|
|
|
67
|
+
Mark the component root in your view:
|
|
68
|
+
|
|
69
|
+
```erb
|
|
70
|
+
<button id="counter" data-component-class="CounterComponent">0</button>
|
|
71
|
+
```
|
|
72
|
+
|
|
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.
|
|
76
|
+
|
|
77
|
+
## Lifecycle
|
|
78
|
+
|
|
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.
|
|
84
|
+
|
|
85
|
+
## Timezone
|
|
86
|
+
|
|
87
|
+
If the page includes an element with `data-app-timezone`, Achilles exposes the
|
|
88
|
+
value through `achilles.timezone.timezoneString`.
|
|
89
|
+
|
|
90
|
+
```erb
|
|
91
|
+
<div data-app-timezone="<%= Time.zone.tzinfo.name %>"></div>
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
If no timezone is present, Achilles falls back to `Etc/UTC`.
|
|
95
|
+
|
|
24
96
|
## Contributing
|
|
25
|
-
|
|
97
|
+
|
|
98
|
+
Run the test suite with:
|
|
99
|
+
|
|
100
|
+
```bash
|
|
101
|
+
bin/rails test
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
## Migration
|
|
105
|
+
|
|
106
|
+
Applications upgrading from `0.1.3` should read the
|
|
107
|
+
[v1 migration guide](docs/migrating-from-0.1.3-to-v1.md).
|
|
26
108
|
|
|
27
109
|
## License
|
|
28
|
-
|
|
110
|
+
|
|
111
|
+
Achilles is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
|
data/Rakefile
CHANGED
|
@@ -13,7 +13,7 @@ class Observer {
|
|
|
13
13
|
|
|
14
14
|
start() {
|
|
15
15
|
// Listen on html instead of body since turbo replaces body and the observer stops after one page transition
|
|
16
|
-
this._mutationObserver.observe(
|
|
16
|
+
this._mutationObserver.observe(document.documentElement, this.config());
|
|
17
17
|
}
|
|
18
18
|
|
|
19
19
|
stop() {
|
|
@@ -14,22 +14,13 @@ class Turbo {
|
|
|
14
14
|
// Setups relevant hooks to the page for component lifecycles. This depends on the framework being used.
|
|
15
15
|
// Here we are using turbo drive, so hooking into that.
|
|
16
16
|
setupEvents() {
|
|
17
|
-
|
|
18
|
-
// Turbolinks lifecycle ref: https://sevos.io/2017/02/27/turbolinks-lifecycle-explained.html
|
|
19
|
-
// Render is not called on initial page load. So execute only once during page load
|
|
20
|
-
$(document).on("turbo:load", () => {
|
|
21
|
-
console.log('[achilles] turbo load event')
|
|
17
|
+
document.addEventListener("turbo:load", () => {
|
|
22
18
|
this._setupCallback();
|
|
23
19
|
});
|
|
24
20
|
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
// });
|
|
29
|
-
//
|
|
30
|
-
// $(document).on("turbo:before-render", () => {
|
|
31
|
-
// this._teardownCallback();
|
|
32
|
-
// });
|
|
21
|
+
document.addEventListener("turbo:before-render", () => {
|
|
22
|
+
this._teardownCallback();
|
|
23
|
+
});
|
|
33
24
|
}
|
|
34
25
|
}
|
|
35
26
|
|
|
@@ -11,7 +11,7 @@ class Timezone {
|
|
|
11
11
|
}
|
|
12
12
|
|
|
13
13
|
getTimezoneFromHtml() {
|
|
14
|
-
this._timezoneString =
|
|
14
|
+
this._timezoneString = document.querySelector("[data-app-timezone]")?.dataset.appTimezone || "Etc/UTC";
|
|
15
15
|
}
|
|
16
16
|
}
|
|
17
17
|
|
|
@@ -18,10 +18,18 @@ class ComponentBase {
|
|
|
18
18
|
teardown() {}
|
|
19
19
|
|
|
20
20
|
rootElement() {
|
|
21
|
-
return
|
|
21
|
+
return this.rootNode();
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
rootNode() {
|
|
25
|
+
return document.getElementById(this.id);
|
|
22
26
|
}
|
|
23
27
|
|
|
24
28
|
rootElementSelector() {
|
|
29
|
+
if(window.CSS && typeof window.CSS.escape === 'function') {
|
|
30
|
+
return `#${window.CSS.escape(this.id)}`;
|
|
31
|
+
}
|
|
32
|
+
|
|
25
33
|
return `#${this.id}`;
|
|
26
34
|
}
|
|
27
35
|
}
|
|
@@ -8,25 +8,25 @@ class ComponentParser {
|
|
|
8
8
|
}
|
|
9
9
|
|
|
10
10
|
parse() {
|
|
11
|
-
[
|
|
12
|
-
let klassName =
|
|
11
|
+
[...document.querySelectorAll('[data-component-class]')].forEach((elem) => {
|
|
12
|
+
let klassName = elem.dataset.componentClass;
|
|
13
13
|
if(klassName.trim() === '') { return; }
|
|
14
14
|
|
|
15
15
|
let klass = this._componentsClassMapper.getComponentClass(klassName);
|
|
16
16
|
if(typeof klass === 'undefined' || klass === null) {
|
|
17
17
|
console.error(`Component class not found: ${klassName} | Element:`);
|
|
18
|
-
console.error(
|
|
18
|
+
console.error(elem);
|
|
19
19
|
return;
|
|
20
20
|
}
|
|
21
|
-
if(
|
|
21
|
+
if(elem.dataset.componentRegistered === 'true') {
|
|
22
22
|
return;
|
|
23
23
|
}
|
|
24
24
|
try {
|
|
25
|
-
let obj = new klass(
|
|
25
|
+
let obj = new klass(elem.id, AppConstants.PageComponentId)
|
|
26
26
|
this._componentRegistry.registerComponentByObj(obj);
|
|
27
27
|
} catch (e) {
|
|
28
|
-
console.error(`Error parsing component. className: ${klassName} | Element ID: ${
|
|
29
|
-
console.error(
|
|
28
|
+
console.error(`Error parsing component. className: ${klassName} | Element ID: ${elem.id}`);
|
|
29
|
+
console.error(elem);
|
|
30
30
|
console.error(e);
|
|
31
31
|
}
|
|
32
32
|
})
|
|
@@ -4,12 +4,20 @@ import { AppConstants } from "achilles/application/app_constants";
|
|
|
4
4
|
class ComponentsRegistry {
|
|
5
5
|
_registeredComponents = {};
|
|
6
6
|
|
|
7
|
+
matchingElementsForId(id) {
|
|
8
|
+
return [...document.querySelectorAll('[id]')].filter((elem) => elem.id === id);
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
elementForId(id) {
|
|
12
|
+
return document.getElementById(id);
|
|
13
|
+
}
|
|
14
|
+
|
|
7
15
|
registerComponentByObj(obj) {
|
|
8
16
|
this.registerComponent(obj.id, obj, obj.defaultParams, obj.parentComponentId);
|
|
9
17
|
}
|
|
10
18
|
|
|
11
19
|
registerComponent(id, obj, defaultParams, parentComponentId) {
|
|
12
|
-
if(
|
|
20
|
+
if(this.matchingElementsForId(id).length > 1) {
|
|
13
21
|
console.error(`Error while registering component: There are more than one elements with the same id: ${id}. Skipping registering component`);
|
|
14
22
|
return;
|
|
15
23
|
}
|
|
@@ -29,7 +37,7 @@ class ComponentsRegistry {
|
|
|
29
37
|
let parentComponent = this.getRegisteredComponent(parentComponentId);
|
|
30
38
|
parentComponent.subComponents.push(id);
|
|
31
39
|
}
|
|
32
|
-
|
|
40
|
+
this.elementForId(id)?.setAttribute('data-component-registered', 'true');
|
|
33
41
|
}
|
|
34
42
|
|
|
35
43
|
deregisterComponent(id) {
|
|
@@ -44,7 +52,7 @@ class ComponentsRegistry {
|
|
|
44
52
|
}
|
|
45
53
|
|
|
46
54
|
this._registeredComponents[id] = null;
|
|
47
|
-
|
|
55
|
+
this.elementForId(id)?.removeAttribute('data-component-registered');
|
|
48
56
|
}
|
|
49
57
|
|
|
50
58
|
getRegisteredComponent(id) {
|
|
@@ -55,7 +63,7 @@ class ComponentsRegistry {
|
|
|
55
63
|
let component = this.getRegisteredComponent(id);
|
|
56
64
|
if(!component || !component.obj)
|
|
57
65
|
return;
|
|
58
|
-
if(id !== AppConstants.PageComponentId &&
|
|
66
|
+
if(id !== AppConstants.PageComponentId && this.elementForId(id) === null) {
|
|
59
67
|
this.elementNotFound(id);
|
|
60
68
|
return;
|
|
61
69
|
}
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
# Migrating From 0.1.3 To v1
|
|
2
|
+
|
|
3
|
+
This guide is for applications already using Achilles `0.1.3`.
|
|
4
|
+
|
|
5
|
+
The recommended path is to first adopt the compatibility release, then test the
|
|
6
|
+
v1 release candidate in one real application before upgrading every app.
|
|
7
|
+
|
|
8
|
+
## Upgrade Strategy
|
|
9
|
+
|
|
10
|
+
1. Upgrade one application at a time.
|
|
11
|
+
2. Run the app locally with Turbo navigation enabled.
|
|
12
|
+
3. Visit pages with Achilles components.
|
|
13
|
+
4. Watch the browser console for missing component classes, duplicate ids, and
|
|
14
|
+
lifecycle errors.
|
|
15
|
+
5. Verify that components still clean up event listeners on navigation.
|
|
16
|
+
6. Repeat the same checks after dynamically inserting component markup.
|
|
17
|
+
|
|
18
|
+
## Compatibility Release Checklist
|
|
19
|
+
|
|
20
|
+
These changes should be safe for existing `0.1.3` applications:
|
|
21
|
+
|
|
22
|
+
- `importmap-rails` is declared as an Achilles dependency.
|
|
23
|
+
- Achilles calls `teardown()` before Turbo renders a new page.
|
|
24
|
+
- Achilles internals no longer require jQuery.
|
|
25
|
+
- `rootNode()` is available for new code.
|
|
26
|
+
- `rootElement()` returns the component's DOM element in v1.
|
|
27
|
+
|
|
28
|
+
Use the compatibility release to review component code that expects
|
|
29
|
+
`rootElement()` to return a jQuery object.
|
|
30
|
+
|
|
31
|
+
## v1 Breaking API Direction
|
|
32
|
+
|
|
33
|
+
In v1, component code should use DOM APIs:
|
|
34
|
+
|
|
35
|
+
```js
|
|
36
|
+
class MenuComponent extends ComponentBase {
|
|
37
|
+
setup() {
|
|
38
|
+
this.rootElement().addEventListener("click", this.toggle);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
teardown() {
|
|
42
|
+
this.rootElement().removeEventListener("click", this.toggle);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
toggle = () => {
|
|
46
|
+
this.rootElement().classList.toggle("is-open");
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
Avoid relying on `rootElement()` returning a jQuery object:
|
|
52
|
+
|
|
53
|
+
```js
|
|
54
|
+
// Old style
|
|
55
|
+
this.rootElement().addClass("is-open");
|
|
56
|
+
|
|
57
|
+
// v1 style
|
|
58
|
+
this.rootElement().classList.add("is-open");
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
If an application still wants to use jQuery inside its own component code, keep
|
|
62
|
+
that dependency in the application and wrap `rootElement()` explicitly:
|
|
63
|
+
|
|
64
|
+
```js
|
|
65
|
+
$(this.rootElement()).addClass("is-open");
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
## Component Markup
|
|
69
|
+
|
|
70
|
+
Every Achilles component root must have a unique `id` and a
|
|
71
|
+
`data-component-class` value that matches a registered component class:
|
|
72
|
+
|
|
73
|
+
```erb
|
|
74
|
+
<div id="account-menu" data-component-class="MenuComponent"></div>
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
Duplicate ids are invalid HTML and Achilles will skip registering those
|
|
78
|
+
components.
|
|
79
|
+
|
|
80
|
+
## Lifecycle Changes To Verify
|
|
81
|
+
|
|
82
|
+
Achilles calls:
|
|
83
|
+
|
|
84
|
+
- `setup()` after `turbo:load`
|
|
85
|
+
- `setup()` when matching component markup is inserted dynamically
|
|
86
|
+
- `teardown()` before Turbo renders a new page
|
|
87
|
+
|
|
88
|
+
Review every component for setup work that must be undone in teardown:
|
|
89
|
+
|
|
90
|
+
- event listeners
|
|
91
|
+
- timers
|
|
92
|
+
- observers
|
|
93
|
+
- third-party widgets
|
|
94
|
+
- subscriptions
|
|
95
|
+
- global document/window handlers
|
|
96
|
+
|
|
97
|
+
## Search Checklist
|
|
98
|
+
|
|
99
|
+
In each application, search for:
|
|
100
|
+
|
|
101
|
+
```bash
|
|
102
|
+
rg "rootElement\\("
|
|
103
|
+
rg "\\$\\(this\\.root"
|
|
104
|
+
rg "data-component-class"
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
Convert jQuery-style `rootElement()` usage to DOM APIs or explicit jQuery
|
|
108
|
+
wrapping before testing v1.
|
|
109
|
+
|
|
110
|
+
## When To Test v1.0.0.rc1
|
|
111
|
+
|
|
112
|
+
Test the release candidate after:
|
|
113
|
+
|
|
114
|
+
- the application passes its own test suite
|
|
115
|
+
- all obvious `rootElement()` usages have been reviewed
|
|
116
|
+
- pages with Achilles components work across Turbo navigation
|
|
117
|
+
- dynamic components are verified manually or by system tests
|
|
118
|
+
|
|
119
|
+
Do not upgrade every application at once. Test one representative app first,
|
|
120
|
+
then move the rest after the migration path is proven.
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
# Release Checklist
|
|
2
|
+
|
|
3
|
+
Use this checklist for `1.0.0.rc1` and future releases.
|
|
4
|
+
|
|
5
|
+
## Before Building
|
|
6
|
+
|
|
7
|
+
- Confirm `lib/achilles/version.rb` has the intended version.
|
|
8
|
+
- Confirm `CHANGELOG.md` has an entry for the intended version.
|
|
9
|
+
- Confirm CI is green on GitHub.
|
|
10
|
+
- Run the local verification commands:
|
|
11
|
+
|
|
12
|
+
```bash
|
|
13
|
+
bin/rails test
|
|
14
|
+
for file in $(find app/javascript/achilles -name '*.js' -print); do node --input-type=module --check < "$file" || exit 1; done
|
|
15
|
+
RAILS_ENV=test bin/rails app:assets:precompile
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
## Build The Gem
|
|
19
|
+
|
|
20
|
+
```bash
|
|
21
|
+
gem build achilles.gemspec
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
Confirm the generated gem name matches the intended version:
|
|
25
|
+
|
|
26
|
+
```bash
|
|
27
|
+
ls achilles-*.gem
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
## Test In A Real Application
|
|
31
|
+
|
|
32
|
+
In one application that currently uses Achilles `0.1.3`, point the Gemfile to
|
|
33
|
+
the local checkout or install the built prerelease gem.
|
|
34
|
+
|
|
35
|
+
Check:
|
|
36
|
+
|
|
37
|
+
- app boot
|
|
38
|
+
- importmap rendering
|
|
39
|
+
- pages with Achilles components
|
|
40
|
+
- Turbo navigation between pages
|
|
41
|
+
- dynamic component insertion
|
|
42
|
+
- browser console errors
|
|
43
|
+
- components that use `rootElement()`
|
|
44
|
+
- components that attach window, document, timer, observer, or third-party
|
|
45
|
+
widget state
|
|
46
|
+
|
|
47
|
+
For v1, `rootElement()` returns a DOM element. If a component expects a jQuery
|
|
48
|
+
object, update it to use DOM APIs or wrap explicitly with
|
|
49
|
+
`$(this.rootElement())`.
|
|
50
|
+
|
|
51
|
+
## Publish A Prerelease
|
|
52
|
+
|
|
53
|
+
Only publish `1.0.0.rc1` after local checks and GitHub CI pass.
|
|
54
|
+
|
|
55
|
+
```bash
|
|
56
|
+
gem push achilles-1.0.0.rc1.gem
|
|
57
|
+
git tag v1.0.0.rc1
|
|
58
|
+
git push origin v1.0.0.rc1
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
## Publish Final v1.0.0
|
|
62
|
+
|
|
63
|
+
Publish final `1.0.0` only after at least one real application has successfully
|
|
64
|
+
tested the release candidate.
|
|
65
|
+
|
|
66
|
+
Before final release:
|
|
67
|
+
|
|
68
|
+
- update `lib/achilles/version.rb` to `1.0.0`
|
|
69
|
+
- update `CHANGELOG.md` from `1.0.0.rc1` to `1.0.0`
|
|
70
|
+
- run all local checks
|
|
71
|
+
- confirm GitHub CI is green
|
|
72
|
+
- build and push `achilles-1.0.0.gem`
|
|
73
|
+
- tag `v1.0.0`
|
data/docs/v1-roadmap.md
ADDED
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
# Achilles v1 Roadmap
|
|
2
|
+
|
|
3
|
+
This document tracks the work needed before publishing Achilles `1.0.0`.
|
|
4
|
+
The goal is a stable open source release that is safe to evaluate in real Rails
|
|
5
|
+
applications, even if it intentionally removes legacy behavior from the `0.x`
|
|
6
|
+
line.
|
|
7
|
+
|
|
8
|
+
## Product Direction
|
|
9
|
+
|
|
10
|
+
Achilles is a small JavaScript lifecycle layer for Rails + Turbo applications.
|
|
11
|
+
It gives teams explicit component classes mapped to DOM nodes without requiring
|
|
12
|
+
Stimulus controller conventions or a JavaScript build step.
|
|
13
|
+
|
|
14
|
+
## v1 Public API
|
|
15
|
+
|
|
16
|
+
The v1 API should be small and documented:
|
|
17
|
+
|
|
18
|
+
- `Application`
|
|
19
|
+
- `ComponentBase`
|
|
20
|
+
- `ComponentsClassMapper`
|
|
21
|
+
- DOM discovery through `data-component-class`
|
|
22
|
+
- component root lookup through `rootElement()`
|
|
23
|
+
- `rootNode()` as an alias for `rootElement()`
|
|
24
|
+
- CSS selector lookup through `rootElementSelector()`
|
|
25
|
+
- lifecycle hooks: `setup()` and `teardown()`
|
|
26
|
+
- Turbo integration through `turbo:load` and `turbo:before-render`
|
|
27
|
+
|
|
28
|
+
## Planned Breaking Changes
|
|
29
|
+
|
|
30
|
+
- Make `rootElement()` return the component's DOM element.
|
|
31
|
+
- Keep `rootNode()` as an alias for `rootElement()`.
|
|
32
|
+
- Remove the jQuery return behavior from `rootElement()`.
|
|
33
|
+
- Keep Achilles internals independent from jQuery.
|
|
34
|
+
- Require every component root to have a unique `id`.
|
|
35
|
+
- Treat setup and teardown as idempotent lifecycle operations managed by
|
|
36
|
+
Achilles.
|
|
37
|
+
|
|
38
|
+
## Release Gates
|
|
39
|
+
|
|
40
|
+
Do not publish `1.0.0` until these are true:
|
|
41
|
+
|
|
42
|
+
- Rails test suite passes.
|
|
43
|
+
- JavaScript lifecycle tests cover parser, registry, Turbo hooks, root lookup,
|
|
44
|
+
setup idempotence, teardown idempotence, missing elements, and dynamic DOM
|
|
45
|
+
insertion.
|
|
46
|
+
- Dummy app asset precompile passes.
|
|
47
|
+
- README includes install, usage, lifecycle, dynamic DOM, migration, and API
|
|
48
|
+
reference sections.
|
|
49
|
+
- `CHANGELOG.md` documents all breaking changes from `0.1.3`.
|
|
50
|
+
- CI runs tests on supported Ruby and Rails versions.
|
|
51
|
+
- At least one real application has tested the release candidate.
|
|
52
|
+
|
|
53
|
+
## Release Plan
|
|
54
|
+
|
|
55
|
+
1. Publish a compatibility release in the `0.x` line with dependency and
|
|
56
|
+
lifecycle fixes.
|
|
57
|
+
2. Create a `v1.0.0.rc1` prerelease after the v1 API and tests are complete.
|
|
58
|
+
3. Test `v1.0.0.rc1` in existing applications.
|
|
59
|
+
4. Publish `1.0.0` only after the migration guide and real-app testing are done.
|
|
60
|
+
|
|
61
|
+
Follow the [release checklist](release-checklist.md) when building and testing
|
|
62
|
+
release candidates.
|
|
63
|
+
|
|
64
|
+
## Current Recommendation
|
|
65
|
+
|
|
66
|
+
Do not push `v1.0.0` yet. The project is ready for a compatibility release, but
|
|
67
|
+
`1.0.0` should wait for the final v1 API cleanup and real-application testing
|
|
68
|
+
through a release candidate.
|
data/lib/achilles/version.rb
CHANGED
data/lib/achilles.rb
CHANGED
metadata
CHANGED
|
@@ -1,14 +1,13 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: achilles
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 1.0.0.rc1
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Jey Geethan
|
|
8
|
-
autorequire:
|
|
9
8
|
bindir: bin
|
|
10
9
|
cert_chain: []
|
|
11
|
-
date:
|
|
10
|
+
date: 1980-01-02 00:00:00.000000000 Z
|
|
12
11
|
dependencies:
|
|
13
12
|
- !ruby/object:Gem::Dependency
|
|
14
13
|
name: rails
|
|
@@ -24,13 +23,43 @@ dependencies:
|
|
|
24
23
|
- - ">="
|
|
25
24
|
- !ruby/object:Gem::Version
|
|
26
25
|
version: 7.0.2.3
|
|
27
|
-
|
|
26
|
+
- !ruby/object:Gem::Dependency
|
|
27
|
+
name: importmap-rails
|
|
28
|
+
requirement: !ruby/object:Gem::Requirement
|
|
29
|
+
requirements:
|
|
30
|
+
- - ">="
|
|
31
|
+
- !ruby/object:Gem::Version
|
|
32
|
+
version: 2.0.0
|
|
33
|
+
type: :runtime
|
|
34
|
+
prerelease: false
|
|
35
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
36
|
+
requirements:
|
|
37
|
+
- - ">="
|
|
38
|
+
- !ruby/object:Gem::Version
|
|
39
|
+
version: 2.0.0
|
|
40
|
+
- !ruby/object:Gem::Dependency
|
|
41
|
+
name: turbo-rails
|
|
42
|
+
requirement: !ruby/object:Gem::Requirement
|
|
43
|
+
requirements:
|
|
44
|
+
- - ">="
|
|
45
|
+
- !ruby/object:Gem::Version
|
|
46
|
+
version: 2.0.11
|
|
47
|
+
type: :runtime
|
|
48
|
+
prerelease: false
|
|
49
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
50
|
+
requirements:
|
|
51
|
+
- - ">="
|
|
52
|
+
- !ruby/object:Gem::Version
|
|
53
|
+
version: 2.0.11
|
|
54
|
+
description: A small JavaScript lifecycle layer for Rails and Turbo applications,
|
|
55
|
+
positioned as an explicit component-class alternative to Stimulus.
|
|
28
56
|
email:
|
|
29
57
|
- jey@jeygeethan.com
|
|
30
58
|
executables: []
|
|
31
59
|
extensions: []
|
|
32
60
|
extra_rdoc_files: []
|
|
33
61
|
files:
|
|
62
|
+
- CHANGELOG.md
|
|
34
63
|
- MIT-LICENSE
|
|
35
64
|
- README.md
|
|
36
65
|
- Rakefile
|
|
@@ -54,6 +83,9 @@ files:
|
|
|
54
83
|
- app/views/layouts/achilles/application.html.erb
|
|
55
84
|
- config/importmap.rb
|
|
56
85
|
- config/routes.rb
|
|
86
|
+
- docs/migrating-from-0.1.3-to-v1.md
|
|
87
|
+
- docs/release-checklist.md
|
|
88
|
+
- docs/v1-roadmap.md
|
|
57
89
|
- lib/achilles.rb
|
|
58
90
|
- lib/achilles/engine.rb
|
|
59
91
|
- lib/achilles/version.rb
|
|
@@ -63,10 +95,8 @@ licenses:
|
|
|
63
95
|
- MIT
|
|
64
96
|
metadata:
|
|
65
97
|
allowed_push_host: https://rubygems.org
|
|
66
|
-
homepage_uri: https://github.com/RocketApex/achilles
|
|
67
98
|
source_code_uri: https://github.com/RocketApex/achilles
|
|
68
|
-
changelog_uri: https://github.com/RocketApex/achilles
|
|
69
|
-
post_install_message:
|
|
99
|
+
changelog_uri: https://github.com/RocketApex/achilles/blob/main/CHANGELOG.md
|
|
70
100
|
rdoc_options: []
|
|
71
101
|
require_paths:
|
|
72
102
|
- lib
|
|
@@ -74,15 +104,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
|
74
104
|
requirements:
|
|
75
105
|
- - ">="
|
|
76
106
|
- !ruby/object:Gem::Version
|
|
77
|
-
version: '
|
|
107
|
+
version: '3.1'
|
|
78
108
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
79
109
|
requirements:
|
|
80
110
|
- - ">="
|
|
81
111
|
- !ruby/object:Gem::Version
|
|
82
112
|
version: '0'
|
|
83
113
|
requirements: []
|
|
84
|
-
rubygems_version:
|
|
85
|
-
signing_key:
|
|
114
|
+
rubygems_version: 4.0.11
|
|
86
115
|
specification_version: 4
|
|
87
|
-
summary:
|
|
116
|
+
summary: JavaScript component lifecycle for Rails and Turbo apps
|
|
88
117
|
test_files: []
|