view_component_reflex 3.1.2 → 3.1.7

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