2
# profile.rb: profile plugin for tDiary
5
# profile(id[, service = :twitter])
6
# - id: user ID for profile service
7
# - service: profile service (default is :twitter)
8
# Choose from :github, :twitter, :friendfeed, :iddy
10
# Copyright (C) 2009 by MATSUOKA Kohei < http://www.machu.jp/ >
11
# Distributed under the GPL.
14
require 'rexml/document'
22
# base class for profile services
29
attr_reader :description
32
# class instance variables
34
attr_reader :properties
35
attr_reader :endpoint_proc
38
# set property and xpath pair for parse XML document
39
def self.property(property, path)
41
@properties[property] = path
44
# set endpoint proc (this proc is called by initialize method with id)
45
def self.endpoint(&block)
46
@endpoint_proc = block
49
def initialize(id, options = {})
53
if self.class.endpoint_proc
54
endpoint = self.class.endpoint_proc.call(id)
60
# get a XML document from endpoint and create REXML::Document instance
64
doc = REXML::Document.new(f)
69
# parse XML document with properties
71
self.class.properties.each do |property, path|
73
value = doc.elements[path].text
74
instance_variable_set("@#{property}", value)
82
property :name, '//user/name'
83
property :mail, '//user/email'
84
endpoint {|id| "http://github.com/api/v2/xml/user/show/#{id}" }
87
Gravatar.new(@mail, @options).image
88
# "http://www.gravatar.com/avatar/#{Digest::MD5.hexdigest(@mail)}.jpg"
92
"http://github.com/#{@id}"
98
property :name, '//user/name'
99
property :image, '//user/profile_image_url'
100
property :description, '//user/description'
101
endpoint {|id| "http://twitter.com/users/show/#{id}.xml" }
104
"http://twitter.com/#{@id}"
109
class FriendFeed < Base
110
property :name, '//feed/name'
111
property :description, '//feed/description'
112
endpoint {|id| "http://friendfeed-api.com/v2/feed/#{id}?format=xml&num=0" }
115
"http://friendfeed-api.com/v2/picture/#{id}"
119
"http://friendfeed.com/#{@id}"
124
# this class is based on iddy.rb
126
######################################################################
127
# If you will modify or release another version of this code,
128
# please get your own application key from iddy.jp and replace below.
129
######################################################################
130
API_KEY = '9262ea8ffba962aabb4f1a1d3f1cfa953b11aa23' unless defined? API_KEY
132
property :name, '//response/users/user/accountname'
133
property :image, '//response/users/user/imageurl'
134
property :description, '/response/users/user/profile'
135
endpoint {|id| "http://iddy.jp/api/user/?apikey=#{API_KEY}&accountname=#{id}" }
138
"http://iddy.jp/profile/#{@id}/"
143
class Gravatar < Base
145
size = @options[:size] ? "?s=#{@options[:size]}" : ""
146
"http://www.gravatar.com/avatar/#{Digest::MD5.hexdigest(@id.downcase)}.jpg#{size}"
151
property :image, '//statuses/status/user/profile_image_url'
152
endpoint {|id| "http://api.wassr.jp/statuses/show.xml?id=#{id}" }
155
"http://wassr.jp/user/#{id}"
162
"http://www.hatena.ne.jp/users/#{prefix}/#{id}/profile.gif"
166
"http://www.hatena.ne.jp/#{id}/"
172
PROFILE_VERSION = '20090909'
174
def profile(id, service = :twitter, options = {})
178
:twitter => Profile::Service::Twitter,
179
:github => Profile::Service::GitHub,
180
:friendfeed => Profile::Service::FriendFeed,
181
:iddy => Profile::Service::Iddy,
182
:gravatar => Profile::Service::Gravatar,
183
:wassr => Profile::Service::Wassr,
184
:hatena => Profile::Service::Hatena,
185
}[service.to_s.downcase.to_sym]
187
# TODO: create cache manager class
189
# cache = "#{@cache_path}/profile.yaml"
190
cache = "#{@cache_path}/profile.pstore"
192
# db = YAML::Store.new(cache)
193
db = PStore.new(cache)
195
key = service_class.name
196
db[key] ||= {} # initialize db
197
updated = db[key][:updated]
198
if updated && (Time::now < updated + 60 * 60) && db[key][:version] == PROFILE_VERSION
200
profile = db[key][:profile]
202
# get latest date and update cache
204
profile = service_class.new(id, options)
205
rescue Timeout::Error, StandardError
206
return html << %Q{ <div class="profile">no profile</div> }
208
db[key][:updated] = Time::now
209
db[key][:profile] = profile
210
db[key][:version] = PROFILE_VERSION
214
html << %Q{ <div class="profile"><a href="#{CGI.escapeHTML profile.link}"> }
215
html << %Q{ <span class="profile-image"><img src="#{CGI.escapeHTML profile.image}" alt="profile image"></span> } if profile.image
216
html << %Q{ <span class="profile-name">#{CGI.escapeHTML profile.name}</span> } if profile.name
217
html << %Q{ <span class="profile-mail">#{CGI.escapeHTML profile.mail}</span> } if profile.mail
218
html << %Q{ <span class="profile-description">#{CGI.escapeHTML profile.description}</span> } if profile.description
219
html << %Q{ </a></div> }