fake_friends 0.1.6 → 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +8 -6
- data/dev/fetch_from_twitter.rb +142 -0
- data/fake_friends.gemspec +5 -5
- data/lib/fake_friends/version.rb +1 -1
- data/lib/fake_friends.rb +1 -1
- data/spec/fake_friends_spec.rb +4 -7
- data/spec/spec_helper.rb +2 -2
- metadata +15 -15
- data/dev/pull_from_twitter.rb +0 -116
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 91c52fe6550cce6abc312dbfccd2743415889406
|
4
|
+
data.tar.gz: e1fda60d62b933d57fd7073f02239e0b48ad3a20
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 7fda149947ae5445946d3e0076291e85be64afd91cbca5256a28141463ae8497ccf5e6ee2acd97782547c4ff65ef18a4c61a6a7e84648b2c5835a383ddb38b81
|
7
|
+
data.tar.gz: 47cb69a59f89ab1ba33c1edc85343513efc08260d1fb520bc04a7976ce890c99f3ba330645216e6e74f4c3100d107fd3a68e31d5f4b478198a0761beb958a29d
|
data/README.md
CHANGED
@@ -1,6 +1,10 @@
|
|
1
1
|
# FakeFriends
|
2
2
|
|
3
|
-
A simple [ruby gem](https://rubygems.org/gems/fake_friends) to generate consistent and realistic fake user data for demoing social networking apps (e.g., user names match their avatars, fake posts are pulled from actual Twitter posts rather than lorem text, etc).
|
3
|
+
A simple [ruby gem](https://rubygems.org/gems/fake_friends) to generate consistent and realistic fake user data for demoing social networking apps (e.g., user names match their avatars, fake posts are pulled from actual Twitter posts rather than lorem text, etc), modeled on the popular [Faker](https://github.com/stympy/faker) gem.
|
4
|
+
|
5
|
+
## Release Notes
|
6
|
+
0.1.6 December 2, 2013 (152 KB) Adds tests in RSpec
|
7
|
+
0.1.5 November 26, 2013 (148 KB) Inital release
|
4
8
|
|
5
9
|
## Installation
|
6
10
|
|
@@ -24,7 +28,7 @@ Or install it yourself as: `$ gem install fake_friends`
|
|
24
28
|
* `#name`
|
25
29
|
* `#description`
|
26
30
|
* `#avatar_url(size)`
|
27
|
-
`size`: requested size of image. Available in 128,
|
31
|
+
`size`: requested size of image. Available in 128, 73, 48, and 24 px.
|
28
32
|
Returns a url to an image in the closest available size.
|
29
33
|
* `#url`
|
30
34
|
* `#posts`
|
@@ -42,7 +46,7 @@ to return an array of 5 `FakeFriend` objects.
|
|
42
46
|
returns the fifth user in the library and assigns it to `user`.
|
43
47
|
|
44
48
|
`user.avatar_url(size)` pulls an avatar from uiFaces.com, where Twitter users have contributed their profile photos.
|
45
|
-
The available sizes (in pixels) are 128,
|
49
|
+
The available sizes (in pixels) are 128, 73, 48, and 24. The method will choose the image closest in size
|
46
50
|
to the requested `size`.
|
47
51
|
|
48
52
|
`user.url` returns a hash with an `:expanded` url (e.g. `http://www.google.com`) and a `:display` url (e.g. `google.com`).
|
@@ -57,14 +61,12 @@ The library currently holds 101 users with associated status updates. Associated
|
|
57
61
|
## Source
|
58
62
|
Images come from user contributions on uiFaces.com.
|
59
63
|
Posts are non-retweet tweets from the associated twitter profiles (all public).
|
60
|
-
|
61
64
|
Many thanks to these users for their contributions.
|
62
65
|
|
63
66
|
|
64
67
|
## Future work
|
65
68
|
|
66
|
-
A hundred users should be enough for most demoing needs, but a method to fetch
|
67
|
-
data right from the FakeFriends class may be added in future.
|
69
|
+
A hundred users should be enough for most demoing needs, but a class method to fetch fresh data from the Twitter API may be added in future.
|
68
70
|
|
69
71
|
## Contributing
|
70
72
|
|
@@ -0,0 +1,142 @@
|
|
1
|
+
require 'twitter'
|
2
|
+
require 'yaml'
|
3
|
+
|
4
|
+
class TweetFetcher
|
5
|
+
attr_reader :twitter_client, :number_of_users, :max_posts_per_user,
|
6
|
+
:avail_users, :output_file_rel_path, :fake_friends
|
7
|
+
|
8
|
+
def initialize(opt={ users: 100, max_posts_per_user: 50,
|
9
|
+
user_list: 'usernames.yml',
|
10
|
+
output_file: '../lib/fake_friends/users.yml' } )
|
11
|
+
|
12
|
+
@number_of_users = opt[:users]
|
13
|
+
@max_posts_per_user = opt[:max_posts_per_user]
|
14
|
+
@output_file_rel_path = opt[:output_file]
|
15
|
+
@avail_users = YAML.load_file(opt[:user_list])
|
16
|
+
|
17
|
+
@fake_friends = {} # users to be stored as FakeFriend objects
|
18
|
+
@twitter_client = initialize_twitter_api_client
|
19
|
+
end
|
20
|
+
|
21
|
+
# ---
|
22
|
+
# prompts for twitter api credentials and returns a client
|
23
|
+
# ---
|
24
|
+
def initialize_twitter_api_client
|
25
|
+
puts 'Enter your Twitter API credentials (get some @ dev.twitter.com).'
|
26
|
+
|
27
|
+
Twitter::REST::Client.new do |config|
|
28
|
+
print 'consumer key: '
|
29
|
+
config.consumer_key = gets.chomp
|
30
|
+
print 'consumer secret: '
|
31
|
+
config.consumer_secret = gets.chomp
|
32
|
+
print 'oauth token: '
|
33
|
+
config.oauth_token = gets.chomp
|
34
|
+
print 'oauth token secret: '
|
35
|
+
config.oauth_token_secret = gets.chomp
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
# ---
|
40
|
+
# returns array of non-retweet tweets, each as a hash containing
|
41
|
+
# the tweet's with :time of creation and :text
|
42
|
+
# ---
|
43
|
+
def posts(twitter_username, count)
|
44
|
+
options = { count: count, exclude_replies: true }
|
45
|
+
twitter_client.user_timeline(twitter_username, options)
|
46
|
+
.delete_if{ |t| t.retweeted || (t.text =~ /^RT\s@/) } # remove retweets
|
47
|
+
.map { |tweet| { time: tweet.created_at, text: tweet.text } }
|
48
|
+
end
|
49
|
+
|
50
|
+
# ---
|
51
|
+
# Fetches users and their tweets, iteratively saves them to file as YAML
|
52
|
+
# ---
|
53
|
+
def fetch_users_and_their_tweets
|
54
|
+
users = avail_users.sample( number_of_users )
|
55
|
+
|
56
|
+
puts ""
|
57
|
+
users.each_with_index do |username, user_num|
|
58
|
+
fake_friends[username] = create_user_hash_for username
|
59
|
+
update_output_file_with username, user_num
|
60
|
+
end
|
61
|
+
|
62
|
+
puts "Finished fetching users and tweets"
|
63
|
+
end
|
64
|
+
|
65
|
+
# ---
|
66
|
+
# For a given Twitter user, returns a hash with the following strings:
|
67
|
+
# name, location, description, url[:expanded], url[:display], image url
|
68
|
+
# and an array containing the desired number of tweets
|
69
|
+
# ---
|
70
|
+
def create_user_hash_for(u)
|
71
|
+
## Ensure user exists and tweets are public
|
72
|
+
if twitter_client.user?(u)
|
73
|
+
unless twitter_client.user(u).protected?
|
74
|
+
|
75
|
+
user = twitter_client.user(u) # load user
|
76
|
+
posts = posts(u, max_posts_per_user) # fetch 100 posts
|
77
|
+
|
78
|
+
begin # get expanded url if it exists
|
79
|
+
expanded_url = user.attrs[:entities][:url][:urls].first[:expanded_url]
|
80
|
+
rescue
|
81
|
+
expanded_url = nil
|
82
|
+
end
|
83
|
+
|
84
|
+
begin # get display url if it exists
|
85
|
+
display_url = user.attrs[:entities][:url][:urls].first[:display_url]
|
86
|
+
rescue
|
87
|
+
display_url = nil
|
88
|
+
end
|
89
|
+
|
90
|
+
{
|
91
|
+
name: user.name, location: user.location,
|
92
|
+
description: user.description,
|
93
|
+
url: { expanded: expanded_url, display: display_url },
|
94
|
+
image: user.profile_image_url, posts: posts
|
95
|
+
}
|
96
|
+
end
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
# ---
|
101
|
+
# Updates the ouput file with YAML for the array of
|
102
|
+
# users in its current state
|
103
|
+
# ---
|
104
|
+
def update_output_file_with(user, number)
|
105
|
+
File.open(output_file_rel_path, 'w') do |f|
|
106
|
+
f.write(fake_friends.to_yaml)
|
107
|
+
end
|
108
|
+
|
109
|
+
puts "fetched and saved user #{number+1}: #{user}"
|
110
|
+
|
111
|
+
if number_of_users <= 75
|
112
|
+
# small number fetched in batches, rest every 15th user
|
113
|
+
countdown_minutes(15) if (number+1 % 15 == 0)
|
114
|
+
else
|
115
|
+
# large number fetched at slow, steady pace
|
116
|
+
countdown_minutes(1)
|
117
|
+
end
|
118
|
+
end
|
119
|
+
|
120
|
+
# ---
|
121
|
+
# Displays a countdown timer on the command line
|
122
|
+
# for the given number of minutes
|
123
|
+
# ---
|
124
|
+
def countdown_minutes(min)
|
125
|
+
puts "taking a #{min}-minute power nap to stay within Twitter API rate limits..."
|
126
|
+
|
127
|
+
clock =<<-SHELL
|
128
|
+
MIN=#{min}
|
129
|
+
for i in $(seq $(($MIN*60)) -1 1);
|
130
|
+
do
|
131
|
+
printf "\r%02d:%02d:%02d" $((i/3600)) $(( (i/60)%60)) $((i%60));
|
132
|
+
sleep 1;
|
133
|
+
done
|
134
|
+
SHELL
|
135
|
+
|
136
|
+
system(clock)
|
137
|
+
puts ""
|
138
|
+
end
|
139
|
+
end
|
140
|
+
|
141
|
+
twitter_api = TweetFetcher.new
|
142
|
+
twitter_api.fetch_users_and_their_tweets
|
data/fake_friends.gemspec
CHANGED
@@ -20,11 +20,11 @@ Gem::Specification.new do |spec|
|
|
20
20
|
spec.require_paths = ["lib"]
|
21
21
|
|
22
22
|
# TO BE ADDED
|
23
|
-
# spec.add_runtime_dependency "twitter", "~>
|
23
|
+
# spec.add_runtime_dependency "twitter", "~> 5.2.0"
|
24
24
|
|
25
|
-
spec.add_development_dependency "twitter", "~>
|
26
|
-
spec.add_development_dependency "rspec"
|
25
|
+
spec.add_development_dependency "twitter", "~> 5.2.0"
|
26
|
+
spec.add_development_dependency "rspec", "~> 2.14.1"
|
27
27
|
|
28
|
-
spec.add_development_dependency "bundler", "~> 1.3"
|
29
|
-
spec.add_development_dependency "rake"
|
28
|
+
spec.add_development_dependency "bundler", "~> 1.3.5"
|
29
|
+
spec.add_development_dependency "rake", "~> 10.1.0"
|
30
30
|
end
|
data/lib/fake_friends/version.rb
CHANGED
data/lib/fake_friends.rb
CHANGED
@@ -61,7 +61,7 @@ module FakeFriends
|
|
61
61
|
# avatar_url(size)
|
62
62
|
# returns the user's uiFaces url in the closest available size
|
63
63
|
def avatar_url(size)
|
64
|
-
valid_sizes = [128,
|
64
|
+
valid_sizes = [128, 73, 48, 24]
|
65
65
|
size = valid_sizes.min { |a,b| (size-a).abs <=> (size-b).abs }
|
66
66
|
"https://s3.amazonaws.com/uifaces/faces/twitter/#{username}/#{size}.jpg"
|
67
67
|
end
|
data/spec/fake_friends_spec.rb
CHANGED
@@ -3,11 +3,7 @@ include FakeFriends
|
|
3
3
|
|
4
4
|
describe FakeFriend do
|
5
5
|
|
6
|
-
# it { should respond_to(:gather).with(1).argument }
|
7
|
-
# it { should respond_to(:find_by).with(1).argument }
|
8
|
-
|
9
6
|
describe "::gather" do
|
10
|
-
|
11
7
|
context "with valid input" do
|
12
8
|
let(:users){ FakeFriend.gather(5) }
|
13
9
|
|
@@ -16,12 +12,12 @@ describe FakeFriend do
|
|
16
12
|
expect(users).to be_composed_of FakeFriend
|
17
13
|
end
|
18
14
|
|
19
|
-
it "returns the requested number
|
15
|
+
it "returns the requested number of FakeFriends" do
|
20
16
|
expect(users.count).to be 5
|
21
17
|
end
|
22
18
|
|
23
19
|
it "does not return duplicates" do
|
24
|
-
expect(users.
|
20
|
+
expect(users.count).to be users.map(&:username).uniq.count
|
25
21
|
end
|
26
22
|
end
|
27
23
|
|
@@ -35,6 +31,7 @@ describe FakeFriend do
|
|
35
31
|
end
|
36
32
|
end
|
37
33
|
|
34
|
+
|
38
35
|
describe "::find_by" do
|
39
36
|
context "with valid input (by id)" do
|
40
37
|
let(:user){ FakeFriend.find_by(id: 1) }
|
@@ -72,4 +69,4 @@ describe FakeFriend do
|
|
72
69
|
end
|
73
70
|
end
|
74
71
|
end
|
75
|
-
end
|
72
|
+
end
|
data/spec/spec_helper.rb
CHANGED
@@ -11,10 +11,10 @@ end
|
|
11
11
|
|
12
12
|
RSpec::Matchers.define :be_composed_of do |expected_type|
|
13
13
|
match do |actual|
|
14
|
-
actual.all?{|u| u.is_a? expected_type}
|
14
|
+
actual.all?{|u| u.is_a? expected_type}
|
15
15
|
end
|
16
16
|
|
17
17
|
failure_message_for_should do |actual|
|
18
|
-
"expected
|
18
|
+
"expected #{actual.class} to be composed of #{expected_type}s"
|
19
19
|
end
|
20
20
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: fake_friends
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 1.0.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Jake Romer
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2013-12-
|
11
|
+
date: 2013-12-18 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: twitter
|
@@ -16,56 +16,56 @@ dependencies:
|
|
16
16
|
requirements:
|
17
17
|
- - ~>
|
18
18
|
- !ruby/object:Gem::Version
|
19
|
-
version:
|
19
|
+
version: 5.2.0
|
20
20
|
type: :development
|
21
21
|
prerelease: false
|
22
22
|
version_requirements: !ruby/object:Gem::Requirement
|
23
23
|
requirements:
|
24
24
|
- - ~>
|
25
25
|
- !ruby/object:Gem::Version
|
26
|
-
version:
|
26
|
+
version: 5.2.0
|
27
27
|
- !ruby/object:Gem::Dependency
|
28
28
|
name: rspec
|
29
29
|
requirement: !ruby/object:Gem::Requirement
|
30
30
|
requirements:
|
31
|
-
- -
|
31
|
+
- - ~>
|
32
32
|
- !ruby/object:Gem::Version
|
33
|
-
version:
|
33
|
+
version: 2.14.1
|
34
34
|
type: :development
|
35
35
|
prerelease: false
|
36
36
|
version_requirements: !ruby/object:Gem::Requirement
|
37
37
|
requirements:
|
38
|
-
- -
|
38
|
+
- - ~>
|
39
39
|
- !ruby/object:Gem::Version
|
40
|
-
version:
|
40
|
+
version: 2.14.1
|
41
41
|
- !ruby/object:Gem::Dependency
|
42
42
|
name: bundler
|
43
43
|
requirement: !ruby/object:Gem::Requirement
|
44
44
|
requirements:
|
45
45
|
- - ~>
|
46
46
|
- !ruby/object:Gem::Version
|
47
|
-
version:
|
47
|
+
version: 1.3.5
|
48
48
|
type: :development
|
49
49
|
prerelease: false
|
50
50
|
version_requirements: !ruby/object:Gem::Requirement
|
51
51
|
requirements:
|
52
52
|
- - ~>
|
53
53
|
- !ruby/object:Gem::Version
|
54
|
-
version:
|
54
|
+
version: 1.3.5
|
55
55
|
- !ruby/object:Gem::Dependency
|
56
56
|
name: rake
|
57
57
|
requirement: !ruby/object:Gem::Requirement
|
58
58
|
requirements:
|
59
|
-
- -
|
59
|
+
- - ~>
|
60
60
|
- !ruby/object:Gem::Version
|
61
|
-
version:
|
61
|
+
version: 10.1.0
|
62
62
|
type: :development
|
63
63
|
prerelease: false
|
64
64
|
version_requirements: !ruby/object:Gem::Requirement
|
65
65
|
requirements:
|
66
|
-
- -
|
66
|
+
- - ~>
|
67
67
|
- !ruby/object:Gem::Version
|
68
|
-
version:
|
68
|
+
version: 10.1.0
|
69
69
|
description: A simple fake user generator
|
70
70
|
email:
|
71
71
|
- jake@jakeromer.com
|
@@ -79,7 +79,7 @@ files:
|
|
79
79
|
- LICENSE.txt
|
80
80
|
- README.md
|
81
81
|
- Rakefile
|
82
|
-
- dev/
|
82
|
+
- dev/fetch_from_twitter.rb
|
83
83
|
- dev/usernames.yml
|
84
84
|
- fake_friends.gemspec
|
85
85
|
- lib/fake_friends.rb
|
data/dev/pull_from_twitter.rb
DELETED
@@ -1,116 +0,0 @@
|
|
1
|
-
require 'twitter'
|
2
|
-
require 'yaml'
|
3
|
-
|
4
|
-
# TODO: Method to pull a user from Twitter directly into FakeFriend.find_by(username)
|
5
|
-
|
6
|
-
puts 'Enter your Twitter API credentials (get some @ dev.twitter.com).'
|
7
|
-
print 'consumer key: '
|
8
|
-
twitter_cons_key = gets.chomp
|
9
|
-
print 'consumer secret: '
|
10
|
-
twitter_cons_sec = gets.chomp
|
11
|
-
print 'oauth token: '
|
12
|
-
twitter_auth_tok = gets.chomp
|
13
|
-
print 'oauth token secret: '
|
14
|
-
twitter_auth_sec = gets.chomp
|
15
|
-
|
16
|
-
Twitter.configure do |config|
|
17
|
-
config.consumer_key = twitter_cons_key
|
18
|
-
config.consumer_secret = twitter_cons_sec
|
19
|
-
config.oauth_token = twitter_auth_tok
|
20
|
-
config.oauth_token_secret = twitter_auth_sec
|
21
|
-
end
|
22
|
-
|
23
|
-
# returns array of non-retweets with time and text
|
24
|
-
def posts(twitter_username, count)
|
25
|
-
options = { count: count, exclude_replies: true }
|
26
|
-
|
27
|
-
Twitter.user_timeline(twitter_username, options)
|
28
|
-
.delete_if{ |t| t.retweeted || (t.text =~ /^RT\s@/) } # remove retweets
|
29
|
-
.map do |tweet|
|
30
|
-
{ time: tweet.created_at, text: tweet.text }
|
31
|
-
end
|
32
|
-
end
|
33
|
-
|
34
|
-
|
35
|
-
number_of_users_to_pull = 100
|
36
|
-
number_of_posts_to_pull = 50
|
37
|
-
|
38
|
-
usernames = 'usernames.yml'
|
39
|
-
users = File.open(usernames, 'r'){|file| YAML.load(file, usernames) }
|
40
|
-
users = users.sample(number_of_users_to_pull)
|
41
|
-
friends = {}
|
42
|
-
|
43
|
-
users.each_with_index do |u, i|
|
44
|
-
## Ensure user exists and tweets are public
|
45
|
-
if Twitter.user?(u)
|
46
|
-
unless Twitter.user(u).protected?
|
47
|
-
|
48
|
-
# load user
|
49
|
-
user = Twitter.user(u)
|
50
|
-
|
51
|
-
# pull 100 posts
|
52
|
-
posts = posts(u, number_of_posts_to_pull)
|
53
|
-
|
54
|
-
# get urls if they exist
|
55
|
-
begin
|
56
|
-
expanded_url = user.attrs[:entities][:url][:urls].first[:expanded_url]
|
57
|
-
rescue
|
58
|
-
expanded_url = nil
|
59
|
-
end
|
60
|
-
|
61
|
-
begin
|
62
|
-
display_url = user.attrs[:entities][:url][:urls].first[:display_url]
|
63
|
-
rescue
|
64
|
-
display_url = nil
|
65
|
-
end
|
66
|
-
|
67
|
-
# populate friends hash
|
68
|
-
friends[u] = {
|
69
|
-
name: user.name,
|
70
|
-
location: user.location,
|
71
|
-
description: user.description,
|
72
|
-
url: { expanded: expanded_url, display: display_url },
|
73
|
-
image: user.profile_image_url,
|
74
|
-
posts: posts
|
75
|
-
}
|
76
|
-
end
|
77
|
-
end
|
78
|
-
|
79
|
-
# write to file as yaml
|
80
|
-
users_lib = '../lib/fake_friends/users.yml'
|
81
|
-
File.open(users_lib, 'w'){|f| f.write(friends.to_yaml) }
|
82
|
-
puts "loaded #{i+1}: #{u}"
|
83
|
-
|
84
|
-
if number_of_users_to_pull <= 75
|
85
|
-
# Twitter API limit: 15 per 15 minutes
|
86
|
-
if (i+1 % 15 == 0)
|
87
|
-
puts "taking a 15-minute power nap to stay within Twitter API rate limits..."
|
88
|
-
|
89
|
-
clock =<<-SHELL
|
90
|
-
MIN=15
|
91
|
-
for i in $(seq $(($MIN*60)) -1 1);
|
92
|
-
do
|
93
|
-
printf "\r%02d:%02d:%02d" $((i/3600)) $(( (i/60)%60)) $((i%60));
|
94
|
-
sleep 1;
|
95
|
-
done
|
96
|
-
SHELL
|
97
|
-
|
98
|
-
system(clock)
|
99
|
-
puts ""
|
100
|
-
end
|
101
|
-
else
|
102
|
-
puts "taking a 1-minute power nap to stay within Twitter API rate limits..."
|
103
|
-
|
104
|
-
clock =<<-SHELL
|
105
|
-
MIN=1
|
106
|
-
for i in $(seq $(($MIN*60)) -1 1);
|
107
|
-
do
|
108
|
-
printf "\r%02d:%02d:%02d" $((i/3600)) $(( (i/60)%60)) $((i%60));
|
109
|
-
sleep 1;
|
110
|
-
done
|
111
|
-
SHELL
|
112
|
-
|
113
|
-
system(clock)
|
114
|
-
puts ""
|
115
|
-
end
|
116
|
-
end
|