stimulus_reflex 2.0.0 → 2.0.1

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.

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