turbo-rails 0.7.4 → 0.7.8
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +11 -0
- data/app/assets/javascripts/turbo.js +202 -107
- data/app/channels/turbo/streams/broadcasts.rb +30 -32
- data/app/helpers/turbo/streams/action_helper.rb +2 -3
- data/app/models/turbo/streams/tag_builder.rb +2 -12
- data/lib/install/turbo_needs_redis.rb +9 -0
- data/lib/install/turbo_with_asset_pipeline.rb +8 -19
- data/lib/install/turbo_with_webpacker.rb +1 -11
- data/lib/tasks/turbo_tasks.rake +5 -0
- data/lib/turbo/version.rb +1 -1
- metadata +7 -6
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: b462e206a285eef400a2bab8a76a9eb74a9eba0d198acc1629b30095a87fa196
|
4
|
+
data.tar.gz: 1201d9ad8acb6179db7418850fe4b4afd22089b006d7d88ae23f3c87b918cbd5
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 347aa59be1a2cec371863c180680d01942d1d2f0ada0ec6bdf73f79a9492b5af33bdcb4e58b0de14f9c155cee558583e9821813ef10a9d0c11b30b6fb3cc7638
|
7
|
+
data.tar.gz: 140dfbfe9c4091351fddce82c3e717563803d3979689ac546b5a41e1215fc668793c4895466be6d37967fb274d674c643075d37945267098c4551723901d1344
|
data/README.md
CHANGED
@@ -15,6 +15,15 @@ During rendering, Turbo replaces the current `<body>` element outright and merge
|
|
15
15
|
|
16
16
|
Whereas Turbolinks previously just dealt with links, Turbo can now also process form submissions and responses. This means the entire flow in the web application is wrapped into Turbo, making all the parts fast. No more need for `data-remote=true`.
|
17
17
|
|
18
|
+
Turbo Drive can be disabled on a per-element basis by annotating the element or any of its ancestors with `data-turbo="false"`. If you want Turbo Drive to be disabled by default, then you can adjust your import like this:
|
19
|
+
|
20
|
+
```js
|
21
|
+
import { Turbo } from "@hotwired/turbo-rails"
|
22
|
+
Turbo.session.drive = false
|
23
|
+
```
|
24
|
+
|
25
|
+
Then you can use `data-turbo="true"` to enable Drive on a per-element basis.
|
26
|
+
|
18
27
|
|
19
28
|
## Turbo Frames
|
20
29
|
|
@@ -37,6 +46,7 @@ The JavaScript for Turbo can either be run through the asset pipeline, which is
|
|
37
46
|
1. Add the `turbo-rails` gem to your Gemfile: `gem 'turbo-rails'`
|
38
47
|
2. Run `./bin/bundle install`
|
39
48
|
3. Run `./bin/rails turbo:install`
|
49
|
+
4. Run `./bin/rails turbo:install:redis` to change the development Action Cable adapter from Async (the default one) to Redis. The Async adapter does not support Turbo Stream broadcasting.
|
40
50
|
|
41
51
|
Running `turbo:install` will install through NPM if Webpacker is installed in the application. Otherwise the asset pipeline version is used. To use the asset pipeline version, you must have `importmap-rails` installed first and listed higher in the Gemfile.
|
42
52
|
|
@@ -48,6 +58,7 @@ The `Turbo` instance is automatically assigned to `window.Turbo` upon import:
|
|
48
58
|
import "@hotwired/turbo-rails"
|
49
59
|
```
|
50
60
|
|
61
|
+
|
51
62
|
## Usage
|
52
63
|
|
53
64
|
You can watch [the video introduction to Hotwire](https://hotwired.dev/#screencast), which focuses extensively on demonstration Turbo in a Rails demo. Then you should familiarize yourself with [Turbo handbook](https://turbo.hotwired.dev/handbook/introduction) to understand Drive, Frames, and Streams in-depth. Finally, dive into the code documentation by starting with [`Turbo::FramesHelper`](https://github.com/hotwired/turbo-rails/blob/main/app/helpers/turbo/frames_helper.rb), [`Turbo::StreamsHelper`](https://github.com/hotwired/turbo-rails/blob/main/app/helpers/turbo/streams_helper.rb), [`Turbo::Streams::TagBuilder`](https://github.com/hotwired/turbo-rails/blob/main/app/models/turbo/streams/tag_builder.rb), and [`Turbo::Broadcastable`](https://github.com/hotwired/turbo-rails/blob/main/app/models/concerns/turbo/broadcastable.rb).
|
@@ -361,8 +361,10 @@ class FetchRequest {
|
|
361
361
|
const response = await fetch(this.url.href, fetchOptions);
|
362
362
|
return await this.receive(response);
|
363
363
|
} catch (error) {
|
364
|
-
|
365
|
-
|
364
|
+
if (error.name !== "AbortError") {
|
365
|
+
this.delegate.requestErrored(this, error);
|
366
|
+
throw error;
|
367
|
+
}
|
366
368
|
} finally {
|
367
369
|
this.delegate.requestFinished(this);
|
368
370
|
}
|
@@ -385,13 +387,15 @@ class FetchRequest {
|
|
385
387
|
return fetchResponse;
|
386
388
|
}
|
387
389
|
get fetchOptions() {
|
390
|
+
var _a;
|
388
391
|
return {
|
389
392
|
method: FetchMethod[this.method].toUpperCase(),
|
390
393
|
credentials: "same-origin",
|
391
394
|
headers: this.headers,
|
392
395
|
redirect: "follow",
|
393
396
|
body: this.body,
|
394
|
-
signal: this.abortSignal
|
397
|
+
signal: this.abortSignal,
|
398
|
+
referrer: (_a = this.delegate.referrer) === null || _a === void 0 ? void 0 : _a.href
|
395
399
|
};
|
396
400
|
}
|
397
401
|
get defaultHeaders() {
|
@@ -544,7 +548,8 @@ class FormSubmission {
|
|
544
548
|
}
|
545
549
|
get action() {
|
546
550
|
var _a;
|
547
|
-
|
551
|
+
const formElementAction = typeof this.formElement.action === "string" ? this.formElement.action : null;
|
552
|
+
return ((_a = this.submitter) === null || _a === void 0 ? void 0 : _a.getAttribute("formaction")) || this.formElement.getAttribute("action") || formElementAction || "";
|
548
553
|
}
|
549
554
|
get location() {
|
550
555
|
return expandURL(this.action);
|
@@ -691,11 +696,7 @@ class Snapshot {
|
|
691
696
|
return this.getElementForAnchor(anchor) != null;
|
692
697
|
}
|
693
698
|
getElementForAnchor(anchor) {
|
694
|
-
|
695
|
-
return this.element.querySelector(`[id='${anchor}'], a[name='${anchor}']`);
|
696
|
-
} catch (_a) {
|
697
|
-
return null;
|
698
|
-
}
|
699
|
+
return anchor ? this.element.querySelector(`[id='${anchor}'], a[name='${anchor}']`) : null;
|
699
700
|
}
|
700
701
|
get isConnected() {
|
701
702
|
return this.element.isConnected;
|
@@ -785,6 +786,12 @@ class View {
|
|
785
786
|
scrollToPosition({x: x, y: y}) {
|
786
787
|
this.scrollRoot.scrollTo(x, y);
|
787
788
|
}
|
789
|
+
scrollToTop() {
|
790
|
+
this.scrollToPosition({
|
791
|
+
x: 0,
|
792
|
+
y: 0
|
793
|
+
});
|
794
|
+
}
|
788
795
|
get scrollRoot() {
|
789
796
|
return window;
|
790
797
|
}
|
@@ -1354,6 +1361,9 @@ class Visit {
|
|
1354
1361
|
get restorationData() {
|
1355
1362
|
return this.history.getRestorationDataForIdentifier(this.restorationIdentifier);
|
1356
1363
|
}
|
1364
|
+
get silent() {
|
1365
|
+
return this.isSamePage;
|
1366
|
+
}
|
1357
1367
|
start() {
|
1358
1368
|
if (this.state == VisitState.initialized) {
|
1359
1369
|
this.recordTimingMetric(TimingMetric.visitStart);
|
@@ -1377,6 +1387,7 @@ class Visit {
|
|
1377
1387
|
this.state = VisitState.completed;
|
1378
1388
|
this.adapter.visitCompleted(this);
|
1379
1389
|
this.delegate.visitCompleted(this);
|
1390
|
+
this.followRedirect();
|
1380
1391
|
}
|
1381
1392
|
}
|
1382
1393
|
fail() {
|
@@ -1483,8 +1494,10 @@ class Visit {
|
|
1483
1494
|
}
|
1484
1495
|
followRedirect() {
|
1485
1496
|
if (this.redirectedToLocation && !this.followedRedirect) {
|
1486
|
-
this.
|
1487
|
-
|
1497
|
+
this.adapter.visitProposedToLocation(this.redirectedToLocation, {
|
1498
|
+
action: "replace",
|
1499
|
+
response: this.response
|
1500
|
+
});
|
1488
1501
|
this.followedRedirect = true;
|
1489
1502
|
}
|
1490
1503
|
}
|
@@ -1538,9 +1551,9 @@ class Visit {
|
|
1538
1551
|
performScroll() {
|
1539
1552
|
if (!this.scrolled) {
|
1540
1553
|
if (this.action == "restore") {
|
1541
|
-
this.scrollToRestoredPosition() || this.scrollToAnchor() || this.scrollToTop();
|
1554
|
+
this.scrollToRestoredPosition() || this.scrollToAnchor() || this.view.scrollToTop();
|
1542
1555
|
} else {
|
1543
|
-
this.scrollToAnchor() || this.scrollToTop();
|
1556
|
+
this.scrollToAnchor() || this.view.scrollToTop();
|
1544
1557
|
}
|
1545
1558
|
if (this.isSamePage) {
|
1546
1559
|
this.delegate.visitScrolledToSamePageLocation(this.view.lastRenderedLocation, this.location);
|
@@ -1562,12 +1575,6 @@ class Visit {
|
|
1562
1575
|
return true;
|
1563
1576
|
}
|
1564
1577
|
}
|
1565
|
-
scrollToTop() {
|
1566
|
-
this.view.scrollToPosition({
|
1567
|
-
x: 0,
|
1568
|
-
y: 0
|
1569
|
-
});
|
1570
|
-
}
|
1571
1578
|
recordTimingMetric(metric) {
|
1572
1579
|
this.timingMetrics[metric] = (new Date).getTime();
|
1573
1580
|
}
|
@@ -1643,7 +1650,7 @@ class BrowserAdapter {
|
|
1643
1650
|
visitRequestStarted(visit) {
|
1644
1651
|
this.progressBar.setValue(0);
|
1645
1652
|
if (visit.hasCachedSnapshot() || visit.action != "restore") {
|
1646
|
-
this.
|
1653
|
+
this.showVisitProgressBarAfterDelay();
|
1647
1654
|
} else {
|
1648
1655
|
this.showProgressBar();
|
1649
1656
|
}
|
@@ -1664,24 +1671,42 @@ class BrowserAdapter {
|
|
1664
1671
|
}
|
1665
1672
|
visitRequestFinished(visit) {
|
1666
1673
|
this.progressBar.setValue(1);
|
1667
|
-
this.
|
1668
|
-
}
|
1669
|
-
visitCompleted(visit) {
|
1670
|
-
visit.followRedirect();
|
1674
|
+
this.hideVisitProgressBar();
|
1671
1675
|
}
|
1676
|
+
visitCompleted(visit) {}
|
1672
1677
|
pageInvalidated() {
|
1673
1678
|
this.reload();
|
1674
1679
|
}
|
1675
1680
|
visitFailed(visit) {}
|
1676
1681
|
visitRendered(visit) {}
|
1677
|
-
|
1678
|
-
this.
|
1682
|
+
formSubmissionStarted(formSubmission) {
|
1683
|
+
this.progressBar.setValue(0);
|
1684
|
+
this.showFormProgressBarAfterDelay();
|
1679
1685
|
}
|
1680
|
-
|
1686
|
+
formSubmissionFinished(formSubmission) {
|
1687
|
+
this.progressBar.setValue(1);
|
1688
|
+
this.hideFormProgressBar();
|
1689
|
+
}
|
1690
|
+
showVisitProgressBarAfterDelay() {
|
1691
|
+
this.visitProgressBarTimeout = window.setTimeout(this.showProgressBar, this.session.progressBarDelay);
|
1692
|
+
}
|
1693
|
+
hideVisitProgressBar() {
|
1681
1694
|
this.progressBar.hide();
|
1682
|
-
if (this.
|
1683
|
-
window.clearTimeout(this.
|
1684
|
-
delete this.
|
1695
|
+
if (this.visitProgressBarTimeout != null) {
|
1696
|
+
window.clearTimeout(this.visitProgressBarTimeout);
|
1697
|
+
delete this.visitProgressBarTimeout;
|
1698
|
+
}
|
1699
|
+
}
|
1700
|
+
showFormProgressBarAfterDelay() {
|
1701
|
+
if (this.formProgressBarTimeout == null) {
|
1702
|
+
this.formProgressBarTimeout = window.setTimeout(this.showProgressBar, this.session.progressBarDelay);
|
1703
|
+
}
|
1704
|
+
}
|
1705
|
+
hideFormProgressBar() {
|
1706
|
+
this.progressBar.hide();
|
1707
|
+
if (this.formProgressBarTimeout != null) {
|
1708
|
+
window.clearTimeout(this.formProgressBarTimeout);
|
1709
|
+
delete this.formProgressBarTimeout;
|
1685
1710
|
}
|
1686
1711
|
}
|
1687
1712
|
reload() {
|
@@ -1772,6 +1797,7 @@ class FrameRedirector {
|
|
1772
1797
|
linkClickIntercepted(element, url) {
|
1773
1798
|
const frame = this.findFrameElement(element);
|
1774
1799
|
if (frame) {
|
1800
|
+
frame.setAttribute("reloadable", "");
|
1775
1801
|
frame.src = url;
|
1776
1802
|
}
|
1777
1803
|
}
|
@@ -1781,6 +1807,7 @@ class FrameRedirector {
|
|
1781
1807
|
formSubmissionIntercepted(element, submitter) {
|
1782
1808
|
const frame = this.findFrameElement(element);
|
1783
1809
|
if (frame) {
|
1810
|
+
frame.removeAttribute("reloadable");
|
1784
1811
|
frame.delegate.formSubmissionIntercepted(element, submitter);
|
1785
1812
|
}
|
1786
1813
|
}
|
@@ -1891,7 +1918,8 @@ class LinkClickObserver {
|
|
1891
1918
|
};
|
1892
1919
|
this.clickBubbled = event => {
|
1893
1920
|
if (this.clickEventIsSignificant(event)) {
|
1894
|
-
const
|
1921
|
+
const target = event.composedPath && event.composedPath()[0] || event.target;
|
1922
|
+
const link = this.findLinkFromClickTarget(target);
|
1895
1923
|
if (link) {
|
1896
1924
|
const location = this.getLocationForLink(link);
|
1897
1925
|
if (this.delegate.willFollowLinkToLocation(link, location)) {
|
@@ -1937,7 +1965,7 @@ class Navigator {
|
|
1937
1965
|
this.delegate = delegate;
|
1938
1966
|
}
|
1939
1967
|
proposeVisit(location, options = {}) {
|
1940
|
-
if (this.delegate.
|
1968
|
+
if (this.delegate.allowsVisitingLocationWithAction(location, options.action)) {
|
1941
1969
|
this.delegate.visitProposedToLocation(location, options);
|
1942
1970
|
}
|
1943
1971
|
}
|
@@ -1978,7 +2006,11 @@ class Navigator {
|
|
1978
2006
|
get history() {
|
1979
2007
|
return this.delegate.history;
|
1980
2008
|
}
|
1981
|
-
formSubmissionStarted(formSubmission) {
|
2009
|
+
formSubmissionStarted(formSubmission) {
|
2010
|
+
if (typeof this.adapter.formSubmissionStarted === "function") {
|
2011
|
+
this.adapter.formSubmissionStarted(formSubmission);
|
2012
|
+
}
|
2013
|
+
}
|
1982
2014
|
async formSubmissionSucceededWithResponse(formSubmission, fetchResponse) {
|
1983
2015
|
if (formSubmission == this.formSubmission) {
|
1984
2016
|
const responseHTML = await fetchResponse.responseHTML;
|
@@ -2001,14 +2033,23 @@ class Navigator {
|
|
2001
2033
|
const responseHTML = await fetchResponse.responseHTML;
|
2002
2034
|
if (responseHTML) {
|
2003
2035
|
const snapshot = PageSnapshot.fromHTMLString(responseHTML);
|
2004
|
-
|
2036
|
+
if (fetchResponse.serverError) {
|
2037
|
+
await this.view.renderError(snapshot);
|
2038
|
+
} else {
|
2039
|
+
await this.view.renderPage(snapshot);
|
2040
|
+
}
|
2041
|
+
this.view.scrollToTop();
|
2005
2042
|
this.view.clearSnapshotCache();
|
2006
2043
|
}
|
2007
2044
|
}
|
2008
2045
|
formSubmissionErrored(formSubmission, error) {
|
2009
2046
|
console.error(error);
|
2010
2047
|
}
|
2011
|
-
formSubmissionFinished(formSubmission) {
|
2048
|
+
formSubmissionFinished(formSubmission) {
|
2049
|
+
if (typeof this.adapter.formSubmissionFinished === "function") {
|
2050
|
+
this.adapter.formSubmissionFinished(formSubmission);
|
2051
|
+
}
|
2052
|
+
}
|
2012
2053
|
visitStarted(visit) {
|
2013
2054
|
this.delegate.visitStarted(visit);
|
2014
2055
|
}
|
@@ -2016,7 +2057,10 @@ class Navigator {
|
|
2016
2057
|
this.delegate.visitCompleted(visit);
|
2017
2058
|
}
|
2018
2059
|
locationWithActionIsSamePage(location, action) {
|
2019
|
-
|
2060
|
+
const anchor = getAnchor(location);
|
2061
|
+
const currentAnchor = getAnchor(this.view.lastRenderedLocation);
|
2062
|
+
const isRestorationToTop = action === "restore" && typeof anchor === "undefined";
|
2063
|
+
return action !== "replace" && getRequestURL(location) === getRequestURL(this.view.lastRenderedLocation) && (isRestorationToTop || anchor != null && anchor !== currentAnchor);
|
2020
2064
|
}
|
2021
2065
|
visitScrolledToSamePageLocation(oldURL, newURL) {
|
2022
2066
|
this.delegate.visitScrolledToSamePageLocation(oldURL, newURL);
|
@@ -2408,6 +2452,7 @@ class Session {
|
|
2408
2452
|
this.scrollObserver = new ScrollObserver(this);
|
2409
2453
|
this.streamObserver = new StreamObserver(this);
|
2410
2454
|
this.frameRedirector = new FrameRedirector(document.documentElement);
|
2455
|
+
this.drive = true;
|
2411
2456
|
this.enabled = true;
|
2412
2457
|
this.progressBarDelay = 500;
|
2413
2458
|
this.started = false;
|
@@ -2485,7 +2530,7 @@ class Session {
|
|
2485
2530
|
});
|
2486
2531
|
}
|
2487
2532
|
willFollowLinkToLocation(link, location) {
|
2488
|
-
return
|
2533
|
+
return this.elementDriveEnabled(link) && this.locationIsVisitable(location) && this.applicationAllowsFollowingLinkToLocation(link, location);
|
2489
2534
|
}
|
2490
2535
|
followedLinkToLocation(link, location) {
|
2491
2536
|
const action = this.getActionForLink(link);
|
@@ -2494,13 +2539,12 @@ class Session {
|
|
2494
2539
|
});
|
2495
2540
|
}
|
2496
2541
|
convertLinkWithMethodClickToFormSubmission(link) {
|
2497
|
-
var _a;
|
2498
2542
|
const linkMethod = link.getAttribute("data-turbo-method");
|
2499
2543
|
if (linkMethod) {
|
2500
2544
|
const form = document.createElement("form");
|
2501
2545
|
form.method = linkMethod;
|
2502
2546
|
form.action = link.getAttribute("href") || "undefined";
|
2503
|
-
|
2547
|
+
document.body.appendChild(form);
|
2504
2548
|
return dispatch("submit", {
|
2505
2549
|
cancelable: true,
|
2506
2550
|
target: form
|
@@ -2509,8 +2553,8 @@ class Session {
|
|
2509
2553
|
return false;
|
2510
2554
|
}
|
2511
2555
|
}
|
2512
|
-
|
2513
|
-
return this.applicationAllowsVisitingLocation(location);
|
2556
|
+
allowsVisitingLocationWithAction(location, action) {
|
2557
|
+
return this.locationWithActionIsSamePage(location, action) || this.applicationAllowsVisitingLocation(location);
|
2514
2558
|
}
|
2515
2559
|
visitProposedToLocation(location, options) {
|
2516
2560
|
extendURLWithDeprecatedProperties(location);
|
@@ -2518,7 +2562,9 @@ class Session {
|
|
2518
2562
|
}
|
2519
2563
|
visitStarted(visit) {
|
2520
2564
|
extendURLWithDeprecatedProperties(visit.location);
|
2521
|
-
|
2565
|
+
if (!visit.silent) {
|
2566
|
+
this.notifyApplicationAfterVisitingLocation(visit.location, visit.action);
|
2567
|
+
}
|
2522
2568
|
}
|
2523
2569
|
visitCompleted(visit) {
|
2524
2570
|
this.notifyApplicationAfterPageLoad(visit.getTimingMetrics());
|
@@ -2530,7 +2576,7 @@ class Session {
|
|
2530
2576
|
this.notifyApplicationAfterVisitingSamePageLocation(oldURL, newURL);
|
2531
2577
|
}
|
2532
2578
|
willSubmitForm(form, submitter) {
|
2533
|
-
return
|
2579
|
+
return this.elementDriveEnabled(form) && this.elementDriveEnabled(submitter);
|
2534
2580
|
}
|
2535
2581
|
formSubmitted(form, submitter) {
|
2536
2582
|
this.navigator.submitForm(form, submitter);
|
@@ -2549,7 +2595,10 @@ class Session {
|
|
2549
2595
|
this.renderStreamMessage(message);
|
2550
2596
|
}
|
2551
2597
|
viewWillCacheSnapshot() {
|
2552
|
-
|
2598
|
+
var _a;
|
2599
|
+
if (!((_a = this.navigator.currentVisit) === null || _a === void 0 ? void 0 : _a.silent)) {
|
2600
|
+
this.notifyApplicationBeforeCachingSnapshot();
|
2601
|
+
}
|
2553
2602
|
}
|
2554
2603
|
allowsImmediateRender({element: element}, resume) {
|
2555
2604
|
const event = this.notifyApplicationBeforeRender(element, resume);
|
@@ -2562,6 +2611,12 @@ class Session {
|
|
2562
2611
|
viewInvalidated() {
|
2563
2612
|
this.adapter.pageInvalidated();
|
2564
2613
|
}
|
2614
|
+
frameLoaded(frame) {
|
2615
|
+
this.notifyApplicationAfterFrameLoad(frame);
|
2616
|
+
}
|
2617
|
+
frameRendered(fetchResponse, frame) {
|
2618
|
+
this.notifyApplicationAfterFrameRender(fetchResponse, frame);
|
2619
|
+
}
|
2565
2620
|
applicationAllowsFollowingLinkToLocation(link, location) {
|
2566
2621
|
const event = this.notifyApplicationAfterClickingLinkToLocation(link, location);
|
2567
2622
|
return !event.defaultPrevented;
|
@@ -2624,6 +2679,36 @@ class Session {
|
|
2624
2679
|
newURL: newURL.toString()
|
2625
2680
|
}));
|
2626
2681
|
}
|
2682
|
+
notifyApplicationAfterFrameLoad(frame) {
|
2683
|
+
return dispatch("turbo:frame-load", {
|
2684
|
+
target: frame
|
2685
|
+
});
|
2686
|
+
}
|
2687
|
+
notifyApplicationAfterFrameRender(fetchResponse, frame) {
|
2688
|
+
return dispatch("turbo:frame-render", {
|
2689
|
+
detail: {
|
2690
|
+
fetchResponse: fetchResponse
|
2691
|
+
},
|
2692
|
+
target: frame,
|
2693
|
+
cancelable: true
|
2694
|
+
});
|
2695
|
+
}
|
2696
|
+
elementDriveEnabled(element) {
|
2697
|
+
const container = element === null || element === void 0 ? void 0 : element.closest("[data-turbo]");
|
2698
|
+
if (this.drive) {
|
2699
|
+
if (container) {
|
2700
|
+
return container.getAttribute("data-turbo") != "false";
|
2701
|
+
} else {
|
2702
|
+
return true;
|
2703
|
+
}
|
2704
|
+
} else {
|
2705
|
+
if (container) {
|
2706
|
+
return container.getAttribute("data-turbo") == "true";
|
2707
|
+
} else {
|
2708
|
+
return false;
|
2709
|
+
}
|
2710
|
+
}
|
2711
|
+
}
|
2627
2712
|
getActionForLink(link) {
|
2628
2713
|
const action = link.getAttribute("data-turbo-action");
|
2629
2714
|
return isAction(action) ? action : "advance";
|
@@ -2636,15 +2721,6 @@ class Session {
|
|
2636
2721
|
}
|
2637
2722
|
}
|
2638
2723
|
|
2639
|
-
function elementIsNavigable(element) {
|
2640
|
-
const container = element === null || element === void 0 ? void 0 : element.closest("[data-turbo]");
|
2641
|
-
if (container) {
|
2642
|
-
return container.getAttribute("data-turbo") != "false";
|
2643
|
-
} else {
|
2644
|
-
return true;
|
2645
|
-
}
|
2646
|
-
}
|
2647
|
-
|
2648
2724
|
function extendURLWithDeprecatedProperties(url) {
|
2649
2725
|
Object.defineProperties(url, deprecatedLocationPropertyDescriptors);
|
2650
2726
|
}
|
@@ -2657,6 +2733,58 @@ const deprecatedLocationPropertyDescriptors = {
|
|
2657
2733
|
}
|
2658
2734
|
};
|
2659
2735
|
|
2736
|
+
const session = new Session;
|
2737
|
+
|
2738
|
+
const {navigator: navigator} = session;
|
2739
|
+
|
2740
|
+
function start() {
|
2741
|
+
session.start();
|
2742
|
+
}
|
2743
|
+
|
2744
|
+
function registerAdapter(adapter) {
|
2745
|
+
session.registerAdapter(adapter);
|
2746
|
+
}
|
2747
|
+
|
2748
|
+
function visit(location, options) {
|
2749
|
+
session.visit(location, options);
|
2750
|
+
}
|
2751
|
+
|
2752
|
+
function connectStreamSource(source) {
|
2753
|
+
session.connectStreamSource(source);
|
2754
|
+
}
|
2755
|
+
|
2756
|
+
function disconnectStreamSource(source) {
|
2757
|
+
session.disconnectStreamSource(source);
|
2758
|
+
}
|
2759
|
+
|
2760
|
+
function renderStreamMessage(message) {
|
2761
|
+
session.renderStreamMessage(message);
|
2762
|
+
}
|
2763
|
+
|
2764
|
+
function clearCache() {
|
2765
|
+
session.clearCache();
|
2766
|
+
}
|
2767
|
+
|
2768
|
+
function setProgressBarDelay(delay) {
|
2769
|
+
session.setProgressBarDelay(delay);
|
2770
|
+
}
|
2771
|
+
|
2772
|
+
var Turbo = Object.freeze({
|
2773
|
+
__proto__: null,
|
2774
|
+
navigator: navigator,
|
2775
|
+
session: session,
|
2776
|
+
PageRenderer: PageRenderer,
|
2777
|
+
PageSnapshot: PageSnapshot,
|
2778
|
+
start: start,
|
2779
|
+
registerAdapter: registerAdapter,
|
2780
|
+
visit: visit,
|
2781
|
+
connectStreamSource: connectStreamSource,
|
2782
|
+
disconnectStreamSource: disconnectStreamSource,
|
2783
|
+
renderStreamMessage: renderStreamMessage,
|
2784
|
+
clearCache: clearCache,
|
2785
|
+
setProgressBarDelay: setProgressBarDelay
|
2786
|
+
});
|
2787
|
+
|
2660
2788
|
class FrameController {
|
2661
2789
|
constructor(element) {
|
2662
2790
|
this.resolveVisitPromise = () => {};
|
@@ -2672,6 +2800,7 @@ class FrameController {
|
|
2672
2800
|
connect() {
|
2673
2801
|
if (!this.connected) {
|
2674
2802
|
this.connected = true;
|
2803
|
+
this.reloadable = false;
|
2675
2804
|
if (this.loadingStyle == FrameLoadingStyle.lazy) {
|
2676
2805
|
this.appearanceObserver.start();
|
2677
2806
|
}
|
@@ -2707,7 +2836,7 @@ class FrameController {
|
|
2707
2836
|
}
|
2708
2837
|
}
|
2709
2838
|
async loadSourceURL() {
|
2710
|
-
if (!this.settingSourceURL && this.enabled && this.isActive && this.sourceURL != this.currentURL) {
|
2839
|
+
if (!this.settingSourceURL && this.enabled && this.isActive && (this.reloadable || this.sourceURL != this.currentURL)) {
|
2711
2840
|
const previousURL = this.currentURL;
|
2712
2841
|
this.currentURL = this.sourceURL;
|
2713
2842
|
if (this.sourceURL) {
|
@@ -2716,6 +2845,7 @@ class FrameController {
|
|
2716
2845
|
this.appearanceObserver.stop();
|
2717
2846
|
await this.element.loaded;
|
2718
2847
|
this.hasBeenLoaded = true;
|
2848
|
+
session.frameLoaded(this.element);
|
2719
2849
|
} catch (error) {
|
2720
2850
|
this.currentURL = previousURL;
|
2721
2851
|
throw error;
|
@@ -2735,6 +2865,7 @@ class FrameController {
|
|
2735
2865
|
const renderer = new FrameRenderer(this.view.snapshot, snapshot, false);
|
2736
2866
|
if (this.view.renderPromise) await this.view.renderPromise;
|
2737
2867
|
await this.view.render(renderer);
|
2868
|
+
session.frameRendered(fetchResponse, this.element);
|
2738
2869
|
}
|
2739
2870
|
} catch (error) {
|
2740
2871
|
console.error(error);
|
@@ -2752,6 +2883,7 @@ class FrameController {
|
|
2752
2883
|
}
|
2753
2884
|
}
|
2754
2885
|
linkClickIntercepted(element, url) {
|
2886
|
+
this.reloadable = true;
|
2755
2887
|
this.navigateFrame(element, url);
|
2756
2888
|
}
|
2757
2889
|
shouldInterceptFormSubmission(element, submitter) {
|
@@ -2761,6 +2893,7 @@ class FrameController {
|
|
2761
2893
|
if (this.formSubmission) {
|
2762
2894
|
this.formSubmission.stop();
|
2763
2895
|
}
|
2896
|
+
this.reloadable = false;
|
2764
2897
|
this.formSubmission = new FormSubmission(this, element, submitter);
|
2765
2898
|
if (this.formSubmission.fetchRequest.isIdempotent) {
|
2766
2899
|
this.navigateFrame(element, this.formSubmission.fetchRequest.url.href);
|
@@ -2864,10 +2997,10 @@ class FrameController {
|
|
2864
2997
|
return !frameElement.disabled;
|
2865
2998
|
}
|
2866
2999
|
}
|
2867
|
-
if (!
|
3000
|
+
if (!session.elementDriveEnabled(element)) {
|
2868
3001
|
return false;
|
2869
3002
|
}
|
2870
|
-
if (submitter && !
|
3003
|
+
if (submitter && !session.elementDriveEnabled(submitter)) {
|
2871
3004
|
return false;
|
2872
3005
|
}
|
2873
3006
|
return true;
|
@@ -2883,6 +3016,18 @@ class FrameController {
|
|
2883
3016
|
return this.element.src;
|
2884
3017
|
}
|
2885
3018
|
}
|
3019
|
+
get reloadable() {
|
3020
|
+
const frame = this.findFrameElement(this.element);
|
3021
|
+
return frame.hasAttribute("reloadable");
|
3022
|
+
}
|
3023
|
+
set reloadable(value) {
|
3024
|
+
const frame = this.findFrameElement(this.element);
|
3025
|
+
if (value) {
|
3026
|
+
frame.setAttribute("reloadable", "");
|
3027
|
+
} else {
|
3028
|
+
frame.removeAttribute("reloadable");
|
3029
|
+
}
|
3030
|
+
}
|
2886
3031
|
set sourceURL(sourceURL) {
|
2887
3032
|
this.settingSourceURL = true;
|
2888
3033
|
this.element.src = sourceURL !== null && sourceURL !== void 0 ? sourceURL : null;
|
@@ -3089,57 +3234,6 @@ customElements.define("turbo-stream", StreamElement);
|
|
3089
3234
|
}
|
3090
3235
|
})();
|
3091
3236
|
|
3092
|
-
const session = new Session;
|
3093
|
-
|
3094
|
-
const {navigator: navigator} = session;
|
3095
|
-
|
3096
|
-
function start() {
|
3097
|
-
session.start();
|
3098
|
-
}
|
3099
|
-
|
3100
|
-
function registerAdapter(adapter) {
|
3101
|
-
session.registerAdapter(adapter);
|
3102
|
-
}
|
3103
|
-
|
3104
|
-
function visit(location, options) {
|
3105
|
-
session.visit(location, options);
|
3106
|
-
}
|
3107
|
-
|
3108
|
-
function connectStreamSource(source) {
|
3109
|
-
session.connectStreamSource(source);
|
3110
|
-
}
|
3111
|
-
|
3112
|
-
function disconnectStreamSource(source) {
|
3113
|
-
session.disconnectStreamSource(source);
|
3114
|
-
}
|
3115
|
-
|
3116
|
-
function renderStreamMessage(message) {
|
3117
|
-
session.renderStreamMessage(message);
|
3118
|
-
}
|
3119
|
-
|
3120
|
-
function clearCache() {
|
3121
|
-
session.clearCache();
|
3122
|
-
}
|
3123
|
-
|
3124
|
-
function setProgressBarDelay(delay) {
|
3125
|
-
session.setProgressBarDelay(delay);
|
3126
|
-
}
|
3127
|
-
|
3128
|
-
var Turbo = Object.freeze({
|
3129
|
-
__proto__: null,
|
3130
|
-
navigator: navigator,
|
3131
|
-
PageRenderer: PageRenderer,
|
3132
|
-
PageSnapshot: PageSnapshot,
|
3133
|
-
start: start,
|
3134
|
-
registerAdapter: registerAdapter,
|
3135
|
-
visit: visit,
|
3136
|
-
connectStreamSource: connectStreamSource,
|
3137
|
-
disconnectStreamSource: disconnectStreamSource,
|
3138
|
-
renderStreamMessage: renderStreamMessage,
|
3139
|
-
clearCache: clearCache,
|
3140
|
-
setProgressBarDelay: setProgressBarDelay
|
3141
|
-
});
|
3142
|
-
|
3143
3237
|
window.Turbo = Turbo;
|
3144
3238
|
|
3145
3239
|
start();
|
@@ -3154,6 +3248,7 @@ var turbo_es2017Esm = Object.freeze({
|
|
3154
3248
|
navigator: navigator,
|
3155
3249
|
registerAdapter: registerAdapter,
|
3156
3250
|
renderStreamMessage: renderStreamMessage,
|
3251
|
+
session: session,
|
3157
3252
|
setProgressBarDelay: setProgressBarDelay,
|
3158
3253
|
start: start,
|
3159
3254
|
visit: visit
|
@@ -5,71 +5,69 @@
|
|
5
5
|
module Turbo::Streams::Broadcasts
|
6
6
|
include Turbo::Streams::ActionHelper
|
7
7
|
|
8
|
-
def broadcast_remove_to(*streamables,
|
9
|
-
broadcast_action_to *streamables, action: :remove,
|
8
|
+
def broadcast_remove_to(*streamables, **opts)
|
9
|
+
broadcast_action_to *streamables, action: :remove, **opts
|
10
10
|
end
|
11
11
|
|
12
|
-
def broadcast_replace_to(*streamables,
|
13
|
-
broadcast_action_to *streamables, action: :replace,
|
12
|
+
def broadcast_replace_to(*streamables, **opts)
|
13
|
+
broadcast_action_to *streamables, action: :replace, **opts
|
14
14
|
end
|
15
15
|
|
16
|
-
def broadcast_update_to(*streamables,
|
17
|
-
broadcast_action_to *streamables, action: :update,
|
16
|
+
def broadcast_update_to(*streamables, **opts)
|
17
|
+
broadcast_action_to *streamables, action: :update, **opts
|
18
18
|
end
|
19
19
|
|
20
|
-
def broadcast_before_to(*streamables,
|
21
|
-
broadcast_action_to *streamables, action: :before,
|
20
|
+
def broadcast_before_to(*streamables, **opts)
|
21
|
+
broadcast_action_to *streamables, action: :before, **opts
|
22
22
|
end
|
23
23
|
|
24
|
-
def broadcast_after_to(*streamables,
|
25
|
-
broadcast_action_to *streamables, action: :after,
|
24
|
+
def broadcast_after_to(*streamables, **opts)
|
25
|
+
broadcast_action_to *streamables, action: :after, **opts
|
26
26
|
end
|
27
27
|
|
28
|
-
def broadcast_append_to(*streamables,
|
29
|
-
broadcast_action_to *streamables, action: :append,
|
28
|
+
def broadcast_append_to(*streamables, **opts)
|
29
|
+
broadcast_action_to *streamables, action: :append, **opts
|
30
30
|
end
|
31
31
|
|
32
|
-
def broadcast_prepend_to(*streamables,
|
33
|
-
broadcast_action_to *streamables, action: :prepend,
|
32
|
+
def broadcast_prepend_to(*streamables, **opts)
|
33
|
+
broadcast_action_to *streamables, action: :prepend, **opts
|
34
34
|
end
|
35
35
|
|
36
|
-
def broadcast_action_to(*streamables, action:, target
|
37
|
-
broadcast_stream_to *streamables, content: turbo_stream_action_tag(action, target: target, template:
|
36
|
+
def broadcast_action_to(*streamables, action:, target: nil, targets: nil, **rendering)
|
37
|
+
broadcast_stream_to *streamables, content: turbo_stream_action_tag(action, target: target, targets: targets, template:
|
38
38
|
rendering.delete(:content) || (rendering.any? ? render_format(:html, **rendering) : nil)
|
39
39
|
)
|
40
40
|
end
|
41
41
|
|
42
|
-
|
43
|
-
|
44
|
-
broadcast_action_later_to *streamables, action: :replace, target: target, **rendering
|
42
|
+
def broadcast_replace_later_to(*streamables, **opts)
|
43
|
+
broadcast_action_later_to *streamables, action: :replace, **opts
|
45
44
|
end
|
46
45
|
|
47
|
-
def broadcast_update_later_to(*streamables,
|
48
|
-
broadcast_action_later_to *streamables, action: :update,
|
46
|
+
def broadcast_update_later_to(*streamables, **opts)
|
47
|
+
broadcast_action_later_to *streamables, action: :update, **opts
|
49
48
|
end
|
50
49
|
|
51
|
-
def broadcast_before_later_to(*streamables,
|
52
|
-
broadcast_action_later_to *streamables, action: :before,
|
50
|
+
def broadcast_before_later_to(*streamables, **opts)
|
51
|
+
broadcast_action_later_to *streamables, action: :before, **opts
|
53
52
|
end
|
54
53
|
|
55
|
-
def broadcast_after_later_to(*streamables,
|
56
|
-
broadcast_action_later_to *streamables, action: :after,
|
54
|
+
def broadcast_after_later_to(*streamables, **opts)
|
55
|
+
broadcast_action_later_to *streamables, action: :after, **opts
|
57
56
|
end
|
58
57
|
|
59
|
-
def broadcast_append_later_to(*streamables,
|
60
|
-
broadcast_action_later_to *streamables, action: :append,
|
58
|
+
def broadcast_append_later_to(*streamables, **opts)
|
59
|
+
broadcast_action_later_to *streamables, action: :append, **opts
|
61
60
|
end
|
62
61
|
|
63
|
-
def broadcast_prepend_later_to(*streamables,
|
64
|
-
broadcast_action_later_to *streamables, action: :prepend,
|
62
|
+
def broadcast_prepend_later_to(*streamables, **opts)
|
63
|
+
broadcast_action_later_to *streamables, action: :prepend, **opts
|
65
64
|
end
|
66
65
|
|
67
|
-
def broadcast_action_later_to(*streamables, action:, target
|
66
|
+
def broadcast_action_later_to(*streamables, action:, target: nil, targets: nil, **rendering)
|
68
67
|
Turbo::Streams::ActionBroadcastJob.perform_later \
|
69
|
-
stream_name_from(streamables), action: action, target: target, **rendering
|
68
|
+
stream_name_from(streamables), action: action, target: target, targets: targets, **rendering
|
70
69
|
end
|
71
70
|
|
72
|
-
|
73
71
|
def broadcast_render_to(*streamables, **rendering)
|
74
72
|
broadcast_stream_to *streamables, content: render_format(:turbo_stream, **rendering)
|
75
73
|
end
|
@@ -12,10 +12,9 @@ module Turbo::Streams::ActionHelper
|
|
12
12
|
def turbo_stream_action_tag(action, target: nil, targets: nil, template: nil)
|
13
13
|
template = action.to_sym == :remove ? "" : "<template>#{template}</template>"
|
14
14
|
|
15
|
-
if target
|
16
|
-
target = convert_to_turbo_stream_dom_id(target)
|
15
|
+
if target = convert_to_turbo_stream_dom_id(target)
|
17
16
|
%(<turbo-stream action="#{action}" target="#{target}">#{template}</turbo-stream>).html_safe
|
18
|
-
elsif targets
|
17
|
+
elsif targets = convert_to_turbo_stream_dom_id(targets)
|
19
18
|
%(<turbo-stream action="#{action}" targets="#{targets}">#{template}</turbo-stream>).html_safe
|
20
19
|
else
|
21
20
|
raise ArgumentError, "target or targets must be supplied"
|
@@ -212,29 +212,19 @@ class Turbo::Streams::TagBuilder
|
|
212
212
|
|
213
213
|
# Send an action of the type <tt>name</tt> to <tt>target</tt>. Options described in the concrete methods.
|
214
214
|
def action(name, target, content = nil, allow_inferred_rendering: true, **rendering, &block)
|
215
|
-
target_name = extract_target_name_from(target)
|
216
215
|
template = render_template(target, content, allow_inferred_rendering: allow_inferred_rendering, **rendering, &block)
|
217
216
|
|
218
|
-
turbo_stream_action_tag name, target:
|
217
|
+
turbo_stream_action_tag name, target: target, template: template
|
219
218
|
end
|
220
219
|
|
221
220
|
# Send an action of the type <tt>name</tt> to <tt>targets</tt>. Options described in the concrete methods.
|
222
221
|
def action_all(name, targets, content = nil, allow_inferred_rendering: true, **rendering, &block)
|
223
|
-
targets_name = extract_target_name_from(targets)
|
224
222
|
template = render_template(targets, content, allow_inferred_rendering: allow_inferred_rendering, **rendering, &block)
|
225
223
|
|
226
|
-
turbo_stream_action_tag name, targets:
|
224
|
+
turbo_stream_action_tag name, targets: targets, template: template
|
227
225
|
end
|
228
226
|
|
229
227
|
private
|
230
|
-
def extract_target_name_from(target)
|
231
|
-
if target.respond_to?(:to_key)
|
232
|
-
ActionView::RecordIdentifier.dom_id(target)
|
233
|
-
else
|
234
|
-
target
|
235
|
-
end
|
236
|
-
end
|
237
|
-
|
238
228
|
def render_template(target, content = nil, allow_inferred_rendering: true, **rendering, &block)
|
239
229
|
case
|
240
230
|
when content
|
@@ -0,0 +1,9 @@
|
|
1
|
+
if (cable_config_path = Rails.root.join("config/cable.yml")).exist?
|
2
|
+
say "Enable redis in bundle"
|
3
|
+
uncomment_lines "Gemfile", %(gem 'redis')
|
4
|
+
|
5
|
+
say "Switch development cable to use redis"
|
6
|
+
gsub_file cable_config_path.to_s, /development:\n\s+adapter: async/, "development:\n adapter: redis\n url: redis://localhost:6379/1"
|
7
|
+
else
|
8
|
+
say 'ActionCable config file (config/cable.yml) is missing. Uncomment "gem \'redis\'" in your Gemfile and create config/cable.yml to use the Turbo Streams broadcast feature.'
|
9
|
+
end
|
@@ -1,34 +1,23 @@
|
|
1
|
-
|
2
|
-
CABLE_CONFIG_PATH = Rails.root.join("config/cable.yml")
|
3
|
-
IMPORTMAP_PATH = Rails.root.join("config/importmap.rb")
|
4
|
-
|
5
|
-
if APP_JS_PATH.exist?
|
1
|
+
if (app_js_path = Rails.root.join("app/javascript/application.js")).exist?
|
6
2
|
say "Import turbo-rails in existing app/javascript/application.js"
|
7
|
-
append_to_file
|
3
|
+
append_to_file app_js_path, %(import "@hotwired/turbo-rails"\n)
|
8
4
|
else
|
9
5
|
say <<~INSTRUCTIONS, :red
|
10
|
-
You must import @
|
6
|
+
You must import @hotwired/turbo-rails in your application.js.
|
11
7
|
INSTRUCTIONS
|
12
8
|
end
|
13
9
|
|
14
|
-
if
|
10
|
+
if (importmap_path = Rails.root.join("config/importmap.rb")).exist?
|
15
11
|
say "Pin @hotwired/turbo-rails in config/importmap.rb"
|
16
12
|
insert_into_file \
|
17
|
-
|
13
|
+
importmap_path.to_s,
|
18
14
|
%( pin "@hotwired/turbo-rails", to: "turbo.js"\n\n),
|
19
15
|
after: "Rails.application.config.importmap.draw do\n"
|
20
16
|
else
|
21
17
|
say <<~INSTRUCTIONS, :red
|
22
|
-
You must add @
|
18
|
+
You must add @hotwired/turbo-rails to your importmap to reference them via ESM.
|
19
|
+
Example: pin "@hotwired/turbo-rails", to: "turbo.js"
|
23
20
|
INSTRUCTIONS
|
24
21
|
end
|
25
22
|
|
26
|
-
|
27
|
-
say "Enable redis in bundle"
|
28
|
-
uncomment_lines "Gemfile", %(gem 'redis')
|
29
|
-
|
30
|
-
say "Switch development cable to use redis"
|
31
|
-
gsub_file CABLE_CONFIG_PATH.to_s, /development:\n\s+adapter: async/, "development:\n adapter: redis\n url: redis://localhost:6379/1"
|
32
|
-
else
|
33
|
-
say 'ActionCable config file (config/cable.yml) is missing. Uncomment "gem \'redis\'" in your Gemfile and create config/cable.yml to use the Turbo Streams broadcast feature.'
|
34
|
-
end
|
23
|
+
say "Run turbo:install:redis to switch on Redis and use it in development for turbo streams", :red
|
@@ -4,14 +4,4 @@ append_to_file "#{Webpacker.config.source_entry_path}/application.js", %(\nimpor
|
|
4
4
|
say "Install Turbo"
|
5
5
|
run "yarn add @hotwired/turbo-rails"
|
6
6
|
|
7
|
-
|
8
|
-
|
9
|
-
if CABLE_CONFIG_PATH.exist?
|
10
|
-
say "Enable redis in bundle"
|
11
|
-
uncomment_lines "Gemfile", %(gem 'redis')
|
12
|
-
|
13
|
-
say "Switch development cable to use redis"
|
14
|
-
gsub_file CABLE_CONFIG_PATH.to_s, /development:\n\s+adapter: async/, "development:\n adapter: redis\n url: redis://localhost:6379/1"
|
15
|
-
else
|
16
|
-
say 'ActionCable config file (config/cable.yml) is missing. Uncomment "gem \'redis\'" in your Gemfile and create config/cable.yml to use the Turbo Streams broadcast feature.'
|
17
|
-
end
|
7
|
+
say "Run turbo:install:redis to switch on Redis and use it in development for turbo streams"
|
data/lib/tasks/turbo_tasks.rake
CHANGED
data/lib/turbo/version.rb
CHANGED
metadata
CHANGED
@@ -1,16 +1,16 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: turbo-rails
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.7.
|
4
|
+
version: 0.7.8
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Sam Stephenson
|
8
8
|
- Javan Mahkmali
|
9
9
|
- David Heinemeier Hansson
|
10
|
-
autorequire:
|
10
|
+
autorequire:
|
11
11
|
bindir: bin
|
12
12
|
cert_chain: []
|
13
|
-
date: 2021-08-
|
13
|
+
date: 2021-08-30 00:00:00.000000000 Z
|
14
14
|
dependencies:
|
15
15
|
- !ruby/object:Gem::Dependency
|
16
16
|
name: rails
|
@@ -26,7 +26,7 @@ dependencies:
|
|
26
26
|
- - ">="
|
27
27
|
- !ruby/object:Gem::Version
|
28
28
|
version: 6.0.0
|
29
|
-
description:
|
29
|
+
description:
|
30
30
|
email: david@loudthinking.com
|
31
31
|
executables: []
|
32
32
|
extensions: []
|
@@ -56,6 +56,7 @@ files:
|
|
56
56
|
- app/models/concerns/turbo/broadcastable.rb
|
57
57
|
- app/models/turbo/streams/tag_builder.rb
|
58
58
|
- config/routes.rb
|
59
|
+
- lib/install/turbo_needs_redis.rb
|
59
60
|
- lib/install/turbo_with_asset_pipeline.rb
|
60
61
|
- lib/install/turbo_with_webpacker.rb
|
61
62
|
- lib/tasks/turbo_tasks.rake
|
@@ -67,7 +68,7 @@ homepage: https://github.com/hotwired/turbo-rails
|
|
67
68
|
licenses:
|
68
69
|
- MIT
|
69
70
|
metadata: {}
|
70
|
-
post_install_message:
|
71
|
+
post_install_message:
|
71
72
|
rdoc_options: []
|
72
73
|
require_paths:
|
73
74
|
- lib
|
@@ -83,7 +84,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
83
84
|
version: '0'
|
84
85
|
requirements: []
|
85
86
|
rubygems_version: 3.1.4
|
86
|
-
signing_key:
|
87
|
+
signing_key:
|
87
88
|
specification_version: 4
|
88
89
|
summary: The speed of a single-page web application without having to write any JavaScript.
|
89
90
|
test_files: []
|