Created
May 2, 2021 19:46
-
-
Save JoshCheek/fb5ceb16e7aca7772eb1553294e788be to your computer and use it in GitHub Desktop.
Implementing Struct (solution to https://gist.github.com/JoshCheek/2641441)
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| # 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