view_component_reflex 3.1.1 → 3.1.6

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,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,13 @@
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/null_broadcaster"
3
+ require 'view_component_reflex/reflex_factory'
4
+ require "view_component_reflex/state_adapter/base"
5
+ require "view_component_reflex/state_adapter/session"
6
+ require "view_component_reflex/state_adapter/memory"
7
+ require "view_component_reflex/state_adapter/redis"
8
+ require "view_component_reflex/reflex"
9
+ require "view_component_reflex/engine"
10
+
11
+ module ViewComponentReflex
12
+ # Your code goes here...
13
+ 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