class Data

Private Class Methods

define(*members, &block) click to toggle source
# File lib/backports/3.2.0/data.rb, line 30
    def self.define(*members, &block)
      members.each do |m|
        raise TypeError, "#{m} is not a Symbol" unless m.is_a?(Symbol) || m.is_a?(String)
        raise ArgumentError, "invalid data member: #{m}" if m.end_with?("=")
      end
      members = members.map(&:to_sym)
      raise ArgumentError, "duplicate members" if members.uniq!

      klass = instance_eval <<-"end_define", __FILE__, __LINE__ + 1
        Class.new(::Backports::Data) do     # Class.new(::Data) do
          def self.members       #   def self.members
            #{members.inspect}   #     [:a_member, :another_member]
          end                    #   end
        end                      # end
      end_define

      members.each do |m|
        klass.define_method(m) { @__members__[m]}
      end

      class << klass
        def new(*values, **named_values)
          if named_values.empty?
            if values.size > members.size
              raise ArgumentError, "wrong number of arguments (given #{values.size}, expected 0..#{members.size})"
            end
            super(**members.first(values.size).zip(values).to_h)
          else
            unless values.empty?
              raise ArgumentError, "wrong number of arguments (given #{values.size}, expected 0)"
            end
            super(**named_values)
          end
        end
        undef :define
      end

      klass.class_eval(&block) if block

      klass
    end
new(**named_values) click to toggle source
# File lib/backports/3.2.0/data.rb, line 88
def initialize(**named_values)
  given = named_values.keys
  missing = members - given
  unless missing.empty?
    missing = missing.map(&:inspect).join(", ")
    raise ArgumentError, "missing keywords: #{missing}"
  end
  if members.size < given.size
    extra = (given - members).map(&:inspect).join(", ")
    raise ArgumentError, "unknown keywords: #{extra}"
  end
  @__members__ = named_values.freeze
  freeze
end

Private Instance Methods

==(other) click to toggle source
# File lib/backports/3.2.0/data.rb, line 78
def ==(other)
  return false unless other.instance_of?(self.class)

  @__members__ == other.to_h
end
deconstruct() click to toggle source
# File lib/backports/3.2.0/data.rb, line 19
def deconstruct
  @__members__.values
end
deconstruct_keys(keys_or_nil) click to toggle source
# File lib/backports/3.2.0/data.rb, line 23
def deconstruct_keys(keys_or_nil)
  return @__members__ unless keys_or_nil

  raise TypeError, "Expected symbols" unless keys_or_nil.is_a?(Array) && keys_or_nil.all? {|s| s.is_a?(Symbol)}
  @__members__.slice(*keys_or_nil)
end
eql?(other) click to toggle source
# File lib/backports/3.2.0/data.rb, line 72
def eql?(other)
  return false unless other.instance_of?(self.class)

  @__members__.eql?(other.to_h)
end
hash() click to toggle source
# File lib/backports/3.2.0/data.rb, line 84
def hash
  @__members__.hash
end
initialize_copy(other) click to toggle source

Why is ‘initialize_copy` specialized in MRI and not just `initialize_dup`? Let’s follow the pattern anyways

# File lib/backports/3.2.0/data.rb, line 105
def initialize_copy(other)
  @__members__ = other.to_h
  freeze
end
inspect() click to toggle source
# File lib/backports/3.2.0/data.rb, line 110
def inspect
  data = @__members__.map {|k, v| "#{k}=#{v.inspect}"}.join(", ")
  space = data != "" && self.class.name ? " " : ""
  "#<data #{self.class.name}#{space}#{data}>"
end
marshal_dump() click to toggle source
# File lib/backports/3.2.0/data.rb, line 116
def marshal_dump
  @__members__
end
marshal_load(members) click to toggle source
# File lib/backports/3.2.0/data.rb, line 120
def marshal_load(members)
  @__members__ = members
  freeze
end
members() click to toggle source

class method defined in ‘define`

# File lib/backports/3.2.0/data.rb, line 126
def members
  self.class.members
end
to_h(&block) click to toggle source
# File lib/backports/3.2.0/data.rb, line 134
def to_h(&block)
  @__members__.to_h(&block)
end
with(**update) click to toggle source
# File lib/backports/3.2.0/data.rb, line 138
def with(**update)
  return self if update.empty?

  self.class.new(**@__members__.merge(update))
end