view_component_reflex 2.5.0 → 2.6.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,214 +1,220 @@
1
- module ViewComponentReflex
2
- class Component < ViewComponent::Base
3
- class << self
4
- def init_stimulus_reflex
5
- factory = ViewComponentReflex::ReflexFactory.new(self)
6
- @stimulus_reflex ||= factory.reflex
7
- wire_up_callbacks if factory.new?
8
- end
9
-
10
- def reflex_base_class(new_base_class = nil)
11
- if new_base_class.nil?
12
- @reflex_base_class ||= ViewComponentReflex::Reflex
13
- else
14
- if new_base_class <= ViewComponentReflex::Reflex
15
- @reflex_base_class = new_base_class
16
- else
17
- raise StandardError.new("The reflex base class must inherit from ViewComponentReflex::Reflex")
18
- end
19
- end
20
- end
21
-
22
- def queue_callback(key, args, blk)
23
- callbacks(key).push({
24
- args: args,
25
- blk: blk
26
- })
27
- end
28
-
29
- def callbacks(key)
30
- @callbacks ||= {}
31
- @callbacks[key] ||= []
32
- end
33
-
34
- def register_callbacks(key)
35
- callbacks(key).each do |cb|
36
- @stimulus_reflex.send("#{key}_reflex", *cb[:args], &cb[:blk])
37
- end
38
- end
39
-
40
- def before_reflex(*args, &blk)
41
- queue_callback(:before, args, blk)
42
- end
43
-
44
- def after_reflex(*args, &blk)
45
- queue_callback(:after, args, blk)
46
- end
47
-
48
- def around_reflex(*args, &blk)
49
- queue_callback(:around, args, blk)
50
- end
51
-
52
- def wire_up_callbacks
53
- register_callbacks(:before)
54
- register_callbacks(:after)
55
- register_callbacks(:around)
56
- end
57
- end
58
-
59
- def self.stimulus_controller
60
- name.chomp("Component").underscore.dasherize
61
- end
62
-
63
- def stimulus_reflex?
64
- helpers.controller.instance_variable_get(:@stimulus_reflex)
65
- end
66
-
67
- def component_controller(opts_or_tag = :div, opts = {}, &blk)
68
- init_key
69
-
70
- tag = :div
71
- options = if opts_or_tag.is_a? Hash
72
- opts_or_tag
73
- else
74
- tag = opts_or_tag
75
- opts
76
- end
77
-
78
- options[:data] = {
79
- controller: self.class.stimulus_controller,
80
- key: key,
81
- **(options[:data] || {})
82
- }
83
- content_tag tag, capture(&blk), options
84
- end
85
-
86
- def can_render_to_string?
87
- omitted_from_state.empty?
88
- end
89
-
90
- # key is required if you're using state
91
- # We can't initialize the session state in the initial method
92
- # because it doesn't have a view_context yet
93
- # This is the next best place to do it
94
- def init_key
95
- # we want the erb file that renders the component. `caller` gives the file name,
96
- # and line number, which should be unique. We hash it to make it a nice number
97
- erb_file = caller.select { |p| p.match? /.\.html\.(haml|erb|slim)/ }[1]
98
- key = if erb_file
99
- Digest::SHA2.hexdigest(erb_file.split(":in")[0])
100
- else
101
- ""
102
- end
103
- key += collection_key.to_s if collection_key
104
- @key = key
105
- end
106
-
107
- # Helper to use to create the proper reflex data attributes for an element
108
- def reflex_data_attributes(reflex)
109
- action, method = reflex.to_s.split("->")
110
- if method.nil?
111
- method = action
112
- action = "click"
113
- end
114
-
115
- {
116
- reflex: "#{action}->#{self.class.name}##{method}",
117
- key: key
118
- }
119
- end
120
-
121
- def reflex_tag(reflex, name, content_or_options_with_block = {}, options = {}, escape = true, &block)
122
- if content_or_options_with_block.is_a?(Hash)
123
- merge_data_attributes(content_or_options_with_block, reflex_data_attributes(reflex))
124
- else
125
- merge_data_attributes(options, reflex_data_attributes(reflex))
126
- end
127
- content_tag(name, content_or_options_with_block, options, escape, &block)
128
- end
129
-
130
- def collection_key
131
- nil
132
- end
133
-
134
- def permit_parameter?(initial_param, new_param)
135
- initial_param != new_param
136
- end
137
-
138
- def omitted_from_state
139
- []
140
- end
141
-
142
- def after_state_initialized(parameters_changed)
143
- # called after state component has been hydrated
144
- end
145
-
146
- # def receive_params(old_state, params)
147
- # # no op
148
- # end
149
-
150
- def key
151
- # initialize session state
152
- if !stimulus_reflex? || ViewComponentReflex::Engine.state_adapter.state(request, @key).empty?
153
-
154
- new_state = create_safe_state
155
-
156
- ViewComponentReflex::Engine.state_adapter.store_state(request, @key, new_state)
157
- ViewComponentReflex::Engine.state_adapter.store_state(request, "#{@key}_initial", new_state)
158
- elsif !@initialized_state
159
- initial_state = ViewComponentReflex::Engine.state_adapter.state(request, "#{@key}_initial")
160
-
161
- # incoming_params = safe_instance_variables.each_with_object({}) { |var, obj| obj[var] = instance_variable_get(var) }
162
- # receive_params(ViewComponentReflex::Engine.state_adapter.state(request, @key), incoming_params)
163
-
164
- parameters_changed = []
165
- ViewComponentReflex::Engine.state_adapter.state(request, @key).each do |k, v|
166
- instance_value = instance_variable_get(k)
167
- if permit_parameter?(initial_state[k], instance_value)
168
- parameters_changed << k
169
- ViewComponentReflex::Engine.state_adapter.set_state(request, controller, "#{@key}_initial", {k => instance_value})
170
- ViewComponentReflex::Engine.state_adapter.set_state(request, controller, @key, {k => instance_value})
171
- else
172
- instance_variable_set(k, v)
173
- end
174
- end
175
- after_state_initialized(parameters_changed)
176
- @initialized_state = true
177
- end
178
- @key
179
- end
180
-
181
- def safe_instance_variables
182
- instance_variables - unsafe_instance_variables
183
- end
184
-
185
- private
186
-
187
- def unsafe_instance_variables
188
- [
189
- :@view_context, :@lookup_context, :@view_renderer, :@view_flow,
190
- :@virtual_path, :@variant, :@current_template, :@output_buffer, :@key,
191
- :@helpers, :@controller, :@request, :@tag_builder, :@initialized_state
192
- ]
193
- end
194
-
195
- def create_safe_state
196
- new_state = {}
197
-
198
- # this will almost certainly break
199
- safe_instance_variables.each do |k|
200
- new_state[k] = instance_variable_get(k) unless omitted_from_state.include?(k)
201
- end
202
- new_state
203
- end
204
-
205
- def merge_data_attributes(options, attributes)
206
- data = options[:data]
207
- if data.nil?
208
- options[:data] = attributes
209
- else
210
- options[:data].merge! attributes
211
- end
212
- end
213
- end
214
- end
1
+ module ViewComponentReflex
2
+ class Component < ViewComponent::Base
3
+ class << self
4
+ def init_stimulus_reflex
5
+ factory = ViewComponentReflex::ReflexFactory.new(self)
6
+ @stimulus_reflex ||= factory.reflex
7
+ wire_up_callbacks if factory.new?
8
+ end
9
+
10
+ def reflex_base_class(new_base_class = nil)
11
+ if new_base_class.nil?
12
+ @reflex_base_class ||= ViewComponentReflex::Reflex
13
+ else
14
+ if new_base_class <= ViewComponentReflex::Reflex
15
+ @reflex_base_class = new_base_class
16
+ else
17
+ raise StandardError.new("The reflex base class must inherit from ViewComponentReflex::Reflex")
18
+ end
19
+ end
20
+ end
21
+
22
+ def queue_callback(key, args, blk)
23
+ callbacks(key).push({
24
+ args: args,
25
+ blk: blk
26
+ })
27
+ end
28
+
29
+ def callbacks(key)
30
+ @callbacks ||= {}
31
+ @callbacks[key] ||= []
32
+ end
33
+
34
+ def register_callbacks(key)
35
+ callbacks(key).each do |cb|
36
+ @stimulus_reflex.send("#{key}_reflex", *cb[:args], &cb[:blk])
37
+ end
38
+ end
39
+
40
+ def before_reflex(*args, &blk)
41
+ queue_callback(:before, args, blk)
42
+ end
43
+
44
+ def after_reflex(*args, &blk)
45
+ queue_callback(:after, args, blk)
46
+ end
47
+
48
+ def around_reflex(*args, &blk)
49
+ queue_callback(:around, args, blk)
50
+ end
51
+
52
+ def wire_up_callbacks
53
+ register_callbacks(:before)
54
+ register_callbacks(:after)
55
+ register_callbacks(:around)
56
+ end
57
+ end
58
+
59
+ def self.stimulus_controller
60
+ name.chomp("Component").underscore.dasherize
61
+ end
62
+
63
+ def stimulus_reflex?
64
+ helpers.controller.instance_variable_get(:@stimulus_reflex)
65
+ end
66
+
67
+ def component_controller(opts_or_tag = :div, opts = {}, &blk)
68
+ init_key
69
+
70
+ tag = :div
71
+ options = if opts_or_tag.is_a? Hash
72
+ opts_or_tag
73
+ else
74
+ tag = opts_or_tag
75
+ opts
76
+ end
77
+
78
+ options[:data] = {
79
+ controller: self.class.stimulus_controller,
80
+ key: key,
81
+ **(options[:data] || {})
82
+ }
83
+ content_tag tag, capture(&blk), options
84
+ end
85
+
86
+ def can_render_to_string?
87
+ omitted_from_state.empty?
88
+ end
89
+
90
+ # key is required if you're using state
91
+ # We can't initialize the session state in the initial method
92
+ # because it doesn't have a view_context yet
93
+ # This is the next best place to do it
94
+ def init_key
95
+ # we want the erb file that renders the component. `caller` gives the file name,
96
+ # and line number, which should be unique. We hash it to make it a nice number
97
+ erb_file = caller.select { |p| p.match? /.\.html\.(haml|erb|slim)/ }[1]
98
+ key = if erb_file
99
+ Digest::SHA2.hexdigest(erb_file.split(":in")[0])
100
+ else
101
+ ""
102
+ end
103
+ key += collection_key.to_s if collection_key
104
+ @key = key
105
+ end
106
+
107
+ # Helper to use to create the proper reflex data attributes for an element
108
+ def reflex_data_attributes(reflex)
109
+ action, method = reflex.to_s.split("->")
110
+ if method.nil?
111
+ method = action
112
+ action = "click"
113
+ end
114
+
115
+ {
116
+ reflex: "#{action}->#{self.class.name}##{method}",
117
+ key: key
118
+ }
119
+ end
120
+
121
+ def reflex_tag(reflex, name, content_or_options_with_block = {}, options = {}, escape = true, &block)
122
+ if content_or_options_with_block.is_a?(Hash)
123
+ merge_data_attributes(content_or_options_with_block, reflex_data_attributes(reflex))
124
+ else
125
+ merge_data_attributes(options, reflex_data_attributes(reflex))
126
+ end
127
+ content_tag(name, content_or_options_with_block, options, escape, &block)
128
+ end
129
+
130
+ def collection_key
131
+ nil
132
+ end
133
+
134
+ def permit_parameter?(initial_param, new_param)
135
+ initial_param != new_param
136
+ end
137
+
138
+ def omitted_from_state
139
+ []
140
+ end
141
+
142
+ def after_state_initialized(parameters_changed)
143
+ # called after state component has been hydrated
144
+ end
145
+
146
+ # def receive_params(old_state, params)
147
+ # # no op
148
+ # end
149
+
150
+ def key
151
+ adapter = ViewComponentReflex::Engine.state_adapter
152
+
153
+ # initialize session state
154
+ if !stimulus_reflex? || adapter.state(request, @key).empty?
155
+
156
+ new_state = create_safe_state
157
+
158
+ adapter.wrap_write_async do
159
+ adapter.store_state(request, @key, new_state)
160
+ adapter.store_state(request, "#{@key}_initial", new_state)
161
+ end
162
+ elsif !@initialized_state
163
+ initial_state = adapter.state(request, "#{@key}_initial")
164
+
165
+ # incoming_params = safe_instance_variables.each_with_object({}) { |var, obj| obj[var] = instance_variable_get(var) }
166
+ # receive_params(ViewComponentReflex::Engine.state_adapter.state(request, @key), incoming_params)
167
+
168
+ parameters_changed = []
169
+ adapter.state(request, @key).each do |k, v|
170
+ instance_value = instance_variable_get(k)
171
+ if permit_parameter?(initial_state[k], instance_value)
172
+ parameters_changed << k
173
+ adapter.wrap_write_async do
174
+ adapter.set_state(request, controller, "#{@key}_initial", {k => instance_value})
175
+ adapter.set_state(request, controller, @key, {k => instance_value})
176
+ end
177
+ else
178
+ instance_variable_set(k, v)
179
+ end
180
+ end
181
+ after_state_initialized(parameters_changed)
182
+ @initialized_state = true
183
+ end
184
+ @key
185
+ end
186
+
187
+ def safe_instance_variables
188
+ instance_variables - unsafe_instance_variables
189
+ end
190
+
191
+ private
192
+
193
+ def unsafe_instance_variables
194
+ [
195
+ :@view_context, :@lookup_context, :@view_renderer, :@view_flow,
196
+ :@virtual_path, :@variant, :@current_template, :@output_buffer, :@key,
197
+ :@helpers, :@controller, :@request, :@tag_builder, :@initialized_state
198
+ ]
199
+ end
200
+
201
+ def create_safe_state
202
+ new_state = {}
203
+
204
+ # this will almost certainly break
205
+ safe_instance_variables.each do |k|
206
+ new_state[k] = instance_variable_get(k) unless omitted_from_state.include?(k)
207
+ end
208
+ new_state
209
+ end
210
+
211
+ def merge_data_attributes(options, attributes)
212
+ data = options[:data]
213
+ if data.nil?
214
+ options[:data] = attributes
215
+ else
216
+ options[:data].merge! attributes
217
+ end
218
+ end
219
+ end
220
+ end
@@ -1,10 +1,11 @@
1
- require "stimulus_reflex"
2
- require 'view_component_reflex/reflex_factory'
3
- require "view_component_reflex/state_adapter/session"
4
- require "view_component_reflex/state_adapter/memory"
5
- require "view_component_reflex/reflex"
6
- require "view_component_reflex/engine"
7
-
8
- module ViewComponentReflex
9
- # Your code goes here...
10
- end
1
+ require "stimulus_reflex"
2
+ require 'view_component_reflex/reflex_factory'
3
+ require "view_component_reflex/state_adapter/session"
4
+ require "view_component_reflex/state_adapter/memory"
5
+ require "view_component_reflex/state_adapter/redis"
6
+ require "view_component_reflex/reflex"
7
+ require "view_component_reflex/engine"
8
+
9
+ module ViewComponentReflex
10
+ # Your code goes here...
11
+ end