X-Git-Url: http://nitlanguage.org?ds=sidebyside diff --git a/lib/popcorn/pop_validation.nit b/lib/popcorn/pop_validation.nit index 86860e5..c3eee92 100644 --- a/lib/popcorn/pop_validation.nit +++ b/lib/popcorn/pop_validation.nit @@ -24,11 +24,12 @@ # # ~~~ # import popcorn +# import popcorn::pop_json # import serialization # # # Serializable book representation. # class Book -# super Jsonable +# super Serializable # # # Book ISBN # var isbn: String @@ -96,7 +97,7 @@ end # Errors messages are grouped into *scopes*. A scope is a string that specify wich # field or document the error message is related to. class ValidationResult - super Jsonable + super Serializable # Object parsed during validation # @@ -138,20 +139,15 @@ class ValidationResult # Does `self` contains `errors`? fun has_error: Bool do return errors.not_empty - # Render self as a JsonObject - fun json: JsonObject do - var obj = new JsonObject - obj["has_error"] = has_error - var e = new JsonObject - for k, v in errors do - e[k] = new JsonArray.from(v) + redef fun core_serialize_to(v) do + var errors = new JsonObject + for k, e in self.errors do + errors[k] = new JsonArray.from(e) end - obj["errors"] = e - return obj + v.serialize_attribute("has_error", has_error) + v.serialize_attribute("errors", errors) end - redef fun serialize_to(v) do json.serialize_to(v) - # Returns the validation result as a pretty formated string fun to_pretty_string: String do var b = new Buffer @@ -197,8 +193,8 @@ class ObjectValidator return validate_json(json) end - # Validate a Jsonable input - fun validate_json(json: Jsonable): Bool do + # Validate a Serializable input + fun validate_json(json: Serializable): Bool do if not json isa JsonObject then validation.add_error("document", "Expected JsonObject got `{json.class_name}`") return false @@ -249,8 +245,8 @@ class ArrayValidator return validate_json(json) end - # Validate a Jsonable input - fun validate_json(json: Jsonable): Bool do + # Validate a Serializable input + fun validate_json(json: Serializable): Bool do if not json isa JsonArray then validation.add_error("document", "Expected JsonArray got `{json.class_name}`") return false @@ -521,6 +517,52 @@ class FloatField end end +# Check if a field is a Bool +# +# ~~~ +# var validator = new ObjectValidator +# validator.add new BoolField("field", required=false) +# assert validator.validate("""{}""") +# assert validator.validate("""{ "field": true }""") +# assert validator.validate("""{ "field": false }""") +# assert not validator.validate("""{ "field": "foo" }""") +# +# validator = new ObjectValidator +# validator.add new BoolField("field") +# assert not validator.validate("""{}""") +# assert validator.validate("""{ "field": true }""") +# assert validator.validate("""{ "field": false }""") +# assert not validator.validate("""{ "field": "foo" }""") +# ~~~ +# +# No type conversion is applied on the input value: +# ~~~ +# assert not validator.validate("""{ "field": "true" }""") +# assert not validator.validate("""{ "field": 1 }""") +# assert not validator.validate("""{ "field": [true] }""") +# ~~~ +class BoolField + super RequiredField + + redef fun validate_field(v, obj) do + if not super then return false + var val = obj.get_or_null(field) + if val == null then + if required == null or required == true then + v.validation.add_error(field, "Expected Bool got `null`") + return false + else + return true + end + end + if not val isa Bool then + v.validation.add_error(field, "Expected Bool got `{val.class_name}`") + return false + end + return true + end +end + # Check that a field is a JsonObject # # ~~~ @@ -699,3 +741,70 @@ class ISBNField redef var re = "(^ISBN [0-9]-[0-9]\{3\}-[0-9]\{5\}-[0-9]?$)".to_re end + +# Check if a field is a valid URL +# +# Matched against the following regular expression: +# ~~~raw +# ^(http|https):\/\/[a-zA-Z0-9\-_]+(\.[a-zA-Z0-9\-_]+)+([a-zA-Z0-9\-\.,@?^=%&:/~\+#]*[a-zA-Z0-9\-\@?^=%&/~\+#])? +# ~~~ +# You should redefine the base regular expression `re` with your own. +# +# ~~~ +# var validator = new ObjectValidator +# validator.add new URLField("url") +# assert not validator.validate("""{ "url": "" }""") +# assert not validator.validate("""{ "url": "foo" }""") +# assert not validator.validate("""{ "url": "http://foo" }""") +# assert validator.validate("""{ "url": "http://nitlanguage.org" }""") +# assert validator.validate("""{ "url": "http://nitlanguage.org/foo" }""") +# assert validator.validate("""{ "url": "http://nitlanguage.org/foo?q" }""") +# assert validator.validate("""{ "url": "http://nitlanguage.org/foo?q&a" }""") +# assert validator.validate("""{ "url": "http://nitlanguage.org/foo?q&a=1" }""") +# ~~~ +class URLField + super RegexField + + autoinit field, required + + redef var re = "^(http|https):\\/\\/[a-zA-Z0-9\\-_]+(\\.[a-zA-Z0-9\\-_]+)+([a-zA-Z0-9\\-\\.,@?^=%&:/~\\+#]*[a-zA-Z0-9\\-\\@?^=%&/~\\+#])?".to_re +end + +# Check if a field value is already used +# +# This class provides a stub validator for fields that should contain a unique value along an +# application (typically logins or ids). +# +# Here an example that uses a `Repository` if an email is unique: +# ~~~nitish +# class UniqueEmailField +# super UniqueField +# +# var users: UsersRepository +# +# redef fun check_unicity(v, field, val) do +# var user = users.find_by_email(val) +# if user != null then +# v.validation.add_error(field, "Email `{val}` already used") +# return false +# end +# return true +# end +# end +# ~~~ +class UniqueField + super StringField + + # Check if `val` is already used somewhere + # + # You must redefine this method to handle your own validation. + fun check_unicity(v: ObjectValidator, field, val: String): Bool is abstract + + redef fun validate_field(v, obj) do + if not super then return false + var val = obj.get_or_null(field) + if not val isa String then return false + if not check_unicity(v, field, val) then return false + return true + end +end