API-Instagram

 view release on metacpan or  search on metacpan

lib/API/Instagram.pm  view on Meta::CPAN

use API::Instagram::User;
use API::Instagram::Location;
use API::Instagram::Tag;
use API::Instagram::Media;
use API::Instagram::Media::Comment;
use API::Instagram::Search;

has client_id         => ( is => 'ro', required => 1 );
has client_secret     => ( is => 'ro', required => 1 );
has redirect_uri      => ( is => 'ro', required => 1 );
has scope             => ( is => 'ro', default => sub { 'basic' } );
has response_type     => ( is => 'ro', default => sub { 'code'  } );
has grant_type        => ( is => 'ro', default => sub { 'authorization_code' } );
has code              => ( is => 'rw', isa => sub { confess "Code not provided"        unless $_[0] } );
has access_token      => ( is => 'rw', isa => sub { confess "No access token provided" unless $_[0] } );
has no_cache          => ( is => 'rw', default => sub { 0 } );

has _ua               => ( is => 'ro', default => sub { Furl->new() } );
has _obj_cache        => ( is => 'ro', default => sub { { User => {}, Media => {}, Location => {}, Tag => {}, 'Media::Comment' => {} } } );
has _endpoint_url     => ( is => 'ro', default => sub { 'https://api.instagram.com/v1'                 } );
has _authorize_url    => ( is => 'ro', default => sub { 'https://api.instagram.com/oauth/authorize'    } );
has _access_token_url => ( is => 'ro', default => sub { 'https://api.instagram.com/oauth/access_token' } );

has _debug => ( is => 'rw', lazy => 1 );

my $instance;
sub BUILD { $instance = shift }

sub instance { $instance //= shift->new(@_) }

sub get_auth_url { 
	my $self = shift;

	carp "User already authorized with code: " . $self->code if $self->code;

	my @auth_fields = qw(client_id redirect_uri response_type scope);
	for ( @auth_fields ) {
		carp "ERROR: $_ required for generating authorization URL" and return unless defined $self->$_;
	}

	my $uri = URI->new( $self->_authorize_url );
	$uri->query_form( map { $_ => $self->$_ } @auth_fields );
	$uri->as_string();
}


sub get_access_token {
	my $self = shift;

	my @access_token_fields = qw(client_id redirect_uri grant_type client_secret code);
	for ( @access_token_fields ) {
		carp "ERROR: $_ required for generating access token." and return unless defined $self->$_;
	}

	my $data = { map { $_ => $self->$_ } @access_token_fields };
	my $json = $self->_request( 'post', $self->_access_token_url, $data, { token_not_required => 1 } );

	wantarray ? ( $json->{access_token}, $self->user( $json->{user} ) ) : $json->{access_token};
}


sub media { shift->_get_obj( 'Media', 'id', shift ) }

sub user { shift->_get_obj( 'User', 'id', shift // 'self' ) }

sub location { shift->_get_obj( 'Location', 'id', shift, 1 ) }

sub tag { shift->_get_obj( 'Tag', 'name', shift ) }

sub search {
	my $self = shift;
	my $type = shift;
	API::Instagram::Search->new( type => $type )
}


sub popular_medias {
	my $self = shift;
	my $url  = "/media/popular";
	$self->_medias( $url, { @_%2?():@_ } );
}

sub _comment { shift->_get_obj( 'Media::Comment', 'id', shift ) }

#####################################################
# Returns cached wanted object or creates a new one #
#####################################################
sub _get_obj {
	my ( $self, $type, $key, $code, $optional_code ) = @_;

	my $data = { $key => $code };
	$data = $code if ref $code eq 'HASH';
	$code = $data->{$key};

	# Returns if CODE is not optional and not defined or if it's not a string
	return if (!$optional_code and !defined $code) or ref $code;

	# Code used as cache key

lib/API/Instagram.pm  view on Meta::CPAN


	# Deletes cache if no-cache is set
	delete $self->_cache($type)->{$cache_code} if $self->no_cache;

	return $return;
}

###################################
# Returns a list of Media Objects #
###################################
sub _medias {
	my ($self, $url, $params, $opts) = @_;
	$params->{count} //= 33;
	$params->{url}     = $url;
	[ map { $self->media($_) } $self->_get_list( { %$params, url => $url }, $opts ) ]
}

####################################################################
# Returns a list of the requested items. Does pagination if needed #
####################################################################
sub _get_list {
	my $self   = shift;
	my $params = shift;
	my $opts   = shift;

	my $url      = delete $params->{url} || return [];
	my $count    = $params->{count} // 999_999_999;
	$count       = 999_999_999 if $count < 0;
	$params->{count} = $count;

	my $request = $self->_request( 'get', $url, $params, $opts );

lib/API/Instagram.pm  view on Meta::CPAN

		$request = $self->_request( 'get', $pagination->{next_url}, $params, $opts );
		push @$data, @{ $request->{data} };
	}

	return @$data;
}

##############################################################
# Requests the data from the given URL with QUERY parameters #
##############################################################
sub _request {
	my ( $self, $method, $url, $params, $opts ) = @_;

	# Verifies access requirements
	unless ( defined $self->access_token ) {
		if ( !$opts->{token_not_required} or !defined $self->client_id ) {
			carp "A valid access_token is required";
			return {}
		}
	}

lib/API/Instagram.pm  view on Meta::CPAN


	# Verifies meta node
	my $meta = $res->{meta};
	carp "$meta->{error_type}: $meta->{error_message}" if $meta->{code} ne '200';

use Data::Dumper;
# die Dumper $res;
	$res;
}

sub _request_data { shift->_request(@_)->{data} || {} }

sub _del  { shift->_request_data( 'delete', @_ ) }
sub _get  { shift->_request_data( 'get',    @_ ) }
sub _post { shift->_request_data( 'post',   @_ ) }

################################
# Returns requested cache hash #
################################
sub _cache { shift->_obj_cache->{ shift() } }


1;

__END__

=pod

=encoding UTF-8

lib/API/Instagram/Location.pm  view on Meta::CPAN

use Moo;
use Carp;

has id        => ( is => 'ro', predicate => 1 );
has latitude  => ( is => 'lazy' );
has longitude => ( is => 'lazy' );
has name      => ( is => 'lazy' );
has _data     => ( is => 'rwp', lazy => 1, builder => 1, clearer => 1 );


sub recent_medias {
	my $self = shift;

	carp "Not available for location with no ID." and return [] unless $self->has_id;

	my $url  = sprintf "locations/%s/media/recent", $self->id;
	API::Instagram->instance->_medias( $url, { @_%2?():@_ } );
}

sub _build_name      { shift->_data->{name}      }
sub _build_latitude  { shift->_data->{latitude}  }
sub _build_longitude { shift->_data->{longitude} }

sub _build__data {
	my $self = shift;
	carp "Not available for location with no ID." and return {} unless $self->has_id;
	my $url  = sprintf "locations/%s", $self->id;
	API::Instagram->instance->_get( $url );
}

1;

__END__

lib/API/Instagram/Media.pm  view on Meta::CPAN

has id             => ( is => 'ro', required => 1 );
has type           => ( is => 'lazy' );
has link           => ( is => 'lazy' );
has filter         => ( is => 'lazy' );
has images         => ( is => 'lazy' );
has videos         => ( is => 'lazy' );
has user           => ( is => 'lazy', coerce => \&_coerce_user           );
has tags           => ( is => 'lazy', coerce => \&_coerce_tags           );
has location       => ( is => 'lazy', coerce => \&_coerce_location       );
has users_in_photo => ( is => 'lazy', coerce => \&_coerce_users_in_photo );
has caption        => ( is => 'lazy', coerce => sub { $_[0]->{text} if $_[0] and ref $_[0] eq 'HASH' } );
has created_time   => ( is => 'lazy', coerce => sub { Time::Moment->from_epoch( $_[0] ) } );
has _api           => ( is => 'lazy' );
has _data          => ( is => 'rwp', lazy => 1, builder => 1, clearer => 1 );

sub likes {
	my $self = shift;
	$self->_clear_data if shift;
	$self->_data->{likes}->{count}
}

sub last_likes {
	my $self = shift;
	$self->_clear_data if shift;
	my $api  = $self->_api;
	[ map { $api->user($_) } @{ $self->_data->{likes}->{data} } ]
}

sub get_likes {
	my $self = shift;
	my %opts = @_;
	my $url  = sprintf "media/%s/likes", $self->id;
	my $api  = $self->_api;
	[ map { $api->user($_) } $api->_get_list( { %opts, url => $url } ) ]
}

sub like {
	my $self = shift;
	my $url  = sprintf "media/%s/likes", $self->id;
	$self->_api->_post( $url )
}

sub dislike {
	my $self = shift;
	my $url  = sprintf "media/%s/likes", $self->id;
	$self->_api->_del( $url )
}

sub comments {
	my $self = shift;
	$self->_clear_data if shift;
	$self->_data->{comments}->{count}
}

sub last_comments {
	my $self = shift;
	$self->_clear_data if shift;
	my $api  = $self->_api;
	[ map { $api->_comment( { %$_, media => $self } ) } @{ $self->_data->{comments}->{data} } ]
}

sub get_comments {
	my $self = shift;
	my %opts = @_;
	my $url  = sprintf "media/%s/comments", $self->id;
	my $api  = $self->_api;
	[ map { $api->_comment( { %$_, media => $self } ) } $api->_get_list( { %opts, url => $url } ) ]
}

sub comment {
	my $self = shift;
	my $text = shift;
	my $url  = sprintf "media/%s/comments", $self->id;
	$self->_api->_post( $url, { text => $text } )
}


sub _build__api           { API::Instagram->instance       }
sub _build_user           { shift->_data->{user}           }
sub _build_tags           { shift->_data->{tags}           }
sub _build_location       { shift->_data->{location}       }
sub _build_users_in_photo { shift->_data->{users_in_photo} }
sub _build_type           { shift->_data->{type}           }
sub _build_link           { shift->_data->{link}           }
sub _build_filter         { shift->_data->{filter}         }
sub _build_images         { shift->_data->{images}         }
sub _build_videos         { shift->_data->{videos}         }
sub _build_caption        { shift->_data->{caption }       }
sub _build_created_time   { shift->_data->{created_time}   }

sub _build__data {
	my $self = shift;
	my $url  = sprintf "media/%s", $self->id;
	$self->_api->_get( $url );
}

############################################################
# Attributes coercion that API::Instagram object reference #
############################################################
sub _coerce_user     { API::Instagram->instance->user    ( $_[0] ) };
sub _coerce_location { API::Instagram->instance->location( $_[0] ) if $_[0] };

sub _coerce_tags {
	my $data = $_[0];
	return if ref $data ne 'ARRAY';
	[ map { API::Instagram->instance->tag($_) } @$data ]
};
	
sub _coerce_users_in_photo {
	my $data = $_[0];
	return if ref $data ne 'ARRAY';
	[
		map {{
			user     => API::Instagram->instance->user( $_->{user} ),
			position => $_->{position},
		}} @$data
	]
};

lib/API/Instagram/Media/Comment.pm  view on Meta::CPAN

package API::Instagram::Media::Comment;

# ABSTRACT: Instagram Media Comment Object

use Moo;
use Time::Moment;

has id           => ( is => 'ro', required => 1 );
has from         => ( is => 'ro', required => 1, coerce => sub { API::Instagram->instance->user( $_[0] ) } );
has text         => ( is => 'ro', required => 1 );
has media        => ( is => 'ro', required => 1 );
has created_time => ( is => 'ro', coerce => sub { Time::Moment->from_epoch( $_[0] ) } );

sub remove {
	my $self = shift;
	my $url  = sprintf "media/%s/comments/%s", $self->media->id, $self->id;
	$self->media->_api->_del( $url )
}

1;

__END__

=pod

lib/API/Instagram/Search.pm  view on Meta::CPAN


use Moo;

my $search = {
	user     => 'users/search',
	media    => 'media/search',
	tag      => 'tags/search',
	location => 'locations/search',
};

has type => ( is => 'ro', required => 1, isa => sub { die "Type not supported." unless $search->{$_[0]} } );


sub find {
	my $self = shift;
	my %opts = @_;
	my $type = $self->type;
	my $url  = $search->{$type};
	my $api  = API::Instagram->instance;
	[ map { $api->$type($_) } $api->_get_list( { %opts, url => $url } ) ]
}


1;

lib/API/Instagram/Tag.pm  view on Meta::CPAN

package API::Instagram::Tag;

# ABSTRACT: Instagram Tag Object

use Moo;

has name  => ( is => 'ro', required => 1 );
has _data => ( is => 'rwp', lazy => 1, builder => 1, clearer => 1 );

sub media_count {
	my $self = shift;
	$self->_clear_data if shift;
	$self->_data->{media_count}
}


sub recent_medias {
	my $self = shift;
	my $url  = sprintf "tags/%s/media/recent", $self->name;
	API::Instagram->instance->_medias( $url, { @_%2?():@_ } );
}

sub _build__data {
	my $self = shift;
	my $url  = sprintf "tags/%s", $self->name;
	API::Instagram->instance->_get( $url );
}

1;

__END__

=pod

lib/API/Instagram/User.pm  view on Meta::CPAN


has id              => ( is => 'ro', required => 1 );
has username        => ( is => 'lazy' );
has full_name       => ( is => 'lazy' );
has bio             => ( is => 'lazy' );
has website         => ( is => 'lazy' );
has profile_picture => ( is => 'lazy' );
has _api            => ( is => 'lazy' );
has _data           => ( is => 'rwp', lazy => 1, builder => 1, clearer => 1 );

sub media {
	my $self = shift;
	$self->_clear_data if shift;
	return $_->{media} for $self->_data->{counts}
}

sub follows {
	my $self = shift;
	$self->_clear_data if shift;
	return $_->{follows} for $self->_data->{counts}
}

sub followed_by {
	my $self = shift;
	$self->_clear_data if shift;
	return $_->{followed_by} for $self->_data->{counts}
}


sub feed {
	my $self = shift;
	my @list = $self->_self_requests( 'feed', '/users/self/feed', @_ ) or return;
	[ map { $self->_api->media($_) } @list ];
}


sub liked_media {
	my $self = shift;
	my @list = $self->_self_requests( 'liked-media', '/users/self/media/liked', @_ ) or return;
	[ map { $self->_api->media($_) } @list ];
}


sub requested_by {
	my $self = shift;
	my @list = $self->_self_requests( 'requested-by', '/users/self/requested-by', @_ ) or return;
	[ map { $self->_api->user($_) } @list ];
}


sub get_follows {
	shift->_get_relashions( @_, relationship => 'follows' );
}


sub get_followers {
	shift->_get_relashions( @_, relationship => 'followed-by' );
}


sub recent_medias {
	my $self = shift;
	my $url  = sprintf "users/%s/media/recent", $self->id;
	$self->_api->_medias( $url, { @_%2?():@_ }, { token_not_required => 1 } );
}

sub relationship {
	my $self    = shift;
	my $action  = shift;
	my $url     = sprintf "users/%s/relationship", $self->id;
	my @actions = qw/ follow unfollow block unblock approve ignore/;

	use experimental 'smartmatch';
	if ( $action ) {
		if ( $action ~~ @actions ){
			return $self->_api->_post( $url, { action => $action } )
		}
		carp "Invalid action";
	}

	$self->_api->_get( $url );
}


sub _get_relashions {
	my $self = shift;
	my %opts = @_;
	my $url  = sprintf "users/%s/%s", $self->id, $opts{relationship};
	my $api  = $self->_api;
	[ map { $api->user($_) } $api->_get_list( { %opts, url => $url } ) ]
}

sub _self_requests {
	my ($self, $type, $url, %opts) = @_;

	if ( $self->id ne $self->_api->user->id ){
		carp "The $type is only available for the authenticated user";
		return;
	}

	$self->_api->_get_list( { %opts, url => $url } )
}


sub BUILDARGS {
	my $self = shift;
	my $opts = shift;

	$opts->{profile_picture} //= delete $opts->{profile_pic_url} if exists $opts->{profile_pic_url};

	return $opts;
}


sub _build__api            { API::Instagram->instance        }
sub _build_username        { shift->_data->{username}        }
sub _build_full_name       { shift->_data->{full_name}       }
sub _build_bio             { shift->_data->{bio}             }
sub _build_website         { shift->_data->{website}         }
sub _build_profile_picture { shift->_data->{profile_picture} }

sub _build__data {
	my $self = shift;
	my $url  = sprintf "users/%s", $self->id;
	$self->_api->_get( $url );
}



1;

__END__

t/00-comment.t  view on Meta::CPAN

	API::Instagram->new({
			client_id     => '123',
			client_secret => '456',
			redirect_uri  => 'http://localhost',
            no_cache      => 1,
	})
);

my $data = join '', <DATA>;
my $json = decode_json $data;
$api->mock('_request', sub { $json });

my $media = $api->media(123);
isa_ok( $media, 'API::Instagram::Media' );

my $get_comments = $media->get_comments;
is ref $get_comments, 'ARRAY';

my $comment = $get_comments->[0];
isa_ok( $comment, 'API::Instagram::Media::Comment' );

t/00-instagram.t  view on Meta::CPAN

use Furl::Response;
use Inline::Files;

use API::Instagram;
use Test::More tests => 17;

my $data = join '', <DATA>;
my $ua   = Test::MockObject::Extends->new( Furl->new() );
my $res  = Test::MockObject::Extends->new( Furl::Response->new( 1, 200, 'OK', {}, $data) );

$ua->mock('get',  sub { $res });
$ua->mock('post', sub { $res });

my $api = API::Instagram->new({
			client_id     => '123',
			client_secret => '456',
			redirect_uri  => 'http://localhost',
            no_cache      => 1,
            _ua           => $ua,
});


t/00-instagram.t  view on Meta::CPAN

is $me->username, "snoopdogg";

is ref $api->_request('get','media'), 'HASH';

my @list = $api->_get_list( { url => 'media', count => 2 } );
is ~~@list , 2;

# Tests Popular Medias method with new DATA (__POPULAR__)
my $popular = join '', <POPULAR>;
my $res2 = Test::MockObject::Extends->new( Furl::Response->new( 1, 200, 'OK', {}, $popular) );
$ua->mock('get',  sub { $res2 });

is ref $api2->popular_medias, 'ARRAY';
is $api2->popular_medias->[0]->user->username, 'cocomiin';

__DATA__
{
    "meta": {
        "code": 200
    },
    "pagination": {

t/00-location.t  view on Meta::CPAN

my $api = Test::MockObject::Extends->new(
	API::Instagram->new({
			client_id     => '123',
			client_secret => '456',
			redirect_uri  => 'http://localhost',
	})
);

my $data = join '', <DATA>;
my $json = decode_json $data;
$api->mock('_request', sub { $json });
$api->mock('_get_list', sub { [] });

my $location = $api->location('1');
isa_ok( $location, 'API::Instagram::Location' );

is $location->id, 1;
is $location->name, 'Dogpatch Labs';
is $location->latitude, 37.782;
is $location->longitude, -122.387;
is ref $location->recent_medias, 'ARRAY';

t/00-media.t  view on Meta::CPAN

use API::Instagram;
use Test::More tests => 27;

my $api = Test::MockObject::Extends->new(
	API::Instagram->new({
			client_id     => '123', client_secret => '456', redirect_uri  => 'http://localhost', no_cache      => 1, })
);

my $data = join '', <DATA>;
my $json = decode_json $data;
$api->mock('_request', sub { $json });
$api->mock('_get_list', sub { {} });

# First Object
my $media = $api->media(3);
isa_ok( $media, 'API::Instagram::Media' );

is $media->id, 3;
is $media->type, 'video';
is $media->filter, 'Vesper';
is $media->likes, 1;
is $media->comments, 2;

t/00-search.t  view on Meta::CPAN

my $api = Test::MockObject::Extends->new(
	API::Instagram->new({
			client_id     => '123',
			client_secret => '456',
			redirect_uri  => 'http://localhost',
	})
);

my $data = join '', <DATA>;
my $json = decode_json $data;
$api->mock('_request', sub { $json });

my $search = $api->search('tag');
isa_ok( $search, 'API::Instagram::Search' );

my $tags = $search->find( q => 'x' );
is ref $tags, 'ARRAY';

isa_ok( $tags->[0], 'API::Instagram::Tag' );
is $tags->[3]->name, 'snowydays';

t/00-tag.t  view on Meta::CPAN

my $api = Test::MockObject::Extends->new(
	API::Instagram->new({
			client_id     => '123',
			client_secret => '456',
			redirect_uri  => 'http://localhost',
	})
);

my $data = join '', <DATA>;
my $json = decode_json $data;
$api->mock('_request', sub { $json });
$api->mock('_get_list', sub { [] });

my $tag = $api->tag('nofilter');
isa_ok( $tag, 'API::Instagram::Tag' );

is $tag->name, 'nofilter';
is $tag->media_count, 472;
is $tag->media_count(1), 472;
is ref $tag->recent_medias, 'ARRAY';

__DATA__

t/00-user.t  view on Meta::CPAN

	API::Instagram->new({
			client_id     => '123',
			client_secret => '456',
			redirect_uri  => 'http://localhost',
            no_cache      => 1
	})
);

my $data = join '', <DATA>;
my $json = decode_json $data;
$api->mock('_request', sub { $json });
$api->mock('_get_list', sub { [] });

my $user = $api->user("1574083");
isa_ok( $user, 'API::Instagram::User' );

is $user->id, 1574083;
is $user->username, 'snoopdogg';
is $user->full_name, 'Snoop Dogg';
is $user->bio, 'This is my bio';
is $user->website, 'http://snoopdogg.com';
is $user->profile_picture, 'http://distillery.s3.amazonaws.com/profiles/profile_1574083_75sq_1295469061.jpg';

t/01-location.t  view on Meta::CPAN

my $api = Test::MockObject::Extends->new(
	API::Instagram->new({
			client_id     => '123',
			client_secret => '456',
			redirect_uri  => 'http://localhost',
	})
);

my $data = join '', <DATA>;
my $json = decode_json $data;
$api->mock('_request', sub { $json });
$api->mock('_get_list', sub { [] });

my $location = $api->location( $json->{data} );
isa_ok( $location, 'API::Instagram::Location' );

is $location->id, undef;
is $location->name, undef;
is ref $location->recent_medias, 'ARRAY';

__DATA__
{

t/01-media.t  view on Meta::CPAN

	API::Instagram->new({
			client_id     => '123',
			client_secret => '456',
			redirect_uri  => 'http://localhost',
            no_cache      => 1,
	})
);

my $data = join '', <DATA>;
my $json = decode_json $data;
$api->mock('_request', sub { $json });
$api->mock('_get_list', sub { [] });

my $media = $api->media( $json->{data} );
isa_ok( $media, 'API::Instagram::Media' );

is $media->location, undef;
is ref $media->tags, 'ARRAY';
is $media->tags->[0], undef;

my $uip = $media->users_in_photo;
is ref $uip, 'ARRAY';

t/01-search.t  view on Meta::CPAN

my $api = Test::MockObject::Extends->new(
	API::Instagram->new({
			client_id     => '123',
			client_secret => '456',
			redirect_uri  => 'http://localhost',
	})
);

my $data = join '', <DATA>;
my $json = decode_json $data;
$api->mock('_request', sub { $json });

my $search = eval { $api->search('car') };
is $search, undef;

__DATA__
{
    "data": [
        {
            "media_count": 43590,
            "name": "snowy"

t/01-user.t  view on Meta::CPAN

	API::Instagram->new({
			client_id     => '123',
			client_secret => '456',
			redirect_uri  => 'http://localhost',
            no_cache      => 1
	})
);

my $data = join '', <DATA>;
my $json = decode_json $data;
$api->mock('_request', sub { $json });
$api->mock('_get_list', sub { [] });

my $user = $api->user( $json->{data} );
isa_ok( $user, 'API::Instagram::User' );

is $user->id, 'self';
is $user->profile_picture, 'http://distillery.s3.amazonaws.com/profiles/profile_1574083_75sq_1295469061.jpg';
is ref $user->feed, 'ARRAY';
is ref $user->liked_media, 'ARRAY';
is ref $user->requested_by, 'ARRAY';

t/02-user.t  view on Meta::CPAN

	API::Instagram->new({
			client_id     => '123',
			client_secret => '456',
			redirect_uri  => 'http://localhost',
            no_cache      => 1
	})
);

my $data = join '', <DATA>;
my $json = decode_json $data;
$api->mock('_request', sub { $json });
$api->mock('_get_list', sub { [] });

my $user = $api->user( $json->{data} );
isa_ok( $user, 'API::Instagram::User' );

is $user->id, 123;
is $user->profile_picture, 'http://test.com/picture.jpg';
is $user->feed, undef;
is $user->liked_media, undef;
is $user->requested_by, undef;

t/03-user.t  view on Meta::CPAN

	API::Instagram->new({
			client_id     => '123',
			client_secret => '456',
			redirect_uri  => 'http://localhost',
            no_cache      => 1
	})
);

my $data = join '', <DATA>;
my $json = decode_json $data;
$api->mock('_request', sub { $json });
$api->mock('_get_list', sub { [] });

my $user = $api->user( $json->{data} );
isa_ok( $user, 'API::Instagram::User' );

is $user->profile_picture, undef;

__DATA__
{
    "data": {
        "id": "1574083",

t/04-user.t  view on Meta::CPAN

	API::Instagram->new({
			client_id     => '123',
			client_secret => '456',
			redirect_uri  => 'http://localhost',
            no_cache      => 1
	})
);

my $data = join '', <DATA>;
my $json = decode_json $data;
$api->mock('_request', sub { $json });
$api->mock('_post',    sub { $json });

my $user = $api->user( 123 );
isa_ok( $user, 'API::Instagram::User' );

is ref $user->relationship, 'HASH';
is $user->relationship->{incoming_status}, 'requested_by';
is ref $user->relationship('block'), 'HASH';
is ref $user->relationship('undef'), 'HASH';

__DATA__



( run in 0.946 second using v1.01-cache-2.11-cpan-13bb782fe5a )