1 package TB_Temp_Packname;
4 use Module::Runtime qw(use_module);
5 use Scalar::Util qw(blessed);
9 use Data::Dump qw( pp );
11 has root => (is => 'lazy');
13 has json => (is => 'lazy');
16 io->dir( $ENV{JTV_ROOT} || die "JTV_ROOT env var not set" )
20 JSON->new->relaxed->pretty
23 sub dispatch_request {
26 use_module('Plack::App::Directory')->new({
27 root => $self->root->name
32 ref($_[0][0]) eq 'HASH' ? $self->render_table($_[0][0]) : $_[0]
37 [ 302, [ 'Location', '/'.join('+', @{$_[1]}).'/' ], [] ];
40 $self->root_structure;
44 $self->structure(map {
48 } split qr{(?<!\\)/}, $_[1]);
54 my $struct = $self->mangle_structure($self->root);
55 push @{$struct->[0]{columns}}, 'select';
56 foreach my $host (@{$struct->[0]{data}}) {
58 my $name = $host->{name};
59 my $html = join '', HTML::Tags::to_html_string(
60 <input type="checkbox" name="host" value="$name" />
62 $host->{select} = \$html;
64 $struct->[0]{wrapper} = sub {
67 (map /^\s*$/ ? " $_" : $_, @_),
68 ' ', <input type="submit" value="Now Multify" />, "\n",
74 sub merge_pair_structures {
75 my ($self, $cols, @structures) = @_;
76 my ($key_name, $value_name) = @$cols;
79 for map $_->{$key_name},
80 map { ($_ ? @$_ : ()) } map $_->[1]{data}, @structures;
81 my %value_by_host = (map {
83 my $data = $_->[1]{data};
85 map { ($_->{$key_name}, $_->{$value_name}) } @$data,
88 my @hosts = map $_->[0], @structures;
90 columns => ['key', @hosts],
94 +{ key => $key, (map {
95 ($_, $value_by_host{$_}{$key});
101 sub merge_generic_structures {
102 my ($self, $cols, @structures) = @_;
107 my $is_explore = grep { $_ eq 'explore' } @$cols;
108 foreach my $thing (@structures) {
109 foreach my $el (@{$thing->[1]{data}}) {
110 my $by = $by_name{$el->{name}} ||= { name => $el->{name} };
111 foreach my $key (keys %$el) {
112 next if $is_explore and $key eq 'name';
113 if (ref($el->{$key}) eq 'HASH') {
114 $complex_cols{$key} = 1;
117 my $full_key = $key.' ('.$thing->[0].')';
118 $alias{$full_key} = $key;
119 $host_cols{$full_key} = 1;
120 $by->{$full_key} = $el->{$key};
127 $is_explore ? ('name') : (),
128 sort(keys %host_cols),
129 sort(keys %complex_cols),
134 map $by_name{$_}, sort keys %by_name
139 sub merge_unrelated_structures {
140 my ($self, $cols, @structures) = @_;
142 columns => ['host', sort @$cols],
145 my ($host, $data) = @$_;
146 (map +{ host => $host, %$_ }, @{ $data->{data} || [] });
152 sub _pred_columns_kv { @_ == 3 and $_[1] eq 'key' and $_[2] eq 'value' }
155 my ($self, @parts) = @_;
156 if ($parts[0] =~ /\+/) {
157 ($parts[0], my @extra) = split /\+/, $parts[0];
158 my $struct = $self->mangle_structure($self->descend($self->root, @parts));
159 my $first = shift @parts;
162 my $data = $self->mangle_structure(
163 $self->descend($self->root, $name, @parts),
165 [$name, $data ? $data->[0] : {}];
169 for map { (@{$_->{columns}||[]}) }
170 $struct->[0], map $_->[1], @rest;
171 my @cols = sort keys %col;
174 for grep { $_->{show_columns} }
175 $struct->[0], map $_->[1], @rest;
176 my @structures = ([$first, $struct->[0]], @rest);
177 if ($self->_pred_columns_kv(@cols)) {
178 return $self->merge_pair_structures(
183 elsif (not $col{name}) {
184 return $self->merge_unrelated_structures([@cols], @structures);
186 return $self->merge_generic_structures([@cols], @structures);
188 return $self->mangle_structure($self->descend($self->root, @parts));
191 sub mangle_hash_structure {
192 my ($self, $data) = @_;
194 and values %$data == grep ref($_) eq 'HASH', values %$data) {
196 $tmp{join '|', keys %$_} = 1 for values %$data;
197 if (keys %tmp == 1) {
198 $data->{$_}->{name} ||= $_ for keys %$data;
199 my @cols = grep $_ ne 'name', sort keys %{(values %$data)[0]};
200 unshift @cols, 'name';
204 data => [ @{$data}{sort keys %$data} ],
209 columns => [ 'key', 'value' ],
210 data => [ map +{ key => $_, value => $data->{$_} }, sort keys %$data ],
214 sub mangle_array_structure {
215 my ($self, $data) = @_;
216 if (not grep { not ref($_) eq 'HASH' } @$data) {
219 for map { keys %$_ } @$data;
221 columns => [sort keys %key],
228 sub mangle_directory {
229 my ($self, $data) = @_;
231 columns => [ 'name', 'explore' ],
233 map +{ name => $_, explore => $self->link_to($_) }, keys %$data,
238 sub mangle_structure {
239 my ($self, $data) = @_;
241 if (ref($data) eq 'HASH') {
242 return $self->mangle_hash_structure($data);
243 } elsif (ref($data) eq 'ARRAY') {
244 return $self->mangle_array_structure($data);
245 } elsif (blessed($data) and $data->isa('IO::All::Dir')) {
246 return $self->mangle_directory($data);
248 die "Confused by $data";
253 my ($self, @to) = @_;
257 $link =~ s{\\}{\\\\}g;
261 my $link = join('/', @link, '');
263 my $html = join '', HTML::Tags::to_html_string(
264 <a href="./${link}">, "Explore $to", </a>
270 my ($self, $target, @path) = @_;
271 return unless $target;
272 if (blessed($target) and $target->isa('IO::All::File')) {
273 my $all = $target->all;
274 $target = $self->json->decode($all);
276 return $target unless @path;
277 my $step = shift @path;
278 $self->descend($target->{$step}, @path);
282 my ($self, $data) = @_;
285 $data->{show_columns} ? { map +($_ => $_), @{$data->{columns}} } : (),
288 my $column_count = scalar @{$data->{columns}};
289 [ 200, [ 'Content-type' => 'text/html' ], [
290 HTML::Tags::to_html_string(
291 <html>, <body>, "\n",
292 ($data->{wrapper}||sub{@_})->(
295 ' ', ($el->{key} eq '__error__') ? <tr class="error"> : <tr>,
297 <td>, $self->render_el(
301 $data->{aliases}{$_},
303 } @{$data->{columns}}),
308 : (<tr class="no-rows">,
309 <td colspan="$column_count">,
310 'No entries in this data structure',
315 </body>, </html>, "\n",
321 my ($self, $whole, $key, $part, $alias) = @_;
322 my $link_key = defined($alias) ? $alias : $key;
323 if (ref($part) eq 'ARRAY') {
324 if (grep { ref($_) eq 'HASH' } @$part) {
326 return $self->link_to($whole->{key})
327 } elsif ($whole->{name}) {
328 return $self->link_to($whole->{name}, $link_key);
331 return join ', ', @$part
334 return <ul>, (map { (<li>, $_, </li>) } @$part), </ul>;
336 if (ref($part) eq 'HASH') {
338 return $self->link_to($whole->{key})
339 } elsif ($whole->{name}) {
340 return $self->link_to($whole->{name}, $link_key);
345 return $part =~ /\n/ ? (<pre>, $part, </pre>) : $part;
348 __PACKAGE__->run_if_script;