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
@@ -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