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.
Files changed (34) hide show
  1. checksums.yaml +4 -4
  2. data/lib/twitter_with_auto_pagination/analysis/api.rb +9 -0
  3. data/lib/twitter_with_auto_pagination/{rest/uncategorized.rb → analysis/timelines.rb} +2 -2
  4. data/lib/twitter_with_auto_pagination/cache.rb +78 -0
  5. data/lib/twitter_with_auto_pagination/caching_and_logging.rb +80 -0
  6. data/lib/twitter_with_auto_pagination/client.rb +57 -0
  7. data/lib/twitter_with_auto_pagination/collector.rb +18 -0
  8. data/lib/twitter_with_auto_pagination/log_subscriber.rb +59 -26
  9. data/lib/twitter_with_auto_pagination/logger.rb +10 -0
  10. data/lib/twitter_with_auto_pagination/parallel.rb +25 -0
  11. data/lib/twitter_with_auto_pagination/rate_limit.rb +56 -0
  12. data/lib/twitter_with_auto_pagination/rest/api.rb +16 -14
  13. data/lib/twitter_with_auto_pagination/rest/extension/friends_and_followers.rb +0 -7
  14. data/lib/twitter_with_auto_pagination/rest/favorites.rb +13 -7
  15. data/lib/twitter_with_auto_pagination/rest/friends_and_followers.rb +33 -70
  16. data/lib/twitter_with_auto_pagination/rest/lists.rb +10 -9
  17. data/lib/twitter_with_auto_pagination/rest/search.rb +16 -6
  18. data/lib/twitter_with_auto_pagination/rest/timelines.rb +13 -26
  19. data/lib/twitter_with_auto_pagination/rest/users.rb +27 -40
  20. data/lib/twitter_with_auto_pagination/rest/utils.rb +29 -149
  21. data/lib/twitter_with_auto_pagination/serializer.rb +27 -0
  22. data/lib/twitter_with_auto_pagination.rb +2 -38
  23. data/spec/helper.rb +167 -36
  24. data/spec/twitter_with_auto_pagination/cache_spec.rb +71 -0
  25. data/spec/twitter_with_auto_pagination/client_spec.rb +7 -145
  26. data/spec/twitter_with_auto_pagination/parallel_spec.rb +23 -0
  27. data/spec/twitter_with_auto_pagination/rest/favorites_spec.rb +58 -0
  28. data/spec/twitter_with_auto_pagination/rest/friends_and_followers_spec.rb +161 -0
  29. data/spec/twitter_with_auto_pagination/rest/lists_spec.rb +39 -0
  30. data/spec/twitter_with_auto_pagination/rest/search_spec.rb +42 -0
  31. data/spec/twitter_with_auto_pagination/rest/timelines_spec.rb +82 -0
  32. data/spec/twitter_with_auto_pagination/rest/users_spec.rb +143 -0
  33. data/twitter_with_auto_pagination.gemspec +1 -1
  34. metadata +28 -3
@@ -5,93 +5,56 @@ module TwitterWithAutoPagination
5
5
  module FriendsAndFollowers
6
6
  include TwitterWithAutoPagination::REST::Utils
7
7
 
8
- def friendship?(*args)
9
- options = args.extract_options!
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
- def friend_ids(*args)
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
- def follower_ids(*args)
28
- options = {count: 5000, cursor: -1}.merge(args.extract_options!)
29
- args[0] = verify_credentials(super_operation: __method__).id if args.empty?
30
- instrument(__method__, nil, options) do
31
- fetch_cache_or_call_api(__method__, args[0], options) do
32
- collect_with_cursor(method(__method__).super_method, *args, options)
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
- instrument(__method__, nil, options) do
41
- if options.delete(:serial)
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 _friends_serially(*args)
50
- options = {count: 200, include_user_entities: true, cursor: -1, super_operation: :friends}.merge(args.extract_options!)
51
- args[0] = verify_credentials(super_operation: __method__).id if args.empty?
52
- instrument(__method__, nil, options) do
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 _friends_parallelly(*args)
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
- # specify reduce: false to use tweet for inactive_*
68
- def followers(*args)
69
- options = args.extract_options!
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 _followers_serially(*args)
80
- options = {count: 200, include_user_entities: true, cursor: -1, super_operation: :followers}.merge(args.extract_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
- def _followers_parallelly(*args)
90
- options = args.extract_options!
91
- instrument(__method__, nil, {super_operation: :followers}.merge(options)) do
92
- opt = {super_operation: __method__}.merge(options)
93
- users(follower_ids(*args, opt).map { |id| id.to_i }, opt)
94
- end
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
- args[0] = verify_credentials(super_operation: __method__).id if args.empty?
11
- instrument(__method__, nil, options) do
12
- fetch_cache_or_call_api(__method__, args[0], options) do
13
- collect_with_cursor(method(__method__).super_method, *args, options)
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
- 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
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
- def search(*args)
9
- options = {count: 100, result_type: :recent, call_limit: 1}.merge(args.extract_options!)
10
- instrument(__method__, nil, options) do
11
- fetch_cache_or_call_api(__method__, args[0], options) do
12
- collect_with_max_id(method(__method__).super_method, *args, options) { |response| response.attrs[:statuses] }.map { |s| s.to_hash }
13
- end
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
- def home_timeline(*args)
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
- def user_timeline(*args)
19
- mtd = __method__
20
- options = {count: 200, include_rts: true, call_limit: 3}.merge(args.extract_options!)
21
- args[0] = verify_credentials(super_operation: mtd).id if args.empty?
22
- instrument(mtd, nil, options) do
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
- def mentions_timeline(*args)
30
- mtd = __method__
31
- options = {count: 200, include_rts: true, call_limit: 1}.merge(args.extract_options!)
32
- instrument(mtd, nil, options) do
33
- fetch_cache_or_call_api(mtd, verify_credentials(super_operation: mtd).id, options) do
34
- collect_with_max_id(method(mtd).super_method, options).map { |s| s.attrs }
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(*args)
10
- options = {skip_status: true}.merge(args.extract_options!)
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
- options = args.extract_options!
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
- options = args.extract_options!
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
- # use compact, not use sort and uniq
39
- # specify reduce: false to use tweet for inactive_*
40
- # TODO Perhaps `old_users` automatically merges result...
41
- def users(*args)
42
- options = args.extract_options!
43
- options[:reduce] = false
44
- users_per_workers = args.first.compact.each_slice(100).to_a
45
- processed_users = []
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
- processed_users.sort_by { |p| p[:i] }.map { |p| p[:users] }.flatten.compact
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
- def uid_or_screen_name?(object)
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
- "#{method}#{delim}#{identifier}"
129
- end
11
+ DEFAULT_CALL_LIMIT = 1
130
12
 
131
- CODER = JSON
13
+ private
132
14
 
133
- def encode(obj)
134
- obj.in?([true, false]) ? obj : CODER.dump(obj)
135
- end
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
- def decode(str)
138
- obj = str.kind_of?(String) ? CODER.load(str) : str
139
- to_mash(obj)
140
- end
22
+ def uid_or_screen_name?(object)
23
+ object.kind_of?(String) || object.kind_of?(Integer)
24
+ end
141
25
 
142
- def to_mash(obj)
143
- case
144
- when obj.kind_of?(Array)
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
- def fetch_cache_or_call_api(method, user, options = {})
154
- key = normalize_key(method, user, options)
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
- fetch_result =
157
- cache.fetch(key, expires_in: 1.hour, race_condition_ttl: 5.minutes) do
158
- block_result = yield
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
- instrument('deserialize', nil, key: key, caller: method) { decode(fetch_result) }
163
- end
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/log_subscriber'
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