| line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
|
1
|
|
|
|
|
|
|
package Web::Dash; |
|
2
|
|
|
|
|
|
|
|
|
3
|
2
|
|
|
2
|
|
42928
|
use strict; |
|
|
2
|
|
|
|
|
3
|
|
|
|
2
|
|
|
|
|
92
|
|
|
4
|
2
|
|
|
2
|
|
11
|
use warnings; |
|
|
2
|
|
|
|
|
4
|
|
|
|
2
|
|
|
|
|
68
|
|
|
5
|
2
|
|
|
2
|
|
1210
|
use Plack::Request; |
|
|
2
|
|
|
|
|
135838
|
|
|
|
2
|
|
|
|
|
60
|
|
|
6
|
2
|
|
|
2
|
|
1129
|
use Plack::Util; |
|
|
2
|
|
|
|
|
4392
|
|
|
|
2
|
|
|
|
|
69
|
|
|
7
|
2
|
|
|
2
|
|
16
|
use File::Find (); |
|
|
2
|
|
|
|
|
3
|
|
|
|
2
|
|
|
|
|
29
|
|
|
8
|
2
|
|
|
2
|
|
1038
|
use Web::Dash::Lens; |
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
9
|
|
|
|
|
|
|
use Encode; |
|
10
|
|
|
|
|
|
|
use Future::Q 0.012; |
|
11
|
|
|
|
|
|
|
use AnyEvent::DBus 0.31; |
|
12
|
|
|
|
|
|
|
use AnyEvent; |
|
13
|
|
|
|
|
|
|
use JSON qw(to_json); |
|
14
|
|
|
|
|
|
|
use Try::Tiny; |
|
15
|
|
|
|
|
|
|
use Carp; |
|
16
|
|
|
|
|
|
|
|
|
17
|
|
|
|
|
|
|
my $index_page_template = <<'EOD'; |
|
18
|
|
|
|
|
|
|
|
|
19
|
|
|
|
|
|
|
|
|
20
|
|
|
|
|
|
|
|
|
21
|
|
|
|
|
|
|
Web Dash |
|
22
|
|
|
|
|
|
|
|
|
111
|
|
|
|
|
|
|
|
|
112
|
|
|
|
|
|
|
|
|
113
|
|
|
|
|
|
|
|
|
114
|
|
|
|
|
|
|
|
|
115
|
|
|
|
|
|
|
|
|
116
|
|
|
|
|
|
|
|
|
117
|
|
|
|
|
|
|
|
|
118
|
|
|
|
|
|
|
|
|
119
|
|
|
|
|
|
|
[%LENSES_LIST%] |
|
120
|
|
|
|
|
|
|
|
|
121
|
|
|
|
|
|
|
|
|
122
|
|
|
|
|
|
|
Hint: You can change lens with arrow Up and Down keys. |
|
123
|
|
|
|
|
|
|
|
|
124
|
|
|
|
|
|
|
|
|
125
|
|
|
|
|
|
|
|
|
377
|
|
|
|
|
|
|
|
|
378
|
|
|
|
|
|
|
|
|
379
|
|
|
|
|
|
|
EOD |
|
380
|
|
|
|
|
|
|
|
|
381
|
|
|
|
|
|
|
sub _render_lenses_info { |
|
382
|
|
|
|
|
|
|
my ($lenses_info_ref) = @_; |
|
383
|
|
|
|
|
|
|
return join "", map { |
|
384
|
|
|
|
|
|
|
my $lens = $lenses_info_ref->[$_]; |
|
385
|
|
|
|
|
|
|
my $name = Plack::Util::encode_html($lens->{name}); |
|
386
|
|
|
|
|
|
|
my $hint = Plack::Util::encode_html($lens->{hint}); |
|
387
|
|
|
|
|
|
|
qq{\n}; |
|
388
|
|
|
|
|
|
|
} 0 .. $#$lenses_info_ref; |
|
389
|
|
|
|
|
|
|
} |
|
390
|
|
|
|
|
|
|
|
|
391
|
|
|
|
|
|
|
sub new { |
|
392
|
|
|
|
|
|
|
my ($class, %args) = @_; |
|
393
|
|
|
|
|
|
|
my $self = bless { |
|
394
|
|
|
|
|
|
|
lenses => [], |
|
395
|
|
|
|
|
|
|
lens_for_service_name => {}, |
|
396
|
|
|
|
|
|
|
cache_index_page => undef, |
|
397
|
|
|
|
|
|
|
}, $class; |
|
398
|
|
|
|
|
|
|
if(defined $args{lenses}) { |
|
399
|
|
|
|
|
|
|
croak "lenses param must be an array-ref" if ref($args{lenses}) ne 'ARRAY'; |
|
400
|
|
|
|
|
|
|
$self->{lenses} = $args{lenses}; |
|
401
|
|
|
|
|
|
|
}else { |
|
402
|
|
|
|
|
|
|
$self->_init_lenses(defined($args{lenses_dir}) ? $args{lenses_dir} : '/usr/share/unity/lenses'); |
|
403
|
|
|
|
|
|
|
} |
|
404
|
|
|
|
|
|
|
|
|
405
|
|
|
|
|
|
|
if(@{$self->{lenses}}) { |
|
406
|
|
|
|
|
|
|
## ** Wait for all the lenses to respond and recreate them. |
|
407
|
|
|
|
|
|
|
## ** This is because lenses can be unstable at first. |
|
408
|
|
|
|
|
|
|
## ** See xt/spawning-lens.t for detail. |
|
409
|
|
|
|
|
|
|
my $cv = AnyEvent->condvar; |
|
410
|
|
|
|
|
|
|
foreach my $lens (@{$self->{lenses}}) { |
|
411
|
|
|
|
|
|
|
$cv->begin; |
|
412
|
|
|
|
|
|
|
$lens->search_hint->then(sub { $cv->end }) |
|
413
|
|
|
|
|
|
|
} |
|
414
|
|
|
|
|
|
|
$cv->recv; |
|
415
|
|
|
|
|
|
|
$self->_recreate_lenses(); |
|
416
|
|
|
|
|
|
|
%{$self->{lens_for_service_name}} = map { $_->service_name => $_ } @{$self->{lenses}}; |
|
417
|
|
|
|
|
|
|
} |
|
418
|
|
|
|
|
|
|
|
|
419
|
|
|
|
|
|
|
return $self; |
|
420
|
|
|
|
|
|
|
} |
|
421
|
|
|
|
|
|
|
|
|
422
|
|
|
|
|
|
|
sub _init_lenses { |
|
423
|
|
|
|
|
|
|
my ($self, @search_dirs) = @_; |
|
424
|
|
|
|
|
|
|
File::Find::find(sub { |
|
425
|
|
|
|
|
|
|
my $filepath = $File::Find::name; |
|
426
|
|
|
|
|
|
|
return if $filepath !~ /\.lens$/; |
|
427
|
|
|
|
|
|
|
push(@{$self->{lenses}}, Web::Dash::Lens->new(lens_file => $filepath)); |
|
428
|
|
|
|
|
|
|
}, @search_dirs); |
|
429
|
|
|
|
|
|
|
} |
|
430
|
|
|
|
|
|
|
|
|
431
|
|
|
|
|
|
|
sub _recreate_lenses { |
|
432
|
|
|
|
|
|
|
my ($self) = @_; |
|
433
|
|
|
|
|
|
|
my @new_lenses = map { $_->clone } @{$self->{lenses}}; |
|
434
|
|
|
|
|
|
|
$self->{lenses} = \@new_lenses; |
|
435
|
|
|
|
|
|
|
} |
|
436
|
|
|
|
|
|
|
|
|
437
|
|
|
|
|
|
|
sub _render_index { |
|
438
|
|
|
|
|
|
|
my ($self, $req) = @_; |
|
439
|
|
|
|
|
|
|
return sub { |
|
440
|
|
|
|
|
|
|
my ($responder) = @_; |
|
441
|
|
|
|
|
|
|
if(defined $self->{cache_index_page}) { |
|
442
|
|
|
|
|
|
|
$responder->([ |
|
443
|
|
|
|
|
|
|
200, ['Content-Type', 'text/html; charset=utf8'], |
|
444
|
|
|
|
|
|
|
[$self->{cache_index_page}] |
|
445
|
|
|
|
|
|
|
]); |
|
446
|
|
|
|
|
|
|
return; |
|
447
|
|
|
|
|
|
|
} |
|
448
|
|
|
|
|
|
|
Future::Q->wait_all(map { $_->search_hint } @{$self->{lenses}})->then(sub { |
|
449
|
|
|
|
|
|
|
my (@search_hints) = map { $_->get } @_; |
|
450
|
|
|
|
|
|
|
my @lenses_info = map { |
|
451
|
|
|
|
|
|
|
my $hint = $search_hints[$_]; |
|
452
|
|
|
|
|
|
|
my $lens = $self->{lenses}[$_]; |
|
453
|
|
|
|
|
|
|
+{hint => $hint, name => $lens->service_name}; |
|
454
|
|
|
|
|
|
|
} (0 .. $#search_hints); |
|
455
|
|
|
|
|
|
|
my $lenses_list = _render_lenses_info(\@lenses_info); |
|
456
|
|
|
|
|
|
|
my $page = $index_page_template; |
|
457
|
|
|
|
|
|
|
$page =~ s/\[%LENSES_LIST%\]/$lenses_list/; |
|
458
|
|
|
|
|
|
|
$page = Encode::encode('utf8', $page); |
|
459
|
|
|
|
|
|
|
$self->{cache_index_page} = $page; |
|
460
|
|
|
|
|
|
|
$responder->([ |
|
461
|
|
|
|
|
|
|
200, ['Content-Type', 'text/html; charset=utf8'], |
|
462
|
|
|
|
|
|
|
[$page] |
|
463
|
|
|
|
|
|
|
]); |
|
464
|
|
|
|
|
|
|
})->catch(sub { |
|
465
|
|
|
|
|
|
|
my $error = shift; |
|
466
|
|
|
|
|
|
|
$responder->([ |
|
467
|
|
|
|
|
|
|
500, ['Content-Type', 'text/plain'], |
|
468
|
|
|
|
|
|
|
[Encode::encode('utf8', $error)] |
|
469
|
|
|
|
|
|
|
]); |
|
470
|
|
|
|
|
|
|
}); |
|
471
|
|
|
|
|
|
|
}; |
|
472
|
|
|
|
|
|
|
} |
|
473
|
|
|
|
|
|
|
|
|
474
|
|
|
|
|
|
|
sub _json_response { |
|
475
|
|
|
|
|
|
|
my ($response_object, $code) = @_; |
|
476
|
|
|
|
|
|
|
if(!defined($code)) { |
|
477
|
|
|
|
|
|
|
$code = $response_object->{error} ? 500 : 200; |
|
478
|
|
|
|
|
|
|
} |
|
479
|
|
|
|
|
|
|
return [ |
|
480
|
|
|
|
|
|
|
$code, ['Content-Type', 'application/json; charset=utf8'], |
|
481
|
|
|
|
|
|
|
[to_json($response_object, {ascii => 1})] |
|
482
|
|
|
|
|
|
|
]; |
|
483
|
|
|
|
|
|
|
} |
|
484
|
|
|
|
|
|
|
|
|
485
|
|
|
|
|
|
|
sub _render_search { |
|
486
|
|
|
|
|
|
|
my ($self, $req) = @_; |
|
487
|
|
|
|
|
|
|
return sub { |
|
488
|
|
|
|
|
|
|
my $responder = shift; |
|
489
|
|
|
|
|
|
|
my $lens_name = $req->query_parameters->{lens} || 0; |
|
490
|
|
|
|
|
|
|
my $query_string = Encode::decode('utf8', scalar($req->query_parameters->{'q'}) || ''); |
|
491
|
|
|
|
|
|
|
my $lens = $self->{lens_for_service_name}{$lens_name}; |
|
492
|
|
|
|
|
|
|
Future::Q->try(sub { |
|
493
|
|
|
|
|
|
|
if(not defined $lens) { |
|
494
|
|
|
|
|
|
|
die "Unknown lens name: $lens_name\n"; |
|
495
|
|
|
|
|
|
|
} |
|
496
|
|
|
|
|
|
|
return $lens->search($query_string); |
|
497
|
|
|
|
|
|
|
})->then(sub { |
|
498
|
|
|
|
|
|
|
my @results = @_; |
|
499
|
|
|
|
|
|
|
if(@results) { |
|
500
|
|
|
|
|
|
|
return Future::Q->needs_all(map { $lens->category($_->{category_index}) } @results)->then(sub { |
|
501
|
|
|
|
|
|
|
my (@categories) = @_; |
|
502
|
|
|
|
|
|
|
foreach my $i (0 .. $#categories) { |
|
503
|
|
|
|
|
|
|
$results[$i]{category} = $categories[$i]; |
|
504
|
|
|
|
|
|
|
} |
|
505
|
|
|
|
|
|
|
return @results; |
|
506
|
|
|
|
|
|
|
})->catch(sub { |
|
507
|
|
|
|
|
|
|
my $e = shift; |
|
508
|
|
|
|
|
|
|
warn "WARN: $e"; |
|
509
|
|
|
|
|
|
|
return @results; |
|
510
|
|
|
|
|
|
|
}); |
|
511
|
|
|
|
|
|
|
} |
|
512
|
|
|
|
|
|
|
return @results; |
|
513
|
|
|
|
|
|
|
})->then(sub { |
|
514
|
|
|
|
|
|
|
$responder->(_json_response({error => undef, results => \@_}), 200); |
|
515
|
|
|
|
|
|
|
})->catch(sub { |
|
516
|
|
|
|
|
|
|
my $e = shift; |
|
517
|
|
|
|
|
|
|
$responder->(_json_response({error => $e}, 500)); |
|
518
|
|
|
|
|
|
|
}); |
|
519
|
|
|
|
|
|
|
}; |
|
520
|
|
|
|
|
|
|
} |
|
521
|
|
|
|
|
|
|
|
|
522
|
|
|
|
|
|
|
sub to_app { |
|
523
|
|
|
|
|
|
|
my $self = shift; |
|
524
|
|
|
|
|
|
|
return sub { |
|
525
|
|
|
|
|
|
|
my ($env) = @_; |
|
526
|
|
|
|
|
|
|
my $req = Plack::Request->new($env); |
|
527
|
|
|
|
|
|
|
if($req->path eq '/') { |
|
528
|
|
|
|
|
|
|
return $self->_render_index($req); |
|
529
|
|
|
|
|
|
|
}elsif($req->path eq '/search.json') { |
|
530
|
|
|
|
|
|
|
return $self->_render_search($req); |
|
531
|
|
|
|
|
|
|
}else { |
|
532
|
|
|
|
|
|
|
return [404, ['Content-Type', 'text/plain'], ['Not Found']]; |
|
533
|
|
|
|
|
|
|
} |
|
534
|
|
|
|
|
|
|
}; |
|
535
|
|
|
|
|
|
|
} |
|
536
|
|
|
|
|
|
|
|
|
537
|
|
|
|
|
|
|
|
|
538
|
|
|
|
|
|
|
our $VERSION = '0.041'; |
|
539
|
|
|
|
|
|
|
|
|
540
|
|
|
|
|
|
|
1; |
|
541
|
|
|
|
|
|
|
|
|
542
|
|
|
|
|
|
|
__END__ |