Ravenel

 view release on metacpan or  search on metacpan

lib/Ravenel/Document.pm  view on Meta::CPAN

use Ravenel::Tag::Replace;
use Ravenel::SubScraper;

use fields qw(
	document name prefix content_type docroot
	tags tags_by_depth static_content
	packages arguments
	dynamic document_is_totally_dynamic
	functions
	default_post default_put default_delete
	caller
	lib_path
);

sub new {
	my Ravenel::Document $self = shift;
	my $option                 = shift;

	unless ( ref($self) ) {
		$self = fields::new($self);

		$self->{'docroot'} = $option->{'docroot'} if ( $option->{'docroot'} );

		if ( $self->{'docroot'} and $self->{'docroot'} !~ /\/$/ ) {
			$self->{'docroot'} = "$self->{'docroot'}/";
		}

		if ( $option->{'data'} ) {
			$self->{'document'} = $option->{'data'};
			$self->{'content_type'} = $option->{'content_type'} || 'html'; # XXX Might change my mind :-(
			$self->{'name'}     = $option->{'name'};
		} elsif ( $option->{'file'} ) {

			if ( $option->{'content_type'} ) {
				$self->{'content_type'} = $option->{'content_type'};
			} else {
				( $self->{'content_type'} ) = $option->{'file'} =~ ( /\.(.+)$/ );
			}
			if ( $option->{'name'} ) {
				$self->{'name'}     = $option->{'name'};
			} else {
				( $self->{'name'} ) = $option->{'file'} =~ ( /\/?([^\/\.]+)\..+$/ );
			}

			my $filename = ( $self->{'docroot'} ? $self->{'docroot'} . $option->{'file'} : $option->{'file'} );
			confess("File not found: $filename, $!") if ( not -f $filename );

			open(F, $filename);
			$self->{'document'} = do { local $/; <F> };
			close F;
		}
		$self->{'prefix'}                    ||= 'r:';
		$self->{'dynamic'}                     = $option->{'dynamic'};

		confess("'arguments' is invalid when generating a package (dynamic=$self->{'dynamic'})") if ( $self->{'arguments'} and $self->{'dynamic'} == 0 );

		$self->{'document_is_totally_dynamic'} = $option->{'document_is_totally_dynamic'};
		$self->{'arguments'}                   = $option->{'arguments'};
		$self->{'tags'}                        = [];
		$self->{'functions'}                   = $option->{'functions'};
		$self->{'caller'}                      = $option->{'caller'} || (caller)[1];
 		if ( not $self->{'dynamic'} ) { # if static
			confess("'name' required if not dynamically generated (dynamic=$self->{'dynamic'}, name=$self->{'name'})") if ( not $self->{'name'} );

			# get the libraries
			$self->{'lib_path'} = &get_libraries();
		}

		# replace wrap!
		$self->{'document'} = "<r:replace>$self->{'document'}</r:replace>" if ( $option->{'replace_wrap'} );
	}
	return $self;
}

#########################################
#########################################
# Built in tag handling below

sub _look_for_tag {
        my Ravenel::Document $self = shift;
        my $tag                    = shift || '';
        my $pos                    = shift || 0;
        my $get_close_tag          = shift;
	my $expect_singleton       = shift;

        $get_close_tag = ( $get_close_tag ? '/' : '' );

        my $start_pos = index($self->{'document'}, '<' . $get_close_tag . $self->{'prefix'} . $tag, $pos);

        return if ( $start_pos == -1 );

        my $end_pos = index($self->{'document'}, '>', $start_pos);

        my $beginning_of_tag_content = $start_pos + length("<$self->{'prefix'}");
        my $end_of_tag_content       = $end_pos - $start_pos - length($self->{'prefix'});

        if ( $get_close_tag) { # account for "</" closing tag
                $beginning_of_tag_content++;
                $end_of_tag_content -= 2;
        }

        my $inner = substr( $self->{'document'}, $beginning_of_tag_content, $end_of_tag_content );

        if ( $get_close_tag ) {
                return { 'close_start_pos' => $start_pos, 'close_end_pos' => $end_pos };
        } else {
                if ( $inner =~ /\/>$/ ) {
                        $inner =~ s|/?>||;
                        my ( $action, $option ) = $inner =~ /^([^\s]+)[\s\=]*([^\/]+)?$/;
                        #print "inner=$inner, start_pos=$start_pos, end_pos=$end_pos, action=$action, option=$option\n";
                        return { 'start_pos' => $start_pos, 'end_pos' => $end_pos, 'inner' => $inner, 'action' => $action, 'option' => $option, 'pos' => $end_pos };
		} elsif ( $expect_singleton ) {
			confess("Expected a singleton for tag '$tag'");
                } else {
        		$inner = substr( $self->{'document'}, $beginning_of_tag_content, $end_of_tag_content -1 );

                        my ( $action, $option ) = $inner =~ /^([^\s]+)[\s\=]*([^\/]+)?>$/;
                        #print "look for end tag, action=$action, option=$option\n";

                        my $et = $self->_look_for_tag($action, $end_pos, 'close_tag');
			#print Dumper($et);

lib/Ravenel/Document.pm  view on Meta::CPAN

		if ( $block->isa("Ravenel::Redirect") ) {
			# XXX handle redirect
		} else {
			confess("Invalid return type: " . ref($block));
		}
	} else {
		if ( index($block, "<$prefix") >= 0 ) {
			my Ravenel::Document $doc = new Ravenel::Document( {
				'data'         => $block,
				'prefix'       => $prefix,
				'content_type' => $content_type,
				'dynamic'      => 1,
				'functions'    => $functions,
				'name'         => $name,
				'arguments'    => $arguments,
			} );
			return $doc->parse('dynamic');
		} else {
			return $block;
		}
	}
	return;
}

sub get_libraries {
	my $VAR1;
	my $reg_lib = `perl -MData::Dumper -e "print Dumper(\\\@INC);"`;

	eval $reg_lib;
	my %seen;
	my @unique = grep { $seen{$_} == 1 } map { $seen{$_}++; $_ } @{$VAR1}, @INC;
	return \@unique;
}

sub parse {
	my Ravenel::Document $self = shift;

	$self->look_for_actions();

	if ( $self->{'dynamic'} ) {
		while ( $self->get_all_tags() ) {
			$self->expand_tags();
			$self->replace_tags_with_content();
			$self->look_for_actions();
		}
		return $self->{'document'};
	} else {
		$self->get_all_tags();
		return $self->build_document();
	}
	return;
}

sub render {
	my $class  = shift;
        my $option = shift;
        
        confess("Option hash required") if ( not $option or not ref($option) );

        $option->{'dynamic'} = ( defined($option->{'dynamic'}) ? $option->{'dynamic'} : 1 );
	$option->{'caller'} = (caller)[1];

        my Ravenel::Document $document = new Ravenel::Document($option);

        $document->{'document_is_totally_dynamic'} = $option->{'dynamic'};

	return $document->parse();
}

1;



( run in 4.518 seconds using v1.01-cache-2.11-cpan-364913b4093 )