stimulus_reflex 3.5.0.pre9 → 3.5.0.pre10

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of stimulus_reflex might be problematic. Click here for more details.

Files changed (103) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile +0 -1
  3. data/Gemfile.lock +122 -127
  4. data/README.md +10 -16
  5. data/app/assets/javascripts/stimulus_reflex.js +710 -505
  6. data/app/assets/javascripts/stimulus_reflex.min.js +1 -1
  7. data/app/assets/javascripts/stimulus_reflex.min.js.map +1 -1
  8. data/app/assets/javascripts/stimulus_reflex.umd.js +660 -500
  9. data/app/assets/javascripts/stimulus_reflex.umd.min.js +660 -500
  10. data/app/assets/javascripts/stimulus_reflex.umd.min.js.map +1 -1
  11. data/app/channels/stimulus_reflex/channel.rb +9 -7
  12. data/bin/console +0 -2
  13. data/bin/standardize +2 -1
  14. data/lib/generators/stimulus_reflex/stimulus_reflex_generator.rb +72 -7
  15. data/lib/generators/stimulus_reflex/templates/app/controllers/examples_controller.rb.tt +9 -0
  16. data/lib/generators/stimulus_reflex/templates/app/javascript/channels/consumer.js.tt +6 -0
  17. data/lib/generators/stimulus_reflex/templates/app/javascript/channels/index.js.esbuild.tt +4 -0
  18. data/lib/generators/stimulus_reflex/templates/app/javascript/channels/index.js.importmap.tt +2 -0
  19. data/lib/generators/stimulus_reflex/templates/app/javascript/channels/index.js.shakapacker.tt +5 -0
  20. data/lib/generators/stimulus_reflex/templates/app/javascript/channels/index.js.vite.tt +1 -0
  21. data/lib/generators/stimulus_reflex/templates/app/javascript/channels/index.js.webpacker.tt +5 -0
  22. data/lib/generators/stimulus_reflex/templates/app/javascript/config/cable_ready.js.tt +4 -0
  23. data/lib/generators/stimulus_reflex/templates/app/javascript/config/index.js.tt +2 -0
  24. data/lib/generators/stimulus_reflex/templates/app/javascript/config/mrujs.js.tt +9 -0
  25. data/lib/generators/stimulus_reflex/templates/app/javascript/config/stimulus_reflex.js.tt +5 -0
  26. data/lib/generators/stimulus_reflex/templates/app/javascript/controllers/%file_name%_controller.js.tt +141 -0
  27. data/lib/generators/stimulus_reflex/templates/app/javascript/controllers/application.js.tt +11 -0
  28. data/lib/generators/stimulus_reflex/templates/app/javascript/controllers/application_controller.js.tt +74 -0
  29. data/lib/generators/stimulus_reflex/templates/app/javascript/controllers/index.js.esbuild.tt +7 -0
  30. data/lib/generators/stimulus_reflex/templates/app/javascript/controllers/index.js.importmap.tt +5 -0
  31. data/lib/generators/stimulus_reflex/templates/app/javascript/controllers/index.js.shakapacker.tt +5 -0
  32. data/lib/generators/stimulus_reflex/templates/app/javascript/controllers/index.js.vite.tt +5 -0
  33. data/lib/generators/stimulus_reflex/templates/app/javascript/controllers/index.js.webpacker.tt +5 -0
  34. data/{test/tmp/app/reflexes/user_reflex.rb → lib/generators/stimulus_reflex/templates/app/reflexes/%file_name%_reflex.rb.tt} +38 -9
  35. data/lib/generators/stimulus_reflex/templates/app/reflexes/application_reflex.rb.tt +27 -0
  36. data/lib/generators/stimulus_reflex/templates/app/views/examples/show.html.erb.tt +207 -0
  37. data/lib/generators/stimulus_reflex/templates/config/initializers/cable_ready.rb +22 -0
  38. data/lib/generators/stimulus_reflex/templates/config/initializers/stimulus_reflex.rb +18 -13
  39. data/lib/generators/stimulus_reflex/templates/esbuild.config.mjs.tt +94 -0
  40. data/lib/install/action_cable.rb +155 -0
  41. data/lib/install/broadcaster.rb +90 -0
  42. data/lib/install/bundle.rb +56 -0
  43. data/lib/install/compression.rb +41 -0
  44. data/lib/install/config.rb +87 -0
  45. data/lib/install/development.rb +110 -0
  46. data/lib/install/esbuild.rb +114 -0
  47. data/lib/install/example.rb +22 -0
  48. data/lib/install/importmap.rb +133 -0
  49. data/lib/install/initializers.rb +25 -0
  50. data/lib/install/mrujs.rb +133 -0
  51. data/lib/install/npm_packages.rb +25 -0
  52. data/lib/install/reflexes.rb +25 -0
  53. data/lib/install/shakapacker.rb +64 -0
  54. data/lib/install/spring.rb +54 -0
  55. data/lib/install/updatable.rb +34 -0
  56. data/lib/install/vite.rb +64 -0
  57. data/lib/install/webpacker.rb +90 -0
  58. data/lib/install/yarn.rb +55 -0
  59. data/lib/stimulus_reflex/broadcasters/broadcaster.rb +15 -8
  60. data/lib/stimulus_reflex/broadcasters/page_broadcaster.rb +7 -8
  61. data/lib/stimulus_reflex/broadcasters/selector_broadcaster.rb +10 -10
  62. data/lib/stimulus_reflex/broadcasters/update.rb +3 -0
  63. data/lib/stimulus_reflex/cable_readiness.rb +29 -0
  64. data/lib/stimulus_reflex/cable_ready_channels.rb +5 -5
  65. data/lib/stimulus_reflex/callbacks.rb +17 -1
  66. data/lib/stimulus_reflex/concern_enhancer.rb +6 -4
  67. data/lib/stimulus_reflex/configuration.rb +12 -2
  68. data/lib/stimulus_reflex/dataset.rb +11 -1
  69. data/lib/stimulus_reflex/engine.rb +20 -9
  70. data/lib/stimulus_reflex/html/document.rb +59 -0
  71. data/lib/stimulus_reflex/html/document_fragment.rb +13 -0
  72. data/lib/stimulus_reflex/importmap.rb +3 -0
  73. data/lib/stimulus_reflex/installer.rb +274 -0
  74. data/lib/stimulus_reflex/open_struct_fix.rb +2 -0
  75. data/lib/stimulus_reflex/reflex.rb +25 -25
  76. data/lib/stimulus_reflex/reflex_data.rb +15 -3
  77. data/lib/stimulus_reflex/reflex_factory.rb +4 -2
  78. data/lib/stimulus_reflex/request_parameters.rb +2 -0
  79. data/lib/stimulus_reflex/utils/logger.rb +10 -0
  80. data/lib/stimulus_reflex/utils/sanity_checker.rb +8 -48
  81. data/lib/stimulus_reflex/version.rb +1 -1
  82. data/lib/stimulus_reflex.rb +2 -0
  83. data/lib/tasks/stimulus_reflex/stimulus_reflex.rake +252 -0
  84. data/package.json +34 -28
  85. data/{rollup.config.js → rollup.config.mjs} +8 -7
  86. data/stimulus_reflex.gemspec +16 -19
  87. data/yarn.lock +1320 -742
  88. metadata +124 -77
  89. data/LATEST +0 -1
  90. data/lib/generators/stimulus_reflex/initializer_generator.rb +0 -14
  91. data/test/broadcasters/broadcaster_test.rb +0 -11
  92. data/test/broadcasters/broadcaster_test_case.rb +0 -39
  93. data/test/broadcasters/nothing_broadcaster_test.rb +0 -31
  94. data/test/broadcasters/page_broadcaster_test.rb +0 -79
  95. data/test/broadcasters/selector_broadcaster_test.rb +0 -173
  96. data/test/callbacks_test.rb +0 -652
  97. data/test/concern_enhancer_test.rb +0 -54
  98. data/test/element_test.rb +0 -254
  99. data/test/generators/stimulus_reflex_generator_test.rb +0 -58
  100. data/test/reflex_test.rb +0 -43
  101. data/test/test_helper.rb +0 -71
  102. data/test/tmp/app/reflexes/application_reflex.rb +0 -12
  103. data/yarn-error.log +0 -4964
@@ -0,0 +1 @@
1
+ const channels = import.meta.globEager('./**/*_channel.js')
@@ -0,0 +1,5 @@
1
+ // Load all the channels within this directory and all subdirectories.
2
+ // Channel files must be named *_channel.js.
3
+
4
+ const channels = require.context('.', true, /_channel\.js$/)
5
+ channels.keys().forEach(channels)
@@ -0,0 +1,4 @@
1
+ import consumer from '../channels/consumer'
2
+ import CableReady from 'cable_ready'
3
+
4
+ CableReady.initialize({ consumer })
@@ -0,0 +1,2 @@
1
+ import './cable_ready'
2
+ import './stimulus_reflex'
@@ -0,0 +1,9 @@
1
+ import CableReady from "cable_ready"
2
+ import mrujs from "mrujs"
3
+ import { CableCar } from "mrujs/plugins"
4
+
5
+ mrujs.start({
6
+ plugins: [
7
+ new CableCar(CableReady)
8
+ ]
9
+ })
@@ -0,0 +1,5 @@
1
+ import { application } from "../controllers/application"
2
+ import controller from "../controllers/application_controller"
3
+ import StimulusReflex from "stimulus_reflex"
4
+
5
+ StimulusReflex.initialize(application, { controller, isolate: true })
@@ -0,0 +1,141 @@
1
+ import ApplicationController from './application_controller'
2
+
3
+ <%- if class_name == "Example" -%>
4
+ //
5
+ // This is the Stimulus controller for the Example Reflex.
6
+ //
7
+ // It corresponds to the server-side Reflex class located at /app/reflexes/example.rb
8
+ //
9
+ // Learn more at: https://docs.stimulusreflex.com/guide/reflexes
10
+ //
11
+
12
+ <%- end -%>
13
+ export default class extends ApplicationController {
14
+ connect () {
15
+ <%- if class_name == "Example" -%>
16
+ //
17
+ // StimulusReflex overrides the Stimulus `connect` method. Make sure to call
18
+ // `super.connect()` so that any code in your superclass `connect` is run.
19
+ //
20
+ <%- end -%>
21
+ super.connect()
22
+ }
23
+ <%- if class_name == "Example" -%>
24
+
25
+ // With StimulusReflex active in your project, it will continuously scan your DOM for
26
+ // `data-reflex` attributes on your elements, even if they are dynamically created.
27
+ //
28
+ // <a href="#" data-reflex="click->Example#dance">Dance!</a>
29
+ //
30
+ // We call this a "declared" Reflex, because it doesn't require any JS to run.
31
+ //
32
+ // When your user clicks this link, a Reflex is launched and it calls the `dance` method
33
+ // on your Example Reflex class. You don't have to do anything else!
34
+ //
35
+ // This Stimulus controller doesn't even need to exist for StimulusReflex to work its magic.
36
+ // https://docs.stimulusreflex.com/guide/reflexes#declaring-a-reflex-in-html-with-data-attributes
37
+ //
38
+ // However...
39
+ //
40
+ // If we do create an `example` Stimulus controller that extends `ApplicationController`,
41
+ // we can unlock several additional capabilities, including creating Reflexes with code.
42
+ //
43
+ // <a href="#" data-controller="example" data-action="example#dance">Dance!</a>
44
+ //
45
+ // StimulusReflex gives our controller a new method, `stimulate`:
46
+ //
47
+ // dance() {
48
+ // this.stimulate('Example#dance')
49
+ // }
50
+ //
51
+ // The `stimulate` method is very flexible, and it gives you the opportunity to pass
52
+ // parameter options that will be passed to the `dance` method on the server.
53
+ // https://docs.stimulusreflex.com/guide/reflexes#calling-a-reflex-in-a-stimulus-controller
54
+ //
55
+ // Reflex lifecycle methods
56
+ //
57
+ // For every method defined in your Reflex class, a matching set of optional
58
+ // lifecycle callback methods become available in this javascript controller.
59
+ // https://docs.stimulusreflex.com/guide/lifecycle#client-side-reflex-callbacks
60
+ //
61
+ // <a href="#" data-reflex="click->Example#dance" data-controller="example">Dance!</a>
62
+ //
63
+ // StimulusReflex will check for the presence of several methods:
64
+ //
65
+ // afterReflex(element, reflex, noop, id) {
66
+ // // fires after every Example Reflex action
67
+ // }
68
+ //
69
+ // afterDance(element, reflex, noop, id) {
70
+ // // fires after Example Reflexes calling the dance action
71
+ // }
72
+ //
73
+ // Arguments:
74
+ //
75
+ // element - the element that triggered the reflex
76
+ // may be different than the Stimulus controller's this.element
77
+ //
78
+ // reflex - the name of the reflex e.g. "Example#dance"
79
+ //
80
+ // error/noop - the error message (for reflexError), otherwise null
81
+ //
82
+ // id - a UUID4 or developer-provided unique identifier for each Reflex
83
+ //
84
+ // Access to the client-side Reflex objects created by this controller
85
+ //
86
+ // Every Reflex you create is represented by an object in the global Reflexes collection.
87
+ // You can access the Example Reflexes created by this controller via the `reflexes` proxy.
88
+ //
89
+ // this.reflexes.last
90
+ // this.reflexes.all
91
+ // this.reflexes.all[id]
92
+ // this.reflexes.error
93
+ //
94
+ // The proxy allows you to access the most recent Reflex, an array of all Reflexes, a specific
95
+ // Reflex specified by its `id` and an array of all Reflexes in a given lifecycle stage.
96
+ //
97
+ // If you explore the Reflex object, you'll see all relevant details,
98
+ // including the `data` that is being delivered to the server.
99
+ //
100
+ // Pretty cool, right?
101
+ //
102
+ <%- end -%>
103
+ <%- actions.each do |action| -%>
104
+
105
+ // <%= "before_#{action}".camelize(:lower) %>(element, reflex, noop, id) {
106
+ // console.log("before <%= action %>", element, reflex, id)
107
+ // }
108
+
109
+ // <%= "#{action}_queued".camelize(:lower) %>(element, reflex, noop, id) {
110
+ // console.log("<%= action %> enqueued for delivery upon connection", element, reflex, id)
111
+ // }
112
+
113
+ // <%= "#{action}_delivered".camelize(:lower) %>(element, reflex, noop, id) {
114
+ // console.log("<%= action %> delivered to the server", element, reflex, id)
115
+ // }
116
+
117
+ // <%= "#{action}_success".camelize(:lower) %>(element, reflex, noop, id) {
118
+ // console.log("<%= action %> successfully executed", element, reflex, id)
119
+ // }
120
+
121
+ // <%= "#{action}_error".camelize(:lower) %>(element, reflex, error, id) {
122
+ // console.error("<%= action %> server-side error", element, reflex, error, id)
123
+ // }
124
+
125
+ // <%= "#{action}_halted".camelize(:lower) %>(element, reflex, noop, id) {
126
+ // console.warn("<%= action %> halted before execution", element, reflex, id)
127
+ // }
128
+
129
+ // <%= "#{action}_forbidden".camelize(:lower) %>(element, reflex, noop, id) {
130
+ // console.warn("<%= action %> forbidden from executing", element, reflex, id)
131
+ // }
132
+
133
+ // <%= "after_#{action}".camelize(:lower) %>(element, reflex, noop, id) {
134
+ // console.log("<%= action %> has been executed by the server", element, reflex, id)
135
+ // }
136
+
137
+ // <%= "finalize_#{action}".camelize(:lower) %>(element, reflex, noop, id) {
138
+ // console.log("<%= action %> changes have been applied", element, reflex, id)
139
+ // }
140
+ <%- end -%>
141
+ }
@@ -0,0 +1,11 @@
1
+ import { Application } from "@hotwired/stimulus"
2
+ import consumer from "../channels/consumer"
3
+
4
+ const application = Application.start()
5
+
6
+ // Configure Stimulus development experience
7
+ application.debug = false
8
+ application.consumer = consumer
9
+ window.Stimulus = application
10
+
11
+ export { application }
@@ -0,0 +1,74 @@
1
+ import { Controller } from '@hotwired/stimulus'
2
+ import StimulusReflex from 'stimulus_reflex'
3
+
4
+ // This is the Stimulus ApplicationController.
5
+ // All StimulusReflex controllers should inherit from this class.
6
+ //
7
+ // Example:
8
+ //
9
+ // import ApplicationController from './application_controller'
10
+ //
11
+ // export default class extends ApplicationController { ... }
12
+ //
13
+ // Learn more at: https://docs.stimulusreflex.com
14
+ //
15
+
16
+ export default class extends Controller {
17
+ connect () {
18
+ StimulusReflex.register(this)
19
+ }
20
+
21
+ // Application-wide lifecycle methods
22
+ //
23
+ // Use these methods to handle lifecycle callbacks for all controllers.
24
+ // Using lifecycle methods is optional, so feel free to delete these if you don't need them.
25
+ //
26
+ // Arguments:
27
+ //
28
+ // element - the element that triggered the reflex
29
+ // may be different than the Stimulus controller's this.element
30
+ //
31
+ // reflex - the name of the reflex e.g. "Example#demo"
32
+ //
33
+ // error/noop - the error message (for reflexError), otherwise null
34
+ //
35
+ // id - a UUID4 or developer-provided unique identifier for each Reflex
36
+ //
37
+
38
+ beforeReflex (element, reflex, noop, id) {
39
+ // document.body.classList.add('wait')
40
+ }
41
+
42
+ reflexQueued (element, reflex, noop, id) {
43
+ // Reflex will be delivered to server upon reconnection
44
+ }
45
+
46
+ reflexDelivered (element, reflex, noop, id) {
47
+ // Reflex has been delivered to the server
48
+ }
49
+
50
+ reflexSuccess (element, reflex, noop, id) {
51
+ // show success message
52
+ }
53
+
54
+ reflexError (element, reflex, error, id) {
55
+ // show error message
56
+ }
57
+
58
+ reflexForbidden (element, reflex, noop, id) {
59
+ // Reflex action did not have permission to run
60
+ // window.location = '/'
61
+ }
62
+
63
+ reflexHalted (element, reflex, noop, id) {
64
+ // handle aborted Reflex action
65
+ }
66
+
67
+ afterReflex (element, reflex, noop, id) {
68
+ // document.body.classList.remove('wait')
69
+ }
70
+
71
+ finalizeReflex (element, reflex, noop, id) {
72
+ // all operations have completed, animation etc is now safe
73
+ }
74
+ }
@@ -0,0 +1,7 @@
1
+ import { application } from "./application"
2
+
3
+ import controllers from "./**/*_controller.js"
4
+
5
+ controllers.forEach((controller) => {
6
+ application.register(controller.name, controller.module.default)
7
+ })
@@ -0,0 +1,5 @@
1
+ import { application } from "./application"
2
+
3
+ // Eager load all controllers defined in the import map under controllers/**/*_controller
4
+ import { eagerLoadControllersFrom } from "@hotwired/stimulus-loading"
5
+ eagerLoadControllersFrom("controllers", application)
@@ -0,0 +1,5 @@
1
+ import { application } from "./application"
2
+ import { definitionsFromContext } from "@hotwired/stimulus-webpack-helpers"
3
+
4
+ const controllers = definitionsFromContext(require.context("controllers", true, /_controller\.js$/))
5
+ application.load(controllers)
@@ -0,0 +1,5 @@
1
+ import { application } from "./application"
2
+ import { registerControllers } from "stimulus-vite-helpers"
3
+
4
+ const controllers = import.meta.globEager("./**/*_controller.js");
5
+ registerControllers(application, controllers)
@@ -0,0 +1,5 @@
1
+ import { application } from "./application"
2
+ import { definitionsFromContext } from "@hotwired/stimulus-webpack-helpers"
3
+
4
+ const controllers = definitionsFromContext(require.context("controllers", true, /_controller\.js$/))
5
+ application.load(controllers)
@@ -1,6 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- class UserReflex < ApplicationReflex
3
+ class <%= class_name %>Reflex < ApplicationReflex
4
+ <%- if class_name == "Example" -%>
4
5
  # Add Reflex methods in this file.
5
6
  #
6
7
  # All Reflex instances include CableReady::Broadcaster and expose the following properties:
@@ -16,28 +17,56 @@ class UserReflex < ApplicationReflex
16
17
  # - signed - use a signed Global ID to map dataset attribute to a model eg. element.signed[:foo]
17
18
  # - unsigned - use an unsigned Global ID to map dataset attribute to a model eg. element.unsigned[:foo]
18
19
  # - cable_ready - a special cable_ready that can broadcast to the current visitor (no brackets needed)
19
- # - reflex_id - a UUIDv4 that uniquely identies each Reflex
20
+ # - id - a UUIDv4 that uniquely identifies each Reflex
21
+ # - tab_id - a UUIDv4 that uniquely identifies the browser tab
20
22
  #
21
23
  # Example:
22
24
  #
23
25
  # before_reflex do
24
26
  # # throw :abort # this will prevent the Reflex from continuing
25
- # # learn more about callbacks at https://docs.stimulusreflex.com/rtfm/lifecycle
27
+ # # learn more about callbacks at https://docs.stimulusreflex.com/guide/lifecycle
26
28
  # end
27
29
  #
28
30
  # def example(argument=true)
29
- # # Your logic here...
31
+ # # Your logic here! Update models, launch jobs, poll Redis...
32
+ #
33
+ # # By default, Reflexes call the controller action for the current page and render the view.
30
34
  # # Any declared instance variables will be made available to the Rails controller and view.
35
+ #
36
+ # # You can also use the `morph` method to mark a Reflex as Selector or Nothing.
37
+ # # https://docs.stimulusreflex.com/guide/morph-modes#introducing-morphs
31
38
  # end
32
39
  #
33
- # Learn more at: https://docs.stimulusreflex.com/rtfm/reflex-classes
40
+ # Learn more at: https://docs.stimulusreflex.com/guide/reflex-classes
34
41
 
35
- def update
36
- end
42
+ <%- end -%>
43
+ <%- if class_name == "Example" -%>
44
+ def increment
45
+ count = session[:count] || 0
46
+ session[:count] = count + 1
47
+
48
+ morph "#time", Time.now
49
+ morph "#count", session[:count]
37
50
 
38
- def do_stuff
51
+ cable_ready.set_value(selector: "progress", value: session[:count])
52
+
53
+ if session[:count] == 5
54
+ cable_ready.text_content(selector: "#reload", text: "Try reloading your browser window. The count is persisted in the session!")
55
+ end
56
+
57
+ if session[:count] >= 10
58
+ cable_ready.dispatch_event(name: "fireworks")
59
+ end
39
60
  end
40
61
 
41
- def do_more_stuff
62
+ def reset
63
+ session[:count] = 0
64
+ end
65
+ <%- else -%>
66
+ <%- actions.each do |action| -%>
67
+ def <%= action %>
42
68
  end
69
+ <%= "\n" unless action == actions.last -%>
70
+ <%- end -%>
71
+ <%- end -%>
43
72
  end
@@ -0,0 +1,27 @@
1
+ # frozen_string_literal: true
2
+
3
+ class ApplicationReflex < StimulusReflex::Reflex
4
+ # Put application-wide Reflex behavior and callbacks in this file.
5
+ #
6
+ # Learn more at: https://docs.stimulusreflex.com/guide/reflex-classes
7
+ #
8
+ # If your ActionCable connection is: `identified_by :current_user`
9
+ # delegate :current_user, to: :connection
10
+ #
11
+ # current_user delegation alloqs you to use the Current pattern, too:
12
+ # before_reflex do
13
+ # Current.user = current_user
14
+ # end
15
+ #
16
+ # To access view helpers inside Reflexes:
17
+ # delegate :helpers, to: :ApplicationController
18
+ #
19
+ # If you need to localize your Reflexes, you can set the I18n locale here:
20
+ #
21
+ # before_reflex do
22
+ # I18n.locale = :fr
23
+ # end
24
+ #
25
+ # For code examples, considerations and caveats, see:
26
+ # https://docs.stimulusreflex.com/guide/patterns#internationalization
27
+ end
@@ -0,0 +1,207 @@
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <title>StimulusReflex Demo</title>
5
+ <meta charset="utf-8">
6
+ <meta name="viewport" content="width=device-width,initial-scale=1">
7
+ <%%= csrf_meta_tags %>
8
+ <%%= csp_meta_tag %>
9
+
10
+ <%% unless Rails.root.join("config/importmap.rb").exist? %>
11
+ <script type="importmap">
12
+ {
13
+ "imports": {
14
+ "fireworks-js": "https://ga.jspm.io/npm:fireworks-js@2.10.0/dist/index.es.js"
15
+ }
16
+ }
17
+ </script>
18
+ <%% end %>
19
+
20
+ <%% if respond_to?(:vite_javascript_tag) %>
21
+ <%%= vite_client_tag %>
22
+ <%%= vite_javascript_tag "application", defer: true %>
23
+ <%% elsif respond_to?(:javascript_pack_tag) %>
24
+ <%%= javascript_pack_tag "application", defer: true %>
25
+ <%% elsif respond_to?(:javascript_importmap_tags) %>
26
+ <%%= javascript_importmap_tags %>
27
+ <%% elsif respond_to?(:javascript_include_tag) %>
28
+ <%%= javascript_include_tag "application", defer: true %>
29
+ <%% end %>
30
+
31
+ <script async src="https://ga.jspm.io/npm:es-module-shims@1.5.1/dist/es-module-shims.js" crossorigin="anonymous"></script>
32
+ <script type="module">
33
+ import { Fireworks } from 'fireworks-js'
34
+
35
+ const fireworks = new Fireworks(document.querySelector('.fireworks'))
36
+ document.addEventListener('fireworks', () => fireworks.launch(12))
37
+ </script>
38
+
39
+ <link rel="stylesheet" href="https://unpkg.com/@picocss/pico@latest/css/pico.min.css">
40
+ </head>
41
+
42
+ <body style="cursor: auto;">
43
+ <main class="container">
44
+ <h1>StimulusReflex</h1>
45
+
46
+ <p>Actual demonstrations will show up in this space, soon. In the meantime, verify that your installation was successful:</p>
47
+
48
+ <button data-reflex="click->Example#increment">Increment</button>
49
+ <button data-reflex="click->Example#reset">Reset</button>
50
+
51
+ <br />
52
+
53
+ <div>
54
+ Last Refresh:
55
+ <b><mark id="time"><%%= Time.now %></mark></b>
56
+ </div>
57
+
58
+ <div>
59
+ Clicked
60
+ <b><mark id="count"><%%= session[:count] || 0 %></mark></b>
61
+ times
62
+ </div>
63
+
64
+ <br/>
65
+
66
+ <progress value="<%%= session[:count] %>" max="10"></progress>
67
+
68
+ <div id="reload"></div>
69
+
70
+ <h1>CableReady</h1>
71
+
72
+ <p>CableReady lets you control one or many clients from the server in real-time.</p>
73
+
74
+ <p>Everything in CableReady revolves around its <a href="https://cableready.stimulusreflex.com/reference/operations" target="_blank">38+</a> <b>operations</b>, which are commands that can update content, raise events, write cookies and even play audio. A group of one or more operations is called a <b>broadcast</b>. Broadcasts follow a <a href="https://dev.to/leastbad/the-cableready-language-implementation-project-4hjd">simple JSON format</a>.</p>
75
+
76
+ <p>We're going to go through the main ways developers use CableReady with some live demonstrations and code samples. We recommend that you open the controller class and ERB template for this page to follow along.</p>
77
+
78
+ <article>
79
+ <h3>Subscribe to a channel to receive broadcasts</h3>
80
+
81
+ WebSockets is the primary way most Rails developers use CableReady, via the <ins>cable_ready</ins> method.
82
+
83
+ <p>Use the <ins>cable_ready_stream_from</ins> helper to create a secure Action Cable subscription:</p>
84
+
85
+ <kbd>cable_ready_stream_from :example_page</kbd>
86
+
87
+ <%%= cable_ready_stream_from :example_page %>
88
+
89
+ <p style="margin-top: 1rem;">Every user looking at a page subscribed to the <ins>:example_page</ins> channel will receive the same broadcasts.</p>
90
+
91
+ <p>You can call <ins>cable_ready</ins> <a href="https://cableready.stimulusreflex.com/guide/cableready-everywhere" target="_blank">pretty much anywhere</a> in your application. Try it in your <kbd>rails console</kbd> now:</p>
92
+
93
+ <kbd>include CableReady::Broadcaster<br>cable_ready[:example_page].text_content("#cable_ready_stream_from_output", text: "Hello from the console!").broadcast</kbd>
94
+
95
+ <p style="margin-top: 1rem;">Any message you send will appear in the <ins>#cable_ready_stream_from_output</ins> DIV below &mdash; even if you <i>open multiple tabs</i>.</p>
96
+
97
+ <div id="cable_ready_stream_from_output" style="height: 2rem; font-weight: bolder; border: coral 2px dashed; padding: 0.15rem 0.4rem;"></div>
98
+
99
+ <p style="margin-top: 1rem;">While it's easy to <a href="https://cableready.stimulusreflex.com/guide/broadcasting-to-resources" target="_blank">create your own custom Action Cable channels</a>, <ins>cable_ready_stream_from</ins> will be the first tool you reach for, because it doesn't require any additional code.</p>
100
+
101
+ <p>Specify Active Record models or compound qualifiers to go full-ninja: 🥷</p>
102
+
103
+ <kbd>cable_ready_stream_from current_user</kbd><br>
104
+
105
+ <kbd style="margin-top: 0.3rem;">cable_ready_stream_from @post, :comments</kbd>
106
+
107
+ <p style="margin-top: 1rem;">These examples barely scrape the surface of what's possible. Be sure to check out the <a href="https://cableready.stimulusreflex.com/guide/identifiers" target="_blank">Stream Identifiers</a> chapter.</p>
108
+ </article>
109
+
110
+ <article>
111
+ <h3>Updatable: magically update the DOM when server-side data changes</h3>
112
+
113
+ <p>The <ins>updates_for</ins> helper allow you to designate sections of your page that will <a href="https://cableready.stimulusreflex.com/guide/updatable" target="_blank">update automatically</a> with new content when an Active Record model changes. 🤯</p>
114
+
115
+ <small>It's difficult to demonstrate this feature without creating a temporary model and a migration; a road to hell, paved with good intentions. However, you likely have these models (or similar) in your application. Uncomment, tweak if necessary and follow along!</small>
116
+
117
+ <p style="margin-top: 1rem;">First, call <ins>enable_updates</ins> in your model. You can use it on associations, too.</p>
118
+
119
+ <kbd> class User < ApplicationRecord<br>
120
+ &nbsp;&nbsp;enable_updates<br>
121
+ &nbsp;&nbsp;has_many :posts, enable_updates: true<br>
122
+ end<br>
123
+ <br>
124
+ class Post < ApplicationRecord<br>
125
+ &nbsp;&nbsp;belongs_to :user<br>
126
+ end
127
+ </kbd>
128
+
129
+ <p style="margin-top: 1rem;">By default, updates will be broadcast when any CRUD action is performed on the model. You can customize this behavior by passing options such as <ins>on: [:create, :update]</ins> or <ins>if: -> { id.even? }</ins>.</p>
130
+
131
+ <p>Next, use the <ins>updates_for</ins> helper to create one or more containers that will receive content updates.</p>
132
+
133
+ <kbd> &lt;%= cable_ready_updates_for current_user do %&gt;<br>
134
+ &nbsp;&nbsp;&lt;%= current_user.name %&gt;<br>
135
+ &lt;% end %&gt;
136
+ </kbd>
137
+
138
+ <!--
139
+ <%%#= cable_ready_updates_for current_user do %>
140
+ <p style="margin-top: 1rem;"><%%#= current_user.name %></p>
141
+ <%%# end %>
142
+ -->
143
+
144
+ <p style="margin-top: 1rem;">Update the current user in Rails console, and your page instantly reflects the new name. 🪄</p>
145
+
146
+ <p>Specify the class constant to get updates when records are created or deleted:</p>
147
+
148
+ <kbd> &lt;%= cable_ready_updates_for User do %&gt;<br>
149
+ &nbsp;&nbsp;&lt;ul&gt;<br>
150
+ &nbsp;&nbsp;&lt;% @users.each do |user| %&gt;<br>
151
+ &nbsp;&nbsp;&nbsp;&nbsp;&lt;li&gt;&lt;%= user.name %&gt;&lt;/li&gt;<br>
152
+ &nbsp;&nbsp;&lt;% end %&gt;<br>
153
+ &nbsp;&nbsp;&lt;/ul&gt;<br>
154
+ &lt;% end %&gt;
155
+ </kbd>
156
+
157
+ <!--
158
+ <%%#= cable_ready_updates_for User do %>
159
+ <ul style="margin-top: 1rem;">
160
+ <%%# @users.each do |user| %>
161
+ <li><%%#= user.name %></li>
162
+ <%%# end %>
163
+ </ul>
164
+ <%%# end %>
165
+ -->
166
+
167
+ <p style="margin-top: 1rem;">Update when new posts are created by the current user:</p>
168
+
169
+ <kbd> &lt;%= cable_ready_updates_for current_user, :posts do %&gt;<br>
170
+ &nbsp;&nbsp;&lt;ul&gt;<br>
171
+ &nbsp;&nbsp;&lt;% @posts.each do |post| %&gt;<br>
172
+ &nbsp;&nbsp;&nbsp;&nbsp;&lt;li&gt;&lt;%= post.title %&gt;&lt;/li&gt;<br>
173
+ &nbsp;&nbsp;&lt;% end %&gt;<br>
174
+ &nbsp;&nbsp;&lt;/ul&gt;<br>
175
+ &lt;% end %&gt;
176
+ </kbd>
177
+
178
+ <!--
179
+ <%%#= cable_ready_updates_for current_user, :posts do %>
180
+ <ul style="margin-top: 1rem;">
181
+ <%%# @posts.each do |post| %>
182
+ <li><%%#= post.title %></li>
183
+ <%%# end %>
184
+ </ul>
185
+ <%%# end %>
186
+ -->
187
+
188
+ <p style="margin-top: 1rem;">One major advantage of the Updatable approach is that each visitor sees <b>personalized content</b>. This is difficult with a WebSockets broadcast, where every subscriber receives the same data.</p>
189
+
190
+ <p>Instead, Updatable notifies all subscribers that an update is available, prompting each client to make a fetch request and refresh sections of the page.</p>
191
+
192
+ <p>There's more to <a href="https://cableready.stimulusreflex.com/guide/updatable" target="_blank">Updatable</a> than what's covered here... <i>but, not much more</i>. It really is that simple.</p>
193
+ </article>
194
+
195
+ <article>
196
+ <p>If you're finished with this example page and resource controller, you can destroy them:</p>
197
+
198
+ <kbd>rails destroy stimulus_reflex example</kbd>
199
+ </article>
200
+
201
+ <p>As always, please drop by the <a href="https://discord.gg/stimulus-reflex" target="_blank">StimulusReflex Discord server</a> if you have any questions or need support of any kind. We're incredibly proud of the community that has formed around these libraries, and we discuss everything from JavaScript/Ruby/CSS to View Component/Phlex to databases and CRDTs. <b>We'd love to hear what you're building with StimulusReflex and CableReady.</b></p>
202
+
203
+ <p>You can find the documentation for StimulusReflex <a href="https://docs.stimulusreflex.com" target="_blank">here</a> and CableReady <a href="https://cableready.stimulusreflex.com" target="_blank">here</a>.</p>
204
+ </main>
205
+ <div class="fireworks" style="position: fixed; bottom: 0; width: 100vw; height: 100vh; pointer-events: none;"></div>
206
+ </body>
207
+ </html>
@@ -0,0 +1,22 @@
1
+ # frozen_string_literal: true
2
+
3
+ CableReady.configure do |config|
4
+ # Enable/disable exiting / warning when the sanity checks fail options:
5
+ # `:exit` or `:warn` or `:ignore`
6
+ #
7
+ # config.on_failed_sanity_checks = :exit
8
+
9
+ # Enable/disable assets compilation
10
+ # `true` or `false`
11
+ #
12
+ # config.precompile_assets = true
13
+
14
+ # Define your own custom operations
15
+ # https://cableready.stimulusreflex.com/customization#custom-operations
16
+ #
17
+ # config.add_operation_name :jazz_hands
18
+
19
+ # Change the default Active Job queue used for broadcast_later and broadcast_later_to
20
+ #
21
+ # config.broadcast_job_queue = :default
22
+ end