stimulus_reflex 0.1.5 → 0.1.6
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.
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 +60 -5
- data/lib/stimulus_reflex/channel.rb +53 -49
- data/lib/stimulus_reflex/version.rb +1 -1
- data/vendor/assets/javascripts/stimulus_reflex.js +1 -1
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: aa1b3385c6fc339dd85cff34e9fbddc6321dfb44dc2921db48e6d54ec41e12ab
|
4
|
+
data.tar.gz: 51fae31f79cc65a44d052980d051d50470b39c9805dc143cc9c62cb60dec521b
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 5a756d9b78d8e4be44720a81c3506716d8a42d532a001fc84d266449a15f26833c489a4f99b0f2960a978e2ac976fe27fd970f3bdd5061db72af71176e4ff6f0
|
7
|
+
data.tar.gz: 06cf961977708a6375eb85a7532ee4e406eca085a925c7060d5d190b1fd4622cba29214e068e60ab40fbdd34c1b95d71dd1944bfd4af559f31943bcf5f326316
|
data/Gemfile.lock
CHANGED
data/README.md
CHANGED
@@ -1,22 +1,24 @@
|
|
1
|
-
[](http://blog.codinghorror.com/the-best-code-is-no-code-at-all/)
|
2
|
+
[](https://codeclimate.com/github/hopsoft/stimulus_reflex)
|
2
3
|
|
3
4
|
# StimulusReflex
|
4
5
|
|
5
6
|
#### Server side reactive behavior for Stimulus controllers
|
6
7
|
|
7
|
-
|
8
|
+
Add the benefits of single page apps (SPA) to server rendered Rails/Stimulus projects with a minimal investment of time, resources, and complexity.
|
9
|
+
_The goal is to provide 80% of the benefits of SPAs with 20% of the typical effort._
|
8
10
|
|
9
|
-
|
10
|
-
- [ ] Support send without render
|
11
|
+
> This library provides functionality similar to [Phoenix LiveView](https://youtu.be/Z2DU0qLfPIY?t=670) for Rails applications.
|
11
12
|
|
12
13
|
## Usage
|
13
14
|
|
14
15
|
```ruby
|
15
16
|
# Gemfile
|
17
|
+
gem "cable_ready"
|
16
18
|
gem "stimulus_reflex"
|
17
19
|
```
|
18
20
|
|
19
|
-
```
|
21
|
+
```javascript
|
20
22
|
// app/assets/javascripts/cable.js
|
21
23
|
//= require cable_ready
|
22
24
|
//= require stimulus_reflex
|
@@ -37,6 +39,59 @@ export default class extends Controller {
|
|
37
39
|
}
|
38
40
|
```
|
39
41
|
|
42
|
+
```ruby
|
43
|
+
# app/stimulus_controllers/example_stimulus_controller.rb
|
44
|
+
class ExampleStimulusController < StimulusReflex::Controller
|
45
|
+
def do_stuff(arg1, arg2, ...)
|
46
|
+
# hard work...
|
47
|
+
# 1. the page that triggered this call will rererender
|
48
|
+
# 2. the HTML will be sent over the ActionCable socket
|
49
|
+
# 3. client side JavaScript will DOM diff and mutate only the changed nodes
|
50
|
+
end
|
51
|
+
end
|
52
|
+
```
|
53
|
+
|
54
|
+
## Advanced Usage
|
55
|
+
|
56
|
+
### Page Rerender
|
57
|
+
|
58
|
+
The page is always rerendered after triggering a `StimulusReflex`.
|
59
|
+
The client side JavaScript debounces this render via `setTimeout` to prevent a jarring user experience.
|
60
|
+
The default delay of `400ms` can be overriddend with the following JavaScript.
|
61
|
+
|
62
|
+
```javascript
|
63
|
+
StimulusReflex.renderDelay = 200;
|
64
|
+
```
|
65
|
+
|
66
|
+
## Instrumentation
|
67
|
+
|
68
|
+
SEE: https://guides.rubyonrails.org/active_support_instrumentation.html
|
69
|
+
|
70
|
+
```ruby
|
71
|
+
# wraps the stimulus controller method invocation
|
72
|
+
ActiveSupport::Notifications.subscribe "delegate_call.stimulus_reflex" do |*args|
|
73
|
+
event = ActiveSupport::Notifications::Event.new(*args)
|
74
|
+
Rails.logger.debug "#{event.name} #{event.duration} #{event.payload.inspect}"
|
75
|
+
end
|
76
|
+
|
77
|
+
# instruments the page rerender
|
78
|
+
ActiveSupport::Notifications.subscribe "render_page.stimulus_reflex" do |*args|
|
79
|
+
event = ActiveSupport::Notifications::Event.new(*args)
|
80
|
+
Rails.logger.debug "#{event.name} #{event.duration} #{event.payload.inspect}"
|
81
|
+
end
|
82
|
+
|
83
|
+
# wraps the web socket broadcast
|
84
|
+
ActiveSupport::Notifications.subscribe "broadcast.stimulus_reflex" do |*args|
|
85
|
+
event = ActiveSupport::Notifications::Event.new(*args)
|
86
|
+
Rails.logger.debug "#{event.name} #{event.duration} #{event.payload.inspect}"
|
87
|
+
end
|
88
|
+
|
89
|
+
# wraps the entire receive operation which includes everything above
|
90
|
+
ActiveSupport::Notifications.subscribe "receive.stimulus_reflex" do |*args|
|
91
|
+
event = ActiveSupport::Notifications::Event.new(*args)
|
92
|
+
Rails.logger.debug "#{event.name} #{event.duration} #{event.payload.inspect}"
|
93
|
+
end
|
94
|
+
```
|
40
95
|
|
41
96
|
## JavaScript Development
|
42
97
|
|
@@ -5,7 +5,6 @@ require "active_support/all"
|
|
5
5
|
require "action_dispatch"
|
6
6
|
require "cable_ready"
|
7
7
|
|
8
|
-
#class StimulusReflex::Channel < ApplicationCable::Channel
|
9
8
|
class StimulusReflex::Channel < ActionCable::Channel::Base
|
10
9
|
include CableReady::Broadcaster
|
11
10
|
|
@@ -21,70 +20,75 @@ class StimulusReflex::Channel < ActionCable::Channel::Base
|
|
21
20
|
end
|
22
21
|
|
23
22
|
def receive(data)
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
arguments = data["args"]
|
32
|
-
|
33
|
-
begin
|
34
|
-
ActiveSupport::Notifications.instrument "delegate_call.stimulus_reflex", url: url, target: target, arguments: arguments do
|
35
|
-
stimulus_controller = stimulus_controller_name.constantize.new(self)
|
36
|
-
if arguments.present?
|
37
|
-
stimulus_controller.send stimulus_method_name, *arguments
|
38
|
-
else
|
39
|
-
stimulus_controller.send stimulus_method_name
|
40
|
-
end
|
41
|
-
end
|
23
|
+
ActiveSupport::Notifications.instrument "receive.stimulus_reflex", data do
|
24
|
+
start = Time.current
|
25
|
+
url = data["url"].to_s
|
26
|
+
target = data["target"].to_s
|
27
|
+
stimulus_controller_name, method_name = target.split("#")
|
28
|
+
stimulus_controller_name = "#{stimulus_controller_name.classify}StimulusController"
|
29
|
+
arguments = data["args"] || []
|
42
30
|
|
43
31
|
begin
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
32
|
+
stimulus_controller = stimulus_controller_name.constantize.new(self)
|
33
|
+
delegate_call_to_stimulus_controller stimulus_controller, method_name, arguments
|
34
|
+
render_page_and_broadcast_morph url
|
35
|
+
rescue StandardError => invoke_error
|
36
|
+
logger.error "StimulusReflex::Channel Failed to invoke #{target}! #{url} #{invoke_error}"
|
48
37
|
end
|
49
|
-
rescue StandardError => invoke_error
|
50
|
-
logger.error "StimulusReflex::Channel: #{url} Failed to invoke #{target}! #{invoke_error}"
|
51
38
|
end
|
52
39
|
end
|
53
40
|
|
54
41
|
private
|
55
42
|
|
56
|
-
def
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
43
|
+
def delegate_call_to_stimulus_controller(stimulus_controller, method_name, arguments = [])
|
44
|
+
instrument_payload = { stimulus_controller: stimulus_controller.class.name, method_name: method_name, arguments: arguments.inspect }
|
45
|
+
ActiveSupport::Notifications.instrument "delegate_call.stimulus_reflex", instrument_payload do
|
46
|
+
if stimulus_controller.method(method_name).arity > 0
|
47
|
+
stimulus_controller.send method_name, *arguments
|
48
|
+
else
|
49
|
+
stimulus_controller.send method_name
|
50
|
+
end
|
63
51
|
end
|
52
|
+
end
|
64
53
|
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
Rack::PATH_INFO => ""
|
70
|
-
}
|
71
|
-
request = ActionDispatch::Request.new(connection.env.merge(env))
|
72
|
-
controller.request = request
|
73
|
-
controller.response = ActionDispatch::Response.new
|
54
|
+
def render_page_and_broadcast_morph(url)
|
55
|
+
html = render_page(url)
|
56
|
+
broadcast_morph url, html if html.present?
|
57
|
+
end
|
74
58
|
|
75
|
-
|
76
|
-
|
59
|
+
def render_page(url)
|
60
|
+
html = nil
|
61
|
+
ActiveSupport::Notifications.instrument "render_page.stimulus_reflex", url: url do
|
62
|
+
uri = URI.parse(url)
|
63
|
+
url_params = Rails.application.routes.recognize_path(url)
|
64
|
+
controller_class = "#{url_params[:controller]}_controller".classify.constantize
|
65
|
+
controller = controller_class.new
|
66
|
+
controller.instance_variable_set :"@stimulus_reflex", true
|
67
|
+
|
68
|
+
env = {
|
69
|
+
Rack::SCRIPT_NAME => uri.path,
|
70
|
+
Rack::QUERY_STRING => uri.query,
|
71
|
+
Rack::PATH_INFO => "",
|
72
|
+
}
|
73
|
+
request = ActionDispatch::Request.new(connection.env.merge(env))
|
74
|
+
controller.request = request
|
75
|
+
controller.response = ActionDispatch::Response.new
|
76
|
+
controller.process url_params[:action]
|
77
|
+
html = controller.response.body
|
78
|
+
end
|
79
|
+
html
|
80
|
+
end
|
81
|
+
|
82
|
+
def broadcast_morph(url, html)
|
83
|
+
ActiveSupport::Notifications.instrument "broadcast.stimulus_reflex", url: url, cable_ready: :morph do
|
84
|
+
html = extract_body_html(html)
|
85
|
+
cable_ready[stream_name].morph selector: "body", html: html, children_only: true
|
86
|
+
cable_ready.broadcast
|
77
87
|
end
|
78
|
-
controller.response.body
|
79
88
|
end
|
80
89
|
|
81
90
|
def extract_body_html(html)
|
82
91
|
doc = Nokogiri::HTML(html)
|
83
92
|
doc.css("body").to_s
|
84
93
|
end
|
85
|
-
|
86
|
-
def broadcast_morph(html)
|
87
|
-
cable_ready[stream_name].morph selector: "body", html: html, children_only: true
|
88
|
-
cable_ready.broadcast
|
89
|
-
end
|
90
94
|
end
|
@@ -1 +1 @@
|
|
1
|
-
window.StimulusReflex=function(
|
1
|
+
window.StimulusReflex=function(e){var t={};function r(n){if(t[n])return t[n].exports;var u=t[n]={i:n,l:!1,exports:{}};return e[n].call(u.exports,u,u.exports,r),u.l=!0,u.exports}return r.m=e,r.c=t,r.d=function(e,t,n){r.o(e,t)||Object.defineProperty(e,t,{enumerable:!0,get:n})},r.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},r.t=function(e,t){if(1&t&&(e=r(e)),8&t)return e;if(4&t&&"object"==typeof e&&e&&e.__esModule)return e;var n=Object.create(null);if(r.r(n),Object.defineProperty(n,"default",{enumerable:!0,value:e}),2&t&&"string"!=typeof e)for(var u in e)r.d(n,u,function(t){return e[t]}.bind(null,u));return n},r.n=function(e){var t=e&&e.__esModule?function(){return e.default}:function(){return e};return r.d(t,"a",t),t},r.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},r.p="",r(r.s=0)}([function(e,t,r){"use strict";var n;r.r(t),r.d(t,"register",function(){return o});window.App=window.App||{},App.cable=App.cable||ActionCable.createConsumer(),App.stimulusReflex=App.stimulusReflex||App.cable.subscriptions.create("StimulusReflex::Channel",{received:function(e){e.cableReady&&(clearTimeout(n),n=setTimeout(function(){CableReady.perform(e.operations),document.dispatchEvent(new Event("turbolinks:load"))},StimulusReflex.renderDelay||400))}});var u={send:function(){clearTimeout(n);var e=Array.prototype.slice.call(arguments),t=e.shift();App.stimulusReflex.send({url:location.href,target:t,args:e})}},o=function(e){return Object.assign(e,u),e}}]);
|
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: 0.1.
|
4
|
+
version: 0.1.6
|
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: 2018-10-
|
12
|
+
date: 2018-10-21 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: rack
|