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__ |