view_component_reflex 3.1.2 → 3.1.7

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.
@@ -1,37 +1,37 @@
1
- module ViewComponentReflex
2
- class Engine < ::Rails::Engine
3
-
4
- mattr_accessor :state_adapter
5
- Engine.state_adapter = StateAdapter::Session
6
-
7
- config.to_prepare do
8
- StimulusReflex::Channel.class_eval do
9
- unless instance_methods.include?(:receive_original)
10
- alias_method :receive_original, :receive
11
- def receive(data)
12
- target = data["target"].to_s
13
- reflex_name, _ = target.split("#")
14
- reflex_name = reflex_name.camelize
15
- component_name = reflex_name.end_with?("Reflex") ? reflex_name[0...-6] : reflex_name
16
- component = begin
17
- component_name.constantize
18
- rescue
19
- # Since every reflex runs through this monkey patch, we're just going to ignore the ones that aren't for components
20
- end
21
-
22
- if component&.respond_to?(:init_stimulus_reflex)
23
- component.init_stimulus_reflex
24
- else
25
- p "Tried to initialize view_component_reflex on #{component_name}, but it's not a view_component_reflex"
26
- end
27
- receive_original(data)
28
- end
29
- end
30
- end
31
- end
32
-
33
- def self.configure
34
- yield self if block_given?
35
- end
36
- end
37
- end
1
+ module ViewComponentReflex
2
+ class Engine < ::Rails::Engine
3
+
4
+ mattr_accessor :state_adapter
5
+ Engine.state_adapter = StateAdapter::Session
6
+
7
+ config.to_prepare do
8
+ StimulusReflex::Channel.class_eval do
9
+ unless instance_methods.include?(:receive_original)
10
+ alias_method :receive_original, :receive
11
+ def receive(data)
12
+ target = data["target"].to_s
13
+ reflex_name, _ = target.split("#")
14
+ reflex_name = reflex_name.camelize
15
+ component_name = reflex_name.end_with?("Reflex") ? reflex_name[0...-6] : reflex_name
16
+ component = begin
17
+ component_name.constantize
18
+ rescue
19
+ # Since every reflex runs through this monkey patch, we're just going to ignore the ones that aren't for components
20
+ end
21
+
22
+ if component&.respond_to?(:init_stimulus_reflex)
23
+ component.init_stimulus_reflex
24
+ else
25
+ p "Tried to initialize view_component_reflex on #{component_name}, but it's not a view_component_reflex"
26
+ end
27
+ receive_original(data)
28
+ end
29
+ end
30
+ end
31
+ end
32
+
33
+ def self.configure
34
+ yield self if block_given?
35
+ end
36
+ end
37
+ end
@@ -1,198 +1,201 @@
1
- module ViewComponentReflex
2
- class Reflex < StimulusReflex::Reflex
3
-
4
- class << self
5
- attr_accessor :component_class
6
- end
7
-
8
- def refresh!(primary_selector = nil, *rest)
9
- save_state
10
-
11
- if primary_selector.nil? && !component.can_render_to_string?
12
- primary_selector = selector
13
- end
14
- if primary_selector
15
- prevent_refresh!
16
-
17
- controller.process(params[:action])
18
- document = Nokogiri::HTML(controller.response.body)
19
- [primary_selector, *rest].each do |s|
20
- html = document.css(s)
21
- if html.present?
22
- CableReady::Channels.instance[stream].morph(
23
- selector: s,
24
- html: html.inner_html,
25
- children_only: true,
26
- permanent_attribute_name: "data-reflex-permanent",
27
- stimulus_reflex: stimulus_reflex_data
28
- )
29
- end
30
- end
31
- else
32
- refresh_component!
33
- end
34
- CableReady::Channels.instance.broadcast
35
- end
36
-
37
- def stream
38
- @stream ||= stream_name
39
- end
40
-
41
- def stream_to(channel)
42
- @stream = channel
43
- end
44
-
45
- def refresh_component!
46
- component.tap do |k|
47
- k.define_singleton_method(:key) do
48
- element.dataset[:key]
49
- end
50
- end
51
- document = Nokogiri::HTML(component.render_in(controller.view_context))
52
- CableReady::Channels.instance[stream].morph(
53
- selector: selector,
54
- children_only: true,
55
- html: document.css(selector).inner_html,
56
- permanent_attribute_name: "data-reflex-permanent",
57
- stimulus_reflex: stimulus_reflex_data
58
- )
59
- end
60
-
61
- def stimulus_reflex_data
62
- {
63
- reflex_id: reflex_id,
64
- xpath: xpath,
65
- target: target,
66
- c_xpath: c_xpath,
67
- reflex_controller: reflex_controller,
68
- url: url,
69
- morph: :page,
70
- attrs: {
71
- key: element.dataset[:key]
72
- }
73
- }
74
- end
75
-
76
- def target
77
- "#{component_class}##{method_name}"
78
- end
79
-
80
- def refresh_all!
81
- refresh!("body")
82
- end
83
-
84
- def selector
85
- "[data-controller~=\"#{stimulus_controller}\"][data-key=\"#{element.dataset[:key]}\"]"
86
- end
87
-
88
- # SR's delegate_call_to_reflex in channel.rb
89
- # uses method to gather the method parameters, but since we're abusing
90
- # method_missing here, that'll always fail
91
- def method(name)
92
- component.method(name.to_sym)
93
- end
94
-
95
- def respond_to_missing?(name, _ = false)
96
- !!name.to_proc
97
- end
98
-
99
- # this is copied out of stimulus_reflex/reflex.rb and modified
100
- def morph(selectors, html = "")
101
- case selectors
102
- when :nothing
103
- @broadcaster = StimulusReflex::NothingBroadcaster.new(self)
104
- else
105
- @broadcaster = StimulusReflex::SelectorBroadcaster.new(self) unless broadcaster.selector?
106
- broadcaster.morphs << [selectors, html]
107
- end
108
- end
109
-
110
- def method_missing(name, *args)
111
- morph :nothing
112
- super unless respond_to_missing?(name)
113
- state.each do |k, v|
114
- component.instance_variable_set(k, v)
115
- end
116
- name.to_proc.call(component, *args)
117
- refresh! unless @prevent_refresh
118
- end
119
-
120
- def prevent_refresh!
121
- @prevent_refresh = true
122
- end
123
-
124
- private
125
-
126
- def component_class
127
- self.class.component_class
128
- end
129
-
130
- def stimulus_controller
131
- component_class.stimulus_controller
132
- end
133
-
134
- def stimulate(target, data)
135
- data_to_receive = {}
136
-
137
- stimulus_reflex_data.each do |k, v|
138
- data_to_receive[k.to_s.camelize(:lower)] = v
139
- end
140
-
141
- data_to_receive["dataset"] = data.each_with_object({}) do |(k, v), o|
142
- o["data-#{k}"] = v
143
- end
144
-
145
- data_to_receive["attrs"] = element.attributes.to_h.symbolize_keys
146
- data_to_receive["target"] = target
147
-
148
- channel.receive data_to_receive
149
- end
150
-
151
- def component
152
- return @component if @component
153
- @component = component_class.allocate
154
- reflex = self
155
- exposed_methods = [
156
- :params,
157
- :request,
158
- :connection,
159
- :element,
160
- :refresh!,
161
- :refresh_all!,
162
- :stimulus_controller,
163
- :session,
164
- :prevent_refresh!,
165
- :selector,
166
- :stimulate,
167
- :stream_to
168
- ]
169
- exposed_methods.each do |meth|
170
- @component.define_singleton_method(meth) do |*a|
171
- reflex.send(meth, *a)
172
- end
173
- end
174
-
175
- @component.define_singleton_method(:reflex) do
176
- reflex
177
- end
178
-
179
- @component
180
- end
181
-
182
- def set_state(new_state = {})
183
- ViewComponentReflex::Engine.state_adapter.set_state(request, controller, element.dataset[:key], new_state)
184
- end
185
-
186
- def state
187
- ViewComponentReflex::Engine.state_adapter.state(request, element.dataset[:key])
188
- end
189
-
190
- def save_state
191
- new_state = {}
192
- component.safe_instance_variables.each do |k|
193
- new_state[k] = component.instance_variable_get(k)
194
- end
195
- set_state(new_state)
196
- end
197
- end
198
- end
1
+ module ViewComponentReflex
2
+ class Reflex < StimulusReflex::Reflex
3
+
4
+ class << self
5
+ attr_accessor :component_class
6
+ end
7
+
8
+ def refresh!(primary_selector = nil, *rest)
9
+ save_state
10
+
11
+ if primary_selector.nil? && !component.can_render_to_string?
12
+ primary_selector = selector
13
+ end
14
+ if primary_selector
15
+ prevent_refresh!
16
+
17
+ controller.process(params[:action])
18
+ document = Nokogiri::HTML(controller.response.body)
19
+ [primary_selector, *rest].each do |s|
20
+ html = document.css(s)
21
+ if html.present?
22
+ CableReady::Channels.instance[stream].morph(
23
+ selector: s,
24
+ html: html.inner_html,
25
+ children_only: true,
26
+ permanent_attribute_name: "data-reflex-permanent",
27
+ )
28
+ end
29
+ end
30
+ else
31
+ refresh_component!
32
+ end
33
+ CableReady::Channels.instance[stream].broadcast
34
+ end
35
+
36
+ def stream
37
+ @stream ||= stream_name
38
+ end
39
+
40
+ def stream_to(channel)
41
+ @stream = channel
42
+ end
43
+
44
+ def refresh_component!
45
+ component.tap do |k|
46
+ k.define_singleton_method(:initialize_component) do
47
+ @key = element.dataset[:key]
48
+ end
49
+ end
50
+
51
+ document = Nokogiri::HTML(component.render_in(controller.view_context))
52
+ CableReady::Channels.instance[stream].morph(
53
+ selector: selector,
54
+ children_only: true,
55
+ html: document.css(selector).inner_html,
56
+ permanent_attribute_name: "data-reflex-permanent",
57
+ )
58
+ end
59
+
60
+ def stimulus_reflex_data
61
+ {
62
+ reflex_id: reflex_id,
63
+ xpath_controller: xpath_controller,
64
+ xpath_element: xpath_element,
65
+ target: target,
66
+ reflex_controller: reflex_controller,
67
+ url: url,
68
+ morph: :page,
69
+ attrs: {
70
+ key: element.dataset[:key]
71
+ }
72
+ }
73
+ end
74
+
75
+ def target
76
+ "#{component_class}##{method_name}"
77
+ end
78
+
79
+ def refresh_all!
80
+ refresh!("body")
81
+ end
82
+
83
+ def selector
84
+ "[data-controller~=\"#{stimulus_controller}\"][data-key=\"#{element.dataset[:key]}\"]"
85
+ end
86
+
87
+ # SR's delegate_call_to_reflex in channel.rb
88
+ # uses method to gather the method parameters, but since we're abusing
89
+ # method_missing here, that'll always fail
90
+ def method(name)
91
+ component.method(name.to_sym)
92
+ end
93
+
94
+ def respond_to_missing?(name, _ = false)
95
+ !!name.to_proc
96
+ end
97
+
98
+ def method_missing(name, *args, &blk)
99
+ morph :nothing
100
+ super unless respond_to_missing?(name)
101
+
102
+ state.each do |k, v|
103
+ component.instance_variable_set(k, v)
104
+ end
105
+
106
+ component.send(name, *args, &blk)
107
+
108
+ refresh! unless @prevent_refresh
109
+ end
110
+
111
+ def prevent_refresh!
112
+ @prevent_refresh = true
113
+ end
114
+
115
+ private
116
+
117
+ def component_class
118
+ self.class.component_class
119
+ end
120
+
121
+ def stimulus_controller
122
+ component_class.stimulus_controller
123
+ end
124
+
125
+ def stimulate(target, data)
126
+ data_to_receive = {}
127
+
128
+ stimulus_reflex_data.each do |k, v|
129
+ data_to_receive[k.to_s.camelize(:lower)] = v
130
+ end
131
+
132
+ data_to_receive["dataset"] = data.each_with_object({}) do |(k, v), o|
133
+ o["data-#{k}"] = v
134
+ end
135
+
136
+ data_to_receive["attrs"] = element.attributes.to_h.symbolize_keys
137
+ data_to_receive["target"] = target
138
+
139
+ channel.receive data_to_receive
140
+ end
141
+
142
+ def component
143
+ return @component if @component
144
+ @component = component_class.allocate
145
+ reflex = self
146
+ exposed_methods = [
147
+ :params,
148
+ :request,
149
+ :connection,
150
+ :element,
151
+ :refresh!,
152
+ :refresh_all!,
153
+ :stimulus_controller,
154
+ :session,
155
+ :prevent_refresh!,
156
+ :selector,
157
+ :stimulate,
158
+ :stream_to
159
+ ]
160
+ exposed_methods.each do |meth|
161
+ @component.define_singleton_method(meth) do |*a|
162
+ reflex.send(meth, *a)
163
+ end
164
+ end
165
+
166
+ @component.define_singleton_method(:reflex) do
167
+ reflex
168
+ end
169
+
170
+ @component
171
+ end
172
+
173
+ def set_state(new_state = {})
174
+ state_adapter.set_state(request, controller, key, new_state)
175
+ end
176
+
177
+ def key
178
+ element.dataset[:key]
179
+ end
180
+
181
+ def state_adapter
182
+ ViewComponentReflex::Engine.state_adapter
183
+ end
184
+
185
+ def state
186
+ state_adapter.state(request, key)
187
+ end
188
+
189
+ def initial_state
190
+ state_adapter.state(request, "#{key}_initial")
191
+ end
192
+
193
+ def save_state
194
+ new_state = {}
195
+ component.safe_instance_variables.each do |k|
196
+ new_state[k] = component.instance_variable_get(k)
197
+ end
198
+ set_state(new_state)
199
+ end
200
+ end
201
+ end