module JSI
module Util::Private
class AttrStruct
class AttrStructError < StandardError
end
class UndefinedAttributeKey < AttrStructError
end
class << self
def subclass(*attribute_keys)
bad = attribute_keys.reject { |key| key.respond_to?(:to_str) || key.is_a?(Symbol) }
unless bad.empty?
raise ArgumentError, "attribute keys must be String or Symbol; got keys: #{bad.map(&:inspect).join(', ')}"
end
attribute_keys = attribute_keys.map { |key| convert_key(key) }
all_attribute_keys = (self.attribute_keys + attribute_keys).freeze
Class.new(self).tap do |klass|
klass.define_singleton_method(:attribute_keys) { all_attribute_keys }
attribute_keys.each do |attribute_key|
klass.send(:define_method, attribute_key) do
@attributes[attribute_key]
end
klass.send(:define_method, "#{attribute_key}=") do |value|
@attributes[attribute_key] = value
end
end
end
end
alias_method :[], :subclass
def attribute_keys
Util::Private::EMPTY_SET
end
def convert_key(key)
key.is_a?(Symbol) ? key.to_s.freeze : key.frozen? ? key : key.is_a?(String) ? key.dup.freeze : key
end
end
def initialize(attributes = {})
unless attributes.respond_to?(:to_hash)
raise(TypeError, "expected attributes to be a Hash; got: #{attributes.inspect}")
end
@attributes = {}
attributes.to_hash.each do |k, v|
@attributes[self.class.convert_key(k)] = v
end
bad = @attributes.keys.reject { |k| attribute_keys.include?(k) }
unless bad.empty?
raise UndefinedAttributeKey, "undefined attribute keys: #{bad.map(&:inspect).join(', ')}"
end
end
def [](key)
@attributes[key.is_a?(Symbol) ? key.to_s : key]
end
def []=(key, value)
key = self.class.convert_key(key)
unless attribute_keys.include?(key)
raise UndefinedAttributeKey, "undefined attribute key: #{key.inspect}"
end
@attributes[key] = value
end
def inspect
-"\#<#{self.class.name}#{@attributes.map { |k, v| " #{k}: #{v.inspect}" }.join(',')}>"
end
alias_method :to_s, :inspect
def pretty_print(q)
q.text '#<'
q.text self.class.name
q.group_sub {
q.nest(2) {
q.breakable(@attributes.empty? ? '' : ' ')
q.seplist(@attributes, nil, :each_pair) { |k, v|
q.group {
q.text k
q.text ': '
q.pp v
}
}
}
}
q.breakable ''
q.text '>'
end
def attribute_keys
self.class.attribute_keys
end
include FingerprintHash
def jsi_fingerprint
{class: self.class, attributes: @attributes}
end
end
end
end