twitter_with_auto_pagination 0.8.12 → 0.9.0

Sign up to get free protection for your applications and to get access to all the features.
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