net 0.0.1 → 0.1.0

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 (33) hide show
  1. checksums.yaml +4 -4
  2. data/.rspec +3 -0
  3. data/CHANGELOG.md +11 -0
  4. data/{LICENSE.txt → MIT-LICENSE} +2 -4
  5. data/README.md +108 -17
  6. data/lib/net.rb +0 -1
  7. data/lib/net/twitter.rb +11 -0
  8. data/lib/net/twitter/api/configuration.rb +16 -0
  9. data/lib/net/twitter/api/request.rb +121 -0
  10. data/lib/net/twitter/config.rb +15 -0
  11. data/lib/net/twitter/errors.rb +4 -0
  12. data/lib/net/twitter/errors/response_error.rb +14 -0
  13. data/lib/net/twitter/errors/suspended_user.rb +11 -0
  14. data/lib/net/twitter/errors/too_many_users.rb +11 -0
  15. data/lib/net/twitter/errors/unknown_user.rb +11 -0
  16. data/lib/net/twitter/models.rb +1 -0
  17. data/lib/net/twitter/models/user.rb +80 -0
  18. data/lib/net/version.rb +1 -1
  19. data/net.gemspec +14 -7
  20. data/spec/net/twitter/models/user_spec.rb +98 -0
  21. data/spec/spec_helper.rb +21 -0
  22. data/spec/support/cassettes/Net_Twitter_Models_User/_find_by/given_a_suspended_screen_name/.yml +69 -0
  23. data/spec/support/cassettes/Net_Twitter_Models_User/_find_by/given_an_existing_case-insensitive_screen_name/returns_an_object_representing_that_user.yml +136 -0
  24. data/spec/support/cassettes/Net_Twitter_Models_User/_find_by/given_an_unknown_screen_name/.yml +69 -0
  25. data/spec/support/cassettes/Net_Twitter_Models_User/_find_by_/given_a_suspended_screen_name/.yml +69 -0
  26. data/spec/support/cassettes/Net_Twitter_Models_User/_find_by_/given_an_existing_case-insensitive_screen_name/returns_an_object_representing_that_user.yml +80 -0
  27. data/spec/support/cassettes/Net_Twitter_Models_User/_find_by_/given_an_unknown_screen_name/.yml +69 -0
  28. data/spec/support/cassettes/Net_Twitter_Models_User/_where/called_more_times_than_Twitter_s_rate_limit/waits_for_the_rate_limit_to_expire_then_continues.yml +4802 -0
  29. data/spec/support/cassettes/Net_Twitter_Models_User/_where/given_more_screen_names_than_allowed_by_Twitter/.yml +69 -0
  30. data/spec/support/cassettes/Net_Twitter_Models_User/_where/given_multiple_existing_case-insensitive_screen_names/returns_an_array_of_objects_representing_those_users.yml +87 -0
  31. data/spec/support/cassettes/Net_Twitter_Models_User/_where/given_multiple_unknown_or_suspended_screen_names/returns_an_empty_array.yml +69 -0
  32. data/spec/support/vcr.rb +22 -0
  33. metadata +138 -13
@@ -1,3 +1,3 @@
1
1
  module Net
2
- VERSION = "0.0.1"
2
+ VERSION = "0.1.0"
3
3
  end
@@ -6,11 +6,11 @@ require 'net/version'
6
6
  Gem::Specification.new do |spec|
7
7
  spec.name = "net"
8
8
  spec.version = Net::VERSION
9
- spec.authors = ["claudiob"]
10
- spec.email = ["claudiob@gmail.com"]
11
- spec.summary = %q{Write a short summary. Required.}
12
- spec.description = %q{Write a longer description. Optional.}
13
- spec.homepage = ""
9
+ spec.authors = ["Jeremy Cohen Hoffing", "Claudio Baccigalupo"]
10
+ spec.email = ["jcohenhoffing@gmail.com", "claudio@fullscreen.net"]
11
+ spec.summary = %q{An API Client for social networks}
12
+ spec.description = %q{Retrieves information for Twitter users}
13
+ spec.homepage = "https://github.com/Fullscreen/net"
14
14
  spec.license = "MIT"
15
15
 
16
16
  spec.files = `git ls-files -z`.split("\x0")
@@ -18,6 +18,13 @@ Gem::Specification.new do |spec|
18
18
  spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
19
19
  spec.require_paths = ["lib"]
20
20
 
21
+ spec.add_dependency 'activesupport'
22
+
21
23
  spec.add_development_dependency "bundler", "~> 1.6"
22
- spec.add_development_dependency "rake"
23
- end
24
+ spec.add_development_dependency "rake", "~> 10.3"
25
+ spec.add_development_dependency "rspec", "~> 3.1"
26
+ spec.add_development_dependency "yard", "~> 0.8.7"
27
+ spec.add_development_dependency "coveralls", "~> 0.7.1"
28
+ spec.add_development_dependency "vcr", "~> 2.9"
29
+ spec.add_development_dependency "webmock", "~> 1.19"
30
+ end
@@ -0,0 +1,98 @@
1
+ require 'spec_helper'
2
+ require 'net/twitter'
3
+
4
+ describe Net::Twitter::User, :vcr do
5
+ let(:unknown_screen_name) { '09hlQHE29534awe' }
6
+ let(:suspended_screen_name) { 'CodFatherLucas' }
7
+ let(:existing_screen_name) { 'fullscreen' }
8
+
9
+ describe '.find_by' do
10
+ subject(:user) { Net::Twitter::User.find_by screen_name: screen_name }
11
+
12
+ context 'given an existing (case-insensitive) screen name' do
13
+ let(:screen_name) { existing_screen_name }
14
+
15
+ it 'returns an object representing that user' do
16
+ expect(user.screen_name).to eq 'Fullscreen'
17
+ expect(user.followers_count).to be_an Integer
18
+ end
19
+ end
20
+
21
+ context 'given an unknown screen name' do
22
+ let(:screen_name) { unknown_screen_name }
23
+ it { expect(user).to be_nil }
24
+ end
25
+
26
+ context 'given a suspended screen name' do
27
+ let(:screen_name) { suspended_screen_name }
28
+ it { expect(user).to be_nil }
29
+ end
30
+ end
31
+
32
+ describe '.find_by!' do
33
+ subject(:user) { Net::Twitter::User.find_by! screen_name: screen_name }
34
+
35
+ context 'given an existing (case-insensitive) screen name' do
36
+ let(:screen_name) { existing_screen_name }
37
+
38
+ it 'returns an object representing that user' do
39
+ expect(user.screen_name).to eq 'Fullscreen'
40
+ expect(user.followers_count).to be_an Integer
41
+ end
42
+ end
43
+
44
+ context 'given an unknown screen name' do
45
+ let(:screen_name) { unknown_screen_name }
46
+ it { expect{user}.to raise_error Net::Twitter::UnknownUser }
47
+ end
48
+
49
+ context 'given a suspended screen name' do
50
+ let(:screen_name) { suspended_screen_name }
51
+ it { expect{user}.to raise_error Net::Twitter::SuspendedUser }
52
+ end
53
+ end
54
+
55
+ describe '.where' do
56
+ context 'given multiple existing (case-insensitive) screen names' do
57
+ let(:screen_names) { ['brohemian6', existing_screen_name, suspended_screen_name, unknown_screen_name] }
58
+ subject(:users) { Net::Twitter::User.where screen_name: screen_names }
59
+
60
+ it 'returns an array of objects representing those users' do
61
+ expect(users.map &:screen_name).to contain_exactly('Fullscreen', 'brohemian6')
62
+ expect(users.map &:followers_count).to all(be_an Integer)
63
+ end
64
+ end
65
+
66
+ context 'given multiple unknown or suspended screen names' do
67
+ let(:screen_names) { [suspended_screen_name, unknown_screen_name] }
68
+ subject(:users) { Net::Twitter::User.where screen_name: screen_names }
69
+
70
+ it 'returns an empty array' do
71
+ expect(users).to be_empty
72
+ end
73
+ end
74
+
75
+ # @see https://dev.twitter.com/rest/reference/get/users/lookup
76
+ context 'given more screen names than allowed by Twitter' do
77
+ let(:screen_names) { ('a'..'z').map{|x| ('a'..'e').map{|y| "#{x}#{y}"}}.flatten }
78
+ subject(:users) { Net::Twitter::User.where screen_name: screen_names }
79
+
80
+ it { expect{users}.to raise_error Net::Twitter::TooManyUsers }
81
+ end
82
+
83
+ # @see https://dev.twitter.com/rest/public/rate-limits
84
+ context 'called more times than Twitter’s rate limit' do
85
+
86
+ before { expect(Time).to receive(:now).at_least(:once).and_return 12345678 }
87
+ let(:lookup_limit) { 60 }
88
+ let(:screen_names) { [existing_screen_name] }
89
+ subject(:user_sets) { (lookup_limit + 1).times.map do
90
+ Net::Twitter::User.where screen_name: screen_names
91
+ end}
92
+
93
+ it 'waits for the rate limit to expire then continues' do
94
+ expect(user_sets.compact.size).to be > lookup_limit
95
+ end
96
+ end
97
+ end
98
+ end
@@ -0,0 +1,21 @@
1
+ require 'simplecov'
2
+ require 'coveralls'
3
+
4
+ SimpleCov.formatter = SimpleCov::Formatter::MultiFormatter[
5
+ SimpleCov::Formatter::HTMLFormatter,
6
+ Coveralls::SimpleCov::Formatter
7
+ ]
8
+ SimpleCov.start
9
+
10
+ Dir['./spec/support/**/*.rb'].each {|f| require f}
11
+
12
+ RSpec.configure do |config|
13
+ # config.order = 'random'
14
+ config.before(:all) do
15
+ Net::Twitter.configure do |config|
16
+ if config.apps.empty?
17
+ config.apps.push key: 'TWITTER_API_KEY', secret: 'TWITTER_API_SECRET'
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,69 @@
1
+ ---
2
+ http_interactions:
3
+ - request:
4
+ method: get
5
+ uri: https://api.twitter.com/1.1/users/show.json?screen_name=CodFatherLucas
6
+ body:
7
+ encoding: US-ASCII
8
+ string: ''
9
+ headers:
10
+ Accept-Encoding:
11
+ - gzip;q=1.0,deflate;q=0.6,identity;q=0.3
12
+ Accept:
13
+ - "*/*"
14
+ User-Agent:
15
+ - Ruby
16
+ Authorization:
17
+ - ACCESS_TOKEN
18
+ response:
19
+ status:
20
+ code: 403
21
+ message: Forbidden
22
+ headers:
23
+ Cache-Control:
24
+ - no-cache, no-store, must-revalidate, pre-check=0, post-check=0
25
+ Content-Length:
26
+ - '86'
27
+ Content-Type:
28
+ - application/json;charset=utf-8
29
+ Date:
30
+ - Sat, 11 Oct 2014 01:08:22 UTC
31
+ Expires:
32
+ - Tue, 31 Mar 1981 05:00:00 GMT
33
+ Last-Modified:
34
+ - Sat, 11 Oct 2014 01:08:22 GMT
35
+ Pragma:
36
+ - no-cache
37
+ Server:
38
+ - tsa_a
39
+ Set-Cookie:
40
+ - guest_id=v1%3A141298970231864986; Domain=.twitter.com; Path=/; Expires=Mon,
41
+ 10-Oct-2016 01:08:22 UTC
42
+ Status:
43
+ - 403 Forbidden
44
+ Strict-Transport-Security:
45
+ - max-age=631138519
46
+ X-Access-Level:
47
+ - read
48
+ X-Connection-Hash:
49
+ - 5a90ce3d1871c791b4020318e67b4e6e
50
+ X-Content-Type-Options:
51
+ - nosniff
52
+ X-Frame-Options:
53
+ - SAMEORIGIN
54
+ X-Rate-Limit-Limit:
55
+ - '180'
56
+ X-Rate-Limit-Remaining:
57
+ - '177'
58
+ X-Rate-Limit-Reset:
59
+ - '1412990602'
60
+ X-Transaction:
61
+ - c977632fada49967
62
+ X-Xss-Protection:
63
+ - 1; mode=block
64
+ body:
65
+ encoding: UTF-8
66
+ string: '{"errors":[{"code":63,"message":"User has been suspended."}]}'
67
+ http_version:
68
+ recorded_at: Sat, 11 Oct 2014 01:08:15 GMT
69
+ recorded_with: VCR 2.9.3
@@ -0,0 +1,136 @@
1
+ ---
2
+ http_interactions:
3
+ - request:
4
+ method: post
5
+ uri: https://TWITTER_API_KEY:TWITTER_API_SECRET@api.twitter.com/oauth2/token
6
+ body:
7
+ encoding: US-ASCII
8
+ string: grant_type=client_credentials
9
+ headers:
10
+ Content-Type:
11
+ - application/x-www-form-urlencoded
12
+ response:
13
+ status:
14
+ code: 200
15
+ message: OK
16
+ headers:
17
+ Cache-Control:
18
+ - no-cache, no-store, must-revalidate, pre-check=0, post-check=0
19
+ Content-Length:
20
+ - '157'
21
+ Content-Type:
22
+ - application/json;charset=utf-8
23
+ Date:
24
+ - Sat, 11 Oct 2014 01:08:21 UTC
25
+ Expires:
26
+ - Tue, 31 Mar 1981 05:00:00 GMT
27
+ Last-Modified:
28
+ - Sat, 11 Oct 2014 01:08:21 GMT
29
+ Ml:
30
+ - S
31
+ Pragma:
32
+ - no-cache
33
+ Server:
34
+ - tsa_a
35
+ Set-Cookie:
36
+ - guest_id=v1%3A141298970195572286; Domain=.twitter.com; Path=/; Expires=Mon,
37
+ 10-Oct-2016 01:08:21 UTC
38
+ Status:
39
+ - 200 OK
40
+ Strict-Transport-Security:
41
+ - max-age=631138519
42
+ X-Connection-Hash:
43
+ - d63d4d3bf2e355106c486bf3c279d075
44
+ X-Content-Type-Options:
45
+ - nosniff
46
+ X-Frame-Options:
47
+ - SAMEORIGIN
48
+ X-Transaction:
49
+ - 038d099f9b129c61
50
+ X-Ua-Compatible:
51
+ - IE=edge,chrome=1
52
+ X-Xss-Protection:
53
+ - 1; mode=block
54
+ body:
55
+ encoding: UTF-8
56
+ string: '{"token_type":"bearer","access_token":"ACCESS_TOKEN"}'
57
+ http_version:
58
+ recorded_at: Sat, 11 Oct 2014 01:08:15 GMT
59
+ - request:
60
+ method: get
61
+ uri: https://api.twitter.com/1.1/users/show.json?screen_name=fullscreen
62
+ body:
63
+ encoding: US-ASCII
64
+ string: ''
65
+ headers:
66
+ Accept-Encoding:
67
+ - gzip;q=1.0,deflate;q=0.6,identity;q=0.3
68
+ Accept:
69
+ - "*/*"
70
+ User-Agent:
71
+ - Ruby
72
+ Authorization:
73
+ - ACCESS_TOKEN
74
+ response:
75
+ status:
76
+ code: 200
77
+ message: OK
78
+ headers:
79
+ Cache-Control:
80
+ - no-cache, no-store, must-revalidate, pre-check=0, post-check=0
81
+ Content-Length:
82
+ - '1270'
83
+ Content-Type:
84
+ - application/json;charset=utf-8
85
+ Date:
86
+ - Sat, 11 Oct 2014 01:08:22 UTC
87
+ Expires:
88
+ - Tue, 31 Mar 1981 05:00:00 GMT
89
+ Last-Modified:
90
+ - Sat, 11 Oct 2014 01:08:22 GMT
91
+ Pragma:
92
+ - no-cache
93
+ Server:
94
+ - tsa_a
95
+ Set-Cookie:
96
+ - guest_id=v1%3A141298970205889731; Domain=.twitter.com; Path=/; Expires=Mon,
97
+ 10-Oct-2016 01:08:22 UTC
98
+ Status:
99
+ - 200 OK
100
+ Strict-Transport-Security:
101
+ - max-age=631138519
102
+ X-Access-Level:
103
+ - read
104
+ X-Connection-Hash:
105
+ - 212c0e3bb4a455bc0ab147f8f4b9524b
106
+ X-Content-Type-Options:
107
+ - nosniff
108
+ X-Frame-Options:
109
+ - SAMEORIGIN
110
+ X-Rate-Limit-Limit:
111
+ - '180'
112
+ X-Rate-Limit-Remaining:
113
+ - '179'
114
+ X-Rate-Limit-Reset:
115
+ - '1412990602'
116
+ X-Transaction:
117
+ - f0566e9fa1c91113
118
+ X-Xss-Protection:
119
+ - 1; mode=block
120
+ body:
121
+ encoding: UTF-8
122
+ string: '{"id":238110322,"id_str":"238110322","name":"Fullscreen","screen_name":"Fullscreen","location":"LA
123
+ \/ Worldwide","description":"The First Media Company for the Connected Generation.\r\n#PowerToTheCreators","url":"http:\/\/t.co\/IuYBnxFitD","entities":{"url":{"urls":[{"url":"http:\/\/t.co\/IuYBnxFitD","expanded_url":"http:\/\/www.fullscreen.com","display_url":"fullscreen.com","indices":[0,22]}]},"description":{"urls":[]}},"protected":false,"followers_count":48138,"friends_count":2713,"listed_count":378,"created_at":"Fri
124
+ Jan 14 11:36:27 +0000 2011","favourites_count":58329,"utc_offset":-25200,"time_zone":"Pacific
125
+ Time (US & Canada)","geo_enabled":true,"verified":true,"statuses_count":19163,"lang":"en","status":{"created_at":"Sat
126
+ Oct 11 00:52:51 +0000 2014","id":520738765266886656,"id_str":"520738765266886656","text":"#ICYMI:
127
+ 15 Secs of Fullscreen with @SourPatchKids @WalkingDead_AMC @AsapSCIENCE @WeeklyChris
128
+ @AndreaRussett + More! http:\/\/t.co\/zoLiS9CZSL","source":"\u003ca href=\"http:\/\/twitter.com\"
129
+ rel=\"nofollow\"\u003eTwitter Web Client\u003c\/a\u003e","truncated":false,"in_reply_to_status_id":null,"in_reply_to_status_id_str":null,"in_reply_to_user_id":null,"in_reply_to_user_id_str":null,"in_reply_to_screen_name":null,"geo":null,"coordinates":null,"place":null,"contributors":null,"retweet_count":4,"favorite_count":9,"entities":{"hashtags":[{"text":"ICYMI","indices":[0,6]}],"symbols":[],"urls":[{"url":"http:\/\/t.co\/zoLiS9CZSL","expanded_url":"http:\/\/instagram.com\/p\/t_iCt4rhXy\/","display_url":"instagram.com\/p\/t_iCt4rhXy\/","indices":[116,138]}],"user_mentions":[{"screen_name":"SourPatchKids","name":"Sour
130
+ Patch Kids","id":122480273,"id_str":"122480273","indices":[35,49]},{"screen_name":"WalkingDead_AMC","name":"The
131
+ Walking Dead AMC","id":65184105,"id_str":"65184105","indices":[50,66]},{"screen_name":"AsapSCIENCE","name":"AsapSCIENCE","id":592912724,"id_str":"592912724","indices":[67,79]},{"screen_name":"WeeklyChris","name":"Christian
132
+ Collins","id":125806240,"id_str":"125806240","indices":[80,92]},{"screen_name":"AndreaRussett","name":"Andrea
133
+ Russett","id":30495613,"id_str":"30495613","indices":[93,107]}]},"favorited":false,"retweeted":false,"possibly_sensitive":false,"lang":"en"},"contributors_enabled":false,"is_translator":false,"is_translation_enabled":false,"profile_background_color":"16121E","profile_background_image_url":"http:\/\/pbs.twimg.com\/profile_background_images\/449347350306111489\/zWlAXsXf.png","profile_background_image_url_https":"https:\/\/pbs.twimg.com\/profile_background_images\/449347350306111489\/zWlAXsXf.png","profile_background_tile":false,"profile_image_url":"http:\/\/pbs.twimg.com\/profile_images\/449344241752227841\/xJeF6VQn_normal.png","profile_image_url_https":"https:\/\/pbs.twimg.com\/profile_images\/449344241752227841\/xJeF6VQn_normal.png","profile_banner_url":"https:\/\/pbs.twimg.com\/profile_banners\/238110322\/1411497387","profile_link_color":"E62E4C","profile_sidebar_border_color":"FFFFFF","profile_sidebar_fill_color":"DDEEF6","profile_text_color":"333333","profile_use_background_image":false,"default_profile":false,"default_profile_image":false,"following":null,"follow_request_sent":null,"notifications":null}'
134
+ http_version:
135
+ recorded_at: Sat, 11 Oct 2014 01:08:15 GMT
136
+ recorded_with: VCR 2.9.3
@@ -0,0 +1,69 @@
1
+ ---
2
+ http_interactions:
3
+ - request:
4
+ method: get
5
+ uri: https://api.twitter.com/1.1/users/show.json?screen_name=09hlQHE29534awe
6
+ body:
7
+ encoding: US-ASCII
8
+ string: ''
9
+ headers:
10
+ Accept-Encoding:
11
+ - gzip;q=1.0,deflate;q=0.6,identity;q=0.3
12
+ Accept:
13
+ - "*/*"
14
+ User-Agent:
15
+ - Ruby
16
+ Authorization:
17
+ - ACCESS_TOKEN
18
+ response:
19
+ status:
20
+ code: 404
21
+ message: Not Found
22
+ headers:
23
+ Cache-Control:
24
+ - no-cache, no-store, must-revalidate, pre-check=0, post-check=0
25
+ Content-Length:
26
+ - '92'
27
+ Content-Type:
28
+ - application/json;charset=utf-8
29
+ Date:
30
+ - Sat, 11 Oct 2014 01:08:22 UTC
31
+ Expires:
32
+ - Tue, 31 Mar 1981 05:00:00 GMT
33
+ Last-Modified:
34
+ - Sat, 11 Oct 2014 01:08:22 GMT
35
+ Pragma:
36
+ - no-cache
37
+ Server:
38
+ - tsa_a
39
+ Set-Cookie:
40
+ - guest_id=v1%3A141298970220021935; Domain=.twitter.com; Path=/; Expires=Mon,
41
+ 10-Oct-2016 01:08:22 UTC
42
+ Status:
43
+ - 404 Not Found
44
+ Strict-Transport-Security:
45
+ - max-age=631138519
46
+ X-Access-Level:
47
+ - read
48
+ X-Connection-Hash:
49
+ - 278156e7b09f629df55ab8d5cf289b58
50
+ X-Content-Type-Options:
51
+ - nosniff
52
+ X-Frame-Options:
53
+ - SAMEORIGIN
54
+ X-Rate-Limit-Limit:
55
+ - '180'
56
+ X-Rate-Limit-Remaining:
57
+ - '178'
58
+ X-Rate-Limit-Reset:
59
+ - '1412990602'
60
+ X-Transaction:
61
+ - 2b2b1106b457ce4a
62
+ X-Xss-Protection:
63
+ - 1; mode=block
64
+ body:
65
+ encoding: UTF-8
66
+ string: '{"errors":[{"message":"Sorry, that page does not exist","code":34}]}'
67
+ http_version:
68
+ recorded_at: Sat, 11 Oct 2014 01:08:15 GMT
69
+ recorded_with: VCR 2.9.3
@@ -0,0 +1,69 @@
1
+ ---
2
+ http_interactions:
3
+ - request:
4
+ method: get
5
+ uri: https://api.twitter.com/1.1/users/show.json?screen_name=CodFatherLucas
6
+ body:
7
+ encoding: US-ASCII
8
+ string: ''
9
+ headers:
10
+ Accept-Encoding:
11
+ - gzip;q=1.0,deflate;q=0.6,identity;q=0.3
12
+ Accept:
13
+ - "*/*"
14
+ User-Agent:
15
+ - Ruby
16
+ Authorization:
17
+ - ACCESS_TOKEN
18
+ response:
19
+ status:
20
+ code: 403
21
+ message: Forbidden
22
+ headers:
23
+ Cache-Control:
24
+ - no-cache, no-store, must-revalidate, pre-check=0, post-check=0
25
+ Content-Length:
26
+ - '86'
27
+ Content-Type:
28
+ - application/json;charset=utf-8
29
+ Date:
30
+ - Sat, 11 Oct 2014 01:08:22 UTC
31
+ Expires:
32
+ - Tue, 31 Mar 1981 05:00:00 GMT
33
+ Last-Modified:
34
+ - Sat, 11 Oct 2014 01:08:22 GMT
35
+ Pragma:
36
+ - no-cache
37
+ Server:
38
+ - tsa_a
39
+ Set-Cookie:
40
+ - guest_id=v1%3A141298970268472432; Domain=.twitter.com; Path=/; Expires=Mon,
41
+ 10-Oct-2016 01:08:22 UTC
42
+ Status:
43
+ - 403 Forbidden
44
+ Strict-Transport-Security:
45
+ - max-age=631138519
46
+ X-Access-Level:
47
+ - read
48
+ X-Connection-Hash:
49
+ - d5f954bca7a9b3a762c62153a96e442e
50
+ X-Content-Type-Options:
51
+ - nosniff
52
+ X-Frame-Options:
53
+ - SAMEORIGIN
54
+ X-Rate-Limit-Limit:
55
+ - '180'
56
+ X-Rate-Limit-Remaining:
57
+ - '174'
58
+ X-Rate-Limit-Reset:
59
+ - '1412990602'
60
+ X-Transaction:
61
+ - 5ec8678f24b910dd
62
+ X-Xss-Protection:
63
+ - 1; mode=block
64
+ body:
65
+ encoding: UTF-8
66
+ string: '{"errors":[{"code":63,"message":"User has been suspended."}]}'
67
+ http_version:
68
+ recorded_at: Sat, 11 Oct 2014 01:08:16 GMT
69
+ recorded_with: VCR 2.9.3