twitter_with_auto_pagination 0.6.2 → 0.7.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (26) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +8 -18
  3. data/lib/twitter_with_auto_pagination.rb +42 -1
  4. data/lib/twitter_with_auto_pagination/log_subscriber.rb +2 -2
  5. data/lib/twitter_with_auto_pagination/rest/api.rb +31 -0
  6. data/lib/twitter_with_auto_pagination/rest/extension/clusters.rb +43 -0
  7. data/lib/twitter_with_auto_pagination/rest/extension/favoriting.rb +106 -0
  8. data/lib/twitter_with_auto_pagination/rest/extension/friends_and_followers.rb +131 -0
  9. data/lib/twitter_with_auto_pagination/rest/extension/replying.rb +90 -0
  10. data/lib/twitter_with_auto_pagination/rest/extension/unfollowing.rb +29 -0
  11. data/lib/twitter_with_auto_pagination/rest/favorites.rb +20 -0
  12. data/lib/twitter_with_auto_pagination/rest/friends_and_followers.rb +94 -0
  13. data/lib/twitter_with_auto_pagination/rest/search.rb +19 -0
  14. data/lib/twitter_with_auto_pagination/rest/timelines.rb +37 -0
  15. data/lib/twitter_with_auto_pagination/rest/uncategorized.rb +83 -0
  16. data/lib/twitter_with_auto_pagination/rest/users.rb +62 -0
  17. data/lib/twitter_with_auto_pagination/rest/utils.rb +303 -0
  18. data/spec/helper.rb +60 -1
  19. data/spec/twitter_with_auto_pagination/client_spec.rb +150 -0
  20. data/twitter_with_auto_pagination.gemspec +1 -1
  21. metadata +17 -8
  22. data/lib/twitter_with_auto_pagination/client.rb +0 -139
  23. data/lib/twitter_with_auto_pagination/existing_api.rb +0 -127
  24. data/lib/twitter_with_auto_pagination/new_api.rb +0 -337
  25. data/lib/twitter_with_auto_pagination/utils.rb +0 -303
  26. data/spec/twitter_with_auto_pagination_spec.rb +0 -131
data/spec/helper.rb CHANGED
@@ -1,9 +1,68 @@
1
1
  require 'twitter_with_auto_pagination'
2
2
  require 'rspec'
3
+ require 'stringio'
4
+ require 'tempfile'
5
+ require 'timecop'
6
+ require 'webmock/rspec'
7
+
8
+ require_relative 'support/media_object_examples'
3
9
 
4
10
  RSpec.configure do |config|
5
11
  config.expect_with :rspec do |c|
6
12
  c.syntax = :expect
7
13
  end
8
- config.order = 'random'
14
+ end
15
+
16
+ BASE_URL = 'https://api.twitter.com'.freeze
17
+
18
+ def a_delete(path)
19
+ a_request(:delete, BASE_URL + path)
20
+ end
21
+
22
+ def a_get(path)
23
+ a_request(:get, BASE_URL + path)
24
+ end
25
+
26
+ def a_post(path)
27
+ a_request(:post, BASE_URL + path)
28
+ end
29
+
30
+ def a_put(path)
31
+ a_request(:put, BASE_URL + path)
32
+ end
33
+
34
+ def stub_delete(path)
35
+ stub_request(:delete, BASE_URL + path)
36
+ end
37
+
38
+ def stub_get(path)
39
+ stub_request(:get, BASE_URL + path)
40
+ end
41
+
42
+ def stub_post(path)
43
+ stub_request(:post, BASE_URL + path)
44
+ end
45
+
46
+ def stub_put(path)
47
+ stub_request(:put, BASE_URL + path)
48
+ end
49
+
50
+ def fixture_path
51
+ File.expand_path('../fixtures', __FILE__)
52
+ end
53
+
54
+ def fixture(file)
55
+ File.new(fixture_path + '/' + file)
56
+ end
57
+
58
+ def capture_warning
59
+ begin
60
+ old_stderr = $stderr
61
+ $stderr = StringIO.new
62
+ yield
63
+ result = $stderr.string
64
+ ensure
65
+ $stderr = old_stderr
66
+ end
67
+ result
9
68
  end
@@ -0,0 +1,150 @@
1
+ require 'helper'
2
+
3
+ describe TwitterWithAutoPagination::Client do
4
+ let(:config) {
5
+ {
6
+ consumer_key: 'CK',
7
+ consumer_secret: 'CS',
8
+ access_token: 'AT',
9
+ access_token_secret: 'ATS',
10
+ }
11
+ }
12
+ let(:client) { TwitterWithAutoPagination::Client.new(config) }
13
+
14
+ describe '#user?' do
15
+ before do
16
+ stub_get('/1.1/users/show.json').with(query: {screen_name: 'sferik'}).to_return(body: fixture('sferik.json'), headers: {content_type: 'application/json; charset=utf-8'})
17
+ stub_get('/1.1/users/show.json').with(query: {screen_name: 'pengwynn'}).to_return(body: fixture('not_found.json'), status: 404, headers: {content_type: 'application/json; charset=utf-8'})
18
+ end
19
+ it 'requests the correct resource' do
20
+ client.user?('sferik')
21
+ expect(a_get('/1.1/users/show.json').with(query: {screen_name: 'sferik'})).to have_been_made
22
+ end
23
+ it 'returns true if user exists' do
24
+ user = client.user?('sferik')
25
+ expect(user).to be true
26
+ end
27
+ it 'returns false if user does not exist' do
28
+ user = client.user?('pengwynn')
29
+ expect(user).to be false
30
+ end
31
+ end
32
+
33
+ # describe '#initialize' do
34
+ # let(:default_call_count) { 0 }
35
+ #
36
+ # it 'sets call_count to 0' do
37
+ # expect(client.call_count).to eq(default_call_count)
38
+ # end
39
+ #
40
+ # context 'without params' do
41
+ # end
42
+ #
43
+ # context 'with params' do
44
+ # end
45
+ # end
46
+ #
47
+ # describe '#logger' do
48
+ # it 'has logger' do
49
+ # expect(client.logger).to be_truthy
50
+ # end
51
+ # end
52
+ #
53
+ # describe '#call_old_method' do
54
+ # end
55
+ #
56
+ # describe '#collect_with_max_id' do
57
+ # end
58
+ #
59
+ # describe '#collect_with_cursor' do
60
+ # end
61
+ #
62
+ # describe '#file_cache_key' do
63
+ # end
64
+ #
65
+ # describe '#namespaced_key' do
66
+ # end
67
+ #
68
+ # describe '#encode_json' do
69
+ # end
70
+ #
71
+ # describe '#decode_json' do
72
+ # end
73
+ #
74
+ # describe '#fetch_cache_or_call_api' do
75
+ # end
76
+ #
77
+ # describe '#user_timeline' do
78
+ # it 'calls old_user_timeline' do
79
+ # expect(client).to receive(:old_user_timeline)
80
+ # client.user_timeline
81
+ # end
82
+ #
83
+ # it 'calls collect_with_max_id' do
84
+ # expect(client).to receive(:collect_with_max_id)
85
+ # client.user_timeline
86
+ # end
87
+ # end
88
+ #
89
+ # describe '#user_photos' do
90
+ # it 'calls user_timeline' do
91
+ # expect(client).to receive(:user_timeline)
92
+ # client.user_photos
93
+ # end
94
+ # end
95
+ #
96
+ # describe '#friends' do
97
+ # it 'calls old_friends' do
98
+ # expect(client).to receive(:old_friends)
99
+ # client.friends
100
+ # end
101
+ #
102
+ # it 'calls collect_with_cursor' do
103
+ # expect(client).to receive(:collect_with_cursor)
104
+ # client.friends
105
+ # end
106
+ # end
107
+ #
108
+ # describe '#followers' do
109
+ # it 'calls old_followers' do
110
+ # expect(client).to receive(:old_followers)
111
+ # client.followers
112
+ # end
113
+ #
114
+ # it 'calls collect_with_cursor' do
115
+ # expect(client).to receive(:collect_with_cursor)
116
+ # client.followers
117
+ # end
118
+ # end
119
+ #
120
+ # describe '#friend_ids' do
121
+ # it 'calls old_friend_ids' do
122
+ # expect(client).to receive(:old_friend_ids)
123
+ # client.friend_ids
124
+ # end
125
+ #
126
+ # it 'calls collect_with_cursor' do
127
+ # expect(client).to receive(:collect_with_cursor)
128
+ # client.friend_ids
129
+ # end
130
+ # end
131
+ #
132
+ # describe '#follower_ids' do
133
+ # it 'calls old_follower_ids' do
134
+ # expect(client).to receive(:old_follower_ids)
135
+ # client.follower_ids
136
+ # end
137
+ #
138
+ # it 'calls collect_with_cursor' do
139
+ # expect(client).to receive(:collect_with_cursor)
140
+ # client.follower_ids
141
+ # end
142
+ # end
143
+ #
144
+ # describe '#users' do
145
+ # it 'calls old_users' do
146
+ # expect(client).to receive(:old_users)
147
+ # client.users([1, 2, 3])
148
+ # end
149
+ # end
150
+ end
@@ -22,5 +22,5 @@ Gem::Specification.new do |spec|
22
22
  spec.required_ruby_version = '>= 2.3'
23
23
  spec.summary = spec.description
24
24
  spec.test_files = Dir.glob('spec/**/*')
25
- spec.version = '0.6.2'
25
+ spec.version = '0.7.0'
26
26
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: twitter_with_auto_pagination
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.6.2
4
+ version: 0.7.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Shinohara Teruki
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2016-07-04 00:00:00.000000000 Z
11
+ date: 2016-07-06 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: twitter
@@ -91,13 +91,22 @@ files:
91
91
  - README.md
92
92
  - Rakefile
93
93
  - lib/twitter_with_auto_pagination.rb
94
- - lib/twitter_with_auto_pagination/client.rb
95
- - lib/twitter_with_auto_pagination/existing_api.rb
96
94
  - lib/twitter_with_auto_pagination/log_subscriber.rb
97
- - lib/twitter_with_auto_pagination/new_api.rb
98
- - lib/twitter_with_auto_pagination/utils.rb
95
+ - lib/twitter_with_auto_pagination/rest/api.rb
96
+ - lib/twitter_with_auto_pagination/rest/extension/clusters.rb
97
+ - lib/twitter_with_auto_pagination/rest/extension/favoriting.rb
98
+ - lib/twitter_with_auto_pagination/rest/extension/friends_and_followers.rb
99
+ - lib/twitter_with_auto_pagination/rest/extension/replying.rb
100
+ - lib/twitter_with_auto_pagination/rest/extension/unfollowing.rb
101
+ - lib/twitter_with_auto_pagination/rest/favorites.rb
102
+ - lib/twitter_with_auto_pagination/rest/friends_and_followers.rb
103
+ - lib/twitter_with_auto_pagination/rest/search.rb
104
+ - lib/twitter_with_auto_pagination/rest/timelines.rb
105
+ - lib/twitter_with_auto_pagination/rest/uncategorized.rb
106
+ - lib/twitter_with_auto_pagination/rest/users.rb
107
+ - lib/twitter_with_auto_pagination/rest/utils.rb
99
108
  - spec/helper.rb
100
- - spec/twitter_with_auto_pagination_spec.rb
109
+ - spec/twitter_with_auto_pagination/client_spec.rb
101
110
  - twitter_with_auto_pagination.gemspec
102
111
  homepage: http://github.com/ts-3156/twitter_with_auto_pagination/
103
112
  licenses:
@@ -125,4 +134,4 @@ specification_version: 4
125
134
  summary: Add auto paginate feature to Twitter gem.
126
135
  test_files:
127
136
  - spec/helper.rb
128
- - spec/twitter_with_auto_pagination_spec.rb
137
+ - spec/twitter_with_auto_pagination/client_spec.rb
@@ -1,139 +0,0 @@
1
- require 'active_support'
2
- require 'active_support/cache'
3
- require 'active_support/core_ext/string'
4
-
5
- require 'twitter_with_auto_pagination/log_subscriber'
6
- require 'twitter_with_auto_pagination/utils'
7
- require 'twitter_with_auto_pagination/existing_api'
8
- require 'twitter_with_auto_pagination/new_api'
9
-
10
- require 'twitter'
11
- require 'hashie'
12
- require 'parallel'
13
-
14
- module TwitterWithAutoPagination
15
- class Client < Twitter::REST::Client
16
- def initialize(options = {})
17
- @cache = ActiveSupport::Cache::FileStore.new(File.join('tmp', 'api_cache'))
18
- @call_count = 0
19
-
20
- @uid = options.has_key?(:uid) ? options.delete(:uid).to_i : nil
21
- @screen_name = options.has_key?(:screen_name) ? options.delete(:screen_name).to_s : nil
22
-
23
- logger =
24
- if options.has_key?(:logger)
25
- options.delete(:logger)
26
- else
27
- Dir.mkdir('log') unless File.exists?('log')
28
- Logger.new('log/twitter_with_auto_pagination.log')
29
- end
30
- logger.level = options.has_key?(:log_level) ? options.delete(:log_level) : :debug
31
- @@logger = @logger = logger
32
-
33
- super
34
- end
35
-
36
- def self.logger
37
- @@logger
38
- end
39
-
40
- attr_accessor :call_count
41
- attr_reader :cache, :authenticated_user, :logger
42
-
43
- INDENT = 4
44
-
45
- include TwitterWithAutoPagination::Utils
46
-
47
- alias :old_verify_credentials :verify_credentials
48
- alias :old_friendship? :friendship?
49
- alias :old_user? :user?
50
- alias :old_user :user
51
- alias :old_friend_ids :friend_ids
52
- alias :old_follower_ids :follower_ids
53
- alias :old_friends :friends
54
- alias :old_followers :followers
55
- alias :old_users :users
56
- alias :old_home_timeline :home_timeline
57
- alias :old_user_timeline :user_timeline
58
- alias :old_mentions_timeline :mentions_timeline
59
- alias :old_favorites :favorites
60
- alias :old_search :search
61
-
62
- include TwitterWithAutoPagination::ExistingApi
63
- include TwitterWithAutoPagination::NewApi
64
-
65
- def usage_stats_wday_series_data(times)
66
- wday_count = times.each_with_object((0..6).map { |n| [n, 0] }.to_h) do |time, memo|
67
- memo[time.wday] += 1
68
- end
69
- wday_count.map { |k, v| [I18n.t('date.abbr_day_names')[k], v] }.map do |key, value|
70
- {name: key, y: value, drilldown: key}
71
- end
72
- end
73
-
74
- def usage_stats_wday_drilldown_series(times)
75
- hour_count =
76
- (0..6).each_with_object((0..6).map { |n| [n, nil] }.to_h) do |wday, wday_memo|
77
- wday_memo[wday] =
78
- times.select { |t| t.wday == wday }.map { |t| t.hour }.each_with_object((0..23).map { |n| [n, 0] }.to_h) do |hour, hour_memo|
79
- hour_memo[hour] += 1
80
- end
81
- end
82
- hour_count.map { |k, v| [I18n.t('date.abbr_day_names')[k], v] }.map do |key, value|
83
- {name: key, id: key, data: value.to_a.map{|a| [a[0].to_s, a[1]] }}
84
- end
85
- end
86
-
87
- def usage_stats_hour_series_data(times)
88
- hour_count = times.each_with_object((0..23).map { |n| [n, 0] }.to_h) do |time, memo|
89
- memo[time.hour] += 1
90
- end
91
- hour_count.map do |key, value|
92
- {name: key.to_s, y: value, drilldown: key.to_s}
93
- end
94
- end
95
-
96
- def usage_stats_hour_drilldown_series(times)
97
- wday_count =
98
- (0..23).each_with_object((0..23).map { |n| [n, nil] }.to_h) do |hour, hour_memo|
99
- hour_memo[hour] =
100
- times.select { |t| t.hour == hour }.map { |t| t.wday }.each_with_object((0..6).map { |n| [n, 0] }.to_h) do |wday, wday_memo|
101
- wday_memo[wday] += 1
102
- end
103
- end
104
- wday_count.map do |key, value|
105
- {name: key.to_s, id: key.to_s, data: value.to_a.map{|a| [I18n.t('date.abbr_day_names')[a[0]], a[1]] }}
106
- end
107
- end
108
-
109
- def twitter_addiction_series(times)
110
- five_mins = 5.minutes
111
- wday_expended_seconds =
112
- (0..6).each_with_object((0..6).map { |n| [n, nil] }.to_h) do |wday, wday_memo|
113
- target_times = times.select { |t| t.wday == wday }
114
- wday_memo[wday] = target_times.empty? ? nil : target_times.each_cons(2).map {|a, b| (a - b) < five_mins ? a - b : five_mins }.sum
115
- end
116
- days = times.map{|t| t.to_date.to_s(:long) }.uniq.size
117
- weeks = (days > 7) ? days / 7.0 : 1.0
118
- wday_expended_seconds.map { |k, v| [I18n.t('date.abbr_day_names')[k], (v.nil? ? nil : v / weeks / 60)] }.map do |key, value|
119
- {name: key, y: value}
120
- end
121
- end
122
-
123
- def usage_stats(user, options = {})
124
- n_days_ago = options.has_key?(:days) ? options[:days].days.ago : 100.years.ago
125
- tweets = options.has_key?(:tweets) ? options.delete(:tweets) : user_timeline(user)
126
- times =
127
- # TODO Use user specific time zone
128
- tweets.map { |t| ActiveSupport::TimeZone['Tokyo'].parse(t.created_at.to_s) }.
129
- select { |t| t > n_days_ago }
130
- [
131
- usage_stats_wday_series_data(times),
132
- usage_stats_wday_drilldown_series(times),
133
- usage_stats_hour_series_data(times),
134
- usage_stats_hour_drilldown_series(times),
135
- twitter_addiction_series(times)
136
- ]
137
- end
138
- end
139
- end
@@ -1,127 +0,0 @@
1
- module TwitterWithAutoPagination
2
- module ExistingApi
3
- def verify_credentials(*args)
4
- options = {skip_status: true}.merge(args.extract_options!)
5
- fetch_cache_or_call_api(__method__, args) {
6
- call_old_method("old_#{__method__}", *args, options)
7
- }
8
- end
9
-
10
- def friendship?(*args)
11
- options = args.extract_options!
12
- fetch_cache_or_call_api(__method__, args) {
13
- call_old_method("old_#{__method__}", *args, options)
14
- }
15
- end
16
-
17
- def user?(*args)
18
- options = args.extract_options!
19
- args[0] = verify_credentials(skip_status: true).id if args.empty?
20
- fetch_cache_or_call_api(__method__, args[0], options) {
21
- call_old_method("old_#{__method__}", args[0], options)
22
- }
23
- end
24
-
25
- def user(*args)
26
- options = args.extract_options!
27
- args[0] = verify_credentials(skip_status: true).id if args.empty?
28
- fetch_cache_or_call_api(__method__, args[0], options) {
29
- call_old_method("old_#{__method__}", args[0], options)
30
- }
31
- end
32
-
33
- def friend_ids(*args)
34
- options = {count: 5000, cursor: -1}.merge(args.extract_options!)
35
- args[0] = verify_credentials(skip_status: true).id if args.empty?
36
- fetch_cache_or_call_api(__method__, args[0], options) {
37
- collect_with_cursor("old_#{__method__}", *args, options)
38
- }
39
- end
40
-
41
- def follower_ids(*args)
42
- options = {count: 5000, cursor: -1}.merge(args.extract_options!)
43
- args[0] = verify_credentials(skip_status: true).id if args.empty?
44
- fetch_cache_or_call_api(__method__, args[0], options) {
45
- collect_with_cursor("old_#{__method__}", *args, options)
46
- }
47
- end
48
-
49
- # specify reduce: false to use tweet for inactive_*
50
- def friends(*args)
51
- options = {count: 200, include_user_entities: true, cursor: -1}.merge(args.extract_options!)
52
- options[:reduce] = false unless options.has_key?(:reduce)
53
- args[0] = verify_credentials(skip_status: true).id if args.empty?
54
- fetch_cache_or_call_api(__method__, args[0], options) {
55
- collect_with_cursor("old_#{__method__}", *args, options)
56
- }
57
- end
58
-
59
- # specify reduce: false to use tweet for inactive_*
60
- def followers(*args)
61
- options = {count: 200, include_user_entities: true, cursor: -1}.merge(args.extract_options!)
62
- options[:reduce] = false unless options.has_key?(:reduce)
63
- args[0] = verify_credentials(skip_status: true).id if args.empty?
64
- fetch_cache_or_call_api(__method__, args[0], options) {
65
- collect_with_cursor("old_#{__method__}", *args, options)
66
- }
67
- end
68
-
69
- # use compact, not use sort and uniq
70
- # specify reduce: false to use tweet for inactive_*
71
- # TODO Perhaps `old_users` automatically merges result...
72
- def users(*args)
73
- options = args.extract_options!
74
- options[:reduce] = false
75
- users_per_workers = args.first.compact.each_slice(100).to_a
76
- processed_users = []
77
-
78
- Parallel.each_with_index(users_per_workers, in_threads: [users_per_workers.size, 10].min) do |users_per_worker, i|
79
- _users = fetch_cache_or_call_api(__method__, users_per_worker, options) {
80
- call_old_method("old_#{__method__}", users_per_worker, options)
81
- }
82
-
83
- processed_users << {i: i, users: _users}
84
- end
85
-
86
- processed_users.sort_by{|p| p[:i] }.map{|p| p[:users] }.flatten.compact
87
- end
88
-
89
- def home_timeline(*args)
90
- options = {count: 200, include_rts: true, call_limit: 3}.merge(args.extract_options!)
91
- fetch_cache_or_call_api(__method__, user.id, options) {
92
- collect_with_max_id("old_#{__method__}", options)
93
- }
94
- end
95
-
96
- def user_timeline(*args)
97
- options = {count: 200, include_rts: true, call_limit: 3}.merge(args.extract_options!)
98
- args[0] = verify_credentials(skip_status: true).id if args.empty?
99
- fetch_cache_or_call_api(__method__, args[0], options) {
100
- collect_with_max_id("old_#{__method__}", *args, options)
101
- }
102
- end
103
-
104
- def mentions_timeline(*args)
105
- options = {count: 200, include_rts: true, call_limit: 1}.merge(args.extract_options!)
106
- fetch_cache_or_call_api(__method__, user.id, options) {
107
- collect_with_max_id("old_#{__method__}", options)
108
- }
109
- end
110
-
111
- def favorites(*args)
112
- options = {count: 100, call_count: 1}.merge(args.extract_options!)
113
- args[0] = verify_credentials(skip_status: true).id if args.empty?
114
- fetch_cache_or_call_api(__method__, args[0], options) {
115
- collect_with_max_id("old_#{__method__}", *args, options)
116
- }
117
- end
118
-
119
- def search(*args)
120
- options = {count: 100, result_type: :recent, call_limit: 1}.merge(args.extract_options!)
121
- options[:reduce] = false
122
- fetch_cache_or_call_api(__method__, args[0], options) {
123
- collect_with_max_id("old_#{__method__}", *args, options) { |response| response.attrs[:statuses] }
124
- }
125
- end
126
- end
127
- end