נתוני Google לגבי מסילות

אריק בידלמן, צוות Google Data APIs
פברואר 2009

מבוא

"Where's Ruby on the customer directories?"

התשוקה הגדולה של המפתחים שלנו והפופולריות הגבוהה של Ruby on Rails (RoR) מעוררת תיאבון, והעמיתה שלי ג'ף פישר זילבה ספריית כלי אודם במעמקי הגבעה של מאונט דום. חשוב לזכור שזוהי לא ספריית לקוח מלאה, אבל היא מטפלת בעקרונות בסיסיים, כמו אימות ומניפולציה בסיסית ב-XML. כדי להשתמש בו צריך לעבוד ישירות עם פיד ה-Atom באמצעות המודול REXML ו-XPath.

Audience

המאמר הזה מיועד למפתחים שרוצים לקבל גישה לממשקי ה-API של Google Data באמצעות Ruby, ובאופן ספציפי Ruby on Rails. הוא יוצא מנקודת הנחה שהקורא מכיר היטב את שפת התכנות Ruby ואת המסגרת לפיתוח אתרים של Rails. אני מתמקד ב-Docs List API לרוב הדוגמאות, אבל ניתן ליישם את אותם קונספטים בכל אחד מה-API של הנתונים.

תחילת העבודה

הדרישות

התקנת ספריית הנתונים של Google Data Ruby Utility

כדי למצוא את הספרייה, אפשר להוריד את מקור הספרייה ישירות מאירוח הפרויקט או להתקין את אבן החן:

sudo gem install gdata

טיפ: מומלץ להשתמש ב-gem list --local כדי לוודא שהסמן הותקן כראוי.

אימות

ClientLogin

בעזרת ClientLogin האפליקציה שלך יכולה להתחבר באופן פרוגרמטי לחשבונות Google או G Suite. לאחר אימות פרטי הכניסה של המשתמש, Google מנפיקה אסימון אימות כהפניה לבקשות API נוספות. האסימון תקף למשך פרק זמן מוגדר, המוגדר לפי שירות Google שאיתו אתם עובדים. מטעמי אבטחה וכדי לספק למשתמשים שלך את החוויה הטובה ביותר, עליך להשתמש ב-ClientLogin רק בעת פיתוח יישומים שולחניים ומותקנים. באפליקציות אינטרנט, עדיף להשתמש ב-AuthSub או ב-OAuth.

לספריית Ruby יש סיווג לקוח לכל אחד מממשקי ה-API. לדוגמה, אפשר להשתמש בקטע הקוד הבא כדי להתחבר ל-user@gmail.com ל-Docs Documents Data API:

client = GData::Client::DocList.new
client.clientlogin('user@gmail.com', 'pa$$word')

The YouTube Data API would be:

client = GData::Client::YouTube.new
client.clientlogin('user@gmail.com', 'pa$$word')

להצגת הרשימה המלאה של מחלקות השירות המיושמות. אם השירות לא כולל את סיווג הלקוח, תוכלו להשתמש באותה מחלקה GData::Client::Base. לדוגמה, הקוד הבא מאלץ את המשתמשים להיכנס עם חשבון G Suite.

client_login_handler = GData::Auth::ClientLogin.new('writely', :account_type => 'HOSTED')
token = client_login_handler.get_token('user@example.com', 'pa$$word', 'google-RailsArticleSample-v1')
client = GData::Client::Base.new(:auth_handler => client_login_handler)

הערה: כברירת מחדל, הספרייה משתמשת ב-HOSTED_OR_GOOGLE של accountType. הערכים האפשריים הם HOSTED_OR_GOOGLE, HOSTED או GOOGLE.

אחד החסרונות בשימוש ב-ClientLogin הוא שניתן לשלוח לאפליקציה שלך בקשות CAPTCHA במהלך ניסיונות התחברות שנכשלו. במקרה כזה, תוכלו לתקן את השגיאה על ידי קריאה לשיטה clientlogin() באמצעות הפרמטרים הנוספים שלה: client.clientlogin(username, password, captcha_token, captcha_answer). במסמכי התיעוד המלאים על אימות לאפליקציות מותקנות תוכלו לקרוא מידע נוסף על השימוש ב-CAPTCHA.

AuthSub

יצירת כתובת האתר של AuthSubRequest

scope = 'http://www.google.com/calendar/feeds/'
next_url = 'http://example.com/change/to/your/app'
secure = false  # set secure = true for signed AuthSub requests
sess = true
authsub_link = GData::Auth::AuthSub.get_url(next_url, scope, secure, sess)

בלוק הקוד הקודם יוצר את כתובת ה-URL הבאה ב-authsub_link:

https://www.google.com/accounts/AuthSubRequest?next=http%3A%2F%2Fexample.com%2Fchange%2Fto%2Fyour%2Fapp&scope=http%3A%2F%2Fwww.google.com%2Fcalendar%2Ffeeds%2F&session=1&secure=0

אפשר גם להשתמש בשיטה authsub_url של אובייקט הלקוח. לכל רמת שירות הוגדר מאפיין ברירת מחדל authsub_scope, כך שאין צורך לציין מאפיין משלכם.

client = GData::Client::DocList.new
next_url = 'http://example.com/change/to/your/app'
secure = false  # set secure = true for signed AuthSub requests
sess = true
domain = 'example.com'  # force users to login to a G Suite hosted domain
authsub_link = client.authsub_url(next_url, secure, sess, domain)

בלוק הקוד הקודם יוצר את כתובת ה-URL הבאה:

https://www.google.com/accounts/AuthSubRequest?next=http%3A%2F%2Fexample.com%2Fchange%2Fto%2Fyour%2Fapp&scope=http%3A%2F%2Fdocs.google.com%2Ffeeds%2F&session=1&secure=0&hd=example.com

שדרוג של אסימון לשימוש חד-פעמי באסימון

AuthSub יפנה את המשתמש בחזרה אל http://example.com/change/to/your/app?token=SINGLE_USE_TOKEN לאחר שתעניק גישה לנתונים שלו. שימו לב שכתובת ה-URL היא רק next_url עם האסימון לשימוש יחיד שמתווסף כפרמטר של שאילתה.

לאחר מכן, מחליפים את האסימון לשימוש חד-פעמי באסימון עם משך חיים ארוך:

client.authsub_token = params[:token] # extract the single-use token from the URL query params
session[:token] = client.auth_handler.upgrade()
client.authsub_token = session[:token] if session[:token]

AuthSub מאובטח דומה מאוד. התוספת היחידה היא הגדרת המפתח הפרטי שלך לפני שדרוג האסימון:

PRIVATE_KEY = '/path/to/private_key.pem'

client.authsub_token = params[:token]
client.authsub_private_key = PRIVATE_KEY
session[:token] = client.auth_handler.upgrade()
client.authsub_token = session[:token] if session[:token]

הערה: כדי להשתמש באסימונים מאובטחים, חשוב להגדיר את secure=true כשמבקשים אסימון לשימוש חד-פעמי. יש לעיין למעלה בקטע יצירת כתובת ה-URL של AuthSubRequest.

ניהול אסימונים

AuthSub מספק שני רכיבי handler נוספים, AuthSubTokenInfo ו-AuthSubRevokeToken לניהול אסימונים. בעזרת AuthSubTokenInfo אפשר לבדוק את התקינות של האסימון. AuthSubRevokeToken מאפשר למשתמשים להפסיק את הגישה לנתונים שלהם. השיטה המומלצת לאפליקציה היא AuthSubRevokeToken. שתי השיטות נתמכות בספריית Ruby.

כדי לשלוח שאילתה למטא-נתונים של אסימון:

client.auth_handler.info

כדי לבטל אסימון סשן:

client.auth_handler.revoke

עיין בתיעוד המלא של אימות SubSub עבור יישומי אינטרנט עבור הסקופ המלא ב-AuthSub.

OAuth

במועד כתיבת המאמר הזה, ה-OAuth לא נוסף למודול GData::Auth.

השימוש ב-OAuth בספריית השירות צריך להיות קל יחסית כשמשתמשים ב-Rails oauth-plugin או Ruby oauth gem. בכל מקרה, כדאי ליצור אובייקט GData::HTTP::Request ולהעביר אותו לכותרת Authorization שנוצרת על ידי כל ספרייה.

גישה לפידים

GET (שליפת נתונים)

אחרי שמגדירים אובייקט לקוח, צריך להשתמש בשיטה get() כדי לשלוח שאילתה לגבי פיד נתוני Google. אפשר להשתמש ב-XPath כדי לאחזר רכיבי Atom ספציפיים. הנה דוגמה לאחזור מסמכי Google של משתמש:

feed = client.get('http://docs.google.com/feeds/documents/private/full').to_xml

feed.elements.each('entry') do |entry|
  puts 'title: ' + entry.elements['title'].text
  puts 'type: ' + entry.elements['category'].attribute('label').value
  puts 'updated: ' + entry.elements['updated'].text
  puts 'id: ' + entry.elements['id'].text
  
  # Extract the href value from each <atom:link>
  links = {}
  entry.elements.each('link') do |link|
    links[link.attribute('rel').value] = link.attribute('href').value
  end
  puts links.to_s
end

POST (יצירת נתונים חדשים)

צריך להשתמש בשיטה post() של לקוח כדי ליצור נתונים חדשים בשרת. בדוגמה הבאה יתווסף new_writer@example.com בתור שותף עריכה למסמך עם המזהה: doc_id.

# Return documents the authenticated user owns
feed = client.get('http://docs.google.com/feeds/documents/private/full/-/mine').to_xml
entry = feed.elements['entry']  # first <atom:entry>

acl_entry = <<-EOF
<entry xmlns="http://www.w3.org/2005/Atom" xmlns:gAcl='http://schemas.google.com/acl/2007'>
  <category scheme='http://schemas.google.com/g/2005#kind'
    term='http://schemas.google.com/acl/2007#accessRule'/>
  <gAcl:role value='writer'/>
  <gAcl:scope type='user' value='new_writer@example.com'/>
</entry>
EOF

# Regex the document id out from the full <atom:id>.
# http://docs.google.com/feeds/documents/private/full/document%3Adfrk14g25fdsdwf -> document%3Adfrk14g25fdsdwf
doc_id = entry.elements['id'].text[/full\/(.*%3[aA].*)$/, 1]
response = client.post("http://docs.google.com/feeds/acl/private/full/#{doc_id}", acl_entry)

PUT (עדכון נתונים)

כדי לעדכן נתונים בשרת, יש להשתמש בשיטה put() של הלקוח. הדוגמה הבאה תעדכן את הכותרת של המסמך. ההנחה היא שיש לכם פיד משאילתה קודמת.

entry = feed.elements['entry'] # first <atom:entry>

# Update the document's title
entry.elements['title'].text = 'Updated title'
entry.add_namespace('http://www.w3.org/2005/Atom')
entry.add_namespace('gd','http://schemas.google.com/g/2005')

edit_uri = entry.elements["link[@rel='edit']"].attributes['href']
response = client.put(edit_uri, entry.to_s)

מחיקה

כדי למחוק <atom:entry> או נתונים אחרים מהשרת, יש להשתמש בשיטה delete(). הדוגמה הבאה תמחק מסמך. הקוד יוצא מנקודת הנחה שיש רשומת מסמך משאילתה קודמת.

entry = feed.elements['entry'] # first <atom:entry>
edit_uri = entry.elements["link[@rel='edit']"].attributes['href']
client.headers['If-Match'] = entry.attribute('etag').value  # make sure we don't nuke another client's updates
client.delete(edit_uri)

יצירת אפליקציה חדשה של Rails

בדרך כלל התרגיל הראשון ביצירה של אפליקציית Rails חדשה כרוך בהפעלת מחוללי הפיגומים כדי ליצור את קובצי ה-MVC. לאחר מכן, הוא מפעיל את rake db:migrate כדי להגדיר את טבלאות מסדי הנתונים. עם זאת, מאחר שהאפליקציה שלנו שולחת שאילתה לממשק ה-API של רשימת המסמכים של Google, יש לנו צורך מועט בפיגומים כלליים או במסדי נתונים. במקום זאת, צרו אפליקציה חדשה ובקר פשוט:

rails doclist
cd doclist
ruby script/generate controller doclist

ולבצע את השינויים הבאים ב-config/environment.rb:

config.frameworks -= [ :active_record, :active_resource, :action_mailer ]
config.gem 'gdata', :lib => 'gdata'

השורה הראשונה מבטלת את הנעילה של ActiveRecord מהאפליקציה. השורה השנייה טוענת את אבן החן gdata בזמן האתחול.

לבסוף, בחרתי לחבר את מסלול ברירת המחדל ('/') לפעולה documents בDoclistController. הוספת השורה הזו אל config/routes.rb:

map.root :controller => 'doclist', :action => 'all'

הפעלת שלט רחוק

מאחר שלא יצרנו פיגומים, הוסיפו באופן ידני פעולה בשם 'all' ל-DoclistController ב-app/controllers/doclist_controller.rb.

class DoclistController < ApplicationController
  def all
    @foo = 'I pity the foo!'
  end
end

וליצור all.html.erb במסגרת app/views/doclist/:

<%= @foo %>

הפעלת שרת האינטרנט והתחלת הפיתוח

עכשיו אמורה להיות לך אפשרות להפעיל את שרת האינטרנט המוגדר כברירת מחדל על ידי הפעלה של ruby script/server. אם אין בעיה, הכוונה של הדפדפן ל-http://localhost:3000/ היא 'I pity the foo!'.

טיפ: חשוב לזכור להסיר את השם של public/index.html או לשנות את השם שלו.

כשיהיה לכם משהו לעבוד, תוכלו לצפות ב-DoclistController האחרון שלי וב-ApplicationController לבשר הפרויקט של DocList Manager. כדאי גם לבדוק את ContactsController, המטפל בקריאות ל-Google contacts API.

סיכום

החלק הקשה ביותר ביצירה של אפליקציית Google Data Rails הוא הגדרת Rails! עם זאת, המערכת מבצעת פריסה של האפליקציה שלכם. לכן מומלץ מאוד להשתמש ב-mod_rails ב-Apache. קל מאוד להגדיר, להתקין ולהפעיל. תוך זמן קצר תוכל להתחיל לפרסם!

משאבים

נספח

דוגמאות

DocList Manager הוא דוגמה מלאה ל-Ruby on Rails שממחישה את הנושאים שמוזכרים במאמר הזה. קוד המקור המלא זמין לאירוח פרויקטים.