פברואר 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. קל מאוד להגדיר, להתקין ולהפעיל. תוך זמן קצר תוכל להתחיל לפרסם!
משאבים
- רשימת ממשקי API של נתונים של Google
- דף הפרויקט Google Data Ruby Utility Library
- מאמר: שימוש ב-Ruby עם ממשקי ה-API של נתונים של Google
- להורדת Ruby
- להורדת RubyGems and Rails
נספח
דוגמאות
DocList Manager הוא דוגמה מלאה ל-Ruby on Rails שממחישה את הנושאים שמוזכרים במאמר הזה. קוד המקור המלא זמין לאירוח פרויקטים.
דוגמאות נוספות
אחזור רשימת משתמשים של מסמכי Google באמצעות ClientLogin.
#!/usr/bin/ruby
require 'rubygems'
require 'gdata'
require 'highline/import'
def user_input(prompt='> ', echo=true)
ask(prompt) {|q| q.echo = echo}
end
email = user_input('email: ')
pass = user_input('password: ', false)
client = GData::Client::DocList.new({:source => 'google-RailsArticleSample-v1'})
client.clientlogin(email, pass)
feed = client.get(client.authsub_scope + 'documents/private/full').to_xml
puts 'Listing all documents: --------------------'
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[/%3[aA](.*)$/, 1]
puts
end
ערבוב באמצעות ממשקי ה-API של רשימת המסמכים, אנשי הקשר והגיליונות האלקטרוניים. הדוגמה הזו מדגימה את השימוש ב-AuthSub מאובטח ב-RoR.
require 'gdata'
class YourController < ApplicationController
before_filter :authenticate_client
PRIVATE_KEY = '/path/to/private_key.pem'
DOCLIST_SCOPE = 'http://docs.google.com/feeds/'
CONTACTS_SCOPE = 'http://www.google.com/m8/feeds/'
SPREADSHEETS_SCOPE = 'http://spreadsheets.google.com/feeds/'
def authenticate_client
scopes = [DOCLIST_SCOPE, SPREADSHEETS_SCOPE, CONTACTS_SCOPE]
@client = GData::Client::Base.new({:authsub_scope => scopes.join(' '),
:source => 'google-RailsArticleSample-v1'})
if params[:token].nil? and session[:token].nil?
next_url = url_for :controller => self.controller_name, :action => self.action_name
secure = true
@authsub_link = @client.authsub_url(next_url, secure, true)
render :controller => 'your_controller', :action => 'your_action'
elsif params[:token] and session[:token].nil?
@client.authsub_token = params[:token]
@client.authsub_private_key = PRIVATE_KEY
session[:token] = @client.auth_handler.upgrade()
end
if session[:token]
@client.authsub_token = session[:token]
@client.authsub_private_key = PRIVATE_KEY # Make sure to set your private key for subsequent requests
end
end
def get_docs
@feed = @client.get(@client.authsub_scope + 'documents/private/full').to_xml
end
end
יצירה, עריכה ומחיקה של פלייליסט ב-YouTube באמצעות אסימון AuthSub.
#!/usr/bin/ruby require 'rubygems' require 'gdata' yt = GData::Client::YouTube.new({:source => 'google-RailsArticleSample-v1'}) yt.authsub_token = 'SESSION_TOKEN' yt.client_id = 'CLIENT_ID' yt.developer_key = 'DEVELOPER_KEY' # 1.) Creating a new playlist entry = <<-EOF <entry xmlns="http://www.w3.org/2005/Atom" xmlns:yt="http://gdata.youtube.com/schemas/2007"> <title type="text">Ruby Utility Unit Test</title> <summary>This is a test playlist.</summary> </entry> EOF response = yt.post('http://gdata.youtube.com/feeds/api/users/default/playlists', entry).to_xml # 2.) Updating the received playlist edit_uri = response.elements["link[@rel='edit']"].attributes['href'] response.elements['summary'].text = 'Updated description' response = yt.put(edit_uri, response.to_s).to_xml # 3.) Deleting the playlist yt.delete(edit_uri)
פקודות שימושיות של המסילה
אם היית כמוני אבל לא השתמשת ב-Rails במשך זמן מה, הנה רשימת פקודות שכדאי לזכור.
- עדכון ההתקנה של RubyGem
sudo gem update --system
- עדכון אבני חן מותקנות
sudo gem update
- יצירת Rails חדשים עם MySQL בתור ה-db
rails -d mysql APP_NAME
- הפעלת השרת בסביבת ייצור ביציאה אחרת
ruby script/server -p PORT -e production
- ניקוי קובצי היומן
rake log:clear