Class: Net::IMAP::DataLite

Inherits:
Object
  • Object
show all
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 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 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.

Class Method Summary collapse

Instance Method Summary collapse

Class Method Details

.define(*args, &block) ⇒ Object

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.



81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
# 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) ⇒ Object



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

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

#deconstructObject



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

def deconstruct; __to_h__.values                                 end

#deconstruct_keys(keys) ⇒ Object

Raises:

  • (TypeError)


168
169
170
171
172
# 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) ⇒ Object



32
# 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

Returns:

  • (Boolean)


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

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

#hashObject



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

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

#init_with(coder) ⇒ Object



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

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

#inspectObject Also known as: to_s



179
180
181
182
183
184
185
186
# 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

#membersObject



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

def members;     self.class.members                              end

#to_h(&block) ⇒ Object



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

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

#with(**kwargs) ⇒ Object



174
175
176
177
# 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