5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
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
65
66
67
68
69
70
71
72
|
# File 'lib/rstructural/struct.rb', line 5
def self.new(*attributes, __caller: nil, &block)
begin
kaller = __caller || caller
names = kaller.map do |stack|
if (m = stack.match(/\A.+in `<(module|class):(.+)>.+/))
m[2]
end
end.reject(&:nil?).reverse
file_name, line_num = kaller[0].split(':')
line_executed = File.readlines(file_name)[line_num.to_i - 1]
names << line_executed.match(/\A\s*(\S+)\s*=/)[1] class_name = names.join('::')
rescue StandardError
class_name = 'Struct'
end
Class.new.tap do |k|
k.class_eval <<~RUBY
def initialize(#{attributes.join(', ')})
#{attributes.map { |attr| "@#{attr} = #{attr}" }.join("\n")}
end
#{attributes.map { |attr| "attr_reader(:#{attr})" }.join("\n")}
def self.name
"#{class_name}"
end
def self.to_s
"<#{class_name}>"
end
def copy(#{attributes.map { |attr| "#{attr}: self.#{attr}"}.join(', ')})
self.class.new(#{attributes.map { |attr| "#{attr}" }.join(', ')})
end
def [](key)
_key = ("@" + key.to_s).to_sym
self.instance_variable_get(_key)
end
def ==(other)
return false if other.class != self.class
#{attributes.empty? ? true : attributes.map { |attr| "other.#{attr} == self.#{attr}" }.join(" && ")}
end
def inspect
if #{attributes.empty?}
"#{class_name}"
else
__attrs = Array[#{attributes.map { |attr| "'#{attr}: ' + (@#{attr}.nil? ? 'nil' : @#{attr}.to_s)" }.join(', ')}].join(", ")
"#{class_name}(" + __attrs + ")"
end
end
alias :to_s :inspect
def deconstruct
[#{attributes.map { |attr| "@#{attr}" }.join(', ')}]
end
def deconstruct_keys(keys = nil)
{#{attributes.map { |attr| "'#{attr}'.to_sym => @#{attr}" }.join(', ')}}
end
RUBY
k.class_eval(&block) if block
end
end
|