view_component_reflex 0.2.5 → 0.6.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.
- checksums.yaml +4 -4
- data/README.md +64 -2
- data/app/components/view_component_reflex/component.rb +41 -30
- data/lib/view_component_reflex.rb +1 -0
- data/lib/view_component_reflex/engine.rb +7 -10
- data/lib/view_component_reflex/state_adapter/session.rb +38 -0
- data/lib/view_component_reflex/version.rb +1 -1
- metadata +3 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: f8bf2b7cc30e75bc01877f62705927b40e5f16fb7f763e065f33c857f8a6e9f2
|
4
|
+
data.tar.gz: 5af71af978af81acc1eef07609937e763ead6d9bbfd89dc613017342b1ffa17d
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 7362f2ea46bf50b44f14b186ec95375a42e2a3a582e97876ab4f468eb6a13ebf755d20472a684ff8023b6cee8063ba48409d497596b95c4c435fc584c57216d9
|
7
|
+
data.tar.gz: 99be6f450fefb1bd19c5421f883b689b677d8ea6317fc140e7b01ecef9d6fcd177675509c956434506abb81c65845dc66427f1d65a577b7f0bb56a80fa67095d
|
data/README.md
CHANGED
@@ -11,15 +11,19 @@ To add a reflex to your component, use the `reflex` method.
|
|
11
11
|
```ruby
|
12
12
|
reflex :my_cool_reflex do
|
13
13
|
# do stuff
|
14
|
+
refresh!
|
14
15
|
end
|
15
16
|
```
|
16
17
|
|
17
18
|
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
|
18
19
|
using stimulus reflex.
|
19
20
|
|
21
|
+
#####note: A reflex will not automatically re-render the component upon its completion. A component will re-render whenever the `set_state` or `refresh!` method is called.
|
22
|
+
|
20
23
|
In addition to calling reflexes, there is a rudimentary state system. You can initialize component-local state with `initialize_state(obj)`, where `obj` is a hash.
|
21
24
|
|
22
|
-
You can access state with the `state` helper. See the code below for an example.
|
25
|
+
You can access state with the `state` helper. See the code below for an example. Calling `set_state` will set the state,
|
26
|
+
and also re-render your component.
|
23
27
|
|
24
28
|
If you're using state add `data-key="<%= key %>"` to any html element using a reflex. This
|
25
29
|
lets ViewComponentReflex keep track of which state belongs to which component.
|
@@ -36,7 +40,7 @@ lets ViewComponentReflex keep track of which state belongs to which component.
|
|
36
40
|
end
|
37
41
|
|
38
42
|
reflex :increment do
|
39
|
-
|
43
|
+
set_state(count: state[:count] + 1)
|
40
44
|
end
|
41
45
|
end
|
42
46
|
```
|
@@ -49,6 +53,64 @@ lets ViewComponentReflex keep track of which state belongs to which component.
|
|
49
53
|
</div>
|
50
54
|
```
|
51
55
|
|
56
|
+
## Custom State Adapters
|
57
|
+
|
58
|
+
ViewComponentReflex uses session for its state by default. To change this, add
|
59
|
+
an initializer to `config/initializers/view_component_reflex.rb`.
|
60
|
+
|
61
|
+
```ruby
|
62
|
+
ViewComponentReflex.configure do |config|
|
63
|
+
config.state_adapter = YourAdapter
|
64
|
+
end
|
65
|
+
```
|
66
|
+
|
67
|
+
`YourAdapter` should implement
|
68
|
+
|
69
|
+
```ruby
|
70
|
+
class YourAdapter
|
71
|
+
##
|
72
|
+
# request - a rails request object
|
73
|
+
# key - a unique string that identifies the component instance
|
74
|
+
def self.state(request, key)
|
75
|
+
# Return state for a given key
|
76
|
+
end
|
77
|
+
|
78
|
+
##
|
79
|
+
# reflex - The reflex instance that's trying to set the state
|
80
|
+
# key - a unique string that identifies the component
|
81
|
+
# new_state - the new state to set
|
82
|
+
def self.set_state(reflex, key, new_state)
|
83
|
+
end
|
84
|
+
|
85
|
+
|
86
|
+
##
|
87
|
+
# request - a rails request object
|
88
|
+
# key - a unique string that identifies the component instance
|
89
|
+
# new_state - a hash containing the component state
|
90
|
+
def self.store_state(request, key, new_state = {})
|
91
|
+
# store the state
|
92
|
+
# this will be called twice, once with key, once with key_initial
|
93
|
+
# key_initial contains the initial, unmodified state.
|
94
|
+
# it should be used in reconcile_state to decide whether or not
|
95
|
+
# to re-initialize the state
|
96
|
+
end
|
97
|
+
|
98
|
+
##
|
99
|
+
# request - a rails request object
|
100
|
+
# key - a unique string that identifies the component instance
|
101
|
+
# new_state - a hash containing the component state
|
102
|
+
def self.reconcile_state(request, key, new_state)
|
103
|
+
# The passed state should always match the initial state of the component
|
104
|
+
# if it doesn't, we need to reset the state to the passed value.
|
105
|
+
#
|
106
|
+
# This handles cases where your initialize_state param computes some value that changes
|
107
|
+
# initialize_state({ transaction: @customer.transactions.first })
|
108
|
+
# if you delete the first transaction, that ^ is no longer valid. We need to update the state.
|
109
|
+
end
|
110
|
+
end
|
111
|
+
```
|
112
|
+
|
113
|
+
|
52
114
|
## Installation
|
53
115
|
Add this line to your application's Gemfile:
|
54
116
|
|
@@ -9,7 +9,37 @@ module ViewComponentReflex
|
|
9
9
|
klass = self
|
10
10
|
@stimulus_reflex ||= Object.const_set(name + "Reflex", Class.new(StimulusReflex::Reflex) {
|
11
11
|
def state
|
12
|
-
|
12
|
+
ViewComponentReflex::Engine.state_adapter.state(request, element.dataset[:key])
|
13
|
+
end
|
14
|
+
|
15
|
+
def refresh!(selectors = ["[data-controller=\"#{stimulus_controller}\"]"])
|
16
|
+
@channel.send :render_page_and_broadcast_morph, self, selectors , {
|
17
|
+
dataset: element.dataset.to_h,
|
18
|
+
args: [],
|
19
|
+
attrs: element.attributes.to_h,
|
20
|
+
selectors: ['body'],
|
21
|
+
target: "#{self.class.name}##{method_name}",
|
22
|
+
url: request.url,
|
23
|
+
permanentAttributeName: "data-reflex-permanent"
|
24
|
+
}
|
25
|
+
end
|
26
|
+
|
27
|
+
def refresh_all!
|
28
|
+
refresh!(['body'])
|
29
|
+
end
|
30
|
+
|
31
|
+
def set_state(new_state = {})
|
32
|
+
ViewComponentReflex::Engine.state_adapter.set_state(self, element.dataset[:key], new_state)
|
33
|
+
refresh!
|
34
|
+
end
|
35
|
+
|
36
|
+
before_reflex do |reflex, *args|
|
37
|
+
instance_exec(*args, &self.class.callbacks[self.method_name.to_sym]) if self.class.callbacks.include?(self.method_name.to_sym)
|
38
|
+
throw :abort
|
39
|
+
end
|
40
|
+
|
41
|
+
def self.callbacks
|
42
|
+
@callbacks ||= {}
|
13
43
|
end
|
14
44
|
|
15
45
|
define_method :stimulus_controller do
|
@@ -17,8 +47,8 @@ module ViewComponentReflex
|
|
17
47
|
end
|
18
48
|
|
19
49
|
define_singleton_method(:reflex) do |name, &blk|
|
50
|
+
callbacks[name] = blk
|
20
51
|
define_method(name) do |*args|
|
21
|
-
instance_exec(*args, &blk)
|
22
52
|
end
|
23
53
|
end
|
24
54
|
})
|
@@ -29,6 +59,10 @@ module ViewComponentReflex
|
|
29
59
|
@state = obj
|
30
60
|
end
|
31
61
|
|
62
|
+
def stimulus_reflex?
|
63
|
+
helpers.controller.instance_variable_get(:@stimulus_reflex)
|
64
|
+
end
|
65
|
+
|
32
66
|
# key is required if you're using state
|
33
67
|
# We can't initialize the session state in the initial method
|
34
68
|
# because it doesn't have a view_context yet
|
@@ -37,40 +71,17 @@ module ViewComponentReflex
|
|
37
71
|
@key ||= caller.find { |p| p.include? ".html.erb" }&.hash.to_s
|
38
72
|
|
39
73
|
# initialize session state
|
40
|
-
if session[@key].nil?
|
41
|
-
store_state(@key)
|
42
|
-
store_state("#{@key}_initial")
|
74
|
+
if !stimulus_reflex? || session[@key].nil?
|
75
|
+
ViewComponentReflex::Engine.state_adapter.store_state(request, @key, @state)
|
76
|
+
ViewComponentReflex::Engine.state_adapter.store_state(request, "#{@key}_initial", @state)
|
43
77
|
else
|
44
|
-
reconcile_state
|
78
|
+
# ViewComponentReflex::Engine.state_adapter.reconcile_state(request, @key, @state)
|
45
79
|
end
|
46
80
|
@key
|
47
81
|
end
|
48
82
|
|
49
83
|
def state
|
50
|
-
|
51
|
-
end
|
52
|
-
|
53
|
-
private
|
54
|
-
|
55
|
-
# The passed state should always match the initial state of the component
|
56
|
-
# if it doesn't, we need to reset the state to the passed value.
|
57
|
-
#
|
58
|
-
# This handles cases where your initialize_state param computes some value that changes
|
59
|
-
# initialize_state({ transaction: @customer.transactions.first })
|
60
|
-
# if you delete the first transaction, that ^ is no longer valid. We need to update the state.
|
61
|
-
def reconcile_state
|
62
|
-
session["#{@key}_initial"].each do |k, v|
|
63
|
-
if @state[k] != v
|
64
|
-
session[@key][k] = @state[k]
|
65
|
-
end
|
66
|
-
end
|
67
|
-
end
|
68
|
-
|
69
|
-
def store_state(a_key)
|
70
|
-
session[a_key] = {}
|
71
|
-
(@state ||= {}).each do |key, v|
|
72
|
-
session[a_key][key] = v
|
73
|
-
end
|
84
|
+
ViewComponentReflex::Engine.state_adapter.state(request, key)
|
74
85
|
end
|
75
86
|
end
|
76
87
|
end
|
@@ -1,16 +1,13 @@
|
|
1
1
|
module ViewComponentReflex
|
2
2
|
class Engine < ::Rails::Engine
|
3
|
-
|
4
|
-
|
5
|
-
def render_page_and_broadcast_morph(reflex, selectors, data = {})
|
6
|
-
html = render_page(reflex)
|
7
|
-
if reflex.respond_to? :stimulus_controller
|
8
|
-
selectors = ["[data-controller=\"#{reflex.stimulus_controller}\"]"]
|
9
|
-
end
|
10
|
-
broadcast_morphs selectors, data, html if html.present?
|
11
|
-
end
|
12
|
-
end
|
3
|
+
class << self
|
4
|
+
mattr_accessor :state_adapter
|
13
5
|
|
6
|
+
self.state_adapter = StateAdapter::Session
|
7
|
+
end
|
8
|
+
|
9
|
+
def self.configure
|
10
|
+
yield self if block_given?
|
14
11
|
end
|
15
12
|
end
|
16
13
|
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
module ViewComponentReflex
|
2
|
+
module StateAdapter
|
3
|
+
class Session
|
4
|
+
def self.state(request, key)
|
5
|
+
request.session[key] ||= {}
|
6
|
+
end
|
7
|
+
|
8
|
+
def self.set_state(reflex, key, new_state)
|
9
|
+
new_state.each do |k, v|
|
10
|
+
state(reflex.request, key)[k] = v
|
11
|
+
end
|
12
|
+
store = reflex.request.session.instance_variable_get("@by")
|
13
|
+
store.commit_session reflex.request, reflex.controller.response
|
14
|
+
end
|
15
|
+
|
16
|
+
def self.store_state(request, key, new_state = {})
|
17
|
+
request.session[key] = {}
|
18
|
+
new_state.each do |k, v|
|
19
|
+
request.session[key][k] = v
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
# The passed state should always match the initial state of the component
|
24
|
+
# if it doesn't, we need to reset the state to the passed value.
|
25
|
+
#
|
26
|
+
# This handles cases where your initialize_state param computes some value that changes
|
27
|
+
# initialize_state({ transaction: @customer.transactions.first })
|
28
|
+
# if you delete the first transaction, that ^ is no longer valid. We need to update the state.
|
29
|
+
def self.reconcile_state(request, key, new_state)
|
30
|
+
request.session["#{key}_initial"].each do |k, v|
|
31
|
+
if new_state[k] != v
|
32
|
+
request.session[key][k] = new_state[k]
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: view_component_reflex
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.6.0
|
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-06-
|
11
|
+
date: 2020-06-11 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rails
|
@@ -71,6 +71,7 @@ files:
|
|
71
71
|
- app/components/view_component_reflex/component.rb
|
72
72
|
- lib/view_component_reflex.rb
|
73
73
|
- lib/view_component_reflex/engine.rb
|
74
|
+
- lib/view_component_reflex/state_adapter/session.rb
|
74
75
|
- lib/view_component_reflex/version.rb
|
75
76
|
homepage: https://github.com/joshleblanc/view_component_reflex
|
76
77
|
licenses:
|