view_component_reflex 1.6.1 → 1.7.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: 2269d8639ced76028ffe2a5970960d7381c862a75ce36481a84d8cf8886acd6c
4
- data.tar.gz: c368a487c3a2d97b4207ecb47558877596f9f9dccc7aa63f363598bc72a4eb82
3
+ metadata.gz: eb949fc8de60d18d54d72195cd04947ba24ef73db4091b2ddd0c2992287df7d0
4
+ data.tar.gz: c474384a6b9c073ba4efbf924067113971e67c4e8bdc0fbfa6462471ecf95b6e
5
5
  SHA512:
6
- metadata.gz: 4822e9370947d8881b8f33ca8a00df19a9458ca31f9da4bc84bcfb438e768f37226a2790f489efe94ec36418f77ed4d32a122a003653ec3120ddc49df5af2a1d
7
- data.tar.gz: 0f38085c800aa78f43e6df67cec9664115fed2cbce0cefc7fb89dbe2a0b3f624d7adfff1a71e542039af40a6b247744677e96a0115afe6f5f5ad65fb4c2a78df
6
+ metadata.gz: e0f4a365d380e55ba8deb5f567f35fa81b2aa8ab108cd2aca4f6e97041139e8e53f446f30db3ddcea0ee0fcb08e62b2f98feef14b83f5810c54ce705a6564716
7
+ data.tar.gz: df46044a3c7bddc9ab204ddb052fce04f0cecf190428043a3aa4ff2fb5a1b86d7d4e9524fa509ecbea51991641f8b4da9b8eaed2b1f4ede565c6cdb922a07724
data/README.md CHANGED
@@ -70,8 +70,8 @@ end
70
70
  ```
71
71
 
72
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.
73
+ Return an array of instance variables you want to omit from state. Only really useful if you're using the session state
74
+ adapter, and you have an instance variable that can't be serialized.
75
75
 
76
76
  ```ruby
77
77
  def omitted_from_state
@@ -181,6 +181,11 @@ end
181
181
  <% end
182
182
  ```
183
183
 
184
+ ## State
185
+
186
+ By default, view_component_reflex stores component state in memory. You can optionally set the state adapter
187
+ to use the session by changing `config.state_adapter` to `ViewComponentReflex::StateAdapter::Session`
188
+
184
189
  ## Custom State Adapters
185
190
 
186
191
  ViewComponentReflex uses session for its state by default. To change this, add
@@ -4,21 +4,53 @@ module ViewComponentReflex
4
4
  def init_stimulus_reflex
5
5
  klass = self
6
6
  @stimulus_reflex ||= Object.const_set(name + "Reflex", Class.new(StimulusReflex::Reflex) {
7
- def refresh!(primary_selector = "[data-controller~=\"#{stimulus_controller}\"][data-key=\"#{element.dataset[:key]}\"]", *selectors)
7
+ def refresh!(*selectors)
8
8
  save_state
9
- @channel.send :render_page_and_broadcast_morph, self, [primary_selector, *selectors], {
10
- "dataset" => element.dataset.to_h,
11
- "args" => [],
12
- "attrs" => element.attributes.to_h,
13
- "selectors" => ["body"],
14
- "target" => "#{self.class.name}##{method_name}",
15
- "url" => request.url,
16
- "permanent_attribute_name" => "data-reflex-permanent"
17
- }
9
+
10
+ # If the component has instance variables omitted from state,
11
+ # we can't render it to string from here because those instance
12
+ # variables will be missing. In that case, set the selectors to the
13
+ # default selector and manually morph the page
14
+ if selectors.empty? && !component.can_render_to_string?
15
+ selectors.push selector
16
+ end
17
+
18
+ # If we're just updating the component itself, we can
19
+ # directly render it instead of rendering the entire page again
20
+ if selectors.empty?
21
+ refresh_component!
22
+ else
23
+ @channel.send :render_page_and_broadcast_morph, self, selectors, {
24
+ "dataset" => element.dataset.to_h,
25
+ "args" => [],
26
+ "attrs" => element.attributes.to_h,
27
+ "selectors" => ["body"],
28
+ "target" => "#{self.class.name}##{method_name}",
29
+ "url" => request.url,
30
+ "permanent_attribute_name" => permanent_attribute_name
31
+ }
32
+ end
33
+ end
34
+
35
+ def refresh_component!
36
+ # The component can't figure out the key when we render from here
37
+ # Luckily we already know the key, so we can manually override it
38
+ component.tap do |k|
39
+ k.define_singleton_method(:key) do
40
+ element.dataset[:key]
41
+ end
42
+ end
43
+ html = controller.render_component_to_string(component)
44
+ document = Nokogiri::HTML(html)
45
+ morph selector, document.css("#{selector} > *").to_s
46
+ end
47
+
48
+ def selector
49
+ "[data-controller~=\"#{stimulus_controller}\"][data-key=\"#{element.dataset[:key]}\"]"
18
50
  end
19
51
 
20
52
  def refresh_all!
21
- refresh!("body")
53
+ morph :body, render_page(self)
22
54
  end
23
55
 
24
56
  # SR's delegate_call_to_reflex in channel.rb
@@ -32,11 +64,6 @@ module ViewComponentReflex
32
64
  !!name.to_proc
33
65
  end
34
66
 
35
- before_reflex do |a|
36
- a.send a.method_name
37
- throw :abort
38
- end
39
-
40
67
  def method_missing(name, *args)
41
68
  super unless respond_to_missing?(name)
42
69
  state.each do |k, v|
@@ -85,7 +112,7 @@ module ViewComponentReflex
85
112
 
86
113
  def save_state
87
114
  new_state = {}
88
- component.instance_variables.each do |k|
115
+ component.safe_instance_variables.each do |k|
89
116
  new_state[k] = component.instance_variable_get(k)
90
117
  end
91
118
  set_state(new_state)
@@ -102,6 +129,10 @@ module ViewComponentReflex
102
129
  helpers.controller.instance_variable_get(:@stimulus_reflex)
103
130
  end
104
131
 
132
+ def can_render_to_string?
133
+ omitted_from_state.empty?
134
+ end
135
+
105
136
  def component_controller(opts_or_tag = :div, opts = {}, &blk)
106
137
  self.class.init_stimulus_reflex
107
138
  init_key
@@ -165,18 +196,10 @@ module ViewComponentReflex
165
196
 
166
197
  def key
167
198
  # initialize session state
168
- if !stimulus_reflex? || session[@key].nil?
169
- new_state = {}
170
-
171
- # this will almost certainly break
172
- blacklist = [
173
- :@view_context, :@lookup_context, :@view_renderer, :@view_flow,
174
- :@virtual_path, :@variant, :@current_template, :@output_buffer, :@key,
175
- :@helpers, :@controller, :@request, :@content
176
- ]
177
- instance_variables.reject { |k| blacklist.include?(k) }.each do |k|
178
- new_state[k] = instance_variable_get(k) unless omitted_from_state.include?(k)
179
- end
199
+ if !stimulus_reflex? || ViewComponentReflex::Engine.state_adapter.state(request, @key).empty?
200
+
201
+ new_state = create_safe_state
202
+
180
203
  ViewComponentReflex::Engine.state_adapter.store_state(request, @key, new_state)
181
204
  ViewComponentReflex::Engine.state_adapter.store_state(request, "#{@key}_initial", new_state)
182
205
  else
@@ -190,8 +213,30 @@ module ViewComponentReflex
190
213
  @key
191
214
  end
192
215
 
216
+ def safe_instance_variables
217
+ instance_variables - unsafe_instance_variables
218
+ end
219
+
193
220
  private
194
221
 
222
+ def unsafe_instance_variables
223
+ [
224
+ :@view_context, :@lookup_context, :@view_renderer, :@view_flow,
225
+ :@virtual_path, :@variant, :@current_template, :@output_buffer, :@key,
226
+ :@helpers, :@controller, :@request, :@content, :@tag_builder
227
+ ]
228
+ end
229
+
230
+ def create_safe_state
231
+ new_state = {}
232
+
233
+ # this will almost certainly break
234
+ safe_instance_variables.each do |k|
235
+ new_state[k] = instance_variable_get(k) unless omitted_from_state.include?(k)
236
+ end
237
+ new_state
238
+ end
239
+
195
240
  def merge_data_attributes(options, attributes)
196
241
  data = options[:data]
197
242
  if data.nil?
@@ -1,7 +1,8 @@
1
- require "view_component_reflex/state_adapter/session"
2
- require "view_component_reflex/engine"
3
- require 'stimulus_reflex'
4
-
5
- module ViewComponentReflex
6
- # Your code goes here...
7
- end
1
+ require "view_component_reflex/state_adapter/session"
2
+ require "view_component_reflex/state_adapter/memory"
3
+ require "view_component_reflex/engine"
4
+ require 'stimulus_reflex'
5
+
6
+ module ViewComponentReflex
7
+ # Your code goes here...
8
+ end
@@ -1,13 +1,13 @@
1
- module ViewComponentReflex
2
- class Engine < ::Rails::Engine
3
- class << self
4
- mattr_accessor :state_adapter
5
-
6
- self.state_adapter = StateAdapter::Session
7
- end
8
-
9
- def self.configure
10
- yield self if block_given?
11
- end
12
- end
13
- end
1
+ module ViewComponentReflex
2
+ class Engine < ::Rails::Engine
3
+ class << self
4
+ mattr_accessor :state_adapter
5
+
6
+ self.state_adapter = StateAdapter::Memory
7
+ end
8
+
9
+ def self.configure
10
+ yield self if block_given?
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,25 @@
1
+ VIEW_COMPONENT_REFLEX_MEMORY_STATE = {}
2
+ module ViewComponentReflex
3
+ module StateAdapter
4
+ class Memory
5
+ def self.state(request, key)
6
+ VIEW_COMPONENT_REFLEX_MEMORY_STATE[request.session.id.to_s] ||= {}
7
+ VIEW_COMPONENT_REFLEX_MEMORY_STATE[request.session.id.to_s][key] ||= {}
8
+ end
9
+
10
+ def self.set_state(request, _, key, new_state)
11
+ new_state.each do |k, v|
12
+ state(request, key)[k] = v
13
+ end
14
+ end
15
+
16
+ def self.store_state(request, key, new_state = {})
17
+ VIEW_COMPONENT_REFLEX_MEMORY_STATE[request.session.id.to_s] ||= {}
18
+ VIEW_COMPONENT_REFLEX_MEMORY_STATE[request.session.id.to_s][key] ||= {}
19
+ new_state.each do |k, v|
20
+ VIEW_COMPONENT_REFLEX_MEMORY_STATE[request.session.id.to_s][key][k] = v
21
+ end
22
+ end
23
+ end
24
+ end
25
+ end
@@ -1,3 +1,3 @@
1
1
  module ViewComponentReflex
2
- VERSION = '1.6.1'
2
+ VERSION = '1.7.0'
3
3
  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: 1.6.1
4
+ version: 1.7.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-26 00:00:00.000000000 Z
11
+ date: 2020-07-05 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/memory.rb
74
75
  - lib/view_component_reflex/state_adapter/session.rb
75
76
  - lib/view_component_reflex/version.rb
76
77
  homepage: https://github.com/joshleblanc/view_component_reflex