class Net::IMAP::DataLite
DataLite
is a temporary substitute for ruby 3.2’s Data
class. DataLite
is aliased as Net::IMAP::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 innet-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 whenpsych
adds support forData
.
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.
Constants
- ARITY_ERROR
- ATTRSET_ERROR
- DUP_ERROR
- TYPE_ERROR
Public Class Methods
Defines a new Data
class.
NOTE: Unlike ruby 3.2’s Data.define
, DataLite.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.
# File net-imap-0.5.4/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
Public Instance Methods
# File net-imap-0.5.4/lib/net/imap/data_lite.rb, line 165 def ==(other) self.class == other.class && to_h == other.to_h end
# File net-imap-0.5.4/lib/net/imap/data_lite.rb, line 162 def attributes; Hash[members.map {|m| [m, send(m)] }] end
# File net-imap-0.5.4/lib/net/imap/data_lite.rb, line 167 def deconstruct; attributes.values end
# File net-imap-0.5.4/lib/net/imap/data_lite.rb, line 169 def deconstruct_keys(keys) raise TypeError unless keys.is_a?(Array) || keys.nil? return attributes if keys&.first.nil? attributes.slice(*keys) end
# File net-imap-0.5.4/lib/net/imap/data_lite.rb, line 32 def encode_with(coder) coder.map = attributes.transform_keys(&:to_s) end
# File net-imap-0.5.4/lib/net/imap/data_lite.rb, line 166 def eql?(other) self.class == other.class && hash == other.hash end
# File net-imap-0.5.4/lib/net/imap/data_lite.rb, line 164 def hash; [self.class, attributes].hash end
# File net-imap-0.5.4/lib/net/imap/data_lite.rb, line 33 def init_with(coder) initialize(**coder.map.transform_keys(&:to_sym)) end
# File net-imap-0.5.4/lib/net/imap/data_lite.rb, line 180 def inspect __inspect_guard__(self) do |seen| return "#<data #{self.class}:...>" if seen attrs = attributes.map {|kv| "%s=%p" % kv }.join(", ") display = ["data", self.class.name, attrs].compact.join(" ") "#<#{display}>" end end
# File net-imap-0.5.4/lib/net/imap/data_lite.rb, line 161 def members; self.class.members end
# File net-imap-0.5.4/lib/net/imap/data_lite.rb, line 163 def to_h(&block) attributes.to_h(&block) end
# File net-imap-0.5.4/lib/net/imap/data_lite.rb, line 175 def with(**kwargs) return self if kwargs.empty? self.class.new(**attributes.merge(kwargs)) end
Private Instance Methods
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.
# File net-imap-0.5.4/lib/net/imap/data_lite.rb, line 201 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
# File net-imap-0.5.4/lib/net/imap/data_lite.rb, line 192 def initialize_copy(source) super.freeze end
# File net-imap-0.5.4/lib/net/imap/data_lite.rb, line 193 def marshal_dump; attributes end