castkit 0.2.0 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.rspec_status +118 -119
- data/CHANGELOG.md +1 -1
- data/README.md +287 -11
- data/castkit.gemspec +1 -0
- data/lib/castkit/castkit.rb +5 -2
- data/lib/castkit/cli/generate.rb +98 -0
- data/lib/castkit/cli/list.rb +200 -0
- data/lib/castkit/cli/main.rb +43 -0
- data/lib/castkit/cli.rb +24 -0
- data/lib/castkit/configuration.rb +31 -8
- data/lib/castkit/contract/{generic.rb → base.rb} +5 -5
- data/lib/castkit/contract/result.rb +2 -2
- data/lib/castkit/contract.rb +5 -5
- data/lib/castkit/data_object.rb +11 -7
- data/lib/castkit/ext/data_object/contract.rb +1 -1
- data/lib/castkit/ext/data_object/plugins.rb +86 -0
- data/lib/castkit/inflector.rb +1 -1
- data/lib/castkit/plugins.rb +82 -0
- data/lib/castkit/serializers/base.rb +94 -0
- data/lib/castkit/serializers/default_serializer.rb +156 -0
- data/lib/castkit/types/{generic.rb → base.rb} +6 -7
- data/lib/castkit/types/boolean.rb +14 -10
- data/lib/castkit/types/collection.rb +13 -2
- data/lib/castkit/types/date.rb +2 -2
- data/lib/castkit/types/date_time.rb +2 -2
- data/lib/castkit/types/float.rb +5 -5
- data/lib/castkit/types/integer.rb +5 -5
- data/lib/castkit/types/string.rb +2 -2
- data/lib/castkit/types.rb +1 -1
- data/lib/castkit/validators/base.rb +59 -0
- data/lib/castkit/validators/boolean_validator.rb +39 -0
- data/lib/castkit/validators/collection_validator.rb +29 -0
- data/lib/castkit/validators/float_validator.rb +31 -0
- data/lib/castkit/validators/integer_validator.rb +31 -0
- data/lib/castkit/validators/numeric_validator.rb +2 -2
- data/lib/castkit/validators/string_validator.rb +3 -4
- data/lib/castkit/version.rb +1 -1
- data/lib/generators/base.rb +97 -0
- data/lib/generators/contract.rb +68 -0
- data/lib/generators/data_object.rb +48 -0
- data/lib/generators/plugin.rb +25 -0
- data/lib/generators/serializer.rb +28 -0
- data/lib/generators/templates/contract.rb.tt +24 -0
- data/lib/generators/templates/contract_spec.rb.tt +76 -0
- data/lib/generators/templates/data_object.rb.tt +15 -0
- data/lib/generators/templates/data_object_spec.rb.tt +36 -0
- data/lib/generators/templates/plugin.rb.tt +37 -0
- data/lib/generators/templates/plugin_spec.rb.tt +18 -0
- data/lib/generators/templates/serializer.rb.tt +24 -0
- data/lib/generators/templates/serializer_spec.rb.tt +14 -0
- data/lib/generators/templates/type.rb.tt +55 -0
- data/lib/generators/templates/type_spec.rb.tt +42 -0
- data/lib/generators/templates/validator.rb.tt +26 -0
- data/lib/generators/templates/validator_spec.rb.tt +23 -0
- data/lib/generators/type.rb +29 -0
- data/lib/generators/validator.rb +41 -0
- metadata +50 -7
- data/lib/castkit/default_serializer.rb +0 -154
- data/lib/castkit/serializer.rb +0 -92
- data/lib/castkit/validators/base_validator.rb +0 -39
@@ -0,0 +1,29 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "thor/group"
|
4
|
+
require "castkit/inflector"
|
5
|
+
require_relative "base"
|
6
|
+
|
7
|
+
module Castkit
|
8
|
+
module Generators
|
9
|
+
# Generator for creating a new Castkit type.
|
10
|
+
#
|
11
|
+
# Types define custom deserialization, serialization, and validation behavior
|
12
|
+
# for attributes used in `Castkit::DataObject` or `Castkit::Contract`.
|
13
|
+
#
|
14
|
+
# Example usage:
|
15
|
+
# $ castkit generate type Money
|
16
|
+
#
|
17
|
+
# Generates:
|
18
|
+
# - lib/castkit/types/money.rb
|
19
|
+
# - spec/castkit/types/money_spec.rb
|
20
|
+
#
|
21
|
+
# These files scaffold a `Castkit::Types::Money` class inheriting from `Castkit::Types::Base`,
|
22
|
+
# along with a basic RSpec test suite.
|
23
|
+
#
|
24
|
+
# @see Castkit::Generators::Base
|
25
|
+
class Type < Castkit::Generators::Base
|
26
|
+
component :type
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "thor/group"
|
4
|
+
require "castkit/inflector"
|
5
|
+
require_relative "base"
|
6
|
+
|
7
|
+
module Castkit
|
8
|
+
module Generators
|
9
|
+
# Generator for creating a new Castkit validator.
|
10
|
+
#
|
11
|
+
# Validators are responsible for asserting that a given value conforms to a rule.
|
12
|
+
# They are typically used inside a type’s `#validate!` method or within custom contract logic.
|
13
|
+
#
|
14
|
+
# Example usage:
|
15
|
+
# $ castkit generate validator Money
|
16
|
+
#
|
17
|
+
# Generates:
|
18
|
+
# - lib/castkit/validators/money.rb
|
19
|
+
# - spec/castkit/validators/money_spec.rb
|
20
|
+
#
|
21
|
+
# These files scaffold a `Castkit::Validators::Money` class with a `#call` method
|
22
|
+
# and a corresponding RSpec test suite.
|
23
|
+
#
|
24
|
+
# @see Castkit::Generators::Base
|
25
|
+
class Validator < Castkit::Generators::Base
|
26
|
+
component :validator
|
27
|
+
|
28
|
+
private
|
29
|
+
|
30
|
+
# Provides extra context used within ERB templates for this generator.
|
31
|
+
#
|
32
|
+
# @return [Hash]
|
33
|
+
def config
|
34
|
+
super.merge(
|
35
|
+
default_value: "example",
|
36
|
+
sample_context: "field_name"
|
37
|
+
)
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
metadata
CHANGED
@@ -1,15 +1,29 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: castkit
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.3.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Nathan Lucas
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2025-04-
|
11
|
+
date: 2025-04-17 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: thor
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ">="
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '0'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - ">="
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '0'
|
13
27
|
- !ruby/object:Gem::Dependency
|
14
28
|
name: rspec
|
15
29
|
requirement: !ruby/object:Gem::Requirement
|
@@ -73,10 +87,14 @@ files:
|
|
73
87
|
- lib/castkit.rb
|
74
88
|
- lib/castkit/attribute.rb
|
75
89
|
- lib/castkit/castkit.rb
|
90
|
+
- lib/castkit/cli.rb
|
91
|
+
- lib/castkit/cli/generate.rb
|
92
|
+
- lib/castkit/cli/list.rb
|
93
|
+
- lib/castkit/cli/main.rb
|
76
94
|
- lib/castkit/configuration.rb
|
77
95
|
- lib/castkit/contract.rb
|
96
|
+
- lib/castkit/contract/base.rb
|
78
97
|
- lib/castkit/contract/data_object.rb
|
79
|
-
- lib/castkit/contract/generic.rb
|
80
98
|
- lib/castkit/contract/result.rb
|
81
99
|
- lib/castkit/contract/validator.rb
|
82
100
|
- lib/castkit/core/attribute_types.rb
|
@@ -84,7 +102,6 @@ files:
|
|
84
102
|
- lib/castkit/core/config.rb
|
85
103
|
- lib/castkit/core/registerable.rb
|
86
104
|
- lib/castkit/data_object.rb
|
87
|
-
- lib/castkit/default_serializer.rb
|
88
105
|
- lib/castkit/error.rb
|
89
106
|
- lib/castkit/ext/attribute/access.rb
|
90
107
|
- lib/castkit/ext/attribute/error_handling.rb
|
@@ -92,23 +109,49 @@ files:
|
|
92
109
|
- lib/castkit/ext/attribute/validation.rb
|
93
110
|
- lib/castkit/ext/data_object/contract.rb
|
94
111
|
- lib/castkit/ext/data_object/deserialization.rb
|
112
|
+
- lib/castkit/ext/data_object/plugins.rb
|
95
113
|
- lib/castkit/ext/data_object/serialization.rb
|
96
114
|
- lib/castkit/inflector.rb
|
97
|
-
- lib/castkit/
|
115
|
+
- lib/castkit/plugins.rb
|
116
|
+
- lib/castkit/serializers/base.rb
|
117
|
+
- lib/castkit/serializers/default_serializer.rb
|
98
118
|
- lib/castkit/types.rb
|
119
|
+
- lib/castkit/types/base.rb
|
99
120
|
- lib/castkit/types/boolean.rb
|
100
121
|
- lib/castkit/types/collection.rb
|
101
122
|
- lib/castkit/types/date.rb
|
102
123
|
- lib/castkit/types/date_time.rb
|
103
124
|
- lib/castkit/types/float.rb
|
104
|
-
- lib/castkit/types/generic.rb
|
105
125
|
- lib/castkit/types/integer.rb
|
106
126
|
- lib/castkit/types/string.rb
|
107
127
|
- lib/castkit/validator.rb
|
108
|
-
- lib/castkit/validators/
|
128
|
+
- lib/castkit/validators/base.rb
|
129
|
+
- lib/castkit/validators/boolean_validator.rb
|
130
|
+
- lib/castkit/validators/collection_validator.rb
|
131
|
+
- lib/castkit/validators/float_validator.rb
|
132
|
+
- lib/castkit/validators/integer_validator.rb
|
109
133
|
- lib/castkit/validators/numeric_validator.rb
|
110
134
|
- lib/castkit/validators/string_validator.rb
|
111
135
|
- lib/castkit/version.rb
|
136
|
+
- lib/generators/base.rb
|
137
|
+
- lib/generators/contract.rb
|
138
|
+
- lib/generators/data_object.rb
|
139
|
+
- lib/generators/plugin.rb
|
140
|
+
- lib/generators/serializer.rb
|
141
|
+
- lib/generators/templates/contract.rb.tt
|
142
|
+
- lib/generators/templates/contract_spec.rb.tt
|
143
|
+
- lib/generators/templates/data_object.rb.tt
|
144
|
+
- lib/generators/templates/data_object_spec.rb.tt
|
145
|
+
- lib/generators/templates/plugin.rb.tt
|
146
|
+
- lib/generators/templates/plugin_spec.rb.tt
|
147
|
+
- lib/generators/templates/serializer.rb.tt
|
148
|
+
- lib/generators/templates/serializer_spec.rb.tt
|
149
|
+
- lib/generators/templates/type.rb.tt
|
150
|
+
- lib/generators/templates/type_spec.rb.tt
|
151
|
+
- lib/generators/templates/validator.rb.tt
|
152
|
+
- lib/generators/templates/validator_spec.rb.tt
|
153
|
+
- lib/generators/type.rb
|
154
|
+
- lib/generators/validator.rb
|
112
155
|
- sig/castkit.rbs
|
113
156
|
homepage: https://github.com/bnlucas/castkit
|
114
157
|
licenses:
|
@@ -1,154 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require_relative "serializer"
|
4
|
-
|
5
|
-
module Castkit
|
6
|
-
# Default serializer for Castkit::DataObject instances.
|
7
|
-
#
|
8
|
-
# Serializes attributes into a plain Ruby hash, applying access rules, nil/blank filtering,
|
9
|
-
# and nested structure handling. The output format supports JSON-compatible structures
|
10
|
-
# and respects the class-level serialization configuration.
|
11
|
-
class DefaultSerializer < Castkit::Serializer
|
12
|
-
# @return [Hash{Symbol => Castkit::Attribute}] the attributes to serialize
|
13
|
-
attr_reader :attributes
|
14
|
-
|
15
|
-
# @return [Hash{Symbol => Object}] unrecognized attributes captured during deserialization
|
16
|
-
attr_reader :unknown_attributes
|
17
|
-
|
18
|
-
# @return [Hash] serialization config flags like :root, :ignore_nil, :allow_unknown
|
19
|
-
attr_reader :options
|
20
|
-
|
21
|
-
# Serializes the object to a hash.
|
22
|
-
#
|
23
|
-
# Includes unknown attributes if configured, and wraps in a root key if defined.
|
24
|
-
#
|
25
|
-
# @return [Hash] the fully serialized result
|
26
|
-
def call
|
27
|
-
result = serialize_attributes
|
28
|
-
result.merge!(unknown_attributes) if options[:allow_unknown]
|
29
|
-
|
30
|
-
options[:root] ? { options[:root].to_sym => result } : result
|
31
|
-
end
|
32
|
-
|
33
|
-
private
|
34
|
-
|
35
|
-
# Initializes the serializer.
|
36
|
-
#
|
37
|
-
# @param obj [Castkit::DataObject] the object to serialize
|
38
|
-
# @param visited [Set, nil] tracks circular references
|
39
|
-
def initialize(obj, visited: nil)
|
40
|
-
super
|
41
|
-
|
42
|
-
@skip_flag = "__castkit_#{obj.object_id}"
|
43
|
-
@attributes = obj.class.attributes.freeze
|
44
|
-
@unknown_attributes = obj.unknown_attributes.freeze
|
45
|
-
@options = {
|
46
|
-
root: obj.class.root,
|
47
|
-
ignore_nil: obj.class.ignore_nil || false,
|
48
|
-
allow_unknown: obj.class.allow_unknown || false
|
49
|
-
}
|
50
|
-
end
|
51
|
-
|
52
|
-
# Serializes all defined attributes.
|
53
|
-
#
|
54
|
-
# @return [Hash] serialized attribute key-value pairs
|
55
|
-
def serialize_attributes
|
56
|
-
attributes.values.each_with_object({}) do |attribute, hash|
|
57
|
-
next if attribute.skip_serialization?
|
58
|
-
|
59
|
-
serialized_value = serialize_attribute(attribute)
|
60
|
-
next if serialized_value == @skip_flag
|
61
|
-
|
62
|
-
assign_attribute_key!(attribute, serialized_value, hash)
|
63
|
-
end
|
64
|
-
end
|
65
|
-
|
66
|
-
# Serializes a single attribute.
|
67
|
-
#
|
68
|
-
# @param attribute [Castkit::Attribute]
|
69
|
-
# @return [Object] the serialized value or skip flag
|
70
|
-
def serialize_attribute(attribute)
|
71
|
-
value = obj.public_send(attribute.field)
|
72
|
-
return @skip_flag if skip_nil?(attribute, value)
|
73
|
-
|
74
|
-
serialized_value = process_attribute(attribute, value)
|
75
|
-
return @skip_flag if skip_blank?(attribute, serialized_value)
|
76
|
-
|
77
|
-
serialized_value
|
78
|
-
end
|
79
|
-
|
80
|
-
# Delegates serialization based on type.
|
81
|
-
#
|
82
|
-
# @param attribute [Castkit::Attribute]
|
83
|
-
# @param value [Object]
|
84
|
-
# @return [Object]
|
85
|
-
def process_attribute(attribute, value)
|
86
|
-
if attribute.dataobject?
|
87
|
-
serialize_dataobject(attribute, value)
|
88
|
-
elsif attribute.dataobject_collection?
|
89
|
-
Array(value).map { |v| serialize_dataobject(attribute, v) }
|
90
|
-
else
|
91
|
-
type = Array(attribute.type).first
|
92
|
-
Castkit.type_serializer(type).call(value)
|
93
|
-
end
|
94
|
-
end
|
95
|
-
|
96
|
-
# Assigns value into nested hash structure based on key path.
|
97
|
-
#
|
98
|
-
# @param attribute [Castkit::Attribute]
|
99
|
-
# @param value [Object]
|
100
|
-
# @param hash [Hash]
|
101
|
-
# @return [void]
|
102
|
-
def assign_attribute_key!(attribute, value, hash)
|
103
|
-
key_path = attribute.key_path
|
104
|
-
last = key_path.pop
|
105
|
-
current = hash
|
106
|
-
|
107
|
-
key_path.each do |key|
|
108
|
-
current[key] ||= {}
|
109
|
-
current = current[key]
|
110
|
-
end
|
111
|
-
|
112
|
-
current[last] = value
|
113
|
-
end
|
114
|
-
|
115
|
-
# Whether to skip serialization for nil values.
|
116
|
-
#
|
117
|
-
# @param attribute [Castkit::Attribute]
|
118
|
-
# @param value [Object]
|
119
|
-
# @return [Boolean]
|
120
|
-
def skip_nil?(attribute, value)
|
121
|
-
value.nil? && (attribute.ignore_nil? || options[:ignore_nil])
|
122
|
-
end
|
123
|
-
|
124
|
-
# Whether to skip serialization for blank values.
|
125
|
-
#
|
126
|
-
# @param attribute [Castkit::Attribute]
|
127
|
-
# @param value [Object]
|
128
|
-
# @return [Boolean]
|
129
|
-
def skip_blank?(attribute, value)
|
130
|
-
blank?(value) && (attribute.ignore_blank? || options[:ignore_blank])
|
131
|
-
end
|
132
|
-
|
133
|
-
# True if value is nil or empty.
|
134
|
-
#
|
135
|
-
# @param value [Object]
|
136
|
-
# @return [Boolean]
|
137
|
-
def blank?(value)
|
138
|
-
value.nil? || (value.respond_to?(:empty?) && value.empty?)
|
139
|
-
end
|
140
|
-
|
141
|
-
# Serializes a DataObject using the proper serializer.
|
142
|
-
#
|
143
|
-
# @param attribute [Castkit::Attribute]
|
144
|
-
# @param value [Castkit::DataObject]
|
145
|
-
# @return [Object]
|
146
|
-
def serialize_dataobject(attribute, value)
|
147
|
-
serializer = attribute.options[:serializer]
|
148
|
-
serializer ||= value.class.serializer
|
149
|
-
serializer ||= Castkit::DefaultSerializer
|
150
|
-
|
151
|
-
serializer.call(value, visited: visited)
|
152
|
-
end
|
153
|
-
end
|
154
|
-
end
|
data/lib/castkit/serializer.rb
DELETED
@@ -1,92 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require "set"
|
4
|
-
|
5
|
-
module Castkit
|
6
|
-
# Abstract base class for defining custom serializers for Castkit::DataObject instances.
|
7
|
-
#
|
8
|
-
# Handles circular reference detection and provides a consistent `call` API.
|
9
|
-
#
|
10
|
-
# Subclasses must implement an instance method `#call` that returns a hash-like representation.
|
11
|
-
#
|
12
|
-
# @example Usage
|
13
|
-
# class CustomSerializer < Castkit::Serializer
|
14
|
-
# private
|
15
|
-
#
|
16
|
-
# def call
|
17
|
-
# { type: obj.class.name, data: obj.to_h }
|
18
|
-
# end
|
19
|
-
# end
|
20
|
-
#
|
21
|
-
# CustomSerializer.call(user_dto)
|
22
|
-
class Serializer
|
23
|
-
class << self
|
24
|
-
# Entrypoint for serializing an object.
|
25
|
-
#
|
26
|
-
# @param obj [Castkit::DataObject] the object to serialize
|
27
|
-
# @param visited [Set, nil] used to track visited object IDs
|
28
|
-
# @return [Object] result of custom serialization
|
29
|
-
def call(obj, visited: nil)
|
30
|
-
new(obj, visited: visited).send(:serialize)
|
31
|
-
end
|
32
|
-
end
|
33
|
-
|
34
|
-
# @return [Castkit::DataObject] the object being serialized
|
35
|
-
attr_reader :obj
|
36
|
-
|
37
|
-
protected
|
38
|
-
|
39
|
-
# Fallback to the default serializer.
|
40
|
-
#
|
41
|
-
# @return [Hash]
|
42
|
-
def serialize_with_default
|
43
|
-
Castkit::DefaultSerializer.call(obj, visited: visited)
|
44
|
-
end
|
45
|
-
|
46
|
-
private
|
47
|
-
|
48
|
-
# @return [Set<Integer>] a set of visited object IDs to detect circular references
|
49
|
-
attr_reader :visited
|
50
|
-
|
51
|
-
# Initializes the serializer instance.
|
52
|
-
#
|
53
|
-
# @param obj [Castkit::DataObject]
|
54
|
-
# @param visited [Set, nil]
|
55
|
-
def initialize(obj, visited: nil)
|
56
|
-
@obj = obj
|
57
|
-
@visited = visited || Set.new
|
58
|
-
end
|
59
|
-
|
60
|
-
# Subclasses must override this method to implement serialization logic.
|
61
|
-
#
|
62
|
-
# @raise [NotImplementedError]
|
63
|
-
# @return [Object]
|
64
|
-
def call
|
65
|
-
raise NotImplementedError, "#{self.class.name} must implement `#call`"
|
66
|
-
end
|
67
|
-
|
68
|
-
# Wraps the actual serialization logic with circular reference detection.
|
69
|
-
#
|
70
|
-
# @return [Object]
|
71
|
-
# @raise [Castkit::SerializationError] if a circular reference is detected
|
72
|
-
def serialize
|
73
|
-
check_circular_reference!
|
74
|
-
visited << obj.object_id
|
75
|
-
|
76
|
-
result = call
|
77
|
-
visited.delete(obj.object_id)
|
78
|
-
|
79
|
-
result
|
80
|
-
end
|
81
|
-
|
82
|
-
# Raises if the object has already been visited (circular reference).
|
83
|
-
#
|
84
|
-
# @raise [Castkit::SerializationError]
|
85
|
-
# @return [void]
|
86
|
-
def check_circular_reference!
|
87
|
-
return unless visited.include?(obj.object_id)
|
88
|
-
|
89
|
-
raise Castkit::SerializationError, "Circular reference detected for #{obj.class}"
|
90
|
-
end
|
91
|
-
end
|
92
|
-
end
|
@@ -1,39 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module Castkit
|
4
|
-
module Validators
|
5
|
-
# Abstract base class for all attribute validators.
|
6
|
-
#
|
7
|
-
# Validators ensure that a value conforms to specific rules (e.g., type, format, range).
|
8
|
-
# Subclasses must implement the instance method `#call`.
|
9
|
-
#
|
10
|
-
# @abstract
|
11
|
-
class BaseValidator
|
12
|
-
class << self
|
13
|
-
# Invokes the validator with the given arguments.
|
14
|
-
#
|
15
|
-
# @param value [Object] the attribute value to validate
|
16
|
-
# @param options [Hash] the attribute options (e.g., `min`, `max`, `format`)
|
17
|
-
# @param context [Symbol, String, Hash] the attribute name or context for error reporting
|
18
|
-
# @return [void]
|
19
|
-
# @raise [Castkit::AttributeError] if validation fails
|
20
|
-
def call(value, options:, context:)
|
21
|
-
new.call(value, options: options, context: context)
|
22
|
-
end
|
23
|
-
end
|
24
|
-
|
25
|
-
# Validates the attribute value.
|
26
|
-
#
|
27
|
-
# @abstract Override in subclasses.
|
28
|
-
#
|
29
|
-
# @param value [Object] the attribute value to validate
|
30
|
-
# @param options [Hash] the attribute options
|
31
|
-
# @param context [Symbol, String, Hash] the attribute name or context
|
32
|
-
# @return [void]
|
33
|
-
# @raise [NotImplementedError] unless implemented in a subclass
|
34
|
-
def call(value, options:, context:)
|
35
|
-
raise NotImplementedError, "#{self.class.name} must implement `#call`"
|
36
|
-
end
|
37
|
-
end
|
38
|
-
end
|
39
|
-
end
|