ex_twitter 0.0.1 → 0.0.2
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 +7 -0
- data/Rakefile +9 -0
- data/ex_twitter.gemspec +3 -1
- data/lib/ex_twitter.rb +148 -263
- data/spec/ex_twitter_spec.rb +70 -0
- data/spec/helper.rb +8 -0
- metadata +21 -27
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 4bd5d8eb5590d0a1eebb86ac2a6d3098c1272467
|
4
|
+
data.tar.gz: 5c8b5655e88e08dce94069e5850d6c682c7a810a
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 7e7b2026369bc3308a1982d513aff1f2fda23005054220a6bb0fbb791b903a367bc6a2fa0366e5026b12ef6f5e06e7d12ff6c64338e084d39f8625eb2c80b945
|
7
|
+
data.tar.gz: f7c0d73420daa1ae4d7121f982f1bbf3e6aca81d799bd8ed703504fc94a3a5009a4e47544082a39126af9a20814c7dba9e2823c4577985ddec94afd4db4ae91e
|
data/Rakefile
CHANGED
data/ex_twitter.gemspec
CHANGED
@@ -5,7 +5,9 @@ Gem::Specification.new do |spec|
|
|
5
5
|
spec.add_dependency 'twitter'
|
6
6
|
spec.add_dependency 'activesupport'
|
7
7
|
spec.add_dependency 'parallel'
|
8
|
+
|
8
9
|
spec.add_development_dependency 'bundler'
|
10
|
+
|
9
11
|
spec.authors = ['Shinohara Teruki']
|
10
12
|
spec.description = %q(A wrapper to the Twitter gem.)
|
11
13
|
spec.email = %w[ts_3156@yahoo.co.jp]
|
@@ -18,5 +20,5 @@ Gem::Specification.new do |spec|
|
|
18
20
|
spec.require_paths = %w[lib]
|
19
21
|
spec.summary = spec.description
|
20
22
|
spec.test_files = Dir.glob('spec/**/*')
|
21
|
-
spec.version = '0.0.
|
23
|
+
spec.version = '0.0.2'
|
22
24
|
end
|
data/lib/ex_twitter.rb
CHANGED
@@ -1,312 +1,219 @@
|
|
1
|
-
# -*- coding: utf-8 -*-
|
2
1
|
require 'twitter'
|
3
2
|
require 'yaml'
|
4
3
|
require 'active_support'
|
5
4
|
require 'parallel'
|
5
|
+
require 'logger'
|
6
6
|
|
7
7
|
# extended twitter
|
8
8
|
class ExTwitter < Twitter::REST::Client
|
9
|
-
attr_accessor :cache, :cache_expires_in
|
9
|
+
attr_accessor :cache, :cache_expires_in, :max_attempts, :wait, :auto_paginate, :logger
|
10
10
|
|
11
11
|
MAX_ATTEMPTS = 1
|
12
12
|
WAIT = false
|
13
13
|
CACHE_EXPIRES_IN = 300
|
14
14
|
|
15
|
+
def self.get_config_value(config)
|
16
|
+
return {} if config.nil?
|
17
|
+
{
|
18
|
+
consumer_key: config['consumer_key'],
|
19
|
+
consumer_secret: config['consumer_secret'],
|
20
|
+
access_token: config['access_token'],
|
21
|
+
access_token_secret: config['access_token_secret']
|
22
|
+
}
|
23
|
+
end
|
24
|
+
|
15
25
|
def initialize(config={})
|
26
|
+
if config.empty?
|
27
|
+
yml_config = if File.exists?(File.expand_path('./', 'config.yml'))
|
28
|
+
YAML.load_file('config.yml')
|
29
|
+
elsif File.exists?(File.expand_path('./config/', 'config.yml'))
|
30
|
+
YAML.load_file('config/config.yml')
|
31
|
+
end
|
32
|
+
config = self.class.get_config_value(yml_config)
|
33
|
+
end
|
34
|
+
|
35
|
+
self.auto_paginate = (config[:auto_paginate] || true)
|
36
|
+
self.max_attempts = (config[:max_attempts] || MAX_ATTEMPTS)
|
37
|
+
self.wait = (config[:wait] || WAIT)
|
16
38
|
self.cache_expires_in = (config[:cache_expires_in] || CACHE_EXPIRES_IN)
|
17
39
|
self.cache = ActiveSupport::Cache::FileStore.new(File.join(Dir::pwd, 'ex_twitter_cache'),
|
18
40
|
{expires_in: self.cache_expires_in, race_condition_ttl: self.cache_expires_in})
|
41
|
+
|
42
|
+
self.logger = Logger.new(STDOUT)
|
43
|
+
self.logger.level = Logger::DEBUG
|
19
44
|
super
|
20
45
|
end
|
21
46
|
|
22
47
|
def read(key)
|
23
48
|
self.cache.read(key)
|
24
49
|
rescue => e
|
25
|
-
|
50
|
+
logger.warn "in read #{key} #{e.inspect}"
|
26
51
|
nil
|
27
52
|
end
|
28
53
|
|
29
54
|
def write(key, value)
|
30
55
|
self.cache.write(key, value)
|
31
56
|
rescue => e
|
32
|
-
|
57
|
+
logger.warn "in write #{key} #{value} #{e.inspect}"
|
33
58
|
false
|
34
59
|
end
|
35
60
|
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
61
|
+
# githubでは、レスポンスが次のエンドポイントを知っているため、ブロックをデータの結合のみに使っている。引数ではAPI名を渡している。
|
62
|
+
# 一方、twitterでは、レスポンスが次のエンドポイントを知らないため、ブロック内にAPI名を持ち、再帰的にブロックを呼び出している。
|
63
|
+
# また、再帰を使うため、引数をコレクションの引き渡しに使ってしまっている。(この問題については通常のループを使えば解決する)
|
64
|
+
#
|
65
|
+
# APIは通常繰り返しの呼び出しになるため、API名は常に同じものを保持しておき、ブロックはデータの結合に使った方が無駄がなく自由度が高い。
|
66
|
+
# また、再帰ではなく通常のループを使った方が分かりやすい。
|
67
|
+
#
|
68
|
+
# このgithub方式を実現するためには、メソッド名(例:user_timeline)を渡し、ループさせるか、user_timelineが内部で使っている
|
69
|
+
# objects_from_response_with_userをループさせればよい。
|
70
|
+
#
|
71
|
+
# この2種類の方法を比較した場合、より外側のレイヤーを使った方が、使うライブラリの内部情報に依存しなくなるため、好ましい。
|
72
|
+
#
|
73
|
+
# twitterで再帰方式がとられている理由は、おそらく、一般ユーザー向けのサンプルでメソッド名を渡すようなリフレクションを避けるため、
|
74
|
+
# なのかもしれない。
|
75
|
+
def collect_with_max_id(method_name, *args, &block)
|
76
|
+
options = args.last.is_a?(Hash) ? args.pop : {}
|
77
|
+
max_calls = options.delete(:max_calls) || 3
|
78
|
+
data = last_response = send(method_name, *args, options)
|
79
|
+
logger.info "#{method_name}, #{args.inspect} #{options.inspect}"
|
47
80
|
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
81
|
+
if auto_paginate
|
82
|
+
num_attempts = 0
|
83
|
+
(max_calls - 1).times do
|
84
|
+
break unless last_response.any?
|
52
85
|
|
53
|
-
|
54
|
-
num_attempts = 0
|
55
|
-
options = {count: 200, include_rts: true}
|
56
|
-
begin
|
57
|
-
num_attempts += 1
|
58
|
-
[user_timeline(user, options), nil]
|
59
|
-
rescue Twitter::Error::TooManyRequests => e
|
60
|
-
if num_attempts <= MAX_ATTEMPTS
|
61
|
-
if WAIT
|
62
|
-
sleep e.rate_limit.reset_in
|
63
|
-
retry
|
64
|
-
else
|
65
|
-
puts "retry #{e.rate_limit.reset_in} seconds later"
|
66
|
-
[[], e]
|
67
|
-
end
|
68
|
-
else
|
69
|
-
puts "fail. num_attempts > MAX_ATTEMPTS(=#{MAX_ATTEMPTS})"
|
70
|
-
[[], e]
|
71
|
-
end
|
72
|
-
rescue => e
|
73
|
-
if num_attempts <= MAX_ATTEMPTS
|
74
|
-
retry
|
75
|
-
else
|
76
|
-
puts "fail. num_attempts > MAX_ATTEMPTS(=#{MAX_ATTEMPTS}), something error #{e.inspect}"
|
77
|
-
[[], e]
|
78
|
-
end
|
79
|
-
end
|
80
|
-
end
|
86
|
+
options[:max_id] = last_response.last.id - 1
|
81
87
|
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
retry
|
88
|
+
begin
|
89
|
+
last_response = send(method_name, *args, options)
|
90
|
+
logger.info "#{method_name}, #{args.inspect} #{options.inspect}"
|
91
|
+
rescue Twitter::Error::TooManyRequests => e
|
92
|
+
if num_attempts <= MAX_ATTEMPTS
|
93
|
+
if WAIT
|
94
|
+
sleep e.rate_limit.reset_in
|
95
|
+
num_attempts += 1
|
96
|
+
retry
|
97
|
+
else
|
98
|
+
logger.warn "retry #{e.rate_limit.reset_in} seconds later, #{e.inspect}"
|
99
|
+
end
|
95
100
|
else
|
96
|
-
|
97
|
-
[[], e]
|
101
|
+
logger.warn "fail. num_attempts > MAX_ATTEMPTS(=#{MAX_ATTEMPTS}), #{e.inspect}"
|
98
102
|
end
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
rescue => e
|
104
|
-
if num_attempts <= MAX_ATTEMPTS
|
105
|
-
retry
|
106
|
-
else
|
107
|
-
puts "fail. num_attempts > MAX_ATTEMPTS(=#{MAX_ATTEMPTS}), something error #{e.inspect}"
|
108
|
-
[[], e]
|
109
|
-
end
|
110
|
-
end
|
111
|
-
end
|
112
|
-
end
|
113
|
-
|
114
|
-
def get_all_friends(user=nil)
|
115
|
-
num_attempts = 0
|
116
|
-
collect_with_cursor do |cursor|
|
117
|
-
options = {count: 200, include_user_entities: true}
|
118
|
-
options[:cursor] = cursor unless cursor.nil?
|
119
|
-
cache_key = "#{self.class.name}:#{__callee__}:#{user}:#{options}"
|
120
|
-
|
121
|
-
cache = self.read(cache_key)
|
122
|
-
next [cache, nil] unless cache.nil?
|
123
|
-
begin
|
124
|
-
num_attempts += 1
|
125
|
-
object = friends(user, options)
|
126
|
-
self.write(cache_key, object.attrs)
|
127
|
-
[object.attrs, nil]
|
128
|
-
rescue Twitter::Error::TooManyRequests => e
|
129
|
-
if num_attempts <= MAX_ATTEMPTS
|
130
|
-
if WAIT
|
131
|
-
sleep e.rate_limit.reset_in
|
103
|
+
rescue => e
|
104
|
+
if num_attempts <= MAX_ATTEMPTS
|
105
|
+
logger.warn "retry till num_attempts > MAX_ATTEMPTS(=#{MAX_ATTEMPTS}), #{e.inspect}"
|
106
|
+
num_attempts += 1
|
132
107
|
retry
|
133
108
|
else
|
134
|
-
|
135
|
-
[{}, e]
|
109
|
+
logger.warn "fail. num_attempts > MAX_ATTEMPTS(=#{MAX_ATTEMPTS}), something error #{e.inspect}"
|
136
110
|
end
|
137
|
-
else
|
138
|
-
puts "fail. num_attempts > MAX_ATTEMPTS(=#{MAX_ATTEMPTS})"
|
139
|
-
[{}, e]
|
140
111
|
end
|
141
|
-
rescue => e
|
142
|
-
if num_attempts <= MAX_ATTEMPTS
|
143
|
-
retry
|
144
|
-
else
|
145
|
-
puts "fail. num_attempts > MAX_ATTEMPTS(=#{MAX_ATTEMPTS}), something error #{e.inspect}"
|
146
|
-
[{}, e]
|
147
|
-
end
|
148
|
-
end
|
149
|
-
end
|
150
|
-
end
|
151
112
|
|
152
|
-
|
153
|
-
|
154
|
-
collect_with_cursor do |cursor|
|
155
|
-
options = {count: 200, include_user_entities: true}
|
156
|
-
options[:cursor] = cursor unless cursor.nil?
|
157
|
-
cache_key = "#{self.class.name}:#{__callee__}:#{user}:#{options}"
|
158
|
-
|
159
|
-
cache = self.read(cache_key)
|
160
|
-
next [cache, nil] unless cache.nil?
|
161
|
-
begin
|
162
|
-
num_attempts += 1
|
163
|
-
object = followers(user, options)
|
164
|
-
self.write(cache_key, object.attrs)
|
165
|
-
[object.attrs, nil]
|
166
|
-
rescue Twitter::Error::TooManyRequests => e
|
167
|
-
if num_attempts <= MAX_ATTEMPTS
|
168
|
-
if WAIT
|
169
|
-
sleep e.rate_limit.reset_in
|
170
|
-
retry
|
171
|
-
else
|
172
|
-
puts "retry #{e.rate_limit.reset_in} seconds later"
|
173
|
-
[{}, e]
|
174
|
-
end
|
175
|
-
else
|
176
|
-
puts "fail. num_attempts > MAX_ATTEMPTS(=#{MAX_ATTEMPTS})"
|
177
|
-
[{}, e]
|
178
|
-
end
|
179
|
-
rescue => e
|
180
|
-
if num_attempts <= MAX_ATTEMPTS
|
181
|
-
retry
|
113
|
+
if block_given?
|
114
|
+
yield(data, last_response)
|
182
115
|
else
|
183
|
-
|
184
|
-
[{}, e]
|
116
|
+
data.concat(last_response) if last_response.is_a?(Array)
|
185
117
|
end
|
186
118
|
end
|
187
119
|
end
|
188
|
-
end
|
189
120
|
|
190
|
-
|
191
|
-
num_attempts = 0
|
192
|
-
collect_with_cursor do |cursor|
|
193
|
-
options = {count: 5000}
|
194
|
-
options[:cursor] = cursor unless cursor.nil?
|
195
|
-
cache_key = "#{self.class.name}:#{__callee__}:#{user}:#{options}"
|
196
|
-
|
197
|
-
cache = self.read(cache_key)
|
198
|
-
next [cache, nil] unless cache.nil?
|
199
|
-
begin
|
200
|
-
num_attempts += 1
|
201
|
-
object = friend_ids(user, options)
|
202
|
-
self.write(cache_key, object.attrs)
|
203
|
-
[object.attrs, nil]
|
204
|
-
rescue Twitter::Error::TooManyRequests => e
|
205
|
-
if num_attempts <= MAX_ATTEMPTS
|
206
|
-
if WAIT
|
207
|
-
sleep e.rate_limit.reset_in
|
208
|
-
retry
|
209
|
-
else
|
210
|
-
puts "retry #{e.rate_limit.reset_in} seconds later"
|
211
|
-
[{}, e]
|
212
|
-
end
|
213
|
-
else
|
214
|
-
puts "fail. num_attempts > MAX_ATTEMPTS(=#{MAX_ATTEMPTS})"
|
215
|
-
[{}, e]
|
216
|
-
end
|
217
|
-
rescue => e
|
218
|
-
if num_attempts <= MAX_ATTEMPTS
|
219
|
-
retry
|
220
|
-
else
|
221
|
-
puts "fail. num_attempts > MAX_ATTEMPTS(=#{MAX_ATTEMPTS}), something error #{e.inspect}"
|
222
|
-
[{}, e]
|
223
|
-
end
|
224
|
-
end
|
225
|
-
end
|
121
|
+
data
|
226
122
|
end
|
227
123
|
|
228
|
-
def
|
229
|
-
|
230
|
-
|
231
|
-
|
232
|
-
|
233
|
-
|
234
|
-
|
235
|
-
cache = self.read(cache_key)
|
236
|
-
next [cache, nil] unless cache.nil?
|
237
|
-
begin
|
238
|
-
num_attempts += 1
|
239
|
-
object = follower_ids(user, options)
|
240
|
-
self.write(cache_key, object.attrs)
|
241
|
-
[object.attrs, nil]
|
242
|
-
rescue Twitter::Error::TooManyRequests => e
|
243
|
-
if num_attempts <= MAX_ATTEMPTS
|
244
|
-
if WAIT
|
245
|
-
sleep e.rate_limit.reset_in
|
246
|
-
retry
|
247
|
-
else
|
248
|
-
puts "retry #{e.rate_limit.reset_in} seconds later"
|
249
|
-
[{}, e]
|
250
|
-
end
|
251
|
-
else
|
252
|
-
puts "fail. num_attempts > MAX_ATTEMPTS(=#{MAX_ATTEMPTS})"
|
253
|
-
[{}, e]
|
254
|
-
end
|
255
|
-
rescue => e
|
256
|
-
if num_attempts <= MAX_ATTEMPTS
|
257
|
-
retry
|
258
|
-
else
|
259
|
-
puts "fail. num_attempts > MAX_ATTEMPTS(=#{MAX_ATTEMPTS}), something error #{e.inspect}"
|
260
|
-
[{}, e]
|
261
|
-
end
|
262
|
-
end
|
263
|
-
end
|
264
|
-
end
|
124
|
+
def collect_with_cursor(method_name, *args, &block)
|
125
|
+
options = args.last.is_a?(Hash) ? args.pop : {}
|
126
|
+
max_calls = options.delete(:max_calls) || 3
|
127
|
+
last_response = send(method_name, *args, options).attrs
|
128
|
+
logger.info "#{method_name}, #{args.inspect} #{options.inspect}"
|
129
|
+
data = last_response[:users] || last_response[:ids]
|
265
130
|
|
266
|
-
|
267
|
-
|
268
|
-
|
269
|
-
|
270
|
-
|
131
|
+
if auto_paginate
|
132
|
+
num_attempts = 0
|
133
|
+
(max_calls - 1).times do
|
134
|
+
next_cursor = last_response[:next_cursor]
|
135
|
+
break if !next_cursor || next_cursor == 0
|
136
|
+
|
137
|
+
options[:cursor] = next_cursor
|
271
138
|
|
272
|
-
num_attempts = 0
|
273
|
-
processed_users = []
|
274
|
-
Parallel.each_with_index(ids_per_worker, in_threads: ids_per_worker.size) do |ids, i|
|
275
|
-
cache_key = "#{self.class.name}:#{__callee__}:#{i}:#{ids}"
|
276
|
-
cache = self.read(cache_key)
|
277
|
-
if cache.nil?
|
278
139
|
begin
|
279
|
-
|
280
|
-
|
281
|
-
self.write(cache_key, object)
|
282
|
-
processed_users << object
|
140
|
+
last_response = send(method_name, *args, options).attrs
|
141
|
+
logger.info "#{method_name}, #{args.inspect} #{options.inspect}"
|
283
142
|
rescue Twitter::Error::TooManyRequests => e
|
284
143
|
if num_attempts <= MAX_ATTEMPTS
|
285
144
|
if WAIT
|
286
145
|
sleep e.rate_limit.reset_in
|
146
|
+
num_attempts += 1
|
287
147
|
retry
|
288
148
|
else
|
289
|
-
|
290
|
-
{i: i, users: [], e: e}
|
149
|
+
logger.warn "retry #{e.rate_limit.reset_in} seconds later, #{e.inspect}"
|
291
150
|
end
|
292
151
|
else
|
293
|
-
|
294
|
-
{i: i, users: [], e: e}
|
152
|
+
logger.warn "fail. num_attempts > MAX_ATTEMPTS(=#{MAX_ATTEMPTS}), #{e.inspect}"
|
295
153
|
end
|
296
154
|
rescue => e
|
297
155
|
if num_attempts <= MAX_ATTEMPTS
|
156
|
+
logger.warn "retry till num_attempts > MAX_ATTEMPTS(=#{MAX_ATTEMPTS}), #{e.inspect}"
|
157
|
+
num_attempts += 1
|
298
158
|
retry
|
299
159
|
else
|
300
|
-
|
301
|
-
{i: i, users: [], e: e}
|
160
|
+
logger.warn "fail. num_attempts > MAX_ATTEMPTS(=#{MAX_ATTEMPTS}), something error #{e.inspect}"
|
302
161
|
end
|
303
162
|
end
|
304
|
-
|
305
|
-
|
163
|
+
|
164
|
+
if block_given?
|
165
|
+
yield(data, last_response)
|
166
|
+
else
|
167
|
+
items = last_response[:users] || last_response[:ids]
|
168
|
+
data.concat(items) if items.is_a?(Array)
|
169
|
+
end
|
306
170
|
end
|
307
171
|
end
|
308
|
-
|
309
|
-
|
172
|
+
|
173
|
+
data
|
174
|
+
end
|
175
|
+
|
176
|
+
alias :old_user_timeline :user_timeline
|
177
|
+
def user_timeline(user = nil, options = {})
|
178
|
+
options = options.merge({count: 200, include_rts: true})
|
179
|
+
collect_with_max_id(:old_user_timeline, user, options)
|
180
|
+
end
|
181
|
+
|
182
|
+
alias :old_friends :friends
|
183
|
+
def friends(user = nil, options = {})
|
184
|
+
options = options.merge({count: 200, include_user_entities: true})
|
185
|
+
collect_with_cursor(:old_friends, user, options)
|
186
|
+
end
|
187
|
+
|
188
|
+
alias :old_followers :followers
|
189
|
+
def followers(user = nil, options = {})
|
190
|
+
options = options.merge({count: 200, include_user_entities: true})
|
191
|
+
collect_with_cursor(:old_followers, user, options)
|
192
|
+
end
|
193
|
+
|
194
|
+
alias :old_friend_ids :friend_ids
|
195
|
+
def friend_ids(user = nil, options = {})
|
196
|
+
options = options.merge({count: 5000})
|
197
|
+
collect_with_cursor(:old_friend_ids, user, options)
|
198
|
+
end
|
199
|
+
|
200
|
+
alias :old_follower_ids :follower_ids
|
201
|
+
def follower_ids(user = nil, options = {})
|
202
|
+
options = options.merge({count: 5000})
|
203
|
+
collect_with_cursor(:old_follower_ids, user, options)
|
204
|
+
end
|
205
|
+
|
206
|
+
alias :old_users :users
|
207
|
+
def users(ids, options = {})
|
208
|
+
ids_per_worker = ids.each_slice(100).to_a
|
209
|
+
processed_users = []
|
210
|
+
|
211
|
+
Parallel.each_with_index(ids_per_worker, in_threads: ids_per_worker.size) do |ids, i|
|
212
|
+
_users = {i: i, users: old_users(ids, options)}
|
213
|
+
processed_users << _users
|
214
|
+
end
|
215
|
+
|
216
|
+
processed_users.sort_by{|p| p[:i] }.map{|p| p[:users] }.flatten
|
310
217
|
end
|
311
218
|
|
312
219
|
# mentions_timeline is to fetch the timeline of Tweets mentioning the authenticated user
|
@@ -353,26 +260,4 @@ class ExTwitter < Twitter::REST::Client
|
|
353
260
|
end
|
354
261
|
end
|
355
262
|
|
356
|
-
if __FILE__ == $0
|
357
|
-
puts '--start--'
|
358
|
-
yml_config = YAML.load_file('config.yml')
|
359
|
-
config = {
|
360
|
-
consumer_key: yml_config['consumer_key'],
|
361
|
-
consumer_secret: yml_config['consumer_secret'],
|
362
|
-
access_token: yml_config['access_token'],
|
363
|
-
access_token_secret: yml_config['access_token_secret']
|
364
|
-
}
|
365
|
-
client = ExTwitter.new(config)
|
366
|
-
#followers, error = client.get_all_followers
|
367
|
-
#puts "#{followers.size} #{error.inspect}"
|
368
|
-
tweets, error = client.search_tweets_except_rt('#グラドル自画撮り部')
|
369
|
-
puts tweets.size
|
370
|
-
tweets.each do |t|
|
371
|
-
next if !t.media?
|
372
|
-
puts t.text
|
373
|
-
puts t.media[0].attrs
|
374
|
-
puts '----'
|
375
|
-
end
|
376
|
-
end
|
377
|
-
|
378
263
|
|
@@ -0,0 +1,70 @@
|
|
1
|
+
require 'helper'
|
2
|
+
|
3
|
+
describe ExTwitter do
|
4
|
+
let(:config) {
|
5
|
+
{
|
6
|
+
consumer_key: 'CK',
|
7
|
+
consumer_secret: 'CS',
|
8
|
+
access_token: 'AT',
|
9
|
+
access_token_secret: 'ATS'
|
10
|
+
}
|
11
|
+
}
|
12
|
+
let(:client) { ExTwitter.new(config) }
|
13
|
+
|
14
|
+
describe '#initialize' do
|
15
|
+
end
|
16
|
+
|
17
|
+
describe '#read' do
|
18
|
+
end
|
19
|
+
|
20
|
+
describe '#write' do
|
21
|
+
end
|
22
|
+
|
23
|
+
describe '#collect_with_max_id' do
|
24
|
+
end
|
25
|
+
|
26
|
+
describe '#collect_with_cursor' do
|
27
|
+
end
|
28
|
+
|
29
|
+
describe '#user_timeline' do
|
30
|
+
it 'call collect_with_max_id' do
|
31
|
+
expect(client).to receive(:collect_with_max_id)
|
32
|
+
client.user_timeline
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
describe '#friends' do
|
37
|
+
it 'call collect_with_cursor' do
|
38
|
+
expect(client).to receive(:collect_with_cursor)
|
39
|
+
client.friends
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
describe '#followers' do
|
44
|
+
it 'call collect_with_cursor' do
|
45
|
+
expect(client).to receive(:collect_with_cursor)
|
46
|
+
client.followers
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
describe '#friend_ids' do
|
51
|
+
it 'call collect_with_cursor' do
|
52
|
+
expect(client).to receive(:collect_with_cursor)
|
53
|
+
client.friend_ids
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
describe '#follower_ids' do
|
58
|
+
it 'call collect_with_cursor' do
|
59
|
+
expect(client).to receive(:collect_with_cursor)
|
60
|
+
client.follower_ids
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
describe '#users' do
|
65
|
+
it 'call old_users' do
|
66
|
+
expect(client).to receive(:old_users)
|
67
|
+
client.users([1, 2, 3])
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
data/spec/helper.rb
ADDED
metadata
CHANGED
@@ -1,78 +1,69 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: ex_twitter
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
5
|
-
prerelease:
|
4
|
+
version: 0.0.2
|
6
5
|
platform: ruby
|
7
6
|
authors:
|
8
7
|
- Shinohara Teruki
|
9
8
|
autorequire:
|
10
9
|
bindir: bin
|
11
10
|
cert_chain: []
|
12
|
-
date: 2014-
|
11
|
+
date: 2014-12-16 00:00:00.000000000 Z
|
13
12
|
dependencies:
|
14
13
|
- !ruby/object:Gem::Dependency
|
15
14
|
name: twitter
|
16
15
|
requirement: !ruby/object:Gem::Requirement
|
17
|
-
none: false
|
18
16
|
requirements:
|
19
|
-
- -
|
17
|
+
- - ">="
|
20
18
|
- !ruby/object:Gem::Version
|
21
19
|
version: '0'
|
22
20
|
type: :runtime
|
23
21
|
prerelease: false
|
24
22
|
version_requirements: !ruby/object:Gem::Requirement
|
25
|
-
none: false
|
26
23
|
requirements:
|
27
|
-
- -
|
24
|
+
- - ">="
|
28
25
|
- !ruby/object:Gem::Version
|
29
26
|
version: '0'
|
30
27
|
- !ruby/object:Gem::Dependency
|
31
28
|
name: activesupport
|
32
29
|
requirement: !ruby/object:Gem::Requirement
|
33
|
-
none: false
|
34
30
|
requirements:
|
35
|
-
- -
|
31
|
+
- - ">="
|
36
32
|
- !ruby/object:Gem::Version
|
37
33
|
version: '0'
|
38
34
|
type: :runtime
|
39
35
|
prerelease: false
|
40
36
|
version_requirements: !ruby/object:Gem::Requirement
|
41
|
-
none: false
|
42
37
|
requirements:
|
43
|
-
- -
|
38
|
+
- - ">="
|
44
39
|
- !ruby/object:Gem::Version
|
45
40
|
version: '0'
|
46
41
|
- !ruby/object:Gem::Dependency
|
47
42
|
name: parallel
|
48
43
|
requirement: !ruby/object:Gem::Requirement
|
49
|
-
none: false
|
50
44
|
requirements:
|
51
|
-
- -
|
45
|
+
- - ">="
|
52
46
|
- !ruby/object:Gem::Version
|
53
47
|
version: '0'
|
54
48
|
type: :runtime
|
55
49
|
prerelease: false
|
56
50
|
version_requirements: !ruby/object:Gem::Requirement
|
57
|
-
none: false
|
58
51
|
requirements:
|
59
|
-
- -
|
52
|
+
- - ">="
|
60
53
|
- !ruby/object:Gem::Version
|
61
54
|
version: '0'
|
62
55
|
- !ruby/object:Gem::Dependency
|
63
56
|
name: bundler
|
64
57
|
requirement: !ruby/object:Gem::Requirement
|
65
|
-
none: false
|
66
58
|
requirements:
|
67
|
-
- -
|
59
|
+
- - ">="
|
68
60
|
- !ruby/object:Gem::Version
|
69
61
|
version: '0'
|
70
62
|
type: :development
|
71
63
|
prerelease: false
|
72
64
|
version_requirements: !ruby/object:Gem::Requirement
|
73
|
-
none: false
|
74
65
|
requirements:
|
75
|
-
- -
|
66
|
+
- - ">="
|
76
67
|
- !ruby/object:Gem::Version
|
77
68
|
version: '0'
|
78
69
|
description: A wrapper to the Twitter gem.
|
@@ -86,31 +77,34 @@ files:
|
|
86
77
|
- README.md
|
87
78
|
- Rakefile
|
88
79
|
- ex_twitter.gemspec
|
89
|
-
- lib/ex_twitter.rb
|
90
80
|
- lib/ex_stream.rb
|
81
|
+
- lib/ex_twitter.rb
|
82
|
+
- spec/ex_twitter_spec.rb
|
83
|
+
- spec/helper.rb
|
91
84
|
homepage: http://github.com/ts-3156/ex-twitter/
|
92
85
|
licenses:
|
93
86
|
- MIT
|
87
|
+
metadata: {}
|
94
88
|
post_install_message:
|
95
89
|
rdoc_options: []
|
96
90
|
require_paths:
|
97
91
|
- lib
|
98
92
|
required_ruby_version: !ruby/object:Gem::Requirement
|
99
|
-
none: false
|
100
93
|
requirements:
|
101
|
-
- -
|
94
|
+
- - ">="
|
102
95
|
- !ruby/object:Gem::Version
|
103
96
|
version: '0'
|
104
97
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
105
|
-
none: false
|
106
98
|
requirements:
|
107
|
-
- -
|
99
|
+
- - ">="
|
108
100
|
- !ruby/object:Gem::Version
|
109
101
|
version: '0'
|
110
102
|
requirements: []
|
111
103
|
rubyforge_project:
|
112
|
-
rubygems_version:
|
104
|
+
rubygems_version: 2.2.2
|
113
105
|
signing_key:
|
114
|
-
specification_version:
|
106
|
+
specification_version: 4
|
115
107
|
summary: A wrapper to the Twitter gem.
|
116
|
-
test_files:
|
108
|
+
test_files:
|
109
|
+
- spec/ex_twitter_spec.rb
|
110
|
+
- spec/helper.rb
|