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