stimulus_reflex 1.0.2 → 1.1.0

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 6798571d8f8229083855114f5f499414f308a47e03c438f1b74b5eb534acd78f
4
- data.tar.gz: 62e2d714b2ae14c16bf4435665d339717d9fa99330d3eb604fa6720c15f64f80
3
+ metadata.gz: 253dc2246ab12a13e75ea23f907375b011d3ef16fae1836638a81c00017f5eb1
4
+ data.tar.gz: f1a2a7e3555a9509f9a3a1fb1bdccb0ecd36fb806511302e7cc5f1fe18d7d2c3
5
5
  SHA512:
6
- metadata.gz: 2d22087e7519727c787770fb971aa510d2480a5b45e00efa78eb708adcc69d89cf085b3fad6544c9a46bb183240ea452ded9ce665495ca7f5d47bcc021c8a7e0
7
- data.tar.gz: 3b0aaf7d1c98ba7281f24f2d96fb1d043e297847837c2ad38028bac114c7ed7fe80c929e0c58491b646533f8d4958921e8e1483d5bc320c4d04d3034ec2e53aa
6
+ metadata.gz: 93a93f0ecf009492eeb422249814d1b9bfdd4de8592cac5e4d6feb029a670ea3dd6b15b156b11da564e4b4ec6453e97bdddccecdb8bd0df5c29c8d3b45efa264
7
+ data.tar.gz: be64d27ccfa1a7c8cec4781c199628af9e4ad345bbf0744fbc64f4bb9b6bb9de0de8fcdb413ad2fb58e666dcd7362198b45355d64e8f25dd87f882fd9f942fef
data/Gemfile CHANGED
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  source "https://rubygems.org"
2
4
 
3
5
  git_source(:github) { |repo_name| "https://github.com/#{repo_name}" }
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- stimulus_reflex (1.0.2)
4
+ stimulus_reflex (1.1.0)
5
5
  cable_ready (>= 4.0.3)
6
6
  nokogiri
7
7
  rack
@@ -90,11 +90,11 @@ GEM
90
90
  mini_mime (1.0.2)
91
91
  mini_portile2 (2.4.0)
92
92
  minitest (5.11.3)
93
- nio4r (2.4.0)
93
+ nio4r (2.5.1)
94
94
  nokogiri (1.10.4)
95
95
  mini_portile2 (~> 2.4.0)
96
96
  parallel (1.17.0)
97
- parser (2.6.3.0)
97
+ parser (2.6.4.0)
98
98
  ast (~> 2.4.0)
99
99
  pry (0.12.2)
100
100
  coderay (~> 1.1.0)
@@ -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.2)
152
+ standard (0.1.3)
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.9)
165
+ zeitwerk (2.1.10)
166
166
 
167
167
  PLATFORMS
168
168
  ruby
data/README.md CHANGED
@@ -1,8 +1,10 @@
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/)
1
+ [![Lines of Code](http://img.shields.io/badge/lines_of_code-203-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
3
 
4
4
  # StimulusReflex
5
5
 
6
+ _reflex_ - an action that is performed as a response to a stimulus
7
+
6
8
  ### 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
9
 
8
10
  This project supports building [reactive applications](https://en.wikipedia.org/wiki/Reactive_programming)
@@ -11,19 +13,51 @@ It's designed to work perfectly with [server rendered HTML](https://guides.rubyo
11
13
  [Russian doll caching](https://edgeguides.rubyonrails.org/caching_with_rails.html#russian-doll-caching),
12
14
  [Stimulus](https://stimulusjs.org), [Turbolinks](https://www.youtube.com/watch?v=SWEts0rlezA), etc...
13
15
 
14
-
15
16
  __No need for a complex front-end framework. No need to grow your team or duplicate your efforts.__
16
17
 
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
18
  _Inspired by [Phoenix LiveView](https://youtu.be/Z2DU0qLfPIY?t=670)._ 🙌
26
19
 
20
+ ## Table of Contents
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
+
27
61
  ## How it Works
28
62
 
29
63
  1. Render a standard Rails view template
@@ -100,12 +134,64 @@ The following happens after the `StimulusReflex::Reflex` method call finishes.
100
134
 
101
135
  ## Advanced Usage
102
136
 
137
+ ### Reflex Methods
138
+
139
+ #### The `options` Keyword Argument
140
+
141
+ Reflex methods support an _optional_ `options` keyword argument.
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
+
103
189
  ### ActionCable
104
190
 
105
191
  StimulusReflex will use the ActionCable defaults of `window.App` and `App.cable` if they exist.
106
192
  If these defaults do not exist, StimulusReflex will establish a new socket connection.
107
193
 
108
- ### Performance
194
+ #### Performance
109
195
 
110
196
  ActionCable emits verbose log messages. Disabling ActionCable logs may improve performance.
111
197
 
@@ -115,7 +201,7 @@ ActionCable emits verbose log messages. Disabling ActionCable logs may improve p
115
201
  ActionCable.server.config.logger = Logger.new(nil)
116
202
  ```
117
203
 
118
- ### ActionCable Rooms
204
+ #### ActionCable Rooms
119
205
 
120
206
  You may find the need to restrict notifications to a specific room.
121
207
  This can be accomplished by setting the `data-room` attribute on the StimulusController element.
@@ -151,6 +237,22 @@ Building apps with StimulusReflex should evoke your memories of the original [Ra
151
237
 
152
238
  ## Contributing
153
239
 
240
+ ### Coding Standards
241
+
154
242
  This project uses [Standard](https://github.com/testdouble/standard)
155
243
  and [Prettier](https://github.com/prettier/prettier) to minimize bike shedding related to code formatting.
156
244
  Please run `./bin/standardize` prior submitting pull requests.
245
+
246
+ ### Releasing
247
+
248
+ 1. Bump version number at `lib/stimulus_reflex/version.rb`
249
+ 1. Push changes to GitHub
250
+ 1. Run `rake release`
251
+ 1. Run `yarn publish`
252
+ 1. Assign same version number to the JavaScript package
253
+ 1. Push changes to GitHub
254
+ 1. Push tags to GitHub
255
+
256
+ ```
257
+ git push --tags
258
+ ```
data/Rakefile CHANGED
@@ -1,2 +1,4 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require "bundler/gem_tasks"
2
4
  task default: :spec
data/bin/standardize CHANGED
@@ -1,4 +1,4 @@
1
1
  #!/bin/bash
2
2
 
3
3
  bundle exec standardrb --fix
4
- prettier --write javascript/stimulus_reflex.js
4
+ cd ./javascript && yarn run prettier --write stimulus_reflex.js
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  class StimulusReflex::Channel < ActionCable::Channel::Base
2
4
  include CableReady::Broadcaster
3
5
 
@@ -20,28 +22,44 @@ class StimulusReflex::Channel < ActionCable::Channel::Base
20
22
  reflex_name, method_name = target.split("#")
21
23
  reflex_name = reflex_name.classify
22
24
  arguments = data["args"] || []
25
+ options = StimulusReflex::DomElement.new(data["attrs"])
23
26
 
24
27
  begin
25
28
  reflex = reflex_name.constantize.new(self, url: url)
26
- delegate_call_to_reflex reflex, method_name, arguments
29
+ delegate_call_to_reflex reflex, method_name, arguments, options
27
30
  rescue => invoke_error
28
- logger.error "StimulusReflex::Channel Failed to invoke #{target}! #{url} #{invoke_error}"
31
+ logger.error "\e[31mStimulusReflex::Channel Failed to invoke #{target}! #{url} #{invoke_error}\e[0m"
29
32
  end
30
33
 
31
34
  begin
32
35
  render_page_and_broadcast_morph url, reflex
33
36
  rescue => render_error
34
- logger.error "StimulusReflex::Channel Failed to rerender #{url} #{render_error}"
37
+ logger.error "\e[31mStimulusReflex::Channel Failed to rerender #{url} #{render_error}\e[0m"
35
38
  end
36
39
  end
37
40
 
38
41
  private
39
42
 
40
- def delegate_call_to_reflex(reflex, method_name, arguments = [])
41
- if reflex.method(method_name).arity != 0
42
- reflex.send method_name, *arguments
43
+ def delegate_call_to_reflex(reflex, method_name, arguments = [], options = {})
44
+ method = reflex.method(method_name)
45
+ required_params = method.parameters.select { |(kind, _)| kind == :req }
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
+
49
+ if arguments.size == 0 && required_params.size == 0
50
+ if accepts_options_kwarg
51
+ reflex.public_send method_name, {options: options}
52
+ else
53
+ reflex.public_send method_name
54
+ end
55
+ elsif arguments.size >= required_params.size && arguments.size <= required_params.size + optional_params.size
56
+ if accepts_options_kwarg
57
+ reflex.public_send method_name, *arguments, {options: options}
58
+ else
59
+ reflex.public_send method_name, *arguments
60
+ end
43
61
  else
44
- reflex.send method_name
62
+ raise ArgumentError.new("wrong number of arguments (given #{arguments.inspect}, expected #{required_params.inspect}, optional #{optional_params.inspect})")
45
63
  end
46
64
  end
47
65
 
@@ -0,0 +1,18 @@
1
+ # frozen_string_literal: true
2
+
3
+ class StimulusReflex::DomElement
4
+ attr_reader :attributes
5
+
6
+ delegate :[], to: :"@attributes"
7
+
8
+ def initialize(attrs = {})
9
+ @attributes = HashWithIndifferentAccess.new(attrs || {}).freeze
10
+ end
11
+
12
+ def dataset
13
+ @dataset ||= attributes.each_with_object(HashWithIndifferentAccess.new) { |(key, value), memo|
14
+ next unless key.start_with?("data-")
15
+ memo[key.delete_prefix("data-")] = value
16
+ }.freeze
17
+ end
18
+ end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  class StimulusReflex::Reflex
2
4
  attr_reader :channel, :url
3
5
 
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module StimulusReflex
2
- VERSION = "1.0.2"
4
+ VERSION = "1.1.0"
3
5
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require "uri"
2
4
  require "rack"
3
5
  require "rails/engine"
@@ -8,6 +10,7 @@ require "nokogiri"
8
10
  require "cable_ready"
9
11
  require "stimulus_reflex/version"
10
12
  require "stimulus_reflex/reflex"
13
+ require "stimulus_reflex/dom_element"
11
14
  require "stimulus_reflex/channel"
12
15
 
13
16
  module StimulusReflex
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: 1.0.2
4
+ version: 1.1.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-08-17 00:00:00.000000000 Z
12
+ date: 2019-09-09 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: rack
@@ -156,6 +156,7 @@ files:
156
156
  - bin/standardize
157
157
  - lib/stimulus_reflex.rb
158
158
  - lib/stimulus_reflex/channel.rb
159
+ - lib/stimulus_reflex/dom_element.rb
159
160
  - lib/stimulus_reflex/reflex.rb
160
161
  - lib/stimulus_reflex/version.rb
161
162
  homepage: https://github.com/hopsoft/stimulus_reflex