view_component_reflex 1.7.1 → 2.1.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: c34425ccfef14458d218da7f297859dee0d17e5b922c9cefc82059e13fa5af99
4
- data.tar.gz: 6f201ab4b1a8a84e08a840a3de07e117d4c4dc1095dbe8decaf2c6b70bed971f
3
+ metadata.gz: 415195dd61211d392433d4e85963a6807871143f255b3264e2f7b4871bb42c42
4
+ data.tar.gz: 69f6030f02557d4d9886e3cfc00c738c66d748eb7a1a4b8149136ddaef8f8e9d
5
5
  SHA512:
6
- metadata.gz: d4ee24cfffed38e1e6f72c33c6aaffb3d8573328759da2be6175d85bb1d2fa1973c5314aa79ec5ed94be73655f4c42929674cff964bcc7ee0bb600db331d08bf
7
- data.tar.gz: f89ab463f3aaf436da93442ac9c212b3552367ef6a10815180142a83022944a0188c2993cfe678bd3d579b8a5d334d3bb302a44bf29e480399daa6393b0159c3
6
+ metadata.gz: e12ff695c21dc3f6c140c9d7aa7ccc854eeaeacf2a8071dc519f87c4ca1447464c01f8dfc9c200c0ac720dd864a71788fcc80f74072577c4c833743ccea29c79
7
+ data.tar.gz: ed7a85c244de733490571e7c4b529ce7b8dd295c0f6c1a5d57b15f40460b4a3737f2669bf8f28845d80a06b2ed721de49bc810c7ac9477629ead664635b2f2ee
data/README.md CHANGED
@@ -108,6 +108,33 @@ def collection_key
108
108
  end
109
109
  ```
110
110
 
111
+ ### stimulate(target, data)
112
+ Stimulate another reflex from within your component. This typically requires the key of the component you're stimulating
113
+ which can be passed in via parameters.
114
+
115
+ ```ruby
116
+ def initialize(parent_key)
117
+ @parent_key = parent_key
118
+ end
119
+
120
+ def stimulate_other
121
+ stimulate("OtherComponent#method", { key: @parent_key })
122
+ end
123
+ ```
124
+
125
+ ### refresh!(selectors)
126
+ Refresh a specific element on the page. Using this will implicitly run `prevent_render!`.
127
+ If you want to render a specific element, as well as the component, a common pattern would be to pass `selector` as one of the parameters
128
+
129
+ ```
130
+ def my_method
131
+ refresh! '#my-special-element', selector
132
+ end
133
+ ```
134
+
135
+ ### selector
136
+ Returns the unique selector for this component. Useful to pass to `refresh!` when refreshing custom elements.
137
+
111
138
  ### prevent_refresh!
112
139
  By default, VCR will re-render your component after it executes your method. `revent_refresh!` prevents this from happening.
113
140
 
@@ -2,122 +2,8 @@ module ViewComponentReflex
2
2
  class Component < ViewComponent::Base
3
3
  class << self
4
4
  def init_stimulus_reflex
5
- klass = self
6
- @stimulus_reflex ||= Object.const_set(name + "Reflex", Class.new(StimulusReflex::Reflex) {
7
- def refresh!(*selectors)
8
- save_state
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]}\"]"
50
- end
51
-
52
- def refresh_all!
53
- morph :body, render_page(self)
54
- end
55
-
56
- # SR's delegate_call_to_reflex in channel.rb
57
- # uses method to gather the method parameters, but since we're abusing
58
- # method_missing here, that'll always fail
59
- def method(name)
60
- name.to_sym.to_proc
61
- end
62
-
63
- def respond_to_missing?(name, _ = false)
64
- !!name.to_proc
65
- end
66
-
67
- def method_missing(name, *args)
68
- super unless respond_to_missing?(name)
69
- state.each do |k, v|
70
- component.instance_variable_set(k, v)
71
- end
72
- name.to_proc.call(component, *args)
73
- refresh! unless @prevent_refresh
74
- end
75
-
76
- def prevent_refresh!
77
- @prevent_refresh = true
78
- end
79
-
80
- define_method :component_class do
81
- @component_class ||= klass
82
- end
83
-
84
- private :component_class
85
-
86
- private
87
-
88
- def stimulus_controller
89
- component_class.stimulus_controller
90
- end
91
-
92
- def component
93
- return @component if @component
94
- @component = component_class.allocate
95
- reflex = self
96
- exposed_methods = [:params, :request, :element, :refresh!, :refresh_all!, :stimulus_controller, :session, :prevent_refresh!]
97
- exposed_methods.each do |meth|
98
- @component.define_singleton_method(meth) do |*a|
99
- reflex.send(meth, *a)
100
- end
101
- end
102
- @component
103
- end
104
-
105
- def set_state(new_state = {})
106
- ViewComponentReflex::Engine.state_adapter.set_state(request, controller, element.dataset[:key], new_state)
107
- end
108
-
109
- def state
110
- ViewComponentReflex::Engine.state_adapter.state(request, element.dataset[:key])
111
- end
112
-
113
- def save_state
114
- new_state = {}
115
- component.safe_instance_variables.each do |k|
116
- new_state[k] = component.instance_variable_get(k)
117
- end
118
- set_state(new_state)
119
- end
120
- })
5
+ @stimulus_reflex ||= Object.const_set(name + "Reflex", Class.new(Reflex))
6
+ @stimulus_reflex.component_class = self
121
7
  end
122
8
  end
123
9
 
@@ -129,10 +15,6 @@ module ViewComponentReflex
129
15
  helpers.controller.instance_variable_get(:@stimulus_reflex)
130
16
  end
131
17
 
132
- def can_render_to_string?
133
- omitted_from_state.empty?
134
- end
135
-
136
18
  def component_controller(opts_or_tag = :div, opts = {}, &blk)
137
19
  self.class.init_stimulus_reflex
138
20
  init_key
@@ -152,6 +34,10 @@ module ViewComponentReflex
152
34
  content_tag tag, capture(&blk), options
153
35
  end
154
36
 
37
+ def can_render_to_string?
38
+ omitted_from_state.empty?
39
+ end
40
+
155
41
  # key is required if you're using state
156
42
  # We can't initialize the session state in the initial method
157
43
  # because it doesn't have a view_context yet
@@ -205,7 +91,11 @@ module ViewComponentReflex
205
91
  else
206
92
  initial_state = ViewComponentReflex::Engine.state_adapter.state(request, "#{@key}_initial")
207
93
  ViewComponentReflex::Engine.state_adapter.state(request, @key).each do |k, v|
208
- unless permit_parameter?(initial_state[k], instance_variable_get(k))
94
+ instance_value = instance_variable_get(k)
95
+ if permit_parameter?(initial_state[k], instance_value)
96
+ ViewComponentReflex::Engine.state_adapter.set_state(request, controller, "#{@key}_initial", { k => instance_value })
97
+ ViewComponentReflex::Engine.state_adapter.set_state(request, controller, @key, { k => instance_value })
98
+ else
209
99
  instance_variable_set(k, v)
210
100
  end
211
101
  end
@@ -223,7 +113,7 @@ module ViewComponentReflex
223
113
  [
224
114
  :@view_context, :@lookup_context, :@view_renderer, :@view_flow,
225
115
  :@virtual_path, :@variant, :@current_template, :@output_buffer, :@key,
226
- :@helpers, :@controller, :@request, :@content, :@tag_builder
116
+ :@helpers, :@controller, :@request, :@tag_builder
227
117
  ]
228
118
  end
229
119
 
@@ -0,0 +1,140 @@
1
+ module ViewComponentReflex
2
+ class Reflex < StimulusReflex::Reflex
3
+ include CableReady::Broadcaster
4
+
5
+ class << self
6
+ attr_accessor :component_class
7
+ end
8
+
9
+ def refresh!(primary_selector = nil, *rest)
10
+ save_state
11
+
12
+ if primary_selector.nil? && !component.can_render_to_string?
13
+ primary_selector = selector
14
+ end
15
+ if primary_selector
16
+ prevent_refresh!
17
+
18
+ controller.process(url_params[:action])
19
+ document = Nokogiri::HTML(controller.response.body)
20
+ [primary_selector, *rest].each do |s|
21
+ html = document.css(s)
22
+ if html.present?
23
+ cable_ready[channel.stream_name].morph(
24
+ selector: s,
25
+ html: html.inner_html,
26
+ children_only: true,
27
+ permanent_attribute_name: "data-reflex-permanent"
28
+ )
29
+ end
30
+ end
31
+ else
32
+ refresh_component!
33
+ end
34
+ cable_ready.broadcast
35
+ end
36
+
37
+ def refresh_component!
38
+ component.tap do |k|
39
+ k.define_singleton_method(:key) do
40
+ element.dataset[:key]
41
+ end
42
+ end
43
+ document = Nokogiri::HTML(controller.render_component_to_string(component))
44
+ cable_ready[channel.stream_name].morph(
45
+ selector: selector,
46
+ children_only: true,
47
+ html: document.css(selector).inner_html,
48
+ permanent_attribute_name: "data-reflex-permanent"
49
+ )
50
+ end
51
+
52
+ def refresh_all!
53
+ refresh!("body")
54
+ end
55
+
56
+ def selector
57
+ "[data-controller~=\"#{stimulus_controller}\"][data-key=\"#{element.dataset[:key]}\"]"
58
+ end
59
+
60
+ # SR's delegate_call_to_reflex in channel.rb
61
+ # uses method to gather the method parameters, but since we're abusing
62
+ # method_missing here, that'll always fail
63
+ def method(name)
64
+ name.to_sym.to_proc
65
+ end
66
+
67
+ def respond_to_missing?(name, _ = false)
68
+ !!name.to_proc
69
+ end
70
+
71
+ before_reflex do |a|
72
+ a.send a.method_name
73
+ throw :abort
74
+ end
75
+
76
+ def method_missing(name, *args)
77
+ super unless respond_to_missing?(name)
78
+ state.each do |k, v|
79
+ component.instance_variable_set(k, v)
80
+ end
81
+ name.to_proc.call(component, *args)
82
+ refresh! unless @prevent_refresh
83
+ end
84
+
85
+ def prevent_refresh!
86
+ @prevent_refresh = true
87
+ end
88
+
89
+ private
90
+
91
+ def component_class
92
+ self.class.component_class
93
+ end
94
+
95
+ def stimulus_controller
96
+ component_class.stimulus_controller
97
+ end
98
+
99
+ def stimulate(target, data)
100
+ dataset = {}
101
+ data.each do |k, v|
102
+ dataset["data-#{k}"] = v.to_s
103
+ end
104
+ channel.receive({
105
+ "target" => target,
106
+ "attrs" => element.attributes.to_h.symbolize_keys,
107
+ "dataset" => dataset
108
+ })
109
+ end
110
+
111
+ def component
112
+ return @component if @component
113
+ @component = component_class.allocate
114
+ reflex = self
115
+ exposed_methods = [:params, :request, :element, :refresh!, :refresh_all!, :stimulus_controller, :session, :prevent_refresh!, :selector, :stimulate]
116
+ exposed_methods.each do |meth|
117
+ @component.define_singleton_method(meth) do |*a|
118
+ reflex.send(meth, *a)
119
+ end
120
+ end
121
+ @component
122
+ end
123
+
124
+ def set_state(new_state = {})
125
+ ViewComponentReflex::Engine.state_adapter.set_state(request, controller, element.dataset[:key], new_state)
126
+ end
127
+
128
+ def state
129
+ ViewComponentReflex::Engine.state_adapter.state(request, element.dataset[:key])
130
+ end
131
+
132
+ def save_state
133
+ new_state = {}
134
+ component.safe_instance_variables.each do |k|
135
+ new_state[k] = component.instance_variable_get(k)
136
+ end
137
+ set_state(new_state)
138
+ end
139
+ end
140
+ end
@@ -1,7 +1,7 @@
1
1
  require "view_component_reflex/state_adapter/session"
2
2
  require "view_component_reflex/state_adapter/memory"
3
3
  require "view_component_reflex/engine"
4
- require 'stimulus_reflex'
4
+ require "stimulus_reflex"
5
5
 
6
6
  module ViewComponentReflex
7
7
  # Your code goes here...
@@ -1,3 +1,3 @@
1
1
  module ViewComponentReflex
2
- VERSION = '1.7.1'
2
+ VERSION = '2.1.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.7.1
4
+ version: 2.1.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-07-05 00:00:00.000000000 Z
11
+ date: 2020-07-10 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rails
@@ -36,28 +36,28 @@ dependencies:
36
36
  requirements:
37
37
  - - ">="
38
38
  - !ruby/object:Gem::Version
39
- version: 3.3.0.pre0
39
+ version: '0'
40
40
  type: :runtime
41
41
  prerelease: false
42
42
  version_requirements: !ruby/object:Gem::Requirement
43
43
  requirements:
44
44
  - - ">="
45
45
  - !ruby/object:Gem::Version
46
- version: 3.3.0.pre0
46
+ version: '0'
47
47
  - !ruby/object:Gem::Dependency
48
48
  name: view_component
49
49
  requirement: !ruby/object:Gem::Requirement
50
50
  requirements:
51
51
  - - ">="
52
52
  - !ruby/object:Gem::Version
53
- version: 2.13.0
53
+ version: '0'
54
54
  type: :runtime
55
55
  prerelease: false
56
56
  version_requirements: !ruby/object:Gem::Requirement
57
57
  requirements:
58
58
  - - ">="
59
59
  - !ruby/object:Gem::Version
60
- version: 2.13.0
60
+ version: '0'
61
61
  description: Allow stimulus reflexes in a view component
62
62
  email:
63
63
  - joshleblanc94@gmail.com
@@ -69,6 +69,7 @@ files:
69
69
  - README.md
70
70
  - Rakefile
71
71
  - app/components/view_component_reflex/component.rb
72
+ - app/reflexes/view_component_reflex/reflex.rb
72
73
  - lib/view_component_reflex.rb
73
74
  - lib/view_component_reflex/engine.rb
74
75
  - lib/view_component_reflex/state_adapter/memory.rb