view_component_reflex 2.3.12 → 2.6.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/README.md +45 -5
- data/app/components/view_component_reflex/component.rb +20 -7
- data/lib/view_component_reflex.rb +1 -0
- data/lib/view_component_reflex/engine.rb +2 -4
- data/lib/view_component_reflex/reflex.rb +2 -2
- data/lib/view_component_reflex/state_adapter/memory.rb +4 -0
- data/lib/view_component_reflex/state_adapter/redis.rb +74 -0
- data/lib/view_component_reflex/state_adapter/session.rb +4 -0
- data/lib/view_component_reflex/version.rb +1 -1
- metadata +19 -6
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 707610d40c14c49d025779455b65bc00e5a62f6ac5efb45b67465ddaf877e037
|
4
|
+
data.tar.gz: d968005df1c10fc89d6a596acccaa114a1c7fda23d159f1e49f7a065591c55ce
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 6a852616da1e12e5cc0d54017b738ea97040092a1c9801a8355aca89681846dcc08b6e5e62ab6209d2c48b7328f46e4c42896a20f54cb465af896c05442dc94e
|
7
|
+
data.tar.gz: fd389d8e0706b8f7133d01953b31c06e8d08c88dfa3015bb5f443c57b6e34604bb81de16bea00e2861eb39aadd90d0c36a3a733e247af74b0fa9c7f33b9ee8b4
|
data/README.md
CHANGED
@@ -6,7 +6,7 @@ It builds upon [stimulus_reflex](https://github.com/hopsoft/stimulus_reflex) and
|
|
6
6
|
|
7
7
|
## Usage
|
8
8
|
|
9
|
-
You can add reflexes to your component by
|
9
|
+
You can add reflexes to your component by inheriting from `ViewComponentReflex::Component`.
|
10
10
|
|
11
11
|
This will act as if you created a reflex with the method `my_cool_stuff`. To call this reflex, add `data-reflex="click->MyComponentReflex#my_cool_reflex"`, just like you're
|
12
12
|
using stimulus reflex.
|
@@ -124,7 +124,7 @@ def initialize
|
|
124
124
|
end
|
125
125
|
|
126
126
|
def collection_key
|
127
|
-
|
127
|
+
@my_model.id
|
128
128
|
end
|
129
129
|
```
|
130
130
|
|
@@ -161,7 +161,7 @@ By default, VCR will re-render your component after it executes your method. `pr
|
|
161
161
|
```ruby
|
162
162
|
def my_method
|
163
163
|
prevent_refresh!
|
164
|
-
@foo =
|
164
|
+
@foo = :bar
|
165
165
|
end # the rendered page will not reflect this change
|
166
166
|
```
|
167
167
|
|
@@ -193,6 +193,19 @@ You *must* wrap your component in this for everything to work properly.
|
|
193
193
|
<% end %>
|
194
194
|
```
|
195
195
|
|
196
|
+
### after_state_initialized(parameters_changed)
|
197
|
+
|
198
|
+
This is called after the state has been inserted in the component. You can use this to run conditional functions after
|
199
|
+
some parameter has superseeded whatever's in state
|
200
|
+
|
201
|
+
```
|
202
|
+
def after_state_initialized(parameters_changed)
|
203
|
+
if parameters_changed.include?(:@filter)
|
204
|
+
calculate_visible_rows
|
205
|
+
end
|
206
|
+
end
|
207
|
+
```
|
208
|
+
|
196
209
|
## Custom reflex base class
|
197
210
|
Reflexes typically inherit from a base ApplicationReflex. You can define the base class for a view_component_reflex by using the `reflex_base_class` method.
|
198
211
|
The parent class must inherit ViewComponentReflex::Reflex, and will throw an error if it does not.
|
@@ -245,8 +258,8 @@ end
|
|
245
258
|
|
246
259
|
## State
|
247
260
|
|
248
|
-
By default, view_component_reflex stores component state in
|
249
|
-
to use the
|
261
|
+
By default (since version `2.3.2`), view_component_reflex stores component state in session. You can optionally set the state adapter
|
262
|
+
to use the memory by changing `config.state_adapter` to `ViewComponentReflex::StateAdapter::Memory`.
|
250
263
|
|
251
264
|
## Custom State Adapters
|
252
265
|
|
@@ -259,6 +272,22 @@ ViewComponentReflex::Engine.configure do |config|
|
|
259
272
|
end
|
260
273
|
```
|
261
274
|
|
275
|
+
|
276
|
+
## Existing Fast Redis based State Adapter
|
277
|
+
|
278
|
+
This adapter uses hmset and hgetall to reduce the number of operations.
|
279
|
+
This is the recommended adapter if you are using AnyCable.
|
280
|
+
|
281
|
+
```ruby
|
282
|
+
ViewComponentReflex::Engine.configure do |config|
|
283
|
+
config.state_adapter = ViewComponentReflex::StateAdapter::Redis.new(
|
284
|
+
redis_opts: {
|
285
|
+
url: "redis://localhost:6379/1", driver: :hiredis
|
286
|
+
},
|
287
|
+
ttl: 3600)
|
288
|
+
end
|
289
|
+
```
|
290
|
+
|
262
291
|
`YourAdapter` should implement
|
263
292
|
|
264
293
|
```ruby
|
@@ -319,6 +348,17 @@ $ gem install view_component_reflex
|
|
319
348
|
## Uninitialized constants \<component\>Reflex
|
320
349
|
A component needs to be wrapped in `<%= component_controller do %>` in order to properly initialize, otherwise the Reflex class won't get created.
|
321
350
|
|
351
|
+
## Session is an empty hash
|
352
|
+
StimulusReflex 3.3 introduced _selector morphs_, allowing you to render arbitrary strings via `ApplicationController.render`, for example:
|
353
|
+
|
354
|
+
```rb
|
355
|
+
def test_selector
|
356
|
+
morph '#some-container', ApplicationController.render(MyComponent.new(some: :param))
|
357
|
+
end
|
358
|
+
```
|
359
|
+
|
360
|
+
StimulusReflex 3.4 introduced a fix that merges the current `request.env` and provides the CSRF token to fetch the session.
|
361
|
+
|
322
362
|
## License
|
323
363
|
The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
|
324
364
|
|
@@ -139,33 +139,46 @@ module ViewComponentReflex
|
|
139
139
|
[]
|
140
140
|
end
|
141
141
|
|
142
|
+
def after_state_initialized(parameters_changed)
|
143
|
+
# called after state component has been hydrated
|
144
|
+
end
|
145
|
+
|
142
146
|
# def receive_params(old_state, params)
|
143
147
|
# # no op
|
144
148
|
# end
|
145
149
|
|
146
150
|
def key
|
151
|
+
adapter = ViewComponentReflex::Engine.state_adapter
|
152
|
+
|
147
153
|
# initialize session state
|
148
|
-
if !stimulus_reflex? ||
|
154
|
+
if !stimulus_reflex? || adapter.state(request, @key).empty?
|
149
155
|
|
150
156
|
new_state = create_safe_state
|
151
157
|
|
152
|
-
|
153
|
-
|
158
|
+
adapter.wrap_write_async do
|
159
|
+
adapter.store_state(request, @key, new_state)
|
160
|
+
adapter.store_state(request, "#{@key}_initial", new_state)
|
161
|
+
end
|
154
162
|
elsif !@initialized_state
|
155
|
-
initial_state =
|
163
|
+
initial_state = adapter.state(request, "#{@key}_initial")
|
156
164
|
|
157
165
|
# incoming_params = safe_instance_variables.each_with_object({}) { |var, obj| obj[var] = instance_variable_get(var) }
|
158
166
|
# receive_params(ViewComponentReflex::Engine.state_adapter.state(request, @key), incoming_params)
|
159
167
|
|
160
|
-
|
168
|
+
parameters_changed = []
|
169
|
+
adapter.state(request, @key).each do |k, v|
|
161
170
|
instance_value = instance_variable_get(k)
|
162
171
|
if permit_parameter?(initial_state[k], instance_value)
|
163
|
-
|
164
|
-
|
172
|
+
parameters_changed << k
|
173
|
+
adapter.wrap_write_async do
|
174
|
+
adapter.set_state(request, controller, "#{@key}_initial", {k => instance_value})
|
175
|
+
adapter.set_state(request, controller, @key, {k => instance_value})
|
176
|
+
end
|
165
177
|
else
|
166
178
|
instance_variable_set(k, v)
|
167
179
|
end
|
168
180
|
end
|
181
|
+
after_state_initialized(parameters_changed)
|
169
182
|
@initialized_state = true
|
170
183
|
end
|
171
184
|
@key
|
@@ -2,6 +2,7 @@ require "stimulus_reflex"
|
|
2
2
|
require 'view_component_reflex/reflex_factory'
|
3
3
|
require "view_component_reflex/state_adapter/session"
|
4
4
|
require "view_component_reflex/state_adapter/memory"
|
5
|
+
require "view_component_reflex/state_adapter/redis"
|
5
6
|
require "view_component_reflex/reflex"
|
6
7
|
require "view_component_reflex/engine"
|
7
8
|
|
@@ -1,10 +1,8 @@
|
|
1
1
|
module ViewComponentReflex
|
2
2
|
class Engine < ::Rails::Engine
|
3
|
-
class << self
|
4
|
-
mattr_accessor :state_adapter
|
5
3
|
|
6
|
-
|
7
|
-
|
4
|
+
mattr_accessor :state_adapter
|
5
|
+
Engine.state_adapter = StateAdapter::Session
|
8
6
|
|
9
7
|
config.to_prepare do
|
10
8
|
StimulusReflex::Channel.class_eval do
|
@@ -40,7 +40,7 @@ module ViewComponentReflex
|
|
40
40
|
element.dataset[:key]
|
41
41
|
end
|
42
42
|
end
|
43
|
-
document = Nokogiri::HTML(controller.
|
43
|
+
document = Nokogiri::HTML(component.render_in(controller.view_context))
|
44
44
|
cable_ready[channel.stream_name].morph(
|
45
45
|
selector: selector,
|
46
46
|
children_only: true,
|
@@ -119,7 +119,7 @@ module ViewComponentReflex
|
|
119
119
|
return @component if @component
|
120
120
|
@component = component_class.allocate
|
121
121
|
reflex = self
|
122
|
-
exposed_methods = [:params, :request, :element, :refresh!, :refresh_all!, :stimulus_controller, :session, :prevent_refresh!, :selector, :stimulate]
|
122
|
+
exposed_methods = [:params, :request, :connection, :element, :refresh!, :refresh_all!, :stimulus_controller, :session, :prevent_refresh!, :selector, :stimulate]
|
123
123
|
exposed_methods.each do |meth|
|
124
124
|
@component.define_singleton_method(meth) do |*a|
|
125
125
|
reflex.send(meth, *a)
|
@@ -0,0 +1,74 @@
|
|
1
|
+
# A redis based adapter for component_reflex
|
2
|
+
#
|
3
|
+
# ViewComponentReflex::Engine.configure do |config|
|
4
|
+
# config.state_adapter = ViewComponentReflex::StateAdapter::Redis.new(
|
5
|
+
# redis_opts: {
|
6
|
+
# url: "redis://localhost:6379/1", driver: :hiredis
|
7
|
+
# },
|
8
|
+
# ttl: 3600)
|
9
|
+
# end
|
10
|
+
|
11
|
+
module ViewComponentReflex
|
12
|
+
module StateAdapter
|
13
|
+
class Redis
|
14
|
+
attr_reader :client, :ttl
|
15
|
+
|
16
|
+
def initialize(redis_opts:, ttl: 3600)
|
17
|
+
@client = ::Redis.new(redis_opts)
|
18
|
+
@ttl = ttl
|
19
|
+
end
|
20
|
+
|
21
|
+
def state(request, key)
|
22
|
+
cache_key = get_key(request, key)
|
23
|
+
value = client.hgetall(cache_key)
|
24
|
+
|
25
|
+
return {} if value.nil?
|
26
|
+
|
27
|
+
value.map do |k, v|
|
28
|
+
[k, Marshal.load(v)]
|
29
|
+
end.to_h
|
30
|
+
end
|
31
|
+
|
32
|
+
def set_state(request, _, key, new_state)
|
33
|
+
cache_key = get_key(request, key)
|
34
|
+
save_to_redis(cache_key, new_state)
|
35
|
+
end
|
36
|
+
|
37
|
+
def store_state(request, key, new_state = {})
|
38
|
+
cache_key = get_key(request, key)
|
39
|
+
optimized_store_to_redis(cache_key, new_state, request)
|
40
|
+
end
|
41
|
+
|
42
|
+
def wrap_write_async
|
43
|
+
client.pipelined do
|
44
|
+
yield
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
private
|
49
|
+
|
50
|
+
# Reduce number of calls coming from #store_state to save Redis
|
51
|
+
# when it's first rendered
|
52
|
+
def optimized_store_to_redis(cache_key, new_state, request)
|
53
|
+
request.env["CACHE_REDIS_REFLEX"] ||= []
|
54
|
+
return if request.env["CACHE_REDIS_REFLEX"].include?(cache_key)
|
55
|
+
request.env["CACHE_REDIS_REFLEX"].push(cache_key)
|
56
|
+
|
57
|
+
save_to_redis(cache_key, new_state)
|
58
|
+
end
|
59
|
+
|
60
|
+
def save_to_redis(cache_key, new_state)
|
61
|
+
new_state_json = new_state.map do |k, v|
|
62
|
+
[k, Marshal.dump(v)]
|
63
|
+
end
|
64
|
+
|
65
|
+
client.hmset(cache_key, new_state_json.flatten)
|
66
|
+
client.expire(cache_key, ttl)
|
67
|
+
end
|
68
|
+
|
69
|
+
def get_key(request, key)
|
70
|
+
"#{request.session.id.to_s}_#{key}_session_reflex_redis"
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
metadata
CHANGED
@@ -1,43 +1,55 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: view_component_reflex
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 2.
|
4
|
+
version: 2.6.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Joshua LeBlanc
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2020-
|
11
|
+
date: 2020-11-06 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rails
|
15
15
|
requirement: !ruby/object:Gem::Requirement
|
16
16
|
requirements:
|
17
|
-
- - "
|
17
|
+
- - ">="
|
18
18
|
- !ruby/object:Gem::Version
|
19
19
|
version: '5.2'
|
20
|
+
- - "<"
|
21
|
+
- !ruby/object:Gem::Version
|
22
|
+
version: '7.0'
|
20
23
|
type: :runtime
|
21
24
|
prerelease: false
|
22
25
|
version_requirements: !ruby/object:Gem::Requirement
|
23
26
|
requirements:
|
24
|
-
- - "
|
27
|
+
- - ">="
|
25
28
|
- !ruby/object:Gem::Version
|
26
29
|
version: '5.2'
|
30
|
+
- - "<"
|
31
|
+
- !ruby/object:Gem::Version
|
32
|
+
version: '7.0'
|
27
33
|
- !ruby/object:Gem::Dependency
|
28
34
|
name: stimulus_reflex
|
29
35
|
requirement: !ruby/object:Gem::Requirement
|
30
36
|
requirements:
|
31
|
-
- - "
|
37
|
+
- - ">="
|
32
38
|
- !ruby/object:Gem::Version
|
33
39
|
version: 3.3.0
|
40
|
+
- - "<"
|
41
|
+
- !ruby/object:Gem::Version
|
42
|
+
version: '3.4'
|
34
43
|
type: :runtime
|
35
44
|
prerelease: false
|
36
45
|
version_requirements: !ruby/object:Gem::Requirement
|
37
46
|
requirements:
|
38
|
-
- - "
|
47
|
+
- - ">="
|
39
48
|
- !ruby/object:Gem::Version
|
40
49
|
version: 3.3.0
|
50
|
+
- - "<"
|
51
|
+
- !ruby/object:Gem::Version
|
52
|
+
version: '3.4'
|
41
53
|
- !ruby/object:Gem::Dependency
|
42
54
|
name: view_component
|
43
55
|
requirement: !ruby/object:Gem::Requirement
|
@@ -68,6 +80,7 @@ files:
|
|
68
80
|
- lib/view_component_reflex/reflex.rb
|
69
81
|
- lib/view_component_reflex/reflex_factory.rb
|
70
82
|
- lib/view_component_reflex/state_adapter/memory.rb
|
83
|
+
- lib/view_component_reflex/state_adapter/redis.rb
|
71
84
|
- lib/view_component_reflex/state_adapter/session.rb
|
72
85
|
- lib/view_component_reflex/version.rb
|
73
86
|
homepage: https://github.com/joshleblanc/view_component_reflex
|