view_component_reflex 3.1.1 → 3.1.6

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,19 @@
1
+ module ViewComponentReflex
2
+ class NullBroadcaster < StimulusReflex::Broadcaster
3
+ def broadcast(_, data)
4
+ nil
5
+ end
6
+
7
+ def nothing?
8
+ true
9
+ end
10
+
11
+ def to_sym
12
+ :nothing
13
+ end
14
+
15
+ def to_s
16
+ "Nothing"
17
+ end
18
+ end
19
+ end
@@ -1,198 +1,202 @@
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
- cable_ready.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
+ stimulus_reflex: stimulus_reflex_data
28
+ )
29
+ end
30
+ end
31
+ else
32
+ refresh_component!
33
+ end
34
+ CableReady::Channels.instance[stream].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_controller: xpath_controller,
65
+ xpath_element: xpath_element,
66
+ target: target,
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
+ when :null
105
+ @broadcaster = NullBroadcaster.new(self)
106
+ else
107
+ @broadcaster = StimulusReflex::SelectorBroadcaster.new(self) unless broadcaster.selector?
108
+ broadcaster.morphs << [selectors, html]
109
+ end
110
+ end
111
+
112
+ def method_missing(name, *args, &blk)
113
+ morph :null
114
+ super unless respond_to_missing?(name)
115
+ state.each do |k, v|
116
+ component.instance_variable_set(k, v)
117
+ end
118
+
119
+ component.send(name, *args, &blk)
120
+
121
+ refresh! unless @prevent_refresh
122
+ end
123
+
124
+ def prevent_refresh!
125
+ @prevent_refresh = true
126
+ end
127
+
128
+ private
129
+
130
+ def component_class
131
+ self.class.component_class
132
+ end
133
+
134
+ def stimulus_controller
135
+ component_class.stimulus_controller
136
+ end
137
+
138
+ def stimulate(target, data)
139
+ data_to_receive = {}
140
+
141
+ stimulus_reflex_data.each do |k, v|
142
+ data_to_receive[k.to_s.camelize(:lower)] = v
143
+ end
144
+
145
+ data_to_receive["dataset"] = data.each_with_object({}) do |(k, v), o|
146
+ o["data-#{k}"] = v
147
+ end
148
+
149
+ data_to_receive["attrs"] = element.attributes.to_h.symbolize_keys
150
+ data_to_receive["target"] = target
151
+
152
+ channel.receive data_to_receive
153
+ end
154
+
155
+ def component
156
+ return @component if @component
157
+ @component = component_class.allocate
158
+ reflex = self
159
+ exposed_methods = [
160
+ :params,
161
+ :request,
162
+ :connection,
163
+ :element,
164
+ :refresh!,
165
+ :refresh_all!,
166
+ :stimulus_controller,
167
+ :session,
168
+ :prevent_refresh!,
169
+ :selector,
170
+ :stimulate,
171
+ :stream_to
172
+ ]
173
+ exposed_methods.each do |meth|
174
+ @component.define_singleton_method(meth) do |*a|
175
+ reflex.send(meth, *a)
176
+ end
177
+ end
178
+
179
+ @component.define_singleton_method(:reflex) do
180
+ reflex
181
+ end
182
+
183
+ @component
184
+ end
185
+
186
+ def set_state(new_state = {})
187
+ ViewComponentReflex::Engine.state_adapter.set_state(request, controller, element.dataset[:key], new_state)
188
+ end
189
+
190
+ def state
191
+ ViewComponentReflex::Engine.state_adapter.state(request, element.dataset[:key])
192
+ end
193
+
194
+ def save_state
195
+ new_state = {}
196
+ component.safe_instance_variables.each do |k|
197
+ new_state[k] = component.instance_variable_get(k)
198
+ end
199
+ set_state(new_state)
200
+ end
201
+ end
202
+ end
@@ -1,61 +1,61 @@
1
- module ViewComponentReflex
2
- class ReflexFactory
3
- def initialize(component)
4
- @component = component
5
- @new = false
6
- reflex.component_class = component
7
- end
8
-
9
- def nested?
10
- @nested ||= @component.name.include?("::")
11
- end
12
-
13
- def reflex_name
14
- @reflex_name ||= if nested?
15
- @component.name.split("::").last
16
- else
17
- @component.name
18
- end + "Reflex"
19
- end
20
-
21
- def new?
22
- @new
23
- end
24
-
25
- def reflex
26
- @reflex ||= if nested?
27
- reflex_from_nested_component
28
- else
29
- reflex_from_component
30
- end
31
- end
32
-
33
- def reflex_instance
34
- @reflex_instance ||= Class.new(@component.reflex_base_class)
35
- end
36
-
37
- def reflex_from_nested_component
38
- parent = if @component.respond_to? :module_parent
39
- @component.module_parent
40
- else
41
- @component.parent
42
- end
43
-
44
- if parent.const_defined?(reflex_name)
45
- parent.const_get(reflex_name)
46
- else
47
- @new = true
48
- parent.const_set(reflex_name, reflex_instance)
49
- end
50
- end
51
-
52
- def reflex_from_component
53
- if Object.const_defined?(reflex_name)
54
- Object.const_get(reflex_name)
55
- else
56
- @new = true
57
- Object.const_set(reflex_name, reflex_instance)
58
- end
59
- end
60
- end
61
- end
1
+ module ViewComponentReflex
2
+ class ReflexFactory
3
+ def initialize(component)
4
+ @component = component
5
+ @new = false
6
+ reflex.component_class = component
7
+ end
8
+
9
+ def nested?
10
+ @nested ||= @component.name.include?("::")
11
+ end
12
+
13
+ def reflex_name
14
+ @reflex_name ||= if nested?
15
+ @component.name.split("::").last
16
+ else
17
+ @component.name
18
+ end + "Reflex"
19
+ end
20
+
21
+ def new?
22
+ @new
23
+ end
24
+
25
+ def reflex
26
+ @reflex ||= if nested?
27
+ reflex_from_nested_component
28
+ else
29
+ reflex_from_component
30
+ end
31
+ end
32
+
33
+ def reflex_instance
34
+ @reflex_instance ||= Class.new(@component.reflex_base_class)
35
+ end
36
+
37
+ def reflex_from_nested_component
38
+ parent = if @component.respond_to? :module_parent
39
+ @component.module_parent
40
+ else
41
+ @component.parent
42
+ end
43
+
44
+ if parent.const_defined?(reflex_name)
45
+ parent.const_get(reflex_name)
46
+ else
47
+ @new = true
48
+ parent.const_set(reflex_name, reflex_instance)
49
+ end
50
+ end
51
+
52
+ def reflex_from_component
53
+ if Object.const_defined?(reflex_name)
54
+ Object.const_get(reflex_name)
55
+ else
56
+ @new = true
57
+ Object.const_set(reflex_name, reflex_instance)
58
+ end
59
+ end
60
+ end
61
+ end