stimulus_reflex 1.1.1 → 2.1.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 +4 -4
- data/CODE_OF_CONDUCT.md +76 -0
- data/Gemfile.lock +14 -14
- data/README.md +19 -233
- data/Rakefile +6 -1
- data/bin/loc +3 -0
- data/bin/standardize +2 -2
- data/lib/stimulus_reflex.rb +1 -1
- data/lib/stimulus_reflex/channel.rb +39 -26
- data/lib/stimulus_reflex/{dom_element.rb → element.rb} +1 -1
- data/lib/stimulus_reflex/reflex.rb +4 -2
- data/lib/stimulus_reflex/version.rb +1 -1
- metadata +7 -6
- 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: 8966252da698bc7f603c5899b1f2a8b38aa3d8af53d20188eba815a293b1388d
|
|
4
|
+
data.tar.gz: 4ca61b2211ff6cbafb84ccf59ac9cfdb6803bfb5dcf38f56782ec8babb307e4c
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 6b21d5c651bd372d5cdd2ddc2928f8e15f118121a430db8fc0bf609585a2fc1a02d5e19a5decb504d5e262aae36c89b1b706093a4aa9eadc9e69886b56da5bda
|
|
7
|
+
data.tar.gz: 19cea4683e33acfd2abe563e4c27c37a7335d9ce32b543e59b606d7d58a6e5c58d819fac0f4eaf1cc9c9bee4524c01a8b00a13a5c9d329ac4b403ee980615f8f
|
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 (
|
|
5
|
-
cable_ready (>= 4.0.
|
|
4
|
+
stimulus_reflex (2.1.1)
|
|
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)
|
|
@@ -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.
|
|
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,259 +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
|
+

|
|
3
6
|
|
|
4
7
|
# StimulusReflex
|
|
5
8
|
|
|
6
9
|
_reflex_ - an action that is performed as a response to a stimulus
|
|
7
10
|
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
This project supports building [reactive applications](https://en.wikipedia.org/wiki/Reactive_programming)
|
|
11
|
-
with the Rails tooling you already know and love.
|
|
12
|
-
It's designed to work perfectly with [server rendered HTML](https://guides.rubyonrails.org/action_view_overview.html),
|
|
13
|
-
[Russian doll caching](https://edgeguides.rubyonrails.org/caching_with_rails.html#russian-doll-caching),
|
|
14
|
-
[Stimulus](https://stimulusjs.org), [Turbolinks](https://www.youtube.com/watch?v=SWEts0rlezA), etc...
|
|
15
|
-
|
|
16
|
-
__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/).
|
|
17
12
|
|
|
18
13
|
_Inspired by [Phoenix LiveView](https://youtu.be/Z2DU0qLfPIY?t=670)._ 🙌
|
|
19
14
|
|
|
20
|
-
##
|
|
21
|
-
|
|
22
|
-
<!-- toc -->
|
|
23
|
-
|
|
24
|
-
- [Before you Begin](#before-you-begin)
|
|
25
|
-
- [How it Works](#how-it-works)
|
|
26
|
-
- [Setup](#setup)
|
|
27
|
-
* [JavaScript](#javascript)
|
|
28
|
-
* [Gemfile](#gemfile)
|
|
29
|
-
- [Basic Usage](#basic-usage)
|
|
30
|
-
* [app/views/pages/example.html.erb](#appviewspagesexamplehtmlerb)
|
|
31
|
-
* [app/javascript/controllers/example.js](#appjavascriptcontrollersexamplejs)
|
|
32
|
-
* [app/reflexes/example_reflex.rb](#appreflexesexample_reflexrb)
|
|
33
|
-
- [Advanced Usage](#advanced-usage)
|
|
34
|
-
* [Reflex Methods](#reflex-methods)
|
|
35
|
-
+ [The `options` Keyword Argument](#the-options-keyword-argument)
|
|
36
|
-
* [ActionCable](#actioncable)
|
|
37
|
-
+ [Performance](#performance)
|
|
38
|
-
+ [ActionCable Rooms](#actioncable-rooms)
|
|
39
|
-
* [Render Delay](#render-delay)
|
|
40
|
-
- [Demo Applications](#demo-applications)
|
|
41
|
-
- [Contributing](#contributing)
|
|
42
|
-
* [Coding Standards](#coding-standards)
|
|
43
|
-
* [Releasing](#releasing)
|
|
44
|
-
|
|
45
|
-
<!-- tocstop -->
|
|
46
|
-
|
|
47
|
-
## Before you Begin
|
|
48
|
-
|
|
49
|
-
StimulusReflex provides functionality similar to what can already be achieved with Rails by combining
|
|
50
|
-
[UJS remote elements](https://guides.rubyonrails.org/working_with_javascript_in_rails.html#remote-elements)
|
|
51
|
-
, [Stimulus](https://stimulusjs.org), and [Turbolinks](https://github.com/turbolinks/turbolinks).
|
|
52
|
-
_Consider building with standard Rails tooling before introducing StimulusReflex._
|
|
53
|
-
_Check out the [Stimulus TodoMVC](https://github.com/hopsoft/stimulus_todomvc) example if you are unsure how to accomplish this._
|
|
54
|
-
|
|
55
|
-
StimulusReflex offers 3 primary benefits over the traditional Rails HTTP request/response cycle.
|
|
56
|
-
|
|
57
|
-
1. __Communication happens on the ActionCable web socket__ _- saves time by avoiding the overhead of establishishing traditional HTTP connections_
|
|
58
|
-
1. __The controller action is invoked directly__ _- skips framework overhead such as the middleware chain, etc..._
|
|
59
|
-
1. __DOM diffing is used to update the page__ _- provides faster rendering and less jitter_
|
|
60
|
-
|
|
61
|
-
## How it Works
|
|
62
|
-
|
|
63
|
-
1. Render a standard Rails view template
|
|
64
|
-
1. Use [Stimulus](https://stimulusjs.org) and [ActionCable](https://edgeguides.rubyonrails.org/action_cable_overview.html) to invoke a method on the server
|
|
65
|
-
1. Watch the page automatically render updates via fast [DOM diffing](https://github.com/patrick-steele-idem/morphdom)
|
|
66
|
-
1. That's it...
|
|
67
|
-
|
|
68
|
-
__Yes, it really is that simple.__
|
|
69
|
-
There are no hidden gotchas.
|
|
70
|
-
|
|
71
|
-

|
|
72
|
-
|
|
73
|
-
## Setup
|
|
74
|
-
|
|
75
|
-
### JavaScript
|
|
76
|
-
|
|
77
|
-
```
|
|
78
|
-
yarn add stimulus_reflex
|
|
79
|
-
```
|
|
80
|
-
|
|
81
|
-
### Gemfile
|
|
82
|
-
|
|
83
|
-
```ruby
|
|
84
|
-
gem "stimulus_reflex"
|
|
85
|
-
```
|
|
86
|
-
|
|
87
|
-
## Basic Usage
|
|
88
|
-
|
|
89
|
-
### app/views/pages/example.html.erb
|
|
90
|
-
|
|
91
|
-
```erb
|
|
92
|
-
<head></head>
|
|
93
|
-
<body>
|
|
94
|
-
<a href="#" data-controller="example" data-action="click->example#increment">
|
|
95
|
-
Increment <%= @count.to_i %>
|
|
96
|
-
</a>
|
|
97
|
-
</body>
|
|
98
|
-
</html>
|
|
99
|
-
```
|
|
100
|
-
|
|
101
|
-
### app/javascript/controllers/example.js
|
|
102
|
-
|
|
103
|
-
```javascript
|
|
104
|
-
import { Controller } from "stimulus"
|
|
105
|
-
import StimulusReflex from "stimulus_reflex"
|
|
106
|
-
|
|
107
|
-
export default class extends Controller {
|
|
108
|
-
connect() {
|
|
109
|
-
StimulusReflex.register(this);
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
increment() {
|
|
113
|
-
// trigger a server-side reflex and a client-side page update
|
|
114
|
-
this.stimulate('ExampleReflex#increment', 1);
|
|
115
|
-
}
|
|
116
|
-
}
|
|
117
|
-
```
|
|
118
|
-
|
|
119
|
-
### app/reflexes/example_reflex.rb
|
|
120
|
-
|
|
121
|
-
```ruby
|
|
122
|
-
class ExampleReflex < StimulusReflex::Reflex
|
|
123
|
-
def increment(step = 1)
|
|
124
|
-
@count = @count.to_i + step
|
|
125
|
-
end
|
|
126
|
-
end
|
|
127
|
-
```
|
|
128
|
-
|
|
129
|
-
The following happens after the `StimulusReflex::Reflex` method call finishes.
|
|
130
|
-
|
|
131
|
-
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._
|
|
132
|
-
2. The re-rendered HTML is sent to the client over the ActionCable socket.
|
|
133
|
-
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._
|
|
134
|
-
|
|
135
|
-
## Advanced Usage
|
|
136
|
-
|
|
137
|
-
### Reflex Methods
|
|
15
|
+
## Docs
|
|
138
16
|
|
|
139
|
-
|
|
17
|
+
- [Official Documentation](https://docs.stimulusreflex.com)
|
|
18
|
+
- [Documentation Source Code](https://github.com/hopsoft/stimulus_reflex/tree/master/docs)
|
|
140
19
|
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
- This is the only supported keyword argument.
|
|
144
|
-
- It must appear after ordinal arguments.
|
|
145
|
-
|
|
146
|
-
```ruby
|
|
147
|
-
class ExampleReflex < StimulusReflex::Reflex
|
|
148
|
-
def work(options: {})
|
|
149
|
-
# ...
|
|
150
|
-
end
|
|
151
|
-
|
|
152
|
-
def other_work(value, options: {})
|
|
153
|
-
# ...
|
|
154
|
-
end
|
|
155
|
-
end
|
|
156
|
-
```
|
|
157
|
-
|
|
158
|
-
The `options` value contains all of the Stimulus controller's
|
|
159
|
-
[DOM element attributes](https://developer.mozilla.org/en-US/docs/Web/API/Element/attributes) as well as other properties like `checked` and `value`.
|
|
160
|
-
_Most of the values will be strings._
|
|
161
|
-
Here's an example:
|
|
162
|
-
|
|
163
|
-
```html
|
|
164
|
-
<checkbox checked label="Example" data-controller="checkbox" data-value="123" />
|
|
165
|
-
```
|
|
166
|
-
|
|
167
|
-
The markup above produces the following behavior in a reflex method.
|
|
168
|
-
|
|
169
|
-
```ruby
|
|
170
|
-
class ExampleReflex < StimulusReflex::Reflex
|
|
171
|
-
def work(options: {})
|
|
172
|
-
options[:checked] # => true
|
|
173
|
-
options[:label] # => "Example"
|
|
174
|
-
options["data-controller"] # => "checkbox"
|
|
175
|
-
options["data-value"] # => "123"
|
|
176
|
-
options.dataset[:controller] # => "checkbox"
|
|
177
|
-
options.dataset[:value] # => "123"
|
|
178
|
-
end
|
|
179
|
-
end
|
|
180
|
-
```
|
|
181
|
-
|
|
182
|
-
- `options[:checked]` holds a boolean
|
|
183
|
-
- `options[:selected]` holds a boolean
|
|
184
|
-
- `options[:value]` holds the [DOM element's value](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input#value)
|
|
185
|
-
- `select` elements assign `options[:value]` to their selected option's value
|
|
186
|
-
- `select` elements with _multiselect_ enabled assign `options[:values]` to their selected options values
|
|
187
|
-
- All other values stored in `options` are extracted from the DOM element's attributes
|
|
188
|
-
|
|
189
|
-
### ActionCable
|
|
190
|
-
|
|
191
|
-
StimulusReflex will use the ActionCable defaults of `window.App` and `App.cable` if they exist.
|
|
192
|
-
If these defaults do not exist, StimulusReflex will establish a new socket connection.
|
|
193
|
-
|
|
194
|
-
#### Performance
|
|
195
|
-
|
|
196
|
-
ActionCable emits verbose log messages. Disabling ActionCable logs may improve performance.
|
|
197
|
-
|
|
198
|
-
```ruby
|
|
199
|
-
# config/application.rb
|
|
200
|
-
|
|
201
|
-
ActionCable.server.config.logger = Logger.new(nil)
|
|
202
|
-
```
|
|
203
|
-
|
|
204
|
-
#### ActionCable Rooms
|
|
205
|
-
|
|
206
|
-
You may find the need to restrict notifications to a specific room.
|
|
207
|
-
This can be accomplished by setting the `data-room` attribute on the StimulusController element.
|
|
208
|
-
|
|
209
|
-
```erb
|
|
210
|
-
<a href="#" data-controller="example" data-action="click->example#increment" data-room="12345">
|
|
211
|
-
```
|
|
212
|
-
|
|
213
|
-
### Render Delay
|
|
214
|
-
|
|
215
|
-
An attempt is made to reduce repaint/reflow jitter when users trigger lots of updates.
|
|
216
|
-
|
|
217
|
-
You can control how long to wait _(think debounce)_ prior to updating the page.
|
|
218
|
-
Simply set the `renderDelay` _(milliseconds)_ option when registering the controller.
|
|
219
|
-
|
|
220
|
-
```javascript
|
|
221
|
-
export default class extends Controller {
|
|
222
|
-
connect() {
|
|
223
|
-
StimulusReflex.register(this, {renderDelay: 200});
|
|
224
|
-
}
|
|
225
|
-
}
|
|
226
|
-
```
|
|
227
|
-
|
|
228
|
-
The default value is `25`.
|
|
229
|
-
|
|
230
|
-
## Demo Applications
|
|
231
|
-
|
|
232
|
-
Building apps with StimulusReflex should evoke your memories of the original [Rails demo video](https://www.youtube.com/watch?v=Gzj723LkRJY).
|
|
233
|
-
|
|
234
|
-
> Look at all the things I'm **not** doing. -DHH
|
|
20
|
+
## Contributing
|
|
235
21
|
|
|
236
|
-
|
|
22
|
+
### Code of Conduct
|
|
237
23
|
|
|
238
|
-
|
|
24
|
+
Everyone interacting with StimulusReflex is expected to follow the [Code of Conduct](CODE_OF_CONDUCT.md)
|
|
239
25
|
|
|
240
26
|
### Coding Standards
|
|
241
27
|
|
|
242
28
|
This project uses [Standard](https://github.com/testdouble/standard)
|
|
243
|
-
and [Prettier](https://github.com/
|
|
29
|
+
and [Prettier-Standard](https://github.com/sheerun/prettier-standard) to minimize bike shedding related to code formatting.
|
|
30
|
+
|
|
244
31
|
Please run `./bin/standardize` prior submitting pull requests.
|
|
245
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
|
+
|
|
246
35
|
### Releasing
|
|
247
36
|
|
|
248
37
|
1. Bump version number at `lib/stimulus_reflex/version.rb`
|
|
249
38
|
1. Run `rake build`
|
|
250
39
|
1. Run `rake release`
|
|
251
40
|
1. Change directories `cd ./javascript`
|
|
252
|
-
1. Run `yarn publish`
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
1. Push tags to GitHub
|
|
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
|
|
256
44
|
|
|
257
|
-
|
|
258
|
-
git push --tags
|
|
259
|
-
```
|
|
45
|
+
StimulusReflex is released under the [MIT License](LICENSE.txt).
|
data/Rakefile
CHANGED
data/bin/loc
ADDED
data/bin/standardize
CHANGED
data/lib/stimulus_reflex.rb
CHANGED
|
@@ -18,54 +18,53 @@ 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
|
|
24
26
|
arguments = data["args"] || []
|
|
25
|
-
|
|
27
|
+
element = StimulusReflex::Element.new(data["attrs"])
|
|
26
28
|
|
|
27
29
|
begin
|
|
28
|
-
|
|
29
|
-
|
|
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)
|
|
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
|
|
|
43
|
-
def
|
|
47
|
+
def is_reflex?(reflex_class)
|
|
48
|
+
reflex_class.ancestors.include? StimulusReflex::Reflex
|
|
49
|
+
end
|
|
50
|
+
|
|
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 }
|
|
46
54
|
optional_params = method.parameters.select { |(kind, _)| kind == :opt }
|
|
47
|
-
accepts_options_kwarg = method.parameters.select { |(kind, name)| name == :options && kind.to_s.start_with?("key") }.size > 0
|
|
48
55
|
|
|
49
56
|
if arguments.size == 0 && required_params.size == 0
|
|
50
|
-
|
|
51
|
-
reflex.public_send method_name, {options: options}
|
|
52
|
-
else
|
|
53
|
-
reflex.public_send method_name
|
|
54
|
-
end
|
|
57
|
+
reflex.public_send method_name
|
|
55
58
|
elsif arguments.size >= required_params.size && arguments.size <= required_params.size + optional_params.size
|
|
56
|
-
|
|
57
|
-
reflex.public_send method_name, *arguments, {options: options}
|
|
58
|
-
else
|
|
59
|
-
reflex.public_send method_name, *arguments
|
|
60
|
-
end
|
|
59
|
+
reflex.public_send method_name, *arguments
|
|
61
60
|
else
|
|
62
61
|
raise ArgumentError.new("wrong number of arguments (given #{arguments.inspect}, expected #{required_params.inspect}, optional #{optional_params.inspect})")
|
|
63
62
|
end
|
|
64
63
|
end
|
|
65
64
|
|
|
66
|
-
def render_page_and_broadcast_morph(url, reflex)
|
|
65
|
+
def render_page_and_broadcast_morph(url, reflex, selectors, data = {})
|
|
67
66
|
html = render_page(url, reflex)
|
|
68
|
-
|
|
67
|
+
broadcast_morphs selectors, data, html if html.present?
|
|
69
68
|
end
|
|
70
69
|
|
|
71
70
|
def render_page(url, reflex)
|
|
@@ -99,14 +98,28 @@ class StimulusReflex::Channel < ActionCable::Channel::Base
|
|
|
99
98
|
controller.response.body
|
|
100
99
|
end
|
|
101
100
|
|
|
102
|
-
def
|
|
103
|
-
|
|
104
|
-
|
|
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
|
|
105
114
|
cable_ready.broadcast
|
|
106
115
|
end
|
|
107
116
|
|
|
108
|
-
def
|
|
109
|
-
|
|
110
|
-
|
|
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
|
|
111
124
|
end
|
|
112
125
|
end
|
|
@@ -1,14 +1,16 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
class StimulusReflex::Reflex
|
|
4
|
-
attr_reader :channel, :url
|
|
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)
|
|
9
|
+
def initialize(channel, url: nil, element: nil, selectors: [])
|
|
10
10
|
@channel = channel
|
|
11
11
|
@url = url
|
|
12
|
+
@element = element
|
|
13
|
+
@selectors = selectors
|
|
12
14
|
end
|
|
13
15
|
|
|
14
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:
|
|
4
|
+
version: 2.1.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-
|
|
12
|
+
date: 2019-10-08 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,18 +145,19 @@ 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
|
|
158
159
|
- lib/stimulus_reflex/channel.rb
|
|
159
|
-
- lib/stimulus_reflex/
|
|
160
|
+
- lib/stimulus_reflex/element.rb
|
|
160
161
|
- lib/stimulus_reflex/reflex.rb
|
|
161
162
|
- lib/stimulus_reflex/version.rb
|
|
162
163
|
homepage: https://github.com/hopsoft/stimulus_reflex
|
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
|