File Coverage

blib/lib/Metaweb.pm
Criterion Covered Total %
statement 48 94 51.0
branch 4 18 22.2
condition 7 19 36.8
subroutine 9 12 75.0
pod 4 4 100.0
total 72 147 48.9


line stmt bran cond sub pod time code
1             package Metaweb;
2              
3 2     2   24820 use warnings;
  2         5  
  2         75  
4 2     2   12 use strict;
  2         3  
  2         76  
5              
6 2     2   11 use base qw(Class::Accessor);
  2         8  
  2         2219  
7 2     2   8140 use URI::Escape;
  2         3796  
  2         169  
8 2     2   2553 use LWP::UserAgent;
  2         122305  
  2         74  
9 2     2   2417 use JSON::XS;
  2         20340  
  2         155  
10 2     2   1165 use Metaweb::Result;
  2         7  
  2         1959  
11              
12             __PACKAGE__->mk_accessors(qw(
13             username
14             password
15             server
16             login_path
17             read_path
18             write_path
19             credentials
20             ua
21             raw_query
22             raw_result
23             err_code
24             err_message
25             ));
26              
27             =head1 NAME
28              
29             Metaweb - Perl interface to the Metaweb/Freebase API
30              
31             =head1 VERSION
32              
33             Version 0.05
34              
35             =cut
36              
37             our $VERSION = '0.05';
38              
39             =head1 SYNOPSIS
40              
41             use Metaweb;
42              
43             my $mw = Metaweb->new({
44             username => $username,
45             password => $password
46             });
47             $mw->login();
48              
49             my $result = $mw->query({
50             name => 'my_query',
51             query => \%query,
52             });
53              
54             =head1 DESCRIPTION
55              
56             This is a Perl interface to the Metaweb database, best known through the
57             application Freebase (http://freebase.com).
58              
59             If this is your first encounter with Metaweb/Freebase, chances are
60             you're confused about what the two terms mean. In short, Metaweb is the
61             underlying database technology and Freebase is large, well-known
62             application that runs on it. For comparison, consider Mediawiki
63             (software) and Wikipedia (website and data collection).
64              
65             This means that you can use this Metaweb module to talk to Freebase or
66             - in future - any other website built on the Metaweb platform.
67              
68             The Metaweb Query Language (MQL) is based on JSON and query-by-example.
69             The MQL and API documentation can be found on the Freebase website in
70             the developers' area. There is also an online Query Editor tool, with
71             many examples of MQL, at http://www.freebase.com/view/queryeditor/
72              
73             This CPAN module provides you with everything you need to build an
74             application around Freebase or any other Metaweb database. It also
75             provides a command line client ('metaweb') for playing around with MQL.
76              
77             =head1 IMPORTANT NOTES
78              
79             =head2 Alpha release
80              
81             Freebase is currently in alpha release, with world-readable data but
82             requiring an invitation and login to be able to update/write data.
83              
84             This module is very much alpha code. It has lots of stuff not
85             implemented and will undergo significant changes. Breakage may occur
86             between versions, so consider yourself warned.
87              
88             =head2 TMTOWTDI
89              
90             Also note that Hayden Stainsby is working on a different Metaweb module
91             called L. There's more than one way to do it. I
92             encourage you to check out both modules and provide feedback/suggestions
93             to both of us, either directly or via the Freebase developers' mailing
94             list.
95              
96             =head1 FUNCTIONS
97              
98             =head2 new()
99              
100             Instantiate a Metaweb client object. Takes various options including:
101              
102             =over 4
103              
104             =item username
105              
106             The username to login with
107              
108             =item password
109              
110             The password to login with
111              
112             =item server
113              
114             The server address
115              
116             =item read_path
117              
118             The URL path of the read service, relative to the server address
119              
120             =item write_path
121              
122             The URL path of the write service, relative to the server address
123              
124             =item login_path
125              
126             The URL path of the login service, relative to the server address
127              
128             =back
129              
130             None of these are actually required; the server and path options default
131             to Freebase's, and the username/login are only required for write
132             access. Therefore, if you only want to read from Freebase, all you need
133             is:
134              
135             my $mw = Metaweb->new();
136              
137             =cut
138              
139             sub new {
140 2     2 1 1981 my ($class, $args) = @_;
141 2         5 my $self = {};
142 2         5 bless $self, $class;
143 2         10 $self->username($args->{username});
144 2         45 $self->password($args->{password});
145 2   50     38 $self->server ( $args->{server} || 'http://www.freebase.com' );
146 2   50     28 $self->read_path ( $args->{read_path} || '/api/service/mqlread' );
147 2   50     32 $self->write_path ( $args->{write_path} || '/api/service/mqlwrite' );
148 2   50     25 $self->login_path ( $args->{login_path} || '/api/account/login' );
149 2         22 return $self;
150             }
151              
152             =head2 login()
153              
154             Perform a login to the Metaweb server and pick up the necessary cookie.
155             Uses the username/password details provided to the constructor method,
156             or via the appropriately named accessor methods (see below).
157              
158             =cut
159              
160             sub login {
161 0     0 1 0 my ($self, $args) = @_;
162              
163 0         0 my $username = $self->username();
164 0         0 my $password = $self->password();
165 0         0 my $server = $self->server();
166 0         0 my $login_path = $self->login_path();
167              
168             # warn "Server: $server\n";
169             # warn "Login URL: $login_path\n";
170              
171 0 0       0 unless ($self->ua()) {
172 0         0 $self->ua(LWP::UserAgent->new());
173             }
174 0         0 my $res = $self->ua->post("$server$login_path", {username=>$username,password=>$password});
175              
176 0         0 my $raw = $res->header('Set-Cookie');
177 0 0       0 unless ($raw) {
178 0         0 warn "Couldn't login to $server";
179 0         0 return undef;
180             }
181 0         0 my @cookies = split(', ',$raw); # Break cookies at commas
182              
183             # Each cookie is broken into fields with semicolons.
184             # We want the only first field of each cookie
185 0         0 my $credentials = ''; # We'll accumulate login credentials here
186 0         0 for my $cookie (@cookies) { # Loop through cookies
187 0         0 my @parts = split(";", $cookie); # Split each one on ;
188 0         0 $credentials = $credentials . $parts[0] . ';'; # Remember first part
189             }
190 0         0 chop($credentials); # Remove trailing semicolon
191 0         0 $self->credentials($credentials);
192 0         0 $self->ua->default_header('Cookie' => $credentials);
193              
194 0         0 return 1;
195             }
196              
197             =head2 query()
198              
199             Perform a MQL query. You must provide a name and a query hash as
200             arguments:
201              
202             my $result = $mw->query({
203             name => 'my_query',
204             query => { type => 'person', name => undef } # all people!
205             });
206              
207             The query is a a Perl data structure that's converted to JSON using the
208             L module's C method. The MQL envelope will
209             automatically be put around the query, using the name you provide.
210              
211             Currently this method only supports "read" queries. If you want to
212             write/upload, use C.
213              
214             The results of this method are returned as a Perl data structure (or
215             undef on failure); the following attributes are also set for diagnostic
216             purposes.
217              
218             =over 4
219              
220             =item raw_query
221              
222             The raw JSON used in the query.
223              
224             =item raw_result
225              
226             The raw JSON returned.
227              
228             =item err_code
229              
230             Error code (only used if an error occurs).
231              
232             =item err_message
233              
234             Error message (only used if an error occurs).
235              
236             =back
237              
238             See the accessor methods (below) for how to access all these attributes.
239              
240             =cut
241              
242             sub query {
243 0     0 1 0 my ($self, $args) = @_;
244              
245 0 0       0 warn "Query name not specified" unless $args->{name};
246 0 0       0 warn "Query not specified" unless $args->{query};
247              
248 0         0 $args->{query} = _add_envelope($args->{name}, to_json($args->{query}));
249              
250 0         0 my $raw_result = $self->json_query($args);
251 0         0 my $outer = from_json($raw_result);
252 0         0 my $inner = $outer->{$args->{name}};
253            
254 0 0       0 if ($inner->{code} !~ m|^/api/status/ok|) { # If the query was not okay
255 0         0 my $err = $inner->{messages}[0];
256 0         0 $self->err_code($err->{code});
257 0         0 $self->err_message($err->{message});
258 0         0 return undef;
259             } else {
260 0         0 $self->err_code(undef);
261 0         0 $self->err_message(undef);
262             }
263              
264 0         0 my $result = Metaweb::Result->new($inner->{result});
265 0         0 return $result;
266             }
267              
268             =head2 json_query
269              
270             This method sends and receives raw JSON to the Metaweb API.
271              
272             Arguments are passed as a hashref and include:
273              
274             =over 4
275              
276             =item type
277              
278             May be "read", "write", or "update". Default is "read".
279              
280             =item query
281              
282             The query in JSON format. You are expected to send the full JSON,
283             including the envelope.
284              
285             =back
286              
287             The raw JSON is returned. No parsing whatsoever is done.
288              
289             C and C are set as a side effect, same as for
290             C, but C and C are *not* set, as we'd
291             need to parse the JSON to get at it and the whole point of this is that
292             it's unparsed.
293              
294             =cut
295              
296             sub json_query {
297 1     1 1 2016 my ($self, $args) = @_;
298              
299 1 50       4 warn "Query not specified" unless $args->{query};
300 1         3 my $query = $args->{query};
301 1         5 $self->raw_query($query);
302 1   50     11 my $type = $args->{type} || "read";
303              
304 1 50       4 unless ($self->ua()) {
305 1         19 $self->ua(LWP::UserAgent->new());
306             }
307              
308 1   33     3598 my $server = $self->server() || warn "Server not specified";
309              
310 1         17 my $response;
311 1 50       5 if ($type eq 'write') {
312 0         0 $self->ua->default_header('X-Metaweb-Request' => 1);
313 0   0     0 my $path = $self->write_path() || warn "Query URL not specified for write";
314 0         0 my $url = $server . $path;
315 0         0 $response = $self->ua->post($url, { queries => $query });
316             } else {
317 1   33     6 my $path = $self->read_path() || warn "Query URL not specified for read";
318 1         17 $query = uri_escape($query);
319 1         417 my $url = $server . $path . "?queries=" . $query;
320 1         6 $response = $self->ua->get($url);
321             }
322              
323 1 50       660020 if ($response->is_success()) {
324 0         0 my $raw = $response->content();
325 0         0 $self->raw_result($raw);
326 0         0 return $raw;
327             } else {
328 1         199 warn "Request failed";
329 1         12 print $self->ua->content();
330             }
331             }
332              
333             sub _add_envelope {
334 0     0     my ($name, $query) = @_;
335 0           return qq({
336             "$name": {
337             "query": $query
338             }
339             });
340             }
341              
342             =head1 ACCESSOR METHODS
343              
344             You probably won't need these much in day-to-day use, but they're here
345             for you if you want them.
346              
347             =head2 username()
348              
349             Get/set default login username.
350              
351             =head2 password()
352              
353             Get/set default login password.
354              
355             =head2 server()
356              
357             Get/set server to login to. Defaults to 'http://www.freebase.com'.
358              
359             =head2 login_path()
360              
361             Get/set the URL to login to, relative to the server. Defaults to
362             '/api/account/login'.
363              
364             =head2 read_path()
365              
366             Get/set the URL to perform read queries, relative to the server. Defaults to
367             '/api/service/mqlread'.
368              
369             =head2 write_path()
370              
371             Get/set the URL to perform write queries, relative to the server. Defaults to
372             '/api/service/mqlwrite'.
373              
374             =head2 raw_query()
375              
376             The raw JSON of the last query made. This is set by both C and
377             C.
378              
379             =head2 raw_result()
380              
381             The raw JSON from the response. This is set by both C and
382             C.
383              
384             =head2 err_code()
385              
386             Set on error by C. C doesn't set this; you need
387             to parse the JSON yourself.
388              
389             =head2 err_message()
390              
391             Set on error by C. C doesn't set this; you need
392             to parse the JSON yourself.
393              
394             =head1 SEE ALSO
395              
396             L, L (command line client), L (alternative
397             interface).
398              
399             =head1 AUTHOR
400              
401             Kirrily Robert, C<< >>
402              
403             =head1 BUGS
404              
405             Please report any bugs or feature requests to
406             C, or through the web interface at
407             L.
408             I will be notified, and then you'll automatically be notified of progress on
409             your bug as I make changes.
410              
411             =head1 SUPPORT
412              
413             You can find documentation for this module with the perldoc command.
414              
415             perldoc Metaweb
416              
417             You can also look for information at:
418              
419             =over 4
420              
421             =item * AnnoCPAN: Annotated CPAN documentation
422              
423             L
424              
425             =item * CPAN Ratings
426              
427             L
428              
429             =item * RT: CPAN's request tracker
430              
431             L
432              
433             =item * Search CPAN
434              
435             L
436              
437             =back
438              
439             =head1 ACKNOWLEDGEMENTS
440              
441             Thanks to the following people with whom I have discussed Metaweb Perl
442             APIs recently...
443              
444             Hayden Stainsby (CPAN: HDS)
445             Kirsten Jones (CPAN: SYNEDRA)
446              
447             =head1 COPYRIGHT & LICENSE
448              
449             Copyright 2007 Kirrily Robert, all rights reserved.
450              
451             This program is free software; you can redistribute it and/or modify it
452             under the same terms as Perl itself.
453              
454             =cut
455              
456             1; # End of Metaweb