CatalystX::CRUD::YUI::Serializer - flatten CatalystX::CRUD::Object instances


CatalystX-CRUD-YUI documentation Contained in the CatalystX-CRUD-YUI distribution.

Index


Code Index:

NAME

Top

CatalystX::CRUD::YUI::Serializer - flatten CatalystX::CRUD::Object instances

SYNOPSIS

Top

 use CatalystX::CRUD::YUI::Serializer;

 my $serializer = CatalystX::CRUD::YUI::Serializer->new(
                    datetime_format => '%Y-%m-%d %H:%M:%S',
                    html_escape     => 1,
                    );

 my $hashref = $serializer->serialize_object( 
                    object      => $my_object,
                    col_names   => [qw( id name email )],  
                    cat_context => $c,
                    rel_info    => $rel_info,
                );




DESCRIPTION

Top

CatalystX::CRUD::YUI::Serializer turns objects into hashrefs, typically for rendering as JSON.

METHODS

Top

Only new or overridden method are documented here.

new

Instantiate new Serializer.

datetime_format

Set strftime-style DateTime format string. Default is '%Y-%m-%d %H:%M:%S'. Used in serialize_object().

html_escape

serialize_object() will escape all special HTML characters by default. Set html_escape to false (0) if turn that feature off.

serialize_object( params )

Serialize a CatalystX::CRUD::Object instance, or an object that acts like one. params should be a hash or hashref of key/value pairs. The "object" key pair and "col_names" key pair are required.

params include:

object is the CRUD object to be serialized. Required

rel_info is a Rose::HTMLx::Form::Related::RelInfo object.

col_names is the list of column names to include in the serialized hashref. Required

parent_object is the originating object, in the case where you are serializing related objects.

cat_context is a $c object.

show_related_values is a hash ref of methods and foreign fields, as defined by Rose::HTMLx::Form::Related.

takes_object_as_argument is a hashref of method names where parent_object is expected as a single argument.

Returns a hashref of key/value pairs representing the object.

serialize_livegrid( livegrid_object )

Returns array ref of hash refs as passed through serialize_object().

AUTHOR

Top

Peter Karman, <karman@cpan.org>

BUGS

Top

Please report any bugs or feature requests to bug-catalystx-crud-yui@rt.cpan.org, or through the web interface at http://rt.cpan.org. I will be notified, and then you'll automatically be notified of progress on your bug as I make changes.

ACKNOWLEDGEMENTS

Top

The Minnesota Supercomputing Institute http://www.msi.umn.edu/ sponsored the development of this software.

COPYRIGHT & LICENSE

Top


CatalystX-CRUD-YUI documentation Contained in the CatalystX-CRUD-YUI distribution.
package CatalystX::CRUD::YUI::Serializer;

use warnings;
use strict;
use Carp;
use base 'Class::Accessor::Fast';
use MRO::Compat;
use mro "c3";
use Scalar::Util qw( blessed );
use JSON::XS ();
use Data::Dump qw( dump );

__PACKAGE__->mk_accessors(qw( datetime_format yui html_escape ));

our $VERSION = '0.025';

# html escaping
my %Ents = (
    '>' => '&gt;',
    '<' => '&lt;',
    '&' => '&amp;',
    '"' => '&quot;',
    "'" => '&apos;'
);
my $ToEscape = join( '', keys %Ents );

sub new {
    my $class = shift;
    my $self = $class->next::method( ref $_[0] ? @_ : {@_} );
    $self->{datetime_format} ||= '%Y-%m-%d %H:%M:%S';
    $self->{html_escape} = 1 unless defined $self->{html_escape};
    return $self;
}

sub serialize_object {
    my $self         = shift;
    my %opts         = ref( $_[0] ) ? %{ $_[0] } : @_;
    my $object       = delete $opts{object} or croak "CRUD object required";
    my $show_related = delete $opts{show_related_values};
    my $takes_object = delete $opts{takes_object_as_argument};
    my $col_names    = delete $opts{col_names} or croak "col_names required";

    if ( defined $show_related
        and ref($show_related) ne 'HASH' )
    {
        croak "show_related_values should be a hashref";
    }
    if ( defined $takes_object
        and ref($takes_object) ne 'HASH' )
    {
        croak "takes_object_as_argument should be a hashref";
    }
    if ( ref($col_names) ne 'ARRAY' ) {
        croak "col_names array ref required";
    }

    my $flat = {};
    if ( defined $opts{livegrid} and $opts{livegrid}->show_remove_button ) {
        $flat->{'_remove'} = ' X ';
    }

    #carp "calling col_names on $object : " . dump $col_names;

    #carp dump $takes_object;

    for my $col (@$col_names) {

        # if $col is array ref, then it is PK.
        # however, value may not always be primary_key_uri_escaped()
        # since the controller may define an alternate primary_key.
        # so use the controller to get the value.
        if ( ref($col) eq 'ARRAY' ) {
            $flat->{ join( ';;', @$col ) } = $opts{livegrid}
                ->controller->make_primary_key_string($object);
            next;
        }

        # if $col has a . (dot) then it is a chained string of methods
        my @methods = split( m/\./, $col );
        my $first_method = shift @methods;

        # sanity check
        if ( !$object->can($first_method) ) {
            croak "no such method '$first_method' for object $object";
        }

        # non-accessor methods. these are NOT FK methods.
        # see below for $show_related_values.
        if ( exists $takes_object->{$col} and exists $opts{parent} ) {

            # TODO revisit this api
            # right now we only pass parent if it isa class
            # designated in the $takes_object hash

            #warn "FOUND takes_object $col => $opts{parent}";

            if ( my $parent_class = blessed( $opts{parent} ) ) {
                my $obj_to_pass;
                if ( $opts{parent}->can('delegate')
                    and blessed( $opts{parent}->delegate ) )
                {

                    #warn " obj with delegate = " . $opts{parent}->delegate;
                    if ( $opts{parent}->delegate->isa( $takes_object->{$col} )
                        )
                    {
                        $obj_to_pass = $opts{parent}->delegate;
                    }
                }
                elsif ( $opts{parent}->isa( $takes_object->{$col} ) ) {
                    $obj_to_pass = $opts{parent};
                }

                if ($obj_to_pass) {
                    eval {
                        $flat->{$col}
                            = $object->$first_method( $opts{parent} );
                    };
                    if ($@) {
                        $flat->{$col} = '[not available]';
                    }
                }
                else {
                    $flat->{$col} = $object->$first_method;
                }

            }
            else {
                eval {
                    $flat->{$col} = $object->$first_method( $opts{parent} );
                };
                if ($@) {
                    $flat->{$col} = '[not available]';
                }
            }

            next;

        }

        # get end value
        my $value = $object->$first_method;
        if ( defined $value ) {
            for my $m (@methods) {
                my $v = $value->$m;
                if ( defined $v ) {
                    $value = $v;
                }
            }
        }

        # DateTime objects
        if ( blessed($value) && $value->isa('DateTime') ) {
            if ( defined $value->epoch ) {
                $flat->{$col} = $value->strftime( $self->datetime_format );
            }
            else {
                $flat->{$col} = '';
            }
        }

        # FKs
        elsif ( defined $show_related
            and exists $show_related->{$col} )
        {
            my $srv    = $show_related->{$col};
            my $method = $srv->{method};
            my $ff     = $srv->{foreign_field};

            #warn "col: $col  rdbo: $rdbo  method: $method  ff: $ff";
            if ( defined $object->$method && defined $ff ) {
                $flat->{$col} = $object->$method->$ff;
            }
            else {
                $flat->{$col} = $value;
            }
        }

        # booleans
        elsif ( $object->can('column_is_boolean')
            and $object->column_is_boolean($col) )
        {
            $flat->{$col} = $value ? 'true' : 'false';
        }

        # default
        else {
            $flat->{$col} = $value;

        }

    }

    # html escape
    if ( $self->html_escape ) {
        for ( keys %$flat ) {
            next if !defined $flat->{$_};
            $flat->{$_} =~ s/([$ToEscape])/$Ents{$1}/og;
        }
    }

    return $flat;

}

sub serialize_livegrid {
    my $self     = shift;
    my $livegrid = shift or croak "LiveGrid object required";
    my $results  = $livegrid->results
        or croak "no results in LiveGrid object";
    my $method_name = $livegrid->method_name || '';
    my $params = $livegrid->c->req->params;
    my $max_loops;
    if ( $results->query->{limit} ) {
        $max_loops = 0;    # db handled the limit
    }
    else {
        $max_loops
            = $params->{'cxc-no_page'}
            ? 0
            : (    $params->{'cxc-page_size'}
                || $livegrid->controller->page_size );
    }

    my $counter = 0;
    my @data;
    my $iterator;

    if ( $results->isa('CatalystX::CRUD::Results') ) {
        $iterator = $results;
    }
    else {
        if ( !$method_name ) {
            croak
                "method_name required for non-CatalystX::CRUD::Results object with 'results' key";
        }
        my $method = $method_name . '_iterator';
        $iterator = $results->$method;
    }

    while ( my $object = $iterator->next ) {
        push(
            @data,
            $self->serialize_object(
                {   object      => $object,
                    method_name => $method_name,
                    col_names   => $livegrid->col_names,
                    parent      => $livegrid->c->stash->{object},
                    c           => $livegrid->c,
                    show_related_values => $livegrid->show_related_values,
                    takes_object_as_argument =>
                        $livegrid->form->metadata->takes_object_as_argument,
                    livegrid => $livegrid,
                }
            )
        );
        last if $max_loops > 0 && ++$counter > $max_loops;
    }

    $livegrid->{count} = $counter;

    return \@data;
}

1;

__END__