Class: Kernel::Maplet
Overview
Class that allows for dynamic definition of properties
Instance Method Summary collapse
-
#[](*prop) ⇒ Object
Accesses a prop’s value, allowing for nested access.
-
#define!(*args, &block) ⇒ self
Dynamically defines properties based on the provided arguments and/or block.
-
#each {|value, path| ... } ⇒ self, Enumerator
Iterates over each leaf property of the Maplet.
-
#initialize ⇒ Maplet
constructor
A new instance of Maplet.
-
#map(*only) {|value, prop| ... } ⇒ Maplet
Creates a new Maplet by applying a block to each property’s value.
-
#to_h ⇒ Hash{Symbol => Object}
Converts the Maplet and any nested Maplets back into a Hash.
-
#without(*props) ⇒ Maplet
Returns a new Maplet that excludes the specified top-level properties.
Constructor Details
#initialize ⇒ Maplet
126 127 128 |
# File 'lib/patch/let.rb', line 126 def initialize @props = [] end |
Instance Method Details
#[](*prop) ⇒ Object
Accesses a prop’s value, allowing for nested access.
260 261 262 263 264 265 266 267 268 269 270 271 272 273 |
# File 'lib/patch/let.rb', line 260 def [](*prop) raise ArgumentError, "No property specified." if prop.empty? value = instance_variable_get("@#{prop.first}") if prop.size == 1 value else value[*prop[1..]] end rescue NameError error = "Property '#{prop.join ?.}' is not defined in this Maplet." error += " Available properties: [#{@props.join(", ")}]" raise ArgumentError, error end |
#define!(*args, &block) ⇒ self
Dynamically defines properties based on the provided arguments and/or block.
Arguments can be Hashes or “hashy” Arrays (arrays of ‘[key, value]` pairs). If a block is given, its local variables are also used to define methods. Keys are converted to symbols and validated as method names.
If a value is a Hash or a “hashy” Array, it’s recursively used to define nested properties.
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 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 |
# File 'lib/patch/let.rb', line 190 def define!(*args, &block) # Stores all key-value pairs to be defined variable = {} # Process Hashes and "hashy" Arrays first args.each do |arg| case arg when Hash variable.merge!(arg) when Array raise ArgumentError, "Array should be Hash like." unless arg.hashy? variable.merge!(arg.to_h) else raise ArgumentError, "Invalid argument type: #{arg.class}" end end # Process local variables from the block if block_given? binding = block.call # The block is expected to return its binding. raise ArgumentError, "Block must return a Binding object." unless binding.is_a?(Binding) variable.merge!(binding.variables) end # Define getters and setters and store values variable.each do |prop, value| prop = prop.to_s.to_sym raise ArgumentError, "Invalid name: #{prop}" unless prop.to_s.valid_name? # Recursively define for nested Hashes or "hashy" Arrays if value.is_a? Hash value = Maplet.new.define!(value) elsif value.is_a?(Array) && value.hashy? value = Maplet.new.define!(value.to_h) end # Store the original value in an instance variable instance_variable_set("@#{prop}", value) define_singleton_method(prop) do instance_variable_get("@#{prop}") end define_singleton_method("#{prop}=") do |value| instance_variable_set("@#{prop}", value) end end @props += variable.keys.map(&:to_sym) self end |
#each {|value, path| ... } ⇒ self, Enumerator
Iterates over each leaf property of the Maplet.
310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 |
# File 'lib/patch/let.rb', line 310 def each(&block) return enum_for(:each) unless block_given? tap do @props.each do |prop| value = self[prop] if value.is_a?(Maplet) value.each do |inner, nested| yield inner, "#{prop}.#{nested}" end else yield value, prop.to_s end end end end |
#map(*only) {|value, prop| ... } ⇒ Maplet
Creates a new Maplet by applying a block to each property’s value. You can transform all properties or just a select few.
349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 |
# File 'lib/patch/let.rb', line 349 def map(*only, &block) return enum_for(:map) unless block_given? hash = {} @props.each do |prop| value = self[prop] if value.is_a?(Maplet) hash[prop] = value.map(*only, &block) elsif only.empty? hash[prop] = yield(value, prop) elsif only.any? { |p| p.to_sym == prop } hash[prop] = yield(value, prop) else hash[prop] = value end end Maplet.new.define!(hash) end |
#to_h ⇒ Hash{Symbol => Object}
Converts the Maplet and any nested Maplets back into a Hash. Recursively transforms inner maplets into nested Hashes.
283 284 285 286 287 288 289 290 291 292 293 |
# File 'lib/patch/let.rb', line 283 def to_h @props.each_with_object({}) do |prop, hash| value = self[prop] if value.is_a?(Maplet) hash[prop] = value.to_h else hash[prop] = value end end end |
#without(*props) ⇒ Maplet
Returns a new Maplet that excludes the specified top-level properties. Note: This only works on top-level properties.
380 381 382 383 384 385 386 387 |
# File 'lib/patch/let.rb', line 380 def without(*props) return self if props.empty? remaining = to_h props.each { |p| remaining.delete(p.to_sym) } Maplet.new.define!(remaining) end |