The Evernote SDK for Ruby Quick Start Guide
The purpose of this guide is describing how to download, install and configure the Evernote SDK for Ruby. If everything goes as planned, this shouldn’t take more than 10–15 minutes.
The sample application described in this tutorial will perform the following actions:
- Authenticate with the Evernote Cloud API using OAuth.
- List all of the notebooks in the authenticated user’s account, their username and the total number of notes in their account.
-
1. What You Need
Before we start working with the Evernote Cloud API, let’s make sure everything we need is present and accounted for.
Evernote API Key
If you don’t already have an Evernote Cloud API Key, you can request one on the Evernote Developer site. The API key is made up of two pieces: a Consumer Key and a Consumer Secret. You’ll need both of these to complete the sample app below.
Evernote Sandbox Account
Sandbox is Evernote’s development server. You’ll need to create an account on that server for use with the sample app below. If you don’t already have a Sandbox account, you can create one here.
Ruby
The Ruby programming language runtime is installed by default on Mac OS X and most Unix-based operating systems (including Linux). This tutorial assumes you’re using version 1.9.3 or higher of the Ruby runtime. To determine the version of Ruby running on your computer, issue the following command at the console:
ruby -vIf your computer doesn’t have Ruby installed, you can find information on how to do that on the Ruby language website.
RubyGems
The Evernote SDK for Ruby is available as a gem (a package format used by the popular RubyGems package manager). You’ll need to download and install RubyGems if you haven’t already, as we’ll be using it to install a few packages upon which our sample app depends. Once you’ve installed it, issue this command in your terminal/console:
gem --versionThis will print the current version of RubyGems. This tutorial assumes you’re using version 1.8.24 (the current version as of this writing). If the number displayed is lower, you would do well to update to the latest version by issuing the following command:
gem update --system(Note: updating RubyGems may require root or administrative privileges.)
Sinatra
The sample application described in this chapter uses Sinatra, a popular web development framework and DSL, written in Ruby. Some familiarity with Sinatra will make this example easier to understand, but isn’t required.
Sinatra is available from RubyGems and can be installed using the
gemcommand:gem install sinatraOur sample app uses the current stable version of Sinatra, version 1.3.3.
-
2. Installing the Evernote SDK for Ruby
The Evernote SDK for Ruby can be easily installed by issuing the following command:
gem install evernote_oauthThis package includes the complete Evernote SDK and some additional functionality to make it simpler to work with the OAuth authentication protocol (which is required by the Evernote Cloud API).
We now have all of the necessary software installed to construct our sample application. Let’s do that now.
-
3. The Sample App
(You can view the complete sample app on GitHub if you have trouble following along here.)
Our sample app is comprised of two Ruby source files:
evernote_config.rb, which contains a few bits of configuration information.en_oauth.rb, the application source code.
Configuration
evernote_config.rbwill look something like this:evernote_config.rb # Load libraries required by the Evernote OAuth require 'oauth' require 'oauth/consumer' # Load Thrift & Evernote Ruby libraries require "evernote_oauth" # Client credentials OAUTH_CONSUMER_KEY = "your key" OAUTH_CONSUMER_SECRET = "your secret" # Connect to Sandbox server? SANDBOX = trueThe two
requirestatements at the top are for the Ruby OAuth gem, followed by theevernote_oauthgem. This gem depends on theoauthgem and implements the entire Evernote Cloud API, plus additional OAuth-related functionality.Below that, we have our Consumer Key and Consumer Secret values (which should be set to the corresponding parts of your Evernote Cloud API Key).
Finally, we have a boolean variable
SANDBOXwhich controls whether our app will connect to the Sandbox development server. If false, our app would connect to the production Evernote service.Our App’s Components
Within
en_oauth.rb, we have a few different pieces of our application: thebeforefilter, thehelpersfunctions, ourgetroutes and a pair of simple ERB templates.First, let’s cover what I’m lovingly calling “the preamble”:
en_oauth.rb require 'sinatra' enable :sessions # Load our dependencies and configuration settings $LOAD_PATH.push(File.expand_path(File.dirname(__FILE__))) require "evernote_config.rb"First, we include the Sinatra gem and enable cookie-based sessions (part of Sinatra). Second, we include the current directory in the
$LOAD_PATH(so we can load our configuration file). Finally, we load said configuration file. Pretty straightforward.Now, on with the meat of the app…
beforeThis filter runs when the app is initialized and verifies that the
OAUTH_CONSUMER_KEYandOAUTH_CONSUMER_SECRETconstants have been set inevernote_config.rb. If either constant is initialized to empty string, the error message is displayed:en_oauth.rb before do if OAUTH_CONSUMER_KEY.empty? || OAUTH_CONSUMER_SECRET.empty? halt '<span style="color:red">Before using this sample code you must edit evernote_config.rb and replace OAUTH_CONSUMER_KEY and OAUTH_CONSUMER_SECRET with the values that you received from Evernote. If you do not have an API key, you can request one from <a href="http://dev.evernote.com/documentation/cloud/">dev.evernote.com/documentation/cloud/</a>.</span>' end endhelpersA collection of plain Ruby functions that will be used in our application. Many of them simply represent variables that we’ve abstracted out of the routes portion of the application. As it happens, this section also contains the calls to the Evernote Cloud API. Let’s quickly look at each of these:
auth_tokenstores the authentication token we’ll be using to make calls against the Evernote Cloud API. Because our web app is necessarily stateless, this value is stored in Sinatra'ssession:
en_oauth.rb def auth_token session[:access_token].token if session[:access_token] endclientrepresents the instance ofEvernoteOAuth::Client. It is instantiated usingauth_token(if we have one),OAUTH_CONSUMER_KEY,OAUTH_CONSUMER_SECRETandSANDBOX.
en_oauth.rb def client @client ||= EvernoteOAuth::Client.new(token: auth_token, consumer_key:OAUTH_CONSUMER_KEY, consumer_secret:OAUTH_CONSUMER_SECRET, sandbox: SANDBOX) enduser_storeandnote_storeare the two services of the Evernote Cloud API: the former deals with user and account information, the latter with the user’s notes, notebooks, etc.
en_oauth.rb def user_store @user_store ||= client.user_store end def note_store @note_store ||= client.note_store enden_userrepresents a single EvernoteUserinstance retrieved from the Evernote Cloud API.
en_oauth.rb def en_user user_store.getUser(auth_token) endnotebookscontains a collection ofNotebookobjects as returned by the Evernote Cloud API whenNoteStore.listNotebooks()is called.
en_oauth.rb def notebooks @notebooks ||= note_store.listNotebooks(auth_token) end
total_note_countis the only function in our app that performs logic beyond simply returning a value and deserves a little more explanation…en_oauth.rb def total_note_count filter = Evernote::EDAM::NoteStore::NoteFilter.new counts = note_store.findNoteCounts(auth_token, filter, false) notebooks.inject(0) do |total_count, notebook| total_count + (counts.notebookCounts[notebook.guid] || 0) end endWe begin with
filter, a new (empty) instance ofNoteFilter. Since we want to query the entire account, we won’t be adding any additional filtering criteria (see the fullNoteFilterdefinition here).Next, we call
findNoteCounts, passing ourauth_tokenandfilteras parameters (thefalseparameter indicates that we don’t want notes from the account’s Trash container).findNoteCountsreturns an instance ofNoteCollectionCounts— a hash of notebook GUIDs mapped to the number of notes in the corresponding notebook. Iterating over all of our notebooks, we askcounts(our instance ofNoteCollectionCounts) for the number of notes in that notebook—using its GUID—and return the total of these counts.getRoutesEach instance of
getin our app represents an HTTP request and a corresponding URL pattern. Our application has a total of seven routes. Four of them deal directly with our OAuth implementation and will be discussed in the next section. The remaining three are:en_oauth.rb get '/' do erb :index endThe root of our site, this route simply displays the
indextemplate (defined as@@indexat the end ofen_auth.rb) and prompts the user to begin the OAuth authentication process.en_oauth.rb get '/reset' do session.clear redirect '/' endIn this route, we first clear the
sessionobject (which is used to track the user’s progress and state across a necessarily stateless web interaction), then redirect the user to/so they can begin the process again.en_oauth.rb get '/list' do begin # Get notebooks session[:notebooks] = notebooks.map(&:name) # Get username session[:username] = en_user.username # Get total note count session[:total_notes] = total_note_count erb :index rescue => e @last_error = "Error listing notebooks: #{e.message}" erb :error end endThis method collects various information about the user’s account (using methods defined in the
helperssection of our app), adds them to the currentsessionobject and renders theindextemplate. If there are any problems, therescueblock of our code is triggered and the@last_errorvariable is populated with the cause of the error and is sent to theerrortemplate (also defined at the end ofen_auth.rb).OAuth
As with most OAuth flows, our authentication takes places over a series of steps:
- Retrieve a request token from the Evernote Cloud API. This token is used to request an authentication token in the next step.
- Using the request token, send the user to the Evernote site where they will authenticate with their login credentials and authorize our application to access their account.
- The Evernote website will redirect the user back to our defined callback URL and append, among other things, the authentication token we will use when making calls against the Evernote Cloud API.
This process is somewhat automated in our sample application (at least, in terms of user interaction). That is to say, as each step of the process is completed, the next step will be invoked automatically using Sinatra’s
redirectfacility.en_oauth.rb get '/requesttoken' do callback_url = request.url.chomp("requesttoken").concat("callback") begin session[:request_token] = client.request_token(:oauth_callback => callback_url) redirect '/authorize' rescue => e @last_error = "Error obtaining temporary credentials: #{e.message}" erb :error end endHere, we ask the Evernote Cloud API for our temporary request token.
To start, we take the current URL as defined by the
requestobject’surlmember, remove therequesttokenportion and replace it withcallbackto build our callback URL. Then, we callclient.authentication_request_tokento request our token (and sending thecallback_urlvariable to tell the Evernote Cloud API where to send the response. If all of this goes well, thesession[:request_token]is populated with the token value and the user is scuttled off to/authorize:en_oauth.rb get '/authorize' do if session[:request_token] redirect session[:request_token].authorize_url else # You shouldn't be invoking this if you don't have a request token @last_error = "Request token not set." erb :error end endOnce we verify that
session[:request_token]exists, we grab theauthorize_urlfrom the request token and send the user there where they’ll be prompted to login with their Evernote account credentials and authorize our application to access their account. Ifsession[:requesttoken]is not set, the user will see an error and be prompted to start the process over.After successfully authenticating with Evernote and authorizing our app, they’re sent to
/callback(which we defined during the/requesttokenroute):en_oauth.rb get '/callback' do unless params['oauth_verifier'] || session['request_token'] @last_error = "Content owner did not authorize the temporary credentials" halt erb :error end session[:oauth_verifier] = params['oauth_verifier'] begin session[:access_token] = session[:request_token].get_access_token(:oauth_verifier => session[:oauth_verifier]) redirect '/list' rescue => e @last_error = 'Error extracting access token' erb :error end endWhen the user is sent back to our callback URL, we verify that either:
- The
oauth_verifierparameter is set in the URL. - The
request_tokenvalue is already present in thesession.
If one of these two conditions is true, we include the
oauth_verifierparameter in oursession. If neither of these conditions is true, the user is shown an error.Next, we extract the access token (used to make API calls) from the and add it to
session. If it’s not there, an error is displayed. If it is, we send the user to the/listroute we defined earlier. Assuming everything worked as advertised, the user will see their username, number of notes in their account and the name of each notebook in their account.Templates
Our app utilizes two ERB templates:
indexanderror:en_oauth.rb __END__ @@ index <html> <head> <title>Evernote Ruby Example App</title> </head> <body> <a href="/requesttoken">Click here</a> to authenticate this application using OAuth. <% if session[:notebooks] %> <hr /> <h3>The current user is <%= session[:username] %> and there are <%= session[:total_notes] %> notes in their account</h3> <br /> <h3>Here are the notebooks in this account:</h3> <ul> <% session[:notebooks].each do |notebook| %> <li><%= notebook %></li> <% end %> </ul> <% end %> </body> </html>Clearly, this is a very simple template. First, we allow the user to authenticate with the Evernote Cloud API. This is displayed no matter what. If
session[:notebooks]is defined, we can be reasonably sure that the user’s data has been retrieved from the Evernote Cloud API. Then we display each piece of data.en_oauth.rb @@ error <html> <head> <title>Evernote Ruby Example App — Error</title> </head> <body> <p>An error occurred: <%= @last_error %></p> <p>Please <a href="/reset">start over</a>.</p> </body> </html>Even simpler than
indexiserror, which displays the value of@last_errorand prompts the user to reset their session and begin the process again. -
4. Conclusion
We made it!
If you had any problems with the above application, ensure that you’re running the required versions of the various components. If you still need help, head over to the Evernote Developer site and get in touch.