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
data/test/element_test.rb DELETED
@@ -1,254 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require_relative "test_helper"
4
-
5
- class StimulusReflex::ElementTest < ActiveSupport::TestCase
6
- element = StimulusReflex::Element.new({
7
- "attrs" => {
8
- "user" => "First User",
9
- "user-id" => "1"
10
- },
11
- "dataset" => {
12
- "dataset" => {
13
- "data-post" => "The Post",
14
- "data-post-id" => "2"
15
- },
16
- "datasetAll" => {}
17
- }
18
- })
19
-
20
- test "should be able to access attributes on element itself" do
21
- assert_equal "First User", element.user
22
- assert_equal "First User", element["user"]
23
- assert_equal "First User", element[:user]
24
-
25
- assert_equal "1", element.user_id
26
- assert_equal "1", element["user_id"]
27
- assert_equal "1", element["user-id"]
28
- assert_equal "1", element[:user_id]
29
-
30
- assert_equal "The Post", element.data_post
31
- assert_equal "The Post", element["data_post"]
32
- assert_equal "The Post", element["data-post"]
33
- assert_equal "The Post", element[:data_post]
34
-
35
- assert_equal "2", element.data_post_id
36
- assert_equal "2", element["data_post_id"]
37
- assert_equal "2", element["data-post-id"]
38
- assert_equal "2", element[:data_post_id]
39
- end
40
-
41
- test "should be able to access attributes via attributes" do
42
- assert_equal "First User", element.attributes.user
43
- assert_equal "First User", element.attributes["user"]
44
- assert_equal "First User", element.attributes[:user]
45
-
46
- assert_equal "1", element.attributes.user_id
47
- assert_equal "1", element.attributes["user_id"]
48
- assert_equal "1", element.attributes["user-id"]
49
- assert_equal "1", element.attributes[:user_id]
50
- end
51
-
52
- test "should be able to access attributes via dataset" do
53
- assert_equal "The Post", element.dataset.post
54
- assert_equal "The Post", element.dataset["post"]
55
- assert_equal "The Post", element.dataset[:post]
56
-
57
- assert_equal "2", element.dataset.post_id
58
- assert_equal "2", element.dataset["post-id"]
59
- assert_equal "2", element.dataset["post_id"]
60
- assert_equal "2", element.dataset[:post_id]
61
- end
62
-
63
- test "should be able to access attributes via data_attributes" do
64
- assert_equal "The Post", element.data_attributes.post
65
- assert_equal "The Post", element.data_attributes["post"]
66
- assert_equal "The Post", element.data_attributes[:post]
67
-
68
- assert_equal "2", element.data_attributes.post_id
69
- assert_equal "2", element.data_attributes["post-id"]
70
- assert_equal "2", element.data_attributes["post_id"]
71
- assert_equal "2", element.data_attributes[:post_id]
72
- end
73
-
74
- test "should pluralize keys from datasetAll" do
75
- data = {
76
- "dataset" => {
77
- "dataset" => {
78
- "data-reflex" => "click",
79
- "data-sex" => "male"
80
- },
81
- "datasetAll" => {
82
- "data-reflex" => ["click"],
83
- "data-post-id" => ["1", "2", "3", "4"],
84
- "data-name" => ["steve", "bill", "steve", "mike"]
85
- }
86
- }
87
- }
88
-
89
- dataset_all_element = StimulusReflex::Element.new(data)
90
-
91
- assert_equal "click", dataset_all_element.dataset.reflex
92
- assert_equal "male", dataset_all_element.dataset.sex
93
-
94
- assert_equal ["steve", "bill", "steve", "mike"], dataset_all_element.dataset.names
95
- assert_equal ["1", "2", "3", "4"], dataset_all_element.dataset.post_ids
96
- assert_equal ["click"], dataset_all_element.dataset.reflexes
97
- end
98
-
99
- test "should pluralize irregular words from datasetAll" do
100
- data = {
101
- "dataset" => {
102
- "dataset" => {},
103
- "datasetAll" => {
104
- "data-cat" => ["cat"],
105
- "data-child" => ["child"],
106
- "data-women" => ["woman"],
107
- "data-man" => ["man"],
108
- "data-wolf" => ["wolf"],
109
- "data-library" => ["library"],
110
- "data-mouse" => ["mouse"]
111
- }
112
- }
113
- }
114
-
115
- pluralize_element = StimulusReflex::Element.new(data)
116
-
117
- assert_equal ["cat"], pluralize_element.dataset.cats
118
- assert_equal ["child"], pluralize_element.dataset.children
119
- assert_equal ["woman"], pluralize_element.dataset.women
120
- assert_equal ["man"], pluralize_element.dataset.men
121
- assert_equal ["wolf"], pluralize_element.dataset.wolves
122
- assert_equal ["library"], pluralize_element.dataset.libraries
123
- assert_equal ["mouse"], pluralize_element.dataset.mice
124
- end
125
-
126
- test "should not pluralize plural key" do
127
- data = {
128
- "dataset" => {
129
- "datasetAll" => {
130
- "data-ids" => ["1", "2"]
131
- }
132
- }
133
- }
134
-
135
- assert_equal ["1", "2"], StimulusReflex::Element.new(data).dataset.ids
136
- assert_nil StimulusReflex::Element.new(data).dataset.idss
137
- end
138
-
139
- test "should not build array with pluralized key" do
140
- data = {
141
- "dataset" => {
142
- "dataset" => {
143
- "data-ids" => "1"
144
- }
145
- }
146
- }
147
-
148
- assert_equal "1", StimulusReflex::Element.new(data).dataset.ids
149
- end
150
-
151
- test "should handle overlapping singluar and plural key names" do
152
- data = {
153
- "dataset" => {
154
- "dataset" => {
155
- "data-id" => "1",
156
- "data-ids" => "2",
157
- "data-post-id" => "9",
158
- "data-post-ids" => "10",
159
- "data-duplicate-value" => "19",
160
- "data-duplicate-values" => "20"
161
- },
162
- "datasetAll" => {
163
- "data-id" => ["3", "4"],
164
- "data-post-ids" => ["11", "12"],
165
- "data-duplicate-value" => ["20", "21", "22"]
166
- }
167
- }
168
- }
169
-
170
- overlapping_keys_element = StimulusReflex::Element.new(data)
171
-
172
- assert_equal "1", overlapping_keys_element.dataset.id
173
- assert_equal ["2", "3", "4"], overlapping_keys_element.dataset.ids
174
-
175
- assert_equal "9", overlapping_keys_element.dataset.post_id
176
- assert_equal ["10", "11", "12"], overlapping_keys_element.dataset.post_ids
177
-
178
- assert_equal "19", overlapping_keys_element.dataset.duplicate_value
179
- assert_equal ["20", "20", "21", "22"], overlapping_keys_element.dataset.duplicate_values
180
- end
181
-
182
- test "should return true for boolean data attributes" do
183
- data = {
184
- "dataset" => {
185
- "dataset" => {
186
- "data-short" => "t",
187
- "data-long" => "true",
188
- "data-num" => "1",
189
- "data-empty" => ""
190
- }
191
- }
192
- }
193
-
194
- element_with_boolean_attributes = StimulusReflex::Element.new(data)
195
-
196
- assert element_with_boolean_attributes.boolean[:short]
197
- assert element_with_boolean_attributes.boolean[:long]
198
- assert element_with_boolean_attributes.boolean[:num]
199
- assert element_with_boolean_attributes.boolean[:empty]
200
-
201
- assert element_with_boolean_attributes.dataset.boolean[:short]
202
- assert element_with_boolean_attributes.dataset.boolean[:long]
203
- assert element_with_boolean_attributes.dataset.boolean[:num]
204
- assert element_with_boolean_attributes.dataset.boolean[:empty]
205
- end
206
-
207
- test "should return false for falsey data attributes" do
208
- data = {
209
- "dataset" => {
210
- "dataset" => {
211
- "data-short" => "f",
212
- "data-long" => "false",
213
- "data-num" => "0"
214
- }
215
- }
216
- }
217
-
218
- element_with_falsey_attributes = StimulusReflex::Element.new(data)
219
-
220
- refute element_with_falsey_attributes.boolean[:short]
221
- refute element_with_falsey_attributes.boolean[:long]
222
- refute element_with_falsey_attributes.boolean[:num]
223
-
224
- refute element_with_falsey_attributes.dataset.boolean[:short]
225
- refute element_with_falsey_attributes.dataset.boolean[:long]
226
- refute element_with_falsey_attributes.dataset.boolean[:num]
227
- end
228
-
229
- test "should return numeric values" do
230
- data = {
231
- "dataset" => {
232
- "dataset" => {
233
- "data-int" => "123",
234
- "data-float" => "123.456",
235
- "data-string" => "asdf"
236
- }
237
- }
238
- }
239
-
240
- element_with_numeric_attributes = StimulusReflex::Element.new(data)
241
-
242
- assert_equal 123.0, element_with_numeric_attributes.numeric[:int]
243
- assert_equal 123.456, element_with_numeric_attributes.numeric[:float]
244
- assert_raises do
245
- element_with_numeric_attributes.numeric[:string]
246
- end
247
-
248
- assert_equal 123.0, element_with_numeric_attributes.dataset.numeric[:int]
249
- assert_equal 123.456, element_with_numeric_attributes.dataset.numeric[:float]
250
- assert_raises do
251
- element_with_numeric_attributes.dataset.numeric[:string]
252
- end
253
- end
254
- end
@@ -1,58 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require "rails/generators/test_case"
4
- require_relative "../test_helper"
5
- require "./lib/generators/stimulus_reflex/stimulus_reflex_generator"
6
-
7
- class StimulusReflexGeneratorTest < Rails::Generators::TestCase
8
- tests StimulusReflexGenerator
9
- destination File.expand_path("../../tmp", __FILE__)
10
- setup :prepare_destination
11
-
12
- test "creates singular named controller and reflex files" do
13
- run_generator %w[demo]
14
- assert_file "app/javascript/controllers/application_controller.js"
15
- assert_file "app/javascript/controllers/demo_controller.js", /Demo/
16
- assert_file "app/reflexes/application_reflex.rb"
17
- assert_file "app/reflexes/demo_reflex.rb", /DemoReflex/
18
- end
19
-
20
- test "creates plural named controller and reflex files" do
21
- run_generator %w[posts]
22
- assert_file "app/javascript/controllers/application_controller.js"
23
- assert_file "app/javascript/controllers/posts_controller.js", /Posts/
24
- assert_file "app/reflexes/application_reflex.rb"
25
- assert_file "app/reflexes/posts_reflex.rb", /PostsReflex/
26
- end
27
-
28
- test "skips stimulus controller and reflex if option provided" do
29
- run_generator %w[users --skip-stimulus --skip-reflex --skip-app-controller --skip-app-reflex]
30
- assert_no_file "app/javascript/controllers/application_controller.js"
31
- assert_no_file "app/javascript/controllers/users_controller.js"
32
- assert_no_file "app/reflexes/application_reflex.rb"
33
- assert_no_file "app/reflexes/users_reflex.rb"
34
- end
35
-
36
- test "creates reflex with given reflex actions" do
37
- run_generator %w[User update do_stuff DoMoreStuff]
38
- assert_file "app/reflexes/user_reflex.rb" do |reflex|
39
- assert_instance_method :update, reflex
40
- assert_instance_method :do_stuff, reflex
41
- assert_instance_method :do_more_stuff, reflex
42
- end
43
- assert_file "app/javascript/controllers/user_controller.js" do |controller|
44
- assert_match(/beforeUpdate/, controller)
45
- assert_match(/updateSuccess/, controller)
46
- assert_match(/updateError/, controller)
47
- assert_match(/afterUpdate/, controller)
48
- assert_match(/beforeDoStuff/, controller)
49
- assert_match(/doStuffSuccess/, controller)
50
- assert_match(/doStuffError/, controller)
51
- assert_match(/afterDoStuff/, controller)
52
- assert_match(/beforeDoMoreStuff/, controller)
53
- assert_match(/doMoreStuffSuccess/, controller)
54
- assert_match(/doMoreStuffError/, controller)
55
- assert_match(/afterDoMoreStuff/, controller)
56
- end
57
- end
58
- end
data/test/reflex_test.rb DELETED
@@ -1,43 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require_relative "test_helper"
4
- require "mocha/minitest"
5
-
6
- class StimulusReflex::ReflexTest < ActionCable::Channel::TestCase
7
- tests StimulusReflex::Channel
8
-
9
- setup do
10
- stub_connection(session_id: SecureRandom.uuid)
11
- def connection.env
12
- @env ||= {}
13
- end
14
- @reflex = StimulusReflex::Reflex.new(subscribe, url: "https://test.stimulusreflex.com", client_attributes: {reflex_id: "666", version: StimulusReflex::VERSION})
15
- @reflex.controller_class.view_paths << Rails.root.join("test/views")
16
- end
17
-
18
- test "render plain" do
19
- assert @reflex.render(plain: "Some text") == "Some text"
20
- end
21
-
22
- test "render template" do
23
- assert @reflex.render("/hello_template", assigns: {message: "Testing 123"}) == "<p>Hello from template! Testing 123</p>\n"
24
- end
25
-
26
- test "render partial" do
27
- assert @reflex.render(partial: "/hello_partial", assigns: {message: "Testing 123"}) == "<p>Hello from partial! Testing 123</p>\n"
28
- end
29
-
30
- test "dom_id" do
31
- assert @reflex.dom_id(TestModel.new(id: 123)) == "#test_model_123"
32
- end
33
-
34
- test "params behave like ActionController::Parameters" do
35
- ActionDispatch::Request.any_instance.stubs(:parameters).returns({"a" => "1", "b" => "2", "c" => "3"})
36
- reflex = StimulusReflex::Reflex.new(subscribe, url: "https://test.stimulusreflex.com", client_attributes: {version: StimulusReflex::VERSION})
37
-
38
- deleted_param = reflex.params.delete("a")
39
-
40
- assert deleted_param == "1"
41
- assert reflex.params.to_unsafe_h == {"b" => "2", "c" => "3"}
42
- end
43
- end
data/test/test_helper.rb DELETED
@@ -1,71 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- ENV["RAILS_ENV"] ||= "test"
4
-
5
- require "mocha"
6
- require "rails"
7
- require "active_model"
8
- require "active_record"
9
- require "action_controller"
10
- require "minitest/mock"
11
- require "pry"
12
-
13
- require_relative "../lib/stimulus_reflex"
14
-
15
- class TestApp < Rails::Application
16
- routes.draw { root to: "test#index" }
17
- end
18
-
19
- class ApplicationController < ActionController::Base; end
20
-
21
- class TestController < ApplicationController
22
- include Rails.application.routes.url_helpers
23
-
24
- def index
25
- head :ok
26
- end
27
- end
28
-
29
- class SessionMock
30
- def load!
31
- nil
32
- end
33
- end
34
-
35
- class ActionDispatch::Request
36
- def session
37
- @session ||= SessionMock.new
38
- end
39
- end
40
-
41
- class TestModel
42
- include ActiveModel::Model
43
- attr_accessor :id
44
- def is_a?(klass)
45
- klass == ActiveRecord::Base
46
- end
47
-
48
- def to_gid_param
49
- "xxxyyyzzz"
50
- end
51
- end
52
-
53
- module ActionCable
54
- module Channel
55
- class ConnectionStub
56
- def connection_identifier
57
- connection_gid identifiers.map { |id| send(id.to_sym) if id }.compact
58
- end
59
-
60
- def connection_gid(ids)
61
- ids.map { |o| o.respond_to?(:to_gid_param) ? o.to_gid_param : o.to_s }.sort.join(":")
62
- end
63
- end
64
- end
65
- end
66
-
67
- StimulusReflex.configuration.parent_channel = "ActionCable::Channel::Base"
68
- ActionCable::Server::Base.config.cable = {adapter: "test"}
69
- ActionCable::Server::Base.config.logger = Logger.new(nil)
70
-
71
- require_relative "../app/channels/stimulus_reflex/channel"
@@ -1,12 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- class ApplicationReflex < StimulusReflex::Reflex
4
- # Put application-wide Reflex behavior and callbacks in this file.
5
- #
6
- # Example:
7
- #
8
- # # If your ActionCable connection is: `identified_by :current_user`
9
- # delegate :current_user, to: :connection
10
- #
11
- # Learn more at: https://docs.stimulusreflex.com/rtfm/reflex-classes
12
- end