view_component_reflex 2.5.0 → 2.6.0

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,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