File Coverage

blib/lib/Finance/Currency/Convert/KlikBCA.pm
Criterion Covered Total %
statement 46 54 85.1
branch 10 18 55.5
condition 2 2 100.0
subroutine 8 8 100.0
pod 2 2 100.0
total 68 84 80.9


line stmt bran cond sub pod time code
1             package Finance::Currency::Convert::KlikBCA;
2              
3             our $DATE = '2016-07-22'; # DATE
4             our $VERSION = '0.12'; # VERSION
5              
6 2     2   7845 use 5.010001;
  2         5  
7 2     2   7 use strict;
  2         0  
  2         28  
8 2     2   6 use warnings;
  2         2  
  2         34  
9 2     2   1288 use Log::Any::IfLOG '$log';
  2         17  
  2         7  
10              
11 2     2   98 use Exporter 'import';
  2         3  
  2         1147  
12             our @EXPORT_OK = qw(get_currencies convert_currency);
13              
14             our %SPEC;
15              
16             my $url = "http://www.bca.co.id/id/Individu/Sarana/Kurs-dan-Suku-Bunga/Kurs-dan-Kalkulator";
17              
18             $SPEC{get_currencies} = {
19             v => 1.1,
20             summary => 'Extract data from KlikBCA/BCA page',
21             result => {
22             description => <<'_',
23             Will return a hash containing key `currencies`.
24              
25             The currencies is a hash with currency symbols as keys and prices as values.
26              
27             Tha values is a hash with these keys: `buy_bn` and `sell_bn` (Bank Note buy/sell
28             rates), `buy_er` and `sell_er` (e-Rate buy/sell rates), `buy_ttc` and `sell_ttc`
29             (Telegraphic Transfer Counter buy/sell rates).
30              
31             _
32             },
33             };
34             sub get_currencies {
35 2     2 1 1550 require Mojo::DOM;
36 2         105618 require Parse::Number::ID;
37              
38 2         1092 my %args = @_;
39              
40             #return [543, "Test parse failure response"];
41              
42 2         5 my $page;
43 2 100       7 if ($args{_page_content}) {
44 1         3 $page = $args{_page_content};
45             } else {
46 1         503 require Mojo::UserAgent;
47 1         110305 my $ua = Mojo::UserAgent->new;
48 1         8 my $tx = $ua->get($url);
49 1 50       3536790 unless ($tx->success) {
50 0         0 my $err = $tx->error;
51 0         0 return [500, "Can't retrieve BCA page ($url): ".
52             "$err->{code} - $err->{message}"];
53             }
54 1         19 $page = $tx->res->body;
55             }
56              
57 2         567 my $dom = Mojo::DOM->new($page);
58              
59 2         236195 my %currencies;
60 2         13 my $tbody = $dom->find("tbody.text-right")->[0];
61             $tbody->find("tr")->each(
62             sub {
63 28     28   5720 my $row0 = shift;
64             my $row = $row0->find("td")->map(
65 28         51 sub { $_->text })->to_array;
  196         10612  
66             #use DD; dd $row;
67 28 50       735 next unless $row->[0] =~ /\A[A-Z]{3}\z/;
68 28         59 $currencies{$row->[0]} = {
69             sell_er => Parse::Number::ID::parse_number_id(text=>$row->[1]),
70             buy_er => Parse::Number::ID::parse_number_id(text=>$row->[2]),
71             sell_ttc => Parse::Number::ID::parse_number_id(text=>$row->[3]),
72             buy_ttc => Parse::Number::ID::parse_number_id(text=>$row->[4]),
73             sell_bn => Parse::Number::ID::parse_number_id(text=>$row->[5]),
74             buy_bn => Parse::Number::ID::parse_number_id(text=>$row->[6]),
75             };
76             }
77 2         38853 );
78              
79 2 50       198 if (keys %currencies < 3) {
80 0         0 return [543, "Check: no/too few currencies found"];
81             }
82              
83             # XXX parse update dates (mtime_er, mtime_ttc, mtime_bn)
84 2         1804 [200, "OK", {currencies=>\%currencies}];
85             }
86              
87             # used for testing only
88             our $_get_res;
89              
90             $SPEC{convert_currency} = {
91             v => 1.1,
92             summary => 'Convert currency using KlikBCA',
93             args => {
94             n => {
95             schema=>'float*',
96             req => 1,
97             pos => 0,
98             },
99             from => {
100             schema=>'str*',
101             req => 1,
102             pos => 1,
103             },
104             to => {
105             schema=>'str*',
106             req => 1,
107             pos => 2,
108             },
109             which => {
110             summary => 'Select which rate to use (default is average buy+sell for e-Rate)',
111             schema => ['str*', in=>[map { my $bsa = $_; map {"${bsa}_$_"} qw(bn er ttc) } qw(buy sell avg)]],
112             description => <<'_',
113              
114             {buy,sell,avg}_{bn,er,ttc}.
115              
116             _
117             default => 'avg_er',
118             pos => 3,
119             },
120             },
121             args_as => 'array',
122             result_naked => 1,
123             };
124             sub convert_currency {
125 2     2 1 694 my ($n, $from, $to, $which) = @_;
126              
127 2   100     10 $which //= 'avg_er';
128              
129 2 50       5 unless ($_get_res) {
130 0         0 $_get_res = get_currencies();
131 0 0       0 unless ($_get_res->[0] == 200) {
132 0         0 warn "Can't get currencies: $_get_res->[0] - $_get_res->[1]\n";
133 0         0 return undef;
134             }
135             }
136              
137 2 50       7 if (uc($to) ne 'IDR') {
138 0         0 die "Currently only conversion to IDR is supported".
139             " (you asked for conversion to '$to')\n";
140             }
141              
142 2 50       6 my $c = $_get_res->[2]{currencies}{uc $from} or return undef;
143              
144 2         2 my $rate;
145 2 100       8 if ($which =~ /\Aavg_(.+)/) {
146 1         6 $rate = ($c->{"buy_$1"} + $c->{"sell_$1"}) / 2;
147             } else {
148 1         3 $rate = $c->{$which};
149             }
150              
151 2         7 $n * $rate;
152             }
153              
154             1;
155             # ABSTRACT: Convert currency using KlikBCA
156              
157             __END__
158              
159             =pod
160              
161             =encoding UTF-8
162              
163             =head1 NAME
164              
165             Finance::Currency::Convert::KlikBCA - Convert currency using KlikBCA
166              
167             =head1 VERSION
168              
169             This document describes version 0.12 of Finance::Currency::Convert::KlikBCA (from Perl distribution Finance-Currency-Convert-KlikBCA), released on 2016-07-22.
170              
171             =head1 SYNOPSIS
172              
173             use Finance::Currency::Convert::KlikBCA qw(convert_currency);
174              
175             printf "1 USD = Rp %.0f\n", convert_currency(1, 'USD', 'IDR');
176              
177             =head1 DESCRIPTION
178              
179             =head1 FUNCTIONS
180              
181             =head2 convert_currency($amount, $from, $to) => NUM
182              
183             Currently can only handle conversion *to* IDR. Dies if given other currency.
184              
185             Will warn if failed getting currencies from the webpage.
186              
187             Currency rate is not cached (retrieved from the website every time). Employ your
188             own caching.
189              
190             Currently uses the Bank Notes rate.
191              
192             Will return undef if no conversion rate is available for the requested currency.
193              
194             Use get_currencies(), which actually retrieves and scrapes the source web page,
195             if you need the more complete result.
196              
197              
198             =head2 convert_currency($n, $from, $to, $which) -> any
199              
200             Convert currency using KlikBCA.
201              
202             This function is not exported by default, but exportable.
203              
204             Arguments ('*' denotes required arguments):
205              
206             =over 4
207              
208             =item * B<$from>* => I<str>
209              
210             =item * B<$n>* => I<float>
211              
212             =item * B<$to>* => I<str>
213              
214             =item * B<$which> => I<str> (default: "avg_er")
215              
216             Select which rate to use (default is average buy+sell for e-Rate).
217              
218             {buy,sell,avg}_{bn,er,ttc}.
219              
220             =back
221              
222             Return value: (any)
223              
224              
225             =head2 get_currencies() -> [status, msg, result, meta]
226              
227             Extract data from KlikBCA/BCA page.
228              
229             This function is not exported by default, but exportable.
230              
231             No arguments.
232              
233             Returns an enveloped result (an array).
234              
235             First element (status) is an integer containing HTTP status code
236             (200 means OK, 4xx caller error, 5xx function error). Second element
237             (msg) is a string containing error message, or 'OK' if status is
238             200. Third element (result) is optional, the actual result. Fourth
239             element (meta) is called result metadata and is optional, a hash
240             that contains extra information.
241              
242             Return value: (any)
243              
244              
245             Will return a hash containing key C<currencies>.
246              
247             The currencies is a hash with currency symbols as keys and prices as values.
248              
249             Tha values is a hash with these keys: C<buy_bn> and C<sell_bn> (Bank Note buy/sell
250             rates), C<buy_er> and C<sell_er> (e-Rate buy/sell rates), C<buy_ttc> and C<sell_ttc>
251             (Telegraphic Transfer Counter buy/sell rates).
252              
253             =head1 HOMEPAGE
254              
255             Please visit the project's homepage at L<https://metacpan.org/release/Finance-Currency-Convert-KlikBCA>.
256              
257             =head1 SOURCE
258              
259             Source repository is at L<https://github.com/perlancar/perl-Finance-Currency-Convert-KlikBCA>.
260              
261             =head1 BUGS
262              
263             Please report any bugs or feature requests on the bugtracker website L<https://rt.cpan.org/Public/Dist/Display.html?Name=Finance-Currency-Convert-KlikBCA>
264              
265             When submitting a bug or request, please include a test-file or a
266             patch to an existing test-file that illustrates the bug or desired
267             feature.
268              
269             =head1 SEE ALSO
270              
271             L<http://www.klikbca.com/>
272              
273             =head1 AUTHOR
274              
275             perlancar <perlancar@cpan.org>
276              
277             =head1 COPYRIGHT AND LICENSE
278              
279             This software is copyright (c) 2016 by perlancar@cpan.org.
280              
281             This is free software; you can redistribute it and/or modify it under
282             the same terms as the Perl 5 programming language system itself.
283              
284             =cut