Module: Minitwin::Initialization
- Included in:
- Minitwin
- Defined in:
- lib/minitwin/initialization.rb,
sig/generated/minitwin/initialization.rbs
Overview
Instance construction and low-level helpers used by the DSL-generated accessors. Filters unknown keys on initialize and seeds nested block properties so that validations on nested twins can run.
Constant Summary collapse
- ALIASES_VAR =
Cache constant references for JIT optimization
Minitwin::DYNAMIC_ALIASES_VAR
- ALIASES_REV_VAR =
Minitwin::DYNAMIC_ALIASES_REV_VAR
- FORBIDDEN_ALIAS_NAMES =
Forbidden method names that should never be aliased for security reasons
%i[ eval instance_eval class_eval module_eval send __send__ public_send method_missing respond_to_missing? define_method remove_method undef_method instance_variable_get instance_variable_set instance_variables instance_variable_defined? const_get const_set class_variable_get class_variable_set binding tap then yield_self to_proc freeze __id__ object_id == equal? eql? hash <=> ].freeze
Instance Method Summary collapse
- #__apply_dynamic_alias__(target_method, alias_name) ⇒ Object
- #__compute_alias_name__(as_proc) ⇒ Object
- #__compute_nested_alias_name__(entry) ⇒ Object
- #__recompute_aliases_for_collection__(collection_method) ⇒ Object
-
#__recompute_dynamic_aliases__ ⇒ Object
Define or update per-instance alias methods for properties/collections where
as:was provided as a Proc. - #__recompute_nested_aliases__ ⇒ Object
- #assign_attribute(method:, value:) ⇒ Object
- #attribute_methods ⇒ Object
- #define_instance_variable(name:, value:) ⇒ Object
-
#dynamic_aliases ⇒ Object
Public: expose current dynamic aliases as a Hash of alias_name => target_method.
-
#initialize(**args) ⇒ instance
: (**untyped) -> instance.
Instance Method Details
#__apply_dynamic_alias__(target_method, alias_name) ⇒ Object
139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 |
# File 'lib/minitwin/initialization.rb', line 139 def __apply_dynamic_alias__(target_method, alias_name) unless alias_name.is_a?(String) || alias_name.is_a?(Symbol) raise ArgumentError, "Invalid alias name #{alias_name.inspect}: must be a String or Symbol" end alias_key = alias_name.to_sym aliases = instance_variable_get(ALIASES_VAR) aliases_rev = instance_variable_get(ALIASES_REV_VAR) # Security check: prevent aliasing to forbidden method names if FORBIDDEN_ALIAS_NAMES.include?(alias_key) raise ArgumentError, "Cannot define dynamic alias '#{alias_key}': forbidden method name for security reasons" end prev = aliases[target_method] if prev && prev != alias_key begin singleton_class.send(:remove_method, prev) rescue NameError # Expected: method may not exist if previously failed to define end aliases_rev.delete(prev) end # Collision checks: alias already used by another target or an existing method if aliases_rev.key?(alias_key) && aliases_rev[alias_key] != target_method raise ArgumentError, "Dynamic alias '#{alias_key}' already defined for '#{aliases_rev[alias_key]}'" end if respond_to?(alias_key, true) && aliases_rev[alias_key] != target_method raise ArgumentError, "Cannot define dynamic alias '#{alias_key}': method already exists" end # Define forwarding method on the singleton class singleton_class.send(:define_method, alias_key) do send(target_method) end aliases[target_method] = alias_key aliases_rev[alias_key] = target_method end |
#__compute_alias_name__(as_proc) ⇒ Object
121 122 123 124 125 126 127 |
# File 'lib/minitwin/initialization.rb', line 121 def __compute_alias_name__(as_proc) instance_exec(&as_proc) rescue StandardError # Expected: Dynamic alias proc may fail or return invalid names. # Return nil to skip this alias definition. nil end |
#__compute_nested_alias_name__(entry) ⇒ Object
129 130 131 132 133 134 135 136 137 |
# File 'lib/minitwin/initialization.rb', line 129 def __compute_nested_alias_name__(entry) obj = public_send(entry[:group]) obj = Minitwin::Utils.traverse_path(obj, entry[:path][0..-2]) obj.instance_exec(&entry[:as]) rescue StandardError # Expected: Nested path traversal or dynamic alias proc may fail. # Return nil to skip this alias definition. nil end |
#__recompute_aliases_for_collection__(collection_method) ⇒ Object
88 89 90 91 92 93 94 95 96 97 98 99 100 |
# File 'lib/minitwin/initialization.rb', line 88 def __recompute_aliases_for_collection__(collection_method) return unless self.class.respond_to?(collection_method) self.class.public_send(collection_method).each do |key, | = [:as] next unless .is_a?(Proc) alias_name = __compute_alias_name__() next if alias_name.nil? __apply_dynamic_alias__(key, alias_name) end end |
#__recompute_dynamic_aliases__ ⇒ Object
Define or update per-instance alias methods for properties/collections
where as: was provided as a Proc. The Proc is executed in the context
of the instance to compute the alias name.
76 77 78 79 80 81 82 83 84 85 86 |
# File 'lib/minitwin/initialization.rb', line 76 def __recompute_dynamic_aliases__ instance_variable_set(ALIASES_VAR, {}) unless instance_variable_defined?(ALIASES_VAR) instance_variable_set(ALIASES_REV_VAR, {}) unless instance_variable_defined?(ALIASES_REV_VAR) # Handle scalar properties and collections __recompute_aliases_for_collection__(:properties) __recompute_aliases_for_collection__(:collections) # Handle nested dynamic aliases (registered by DSL#nested) __recompute_nested_aliases__ end |
#__recompute_nested_aliases__ ⇒ Object
102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 |
# File 'lib/minitwin/initialization.rb', line 102 def __recompute_nested_aliases__ return unless self.class.respond_to?(:dynamic_nested_aliases) self.class.dynamic_nested_aliases.each do |entry| = entry[:as] target = entry[:target] if .is_a?(Proc) alias_name = __compute_nested_alias_name__(entry) next if alias_name.nil? __apply_dynamic_alias__(target, alias_name) else # Static alias recorded by nested to support protected inner readers __apply_dynamic_alias__(target, ) end end end |
#assign_attribute(method:, value:) ⇒ Object
57 58 59 60 61 62 63 |
# File 'lib/minitwin/initialization.rb', line 57 def assign_attribute(method:, value:) if respond_to?("#{method}=") send("#{method}=", value) else instance_variable_set("@#{method}", value) end end |
#attribute_methods ⇒ Object
65 66 67 |
# File 'lib/minitwin/initialization.rb', line 65 def attribute_methods self.class.send(:allowed_attribute_keys_array) end |
#define_instance_variable(name:, value:) ⇒ Object
69 70 71 |
# File 'lib/minitwin/initialization.rb', line 69 def define_instance_variable(name:, value:) instance_variable_set(Minitwin::Utils.ivar_name(name), value) end |
#dynamic_aliases ⇒ Object
Public: expose current dynamic aliases as a Hash of alias_name => target_method
182 183 184 185 186 |
# File 'lib/minitwin/initialization.rb', line 182 def dynamic_aliases return {} unless instance_variable_defined?(ALIASES_REV_VAR) && instance_variable_get(ALIASES_REV_VAR) instance_variable_get(ALIASES_REV_VAR).dup end |
#initialize(**args) ⇒ instance
: (**untyped) -> instance
29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 |
# File 'lib/minitwin/initialization.rb', line 29 def initialize(**args) allowed_keys = self.class.send(:allowed_attribute_keys) args.select! { |arg, _| allowed_keys.include?(arg.to_sym) } getter_defaults = {} self.class.block_properties.each do |method| getter_defaults[method] = {} if allowed_keys.include?(method) end attrs = getter_defaults.merge(args) # Skip per-setter alias recomputation during bulk init; recompute once after @__skip_alias_recompute__ = true if Minitwin.send(:active_model_initialized?, self.class) super(attrs) else # :nocov: (exercised only without ActiveModel; covered by subprocess test) attrs.each { |k, v| assign_attribute(method: k, value: v) } # :nocov: end @__skip_alias_recompute__ = false __recompute_dynamic_aliases__ if self.class.dynamic_aliases? end |