view_component_reflex 3.1.2 → 3.1.3

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