File Coverage

blib/lib/WWW/Jawbone/Up.pm
Criterion Covered Total %
statement 82 99 82.8
branch 8 20 40.0
condition 3 9 33.3
subroutine 21 25 84.0
pod 6 6 100.0
total 120 159 75.4


line stmt bran cond sub pod time code
1             package WWW::Jawbone::Up;
2              
3 6     6   111 use 5.010;
  6         21  
  6         309  
4 6     6   33 use strict;
  6         11  
  6         164  
5 6     6   30 use warnings;
  6         13  
  6         343  
6              
7             our $VERSION = '1.32.4';
8              
9             =encoding utf-8
10              
11             =head1 NAME
12              
13             WWW::Jawbone::Up - Unofficial Jawbone UP API
14              
15             =head1 SYNOPSIS
16              
17             use WWW::Jawbone::Up;
18              
19             my $up = WWW::Jawbone::Up->connect('alan@eatabrick.org', 's3kr3t');
20              
21             my $user = $up->user;
22             my $score = $up->score;
23              
24             say $user->name . ' walked ' . $score->move->distance . 'km today';
25              
26             my $hours = int($score->sleep->asleep / 3600);
27             my $minutes = int($score->sleep->asleep / 60); % 60;
28              
29             say $user->name . " slept for ${hours}h${minutes}m last night";
30              
31             =head1 DESCRIPTION
32              
33             WWW::Jawbone::Up is a perl binding to the unofficial Jawbone UP API. After
34             authenticating you can find interesting bits of data. The version number of
35             this library should reflect the associated version of the Jawbone API but I
36             have no way of knowing if they will be sane with their version numbers so we'll
37             see how that pans out.
38              
39             =head1 METHODS
40              
41             =cut
42              
43 6     6   43 use Carp;
  6         16  
  6         549  
44 6     6   29253 use DateTime;
  6         1677618  
  6         338  
45 6     6   10215 use JSON 2.0;
  6         120142  
  6         50  
46 6     6   10374 use LWP::UserAgent;
  6         462328  
  6         292  
47 6     6   74 use URI::Escape;
  6         15  
  6         1592  
48              
49 6     6   5308 use WWW::Jawbone::Up::Feed;
  6         19  
  6         252  
50 6     6   4560 use WWW::Jawbone::Up::Score;
  6         15  
  6         156  
51 6     6   5630 use WWW::Jawbone::Up::Tick;
  6         17  
  6         151  
52 6     6   3384 use WWW::Jawbone::Up::User;
  6         17  
  6         165  
53 6     6   4100 use WWW::Jawbone::Up::Workout;
  6         18  
  6         181  
54              
55 6     6   40 use constant URI_BASE => 'https://jawbone.com';
  6         11  
  6         542  
56 6     6   40 use constant URI_API => URI_BASE . '/nudge/api/v.1.32';
  6         12  
  6         7144  
57              
58             sub _request {
59 0     0   0 my ($self, $method, $uri, $data) = @_;
60              
61 0   0     0 $self->{ua} ||= LWP::UserAgent->new();
62              
63 0         0 my $request = HTTP::Request->new($method, $uri);
64 0 0       0 $request->header('x-nudge-token', $self->{token}) if $self->{token};
65 0         0 $request->content($data);
66              
67 0         0 my $response = $self->{ua}->request($request);
68              
69 0 0       0 croak $response->status_line if $response->is_error;
70              
71 0         0 return decode_json($response->decoded_content);
72             }
73              
74             sub __encode {
75 0     0   0 my ($hash) = @_;
76              
77 0         0 return join '&',
78             map sprintf('%s=%s', uri_escape($_), uri_escape($hash->{$_})),
79             keys %$hash;
80             }
81              
82             sub _get {
83 0     0   0 my ($self, $uri, $data) = @_;
84              
85 0         0 my $query_string = __encode($data);
86 0 0       0 $uri .= ($uri =~ /\?/ ? '&' : '?') . $query_string;
87              
88 0         0 return $self->_request(GET => $uri);
89             }
90              
91             sub _post {
92 0     0   0 my ($self, $uri, $data) = @_;
93              
94 0         0 my $body = __encode($data);
95              
96 0         0 return $self->_request(POST => $uri, $body);
97             }
98              
99             =head2 connect($email, $password)
100              
101             Authenticates with the API. Will return undef if there are any errors,
102             possibly also dying or warning about them.
103              
104             =cut
105              
106             sub connect {
107 7     7 1 1151 my ($class, $email, $password) = @_;
108              
109 7         28 my $self = bless {}, $class;
110              
111 7         68 my $json = $self->_post(
112             URI_BASE . '/user/signin/login', {
113             service => 'nudge',
114             email => $email,
115             pwd => $password,
116             });
117              
118 7 100       39 if ($json->{error}) {
119 1         420 carp $json->{error}{msg};
120 1         12 return undef;
121             }
122              
123 6         78 $self->{token} = $json->{token};
124              
125 6         21 return $self;
126             }
127              
128             =head2 user()
129              
130             Returns a L object representing the currently
131             authenticated user.
132              
133             =cut
134              
135             sub user {
136 1     1 1 7 my ($self) = @_;
137              
138 1 50       3 unless ($self->{user}) {
139 1         5 my $json = $self->_get(URI_API . '/users/@me');
140 1         14 $self->{user} = WWW::Jawbone::Up::User->new($json->{data});
141             }
142              
143 1         6 return $self->{user};
144             }
145              
146             =head2 feed($date)
147              
148             Returns an array of L objects for the given C<$date>.
149             If no date is given, the current date is used.
150              
151             =cut
152              
153             sub feed {
154 1     1 1 10 my ($self, $date, $options) = @_;
155              
156 1   50     212 $options ||= {};
157 1 50       9 $options->{date} = $date if defined $date;
158              
159 1         8 my $json = $self->_get(URI_API . '/users/@me/social', $options);
160              
161 1         3 return map WWW::Jawbone::Up::Feed->new($_), @{ $json->{data}{feed} };
  1         21  
162             }
163              
164             =head2 score($date)
165              
166             Returns a L object for the given C<$date>. If no date
167             is given, the current date is used.
168              
169             =cut
170              
171             sub score {
172 1     1 1 12 my ($self, $date, $options) = @_;
173              
174 1         2 $options = {};
175 1 50       7 $options->{date} = $date if defined $date;
176              
177 1         7 my $json = $self->_get(URI_API . '/users/@me/score', $options);
178              
179 1         16 return WWW::Jawbone::Up::Score->new($json->{data});
180             }
181              
182             =head2 band($start, $end)
183              
184             Returns an array of L objects for the given time range.
185             Both C<$start> and C<$end> should be epoch times (seconds since epoch). If no
186             range is given, I think the current day is used, but I haven't really
187             researched that fact too much.
188              
189             =cut
190              
191             sub band {
192 1     1 1 8 my ($self, $start, $end, $options) = @_;
193              
194 1   50     6 $options ||= {};
195 1 50       4 $options->{start_time} = $start if defined $start;
196 1 50       3 $options->{end_time} = $end if defined $end;
197              
198 1         5 my $json = $self->_get(URI_API . '/users/@me/band', $options);
199              
200 1         15 return map WWW::Jawbone::Up::Tick->new($_->{value}),
201 1         2 @{ $json->{data}{ticks} };
202             }
203              
204             =head2 workouts($date)
205              
206             Returns an array of L objects for the give C<$date>.
207             If no $date is given, I think some number of recent workouts are given, but I
208             haven't really researched that fact too much.
209              
210             =cut
211              
212             sub workouts {
213 1     1 1 8 my ($self, $date, $options) = @_;
214              
215 1   50     8 $options ||= {};
216 1 50       6 $options->{date} = $date if defined $date;
217              
218 1         5 my $json = $self->_get(URI_API . '/users/@me/workouts', $options);
219              
220 1         3 return map WWW::Jawbone::Up::Workout->new($_), @{ $json->{data}{items} };
  1         14  
221             }
222              
223             1;
224              
225             =head1 TODO
226              
227             The documentation for this distribution is utterly lacking. I will fix that
228             sometime soon.
229              
230             =head1 AUTHOR
231              
232             Alan Berndt Ealan@eatabrick.orgE
233              
234             =head1 COPYRIGHT
235              
236             Copyright 2013 Alan Berndt
237              
238             =head1 LICENSE
239              
240             Permission is hereby granted, free of charge, to any person obtaining a copy of
241             this software and associated documentation files (the "Software"), to deal in
242             the Software without restriction, including without limitation the rights to
243             use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
244             of the Software, and to permit persons to whom the Software is furnished to do
245             so, subject to the following conditions:
246              
247             The above copyright notice and this permission notice shall be included in all
248             copies or substantial portions of the Software.
249              
250             THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
251             IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
252             FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
253             AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
254             LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
255             OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
256             SOFTWARE.
257              
258             =head1 SEE ALSO
259              
260             L
261             L
262             L
263             L
264             L
265              
266             =cut