treaty 0.7.0 → 0.8.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/README.md +5 -0
- data/config/locales/en.yml +3 -0
- data/lib/treaty/attribute/base.rb +13 -5
- data/lib/treaty/attribute/dsl.rb +90 -0
- data/lib/treaty/attribute/entity/attribute.rb +25 -0
- data/lib/treaty/attribute/entity/builder.rb +23 -0
- data/lib/treaty/attribute/option/base.rb +17 -1
- data/lib/treaty/attribute/option/modifiers/as_modifier.rb +5 -3
- data/lib/treaty/attribute/option/validators/inclusion_validator.rb +20 -8
- data/lib/treaty/attribute/option/validators/required_validator.rb +8 -2
- data/lib/treaty/attribute/option/validators/type_validator.rb +51 -40
- data/lib/treaty/attribute/option_orchestrator.rb +7 -5
- data/lib/treaty/attribute/validation/nested_array_validator.rb +18 -12
- data/lib/treaty/attribute/validation/nested_transformer.rb +18 -12
- data/lib/treaty/base.rb +1 -1
- data/lib/treaty/controller/dsl.rb +4 -1
- data/lib/treaty/entity.rb +84 -0
- data/lib/treaty/info/entity/builder.rb +50 -0
- data/lib/treaty/info/entity/dsl.rb +28 -0
- data/lib/treaty/info/entity/result.rb +15 -0
- data/lib/treaty/info/rest/builder.rb +110 -0
- data/lib/treaty/info/rest/dsl.rb +28 -0
- data/lib/treaty/info/rest/result.rb +15 -0
- data/lib/treaty/request/attribute/attribute.rb +1 -0
- data/lib/treaty/request/attribute/builder.rb +1 -0
- data/lib/treaty/request/entity.rb +33 -0
- data/lib/treaty/request/factory.rb +61 -14
- data/lib/treaty/request/validator.rb +65 -0
- data/lib/treaty/response/attribute/attribute.rb +1 -0
- data/lib/treaty/response/attribute/builder.rb +1 -0
- data/lib/treaty/response/entity.rb +33 -0
- data/lib/treaty/response/factory.rb +61 -14
- data/lib/treaty/response/validator.rb +57 -0
- data/lib/treaty/version.rb +1 -1
- data/lib/treaty/versions/execution/request.rb +10 -5
- data/lib/treaty/versions/factory.rb +16 -5
- data/lib/treaty/versions/resolver.rb +8 -2
- data/lib/treaty/versions/workspace.rb +2 -2
- metadata +15 -8
- data/lib/treaty/info/builder.rb +0 -108
- data/lib/treaty/info/dsl.rb +0 -26
- data/lib/treaty/info/result.rb +0 -13
- data/lib/treaty/request/attribute/validation/orchestrator.rb +0 -19
- data/lib/treaty/request/attribute/validator.rb +0 -50
- data/lib/treaty/response/attribute/validation/orchestrator.rb +0 -19
- data/lib/treaty/response/attribute/validator.rb +0 -44
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 44bc6d4e290eabdc45efdc7d1e73003f2bb1077e4eee99ad2e9912852d87ecb4
|
|
4
|
+
data.tar.gz: 72d4767ab3b9e69ec5eec87429cfabd54e6e82783ac4ea321b99eeb04eb85c07
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: c5d922b7116ebb82cf7d9c60d8bca8c9f45594744cc10e19319f0b3e22d631f8310a1695861f7979f8bf472b87b8bf5687248faae8ca93f90bec04e6fcfa7fd1
|
|
7
|
+
data.tar.gz: 2fbebe3ceaeeaf1bd743f36fbd40678bda0f9c8dbdbd284610f2de4a0101c6e8d865fa11f555b1fa195fd37e06eae519f82aa08fae8fb29694296ad65fa42be7
|
data/README.md
CHANGED
|
@@ -12,6 +12,9 @@
|
|
|
12
12
|
|
|
13
13
|
</div>
|
|
14
14
|
|
|
15
|
+
> [!WARNING]
|
|
16
|
+
> **Development Status**: Treaty is currently under active development in the 0.x version series. Breaking changes may occur between minor versions (0.x) as we refine the API and add new features. The library will stabilize with the 1.0 release. We recommend pinning to specific patch versions in your Gemfile (e.g., `gem "treaty", "~> 0.5.0"`) until the 1.0 release.
|
|
17
|
+
|
|
15
18
|
## 📚 Documentation
|
|
16
19
|
|
|
17
20
|
Explore comprehensive guides and documentation at [docs](./docs):
|
|
@@ -29,6 +32,8 @@ Treaty provides a complete solution for building versioned APIs in Ruby on Rails
|
|
|
29
32
|
|
|
30
33
|
- **Type Safety** - Enforce strict type checking for request and response data
|
|
31
34
|
- **API Versioning** - Manage multiple concurrent API versions effortlessly
|
|
35
|
+
- **Unified Architecture** - Request blocks, response blocks, and Entity classes share the same validation system
|
|
36
|
+
- **Entity Classes (DTOs)** - Define reusable data transfer objects for better code organization
|
|
32
37
|
- **Built-in Validation** - Validate incoming requests and outgoing responses automatically
|
|
33
38
|
- **Data Transformation** - Transform data seamlessly between different API versions
|
|
34
39
|
- **Deprecation Management** - Mark versions as deprecated with flexible conditions
|
data/config/locales/en.yml
CHANGED
|
@@ -47,6 +47,7 @@ en:
|
|
|
47
47
|
# Attribute builder DSL
|
|
48
48
|
builder:
|
|
49
49
|
not_implemented: "%{class} must implement #create_attribute"
|
|
50
|
+
create_attribute_not_implemented: "Subclass %{class} must implement #create_attribute method"
|
|
50
51
|
|
|
51
52
|
# Attribute-level errors
|
|
52
53
|
errors:
|
|
@@ -61,6 +62,7 @@ en:
|
|
|
61
62
|
# Request factory DSL
|
|
62
63
|
factory:
|
|
63
64
|
unknown_method: "Unknown method '%{method}' in request definition. Use 'object :name do ... end' to define request structure"
|
|
65
|
+
invalid_entity_class: "Request expects a Treaty::Entity subclass, got %{type}: %{value}"
|
|
64
66
|
|
|
65
67
|
# ============================================================================
|
|
66
68
|
# Response: Response definition and structure
|
|
@@ -69,6 +71,7 @@ en:
|
|
|
69
71
|
# Response factory DSL
|
|
70
72
|
factory:
|
|
71
73
|
unknown_method: "Unknown method '%{method}' in response definition. Use 'object :name do ... end' to define response structure"
|
|
74
|
+
invalid_entity_class: "Response expects a Treaty::Entity subclass, got %{type}: %{value}"
|
|
72
75
|
|
|
73
76
|
# ============================================================================
|
|
74
77
|
# Versioning: API version management and resolution
|
|
@@ -122,9 +122,11 @@ module Treaty
|
|
|
122
122
|
return unless @nesting_level > Treaty::Engine.config.treaty.attribute_nesting_level
|
|
123
123
|
|
|
124
124
|
raise Treaty::Exceptions::NestedAttributes,
|
|
125
|
-
I18n.t(
|
|
126
|
-
|
|
127
|
-
|
|
125
|
+
I18n.t(
|
|
126
|
+
"treaty.attributes.errors.nesting_level_exceeded",
|
|
127
|
+
level: @nesting_level,
|
|
128
|
+
max_level: Treaty::Engine.config.treaty.attribute_nesting_level
|
|
129
|
+
)
|
|
128
130
|
end
|
|
129
131
|
|
|
130
132
|
# Extracts helper symbols from arguments
|
|
@@ -155,7 +157,10 @@ module Treaty
|
|
|
155
157
|
def apply_defaults!
|
|
156
158
|
# Must be implemented in subclasses
|
|
157
159
|
raise Treaty::Exceptions::NotImplemented,
|
|
158
|
-
I18n.t(
|
|
160
|
+
I18n.t(
|
|
161
|
+
"treaty.attributes.errors.apply_defaults_not_implemented",
|
|
162
|
+
class: self.class
|
|
163
|
+
)
|
|
159
164
|
end
|
|
160
165
|
|
|
161
166
|
# Processes nested attributes block for object/array types
|
|
@@ -167,7 +172,10 @@ module Treaty
|
|
|
167
172
|
def process_nested_attributes
|
|
168
173
|
# Must be implemented in subclasses
|
|
169
174
|
raise Treaty::Exceptions::NotImplemented,
|
|
170
|
-
I18n.t(
|
|
175
|
+
I18n.t(
|
|
176
|
+
"treaty.attributes.errors.process_nested_not_implemented",
|
|
177
|
+
class: self.class
|
|
178
|
+
)
|
|
171
179
|
end
|
|
172
180
|
end
|
|
173
181
|
end
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Treaty
|
|
4
|
+
module Attribute
|
|
5
|
+
# DSL module for defining attributes in Entity-like classes.
|
|
6
|
+
#
|
|
7
|
+
# This module provides the class-level DSL for defining attributes.
|
|
8
|
+
# It can be included in any class that needs attribute definition capabilities.
|
|
9
|
+
#
|
|
10
|
+
# ## Usage
|
|
11
|
+
#
|
|
12
|
+
# ```ruby
|
|
13
|
+
# class MyEntity
|
|
14
|
+
# include Treaty::Attribute::DSL
|
|
15
|
+
#
|
|
16
|
+
# string :name
|
|
17
|
+
# integer :age
|
|
18
|
+
# end
|
|
19
|
+
# ```
|
|
20
|
+
module DSL
|
|
21
|
+
def self.included(base)
|
|
22
|
+
base.extend(ClassMethods)
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
module ClassMethods
|
|
26
|
+
# Defines an attribute with explicit type
|
|
27
|
+
#
|
|
28
|
+
# @param name [Symbol] The attribute name
|
|
29
|
+
# @param type [Symbol] The attribute type
|
|
30
|
+
# @param helpers [Array<Symbol>] Helper symbols (:required, :optional)
|
|
31
|
+
# @param options [Hash] Attribute options
|
|
32
|
+
# @param block [Proc] Block for nested attributes
|
|
33
|
+
# @return [void]
|
|
34
|
+
def attribute(name, type, *helpers, **options, &block)
|
|
35
|
+
collection_of_attributes << create_attribute(
|
|
36
|
+
name,
|
|
37
|
+
type,
|
|
38
|
+
*helpers,
|
|
39
|
+
nesting_level: 0,
|
|
40
|
+
**options,
|
|
41
|
+
&block
|
|
42
|
+
)
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
# Returns collection of attributes for this class
|
|
46
|
+
#
|
|
47
|
+
# @return [Collection] Collection of attributes
|
|
48
|
+
def collection_of_attributes
|
|
49
|
+
@collection_of_attributes ||= Treaty::Attribute::Collection.new
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
# Handles DSL methods like `string :name` where method name is the type
|
|
53
|
+
#
|
|
54
|
+
# @param type [Symbol] The attribute type (method name)
|
|
55
|
+
# @param name [Symbol] The attribute name (first argument)
|
|
56
|
+
# @param helpers [Array<Symbol>] Helper symbols
|
|
57
|
+
# @param options [Hash] Attribute options
|
|
58
|
+
# @param block [Proc] Block for nested attributes
|
|
59
|
+
# @return [void]
|
|
60
|
+
def method_missing(type, *helpers, **options, &block)
|
|
61
|
+
name = helpers.shift
|
|
62
|
+
|
|
63
|
+
# If no attribute name provided, this is not an attribute definition
|
|
64
|
+
# Pass to super to handle it properly (e.g., for methods like 'info', 'call!', etc.)
|
|
65
|
+
return super if name.nil?
|
|
66
|
+
|
|
67
|
+
attribute(name, type, *helpers, **options, &block)
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
def respond_to_missing?(name, *)
|
|
71
|
+
super
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
private
|
|
75
|
+
|
|
76
|
+
# Creates an attribute instance (must be implemented by including class)
|
|
77
|
+
#
|
|
78
|
+
# @raise [Treaty::Exceptions::NotImplemented] If not implemented
|
|
79
|
+
# @return [Attribute::Base] Created attribute instance
|
|
80
|
+
def create_attribute(*)
|
|
81
|
+
raise Treaty::Exceptions::NotImplemented,
|
|
82
|
+
I18n.t(
|
|
83
|
+
"treaty.attributes.dsl.create_attribute_not_implemented",
|
|
84
|
+
class: self
|
|
85
|
+
)
|
|
86
|
+
end
|
|
87
|
+
end
|
|
88
|
+
end
|
|
89
|
+
end
|
|
90
|
+
end
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Treaty
|
|
4
|
+
module Attribute
|
|
5
|
+
module Entity
|
|
6
|
+
# Entity-specific attribute that defaults to required: true
|
|
7
|
+
class Attribute < Treaty::Attribute::Base
|
|
8
|
+
private
|
|
9
|
+
|
|
10
|
+
def apply_defaults!
|
|
11
|
+
# For entity: required by default (true).
|
|
12
|
+
# message: nil means use I18n default message from validators
|
|
13
|
+
@options[:required] ||= { is: true, message: nil }
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def process_nested_attributes(&block)
|
|
17
|
+
return unless object_or_array?
|
|
18
|
+
|
|
19
|
+
builder = Builder.new(collection_of_attributes, @nesting_level + 1)
|
|
20
|
+
builder.instance_eval(&block)
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
end
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Treaty
|
|
4
|
+
module Attribute
|
|
5
|
+
module Entity
|
|
6
|
+
# Entity-specific attribute builder
|
|
7
|
+
class Builder < Treaty::Attribute::Builder::Base
|
|
8
|
+
private
|
|
9
|
+
|
|
10
|
+
def create_attribute(name, type, *helpers, nesting_level:, **options, &block)
|
|
11
|
+
Attribute.new(
|
|
12
|
+
name,
|
|
13
|
+
type,
|
|
14
|
+
*helpers,
|
|
15
|
+
nesting_level:,
|
|
16
|
+
**options,
|
|
17
|
+
&block
|
|
18
|
+
)
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
end
|
|
@@ -134,13 +134,29 @@ module Treaty
|
|
|
134
134
|
# Gets custom error message from advanced mode schema
|
|
135
135
|
# Returns nil if no custom message, which triggers I18n default message
|
|
136
136
|
#
|
|
137
|
-
# @return [String, nil] Custom error message or nil for default message
|
|
137
|
+
# @return [String, Proc, nil] Custom error message, lambda, or nil for default message
|
|
138
138
|
def custom_message
|
|
139
139
|
return nil unless @option_schema.is_a?(Hash)
|
|
140
140
|
|
|
141
141
|
@option_schema.fetch(:message, nil)
|
|
142
142
|
end
|
|
143
143
|
|
|
144
|
+
# Resolves custom message with lambda support
|
|
145
|
+
# If message is a lambda, calls it with provided named arguments
|
|
146
|
+
#
|
|
147
|
+
# @param context [Hash] Named arguments to pass to lambda
|
|
148
|
+
# @return [String, nil] Resolved message string or nil
|
|
149
|
+
def resolve_custom_message(**context)
|
|
150
|
+
message = custom_message
|
|
151
|
+
return nil if message.nil?
|
|
152
|
+
|
|
153
|
+
if message.respond_to?(:call)
|
|
154
|
+
message.call(**context)
|
|
155
|
+
else
|
|
156
|
+
message
|
|
157
|
+
end
|
|
158
|
+
end
|
|
159
|
+
|
|
144
160
|
# Checks if schema is in advanced mode
|
|
145
161
|
#
|
|
146
162
|
# @return [Boolean] True if schema is in advanced mode (hash with value key)
|
|
@@ -54,9 +54,11 @@ module Treaty
|
|
|
54
54
|
return if target.is_a?(Symbol)
|
|
55
55
|
|
|
56
56
|
raise Treaty::Exceptions::Validation,
|
|
57
|
-
I18n.t(
|
|
58
|
-
|
|
59
|
-
|
|
57
|
+
I18n.t(
|
|
58
|
+
"treaty.attributes.modifiers.as.invalid_type",
|
|
59
|
+
attribute: @attribute_name,
|
|
60
|
+
type: target.class
|
|
61
|
+
)
|
|
60
62
|
end
|
|
61
63
|
|
|
62
64
|
# Indicates that AsModifier transforms attribute names
|
|
@@ -29,7 +29,10 @@ module Treaty
|
|
|
29
29
|
return if allowed_values.is_a?(Array) && !allowed_values.empty?
|
|
30
30
|
|
|
31
31
|
raise Treaty::Exceptions::Validation,
|
|
32
|
-
I18n.t(
|
|
32
|
+
I18n.t(
|
|
33
|
+
"treaty.attributes.validators.inclusion.invalid_schema",
|
|
34
|
+
attribute: @attribute_name
|
|
35
|
+
)
|
|
33
36
|
end
|
|
34
37
|
|
|
35
38
|
# Validates that value is included in allowed set
|
|
@@ -45,7 +48,13 @@ module Treaty
|
|
|
45
48
|
|
|
46
49
|
return if allowed_values.include?(value)
|
|
47
50
|
|
|
48
|
-
|
|
51
|
+
attributes = {
|
|
52
|
+
attribute: @attribute_name,
|
|
53
|
+
value:,
|
|
54
|
+
allowed_values:
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
message = resolve_custom_message(**attributes) || default_message(**attributes)
|
|
49
58
|
|
|
50
59
|
raise Treaty::Exceptions::Validation, message
|
|
51
60
|
end
|
|
@@ -64,14 +73,17 @@ module Treaty
|
|
|
64
73
|
|
|
65
74
|
# Generates default error message with allowed values using I18n
|
|
66
75
|
#
|
|
67
|
-
# @param
|
|
76
|
+
# @param attribute [Symbol] The attribute name
|
|
68
77
|
# @param value [Object] The actual value that failed validation
|
|
78
|
+
# @param allowed_values [Array] Array of allowed values
|
|
69
79
|
# @return [String] Default error message
|
|
70
|
-
def default_message(
|
|
71
|
-
I18n.t(
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
80
|
+
def default_message(attribute:, value:, allowed_values:)
|
|
81
|
+
I18n.t(
|
|
82
|
+
"treaty.attributes.validators.inclusion.not_included",
|
|
83
|
+
attribute:,
|
|
84
|
+
allowed: allowed_values.join(", "),
|
|
85
|
+
value:
|
|
86
|
+
)
|
|
75
87
|
end
|
|
76
88
|
end
|
|
77
89
|
end
|
|
@@ -51,7 +51,10 @@ module Treaty
|
|
|
51
51
|
return unless required?
|
|
52
52
|
return if present?(value)
|
|
53
53
|
|
|
54
|
-
message =
|
|
54
|
+
message = resolve_custom_message(
|
|
55
|
+
attribute: @attribute_name,
|
|
56
|
+
value:
|
|
57
|
+
) || default_message
|
|
55
58
|
|
|
56
59
|
raise Treaty::Exceptions::Validation, message
|
|
57
60
|
end
|
|
@@ -83,7 +86,10 @@ module Treaty
|
|
|
83
86
|
#
|
|
84
87
|
# @return [String] Default error message
|
|
85
88
|
def default_message
|
|
86
|
-
I18n.t(
|
|
89
|
+
I18n.t(
|
|
90
|
+
"treaty.attributes.validators.required.blank",
|
|
91
|
+
attribute: @attribute_name
|
|
92
|
+
)
|
|
87
93
|
end
|
|
88
94
|
end
|
|
89
95
|
end
|
|
@@ -53,10 +53,12 @@ module Treaty
|
|
|
53
53
|
return if ALLOWED_TYPES.include?(@attribute_type)
|
|
54
54
|
|
|
55
55
|
raise Treaty::Exceptions::Validation,
|
|
56
|
-
I18n.t(
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
56
|
+
I18n.t(
|
|
57
|
+
"treaty.attributes.validators.type.unknown_type",
|
|
58
|
+
type: @attribute_type,
|
|
59
|
+
attribute: @attribute_name,
|
|
60
|
+
allowed: ALLOWED_TYPES.join(", ")
|
|
61
|
+
)
|
|
60
62
|
end
|
|
61
63
|
|
|
62
64
|
# Validates that the value matches the declared type
|
|
@@ -86,18 +88,52 @@ module Treaty
|
|
|
86
88
|
|
|
87
89
|
private
|
|
88
90
|
|
|
91
|
+
# Common type validation logic
|
|
92
|
+
# Checks if value matches expected type and raises exception with appropriate message
|
|
93
|
+
#
|
|
94
|
+
# @param value [Object] The value to validate
|
|
95
|
+
# @param expected_type [Symbol] The expected type symbol
|
|
96
|
+
# @yield Block that returns true if value is valid
|
|
97
|
+
# @raise [Treaty::Exceptions::Validation] If type validation fails
|
|
98
|
+
# @return [void]
|
|
99
|
+
def validate_type!(value, expected_type)
|
|
100
|
+
return if yield(value)
|
|
101
|
+
|
|
102
|
+
actual_type = value.class
|
|
103
|
+
|
|
104
|
+
attributes = {
|
|
105
|
+
attribute: @attribute_name,
|
|
106
|
+
value:,
|
|
107
|
+
expected_type:,
|
|
108
|
+
actual_type:
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
message = resolve_custom_message(**attributes) || default_message(**attributes)
|
|
112
|
+
|
|
113
|
+
raise Treaty::Exceptions::Validation, message
|
|
114
|
+
end
|
|
115
|
+
|
|
116
|
+
# Generates default error message for type mismatch using I18n
|
|
117
|
+
#
|
|
118
|
+
# @param attribute [Symbol] The attribute name
|
|
119
|
+
# @param expected_type [Symbol] The expected type
|
|
120
|
+
# @param actual_type [Class] The actual class of the value
|
|
121
|
+
# @return [String] Default error message
|
|
122
|
+
def default_message(attribute:, expected_type:, actual_type:, **)
|
|
123
|
+
I18n.t(
|
|
124
|
+
"treaty.attributes.validators.type.mismatch.#{expected_type}",
|
|
125
|
+
attribute:,
|
|
126
|
+
actual: actual_type
|
|
127
|
+
)
|
|
128
|
+
end
|
|
129
|
+
|
|
89
130
|
# Validates that value is an Integer
|
|
90
131
|
#
|
|
91
132
|
# @param value [Object] The value to validate
|
|
92
133
|
# @raise [Treaty::Exceptions::Validation] If value is not an Integer
|
|
93
134
|
# @return [void]
|
|
94
135
|
def validate_integer!(value)
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
raise Treaty::Exceptions::Validation,
|
|
98
|
-
I18n.t("treaty.attributes.validators.type.mismatch.integer",
|
|
99
|
-
attribute: @attribute_name,
|
|
100
|
-
actual: value.class)
|
|
136
|
+
validate_type!(value, :integer) { |v| v.is_a?(Integer) }
|
|
101
137
|
end
|
|
102
138
|
|
|
103
139
|
# Validates that value is a String
|
|
@@ -106,12 +142,7 @@ module Treaty
|
|
|
106
142
|
# @raise [Treaty::Exceptions::Validation] If value is not a String
|
|
107
143
|
# @return [void]
|
|
108
144
|
def validate_string!(value)
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
raise Treaty::Exceptions::Validation,
|
|
112
|
-
I18n.t("treaty.attributes.validators.type.mismatch.string",
|
|
113
|
-
attribute: @attribute_name,
|
|
114
|
-
actual: value.class)
|
|
145
|
+
validate_type!(value, :string) { |v| v.is_a?(String) }
|
|
115
146
|
end
|
|
116
147
|
|
|
117
148
|
# Validates that value is a Boolean (TrueClass or FalseClass)
|
|
@@ -120,12 +151,7 @@ module Treaty
|
|
|
120
151
|
# @raise [Treaty::Exceptions::Validation] If value is not a Boolean
|
|
121
152
|
# @return [void]
|
|
122
153
|
def validate_boolean!(value)
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
raise Treaty::Exceptions::Validation,
|
|
126
|
-
I18n.t("treaty.attributes.validators.type.mismatch.boolean",
|
|
127
|
-
attribute: @attribute_name,
|
|
128
|
-
actual: value.class)
|
|
154
|
+
validate_type!(value, :boolean) { |v| v.is_a?(TrueClass) || v.is_a?(FalseClass) }
|
|
129
155
|
end
|
|
130
156
|
|
|
131
157
|
# Validates that value is a Hash (object type)
|
|
@@ -134,12 +160,7 @@ module Treaty
|
|
|
134
160
|
# @raise [Treaty::Exceptions::Validation] If value is not a Hash
|
|
135
161
|
# @return [void]
|
|
136
162
|
def validate_object!(value)
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
raise Treaty::Exceptions::Validation,
|
|
140
|
-
I18n.t("treaty.attributes.validators.type.mismatch.object",
|
|
141
|
-
attribute: @attribute_name,
|
|
142
|
-
actual: value.class)
|
|
163
|
+
validate_type!(value, :object) { |v| v.is_a?(Hash) }
|
|
143
164
|
end
|
|
144
165
|
|
|
145
166
|
# Validates that value is an Array
|
|
@@ -148,12 +169,7 @@ module Treaty
|
|
|
148
169
|
# @raise [Treaty::Exceptions::Validation] If value is not an Array
|
|
149
170
|
# @return [void]
|
|
150
171
|
def validate_array!(value)
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
raise Treaty::Exceptions::Validation,
|
|
154
|
-
I18n.t("treaty.attributes.validators.type.mismatch.array",
|
|
155
|
-
attribute: @attribute_name,
|
|
156
|
-
actual: value.class)
|
|
172
|
+
validate_type!(value, :array) { |v| v.is_a?(Array) }
|
|
157
173
|
end
|
|
158
174
|
|
|
159
175
|
# Validates that value is a DateTime, Time, or Date
|
|
@@ -163,12 +179,7 @@ module Treaty
|
|
|
163
179
|
# @return [void]
|
|
164
180
|
def validate_datetime!(value)
|
|
165
181
|
# TODO: It is better to divide it into different methods for each class.
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
raise Treaty::Exceptions::Validation,
|
|
169
|
-
I18n.t("treaty.attributes.validators.type.mismatch.datetime",
|
|
170
|
-
attribute: @attribute_name,
|
|
171
|
-
actual: value.class)
|
|
182
|
+
validate_type!(value, :datetime) { |v| v.is_a?(DateTime) || v.is_a?(Time) || v.is_a?(Date) }
|
|
172
183
|
end
|
|
173
184
|
end
|
|
174
185
|
end
|
|
@@ -146,7 +146,7 @@ module Treaty
|
|
|
146
146
|
@attribute.options.each do |option_name, option_schema|
|
|
147
147
|
processor_class = Option::Registry.processor_for(option_name)
|
|
148
148
|
|
|
149
|
-
next
|
|
149
|
+
next if processor_class.nil?
|
|
150
150
|
|
|
151
151
|
processors_hash[option_name] = processor_class.new(
|
|
152
152
|
attribute_name: @attribute.name,
|
|
@@ -177,10 +177,12 @@ module Treaty
|
|
|
177
177
|
return if unknown_options.empty?
|
|
178
178
|
|
|
179
179
|
raise Treaty::Exceptions::Validation,
|
|
180
|
-
I18n.t(
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
180
|
+
I18n.t(
|
|
181
|
+
"treaty.attributes.options.unknown",
|
|
182
|
+
attribute: @attribute.name,
|
|
183
|
+
unknown: unknown_options.join(", "),
|
|
184
|
+
known: Option::Registry.all_options.join(", ")
|
|
185
|
+
)
|
|
184
186
|
end
|
|
185
187
|
end
|
|
186
188
|
end
|
|
@@ -115,10 +115,12 @@ module Treaty
|
|
|
115
115
|
return if validated
|
|
116
116
|
|
|
117
117
|
raise Treaty::Exceptions::Validation,
|
|
118
|
-
I18n.t(
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
118
|
+
I18n.t(
|
|
119
|
+
"treaty.attributes.validators.nested.array.element_validation_error",
|
|
120
|
+
attribute: @attribute.name,
|
|
121
|
+
index:,
|
|
122
|
+
errors: errors.join("; ")
|
|
123
|
+
)
|
|
122
124
|
end
|
|
123
125
|
|
|
124
126
|
# Validates array item for complex arrays (with regular attributes)
|
|
@@ -132,10 +134,12 @@ module Treaty
|
|
|
132
134
|
def validate_regular_array_item!(array_item, index) # rubocop:disable Metrics/MethodLength
|
|
133
135
|
unless array_item.is_a?(Hash)
|
|
134
136
|
raise Treaty::Exceptions::Validation,
|
|
135
|
-
I18n.t(
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
137
|
+
I18n.t(
|
|
138
|
+
"treaty.attributes.validators.nested.array.element_type_error",
|
|
139
|
+
attribute: @attribute.name,
|
|
140
|
+
index:,
|
|
141
|
+
actual: array_item.class
|
|
142
|
+
)
|
|
139
143
|
end
|
|
140
144
|
|
|
141
145
|
regular_validators.each do |nested_attribute, validator|
|
|
@@ -143,10 +147,12 @@ module Treaty
|
|
|
143
147
|
validator.validate_value!(nested_value)
|
|
144
148
|
rescue Treaty::Exceptions::Validation => e
|
|
145
149
|
raise Treaty::Exceptions::Validation,
|
|
146
|
-
I18n.t(
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
+
I18n.t(
|
|
151
|
+
"treaty.attributes.validators.nested.array.attribute_error",
|
|
152
|
+
attribute: @attribute.name,
|
|
153
|
+
index:,
|
|
154
|
+
message: e.message
|
|
155
|
+
)
|
|
150
156
|
end
|
|
151
157
|
end
|
|
152
158
|
|
|
@@ -171,10 +171,12 @@ module Treaty
|
|
|
171
171
|
validator.validate_value!(item)
|
|
172
172
|
rescue Treaty::Exceptions::Validation => e
|
|
173
173
|
raise Treaty::Exceptions::Validation,
|
|
174
|
-
I18n.t(
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
174
|
+
I18n.t(
|
|
175
|
+
"treaty.attributes.validators.nested.array.element_validation_error",
|
|
176
|
+
attribute: attribute.name,
|
|
177
|
+
index:,
|
|
178
|
+
errors: e.message
|
|
179
|
+
)
|
|
178
180
|
end
|
|
179
181
|
end
|
|
180
182
|
|
|
@@ -187,10 +189,12 @@ module Treaty
|
|
|
187
189
|
def transform_array_item(item, index) # rubocop:disable Metrics/MethodLength
|
|
188
190
|
unless item.is_a?(Hash)
|
|
189
191
|
raise Treaty::Exceptions::Validation,
|
|
190
|
-
I18n.t(
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
192
|
+
I18n.t(
|
|
193
|
+
"treaty.attributes.validators.nested.array.element_type_error",
|
|
194
|
+
attribute: attribute.name,
|
|
195
|
+
index:,
|
|
196
|
+
actual: item.class
|
|
197
|
+
)
|
|
194
198
|
end
|
|
195
199
|
|
|
196
200
|
transformed = {}
|
|
@@ -230,10 +234,12 @@ module Treaty
|
|
|
230
234
|
end
|
|
231
235
|
rescue Treaty::Exceptions::Validation => e
|
|
232
236
|
raise Treaty::Exceptions::Validation,
|
|
233
|
-
I18n.t(
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
+
I18n.t(
|
|
238
|
+
"treaty.attributes.validators.nested.array.attribute_error",
|
|
239
|
+
attribute: attribute.name,
|
|
240
|
+
index:,
|
|
241
|
+
message: e.message
|
|
242
|
+
)
|
|
237
243
|
end
|
|
238
244
|
|
|
239
245
|
target_name = validator.target_name
|
data/lib/treaty/base.rb
CHANGED
|
@@ -25,7 +25,10 @@ module Treaty
|
|
|
25
25
|
treaty_class_name.constantize
|
|
26
26
|
rescue NameError
|
|
27
27
|
raise Treaty::Exceptions::ClassName,
|
|
28
|
-
I18n.t(
|
|
28
|
+
I18n.t(
|
|
29
|
+
"treaty.controller.treaty_class_not_found",
|
|
30
|
+
class_name: treaty_class_name
|
|
31
|
+
)
|
|
29
32
|
end
|
|
30
33
|
|
|
31
34
|
def treaty_class_name
|