net 0.1.0 → 0.1.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (27) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +1 -0
  3. data/CHANGELOG.md +4 -0
  4. data/README.md +69 -9
  5. data/lib/net/instagram.rb +10 -0
  6. data/lib/net/instagram/api/configuration.rb +14 -0
  7. data/lib/net/instagram/api/request.rb +51 -0
  8. data/lib/net/instagram/config.rb +16 -0
  9. data/lib/net/instagram/errors.rb +3 -0
  10. data/lib/net/instagram/errors/private_user.rb +11 -0
  11. data/lib/net/instagram/errors/response_error.rb +14 -0
  12. data/lib/net/instagram/errors/unknown_user.rb +11 -0
  13. data/lib/net/instagram/models.rb +1 -0
  14. data/lib/net/instagram/models/user.rb +64 -0
  15. data/lib/net/twitter/models/user.rb +4 -4
  16. data/lib/net/version.rb +1 -1
  17. data/spec/net/instagram/models/user_spec.rb +61 -0
  18. data/spec/net/twitter/models/user_spec.rb +59 -59
  19. data/spec/spec_helper.rb +0 -11
  20. data/spec/support/cassettes/Net_Instagram_Models_User/_find_by/given_a_private_username/.yml +144 -0
  21. data/spec/support/cassettes/Net_Instagram_Models_User/_find_by/given_an_existing_case-insensitive_username/returns_an_object_representing_that_user.yml +149 -0
  22. data/spec/support/cassettes/Net_Instagram_Models_User/_find_by/given_an_unknown_username/.yml +54 -0
  23. data/spec/support/cassettes/Net_Instagram_Models_User/_find_by_/given_a_private_username/.yml +144 -0
  24. data/spec/support/cassettes/Net_Instagram_Models_User/_find_by_/given_an_existing_case-insensitive_username/returns_an_object_representing_that_user.yml +149 -0
  25. data/spec/support/cassettes/Net_Instagram_Models_User/_find_by_/given_an_unknown_username/.yml +54 -0
  26. data/spec/support/vcr.rb +4 -1
  27. metadata +26 -2
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: e8d47667af5750fa95def0080edb95f64ebe5ec5
4
- data.tar.gz: d2339bd6f4b728e27de7c9d3249877c10c000474
3
+ metadata.gz: 936c43b38462cae57221b9992994d3e8e767f5cd
4
+ data.tar.gz: ea789f7e15df8dd201fcaac9bb74f80a3f5982fe
5
5
  SHA512:
6
- metadata.gz: f076d2cf3063b019795f320cbff740ed3fbf07d01e9d4b1b7a4295f5c16bbf25cccb3982a178a6f9dfba6cb0e2c27c6f6ce610f4b2609b9c22b83f39e87f3343
7
- data.tar.gz: c5391de7b4762d3d3e3b7073cd27bae4aac635d21fedc6b696bef465205340bc9c365b07ea5d5180ea158766a1cb59309e7de5d6e2dddf572ee144531639f741
6
+ metadata.gz: 9780280470cdcf98a13c1d3c2cf384a0dd6f1ea3e913224d851ba682a67dd118549fe619987eeaec92852976def7695ecf7e94a88530594b3a0cca8a7f710986
7
+ data.tar.gz: 1c7d9f4a06aa83f4bac0a5f189feed5023a9f50d79ba7b6162a853e027af81ab1d516493e974588a030e8e506117e3f22e78e10aa7f0996c8df9eea7158c7259
data/.gitignore CHANGED
@@ -20,3 +20,4 @@ tmp
20
20
  *.o
21
21
  *.a
22
22
  mkmf.log
23
+ .DS_Store
data/CHANGELOG.md CHANGED
@@ -9,3 +9,7 @@ For more information about changelogs, check
9
9
  ## 0.1.0 - 2014-10-13
10
10
 
11
11
  * Initial release with `Twitter::User` supporting `find_by`, `find_by!` and `where`.
12
+
13
+ ## 0.1.1 - 2014-10-16
14
+
15
+ * [FEATURE] Add `Instagram::User` supporting `find_by` and `find_by!`.
data/README.md CHANGED
@@ -1,16 +1,23 @@
1
1
  Net - a Ruby client for social networks API
2
2
  ===========================================
3
3
 
4
- Net helps you write apps that need to interact with Twitter.
4
+ Net helps you write apps that need to interact with Twitter and Instagram.
5
5
 
6
6
 
7
- After [configuring your Twitter app](#configuring-your-app), you can run commands like:
7
+ After [configuring your Twitter app](#configuring-your-twitter-app), you can run commands like:
8
8
 
9
9
  ```ruby
10
10
  user = Net::Twitter::User.find_by screen_name: 'fullscreen'
11
11
  user.screen_name #=> "Fullscreen"
12
12
  user.followers_count #=> 48_200
13
13
  ```
14
+ After [configuring your Instagram app](#configuring-your-instagram-app), you can run commands like:
15
+
16
+ ```ruby
17
+ user = Net::Instagram::User.find_by username: 'fullscreen_inc'
18
+ user.username #=> "fullscreen_inc"
19
+ user.follower_count #=> 7025
20
+ ```
14
21
 
15
22
  How to install
16
23
  ==============
@@ -21,7 +28,7 @@ To install on your system, run
21
28
 
22
29
  To use inside a bundled Ruby project, add this line to the Gemfile:
23
30
 
24
- gem 'net', '~> 0.1.0'
31
+ gem 'net', '~> 0.1.1'
25
32
 
26
33
  Since the gem follows [Semantic Versioning](http://semver.org),
27
34
  indicating the full version in your Gemfile (~> *major*.*minor*.*patch*)
@@ -32,9 +39,9 @@ Available resources
32
39
  ===================
33
40
 
34
41
  Net::Twitter::User
35
- -----------
42
+ ------------------
36
43
 
37
- Use [Net::Account]() to:
44
+ Use [Net::Twitter::User]() to:
38
45
 
39
46
  * retrieve a Twitter user by screen name
40
47
  * retrieve a list of Twitter users by screen names
@@ -50,8 +57,24 @@ users.map(&:followers_count).sort #=> [12, 48_200]
50
57
 
51
58
  *The methods above require a configured Twitter app (see below).*
52
59
 
53
- Configuring your app
54
- ====================
60
+ Net::Instagram::User
61
+ --------------------
62
+
63
+ Use [Net::Instagram::User]() to:
64
+
65
+ * retrieve an Instagram user by username
66
+ * access the number of followers of an Instagram user
67
+ * access the number of following of an Instagram user
68
+
69
+ ```ruby
70
+ user = Net::Instagram::User.find_by username: 'fullscreen'
71
+ user.follower_count #=> 7025
72
+ ```
73
+
74
+ *The methods above require a configured Instagram app (see below).*
75
+
76
+ Configuring your Twitter app
77
+ ============================
55
78
 
56
79
  In order to use Net you must create an app in the [Twitter Application Manager](https://apps.twitter.com/app/new).
57
80
 
@@ -87,6 +110,43 @@ end
87
110
  so use the approach that you prefer.
88
111
  If a variable is set in both places, then `Net::Twitter.configure` takes precedence.
89
112
 
113
+ Configuring your Instagram app
114
+ ============================
115
+
116
+ In order to use Net you must create an app in the
117
+ [Instagram Client Manager](http://instagram.com/developer/clients/register).
118
+
119
+ Once the app is created, copy the Client ID and add it to your
120
+ code with the following snippet of code (replacing with your own client id)
121
+ :
122
+
123
+ ```ruby
124
+ Net::Instagram.configure do |config|
125
+ config.client_id = 'abcdefg'
126
+ end
127
+ ```
128
+
129
+ Configuring with environment variables
130
+ --------------------------------------
131
+
132
+ As an alternative to the approach above, you can configure your app with
133
+ a variable. Setting the following environment variable:
134
+
135
+ ```bash
136
+ export INSTAGRAM_CLIENT_ID='abcdefg'
137
+ ```
138
+
139
+ is equivalent to configuring your app with the initializer:
140
+
141
+ ```ruby
142
+ Net::Instagram.configure do |config|
143
+ config.client_id = 'abcdefg'
144
+ end
145
+ ```
146
+
147
+ so use the approach that you prefer.
148
+ If a variable is set in both places, then `Net::Instagram.configure` takes precedence.
149
+
90
150
  How to test
91
151
  ===========
92
152
 
@@ -99,8 +159,8 @@ rspec
99
159
  Net uses [VCR](https://github.com/vcr/vcr) so by default tests do not run
100
160
  HTTP requests.
101
161
 
102
- If you need to run tests against the live Twitter API,
103
- [configure your Twitter app](#configuring-your-app) using environment variables,
162
+ If you need to run tests against the live Twitter API or Instagram API,
163
+ configure your [Twitter app](#configuring-your-twitter-app) or your [Instagram app](#configuring-your-instagram-app) using environment variables,
104
164
  erase the cassettes, then run `rspec`.
105
165
 
106
166
 
@@ -0,0 +1,10 @@
1
+ require 'net/instagram/config'
2
+ require 'net/instagram/models'
3
+
4
+ module Net
5
+ module Instagram
6
+ extend Config
7
+ include Errors
8
+ include Models
9
+ end
10
+ end
@@ -0,0 +1,14 @@
1
+ module Net
2
+ module Instagram
3
+ module Api
4
+ class Configuration
5
+ attr_accessor :client_id
6
+
7
+ def initialize
8
+ @client_id = ENV['INSTAGRAM_CLIENT_ID']
9
+ end
10
+ end
11
+ end
12
+ end
13
+ end
14
+
@@ -0,0 +1,51 @@
1
+ require 'net/instagram/errors/response_error'
2
+ require 'net/instagram/errors/unknown_user'
3
+ require 'active_support'
4
+ require 'active_support/core_ext'
5
+
6
+ module Net
7
+ module Instagram
8
+ module Api
9
+ class Request
10
+ def initialize(attrs = {})
11
+ @host = 'api.instagram.com'
12
+ @path = attrs.fetch :path, "/v1/#{attrs[:endpoint]}"
13
+ @query = attrs[:params] if attrs[:params]
14
+ @method = attrs.fetch :method, :get
15
+ end
16
+
17
+ def run
18
+ case response = run_http_request
19
+ when Net::HTTPOK
20
+ JSON(response.body)['data']
21
+ else
22
+ raise Errors::ResponseError, response
23
+ end
24
+ end
25
+
26
+ private
27
+ def run_http_request
28
+ Net::HTTP.start(uri.host, uri.port, use_ssl: true) do |http|
29
+ http.request http_request
30
+ end
31
+ end
32
+
33
+ def http_request
34
+ http_class = "Net::HTTP::#{@method.capitalize}".constantize
35
+ @http_request ||= http_class.new(uri.request_uri)
36
+ end
37
+
38
+ def uri
39
+ @uri ||= URI::HTTPS.build host: @host, path: @path, query: query
40
+ end
41
+
42
+ def query
43
+ {}.tap do |query|
44
+ query.merge! @query if @query
45
+ query.merge! client_id: Net::Instagram.configuration.client_id
46
+ end.to_param
47
+ end
48
+ end
49
+ end
50
+ end
51
+ end
@@ -0,0 +1,16 @@
1
+ require 'net/instagram/api/configuration'
2
+
3
+ module Net
4
+ module Instagram
5
+ module Config
6
+ def configure
7
+ yield configuration if block_given?
8
+ end
9
+
10
+ def configuration
11
+ @configuration ||= Api::Configuration.new
12
+ end
13
+ end
14
+ end
15
+ end
16
+
@@ -0,0 +1,3 @@
1
+ require 'net/instagram/errors/response_error'
2
+ require 'net/instagram/errors/private_user'
3
+ require 'net/instagram/errors/unknown_user'
@@ -0,0 +1,11 @@
1
+ module Net
2
+ module Instagram
3
+ module Errors
4
+ class PrivateUser < StandardError
5
+ def message
6
+ 'Private user'
7
+ end
8
+ end
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,14 @@
1
+ module Net
2
+ module Instagram
3
+ module Errors
4
+ class ResponseError < StandardError
5
+ attr_reader :response
6
+
7
+ def initialize(response = {})
8
+ @response = response
9
+ super response
10
+ end
11
+ end
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,11 @@
1
+ module Net
2
+ module Instagram
3
+ module Errors
4
+ class UnknownUser < StandardError
5
+ def message
6
+ 'Unknown user'
7
+ end
8
+ end
9
+ end
10
+ end
11
+ end
@@ -0,0 +1 @@
1
+ require 'net/instagram/models/user'
@@ -0,0 +1,64 @@
1
+ require 'net/instagram/api/request'
2
+ require 'net/instagram/errors'
3
+
4
+ module Net
5
+ module Instagram
6
+ module Models
7
+ class User
8
+ attr_reader :username, :follower_count
9
+
10
+ def initialize(attrs = {})
11
+ @username = attrs['username']
12
+ @follower_count = attrs['counts']['followed_by']
13
+ end
14
+
15
+ # Returns the existing Instagram user matching the provided attributes or
16
+ # nil when the user is not found.
17
+ #
18
+ # @return [Net::Instagram::Models::User] when the user is found.
19
+ # @return [nil] when the user is not found or has a private account.
20
+ # @param [Hash] params the attributes to find a user by.
21
+ # @option params [String] :username The Instagram user’s username
22
+ # (case-insensitive).
23
+ def self.find_by(params = {})
24
+ find_by! params
25
+ rescue Errors::PrivateUser, Errors::UnknownUser
26
+ nil
27
+ end
28
+
29
+ # Returns the existing Instagram user matching the provided attributes or
30
+ # nil when the user is not found, and raises an error when the user account is private.
31
+ #
32
+ # @return [Net::Instagram::Models::User] the Instagram user.
33
+ # @param [Hash] params the attributes to find a user by.
34
+ # @option params [String] :username The Instagram user’s username
35
+ # (case-insensitive).
36
+ # @raise [Net::Errors::PrivateUser] if the user account is private.
37
+ def self.find_by!(params = {})
38
+ find_by_username! params[:username]
39
+ end
40
+
41
+ private
42
+
43
+ def self.find_by_username!(username)
44
+ request = Api::Request.new endpoint: "users/search", params: {q: username}
45
+ users = Array.wrap request.run
46
+ if user = users.find{|u| u['username'].casecmp(username).zero?}
47
+ find_by_id! user['id']
48
+ else
49
+ raise Errors::UnknownUser
50
+ end
51
+ end
52
+
53
+ def self.find_by_id!(id)
54
+ request = Api::Request.new endpoint: "users/#{id}"
55
+ new request.run
56
+ rescue Errors::ResponseError => error
57
+ case error.response
58
+ when Net::HTTPBadRequest then raise Errors::PrivateUser
59
+ end
60
+ end
61
+ end
62
+ end
63
+ end
64
+ end
@@ -20,8 +20,8 @@ module Net
20
20
  # @option params [String] :screen_name The Twitter user’s screen name
21
21
  # (case-insensitive).
22
22
  def self.find_by(params = {})
23
- find_by! params
24
- rescue Errors::UnknownUser, Errors::SuspendedUser
23
+ find_by! params
24
+ rescue Errors::UnknownUser, Errors::SuspendedUser
25
25
  nil
26
26
  end
27
27
 
@@ -67,9 +67,9 @@ module Net
67
67
  end
68
68
  end
69
69
 
70
- private
70
+ private
71
71
 
72
- def self.to_where_params(conditions = {})
72
+ def self.to_where_params(conditions = {})
73
73
  conditions.dup.tap do |params|
74
74
  params.each{|k,v| params[k] = v.join(',') if v.is_a?(Array)}
75
75
  end
data/lib/net/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module Net
2
- VERSION = "0.1.0"
2
+ VERSION = "0.1.1"
3
3
  end
@@ -0,0 +1,61 @@
1
+ require 'spec_helper'
2
+ require 'net/instagram'
3
+
4
+ describe Net::Instagram::User, :vcr do
5
+ before :all do
6
+ Net::Instagram.configure do |config|
7
+ if config.client_id.blank?
8
+ config.client_id = 'CLIENT_ID'
9
+ end
10
+ end
11
+ end
12
+
13
+ let(:existing_username) { 'Fullscreen' }
14
+ let(:unknown_username) { '01LjqweoojkjR' }
15
+ let(:private_username) { 'brohemian' }
16
+
17
+ describe '.find_by' do
18
+ subject(:user) { Net::Instagram::User.find_by username: username }
19
+
20
+ context 'given an existing (case-insensitive) username' do
21
+ let(:username) { existing_username }
22
+
23
+ it 'returns an object representing that user' do
24
+ expect(user.username).to eq 'fullscreen'
25
+ expect(user.follower_count).to be_an Integer
26
+ end
27
+ end
28
+
29
+ context 'given an unknown username' do
30
+ let(:username) { unknown_username }
31
+ it { expect(user).to be_nil }
32
+ end
33
+
34
+ context 'given a private username' do
35
+ let(:username) { private_username }
36
+ it { expect(user).to be_nil }
37
+ end
38
+ end
39
+
40
+ describe '.find_by!' do
41
+ subject(:user) { Net::Instagram::User.find_by! username: username }
42
+ context 'given an existing (case-insensitive) username' do
43
+ let(:username) { existing_username }
44
+
45
+ it 'returns an object representing that user' do
46
+ expect(user.username).to eq 'fullscreen'
47
+ expect(user.follower_count).to be_an Integer
48
+ end
49
+ end
50
+
51
+ context 'given an unknown username' do
52
+ let(:username) { unknown_username }
53
+ it { expect{user}.to raise_error Net::Instagram::UnknownUser }
54
+ end
55
+
56
+ context 'given a private username' do
57
+ let(:username) { private_username }
58
+ it { expect{user}.to raise_error Net::Instagram::PrivateUser }
59
+ end
60
+ end
61
+ end