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.
Files changed (61) hide show
  1. checksums.yaml +4 -4
  2. data/.rspec_status +118 -119
  3. data/CHANGELOG.md +1 -1
  4. data/README.md +287 -11
  5. data/castkit.gemspec +1 -0
  6. data/lib/castkit/castkit.rb +5 -2
  7. data/lib/castkit/cli/generate.rb +98 -0
  8. data/lib/castkit/cli/list.rb +200 -0
  9. data/lib/castkit/cli/main.rb +43 -0
  10. data/lib/castkit/cli.rb +24 -0
  11. data/lib/castkit/configuration.rb +31 -8
  12. data/lib/castkit/contract/{generic.rb → base.rb} +5 -5
  13. data/lib/castkit/contract/result.rb +2 -2
  14. data/lib/castkit/contract.rb +5 -5
  15. data/lib/castkit/data_object.rb +11 -7
  16. data/lib/castkit/ext/data_object/contract.rb +1 -1
  17. data/lib/castkit/ext/data_object/plugins.rb +86 -0
  18. data/lib/castkit/inflector.rb +1 -1
  19. data/lib/castkit/plugins.rb +82 -0
  20. data/lib/castkit/serializers/base.rb +94 -0
  21. data/lib/castkit/serializers/default_serializer.rb +156 -0
  22. data/lib/castkit/types/{generic.rb → base.rb} +6 -7
  23. data/lib/castkit/types/boolean.rb +14 -10
  24. data/lib/castkit/types/collection.rb +13 -2
  25. data/lib/castkit/types/date.rb +2 -2
  26. data/lib/castkit/types/date_time.rb +2 -2
  27. data/lib/castkit/types/float.rb +5 -5
  28. data/lib/castkit/types/integer.rb +5 -5
  29. data/lib/castkit/types/string.rb +2 -2
  30. data/lib/castkit/types.rb +1 -1
  31. data/lib/castkit/validators/base.rb +59 -0
  32. data/lib/castkit/validators/boolean_validator.rb +39 -0
  33. data/lib/castkit/validators/collection_validator.rb +29 -0
  34. data/lib/castkit/validators/float_validator.rb +31 -0
  35. data/lib/castkit/validators/integer_validator.rb +31 -0
  36. data/lib/castkit/validators/numeric_validator.rb +2 -2
  37. data/lib/castkit/validators/string_validator.rb +3 -4
  38. data/lib/castkit/version.rb +1 -1
  39. data/lib/generators/base.rb +97 -0
  40. data/lib/generators/contract.rb +68 -0
  41. data/lib/generators/data_object.rb +48 -0
  42. data/lib/generators/plugin.rb +25 -0
  43. data/lib/generators/serializer.rb +28 -0
  44. data/lib/generators/templates/contract.rb.tt +24 -0
  45. data/lib/generators/templates/contract_spec.rb.tt +76 -0
  46. data/lib/generators/templates/data_object.rb.tt +15 -0
  47. data/lib/generators/templates/data_object_spec.rb.tt +36 -0
  48. data/lib/generators/templates/plugin.rb.tt +37 -0
  49. data/lib/generators/templates/plugin_spec.rb.tt +18 -0
  50. data/lib/generators/templates/serializer.rb.tt +24 -0
  51. data/lib/generators/templates/serializer_spec.rb.tt +14 -0
  52. data/lib/generators/templates/type.rb.tt +55 -0
  53. data/lib/generators/templates/type_spec.rb.tt +42 -0
  54. data/lib/generators/templates/validator.rb.tt +26 -0
  55. data/lib/generators/templates/validator_spec.rb.tt +23 -0
  56. data/lib/generators/type.rb +29 -0
  57. data/lib/generators/validator.rb +41 -0
  58. metadata +50 -7
  59. data/lib/castkit/default_serializer.rb +0 -154
  60. data/lib/castkit/serializer.rb +0 -92
  61. data/lib/castkit/validators/base_validator.rb +0 -39
@@ -0,0 +1,156 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "base"
4
+
5
+ module Castkit
6
+ module Serializers
7
+ # Default serializer for Castkit::DataObject instances.
8
+ #
9
+ # Serializes attributes into a plain Ruby hash, applying access rules, nil/blank filtering,
10
+ # and nested structure handling. The output format supports JSON-compatible structures
11
+ # and respects the class-level serialization configuration.
12
+ class DefaultSerializer < Castkit::Serializers::Base
13
+ # @return [Hash{Symbol => Castkit::Attribute}] the attributes to serialize
14
+ attr_reader :attributes
15
+
16
+ # @return [Hash{Symbol => Object}] unrecognized attributes captured during deserialization
17
+ attr_reader :unknown_attributes
18
+
19
+ # @return [Hash] serialization config flags like :root, :ignore_nil, :allow_unknown
20
+ attr_reader :options
21
+
22
+ # Serializes the object to a hash.
23
+ #
24
+ # Includes unknown attributes if configured, and wraps in a root key if defined.
25
+ #
26
+ # @return [Hash] the fully serialized result
27
+ def call
28
+ result = serialize_attributes
29
+ result.merge!(unknown_attributes) if options[:allow_unknown]
30
+
31
+ options[:root] ? { options[:root].to_sym => result } : result
32
+ end
33
+
34
+ private
35
+
36
+ # Initializes the serializer.
37
+ #
38
+ # @param object [Castkit::DataObject] the object to serialize
39
+ # @param visited [Set, nil] tracks circular references
40
+ def initialize(object, visited: nil)
41
+ super
42
+
43
+ @skip_flag = "__castkit_#{object.object_id}"
44
+ @attributes = object.class.attributes.freeze
45
+ @unknown_attributes = object.unknown_attributes.freeze
46
+ @options = {
47
+ root: object.class.root,
48
+ ignore_nil: object.class.ignore_nil || false,
49
+ allow_unknown: object.class.allow_unknown || false
50
+ }
51
+ end
52
+
53
+ # Serializes all defined attributes.
54
+ #
55
+ # @return [Hash] serialized attribute key-value pairs
56
+ def serialize_attributes
57
+ attributes.values.each_with_object({}) do |attribute, hash|
58
+ next if attribute.skip_serialization?
59
+
60
+ serialized_value = serialize_attribute(attribute)
61
+ next if serialized_value == @skip_flag
62
+
63
+ assign_attribute_key!(attribute, serialized_value, hash)
64
+ end
65
+ end
66
+
67
+ # Serializes a single attribute.
68
+ #
69
+ # @param attribute [Castkit::Attribute]
70
+ # @return [Object] the serialized value or skip flag
71
+ def serialize_attribute(attribute)
72
+ value = object.public_send(attribute.field)
73
+ return @skip_flag if skip_nil?(attribute, value)
74
+
75
+ serialized_value = process_attribute(attribute, value)
76
+ return @skip_flag if skip_blank?(attribute, serialized_value)
77
+
78
+ serialized_value
79
+ end
80
+
81
+ # Delegates serialization based on type.
82
+ #
83
+ # @param attribute [Castkit::Attribute]
84
+ # @param value [Object]
85
+ # @return [Object]
86
+ def process_attribute(attribute, value)
87
+ if attribute.dataobject?
88
+ serialize_dataobject(attribute, value)
89
+ elsif attribute.dataobject_collection?
90
+ Array(value).map { |v| serialize_dataobject(attribute, v) }
91
+ else
92
+ type = Array(attribute.type).first
93
+ Castkit.type_serializer(type).call(value)
94
+ end
95
+ end
96
+
97
+ # Assigns value into nested hash structure based on key path.
98
+ #
99
+ # @param attribute [Castkit::Attribute]
100
+ # @param value [Object]
101
+ # @param hash [Hash]
102
+ # @return [void]
103
+ def assign_attribute_key!(attribute, value, hash)
104
+ key_path = attribute.key_path
105
+ last = key_path.pop
106
+ current = hash
107
+
108
+ key_path.each do |key|
109
+ current[key] ||= {}
110
+ current = current[key]
111
+ end
112
+
113
+ current[last] = value
114
+ end
115
+
116
+ # Whether to skip serialization for nil values.
117
+ #
118
+ # @param attribute [Castkit::Attribute]
119
+ # @param value [Object]
120
+ # @return [Boolean]
121
+ def skip_nil?(attribute, value)
122
+ value.nil? && (attribute.ignore_nil? || options[:ignore_nil])
123
+ end
124
+
125
+ # Whether to skip serialization for blank values.
126
+ #
127
+ # @param attribute [Castkit::Attribute]
128
+ # @param value [Object]
129
+ # @return [Boolean]
130
+ def skip_blank?(attribute, value)
131
+ blank?(value) && (attribute.ignore_blank? || options[:ignore_blank])
132
+ end
133
+
134
+ # True if value is nil or empty.
135
+ #
136
+ # @param value [Object]
137
+ # @return [Boolean]
138
+ def blank?(value)
139
+ value.nil? || (value.respond_to?(:empty?) && value.empty?)
140
+ end
141
+
142
+ # Serializes a DataObject using the proper serializer.
143
+ #
144
+ # @param attribute [Castkit::Attribute]
145
+ # @param value [Castkit::DataObject]
146
+ # @return [Object]
147
+ def serialize_dataobject(attribute, value)
148
+ serializer = attribute.options[:serializer]
149
+ serializer ||= value.class.serializer
150
+ serializer ||= Castkit::Serializers::DefaultSerializer
151
+
152
+ serializer.call(value, visited: visited)
153
+ end
154
+ end
155
+ end
156
+ end
@@ -2,14 +2,14 @@
2
2
 
3
3
  module Castkit
4
4
  module Types
5
- # Generic base class for type definitions in Castkit.
5
+ # Abstract base class for type definitions in Castkit.
6
6
  #
7
7
  # Provides default behavior for (de)serialization, validation, and coercion.
8
8
  # All primitive types should subclass this and override methods as needed.
9
9
  #
10
10
  # The `cast!` method is the primary entry point used by attribute processing
11
11
  # to validate and coerce values in a predictable order.
12
- class Generic
12
+ class Base
13
13
  class << self
14
14
  # Coerces and validates a value for use in a Castkit DataObject.
15
15
  #
@@ -69,7 +69,7 @@ module Castkit
69
69
 
70
70
  # Builds a default validator from the instance itself.
71
71
  #
72
- # @param instance [Castkit::Types::Generic]
72
+ # @param instance [Castkit::Types::Base]
73
73
  # @return [Proc] a lambda wrapping `#validate!`
74
74
  def default_validator(instance)
75
75
  lambda do |value, options: {}, context: nil|
@@ -111,10 +111,9 @@ module Castkit
111
111
  # @param type [Symbol]
112
112
  # @param value [Object, nil]
113
113
  # @return [void]
114
- def type_error!(type, value)
115
- message = "value must be a #{type}, got #{value.inspect}"
116
-
117
- raise Castkit::TypeError, message if Castkit.configuration.raise_type_errors
114
+ def type_error!(type, value, context: nil)
115
+ message = "#{context || "value"} must be a #{type}, got #{value}"
116
+ raise Castkit::AttributeError, message if Castkit.configuration.raise_type_errors
118
117
 
119
118
  Castkit.warning "[Castkit] #{message}" if Castkit.configuration.enable_warnings
120
119
  end
@@ -1,6 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative "generic"
3
+ require_relative "base"
4
+ require_relative "../validators/boolean_validator"
4
5
 
5
6
  module Castkit
6
7
  module Types
@@ -10,7 +11,7 @@ module Castkit
10
11
  #
11
12
  # This class is used internally by Castkit when an attribute is defined with:
12
13
  # `boolean :is_active`
13
- class Boolean < Generic
14
+ class Boolean < Base
14
15
  # Deserializes the input into a boolean value.
15
16
  #
16
17
  # Accepts:
@@ -21,14 +22,7 @@ module Castkit
21
22
  # @return [Boolean]
22
23
  # @raise [Castkit::TypeError] if the value cannot be coerced to a boolean
23
24
  def deserialize(value)
24
- case value.to_s.downcase
25
- when "true", "1"
26
- true
27
- when "false", "0"
28
- false
29
- else
30
- type_error!(:boolean, value)
31
- end
25
+ value
32
26
  end
33
27
 
34
28
  # Serializes the boolean value (pass-through).
@@ -38,6 +32,16 @@ module Castkit
38
32
  def serialize(value)
39
33
  value
40
34
  end
35
+
36
+ # Validates the Boolean value.
37
+ #
38
+ # @param value [Object]
39
+ # @param options [Hash] validation options
40
+ # @param context [Symbol, String] attribute context for error messages
41
+ # @return [void]
42
+ def validate!(value, options: {}, context: {})
43
+ Castkit::Validators::BooleanValidator.call(value, options: options, context: context)
44
+ end
41
45
  end
42
46
  end
43
47
  end
@@ -1,6 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative "generic"
3
+ require_relative "base"
4
+ require_relative "../validators/collection_validator"
4
5
 
5
6
  module Castkit
6
7
  module Types
@@ -11,7 +12,7 @@ module Castkit
11
12
  #
12
13
  # This class is used internally by Castkit when an attribute is defined with:
13
14
  # `array :tags, of: :string`
14
- class Collection < Generic
15
+ class Collection < Base
15
16
  # Deserializes the value into an array using `Array(value)`.
16
17
  #
17
18
  # @param value [Object]
@@ -19,6 +20,16 @@ module Castkit
19
20
  def deserialize(value)
20
21
  Array(value)
21
22
  end
23
+
24
+ # Validates the Array value.
25
+ #
26
+ # @param value [Object]
27
+ # @param options [Hash] validation options
28
+ # @param context [Symbol, String, nil] attribute context for error messages
29
+ # @return [void]
30
+ def validate!(value, options: {}, context: nil)
31
+ Castkit::Validators::CollectionValidator.call(value, options: options, context: context)
32
+ end
22
33
  end
23
34
  end
24
35
  end
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require "date"
4
- require_relative "generic"
4
+ require_relative "base"
5
5
 
6
6
  module Castkit
7
7
  module Types
@@ -12,7 +12,7 @@ module Castkit
12
12
  #
13
13
  # This class is used internally by Castkit when an attribute is defined with:
14
14
  # `date :published_on`
15
- class Date < Generic
15
+ class Date < Base
16
16
  # Deserializes the input value to a `Date` instance.
17
17
  #
18
18
  # @param value [Object]
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require "date"
4
- require_relative "generic"
4
+ require_relative "base"
5
5
 
6
6
  module Castkit
7
7
  module Types
@@ -12,7 +12,7 @@ module Castkit
12
12
  #
13
13
  # This class is used internally by Castkit when an attribute is defined with:
14
14
  # `datetime :published_ad`
15
- class DateTime < Generic
15
+ class DateTime < Base
16
16
  # Deserializes the input value to a `DateTime` instance.
17
17
  #
18
18
  # @param value [Object]
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative "generic"
4
- require_relative "../validators/numeric_validator"
3
+ require_relative "base"
4
+ require_relative "../validators/float_validator"
5
5
 
6
6
  module Castkit
7
7
  module Types
@@ -13,7 +13,7 @@ module Castkit
13
13
  #
14
14
  # This class is used internally by Castkit when an attribute is defined with:
15
15
  # `integer :count`
16
- class Float < Generic
16
+ class Float < Base
17
17
  # Deserializes the input value to an Float.
18
18
  #
19
19
  # @param value [Object]
@@ -30,7 +30,7 @@ module Castkit
30
30
  value
31
31
  end
32
32
 
33
- # Validates the Float value using Castkit's NumericValidator.
33
+ # Validates the Float value using Castkit's FloatValidator.
34
34
  #
35
35
  # Supports options like `min:` and `max:`.
36
36
  #
@@ -39,7 +39,7 @@ module Castkit
39
39
  # @param context [Symbol, String] attribute context for error messages
40
40
  # @return [void]
41
41
  def validate!(value, options: {}, context: {})
42
- Castkit::Validators::NumericValidator.call(value, options: options, context: context)
42
+ Castkit::Validators::FloatValidator.call(value, options: options, context: context)
43
43
  end
44
44
  end
45
45
  end
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative "generic"
4
- require_relative "../validators/numeric_validator"
3
+ require_relative "base"
4
+ require_relative "../validators/integer_validator"
5
5
 
6
6
  module Castkit
7
7
  module Types
@@ -13,7 +13,7 @@ module Castkit
13
13
  #
14
14
  # This class is used internally by Castkit when an attribute is defined with:
15
15
  # `integer :count`
16
- class Integer < Generic
16
+ class Integer < Base
17
17
  # Deserializes the input value to an Integer.
18
18
  #
19
19
  # @param value [Object]
@@ -30,7 +30,7 @@ module Castkit
30
30
  value
31
31
  end
32
32
 
33
- # Validates the Integer value using Castkit's NumericValidator.
33
+ # Validates the Integer value using Castkit's IntegerValidator.
34
34
  #
35
35
  # Supports options like `min:` and `max:`.
36
36
  #
@@ -39,7 +39,7 @@ module Castkit
39
39
  # @param context [Symbol, String] attribute context for error messages
40
40
  # @return [void]
41
41
  def validate!(value, options: {}, context: {})
42
- Castkit::Validators::NumericValidator.call(value, options: options, context: context)
42
+ Castkit::Validators::IntegerValidator.call(value, options: options, context: context)
43
43
  end
44
44
  end
45
45
  end
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative "generic"
3
+ require_relative "base"
4
4
  require_relative "../validators/string_validator"
5
5
 
6
6
  module Castkit
@@ -12,7 +12,7 @@ module Castkit
12
12
  #
13
13
  # This class is used internally by Castkit when an attribute is defined with:
14
14
  # `string :id`
15
- class String < Generic
15
+ class String < Base
16
16
  # Deserializes the value by coercing it to a string using `to_s`.
17
17
  #
18
18
  # @param value [Object]
data/lib/castkit/types.rb CHANGED
@@ -1,11 +1,11 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require_relative "types/base"
3
4
  require_relative "types/collection"
4
5
  require_relative "types/boolean"
5
6
  require_relative "types/date"
6
7
  require_relative "types/date_time"
7
8
  require_relative "types/float"
8
- require_relative "types/generic"
9
9
  require_relative "types/integer"
10
10
  require_relative "types/string"
11
11
 
@@ -0,0 +1,59 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Castkit
4
+ module Validators
5
+ # Abstract base class for all attribute validators.
6
+ #
7
+ # Validators are responsible for enforcing value constraints (e.g., type checks,
8
+ # format rules, numerical bounds). All validators must inherit from this base class
9
+ # and implement the `#call` instance method.
10
+ #
11
+ # Supports both instance-level and class-level invocation via `.call`.
12
+ #
13
+ # @abstract Subclasses must implement `#call`.
14
+ class Base
15
+ class << self
16
+ # Entry point for validating a value using the validator class.
17
+ #
18
+ # Instantiates the validator and invokes `#call` with the provided arguments.
19
+ #
20
+ # @param value [Object] the value to validate
21
+ # @param options [Hash] additional validation options (e.g., `min`, `max`, `format`)
22
+ # @param context [Symbol, String, Hash] context for the validation (usually the attribute name)
23
+ # @return [void]
24
+ # @raise [Castkit::AttributeError] if validation fails and `raise_type_errors` is true
25
+ def call(value, options:, context:)
26
+ new.call(value, options: options, context: context)
27
+ end
28
+ end
29
+
30
+ # Validates the value.
31
+ #
32
+ # @abstract
33
+ # @param value [Object] the value to validate
34
+ # @param options [Hash] validation options (e.g., min, max, format)
35
+ # @param context [Symbol, String, Hash] context for validation errors
36
+ # @return [void]
37
+ # @raise [NotImplementedError] if not implemented in subclass
38
+ def call(value, options:, context:)
39
+ raise NotImplementedError, "#{self.class.name} must implement `#call`"
40
+ end
41
+
42
+ protected
43
+
44
+ # Emits or raises a type error depending on global configuration.
45
+ #
46
+ # @param type [Symbol] the expected type (e.g., `:integer`)
47
+ # @param value [Object, nil] the received value
48
+ # @param context [Symbol, String, nil] context to include in error messages
49
+ # @raise [Castkit::AttributeError] if `raise_type_errors` is enabled
50
+ # @return [void]
51
+ def type_error!(type, value, context: nil)
52
+ message = "#{context || "value"} must be a #{type}, got #{value}"
53
+ raise Castkit::AttributeError, message if Castkit.configuration.raise_type_errors
54
+
55
+ Castkit.warning "[Castkit] #{message}" if Castkit.configuration.enable_warnings
56
+ end
57
+ end
58
+ end
59
+ end
@@ -0,0 +1,39 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "base"
4
+
5
+ module Castkit
6
+ module Validators
7
+ # Validator for boolean attributes.
8
+ #
9
+ # Accepts various representations of boolean values, including strings and integers.
10
+ # Converts common truthy/falsy string values into booleans, otherwise raises a type error.
11
+ #
12
+ # This validator is typically used internally by `Castkit::Types::Boolean`.
13
+ #
14
+ # @example
15
+ # validator = Castkit::Validators::BooleanValidator.new
16
+ # validator.call("true", _options: {}, context: :enabled) # => true
17
+ # validator.call("0", _options: {}, context: :enabled) # => false
18
+ # validator.call("nope", _options: {}, context: :enabled) # raises Castkit::AttributeError
19
+ class BooleanValidator
20
+ # Validates the Boolean value.
21
+ #
22
+ # @param value [Object] the input to validate
23
+ # @param _options [Hash] unused, provided for consistency with other validators
24
+ # @param context [Symbol, String] the attribute name or path for error messages
25
+ # @return [Boolean]
26
+ # @raise [Castkit::AttributeError] if the value is not a recognizable boolean
27
+ def call(value, _options:, context:)
28
+ case value.to_s.downcase
29
+ when "true", "1"
30
+ true
31
+ when "false", "0"
32
+ false
33
+ else
34
+ type_error!(:boolean, value, context: context)
35
+ end
36
+ end
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,29 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "../validators/base"
4
+
5
+ module Castkit
6
+ module Validators
7
+ # Validator for array (collection) attributes.
8
+ #
9
+ # Ensures that the provided value is an instance of `Array`. This validator is
10
+ # typically used by `Castkit::Types::Collection` for attributes defined as arrays.
11
+ #
12
+ # @example
13
+ # validator = Castkit::Validators::CollectionValidator.new
14
+ # validator.call([1, 2, 3], _options: {}, context: :tags) # => passes
15
+ # validator.call("foo", _options: {}, context: :tags) # raises Castkit::AttributeError
16
+ class CollectionValidator < Castkit::Validators::Base
17
+ # Validates that the value is an Array.
18
+ #
19
+ # @param value [Object] the value to validate
20
+ # @param _options [Hash] unused, for interface consistency
21
+ # @param context [Symbol, String] the field or context for error messaging
22
+ # @return [void]
23
+ # @raise [Castkit::AttributeError] if value is not an Array
24
+ def call(value, _options:, context:)
25
+ type_error!(:array, value, context: context) unless value.is_a?(::Array)
26
+ end
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,31 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "numeric_validator"
4
+
5
+ module Castkit
6
+ module Validators
7
+ # Validator for Float attributes.
8
+ #
9
+ # Ensures the value is a `Float`, and applies any numeric bounds (`min`, `max`)
10
+ # defined in the attribute options. Inherits shared logic from `NumericValidator`.
11
+ #
12
+ # @example
13
+ # validator = Castkit::Validators::FloatValidator.new
14
+ # validator.call(3.14, options: { min: 0.0 }, context: :price) # => passes
15
+ # validator.call(42, options: {}, context: :price) # raises Castkit::AttributeError
16
+ class FloatValidator < Castkit::Validators::NumericValidator
17
+ # Validates that the value is a Float and within optional bounds.
18
+ #
19
+ # @param value [Object, nil] the value to validate
20
+ # @param options [Hash] validation options (e.g., `min`, `max`)
21
+ # @param context [Symbol, String] the attribute name or key for error messages
22
+ # @raise [Castkit::AttributeError] if value is not a Float or out of range
23
+ # @return [void]
24
+ def call(value, options:, context:)
25
+ return type_error!(:float, value, context: context) unless value.is_a?(::Float)
26
+
27
+ super
28
+ end
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,31 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "numeric_validator"
4
+
5
+ module Castkit
6
+ module Validators
7
+ # Validator for Integer attributes.
8
+ #
9
+ # Ensures the value is an instance of `Integer` and optionally checks numerical constraints
10
+ # such as `min` and `max`, inherited from `Castkit::Validators::NumericValidator`.
11
+ #
12
+ # @example Validating an Integer attribute
13
+ # IntegerValidator.call(42, options: { min: 10, max: 100 }, context: :count)
14
+ #
15
+ # @see Castkit::Validators::NumericValidator
16
+ class IntegerValidator < Castkit::Validators::NumericValidator
17
+ # Validates the Integer value.
18
+ #
19
+ # @param value [Object, nil] the value to validate
20
+ # @param options [Hash] validation options (e.g., `min`, `max`)
21
+ # @param context [Symbol, String] the attribute name or context for error messages
22
+ # @raise [Castkit::AttributeError] if the value is not an Integer or fails validation rules
23
+ # @return [void]
24
+ def call(value, options:, context:)
25
+ return type_error!(:integer, value, context: context) unless value.is_a?(::Integer)
26
+
27
+ super
28
+ end
29
+ end
30
+ end
31
+ end
@@ -1,13 +1,13 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative "base_validator"
3
+ require_relative "base"
4
4
 
5
5
  module Castkit
6
6
  module Validators
7
7
  # Validates that a numeric value falls within the allowed range.
8
8
  #
9
9
  # Supports `:min` and `:max` options to enforce bounds.
10
- class NumericValidator < Castkit::Validators::BaseValidator
10
+ class NumericValidator < Castkit::Validators::Base
11
11
  # Validates the numeric value.
12
12
  #
13
13
  # @param value [Numeric] the value to validate
@@ -1,13 +1,13 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative "base_validator"
3
+ require_relative "base"
4
4
 
5
5
  module Castkit
6
6
  module Validators
7
7
  # Validates that a value is a String and optionally conforms to a format.
8
8
  #
9
9
  # Supports format validation using a Regexp or a custom Proc.
10
- class StringValidator < Castkit::Validators::BaseValidator
10
+ class StringValidator < Castkit::Validators::Base
11
11
  # Validates the string value.
12
12
  #
13
13
  # @param value [Object] the value to validate
@@ -16,8 +16,7 @@ module Castkit
16
16
  # @raise [Castkit::AttributeError] if value is not a string or fails format validation
17
17
  # @return [void]
18
18
  def call(value, options:, context:)
19
- raise Castkit::AttributeError, "#{context} must be a string" unless value.is_a?(String)
20
-
19
+ return type_error!(:string, value, context: context) unless value.is_a?(::String)
21
20
  return unless options[:format]
22
21
 
23
22
  case options[:format]
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Castkit
4
- VERSION = "0.2.0"
4
+ VERSION = "0.3.0"
5
5
  end