Class: Struct
Overview
Optional re-implmentation of Struct in Pure Ruby (to get around JS issues in Opal Struct)
Constant Summary collapse
- CLASS_DEFINITION_FOR_ATTRIBUTES =
lambda do |attributes, keyword_init| lambda do |defined_class| attributes.each do |attribute| define_method(attribute) do self[attribute] end define_method("#{attribute}=") do |value| self[attribute] = value end end define_method(:members) do (@members ||= attributes).clone end def []=(attribute, value) normalized_attribute = attribute.to_sym raise NameError, "no member #{attribute} in struct" unless @members.include?(normalized_attribute) @member_values[normalized_attribute] = value end def [](attribute) normalized_attribute = attribute.to_sym raise NameError, "no member #{attribute} in struct" unless @members.include?(normalized_attribute) @member_values[normalized_attribute] end def each(&block) to_a.each(&block) end def each_pair(&block) @member_values.each_pair(&block) end def to_h @member_values.clone end def to_a @member_values.values end def size @members.size end alias length size def dig(*args) @member_values.dig(*args) end def select(&block) to_a.select(&block) end def eql?(other) instance_of?(other.class) && @members.all? { |key| self[key].eql?(other[key]) } end def ==(other) other = coerce(other).first if respond_to?(:coerce, true) other.kind_of?(self.class) && @members.all? { |key| self[key] == other[key] } end def hash if RUBY_ENGINE == 'opal' # Opal doesn't implement hash as Integer everywhere, returning strings as themselves, # so build a String everywhere for now as the safest common denominator to be consistent. self.class.hash.to_s + to_a.each_with_index.map {|value, i| (i+1).to_s + value.hash.to_s}.reduce(:+) else self.class.hash + to_a.each_with_index.map {|value, i| i+1 * value.hash}.sum end end if keyword_init def initialize(struct_class_keyword_args = {}) members @member_values = {} struct_class_keyword_args.each do |attribute, value| self[attribute] = value end end else def initialize(*attribute_values) members @member_values = {} attribute_values.each_with_index do |value, i| attribute = @members[i] self[attribute] = value end end end end end
- ARG_VALIDATION =
lambda do |class_name_or_attribute, *attributes| class_name_or_attribute.nil? || attributes.any?(&:nil?) end
- CLASS_NAME_EXTRACTION =
lambda do |class_name_or_attribute| if class_name_or_attribute.is_a?(String) && RUBY_ENGINE != 'opal' raise NameError, "identifier name needs to be constant" unless class_name_or_attribute.match(/^[A-Z]/) class_name_or_attribute end end
Class Method Summary collapse
Class Method Details
.__new__ ⇒ Object
141 |
# File 'lib/pure-struct.rb', line 141 alias __new__ new |
.new(class_name_or_attribute, *attributes, keyword_init: false) ⇒ Object
142 143 144 145 146 147 148 149 150 |
# File 'lib/pure-struct.rb', line 142 def new(class_name_or_attribute, *attributes, keyword_init: false) raise 'Arguments cannot be nil' if ARG_VALIDATION[class_name_or_attribute, *attributes] class_name = CLASS_NAME_EXTRACTION[class_name_or_attribute] attributes.unshift(class_name_or_attribute) if class_name.nil? attributes = attributes.map(&:to_sym) struct_class = Class.new(self, &CLASS_DEFINITION_FOR_ATTRIBUTES[attributes, keyword_init]) struct_class.singleton_class.define_method(:new) {|*args, &block| __new__(*args, &block)} class_name.nil? ? struct_class : const_set(class_name, struct_class) end |