Skip to content

Instantly share code, notes, and snippets.

@JoshCheek
Created May 2, 2021 19:46
Show Gist options
  • Save JoshCheek/fb5ceb16e7aca7772eb1553294e788be to your computer and use it in GitHub Desktop.
Save JoshCheek/fb5ceb16e7aca7772eb1553294e788be to your computer and use it in GitHub Desktop.
Implementing Struct (solution to https://gist.github.com/JoshCheek/2641441)
# solution to https://gist.github.com/JoshCheek/2641441
class MyStruct
def self.new(*members, &block)
members.none? and raise ArgumentError, 'wrong number of arguments (must provide at least one)'
members.each { raise TypeError, "Expected a Symbol, got #{_1.inspect}" unless _1.is_a? Symbol }
Class.new Instance do
@members = members
members.each do |name|
define_method(name) { self[name] }
define_method("#{name}=") { |value| self[name] = value }
end
class_eval &block if block
end
end
class Instance
def self.members
@members.dup
end
def initialize(*params)
@attributes = self.class.members.zip(params).to_h { [_1, _2] }
end
def [](key)
key = normalize_key key
@attributes.fetch(key) { raise NameError, "no member '#{key}' in struct" }
end
def []=(key, value)
@attributes[normalize_key key] = value
end
def inspect
"#<struct #{@attributes.map { %'#{_1}=#{_2.inspect}' }.join(', ')}>"
end
def members
@attributes.keys
end
def values
@attributes.values
end
def select(&block)
values.select(&block)
end
def size
@attributes.size
end
include Enumerable
def each(&block)
values.each(&block)
end
private
def normalize_key(key)
if key.respond_to? :to_sym
key = key.to_sym
return key if @attributes.key? key
raise NameError, "no member '#{key}' in struct"
end
if key.respond_to? :to_int
return members.fetch key.to_int do
raise IndexError, "offset #{key} too large for struct(size:#{size})"
end
end
raise TypeError, "no implicit conversion of #{key.class} into Integer"
end
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment