123456789_123456789_123456789_123456789_123456789_

Class: Net::IMAP::DataLite

Relationships & Source Files
Inherits: Object
Defined in: lib/net/imap/data_lite.rb,
lib/net/imap/data_lite.rb

Overview

DataLite is a temporary substitute for ruby 3.2’s Data class. DataLite is aliased as Data, so that code using it won’t need to be updated when it is removed.

See ruby 3.2’s documentation for Data.

When running ruby 3.1

This class reimplements the API for ruby 3.2’s Data, and should be compatible for nearly all use-cases. This reimplementation will be removed in net-imap 0.6, when support for ruby 3.1 is dropped.

NOTE: net-imap no longer supports ruby versions prior to 3.1.

When running ruby >= 3.2

This class inherits from Data and only defines the methods needed for YAML serialization. This will be dropped when psych adds support for Data.

Some of the code in this class was copied or adapted from the polyfill-data gem, by Jim Gay and Joel Drapper, under the MIT license terms.

Constant Summary

Class Method Summary

Instance Method Summary

Class Method Details

.define(*args, &block)

Defines a new Data class.

NOTE: Unlike ruby 3.2’s Data.define, .define only supports member names which are valid local variable names. Member names can’t be keywords (e.g: next or class) or start with capital letters, “@”, etc.

[ GitHub ]

  
# File 'lib/net/imap/data_lite.rb', line 81

def self.define(*args, &block)
  members = args.each_with_object({}) do |arg, members|
    arg = arg.to_str unless arg in Symbol | String if arg.respond_to?(:to_str)
    arg = arg.to_sym if     arg in String
    arg in Symbol     or  raise TypeError,     TYPE_ERROR    % [arg]
    arg in %r{=}      and raise ArgumentError, ATTRSET_ERROR % [arg]
    members.key?(arg) and raise ArgumentError, DUP_ERROR     % [arg]
    members[arg] = true
  end
  members = members.keys.freeze

  klass = ::Class.new(self)

  klass.singleton_class.undef_method :define
  klass.define_singleton_method(:members) { members }

  def klass.new(*args, **kwargs, &block)
    if kwargs.size.positive?
      if args.size.positive?
        raise ArgumentError, ARITY_ERROR % [args.size, 0]
      end
    elsif members.size < args.size
      expected = members.size.zero? ? 0 : 0..members.size
      raise ArgumentError, ARITY_ERROR % [args.size, expected]
    else
      kwargs = Hash[members.take(args.size).zip(args)]
    end
    allocate.tap do |instance|
      instance.__send__(:initialize, **kwargs, &block)
    end.freeze
  end

  klass.singleton_class.alias_method :[], :new
  klass.attr_reader(*members)

  # Dynamically defined initializer methods are in an included module,
  # rather than directly on DataLite (like in ruby 3.2+):
  # * simpler to handle required kwarg ArgumentErrors
  # * easier to ensure consistent ivar assignment order (object shape)
  # * faster than instance_variable_set
  klass.include(Module.new do
    if members.any?
      kwargs = members.map{"#{_1.name}:"}.join(", ")
      params = members.map(&:name).join(", ")
      ivars  = members.map{"@#{_1.name}"}.join(", ")
      attrs  = members.map{"attrs[:#{_1.name}]"}.join(", ")
      module_eval <<~RUBY, __FILE__, __LINE__ + 1
        protected
        def initialize(#{kwargs}) #{ivars} = #{params}; freeze end
        def marshal_load(attrs)   #{ivars} = #{attrs};  freeze end
      RUBY
    end
  end)

  klass.module_eval do _1.module_eval(&block) end if block_given?

  klass
end

Instance Method Details

#==(other)

[ GitHub ]

  
# File 'lib/net/imap/data_lite.rb', line 164

def ==(other)    self.class == other.class && to_h == other.to_h end

#__inspect_guard__(obj) (private)

Yields true if obj has been seen already, false if it hasn’t. Marks obj as seen inside the block, so circuler references don’t recursively trigger a SystemStackError (stack level too deep).

Making circular references inside a Data object should be very uncommon, but we’ll support them for the sake of completeness.

[ GitHub ]

  
# File 'lib/net/imap/data_lite.rb', line 202

def __inspect_guard__(obj)
  preexisting = Thread.current[:__net_imap_data__inspect__]
  Thread.current[:__net_imap_data__inspect__] ||= {}.compare_by_identity
  inspect_guard = Thread.current[:__net_imap_data__inspect__]
  if inspect_guard.include?(obj)
    yield true
  else
    begin
      inspect_guard[obj] = true
      yield false
    ensure
      inspect_guard.delete(obj)
    end
  end
ensure
  unless preexisting.equal?(inspect_guard)
    Thread.current[:__net_imap_data__inspect__] = preexisting
  end
end

#__to_h__ (private)

[ GitHub ]

  
# File 'lib/net/imap/data_lite.rb', line 194

def __to_h__; Hash[members.map {|m| [m, send(m)] }] end

#deconstruct

[ GitHub ]

  
# File 'lib/net/imap/data_lite.rb', line 166

def deconstruct; __to_h__.values                                 end

#deconstruct_keys(keys)

Raises:

  • (TypeError)
[ GitHub ]

  
# File 'lib/net/imap/data_lite.rb', line 168

def deconstruct_keys(keys)
  raise TypeError unless keys.is_a?(Array) || keys.nil?
  return __to_h__ if keys&.first.nil?
  __to_h__.slice(*keys)
end

#encode_with(coder)

[ GitHub ]

  
# File 'lib/net/imap/data_lite.rb', line 32

def encode_with(coder) coder.map = to_h.transform_keys(&:to_s)        end

#eql?(other) ⇒ Boolean

[ GitHub ]

  
# File 'lib/net/imap/data_lite.rb', line 165

def eql?(other)  self.class == other.class && hash == other.hash end

#hash

[ GitHub ]

  
# File 'lib/net/imap/data_lite.rb', line 163

def hash;        [self.class, __to_h__].hash                     end

#init_with(coder)

[ GitHub ]

  
# File 'lib/net/imap/data_lite.rb', line 33

def init_with(coder) initialize(**coder.map.transform_keys(&:to_sym)) end

#initialize_copy(source) (private)

[ GitHub ]

  
# File 'lib/net/imap/data_lite.rb', line 191

def initialize_copy(source) super.freeze end

#inspect Also known as: #to_s

[ GitHub ]

  
# File 'lib/net/imap/data_lite.rb', line 179

def inspect
  __inspect_guard__(self) do |seen|
    return "#<data #{self.class}:...>" if seen
    attrs = __to_h__.map {|kv| "%s=%p" % kv }.join(", ")
    display = ["data", self.class.name, attrs].compact.join(" ")
    "#<#{display}>"
  end
end

#marshal_dump (private)

[ GitHub ]

  
# File 'lib/net/imap/data_lite.rb', line 192

def marshal_dump; __to_h__ end

#members

[ GitHub ]

  
# File 'lib/net/imap/data_lite.rb', line 161

def members;     self.class.members                              end

#to_h(&block)

[ GitHub ]

  
# File 'lib/net/imap/data_lite.rb', line 162

def to_h(&block) block ? __to_h__.to_h(&block) : __to_h__        end

#to_s

Alias for #inspect.

[ GitHub ]

  
# File 'lib/net/imap/data_lite.rb', line 187

alias_method :to_s, :inspect

#with(**kwargs)

[ GitHub ]

  
# File 'lib/net/imap/data_lite.rb', line 174

def with(**kwargs)
  return self if kwargs.empty?
  self.class.new(**__to_h__.merge(kwargs))
end