twitter_with_auto_pagination 0.8.12 → 0.9.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.
- checksums.yaml +4 -4
- data/lib/twitter_with_auto_pagination/analysis/api.rb +9 -0
- data/lib/twitter_with_auto_pagination/{rest/uncategorized.rb → analysis/timelines.rb} +2 -2
- data/lib/twitter_with_auto_pagination/cache.rb +78 -0
- data/lib/twitter_with_auto_pagination/caching_and_logging.rb +80 -0
- data/lib/twitter_with_auto_pagination/client.rb +57 -0
- data/lib/twitter_with_auto_pagination/collector.rb +18 -0
- data/lib/twitter_with_auto_pagination/log_subscriber.rb +59 -26
- data/lib/twitter_with_auto_pagination/logger.rb +10 -0
- data/lib/twitter_with_auto_pagination/parallel.rb +25 -0
- data/lib/twitter_with_auto_pagination/rate_limit.rb +56 -0
- data/lib/twitter_with_auto_pagination/rest/api.rb +16 -14
- data/lib/twitter_with_auto_pagination/rest/extension/friends_and_followers.rb +0 -7
- data/lib/twitter_with_auto_pagination/rest/favorites.rb +13 -7
- data/lib/twitter_with_auto_pagination/rest/friends_and_followers.rb +33 -70
- data/lib/twitter_with_auto_pagination/rest/lists.rb +10 -9
- data/lib/twitter_with_auto_pagination/rest/search.rb +16 -6
- data/lib/twitter_with_auto_pagination/rest/timelines.rb +13 -26
- data/lib/twitter_with_auto_pagination/rest/users.rb +27 -40
- data/lib/twitter_with_auto_pagination/rest/utils.rb +29 -149
- data/lib/twitter_with_auto_pagination/serializer.rb +27 -0
- data/lib/twitter_with_auto_pagination.rb +2 -38
- data/spec/helper.rb +167 -36
- data/spec/twitter_with_auto_pagination/cache_spec.rb +71 -0
- data/spec/twitter_with_auto_pagination/client_spec.rb +7 -145
- data/spec/twitter_with_auto_pagination/parallel_spec.rb +23 -0
- data/spec/twitter_with_auto_pagination/rest/favorites_spec.rb +58 -0
- data/spec/twitter_with_auto_pagination/rest/friends_and_followers_spec.rb +161 -0
- data/spec/twitter_with_auto_pagination/rest/lists_spec.rb +39 -0
- data/spec/twitter_with_auto_pagination/rest/search_spec.rb +42 -0
- data/spec/twitter_with_auto_pagination/rest/timelines_spec.rb +82 -0
- data/spec/twitter_with_auto_pagination/rest/users_spec.rb +143 -0
- data/twitter_with_auto_pagination.gemspec +1 -1
- metadata +28 -3
@@ -5,93 +5,56 @@ module TwitterWithAutoPagination
|
|
5
5
|
module FriendsAndFollowers
|
6
6
|
include TwitterWithAutoPagination::REST::Utils
|
7
7
|
|
8
|
-
def friendship?(
|
9
|
-
|
10
|
-
instrument(__method__, nil, options) do
|
11
|
-
fetch_cache_or_call_api(__method__, args) do
|
12
|
-
call_api(method(__method__).super_method, *args, options)
|
13
|
-
end
|
14
|
-
end
|
8
|
+
def friendship?(from, to, options = {})
|
9
|
+
twitter.send(__method__, from, to, options)
|
15
10
|
end
|
16
11
|
|
17
|
-
|
18
|
-
options = {count: 5000, cursor: -1}.merge(args.extract_options!)
|
19
|
-
args[0] = verify_credentials(super_operation: __method__).id if args.empty?
|
20
|
-
instrument(__method__, nil, options) do
|
21
|
-
fetch_cache_or_call_api(__method__, args[0], options) do
|
22
|
-
collect_with_cursor(method(__method__).super_method, *args, options)
|
23
|
-
end
|
24
|
-
end
|
25
|
-
end
|
12
|
+
MAX_IDS_PER_REQUEST = 5000
|
26
13
|
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
14
|
+
%i(friend_ids follower_ids).each do |name|
|
15
|
+
define_method(name) do |*args|
|
16
|
+
options = {count: MAX_IDS_PER_REQUEST, cursor: -1}.merge(args.extract_options!)
|
17
|
+
|
18
|
+
collect_with_cursor do |next_cursor|
|
19
|
+
options[:next_cursor] = next_cursor unless next_cursor.nil?
|
20
|
+
twitter.send(name, *args, options)
|
33
21
|
end
|
34
22
|
end
|
35
23
|
end
|
36
24
|
|
37
|
-
# specify reduce: false to use tweet for inactive_*
|
38
25
|
def friends(*args)
|
39
|
-
options = args.extract_options
|
40
|
-
|
41
|
-
|
42
|
-
_friends_serially(*args, options)
|
43
|
-
else
|
44
|
-
_friends_parallelly(*args, options)
|
45
|
-
end
|
46
|
-
end
|
26
|
+
options = args.extract_options!.merge(super_operation: :friends)
|
27
|
+
ids = friend_ids(*args, options)
|
28
|
+
users_internal(ids, options)
|
47
29
|
end
|
48
30
|
|
49
|
-
def
|
50
|
-
options =
|
51
|
-
|
52
|
-
|
53
|
-
fetch_cache_or_call_api(:friends, args[0], options) do
|
54
|
-
collect_with_cursor(method(:friends).super_method, *args, options).map { |u| u.to_hash }
|
55
|
-
end
|
56
|
-
end
|
31
|
+
def followers(*args)
|
32
|
+
options = args.extract_options!.merge(super_operation: :followers)
|
33
|
+
ids = follower_ids(*args, options)
|
34
|
+
users_internal(ids, options)
|
57
35
|
end
|
58
36
|
|
59
|
-
def
|
60
|
-
options = args.extract_options
|
61
|
-
instrument(__method__, nil, {super_operation: :friends}.merge(options)) do
|
62
|
-
opt = {super_operation: __method__}.merge(options)
|
63
|
-
users(friend_ids(*args, opt).map { |id| id.to_i }, opt)
|
64
|
-
end
|
65
|
-
end
|
37
|
+
def friend_ids_and_follower_ids(*args)
|
38
|
+
options = args.extract_options!.merge(super_operation: :friend_ids_and_follower_ids)
|
66
39
|
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
instrument(__method__, nil, options) do
|
71
|
-
if options.delete(:serial)
|
72
|
-
_followers_serially(*args, options)
|
73
|
-
else
|
74
|
-
_followers_parallelly(*args, options)
|
75
|
-
end
|
40
|
+
parallel(in_threads: 2) do |batch|
|
41
|
+
batch.friend_ids(*args, options)
|
42
|
+
batch.follower_ids(*args, options)
|
76
43
|
end
|
77
44
|
end
|
78
45
|
|
79
|
-
def
|
80
|
-
options =
|
81
|
-
args[0] = verify_credentials(super_operation: __method__).id if args.empty?
|
82
|
-
instrument(__method__, nil, options) do
|
83
|
-
fetch_cache_or_call_api(:followers, args[0], options) do
|
84
|
-
collect_with_cursor(method(:followers).super_method, *args, options).map { |u| u.to_hash }
|
85
|
-
end
|
86
|
-
end
|
87
|
-
end
|
46
|
+
def friends_and_followers(*args)
|
47
|
+
options = args.extract_options!.merge(super_operation: :friends_and_followers)
|
88
48
|
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
49
|
+
following_ids, followed_ids = friend_ids_and_follower_ids(*args, options)
|
50
|
+
unique_ids = (following_ids + followed_ids).uniq
|
51
|
+
people = users_internal(unique_ids).index_by { |u| u[:id] }
|
52
|
+
[people.slice(*following_ids), people.slice(*followed_ids)]
|
53
|
+
|
54
|
+
# parallel(in_threads: 2) do |batch|
|
55
|
+
# batch.friends(*args, options)
|
56
|
+
# batch.followers(*args, options)
|
57
|
+
# end
|
95
58
|
end
|
96
59
|
end
|
97
60
|
end
|
@@ -5,22 +5,23 @@ module TwitterWithAutoPagination
|
|
5
5
|
module Lists
|
6
6
|
include TwitterWithAutoPagination::REST::Utils
|
7
7
|
|
8
|
+
# Returns the lists the specified user has been added to.
|
8
9
|
def memberships(*args)
|
9
10
|
options = {count: 1000, cursor: -1}.merge(args.extract_options!)
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
end
|
11
|
+
|
12
|
+
collect_with_cursor do |next_cursor|
|
13
|
+
options[:next_cursor] = next_cursor unless next_cursor.nil?
|
14
|
+
twitter.send(:memberships, *args, options)
|
15
15
|
end
|
16
16
|
end
|
17
17
|
|
18
|
+
# Returns the members of the specified list.
|
18
19
|
def list_members(*args)
|
19
20
|
options = {count: 5000, skip_status: 1, cursor: -1}.merge(args.extract_options!)
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
21
|
+
|
22
|
+
collect_with_cursor do |next_cursor|
|
23
|
+
options[:next_cursor] = next_cursor unless next_cursor.nil?
|
24
|
+
twitter.send(:list_members, *args, options)
|
24
25
|
end
|
25
26
|
end
|
26
27
|
end
|
@@ -5,12 +5,22 @@ module TwitterWithAutoPagination
|
|
5
5
|
module Search
|
6
6
|
include TwitterWithAutoPagination::REST::Utils
|
7
7
|
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
8
|
+
MAX_TWEETS_PER_REQUEST = 100
|
9
|
+
|
10
|
+
%i(search).each do |name|
|
11
|
+
define_method(name) do |query, options = {}|
|
12
|
+
raise ArgumentError.new('specify a query') unless query.is_a?(String)
|
13
|
+
|
14
|
+
call_limit = calc_call_limit(options.delete(:count), MAX_TWEETS_PER_REQUEST)
|
15
|
+
options = {count: MAX_TWEETS_PER_REQUEST, result_type: :recent, call_count: 0, call_limit: call_limit}.merge(options)
|
16
|
+
|
17
|
+
collect_with_max_id do |max_id|
|
18
|
+
options[:max_id] = max_id unless max_id.nil?
|
19
|
+
options[:call_count] += 1
|
20
|
+
if options[:call_count] <= options[:call_limit]
|
21
|
+
twitter.send(name, query, options).attrs[:statuses].map { |s| Twitter::Tweet.new(s) }
|
22
|
+
end
|
23
|
+
end.map(&:attrs)
|
14
24
|
end
|
15
25
|
end
|
16
26
|
end
|
@@ -5,34 +5,21 @@ module TwitterWithAutoPagination
|
|
5
5
|
module Timelines
|
6
6
|
include TwitterWithAutoPagination::REST::Utils
|
7
7
|
|
8
|
-
|
9
|
-
mtd = __method__
|
10
|
-
options = {count: 200, include_rts: true, call_limit: 3}.merge(args.extract_options!)
|
11
|
-
instrument(mtd, nil, options) do
|
12
|
-
fetch_cache_or_call_api(mtd, verify_credentials(super_operation: mtd).id, options) do
|
13
|
-
collect_with_max_id(method(mtd).super_method, options).map { |s| s.attrs }
|
14
|
-
end
|
15
|
-
end
|
16
|
-
end
|
8
|
+
MAX_TWEETS_PER_REQUEST = 200
|
17
9
|
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
fetch_cache_or_call_api(mtd, args[0], options) do
|
24
|
-
collect_with_max_id(method(mtd).super_method, *args, options).map { |s| s.attrs }
|
25
|
-
end
|
26
|
-
end
|
27
|
-
end
|
10
|
+
%i(home_timeline user_timeline mentions_timeline).each do |name|
|
11
|
+
define_method(name) do |*args|
|
12
|
+
options = args.extract_options!.dup
|
13
|
+
call_limit = calc_call_limit(options.delete(:count), MAX_TWEETS_PER_REQUEST)
|
14
|
+
options = {count: MAX_TWEETS_PER_REQUEST, include_rts: true, call_count: 0, call_limit: call_limit}.merge(options)
|
28
15
|
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
end
|
16
|
+
collect_with_max_id do |max_id|
|
17
|
+
options[:max_id] = max_id unless max_id.nil?
|
18
|
+
options[:call_count] += 1
|
19
|
+
if options[:call_count] <= options[:call_limit]
|
20
|
+
twitter.send(name, *args, options)
|
21
|
+
end
|
22
|
+
end.map(&:attrs)
|
36
23
|
end
|
37
24
|
end
|
38
25
|
end
|
@@ -6,56 +6,43 @@ module TwitterWithAutoPagination
|
|
6
6
|
module Users
|
7
7
|
include TwitterWithAutoPagination::REST::Utils
|
8
8
|
|
9
|
-
def verify_credentials(
|
10
|
-
|
11
|
-
instrument(__method__, nil, options) do
|
12
|
-
fetch_cache_or_call_api(__method__, args) do
|
13
|
-
call_api(method(__method__).super_method, *args, options).to_hash
|
14
|
-
end
|
15
|
-
end
|
9
|
+
def verify_credentials(options = {})
|
10
|
+
twitter.send(__method__, {skip_status: true}.merge(options)).to_hash
|
16
11
|
end
|
17
12
|
|
18
13
|
def user?(*args)
|
19
|
-
|
20
|
-
return false if args.empty? || args[0].nil? || !args[0].to_s.match(/\A([a-zA-Z0-9_]{1,20}|[1-9][0-9]*)\z/)
|
21
|
-
instrument(__method__, nil, options) do
|
22
|
-
fetch_cache_or_call_api(__method__, args[0], options) do
|
23
|
-
call_api(method(__method__).super_method, *args, options)
|
24
|
-
end
|
25
|
-
end
|
14
|
+
twitter.send(__method__, *args)
|
26
15
|
end
|
27
16
|
|
28
17
|
def user(*args)
|
29
|
-
|
30
|
-
args[0] = verify_credentials(super_operation: __method__).id if args.empty?
|
31
|
-
instrument(__method__, nil, options) do
|
32
|
-
fetch_cache_or_call_api(__method__, args[0], options) do
|
33
|
-
call_api(method(__method__).super_method, *args, options).to_hash
|
34
|
-
end
|
35
|
-
end
|
18
|
+
twitter.send(__method__, *args).to_hash
|
36
19
|
end
|
37
20
|
|
38
|
-
|
39
|
-
|
40
|
-
#
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
thread_size = [users_per_workers.size, 10].min
|
47
|
-
|
48
|
-
instrument(__method__, nil, options) do
|
49
|
-
Parallel.each_with_index(users_per_workers, in_threads: thread_size) do |users_per_worker, i|
|
50
|
-
_users = fetch_cache_or_call_api(__method__, users_per_worker, options) do
|
51
|
-
call_api(method(__method__).super_method, users_per_worker, options).map { |u| u.to_hash }
|
52
|
-
end
|
53
|
-
|
54
|
-
processed_users << {i: i, users: _users}
|
55
|
-
end
|
21
|
+
MAX_USERS_PER_REQUEST = 100
|
22
|
+
|
23
|
+
# client.users -> cached
|
24
|
+
# users(internal call) -> cached
|
25
|
+
# super -> not cached
|
26
|
+
def users(values, options = {})
|
27
|
+
if values.size <= MAX_USERS_PER_REQUEST
|
28
|
+
return twitter.send(__method__, *values, options).map(&:to_hash)
|
56
29
|
end
|
57
30
|
|
58
|
-
|
31
|
+
users_internal(values, options)
|
32
|
+
end
|
33
|
+
|
34
|
+
def blocked_ids(*args)
|
35
|
+
twitter.send(__method__, *args).attrs[:ids]
|
36
|
+
end
|
37
|
+
|
38
|
+
private
|
39
|
+
|
40
|
+
def users_internal(values, options = {})
|
41
|
+
options = options.merge(super_operation: :users)
|
42
|
+
|
43
|
+
parallel(in_threads: 10) do |batch|
|
44
|
+
values.each_slice(MAX_USERS_PER_REQUEST) { |targets| batch.users(targets, options) }
|
45
|
+
end.flatten
|
59
46
|
end
|
60
47
|
end
|
61
48
|
end
|
@@ -1,166 +1,46 @@
|
|
1
1
|
require 'hashie'
|
2
2
|
require 'digest/md5'
|
3
3
|
|
4
|
+
require 'twitter_with_auto_pagination/collector'
|
5
|
+
|
4
6
|
module TwitterWithAutoPagination
|
5
7
|
module REST
|
6
8
|
module Utils
|
7
|
-
|
8
|
-
object.kind_of?(String) || object.kind_of?(Integer)
|
9
|
-
end
|
10
|
-
|
11
|
-
def authenticating_user?(target)
|
12
|
-
user.id.to_i == user(target).id.to_i
|
13
|
-
end
|
14
|
-
|
15
|
-
def authorized_user?(target)
|
16
|
-
target_user = user(target)
|
17
|
-
!target_user.protected? || friendship?(user.id.to_i, target_user.id.to_i)
|
18
|
-
end
|
19
|
-
|
20
|
-
def credentials_hash
|
21
|
-
Digest::MD5.hexdigest(access_token + access_token_secret + consumer_key + consumer_secret)
|
22
|
-
end
|
23
|
-
|
24
|
-
def instrument(operation, key, options = nil)
|
25
|
-
payload = {operation: operation}
|
26
|
-
payload.merge!(options) if options.is_a?(Hash)
|
27
|
-
ActiveSupport::Notifications.instrument('call.twitter_with_auto_pagination', payload) { yield(payload) }
|
28
|
-
end
|
29
|
-
|
30
|
-
def call_api(method, *args)
|
31
|
-
api_options = args.extract_options!
|
32
|
-
self.call_count += 1
|
33
|
-
options = {method: method.name, call_count: self.call_count, args: [*args, api_options]}
|
34
|
-
begin
|
35
|
-
instrument('request', nil, options) { method.call(*args, api_options) }
|
36
|
-
rescue Twitter::Error::TooManyRequests => e
|
37
|
-
logger.warn "#{__method__}: #{e.class} #{e.message} Retry after #{e.rate_limit.reset_in} seconds. #{options.inspect}"
|
38
|
-
raise e
|
39
|
-
rescue Twitter::Error::ServiceUnavailable, Twitter::Error::InternalServerError,
|
40
|
-
Twitter::Error::Forbidden, Twitter::Error::NotFound => e
|
41
|
-
logger.warn "#{__method__}: #{e.class} #{e.message} #{options.inspect}"
|
42
|
-
raise e
|
43
|
-
rescue => e
|
44
|
-
logger.warn "CATCH ME! #{__method__}: #{e.class} #{e.message} #{options.inspect}"
|
45
|
-
raise e
|
46
|
-
end
|
47
|
-
end
|
48
|
-
|
49
|
-
# user_timeline, search
|
50
|
-
def collect_with_max_id(method, *args)
|
51
|
-
options = args.extract_options!
|
52
|
-
call_limit = options.delete(:call_limit) || 3
|
53
|
-
return_data = []
|
54
|
-
call_num = 0
|
55
|
-
|
56
|
-
while call_num < call_limit
|
57
|
-
last_response = call_api(method, *args, options)
|
58
|
-
last_response = yield(last_response) if block_given?
|
59
|
-
call_num += 1
|
60
|
-
return_data += last_response
|
61
|
-
if last_response.nil? || last_response.empty?
|
62
|
-
break
|
63
|
-
else
|
64
|
-
options[:max_id] = last_response.last.kind_of?(Hash) ? last_response.last[:id] : last_response.last.id
|
65
|
-
end
|
66
|
-
end
|
67
|
-
|
68
|
-
return_data
|
69
|
-
end
|
70
|
-
|
71
|
-
# friends, followers
|
72
|
-
def collect_with_cursor(method, *args)
|
73
|
-
options = args.extract_options!
|
74
|
-
call_limit = options.delete(:call_limit) || 30
|
75
|
-
return_data = []
|
76
|
-
call_num = 0
|
77
|
-
|
78
|
-
while call_num < call_limit
|
79
|
-
last_response = call_api(method, *args, options).attrs
|
80
|
-
call_num += 1
|
81
|
-
return_data += (last_response[:users] || last_response[:ids] || last_response[:lists])
|
82
|
-
options[:cursor] = last_response[:next_cursor]
|
83
|
-
if options[:cursor].nil? || options[:cursor] == 0
|
84
|
-
break
|
85
|
-
end
|
86
|
-
end
|
87
|
-
|
88
|
-
return_data
|
89
|
-
end
|
90
|
-
|
91
|
-
def normalize_key(method, user, options = {})
|
92
|
-
delim = ':'
|
93
|
-
identifier =
|
94
|
-
case
|
95
|
-
when method == :verify_credentials
|
96
|
-
"token-hash#{delim}#{credentials_hash}"
|
97
|
-
when method == :search
|
98
|
-
"str#{delim}#{user.to_s}"
|
99
|
-
when method == :list_members
|
100
|
-
"list_id#{delim}#{user.to_s}"
|
101
|
-
when method == :mentions_timeline
|
102
|
-
"#{user.kind_of?(Integer) ? 'id' : 'sn'}#{delim}#{user.to_s}"
|
103
|
-
when method == :home_timeline
|
104
|
-
"#{user.kind_of?(Integer) ? 'id' : 'sn'}#{delim}#{user.to_s}"
|
105
|
-
when method.in?([:users, :replying]) && options[:super_operation].present?
|
106
|
-
case
|
107
|
-
when user.kind_of?(Array) && user.first.kind_of?(Integer)
|
108
|
-
"#{options[:super_operation]}-ids#{delim}#{Digest::MD5.hexdigest(user.join(','))}"
|
109
|
-
when user.kind_of?(Array) && user.first.kind_of?(String)
|
110
|
-
"#{options[:super_operation]}-sns#{delim}#{Digest::MD5.hexdigest(user.join(','))}"
|
111
|
-
else
|
112
|
-
raise "#{method.inspect} #{user.inspect}"
|
113
|
-
end
|
114
|
-
when user.kind_of?(Integer)
|
115
|
-
"id#{delim}#{user.to_s}"
|
116
|
-
when user.kind_of?(Array) && user.first.kind_of?(Integer)
|
117
|
-
"ids#{delim}#{Digest::MD5.hexdigest(user.join(','))}"
|
118
|
-
when user.kind_of?(Array) && user.first.kind_of?(String)
|
119
|
-
"sns#{delim}#{Digest::MD5.hexdigest(user.join(','))}"
|
120
|
-
when user.kind_of?(String)
|
121
|
-
"sn#{delim}#{user}"
|
122
|
-
when user.kind_of?(Twitter::User)
|
123
|
-
"user#{delim}#{user.id.to_s}"
|
124
|
-
else
|
125
|
-
raise "#{method.inspect} #{user.inspect}"
|
126
|
-
end
|
9
|
+
include TwitterWithAutoPagination::Collector
|
127
10
|
|
128
|
-
|
129
|
-
end
|
11
|
+
DEFAULT_CALL_LIMIT = 1
|
130
12
|
|
131
|
-
|
13
|
+
private
|
132
14
|
|
133
|
-
|
134
|
-
|
135
|
-
|
15
|
+
def calc_call_limit(count, max_count)
|
16
|
+
return DEFAULT_CALL_LIMIT unless count
|
17
|
+
limit = count / max_count
|
18
|
+
limit += 1 if (count % max_count).nonzero?
|
19
|
+
limit
|
20
|
+
end
|
136
21
|
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
end
|
22
|
+
def uid_or_screen_name?(object)
|
23
|
+
object.kind_of?(String) || object.kind_of?(Integer)
|
24
|
+
end
|
141
25
|
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
obj.map { |o| to_mash(o) }
|
146
|
-
when obj.kind_of?(Hash)
|
147
|
-
Hashie::Mash.new(obj.map { |k, v| [k, to_mash(v)] }.to_h)
|
148
|
-
else
|
149
|
-
obj
|
150
|
-
end
|
151
|
-
end
|
26
|
+
def authenticating_user?(target)
|
27
|
+
user.id.to_i == user(target).id.to_i
|
28
|
+
end
|
152
29
|
|
153
|
-
|
154
|
-
|
30
|
+
def authorized_user?(target)
|
31
|
+
target_user = user(target)
|
32
|
+
!target_user.protected? || friendship?(user.id.to_i, target_user.id.to_i)
|
33
|
+
end
|
155
34
|
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
instrument('serialize', nil, key: key, caller: method) { encode(block_result) }
|
160
|
-
end
|
35
|
+
def credentials_hash
|
36
|
+
Digest::MD5.hexdigest(access_token + access_token_secret + consumer_key + consumer_secret)
|
37
|
+
end
|
161
38
|
|
162
|
-
|
163
|
-
|
39
|
+
def instrument(operation, name, options = nil)
|
40
|
+
payload = {operation: operation}
|
41
|
+
payload.merge!(options) if options.is_a?(Hash) && !options.empty?
|
42
|
+
ActiveSupport::Notifications.instrument("#{name.nil? ? operation : name}.twitter", payload) {yield(payload)}
|
43
|
+
end
|
164
44
|
end
|
165
45
|
end
|
166
46
|
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
module TwitterWithAutoPagination
|
2
|
+
class Serializer
|
3
|
+
CODER = JSON
|
4
|
+
|
5
|
+
class << self
|
6
|
+
def encode(obj, options = {})
|
7
|
+
instrument(options) do
|
8
|
+
(!!obj == obj) ? obj : CODER.dump(obj)
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
def decode(str, options = {})
|
13
|
+
instrument(options) do
|
14
|
+
str.kind_of?(String) ? CODER.parse(str, symbolize_names: true) : str
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
private
|
19
|
+
|
20
|
+
def instrument(options, &block)
|
21
|
+
parent = caller[0][/`([^']*)'/, 1]
|
22
|
+
payload = {operation: parent, args: options[:args]}
|
23
|
+
ActiveSupport::Notifications.instrument("#{payload[:operation]}.twitter", payload) { yield(payload) }
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -2,43 +2,7 @@ require 'active_support'
|
|
2
2
|
require 'active_support/core_ext'
|
3
3
|
require 'twitter'
|
4
4
|
|
5
|
-
require 'twitter_with_auto_pagination/
|
5
|
+
require 'twitter_with_auto_pagination/client'
|
6
6
|
|
7
7
|
module TwitterWithAutoPagination
|
8
|
-
end
|
9
|
-
|
10
|
-
require 'twitter_with_auto_pagination/rest/api'
|
11
|
-
|
12
|
-
module Twitter
|
13
|
-
module REST
|
14
|
-
class Client
|
15
|
-
prepend TwitterWithAutoPagination::REST::API
|
16
|
-
|
17
|
-
def initialize(options = {})
|
18
|
-
@cache = ActiveSupport::Cache::FileStore.new(File.join('tmp', 'api_cache'))
|
19
|
-
@call_count = 0
|
20
|
-
|
21
|
-
logger =
|
22
|
-
if options.has_key?(:logger)
|
23
|
-
options.delete(:logger)
|
24
|
-
else
|
25
|
-
Dir.mkdir('log') unless File.exists?('log')
|
26
|
-
Logger.new('log/twitter_with_auto_pagination.log')
|
27
|
-
end
|
28
|
-
logger.level = options.has_key?(:log_level) ? options.delete(:log_level) : :debug
|
29
|
-
@@logger = @logger = logger
|
30
|
-
|
31
|
-
super
|
32
|
-
end
|
33
|
-
|
34
|
-
def self.logger
|
35
|
-
@@logger ||= nil
|
36
|
-
end
|
37
|
-
|
38
|
-
attr_accessor :call_count, :logger
|
39
|
-
attr_reader :cache
|
40
|
-
|
41
|
-
INDENT = 4
|
42
|
-
end
|
43
|
-
end
|
44
|
-
end
|
8
|
+
end
|