calificador 0.1.0 → 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +35 -16
- data/TODO.md +16 -0
- data/calificador.gemspec +54 -0
- data/lib/calificador.rb +8 -4
- data/lib/calificador/assert.rb +15 -0
- data/lib/calificador/assertor.rb +79 -35
- data/lib/calificador/build/attribute_evaluator.rb +34 -30
- data/lib/calificador/build/basic_factory.rb +195 -0
- data/lib/calificador/build/mock_factory.rb +151 -0
- data/lib/calificador/build/object_factory.rb +85 -0
- data/lib/calificador/build/trait.rb +0 -20
- data/lib/calificador/context/basic_context.rb +406 -0
- data/lib/calificador/context/class_method_context.rb +0 -0
- data/lib/calificador/{spec → context}/condition_context.rb +1 -3
- data/lib/calificador/{spec/type_context.rb → context/instance_context.rb} +5 -10
- data/lib/calificador/context/operation_context.rb +27 -0
- data/lib/calificador/context/override/argument_override.rb +73 -0
- data/lib/calificador/context/override/basic_override.rb +14 -0
- data/lib/calificador/context/override/factory_override.rb +31 -0
- data/lib/calificador/context/override/property_override.rb +61 -0
- data/lib/calificador/context/test_environment.rb +283 -0
- data/lib/calificador/{spec → context}/test_method.rb +2 -31
- data/lib/calificador/{spec → context}/test_root.rb +3 -15
- data/lib/calificador/{spec/examine_context.rb → context/type_context.rb} +7 -10
- data/lib/calificador/key.rb +27 -15
- data/lib/calificador/minitest/minitest_patches.rb +0 -2
- data/lib/calificador/test.rb +1 -3
- data/lib/calificador/test_mixin.rb +143 -139
- data/lib/calificador/util/call_formatter.rb +5 -5
- data/lib/calificador/util/core_extensions.rb +104 -79
- data/lib/calificador/util/proxy_object.rb +63 -0
- data/lib/calificador/version.rb +1 -1
- metadata +22 -42
- data/lib/calificador/build/attribute_container.rb +0 -103
- data/lib/calificador/build/factory.rb +0 -132
- data/lib/calificador/spec/basic_context.rb +0 -353
- data/lib/calificador/spec/class_method_context.rb +0 -42
- data/lib/calificador/spec/instance_method_context.rb +0 -38
- data/lib/calificador/spec/test_environment.rb +0 -141
- data/lib/calificador/spec/value_override.rb +0 -37
@@ -5,11 +5,11 @@ require "pp"
|
|
5
5
|
module Calificador
|
6
6
|
module Util
|
7
7
|
class CallFormatter
|
8
|
-
def
|
8
|
+
def format_method(name:, arguments: [], keywords: {})
|
9
9
|
info = ::StringIO.new
|
10
|
-
info <<
|
10
|
+
info << name
|
11
11
|
|
12
|
-
unless arguments.empty? &&
|
12
|
+
unless arguments.empty? && keywords.empty?
|
13
13
|
info << "("
|
14
14
|
|
15
15
|
arguments.each_with_index do |argument, i|
|
@@ -17,7 +17,7 @@ module Calificador
|
|
17
17
|
append_value(value: argument, out: info)
|
18
18
|
end
|
19
19
|
|
20
|
-
|
20
|
+
keywords.each_with_index do |(name, value), i|
|
21
21
|
info << ", " unless i.zero? && arguments.empty?
|
22
22
|
info << name << ": "
|
23
23
|
append_value(value: value, out: info)
|
@@ -29,7 +29,7 @@ module Calificador
|
|
29
29
|
info.string
|
30
30
|
end
|
31
31
|
|
32
|
-
def
|
32
|
+
def format_value(value:)
|
33
33
|
append_value(value: value, out: StringIO.new).string
|
34
34
|
end
|
35
35
|
|
@@ -1,72 +1,20 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require "docile"
|
4
3
|
require "ostruct"
|
5
4
|
|
6
5
|
module Calificador
|
7
6
|
module Util
|
8
7
|
# Extensions to core classes
|
9
8
|
module CoreExtensions
|
10
|
-
|
11
|
-
|
12
|
-
def map_call_arguments(signature:, arguments:, options:)
|
13
|
-
min_argument_count = 0
|
14
|
-
max_argument_count = 0
|
15
|
-
option_names = Set.new
|
16
|
-
|
17
|
-
signature.each do |type, name|
|
18
|
-
case type
|
19
|
-
when :req
|
20
|
-
min_argument_count += 1
|
21
|
-
max_argument_count += 1
|
22
|
-
when :opt
|
23
|
-
max_argument_count += 1
|
24
|
-
when :rest
|
25
|
-
max_argument_count = nil
|
26
|
-
when :keyreq
|
27
|
-
raise ArgumentError, "Required option #{name} missing for #{self} #{signature}" unless options.key?(name)
|
28
|
-
|
29
|
-
option_names << name
|
30
|
-
when :key
|
31
|
-
option_names << name
|
32
|
-
when :keyrest
|
33
|
-
option_names += options.keys
|
34
|
-
when :block
|
35
|
-
# ignore
|
36
|
-
else
|
37
|
-
raise ArgumentError, "Illegal parameter type #{type} for #{self} #{signature}"
|
38
|
-
end
|
39
|
-
end
|
40
|
-
|
41
|
-
argument_count = arguments.size
|
42
|
-
argument_count = max_argument_count if max_argument_count && argument_count > max_argument_count
|
43
|
-
|
44
|
-
raise ArgumentError, "Not enough parameters to call proc with #{signature}" if argument_count < min_argument_count
|
45
|
-
|
46
|
-
arguments = arguments[0...argument_count]
|
47
|
-
options = options.slice(*option_names)
|
48
|
-
|
49
|
-
[arguments, options]
|
50
|
-
end
|
51
|
-
|
52
|
-
refine Object do
|
9
|
+
module ObjectMixins
|
53
10
|
def to_bool
|
54
11
|
self ? true : false
|
55
12
|
end
|
56
|
-
|
57
|
-
def dsl_config(&block)
|
58
|
-
target = self
|
59
|
-
|
60
|
-
if block
|
61
|
-
dsl = self.class.const_get(:Dsl).new(delegate: target)
|
62
|
-
Docile.dsl_eval(dsl, &block)
|
63
|
-
end
|
64
|
-
|
65
|
-
target
|
66
|
-
end
|
67
13
|
end
|
68
14
|
|
69
|
-
|
15
|
+
Object.include(ObjectMixins)
|
16
|
+
|
17
|
+
module StringMixins
|
70
18
|
def snake_case
|
71
19
|
gsub(%r{(?<=[a-z0-9])(?=[A-Z])|(?<=[A-Z])(?=[A-Z][a-z])}, "_").downcase
|
72
20
|
end
|
@@ -78,9 +26,14 @@ module Calificador
|
|
78
26
|
end
|
79
27
|
end
|
80
28
|
|
81
|
-
|
29
|
+
String.include(StringMixins)
|
30
|
+
|
31
|
+
module ModuleMixins
|
82
32
|
def parent_module
|
83
|
-
|
33
|
+
if !instance_variable_defined?(:@__calificador_parent_module) || @__calificador_parent_module.nil?
|
34
|
+
@__calificador_parent_module = Nil[parent_name&.then { |m| const_get(m) }]
|
35
|
+
end
|
36
|
+
|
84
37
|
@__calificador_parent_module.unmask_nil
|
85
38
|
end
|
86
39
|
|
@@ -89,47 +42,119 @@ module Calificador
|
|
89
42
|
end
|
90
43
|
|
91
44
|
def base_name
|
92
|
-
|
93
|
-
%r{\
|
94
|
-
|
95
|
-
|
45
|
+
if !instance_variable_defined?(:@__calificador_base_name) || @__calificador_base_name.nil?
|
46
|
+
@__calificador_base_name = if %r{(?:^|::)(?<base_name>(?:[^:]|:[^:])+)\z} =~ name
|
47
|
+
Nil[base_name]
|
48
|
+
else
|
49
|
+
Nil.instance
|
50
|
+
end
|
96
51
|
end
|
97
52
|
|
98
53
|
@__calificador_base_name.unmask_nil
|
99
54
|
end
|
100
55
|
|
101
56
|
def parent_name
|
102
|
-
|
103
|
-
%r{
|
104
|
-
|
105
|
-
|
57
|
+
if !instance_variable_defined?(:@__calificador_parent_name) || @__calificador_parent_name.nil?
|
58
|
+
@__calificador_parent_name = if %r{(?<parent_name>\A.*)::} =~ name
|
59
|
+
parent_name
|
60
|
+
else
|
61
|
+
Nil.instance
|
62
|
+
end
|
106
63
|
end
|
107
64
|
|
108
65
|
@__calificador_parent_name.unmask_nil
|
109
66
|
end
|
110
|
-
end
|
111
67
|
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
68
|
+
def name_without_common_parents(base:)
|
69
|
+
path = name&.split("::")
|
70
|
+
|
71
|
+
result = if path
|
72
|
+
base_path = base&.name&.split("::")
|
73
|
+
path.remove_common_prefix(base_path).join("::") if base_path
|
116
74
|
end
|
117
75
|
|
118
|
-
|
119
|
-
|
120
|
-
|
76
|
+
result || name
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
Module.include(ModuleMixins)
|
81
|
+
|
82
|
+
module ArrayMixins
|
83
|
+
def remove_common_prefix(other)
|
84
|
+
drop_while.with_index do |element, index|
|
85
|
+
index < size && element == other[index]
|
121
86
|
end
|
87
|
+
end
|
88
|
+
end
|
122
89
|
|
123
|
-
|
124
|
-
|
125
|
-
|
90
|
+
Array.include(ArrayMixins)
|
91
|
+
|
92
|
+
module CallableMixins
|
93
|
+
REQUIRED_ARGUMENT_TYPES = %i[req keyreq].freeze
|
94
|
+
|
95
|
+
def map_call_arguments(arguments:, keywords:)
|
96
|
+
min_argument_count = 0
|
97
|
+
max_argument_count = 0
|
98
|
+
option_names = Set.new
|
99
|
+
|
100
|
+
parameters.each do |type, name|
|
101
|
+
case type
|
102
|
+
when :req
|
103
|
+
min_argument_count += 1
|
104
|
+
max_argument_count += 1
|
105
|
+
when :opt
|
106
|
+
max_argument_count += 1
|
107
|
+
when :rest
|
108
|
+
max_argument_count = nil
|
109
|
+
when :keyreq
|
110
|
+
raise ArgumentError, "Required option #{name} missing for #{self} #{parameters}" unless keywords.key?(name)
|
111
|
+
|
112
|
+
option_names << name
|
113
|
+
when :key
|
114
|
+
option_names << name
|
115
|
+
when :keyrest
|
116
|
+
option_names += keywords.keys
|
117
|
+
when :block
|
118
|
+
# ignore
|
119
|
+
else
|
120
|
+
raise ArgumentError, "Illegal parameter type #{type} for #{self} #{parameters}"
|
121
|
+
end
|
126
122
|
end
|
127
123
|
|
128
|
-
|
129
|
-
|
124
|
+
argument_count = arguments.size
|
125
|
+
argument_count = max_argument_count if max_argument_count && argument_count > max_argument_count
|
126
|
+
|
127
|
+
if argument_count < min_argument_count
|
128
|
+
raise ArgumentError, "Not enough parameters to call proc with #{parameters}"
|
130
129
|
end
|
130
|
+
|
131
|
+
arguments = arguments[0...argument_count]
|
132
|
+
keywords = keywords.slice(*option_names)
|
133
|
+
|
134
|
+
[arguments, keywords]
|
135
|
+
end
|
136
|
+
|
137
|
+
def invoke(*arguments, **keywords, &block)
|
138
|
+
arguments, keywords = map_call_arguments(arguments: arguments, keywords: keywords)
|
139
|
+
call(*arguments, **keywords, &block)
|
140
|
+
end
|
141
|
+
|
142
|
+
def invoke_with_target(target, *arguments, **keywords)
|
143
|
+
arguments, keywords = map_call_arguments(arguments: arguments, keywords: keywords)
|
144
|
+
target.instance_exec(*arguments, **keywords, &self)
|
145
|
+
end
|
146
|
+
|
147
|
+
def source_location_info
|
148
|
+
source_location ? source_location.join(":") : nil
|
149
|
+
end
|
150
|
+
|
151
|
+
def required_arguments?
|
152
|
+
parameters.any? { |type, _name| REQUIRED_ARGUMENT_TYPES.include?(type) }
|
131
153
|
end
|
132
154
|
end
|
155
|
+
|
156
|
+
Method.include(CallableMixins)
|
157
|
+
Proc.include(CallableMixins)
|
133
158
|
end
|
134
159
|
end
|
135
160
|
end
|
@@ -0,0 +1,63 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Calificador
|
4
|
+
module Util
|
5
|
+
# Base class for proxy objects
|
6
|
+
class ProxyObject < BasicObject
|
7
|
+
FALLBACK_METHODS = %i[class to_s inspect eval].freeze
|
8
|
+
|
9
|
+
def respond_to?(name, include_all = false) # rubocop:disable Style/OptionalBooleanParameter
|
10
|
+
__class.public_instance_methods(true).include?(name) ||
|
11
|
+
include_all && (
|
12
|
+
__class.protected_instance_methods(true).include?(name) ||
|
13
|
+
__class.private_instance_methods(true).include?(name)
|
14
|
+
) ||
|
15
|
+
respond_to_missing?(name, include_all)
|
16
|
+
end
|
17
|
+
|
18
|
+
def respond_to_missing?(name, include_all)
|
19
|
+
name.start_with?("__") ? false : __respond_to_missing?(name: name, include_all: include_all)
|
20
|
+
end
|
21
|
+
|
22
|
+
def method_missing(name, *arguments, **keywords, &block)
|
23
|
+
if name.start_with?("__")
|
24
|
+
super(name, *arguments, **keywords, &block)
|
25
|
+
else
|
26
|
+
__method_missing(name: name, arguments: arguments, keywords: keywords, block: block)
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
protected
|
31
|
+
|
32
|
+
def __class
|
33
|
+
(class << self; self end).superclass
|
34
|
+
end
|
35
|
+
|
36
|
+
def __to_s
|
37
|
+
"#<#{__class}>"
|
38
|
+
end
|
39
|
+
|
40
|
+
def __inspect
|
41
|
+
"#<#{__class}>"
|
42
|
+
end
|
43
|
+
|
44
|
+
def __eval(*arguments)
|
45
|
+
instance_eval(*arguments)
|
46
|
+
end
|
47
|
+
|
48
|
+
def __respond_to_missing?(name:, include_all:)
|
49
|
+
false
|
50
|
+
end
|
51
|
+
|
52
|
+
def __method_missing(name:, arguments:, keywords:, block:)
|
53
|
+
if ::Kernel.respond_to?(name)
|
54
|
+
::Kernel.send(name, *arguments, **keywords, &block)
|
55
|
+
elsif FALLBACK_METHODS.include?(name)
|
56
|
+
__send__(:"__#{name}", *arguments, **keywords, &block)
|
57
|
+
else
|
58
|
+
::Kernel.raise ::NoMethodError, "undefined name `#{name}' for #{__class}"
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
data/lib/calificador/version.rb
CHANGED
metadata
CHANGED
@@ -1,29 +1,15 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: calificador
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.2.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Jochen Seeber
|
8
8
|
autorequire:
|
9
9
|
bindir: cmd
|
10
10
|
cert_chain: []
|
11
|
-
date: 2021-
|
11
|
+
date: 2021-03-04 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
|
-
- !ruby/object:Gem::Dependency
|
14
|
-
name: docile
|
15
|
-
requirement: !ruby/object:Gem::Requirement
|
16
|
-
requirements:
|
17
|
-
- - "~>"
|
18
|
-
- !ruby/object:Gem::Version
|
19
|
-
version: 1.3.5
|
20
|
-
type: :runtime
|
21
|
-
prerelease: false
|
22
|
-
version_requirements: !ruby/object:Gem::Requirement
|
23
|
-
requirements:
|
24
|
-
- - "~>"
|
25
|
-
- !ruby/object:Gem::Version
|
26
|
-
version: 1.3.5
|
27
13
|
- !ruby/object:Gem::Dependency
|
28
14
|
name: minitest
|
29
15
|
requirement: !ruby/object:Gem::Requirement
|
@@ -66,20 +52,6 @@ dependencies:
|
|
66
52
|
- - "~>"
|
67
53
|
- !ruby/object:Gem::Version
|
68
54
|
version: '2.1'
|
69
|
-
- !ruby/object:Gem::Dependency
|
70
|
-
name: coveralls
|
71
|
-
requirement: !ruby/object:Gem::Requirement
|
72
|
-
requirements:
|
73
|
-
- - '='
|
74
|
-
- !ruby/object:Gem::Version
|
75
|
-
version: 0.8.23.js
|
76
|
-
type: :development
|
77
|
-
prerelease: false
|
78
|
-
version_requirements: !ruby/object:Gem::Requirement
|
79
|
-
requirements:
|
80
|
-
- - '='
|
81
|
-
- !ruby/object:Gem::Version
|
82
|
-
version: 0.8.23.js
|
83
55
|
- !ruby/object:Gem::Dependency
|
84
56
|
name: debase
|
85
57
|
requirement: !ruby/object:Gem::Requirement
|
@@ -201,25 +173,32 @@ extra_rdoc_files: []
|
|
201
173
|
files:
|
202
174
|
- LICENSE.txt
|
203
175
|
- README.md
|
176
|
+
- TODO.md
|
177
|
+
- calificador.gemspec
|
204
178
|
- lib/calificador.rb
|
179
|
+
- lib/calificador/assert.rb
|
205
180
|
- lib/calificador/assertor.rb
|
206
181
|
- lib/calificador/build/attribute.rb
|
207
|
-
- lib/calificador/build/attribute_container.rb
|
208
182
|
- lib/calificador/build/attribute_evaluator.rb
|
209
|
-
- lib/calificador/build/
|
183
|
+
- lib/calificador/build/basic_factory.rb
|
184
|
+
- lib/calificador/build/mock_factory.rb
|
185
|
+
- lib/calificador/build/object_factory.rb
|
210
186
|
- lib/calificador/build/trait.rb
|
187
|
+
- lib/calificador/context/basic_context.rb
|
188
|
+
- lib/calificador/context/class_method_context.rb
|
189
|
+
- lib/calificador/context/condition_context.rb
|
190
|
+
- lib/calificador/context/instance_context.rb
|
191
|
+
- lib/calificador/context/operation_context.rb
|
192
|
+
- lib/calificador/context/override/argument_override.rb
|
193
|
+
- lib/calificador/context/override/basic_override.rb
|
194
|
+
- lib/calificador/context/override/factory_override.rb
|
195
|
+
- lib/calificador/context/override/property_override.rb
|
196
|
+
- lib/calificador/context/test_environment.rb
|
197
|
+
- lib/calificador/context/test_method.rb
|
198
|
+
- lib/calificador/context/test_root.rb
|
199
|
+
- lib/calificador/context/type_context.rb
|
211
200
|
- lib/calificador/key.rb
|
212
201
|
- lib/calificador/minitest/minitest_patches.rb
|
213
|
-
- lib/calificador/spec/basic_context.rb
|
214
|
-
- lib/calificador/spec/class_method_context.rb
|
215
|
-
- lib/calificador/spec/condition_context.rb
|
216
|
-
- lib/calificador/spec/examine_context.rb
|
217
|
-
- lib/calificador/spec/instance_method_context.rb
|
218
|
-
- lib/calificador/spec/test_environment.rb
|
219
|
-
- lib/calificador/spec/test_method.rb
|
220
|
-
- lib/calificador/spec/test_root.rb
|
221
|
-
- lib/calificador/spec/type_context.rb
|
222
|
-
- lib/calificador/spec/value_override.rb
|
223
202
|
- lib/calificador/test.rb
|
224
203
|
- lib/calificador/test_mixin.rb
|
225
204
|
- lib/calificador/util/call_formatter.rb
|
@@ -227,6 +206,7 @@ files:
|
|
227
206
|
- lib/calificador/util/core_extensions.rb
|
228
207
|
- lib/calificador/util/missing.rb
|
229
208
|
- lib/calificador/util/nil.rb
|
209
|
+
- lib/calificador/util/proxy_object.rb
|
230
210
|
- lib/calificador/version.rb
|
231
211
|
homepage: https://github.com/jochenseeber/calificador
|
232
212
|
licenses: []
|
@@ -1,103 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
using Calificador::Util::CoreExtensions
|
4
|
-
|
5
|
-
module Calificador
|
6
|
-
module Build
|
7
|
-
# Factory calss
|
8
|
-
class AttributeContainer
|
9
|
-
class Dsl
|
10
|
-
def initialize(delegate:)
|
11
|
-
@delegate = delegate
|
12
|
-
@property_type = nil
|
13
|
-
end
|
14
|
-
|
15
|
-
def add_attribute(name, type: nil, &config)
|
16
|
-
type ||= @delegate.parent&.attribute(name: name)&.type || @property_type || :property
|
17
|
-
@delegate.add_attribute(Attribute.new(name: name, type: type, config: config))
|
18
|
-
end
|
19
|
-
|
20
|
-
def transient(&block)
|
21
|
-
raise ArgumentError, "Transient requires a block" if block.nil?
|
22
|
-
|
23
|
-
old_property_type = @property_type
|
24
|
-
@property_type = :transient
|
25
|
-
|
26
|
-
begin
|
27
|
-
instance_exec(self, &block)
|
28
|
-
ensure
|
29
|
-
@property_type = old_property_type
|
30
|
-
end
|
31
|
-
end
|
32
|
-
|
33
|
-
def init_with(&block)
|
34
|
-
raise "Initializer requires a block to create the object" if block.nil?
|
35
|
-
|
36
|
-
@delegate.init_with = block
|
37
|
-
end
|
38
|
-
|
39
|
-
def before_create(&block)
|
40
|
-
raise "Before requires a block to call" if block.nil?
|
41
|
-
|
42
|
-
@delegate.before_create = block
|
43
|
-
end
|
44
|
-
|
45
|
-
def after_create(&block)
|
46
|
-
raise "After requires a block to call" if block.nil?
|
47
|
-
|
48
|
-
@delegate.after_create = block
|
49
|
-
end
|
50
|
-
|
51
|
-
def respond_to_missing?(method, include_all = false)
|
52
|
-
if method.start_with?("__")
|
53
|
-
super
|
54
|
-
else
|
55
|
-
true
|
56
|
-
end
|
57
|
-
end
|
58
|
-
|
59
|
-
def method_missing(method, *arguments, &block)
|
60
|
-
if method.start_with?("__")
|
61
|
-
super
|
62
|
-
else
|
63
|
-
unless arguments.empty?
|
64
|
-
raise ::ArgumentError, <<~ERROR
|
65
|
-
Attribute #{method} cannot have arguments. Please use a block to configure the value
|
66
|
-
ERROR
|
67
|
-
end
|
68
|
-
|
69
|
-
raise ::ArgumentError, "Attribute #{method} must have a block to provide the value" if block.nil?
|
70
|
-
|
71
|
-
add_attribute(method, &block)
|
72
|
-
end
|
73
|
-
end
|
74
|
-
end
|
75
|
-
|
76
|
-
attr_reader :parent, :description
|
77
|
-
attr_accessor :init_with, :before_create, :after_create
|
78
|
-
|
79
|
-
def initialize(parent:, description:)
|
80
|
-
@parent = parent
|
81
|
-
@description = description.dup.freeze
|
82
|
-
@attributes = {}
|
83
|
-
@init_with = nil
|
84
|
-
@before_create = nil
|
85
|
-
@after_create = nil
|
86
|
-
end
|
87
|
-
|
88
|
-
def attributes
|
89
|
-
@attributes.dup.freeze
|
90
|
-
end
|
91
|
-
|
92
|
-
def attribute(name:)
|
93
|
-
@attributes[name]
|
94
|
-
end
|
95
|
-
|
96
|
-
def add_attribute(attribute)
|
97
|
-
raise KeyError, "Duplicate attribute name #{name}" if @attributes.key?(attribute.name)
|
98
|
-
|
99
|
-
@attributes[attribute.name] = attribute
|
100
|
-
end
|
101
|
-
end
|
102
|
-
end
|
103
|
-
end
|