treaty 0.13.0 → 0.14.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 600a422c9ddcdff83b3ec4cde1c8124adb8ae52e1c1d88744d8474bc4afa8c68
4
- data.tar.gz: dd425b19d451b34f46b07747586f35ba7df4dcac9c554a481ac5dd27e1fe2141
3
+ metadata.gz: 1fe35b7cf209d578b34ee7d904bdd146db4a3ac61f022a1f5f02c80bfe21875e
4
+ data.tar.gz: 4be4a2f29fbfb30115e696baeaf618eaf59fe5c2fc309a0d1ced3eff61713671
5
5
  SHA512:
6
- metadata.gz: 1739edd5d1c38fe70fc6ab892b06ec15df3a665724df68c95f351675dae4e8c6a00368d65489bbfde0900a67841b9aad1e7eb57f81b948248ae902755f485519
7
- data.tar.gz: 627d321a706c846e3249afb9e97f4ceb48573af52fcf962459e38316ed76ed7d979c4fd0fcffa8146cff5e7cfeab36b4cf391f9bfb3d7c82ca8de3cd6c5ea439
6
+ metadata.gz: d256fd30a7553ebfc015c06049c4fc0edceddaa782b3b0bda807364568009465556e72867324bc4a707d61c52da579f5a40f4324922af4349f1a7553023a9443
7
+ data.tar.gz: 4d71a4831e946faf207b9c21947531f3bc6263941f07e3a627c2e504a8c471d6aec597fe167090c0d0d49979b3b4dbf2f8e0620cfe9cd12f492286f60b5a3e7a
data/README.md CHANGED
@@ -13,7 +13,7 @@
13
13
  </div>
14
14
 
15
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.12.0"`) until the 1.0 release.
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.14.0"`) until the 1.0 release.
17
17
 
18
18
  ## 📚 Documentation
19
19
 
@@ -79,7 +79,7 @@ module Posts
79
79
  string :title
80
80
  string :content
81
81
  string :summary
82
- datetime :created_at
82
+ time :created_at
83
83
  end
84
84
  end
85
85
 
@@ -17,7 +17,9 @@ en:
17
17
  boolean: "Attribute '%{attribute}' must be a Boolean (true or false), got %{actual}"
18
18
  object: "Attribute '%{attribute}' must be a Hash (object), got %{actual}"
19
19
  array: "Attribute '%{attribute}' must be an Array, got %{actual}"
20
- datetime: "Attribute '%{attribute}' must be a DateTime/Time/Date, got %{actual}"
20
+ date: "Attribute '%{attribute}' must be a Date, got %{actual}"
21
+ time: "Attribute '%{attribute}' must be a Time, got %{actual}"
22
+ datetime: "Attribute '%{attribute}' must be a DateTime, got %{actual}"
21
23
 
22
24
  inclusion:
23
25
  invalid_schema: "Option 'inclusion' for attribute '%{attribute}' must have a non-empty array of allowed values"
@@ -50,24 +50,42 @@ module Treaty
50
50
  # ### From Integer
51
51
  # - integer -> string: Converts to string representation
52
52
  # - integer -> boolean: 0 = false, non-zero = true
53
- # - integer -> datetime: Treats as Unix timestamp
53
+ # - integer -> date: Treats as Unix timestamp, converts to date
54
+ # - integer -> time: Treats as Unix timestamp
55
+ # - integer -> datetime: Treats as Unix timestamp, converts to datetime
54
56
  #
55
57
  # ### From String
56
58
  # - string -> integer: Parses integer from string
57
59
  # - string -> boolean: Parses truthy/falsy strings (true/false, yes/no, 1/0, on/off)
60
+ # - string -> date: Parses date string
61
+ # - string -> time: Parses time string
58
62
  # - string -> datetime: Parses datetime string (ISO8601, RFC3339, etc.)
59
63
  #
60
64
  # ### From Boolean
61
65
  # - boolean -> string: Converts to "true" or "false"
62
66
  # - boolean -> integer: true = 1, false = 0
63
67
  #
68
+ # ### From Date
69
+ # - date -> string: Converts to ISO8601 format
70
+ # - date -> integer: Converts to Unix timestamp
71
+ # - date -> time: Converts to Time at midnight
72
+ # - date -> datetime: Converts to DateTime at midnight
73
+ #
74
+ # ### From Time
75
+ # - time -> string: Converts to ISO8601 format
76
+ # - time -> integer: Converts to Unix timestamp
77
+ # - time -> date: Converts to Date
78
+ # - time -> datetime: Converts to DateTime
79
+ #
64
80
  # ### From DateTime
65
81
  # - datetime -> string: Converts to ISO8601 format
66
82
  # - datetime -> integer: Converts to Unix timestamp
83
+ # - datetime -> date: Converts to Date
84
+ # - datetime -> time: Converts to Time
67
85
  #
68
86
  # ## Important Notes
69
87
  #
70
- # - Cast option only works with scalar types (integer, string, boolean, datetime)
88
+ # - Cast option only works with scalar types (integer, string, boolean, date, time, datetime)
71
89
  # - Array and Object types are not supported for casting
72
90
  # - Casting to the same type is allowed (no-op)
73
91
  # - Nil values are not transformed (handled by RequiredValidator)
@@ -82,9 +100,9 @@ module Treaty
82
100
  #
83
101
  # Schema format: `{ to: :target_type, message: "Custom error" }`
84
102
  # Note: Uses `:to` key instead of the default `:is` key.
85
- class CastModifier < Treaty::Attribute::Option::Base
103
+ class CastModifier < Treaty::Attribute::Option::Base # rubocop:disable Metrics/ClassLength
86
104
  # Types that support casting (scalar types only)
87
- ALLOWED_CAST_TYPES = %i[integer string boolean datetime].freeze
105
+ ALLOWED_CAST_TYPES = %i[integer string boolean date time datetime].freeze
88
106
 
89
107
  # Validates that cast option is correctly configured
90
108
  #
@@ -202,12 +220,16 @@ module Treaty
202
220
  integer: ->(value:) { value }, # No-op for same type
203
221
  string: ->(value:) { value.to_s },
204
222
  boolean: ->(value:) { value != 0 },
205
- datetime: ->(value:) { Time.at(value) }
223
+ date: ->(value:) { Time.at(value).to_date },
224
+ time: ->(value:) { Time.at(value) },
225
+ datetime: ->(value:) { Time.at(value).to_datetime }
206
226
  },
207
227
  string: {
208
228
  string: ->(value:) { value }, # No-op for same type
209
229
  integer: ->(value:) { Integer(value) },
210
230
  boolean: ->(value:) { parse_boolean(value) },
231
+ date: ->(value:) { Date.parse(value) },
232
+ time: ->(value:) { Time.parse(value) },
211
233
  datetime: ->(value:) { DateTime.parse(value) }
212
234
  },
213
235
  boolean: {
@@ -215,10 +237,26 @@ module Treaty
215
237
  string: ->(value:) { value.to_s },
216
238
  integer: ->(value:) { value ? 1 : 0 }
217
239
  },
240
+ date: {
241
+ date: ->(value:) { value }, # No-op for same type
242
+ string: ->(value:) { value.iso8601 },
243
+ integer: ->(value:) { value.to_time.to_i },
244
+ time: ->(value:) { value.to_time },
245
+ datetime: ->(value:) { value.to_datetime }
246
+ },
247
+ time: {
248
+ time: ->(value:) { value }, # No-op for same type
249
+ string: ->(value:) { value.iso8601 },
250
+ integer: ->(value:) { value.to_i },
251
+ date: ->(value:) { value.to_date },
252
+ datetime: ->(value:) { value.to_datetime }
253
+ },
218
254
  datetime: {
219
255
  datetime: ->(value:) { value }, # No-op for same type
220
256
  string: ->(value:) { value.iso8601 },
221
- integer: ->(value:) { value.to_i }
257
+ integer: ->(value:) { value.to_i },
258
+ date: ->(value:) { value.to_date },
259
+ time: ->(value:) { value.to_time }
222
260
  }
223
261
  }
224
262
  end
@@ -13,7 +13,9 @@ module Treaty
13
13
  # - `:boolean` - Ruby TrueClass or FalseClass
14
14
  # - `:object` - Ruby Hash (for nested objects)
15
15
  # - `:array` - Ruby Array (for collections)
16
- # - `:datetime` - Ruby DateTime, Time, or Date
16
+ # - `:date` - Ruby Date
17
+ # - `:time` - Ruby Time
18
+ # - `:datetime` - Ruby DateTime
17
19
  #
18
20
  # ## Usage Examples
19
21
  #
@@ -21,7 +23,9 @@ module Treaty
21
23
  # integer :age
22
24
  # string :name
23
25
  # boolean :published
24
- # datetime :created_at
26
+ # date :published_on
27
+ # time :created_at
28
+ # datetime :updated_at
25
29
  #
26
30
  # Nested structures:
27
31
  # object :author do
@@ -36,14 +40,16 @@ module Treaty
36
40
  #
37
41
  # - Validates only non-nil values (nil handling is done by RequiredValidator)
38
42
  # - Type mismatch raises Treaty::Exceptions::Validation
39
- # - Datetime accepts DateTime, Time, or Date objects
43
+ # - Date accepts only Date objects (not DateTime or Time)
44
+ # - Time accepts only Time objects (not Date or DateTime)
45
+ # - DateTime accepts only DateTime objects (not Date or Time)
40
46
  #
41
47
  # ## Note
42
48
  #
43
49
  # TypeValidator doesn't use option_schema - it validates based on attribute_type.
44
50
  # This validator is always active for all attributes.
45
51
  class TypeValidator < Treaty::Attribute::Option::Base
46
- ALLOWED_TYPES = %i[integer string boolean object array datetime].freeze
52
+ ALLOWED_TYPES = %i[integer string boolean object array date time datetime].freeze
47
53
 
48
54
  # Validates that the attribute type is one of the allowed types
49
55
  #
@@ -81,6 +87,10 @@ module Treaty
81
87
  validate_object!(value)
82
88
  when :array
83
89
  validate_array!(value)
90
+ when :date
91
+ validate_date!(value)
92
+ when :time
93
+ validate_time!(value)
84
94
  when :datetime
85
95
  validate_datetime!(value)
86
96
  end
@@ -172,14 +182,33 @@ module Treaty
172
182
  validate_type!(value, :array) { |v| v.is_a?(Array) }
173
183
  end
174
184
 
175
- # Validates that value is a DateTime, Time, or Date
185
+ # Validates that value is a Date (but not DateTime, since DateTime < Date)
176
186
  #
177
187
  # @param value [Object] The value to validate
178
- # @raise [Treaty::Exceptions::Validation] If value is not a datetime type
188
+ # @raise [Treaty::Exceptions::Validation] If value is not a Date
189
+ # @return [void]
190
+ def validate_date!(value)
191
+ validate_type!(value, :date) { |v| v.is_a?(Date) && !v.is_a?(DateTime) }
192
+ end
193
+
194
+ # Validates that value is a Time or ActiveSupport::TimeWithZone
195
+ #
196
+ # @param value [Object] The value to validate
197
+ # @raise [Treaty::Exceptions::Validation] If value is not a Time
198
+ # @return [void]
199
+ def validate_time!(value)
200
+ validate_type!(value, :time) do |v|
201
+ v.is_a?(Time) || (defined?(ActiveSupport::TimeWithZone) && v.is_a?(ActiveSupport::TimeWithZone))
202
+ end
203
+ end
204
+
205
+ # Validates that value is a DateTime
206
+ #
207
+ # @param value [Object] The value to validate
208
+ # @raise [Treaty::Exceptions::Validation] If value is not a DateTime
179
209
  # @return [void]
180
210
  def validate_datetime!(value)
181
- # TODO: It is better to divide it into different methods for each class.
182
- validate_type!(value, :datetime) { |v| v.is_a?(DateTime) || v.is_a?(Time) || v.is_a?(Date) }
211
+ validate_type!(value, :datetime) { |v| v.is_a?(DateTime) }
183
212
  end
184
213
  end
185
214
  end
@@ -3,7 +3,7 @@
3
3
  module Treaty
4
4
  module VERSION
5
5
  MAJOR = 0
6
- MINOR = 13
6
+ MINOR = 14
7
7
  PATCH = 0
8
8
  PRE = nil
9
9
 
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: treaty
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.13.0
4
+ version: 0.14.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Anton Sokolov