Group
Extension

Forex/Forex.pm

package Forex;

use JSON::XS;
use DateTime;
use LWP::UserAgent;

our $LASTERROR = undef;

$VERSION  = '1.2';

=pod

=head1 NAME

Forex - Historic Foreign Exchange Rates from Open Exchange Rates 

=head1 VERSION

1.2

=head1 SYNOPSIS

 use Forex;
 
 my $oxr = new Forex( 'APP_ID' => $app_id, 'BASE' => 'USD');
 
 #__fetch and initialize daily rates from $from_date to $to_date in yyyy-mm-dd 
 $oxr->get_rates_from ( $from_date, $to_date );

 #___ fetches rates for $date (yyyy-mm-dd)
 $oxr->get_rates( $date );
	 
 #___ fetches reates for today 
 $oxr->get_rates();
	
 #___ gives AUD on 2012-09-01 in USD Base currency
 $usd = $oxr->get_rate_of ( 'AUD', '2012-09-01' );  
 
 if ($Forex::OXR::LASTERROR )
    { print "\n Something went wrong" , $oxr->last_error_message(); }  

=head1 DESCRIPTION


=head1 METHODS

=head2 Constructors

=head3 new Forex()

returns new Forex object with defaults values for 
OXR_HOME = 'http://openexchangerates.org',
API_HOOK = 'api',
APP_ID = 'temp-e091fc14b3884a516d6cc2c299a',
BASE = 'USD'

 my $oxe = new Forex::OXR( OXR_HOME => 'https://openexchangerates.org',
 									API_HOOK => 'api',
 									APP_ID   => 'temp-e091fc14b3884a516d6cc2c299a',
 									BASE     => 'AUD');

=cut

sub new {
	my $class = shift;
	my $self = {@_};
	bless $self, $class;
	$self->{ 'OXR_HOME' } = 'http://openexchangerates.org';
	$self->{ 'API_HOOK' } = 'api';
	$self->{ 'APP_ID'   } = 'temp-e091fc14b3884a516d6cc2c299a' if !$self->{APP_ID};
	$self->{ 'BASE'     } = 'USD';
	return $self;
}

=pod

=head3 get_rate_of( $currency, <$date> )

This method returns forex rate for $currency on $date in USD, $date should be in C<yyyy-mm-dd> format.
This method looks for currency rates in $obj->{ CURRENCIES } array.Which is filled by C<get_rates> or C<get_rates_from()> methods


my $AUD = $oxr->get_rate_of( 'AUD' , '2012-09-10' );

=cut

sub get_rate_of {
	my ($self, $currency, $date ) = @_;

	if (!$date)
		{ my $_d = DateTime->now();
		  $date = join "-",($_d->year, $_d->month, $_d->day); }
		  
	if ($date !~ m/-/)
		{ $LASTERROR = 1;
		  $self->{ERROR} = 'date parameter not in in yyy-mm-dd format';
		  return undef;                                               }

	if( !$self->{ 'CURRENCIES'} || !$self->{'CURRENCIES'}->{$date})
	  { $self->get_rates( $date ); }
		  
	return $self->{ 'CURRENCIES' }->{ $currency }->{ $date };
}

sub _fetch_data {
	my ($self) = @_;
	my ($response);
		   
	if ( !$self->{'LWP_OBJ'} )
		{  $self->{ 'LWP_OBJ' } = new LWP::UserAgent();
		   $self->{ 'LWP_OBJ' }->timeout( $self->{'TIMEOUT'} );  }

	      $response = $self->{'LWP_OBJ'}->get( $self->{ 'REQUEST_URL' } );

	if ( $response->is_success )
	   { $self->{ 'CONTENT' } = decode_json $response->decoded_content; $LASTERROR = undef; }
	else
		{ $self->{ 'ERROR'} = decode_json $response->decoded_content;
		  $LASTERROR = 1; }
		
	delete $self->{ 'REQUEST_URL' };

	1;
}

=pod

=head3 get_rates_from ( $from_date , $to_date ) 

downloads and fills in values for all currencies in the $obj->{CURRENCIES} hash for $from_date - $to_date
both dates should be in C<yyyy-mm-dd> formate

=cut

sub get_rates_from {
	my ($self, $from , $to) = @_;
	my ( $from_dt, $to_dt );
	
	if ( $from !~ m/-/ && $to !~ m/-/ )
		{ $self->{ERROR} = "to date or from date not in yyyy-mm-dd formate";
		  $LASTERROR = 1; 
		  return undef;                                                    }
		
	   ( $y, $m , $d ) = split '-' , $from;
	     $from_dt = new DateTime( year => $y, month => $m , day => $d );
		( $y, $m , $d ) = split '-' , $to;
	     $to_dt   = new DateTime( year => $y, month => $m , day => $d );
	
	while ( DateTime->compare( $from_dt , $to_dt ) )
	{ $self->get_rates( $from_dt->ymd('-') );
		$from_dt->add( days => 1 );	              }
	
	1;
}

=pod

=head3 get_rates ( <$day> ) 

downloads and fills in values for all currencies in the $obj->{CURRENCIES} hash for given C<$day>
$day should be in C<yyyy-mm-dd> formate
if $day is skipped , it uses C<todays date>,

=cut

sub get_rates {
	my ($self, $day) = @_;
	my ($request_day, $response_day );

	if ( !$day )
		{ $request_day = DateTime->now();
		  $request_day = new DateTime ( year  => $request_day->year,
			  					 				  month => $request_day->month,
			  									  day   => $request_day->day );     	 }
	else
	   { my ( $y, $m , $d ) = split '-' , $day;
	     $request_day = new DateTime( year => $y, month => $m , day => $d ); }
	
	if ( !$request_day )
	   { $LASTERROR = 1; $self->{ERROR} = "request day is not defined ";   return undef;}
	if ( !$self->{'OXR_HOME'} )
	   { $LASTERROR = 1; $self->{ERROR} = "OXR HOME is not defined ";      return undef;}
	if ( !$self->{'API_HOOK'} )
	   { $LASTERROR = 1; $self->{ERROR} = "API HOOK is not defined ";      return undef;}
	if ( !$self->{'APP_ID'} )
	   { $LASTERROR = 1; $self->{ERROR} = "APP ID is not defined ";        return undef;}
	if ( !$self->{'BASE'} )
	   { $LASTERROR = 1; $self->{ERROR} = "BASE Currency is not defined "; return undef;}
	   	   	   	   	
	$self->{'REQUEST_URL'} = join "/", ( $self->{ 'OXR_HOME'},
  												    $self->{'API_HOOK' },
	  												 'historical'        ,
	  												 $request_day->ymd('-') . '.json' );

 	$self->{'REQUEST_URL'} .= '?app_id='   . $self->{'APP_ID'}
								  .   "&base="    . $self->{'BASE'} ;
	
	$self->_fetch_data();
		
	if ( $self->{ CONTENT } )
	 	{ $response_day = DateTime->from_epoch( epoch => $self->{ 'CONTENT' }->{ 'timestamp' } );
		  $response_day = new DateTime (	year  => $response_day->year,
		  											month => $response_day->month,
		  											day   => $response_day->day );                   		}

	if ( DateTime->compare($request_day , $response_day) )
		{ $LASTERROR = 1; $self->{ERROR} = "Request Date is not equal to Received Date"; }
			
	my $hash = $self->{ 'CONTENT' }->{ 'rates' };	
	map {	$self->{ 'CURRENCIES' } { $_ } { $response_day->ymd() } = $hash->{ $_}; } keys( %$hash );	
		
	delete $self->{ 'CONTENT' };
								
}

=pod

=head3 get_currency_symbols()

this method downloads and initializes all currency symbols from openexchangerates site.
this method should be run before either C<get_rates> or C<get_rates_from>

=cut

sub get_currency_symbols {
	my ($self) = @_;
	my $currenciesJSON = 'currencies.json';

	if ( !$self->{'OXR_HOME'} )
	   { $LASTERROR = 1; $self->{ERROR} = "OXR HOME is not defined ";      return undef;}
	if ( !$self->{'API_HOOK'} )
	   { $LASTERROR = 1; $self->{ERROR} = "API HOOK is not defined ";      return undef;}

      $self->{'REQUEST_URL'} = join "/" , ( 	 $self->{ 'OXR_HOME'},
		  													 $self->{ 'API_HOOK'},
		   												 $currenciesJSON
				   										);  
	#_____ fetching data	
	   $self->_fetch_data();
	
	my $hash = $self->{ 'CONTENT' };
		map {	$self->{ 'CURRENCIES' } { $_ } { 'description' } = $hash->{ $_}; } keys( %$hash );

		delete $self->{ 'CONTENT' };	   
}

=pod

=head3 base_currency( <$BASE_CURRENCY> )

sets BASE currency so that succeeding request will request the rates with base currency as specified by $BASE_CURRENCY.
if the parameter is omitted it return the current BASE_CURRENCY value;

Note: you will have to flush the $CURRENCIES hash if you change the BASE currency. with C<flush_values()>

=cut

sub base_currency { 	return ($_[1]) ? $_[0]->{ 'BASE'    } = $_[1] : $_[0]->{ 'BASE'     }; }

=pod

=head3 oxr_home ( <$OXR_HOME> )

sets OXR_HOME parameter to $OXR_HOME value , if the parameter is omitted it returns the current value of the OXR_HOME.
OXR_HOME value should cuntaion "http://" .

Note: you could you it to change the default "http://" to "https://" if you have enterprise APP_ID

=cut

sub oxr_home	   { 	return ($_[1]) ? $_[0]->{ 'OXR_HOME'} = $_[1] : $_[0]->{ 'OXR_HOME' }; }

=pod

=head3 app_id ( <$APP_ID> )

sets app_id for all succeeding requests. return current app_id if the parameter is omitted.

=cut

sub app_id			{ 	return ($_[1]) ? $_[0]->{ 'APP_ID'  } = $_[1] : $_[0]->{ 'APP_ID'   }; }

=pod

=head3 get_currencies()

returns all the currencies in the currencies hash

=cut

sub get_currencies { return  keys ( %{$_[0]->{ 'CURRENCIES'}}); }

=pod

=head3 last_error()

returns last error object as returned by Open Exchange Rates API

=cut

sub last_error    {  return  $_[0]->{ 'ERROR' }; }

=pod

=head3 last_error_message ()

returns last error message 

=cut

sub last_error_message {
	my $self = shift; 
	return ($self->{ ERROR }->{message}) ? $self->{ERROR}->{message} : $self->{ERROR}; 
}
1;

=pod

=head3 ERROR

on errors module sets $LASTERROR global variable which can be accessed by $Forex::LASTERROR.
And error message can be accessed via last_error_message() or last_error()

=head3 KNOWN BUGS


=head3 SUPPORT

please submit known issues or bugs to mail4bhavin@yahoo.com

=head3 AUTHOR

Bhavin Patel <mail4bhavin@gmail.com

=head3 COPYRIGHT AND LICENSE

This Software is free to use , licensed under:

	The Artistic License 2.0 (GPL Compatible)

=cut




Powered by Groonga
Maintained by Kenichi Ishigaki <ishigaki@cpan.org>. If you find anything, submit it on GitHub.