Mojo-Phantom

 view release on metacpan or  search on metacpan

lib/Mojo/Phantom.pm  view on Meta::CPAN

package Mojo::Phantom;

use Mojo::Base -base;

our $VERSION = '0.12';
$VERSION = eval $VERSION;

use Mojo::Phantom::Process;

use Mojo::File;
use Mojo::JSON;
use Mojo::Template;
use Mojo::URL;
use JavaScript::Value::Escape;

use constant DEBUG => $ENV{MOJO_PHANTOM_DEBUG};

use constant CAN_CORE_DIE  => !! CORE->can('die');
use constant CAN_CORE_WARN => !! CORE->can('warn');

has arguments => sub { [] };
has base    => sub { Mojo::URL->new };
has bind    => sub { {} };
has cookies => sub { [] };
has package => 'main';
has 'setup';
has sep     => '--MOJO_PHANTOM_MSG--';
has no_exit => 0;
has note_console => 0;
has 'exe';

has template => <<'TEMPLATE';
  % my ($self, $url, $js) = @_;

  // Setup perl function
  function perl() {
    var system = require('system');
    var args = Array.prototype.slice.call(arguments);
    system.stdout.writeLine(JSON.stringify(args));
    system.stdout.writeLine('<%== $self->sep %>');
    system.stdout.flush();
  }

  // Setup error handling
  var onError = function(msg, trace) {
    var msgStack = ['PHANTOM ERROR: ' + msg];
    if (trace && trace.length) {
      msgStack.push('TRACE:');
      trace.forEach(function(t) {
        msgStack.push(' -> ' + (t.file || t.sourceURL) + ': ' + t.line + (t.function ? ' (in function ' + t.function +')' : ''));
      });
    }

    //phantom.exit isn't exitting immediately, so let Perl kill us
    perl('CORE::die', msgStack.join('\n'));
    //phantom.exit(1);
  };
  phantom.onError = onError;

  // Setup bound functions
  % my $bind = $self->bind || {};
  % foreach my $func (keys %$bind) {
    % my $target = $bind->{$func} || $func;
    perl.<%= $func %> = function() {
      perl.apply(this, ['<%== $target %>'].concat(Array.prototype.slice.call(arguments)));
    };
  % }

  // Setup Cookies
  % foreach my $cookie (@{ $self->cookies }) {
    % my $name = $cookie->name;
    phantom.addCookie({
      name: '<%== $name %>',
      value: '<%== $cookie->value %>',
      domain: '<%== $cookie->domain || $self->base->host %>',
    }) || perl.diag('Failed to import cookie <%== $name %>');
  % }

  // Requst page and inject user-provided javascript
  var page = require('webpage').create();
  page.onError = onError;

  % if($self->note_console) {

    // redirect browser console log to TAP
    page.onConsoleMessage = function(msg) {
      perl.note('js console: ' + msg);
    };

    // redirect console log to TAP
    (function() {
      var old = console.log;
      console.log = function(msg) {
        perl.note('phantom console: ' + msg);
        old.apply(this, Array.prototype.slice.call(arguments));
      };
    }());

  % }

  // Additional setup
  <%= $self->setup || '' %>;

  page.open('<%== $url %>', function(status) {

    <%= $js %>;

    % unless($self->no_exit) {
      phantom.exit();
    % }
  });
TEMPLATE

sub execute_file {
  my ($self, $file, $cb) = @_;
  # note that $file might be an object that needs to have a strong reference

  my $arguments = $self->arguments // [];

  my $proc = Mojo::Phantom::Process->new(arguments => $arguments);
  $proc->exe($self->exe) if $self->exe;

  my $sep = $self->sep;
  my $package = $self->package;
  my $buffer = '';
  my $error;

  $proc->on(read => sub {
    my ($proc, $bytes) = @_;
    warn "\nPerl <<<< Phantom: $bytes\n" if DEBUG;
    $buffer .= $bytes;
    while ($buffer =~ s/^(.*)\n$sep\n//) {
      my ($function, @args) = @{ Mojo::JSON::decode_json $1 };
      local *CORE::die  = sub { die  @_ } unless CAN_CORE_DIE;
      local *CORE::warn = sub { warn @_ } unless CAN_CORE_WARN;
      eval "package $package; no strict 'refs'; &{\$function}(\@args)";
      if ($@) { $error = $@; return $proc->kill }
    }
  });



( run in 2.174 seconds using v1.01-cache-2.11-cpan-39bf76dae61 )