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