README.md in strong_json-0.9.0 vs README.md in strong_json-1.0.0

- old
+ new

@@ -55,30 +55,34 @@ * The value must be an object * Fields, `f1`, `f2`, and ..., must be present and its values must be of `type1`, `type2`, ..., respectively * Objects with other fields will be rejected -#### Performance hint +#### Ignoring unknown attributes -Object attributes test is done in order of the keys. +`Object` type ignores unknown attributes by default. +You can reject the unknown attributes. -```ruby -slower_object = enum( - object(id: numeric, created_at: string, updated_at: string, type: literal("person"), name: string), - object(id: numeric, created_at: string, updated_at: string, type: literal("food"), object: any) -) +``` +object(attrs).ignore(Set.new) # Ignores nothing (== raise an error) +object(attrs).ignore!(Set.new) # Destructive version +``` -faster_object = enum( - object(type: literal("person"), id: numeric, created_at: string, updated_at: string, name: string), - object(type: literal("food"), id: numeric, created_at: string, updated_at: string, object: any) -) +You can selectively ignore attributes: + ``` +object(attrs).ignore(Set.new([:a, :b, :c])) # Ignores only :a, :b, and :c +object(attrs).ignore(:any) # Ignores everything (default) +``` -The two enums represents same object, but testing runs faster with `faster_object`. -Objects in `faster_object` have `type` attribute as their first keys. -Testing `type` is done first, and it soon determines if the object is `"person"` or `"food"`. +`Object` also has `prohibit` method to specify attributes to make the type check failed. +``` +object(attrs).prohibit(Set.new([:created_at, :updated_at])) # Make type check failed if :created_at or :updated_at included +object(attrs).prohibit!(Set.new([:created_at, :updated_at])) # Destructive version +``` + ### array(type) * The value must be an array * All elements in the array must be value of given `type` @@ -90,18 +94,36 @@ ### enum(type1, type2, ...) * The value can be one of the given types * First successfully coerced value will return +#### `detector` keyword + +`enum` has optional keyword argument `detector`, which helps identify the type of value. + +```rb +enum(person, + contact, + detector: -> (value) { + if value.is_a?(Hash) + case + when value[:type] == "person" + person + when value[:type] == "contact" + contact + end + end + }) +``` + ### Base types * `number` The value must be an instance of `Numeric` * `string` The value must be an instance of `String` * `boolean` The value must be `true` or `false` * `numeric` The value must be an instance of `Numeric` or a string which represents a number * `any` Any value except `nil` is accepted -* `ignored` Any value will be ignored * `symbol` The value must be an instance of `String` or `Symbol`; returns the result ot `#to_sym` ### Literals * `literal(lit)` The value must `== lit` @@ -114,16 +136,46 @@ * `string?` * `boolean?` * `numeric?` * `symbol?` * `literal?(lit)` -* `any?` Shortcuts for complex data are also defined as the following: * `optional(array(ty))` → `array?(ty)` * `optional(object(fields))` → `object?(fields)` * `optional(enum(types))` → `enum?(types)` + +## ErrorReporter + +You can pretty print type error using `ErrorReporter`. + +```rb +begin + type_check() +rescue StrongJSON::TypeError, StrongJSON::UnexpectedAttributeError => exn + puts exn.message + puts StrongJSON::ErrorReporter.new(path: exn.path).to_s +end +``` + +It will print a _user-friendly_ error message like: + +``` +TypeError at $.pattern: expected=pattern, value={:pattern=>3} + "pattern" expected to be pattern + $ expected to be rule + +Where: + pattern = enum( + regexp_pattern, + token_pattern, + literal_pattern, + string_pattern, + optional(string) + ) + rule = { "pattern": pattern, "glob": optional(enum(string, array(string))) } +``` ## Type checking StrongJSON ships with type definitions for [Steep](https://github.com/soutaro/steep). You can type check your programs using StrongJSON by Steep.