App-Test-Generator

 view release on metacpan or  search on metacpan

lib/App/Test/Generator/SchemaExtractor.pm  view on Meta::CPAN

		my $schema = $self->_extract_schema_hash_from_block($schema_block);
		return $self->_normalize_validator_schema($schema) if $schema;
	}

	return;
}

# --------------------------------------------------
# _extract_pv_schema
#
# Purpose:    Detect and extract a parameter schema
#             from a Params::Validate validate()
#             call in the method body.
#
# Entry:      $code - method body source string.
#
# Exit:       Returns a schema hashref with input,
#             style, and source keys on success,
#             or undef if no validate() call is
#             found or parsing fails.
#
# Side effects: None.
# --------------------------------------------------
sub _extract_pv_schema {
	my ($self, $code) = @_;

	return unless $code =~ /\bvalidate\s*\(/;

	my $doc = $self->_ppi($code) or return;

	my $calls = $doc->find(sub {
		$_[1]->isa('PPI::Token::Word') && ($_[1]->content eq 'validate' || $_[1]->content eq 'Params::Validate::validate')
	}) or return;

	for my $call (@$calls) {
		my $list = $call->parent;
		while ($list && !$list->isa('PPI::Structure::List')) {
			$list = $list->parent;
		}
		if(!defined($list)) {
			my $next = $call->next_sibling();
			my ($arglist, $schema_text) = $self->_parse_pv_call($next);

			if($schema_text) {
				my $compartment = Safe->new();
				$compartment->permit_only(qw(:base_core :base_mem :base_orig));

				my $schema_str = "my \$schema = $schema_text";
				my $schema = $compartment->reval($schema_str);

				if(scalar keys %{$schema}) {
					foreach my $arg(keys %{$schema}) {
						my $field = $schema->{$arg};
						if(my $type = $field->{'type'}) {
							if($type eq 'ARRAYREF') {
								$field->{'type'} = 'arrayref';
							} elsif($type eq 'SCALAR') {
								$field->{'type'} = 'string';
							}
						}
						delete $field->{'callbacks'};
					}

					return {
						input => $schema,
						style => 'hash',
						source => 'validator'
					}
				}
			}
		}
		next unless $list;

		my ($schema_block) = grep { $_->isa('PPI::Structure::Block') } $list->children;

		next unless $schema_block;

		my $schema = $self->_extract_schema_hash_from_block($schema_block);
		return $self->_normalize_validator_schema($schema) if $schema;
	}

	return;
}

# --------------------------------------------------
# _parse_pv_call
#
# Purpose:    Split a Params::Validate call argument
#             string into its two components: the
#             first argument (typically \@_) and
#             the schema hash string.
#
# Entry:      $string - the raw argument string
#                       from the validate() call,
#                       including outer parentheses.
#
# Exit:       Returns a two-element list:
#               ($first_arg, $hash_str)
#             or an empty list if no comma is found
#             at brace depth zero (malformed call).
#
# Side effects: None.
# --------------------------------------------------
sub _parse_pv_call {
	my ($self, $string) = @_;

	# Remove outer parentheses and whitespace
	$string =~ s/^\s*\(\s*//;
	$string =~ s/\s*\)\s*$//;

	# Find the first comma at brace-depth 0
	my $depth = 0;
	my $comma_pos;

	for my $i (0 .. length($string) - 1) {
		my $char = substr($string, $i, 1);

		if ($char eq '{') {
			$depth++;
		} elsif ($char eq '}') {
			$depth--;

lib/App/Test/Generator/SchemaExtractor.pm  view on Meta::CPAN

#             or undef if no validated_hash() call
#             is found or parsing fails.
#
# Side effects: None.
# --------------------------------------------------
sub _extract_moosex_params_schema
{
	my ($self, $code) = @_;

	return unless $code =~ /\bvalidated_hash\s*\(/;

	my $doc = $self->_ppi($code) or return;

	my $calls = $doc->find(sub {
		$_[1]->isa('PPI::Token::Word') && ($_[1]->content eq 'validated_hash')
	}) or return;

	for my $call (@$calls) {
		my $list = $call->parent();
		while ($list && !$list->isa('PPI::Structure::List')) {
			$list = $list->parent;
		}
		if(!defined($list)) {
			my $next = $call->next_sibling();
			my ($arglist, $schema_text) = $self->_parse_pv_call($next);

			if($schema_text) {
				my $compartment = Safe->new();
				$compartment->permit_only(qw(:base_core :base_mem :base_orig));

				my $schema_str = "my \$schema = { $schema_text }";
				$schema_str =~ s/ArrayRef\[(.+?)\]/arrayref, element_type => $1/g;
				my $schema = $compartment->reval($schema_str);

				if(scalar keys %{$schema}) {
					foreach my $arg(keys %{$schema}) {
						my $field = $schema->{$arg};
						if(my $isa = delete $field->{'isa'}) {
							$field->{'type'} = $isa;
						}
						if(exists($field->{'required'})) {
							my $required = delete $field->{'required'};
							$field->{'optional'} = $required ? 0 : 1;
						} else {
							$field->{'optional'} = 1;
						}
						if(ref($field->{'default'}) eq 'CODE') {
							delete $field->{'default'};	# TODO
						}
					}

					foreach my $arg(keys %{$schema}) {
						my $field = $schema->{$arg};
						if(my $type = $field->{'type'}) {
							if($type eq 'ARRAYREF') {
								$field->{'type'} = 'arrayref';
							} elsif($type eq 'SCALAR') {
								$field->{'type'} = 'string';
							}
						}
						delete $field->{'callbacks'};
					}

					return {
						input => $schema,
						style => 'hash',
						source => 'validator'
					}
				}
			}
		}
		next unless $list;

		my ($schema_block) = grep { $_->isa('PPI::Structure::Block') } $list->children;

		next unless $schema_block;

		my $schema = $self->_extract_schema_hash_from_block($schema_block);
		return $self->_normalize_validator_schema($schema) if $schema;
	}

	return;
}

# --------------------------------------------------
# _extract_schema_hash_from_block
#
# Purpose:    Extract a parameter schema hashref from
#             a PPI::Structure::Block node representing
#             the schema argument to a validator call
#             such as validate_strict({ ... }).
#
# Entry:      $block - a PPI::Structure::Block node.
#
# Exit:       Returns a hashref of parameter name to
#             spec hashref, or undef if parsing fails.
#
# Side effects: None.
#
# Notes:      Delegates to _parse_schema_hash which
#             expects a PPI node with a children()
#             method. This method exists to provide
#             a clear semantic name at the call site.
# --------------------------------------------------
sub _extract_schema_hash_from_block {
	my ($self, $block) = @_;

	return unless $block && $block->can('children');

	my $result = $self->_parse_schema_hash($block);

	return unless $result && ref($result) eq 'HASH' && $result->{input};

	return $result->{input};
}

# --------------------------------------------------
# _normalize_validator_schema
#
# Purpose:    Normalise a raw validator schema
#             hashref (as extracted from PPI) into



( run in 2.437 seconds using v1.01-cache-2.11-cpan-cdf2f3d4e48 )