stimulus_reflex 2.0.0 → 2.1.2
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 +4 -4
- data/CODE_OF_CONDUCT.md +76 -0
- data/Gemfile.lock +16 -16
- data/README.md +20 -280
- data/Rakefile +6 -1
- data/bin/loc +3 -0
- data/bin/standardize +2 -2
- data/lib/stimulus_reflex/channel.rb +34 -12
- data/lib/stimulus_reflex/reflex.rb +3 -2
- data/lib/stimulus_reflex/version.rb +1 -1
- metadata +6 -5
- data/README.md.orig +0 -159
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: f261317eba89f3aca406db338af66069b57121ef68188dd32c22a8b263c84c0d
|
|
4
|
+
data.tar.gz: 16ead06810e664961d5d0bea297a82bfd109616c363eecb63e2211efbe0718e0
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 43c62ef1ddec489d525dd429a78ee0113b215918ac4fa95192cf75fafe356b42705e775b29ccfa2ddf193833b3bc42d5e736828d97234353e2ae06bdd929bd63
|
|
7
|
+
data.tar.gz: 0b5870db51f39af566c2eda1a574783dba205b9f37d1c07083d82a5df6ef6a0a63dde0b43ee0881492155715e945926452f529677b7951d10f27228471a80097
|
data/CODE_OF_CONDUCT.md
ADDED
|
@@ -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,8 +1,8 @@
|
|
|
1
1
|
PATH
|
|
2
2
|
remote: .
|
|
3
3
|
specs:
|
|
4
|
-
stimulus_reflex (2.
|
|
5
|
-
cable_ready (>= 4.0.
|
|
4
|
+
stimulus_reflex (2.1.2)
|
|
5
|
+
cable_ready (>= 4.0.7)
|
|
6
6
|
nokogiri
|
|
7
7
|
rack
|
|
8
8
|
rails (>= 5.2)
|
|
@@ -67,18 +67,18 @@ 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.
|
|
70
|
+
cable_ready (4.0.7)
|
|
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.
|
|
75
|
+
erubi (1.9.0)
|
|
76
76
|
globalid (0.4.2)
|
|
77
77
|
activesupport (>= 4.2.0)
|
|
78
|
-
i18n (1.
|
|
78
|
+
i18n (1.7.0)
|
|
79
79
|
concurrent-ruby (~> 1.0)
|
|
80
80
|
jaro_winkler (1.5.3)
|
|
81
|
-
loofah (2.
|
|
81
|
+
loofah (2.3.0)
|
|
82
82
|
crass (~> 1.0.2)
|
|
83
83
|
nokogiri (>= 1.5.9)
|
|
84
84
|
mail (2.7.1)
|
|
@@ -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.
|
|
93
|
-
nio4r (2.5.
|
|
92
|
+
minitest (5.12.2)
|
|
93
|
+
nio4r (2.5.2)
|
|
94
94
|
nokogiri (1.10.4)
|
|
95
95
|
mini_portile2 (~> 2.4.0)
|
|
96
|
-
parallel (1.
|
|
97
|
-
parser (2.6.
|
|
96
|
+
parallel (1.18.0)
|
|
97
|
+
parser (2.6.5.0)
|
|
98
98
|
ast (~> 2.4.0)
|
|
99
99
|
pry (0.12.2)
|
|
100
100
|
coderay (~> 1.1.0)
|
|
@@ -122,8 +122,8 @@ GEM
|
|
|
122
122
|
rails-dom-testing (2.0.3)
|
|
123
123
|
activesupport (>= 4.2.0)
|
|
124
124
|
nokogiri (>= 1.6)
|
|
125
|
-
rails-html-sanitizer (1.
|
|
126
|
-
loofah (~> 2.
|
|
125
|
+
rails-html-sanitizer (1.3.0)
|
|
126
|
+
loofah (~> 2.3)
|
|
127
127
|
railties (6.0.0)
|
|
128
128
|
actionpack (= 6.0.0)
|
|
129
129
|
activesupport (= 6.0.0)
|
|
@@ -131,7 +131,7 @@ GEM
|
|
|
131
131
|
rake (>= 0.8.7)
|
|
132
132
|
thor (>= 0.20.3, < 2.0)
|
|
133
133
|
rainbow (3.0.0)
|
|
134
|
-
rake (
|
|
134
|
+
rake (13.0.0)
|
|
135
135
|
rubocop (0.72.0)
|
|
136
136
|
jaro_winkler (~> 1.5.1)
|
|
137
137
|
parallel (~> 1.10)
|
|
@@ -142,14 +142,14 @@ GEM
|
|
|
142
142
|
rubocop-performance (1.4.1)
|
|
143
143
|
rubocop (>= 0.71.0)
|
|
144
144
|
ruby-progressbar (1.10.1)
|
|
145
|
-
sprockets (
|
|
145
|
+
sprockets (4.0.0)
|
|
146
146
|
concurrent-ruby (~> 1.0)
|
|
147
147
|
rack (> 1, < 3)
|
|
148
148
|
sprockets-rails (3.2.1)
|
|
149
149
|
actionpack (>= 4.0)
|
|
150
150
|
activesupport (>= 4.0)
|
|
151
151
|
sprockets (>= 3.0.0)
|
|
152
|
-
standard (0.1.
|
|
152
|
+
standard (0.1.4)
|
|
153
153
|
rubocop (~> 0.72.0)
|
|
154
154
|
rubocop-performance (~> 1.4.0)
|
|
155
155
|
standardrb (1.0.0)
|
|
@@ -162,7 +162,7 @@ GEM
|
|
|
162
162
|
websocket-driver (0.7.1)
|
|
163
163
|
websocket-extensions (>= 0.1.0)
|
|
164
164
|
websocket-extensions (0.1.4)
|
|
165
|
-
zeitwerk (2.
|
|
165
|
+
zeitwerk (2.2.0)
|
|
166
166
|
|
|
167
167
|
PLATFORMS
|
|
168
168
|
ruby
|
data/README.md
CHANGED
|
@@ -1,305 +1,45 @@
|
|
|
1
|
-
[](http://blog.codinghorror.com/the-best-code-is-no-code-at-all/)
|
|
2
2
|
[](https://codeclimate.com/github/hopsoft/stimulus_reflex/maintainability)
|
|
3
|
-

|
|
4
|
+

|
|
5
|
+

|
|
5
6
|
|
|
6
7
|
# StimulusReflex
|
|
7
8
|
|
|
8
9
|
_reflex_ - an action that is performed as a response to a stimulus
|
|
9
10
|
|
|
10
|
-
|
|
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.__
|
|
11
|
+
**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/).
|
|
19
12
|
|
|
20
13
|
_Inspired by [Phoenix LiveView](https://youtu.be/Z2DU0qLfPIY?t=670)._ 🙌
|
|
21
14
|
|
|
22
|
-
##
|
|
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 -->
|
|
53
|
-
|
|
54
|
-
## Before you Begin
|
|
55
|
-
|
|
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._
|
|
61
|
-
|
|
62
|
-
StimulusReflex offers 3 primary benefits over the traditional Rails HTTP request/response cycle.
|
|
63
|
-
|
|
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_
|
|
67
|
-
|
|
68
|
-
## How it Works
|
|
69
|
-
|
|
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...
|
|
74
|
-
|
|
75
|
-
__Yes, it really is that simple.__
|
|
76
|
-
There are no hidden gotchas.
|
|
77
|
-
|
|
78
|
-

|
|
79
|
-
|
|
80
|
-
## Setup
|
|
81
|
-
|
|
82
|
-
### JavaScript
|
|
83
|
-
|
|
84
|
-
```
|
|
85
|
-
yarn add stimulus_reflex
|
|
86
|
-
```
|
|
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
|
-
|
|
92
|
-
```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);
|
|
101
|
-
```
|
|
102
|
-
|
|
103
|
-
### Gemfile
|
|
104
|
-
|
|
105
|
-
```ruby
|
|
106
|
-
gem "stimulus_reflex"
|
|
107
|
-
```
|
|
108
|
-
|
|
109
|
-
## Usage
|
|
110
|
-
|
|
111
|
-
### Implicit Declarative Reflexes
|
|
112
|
-
|
|
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.
|
|
115
|
-
|
|
116
|
-
#### app/views/pages/example.html.erb
|
|
117
|
-
|
|
118
|
-
```erb
|
|
119
|
-
<head></head>
|
|
120
|
-
<body>
|
|
121
|
-
<a href="#" data-reflex="click->ExampleReflex#increment" data-step="1" data-count="<%= @count.to_i %>">
|
|
122
|
-
Increment <%= @count.to_i %>
|
|
123
|
-
</a>
|
|
124
|
-
</body>
|
|
125
|
-
</html>
|
|
126
|
-
```
|
|
127
|
-
|
|
128
|
-
#### app/reflexes/example_reflex.rb
|
|
129
|
-
|
|
130
|
-
```ruby
|
|
131
|
-
class ExampleReflex < StimulusReflex::Reflex
|
|
132
|
-
def increment
|
|
133
|
-
@count = element.dataset[:count].to_i + element.dataset[:step].to_i
|
|
134
|
-
end
|
|
135
|
-
end
|
|
136
|
-
```
|
|
137
|
-
|
|
138
|
-
The code above will automatically update the relevant DOM nodes with the updated count whenever the anchor is clicked.
|
|
139
|
-
|
|
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.
|
|
143
|
-
|
|
144
|
-
### Explicitly Defined Reflexes
|
|
145
|
-
|
|
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.
|
|
148
|
-
|
|
149
|
-
#### app/views/pages/example.html.erb
|
|
150
|
-
|
|
151
|
-
```erb
|
|
152
|
-
<head></head>
|
|
153
|
-
<body>
|
|
154
|
-
<a href="#" data-controller="example" data-action="click->example#increment">
|
|
155
|
-
Increment <%= @count.to_i %>
|
|
156
|
-
</a>
|
|
157
|
-
</body>
|
|
158
|
-
</html>
|
|
159
|
-
```
|
|
160
|
-
|
|
161
|
-
#### app/javascript/controllers/example.js
|
|
162
|
-
|
|
163
|
-
```javascript
|
|
164
|
-
import { Controller } from "stimulus"
|
|
165
|
-
import StimulusReflex from "stimulus_reflex"
|
|
15
|
+
## Docs
|
|
166
16
|
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
StimulusReflex.register(this);
|
|
170
|
-
}
|
|
17
|
+
- [Official Documentation](https://docs.stimulusreflex.com)
|
|
18
|
+
- [Documentation Source Code](https://github.com/hopsoft/stimulus_reflex/tree/master/docs)
|
|
171
19
|
|
|
172
|
-
|
|
173
|
-
// trigger a server-side reflex and a client-side page update
|
|
174
|
-
this.stimulate('ExampleReflex#increment', 1);
|
|
175
|
-
}
|
|
176
|
-
}
|
|
177
|
-
```
|
|
178
|
-
|
|
179
|
-
#### app/reflexes/example_reflex.rb
|
|
180
|
-
|
|
181
|
-
```ruby
|
|
182
|
-
class ExampleReflex < StimulusReflex::Reflex
|
|
183
|
-
def increment(step = 1)
|
|
184
|
-
@count = @count.to_i + step
|
|
185
|
-
end
|
|
186
|
-
end
|
|
187
|
-
```
|
|
188
|
-
|
|
189
|
-
## What Just Happened
|
|
190
|
-
|
|
191
|
-
The following happens when a `StimulusReflex::Reflex` is invoked.
|
|
192
|
-
|
|
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._
|
|
194
|
-
2. The re-rendered HTML is sent to the client over the ActionCable socket.
|
|
195
|
-
3. The page is updated via fast DOM diffing courtesy of morphdom.
|
|
196
|
-
|
|
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
|
|
20
|
+
## Contributing
|
|
287
21
|
|
|
288
|
-
|
|
22
|
+
### Code of Conduct
|
|
289
23
|
|
|
290
|
-
|
|
24
|
+
Everyone interacting with StimulusReflex is expected to follow the [Code of Conduct](CODE_OF_CONDUCT.md)
|
|
291
25
|
|
|
292
26
|
### Coding Standards
|
|
293
27
|
|
|
294
28
|
This project uses [Standard](https://github.com/testdouble/standard)
|
|
295
|
-
and [Prettier](https://github.com/
|
|
29
|
+
and [Prettier-Standard](https://github.com/sheerun/prettier-standard) to minimize bike shedding related to code formatting.
|
|
30
|
+
|
|
296
31
|
Please run `./bin/standardize` prior submitting pull requests.
|
|
297
32
|
|
|
33
|
+
View the [wiki](https://github.com/hopsoft/stimulus_reflex/wiki/Editor-Configuration) to see recommendations for configuring your editor to work best with the project.
|
|
34
|
+
|
|
298
35
|
### Releasing
|
|
299
36
|
|
|
300
37
|
1. Bump version number at `lib/stimulus_reflex/version.rb`
|
|
301
38
|
1. Run `rake build`
|
|
302
39
|
1. Run `rake release`
|
|
303
40
|
1. Change directories `cd ./javascript`
|
|
304
|
-
1. Run `yarn publish
|
|
305
|
-
|
|
41
|
+
1. Run `yarn publish` - NOTE: this will throw a fatal error because the tag already exists but the package will still publish
|
|
42
|
+
|
|
43
|
+
## License
|
|
44
|
+
|
|
45
|
+
StimulusReflex is released under the [MIT License](LICENSE.txt).
|
data/Rakefile
CHANGED
data/bin/loc
ADDED
data/bin/standardize
CHANGED
|
@@ -18,6 +18,8 @@ class StimulusReflex::Channel < ActionCable::Channel::Base
|
|
|
18
18
|
|
|
19
19
|
def receive(data)
|
|
20
20
|
url = data["url"].to_s
|
|
21
|
+
selectors = (data["selectors"] || []).select(&:present?)
|
|
22
|
+
selectors = ["body"] if selectors.blank?
|
|
21
23
|
target = data["target"].to_s
|
|
22
24
|
reflex_name, method_name = target.split("#")
|
|
23
25
|
reflex_name = reflex_name.classify
|
|
@@ -25,21 +27,27 @@ class StimulusReflex::Channel < ActionCable::Channel::Base
|
|
|
25
27
|
element = StimulusReflex::Element.new(data["attrs"])
|
|
26
28
|
|
|
27
29
|
begin
|
|
28
|
-
|
|
30
|
+
reflex_class = reflex_name.constantize
|
|
31
|
+
raise ArgumentError.new("#{reflex_name} is not a StimulusReflex::Reflex") unless is_reflex?(reflex_class)
|
|
32
|
+
reflex = reflex_class.new(self, url: url, element: element, selectors: selectors)
|
|
29
33
|
delegate_call_to_reflex reflex, method_name, arguments
|
|
30
34
|
rescue => invoke_error
|
|
31
|
-
|
|
35
|
+
return broadcast_error("StimulusReflex::Channel Failed to invoke #{target}! #{url} #{invoke_error}", data)
|
|
32
36
|
end
|
|
33
37
|
|
|
34
38
|
begin
|
|
35
|
-
render_page_and_broadcast_morph url, reflex
|
|
39
|
+
render_page_and_broadcast_morph url, reflex, selectors, data
|
|
36
40
|
rescue => render_error
|
|
37
|
-
|
|
41
|
+
broadcast_error "StimulusReflex::Channel Failed to re-render #{url} #{render_error}", data
|
|
38
42
|
end
|
|
39
43
|
end
|
|
40
44
|
|
|
41
45
|
private
|
|
42
46
|
|
|
47
|
+
def is_reflex?(reflex_class)
|
|
48
|
+
reflex_class.ancestors.include? StimulusReflex::Reflex
|
|
49
|
+
end
|
|
50
|
+
|
|
43
51
|
def delegate_call_to_reflex(reflex, method_name, arguments = [])
|
|
44
52
|
method = reflex.method(method_name)
|
|
45
53
|
required_params = method.parameters.select { |(kind, _)| kind == :req }
|
|
@@ -54,9 +62,9 @@ class StimulusReflex::Channel < ActionCable::Channel::Base
|
|
|
54
62
|
end
|
|
55
63
|
end
|
|
56
64
|
|
|
57
|
-
def render_page_and_broadcast_morph(url, reflex)
|
|
65
|
+
def render_page_and_broadcast_morph(url, reflex, selectors, data = {})
|
|
58
66
|
html = render_page(url, reflex)
|
|
59
|
-
|
|
67
|
+
broadcast_morphs selectors, data, html if html.present?
|
|
60
68
|
end
|
|
61
69
|
|
|
62
70
|
def render_page(url, reflex)
|
|
@@ -90,14 +98,28 @@ class StimulusReflex::Channel < ActionCable::Channel::Base
|
|
|
90
98
|
controller.response.body
|
|
91
99
|
end
|
|
92
100
|
|
|
93
|
-
def
|
|
94
|
-
|
|
95
|
-
|
|
101
|
+
def broadcast_morphs(selectors, data, html)
|
|
102
|
+
document = Nokogiri::HTML(html)
|
|
103
|
+
selectors.each do |selector|
|
|
104
|
+
match = document.css(selector)
|
|
105
|
+
next if match.blank?
|
|
106
|
+
cable_ready[stream_name].morph(
|
|
107
|
+
selector: selector,
|
|
108
|
+
html: match.inner_html,
|
|
109
|
+
children_only: true,
|
|
110
|
+
permanent_attribute_name: "data-reflex-permanent",
|
|
111
|
+
stimulus_reflex: data
|
|
112
|
+
)
|
|
113
|
+
end
|
|
96
114
|
cable_ready.broadcast
|
|
97
115
|
end
|
|
98
116
|
|
|
99
|
-
def
|
|
100
|
-
|
|
101
|
-
|
|
117
|
+
def broadcast_error(message, data = {})
|
|
118
|
+
logger.error "\e[31m#{message}\e[0m"
|
|
119
|
+
cable_ready[stream_name].dispatch_event(
|
|
120
|
+
name: "stimulus-reflex:500",
|
|
121
|
+
detail: {stimulus_reflex: data.merge(error: message)}
|
|
122
|
+
)
|
|
123
|
+
cable_ready.broadcast
|
|
102
124
|
end
|
|
103
125
|
end
|
|
@@ -1,15 +1,16 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
class StimulusReflex::Reflex
|
|
4
|
-
attr_reader :channel, :url, :element
|
|
4
|
+
attr_reader :channel, :url, :element, :selectors
|
|
5
5
|
|
|
6
6
|
delegate :connection, to: :channel
|
|
7
7
|
delegate :session, to: :request
|
|
8
8
|
|
|
9
|
-
def initialize(channel, url: nil, element: nil)
|
|
9
|
+
def initialize(channel, url: nil, element: nil, selectors: [])
|
|
10
10
|
@channel = channel
|
|
11
11
|
@url = url
|
|
12
12
|
@element = element
|
|
13
|
+
@selectors = selectors
|
|
13
14
|
end
|
|
14
15
|
|
|
15
16
|
def request
|
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.
|
|
4
|
+
version: 2.1.2
|
|
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
|
|
12
|
+
date: 2019-10-09 00:00:00.000000000 Z
|
|
13
13
|
dependencies:
|
|
14
14
|
- !ruby/object:Gem::Dependency
|
|
15
15
|
name: rack
|
|
@@ -59,14 +59,14 @@ dependencies:
|
|
|
59
59
|
requirements:
|
|
60
60
|
- - ">="
|
|
61
61
|
- !ruby/object:Gem::Version
|
|
62
|
-
version: 4.0.
|
|
62
|
+
version: 4.0.7
|
|
63
63
|
type: :runtime
|
|
64
64
|
prerelease: false
|
|
65
65
|
version_requirements: !ruby/object:Gem::Requirement
|
|
66
66
|
requirements:
|
|
67
67
|
- - ">="
|
|
68
68
|
- !ruby/object:Gem::Version
|
|
69
|
-
version: 4.0.
|
|
69
|
+
version: 4.0.7
|
|
70
70
|
- !ruby/object:Gem::Dependency
|
|
71
71
|
name: bundler
|
|
72
72
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -145,13 +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
|
-
- README.md.orig
|
|
153
153
|
- Rakefile
|
|
154
154
|
- bin/console
|
|
155
|
+
- bin/loc
|
|
155
156
|
- bin/setup
|
|
156
157
|
- bin/standardize
|
|
157
158
|
- lib/stimulus_reflex.rb
|
data/README.md.orig
DELETED
|
@@ -1,159 +0,0 @@
|
|
|
1
|
-
[](http://blog.codinghorror.com/the-best-code-is-no-code-at-all/)
|
|
2
|
-
[](https://codeclimate.com/github/hopsoft/stimulus_reflex/maintainability)
|
|
3
|
-
|
|
4
|
-
# StimulusReflex
|
|
5
|
-
|
|
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
|
-
---
|
|
18
|
-
|
|
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.
|
|
22
|
-
|
|
23
|
-
---
|
|
24
|
-
|
|
25
|
-
_Inspired by [Phoenix LiveView](https://youtu.be/Z2DU0qLfPIY?t=670)._ 🙌
|
|
26
|
-
|
|
27
|
-
## How it Works
|
|
28
|
-
|
|
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...
|
|
33
|
-
|
|
34
|
-
__Yes, it really is that simple.__
|
|
35
|
-
There are no hidden gotchas.
|
|
36
|
-
|
|
37
|
-

|
|
38
|
-
|
|
39
|
-
## Setup
|
|
40
|
-
|
|
41
|
-
### JavaScript
|
|
42
|
-
|
|
43
|
-
```
|
|
44
|
-
yarn add stimulus_reflex
|
|
45
|
-
```
|
|
46
|
-
|
|
47
|
-
### Gemfile
|
|
48
|
-
|
|
49
|
-
```ruby
|
|
50
|
-
gem "stimulus_reflex"
|
|
51
|
-
```
|
|
52
|
-
|
|
53
|
-
## Basic Usage
|
|
54
|
-
|
|
55
|
-
### app/views/pages/example.html.erb
|
|
56
|
-
|
|
57
|
-
```erb
|
|
58
|
-
<head></head>
|
|
59
|
-
<body>
|
|
60
|
-
<a href="#" data-controller="example" data-action="click->example#increment">
|
|
61
|
-
Increment <%= @count.to_i %>
|
|
62
|
-
</a>
|
|
63
|
-
</body>
|
|
64
|
-
</html>
|
|
65
|
-
```
|
|
66
|
-
|
|
67
|
-
### app/javascript/controllers/example.js
|
|
68
|
-
|
|
69
|
-
```javascript
|
|
70
|
-
import { Controller } from "stimulus"
|
|
71
|
-
import StimulusReflex from "stimulus_reflex"
|
|
72
|
-
|
|
73
|
-
export default class extends Controller {
|
|
74
|
-
connect() {
|
|
75
|
-
StimulusReflex.register(this);
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
increment() {
|
|
79
|
-
// trigger a server-side reflex and a client-side page update
|
|
80
|
-
this.stimulate('ExampleReflex#increment', 1);
|
|
81
|
-
}
|
|
82
|
-
}
|
|
83
|
-
```
|
|
84
|
-
|
|
85
|
-
### app/reflexes/example_reflex.rb
|
|
86
|
-
|
|
87
|
-
```ruby
|
|
88
|
-
class ExampleReflex < StimulusReflex::Reflex
|
|
89
|
-
def increment(step = 1)
|
|
90
|
-
@count = @count.to_i + step
|
|
91
|
-
end
|
|
92
|
-
end
|
|
93
|
-
```
|
|
94
|
-
|
|
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
|
-
```
|
|
141
|
-
|
|
142
|
-
The default value is `25`.
|
|
143
|
-
|
|
144
|
-
## Demo Applications
|
|
145
|
-
|
|
146
|
-
Building apps with StimulusReflex should evoke your memories of the original [Rails demo video](https://www.youtube.com/watch?v=Gzj723LkRJY).
|
|
147
|
-
|
|
148
|
-
> Look at all the things I'm **not** doing. -DHH
|
|
149
|
-
|
|
150
|
-
- [TodoMVC](https://github.com/hopsoft/stimulus_reflex_todomvc)
|
|
151
|
-
|
|
152
|
-
<<<<<<< HEAD
|
|
153
|
-
## Contributing
|
|
154
|
-
|
|
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
|