net 0.1.0 → 0.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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