Skip to content

Commit

Permalink
Merge pull request #5 from jcohenho/instagram-api
Browse files Browse the repository at this point in the history
Instagram api
  • Loading branch information
claudiofullscreen committed Oct 16, 2014
2 parents 0efc148 + 95d2843 commit 62bdebf
Show file tree
Hide file tree
Showing 23 changed files with 1,028 additions and 21 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -20,3 +20,4 @@ tmp
*.o
*.a
mkmf.log
.DS_Store
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,3 +9,7 @@ For more information about changelogs, check
## 0.1.0 - 2014-10-13

* Initial release with `Twitter::User` supporting `find_by`, `find_by!` and `where`.

## 0.1.1 - 2014-10-16

* [FEATURE] Add `Instagram::User` supporting `find_by` and `find_by!`.
76 changes: 68 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,16 +1,23 @@
Net - a Ruby client for social networks API
===========================================

Net helps you write apps that need to interact with Twitter.
Net helps you write apps that need to interact with Twitter and Instagram.


After [configuring your Twitter app](#configuring-your-app), you can run commands like:
After [configuring your Twitter app](#configuring-your-twitter-app), you can run commands like:

```ruby
user = Net::Twitter::User.find_by screen_name: 'fullscreen'
user.screen_name #=> "Fullscreen"
user.followers_count #=> 48_200
```
After [configuring your Instagram app](#configuring-your-instagram-app), you can run commands like:

```ruby
user = Net::Instagram::User.find_by username: 'fullscreen_inc'
user.username #=> "fullscreen_inc"
user.follower_count #=> 7025
```

How to install
==============
Expand All @@ -21,7 +28,7 @@ To install on your system, run

To use inside a bundled Ruby project, add this line to the Gemfile:

gem 'net', '~> 0.1.0'
gem 'net', '~> 0.1.1'

Since the gem follows [Semantic Versioning](http://semver.org),
indicating the full version in your Gemfile (~> *major*.*minor*.*patch*)
Expand All @@ -32,7 +39,7 @@ Available resources
===================

Net::Twitter::User
-----------
------------------

Use [Net::Twitter::User]() to:

Expand All @@ -50,8 +57,24 @@ users.map(&:followers_count).sort #=> [12, 48_200]

*The methods above require a configured Twitter app (see below).*

Configuring your app
====================
Net::Instagram::User
--------------------

Use [Net::Instagram::User]() to:

* retrieve an Instagram user by username
* access the number of followers of an Instagram user
* access the number of following of an Instagram user

```ruby
user = Net::Instagram::User.find_by username: 'fullscreen'
user.follower_count #=> 7025
```

*The methods above require a configured Instagram app (see below).*

Configuring your Twitter app
============================

In order to use Net you must create an app in the [Twitter Application Manager](https://apps.twitter.com/app/new).

Expand Down Expand Up @@ -87,6 +110,43 @@ end
so use the approach that you prefer.
If a variable is set in both places, then `Net::Twitter.configure` takes precedence.

Configuring your Instagram app
============================

In order to use Net you must create an app in the
[Instagram Client Manager](http://instagram.com/developer/clients/register).

Once the app is created, copy the Client ID and add it to your
code with the following snippet of code (replacing with your own client id)
:

```ruby
Net::Instagram.configure do |config|
config.client_id = 'abcdefg'
end
```

Configuring with environment variables
--------------------------------------

As an alternative to the approach above, you can configure your app with
a variable. Setting the following environment variable:

```bash
export INSTAGRAM_CLIENT_ID='abcdefg'
```

is equivalent to configuring your app with the initializer:

```ruby
Net::Instagram.configure do |config|
config.client_id = 'abcdefg'
end
```

so use the approach that you prefer.
If a variable is set in both places, then `Net::Instagram.configure` takes precedence.

How to test
===========

Expand All @@ -99,8 +159,8 @@ rspec
Net uses [VCR](https://github.com/vcr/vcr) so by default tests do not run
HTTP requests.

If you need to run tests against the live Twitter API,
[configure your Twitter app](#configuring-your-app) using environment variables,
If you need to run tests against the live Twitter API or Instagram API,
configure your [Twitter app](#configuring-your-twitter-app) or your [Instagram app](#configuring-your-instagram-app) using environment variables,
erase the cassettes, then run `rspec`.


Expand Down
10 changes: 10 additions & 0 deletions lib/net/instagram.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
require 'net/instagram/config'
require 'net/instagram/models'

module Net
module Instagram
extend Config
include Errors
include Models
end
end
14 changes: 14 additions & 0 deletions lib/net/instagram/api/configuration.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
module Net
module Instagram
module Api
class Configuration
attr_accessor :client_id

def initialize
@client_id = ENV['INSTAGRAM_CLIENT_ID']
end
end
end
end
end

51 changes: 51 additions & 0 deletions lib/net/instagram/api/request.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
require 'net/instagram/errors/response_error'
require 'net/instagram/errors/unknown_user'
require 'active_support'
require 'active_support/core_ext'

module Net
module Instagram
module Api
class Request
def initialize(attrs = {})
@host = 'api.instagram.com'
@path = attrs.fetch :path, "/v1/#{attrs[:endpoint]}"
@query = attrs[:params] if attrs[:params]
@method = attrs.fetch :method, :get
end

def run
case response = run_http_request
when Net::HTTPOK
JSON(response.body)['data']
else
raise Errors::ResponseError, response
end
end

private
def run_http_request
Net::HTTP.start(uri.host, uri.port, use_ssl: true) do |http|
http.request http_request
end
end

def http_request
http_class = "Net::HTTP::#{@method.capitalize}".constantize
@http_request ||= http_class.new(uri.request_uri)
end

def uri
@uri ||= URI::HTTPS.build host: @host, path: @path, query: query
end

def query
{}.tap do |query|
query.merge! @query if @query
query.merge! client_id: Net::Instagram.configuration.client_id
end.to_param
end
end
end
end
end
16 changes: 16 additions & 0 deletions lib/net/instagram/config.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
require 'net/instagram/api/configuration'

module Net
module Instagram
module Config
def configure
yield configuration if block_given?
end

def configuration
@configuration ||= Api::Configuration.new
end
end
end
end

3 changes: 3 additions & 0 deletions lib/net/instagram/errors.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
require 'net/instagram/errors/response_error'
require 'net/instagram/errors/private_user'
require 'net/instagram/errors/unknown_user'
11 changes: 11 additions & 0 deletions lib/net/instagram/errors/private_user.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
module Net
module Instagram
module Errors
class PrivateUser < StandardError
def message
'Private user'
end
end
end
end
end
14 changes: 14 additions & 0 deletions lib/net/instagram/errors/response_error.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
module Net
module Instagram
module Errors
class ResponseError < StandardError
attr_reader :response

def initialize(response = {})
@response = response
super response
end
end
end
end
end
11 changes: 11 additions & 0 deletions lib/net/instagram/errors/unknown_user.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
module Net
module Instagram
module Errors
class UnknownUser < StandardError
def message
'Unknown user'
end
end
end
end
end
1 change: 1 addition & 0 deletions lib/net/instagram/models.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
require 'net/instagram/models/user'
64 changes: 64 additions & 0 deletions lib/net/instagram/models/user.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
require 'net/instagram/api/request'
require 'net/instagram/errors'

module Net
module Instagram
module Models
class User
attr_reader :username, :follower_count

def initialize(attrs = {})
@username = attrs['username']
@follower_count = attrs['counts']['followed_by']
end

# Returns the existing Instagram user matching the provided attributes or
# nil when the user is not found.
#
# @return [Net::Instagram::Models::User] when the user is found.
# @return [nil] when the user is not found or has a private account.
# @param [Hash] params the attributes to find a user by.
# @option params [String] :username The Instagram user’s username
# (case-insensitive).
def self.find_by(params = {})
find_by! params
rescue Errors::PrivateUser, Errors::UnknownUser
nil
end

# Returns the existing Instagram user matching the provided attributes or
# nil when the user is not found, and raises an error when the user account is private.
#
# @return [Net::Instagram::Models::User] the Instagram user.
# @param [Hash] params the attributes to find a user by.
# @option params [String] :username The Instagram user’s username
# (case-insensitive).
# @raise [Net::Errors::PrivateUser] if the user account is private.
def self.find_by!(params = {})
find_by_username! params[:username]
end

private

def self.find_by_username!(username)
request = Api::Request.new endpoint: "users/search", params: {q: username}
users = Array.wrap request.run
if user = users.find{|u| u['username'].casecmp(username).zero?}
find_by_id! user['id']
else
raise Errors::UnknownUser
end
end

def self.find_by_id!(id)
request = Api::Request.new endpoint: "users/#{id}"
new request.run
rescue Errors::ResponseError => error
case error.response
when Net::HTTPBadRequest then raise Errors::PrivateUser
end
end
end
end
end
end
2 changes: 1 addition & 1 deletion lib/net/version.rb
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
module Net
VERSION = "0.1.0"
VERSION = "0.1.1"
end
Loading

0 comments on commit 62bdebf

Please sign in to comment.