view_component_reflex 2.3.14 → 2.5.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,207 +1,214 @@
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 receive_params(old_state, params)
143
- # # no op
144
- # end
145
-
146
- def key
147
- # initialize session state
148
- if !stimulus_reflex? || ViewComponentReflex::Engine.state_adapter.state(request, @key).empty?
149
-
150
- new_state = create_safe_state
151
-
152
- ViewComponentReflex::Engine.state_adapter.store_state(request, @key, new_state)
153
- ViewComponentReflex::Engine.state_adapter.store_state(request, "#{@key}_initial", new_state)
154
- elsif !@initialized_state
155
- initial_state = ViewComponentReflex::Engine.state_adapter.state(request, "#{@key}_initial")
156
-
157
- # incoming_params = safe_instance_variables.each_with_object({}) { |var, obj| obj[var] = instance_variable_get(var) }
158
- # receive_params(ViewComponentReflex::Engine.state_adapter.state(request, @key), incoming_params)
159
-
160
- ViewComponentReflex::Engine.state_adapter.state(request, @key).each do |k, v|
161
- instance_value = instance_variable_get(k)
162
- if permit_parameter?(initial_state[k], instance_value)
163
- ViewComponentReflex::Engine.state_adapter.set_state(request, controller, "#{@key}_initial", {k => instance_value})
164
- ViewComponentReflex::Engine.state_adapter.set_state(request, controller, @key, {k => instance_value})
165
- else
166
- instance_variable_set(k, v)
167
- end
168
- end
169
- @initialized_state = true
170
- end
171
- @key
172
- end
173
-
174
- def safe_instance_variables
175
- instance_variables - unsafe_instance_variables
176
- end
177
-
178
- private
179
-
180
- def unsafe_instance_variables
181
- [
182
- :@view_context, :@lookup_context, :@view_renderer, :@view_flow,
183
- :@virtual_path, :@variant, :@current_template, :@output_buffer, :@key,
184
- :@helpers, :@controller, :@request, :@tag_builder, :@initialized_state
185
- ]
186
- end
187
-
188
- def create_safe_state
189
- new_state = {}
190
-
191
- # this will almost certainly break
192
- safe_instance_variables.each do |k|
193
- new_state[k] = instance_variable_get(k) unless omitted_from_state.include?(k)
194
- end
195
- new_state
196
- end
197
-
198
- def merge_data_attributes(options, attributes)
199
- data = options[:data]
200
- if data.nil?
201
- options[:data] = attributes
202
- else
203
- options[:data].merge! attributes
204
- end
205
- end
206
- end
207
- 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
+ # 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,10 +1,10 @@
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/reflex"
6
+ require "view_component_reflex/engine"
7
+
8
+ module ViewComponentReflex
9
+ # Your code goes here...
10
+ end
@@ -1,36 +1,36 @@
1
- module ViewComponentReflex
2
- class Engine < ::Rails::Engine
3
- class << self
4
- mattr_accessor :state_adapter
5
-
6
- self.state_adapter = StateAdapter::Session
7
- end
8
-
9
- config.to_prepare do
10
- StimulusReflex::Channel.class_eval do
11
- unless instance_methods.include?(:receive_original)
12
- alias_method :receive_original, :receive
13
- def receive(data)
14
- target = data["target"].to_s
15
- reflex_name, _ = target.split("#")
16
- reflex_name = reflex_name.camelize
17
- component_name = reflex_name.end_with?("Reflex") ? reflex_name[0...-6] : reflex_name
18
- if component_name.end_with?("Component")
19
- begin
20
- component_name.constantize.init_stimulus_reflex
21
- rescue
22
- p "Tried to initialize view_component_reflex on #{component_name}, but it's not a view_component_reflex"
23
- end
24
-
25
- end
26
- receive_original(data)
27
- end
28
- end
29
- end
30
- end
31
-
32
- def self.configure
33
- yield self if block_given?
34
- end
35
- end
36
- end
1
+ module ViewComponentReflex
2
+ class Engine < ::Rails::Engine
3
+ class << self
4
+ mattr_accessor :state_adapter
5
+
6
+ self.state_adapter = StateAdapter::Session
7
+ end
8
+
9
+ config.to_prepare do
10
+ StimulusReflex::Channel.class_eval do
11
+ unless instance_methods.include?(:receive_original)
12
+ alias_method :receive_original, :receive
13
+ def receive(data)
14
+ target = data["target"].to_s
15
+ reflex_name, _ = target.split("#")
16
+ reflex_name = reflex_name.camelize
17
+ component_name = reflex_name.end_with?("Reflex") ? reflex_name[0...-6] : reflex_name
18
+ if component_name.end_with?("Component")
19
+ begin
20
+ component_name.constantize.init_stimulus_reflex
21
+ rescue
22
+ p "Tried to initialize view_component_reflex on #{component_name}, but it's not a view_component_reflex"
23
+ end
24
+
25
+ end
26
+ receive_original(data)
27
+ end
28
+ end
29
+ end
30
+ end
31
+
32
+ def self.configure
33
+ yield self if block_given?
34
+ end
35
+ end
36
+ end