view_component_reflex 1.1.0 → 1.5.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
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: aed815b44b0d4708ba9bb85e9a08912f15f4d66442432df5d0933866f66e3aba
|
4
|
+
data.tar.gz: 96262b95434700007b142be8411d115dc50cdd0d5b94baf5200b582317ce858b
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 8cdd11ece267f4cc206af3e12d3e62b4881fdde56afb28a23b078437448842eb54e7aa1da43a68dd4f071544bb360e95c15db2b4ea4599da9ee7e57e7887a2d8
|
7
|
+
data.tar.gz: 64c8053422748319fd6460881170c3b5a9b84da0577180ebbf8a3dd3e62e5b1563c5d9e17c617de798141466f85894b42b281377aa553ff9a3840d9c522564d8
|
data/README.md
CHANGED
@@ -39,7 +39,7 @@ end
|
|
39
39
|
In order to reconcile state to components in collections, you can specify a `collection_key` method that returns some
|
40
40
|
value unique to that component.
|
41
41
|
|
42
|
-
```
|
42
|
+
```ruby
|
43
43
|
class TodoComponent < ViewComponentReflex::Component
|
44
44
|
def initialize(todo:)
|
45
45
|
@todo = todo
|
@@ -53,6 +53,134 @@ end
|
|
53
53
|
<%= render(TodoComponent.with_collection(Todo.all)) %>
|
54
54
|
```
|
55
55
|
|
56
|
+
## API
|
57
|
+
|
58
|
+
### permit_parameter?(initial_param, new_params)
|
59
|
+
If a new parameter is passed to the component during rendering, it is used instead of what's in state.
|
60
|
+
If you're storing instances in state, you can use this to properly compare them.
|
61
|
+
|
62
|
+
```ruby
|
63
|
+
def permit_parameter?(initial_param, new_param)
|
64
|
+
if new_param.instance_of? MyModel
|
65
|
+
new_param.id == @my_model.id
|
66
|
+
else
|
67
|
+
super
|
68
|
+
end
|
69
|
+
end
|
70
|
+
```
|
71
|
+
|
72
|
+
### omitted_from_state
|
73
|
+
Return an array of instance variables you want to omit from state. Useful if you have an object
|
74
|
+
that isn't serializable as an instance variable, like a form.
|
75
|
+
|
76
|
+
```ruby
|
77
|
+
def omitted_from_state
|
78
|
+
[:@form]
|
79
|
+
end
|
80
|
+
```
|
81
|
+
|
82
|
+
### reflex_tag(reflex, name, content_or_options_with_block = nil, options = nil, escape = true, &block)
|
83
|
+
This shares the same definition as `content_tag`, except it accepts a reflex as the first parameter.
|
84
|
+
|
85
|
+
```erb
|
86
|
+
<%= reflex_tag :increment, :button, "Click me!" %>
|
87
|
+
```
|
88
|
+
|
89
|
+
Would add a click handler to the `increment` method on your component.
|
90
|
+
|
91
|
+
To use a non-click event, specific that with `->` notiation
|
92
|
+
|
93
|
+
```erb
|
94
|
+
<%= reflex_tag "mouseenter->increment", :button, "Click me!" %>
|
95
|
+
```
|
96
|
+
|
97
|
+
### collection_key
|
98
|
+
If you're rendering a component as a collection with `MyComponent.with_collection(SomeCollection)`, you must define this method to return some unique value for the component.
|
99
|
+
This is used to reconcile state in the background.
|
100
|
+
|
101
|
+
```ruby
|
102
|
+
def initialize
|
103
|
+
@my_model = MyModel.new
|
104
|
+
end
|
105
|
+
|
106
|
+
def collection_key
|
107
|
+
@my_model.id
|
108
|
+
end
|
109
|
+
```
|
110
|
+
|
111
|
+
### prevent_refresh!
|
112
|
+
By default, VCR will re-render your component after it executes your method. `revent_refresh!` prevents this from happening.
|
113
|
+
|
114
|
+
```ruby
|
115
|
+
def my_method
|
116
|
+
prevent_refresh!
|
117
|
+
@foo = Lbar
|
118
|
+
end # the rendered page will not reflect this change
|
119
|
+
```
|
120
|
+
|
121
|
+
### refresh_all!
|
122
|
+
Refresh the entire body of the page
|
123
|
+
|
124
|
+
```ruby
|
125
|
+
def do_some_global_action
|
126
|
+
prevent_refresh!
|
127
|
+
session[:model] = MyModel.new
|
128
|
+
refresh_all!
|
129
|
+
end
|
130
|
+
```
|
131
|
+
|
132
|
+
### key
|
133
|
+
This is a key unique to a particular component. It's used to reconcile state between renders, and should be passed as a data attribute whenever a reflex is called
|
134
|
+
|
135
|
+
```erb
|
136
|
+
<button type="button" data-reflex="click->MyComponent#do_something" data-key="<%= key %>">Click me!</button>
|
137
|
+
```
|
138
|
+
|
139
|
+
### component_controller(options = {}, &blk)
|
140
|
+
This is a view helper to properly connect VCR to the component. It outputs `<div data-controller="my-controller" key=<%= key %></div>`
|
141
|
+
You *must* wrap your component in this for everything to work properly.
|
142
|
+
|
143
|
+
```erb
|
144
|
+
<%= component_controller do %>
|
145
|
+
<p><%= @count %></p
|
146
|
+
<% end %>
|
147
|
+
```
|
148
|
+
|
149
|
+
## Common patterns
|
150
|
+
A lot of the time, you only need to update specific components when changing instance variables. For example, changing `@loading` might only need
|
151
|
+
to display a spinner somewhere on the page. You can define setters to implicitly render the appropriate pieces of dom whenever that variable is set
|
152
|
+
|
153
|
+
```ruby
|
154
|
+
def initialize
|
155
|
+
@loading = false
|
156
|
+
end
|
157
|
+
|
158
|
+
def loading=(new_value)
|
159
|
+
@loading = new_value
|
160
|
+
refresh! '#loader'
|
161
|
+
end
|
162
|
+
|
163
|
+
def do_expensive_action
|
164
|
+
prevent_refresh!
|
165
|
+
|
166
|
+
self.loading = true
|
167
|
+
execute_it
|
168
|
+
self.loading = false
|
169
|
+
end
|
170
|
+
```
|
171
|
+
|
172
|
+
```erb
|
173
|
+
<%= component_controller do %>
|
174
|
+
<div id="loader">
|
175
|
+
<% if @loading %>
|
176
|
+
<p>Loading...</p>
|
177
|
+
<% end %>
|
178
|
+
</div>
|
179
|
+
|
180
|
+
<button type="button" data-reflex="click->MyComponent#do_expensive_action" data-key="<%= key %>">Click me!</button>
|
181
|
+
<% end
|
182
|
+
```
|
183
|
+
|
56
184
|
## Custom State Adapters
|
57
185
|
|
58
186
|
ViewComponentReflex uses session for its state by default. To change this, add
|
@@ -76,13 +204,13 @@ class YourAdapter
|
|
76
204
|
end
|
77
205
|
|
78
206
|
##
|
79
|
-
# set_state is used to modify the state.
|
80
|
-
# access to the request, as well as the controller and other useful objects.
|
207
|
+
# set_state is used to modify the state.
|
81
208
|
#
|
82
|
-
#
|
209
|
+
# request - a rails request object
|
210
|
+
# controller - the current controller
|
83
211
|
# key - a unique string that identifies the component
|
84
212
|
# new_state - the new state to set
|
85
|
-
def self.set_state(
|
213
|
+
def self.set_state(request, controller, key, new_state)
|
86
214
|
# update the state
|
87
215
|
end
|
88
216
|
|
@@ -43,7 +43,11 @@ module ViewComponentReflex
|
|
43
43
|
component.instance_variable_set(k, v)
|
44
44
|
end
|
45
45
|
name.to_proc.call(component, *args)
|
46
|
-
refresh!
|
46
|
+
refresh! unless @prevent_refresh
|
47
|
+
end
|
48
|
+
|
49
|
+
def prevent_refresh!
|
50
|
+
@prevent_refresh = true
|
47
51
|
end
|
48
52
|
|
49
53
|
define_method :component_class do
|
@@ -62,7 +66,7 @@ module ViewComponentReflex
|
|
62
66
|
return @component if @component
|
63
67
|
@component = component_class.allocate
|
64
68
|
reflex = self
|
65
|
-
exposed_methods = [:params, :request, :element, :refresh!, :refresh_all!, :stimulus_controller]
|
69
|
+
exposed_methods = [:params, :request, :element, :refresh!, :refresh_all!, :stimulus_controller, :session, :prevent_refresh!]
|
66
70
|
exposed_methods.each do |meth|
|
67
71
|
@component.define_singleton_method(meth) do |*a|
|
68
72
|
reflex.send(meth, *a)
|
@@ -72,7 +76,7 @@ module ViewComponentReflex
|
|
72
76
|
end
|
73
77
|
|
74
78
|
def set_state(new_state = {})
|
75
|
-
ViewComponentReflex::Engine.state_adapter.set_state(
|
79
|
+
ViewComponentReflex::Engine.state_adapter.set_state(request, controller, element.dataset[:key], new_state)
|
76
80
|
end
|
77
81
|
|
78
82
|
def state
|
@@ -100,6 +104,7 @@ module ViewComponentReflex
|
|
100
104
|
|
101
105
|
def component_controller(opts = {}, &blk)
|
102
106
|
self.class.init_stimulus_reflex
|
107
|
+
init_key
|
103
108
|
opts[:data] = {
|
104
109
|
controller: self.class.stimulus_controller,
|
105
110
|
key: key,
|
@@ -108,23 +113,49 @@ module ViewComponentReflex
|
|
108
113
|
content_tag :div, capture(&blk), opts
|
109
114
|
end
|
110
115
|
|
111
|
-
def collection_key
|
112
|
-
nil
|
113
|
-
end
|
114
|
-
|
115
116
|
# key is required if you're using state
|
116
117
|
# We can't initialize the session state in the initial method
|
117
118
|
# because it doesn't have a view_context yet
|
118
119
|
# This is the next best place to do it
|
119
|
-
def
|
120
|
+
def init_key
|
120
121
|
# we want the erb file that renders the component. `caller` gives the file name,
|
121
122
|
# and line number, which should be unique. We hash it to make it a nice number
|
122
123
|
key = caller.select { |p| p.include? ".html.erb" }[1]&.hash.to_s
|
123
124
|
key += collection_key.to_s if collection_key
|
124
|
-
|
125
|
-
|
125
|
+
@key = key
|
126
|
+
end
|
127
|
+
|
128
|
+
def reflex_tag(reflex, name, content_or_options_with_block = nil, options = nil, escape = true, &block)
|
129
|
+
action, method = reflex.split("->")
|
130
|
+
if method.nil?
|
131
|
+
method = action
|
132
|
+
action = "click"
|
126
133
|
end
|
134
|
+
data_attributes = {
|
135
|
+
reflex: "#{action}->#{self.class.name}##{method}",
|
136
|
+
key: key
|
137
|
+
}
|
138
|
+
if content_or_options_with_block.is_a?(Hash)
|
139
|
+
merge_data_attributes(content_or_options_with_block, data_attributes)
|
140
|
+
else
|
141
|
+
merge_data_attributes(options, data_attributes)
|
142
|
+
end
|
143
|
+
content_tag(name, content_or_options_with_block, options, escape, &block)
|
144
|
+
end
|
145
|
+
|
146
|
+
def collection_key
|
147
|
+
nil
|
148
|
+
end
|
149
|
+
|
150
|
+
def permit_parameter?(initial_param, new_param)
|
151
|
+
initial_param != new_param
|
152
|
+
end
|
153
|
+
|
154
|
+
def omitted_from_state
|
155
|
+
[]
|
156
|
+
end
|
127
157
|
|
158
|
+
def key
|
128
159
|
# initialize session state
|
129
160
|
if !stimulus_reflex? || session[@key].nil?
|
130
161
|
new_state = {}
|
@@ -133,18 +164,33 @@ module ViewComponentReflex
|
|
133
164
|
blacklist = [
|
134
165
|
:@view_context, :@lookup_context, :@view_renderer, :@view_flow,
|
135
166
|
:@virtual_path, :@variant, :@current_template, :@output_buffer, :@key,
|
136
|
-
:@helpers, :@controller, :@request
|
167
|
+
:@helpers, :@controller, :@request, :@content
|
137
168
|
]
|
138
169
|
instance_variables.reject { |k| blacklist.include?(k) }.each do |k|
|
139
|
-
new_state[k] = instance_variable_get(k)
|
170
|
+
new_state[k] = instance_variable_get(k) unless omitted_from_state.include?(k)
|
140
171
|
end
|
141
172
|
ViewComponentReflex::Engine.state_adapter.store_state(request, @key, new_state)
|
173
|
+
ViewComponentReflex::Engine.state_adapter.store_state(request, "#{@key}_initial", new_state)
|
142
174
|
else
|
175
|
+
initial_state = ViewComponentReflex::Engine.state_adapter.state(request, "#{@key}_initial")
|
143
176
|
ViewComponentReflex::Engine.state_adapter.state(request, @key).each do |k, v|
|
144
|
-
|
177
|
+
unless permit_parameter?(initial_state[k], instance_variable_get(k))
|
178
|
+
instance_variable_set(k, v)
|
179
|
+
end
|
145
180
|
end
|
146
181
|
end
|
147
182
|
@key
|
148
183
|
end
|
184
|
+
|
185
|
+
private
|
186
|
+
|
187
|
+
def merge_data_attributes(options, attributes)
|
188
|
+
data = options[:data]
|
189
|
+
if data.nil?
|
190
|
+
options[:data] = attributes
|
191
|
+
else
|
192
|
+
options[:data].merge! attributes
|
193
|
+
end
|
194
|
+
end
|
149
195
|
end
|
150
196
|
end
|
@@ -5,12 +5,12 @@ module ViewComponentReflex
|
|
5
5
|
request.session[key] ||= {}
|
6
6
|
end
|
7
7
|
|
8
|
-
def self.set_state(
|
8
|
+
def self.set_state(request, controller, key, new_state)
|
9
9
|
new_state.each do |k, v|
|
10
|
-
state(
|
10
|
+
state(request, key)[k] = v
|
11
11
|
end
|
12
|
-
store =
|
13
|
-
store.commit_session
|
12
|
+
store = request.session.instance_variable_get("@by")
|
13
|
+
store.commit_session request, controller.response
|
14
14
|
end
|
15
15
|
|
16
16
|
def self.store_state(request, key, new_state = {})
|
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: 1.
|
4
|
+
version: 1.5.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-22 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rails
|