castkit 0.1.2 → 0.2.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 +196 -219
- data/CHANGELOG.md +42 -0
- data/README.md +469 -84
- data/lib/castkit/attribute.rb +6 -24
- data/lib/castkit/castkit.rb +58 -10
- data/lib/castkit/configuration.rb +94 -47
- data/lib/castkit/contract/data_object.rb +62 -0
- data/lib/castkit/contract/generic.rb +168 -0
- data/lib/castkit/contract/result.rb +74 -0
- data/lib/castkit/contract/validator.rb +248 -0
- data/lib/castkit/contract.rb +67 -0
- data/lib/castkit/{data_object_extensions → core}/attribute_types.rb +21 -7
- data/lib/castkit/{data_object_extensions → core}/attributes.rb +8 -3
- data/lib/castkit/core/config.rb +74 -0
- data/lib/castkit/core/registerable.rb +59 -0
- data/lib/castkit/data_object.rb +45 -60
- data/lib/castkit/default_serializer.rb +85 -54
- data/lib/castkit/error.rb +15 -3
- data/lib/castkit/ext/attribute/access.rb +67 -0
- data/lib/castkit/ext/attribute/error_handling.rb +63 -0
- data/lib/castkit/ext/attribute/options.rb +142 -0
- data/lib/castkit/ext/attribute/validation.rb +85 -0
- data/lib/castkit/ext/data_object/contract.rb +96 -0
- data/lib/castkit/ext/data_object/deserialization.rb +167 -0
- data/lib/castkit/ext/data_object/serialization.rb +61 -0
- data/lib/castkit/inflector.rb +47 -0
- data/lib/castkit/types/boolean.rb +43 -0
- data/lib/castkit/types/collection.rb +24 -0
- data/lib/castkit/types/date.rb +34 -0
- data/lib/castkit/types/date_time.rb +34 -0
- data/lib/castkit/types/float.rb +46 -0
- data/lib/castkit/types/generic.rb +123 -0
- data/lib/castkit/types/integer.rb +46 -0
- data/lib/castkit/types/string.rb +44 -0
- data/lib/castkit/types.rb +15 -0
- data/lib/castkit/validators/base_validator.rb +39 -0
- data/lib/castkit/validators/numeric_validator.rb +2 -2
- data/lib/castkit/validators/string_validator.rb +3 -3
- data/lib/castkit/version.rb +1 -1
- data/lib/castkit.rb +2 -0
- metadata +29 -13
- data/lib/castkit/attribute_extensions/access.rb +0 -65
- data/lib/castkit/attribute_extensions/casting.rb +0 -147
- data/lib/castkit/attribute_extensions/error_handling.rb +0 -83
- data/lib/castkit/attribute_extensions/options.rb +0 -131
- data/lib/castkit/attribute_extensions/serialization.rb +0 -89
- data/lib/castkit/attribute_extensions/validation.rb +0 -72
- data/lib/castkit/data_object_extensions/config.rb +0 -113
- data/lib/castkit/data_object_extensions/deserialization.rb +0 -110
- data/lib/castkit/validators.rb +0 -4
@@ -1,131 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require_relative "../data_object"
|
4
|
-
|
5
|
-
module Castkit
|
6
|
-
module AttributeExtensions
|
7
|
-
# Provides access to normalized attribute options and helper predicates.
|
8
|
-
#
|
9
|
-
# These methods support Castkit attribute behavior such as default values,
|
10
|
-
# key mapping, optionality, and structural roles (e.g. composite or unwrapped).
|
11
|
-
module Options
|
12
|
-
# Default options for attributes.
|
13
|
-
#
|
14
|
-
# @return [Hash<Symbol, Object>]
|
15
|
-
DEFAULT_OPTIONS = {
|
16
|
-
required: true,
|
17
|
-
ignore_nil: false,
|
18
|
-
ignore_blank: false,
|
19
|
-
ignore: false,
|
20
|
-
composite: false,
|
21
|
-
unwrapped: false,
|
22
|
-
prefix: nil,
|
23
|
-
access: %i[read write]
|
24
|
-
}.freeze
|
25
|
-
|
26
|
-
# Returns the default value for the attribute.
|
27
|
-
#
|
28
|
-
# If the default is callable, it is invoked.
|
29
|
-
#
|
30
|
-
# @return [Object]
|
31
|
-
def default
|
32
|
-
val = @default
|
33
|
-
val.respond_to?(:call) ? val.call : val
|
34
|
-
end
|
35
|
-
|
36
|
-
# Returns the serialization/deserialization key.
|
37
|
-
#
|
38
|
-
# Falls back to the field name if `:key` is not specified.
|
39
|
-
#
|
40
|
-
# @return [Symbol, String]
|
41
|
-
def key
|
42
|
-
options[:key] || field
|
43
|
-
end
|
44
|
-
|
45
|
-
# Returns the key path for accessing nested keys.
|
46
|
-
#
|
47
|
-
# Optionally includes alias key paths if `with_aliases` is true.
|
48
|
-
#
|
49
|
-
# @param with_aliases [Boolean]
|
50
|
-
# @return [Array<Array<Symbol>>] nested key paths
|
51
|
-
def key_path(with_aliases: false)
|
52
|
-
path = key.to_s.split(".").map(&:to_sym) || []
|
53
|
-
return path unless with_aliases
|
54
|
-
|
55
|
-
[path] + alias_paths
|
56
|
-
end
|
57
|
-
|
58
|
-
# Returns all alias key paths as arrays of symbols.
|
59
|
-
#
|
60
|
-
# @return [Array<Array<Symbol>>]
|
61
|
-
def alias_paths
|
62
|
-
options[:aliases].map { |a| a.to_s.split(".").map(&:to_sym) }
|
63
|
-
end
|
64
|
-
|
65
|
-
# Whether the attribute is required for object construction.
|
66
|
-
#
|
67
|
-
# @return [Boolean]
|
68
|
-
def required?
|
69
|
-
options[:required]
|
70
|
-
end
|
71
|
-
|
72
|
-
# Whether the attribute is optional.
|
73
|
-
#
|
74
|
-
# @return [Boolean]
|
75
|
-
def optional?
|
76
|
-
!required?
|
77
|
-
end
|
78
|
-
|
79
|
-
# Whether to ignore `nil` values during serialization.
|
80
|
-
#
|
81
|
-
# @return [Boolean]
|
82
|
-
def ignore_nil?
|
83
|
-
options[:ignore_nil]
|
84
|
-
end
|
85
|
-
|
86
|
-
# Whether to ignore blank values (`[]`, `{}`, empty strings) during serialization.
|
87
|
-
#
|
88
|
-
# @return [Boolean]
|
89
|
-
def ignore_blank?
|
90
|
-
options[:ignore_blank]
|
91
|
-
end
|
92
|
-
|
93
|
-
# Whether the attribute is a nested Castkit::DataObject.
|
94
|
-
#
|
95
|
-
# @return [Boolean]
|
96
|
-
def dataobject?
|
97
|
-
Castkit.dataobject?(type)
|
98
|
-
end
|
99
|
-
|
100
|
-
# Whether the attribute is a collection of Castkit::DataObjects.
|
101
|
-
#
|
102
|
-
# @return [Boolean]
|
103
|
-
def dataobject_collection?
|
104
|
-
type == :array && Castkit.dataobject?(options[:of])
|
105
|
-
end
|
106
|
-
|
107
|
-
# Whether the attribute is considered composite (not exposed in serialized output).
|
108
|
-
#
|
109
|
-
# @return [Boolean]
|
110
|
-
def composite?
|
111
|
-
options[:composite]
|
112
|
-
end
|
113
|
-
|
114
|
-
# Whether the attribute is unwrapped into the parent object.
|
115
|
-
#
|
116
|
-
# Only applies to Castkit::DataObject types.
|
117
|
-
#
|
118
|
-
# @return [Boolean]
|
119
|
-
def unwrapped?
|
120
|
-
dataobject? && options[:unwrapped]
|
121
|
-
end
|
122
|
-
|
123
|
-
# Returns the prefix used for unwrapped attributes.
|
124
|
-
#
|
125
|
-
# @return [String, nil]
|
126
|
-
def prefix
|
127
|
-
options[:prefix]
|
128
|
-
end
|
129
|
-
end
|
130
|
-
end
|
131
|
-
end
|
@@ -1,89 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module Castkit
|
4
|
-
module AttributeExtensions
|
5
|
-
# Handles serialization (`dump`) and deserialization (`load`) of attribute values.
|
6
|
-
#
|
7
|
-
# Supports primitive types, arrays, and nested Castkit::DataObject instances.
|
8
|
-
module Serialization
|
9
|
-
# Serializes a value into a format suitable for output (e.g., JSON or Hash).
|
10
|
-
#
|
11
|
-
# If the value is a Castkit::DataObject, a custom serializer is used if configured.
|
12
|
-
#
|
13
|
-
# @param value [Object, nil] the value to serialize
|
14
|
-
# @param visited [Set, nil] used for circular reference detection
|
15
|
-
# @return [Object, nil] the serialized value
|
16
|
-
def dump(value, visited: nil)
|
17
|
-
return value if value.nil?
|
18
|
-
|
19
|
-
if type == :array
|
20
|
-
Array(value).map { |val| dump_element(val, visited: visited) }
|
21
|
-
else
|
22
|
-
dump_element(value, visited: visited)
|
23
|
-
end
|
24
|
-
end
|
25
|
-
|
26
|
-
# Deserializes and validates a value during object instantiation.
|
27
|
-
#
|
28
|
-
# Applies default value, casts, and runs validators.
|
29
|
-
#
|
30
|
-
# @param value [Object] the input value
|
31
|
-
# @param context [Symbol] the attribute name or context key
|
32
|
-
# @return [Object] the deserialized and validated value
|
33
|
-
# @raise [Castkit::AttributeError] if value is required but missing
|
34
|
-
def load(value, context:)
|
35
|
-
value = default if value.nil?
|
36
|
-
return raise_error!("#{field} is required for instantiation") if value.nil? && required?
|
37
|
-
|
38
|
-
value = cast(value)
|
39
|
-
validate_value!(value, context: context)
|
40
|
-
|
41
|
-
value
|
42
|
-
end
|
43
|
-
|
44
|
-
private
|
45
|
-
|
46
|
-
# Serializes a single element value.
|
47
|
-
#
|
48
|
-
# - Uses a serializer if the value is a Castkit::DataObject.
|
49
|
-
# - Converts `to_h` if the value is hash-like.
|
50
|
-
#
|
51
|
-
# @param value [Object, nil] the element to dump
|
52
|
-
# @param visited [Set, nil]
|
53
|
-
# @return [Object, nil]
|
54
|
-
def dump_element(value, visited: nil)
|
55
|
-
return value if value.nil? || primitive?(value)
|
56
|
-
|
57
|
-
if value.is_a?(Castkit::DataObject)
|
58
|
-
serializer = options[:serializer] || value.class.serializer || Castkit::DefaultSerializer
|
59
|
-
serializer.call(value, visited: visited)
|
60
|
-
elsif hashable?(value)
|
61
|
-
value.to_h(visited)
|
62
|
-
else
|
63
|
-
value
|
64
|
-
end
|
65
|
-
end
|
66
|
-
|
67
|
-
# Checks whether a value is a hashable object suitable for `to_h` dumping.
|
68
|
-
#
|
69
|
-
# @param value [Object, nil]
|
70
|
-
# @return [Boolean]
|
71
|
-
def hashable?(value)
|
72
|
-
value.respond_to?(:to_h) && !primitive?(value) && !value.is_a?(Castkit::Attribute)
|
73
|
-
end
|
74
|
-
|
75
|
-
# Determines if a value is a primitive type.
|
76
|
-
#
|
77
|
-
# @param value [Object, nil]
|
78
|
-
# @return [Boolean]
|
79
|
-
def primitive?(value)
|
80
|
-
case value
|
81
|
-
when String, Symbol, Numeric, TrueClass, FalseClass, NilClass, Hash, Array
|
82
|
-
true
|
83
|
-
else
|
84
|
-
false
|
85
|
-
end
|
86
|
-
end
|
87
|
-
end
|
88
|
-
end
|
89
|
-
end
|
@@ -1,72 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require_relative "error_handling"
|
4
|
-
require_relative "options"
|
5
|
-
|
6
|
-
module Castkit
|
7
|
-
module AttributeExtensions
|
8
|
-
# Provides validation logic for attribute configuration.
|
9
|
-
#
|
10
|
-
# These checks are typically performed at attribute initialization to catch misconfigurations early.
|
11
|
-
module Validation
|
12
|
-
include Castkit::AttributeExtensions::ErrorHandling
|
13
|
-
|
14
|
-
private
|
15
|
-
|
16
|
-
# Runs all validation checks on the attribute definition.
|
17
|
-
#
|
18
|
-
# This includes:
|
19
|
-
# - Custom validator integrity
|
20
|
-
# - Access mode validity
|
21
|
-
# - Unwrapped prefix usage
|
22
|
-
# - Array `of:` type presence
|
23
|
-
#
|
24
|
-
# @return [void]
|
25
|
-
def validate!
|
26
|
-
validate_custom_validator!
|
27
|
-
validate_access!
|
28
|
-
validate_unwrapped_options!
|
29
|
-
validate_array_options!
|
30
|
-
end
|
31
|
-
|
32
|
-
# Validates the presence and interface of a custom validator.
|
33
|
-
#
|
34
|
-
# @return [void]
|
35
|
-
# @raise [Castkit::AttributeError] if the validator is not callable
|
36
|
-
def validate_custom_validator!
|
37
|
-
return unless options[:validator]
|
38
|
-
return if options[:validator].respond_to?(:call)
|
39
|
-
|
40
|
-
raise_error!("Custom validator for `#{field}` must respond to `.call`")
|
41
|
-
end
|
42
|
-
|
43
|
-
# Validates that each declared access mode is valid.
|
44
|
-
#
|
45
|
-
# @return [void]
|
46
|
-
# @raise [Castkit::AttributeError] if any access mode is invalid and enforcement is enabled
|
47
|
-
def validate_access!
|
48
|
-
access.each do |mode|
|
49
|
-
next if Castkit::AttributeExtensions::Options::DEFAULT_OPTIONS[:access].include?(mode)
|
50
|
-
|
51
|
-
handle_error(:access, mode: mode, context: to_h)
|
52
|
-
end
|
53
|
-
end
|
54
|
-
|
55
|
-
# Ensures prefix is only used with unwrapped attributes.
|
56
|
-
#
|
57
|
-
# @return [void]
|
58
|
-
# @raise [Castkit::AttributeError] if prefix is used without `unwrapped: true`
|
59
|
-
def validate_unwrapped_options!
|
60
|
-
handle_error(:unwrapped, context: to_h) if prefix && !unwrapped?
|
61
|
-
end
|
62
|
-
|
63
|
-
# Ensures `of:` is provided for array-typed attributes.
|
64
|
-
#
|
65
|
-
# @return [void]
|
66
|
-
# @raise [Castkit::AttributeError] if `of:` is missing for `type: :array`
|
67
|
-
def validate_array_options!
|
68
|
-
handle_error(:array_options, context: to_h) if type == :array && options[:of].nil?
|
69
|
-
end
|
70
|
-
end
|
71
|
-
end
|
72
|
-
end
|
@@ -1,113 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module Castkit
|
4
|
-
module DataObjectExtensions
|
5
|
-
# Provides per-class configuration for a Castkit::DataObject,
|
6
|
-
# including root key handling, strict mode, and unknown key behavior.
|
7
|
-
module Config
|
8
|
-
# Automatically extends class-level methods when included.
|
9
|
-
#
|
10
|
-
# @param base [Class]
|
11
|
-
def self.included(base)
|
12
|
-
base.extend(ClassMethods)
|
13
|
-
end
|
14
|
-
|
15
|
-
# Class-level configuration methods.
|
16
|
-
module ClassMethods
|
17
|
-
# Sets or retrieves the root key to wrap the object under during (de)serialization.
|
18
|
-
#
|
19
|
-
# @param value [String, Symbol, nil] optional root key
|
20
|
-
# @return [Symbol, nil]
|
21
|
-
def root(value = nil)
|
22
|
-
@root = value.to_s.strip.to_sym if value
|
23
|
-
@root
|
24
|
-
end
|
25
|
-
|
26
|
-
# Sets or retrieves whether to skip `nil` values in output.
|
27
|
-
#
|
28
|
-
# @param value [Boolean, nil]
|
29
|
-
# @return [Boolean, nil]
|
30
|
-
def ignore_nil(value = nil)
|
31
|
-
value.nil? ? @ignore_nil : (@ignore_nil = value)
|
32
|
-
end
|
33
|
-
|
34
|
-
# Sets or retrieves whether to skip blank values (`[]`, `{}`, `""`, etc.) in output.
|
35
|
-
#
|
36
|
-
# Defaults to true unless explicitly set to false.
|
37
|
-
#
|
38
|
-
# @param value [Boolean, nil]
|
39
|
-
# @return [Boolean]
|
40
|
-
def ignore_blank(value = nil)
|
41
|
-
@ignore_blank = value.nil? || value
|
42
|
-
end
|
43
|
-
|
44
|
-
# Sets or retrieves strict mode behavior.
|
45
|
-
#
|
46
|
-
# In strict mode, unknown keys during deserialization raise errors.
|
47
|
-
#
|
48
|
-
# @param value [Boolean, nil]
|
49
|
-
# @return [Boolean]
|
50
|
-
def strict(value = nil)
|
51
|
-
if value.nil?
|
52
|
-
@strict.nil? || @strict
|
53
|
-
else
|
54
|
-
@strict = value
|
55
|
-
end
|
56
|
-
end
|
57
|
-
|
58
|
-
# Enables or disables ignoring unknown keys during deserialization.
|
59
|
-
#
|
60
|
-
# This is the inverse of `strict`.
|
61
|
-
#
|
62
|
-
# @param value [Boolean]
|
63
|
-
# @return [void]
|
64
|
-
def ignore_unknown(value = nil)
|
65
|
-
@strict = !value
|
66
|
-
end
|
67
|
-
|
68
|
-
# Sets or retrieves whether to emit warnings when unknown keys are encountered.
|
69
|
-
#
|
70
|
-
# @param value [Boolean, nil]
|
71
|
-
# @return [Boolean, nil]
|
72
|
-
def warn_on_unknown(value = nil)
|
73
|
-
value.nil? ? @warn_on_unknown : (@warn_on_unknown = value)
|
74
|
-
end
|
75
|
-
|
76
|
-
# Sets or retrieves whether to allow unknown keys when they are encountered.
|
77
|
-
#
|
78
|
-
# @param value [Boolean, nil]
|
79
|
-
# @return [Boolean, nil]
|
80
|
-
def allow_unknown(value = nil)
|
81
|
-
value.nil? ? @allow_unknown : (@allow_unknown = value)
|
82
|
-
end
|
83
|
-
|
84
|
-
# Returns a relaxed version of the current class with strict mode off.
|
85
|
-
#
|
86
|
-
# Useful for tolerant parsing scenarios.
|
87
|
-
#
|
88
|
-
# @param warn_on_unknown [Boolean]
|
89
|
-
# @return [Class] a subclass with relaxed rules
|
90
|
-
def relaxed(warn_on_unknown: true)
|
91
|
-
klass = Class.new(self)
|
92
|
-
klass.strict(false)
|
93
|
-
klass.warn_on_unknown(warn_on_unknown)
|
94
|
-
klass
|
95
|
-
end
|
96
|
-
end
|
97
|
-
|
98
|
-
# Returns the root key for this instance.
|
99
|
-
#
|
100
|
-
# @return [Symbol]
|
101
|
-
def root_key
|
102
|
-
self.class.root.to_s.strip.to_sym
|
103
|
-
end
|
104
|
-
|
105
|
-
# Whether a root key is configured for this instance.
|
106
|
-
#
|
107
|
-
# @return [Boolean]
|
108
|
-
def root_key_set?
|
109
|
-
!self.class.root.to_s.strip.empty?
|
110
|
-
end
|
111
|
-
end
|
112
|
-
end
|
113
|
-
end
|
@@ -1,110 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module Castkit
|
4
|
-
module DataObjectExtensions
|
5
|
-
# Adds deserialization support for Castkit::DataObject instances.
|
6
|
-
#
|
7
|
-
# Handles attribute loading, alias resolution, and unwrapped field extraction.
|
8
|
-
module Deserialization
|
9
|
-
# Hooks in class methods like `.from_hash` when included.
|
10
|
-
#
|
11
|
-
# @param base [Class]
|
12
|
-
def self.included(base)
|
13
|
-
base.extend(ClassMethods)
|
14
|
-
end
|
15
|
-
|
16
|
-
# Class-level deserialization helpers.
|
17
|
-
module ClassMethods
|
18
|
-
# Builds a new instance from a Hash, symbolizing keys as needed.
|
19
|
-
#
|
20
|
-
# @param hash [Hash]
|
21
|
-
# @return [Castkit::DataObject]
|
22
|
-
def from_hash(hash)
|
23
|
-
hash = hash.transform_keys { |k| k.respond_to?(:to_sym) ? k.to_sym : k }
|
24
|
-
new(hash)
|
25
|
-
end
|
26
|
-
|
27
|
-
# @!method from_h(hash)
|
28
|
-
# Alias for {.from_hash}
|
29
|
-
#
|
30
|
-
# @!method creator(hash)
|
31
|
-
# Alias for {.from_hash}
|
32
|
-
alias from_h from_hash
|
33
|
-
alias creator from_hash
|
34
|
-
end
|
35
|
-
|
36
|
-
private
|
37
|
-
|
38
|
-
# Loads attribute values from the given hash.
|
39
|
-
#
|
40
|
-
# Respects access control (e.g., `writeable?`) and uses `.load` for casting/validation.
|
41
|
-
#
|
42
|
-
# @param data [Hash]
|
43
|
-
# @return [void]
|
44
|
-
def deserialize_attributes!(data)
|
45
|
-
self.class.attributes.each do |field, attribute|
|
46
|
-
next if attribute.skip_deserialization?
|
47
|
-
|
48
|
-
value = fetch_attribute_key(data, attribute)
|
49
|
-
value = attribute.load(value, context: field)
|
50
|
-
|
51
|
-
instance_variable_set("@#{field}", value)
|
52
|
-
end
|
53
|
-
end
|
54
|
-
|
55
|
-
# Fetches the best matching value from the hash using attribute key and aliases.
|
56
|
-
#
|
57
|
-
# @param data [Hash]
|
58
|
-
# @param attribute [Castkit::Attribute]
|
59
|
-
# @return [Object]
|
60
|
-
def fetch_attribute_key(data, attribute)
|
61
|
-
attribute.key_path(with_aliases: true).each do |path|
|
62
|
-
value = path.reduce(data) { |memo, key| memo.is_a?(Hash) ? memo[key] : nil }
|
63
|
-
return value unless value.nil?
|
64
|
-
end
|
65
|
-
|
66
|
-
nil
|
67
|
-
end
|
68
|
-
|
69
|
-
# Extracts prefixed fields for unwrapped attributes and groups them under the original field key.
|
70
|
-
#
|
71
|
-
# @param data [Hash]
|
72
|
-
# @return [Hash] modified input hash with unwrapped values nested under their base field
|
73
|
-
def unwrap_prefixed_fields!(data)
|
74
|
-
self.class.attributes.each_value do |attribute|
|
75
|
-
next unless attribute.unwrapped?
|
76
|
-
|
77
|
-
unwrapped, keys_to_remove = unwrap_prefixed_values(data, attribute)
|
78
|
-
next if unwrapped.empty?
|
79
|
-
|
80
|
-
data[attribute.field] = unwrapped
|
81
|
-
keys_to_remove.each { |k| data.delete(k) }
|
82
|
-
end
|
83
|
-
|
84
|
-
data
|
85
|
-
end
|
86
|
-
|
87
|
-
# Returns the prefixed key-value pairs for a given unwrapped attribute.
|
88
|
-
#
|
89
|
-
# @param data [Hash]
|
90
|
-
# @param attribute [Castkit::Attribute]
|
91
|
-
# @return [Array<Hash, Array<Symbol>>] extracted key-value pairs and keys to delete
|
92
|
-
def unwrap_prefixed_values(data, attribute)
|
93
|
-
prefix = attribute.prefix.to_s
|
94
|
-
unwrapped_data = {}
|
95
|
-
keys_to_remove = []
|
96
|
-
|
97
|
-
data.each do |k, v|
|
98
|
-
k_str = k.to_s
|
99
|
-
next unless k_str.start_with?(prefix)
|
100
|
-
|
101
|
-
stripped = k_str.sub(prefix, "").to_sym
|
102
|
-
unwrapped_data[stripped] = v
|
103
|
-
keys_to_remove << k
|
104
|
-
end
|
105
|
-
|
106
|
-
[unwrapped_data, keys_to_remove]
|
107
|
-
end
|
108
|
-
end
|
109
|
-
end
|
110
|
-
end
|
data/lib/castkit/validators.rb
DELETED