stimulus_reflex 1.1.1 → 2.0.0
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 +4 -4
- data/Gemfile.lock +1 -1
- data/README.md +105 -59
- data/lib/stimulus_reflex.rb +1 -1
- data/lib/stimulus_reflex/channel.rb +6 -15
- data/lib/stimulus_reflex/{dom_element.rb → element.rb} +1 -1
- data/lib/stimulus_reflex/reflex.rb +3 -2
- data/lib/stimulus_reflex/version.rb +1 -1
- metadata +3 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 8072c82ab1f16ce62c41e6d970952c3762a9e2fe5d87d2036ff2f37af7f3a4fb
|
4
|
+
data.tar.gz: b3549d33023d9c27e878443f48391e13b82562e4dc9d9cbfb64b9744d8fe0702
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 5ba2646379f9bbe65485d947c55467640e890d516ad571f704e064f0fb78956b977ba405d5698efdb6fd3b7e87f1075a9948d59c4b6ec2855410d3b290137a31
|
7
|
+
data.tar.gz: 756a22a1a4cb5f1d4ad985dd1424ed15edc3d2e4d8efcf080d3c3a9a551a170fc1a76d84543fd105acc104b97c07d7f377348a00ec329222fa6341c0e627051c
|
data/Gemfile.lock
CHANGED
data/README.md
CHANGED
@@ -1,5 +1,7 @@
|
|
1
|
-
[![Lines of Code](http://img.shields.io/badge/lines_of_code-
|
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
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)
|
3
5
|
|
4
6
|
# StimulusReflex
|
5
7
|
|
@@ -25,14 +27,19 @@ _Inspired by [Phoenix LiveView](https://youtu.be/Z2DU0qLfPIY?t=670)._ 🙌
|
|
25
27
|
- [How it Works](#how-it-works)
|
26
28
|
- [Setup](#setup)
|
27
29
|
* [JavaScript](#javascript)
|
30
|
+
+ [app/javascript/controllers/index.js](#appjavascriptcontrollersindexjs)
|
28
31
|
* [Gemfile](#gemfile)
|
29
|
-
- [
|
30
|
-
* [
|
31
|
-
|
32
|
-
|
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)
|
33
41
|
- [Advanced Usage](#advanced-usage)
|
34
|
-
* [Reflex
|
35
|
-
+ [The `options` Keyword Argument](#the-options-keyword-argument)
|
42
|
+
* [The Reflex `element` property](#the-reflex-element-property)
|
36
43
|
* [ActionCable](#actioncable)
|
37
44
|
+ [Performance](#performance)
|
38
45
|
+ [ActionCable Rooms](#actioncable-rooms)
|
@@ -77,6 +84,21 @@ There are no hidden gotchas.
|
|
77
84
|
```
|
78
85
|
yarn add stimulus_reflex
|
79
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
|
+
```
|
80
102
|
|
81
103
|
### Gemfile
|
82
104
|
|
@@ -84,9 +106,47 @@ yarn add stimulus_reflex
|
|
84
106
|
gem "stimulus_reflex"
|
85
107
|
```
|
86
108
|
|
87
|
-
##
|
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.
|
88
115
|
|
89
|
-
|
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
|
90
150
|
|
91
151
|
```erb
|
92
152
|
<head></head>
|
@@ -98,7 +158,7 @@ gem "stimulus_reflex"
|
|
98
158
|
</html>
|
99
159
|
```
|
100
160
|
|
101
|
-
|
161
|
+
#### app/javascript/controllers/example.js
|
102
162
|
|
103
163
|
```javascript
|
104
164
|
import { Controller } from "stimulus"
|
@@ -116,7 +176,7 @@ export default class extends Controller {
|
|
116
176
|
}
|
117
177
|
```
|
118
178
|
|
119
|
-
|
179
|
+
#### app/reflexes/example_reflex.rb
|
120
180
|
|
121
181
|
```ruby
|
122
182
|
class ExampleReflex < StimulusReflex::Reflex
|
@@ -126,65 +186,57 @@ class ExampleReflex < StimulusReflex::Reflex
|
|
126
186
|
end
|
127
187
|
```
|
128
188
|
|
129
|
-
|
189
|
+
## What Just Happened
|
190
|
+
|
191
|
+
The following happens when a `StimulusReflex::Reflex` is invoked.
|
130
192
|
|
131
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._
|
132
194
|
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.
|
134
|
-
|
135
|
-
## Advanced Usage
|
136
|
-
|
137
|
-
### Reflex Methods
|
195
|
+
3. The page is updated via fast DOM diffing courtesy of morphdom.
|
138
196
|
|
139
|
-
|
197
|
+
_NOTE: While future versions of StimulusReflex may support more granular updates, today the entire body is re-rendered and sent over the socket._
|
140
198
|
|
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
|
199
|
+
## Advanced Usage
|
151
200
|
|
152
|
-
|
153
|
-
# ...
|
154
|
-
end
|
155
|
-
end
|
156
|
-
```
|
201
|
+
### The Reflex `element` property
|
157
202
|
|
158
|
-
|
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
|
159
206
|
[DOM element attributes](https://developer.mozilla.org/en-US/docs/Web/API/Element/attributes) as well as other properties like `checked` and `value`.
|
160
207
|
_Most of the values will be strings._
|
161
|
-
Here's an example:
|
162
208
|
|
163
209
|
```html
|
164
|
-
<checkbox
|
210
|
+
<checkbox id="example"
|
211
|
+
label="Example"
|
212
|
+
data-controller="checkbox"
|
213
|
+
data-value="123"
|
214
|
+
checked />
|
165
215
|
```
|
166
216
|
|
167
|
-
The markup above produces the following behavior in a reflex method.
|
168
|
-
|
169
217
|
```ruby
|
170
218
|
class ExampleReflex < StimulusReflex::Reflex
|
171
|
-
def work(
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
|
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"
|
178
230
|
end
|
179
231
|
end
|
180
232
|
```
|
181
233
|
|
182
|
-
- `
|
183
|
-
- `
|
184
|
-
- `
|
185
|
-
- `select` elements assign `
|
186
|
-
- `select` elements with _multiselect_ enabled assign `
|
187
|
-
- All other values
|
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
|
188
240
|
|
189
241
|
### ActionCable
|
190
242
|
|
@@ -249,11 +301,5 @@ Please run `./bin/standardize` prior submitting pull requests.
|
|
249
301
|
1. Run `rake build`
|
250
302
|
1. Run `rake release`
|
251
303
|
1. Change directories `cd ./javascript`
|
252
|
-
1. Run `yarn publish`
|
253
|
-
1. Assign same version number to the JavaScript package
|
254
|
-
1. Push changes to GitHub
|
255
|
-
1. Push tags to GitHub
|
256
|
-
|
257
|
-
```
|
258
|
-
git push --tags
|
259
|
-
```
|
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/lib/stimulus_reflex.rb
CHANGED
@@ -22,11 +22,11 @@ class StimulusReflex::Channel < ActionCable::Channel::Base
|
|
22
22
|
reflex_name, method_name = target.split("#")
|
23
23
|
reflex_name = reflex_name.classify
|
24
24
|
arguments = data["args"] || []
|
25
|
-
|
25
|
+
element = StimulusReflex::Element.new(data["attrs"])
|
26
26
|
|
27
27
|
begin
|
28
|
-
reflex = reflex_name.constantize.new(self, url: url)
|
29
|
-
delegate_call_to_reflex reflex, method_name, arguments
|
28
|
+
reflex = reflex_name.constantize.new(self, url: url, element: element)
|
29
|
+
delegate_call_to_reflex reflex, method_name, arguments
|
30
30
|
rescue => invoke_error
|
31
31
|
logger.error "\e[31mStimulusReflex::Channel Failed to invoke #{target}! #{url} #{invoke_error}\e[0m"
|
32
32
|
end
|
@@ -40,24 +40,15 @@ class StimulusReflex::Channel < ActionCable::Channel::Base
|
|
40
40
|
|
41
41
|
private
|
42
42
|
|
43
|
-
def delegate_call_to_reflex(reflex, method_name, arguments = []
|
43
|
+
def delegate_call_to_reflex(reflex, method_name, arguments = [])
|
44
44
|
method = reflex.method(method_name)
|
45
45
|
required_params = method.parameters.select { |(kind, _)| kind == :req }
|
46
46
|
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
47
|
|
49
48
|
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
|
49
|
+
reflex.public_send method_name
|
55
50
|
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
|
51
|
+
reflex.public_send method_name, *arguments
|
61
52
|
else
|
62
53
|
raise ArgumentError.new("wrong number of arguments (given #{arguments.inspect}, expected #{required_params.inspect}, optional #{optional_params.inspect})")
|
63
54
|
end
|
@@ -1,14 +1,15 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
class StimulusReflex::Reflex
|
4
|
-
attr_reader :channel, :url
|
4
|
+
attr_reader :channel, :url, :element
|
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)
|
10
10
|
@channel = channel
|
11
11
|
@url = url
|
12
|
+
@element = element
|
12
13
|
end
|
13
14
|
|
14
15
|
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.0.0
|
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-09-11 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: rack
|
@@ -156,7 +156,7 @@ files:
|
|
156
156
|
- bin/standardize
|
157
157
|
- lib/stimulus_reflex.rb
|
158
158
|
- lib/stimulus_reflex/channel.rb
|
159
|
-
- lib/stimulus_reflex/
|
159
|
+
- lib/stimulus_reflex/element.rb
|
160
160
|
- lib/stimulus_reflex/reflex.rb
|
161
161
|
- lib/stimulus_reflex/version.rb
|
162
162
|
homepage: https://github.com/hopsoft/stimulus_reflex
|