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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 8072c82ab1f16ce62c41e6d970952c3762a9e2fe5d87d2036ff2f37af7f3a4fb
4
- data.tar.gz: b3549d33023d9c27e878443f48391e13b82562e4dc9d9cbfb64b9744d8fe0702
3
+ metadata.gz: f261317eba89f3aca406db338af66069b57121ef68188dd32c22a8b263c84c0d
4
+ data.tar.gz: 16ead06810e664961d5d0bea297a82bfd109616c363eecb63e2211efbe0718e0
5
5
  SHA512:
6
- metadata.gz: 5ba2646379f9bbe65485d947c55467640e890d516ad571f704e064f0fb78956b977ba405d5698efdb6fd3b7e87f1075a9948d59c4b6ec2855410d3b290137a31
7
- data.tar.gz: 756a22a1a4cb5f1d4ad985dd1424ed15edc3d2e4d8efcf080d3c3a9a551a170fc1a76d84543fd105acc104b97c07d7f377348a00ec329222fa6341c0e627051c
6
+ metadata.gz: 43c62ef1ddec489d525dd429a78ee0113b215918ac4fa95192cf75fafe356b42705e775b29ccfa2ddf193833b3bc42d5e736828d97234353e2ae06bdd929bd63
7
+ data.tar.gz: 0b5870db51f39af566c2eda1a574783dba205b9f37d1c07083d82a5df6ef6a0a63dde0b43ee0881492155715e945926452f529677b7951d10f27228471a80097
@@ -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.0.0)
5
- cable_ready (>= 4.0.3)
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.3)
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.8.0)
75
+ erubi (1.9.0)
76
76
  globalid (0.4.2)
77
77
  activesupport (>= 4.2.0)
78
- i18n (1.6.0)
78
+ i18n (1.7.0)
79
79
  concurrent-ruby (~> 1.0)
80
80
  jaro_winkler (1.5.3)
81
- loofah (2.2.3)
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.11.3)
93
- nio4r (2.5.1)
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.17.0)
97
- parser (2.6.4.0)
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.2.0)
126
- loofah (~> 2.2, >= 2.2.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 (12.3.3)
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 (3.7.2)
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.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)
@@ -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.1.10)
165
+ zeitwerk (2.2.0)
166
166
 
167
167
  PLATFORMS
168
168
  ruby
data/README.md CHANGED
@@ -1,305 +1,45 @@
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/)
1
+ [![Lines of Code](http://img.shields.io/badge/lines_of_code-465-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
+ ![Prettier-Standard](https://github.com/hopsoft/stimulus_reflex/workflows/Prettier-Standard/badge.svg)
4
+ ![StandardRB](https://github.com/hopsoft/stimulus_reflex/workflows/StandardRB/badge.svg)
5
+ ![Tests](https://github.com/hopsoft/stimulus_reflex/workflows/Tests/badge.svg)
5
6
 
6
7
  # StimulusReflex
7
8
 
8
9
  _reflex_ - an action that is performed as a response to a stimulus
9
10
 
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.__
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
- ## 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 -->
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
- ![How it Works](https://raw.githubusercontent.com/hopsoft/stimulus_reflex/master/docs/diagram.png)
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
- export default class extends Controller {
168
- connect() {
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
- increment() {
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
- - [TodoMVC](https://github.com/hopsoft/stimulus_reflex_todomvc)
22
+ ### Code of Conduct
289
23
 
290
- ## Contributing
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/prettier/prettier) to minimize bike shedding related to code formatting.
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 --tag GIT_TAG_CREATED_BY_RUBYGEMS`
305
- 1. Assign same version number to the JavaScript package _Might not be required?_
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
@@ -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
+ exec "cd javascript && yarn run test"
9
+ end
data/bin/loc ADDED
@@ -0,0 +1,3 @@
1
+ #!/usr/bin/env bash
2
+
3
+ cloc --exclude-dir=node_modules,test --include-ext=rb,js .
data/bin/standardize CHANGED
@@ -1,4 +1,4 @@
1
- #!/bin/bash
1
+ #!/usr/bin/env 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 test/*.js
@@ -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
- reflex = reflex_name.constantize.new(self, url: url, element: element)
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
- logger.error "\e[31mStimulusReflex::Channel Failed to invoke #{target}! #{url} #{invoke_error}\e[0m"
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
- logger.error "\e[31mStimulusReflex::Channel Failed to rerender #{url} #{render_error}\e[0m"
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
- broadcast_morph url, html if html.present?
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 broadcast_morph(url, html)
94
- html = extract_body_html(html)
95
- cable_ready[stream_name].morph selector: "body", html: html, children_only: true
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 extract_body_html(html)
100
- doc = Nokogiri::HTML(html)
101
- doc.css("body").to_s
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
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module StimulusReflex
4
- VERSION = "2.0.0"
4
+ VERSION = "2.1.2"
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.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-11 00:00:00.000000000 Z
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.3
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.3
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
- [![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/)
2
- [![Maintainability](https://api.codeclimate.com/v1/badges/2b24fdbd1ae37a24bedb/maintainability)](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
- ![How it Works](https://raw.githubusercontent.com/hopsoft/stimulus_reflex/master/docs/diagram.png)
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