MicroBlogger
In this multi-phase project, you will build a client that interacts with the Twitter messaging service. Your client will both mimic functionality found through the twitter.com web interface as well as perform many new tasks.
Get Ready
If you haven’t already setup Ruby, visit the environment setup page for instructions.
Prerequisites
Before starting this tutorial, you should have a basic understanding of topics covered in Ruby in 100 Minutes, including:
- classes
- objects
- methods and arguments
- string interpolation
You should also be comfortable with:
- installing a gem
- using IRB
- writing methods
Learning Goals
After completing this tutorial, you will be able to:
- require and reload files in irb
- understand the difference between
puts
andprintf
- get user input with
gets.chomp
- use conditional branching and looping techniques with
case
statements andwhile
loops - manipulate strings using methods like
.ljust
and.split
- iterate through collections using
.each
and.collect
- check to see if an element is part of a collection using
.include?
- add elements to an existing array with the syntax
array << element
- select specific elements of array using the syntax
array[0]
andarray[1..-1]
- set up and use an API (Application Programming Interface)
- implement functionality provided by various gems: klout, bitly, jumpstart_auth
What We’re Doing in This Tutorial
We’ll set up a Twitter client using the Jumpstart_Auth gem. Next, we’ll create a
tweet
method to post a message to Twitter. We’ll implement functionality to allow
a command line user to specify whether to tweet, send a direct message, spam followers, see
everybody’s last tweet, or exit the program. Finally, we will use the Bitly gem
to shorten links and the Klout API to measure a user’s social network influence.
We won’t be creating a test suite with this project, but if you finish all
iterations, try writing tests as an extension.
The Twitter API and gem are constantly changing. We do our best to keep this tutorial updated, but sorry if things get confusing.
Iteration 0: Up & Running
Before building fancy features, let’s focus on getting something running.
jumpstart_auth
Install the jumpstart_auth
gem by running this instruction from your command prompt (Windows) or terminal (OS X):
Terminal
$
|
|
It’ll likely install several dependencies in addition to jumpstart_auth
itself.
MicroBlogger Skeleton
Next, open your text editor. Create a file named micro_blogger.rb
and start it off with this structure:
1 2 3 4 5 6 7 8 9 |
|
Fire It Up
Let’s see if that little program is ready to run. From your terminal/command prompt, start "Interactive Ruby" with this instruction:
Terminal
$
|
|
You’ll get a prompt that looks something like this:
IRB
2.1.1 :001>
|
|
At that prompt, load our microblogger.rb file and create a new instance of our MicroBlogger class by running these two instructions:
IRB
2.1.1 :001> 2.1.1 :002> |
|
The line #<MicroBlogger:0x1014012b0>
represents a MicroBlogger object.
Dealing with OAuth
When connecting to a third-party service (such as Twitter), from the developer’s perspective, possibly the simplest form of authentication is passing the user’s username and password. Unfortunately, this puts more work on the user and is not very secure or robust.
That’s where OAuth comes in. The OAuth authentication system is a more complex workflow that involves a lot of moving parts and can be confusing to understand at first.
Because of this, we’ve moved all of the complexity into the jumpstart_auth
gem
so that we can focus on the important parts of this exercise. You can use this
the jumpstart_auth
library inside your initialize method like this:
1 2 3 4 |
|
Re-Run from IRB
Since we made a change to our file, we need to reprocess and load it back into irb. Run this command from Terminal:
IRB
2.1.1 :001> 2.1.1 :002> |
|
The first time this is run it’ll use the Launchy
gem to pop open your web
browser and ask for permission to use your account. We strongly recommend
that you use one of our demo accounts (if you’re in a class) or a fake account
you setup yourself.
Twitter will then give you a pin number that’s about 10 digits. Copy it to your
clipboard, go over to your IRB session, and paste it in where the prompt says
Enter the supplied pin:
.
After entering the pin, we’ll have a @client
variable which holds our
connection to Twitter. With that setup, we can move forward.
Iteration 1: Posting Tweets
Now that we have the @client
object, we need to know what methods are
available from the Twitter library. In other words, what Ruby code can we write
that will interact with Twitter’s API.
The best information is available on the project readme file here.
In the readme you’ll find a section "Usage Examples" which clues you into some
of the functions exposed by the library. One of the methods we have access to is
update
, which allows us to post to the Twitter account we’re using.
Step 1 - Write the tweet
Method
Now add the the following method to your class:
1 2 3 |
|
Then at the very end of the file, add these lines as an execution script:
1 2 |
|
Then run your code by going to your terminal an entering:
Terminal
$
|
|
You should see the output say Initializing
. Now go to your test account’s Twitter page and look for your results!
Step 2 - Length Restrictions
Twitter messages are limited to 140 characters. Experiment with your current
program and see what happens if your try to call tweet
with a message longer
than 140. Did all of the message post? Part of it? Any of it? Let’s create some
error checking that will prevent the user from posting messages longer than 140.
Here’s some pseudocode for what we want our tweet
method to do:
- If the message to tweet is less than or equal to 140 characters long, tweet it.
- Otherwise, print out a warning message and do not post the tweet.
Inside of your tweet
method, write the code that will perform this logic.Test
your new tweet
method with a message that is less than 140 characters, one
that is exactly 140 characters, and one that’s longer than 140 characters.
Wondering how to get a string that’s exactly 140 characters? Here’s how I did it:
1
|
|
Iteration 2: A Better Interface
Our client is off to a good start, but the interface sucks. We have to change lines in the Ruby file for each tweet we want to send – that’s not reasonable! Let’s write code that will allow a user to control the program from the command line.
Let’s build an interactive prompt interface to run our program.
Step 0 - Outline the Process
First, let’s define a method named run
which will be the instruction that gets
repeated over and over:
1 2 3 |
|
Then go to the last line of your program and change it to blogger.run
.
Run your program at the command line with ruby micro_blogger.rb
and you should just see the line "Welcome to the JSL Twitter Client!"
Step 1 - The Loop
Underneath the puts "Welcome to the JSL Twitter Client!"
line we’ll use a while
loop to repeat instructions over and over. Add these lines below the puts
but before the end
:
1 2 3 4 |
|
Remember that you can exit a running Ruby application by pressing Control-C if you just so happen to get in an infinite loop.
Go ahead and run that program and you should see the "enter command: " string over and over. Why?
The while
loop will keep repeating until the variable command
contains the value "q"
. Since we set command
to the empty string and aren’t changing it, the loop continues forever.
Also, you might wonder what printf
is about. Why not puts
? The difference is that printf
prints text and leaves the cursor there, while puts
prints text then starts a new line. For our interface, we’ll have the prompt and the command on the same line.
Step 2 - Accepting Input
In Ruby we can accept user string with the gets
command. Add this line below your printf
:
1
|
|
Now run your program again and it’ll wait for you to enter commands. Try typing some things in. Try entering a q
to quit.
It doesn’t work, right? There’s a little gotcha with using gets
– it picks up the enter key too. So your command
variable is actually getting the string "q\n"
where that backslash-n is a new line. The fix is to change gets
to gets.chomp
. The chomp
method will remove any whitespace (spaces, tabs, newlines) on the back end of a string.
After you add the chomp
try your program again and you should be able to quit with just q
.
Step 3 - Starting a Case Statement
We think we’re getting the instruction from the user, but we need to actually do something with it. We’ll use what’s called a case statement. Case statements in Ruby look like this:
1 2 3 4 5 6 7 |
|
Ruby will look at the variable input
and see what value it holds. If the value matches one of the when
lines, then it’ll do the instruction(s) that follow that line’s then
. If it doesn’t match any of the when
lines, it’ll run the else
.
Start with this case statement in your method just below the command = gets.chomp
line:
1 2 3 4 5 |
|
Run your program and test some commands.
Step 4 - Tweeting & Parameters
Let’s make this thing work for our tweet method. Add a when
line that is run when the command
is "t"
. Have it call our tweet
method.
Run your program and try entering t This is only a test!
.
You should see output like Sorry, I don't know how to (t This is only a test!)
.
I wanted it to call the tweet
method because I started the line with t
,
but then the rest of the line was my message. Instead, it thought the whole line
was the command. We need to divide up the input between the command and the text
that should be sent to that command.
There are a few ways we could accomplish this, but we’ll use the most straightforward method.
Making the Command-Line Interface Smarter
We want the command
variable to be just the first letter(s) that are entered.
But right now, the line command = gets.chomp
isn’t just getting the command
,
it’s getting a command
and a message to send to that command. Let’s change this
line to input = gets.chomp
then we’ll work with input
to pull out the command.
Now that we have input
we need to split it up into pieces. We’ll cut it up
using the split
method. Just below the input = gets.chomp
add a line that
says parts = input.split(" ")
to cut input
into an array of parts
.
split
will take our input string (entered by the user at the
command line) and chop it into an array of smaller strings. Whatever argument we
pass to .split
will be where the string gets chopped.
For example, if the user gave the input: t tweet my message
Our input = gets.chomp
would produce a parts
array that looked like this:
["t", "tweet", "my", "message"]
Knowing the structure of this array will allow us to pull out the
parts we need at various places in our program. In the example parts
array above,
t
is the command we’re looking for, so let’s pull it out by saying command = parts[0]
.
The rest of the elements in parts
are our message. We can change these parts
back into a string with parts[1..-1].join(" ")
. This means: take all the parts
from index 1 to the end of the array (-1) and put them together with a space between
each.
Using that idea in the when
line for t
, here’s what my run
method looks like right now:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
|
Try it out and you should finally be able to post tweets over and over!
Iteration 3: Send Direct Messages
Sending Direct Messages isn’t that different from posting a tweet. In fact, we
can reuse our existing tweet
method to do all the hard work.
Step 0 - Frameworks
Our dm
method will take a target (Twitter handle for some user) and a message:
1 2 3 4 |
|
And we need to add the command to our run
method. We’ll enter the instruction
like dm jumpstartlab Here is the text of the DM
, so our when
line should
look like this:
1
|
|
Remember that parts[0]
is the command itself, here dm
. Then parts[1]
will
be the target username, here jumpstartlab
. Then everything else is the message,
so we join them with spaces parts[2..-1].join(" ")
.
Step 1 - Create and Send the Message
First, let’s add to our dm
method. We’ll create a variable message
which will
be a combination of the letter d, a target, and a message.
1 2 3 4 5 6 |
|
Then call the tweet
method with this new string as the parameter message.
Log into your personal Twitter account and follow the fake account you created. (You will need to do this before you can send a direct message.) Then try sending yourself a direct message from your MicroBlogger.
Step 2 - Error Checking DM-ability
You can only DM people who are following you. If you try and DM someone who doesn’t follow you, it doesn’t give you an error message. It just fails silently.
Let’s add a way to verify that the target is following you before sending the message. In pseudo-code, it’d go something like this:
- Find the list of my followers
- If the target is in this list, send the DM
- Otherwise, print an error message
We can call @client.followers
which gives us back a list of all our followers
but includes lots of information we don’t need right now like their follower count,
web address, last tweet. All we want is to find their screen_name
.
What we need to do is pull out just the screen_name
. We create an array of the
followers’ screen names with this line of code:
1
|
|
To read this line out loud it would be like "Call the followers
method of
@client
, then take that array and, for each element in the array, collect
together the value of screen_name
.
Now you have an array of your followers’ screen names. Create a conditional block that follows this logic:
- If the
target
username is in thescreen_names
list (use Array#include? method), then send the DM - Otherwise, print out an error saying that you can only DM people who follow you
Test your code by sending a DM to someone who does follow your demo account and someone who does not.
Step 3 - Spamming Your Friends
It would be cool to be able to send the same message out to all of our followers. We’ll accomplish this in two parts:
- Create a method named
followers_list
that returns an array containing the usernames of all my followers - Create a method named
spam_my_followers
that finds all followers fromfollowers_list
and tries to send them a Direct Message using thedm
method
To create the followers_list
method…
- Define the method named
followers_list
with no parameters - Create a blank array named
screen_names
- On the
@client
call thefollowers
method iterate througheach
of them performing the instruction below:
1
|
|
- Return the array
screen_names
Then for the spam_my_followers
method…
- Define the method named
spam_my_followers
with a parameter namedmessage
- Get the list of your followers from the
followers_list
method - Iterate through
each
of those followers and use yourdm
method to send them themessage
Then create a when
line in your run
method for the command spam
. It will look just like the tweet
line, except it’ll send the message into the spam_my_friends
method.
Test it out and see how many followers you can annoy at once!
Iteration 4: Last Tweet from All Friends
So now you can post tweets and DMs. There are hundreds of clients that can do that. If you’re a normal twitter user you follow some people who post several times per day and some people who post once per week. It can be difficult to see everyone. Lets create a list of the last tweet for each person you follow.
Step 0 - Framework
Here it is in pseudocode:
Find the list of people you follow
For
each
member of the list…- Find their latest tweet
- Print out their
screen_name
and latest tweet
Turn that into code like this…
1 2 3 4 5 6 7 8 9 |
|
Add a when
line to your run
method for this instruction. I’m using the instruction elt
so my when
line is just when 'elt' then everyones_last_tweet
. Once added, restart your program and try it out.
Step 1: Finding the Last Messages
When you call the friends
method you get an array list where each element of the array is an object representing one friend. The object has all the information about an individual friend such as screen_name
, id
, followers_count
, etc. Here are the useful properties that it has:
1
|
|
So for each of those friend
objects, if you call friend.followers_count
you’ll get their number of followers. Or use friend.id
to get their unique Twitter ID number.
status
contains their last tweet, but there’s a catch – status
is ANOTHER complex object, not simply a string. The status
object has these properties and methods:
1
|
|
So if you want to access one of these pieces of data you’d call it like this: friend.status.created_at
or friend.status.source
.
Now that you understand the hashes available to you, implement code for the three commented lines in our everyones_last_tweet
method. RUN your program and you should see output kinda like this:
Terminal
|
Step 2: Improving the Output
Getting each friend’s last message was cool, but they’re in some random order. Sort them by the screen_name
in alphabetical order! I want you to hack out the code, but the way I did it would read like this:
"take the friends list and use the Array#sort_by method, then call each one friend
and find the friend.screen_name
". You might look at how you used sort_by
in EventManager for syntax clues. (NOTE: Ruby considers all capital letters to come earlier in alphabetical order than lowercase letters. To keep all your letters together regardless of capitalization, change friend.screen_name
to friend.screen_name.downcase
when sorting)
Second, these messages are lacking any time context. The status
hash has a key named created_at
which holds a string like this one: Thu Jul 23 23:31:16 +0000 2009
. That’s the information we need, but it’s in an ugly format. Use these steps to make the data more useable:
1 2 3 4 |
|
DateTime#strftime
is my most hated method in Ruby because every time I use it I need to
lookup the dumb parameters. The "%A, %b %d"
that I gave you will cause it to
output the date formatted like Wednesday, Jul 29
. Implement the sorting and
the timestamping to create output that looks like this:
Terminal
|
Iteration 5: Shorten URLs with Bit.ly
There’s a great library which can be used to automatically create shortened URLs. Let’s add this functionality into our project.
Step 0 - Testing the Library
First, go into Terminal and run this line:
Terminal
$
|
|
Next, open irb
and try out the following:
1 2 3 4 5 6 |
|
It might take a few seconds, but you should now have a shortened URL from Bitly’s shortner service. Try it out in your browser to make sure it works.
Step 1 - Framework
Create this method:
1 2 3 4 |
|
Add a when
line to your run
method so that the command s
will take one parameter and send it into the shorten
method.
Step 2 - Implement the Method
Look at the model for Bitly that we used in Step 0 and use it to fill in the shortening code of your shorten
method. Make sure that your method ends with a return
statement so it sends the shortened URL that to the method that called it.
Step 3 - Tweet with URL
How can we shorten a url while posting a tweet? There are a few ways to do it. Here’s an easy one:
Add a when
line in your run
method for the command turl
which stands for "Tweet with URL". Make it accept commands that look like this:
turl I wrote this twitter client at: http://jumpstartlab.com
You know that parts[0]
is the command, parts[1..-2]
are the message, and parts[-1]
is the URL to be shortened. You can put that all together like this:
tweet(parts[1..-2].join(" ") + " " + shorten(parts[-1]))
Get that working and you’re done with the twitter client!
Iteration 6: The Klout API
Let’s continue experimenting with various APIs.
Klout (klout.com) is a service that, "measures influence on social networks." The Klout service analyzes a variety of networks to determine an individual’s "Klout Score." Measured from 0 to 100, a higher Klout Score indicates greater influence on various social networks.
Klout offers a free public API that lets you retrieve Klout scores by simply providing a Twitter username. Let’s attempt to retrieve the Klout Score of all the users you follow and see who has the greatest influence!
Step 0 - Testing the API
First, hop into Terminal and run this line:
Terminal
$
|
|
Next, open irb
so you can experiment with the Klout API. Run the following lines:
IRB
2.1.1 :001> 2.1.1 :002> 2.1.1 :003> 2.1.1 :004> 2.1.1 :005> |
|
While the format of the second command may be a bit confusing, you’re simply asking Klout to return a user’s Klout Score in a format you can read and interpret. In this example, you’ve provided the Twitter username jack
(the original creator of Twitter), and Klout returned the value 74.61
. You can easily change out jack
for any other Twitter username, so let’s obtain the Klout Score for everyone you follow!
Step 1 - Require Klout
First, you’ll want to require the Klout gem in your MicroBlogger implementation, and make sure you can make requests to the Klout service. At the top of your micro_blogger.rb
file, insert the following just below require jumpstart_auth
:
1
|
|
Next, in your initialize method, insert the following:
1
|
|
Great! Now you’re set up to make requests to Klout’s API!
Step 2 - Obtain Klout Scores
You’ve already written logic to obtain a list of your friends, which looks something like this:
1
|
|
If you wanted to write a method that prints out the Klout score for all of your friends, its logic would be structured something like this:
- Obtain a list of all your friends, and save that list inside a variable named
screen_names
(this is done using the method above). - Step through the list of friends. For each
friend
, issue a request to Klout to obtain their Klout score. - Print out each
screen_name
followed by their Klout score.
Here’s the basic setup for this method, you can fill in the gaps:
1 2 3 4 5 6 7 8 |
|
Once you’re finished, test the method by inserting the following line at the bottom of the micro_blogger.rb
file:
1
|
|
and then run the program. Who has the highest Klout score amongst your friends?