NAME

Venus::Data - Data Class

ABSTRACT

Data Class for Perl 5

SYNOPSIS

package main;

use Venus::Data;

my $data = Venus::Data->new;

# bless({}, 'Venus::Data')

DESCRIPTION

This package provides a value object for encapsulating data validation. It represents a single immutable validation attempt, ensuring unvalidated data cannot be observed. Validation runs at most once per instance, with all observable outcomes flowing from that validation. The big idea is that the schema (or ruleset) is a contract, and if the validate was success you can be certain that the data (or value) is valid and conforms with the schema.

INHERITS

This package inherits behaviors from:

Venus::Kind::Utility

METHODS

This package provides the following methods:

error

error() (arrayref)

The error method returns the first validation error as an arrayref in the format [path, [error_type, args]], or undef if no errors exist. This is a convenience method for accessing the first error when you don't need the complete error list. Call /validate or /valid first to ensure validation has run.

Since 4.15

error example 1
package main;

use Venus::Data;

my $data = Venus::Data->new(
  value => {
    name => undef,
  },
  ruleset => [
    {
      selector => 'name',
      presence => 'required',
      executes => ['string']
    },
  ],
);

$data->validate;

my $error = $data->error;

# ['name', ['required', []]]
error example 2
package main;

use Venus::Data;

my $data = Venus::Data->new(
  value => {
    name => 'Example',
  },
  ruleset => [
    {
      selector => 'name',
      presence => 'required',
      executes => ['string']
    },
  ],
);

$data->validate;

my $error = $data->error;

# undef
error example 3
package main;

use Venus::Data;

my $data = Venus::Data->new(
  value => {
    name => 123,
  },
  ruleset => [
    {
      selector => 'name',
      presence => 'required',
      executes => [['type', 'string']]
    },
  ],
);

$data->validate;

my $error = $data->error;

# ['name', ['type', ['string']]]

errors

errors() (within[arrayref, arrayref])

The errors method returns an arrayref of all validation errors. Each error is an arrayref with the format [path, [error_type, args]] where path indicates which field failed and error_type describes the failure (e.g., 'required', 'type', etc). Returns an empty arrayref if validation succeeded or hasn't run yet.

Since 4.15

errors example 1
package main;

use Venus::Data;

my $data = Venus::Data->new(
  value => {
    name => undef,
    age => 'invalid'
  },
  ruleset => [
    {
      selector => 'name',
      presence => 'required',
      executes => ['string']
    },
    {
      selector => 'age',
      presence => 'required',
      executes => ['number']
    },
  ],
);

$data->validate;

my $errors = $data->errors;

# [
#   ['name', ['required', []]],
#   ['age', ['number', []]],
# ]
errors example 2
package main;

use Venus::Data;

my $data = Venus::Data->new(
  value => {
    name => 'Example',
    age => 25
  },
  ruleset => [
    {
      selector => 'name',
      presence => 'required',
      executes => ['string']
    },
    {
      selector => 'age',
      presence => 'required',
      executes => ['number']
    },
  ],
);

$data->validate;

my $errors = $data->errors;

# []
errors example 3
package main;

use Venus::Data;

my $data = Venus::Data->new(
  value => {
    name => 123,
    email => undef
  },
  ruleset => [
    {
      selector => 'name',
      presence => 'required',
      executes => [['type', 'string']]
    },
    {
      selector => 'email',
      presence => 'required',
      executes => ['string']
    },
  ],
);

$data->validate;

my $errors = $data->errors;

# [
#   ['name', ['type', ['string']]],
#   ['email', ['required', []]],
# ]

new

new(any @args) (Venus::Data)

The new method constructs an instance of the package.

Since 4.15

new example 1
package main;

use Venus::Data;

my $data = Venus::Data->new;

# bless({}, 'Venus::Data')

renew

renew(any @args) (object)

The renew method creates a new instance with updated arguments while preserving the ruleset from the current instance. This is the best way to "update" the value while maintaining the ruleset. The new instance will have its validation state reset and will need to be validated again.

Since 4.15

renew example 1
package main;

use Venus::Data;

my $data = Venus::Data->new(
  value => {
    name => 'Example',
  },
  ruleset => [
    {
      selector => 'name',
      presence => 'required',
      executes => ['string']
    },
  ],
);

my $renewed = $data->renew(value => {name => 'Updated'});

# bless({...}, 'Venus::Data')
renew example 2
package main;

use Venus::Data;

my $data = Venus::Data->new(
  value => {
    name => 'Example',
    age => 25
  },
  ruleset => [
    {
      selector => 'name',
      presence => 'required',
      executes => ['string']
    },
    {
      selector => 'age',
      presence => 'required',
      executes => ['number']
    },
  ],
);

$data->validate;

my $renewed = $data->renew({value => {name => 'Updated', age => 30}});

# bless({}, 'Venus::Data')

shorthand

shorthand(arrayref | hashref $data) (Venus::Data)

The shorthand method accepts an arrayref or hashref of shorthand notation and sets the ruleset on the instance using "shorthand" in Venus::Schema. This provides a concise way to define validation rules. Keys can have suffixes to indicate presence: ! for (explicit) required, ? (explicit) for optional, * for (explicit) present (i.e., must exist but can be null), and no suffix means (implicit) required. Keys using dot notation (e.g., website.url) result in arrayref selectors for nested path validation. Returns the invocant for method chaining.

Since 4.15

shorthand example 1
package main;

use Venus::Data;

my $data = Venus::Data->new(
  value => {
    fname => 'Elliot',
    lname => 'Alderson',
  },
);

$data->shorthand([
  'fname!' => 'string',
  'lname!' => 'string',
]);

my $valid = $data->valid;

# 1
shorthand example 2
package main;

use Venus::Data;

my $data = Venus::Data->new(
  value => {
    fname => 'Elliot',
  },
);

$data->shorthand([
  'fname!' => 'string',
  'lname!' => 'string',
]);

my $valid = $data->valid;

# 0
shorthand example 3
package main;

use Venus::Data;

my $data = Venus::Data->new(
  value => {
    fname => 'Elliot',
    lname => 'Alderson',
    login => 'mrrobot',
  },
);

$data->shorthand([
  'fname!' => 'string',
  'lname!' => 'string',
  'email?' => 'string',
  'login' => 'string',
]);

my $validated = $data->validate;

# {fname => 'Elliot', lname => 'Alderson', login => 'mrrobot'}
shorthand example 4
package main;

use Venus::Data;

my $data = Venus::Data->new(
  value => {
    user => {
      name => 'Elliot',
    },
  },
);

$data->shorthand([
  'user.name' => 'string',
]);

my $validated = $data->validate;

# {user => {name => 'Elliot'}}

valid

valid() (boolean)

The valid method returns a boolean indicating whether the data is valid. Triggers validation on first call if not already validated. Subsequent calls return the cached validation state without re-validating. This is the primary way to check if data passed validation.

Since 4.15

valid example 1
package main;

use Venus::Data;

my $data = Venus::Data->new(
  value => {
    name => 'Example',
  },
  ruleset => [
    {
      selector => 'name',
      presence => 'required',
      executes => ['string']
    },
  ],
);

my $valid = $data->valid;

# 1
valid example 2
package main;

use Venus::Data;

my $data = Venus::Data->new(
  value => {
    name => undef,
  },
  ruleset => [
    {
      selector => 'name',
      presence => 'required',
      executes => ['string']
    },
  ],
);

my $valid = $data->valid;

# 0
valid example 3
package main;

use Venus::Data;

my $data = Venus::Data->new(
  value => {
    name => 'Example',
  },
  ruleset => [
    {
      selector => 'name',
      presence => 'required',
      executes => ['string']
    },
  ],
);

my $check_1 = $data->valid;

# true

my $check_2 = $data->valid;

# true (cached)

validate

validate() (any)

The validate method performs validation of the value against the ruleset and returns the validated (and potentially modified) value on success, or undef on failure. Validation runs at most once per instance, and subsequent calls return cached results. The returned value may differ from the original due to transformations applied during validation (e.g., "trim", "strip", "lowercase", etc). After validation, check /valid to determine success/failure and /errors to get validation errors.

Since 4.15

validate example 1
package main;

use Venus::Data;

my $data = Venus::Data->new(
  value => {
    name => '  Example  ',
  },
  ruleset => [
    {
      selector => 'name',
      presence => 'required',
      executes => ['string', 'trim']
    },
  ],
);

my $validated = $data->validate;

# {name => 'Example'}
validate example 2
package main;

use Venus::Data;

my $data = Venus::Data->new(
  value => {
    name => undef,
  },
  ruleset => [
    {
      selector => 'name',
      presence => 'required',
      executes => ['string']
    },
  ],
);

my $validated = $data->validate;

# undef
validate example 3
package main;

use Venus::Data;

my $data = Venus::Data->new(
  value => {
    name => 'Example',
  },
  ruleset => [
    {
      selector => 'name',
      presence => 'required',
      executes => ['string']
    },
  ],
);

my $validated_1 = $data->validate;

# {name => 'Example'}

my $validated_2 = $data->validate;

# {name => 'Example'} (cached)
validate example 4
package main;

use Venus::Data;

my $data = Venus::Data->new(
  value => {
    fname => 'Elliot',
  },
  ruleset => [
    {
      selector => 'fname',
      presence => 'required',
      executes => ['string', 'trim', 'strip'],
    },
    {
      selector => 'lname',
      presence => 'required',
      executes => ['string', 'trim', 'strip'],
    },
    {
      selector => 'skills',
      presence => 'present',
    },
    {
      selector => 'handles',
      presence => 'required',
      executes => [['type', 'arrayref']],
    },
    {
      selector => ['handles', 'name'],
      presence => 'required',
      executes => ['string', 'trim', 'strip'],
    },
    {
      selector => ['level'],
      presence => 'required',
      executes => ['number', 'trim', 'strip'],
    },
  ],
);

my $validated = $data->validate;

# undef

my $errors = $data->errors;

# [
#   ['lname', ['required', []]],
#   ['skills', ['present', []]],
#   ['handles', ['required', []]],
#   ['handles.name', ['required', []]],
#   ['level', ['required', []]],
# ]

AUTHORS

Awncorp, awncorp@cpan.org

LICENSE

Copyright (C) 2022, Awncorp, awncorp@cpan.org.

This program is free software, you can redistribute it and/or modify it under the terms of the Apache license version 2.0.