From 33673ff0cf0a8d73d8710fadf29bcf1af5bfcc51 Mon Sep 17 00:00:00 2001 From: Edem Topuzov <44903779+mechnicov@users.noreply.github.com> Date: Tue, 7 May 2024 17:59:33 +0300 Subject: [PATCH] Add UUID predefined type (#19) * Add UUID predefined type * Add pattern caster --- README.md | 22 ++++++++++++++++++++++ config/locales/en.yml | 2 ++ lib/datacaster/predefined.rb | 12 ++++++++++++ lib/datacaster/version.rb | 2 +- spec/datacaster_spec.rb | 33 +++++++++++++++++++++++++++++++++ 5 files changed, 70 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 4abaa39..1697744 100644 --- a/README.md +++ b/README.md @@ -31,6 +31,8 @@ It is currently used in production in several projects (mainly as request parame - [`hash_with_symbolized_keys(error_key = nil)`](#hash_with_symbolized_keyserror_key--nil) - [`integer32(error_key = nil)`](#integer32error_key--nil) - [`non_empty_string(error_key = nil)`](#non_empty_stringerror_key--nil) + - [`pattern(regexp, error_key = nil)`](#patternregexp-error_key--nil) + - [`uuid(error_key = nil)`](#uuiderror_key--nil) - [Special types](#special-types) - [`absent(error_key = nil, on: nil)`](#absenterror_key--nil-on-nil) - [`any(error_key = nil)`](#anyerror_key--nil) @@ -525,9 +527,29 @@ I18n keys: Returns ValidResult if and only if provided value is a string and is not empty. Doesn't transform the value. +I18n keys: + * not a string – `error_key`, `'.string'`, `'datacaster.errors.string'` * is empty – `error_key`, `'.non_empty_string'`, `'datacaster.errors.non_empty_string'` +#### `pattern(regexp, error_key = nil)` + +Returns ValidResult if and only if provided value is a string and satisfies regexp. Doesn't transform the value. Don't forget to provide start/end markers in the regexp if needed, e.g. `/\A\d+\z/` for digits-only string. + +I18n keys: + +* not a string – `error_key`, `'.string'`, `'datacaster.errors.string'` +* doesn't satisfy the regexp – `error_key`, `'.pattern'`, `'datacaster.errors.pattern'` + +#### `uuid(error_key = nil)` + +Returns ValidResult if and only if provided value is a string and UUID. Doesn't transform the value. + +I18n keys: + +* not a string – `error_key`, `'.string'`, `'datacaster.errors.string'` +* not UUID – `error_key`, `'.uuid'`, `'datacaster.errors.uuid'` + ### Special types #### `absent(error_key = nil, on: nil)` diff --git a/config/locales/en.yml b/config/locales/en.yml index 279c5ac..9dd2f3d 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -16,6 +16,7 @@ en: integer32: is not a 32-bit integer iso8601: is not a string with ISO-8601 date and time must_be: "is not %{reference}" + pattern: has invalid format relate: "%{left} should be %{op} %{right}" responds_to: "does not respond to %{reference}" string: is not a string @@ -25,3 +26,4 @@ en: non_empty_string: should be non-empty string switch: is invalid try: raised an error + uuid: is not UUID diff --git a/lib/datacaster/predefined.rb b/lib/datacaster/predefined.rb index 1c0ce8e..8b15aca 100644 --- a/lib/datacaster/predefined.rb +++ b/lib/datacaster/predefined.rb @@ -319,6 +319,12 @@ def float(error_key = nil) check { |x| x.is_a?(Float) }.i18n_key(*error_keys) end + def pattern(regexp, error_key = nil) + error_keys = ['.pattern', 'datacaster.errors.pattern'] + error_keys.unshift(error_key) if error_key + string(error_key) & check { |x| x.match?(regexp) }.i18n_key(*error_keys, reference: regexp.inspect) + end + # 'hash' would be a bad method name, because it would override built in Object#hash def hash_value(error_key = nil) error_keys = ['.hash_value', 'datacaster.errors.hash_value'] @@ -360,6 +366,12 @@ def non_empty_string(error_key = nil) string(error_key) & check { |x| !x.empty? }.i18n_key(*error_keys) end + def uuid(error_key = nil) + error_keys = ['.uuid', 'datacaster.errors.uuid'] + error_keys.unshift(error_key) if error_key + string(error_key) & pattern(/\A\h{8}-\h{4}-\h{4}-\h{4}-\h{12}\z/).i18n_key(*error_keys) + end + # Form request types def iso8601(error_key = nil) diff --git a/lib/datacaster/version.rb b/lib/datacaster/version.rb index 881ea44..c11d121 100644 --- a/lib/datacaster/version.rb +++ b/lib/datacaster/version.rb @@ -1,3 +1,3 @@ module Datacaster - VERSION = "3.2.7" + VERSION = "3.2.8" end diff --git a/spec/datacaster_spec.rb b/spec/datacaster_spec.rb index 23a5f5a..bb6b386 100644 --- a/spec/datacaster_spec.rb +++ b/spec/datacaster_spec.rb @@ -136,6 +136,26 @@ end end + describe "UUID string typecasting" do + subject { described_class.schema { uuid } } + + it "passes UUID strings" do + uuid = "58724b11-ff06-485e-bf67-410c96f606d7" + + expect(subject.(uuid).to_dry_result).to eq Success(uuid) + end + + it "returns Failure on integers" do + expect(subject.(1).to_dry_result).to eq Failure(["is not a string"]) + end + + it "returns Failure on non-UUID strings" do + uuid_without_last_symbol = "58724b11-ff06-485e-bf67-410c96f606d" + + expect(subject.(uuid_without_last_symbol).to_dry_result).to eq Failure(["is not UUID"]) + end + end + describe "decimal typecasting" do subject { described_class.schema { decimal } } @@ -168,6 +188,19 @@ end end + describe "pattern typecasting" do + subject { described_class.schema { pattern(/\A\d+\z/) } } + + it "passes correctly formatted strings" do + expect(subject.("123").to_dry_result).to eq Success("123") + expect(subject.("ab123").to_dry_result).to eq Failure(["has invalid format"]) + end + + it "doesn't pass non-strings" do + expect(subject.(:'123').to_dry_result).to eq Failure(["is not a string"]) + end + end + describe "optional string typecasting" do subject { described_class.schema { optional(string) } }