package Pear::LocalLoop::Import::Role::CSV; use strict; use warnings; use Moo::Role; use Text::CSV; use Try::Tiny; use Pear::LocalLoop::Error; requires 'csv_required_columns'; has csv_file => ( is => 'ro', predicate => 1, ); has csv_string => ( is => 'ro', predicate => 1, ); has csv_error => ( is => 'ro', predicate => 1, ); has _csv_filehandle => ( is => 'lazy', builder => sub { my $self = shift; my $fh; if ( $self->has_csv_file ) { open $fh, '<', $self->csv_file; } elsif ( $self->has_csv_string ) { my $string = $self->csv_string; open $fh, '<', \$string; } else { die "Must provide csv_file or csv_string"; } return $fh; } ); has text_csv_options => ( is => 'lazy', builder => sub { return { binary => 1, allow_whitespace => 1, }; } ); has _text_csv => ( is => 'lazy', builder => sub { return Text::CSV->new( shift->text_csv_options ); } ); has csv_data => ( is => 'lazy', builder => sub { my $self = shift; my $header_check = $self->check_headers; return 0 unless $header_check; return $self->_text_csv->getline_hr_all( $self->_csv_filehandle ); } ); sub get_csv_line { my $self = shift; return $self->_text_csv->getline_hr( $self->_csv_filehandle ); } sub check_headers { my $self = shift; my $req_headers = $self->csv_required_columns; my @headers; @headers = $self->_text_csv->header( $self->_csv_filehandle ); my %header_map = ( map { $_ => 1 } @headers ); for my $req_header (@$req_headers) { next if $header_map{$req_header}; die "Require header [" . $req_header . "]"; } return 1; } 1;