Module: RGame::Engine::Resettable
- Defined in:
- lib/rgame/engine/resettable.rb
Overview
Builds value-object classes for pooling. Like Data.define, instances expose
read-only accessors and carry their fields as a unit — but where a Data value
is fully immutable (every change means a fresh allocation), these add exactly
one mutation: reset, which overwrites all fields at once and returns self.
That is the only mutability a pool needs: acquire a recycled instance and
reset it, without exposing the per-field setters a Struct would. The methods
are generated fixed-arity with direct ivar assignment (as Struct/Data do), so
reset is allocation-free and recycling stays zero-allocation in steady state.
Positional by default; pass keyword_init: true for keyword fields. Keywords
are also allocation-free here because they are generated as named parameters
(reset(x:, y:)), not a **kwargs splat — the splat is the one form that builds
a Hash per call.
Point = Engine::Resettable.define(:x, :y)
p = Point.new(3, 4); p.x # => 3
p.reset(5, 6) # same object, new values; returns p
Event = Engine::Resettable.define(:type, :payload, keyword_init: true)
e = Event.new(type: :hit, payload: 10)
e.reset(type: :lose, payload: nil) # same object; returns e
e.respond_to?(:type=) # => false (no setters)
Field names are generated into real methods, so they must be plain identifiers.
Constant Summary collapse
- IDENTIFIER =
/\A[a-z_][a-zA-Z0-9_]*\z/
Class Method Summary collapse
Class Method Details
.define(*fields, keyword_init: false) ⇒ Object
33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 |
# File 'lib/rgame/engine/resettable.rb', line 33 def self.define(*fields, keyword_init: false) fields.each do |field| raise ArgumentError, "invalid field name: #{field.inspect}" unless field.to_s.match?(IDENTIFIER) end params = fields.map { |f| keyword_init ? "#{f}:" : f.to_s }.join(', ') assign = fields.map { |f| "@#{f} = #{f}" }.join("\n") Class.new do class_eval(<<~RUBY, __FILE__, __LINE__ + 1) # For fields [:x, :y] this generates (keyword_init makes `x, y` -> `x:, y:`): # attr_reader :x, :y # def initialize(x, y) # @x = x; @y = y # end # def reset(x, y) # @x = x; @y = y # self # end attr_reader #{fields.map { |f| ":#{f}" }.join(', ')} def initialize(#{params}) #{assign} end def reset(#{params}) #{assign} self end RUBY end end |