twitter_friendly 0.1.0 → 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.gitignore +4 -0
- data/.rspec +2 -0
- data/.travis.yml +41 -0
- data/Gemfile.lock +44 -5
- data/README.md +163 -11
- data/bin/test/friends_and_followers.rb +95 -0
- data/gemfiles/ar_4.2.gemfile +5 -0
- data/gemfiles/ar_5.1.gemfile +5 -0
- data/gemfiles/ar_5.2.gemfile +5 -0
- data/lib/twitter_friendly.rb +12 -0
- data/lib/twitter_friendly/cache.rb +48 -0
- data/lib/twitter_friendly/cache_key.rb +64 -0
- data/lib/twitter_friendly/caching.rb +86 -0
- data/lib/twitter_friendly/client.rb +52 -0
- data/lib/twitter_friendly/log_subscriber.rb +109 -0
- data/lib/twitter_friendly/logger.rb +18 -0
- data/lib/twitter_friendly/rate_limit.rb +81 -0
- data/lib/twitter_friendly/rest/api.rb +34 -0
- data/lib/twitter_friendly/rest/base.rb +32 -0
- data/lib/twitter_friendly/rest/collector.rb +78 -0
- data/lib/twitter_friendly/rest/favorites.rb +15 -0
- data/lib/twitter_friendly/rest/friends_and_followers.rb +64 -0
- data/lib/twitter_friendly/rest/lists.rb +22 -0
- data/lib/twitter_friendly/rest/parallel.rb +30 -0
- data/lib/twitter_friendly/rest/search.rb +16 -0
- data/lib/twitter_friendly/rest/timelines.rb +15 -0
- data/lib/twitter_friendly/rest/tweets.rb +13 -0
- data/lib/twitter_friendly/rest/users.rb +49 -0
- data/lib/twitter_friendly/rest/utils.rb +11 -0
- data/lib/twitter_friendly/serializer.rb +79 -0
- data/lib/twitter_friendly/utils.rb +6 -0
- data/lib/twitter_friendly/version.rb +1 -1
- data/twitter_friendly.gemspec +11 -2
- metadata +127 -10
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: cec5d0d0bab4de7c8cc59727c5c57ce632b76c6e40c3684a66cce539cbb509ef
|
4
|
+
data.tar.gz: d37e61958513f084b5eed73f68f9d073202dd58e774138072cb6a1b28b3e70bc
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 376dbce938d1e5baf735fee3ff09a869ce2b2b562c159046c1a92c37d9e5408d2f5ae4ce005f98eb62d5dbb6f10b31229b15bd402a905c949ad9adbe38aa0aae
|
7
|
+
data.tar.gz: 93f140337e9a317165f974e65a992e34a42d1f278add7add063379c882c51aeef54ccb179cef419e02ad21956f3d9aad2f370fad211d5bb680ab587fa8298eeb
|
data/.gitignore
CHANGED
data/.rspec
ADDED
data/.travis.yml
ADDED
@@ -0,0 +1,41 @@
|
|
1
|
+
language: ruby
|
2
|
+
cache: bundler
|
3
|
+
|
4
|
+
# For ruby compatibility, we test the highest and lowest minor versions only.
|
5
|
+
# For example, if our gemspec says `required_ruby_version = ">= 2.3.0"`, and
|
6
|
+
# ruby 2.5.0 has just been released, then we test 2.3 and 2.5, but not 2.4.
|
7
|
+
rvm:
|
8
|
+
- 2.3.8
|
9
|
+
- 2.4.5
|
10
|
+
- 2.5.3
|
11
|
+
|
12
|
+
env:
|
13
|
+
global:
|
14
|
+
- TRAVIS=true
|
15
|
+
|
16
|
+
# We want to use `sudo: false` because the container infrastructure is supposed
|
17
|
+
# to be faster, but Travis is having issues with containers lately ..
|
18
|
+
#
|
19
|
+
# > No output has been received in the last 10m0s
|
20
|
+
#
|
21
|
+
# .. and they recommend we use the VM infrastructure (`sudo: required`) in
|
22
|
+
# the meantime.
|
23
|
+
sudo: required
|
24
|
+
|
25
|
+
before_install:
|
26
|
+
- gem update bundler
|
27
|
+
|
28
|
+
gemfile:
|
29
|
+
- gemfiles/ar_4.2.gemfile
|
30
|
+
- gemfiles/ar_5.1.gemfile
|
31
|
+
- gemfiles/ar_5.2.gemfile
|
32
|
+
matrix:
|
33
|
+
exclude:
|
34
|
+
# optimization: don't test intermediate rubies (see above)
|
35
|
+
- rvm: 2.4.5
|
36
|
+
gemfile: gemfiles/ar_4.2.gemfile
|
37
|
+
- rvm: 2.4.5
|
38
|
+
gemfile: gemfiles/ar_5.1.gemfile
|
39
|
+
- rvm: 2.4.5
|
40
|
+
gemfile: gemfiles/ar_5.2.gemfile
|
41
|
+
fast_finish: true
|
data/Gemfile.lock
CHANGED
@@ -1,18 +1,29 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
twitter_friendly (0.
|
4
|
+
twitter_friendly (0.2.0)
|
5
|
+
activesupport (>= 4.2, < 6.0)
|
6
|
+
oj (~> 3.7.6)
|
5
7
|
parallel (~> 1.12.1)
|
6
8
|
twitter (~> 6.2.0)
|
7
9
|
|
8
10
|
GEM
|
9
11
|
remote: https://rubygems.org/
|
10
12
|
specs:
|
13
|
+
activesupport (5.2.2)
|
14
|
+
concurrent-ruby (~> 1.0, >= 1.0.2)
|
15
|
+
i18n (>= 0.7, < 2)
|
16
|
+
minitest (~> 5.1)
|
17
|
+
tzinfo (~> 1.1)
|
11
18
|
addressable (2.5.2)
|
12
19
|
public_suffix (>= 2.0.2, < 4.0)
|
13
20
|
buftok (0.2.0)
|
21
|
+
coderay (1.1.2)
|
22
|
+
concurrent-ruby (1.1.4)
|
23
|
+
diff-lcs (1.3)
|
14
24
|
domain_name (0.5.20180417)
|
15
25
|
unf (>= 0.0.5, < 1.0.0)
|
26
|
+
dotenv (2.6.0)
|
16
27
|
equalizer (0.0.11)
|
17
28
|
http (3.3.0)
|
18
29
|
addressable (~> 2.3)
|
@@ -23,13 +34,35 @@ GEM
|
|
23
34
|
domain_name (~> 0.5)
|
24
35
|
http-form_data (2.1.1)
|
25
36
|
http_parser.rb (0.6.0)
|
37
|
+
i18n (1.5.1)
|
38
|
+
concurrent-ruby (~> 1.0)
|
26
39
|
memoizable (0.4.2)
|
27
40
|
thread_safe (~> 0.3, >= 0.3.1)
|
41
|
+
method_source (0.9.2)
|
42
|
+
minitest (5.11.3)
|
28
43
|
multipart-post (2.0.0)
|
29
44
|
naught (1.1.0)
|
45
|
+
oj (3.7.6)
|
30
46
|
parallel (1.12.1)
|
47
|
+
pry (0.12.2)
|
48
|
+
coderay (~> 1.1.0)
|
49
|
+
method_source (~> 0.9.0)
|
31
50
|
public_suffix (3.0.3)
|
32
|
-
rake (
|
51
|
+
rake (12.3.2)
|
52
|
+
rb-readline (0.5.5)
|
53
|
+
rspec (3.8.0)
|
54
|
+
rspec-core (~> 3.8.0)
|
55
|
+
rspec-expectations (~> 3.8.0)
|
56
|
+
rspec-mocks (~> 3.8.0)
|
57
|
+
rspec-core (3.8.0)
|
58
|
+
rspec-support (~> 3.8.0)
|
59
|
+
rspec-expectations (3.8.2)
|
60
|
+
diff-lcs (>= 1.2.0, < 2.0)
|
61
|
+
rspec-support (~> 3.8.0)
|
62
|
+
rspec-mocks (3.8.0)
|
63
|
+
diff-lcs (>= 1.2.0, < 2.0)
|
64
|
+
rspec-support (~> 3.8.0)
|
65
|
+
rspec-support (3.8.0)
|
33
66
|
simple_oauth (0.3.1)
|
34
67
|
thread_safe (0.3.6)
|
35
68
|
twitter (6.2.0)
|
@@ -43,6 +76,8 @@ GEM
|
|
43
76
|
multipart-post (~> 2.0)
|
44
77
|
naught (~> 1.0)
|
45
78
|
simple_oauth (~> 0.3.0)
|
79
|
+
tzinfo (1.2.5)
|
80
|
+
thread_safe (~> 0.1)
|
46
81
|
unf (0.1.4)
|
47
82
|
unf_ext
|
48
83
|
unf_ext (0.0.7.5)
|
@@ -51,9 +86,13 @@ PLATFORMS
|
|
51
86
|
ruby
|
52
87
|
|
53
88
|
DEPENDENCIES
|
54
|
-
bundler
|
55
|
-
|
89
|
+
bundler
|
90
|
+
dotenv
|
91
|
+
pry
|
92
|
+
rake
|
93
|
+
rb-readline
|
94
|
+
rspec (~> 3.8)
|
56
95
|
twitter_friendly!
|
57
96
|
|
58
97
|
BUNDLED WITH
|
59
|
-
|
98
|
+
2.0.1
|
data/README.md
CHANGED
@@ -1,8 +1,13 @@
|
|
1
|
-
#
|
1
|
+
# twitter_friendly
|
2
2
|
|
3
|
-
|
3
|
+
[![Gem Version](https://badge.fury.io/rb/twitter_friendly.png)](https://badge.fury.io/rb/twitter_friendly)
|
4
|
+
[![Build Status](https://travis-ci.org/ts-3156/twitter_friendly.svg?branch=master)](https://travis-ci.org/ts-3156/twitter_friendly)
|
4
5
|
|
5
|
-
|
6
|
+
A twitter-friendly Ruby interface to the Twitter API. This twitter_friendly gem provides multiple features.
|
7
|
+
|
8
|
+
- Auto pagination
|
9
|
+
- Auto caching
|
10
|
+
- Parallelly fetching
|
6
11
|
|
7
12
|
## Installation
|
8
13
|
|
@@ -14,25 +19,172 @@ gem 'twitter_friendly'
|
|
14
19
|
|
15
20
|
And then execute:
|
16
21
|
|
17
|
-
|
22
|
+
```sh
|
23
|
+
$ bundle
|
24
|
+
```
|
18
25
|
|
19
26
|
Or install it yourself as:
|
20
27
|
|
21
|
-
|
28
|
+
```sh
|
29
|
+
$ gem install twitter_friendly
|
30
|
+
```
|
31
|
+
|
32
|
+
## Configuration
|
33
|
+
|
34
|
+
You can pass configuration options as a block to `TwitterFriendly::Client.new` just like the below.
|
35
|
+
|
36
|
+
```
|
37
|
+
client = TwitterFriendly::Client.new do |config|
|
38
|
+
config.consumer_key = "YOUR_CONSUMER_KEY"
|
39
|
+
config.consumer_secret = "YOUR_CONSUMER_SECRET"
|
40
|
+
config.access_token = "YOUR_ACCESS_TOKEN"
|
41
|
+
config.access_token_secret = "YOUR_ACCESS_SECRET"
|
42
|
+
end
|
43
|
+
```
|
44
|
+
|
45
|
+
## Useful features
|
46
|
+
|
47
|
+
After configuring a `client`, you can do the following things.
|
48
|
+
|
49
|
+
Fetch all friends's user IDs (by screen name or user ID, or by implicit authenticated user)
|
50
|
+
|
51
|
+
```ruby
|
52
|
+
ids = client.follower_ids('gem')
|
53
|
+
ids.size
|
54
|
+
# => 1741
|
55
|
+
```
|
56
|
+
|
57
|
+
As using a cache, it's super fast from the second time.
|
58
|
+
|
59
|
+
```ruby
|
60
|
+
Benchmark.bm 20 do |r|
|
61
|
+
r.report "Fetch follower_ids" do
|
62
|
+
client.follower_ids('gem')
|
63
|
+
end
|
64
|
+
r.report "(Cached)" do
|
65
|
+
client.follower_ids('gem')
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
# user system total real
|
70
|
+
# Fetch follower_ids 0.010330 0.003607 0.013937 ( 0.981068)
|
71
|
+
# (Cached) 0.000865 0.000153 0.001018 ( 0.001019) <- Roughly 900 times faster!
|
72
|
+
```
|
73
|
+
|
74
|
+
You don't need to write a boilerplate code as having auto pagination feature.
|
75
|
+
|
76
|
+
```ruby
|
77
|
+
users = client.follower_ids('a_user_has_many_friends')
|
78
|
+
users.size
|
79
|
+
# => 50000
|
80
|
+
```
|
81
|
+
|
82
|
+
If you don't use twitter_friendly gem, you must write the code like the below to fetch all follower's ids.
|
83
|
+
|
84
|
+
```ruby
|
85
|
+
def collect_with_max_id(collection=[], max_id=nil, &block)
|
86
|
+
response = yield(max_id)
|
87
|
+
collection += response
|
88
|
+
response.empty? ? collection.flatten : collect_with_max_id(collection, response.last.id - 1, &block)
|
89
|
+
end
|
90
|
+
|
91
|
+
ids =
|
92
|
+
collect_with_max_id do |max_id|
|
93
|
+
options = {count: 200, include_rts: true}
|
94
|
+
options[:max_id] = max_id unless max_id.nil?
|
95
|
+
client.follower_ids('user_name', options)
|
96
|
+
end
|
97
|
+
```
|
98
|
+
|
99
|
+
Additionally, twitter_friendly gem has a parallel execution feature.
|
100
|
+
|
101
|
+
```ruby
|
102
|
+
ids = [id1, id2, id3, ... , id1000]
|
103
|
+
|
104
|
+
Benchmark.bm 25 do |r|
|
105
|
+
r.report "Fetch users in parallel" do
|
106
|
+
client.users(ids)
|
107
|
+
end
|
108
|
+
|
109
|
+
client.cache.clear
|
22
110
|
|
23
|
-
|
111
|
+
r.report "Fetch users in serial" do
|
112
|
+
client.users(ids, parallel: false)
|
113
|
+
end
|
114
|
+
end
|
24
115
|
|
25
|
-
|
116
|
+
# user system total real
|
117
|
+
# Fetch users in parallel 0.271966 0.057981 0.329947 ( 2.675270) <- Super fast!
|
118
|
+
# Fetch users in serial 0.201375 0.044399 0.245774 ( 8.068372)
|
119
|
+
```
|
120
|
+
|
121
|
+
## Usage examples
|
122
|
+
|
123
|
+
Fetch all friends's user IDs (by screen name or user ID, or by implicit authenticated user)
|
124
|
+
|
125
|
+
```ruby
|
126
|
+
client.friend_ids('gem')
|
127
|
+
client.friend_ids(213747670)
|
128
|
+
client.friend_ids
|
129
|
+
```
|
130
|
+
|
131
|
+
Fetch all followers's user IDs (by screen name or user ID, or by implicit authenticated user)
|
132
|
+
|
133
|
+
```ruby
|
134
|
+
client.follower_ids('gem')
|
135
|
+
client.follower_ids(213747670)
|
136
|
+
client.follower_ids
|
137
|
+
```
|
138
|
+
|
139
|
+
Fetch all friends with profile details (by screen name or user ID, or by implicit authenticated user)
|
140
|
+
|
141
|
+
```ruby
|
142
|
+
client.friends('gem')
|
143
|
+
client.friends(213747670)
|
144
|
+
client.friends
|
145
|
+
```
|
26
146
|
|
27
|
-
|
147
|
+
Fetch all followers with profile details (by screen name or user ID, or by implicit authenticated user)
|
28
148
|
|
29
|
-
|
149
|
+
```ruby
|
150
|
+
client.followers('gem')
|
151
|
+
client.followers(213747670)
|
152
|
+
client.followers
|
153
|
+
```
|
30
154
|
|
31
|
-
|
155
|
+
|
156
|
+
Fetch the timeline of Tweets (by screen name or user ID, or by implicit authenticated user)
|
157
|
+
|
158
|
+
```ruby
|
159
|
+
client.user_timeline('gem')
|
160
|
+
client.user_timeline(213747670)
|
161
|
+
client.user_timeline
|
162
|
+
|
163
|
+
result.size
|
164
|
+
# => 588
|
165
|
+
|
166
|
+
result.first.text
|
167
|
+
# => "Your tweet text..."
|
168
|
+
|
169
|
+
result.first.user.screen_name
|
170
|
+
# => "your_screen_name"
|
171
|
+
```
|
172
|
+
|
173
|
+
Fetch the timeline of Tweets from the authenticated user's home page
|
174
|
+
|
175
|
+
```ruby
|
176
|
+
client.home_timeline
|
177
|
+
```
|
178
|
+
|
179
|
+
Fetch the timeline of Tweets mentioning the authenticated user
|
180
|
+
|
181
|
+
```ruby
|
182
|
+
client.mentions_timeline
|
183
|
+
```
|
32
184
|
|
33
185
|
## Contributing
|
34
186
|
|
35
|
-
Bug reports and pull requests are welcome on GitHub at https://github.com/
|
187
|
+
Bug reports and pull requests are welcome on GitHub at https://github.com/ts-3156/twitter_friendly.
|
36
188
|
|
37
189
|
## License
|
38
190
|
|
@@ -0,0 +1,95 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require 'dotenv/load'
|
4
|
+
require 'twitter_friendly'
|
5
|
+
|
6
|
+
TwitterFriendly.cache.clear
|
7
|
+
|
8
|
+
client =
|
9
|
+
TwitterFriendly::Client.new(
|
10
|
+
consumer_key: ENV['CK'],
|
11
|
+
consumer_secret: ENV['CS'],
|
12
|
+
access_token: ENV['AT'],
|
13
|
+
access_token_secret: ENV['ATS']
|
14
|
+
)
|
15
|
+
|
16
|
+
def diff(ary1, ary2)
|
17
|
+
[ary1 - ary2, ary2 - ary1]
|
18
|
+
end
|
19
|
+
|
20
|
+
def diff?(ary1, ary2)
|
21
|
+
diff(ary1, ary2).flatten.any?
|
22
|
+
end
|
23
|
+
|
24
|
+
def users_diff(ary1, ary2)
|
25
|
+
diff(ary1.map {|a| a[:id]}, ary2.map {|a| a[:id]})
|
26
|
+
end
|
27
|
+
|
28
|
+
def users_diff?(ary1, ary2)
|
29
|
+
users_diff(ary1, ary2).flatten.any?
|
30
|
+
end
|
31
|
+
|
32
|
+
friend_ids = follower_ids = []
|
33
|
+
|
34
|
+
%i(friend_ids follower_ids).each do |method|
|
35
|
+
ids = client.send(method)
|
36
|
+
cached_ids = client.send(method)
|
37
|
+
raw_ids = client.internal_client.send(method).attrs[:ids]
|
38
|
+
|
39
|
+
puts method
|
40
|
+
puts " fetch #{ids.size}, cache #{cached_ids.size}, raw #{raw_ids.size}"
|
41
|
+
puts " ids is different from cached_ids diff=#{diff(ids, cached_ids).inspect}" if diff?(ids, cached_ids)
|
42
|
+
puts " cached_ids is different from raw_ids diff=#{diff(cached_ids, raw_ids).inspect}" if diff?(cached_ids, raw_ids)
|
43
|
+
puts " ids is different from raw_ids diff=#{diff(ids, raw_ids).inspect}" if diff?(ids, raw_ids)
|
44
|
+
puts " #{client.rate_limit.send(method)}"
|
45
|
+
|
46
|
+
eval("#{method}=ids")
|
47
|
+
end
|
48
|
+
|
49
|
+
client.cache.clear
|
50
|
+
|
51
|
+
ids1, ids2 = client.friend_ids_and_follower_ids
|
52
|
+
cached_ids1, cached_ids2 = client.friend_ids_and_follower_ids
|
53
|
+
|
54
|
+
puts 'friend_ids_and_follower_ids'
|
55
|
+
puts " fetch #{ids1.size}, cache #{cached_ids1.size}"
|
56
|
+
puts " fetch #{ids2.size}, cache #{cached_ids2.size}"
|
57
|
+
puts " ids1 is different from cached_ids1 diff=#{diff(ids1, cached_ids1)}" if diff?(ids1, cached_ids1)
|
58
|
+
puts " ids2 is different from cached_ids2 diff=#{diff(ids2, cached_ids2)}" if diff?(ids2, cached_ids2)
|
59
|
+
|
60
|
+
client.cache.clear
|
61
|
+
|
62
|
+
friends = followers = []
|
63
|
+
|
64
|
+
%i(friends followers).each do |method|
|
65
|
+
users = client.send(method)
|
66
|
+
cached_users = client.send(method)
|
67
|
+
# raw_users = client.internal_client.send(method).attrs[:users]
|
68
|
+
|
69
|
+
puts method
|
70
|
+
puts " users #{users.size}, cached_users #{cached_users.size}"
|
71
|
+
puts " users is different from cached_users diff=#{users_diff(users, cached_users)}" if users_diff?(users, cached_users)
|
72
|
+
# puts " cached_users is different from raw_users" if cached_users != raw_users
|
73
|
+
# puts " users is different from raw_users" if users != raw_users
|
74
|
+
puts " #{client.rate_limit.send(method)}"
|
75
|
+
|
76
|
+
eval("#{method}=users")
|
77
|
+
end
|
78
|
+
|
79
|
+
client.cache.clear
|
80
|
+
|
81
|
+
users1, users2 = client.friends_and_followers
|
82
|
+
cached_users1, cached_users2 = client.friends_and_followers
|
83
|
+
|
84
|
+
puts 'friends_and_followers'
|
85
|
+
puts " fetch #{users1.size}, cache #{cached_users1.size}"
|
86
|
+
puts " fetch #{users2.size}, cache #{cached_users2.size}"
|
87
|
+
puts " users1 is different from cached_users1 diff=#{users_diff(users1, cached_users1)}" if users_diff?(users1, cached_users1)
|
88
|
+
puts " users2 is different from cached_users2 diff=#{users_diff(users2, cached_users2)}" if users_diff?(users2, cached_users2)
|
89
|
+
|
90
|
+
client.cache.clear
|
91
|
+
|
92
|
+
puts friend_ids.zip(friends).all? {|id, user| id == user[:id] }
|
93
|
+
puts follower_ids.zip(followers).all? {|id, user| id == user[:id] }
|
94
|
+
|
95
|
+
puts 'ok'
|