Files
news.gitea.happylizard.me/bin/gems/hashery-2.1.2/lib/hashery/open_hash.rb
T
pizza2d1 4cee170d66
Gitea Actions Demo / Explore-Gitea-Actions (push) Failing after 9s
Add bin and edit workflow
2026-09-16 13:11:16 -06:00

146 lines
3.5 KiB
Ruby

require 'hashery/crud_hash'
module Hashery
# OpenHash is a Hash, but also supports open properties much like
# OpenStruct.
#
# Only names that are name methods of Hash can be used as open slots.
# To open a slot for a name that would otherwise be a method, the
# method needs to be made private. The `#open!` method can be used
# to handle this.
#
# Examples
#
# o = OpenHash.new
# o.open!(:send)
# o.send = 4
#
class OpenHash < CRUDHash
alias :object_class :class
#FILTER = /(^__|^\W|^instance_|^object_|^to_)/
#methods = Hash.instance_methods(true).select{ |m| m !~ FILTER }
#methods = methods - [:each, :inspect, :send] # :class, :as]
#private *methods
#
# Initialize new OpenHash instance.
#
# TODO: Maybe `safe` should be the first argument?
#
def initialize(default=nil, safe=false, &block)
@safe = safe
super(*[default].compact, &block)
end
#
# If safe is set to true, then public methods cannot be overriden
# by hash keys.
#
attr_accessor :safe
#
# Alias to original store method.
#
#alias :store! :store
#
# Index `value` to `key`. Unless safe mode, will also open up the
# key if it is not already open.
#
# key - Index key to associate with value.
# value - Value to be associate with key.
#
# Returns +value+.
#
def store(key, value)
#open!(key)
super(key, value)
end
#
# Open up a slot that that would normally be a Hash method.
#
# The only methods that can't be opened are ones starting with `__`.
#
# methods - [Array<String,Symbol>] method names
#
# Returns Array of slot names that were opened.
#
def open!(*methods)
# Only select string and symbols, any other type of key is allowed,
# it just won't be accessible via dynamic methods.
methods = methods.select{ |x| String === x || Symbol === x }
if methods.any?{ |m| m.to_s.start_with?('__') }
raise ArgumentError, "cannot open shadow methods"
end
# only public methods need be made protected
methods = methods.map{ |x| x.to_sym }
methods = methods & public_methods(true).map{ |x| x.to_sym }
if @safe
raise ArgumentError, "cannot set public method" unless methods.empty?
else
(class << self; self; end).class_eval{ protected *methods }
end
methods
end
# @deprecated
alias :omit! :open!
#
# Is a slot open?
#
# method - [String,Symbol] method name
#
# Returns `true` or `false`.
#
def open?(method)
methods = public_methods(true).map{ |m| m.to_sym }
! methods.include?(method.to_sym)
end
#
# Make specific Hash methods available for use that have previously opened.
#
# methods - [Array<String,Symbol>] method names
#
# Returns +methods+.
#
def close!(*methods)
(class << self; self; end).class_eval{ public *methods }
methods
end
#
#
#
def method_missing(s,*a, &b)
type = s.to_s[-1,1]
name = s.to_s.sub(/[!?=]$/, '')
key = name.to_sym
case type
when '='
store(key, a.first)
when '?'
key?(key)
when '!'
# call an underlying private method
# TODO: limit this to omitted methods (from included) ?
__send__(name, *a, &b)
else
#if key?(key)
retrieve(key)
#else
# super(s,*a,&b)
#end
end
end
end
end