From Code to Community: Sponsoring The Perl and Raku Conference 2025 Learn more

our $VERSION = "0.47"; # UR $VERSION;
sub new {
my ($class, %args) = @_;
foreach (qw/iterator show noheaders output/){
die "no value for $_!" unless defined $args{$_};
}
return bless(\%args, $class);
}
sub _get_next_object_from_iterator {
my $self = shift;
my $obj;
for (1) {
$obj = eval { $self->{'iterator'}->next };
if ($@) {
UR::Object::Command::List->warning_message($@);
redo;
}
}
return $obj;
}
sub _object_properties_to_string {
my ($self, $o, $char) = @_;
my @v;
return join(
$char,
map { defined $_ ? $_ : '<NULL>' }
map {
$self->_object_property_to_string($o,$_)
} @{$self->{show}}
);
}
sub _object_property_to_string {
my ($self, $o, $property) = @_;
my @v;
if (substr($property,0,1) eq '(') {
@v = eval $property;
if ($@) {
@v = ('<ERROR>'); # ($@ =~ /^(.*)$/);
}
}
else {
@v = ();
foreach my $i ($o->__get_attr__($property)) {
if (! defined $i) {
push @v, "<NULL>";
}
elsif (Scalar::Util::blessed($i) and $i->isa('UR::Value') and $i->can('create_view')) {
# Here we allow any UR::Values that have their own views to present themselves.
my $v = $i->create_view( perspective => 'default', toolkit => 'text' );
push @v, $v->content();
}
elsif (Scalar::Util::blessed($i) and $i->can('__display_name__')) {
push @v, $i->__display_name__;
}
else {
push @v, $i;
}
}
}
if (@v > 1) {
no warnings;
return join(' ',@v);
}
else {
return $v[0];
}
}
sub format_and_print{
my $self = shift;
unless ( $self->{noheaders} ) {
$self->{output}->print($self->_get_header_string. "\n");
}
my $count = 0;
while (my $object = $self->_get_next_object_from_iterator()) {
$self->{output}->print($self->_get_object_string($object), "\n");
$count++;
}
}
sub _get_header_string{
my $self = shift;
return "<tr><th>". join("</th><th>", map { uc } @{$self->{show}}) ."</th></tr>";
}
sub _get_object_string{
my ($self, $object) = @_;
my $out = "<tr>";
for my $property ( @{$self->{show}} ){
$out .= "<td>" . $object->$property . "</td>";
}
return $out . "</tr>";
}
sub format_and_print{
my $self = shift;
$self->{output}->print("<table>");
#cannot use super because \n screws up javascript
unless ( $self->{noheaders} ) {
$self->{output}->print($self->_get_header_string);
}
my $count = 0;
while (my $object = $self->_get_next_object_from_iterator()) {
$self->{output}->print($self->_get_object_string($object));
$count++;
}
$self->{output}->print("</table>");
}
sub _get_header_string{
my $self = shift;
my $delimiter = $self->{'csv_delimiter'};
return join($delimiter, map { lc } @{$self->{show}});
}
sub _get_object_string {
my ($self, $object) = @_;
return $self->_object_properties_to_string($object, $self->{'csv_delimiter'});
}
sub _get_header_string{
my $self = shift;
my $delimiter = "\t";
return join($delimiter, map { lc } @{$self->{show}});
}
sub _get_object_string {
my ($self, $object) = @_;
return $self->_object_properties_to_string($object, "\t");
}
sub _get_header_string{
return '';
}
sub _get_object_string{
my ($self, $object) = @_;
my $out;
for my $property ( @{$self->{show}} )
{
my $value = join(', ', $self->_object_property_to_string($object,$property));
$out .= sprintf(
"%s: %s\n",
Term::ANSIColor::colored($property, 'red'),
Term::ANSIColor::colored($value, 'cyan'),
);
}
return $out;
}
sub format_and_print{
my $self = shift;
my $out;
eval "use XML::LibXML";
if ($@) {
die "Please install XML::LibXML (run sudo cpanm XML::LibXML) to use this tool!";
}
my $doc = XML::LibXML->createDocument();
my $results_node = $doc->createElement("results");
$results_node->addChild( $doc->createAttribute("generated-at",$UR::Context::current->now()) );
$doc->setDocumentElement($results_node);
my $count = 0;
while (my $object = $self->_get_next_object_from_iterator()) {
my $object_node = $results_node->addChild( $doc->createElement("object") );
my $object_reftype = ref $object;
$object_node->addChild( $doc->createAttribute("type",$object_reftype) );
$object_node->addChild( $doc->createAttribute("id",$object->id) );
for my $property ( @{$self->{show}} ) {
my $property_node = $object_node->addChild ($doc->createElement($property));
my @items = $self->_object_property_to_string($object, $property);
my $reftype = ref $items[0];
if ($reftype && $reftype ne 'ARRAY' && $reftype ne 'HASH') {
foreach (@items) {
my $subobject_node = $property_node->addChild( $doc->createElement("object") );
$subobject_node->addChild( $doc->createAttribute("type",$reftype) );
$subobject_node->addChild( $doc->createAttribute("id",$_->id) );
#$subobject_node->addChild( $doc->createTextNode($_->id) );
#xIF
}
} else {
foreach (@items) {
$property_node->addChild( $doc->createTextNode($_) );
}
}
}
$count++;
}
$self->{output}->print($doc->toString(1));
}
sub _get_header_string{
my $self = shift;
return join (
"\n",
join("\t", map { uc } @{$self->{show}}),
join("\t", map { '-' x length } @{$self->{show}}),
);
}
sub _get_object_string{
my ($self, $object) = @_;
$self->_object_properties_to_string($object, "\t");
}
sub format_and_print{
my $self = shift;
my $tab_delimited;
unless ($self->{noheaders}){
$tab_delimited .= $self->_get_header_string."\n";
}
my $count = 0;
while (my $object = $self->_get_next_object_from_iterator()) {
$tab_delimited .= $self->_get_object_string($object)."\n";
$count++;
}
$self->{output}->print($self->tab2col($tab_delimited));
}
sub tab2col{
my ($self, $data) = @_;
#turn string into 2d array of arrayrefs ($array[$rownum][$colnum])
my @rows = split("\n", $data);
@rows = map { [split("\t", $_)] } @rows;
my $output;
my @width;
#generate array of max widths per column
foreach my $row_ref (@rows) {
my @cols = @$row_ref;
my $index = $#cols;
for (my $i = 0; $i <= $index; $i++) {
my $l = (length $cols[$i]) + 3; #TODO test if we need this buffer space
$width[$i] = $l if ! defined $width[$i] or $l > $width[$i];
}
}
#create a array of blanks to use as a templatel
my @column_template = map { ' ' x $_ } @width;
#iterate through rows and cols, substituting in the row entry in your template
foreach my $row_ref (@rows) {
my @cols = @$row_ref;
my $index = $#cols;
#only apply template for all but the last entry in a row
for (my $i = 0; $i < $index; $i++) {
my $entry = $cols[$i];
my $template = $column_template[$i];
substr($template, 0, length $entry, $entry);
$output.=$template;
}
$output.=$cols[$index]."\n"; #Don't need traling spaces on the last entry
}
return $output;
}
sub format_and_print{
my $self = shift;
my $tab_delimited;
unless ($self->{noheaders}){
$tab_delimited .= $self->_get_header_string."\n";
}
my $view = UR::Object::View->create(
subject_class_name => 'UR::Object',
perspective => 'lister',
toolkit => 'text',
aspects => [ @{$self->{'show'}} ],
);
my $count = 0;
while (my $object = $self->_get_next_object_from_iterator()) {
$view->subject($object);
$tab_delimited .= $view->content() . "\n";
$count++;
}
$self->{output}->print($self->tab2col($tab_delimited));
}
1;
=pod
=head1 NAME
UR::Object::Command::List - Fetches and lists objects in different styles.
=head1 SYNOPSIS
package MyLister;
use strict;
use warnings;
use above "UR";
class MyLister {
is => 'UR::Object::Command::List',
has => [
# add/modify properties
],
};
1;
=head1 Provided by the Developer
=head2 subject_class_name (optional)
The subject_class_name is the class for which the objects will be fetched. It can be specified one of two main ways:
=over
=item I<by_the_end_user_on_the_command_line>
For this do nothing, the end user will have to provide it when the command is run.
=item I<by_the_developer_in the_class_declartion>
For this, in the class declaration, add a has key w/ arrayref of hashrefs. One of the hashrefs needs to be subject_class_name. Give it this declaration:
class MyFetchAndDo {
is => 'UR::Object::Command::FetchAndDo',
has => [
subject_class_name => {
value => <CLASS NAME>,
is_constant => 1,
},
],
};
=back
=head2 show (optional)
Add defaults to the show property:
class MyFetchAndDo {
is => 'UR::Object::Command::FetchAndDo',
has => [
show => {
default_value => 'name,age',
},
],
};
=head2 helps (optional)
Overwrite the help_brief, help_synopsis and help_detail methods to provide specific help. If overwiting the help_detail method, use call '_filter_doc' to get the filter documentation and usage to combine with your specific help.
=head1 List Styles
text, csv, html, xml, pretty (inprogress)
=cut
#$Id: List.pm 50329 2009-08-25 20:10:00Z abrummet $