view_component_reflex 2.3.12 → 2.6.1
Sign up to get free protection for your applications and to get access to all the features.
- 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
|