Module: Vident::Internals::Resolver
- Defined in:
- lib/vident/internals/resolver.rb
Overview
Resolves raw Declarations and instance state into a Draft of typed Stimulus values. Phase ‘:static` skips proc-bearing entries; `:procs` processes only those; `:all` does both. Procs nested inside a Hash descriptor escape the phase gate — use the fluent builder instead.
Class Method Summary collapse
- .absorb_input(draft, kind, input, instance, implied, phase:, alias_map: {}) ⇒ Object
- .absorb_one(kind_meta, entry, instance, implied, kind: nil, alias_map: {}) ⇒ Object
- .absorb_root_element_attributes(draft, instance, implied, phase:, alias_map: {}) ⇒ Object
- .absorb_stimulus_props(draft, instance, implied, phase:, alias_map: {}) ⇒ Object
- .build_alias_map(declarations) ⇒ Object
- .build_implied_controller(instance) ⇒ Object
- .call(declarations, instance, phase: :all) ⇒ Object
- .gated_out?(when_proc, instance) ⇒ Boolean
-
.instance_id(instance) ⇒ Object
Must match the mutation-API path (#id, memoised) so DSL outlet auto-selectors and runtime-added outlets scope identically.
- .instance_ivar(instance, name) ⇒ Object
- .key_for_parse(key) ⇒ Object
- .meta_for_controller(meta) ⇒ Object
- .parse_entry(kind_meta, entry, implied:, component_id:) ⇒ Object
- .parse_single(value_class, args, implied:, component_id:) ⇒ Object
-
.phase_allows?(is_proc, phase) ⇒ Boolean
Uses pattern matching so an unknown phase raises NoMatchingPatternError early.
- .phase_matches?(decl, phase) ⇒ Boolean
- .read_prop(instance, name) ⇒ Object
- .resolve_absorb_alias(kind, entry, alias_map) ⇒ Object
-
.resolve_action_aliases(args, alias_map) ⇒ Object
Unknown aliases raise — a symbolic controller ref is declared intent, not a guess.
-
.resolve_args(args, instance) ⇒ Object
Only nil drops — false, blank strings, and empty collections reach the parser.
- .resolve_declarations(draft, declarations, instance, implied, phase:, alias_map: {}) ⇒ Object
- .resolve_keyed(draft, kind, entries, instance, phase:) ⇒ Object
- .resolve_keyed_scalars(draft, kind, entries, instance, implied, value_class, phase:) ⇒ Object
- .resolve_keyed_values(draft, declarations, instance, implied, phase:) ⇒ Object
- .resolve_positional(draft, kind, entries, instance, phase:) ⇒ Object
- .resolve_procs_into(draft, declarations, instance) ⇒ Object
- .resolve_value_meta(decl, instance) ⇒ Object
- .seed_implied_controller(draft, instance) ⇒ Object
- .splat_single_array(args) ⇒ Object
Class Method Details
.absorb_input(draft, kind, input, instance, implied, phase:, alias_map: {}) ⇒ Object
190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 |
# File 'lib/vident/internals/resolver.rb', line 190 def absorb_input(draft, kind, input, instance, implied, phase:, alias_map: {}) return if input.nil? = Registry.fetch(kind) case input in Hash => h h.each do |key, raw| is_proc = raw.is_a?(Proc) next unless phase_allows?(is_proc, phase) absorbed = is_proc ? instance.instance_exec(&raw) : raw next if absorbed.nil? if .keyed? parsed = .value_class.parse(key, absorbed, implied: implied, component_id: instance_id(instance)) draft.public_send(:"add_#{kind}", parsed) else entry = resolve_absorb_alias(kind, [key, absorbed], alias_map) parsed = parse_entry(, entry, implied: implied, component_id: instance_id(instance)) draft.public_send(:"add_#{kind}", parsed) if parsed end end in Array => a a.each do |entry| is_proc = entry.is_a?(Proc) next unless phase_allows?(is_proc, phase) parsed = absorb_one(, entry, instance, implied, kind:, alias_map:) draft.public_send(:"add_#{kind}", parsed) if parsed end else is_proc = input.is_a?(Proc) return unless phase_allows?(is_proc, phase) parsed = absorb_one(, input, instance, implied, kind:, alias_map:) draft.public_send(:"add_#{kind}", parsed) if parsed end end |
.absorb_one(kind_meta, entry, instance, implied, kind: nil, alias_map: {}) ⇒ Object
244 245 246 247 248 249 250 251 |
# File 'lib/vident/internals/resolver.rb', line 244 def absorb_one(, entry, instance, implied, kind: nil, alias_map: {}) entry = instance.instance_exec(&entry) if entry.is_a?(Proc) return nil if entry.nil? return entry if entry.is_a?(.value_class) entry = resolve_absorb_alias(kind, entry, alias_map) if kind parse_entry(, entry, implied: implied, component_id: instance_id(instance)) end |
.absorb_root_element_attributes(draft, instance, implied, phase:, alias_map: {}) ⇒ Object
176 177 178 179 180 181 182 183 184 185 186 187 188 |
# File 'lib/vident/internals/resolver.rb', line 176 def absorb_root_element_attributes(draft, instance, implied, phase:, alias_map: {}) return unless instance.respond_to?(:resolved_root_element_attributes, true) attrs = instance.send(:resolved_root_element_attributes) return unless attrs.is_a?(Hash) && !attrs.empty? absorb_input(draft, :controllers, attrs[:stimulus_controllers], instance, implied, phase:, alias_map:) absorb_input(draft, :actions, attrs[:stimulus_actions], instance, implied, phase:, alias_map:) absorb_input(draft, :targets, attrs[:stimulus_targets], instance, implied, phase:, alias_map:) absorb_input(draft, :outlets, attrs[:stimulus_outlets], instance, implied, phase:, alias_map:) absorb_input(draft, :values, attrs[:stimulus_values], instance, implied, phase:, alias_map:) absorb_input(draft, :params, attrs[:stimulus_params], instance, implied, phase:, alias_map:) absorb_input(draft, :class_maps, attrs[:stimulus_classes], instance, implied, phase:, alias_map:) end |
.absorb_stimulus_props(draft, instance, implied, phase:, alias_map: {}) ⇒ Object
166 167 168 169 170 171 172 173 174 |
# File 'lib/vident/internals/resolver.rb', line 166 def absorb_stimulus_props(draft, instance, implied, phase:, alias_map: {}) absorb_input(draft, :controllers, instance_ivar(instance, :@stimulus_controllers), instance, implied, phase:, alias_map:) absorb_input(draft, :actions, instance_ivar(instance, :@stimulus_actions), instance, implied, phase:, alias_map:) absorb_input(draft, :targets, instance_ivar(instance, :@stimulus_targets), instance, implied, phase:, alias_map:) absorb_input(draft, :outlets, instance_ivar(instance, :@stimulus_outlets), instance, implied, phase:, alias_map:) absorb_input(draft, :values, instance_ivar(instance, :@stimulus_values), instance, implied, phase:, alias_map:) absorb_input(draft, :params, instance_ivar(instance, :@stimulus_params), instance, implied, phase:, alias_map:) absorb_input(draft, :class_maps, instance_ivar(instance, :@stimulus_classes), instance, implied, phase:, alias_map:) end |
.build_alias_map(declarations) ⇒ Object
38 39 40 41 42 43 44 45 46 47 48 |
# File 'lib/vident/internals/resolver.rb', line 38 def build_alias_map(declarations) map = {} declarations.controllers.each do |decl| alias_name = decl.[:as] next unless alias_name raw_path = decl.args.first next if raw_path.nil? map[alias_name] = raw_path.to_s end map end |
.build_implied_controller(instance) ⇒ Object
50 51 52 53 54 |
# File 'lib/vident/internals/resolver.rb', line 50 def build_implied_controller(instance) path = instance.class.stimulus_identifier_path name = instance.class.stimulus_identifier ::Vident::Stimulus::Controller.new(path: path, name: name) end |
.call(declarations, instance, phase: :all) ⇒ Object
14 15 16 17 18 19 20 21 22 23 24 25 26 27 |
# File 'lib/vident/internals/resolver.rb', line 14 def call(declarations, instance, phase: :all) raise ArgumentError, "use resolve_procs_into for phase: :procs" if phase == :procs draft = Draft.new implied = build_implied_controller(instance) alias_map = build_alias_map(declarations) seed_implied_controller(draft, instance) resolve_declarations(draft, declarations, instance, implied, phase:, alias_map:) absorb_stimulus_props(draft, instance, implied, phase:, alias_map:) absorb_root_element_attributes(draft, instance, implied, phase:, alias_map:) draft end |
.gated_out?(when_proc, instance) ⇒ Boolean
285 286 287 288 |
# File 'lib/vident/internals/resolver.rb', line 285 def gated_out?(when_proc, instance) return false unless when_proc !instance.instance_exec(&when_proc) end |
.instance_id(instance) ⇒ Object
Must match the mutation-API path (#id, memoised) so DSL outlet auto-selectors and runtime-added outlets scope identically.
305 306 307 |
# File 'lib/vident/internals/resolver.rb', line 305 def instance_id(instance) instance.id end |
.instance_ivar(instance, name) ⇒ Object
298 299 300 301 |
# File 'lib/vident/internals/resolver.rb', line 298 def instance_ivar(instance, name) return nil unless instance.instance_variable_defined?(name) instance.instance_variable_get(name) end |
.key_for_parse(key) ⇒ Object
294 |
# File 'lib/vident/internals/resolver.rb', line 294 def key_for_parse(key) = key |
.meta_for_controller(meta) ⇒ Object
296 |
# File 'lib/vident/internals/resolver.rb', line 296 def () = .slice(:as) |
.parse_entry(kind_meta, entry, implied:, component_id:) ⇒ Object
253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 |
# File 'lib/vident/internals/resolver.rb', line 253 def parse_entry(, entry, implied:, component_id:) case entry when Hash if .keyed? first_key, first_val = entry.first .value_class.parse(first_key, first_val, implied: implied, component_id: component_id) else .value_class.parse(entry, implied: implied, component_id: component_id) end when Array .value_class.parse(*entry, implied: implied, component_id: component_id) else .value_class.parse(entry, implied: implied, component_id: component_id) end end |
.parse_single(value_class, args, implied:, component_id:) ⇒ Object
290 291 292 |
# File 'lib/vident/internals/resolver.rb', line 290 def parse_single(value_class, args, implied:, component_id:) value_class.parse(*args, implied: implied, component_id: component_id) end |
.phase_allows?(is_proc, phase) ⇒ Boolean
Uses pattern matching so an unknown phase raises NoMatchingPatternError early.
236 237 238 239 240 241 242 |
# File 'lib/vident/internals/resolver.rb', line 236 def phase_allows?(is_proc, phase) case phase in :all then true in :static then !is_proc in :procs then is_proc end end |
.phase_matches?(decl, phase) ⇒ Boolean
160 161 162 163 164 |
# File 'lib/vident/internals/resolver.rb', line 160 def phase_matches?(decl, phase) return true if phase == :all has_proc = decl.when_proc || decl.args.any? { |a| a.is_a?(Proc) } (phase == :procs) ? has_proc : !has_proc end |
.read_prop(instance, name) ⇒ Object
309 310 311 312 313 |
# File 'lib/vident/internals/resolver.rb', line 309 def read_prop(instance, name) ivar = :"@#{name}" return nil unless instance.instance_variable_defined?(ivar) instance.instance_variable_get(ivar) end |
.resolve_absorb_alias(kind, entry, alias_map) ⇒ Object
225 226 227 228 229 230 231 232 233 |
# File 'lib/vident/internals/resolver.rb', line 225 def resolve_absorb_alias(kind, entry, alias_map) return entry unless kind == :actions && alias_map.any? return entry unless entry.is_a?(Hash) && entry[:controller].is_a?(Symbol) sym = entry[:controller] unless alias_map.key?(sym) raise ::Vident::DeclarationError, "Unknown controller alias :#{sym} in stimulus_actions input. Declared aliases: #{alias_map.keys.inspect}" end entry.merge(controller: alias_map[sym]) end |
.resolve_action_aliases(args, alias_map) ⇒ Object
Unknown aliases raise — a symbolic controller ref is declared intent, not a guess.
85 86 87 88 89 90 91 92 93 94 95 |
# File 'lib/vident/internals/resolver.rb', line 85 def resolve_action_aliases(args, alias_map) return args if alias_map.empty? args.map do |arg| next arg unless arg.is_a?(Hash) && arg[:controller].is_a?(Symbol) sym = arg[:controller] unless alias_map.key?(sym) raise ::Vident::DeclarationError, "Unknown controller alias :#{sym} in action. Declared aliases: #{alias_map.keys.inspect}" end arg.merge(controller: alias_map[sym]) end end |
.resolve_args(args, instance) ⇒ Object
Only nil drops — false, blank strings, and empty collections reach the parser.
270 271 272 273 274 |
# File 'lib/vident/internals/resolver.rb', line 270 def resolve_args(args, instance) resolved = args.map { |arg| arg.is_a?(Proc) ? instance.instance_exec(&arg) : arg } return nil if resolved.any?(&:nil?) resolved end |
.resolve_declarations(draft, declarations, instance, implied, phase:, alias_map: {}) ⇒ Object
61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 |
# File 'lib/vident/internals/resolver.rb', line 61 def resolve_declarations(draft, declarations, instance, implied, phase:, alias_map: {}) resolve_positional(draft, :controllers, declarations.controllers, instance, phase:) do |args, , _inst| ::Vident::Stimulus::Controller.parse(*args, implied: implied, **()) end resolve_positional(draft, :actions, declarations.actions, instance, phase:) do |args, , _inst| parse_single(::Vident::Stimulus::Action, resolve_action_aliases(args, alias_map), implied: implied, component_id: instance_id(instance)) end resolve_positional(draft, :targets, declarations.targets, instance, phase:) do |args, , _inst| parse_single(::Vident::Stimulus::Target, args, implied: implied, component_id: instance_id(instance)) end resolve_keyed(draft, :outlets, declarations.outlets, instance, phase:) do |key, args, | parsed_args = [key_for_parse(key), *args] parse_single(::Vident::Stimulus::Outlet, parsed_args, implied: implied, component_id: instance_id(instance)) end resolve_keyed_values(draft, declarations, instance, implied, phase:) resolve_keyed_scalars(draft, :params, declarations.params, instance, implied, ::Vident::Stimulus::Param, phase:) resolve_keyed_scalars(draft, :class_maps, declarations.class_maps, instance, implied, ::Vident::Stimulus::ClassMap, phase:) end |
.resolve_keyed(draft, kind, entries, instance, phase:) ⇒ Object
113 114 115 116 117 118 119 120 121 122 |
# File 'lib/vident/internals/resolver.rb', line 113 def resolve_keyed(draft, kind, entries, instance, phase:) entries.each do |(key, decl)| next unless phase_matches?(decl, phase) next if gated_out?(decl.when_proc, instance) args = resolve_args(decl.args, instance) next if args.nil? parsed = yield(key, args, decl.) draft.public_send(:"add_#{kind}", parsed) if parsed end end |
.resolve_keyed_scalars(draft, kind, entries, instance, implied, value_class, phase:) ⇒ Object
150 151 152 153 154 155 156 157 158 |
# File 'lib/vident/internals/resolver.rb', line 150 def resolve_keyed_scalars(draft, kind, entries, instance, implied, value_class, phase:) entries.each do |(key, decl)| next unless phase_matches?(decl, phase) next if gated_out?(decl.when_proc, instance) raw = (decl, instance) next if raw.nil? draft.public_send(:"add_#{kind}", value_class.parse(key, raw, implied: implied)) end end |
.resolve_keyed_values(draft, declarations, instance, implied, phase:) ⇒ Object
124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 |
# File 'lib/vident/internals/resolver.rb', line 124 def resolve_keyed_values(draft, declarations, instance, implied, phase:) declarations.values.each do |(key, decl)| next unless phase_matches?(decl, phase) next if gated_out?(decl.when_proc, instance) if decl.[:from_prop] raw = read_prop(instance, key) next if raw.nil? draft.add_values(::Vident::Stimulus::Value.parse(key, raw, implied: implied)) next end raw = (decl, instance) next if raw.nil? draft.add_values(::Vident::Stimulus::Value.parse(key, raw, implied: implied)) end # values_from_props has no when_proc, so phase_matches? doesn't apply; skip on :procs pass. return if phase == :procs declarations.values_from_props.each do |name| raw = read_prop(instance, name) next if raw.nil? draft.add_values(::Vident::Stimulus::Value.parse(name, raw, implied: implied)) end end |
.resolve_positional(draft, kind, entries, instance, phase:) ⇒ Object
97 98 99 100 101 102 103 104 105 106 107 |
# File 'lib/vident/internals/resolver.rb', line 97 def resolve_positional(draft, kind, entries, instance, phase:) entries.each do |decl| next unless phase_matches?(decl, phase) next if gated_out?(decl.when_proc, instance) args = resolve_args(decl.args, instance) next if args.nil? args = splat_single_array(args) parsed = yield(args, decl., instance) draft.public_send(:"add_#{kind}", parsed) if parsed end end |
.resolve_procs_into(draft, declarations, instance) ⇒ Object
29 30 31 32 33 34 35 36 |
# File 'lib/vident/internals/resolver.rb', line 29 def resolve_procs_into(draft, declarations, instance) implied = build_implied_controller(instance) alias_map = build_alias_map(declarations) resolve_declarations(draft, declarations, instance, implied, phase: :procs, alias_map:) absorb_stimulus_props(draft, instance, implied, phase: :procs, alias_map:) absorb_root_element_attributes(draft, instance, implied, phase: :procs, alias_map:) draft end |
.resolve_value_meta(decl, instance) ⇒ Object
276 277 278 279 280 281 282 283 |
# File 'lib/vident/internals/resolver.rb', line 276 def (decl, instance) return decl.[:static] if decl..key?(:static) return nil if decl.args.empty? raw = decl.args.first raw = instance.instance_exec(&raw) if raw.is_a?(Proc) raw end |
.seed_implied_controller(draft, instance) ⇒ Object
56 57 58 59 |
# File 'lib/vident/internals/resolver.rb', line 56 def seed_implied_controller(draft, instance) return unless instance.class.stimulus_controller? draft.add_controllers(build_implied_controller(instance)) end |
.splat_single_array(args) ⇒ Object
109 110 111 |
# File 'lib/vident/internals/resolver.rb', line 109 def splat_single_array(args) (args.size == 1 && args[0].is_a?(Array)) ? args[0] : args end |