stimulus_reflex 2.0.0 → 2.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 8072c82ab1f16ce62c41e6d970952c3762a9e2fe5d87d2036ff2f37af7f3a4fb
4
- data.tar.gz: b3549d33023d9c27e878443f48391e13b82562e4dc9d9cbfb64b9744d8fe0702
3
+ metadata.gz: eb76c0066092e1f7ee8db5642f38b41c2c7f0e6718ab87c6a3358833fbe9b685
4
+ data.tar.gz: 79bb331fcce6f043d1098e41bf0a373c277138fadb0963784f58825fe5786309
5
5
  SHA512:
6
- metadata.gz: 5ba2646379f9bbe65485d947c55467640e890d516ad571f704e064f0fb78956b977ba405d5698efdb6fd3b7e87f1075a9948d59c4b6ec2855410d3b290137a31
7
- data.tar.gz: 756a22a1a4cb5f1d4ad985dd1424ed15edc3d2e4d8efcf080d3c3a9a551a170fc1a76d84543fd105acc104b97c07d7f377348a00ec329222fa6341c0e627051c
6
+ metadata.gz: c55bc084dbf1e05f799a0a17e1d94a11951d79574a1b89492fabfa8c884d4e1e44aa1f9d9223462f043b5ca8f23a7a0f068939d9a0ac1aff53959d07fa0dbddd
7
+ data.tar.gz: 360e7909ab6bf950c426e18de0ed857f65b1fb5c51ce77cf19f8644420f6cd798b79f803f8cf7485c02d6f77c245c88f7326846d37d787d0728e68c8b1e0eb13
@@ -0,0 +1,76 @@
1
+ # Contributor Covenant Code of Conduct
2
+
3
+ ## Our Pledge
4
+
5
+ In the interest of fostering an open and welcoming environment, we as
6
+ contributors and maintainers pledge to making participation in our project and
7
+ our community a harassment-free experience for everyone, regardless of age, body
8
+ size, disability, ethnicity, sex characteristics, gender identity and expression,
9
+ level of experience, education, socio-economic status, nationality, personal
10
+ appearance, race, religion, or sexual identity and orientation.
11
+
12
+ ## Our Standards
13
+
14
+ Examples of behavior that contributes to creating a positive environment
15
+ include:
16
+
17
+ - Using welcoming and inclusive language
18
+ - Being respectful of differing viewpoints and experiences
19
+ - Gracefully accepting constructive criticism
20
+ - Focusing on what is best for the community
21
+ - Showing empathy towards other community members
22
+
23
+ Examples of unacceptable behavior by participants include:
24
+
25
+ - The use of sexualized language or imagery and unwelcome sexual attention or
26
+ advances
27
+ - Trolling, insulting/derogatory comments, and personal or political attacks
28
+ - Public or private harassment
29
+ - Publishing others' private information, such as a physical or electronic
30
+ address, without explicit permission
31
+ - Other conduct which could reasonably be considered inappropriate in a
32
+ professional setting
33
+
34
+ ## Our Responsibilities
35
+
36
+ Project maintainers are responsible for clarifying the standards of acceptable
37
+ behavior and are expected to take appropriate and fair corrective action in
38
+ response to any instances of unacceptable behavior.
39
+
40
+ Project maintainers have the right and responsibility to remove, edit, or
41
+ reject comments, commits, code, wiki edits, issues, and other contributions
42
+ that are not aligned to this Code of Conduct, or to ban temporarily or
43
+ permanently any contributor for other behaviors that they deem inappropriate,
44
+ threatening, offensive, or harmful.
45
+
46
+ ## Scope
47
+
48
+ This Code of Conduct applies both within project spaces and in public spaces
49
+ when an individual is representing the project or its community. Examples of
50
+ representing a project or community include using an official project e-mail
51
+ address, posting via an official social media account, or acting as an appointed
52
+ representative at an online or offline event. Representation of a project may be
53
+ further defined and clarified by project maintainers.
54
+
55
+ ## Enforcement
56
+
57
+ Instances of abusive, harassing, or otherwise unacceptable behavior may be
58
+ reported by contacting the project team at natehop@gmail.com. All
59
+ complaints will be reviewed and investigated and will result in a response that
60
+ is deemed necessary and appropriate to the circumstances. The project team is
61
+ obligated to maintain confidentiality with regard to the reporter of an incident.
62
+ Further details of specific enforcement policies may be posted separately.
63
+
64
+ Project maintainers who do not follow or enforce the Code of Conduct in good
65
+ faith may face temporary or permanent repercussions as determined by other
66
+ members of the project's leadership.
67
+
68
+ ## Attribution
69
+
70
+ This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4,
71
+ available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html
72
+
73
+ [homepage]: https://www.contributor-covenant.org
74
+
75
+ For answers to common questions about this code of conduct, see
76
+ https://www.contributor-covenant.org/faq
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- stimulus_reflex (2.0.0)
4
+ stimulus_reflex (2.0.1)
5
5
  cable_ready (>= 4.0.3)
6
6
  nokogiri
7
7
  rack
@@ -67,12 +67,12 @@ GEM
67
67
  zeitwerk (~> 2.1, >= 2.1.8)
68
68
  ast (2.4.0)
69
69
  builder (3.2.3)
70
- cable_ready (4.0.3)
70
+ cable_ready (4.0.5)
71
71
  rails (>= 5.2)
72
72
  coderay (1.1.2)
73
73
  concurrent-ruby (1.1.5)
74
74
  crass (1.0.4)
75
- erubi (1.8.0)
75
+ erubi (1.9.0)
76
76
  globalid (0.4.2)
77
77
  activesupport (>= 4.2.0)
78
78
  i18n (1.6.0)
@@ -89,12 +89,12 @@ GEM
89
89
  mimemagic (0.3.3)
90
90
  mini_mime (1.0.2)
91
91
  mini_portile2 (2.4.0)
92
- minitest (5.11.3)
93
- nio4r (2.5.1)
92
+ minitest (5.12.0)
93
+ nio4r (2.5.2)
94
94
  nokogiri (1.10.4)
95
95
  mini_portile2 (~> 2.4.0)
96
96
  parallel (1.17.0)
97
- parser (2.6.4.0)
97
+ parser (2.6.4.1)
98
98
  ast (~> 2.4.0)
99
99
  pry (0.12.2)
100
100
  coderay (~> 1.1.0)
@@ -149,7 +149,7 @@ GEM
149
149
  actionpack (>= 4.0)
150
150
  activesupport (>= 4.0)
151
151
  sprockets (>= 3.0.0)
152
- standard (0.1.3)
152
+ standard (0.1.4)
153
153
  rubocop (~> 0.72.0)
154
154
  rubocop-performance (~> 1.4.0)
155
155
  standardrb (1.0.0)
data/README.md CHANGED
@@ -1,132 +1,95 @@
1
- [![Lines of Code](http://img.shields.io/badge/lines_of_code-218-brightgreen.svg?style=flat)](http://blog.codinghorror.com/the-best-code-is-no-code-at-all/)
2
- [![Maintainability](https://api.codeclimate.com/v1/badges/2b24fdbd1ae37a24bedb/maintainability)](https://codeclimate.com/github/hopsoft/stimulus_reflex/maintainability)
3
- ![Prettier](https://github.com/hopsoft/stimulus_reflex/workflows/Prettier%20Check%20Action/badge.svg)
4
- ![StandardRB](https://github.com/hopsoft/stimulus_reflex/workflows/StandardRB%20Check%20Action/badge.svg)
1
+ ---
2
+ description: Ensure your projects are fast to market... with a small team
3
+ ---
5
4
 
6
5
  # StimulusReflex
7
6
 
8
- _reflex_ - an action that is performed as a response to a stimulus
9
-
10
- ### Build reactive [Single Page Applications (SPAs)](https://en.wikipedia.org/wiki/Single-page_application) with [Rails](https://rubyonrails.org) and [Stimulus](https://stimulusjs.org)
11
-
12
- This project supports building [reactive applications](https://en.wikipedia.org/wiki/Reactive_programming)
13
- with the Rails tooling you already know and love.
14
- It's designed to work perfectly with [server rendered HTML](https://guides.rubyonrails.org/action_view_overview.html),
15
- [Russian doll caching](https://edgeguides.rubyonrails.org/caching_with_rails.html#russian-doll-caching),
16
- [Stimulus](https://stimulusjs.org), [Turbolinks](https://www.youtube.com/watch?v=SWEts0rlezA), etc...
17
-
18
- __No need for a complex front-end framework. No need to grow your team or duplicate your efforts.__
19
-
20
- _Inspired by [Phoenix LiveView](https://youtu.be/Z2DU0qLfPIY?t=670)._ 🙌
21
-
22
- ## Table of Contents
23
-
24
- <!-- toc -->
25
-
26
- - [Before you Begin](#before-you-begin)
27
- - [How it Works](#how-it-works)
28
- - [Setup](#setup)
29
- * [JavaScript](#javascript)
30
- + [app/javascript/controllers/index.js](#appjavascriptcontrollersindexjs)
31
- * [Gemfile](#gemfile)
32
- - [Usage](#usage)
33
- * [Implicit Declarative Reflexes](#implicit-declarative-reflexes)
34
- + [app/views/pages/example.html.erb](#appviewspagesexamplehtmlerb)
35
- + [app/reflexes/example_reflex.rb](#appreflexesexample_reflexrb)
36
- * [Explicitly Defined Reflexes](#explicitly-defined-reflexes)
37
- + [app/views/pages/example.html.erb](#appviewspagesexamplehtmlerb-1)
38
- + [app/javascript/controllers/example.js](#appjavascriptcontrollersexamplejs)
39
- + [app/reflexes/example_reflex.rb](#appreflexesexample_reflexrb-1)
40
- - [What Just Happened](#what-just-happened)
41
- - [Advanced Usage](#advanced-usage)
42
- * [The Reflex `element` property](#the-reflex-element-property)
43
- * [ActionCable](#actioncable)
44
- + [Performance](#performance)
45
- + [ActionCable Rooms](#actioncable-rooms)
46
- * [Render Delay](#render-delay)
47
- - [Demo Applications](#demo-applications)
48
- - [Contributing](#contributing)
49
- * [Coding Standards](#coding-standards)
50
- * [Releasing](#releasing)
51
-
52
- <!-- tocstop -->
7
+ ###### Read this document on the [official docs site](https://docs.stimulusreflex.com).
53
8
 
54
- ## Before you Begin
9
+ [![Lines of Code](http://img.shields.io/badge/lines_of_code-359-brightgreen.svg?style=flat)](http://blog.codinghorror.com/the-best-code-is-no-code-at-all/)
10
+ [![Maintainability](https://api.codeclimate.com/v1/badges/2b24fdbd1ae37a24bedb/maintainability)](https://codeclimate.com/github/hopsoft/stimulus_reflex/maintainability)
11
+ ![Prettier](https://github.com/hopsoft/stimulus_reflex/workflows/Prettier/badge.svg)
12
+ ![StandardRB](https://github.com/hopsoft/stimulus_reflex/workflows/StandardRB/badge.svg)
55
13
 
56
- StimulusReflex provides functionality similar to what can already be achieved with Rails by combining
57
- [UJS remote elements](https://guides.rubyonrails.org/working_with_javascript_in_rails.html#remote-elements)
58
- , [Stimulus](https://stimulusjs.org), and [Turbolinks](https://github.com/turbolinks/turbolinks).
59
- _Consider building with standard Rails tooling before introducing StimulusReflex._
60
- _Check out the [Stimulus TodoMVC](https://github.com/hopsoft/stimulus_todomvc) example if you are unsure how to accomplish this._
14
+ **Build reactive applications with the Rails tooling you already know and love.** StimulusReflex is designed to work perfectly with [server rendered HTML](https://guides.rubyonrails.org/action_view_overview.html), [Russian doll caching](https://edgeguides.rubyonrails.org/caching_with_rails.html#russian-doll-caching), [Stimulus](https://stimulusjs.org/), [Turbolinks](https://www.youtube.com/watch?v=SWEts0rlezA), etc... and strives to live up to the vision outlined in [The Rails Doctrine](https://rubyonrails.org/doctrine/).
61
15
 
62
- StimulusReflex offers 3 primary benefits over the traditional Rails HTTP request/response cycle.
16
+ ## Before you Begin
63
17
 
64
- 1. __Communication happens on the ActionCable web socket__ _- saves time by avoiding the overhead of establishishing traditional HTTP connections_
65
- 1. __The controller action is invoked directly__ _- skips framework overhead such as the middleware chain, etc..._
66
- 1. __DOM diffing is used to update the page__ _- provides faster rendering and less jitter_
18
+ A great user experience can be created with Rails alone. Tools like [UJS remote elements](https://guides.rubyonrails.org/working_with_javascript_in_rails.html#remote-elements) , [Stimulus](https://stimulusjs.org/), and [Turbolinks](https://github.com/turbolinks/turbolinks) are incredibly powerful when combined. Try building your application using these tools before introducing StimulusReflex.
67
19
 
68
- ## How it Works
20
+ {% hint style="info" %}
21
+ See the [Stimulus TodoMVC](https://github.com/hopsoft/stimulus_todomvc) example application if you are unsure how to do this.
22
+ {% endhint %}
69
23
 
70
- 1. Render a standard Rails view template
71
- 1. Use [Stimulus](https://stimulusjs.org) and [ActionCable](https://edgeguides.rubyonrails.org/action_cable_overview.html) to invoke a method on the server
72
- 1. Watch the page automatically render updates via fast [DOM diffing](https://github.com/patrick-steele-idem/morphdom)
73
- 1. That's it...
24
+ ## Benefits
74
25
 
75
- __Yes, it really is that simple.__
76
- There are no hidden gotchas.
26
+ StimulusReflex offers 3 primary benefits over the traditional Rails request/response cycle.
77
27
 
78
- ![How it Works](https://raw.githubusercontent.com/hopsoft/stimulus_reflex/master/docs/diagram.png)
28
+ 1. **All communication happens via web socket** - avoids the overhead of traditional HTTP connections
29
+ 2. **The controller action is invoked directly** - skips framework overhead like the middleware chain
30
+ 3. **DOM diffing is used to update the page** - provides faster rendering and less jitter
79
31
 
80
32
  ## Setup
81
33
 
82
- ### JavaScript
83
-
84
- ```
34
+ ```bash
85
35
  yarn add stimulus_reflex
86
36
  ```
87
- #### app/javascript/controllers/index.js
88
-
89
- This is the file where Stimulus is initialized in your application.
90
- _Note that your file location may be different._
91
37
 
38
+ {% code-tabs %}
39
+ {% code-tabs-item title="app/javascript/controllers/index.js" %}
92
40
  ```javascript
93
- import { Application } from 'stimulus';
94
- import { definitionsFromContext } from 'stimulus/webpack-helpers';
95
- import StimulusReflex from 'stimulus_reflex';
96
-
97
- const application = Application.start();
98
- const context = require.context('controllers', true, /_controller\.js$/);
99
- application.load(definitionsFromContext(context));
100
- StimulusReflex.initialize(application);
41
+ import { Application } from 'stimulus'
42
+ import { definitionsFromContext } from 'stimulus/webpack-helpers'
43
+ import StimulusReflex from 'stimulus_reflex'
44
+
45
+ const application = Application.start()
46
+ const context = require.context('controllers', true, /_controller\.js$/)
47
+ application.load(definitionsFromContext(context))
48
+ StimulusReflex.initialize(application)
101
49
  ```
50
+ {% endcode-tabs-item %}
51
+ {% endcode-tabs %}
102
52
 
103
- ### Gemfile
104
-
53
+ {% code-tabs %}
54
+ {% code-tabs-item title="Gemfile" %}
105
55
  ```ruby
106
56
  gem "stimulus_reflex"
107
57
  ```
58
+ {% endcode-tabs-item %}
59
+ {% endcode-tabs %}
60
+
61
+ ## Quick Start
108
62
 
109
- ## Usage
63
+ Here are a few small contrived examples to get you started.
110
64
 
111
- ### Implicit Declarative Reflexes
65
+ ### No JavaScript
112
66
 
113
- This example shows how to create a reactive feature without the need to write any JavaScript
114
- other than initializing StimulusReflex itself _([see the setup instructions](#javascript))_. Everything else is managed entirely by HTML and Ruby.
67
+ It's possible to build a reactive application without writing any JavaScript. This requires 2 steps.
115
68
 
116
- #### app/views/pages/example.html.erb
69
+ 1. Declare the appropriate data attributes in HTML.
70
+ 2. Create a server side reflex object with Ruby.
117
71
 
118
- ```erb
72
+ This example will automatically update the page with the latest count whenever the anchor is clicked.
73
+
74
+ {% code-tabs %}
75
+ {% code-tabs-item title="app/views/pages/example.html.erb" %}
76
+ ```text
119
77
  <head></head>
120
78
  <body>
121
- <a href="#" data-reflex="click->ExampleReflex#increment" data-step="1" data-count="<%= @count.to_i %>">
79
+ <a href="#"
80
+ data-reflex="click->ExampleReflex#increment"
81
+ data-step="1"
82
+ data-count="<%= @count.to_i %>">
122
83
  Increment <%= @count.to_i %>
123
84
  </a>
124
85
  </body>
125
86
  </html>
126
87
  ```
88
+ {% endcode-tabs-item %}
89
+ {% endcode-tabs %}
127
90
 
128
- #### app/reflexes/example_reflex.rb
129
-
91
+ {% code-tabs %}
92
+ {% code-tabs-item title="app/reflexes/example\_reflex.rb" %}
130
93
  ```ruby
131
94
  class ExampleReflex < StimulusReflex::Reflex
132
95
  def increment
@@ -134,35 +97,46 @@ class ExampleReflex < StimulusReflex::Reflex
134
97
  end
135
98
  end
136
99
  ```
100
+ {% endcode-tabs-item %}
101
+ {% endcode-tabs %}
102
+
103
+ {% hint style="success" %}
104
+ **Concerns like managing state and template rendering are handled server side.** This technique works regardless of how complex the UI becomes. For example, we could render multiple instances of `@count` in unrelated sections of the page and they will all update.
105
+ {% endhint %}
137
106
 
138
- The code above will automatically update the relevant DOM nodes with the updated count whenever the anchor is clicked.
107
+ {% hint style="danger" %}
108
+ Do not create server side reflex methods named `reflex` as this is a reserved word.
109
+ {% endhint %}
139
110
 
140
- __Note that all concerns from managing state to rendering views are handled server side.__
141
- This technique works regardless of how complex the UI may become.
142
- For example, we could render multiple instances of `@count` in unrelated sections of the page and they will all update.
111
+ ### Some JavaScript
143
112
 
144
- ### Explicitly Defined Reflexes
113
+ Real world applications typically warrant setting up finer grained control. This requires 3 steps.
145
114
 
146
- This example shows how to create a reactive feature by defining an explicit client side
147
- Stimulus controller to handle the DOM event and trigger the server side reflex.
115
+ 1. Declare the appropriate data attributes in HTML.
116
+ 2. Create a client side StimulusReflex controller with JavaScript.
117
+ 3. Create a server side reflex object with Ruby.
148
118
 
149
- #### app/views/pages/example.html.erb
119
+ This example will automatically update the page with the latest count whenever the anchor is clicked.
150
120
 
151
- ```erb
121
+ {% code-tabs %}
122
+ {% code-tabs-item title="app/views/pages/example.html.erb" %}
123
+ ```text
152
124
  <head></head>
153
125
  <body>
154
- <a href="#" data-controller="example" data-action="click->example#increment">
126
+ <a href="#"
127
+ data-controller="example"
128
+ data-action="click->example#increment">
155
129
  Increment <%= @count.to_i %>
156
130
  </a>
157
131
  </body>
158
132
  </html>
159
133
  ```
160
-
161
- #### app/javascript/controllers/example.js
134
+ {% endcode-tabs-item %}
135
+ {% endcode-tabs %}
162
136
 
163
137
  ```javascript
164
- import { Controller } from "stimulus"
165
- import StimulusReflex from "stimulus_reflex"
138
+ import { Controller } from 'stimulus';
139
+ import StimulusReflex from 'stimulus_reflex';
166
140
 
167
141
  export default class extends Controller {
168
142
  connect() {
@@ -171,135 +145,47 @@ export default class extends Controller {
171
145
 
172
146
  increment() {
173
147
  // trigger a server-side reflex and a client-side page update
148
+ // pass the step argument with a value of `1` to the reflex method
174
149
  this.stimulate('ExampleReflex#increment', 1);
175
150
  }
176
151
  }
177
152
  ```
178
153
 
179
- #### app/reflexes/example_reflex.rb
180
-
154
+ {% code-tabs %}
155
+ {% code-tabs-item title="app/reflexes/example\_reflex.rb" %}
181
156
  ```ruby
182
157
  class ExampleReflex < StimulusReflex::Reflex
183
158
  def increment(step = 1)
184
- @count = @count.to_i + step
159
+ # In a typical Rails app the Rails controller would set the value of @count
160
+ # after fetching it from a persistent data store
161
+ # @count = @count.to_i + step
162
+
163
+ # To keep this example simple, we use session to store the value
164
+ session[:count] = session[:count].to_i + step
165
+ @count = session[:count]
185
166
  end
186
167
  end
187
168
  ```
169
+ {% endcode-tabs-item %}
170
+ {% endcode-tabs %}
188
171
 
189
- ## What Just Happened
172
+ ## How it Works
190
173
 
191
- The following happens when a `StimulusReflex::Reflex` is invoked.
174
+ Here's what happens whenever a `StimulusReflex::Reflex` is invoked.
192
175
 
193
- 1. The page that triggered the reflex is re-rerendered. _Instance variables created in the reflex are available to both the controller and view templates._
176
+ 1. The page that triggered the reflex is re-rerendered.
194
177
  2. The re-rendered HTML is sent to the client over the ActionCable socket.
195
178
  3. The page is updated via fast DOM diffing courtesy of morphdom.
196
179
 
197
- _NOTE: While future versions of StimulusReflex may support more granular updates, today the entire body is re-rendered and sent over the socket._
198
-
199
- ## Advanced Usage
200
-
201
- ### The Reflex `element` property
202
-
203
- All reflex methods expose an `element` property.
204
- This property holds a Hash like data structure that represents the HTML element that triggered the refelx.
205
- It contains all of the Stimulus controller's
206
- [DOM element attributes](https://developer.mozilla.org/en-US/docs/Web/API/Element/attributes) as well as other properties like `checked` and `value`.
207
- _Most of the values will be strings._
208
-
209
- ```html
210
- <checkbox id="example"
211
- label="Example"
212
- data-controller="checkbox"
213
- data-value="123"
214
- checked />
215
- ```
216
-
217
- ```ruby
218
- class ExampleReflex < StimulusReflex::Reflex
219
- def work()
220
- element[:id] # => the HTML element's id attribute value
221
- element.dataset # => a Hash that represents the HTML element's dataset
222
-
223
- element[:id] # => "example"
224
- element[:checked] # => true
225
- element[:label] # => "Example"
226
- element["data-controller"] # => "checkbox"
227
- element["data-value"] # => "123"
228
- element.dataset[:controller] # => "checkbox"
229
- element.dataset[:value] # => "123"
230
- end
231
- end
232
- ```
233
-
234
- - `element[:checked]` holds a boolean
235
- - `element[:selected]` holds a boolean
236
- - `element[:value]` holds the [DOM element's value](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input#value)
237
- - `select` elements assign `element[:value]` to their selected option's value
238
- - `select` elements with _multiselect_ enabled assign `element[:values]` to their selected options values
239
- - All other values exposed in `element` are extracted from the DOM element's attributes
240
-
241
- ### ActionCable
242
-
243
- StimulusReflex will use the ActionCable defaults of `window.App` and `App.cable` if they exist.
244
- If these defaults do not exist, StimulusReflex will establish a new socket connection.
245
-
246
- #### Performance
247
-
248
- ActionCable emits verbose log messages. Disabling ActionCable logs may improve performance.
249
-
250
- ```ruby
251
- # config/application.rb
252
-
253
- ActionCable.server.config.logger = Logger.new(nil)
254
- ```
255
-
256
- #### ActionCable Rooms
257
-
258
- You may find the need to restrict notifications to a specific room.
259
- This can be accomplished by setting the `data-room` attribute on the StimulusController element.
260
-
261
- ```erb
262
- <a href="#" data-controller="example" data-action="click->example#increment" data-room="12345">
263
- ```
264
-
265
- ### Render Delay
266
-
267
- An attempt is made to reduce repaint/reflow jitter when users trigger lots of updates.
268
-
269
- You can control how long to wait _(think debounce)_ prior to updating the page.
270
- Simply set the `renderDelay` _(milliseconds)_ option when registering the controller.
271
-
272
- ```javascript
273
- export default class extends Controller {
274
- connect() {
275
- StimulusReflex.register(this, {renderDelay: 200});
276
- }
277
- }
278
- ```
279
-
280
- The default value is `25`.
281
-
282
- ## Demo Applications
283
-
284
- Building apps with StimulusReflex should evoke your memories of the original [Rails demo video](https://www.youtube.com/watch?v=Gzj723LkRJY).
285
-
286
- > Look at all the things I'm **not** doing. -DHH
287
-
288
- - [TodoMVC](https://github.com/hopsoft/stimulus_reflex_todomvc)
289
-
290
- ## Contributing
180
+ {% hint style="success" %}
181
+ All instance variables created in the reflex are made available to the Rails controller and view.
182
+ {% endhint %}
291
183
 
292
- ### Coding Standards
184
+ {% hint style="info" %}
185
+ **The entire body is re-rendered and sent over the socket.** Smaller scoped DOM updates may come in a future release.
186
+ {% endhint %}
293
187
 
294
- This project uses [Standard](https://github.com/testdouble/standard)
295
- and [Prettier](https://github.com/prettier/prettier) to minimize bike shedding related to code formatting.
296
- Please run `./bin/standardize` prior submitting pull requests.
188
+ ## Example Applications
297
189
 
298
- ### Releasing
190
+ * [TodoMVC](https://stimulus-reflex-todomvc.herokuapp.com) - An implementation of [TodoMVC](http://todomvc.com/) using [Ruby on Rails](https://rubyonrails.org/), [StimulusJS](https://stimulusjs.org/), and [StimulusReflex](https://github.com/hopsoft/stimulus_reflex). [https://github.com/hopsoft/stimulus\_reflex\_todomvc](https://github.com/hopsoft/stimulus_reflex_todomvc)
299
191
 
300
- 1. Bump version number at `lib/stimulus_reflex/version.rb`
301
- 1. Run `rake build`
302
- 1. Run `rake release`
303
- 1. Change directories `cd ./javascript`
304
- 1. Run `yarn publish --tag GIT_TAG_CREATED_BY_RUBYGEMS`
305
- 1. Assign same version number to the JavaScript package _Might not be required?_
data/README.md.orig CHANGED
@@ -1,74 +1,144 @@
1
- [![Lines of Code](http://img.shields.io/badge/lines_of_code-159-brightgreen.svg?style=flat)](http://blog.codinghorror.com/the-best-code-is-no-code-at-all/)
1
+ <<<<<<< HEAD
2
+ [![Lines of Code](http://img.shields.io/badge/lines_of_code-359-brightgreen.svg?style=flat)](http://blog.codinghorror.com/the-best-code-is-no-code-at-all/)
2
3
  [![Maintainability](https://api.codeclimate.com/v1/badges/2b24fdbd1ae37a24bedb/maintainability)](https://codeclimate.com/github/hopsoft/stimulus_reflex/maintainability)
4
+ ![Prettier](https://github.com/hopsoft/stimulus_reflex/workflows/Prettier/badge.svg)
5
+ ![StandardRB](https://github.com/hopsoft/stimulus_reflex/workflows/StandardRB/badge.svg)
6
+ =======
7
+ ---
8
+ description: Ensure your projects are fast to market... with a small team
9
+ ---
10
+ >>>>>>> master
3
11
 
4
12
  # StimulusReflex
5
13
 
6
- ### Build reactive [Single Page Applications (SPAs)](https://en.wikipedia.org/wiki/Single-page_application) with [Rails](https://rubyonrails.org) and [Stimulus](https://stimulusjs.org)
7
-
8
- This project supports building [reactive applications](https://en.wikipedia.org/wiki/Reactive_programming)
9
- with the Rails tooling you already know and love.
10
- It's designed to work perfectly with [server rendered HTML](https://guides.rubyonrails.org/action_view_overview.html),
11
- [Russian doll caching](https://edgeguides.rubyonrails.org/caching_with_rails.html#russian-doll-caching),
12
- [Stimulus](https://stimulusjs.org), [Turbolinks](https://www.youtube.com/watch?v=SWEts0rlezA), etc...
13
-
14
-
15
- __No need for a complex front-end framework. No need to grow your team or duplicate your efforts.__
16
-
17
- ---
14
+ ###### Read this document on the [official docs site](https://docs.stimulusreflex.com).
18
15
 
19
- > The lifecycle of a "modern" SPA app is so convoluted, it requires a team to build and support.
20
- > The wire size and computation demands of these heavy client sites frequently run slower than the server-rendered pages that they replaced.
21
- > With Stimulus Reflex, a Rails developer can build Single Page Applications without the need for client rendering or heavy JS frameworks.
16
+ **Build reactive applications with the Rails tooling you already know and love.** StimulusReflex is designed to work perfectly with [server rendered HTML](https://guides.rubyonrails.org/action_view_overview.html), [Russian doll caching](https://edgeguides.rubyonrails.org/caching_with_rails.html#russian-doll-caching), [Stimulus](https://stimulusjs.org/), [Turbolinks](https://www.youtube.com/watch?v=SWEts0rlezA), etc... and strives to live up to the vision outlined in [The Rails Doctrine](https://rubyonrails.org/doctrine/).
22
17
 
23
- ---
18
+ ## Before you Begin
24
19
 
25
- _Inspired by [Phoenix LiveView](https://youtu.be/Z2DU0qLfPIY?t=670)._ 🙌
20
+ A great user experience can be created with Rails alone. Tools like [UJS remote elements](https://guides.rubyonrails.org/working_with_javascript_in_rails.html#remote-elements) , [Stimulus](https://stimulusjs.org/), and [Turbolinks](https://github.com/turbolinks/turbolinks) are incredibly powerful when combined. Try building your application using these tools before introducing StimulusReflex.
26
21
 
27
- ## How it Works
22
+ {% hint style="info" %}
23
+ See the [Stimulus TodoMVC](https://github.com/hopsoft/stimulus_todomvc) example application if you are unsure how to do this.
24
+ {% endhint %}
28
25
 
29
- 1. Render a standard Rails view template
30
- 1. Use [Stimulus](https://stimulusjs.org) and [ActionCable](https://edgeguides.rubyonrails.org/action_cable_overview.html) to invoke a method on the server
31
- 1. Watch the page automatically render updates via fast [DOM diffing](https://github.com/patrick-steele-idem/morphdom)
32
- 1. That's it...
26
+ ## Benefits
33
27
 
34
- __Yes, it really is that simple.__
35
- There are no hidden gotchas.
28
+ StimulusReflex offers 3 primary benefits over the traditional Rails request/response cycle.
36
29
 
37
- ![How it Works](https://raw.githubusercontent.com/hopsoft/stimulus_reflex/master/docs/diagram.png)
30
+ 1. **All communication happens via web socket** - avoids the overhead of traditional HTTP connections
31
+ 2. **The controller action is invoked directly** - skips framework overhead like the middleware chain
32
+ 3. **DOM diffing is used to update the page** - provides faster rendering and less jitter
38
33
 
39
34
  ## Setup
40
35
 
41
- ### JavaScript
42
-
43
- ```
36
+ ```bash
44
37
  yarn add stimulus_reflex
45
38
  ```
46
39
 
47
- ### Gemfile
40
+ {% code-tabs %}
41
+ {% code-tabs-item title="app/javascript/controllers/index.js" %}
42
+ ```javascript
43
+ import { Application } from 'stimulus'
44
+ import { definitionsFromContext } from 'stimulus/webpack-helpers'
45
+ import StimulusReflex from 'stimulus_reflex'
46
+
47
+ const application = Application.start()
48
+ const context = require.context('controllers', true, /_controller\.js$/)
49
+ application.load(definitionsFromContext(context))
50
+ StimulusReflex.initialize(application)
51
+ ```
52
+ {% endcode-tabs-item %}
53
+ {% endcode-tabs %}
48
54
 
55
+ {% code-tabs %}
56
+ {% code-tabs-item title="Gemfile" %}
49
57
  ```ruby
50
58
  gem "stimulus_reflex"
51
59
  ```
60
+ {% endcode-tabs-item %}
61
+ {% endcode-tabs %}
62
+
63
+ ## Quick Start
64
+
65
+ Here are a few small contrived examples to get you started.
52
66
 
53
- ## Basic Usage
67
+ ### No JavaScript
54
68
 
55
- ### app/views/pages/example.html.erb
69
+ It's possible to build a reactive application without writing any JavaScript. This requires 2 steps.
56
70
 
57
- ```erb
71
+ 1. Declare the appropriate data attributes in HTML.
72
+ 2. Create a server side reflex object with Ruby.
73
+
74
+ This example will automatically update the page with the latest count whenever the anchor is clicked.
75
+
76
+ {% code-tabs %}
77
+ {% code-tabs-item title="app/views/pages/example.html.erb" %}
78
+ ```text
58
79
  <head></head>
59
80
  <body>
60
- <a href="#" data-controller="example" data-action="click->example#increment">
81
+ <a href="#"
82
+ data-reflex="click->ExampleReflex#increment"
83
+ data-step="1"
84
+ data-count="<%= @count.to_i %>">
61
85
  Increment <%= @count.to_i %>
62
86
  </a>
63
87
  </body>
64
88
  </html>
65
89
  ```
90
+ {% endcode-tabs-item %}
91
+ {% endcode-tabs %}
92
+
93
+ {% code-tabs %}
94
+ {% code-tabs-item title="app/reflexes/example\_reflex.rb" %}
95
+ ```ruby
96
+ class ExampleReflex < StimulusReflex::Reflex
97
+ def increment
98
+ @count = element.dataset[:count].to_i + element.dataset[:step].to_i
99
+ end
100
+ end
101
+ ```
102
+ {% endcode-tabs-item %}
103
+ {% endcode-tabs %}
104
+
105
+ {% hint style="success" %}
106
+ **Concerns like managing state and template rendering are handled server side.** This technique works regardless of how complex the UI becomes. For example, we could render multiple instances of `@count` in unrelated sections of the page and they will all update.
107
+ {% endhint %}
66
108
 
67
- ### app/javascript/controllers/example.js
109
+ {% hint style="danger" %}
110
+ Do not create server side reflex methods named `reflex` as this is a reserved word.
111
+ {% endhint %}
112
+
113
+ ### Some JavaScript
114
+
115
+ Real world applications typically warrant setting up finer grained control. This requires 3 steps.
116
+
117
+ 1. Declare the appropriate data attributes in HTML.
118
+ 2. Create a client side StimulusReflex controller with JavaScript.
119
+ 3. Create a server side reflex object with Ruby.
120
+
121
+ This example will automatically update the page with the latest count whenever the anchor is clicked.
122
+
123
+ {% code-tabs %}
124
+ {% code-tabs-item title="app/views/pages/example.html.erb" %}
125
+ ```text
126
+ <head></head>
127
+ <body>
128
+ <a href="#"
129
+ data-controller="example"
130
+ data-action="click->example#increment">
131
+ Increment <%= @count.to_i %>
132
+ </a>
133
+ </body>
134
+ </html>
135
+ ```
136
+ {% endcode-tabs-item %}
137
+ {% endcode-tabs %}
68
138
 
69
139
  ```javascript
70
- import { Controller } from "stimulus"
71
- import StimulusReflex from "stimulus_reflex"
140
+ import { Controller } from 'stimulus';
141
+ import StimulusReflex from 'stimulus_reflex';
72
142
 
73
143
  export default class extends Controller {
74
144
  connect() {
@@ -77,83 +147,47 @@ export default class extends Controller {
77
147
 
78
148
  increment() {
79
149
  // trigger a server-side reflex and a client-side page update
150
+ // pass the step argument with a value of `1` to the reflex method
80
151
  this.stimulate('ExampleReflex#increment', 1);
81
152
  }
82
153
  }
83
154
  ```
84
155
 
85
- ### app/reflexes/example_reflex.rb
86
-
156
+ {% code-tabs %}
157
+ {% code-tabs-item title="app/reflexes/example\_reflex.rb" %}
87
158
  ```ruby
88
159
  class ExampleReflex < StimulusReflex::Reflex
89
160
  def increment(step = 1)
90
- @count = @count.to_i + step
161
+ # In a typical Rails app the Rails controller would set the value of @count
162
+ # after fetching it from a persistent data store
163
+ # @count = @count.to_i + step
164
+
165
+ # To keep this example simple, we use session to store the value
166
+ session[:count] = session[:count].to_i + step
167
+ @count = session[:count]
91
168
  end
92
169
  end
93
170
  ```
171
+ {% endcode-tabs-item %}
172
+ {% endcode-tabs %}
94
173
 
95
- The following happens after the `StimulusReflex::Reflex` method call finishes.
96
-
97
- 1. The page that triggered the reflex is re-rerendered. _Instance variables created in the reflex are available to both the controller and view templates._
98
- 2. The re-rendered HTML is sent to the client over the ActionCable socket.
99
- 3. The page is updated via fast DOM diffing courtesy of morphdom. _While future versions of StimulusReflex might support more granular updates, today the entire body is re-rendered and sent over the socket._
100
-
101
- ## Advanced Usage
102
-
103
- ### ActionCable
104
-
105
- StimulusReflex will use the ActionCable defaults of `window.App` and `App.cable` if they exist.
106
- If these defaults do not exist, StimulusReflex will establish a new socket connection.
107
-
108
- ### Performance
109
-
110
- ActionCable emits verbose log messages. Disabling ActionCable logs may improve performance.
111
-
112
- ```ruby
113
- # config/application.rb
114
-
115
- ActionCable.server.config.logger = Logger.new(nil)
116
- ```
117
-
118
- ### ActionCable Rooms
119
-
120
- You may find the need to restrict notifications to a specific room.
121
- This can be accomplished by setting the `data-room` attribute on the StimulusController element.
122
-
123
- ```erb
124
- <a href="#" data-controller="example" data-action="click->example#increment" data-room="12345">
125
- ```
126
-
127
- ### Render Delay
128
-
129
- An attempt is made to reduce repaint/reflow jitter when users trigger lots of updates.
130
-
131
- You can control how long to wait _(think debounce)_ prior to updating the page.
132
- Simply set the `renderDelay` _(milliseconds)_ option when registering the controller.
133
-
134
- ```javascript
135
- export default class extends Controller {
136
- connect() {
137
- StimulusReflex.register(this, {renderDelay: 200});
138
- }
139
- }
140
- ```
174
+ ## How it Works
141
175
 
142
- The default value is `25`.
176
+ Here's what happens whenever a `StimulusReflex::Reflex` is invoked.
143
177
 
144
- ## Demo Applications
178
+ 1. The page that triggered the reflex is re-rerendered.
179
+ 2. The re-rendered HTML is sent to the client over the ActionCable socket.
180
+ 3. The page is updated via fast DOM diffing courtesy of morphdom.
145
181
 
146
- Building apps with StimulusReflex should evoke your memories of the original [Rails demo video](https://www.youtube.com/watch?v=Gzj723LkRJY).
182
+ {% hint style="success" %}
183
+ All instance variables created in the reflex are made available to the Rails controller and view.
184
+ {% endhint %}
147
185
 
148
- > Look at all the things I'm **not** doing. -DHH
186
+ {% hint style="info" %}
187
+ **The entire body is re-rendered and sent over the socket.** Smaller scoped DOM updates may come in a future release.
188
+ {% endhint %}
149
189
 
150
- - [TodoMVC](https://github.com/hopsoft/stimulus_reflex_todomvc)
190
+ ## Example Applications
151
191
 
152
- <<<<<<< HEAD
153
- ## Contributing
192
+ * [TodoMVC](https://stimulus-reflex-todomvc.herokuapp.com) - An implementation of [TodoMVC](http://todomvc.com/) using [Ruby on Rails](https://rubyonrails.org/), [StimulusJS](https://stimulusjs.org/), and [StimulusReflex](https://github.com/hopsoft/stimulus_reflex). [https://github.com/hopsoft/stimulus\_reflex\_todomvc](https://github.com/hopsoft/stimulus_reflex_todomvc)
154
193
 
155
- This project uses [Standard](https://github.com/testdouble/standard)
156
- and [Prettier](https://github.com/prettier/prettier) to minimize bike shedding related to code formatting.
157
- Please run `./bin/standardize` prior submitting pull requests.
158
- =======
159
- >>>>>>> master
data/Rakefile CHANGED
@@ -1,4 +1,9 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require "bundler/gem_tasks"
4
- task default: :spec
4
+
5
+ task default: [:test]
6
+
7
+ task :test do
8
+ puts "Please write some tests..."
9
+ end
data/SUMMARY.md ADDED
@@ -0,0 +1,7 @@
1
+ # Table of contents
2
+
3
+ * [StimulusReflex](README.md)
4
+ * [Lifecycle](lifecycle.md)
5
+ * [Advanced Use](advanced-use.md)
6
+ * [Security](security.md)
7
+
data/bin/standardize CHANGED
@@ -1,4 +1,4 @@
1
1
  #!/bin/bash
2
2
 
3
3
  bundle exec standardrb --fix
4
- cd ./javascript && yarn run prettier --write stimulus_reflex.js
4
+ cd ./javascript && yarn run prettier-standard stimulus_reflex.js
@@ -25,21 +25,27 @@ class StimulusReflex::Channel < ActionCable::Channel::Base
25
25
  element = StimulusReflex::Element.new(data["attrs"])
26
26
 
27
27
  begin
28
- reflex = reflex_name.constantize.new(self, url: url, element: element)
28
+ reflex_class = reflex_name.constantize
29
+ raise ArgumentError.new("#{reflex_name} is not a StimulusReflex::Reflex") unless is_reflex?(reflex_class)
30
+ reflex = reflex_class.new(self, url: url, element: element)
29
31
  delegate_call_to_reflex reflex, method_name, arguments
30
32
  rescue => invoke_error
31
- logger.error "\e[31mStimulusReflex::Channel Failed to invoke #{target}! #{url} #{invoke_error}\e[0m"
33
+ return broadcast_error("StimulusReflex::Channel Failed to invoke #{target}! #{url} #{invoke_error}", data)
32
34
  end
33
35
 
34
36
  begin
35
- render_page_and_broadcast_morph url, reflex
37
+ render_page_and_broadcast_morph url, reflex, data
36
38
  rescue => render_error
37
- logger.error "\e[31mStimulusReflex::Channel Failed to rerender #{url} #{render_error}\e[0m"
39
+ broadcast_error "StimulusReflex::Channel Failed to re-render #{url} #{render_error}", data
38
40
  end
39
41
  end
40
42
 
41
43
  private
42
44
 
45
+ def is_reflex?(reflex_class)
46
+ reflex_class.ancestors.include? StimulusReflex::Reflex
47
+ end
48
+
43
49
  def delegate_call_to_reflex(reflex, method_name, arguments = [])
44
50
  method = reflex.method(method_name)
45
51
  required_params = method.parameters.select { |(kind, _)| kind == :req }
@@ -54,9 +60,9 @@ class StimulusReflex::Channel < ActionCable::Channel::Base
54
60
  end
55
61
  end
56
62
 
57
- def render_page_and_broadcast_morph(url, reflex)
63
+ def render_page_and_broadcast_morph(url, reflex, data = {})
58
64
  html = render_page(url, reflex)
59
- broadcast_morph url, html if html.present?
65
+ broadcast_morph url, html, data if html.present?
60
66
  end
61
67
 
62
68
  def render_page(url, reflex)
@@ -90,9 +96,15 @@ class StimulusReflex::Channel < ActionCable::Channel::Base
90
96
  controller.response.body
91
97
  end
92
98
 
93
- def broadcast_morph(url, html)
99
+ def broadcast_morph(url, html, data = {})
94
100
  html = extract_body_html(html)
95
- cable_ready[stream_name].morph selector: "body", html: html, children_only: true
101
+ cable_ready[stream_name].morph selector: "body", html: html, children_only: true, stimulus_reflex: data
102
+ cable_ready.broadcast
103
+ end
104
+
105
+ def broadcast_error(message, data = {})
106
+ logger.error "\e[31m#{message}\e[0m"
107
+ cable_ready[stream_name].dispatch_event name: "stimulus-reflex:500", detail: {stimulus_reflex: data.merge(error: message)}
96
108
  cable_ready.broadcast
97
109
  end
98
110
 
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module StimulusReflex
4
- VERSION = "2.0.0"
4
+ VERSION = "2.0.1"
5
5
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: stimulus_reflex
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.0.0
4
+ version: 2.0.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Nathan Hopkins
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2019-09-11 00:00:00.000000000 Z
12
+ date: 2019-09-28 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: rack
@@ -145,12 +145,14 @@ executables: []
145
145
  extensions: []
146
146
  extra_rdoc_files: []
147
147
  files:
148
+ - CODE_OF_CONDUCT.md
148
149
  - Gemfile
149
150
  - Gemfile.lock
150
151
  - LICENSE.txt
151
152
  - README.md
152
153
  - README.md.orig
153
154
  - Rakefile
155
+ - SUMMARY.md
154
156
  - bin/console
155
157
  - bin/setup
156
158
  - bin/standardize