twitter_with_auto_pagination 0.6.2 → 0.7.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 (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
+