stimulus_reflex 3.5.0.pre9 → 3.5.0.rc1

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 (104) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile +0 -1
  3. data/Gemfile.lock +122 -127
  4. data/README.md +13 -19
  5. data/app/assets/javascripts/stimulus_reflex.js +1017 -523
  6. data/app/assets/javascripts/stimulus_reflex.umd.js +940 -496
  7. data/app/channels/stimulus_reflex/channel.rb +9 -24
  8. data/bin/console +0 -2
  9. data/bin/standardize +2 -1
  10. data/lib/generators/stimulus_reflex/stimulus_reflex_generator.rb +68 -9
  11. data/lib/generators/stimulus_reflex/templates/app/controllers/examples_controller.rb.tt +9 -0
  12. data/lib/generators/stimulus_reflex/templates/app/javascript/channels/consumer.js.tt +6 -0
  13. data/lib/generators/stimulus_reflex/templates/app/javascript/channels/index.js.esbuild.tt +4 -0
  14. data/lib/generators/stimulus_reflex/templates/app/javascript/channels/index.js.importmap.tt +2 -0
  15. data/lib/generators/stimulus_reflex/templates/app/javascript/channels/index.js.shakapacker.tt +5 -0
  16. data/lib/generators/stimulus_reflex/templates/app/javascript/channels/index.js.vite.tt +1 -0
  17. data/lib/generators/stimulus_reflex/templates/app/javascript/channels/index.js.webpacker.tt +5 -0
  18. data/lib/generators/stimulus_reflex/templates/app/javascript/config/cable_ready.js.tt +4 -0
  19. data/lib/generators/stimulus_reflex/templates/app/javascript/config/index.js.tt +2 -0
  20. data/lib/generators/stimulus_reflex/templates/app/javascript/config/mrujs.js.tt +9 -0
  21. data/lib/generators/stimulus_reflex/templates/app/javascript/config/stimulus_reflex.js.tt +5 -0
  22. data/lib/generators/stimulus_reflex/templates/app/javascript/controllers/%file_name%_controller.js.tt +141 -0
  23. data/lib/generators/stimulus_reflex/templates/app/javascript/controllers/application.js.tt +11 -0
  24. data/lib/generators/stimulus_reflex/templates/app/javascript/controllers/application_controller.js.tt +74 -0
  25. data/lib/generators/stimulus_reflex/templates/app/javascript/controllers/index.js.esbuild.tt +7 -0
  26. data/lib/generators/stimulus_reflex/templates/app/javascript/controllers/index.js.importmap.tt +5 -0
  27. data/lib/generators/stimulus_reflex/templates/app/javascript/controllers/index.js.shakapacker.tt +5 -0
  28. data/lib/generators/stimulus_reflex/templates/app/javascript/controllers/index.js.vite.tt +5 -0
  29. data/lib/generators/stimulus_reflex/templates/app/javascript/controllers/index.js.webpacker.tt +5 -0
  30. data/{test/tmp/app/reflexes/user_reflex.rb → lib/generators/stimulus_reflex/templates/app/reflexes/%file_name%_reflex.rb.tt} +38 -9
  31. data/lib/generators/stimulus_reflex/templates/app/reflexes/application_reflex.rb.tt +27 -0
  32. data/lib/generators/stimulus_reflex/templates/app/views/examples/show.html.erb.tt +207 -0
  33. data/lib/generators/stimulus_reflex/templates/config/initializers/cable_ready.rb +27 -0
  34. data/lib/generators/stimulus_reflex/templates/config/initializers/stimulus_reflex.rb +18 -13
  35. data/lib/generators/stimulus_reflex/templates/esbuild.config.mjs.tt +94 -0
  36. data/lib/install/action_cable.rb +155 -0
  37. data/lib/install/broadcaster.rb +90 -0
  38. data/lib/install/bundle.rb +56 -0
  39. data/lib/install/compression.rb +41 -0
  40. data/lib/install/config.rb +87 -0
  41. data/lib/install/development.rb +110 -0
  42. data/lib/install/esbuild.rb +114 -0
  43. data/lib/install/example.rb +22 -0
  44. data/lib/install/importmap.rb +133 -0
  45. data/lib/install/initializers.rb +25 -0
  46. data/lib/install/mrujs.rb +133 -0
  47. data/lib/install/npm_packages.rb +25 -0
  48. data/lib/install/reflexes.rb +25 -0
  49. data/lib/install/shakapacker.rb +64 -0
  50. data/lib/install/spring.rb +54 -0
  51. data/lib/install/updatable.rb +34 -0
  52. data/lib/install/vite.rb +64 -0
  53. data/lib/install/webpacker.rb +90 -0
  54. data/lib/install/yarn.rb +55 -0
  55. data/lib/stimulus_reflex/broadcasters/broadcaster.rb +15 -8
  56. data/lib/stimulus_reflex/broadcasters/page_broadcaster.rb +7 -8
  57. data/lib/stimulus_reflex/broadcasters/selector_broadcaster.rb +10 -10
  58. data/lib/stimulus_reflex/broadcasters/update.rb +3 -0
  59. data/lib/stimulus_reflex/cable_readiness.rb +29 -0
  60. data/lib/stimulus_reflex/cable_ready_channels.rb +6 -5
  61. data/lib/stimulus_reflex/callbacks.rb +17 -1
  62. data/lib/stimulus_reflex/concern_enhancer.rb +6 -4
  63. data/lib/stimulus_reflex/configuration.rb +12 -2
  64. data/lib/stimulus_reflex/dataset.rb +11 -1
  65. data/lib/stimulus_reflex/engine.rb +16 -9
  66. data/lib/stimulus_reflex/html/document.rb +59 -0
  67. data/lib/stimulus_reflex/html/document_fragment.rb +13 -0
  68. data/lib/stimulus_reflex/importmap.rb +6 -3
  69. data/lib/stimulus_reflex/installer.rb +274 -0
  70. data/lib/stimulus_reflex/open_struct_fix.rb +2 -0
  71. data/lib/stimulus_reflex/reflex.rb +40 -31
  72. data/lib/stimulus_reflex/reflex_data.rb +19 -3
  73. data/lib/stimulus_reflex/reflex_factory.rb +6 -3
  74. data/lib/stimulus_reflex/request_parameters.rb +2 -0
  75. data/lib/stimulus_reflex/utils/logger.rb +10 -0
  76. data/lib/stimulus_reflex/utils/sanity_checker.rb +8 -48
  77. data/lib/stimulus_reflex/version.rb +1 -1
  78. data/lib/stimulus_reflex/version_checker.rb +54 -0
  79. data/lib/stimulus_reflex.rb +2 -0
  80. data/lib/tasks/stimulus_reflex/stimulus_reflex.rake +250 -0
  81. data/package.json +36 -28
  82. data/{rollup.config.js → rollup.config.mjs} +6 -24
  83. data/stimulus_reflex.gemspec +16 -19
  84. data/yarn.lock +1331 -748
  85. metadata +129 -79
  86. data/LATEST +0 -1
  87. data/app/assets/javascripts/stimulus_reflex.min.js +0 -2
  88. data/app/assets/javascripts/stimulus_reflex.min.js.map +0 -1
  89. data/app/assets/javascripts/stimulus_reflex.umd.min.js +0 -905
  90. data/app/assets/javascripts/stimulus_reflex.umd.min.js.map +0 -1
  91. data/lib/generators/stimulus_reflex/initializer_generator.rb +0 -14
  92. data/test/broadcasters/broadcaster_test.rb +0 -11
  93. data/test/broadcasters/broadcaster_test_case.rb +0 -39
  94. data/test/broadcasters/nothing_broadcaster_test.rb +0 -31
  95. data/test/broadcasters/page_broadcaster_test.rb +0 -79
  96. data/test/broadcasters/selector_broadcaster_test.rb +0 -173
  97. data/test/callbacks_test.rb +0 -652
  98. data/test/concern_enhancer_test.rb +0 -54
  99. data/test/element_test.rb +0 -254
  100. data/test/generators/stimulus_reflex_generator_test.rb +0 -58
  101. data/test/reflex_test.rb +0 -43
  102. data/test/test_helper.rb +0 -71
  103. data/test/tmp/app/reflexes/application_reflex.rb +0 -12
  104. data/yarn-error.log +0 -4964
@@ -4,7 +4,7 @@ class StimulusReflex::Channel < StimulusReflex.configuration.parent_channel.cons
4
4
  attr_reader :reflex_data
5
5
 
6
6
  def stream_name
7
- [params[:channel], connection.connection_identifier].join(":")
7
+ [params[:channel], connection.connection_identifier].reject(&:blank?).join(":")
8
8
  end
9
9
 
10
10
  def subscribed
@@ -25,30 +25,13 @@ class StimulusReflex::Channel < StimulusReflex.configuration.parent_channel.cons
25
25
  if reflex
26
26
  reflex.rescue_with_handler(exception)
27
27
  reflex.logger&.error error_message
28
- reflex.error data: data, body: "#{exception} #{exception.backtrace.first.split(":in ")[0] if Rails.env.development?}"
28
+ reflex.broadcast_error data: data, error: "#{exception} #{exception.backtrace.first.split(":in ")[0] if Rails.env.development?}"
29
29
  else
30
- if exception.is_a? StimulusReflex::Reflex::VersionMismatchError
31
- mismatch = "Reflex failed due to stimulus_reflex gem/NPM package version mismatch. Package versions must match exactly.\nNote that if you are using pre-release builds, gems use the \"x.y.z.preN\" version format, while NPM packages use \"x.y.z-preN\".\n\nstimulus_reflex gem: #{StimulusReflex::VERSION}\nstimulus_reflex NPM: #{data["version"]}"
32
-
33
- StimulusReflex.config.logger.error("\n\e[31m#{mismatch}\e[0m") unless StimulusReflex.config.on_failed_sanity_checks == :ignore
34
-
35
- if Rails.env.development?
36
- CableReady::Channels.instance[stream_name].console_log(
37
- message: mismatch,
38
- level: "error",
39
- reflex_id: data["reflexId"]
40
- ).broadcast
41
- end
42
-
43
- if StimulusReflex.config.on_failed_sanity_checks == :exit
44
- sleep 0.1
45
- exit!
46
- end
47
- else
30
+ unless exception.is_a?(StimulusReflex::Reflex::VersionMismatchError)
48
31
  StimulusReflex.config.logger.error error_message
49
32
  end
50
33
 
51
- if body.to_s.include? "No route matches"
34
+ if error_message.to_s.include? "No route matches"
52
35
  initializer_path = Rails.root.join("config", "initializers", "stimulus_reflex.rb")
53
36
 
54
37
  StimulusReflex.config.logger.warn <<~NOTE
@@ -73,14 +56,16 @@ class StimulusReflex::Channel < StimulusReflex.configuration.parent_channel.cons
73
56
  end
74
57
 
75
58
  if reflex.halted?
76
- reflex.halted data: data
59
+ reflex.broadcast_halt data: data
60
+ elsif reflex.forbidden?
61
+ reflex.broadcast_forbid data: data
77
62
  else
78
63
  begin
79
64
  reflex.broadcast(reflex_data.selectors, data)
80
65
  rescue => exception
81
66
  reflex.rescue_with_handler(exception)
82
67
  error = exception_with_backtrace(exception)
83
- reflex.error data: data, body: "#{exception} #{exception.backtrace.first.split(":in ")[0] if Rails.env.development?}"
68
+ reflex.broadcast_error data: data, error: "#{exception} #{exception.backtrace.first.split(":in ")[0] if Rails.env.development?}"
84
69
  reflex.logger&.error "\e[31mReflex failed to re-render: #{error[:message]} [#{reflex_data.url}]\e[0m\n#{error[:stack]}"
85
70
  end
86
71
  end
@@ -112,7 +97,7 @@ class StimulusReflex::Channel < StimulusReflex.configuration.parent_channel.cons
112
97
  end
113
98
 
114
99
  def commit_session(reflex)
115
- store = reflex.request.session.instance_variable_get("@by")
100
+ store = reflex.request.session.instance_variable_get(:@by)
116
101
  store.commit_session reflex.request, reflex.controller.response
117
102
  rescue => exception
118
103
  error = exception_with_backtrace(exception)
data/bin/console CHANGED
@@ -2,7 +2,5 @@
2
2
 
3
3
  require "bundler/setup"
4
4
  require "stimulus_reflex"
5
- require "pry"
6
5
 
7
6
  StimulusReflex.configuration.parent_channel = "ActionCable::Channel::Base"
8
- Pry.start
data/bin/standardize CHANGED
@@ -1,4 +1,5 @@
1
1
  #!/usr/bin/env bash
2
2
 
3
+ bundle exec magic_frozen_string_literal
3
4
  bundle exec standardrb --fix
4
- yarn run prettier-standard:format
5
+ yarn format
@@ -1,27 +1,86 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require "rails/generators"
4
+ require "stimulus_reflex/version"
4
5
 
5
6
  class StimulusReflexGenerator < Rails::Generators::NamedBase
6
7
  source_root File.expand_path("templates", __dir__)
7
8
 
8
9
  argument :name, type: :string, required: true, banner: "NAME"
9
10
  argument :actions, type: :array, default: [], banner: "action action"
10
- class_options skip_stimulus: false, skip_app_reflex: false, skip_reflex: false, skip_app_controller: false
11
+ class_options skip_stimulus: false, skip_reflex: false
11
12
 
12
13
  def execute
13
14
  actions.map!(&:underscore)
14
15
 
15
- copy_application_files if behavior == :invoke
16
+ cached_entrypoint = Rails.root.join("tmp/stimulus_reflex_installer/entrypoint")
17
+ if cached_entrypoint.exist?
18
+ entrypoint = File.read(cached_entrypoint)
19
+ else
20
+ entrypoint = [
21
+ Rails.root.join("app/javascript"),
22
+ Rails.root.join("app/frontend")
23
+ ].find(&:exist?) || "app/javascript"
24
+ puts "Where do JavaScript files live in your app? Our best guess is: \e[1#{entrypoint}\e[22m 🤔"
25
+ puts "Press enter to accept this, or type a different path."
26
+ print "> "
27
+ input = Rails.env.test? ? "tmp/app/javascript" : $stdin.gets.chomp
28
+ entrypoint = input unless input.blank?
29
+ end
16
30
 
17
- template "app/reflexes/%file_name%_reflex.rb" unless options[:skip_reflex]
18
- template "app/javascript/controllers/%file_name%_controller.js" unless options[:skip_stimulus]
19
- end
31
+ if !options[:skip_stimulus] && entrypoint.blank?
32
+ puts "❌ You must specify a valid JavaScript entrypoint."
33
+ exit
34
+ end
35
+
36
+ reflex_entrypoint = Rails.env.test? ? "tmp/app/reflexes" : "app/reflexes"
37
+ reflex_src = "app/reflexes/%file_name%_reflex.rb.tt"
38
+ reflex_path = Rails.root.join(reflex_entrypoint, "#{file_name}_reflex.rb")
39
+ stimulus_controller_src = "app/javascript/controllers/%file_name%_controller.js.tt"
40
+ stimulus_controller_path = Rails.root.join(entrypoint, "controllers/#{file_name}_controller.js")
41
+
42
+ template(reflex_src, reflex_path) unless options[:skip_reflex]
43
+ template(stimulus_controller_src, stimulus_controller_path) unless options[:skip_stimulus]
44
+
45
+ if file_name == "example"
46
+ controller_src = "app/controllers/examples_controller.rb.tt"
47
+ controller_path = Rails.root.join("app/controllers/examples_controller.rb")
48
+ template(controller_src, controller_path)
49
+
50
+ view_src = "app/views/examples/show.html.erb.tt"
51
+ view_path = Rails.root.join("app/views/examples/show.html.erb")
52
+ template(view_src, view_path)
53
+
54
+ example_path = Rails.root.join("app/views/examples")
55
+ FileUtils.remove_dir(example_path) if behavior == :revoke && example_path.exist? && Dir.empty?(example_path)
56
+
57
+ route "resource :example, constraints: -> { Rails.env.development? }"
58
+
59
+ importmap_path = Rails.root.join("config/importmap.rb")
60
+
61
+ if importmap_path.exist?
62
+ importmap = importmap_path.read
20
63
 
21
- private
64
+ if behavior == :revoke
65
+ if importmap.include?("pin \"fireworks-js\"")
66
+ importmap_path.write importmap_path.readlines.reject { |line| line.include?("pin \"fireworks-js\"") }.join
67
+ say "✅ unpin fireworks-js"
68
+ else
69
+ say "⏩ fireworks-js not pinned. Skipping."
70
+ end
71
+ end
22
72
 
23
- def copy_application_files
24
- template "app/reflexes/application_reflex.rb" unless options[:skip_app_reflex]
25
- template "app/javascript/controllers/application_controller.js" unless options[:skip_app_controller]
73
+ if behavior == :invoke
74
+ if importmap.include?("pin \"fireworks-js\"")
75
+ say "⏩ fireworks-js already pinnned. Skipping."
76
+ else
77
+ append_file(importmap_path, <<~RUBY, verbose: false)
78
+ pin "fireworks-js", to: "https://ga.jspm.io/npm:fireworks-js@2.10.0/dist/index.es.js"
79
+ RUBY
80
+ say "✅ pin fireworks-js"
81
+ end
82
+ end
83
+ end
84
+ end
26
85
  end
27
86
  end
@@ -0,0 +1,9 @@
1
+ class ExamplesController < ApplicationController
2
+ layout false
3
+
4
+ def show
5
+ respond_to do |format|
6
+ format.html
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,6 @@
1
+ // Action Cable provides the framework to deal with WebSockets in Rails.
2
+ // You can generate new channels where WebSocket features live using the `bin/rails generate channel` command.
3
+
4
+ import { createConsumer } from '@rails/actioncable'
5
+
6
+ export default createConsumer()
@@ -0,0 +1,4 @@
1
+ // Load all the channels within this directory and all subdirectories.
2
+ // Channel files must be named *_channel.js.
3
+
4
+ import './**/*_channel.js'
@@ -0,0 +1,2 @@
1
+ // app/channels/index.js
2
+ // Importmaps don't require anything here
@@ -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 @@
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 allows 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