#!/usr/bin/env ruby # frozen_string_literal: true # encoding=utf-8 $pd = false class Poly # attr_reader :table def initialize(table = {}) @table = table.tap { |ret| pp [__LINE__, 'Poly.initialize()', 'table', table.to_yaml] if $pd } end def fetch(key, *args) key_sym = key.to_sym if respond_to?("get_#{key}") send("get_#{key}") elsif @table.key?(key_sym) @table[key_sym] elsif block_given? yield key_sym elsif args.count.positive? # binding.irb args.first else binding.irb raise KeyError, "key not found: #{key}" end.tap { |ret| pp([__LINE__, "Poly.fetch #{key} #{args}", '->', ret]) if $pd } end def key?(name) @table.key?(name.to_sym).tap { |ret| pp([__LINE__, "Poly.key? #{name}", '->', ret]) if $pd } end def method_missing(name, *args) pt = nil if name.to_s.end_with?('=') # Setter method attribute = name.to_s.chomp('=').to_sym value = args.first if respond_to?("set_#{attribute}") pt = 'send set_' send("set_#{attribute}", value) else pt = 'table set' @table[attribute] = value end elsif respond_to?("get_#{name}") pt = 'send get_' # Getter method send("get_#{name}") elsif @table.respond_to?(name) pt = 'send name' @table.send(name, *args) else pt = 'table read' @table[name.to_sym] end.tap { |ret| pp([__LINE__, "Poly.method_missing #{name} #{args.map(&:to_s).join(' ')}", pt, '->', ret]) if $pd } end def respond_to_missing?(name, include_private = false) # name.to_s.end_with?('=') || @table.key?(name.to_sym) || @table.respond_to?(name) || super (name.to_s.end_with?('=') || @table.key?(name.to_sym) || @table.respond_to?(name) || super).tap { |ret| pp([__LINE__, "Poly.respond_to_missing? #{name}", '->', ret]) if $pd } end def [](key) if respond_to?("get_#{key}") send("get_#{key}") else @table[key.to_sym] end.tap { |ret| pp([__LINE__, "Poly.[] #{key}", '->', ret]) if $pd } end def []=(key, value) if respond_to?("set_#{key}") send("set_#{key}", value) else @table[key.to_sym] = value end.tap { |ret| pp([__LINE__, "Poly.[]= #{key} #{value}", '->', ret]) if $pd } end # for export to Prompt library # def merge(*args) # Proc.new { |x| @table.merge x } # end def merge(*args) # pp caller # binding.irb @table.merge(*args).tap { |ret| pp([__LINE__, "Poly.merge", '->', ret]) if $pd } end # for export to Prompt library def to_h @table.tap { |ret| pp([__LINE__, "Poly.to_h", '->', ret]) if $pd } end def to_yaml @table.to_yaml.tap { |ret| pp([__LINE__, "Poly.to_yaml", '->', ret]) if $pd } end end # class CustomStruct < Poly # # Custom setter for virtual attribute :full_name # def set_full_name(value) # names = value.split(' ') # @table[:first_name] = names.first # @table[:last_name] = names.last # end # # Custom getter for virtual attribute :full_name # def get_full_name # "#{@table[:first_name]} #{@table[:last_name]}" # end # end # # Example usage # person = CustomStruct.new # person.first_name = 'John' # person.last_name = 'Doe' # puts person.first_name # => John # puts person.last_name # => Doe # # Setting and getting a virtual attribute # person.full_name = 'Jane Smith' # puts person.first_name # => Jane # puts person.last_name # => Smith # puts person.full_name # => Jane Smith # # Setting and getting a regular attribute # person.age = 30 # puts person.age # => 30 # # Using array notation # person[:age] = 35 # puts person[:age] # => 35 # person[:full_name] = 'Alice Johnson' # puts person[:first_name] # => Alice # puts person[:last_name] # => Johnson # puts person[:full_name] # => Alice Johnson # # Using fetch method # puts person.fetch(:age) # => 35 # puts person.fetch(:nonexistent, 'default') # => default # puts person.fetch(:nonexistent) { |key| "block default for #{key}" } # => block default for nonexistent # # This will raise a KeyError # begin # person.fetch(:nonexistent) # rescue KeyError => e # puts e.message # => key not found: nonexistent # end