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
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: c5ac0a96a8ef591cb272f5760baf4e5b931638bd
4
- data.tar.gz: e1172d7a72e4792347f761f34dd9d516e13f5a23
3
+ metadata.gz: 4c889d340f03d3c1ef475e99fb2ca4860a809312
4
+ data.tar.gz: 216b8dd6601a1417778a6e6fa70af3b8b5c97493
5
5
  SHA512:
6
- metadata.gz: d5b734f49a0d8c625a113a52373399ca91d45d77582a5ad1f4966e320225ffc161d2f59ee3d615c0d46ca318666a536ce62ac96cdddd52e8da1ab118c8954c17
7
- data.tar.gz: 5bfe0d7c34fff554f3253b8c2144065f7a83def1c6a403a43134146e8f215b14e97c1c33b1ad36867f521ae7f005cc2d5c03f896346e879f55d2a224654226b7
6
+ metadata.gz: 53a6aae09286af007d3e5dd857b42263f3173ee4da5ce9b1b8392a16a6da82db6b4f265612d92a3b0b98406d4ef85ad59122eee90aed0284a091a9f335d78509
7
+ data.tar.gz: 3ad068e546f28b445f83654e7603f053a96e4d3c192aec2365d6ccdafff2ffd87414caa7a72edef75b5b3371bef69589f97771614902871ef257f867ae6a4868
data/README.md CHANGED
@@ -1,8 +1,8 @@
1
1
  twitter-with-auto-pagination
2
2
  ============================
3
3
 
4
- [![Gem Version](https://badge.fury.io/rb/ex_twitter.png)](https://badge.fury.io/rb/twitter_with_auto_pagination)
5
- [![Build Status](https://travis-ci.org/ts-3156/ex-twitter.svg?branch=master)](https://travis-ci.org/ts-3156/twitter-with-auto-pagination)
4
+ [![Gem Version](https://badge.fury.io/rb/twitter_with_auto_pagination.png)](https://badge.fury.io/rb/twitter_with_auto_pagination)
5
+ [![Build Status](https://travis-ci.org/ts-3156/twitter-with-auto-pagination.svg?branch=master)](https://travis-ci.org/ts-3156/twitter-with-auto-pagination)
6
6
 
7
7
  Add auto pagination, auto caching and parallelly fetching features to Twitter gem.
8
8
 
@@ -26,16 +26,14 @@ Add `twitter_with_auto_pagination` to your Gemfile, and bundle.
26
26
 
27
27
  ## Configuration
28
28
 
29
- You can pass configuration options as a block to `TwitterWithAutoPagination::Client.new` just like `Twitter::REST::Client.new`.
29
+ You can pass configuration options as a block to `Twitter::REST::Client.new` just like Twitter gem.
30
30
 
31
31
  ```
32
- client = TwitterWithAutoPagination::Client.new do |config|
32
+ client = Twitter::REST::Client.new do |config|
33
33
  config.consumer_key = "YOUR_CONSUMER_KEY"
34
34
  config.consumer_secret = "YOUR_CONSUMER_SECRET"
35
35
  config.access_token = "YOUR_ACCESS_TOKEN"
36
36
  config.access_token_secret = "YOUR_ACCESS_SECRET"
37
- config.log_level = :debug # optional
38
- config.logger = Logger.new(STDOUT) # optional
39
37
  end
40
38
  ```
41
39
 
@@ -135,25 +133,17 @@ client.close_friends
135
133
  ```
136
134
 
137
135
  ```
138
- client.users_which_you_removed(pre_me, cur_me)
136
+ client.removing(past_me, cur_me)
139
137
  ```
140
138
 
141
139
  ```
142
- client.users_who_removed_you(pre_me, cur_me)
140
+ client.removed(past_me, cur_me)
143
141
  ```
144
142
 
145
143
  ```
146
- client.users_which_you_replied_to
144
+ client.replying
147
145
  ```
148
146
 
149
147
  ```
150
- client.users_who_replied_to_you
151
- ```
152
-
153
- ```
154
- client.users_which_you_faved
155
- ```
156
-
157
- ```
158
- client.users_who_faved_you
148
+ client.replied
159
149
  ```
@@ -1,4 +1,45 @@
1
- require 'twitter_with_auto_pagination/client'
1
+ require 'twitter'
2
+
3
+ require 'twitter_with_auto_pagination/log_subscriber'
2
4
 
3
5
  module TwitterWithAutoPagination
6
+ end
7
+
8
+ require 'twitter_with_auto_pagination/rest/api'
9
+
10
+ module Twitter
11
+ module REST
12
+ class Client
13
+ prepend TwitterWithAutoPagination::REST::API
14
+
15
+ def initialize(options = {})
16
+ @cache = ActiveSupport::Cache::FileStore.new(File.join('tmp', 'api_cache'))
17
+ @call_count = 0
18
+
19
+ @uid = options.has_key?(:uid) ? options.delete(:uid).to_i : nil
20
+ @screen_name = options.has_key?(:screen_name) ? options.delete(:screen_name).to_s : nil
21
+
22
+ logger =
23
+ if options.has_key?(:logger)
24
+ options.delete(:logger)
25
+ else
26
+ Dir.mkdir('log') unless File.exists?('log')
27
+ Logger.new('log/twitter_with_auto_pagination.log')
28
+ end
29
+ logger.level = options.has_key?(:log_level) ? options.delete(:log_level) : :debug
30
+ @@logger = @logger = logger
31
+
32
+ super
33
+ end
34
+
35
+ def self.logger
36
+ @@logger
37
+ end
38
+
39
+ attr_accessor :call_count, :logger
40
+ attr_reader :cache
41
+
42
+ INDENT = 4
43
+ end
44
+ end
4
45
  end
@@ -38,7 +38,7 @@ module TwitterWithAutoPagination
38
38
  # sql = color(sql, sql_color(sql), true)
39
39
 
40
40
  key = payload.delete(:key)
41
- debug { "#{name} #{key} #{(payload.inspect)}" }
41
+ debug { "#{name}#{key.nil? ? '' : " #{key}"} #{(payload.inspect)}" }
42
42
  end
43
43
 
44
44
  private
@@ -73,7 +73,7 @@ module TwitterWithAutoPagination
73
73
  end
74
74
 
75
75
  def logger
76
- TwitterWithAutoPagination::Client.logger
76
+ Twitter::REST::Client.logger
77
77
  end
78
78
  end
79
79
  end
@@ -0,0 +1,31 @@
1
+ require 'twitter_with_auto_pagination/rest/favorites'
2
+ require 'twitter_with_auto_pagination/rest/friends_and_followers'
3
+ require 'twitter_with_auto_pagination/rest/search'
4
+ require 'twitter_with_auto_pagination/rest/timelines'
5
+ require 'twitter_with_auto_pagination/rest/users'
6
+ require 'twitter_with_auto_pagination/rest/uncategorized'
7
+
8
+ require 'twitter_with_auto_pagination/rest/extension/clusters'
9
+ require 'twitter_with_auto_pagination/rest/extension/favoriting'
10
+ require 'twitter_with_auto_pagination/rest/extension/friends_and_followers'
11
+ require 'twitter_with_auto_pagination/rest/extension/replying'
12
+ require 'twitter_with_auto_pagination/rest/extension/unfollowing'
13
+
14
+ module TwitterWithAutoPagination
15
+ module REST
16
+ module API
17
+ include TwitterWithAutoPagination::REST::Favorites
18
+ include TwitterWithAutoPagination::REST::FriendsAndFollowers
19
+ include TwitterWithAutoPagination::REST::Search
20
+ include TwitterWithAutoPagination::REST::Timelines
21
+ include TwitterWithAutoPagination::REST::Users
22
+ include TwitterWithAutoPagination::REST::Uncategorized
23
+
24
+ include TwitterWithAutoPagination::REST::Extension::Clusters
25
+ include TwitterWithAutoPagination::REST::Extension::Favoriting
26
+ include TwitterWithAutoPagination::REST::Extension::FriendsAndFollowers
27
+ include TwitterWithAutoPagination::REST::Extension::Replying
28
+ include TwitterWithAutoPagination::REST::Extension::Unfollowing
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,43 @@
1
+ require 'twitter_with_auto_pagination/rest/utils'
2
+
3
+ module TwitterWithAutoPagination
4
+ module REST
5
+ module Extension
6
+ module Clusters
7
+ include TwitterWithAutoPagination::REST::Utils
8
+
9
+ def clusters_belong_to(text)
10
+ return [] if text.blank?
11
+
12
+ exclude_words = JSON.parse(File.read(Rails.configuration.x.constants['cluster_bad_words_path']))
13
+ special_words = JSON.parse(File.read(Rails.configuration.x.constants['cluster_good_words_path']))
14
+
15
+ # クラスタ用の単語の出現回数を記録
16
+ cluster_word_counter =
17
+ special_words.map { |sw| [sw, text.scan(sw)] }
18
+ .delete_if { |item| item[1].empty? }
19
+ .each_with_object(Hash.new(1)) { |item, memo| memo[item[0]] = item[1].size }
20
+
21
+ # 同一文字種の繰り返しを見付ける。漢字の繰り返し、ひらがなの繰り返し、カタカナの繰り返し、など
22
+ text.scan(/[一-龠〆ヵヶ々]+|[ぁ-んー~]+|[ァ-ヴー~]+|[a-zA-Z0-9]+|[、。!!??]+/).
23
+
24
+ # 複数回繰り返される文字を除去
25
+ map { |w| w.remove /[?!?!。、w]|(ー{2,})/ }.
26
+
27
+ # 文字数の少なすぎる単語、ひらがなだけの単語、除外単語を除去する
28
+ delete_if { |w| w.length <= 1 || (w.length <= 2 && w =~ /^[ぁ-んー~]+$/) || exclude_words.include?(w) }.
29
+
30
+ # 出現回数を記録
31
+ each { |w| cluster_word_counter[w] += 1 }
32
+
33
+ # 複数個以上見付かった単語のみを残し、出現頻度順にソート
34
+ cluster_word_counter.select { |_, v| v > 3 }.sort_by { |_, v| -v }.to_h
35
+ end
36
+
37
+ def clusters_assigned_to
38
+ raise NotImplementedError
39
+ end
40
+ end
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,106 @@
1
+ require 'twitter_with_auto_pagination/rest/utils'
2
+
3
+ module TwitterWithAutoPagination
4
+ module REST
5
+ module Extension
6
+ module Favoriting
7
+ include TwitterWithAutoPagination::REST::Utils
8
+
9
+ def _count_users_with_two_sided_threshold(users, options)
10
+ min = options.has_key?(:min) ? options[:min] : 0
11
+ max = options.has_key?(:max) ? options[:max] : 1000
12
+ users.each_with_object(Hash.new(0)) { |u, memo| memo[u.id] += 1 }.
13
+ select { |_k, v| min <= v && v <= max }.
14
+ sort_by { |_, v| -v }.to_h
15
+ end
16
+
17
+ def _extract_favorite_users(favs, options = {})
18
+ counted_value = _count_users_with_two_sided_threshold(favs.map { |t| t.user }, options)
19
+ counted_value.map do |uid, cnt|
20
+ fav = favs.find { |f| f.user.id.to_i == uid.to_i }
21
+ Array.new(cnt, fav.user)
22
+ end.flatten
23
+ end
24
+
25
+ def _retrieve_favs(*args)
26
+ options = args.extract_options!
27
+ if args.empty?
28
+ favorites(options)
29
+ elsif uid_or_screen_name?(args[0])
30
+ favorites(args[0], options)
31
+ elsif args[0].kind_of?(Array) && args[0].all? { |t| t.respond_to?(:text) }
32
+ args[0]
33
+ else
34
+ raise ArgumentError
35
+ end
36
+ end
37
+
38
+ def users_which_you_faved(*args)
39
+ options = args.extract_options!
40
+ instrument(__method__, nil, options) do
41
+ favs = _retrieve_favs(*args, options)
42
+ result = _extract_favorite_users(favs, options)
43
+ if options.has_key?(:uniq) && !options[:uniq]
44
+ result
45
+ else
46
+ result.uniq { |r| r.id }
47
+ end
48
+ end
49
+ rescue => e
50
+ logger.warn "#{__method__} #{user.inspect} #{e.class} #{e.message}"
51
+ raise e
52
+ end
53
+
54
+ alias favoriting users_which_you_faved
55
+
56
+ def users_who_faved_you(*args)
57
+ raise NotImplementedError
58
+ end
59
+
60
+ alias favorited users_who_faved_you
61
+
62
+ def _retrieve_replying_replied_and_favoriting(*args)
63
+ names = %i(replying replied favoriting)
64
+ options = args.extract_options!
65
+ if args.empty?
66
+ _fetch_parallelly(names.map { |n| {method: n, args: [options]} })
67
+ elsif uid_or_screen_name?(args[0])
68
+ _fetch_parallelly(names.map { |n| {method: n, args: [args[0], options]} })
69
+ elsif names.all? { |n| args[0].respond_to?(n) }
70
+ names.map { |n| args[0].send(n) }
71
+ else
72
+ raise ArgumentError
73
+ end
74
+ end
75
+
76
+ def close_friends(*args)
77
+ options = {uniq: false}.merge(args.extract_options!)
78
+ min_max = {
79
+ min: options.has_key?(:min) ? options.delete(:min) : 0,
80
+ max: options.has_key?(:max) ? options.delete(:max) : 1000
81
+ }
82
+
83
+ instrument(__method__, nil, options) do
84
+ replying, replied, favoriting = _retrieve_replying_replied_and_favoriting(*args, options)
85
+
86
+ users = replying + replied + favoriting
87
+ return [] if users.empty?
88
+
89
+ score = _count_users_with_two_sided_threshold(users, min_max)
90
+ replying_score = _count_users_with_two_sided_threshold(replying, min_max)
91
+ replied_score = _count_users_with_two_sided_threshold(replied, min_max)
92
+ favoriting_score = _count_users_with_two_sided_threshold(favoriting, min_max)
93
+
94
+ score.keys.map { |uid| users.find { |u| u.id.to_i == uid.to_i } }.map do |u|
95
+ u[:score] = score[u.id]
96
+ u[:replying_score] = replying_score[u.id]
97
+ u[:replied_score] = replied_score[u.id]
98
+ u[:favoriting_score] = favoriting_score[u.id]
99
+ u
100
+ end
101
+ end
102
+ end
103
+ end
104
+ end
105
+ end
106
+ end
@@ -0,0 +1,131 @@
1
+ require 'twitter_with_auto_pagination/rest/utils'
2
+ require 'parallel'
3
+
4
+ module TwitterWithAutoPagination
5
+ module REST
6
+ module Extension
7
+ module FriendsAndFollowers
8
+ include TwitterWithAutoPagination::REST::Utils
9
+
10
+ def _fetch_parallelly(signatures) # [{method: :friends, args: ['ts_3156', ...], {...}]
11
+ result = Array.new(signatures.size)
12
+
13
+ Parallel.each_with_index(signatures, in_threads: result.size) do |signature, i|
14
+ result[i] = send(signature[:method], *signature[:args])
15
+ end
16
+
17
+ result
18
+ end
19
+
20
+ def friends_and_followers(*args)
21
+ _fetch_parallelly(
22
+ [
23
+ {method: :friends, args: args},
24
+ {method: :followers, args: args}])
25
+ end
26
+
27
+ def friends_followers_and_statuses(*args)
28
+ _fetch_parallelly(
29
+ [
30
+ {method: :friends, args: args},
31
+ {method: :followers, args: args},
32
+ {method: :user_timeline, args: args}])
33
+ end
34
+
35
+ def _retrieve_friends_and_followers(*args)
36
+ obj = args[0]
37
+ if obj.nil?
38
+ friends_and_followers
39
+ elsif uid_or_screen_name?(obj)
40
+ friends_and_followers(obj)
41
+ elsif obj.respond_to?(:friends) && obj.respond_to?(:followers)
42
+ [obj.friends, obj.followers]
43
+ else
44
+ raise ArgumentError, args.inspect
45
+ end
46
+ end
47
+
48
+ def one_sided_friends(me = nil)
49
+ instrument(__method__, nil) do
50
+ _friends, _followers = _retrieve_friends_and_followers(me)
51
+ _friends.to_a - _followers.to_a
52
+ end
53
+ end
54
+
55
+ def one_sided_followers(me = nil)
56
+ instrument(__method__, nil) do
57
+ _friends, _followers = _retrieve_friends_and_followers(me)
58
+ _followers.to_a - _friends.to_a
59
+ end
60
+ end
61
+
62
+ def mutual_friends(me = nil)
63
+ instrument(__method__, nil) do
64
+ _friends, _followers = _retrieve_friends_and_followers(me)
65
+ _friends.to_a & _followers.to_a
66
+ end
67
+ end
68
+
69
+ def _retrieve_friends(*args)
70
+ if args.size == 1
71
+ args[0].nil? ? friends : friends(args[0])
72
+ elsif args.all? { |obj| uid_or_screen_name?(obj) }
73
+ _fetch_parallelly(args.map { |obj| {method: :friends, args: [obj]} })
74
+ elsif args.all? { |obj| obj.respond_to?(:friends) }
75
+ args.map { |obj| obj.friends }
76
+ else
77
+ raise ArgumentError, args.inspect
78
+ end
79
+ end
80
+
81
+ def common_friends(me, you)
82
+ instrument(__method__, nil) do
83
+ my_friends, your_friends = _retrieve_friends(me, you)
84
+ my_friends.to_a & your_friends.to_a
85
+ end
86
+ end
87
+
88
+ def _retrieve_followers(*args)
89
+ if args.size == 1
90
+ args[0].nil? ? followers : followers(args[0])
91
+ elsif args.all? { |obj| uid_or_screen_name?(obj) }
92
+ _fetch_parallelly(args.map { |obj| {method: :followers, args: [obj]} })
93
+ elsif args.all? { |obj| obj.respond_to?(:followers) }
94
+ args.map { |obj| obj.followers }
95
+ else
96
+ raise ArgumentError, args.inspect
97
+ end
98
+ end
99
+
100
+ def common_followers(me, you)
101
+ instrument(__method__, nil) do
102
+ my_followers, your_followers = _retrieve_followers(me, you)
103
+ my_followers.to_a & your_followers.to_a
104
+ end
105
+ end
106
+
107
+ def _extract_inactive_users(users)
108
+ two_weeks_ago = 2.weeks.ago.to_i
109
+ users.select do |u|
110
+ (Time.parse(u.status.created_at).to_i < two_weeks_ago) rescue false
111
+ end
112
+ end
113
+
114
+ def inactive_friends(user = nil)
115
+ instrument(__method__, nil) do
116
+ _friends = _retrieve_friends(user)
117
+ _extract_inactive_users(_friends)
118
+ end
119
+ end
120
+
121
+ def inactive_followers(user = nil)
122
+ instrument(__method__, nil) do
123
+ _followers = _retrieve_followers(user)
124
+ _extract_inactive_users(_followers)
125
+ end
126
+ end
127
+ end
128
+ end
129
+ end
130
+ end
131
+