1 | module Koine |
||
2 | module Attributes |
||
0 ignored issues
–
show
Bug
introduced
by
![]() |
|||
3 | class Attributes |
||
0 ignored issues
–
show
|
|||
4 | def initialize(object, adapters:, options: {}) |
||
0 ignored issues
–
show
|
|||
5 | @object = object |
||
6 | @adapters = adapters |
||
7 | @values = {} |
||
8 | @initializer = { strict: true, freeze: false, initialize: !options[:initializer].nil? } |
||
9 | |||
10 | if options[:initializer].is_a?(Hash) |
||
11 | @initializer = @initializer.merge(options[:initializer]) |
||
12 | end |
||
13 | end |
||
14 | |||
15 | def initialize_values(values = {}) |
||
16 | if !@initializer[:initialize] && !values.empty? |
||
17 | raise InvalidAttributesError, "wrong number of arguments (given #{values.length}, expected 0)" |
||
18 | end |
||
19 | |||
20 | return unless @initializer[:initialize] |
||
21 | set_values(values) && @initializer[:freeze] && freeze |
||
22 | end |
||
23 | |||
24 | def set_values(values) |
||
25 | invalid_attributes = [] |
||
26 | |||
27 | if @initializer[:strict] |
||
28 | attributes = values.keys.map(&:to_sym) |
||
29 | invalid_attributes = attributes - valid_attributes |
||
30 | end |
||
31 | |||
32 | unless invalid_attributes.empty? |
||
33 | raise InvalidAttributesError, "Invalid attributes (#{invalid_attributes.join(', ')})" |
||
34 | end |
||
35 | |||
36 | values.each do |attribute, value| |
||
37 | set(attribute, value) if has_attribute?(attribute) |
||
38 | end |
||
39 | end |
||
40 | |||
41 | def set(attribute, value) |
||
42 | @values[attribute.to_sym] = adapter_for(attribute).coerce(value) |
||
43 | end |
||
44 | |||
45 | def with_attribute(attribute, value) |
||
46 | new_attributes = to_h.merge(attribute => value) |
||
47 | @object.class.new(new_attributes) |
||
48 | end |
||
49 | |||
50 | def get(attribute) |
||
51 | @values[attribute.to_sym] || adapter_for(attribute).default_value |
||
52 | end |
||
53 | |||
54 | def ==(other) |
||
55 | other.to_h == to_h |
||
56 | end |
||
57 | |||
58 | def to_h |
||
59 | valid_attributes.map do |name| |
||
60 | [name.to_sym, @object.send(name)] |
||
61 | end.to_h |
||
62 | end |
||
63 | |||
64 | def respond_to?(method, _include_private = nil) |
||
65 | method = method.to_s |
||
66 | |||
67 | # getter |
||
68 | return true if has_attribute?(method) |
||
69 | |||
70 | # {attribute_name}=value |
||
71 | matches = method.match(/^(.*)=$/) |
||
72 | return has_attribute?(matches[1]) if matches |
||
73 | |||
74 | # with_{attribute}(value) |
||
75 | matches = method.match(/^with_(.*)$/) |
||
76 | return has_attribute?(matches[1]) if matches |
||
77 | |||
78 | false |
||
79 | end |
||
80 | |||
81 | def method_missing(method_name, *args) |
||
82 | unless respond_to?(method_name) |
||
83 | raise NoMethodError, "Undefined method #{method_name} for attributed object #{@object}" |
||
84 | end |
||
85 | |||
86 | method_name = method_name.to_s |
||
87 | |||
88 | if method_name.to_s =~ /=$/ |
||
89 | attribute = method_name.to_s.delete('=') |
||
90 | return set(attribute, *args) |
||
91 | end |
||
92 | |||
93 | matches = method_name.match(/^with_(.*)$/) |
||
94 | return with_attribute(matches[1], *args) if matches |
||
95 | |||
96 | get(method_name) |
||
97 | end |
||
98 | |||
99 | private |
||
100 | |||
101 | def valid_attributes |
||
102 | @adapters.keys |
||
103 | end |
||
104 | |||
105 | def has_attribute?(attribute) |
||
106 | @adapters.key?(attribute.to_sym) |
||
107 | end |
||
108 | |||
109 | def adapter_for(attribute) |
||
110 | @adapters.fetch(attribute.to_sym) |
||
111 | end |
||
112 | |||
113 | def freeze |
||
114 | @object.freeze |
||
115 | @adapters.freeze |
||
116 | @values.freeze |
||
117 | super |
||
118 | end |
||
119 | end |
||
120 | end |
||
121 | end |
||
122 |