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
@@ -1,337 +0,0 @@
1
- module TwitterWithAutoPagination
2
- module NewApi
3
- def friends_parallelly(*args)
4
- options = {super_operation: __method__}.merge(args.extract_options!)
5
- _friend_ids = friend_ids(*(args + [options]))
6
- users(_friend_ids.map { |id| id.to_i }, options)
7
- end
8
-
9
- def followers_parallelly(*args)
10
- options = {super_operation: __method__}.merge(args.extract_options!)
11
- _follower_ids = follower_ids(*(args + [options]))
12
- users(_follower_ids.map { |id| id.to_i }, options)
13
- end
14
-
15
- def _fetch_parallelly(signatures) # [{method: :friends, args: ['ts_3156', ...], {...}]
16
- result = Array.new(signatures.size)
17
-
18
- Parallel.each_with_index(signatures, in_threads: result.size) do |signature, i|
19
- result[i] = send(signature[:method], *signature[:args])
20
- end
21
-
22
- result
23
- end
24
-
25
- def friends_and_followers(*args)
26
- _fetch_parallelly(
27
- [
28
- {method: :friends_parallelly, args: args},
29
- {method: :followers_parallelly, args: args}])
30
- end
31
-
32
- def friends_followers_and_statuses(*args)
33
- _fetch_parallelly(
34
- [
35
- {method: :friends_parallelly, args: args},
36
- {method: :followers_parallelly, args: args},
37
- {method: :user_timeline, args: args}])
38
- end
39
-
40
- def one_sided_friends(me = nil)
41
- if me.nil?
42
- friends_parallelly.to_a - followers_parallelly.to_a
43
- elsif uid_or_screen_name?(me)
44
- # TODO use friends_and_followers
45
- friends_parallelly(me).to_a - followers_parallelly(me).to_a
46
- elsif me.respond_to?(:friends) && me.respond_to?(:followers)
47
- me.friends.to_a - me.followers.to_a
48
- else
49
- raise
50
- end
51
- end
52
-
53
- def one_sided_followers(me = nil)
54
- if me.nil?
55
- followers_parallelly.to_a - friends_parallelly.to_a
56
- elsif uid_or_screen_name?(me)
57
- # TODO use friends_and_followers
58
- followers_parallelly(me).to_a - friends_parallelly(me).to_a
59
- elsif me.respond_to?(:friends) && me.respond_to?(:followers)
60
- me.followers.to_a - me.friends.to_a
61
- else
62
- raise
63
- end
64
- end
65
-
66
- def mutual_friends(me = nil)
67
- if me.nil?
68
- friends_parallelly.to_a & followers_parallelly.to_a
69
- elsif uid_or_screen_name?(me)
70
- # TODO use friends_and_followers
71
- friends_parallelly(me).to_a & followers_parallelly(me).to_a
72
- elsif me.respond_to?(:friends) && me.respond_to?(:followers)
73
- me.friends.to_a & me.followers.to_a
74
- else
75
- raise
76
- end
77
- end
78
-
79
- def common_friends(me, you)
80
- if uid_or_screen_name?(me) && uid_or_screen_name?(you)
81
- friends_parallelly(me).to_a & friends_parallelly(you).to_a
82
- elsif me.respond_to?(:friends) && you.respond_to?(:friends)
83
- me.friends.to_a & you.friends.to_a
84
- else
85
- raise
86
- end
87
- end
88
-
89
- def common_followers(me, you)
90
- if uid_or_screen_name?(me) && uid_or_screen_name?(you)
91
- followers_parallelly(me).to_a & followers_parallelly(you).to_a
92
- elsif me.respond_to?(:followers) && you.respond_to?(:followers)
93
- me.followers.to_a & you.followers.to_a
94
- else
95
- raise
96
- end
97
- end
98
-
99
- def users_which_you_removed(pre_me, cur_me)
100
- if uid_or_screen_name?(pre_me) && uid_or_screen_name?(cur_me)
101
- friends_parallelly(pre_me).to_a - friends_parallelly(cur_me).to_a
102
- elsif pre_me.respond_to?(:friends) && cur_me.respond_to?(:friends)
103
- pre_me.friends.to_a - cur_me.friends.to_a
104
- else
105
- raise
106
- end
107
- end
108
-
109
- def users_who_removed_you(pre_me, cur_me)
110
- if uid_or_screen_name?(pre_me) && uid_or_screen_name?(cur_me)
111
- followers_parallelly(pre_me).to_a - followers_parallelly(cur_me).to_a
112
- elsif pre_me.respond_to?(:followers) && cur_me.respond_to?(:followers)
113
- pre_me.followers.to_a - cur_me.followers.to_a
114
- else
115
- raise
116
- end
117
- end
118
-
119
- def _extract_screen_names(tweets)
120
- tweets.map do |t|
121
- $1 if t.text =~ /^(?:\.)?@(\w+)( |\W)/ # include statuses starts with .
122
- end.compact
123
- end
124
-
125
- # users which specified user is replying
126
- # in_reply_to_user_id and in_reply_to_status_id is not used because of distinguishing mentions from replies
127
- def users_which_you_replied_to(*args)
128
- options = args.extract_options!
129
- tweets =
130
- if args.empty?
131
- user_timeline(options)
132
- elsif uid_or_screen_name?(args[0])
133
- user_timeline(args[0], options)
134
- elsif args[0].kind_of?(Array) && args[0].all? { |t| t.respond_to?(:text) }
135
- args[0]
136
- else
137
- raise
138
- end
139
-
140
- screen_names = _extract_screen_names(tweets)
141
- result = users(screen_names, {super_operation: __method__}.merge(options))
142
- if options.has_key?(:uniq) && !options[:uniq]
143
- screen_names.map { |sn| result.find { |r| r.screen_name == sn } }.compact
144
- else
145
- result.uniq { |r| r.id }
146
- end
147
- rescue Twitter::Error::NotFound => e
148
- e.message == 'No user matches for specified terms.' ? [] : (raise e)
149
- rescue => e
150
- logger.warn "#{__method__} #{args.inspect} #{e.class} #{e.message}"
151
- raise e
152
- end
153
-
154
- def _extract_uids(tweets)
155
- tweets.map do |t|
156
- t.user.id.to_i if t.text =~ /^(?:\.)?@(\w+)( |\W)/ # include statuses starts with .
157
- end.compact
158
- end
159
-
160
- def _extract_users(tweets, uids)
161
- uids.map { |uid| tweets.find { |t| t.user.id.to_i == uid.to_i } }.map { |t| t.user }.compact
162
- end
163
-
164
- # users which specified user is replied
165
- # when user is login you had better to call mentions_timeline
166
- def users_who_replied_to_you(*args)
167
- options = args.extract_options!
168
-
169
- result =
170
- if args.empty? || (uid_or_screen_name?(args[0]) && authenticating_user?(args[0]))
171
- mentions_timeline.map { |m| m.user }
172
- else
173
- searched_result = search('@' + user(args[0]).screen_name, options)
174
- uids = _extract_uids(searched_result)
175
- _extract_users(searched_result, uids)
176
- end
177
-
178
- if options.has_key?(:uniq) && !options[:uniq]
179
- result
180
- else
181
- result.uniq { |r| r.id }
182
- end
183
- end
184
-
185
- def _count_users_with_two_sided_threshold(users, options)
186
- min = options.has_key?(:min) ? options[:min] : 0
187
- max = options.has_key?(:max) ? options[:max] : 1000
188
- users.each_with_object(Hash.new(0)) { |u, memo| memo[u.id] += 1 }.
189
- select { |_k, v| min <= v && v <= max }.
190
- sort_by { |_, v| -v }.to_h
191
- end
192
-
193
- def _extract_favorite_users(favs, options = {})
194
- counted_value = _count_users_with_two_sided_threshold(favs.map { |t| t.user }, options)
195
- counted_value.map do |uid, cnt|
196
- fav = favs.find { |f| f.user.id.to_i == uid.to_i }
197
- Array.new(cnt, fav.user)
198
- end.flatten
199
- end
200
-
201
- def users_which_you_faved(*args)
202
- options = args.extract_options!
203
-
204
- favs =
205
- if args.empty?
206
- favorites(options)
207
- elsif uid_or_screen_name?(args[0])
208
- favorites(args[0], options)
209
- elsif args[0].kind_of?(Array) && args[0].all? { |t| t.respond_to?(:text) }
210
- args[0]
211
- else
212
- raise
213
- end
214
-
215
- result = _extract_favorite_users(favs, options)
216
- if options.has_key?(:uniq) && !options[:uniq]
217
- result
218
- else
219
- result.uniq { |r| r.id }
220
- end
221
- rescue => e
222
- logger.warn "#{__method__} #{user.inspect} #{e.class} #{e.message}"
223
- raise e
224
- end
225
-
226
- def users_who_faved_you(*args)
227
- end
228
-
229
- def _extract_inactive_users(users, options = {})
230
- authorized = options.delete(:authorized)
231
- two_weeks_ago = 2.weeks.ago.to_i
232
- users.select do |u|
233
- if authorized
234
- (Time.parse(u.status.created_at).to_i < two_weeks_ago) rescue false
235
- else
236
- false
237
- end
238
- end
239
- end
240
-
241
- def close_friends(*args)
242
- options = {uniq: false}.merge(args.extract_options!)
243
- min_max = {
244
- min: options.has_key?(:min) ? options.delete(:min) : 0,
245
- max: options.has_key?(:max) ? options.delete(:max) : 1000
246
- }
247
-
248
- _users_which_you_replied_to, _users_who_replied_to_you, _users_which_you_faved =
249
- if args.empty?
250
- [users_which_you_replied_to(options), users_who_replied_to_you(options), users_which_you_faved(options)]
251
- elsif uid_or_screen_name?(args[0])
252
- [users_which_you_replied_to(args[0], options), users_who_replied_to_you(args[0], options), users_which_you_faved(args[0], options)]
253
- elsif (m_names = %i(users_which_you_replied_to users_who_replied_to_you users_which_you_faved)).all? { |m_name| args[0].respond_to?(m_name) }
254
- m_names.map { |mn| args[0].send(mn) }
255
- else
256
- raise
257
- end
258
-
259
- _users = _users_which_you_replied_to + _users_who_replied_to_you + _users_which_you_faved
260
- return [] if _users.empty?
261
-
262
- scores = _count_users_with_two_sided_threshold(_users, min_max)
263
- users_which_you_replied_to_scores = _count_users_with_two_sided_threshold(_users_which_you_replied_to, min_max)
264
- users_who_replied_to_you_scores = _count_users_with_two_sided_threshold(_users_who_replied_to_you, min_max)
265
- users_which_you_faved_scores = _count_users_with_two_sided_threshold(_users_which_you_faved, min_max)
266
-
267
- scores.keys.map { |uid| _users.find { |u| u.id.to_i == uid.to_i } }.
268
- map do |u|
269
- u[:score] = scores[u.id]
270
- u[:users_which_you_replied_to_score] = users_which_you_replied_to_scores[u.id]
271
- u[:users_who_replied_to_you_score] = users_who_replied_to_you_scores[u.id]
272
- u[:users_which_you_faved_score] = users_which_you_faved_scores[u.id]
273
- u
274
- end
275
- end
276
-
277
- def inactive_friends(user = nil)
278
- if user.blank?
279
- _extract_inactive_users(friends_parallelly, authorized: true)
280
- elsif uid_or_screen_name?(user)
281
- authorized = authenticating_user?(user) || authorized_user?(user)
282
- _extract_inactive_users(friends_parallelly(user), authorized: authorized)
283
- elsif user.respond_to?(:friends)
284
- authorized = authenticating_user?(user.uid.to_i) || authorized_user?(user.uid.to_i)
285
- _extract_inactive_users(user.friends, authorized: authorized)
286
- else
287
- raise
288
- end
289
- end
290
-
291
- def inactive_followers(user = nil)
292
- if user.blank?
293
- _extract_inactive_users(followers_parallelly, authorized: true)
294
- elsif uid_or_screen_name?(user)
295
- authorized = authenticating_user?(user) || authorized_user?(user)
296
- _extract_inactive_users(followers_parallelly(user), authorized: authorized)
297
- elsif user.respond_to?(:followers)
298
- authorized = authenticating_user?(user.uid.to_i) || authorized_user?(user.uid.to_i)
299
- _extract_inactive_users(user.followers, authorized: authorized)
300
- else
301
- raise
302
- end
303
- end
304
-
305
- def clusters_belong_to(text)
306
- return [] if text.blank?
307
-
308
- exclude_words = JSON.parse(File.read(Rails.configuration.x.constants['cluster_bad_words_path']))
309
- special_words = JSON.parse(File.read(Rails.configuration.x.constants['cluster_good_words_path']))
310
-
311
- # クラスタ用の単語の出現回数を記録
312
- cluster_word_counter =
313
- special_words.map { |sw| [sw, text.scan(sw)] }
314
- .delete_if { |item| item[1].empty? }
315
- .each_with_object(Hash.new(1)) { |item, memo| memo[item[0]] = item[1].size }
316
-
317
- # 同一文字種の繰り返しを見付ける。漢字の繰り返し、ひらがなの繰り返し、カタカナの繰り返し、など
318
- text.scan(/[一-龠〆ヵヶ々]+|[ぁ-んー~]+|[ァ-ヴー~]+|[a-zA-Z0-9]+|[、。!!??]+/).
319
-
320
- # 複数回繰り返される文字を除去
321
- map { |w| w.remove /[?!?!。、w]|(ー{2,})/ }.
322
-
323
- # 文字数の少なすぎる単語、ひらがなだけの単語、除外単語を除去する
324
- delete_if { |w| w.length <= 1 || (w.length <= 2 && w =~ /^[ぁ-んー~]+$/) || exclude_words.include?(w) }.
325
-
326
- # 出現回数を記録
327
- each { |w| cluster_word_counter[w] += 1 }
328
-
329
- # 複数個以上見付かった単語のみを残し、出現頻度順にソート
330
- cluster_word_counter.select { |_, v| v > 3 }.sort_by { |_, v| -v }.to_h
331
- end
332
-
333
- def clusters_assigned_to
334
- raise NotImplementedError.new
335
- end
336
- end
337
- end
@@ -1,303 +0,0 @@
1
- module TwitterWithAutoPagination
2
- module Utils
3
- # for backward compatibility
4
- def uid
5
- @uid || user.id.to_i
6
- end
7
-
8
- def __uid
9
- ActiveSupport::Deprecation.warn(<<-MESSAGE.strip_heredoc)
10
- `TwitterWithAutoPagination::Utils##{__method__}` is deprecated.
11
- MESSAGE
12
- uid
13
- end
14
-
15
- def __uid_i
16
- ActiveSupport::Deprecation.warn(<<-MESSAGE.strip_heredoc)
17
- `TwitterWithAutoPagination::Utils##{__method__}` is deprecated.
18
- MESSAGE
19
- uid
20
- end
21
-
22
- # for backward compatibility
23
- def screen_name
24
- @screen_name || user.screen_name
25
- end
26
-
27
- def __screen_name
28
- ActiveSupport::Deprecation.warn(<<-MESSAGE.strip_heredoc)
29
- `TwitterWithAutoPagination::Utils##{__method__}` is deprecated.
30
- MESSAGE
31
- screen_name
32
- end
33
-
34
- def uid_or_screen_name?(object)
35
- object.kind_of?(String) || object.kind_of?(Integer)
36
- end
37
-
38
- def authenticating_user?(target)
39
- user.id.to_i == user(target).id.to_i
40
- end
41
-
42
- def authorized_user?(target)
43
- target_user = user(target)
44
- !target_user.protected? || friendship?(user.id.to_i, target_user.id.to_i)
45
- end
46
-
47
- def instrument(operation, key, options = nil)
48
- payload = {operation: operation, key: key}
49
- payload.merge!(options) if options.is_a?(Hash)
50
- ActiveSupport::Notifications.instrument('call.twitter_with_auto_pagination', payload) { yield(payload) }
51
- end
52
-
53
- def call_old_method(method_name, *args)
54
- options = args.extract_options!
55
- begin
56
- self.call_count += 1
57
- _options = {method_name: method_name, call_count: self.call_count, args: args}.merge(options)
58
- instrument('api call', args[0], _options) { send(method_name, *args, options) }
59
- rescue Twitter::Error::TooManyRequests => e
60
- logger.warn "#{__method__}: call=#{method_name} #{args.inspect} #{e.class} Retry after #{e.rate_limit.reset_in} seconds."
61
- raise e
62
- rescue Twitter::Error::ServiceUnavailable => e
63
- logger.warn "#{__method__}: call=#{method_name} #{args.inspect} #{e.class} #{e.message}"
64
- raise e
65
- rescue Twitter::Error::InternalServerError => e
66
- logger.warn "#{__method__}: call=#{method_name} #{args.inspect} #{e.class} #{e.message}"
67
- raise e
68
- rescue Twitter::Error::Forbidden => e
69
- logger.warn "#{__method__}: call=#{method_name} #{args.inspect} #{e.class} #{e.message}"
70
- raise e
71
- rescue Twitter::Error::NotFound => e
72
- logger.warn "#{__method__}: call=#{method_name} #{args.inspect} #{e.class} #{e.message}"
73
- raise e
74
- rescue => e
75
- logger.warn "#{__method__}: call=#{method_name} #{args.inspect} #{e.class} #{e.message}"
76
- raise e
77
- end
78
- end
79
-
80
- # user_timeline, search
81
- def collect_with_max_id(method_name, *args)
82
- options = args.extract_options!
83
- call_limit = options.delete(:call_limit) || 3
84
- last_response = call_old_method(method_name, *args, options)
85
- last_response = yield(last_response) if block_given?
86
- return_data = last_response
87
- call_count = 1
88
-
89
- while last_response.any? && call_count < call_limit
90
- options[:max_id] = last_response.last.kind_of?(Hash) ? last_response.last[:id] : last_response.last.id
91
- last_response = call_old_method(method_name, *args, options)
92
- last_response = yield(last_response) if block_given?
93
- return_data += last_response
94
- call_count += 1
95
- end
96
-
97
- return_data.flatten
98
- end
99
-
100
- # friends, followers
101
- def collect_with_cursor(method_name, *args)
102
- options = args.extract_options!
103
- last_response = call_old_method(method_name, *args, options).attrs
104
- return_data = (last_response[:users] || last_response[:ids])
105
-
106
- while (next_cursor = last_response[:next_cursor]) && next_cursor != 0
107
- options[:cursor] = next_cursor
108
- last_response = call_old_method(method_name, *args, options).attrs
109
- return_data += (last_response[:users] || last_response[:ids])
110
- end
111
-
112
- return_data
113
- end
114
-
115
- require 'digest/md5'
116
-
117
- def file_cache_key(method_name, user, options = {})
118
- delim = ':'
119
- identifier =
120
- case
121
- when method_name == :verify_credentials
122
- "object-id#{delim}#{object_id}"
123
- when method_name == :search
124
- "str#{delim}#{user.to_s}"
125
- when method_name == :mentions_timeline
126
- "#{user.kind_of?(Integer) ? 'id' : 'sn'}#{delim}#{user.to_s}"
127
- when method_name == :home_timeline
128
- "#{user.kind_of?(Integer) ? 'id' : 'sn'}#{delim}#{user.to_s}"
129
- when method_name.in?([:users, :replying]) && options[:super_operation].present?
130
- case
131
- when user.kind_of?(Array) && user.first.kind_of?(Integer)
132
- "#{options[:super_operation]}-ids#{delim}#{Digest::MD5.hexdigest(user.join(','))}"
133
- when user.kind_of?(Array) && user.first.kind_of?(String)
134
- "#{options[:super_operation]}-sns#{delim}#{Digest::MD5.hexdigest(user.join(','))}"
135
- else raise "#{method_name.inspect} #{user.inspect}"
136
- end
137
- when user.kind_of?(Integer)
138
- "id#{delim}#{user.to_s}"
139
- when user.kind_of?(Array) && user.first.kind_of?(Integer)
140
- "ids#{delim}#{Digest::MD5.hexdigest(user.join(','))}"
141
- when user.kind_of?(Array) && user.first.kind_of?(String)
142
- "sns#{delim}#{Digest::MD5.hexdigest(user.join(','))}"
143
- when user.kind_of?(String)
144
- "sn#{delim}#{user}"
145
- when user.kind_of?(Twitter::User)
146
- "user#{delim}#{user.id.to_s}"
147
- else raise "#{method_name.inspect} #{user.inspect}"
148
- end
149
-
150
- "#{method_name}#{delim}#{identifier}"
151
- end
152
-
153
- def namespaced_key(method_name, user, options = {})
154
- file_cache_key(method_name, user, options)
155
- end
156
-
157
- PROFILE_SAVE_KEYS = %i(
158
- id
159
- name
160
- screen_name
161
- location
162
- description
163
- url
164
- protected
165
- followers_count
166
- friends_count
167
- listed_count
168
- favourites_count
169
- utc_offset
170
- time_zone
171
- geo_enabled
172
- verified
173
- statuses_count
174
- lang
175
- status
176
- profile_image_url_https
177
- profile_banner_url
178
- profile_link_color
179
- suspended
180
- verified
181
- entities
182
- created_at
183
- )
184
-
185
- STATUS_SAVE_KEYS = %i(
186
- created_at
187
- id
188
- text
189
- source
190
- truncated
191
- coordinates
192
- place
193
- entities
194
- user
195
- contributors
196
- is_quote_status
197
- retweet_count
198
- favorite_count
199
- favorited
200
- retweeted
201
- possibly_sensitive
202
- lang
203
- )
204
-
205
- # encode
206
- def encode_json(obj, caller_name, options = {})
207
- options[:reduce] = true unless options.has_key?(:reduce)
208
- case caller_name
209
- when :user_timeline, :home_timeline, :mentions_timeline, :favorites # Twitter::Tweet
210
- JSON.pretty_generate(obj.map { |o| o.attrs })
211
-
212
- when :search # Hash
213
- data =
214
- if options[:reduce]
215
- obj.map { |o| o.to_hash.slice(*STATUS_SAVE_KEYS) }
216
- else
217
- obj.map { |o| o.to_hash }
218
- end
219
- JSON.pretty_generate(data)
220
-
221
- when :friends, :followers # Hash
222
- data =
223
- if options[:reduce]
224
- obj.map { |o| o.to_hash.slice(*PROFILE_SAVE_KEYS) }
225
- else
226
- obj.map { |o| o.to_hash }
227
- end
228
- JSON.pretty_generate(data)
229
-
230
- when :friend_ids, :follower_ids # Integer
231
- JSON.pretty_generate(obj)
232
-
233
- when :verify_credentials # Twitter::User
234
- JSON.pretty_generate(obj.to_hash.slice(*PROFILE_SAVE_KEYS))
235
-
236
- when :user # Twitter::User
237
- JSON.pretty_generate(obj.to_hash.slice(*PROFILE_SAVE_KEYS))
238
-
239
- when :users, :friends_parallelly, :followers_parallelly # Twitter::User
240
- data =
241
- if options[:reduce]
242
- obj.map { |o| o.to_hash.slice(*PROFILE_SAVE_KEYS) }
243
- else
244
- obj.map { |o| o.to_hash }
245
- end
246
- JSON.pretty_generate(data)
247
-
248
- when :user? # true or false
249
- obj
250
-
251
- when :friendship? # true or false
252
- obj
253
-
254
- else
255
- raise "#{__method__}: caller=#{caller_name} key=#{options[:key]} obj=#{obj.inspect}"
256
- end
257
- end
258
-
259
- # decode
260
- def decode_json(json_str, caller_name, options = {})
261
- obj = json_str.kind_of?(String) ? JSON.parse(json_str) : json_str
262
- case
263
- when obj.nil?
264
- obj
265
-
266
- when obj.kind_of?(Array) && obj.first.kind_of?(Hash)
267
- obj.map { |o| Hashie::Mash.new(o) }
268
-
269
- when obj.kind_of?(Array) && obj.first.kind_of?(Integer)
270
- obj
271
-
272
- when obj.kind_of?(Hash)
273
- Hashie::Mash.new(obj)
274
-
275
- when obj === true || obj === false
276
- obj
277
-
278
- when obj.kind_of?(Array) && obj.empty?
279
- obj
280
-
281
- else
282
- raise "#{__method__}: caller=#{caller_name} key=#{options[:key]} obj=#{obj.inspect}"
283
- end
284
- end
285
-
286
- def fetch_cache_or_call_api(method_name, user, options = {})
287
- key = namespaced_key(method_name, user, options)
288
- options.update(key: key)
289
-
290
- data =
291
- if options[:cache] == :read
292
- instrument('Cache Read(Force)', key, caller: method_name) { cache.read(key) }
293
- else
294
- cache.fetch(key, expires_in: 1.hour, race_condition_ttl: 5.minutes) do
295
- _d = yield
296
- instrument('serialize', key, caller: method_name) { encode_json(_d, method_name, options) }
297
- end
298
- end
299
-
300
- instrument('deserialize', key, caller: method_name) { decode_json(data, method_name, options) }
301
- end
302
- end
303
- end