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
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 1a5ffffb7a3903265ee83d5340491193424cf6b2
4
- data.tar.gz: df27d9bc220d90ad97c28911ed67960233ba63dd
3
+ metadata.gz: ed91eaf1cf67bef358dda37cdd3886003fa80c0c
4
+ data.tar.gz: e91e410326935bb838a8c9c7d29f678d129b2417
5
5
  SHA512:
6
- metadata.gz: 978c61c49f0fc373584cc3284483a3c775a0035499e2ba49b6271a5e78b8ea163ecc1a6438b682d540ee1e691f4461a78cbc813a2235869b619655777af74496
7
- data.tar.gz: f6956150c2825aeae989067263e1eebf5d51df0dece18767239fedfa2b3970b899cfc51569f99f4e2f0fe671df7712081445636510633dd14d903a6c519db9e0
6
+ metadata.gz: 97ad05cdd642560bb57e5ef28f2d1b7fdbb492e9112dfdd0af8688fd7a3735f799a0d99957d97a28e7699d158724a14dccd57c3424b9b5964e68ead8990af020
7
+ data.tar.gz: f342f1131df4b7fe0410e5ad9b61879bb88a07077d08a4414e07828dea0fcb1a3ba6cec6ee9c638de382fb36b72dce4d36344e99d7a94562dfdf9d7c1eba3197
@@ -0,0 +1,9 @@
1
+ require 'twitter_with_auto_pagination/analysis/timelines'
2
+
3
+ module TwitterWithAutoPagination
4
+ module Analysis
5
+ module API
6
+ include TwitterWithAutoPagination::Analysis::Timelines
7
+ end
8
+ end
9
+ end
@@ -1,8 +1,8 @@
1
1
  require 'twitter_with_auto_pagination/rest/utils'
2
2
 
3
3
  module TwitterWithAutoPagination
4
- module REST
5
- module Uncategorized
4
+ module Analysis
5
+ module Timelines
6
6
  include TwitterWithAutoPagination::REST::Utils
7
7
 
8
8
  EVERY_DAY = (0..6)
@@ -0,0 +1,78 @@
1
+ require 'forwardable'
2
+ require 'digest/md5'
3
+
4
+ require 'twitter_with_auto_pagination/serializer'
5
+
6
+ module TwitterWithAutoPagination
7
+ class Cache
8
+ extend Forwardable
9
+
10
+ attr_reader :serializer, :client
11
+
12
+ def_delegators :@client, :clear, :cleanup
13
+
14
+ def initialize
15
+ @serializer = Serializer
16
+
17
+ path = File.join('tmp', 'twitter_cache')
18
+ Dir.mkdir(path) unless File.exists?(path)
19
+ @client = ActiveSupport::Cache::FileStore.new(path, expires_in: 1.hour, race_condition_ttl: 5.minutes)
20
+ end
21
+
22
+ def fetch(method, user, options = {}, &block)
23
+ key = normalize_key(method, user, options.except(:args))
24
+
25
+ block_result = nil
26
+ fetch_result =
27
+ @client.fetch(key) do
28
+ block_result = yield
29
+ serializer.encode(block_result, args: options[:args])
30
+ end
31
+
32
+ block_result ? block_result : serializer.decode(fetch_result, args: options[:args])
33
+ end
34
+
35
+ private
36
+
37
+ DELIM = ':'
38
+
39
+ def normalize_key(method, user, options = {})
40
+ identifier =
41
+ case
42
+ when method == :search then "query#{DELIM}#{user}"
43
+ when method == :friendship? then "from#{DELIM}#{user[0]}#{DELIM}to#{DELIM}#{user[1]}"
44
+ when method == :list_members then "list_id#{DELIM}#{user}"
45
+ when user.nil? && options[:hash].present? then "token-hash#{DELIM}#{options[:hash]}"
46
+ else user_identifier(user)
47
+ end
48
+
49
+ "#{method}#{DELIM}#{identifier}#{DELIM}#{options_identifier(options)}"
50
+ end
51
+
52
+ def user_identifier(user)
53
+ case
54
+ when user.kind_of?(Integer) then "id#{DELIM}#{user}"
55
+ when user.kind_of?(String) then "screen_name#{DELIM}#{user}"
56
+ when user.kind_of?(Array) && user.empty? then 'The_#users_is_called_with_an_empty_array'
57
+ when user.kind_of?(Array) && user[0].kind_of?(Integer) then "ids#{DELIM}#{user.size}-#{hexdigest(user.join(','))}"
58
+ when user.kind_of?(Array) && user[0].kind_of?(String) then "screen_names#{DELIM}#{user.size}-#{hexdigest(user.join(','))}"
59
+ else raise "#{__method__}: No matches #{user.inspect}"
60
+ end
61
+ end
62
+
63
+ def options_identifier(options)
64
+ options = options.except(:hash, :call_count, :call_limit, :super_operation)
65
+ str =
66
+ if options.empty?
67
+ 'empty'
68
+ else
69
+ options.map { |k, v| "#{k}#{DELIM}#{v}" }.join(',')
70
+ end
71
+ "options#{DELIM}#{str}"
72
+ end
73
+
74
+ def hexdigest(str)
75
+ Digest::MD5.hexdigest(str)
76
+ end
77
+ end
78
+ end
@@ -0,0 +1,80 @@
1
+ module TwitterWithAutoPagination
2
+ module CachingAndLogging
3
+ %i(
4
+ verify_credentials
5
+ user?
6
+ user
7
+ users
8
+ blocked_ids
9
+ home_timeline
10
+ user_timeline
11
+ mentions_timeline
12
+ search
13
+ favorites
14
+ friendship?
15
+ friend_ids
16
+ follower_ids
17
+ memberships
18
+ list_members
19
+ ).each do |name|
20
+ define_method(name) do |*args|
21
+ options = args.extract_options!
22
+
23
+ Instrumenter.api_call(name, options) do
24
+ do_request =
25
+ proc { Instrumenter.perform_request(name, options) { options.empty? ? super(*args) : super(*args, options) } }
26
+
27
+ if Utils.cache_disabled?(options)
28
+ do_request.call
29
+ else
30
+ user = name == :friendship? ? args[0, 2] : args[0]
31
+ cache.fetch(name, user, options.merge(args: [name, options], hash: credentials_hash), &do_request)
32
+ end
33
+ end
34
+ end
35
+ end
36
+
37
+ # Cached in #users
38
+ %i(friends followers friends_and_followers).each do |name|
39
+ define_method(name) do |*args|
40
+ options = args.extract_options!
41
+ Instrumenter.api_call(name, options) { super(*args, options) }
42
+ end
43
+ end
44
+
45
+ module Instrumenter
46
+
47
+ module_function
48
+
49
+ def api_call(operation, options)
50
+ payload = {operation: operation}.merge(options)
51
+ ActiveSupport::Notifications.instrument('api_call.twitter', payload) { yield(payload) }
52
+ end
53
+
54
+ def perform_request(caller, options, &block)
55
+ payload = {operation: 'request', args: [caller, options]}
56
+ ActiveSupport::Notifications.instrument('request.twitter', payload) { yield(payload) }
57
+ end
58
+ end
59
+
60
+ module Utils
61
+
62
+ module_function
63
+
64
+ def cache_disabled?(options)
65
+ options.is_a?(Hash) && options.has_key?(:cache) && !options[:cache]
66
+ end
67
+
68
+ def to_mash(obj)
69
+ case
70
+ when obj.kind_of?(Array)
71
+ obj.map {|o| to_mash(o)}
72
+ when obj.kind_of?(Hash)
73
+ Hashie::Mash.new(obj.map {|k, v| [k, to_mash(v)]}.to_h)
74
+ else
75
+ obj
76
+ end
77
+ end
78
+ end
79
+ end
80
+ end
@@ -0,0 +1,57 @@
1
+ require 'forwardable'
2
+
3
+ require 'twitter_with_auto_pagination/logger'
4
+ require 'twitter_with_auto_pagination/cache'
5
+ require 'twitter_with_auto_pagination/log_subscriber'
6
+ require 'twitter_with_auto_pagination/analysis/api'
7
+ require 'twitter_with_auto_pagination/rest/api'
8
+ require 'twitter_with_auto_pagination/rate_limit'
9
+ require 'twitter_with_auto_pagination/parallel'
10
+
11
+ module TwitterWithAutoPagination
12
+ class Client
13
+ extend Forwardable
14
+
15
+ attr_reader :cache, :twitter
16
+
17
+ def_delegators :@twitter, :perform_get, :access_token, :access_token_secret, :consumer_key, :consumer_secret
18
+
19
+ include TwitterWithAutoPagination::RateLimit
20
+ include TwitterWithAutoPagination::Parallel
21
+ include TwitterWithAutoPagination::REST::API
22
+ include TwitterWithAutoPagination::Analysis::API
23
+
24
+ def initialize(*args)
25
+ options = args.extract_options!
26
+
27
+ @cache = TwitterWithAutoPagination::Cache.new
28
+ Logging.logger = logger = TwitterWithAutoPagination::Logger.new(options)
29
+
30
+ unless subscriber_attached?
31
+ @@subscriber_attached = true
32
+ if logger.debug?
33
+ # Super slow
34
+ TwitterWithAutoPagination::AllLogSubscriber.attach_to :twitter
35
+ TwitterWithAutoPagination::ASLogSubscriber.attach_to :active_support
36
+ elsif logger.info?
37
+ TwitterWithAutoPagination::ApiCallLogSubscriber.attach_to :twitter
38
+ end
39
+ end
40
+
41
+ @twitter = Twitter::REST::Client.new(options)
42
+ end
43
+
44
+ def self.logger
45
+ Logging.logger
46
+ end
47
+
48
+ def subscriber_attached?
49
+ @@subscriber_attached ||= false
50
+ end
51
+
52
+ # Deprecated
53
+ def call_count
54
+ -1
55
+ end
56
+ end
57
+ end
@@ -0,0 +1,18 @@
1
+ module TwitterWithAutoPagination
2
+ module Collector
3
+
4
+ def collect_with_max_id(collection = [], max_id = nil, &block)
5
+ tweets = yield(max_id)
6
+ return collection if tweets.nil?
7
+ collection += tweets
8
+ tweets.empty? ? collection.flatten : collect_with_max_id(collection, tweets.last.id - 1, &block)
9
+ end
10
+
11
+ def collect_with_cursor(collection = [], cursor = nil, &block)
12
+ response = yield(cursor)
13
+ return collection if response.nil?
14
+ collection += (response.attrs[:ids] || response.attrs[:users] || response.attrs[:lists]) # TODO to_a
15
+ response.attrs[:next_cursor].zero? ? collection.flatten : collect_with_cursor(collection, response.attrs[:next_cursor], &block)
16
+ end
17
+ end
18
+ end
@@ -1,57 +1,90 @@
1
1
  module TwitterWithAutoPagination
2
- class LogSubscriber < ActiveSupport::LogSubscriber
2
+ module Logging
3
+ def truncated_payload(payload)
4
+ return payload.inspect if !payload.has_key?(:args) || !payload[:args].is_a?(Array) || payload[:args].empty? || !payload[:args][0].is_a?(Array)
3
5
 
4
- def initialize
5
- super
6
+ args = payload[:args].dup
7
+ args[0] =
8
+ if args[0].size > 3
9
+ "[#{args[0].take(3).join(', ')} ... #{args[0].size}]"
10
+ else
11
+ args[0].inspect
12
+ end
13
+
14
+ {args: args}.merge(payload.except(:args)).inspect
6
15
  end
7
16
 
8
- def call(event)
9
- return unless logger.debug?
17
+ module_function
10
18
 
11
- payload = event.payload
12
- name = "TW::#{payload.delete(:operation)} (#{event.duration.round(1)}ms)"
13
- name = color(name, CYAN, true) # WHITE, RED, GREEN, BLUE, CYAN, MAGENTA, YELLOW
14
- debug { "#{name} #{(payload.inspect)}" }
19
+ def logger
20
+ @@logger
15
21
  end
16
22
 
17
- private
23
+ def logger=(logger)
24
+ @@logger = logger
25
+ end
26
+ end
18
27
 
19
- def logger
20
- Twitter::REST::Client.logger
28
+ class ApiCallLogSubscriber < ActiveSupport::LogSubscriber
29
+ include Logging
30
+
31
+ def api_call(event)
32
+ payload = event.payload
33
+ name = "TW::#{payload.delete(:operation)} (#{event.duration.round(1)}ms)"
34
+ name = color(name, YELLOW, true) # WHITE, RED, GREEN, BLUE, CYAN, MAGENTA, YELLOW
35
+ info { "#{name}#{" #{truncated_payload(payload)}" unless payload.empty?}" }
21
36
  end
22
37
  end
23
38
 
24
- class ASLogSubscriber < ActiveSupport::LogSubscriber
39
+ class AllLogSubscriber < ApiCallLogSubscriber
40
+ include Logging
25
41
 
26
- def initialize
42
+ def api_call(*args)
27
43
  super
28
44
  end
29
45
 
46
+ def twitter_any(event)
47
+ payload = event.payload
48
+ payload.delete(:name)
49
+ name = "TW::#{payload.delete(:operation)} (#{event.duration.round(1)}ms)"
50
+ name = color(name, CYAN, true) # WHITE, RED, GREEN, BLUE, CYAN, MAGENTA, YELLOW
51
+ debug { "#{name}#{" #{truncated_payload(payload)}" unless payload.empty?}" }
52
+ end
53
+
54
+ %w(request encode decode to_mash).each do |operation|
55
+ class_eval <<-METHOD, __FILE__, __LINE__ + 1
56
+ def #{operation}(event)
57
+ event.payload[:name] = '#{operation}'
58
+ twitter_any(event)
59
+ end
60
+ METHOD
61
+ end
62
+ end
63
+
64
+ class ASLogSubscriber < ActiveSupport::LogSubscriber
30
65
  def cache_any(event)
31
66
  return unless logger.debug?
32
67
 
33
68
  payload = event.payload
34
- name= "AS::#{payload.delete(:name)} (#{event.duration.round(1)}ms)"
69
+ name = "AS::#{payload[:name]} (#{event.duration.round(1)}ms)"
35
70
  name = color(name, MAGENTA, true)
36
- debug { "#{name} #{(payload.inspect)}" }
71
+ debug { "#{name} #{(payload.except(:name, :expires_in, :race_condition_ttl).inspect)}" }
37
72
  end
38
73
 
39
- %w(read write fetch_hit generate delete exist?).each do |operation|
74
+ # Ignore generate and fetch_hit
75
+ %w(read write delete exist?).each do |operation|
40
76
  class_eval <<-METHOD, __FILE__, __LINE__ + 1
41
- def cache_#{operation}(event)
42
- event.payload[:name] = '#{operation}'
43
- cache_any(event)
44
- end
77
+ def cache_#{operation}(event)
78
+ event.payload[:name] = '#{operation}'
79
+ cache_any(event)
80
+ end
45
81
  METHOD
46
82
  end
47
83
 
48
84
  private
49
85
 
50
86
  def logger
51
- Twitter::REST::Client.logger
87
+ Logging.logger
52
88
  end
53
89
  end
54
- end
55
-
56
- TwitterWithAutoPagination::LogSubscriber.attach_to :twitter_with_auto_pagination
57
- TwitterWithAutoPagination::ASLogSubscriber.attach_to :active_support
90
+ end
@@ -0,0 +1,10 @@
1
+ module TwitterWithAutoPagination
2
+ class Logger < ::Logger
3
+
4
+ def initialize(options = {})
5
+ Dir.mkdir('log') unless File.exists?('log')
6
+ super('log/twitter.log')
7
+ self.level = options.has_key?(:log_level) ? options.delete(:log_level) : :debug
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,25 @@
1
+ require 'parallel'
2
+
3
+ module TwitterWithAutoPagination
4
+ module Parallel
5
+
6
+ def parallel(options = {}, &block)
7
+ batch = Arguments.new
8
+ yield(batch)
9
+
10
+ in_threads = options.fetch(:in_threads, batch.size)
11
+
12
+ ::Parallel.map_with_index(batch, in_threads: in_threads) do |args, i|
13
+ {i: i, result: send(*args)}
14
+ end.sort_by { |q| q[:i] }.map { |q| q[:result] }
15
+ end
16
+
17
+ class Arguments < Array
18
+ %i(users friend_ids follower_ids friends followers).each do |name|
19
+ define_method(name) do |*args|
20
+ send(:<< , [name, *args])
21
+ end
22
+ end
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,56 @@
1
+ module TwitterWithAutoPagination
2
+ module RateLimit
3
+ def rate_limit
4
+ RateLimit.new(perform_get('/1.1/application/rate_limit_status.json')) rescue nil
5
+ end
6
+
7
+ class RateLimit
8
+ def initialize(status)
9
+ @status = status
10
+ end
11
+
12
+ def resources
13
+ @status[:resources]
14
+ end
15
+
16
+ def verify_credentials
17
+ extract_remaining_and_reset_in(resources[:account][:'/account/verify_credentials'])
18
+ end
19
+
20
+ def friend_ids
21
+ extract_remaining_and_reset_in(resources[:friends][:'/friends/ids'])
22
+ end
23
+
24
+ def follower_ids
25
+ extract_remaining_and_reset_in(resources[:followers][:'/followers/ids'])
26
+ end
27
+
28
+ def users
29
+ extract_remaining_and_reset_in(resources[:users][:'/users/lookup'])
30
+ end
31
+
32
+ def to_h
33
+ {
34
+ verify_credentials: verify_credentials,
35
+ friend_ids: friend_ids,
36
+ follower_ids: follower_ids,
37
+ users: users
38
+ }
39
+ end
40
+
41
+ def inspect
42
+ 'verify_credentials ' + verify_credentials.inspect +
43
+ ' friend_ids ' + friend_ids.inspect +
44
+ ' follower_ids ' + follower_ids.inspect +
45
+ ' users ' + users.inspect
46
+ end
47
+
48
+ private
49
+
50
+ def extract_remaining_and_reset_in(limit)
51
+ {remaining: limit[:remaining], reset_in: (Time.at(limit[:reset]) - Time.now).round}
52
+ end
53
+ end
54
+ end
55
+ end
56
+
@@ -4,14 +4,15 @@ require 'twitter_with_auto_pagination/rest/lists'
4
4
  require 'twitter_with_auto_pagination/rest/search'
5
5
  require 'twitter_with_auto_pagination/rest/timelines'
6
6
  require 'twitter_with_auto_pagination/rest/users'
7
- require 'twitter_with_auto_pagination/rest/uncategorized'
8
7
 
9
- require 'twitter_with_auto_pagination/rest/extension/clusters'
10
- require 'twitter_with_auto_pagination/rest/extension/favoriting'
11
- require 'twitter_with_auto_pagination/rest/extension/friends_and_followers'
12
- require 'twitter_with_auto_pagination/rest/extension/replying'
13
- require 'twitter_with_auto_pagination/rest/extension/unfollowing'
14
- require 'twitter_with_auto_pagination/rest/extension/users'
8
+ # require 'twitter_with_auto_pagination/rest/extension/clusters'
9
+ # require 'twitter_with_auto_pagination/rest/extension/favoriting'
10
+ # require 'twitter_with_auto_pagination/rest/extension/friends_and_followers'
11
+ # require 'twitter_with_auto_pagination/rest/extension/replying'
12
+ # require 'twitter_with_auto_pagination/rest/extension/unfollowing'
13
+ # require 'twitter_with_auto_pagination/rest/extension/users'
14
+
15
+ require 'twitter_with_auto_pagination/caching_and_logging'
15
16
 
16
17
  module TwitterWithAutoPagination
17
18
  module REST
@@ -22,14 +23,15 @@ module TwitterWithAutoPagination
22
23
  include TwitterWithAutoPagination::REST::Search
23
24
  include TwitterWithAutoPagination::REST::Timelines
24
25
  include TwitterWithAutoPagination::REST::Users
25
- include TwitterWithAutoPagination::REST::Uncategorized
26
26
 
27
- include TwitterWithAutoPagination::REST::Extension::Clusters
28
- include TwitterWithAutoPagination::REST::Extension::Favoriting
29
- include TwitterWithAutoPagination::REST::Extension::FriendsAndFollowers
30
- include TwitterWithAutoPagination::REST::Extension::Replying
31
- include TwitterWithAutoPagination::REST::Extension::Unfollowing
32
- include TwitterWithAutoPagination::REST::Extension::Users
27
+ # include TwitterWithAutoPagination::REST::Extension::Clusters
28
+ # include TwitterWithAutoPagination::REST::Extension::Favoriting
29
+ # include TwitterWithAutoPagination::REST::Extension::FriendsAndFollowers
30
+ # include TwitterWithAutoPagination::REST::Extension::Replying
31
+ # include TwitterWithAutoPagination::REST::Extension::Unfollowing
32
+ # include TwitterWithAutoPagination::REST::Extension::Users
33
+
34
+ include TwitterWithAutoPagination::CachingAndLogging
33
35
  end
34
36
  end
35
37
  end
@@ -17,13 +17,6 @@ module TwitterWithAutoPagination
17
17
  result
18
18
  end
19
19
 
20
- def friends_and_followers(*args)
21
- _fetch_parallelly(
22
- [
23
- {method: :friends, args: args},
24
- {method: :followers, args: args}])
25
- end
26
-
27
20
  def friends_followers_and_statuses(*args)
28
21
  _fetch_parallelly(
29
22
  [
@@ -5,13 +5,19 @@ module TwitterWithAutoPagination
5
5
  module Favorites
6
6
  include TwitterWithAutoPagination::REST::Utils
7
7
 
8
- def favorites(*args)
9
- options = {count: 100, call_limit: 3}.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_max_id(method(__method__).super_method, *args, options).map { |s| s.attrs }
14
- end
8
+ MAX_TWEETS_PER_REQUEST = 100
9
+
10
+ %i(favorites).each do |name|
11
+ define_method(name) do |*args|
12
+ options = args.extract_options!
13
+ call_limit = calc_call_limit(options.delete(:count), MAX_TWEETS_PER_REQUEST)
14
+ options = {count: MAX_TWEETS_PER_REQUEST, result_type: :recent, call_count: 0, call_limit: call_limit}.merge(options)
15
+
16
+ collect_with_max_id do |max_id|
17
+ options[:max_id] = max_id unless max_id.nil?
18
+ options[:call_count] += 1
19
+ twitter.send(name, *args, options) if options[:call_count] <= options[:call_limit]
20
+ end.map(&:attrs)
15
21
  end
16
22
  end
17
23
  end