From b670e3636a1c3c659869a03ee238342c6c114981 Mon Sep 17 00:00:00 2001 From: Paul Smith Date: Fri, 29 May 2020 15:50:12 -0400 Subject: [PATCH 1/5] Fix issue with blank strings and type extensions Closes #377 --- spec/type_extensions/array_spec.cr | 20 ++++----- spec/type_extensions/bool_spec.cr | 17 +++++++ spec/type_extensions/enum_spec.cr | 14 ++++-- spec/type_extensions/int32_spec.cr | 19 -------- spec/type_extensions/int_spec.cr | 67 ++++++++++++++++++++++++++++ spec/type_extensions/time_spec.cr | 6 +++ spec/type_extensions/uuid_spec.cr | 4 ++ src/avram/charms/bool_extensions.cr | 2 + src/avram/charms/enum_extensions.cr | 2 + src/avram/charms/int16_extensions.cr | 7 +++ src/avram/charms/int32_extensions.cr | 1 + src/avram/charms/int64_extensions.cr | 1 + src/avram/charms/time_extensions.cr | 4 +- src/avram/charms/uuid_extensions.cr | 1 + src/avram/type.cr | 10 ++++- 15 files changed, 140 insertions(+), 35 deletions(-) delete mode 100644 spec/type_extensions/int32_spec.cr create mode 100644 spec/type_extensions/int_spec.cr diff --git a/spec/type_extensions/array_spec.cr b/spec/type_extensions/array_spec.cr index 143adc301..6a5514dbd 100644 --- a/spec/type_extensions/array_spec.cr +++ b/spec/type_extensions/array_spec.cr @@ -2,27 +2,27 @@ require "../spec_helper" describe "Array" do it "parses Array(Bool) from Array(String)" do - result = Bool::Lucky.parse(["true"]) - result.value.should eq([true]) + result = Bool::Lucky.parse!(["true"]) + result.should eq([true]) end it "parses Array(String)" do - result = String::Lucky.parse(["test"]) - result.value.should eq(["test"]) + result = String::Lucky.parse!(["test"]) + result.should eq(["test"]) end it "parses Array(Int32) from Array(String)" do - result = Int32::Lucky.parse(["10"]) - result.value.should eq([10]) + result = Int32::Lucky.parse!(["10"]) + result.should eq([10]) end it "parses Array(Int16) from Array(String)" do - result = Int16::Lucky.parse(["1"]) - result.value.should eq([1_i16]) + result = Int16::Lucky.parse!(["1"]) + result.should eq([1_i16]) end it "parses Array(Int64) from Array(String)" do - result = Int64::Lucky.parse(["1000"]) - result.value.should eq([1000_i64]) + result = Int64::Lucky.parse!(["1000"]) + result.should eq([1000_i64]) end end diff --git a/spec/type_extensions/bool_spec.cr b/spec/type_extensions/bool_spec.cr index d1793224e..6bb5ab725 100644 --- a/spec/type_extensions/bool_spec.cr +++ b/spec/type_extensions/bool_spec.cr @@ -6,4 +6,21 @@ describe Bool do true.blank?.should be_false end end + + describe "parsing" do + it "parses empty string as nil" do + parse("").should be_a(Avram::Type::SuccessfulCast(Nil)) + end + + it "parses strings" do + parse("true").value.should eq(true) + parse("1").value.should eq(true) + parse("false").value.should eq(false) + parse("0").value.should eq(false) + end + end +end + +private def parse(value) + Bool::Lucky.parse(value) end diff --git a/spec/type_extensions/enum_spec.cr b/spec/type_extensions/enum_spec.cr index 6b75281ca..ac865e43d 100644 --- a/spec/type_extensions/enum_spec.cr +++ b/spec/type_extensions/enum_spec.cr @@ -3,14 +3,22 @@ require "../spec_helper" include LazyLoadHelpers describe "Enum" do - it "check enum" do + it "parses String enum" do + Issue::Status::Lucky.parse("1").value.should eq(Issue::Status.new(Issue::AvramStatus::Closed)) + end + + it "parses empty String enum" do + Issue::Status::Lucky.parse("").should be_a(Avram::Type::SuccessfulCast(Nil)) + end + + it "checks enum" do issue = IssueBox.create issue.status.enum.should eq(Issue::AvramStatus::Opened) issue.role.enum.should eq(Issue::AvramRole::Issue) end - it "update enum" do + it "updates enum" do issue = IssueBox.create updated_issue = Issue::SaveOperation.update!(issue, status: Issue::Status.new(:closed)) @@ -19,7 +27,7 @@ describe "Enum" do updated_issue.role.enum.should eq(Issue::AvramRole::Issue) end - it "access enum methods" do + it "accesses enum methods" do issue = IssueBox.create issue.status.opened?.should eq(true) diff --git a/spec/type_extensions/int32_spec.cr b/spec/type_extensions/int32_spec.cr deleted file mode 100644 index 369ca4c59..000000000 --- a/spec/type_extensions/int32_spec.cr +++ /dev/null @@ -1,19 +0,0 @@ -require "../spec_helper" - -describe "Int32" do - it "parses Int32 from String" do - result = Int32::Lucky.parse("10") - result.value.should eq(10) - end - - it "parses Int32 from Int64" do - result = Int32::Lucky.parse(400_i64) - result.value.should eq(400) - end - - it "returns FailedCast when overflow from Int64 to Int32" do - result = Int32::Lucky.parse(2147483648) - result.value.should eq(nil) - result.should be_a(Avram::Type::FailedCast) - end -end diff --git a/spec/type_extensions/int_spec.cr b/spec/type_extensions/int_spec.cr new file mode 100644 index 000000000..4426e5979 --- /dev/null +++ b/spec/type_extensions/int_spec.cr @@ -0,0 +1,67 @@ +require "../spec_helper" + +describe "Int16" do + it "parses Int16 from String" do + result = Int16::Lucky.parse("10") + result.value.should eq(10_i16) + end + + it "parses Int16 from Int32" do + result = Int16::Lucky.parse(400) + result.value.should eq(400_i16) + end + + it "returns FailedCast when overflow from Int16 to Int32/64" do + result = Int16::Lucky.parse(1234556) + result.value.should eq(nil) + result.should be_a(Avram::Type::FailedCast) + end + + it "returns nil if String is blank" do + result = Int16::Lucky.parse("") + result.value.should be_nil + result.should be_a(Avram::Type::SuccessfulCast(Nil)) + end +end + +describe "Int32" do + it "parses Int32 from String" do + result = Int32::Lucky.parse("10") + result.value.should eq(10) + end + + it "parses Int32 from Int64" do + result = Int32::Lucky.parse(400_i64) + result.value.should eq(400) + end + + it "returns FailedCast when overflow from Int64 to Int32" do + result = Int32::Lucky.parse(2147483648) + result.value.should eq(nil) + result.should be_a(Avram::Type::FailedCast) + end + + it "returns nil if String is blank" do + result = Int32::Lucky.parse("") + result.value.should be_nil + result.should be_a(Avram::Type::SuccessfulCast(Nil)) + end +end + +describe "Int64" do + it "parses Int64 from String" do + result = Int64::Lucky.parse("10") + result.value.should eq(10_i64) + end + + it "parses Int64 from Int32" do + result = Int64::Lucky.parse(400) + result.value.should eq(400_i64) + end + + it "returns nil if String is blank" do + result = Int64::Lucky.parse("") + result.value.should be_nil + result.should be_a(Avram::Type::SuccessfulCast(Nil)) + end +end diff --git a/spec/type_extensions/time_spec.cr b/spec/type_extensions/time_spec.cr index 8c00624ce..ddcd3ca3a 100644 --- a/spec/type_extensions/time_spec.cr +++ b/spec/type_extensions/time_spec.cr @@ -2,6 +2,12 @@ require "../spec_helper" describe "Time column type" do describe ".parse" do + it "returns nil if parsing an empty String" do + result = Time::Lucky.parse("") + + result.should be_a(Avram::Type::SuccessfulCast(Nil)) + end + it "casts various formats successfully" do time = Time.local times = { diff --git a/spec/type_extensions/uuid_spec.cr b/spec/type_extensions/uuid_spec.cr index 4d05ec9b6..94b7adf4c 100644 --- a/spec/type_extensions/uuid_spec.cr +++ b/spec/type_extensions/uuid_spec.cr @@ -2,6 +2,10 @@ require "../spec_helper" describe "UUID column type" do describe ".parse" do + it "returns successful cast if string is nil" do + UUID::Lucky.parse("").should be_a(Avram::Type::SuccessfulCast(Nil)) + end + it "casts a UUID successfully" do uuid = UUID.new("a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a11") UUID::Lucky.parse(uuid).value.should eq uuid diff --git a/src/avram/charms/bool_extensions.cr b/src/avram/charms/bool_extensions.cr index 3b12627c9..6cbe1f303 100644 --- a/src/avram/charms/bool_extensions.cr +++ b/src/avram/charms/bool_extensions.cr @@ -8,6 +8,8 @@ struct Bool include Avram::Type def parse(value : String) + return parse(nil) if value.blank? + if %w(true 1).includes? value SuccessfulCast(Bool).new true elsif %w(false 0).includes? value diff --git a/src/avram/charms/enum_extensions.cr b/src/avram/charms/enum_extensions.cr index 3cac26221..ab9dfb9da 100644 --- a/src/avram/charms/enum_extensions.cr +++ b/src/avram/charms/enum_extensions.cr @@ -8,6 +8,7 @@ macro avram_enum(enum_name, &block) Lucky end + def_equals @enum getter :enum # You may need to prefix with {{ @type }} @@ -39,6 +40,7 @@ macro avram_enum(enum_name, &block) end def parse(value : String) + return parse(nil) if value.blank? SuccessfulCast({{ enum_name }}).new({{ enum_name }}.new(value.to_i)) end diff --git a/src/avram/charms/int16_extensions.cr b/src/avram/charms/int16_extensions.cr index 7b773383c..3ae698989 100644 --- a/src/avram/charms/int16_extensions.cr +++ b/src/avram/charms/int16_extensions.cr @@ -19,7 +19,14 @@ struct Int16 SuccessfulCast(Array(Int16)).new values end + def parse(value : Int32) + SuccessfulCast(Int16).new value.to_i16 + rescue OverflowError + FailedCast.new + end + def parse(value : String) + return parse(nil) if value.blank? SuccessfulCast(Int16).new value.to_i16 rescue ArgumentError FailedCast.new diff --git a/src/avram/charms/int32_extensions.cr b/src/avram/charms/int32_extensions.cr index 617eef24e..53c95f58f 100644 --- a/src/avram/charms/int32_extensions.cr +++ b/src/avram/charms/int32_extensions.cr @@ -12,6 +12,7 @@ struct Int32 end def parse(value : String) + return parse(nil) if value.blank? SuccessfulCast(Int32).new value.to_i rescue ArgumentError FailedCast.new diff --git a/src/avram/charms/int64_extensions.cr b/src/avram/charms/int64_extensions.cr index 6a08a6640..10396f257 100644 --- a/src/avram/charms/int64_extensions.cr +++ b/src/avram/charms/int64_extensions.cr @@ -20,6 +20,7 @@ struct Int64 end def parse(value : String) + return parse(nil) if value.blank? SuccessfulCast(Int64).new value.to_i64 rescue ArgumentError FailedCast.new diff --git a/src/avram/charms/time_extensions.cr b/src/avram/charms/time_extensions.cr index c40020b16..ab163a32c 100644 --- a/src/avram/charms/time_extensions.cr +++ b/src/avram/charms/time_extensions.cr @@ -22,7 +22,9 @@ struct Time value end - def parse(value : String) : SuccessfulCast(Time) | FailedCast + def parse(value : String) : SuccessfulCast(Time) | SuccessfulCast(Nil) | FailedCast + return parse(nil) if value.blank? + # Prefer user defined string formats try_parsing_with_string_formats(value) || # Then try default formats diff --git a/src/avram/charms/uuid_extensions.cr b/src/avram/charms/uuid_extensions.cr index 59443cc4e..87292a4c1 100644 --- a/src/avram/charms/uuid_extensions.cr +++ b/src/avram/charms/uuid_extensions.cr @@ -16,6 +16,7 @@ struct UUID end def parse(value : String) + return parse(nil) if value.blank? SuccessfulCast(UUID).new(UUID.new(value)) rescue FailedCast.new diff --git a/src/avram/type.cr b/src/avram/type.cr index 89f0e5973..d30950f7f 100644 --- a/src/avram/type.cr +++ b/src/avram/type.cr @@ -14,13 +14,19 @@ module Avram::Type def parse(values : Array(String)) casts = values.map { |value| parse(value) } if casts.all?(&.is_a?(SuccessfulCast)) - values = casts.map { |c| c.as(SuccessfulCast).value } - parse(values) + # values = casts.map { |c| c.as(SuccessfulCast).value } + casts.map(&.as(SuccessfulCast)) else FailedCast.new end end + def parse!(values : Array(T)) forall T + values.map do |v| + parse!(v) + end + end + def parse!(value) parse(value).as(SuccessfulCast).value end From 7c813f2b687949e8f9f6d9da32ee54d2521cf3bb Mon Sep 17 00:00:00 2001 From: Jeremy Woertink Date: Fri, 16 Oct 2020 15:29:19 -0700 Subject: [PATCH 2/5] Missed a spec to match all of the others --- spec/type_extensions/array_spec.cr | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/spec/type_extensions/array_spec.cr b/spec/type_extensions/array_spec.cr index 854dd6262..5d41e730a 100644 --- a/spec/type_extensions/array_spec.cr +++ b/spec/type_extensions/array_spec.cr @@ -27,7 +27,7 @@ describe "Array" do end it "parses Array(Float64) from Array(String)" do - result = Float64::Lucky.parse(["3.1415"]) - result.value.should eq([3.1415]) + result = Float64::Lucky.parse!(["3.1415"]) + result.should eq([3.1415]) end end From 3ff50b78a202467a2ff0cac53ae8f7a0c66fd9f5 Mon Sep 17 00:00:00 2001 From: Jeremy Woertink Date: Fri, 16 Oct 2020 15:29:46 -0700 Subject: [PATCH 3/5] removing commented code --- src/avram/type.cr | 1 - 1 file changed, 1 deletion(-) diff --git a/src/avram/type.cr b/src/avram/type.cr index d30950f7f..92766d713 100644 --- a/src/avram/type.cr +++ b/src/avram/type.cr @@ -14,7 +14,6 @@ module Avram::Type def parse(values : Array(String)) casts = values.map { |value| parse(value) } if casts.all?(&.is_a?(SuccessfulCast)) - # values = casts.map { |c| c.as(SuccessfulCast).value } casts.map(&.as(SuccessfulCast)) else FailedCast.new From b17fafd07e3bfa314a84a28bef9f38ef331f6df7 Mon Sep 17 00:00:00 2001 From: Jeremy Woertink Date: Fri, 16 Oct 2020 15:33:24 -0700 Subject: [PATCH 4/5] Adding this Nil now allows specs to compile and pass --- src/avram/where.cr | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/avram/where.cr b/src/avram/where.cr index bda9b60ef..05ccf497e 100644 --- a/src/avram/where.cr +++ b/src/avram/where.cr @@ -30,7 +30,7 @@ module Avram::Where end abstract class ValueHoldingSqlClause < SqlClause - getter value : String | Array(String) | Array(Int32) + getter value : String | Array(String) | Array(Int32) | Nil def initialize(@column, @value) end From db1607f5659d1b1e00e6ba9524dc3d9c726151a1 Mon Sep 17 00:00:00 2001 From: Jeremy Woertink Date: Fri, 16 Oct 2020 19:39:15 -0700 Subject: [PATCH 5/5] removed duplicate method --- src/avram/charms/int16_extensions.cr | 6 ------ 1 file changed, 6 deletions(-) diff --git a/src/avram/charms/int16_extensions.cr b/src/avram/charms/int16_extensions.cr index 3ae698989..c5f04bdc2 100644 --- a/src/avram/charms/int16_extensions.cr +++ b/src/avram/charms/int16_extensions.cr @@ -19,12 +19,6 @@ struct Int16 SuccessfulCast(Array(Int16)).new values end - def parse(value : Int32) - SuccessfulCast(Int16).new value.to_i16 - rescue OverflowError - FailedCast.new - end - def parse(value : String) return parse(nil) if value.blank? SuccessfulCast(Int16).new value.to_i16