Skip to content

Commit

Permalink
Logger support, and some helpful documentation
Browse files Browse the repository at this point in the history
  • Loading branch information
sinisterchipmunk committed Jun 16, 2010
1 parent 75a0c42 commit 8570ea9
Show file tree
Hide file tree
Showing 9 changed files with 263 additions and 40 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
.idea
tmp

## MAC OS
.DS_Store
Expand Down
73 changes: 71 additions & 2 deletions README.rdoc
Original file line number Diff line number Diff line change
@@ -1,6 +1,75 @@
= gravatar
= gravatar-ultimate

Description goes here.
The Ultimate Gravatar Gem!

This gem is used to interface with the entire Gravatar API: it's not just for generating image URLs, but for connecting
to and communicating with the XML-RPC API too! Additionally, it can be used to download the Gravatar image data itself,
rather than just a URL to that data. This saves you the extra step of having to do so.

== Installation

gem install gravitar-ultimate

=== Activate the gem...
As with any gem, you have to type a few lines to tell Ruby to actually *use* it. Here's how to do that...

==== ...in Ruby on Rails

* Edit your config/environment.rb file
* Add this line beneath "Rails::Initializer.run do |config|":
* config.gem 'gravitar-ultimate'

==== ...in vanilla Ruby

require 'rubygems'
gem 'gravitar-ultimate'
require 'gravitar-ultimate'

== Usage
Using the gem is actually pretty simple. Let's say you want the Gravatar image URL for "generic@example.com":
url = Gravatar.new("generic@example.com").image_url

Cool, huh? Let's take it a step further and grab the actual image *data* so that we can render it on the screen:
data = Gravatar.new("generic@example.com").image_data

Fine, but how about the rest of the API as advertised at http://en.gravatar.com/site/implement/xmlrpc? Well, for
that you need either the user's Gravatar password, or their API key:

api = Gravatar.new("generic@example.com", :password => "helloworld")
api = Gravatar.new("generic@example.com", :api_key => "AbCdEfG1234")

After you have that, things get a lot easier:

api.exists? #=> true or false, depending on whether this user has an account.
api.addresses #=> a list of email addresses and their corresponding images
api.save_data!(rating, image_data) #=> saves an image to this user's account and returns a handle to it
api.use_user_image!(handle, an_email_address) #=> uses the specified image handle for the specified email address(es)
api.exists?("another@example.com") #=> true or false, depending on whether the specified email exists.

As you can see this is quite powerful. But it gets better. Gravatar Ultimate actually manages caching for you! That way,
if an error occurs, (such as the Gravatar site being offline), your code won't break. It'll instead gracefully fall
back to the cached copy! By default, if you are using Rails, it'll use the Rails cache. Otherwise, it'll use whatever
cache you're using with Gravatar (by default an instance of ActiveSupport::Cache::FileStore).

=== Configuration

To see settings and options you can give for a particular Gravatar instance, check out the Gravatar class documentation.
There are a few things you can set for Gravatar on a system-wide basis, and that's what we'll go over next.

For a non-Rails project, simply set these options before you start using Gravatar. For a Rails project, you should set
them within an Initializer in config/initializers/any_filename.rb in order to ensure that the settings are applied
(A) after Gravatar has been included into the project, and (B) before it is actually used by Rails.

# You can set the default cache for Gravatar to use:
Gravatar.cache = ActiveSupport::Cache::SynchronizedMemoryStore.new

# You can also set the length of time an item in the Gravatar cache is valid. Default is 24.hours
Gravatar.duration = 20.minutes

# You can also change the logger used by default. It's worth mentioning that, once again, Gravatar will use
# the Rails logger if it's available. Otherwise, the default is $stdout.
grav_log = ""
Gravatar.logger = StringIO.new(grav_log) # logs Gravatar output to a String

== Note on Patches/Pull Requests

Expand Down
11 changes: 8 additions & 3 deletions Rakefile
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,19 @@ require 'rake'
begin
require 'jeweler'
Jeweler::Tasks.new do |gem|
gem.name = "gravatar"
gem.name = "gravatar-ultimate"
gem.summary = %Q{A gem for interfacing with the entire Gravatar API: not just images, but the XML-RPC API too!}
gem.description = %Q{TODO: longer description of your gem}
gem.description = %Q{The Ultimate Gravatar Gem!
This gem is used to interface with the entire Gravatar API: it's not just for generating image URLs, but for connecting
to and communicating with the XML-RPC API too! Additionally, it can be used to download the Gravatar image data itself,
rather than just a URL to that data. This saves you the extra step of having to do so.}
gem.email = "sinisterchipmunk@gmail.com"
gem.homepage = "http://github.com/sinisterchipmunk/gravatar"
gem.authors = ["Colin MacKenzie IV"]
gem.add_dependency "sc-core-ext", ">= 1.2.0"
gem.add_development_dependency "rspec", ">= 0"
gem.add_development_dependency "rspec", ">= 1.3.0"
gem.add_development_dependency "fakeweb", ">= 1.2.8"
end
Jeweler::GemcutterTasks.new
rescue LoadError
Expand Down
76 changes: 76 additions & 0 deletions gravatar-ultimate.gemspec
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
# Generated by jeweler
# DO NOT EDIT THIS FILE DIRECTLY
# Instead, edit Jeweler::Tasks in Rakefile, and run the gemspec command
# -*- encoding: utf-8 -*-

Gem::Specification.new do |s|
s.name = %q{gravatar-ultimate}
s.version = "1.0.0"

s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
s.authors = ["Colin MacKenzie IV"]
s.date = %q{2010-06-16}
s.description = %q{The Ultimate Gravatar Gem!
This gem is used to interface with the entire Gravatar API: it's not just for generating image URLs, but for connecting
to and communicating with the XML-RPC API too! Additionally, it can be used to download the Gravatar image data itself,
rather than just a URL to that data. This saves you the extra step of having to do so.}
s.email = %q{sinisterchipmunk@gmail.com}
s.extra_rdoc_files = [
"LICENSE",
"README.rdoc"
]
s.files = [
".document",
".gitignore",
"LICENSE",
"README.rdoc",
"Rakefile",
"VERSION",
"lib/gravatar-ultimate.rb",
"lib/gravatar.rb",
"lib/gravatar/cache.rb",
"lib/gravatar/dependencies.rb",
"lib/gravatar_ultimate.rb",
"spec/credentials.yml.example",
"spec/fixtures/image.jpg",
"spec/lib/gravatar/cache_and_logger_spec.rb",
"spec/lib/gravatar/cache_setup_spec.rb",
"spec/lib/gravatar/dependencies_spec.rb",
"spec/lib/gravatar_spec.rb",
"spec/spec.opts",
"spec/spec_helper.rb"
]
s.homepage = %q{http://github.com/sinisterchipmunk/gravatar}
s.rdoc_options = ["--charset=UTF-8"]
s.require_paths = ["lib"]
s.rubygems_version = %q{1.3.6}
s.summary = %q{A gem for interfacing with the entire Gravatar API: not just images, but the XML-RPC API too!}
s.test_files = [
"spec/spec_helper.rb",
"spec/lib/gravatar_spec.rb",
"spec/lib/gravatar/dependencies_spec.rb",
"spec/lib/gravatar/cache_and_logger_spec.rb",
"spec/lib/gravatar/cache_setup_spec.rb"
]

if s.respond_to? :specification_version then
current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
s.specification_version = 3

if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
s.add_runtime_dependency(%q<sc-core-ext>, [">= 1.2.0"])
s.add_development_dependency(%q<rspec>, [">= 1.3.0"])
s.add_development_dependency(%q<fakeweb>, [">= 1.2.8"])
else
s.add_dependency(%q<sc-core-ext>, [">= 1.2.0"])
s.add_dependency(%q<rspec>, [">= 1.3.0"])
s.add_dependency(%q<fakeweb>, [">= 1.2.8"])
end
else
s.add_dependency(%q<sc-core-ext>, [">= 1.2.0"])
s.add_dependency(%q<rspec>, [">= 1.3.0"])
s.add_dependency(%q<fakeweb>, [">= 1.2.8"])
end
end

1 change: 1 addition & 0 deletions lib/gravatar-ultimate.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
require File.expand_path("../gravatar", __FILE__)
73 changes: 53 additions & 20 deletions lib/gravatar.rb
Original file line number Diff line number Diff line change
@@ -1,20 +1,8 @@
require File.expand_path('../gravatar/dependencies', __FILE__)
require File.expand_path("../gravatar/cache", __FILE__)

# Connecting
# API Endpoint: https://secure.gravatar.com/xmlrpc?user=[email_hash]
# ==== Errors ====
#
# It is mandatory that you connect to secure.gravatar.com, and that you do so over HTTPS. This is for the safety of our
# mutual users. The email_hash GET parameter is the md5 hash of the users email address after it has been lowercased,
# and trimmed.
#
# Authentication
# User authentication happens at the api method level. You will pass to the method call an apikey or password parameter.
# The data for these parameters will be passed in plain text. Only one valid form of authentication is necessary. The
# apikey and password params are always stripped from the arguments before the methods begin their processing. For this
# reason you should expect not to see either of these values returned from the grav.test method.
#
# Errors
# Errors usually come with a number and human readable text. Generally the text should be followed whenever possible,
# but a brief description of the numeric error codes are as follows:
#
Expand All @@ -28,11 +16,23 @@
class Gravatar
attr_reader :email

# Creates a new instance of Gravatar. Valid options include:
# :password => the password for this account, to be used instead of :api_key (don't supply both)
# :api_key or :apikey or :key => the API key for this account, to be used instead of :password (don't supply both)
# :duration => the cache duration to use for this instance
# :logger => the logger to use for this instance
#
# Note that :password and :api_key are both optional. If omitted, no web services will be available but this
# user's Gravatar image can still be constructed using #image_uri or #image_data.
#
def initialize(email, options = {})
raise ArgumentError, "Expected :email" unless email
@options = options || {}
@email = email
@cache = Gravatar::Cache.new(self.class.cache, options[:duration] || self.class.duration, "gravatar-#{email_hash}")

pw_or_key = auth.keys.first || :none
@cache = Gravatar::Cache.new(self.class.cache, options[:duration] || self.class.duration,
"gravatar-#{email_hash}-#{pw_or_key}", options[:logger] || self.class.logger)

if !auth.empty?
@api = XMLRPC::Client.new("secure.gravatar.com", "/xmlrpc?user=#{email_hash}", 443, nil, nil, nil, nil, true)
Expand All @@ -53,6 +53,8 @@ def cache_duration=(time)
# specified, the one associated with this object is used.
#
# Returns: Boolean for a single email address; a hash of emails => booleans for multiple addresses.
#
# This method is cached for up to the value of @duration or Gravatar.duration.
def exists?(*emails)
hashed_emails = normalize_email_addresses(emails)
cache('exists', hashed_emails) do
Expand All @@ -73,29 +75,39 @@ def exists?(*emails)
# :userimage_url => userimage_url
# }
# }
#
# This method is cached for up to the value of @duration or Gravatar.duration.
def addresses
cache('addresses') { call('grav.addresses') }
end

# Returns a hash of user images for this account in the following format:
# { user_img_hash => [rating, url] }
#
# This method is cached for up to the value of @duration or Gravatar.duration.
def user_images
cache('user_images') { call('grav.userimages') }
end

# Saves binary image data as a userimage for this account and returns the ID of the image.
#
# This method is not cached.
def save_data!(rating, data)
call('grav.saveData', :data => Base64.encode64(data), :rating => _rating(rating))
end
alias save_image! save_data!

# Read an image via its URL and save that as a userimage for this account, returning true or false
#
# This method is not cached.
def save_url!(rating, url)
call('grav.saveUrl', :url => url, :rating => _rating(rating))
end

# Use a userimage as a gravatar for one or more addresses on this account. Returns a hash:
# { email_address => true/false }
#
# This method is not cached.
def use_user_image!(image_hash, *email_addresses)
hashed_email_addresses = normalize_email_addresses(email_addresses)
hash = call('grav.useUserimage', :userimage => image_hash, :addresses => hashed_email_addresses)
Expand All @@ -106,6 +118,8 @@ def use_user_image!(image_hash, *email_addresses)
# Remove the userimage associated with one or more email addresses. Returns a hash of booleans.
# NOTE: This appears to always return false, even when it is really removing an image. If you
# know what the deal with that is, drop me a line so I can update this documentation!
#
# This method is not cached.
def remove_image!(*emails)
hashed_email_addresses = normalize_email_addresses(emails)
hash = call('grav.removeImage', :addresses => hashed_email_addresses)
Expand All @@ -114,19 +128,34 @@ def remove_image!(*emails)

# Remove a userimage from the account and any email addresses with which it is associated. Returns
# true or false.
#
# This method is not cached.
def delete_user_image!(userimage)
boolean(call('grav.deleteUserimage', :userimage => userimage))
end

# Runs a simple Gravatar test. Useful for debugging. Gravatar will echo back any arguments you pass.
# This method is not cached.
def test(hash)
call('grav.test', hash)
end


# Returns the MD5 hash for the specified email address, or the one associated with this object.
def email_hash(email = self.email)
Digest::MD5.hexdigest(email.downcase.strip)
end

# Returns the URL for this user's gravatar image. Options include:
#
# :ssl or :secure if true, HTTPS will be used instead of HTTP. Default is false.
# :rating or :r a rating threshold for this image. Can be one of [ :g, :pg, :r, :x ]. Default is :g.
# :size or :s a size for this image. Can be anywhere between 1 and 512. Default is 80.
# :default or :d a default URL for this image to display if the specified user has no image;
# or this can be one of [ :identicon, :monsterid, :wavatar, 404 ]. By default a generic
# Gravatar image URL will be returned.
# :filetype an extension such as :jpg or :png. Default is omitted.
#
# See http://en.gravatar.com/site/implement/url for much more detailed information.
def image_url(options = {})
secure = options[:ssl] || options[:secure]
proto = "http#{secure ? 's' : ''}"
Expand All @@ -135,9 +164,13 @@ def image_url(options = {})
"#{proto}://#{sub}.gravatar.com/avatar/#{email_hash}#{extension_for_image(options)}#{query_for_image(options)}"
end

# Returns the image data for this user's gravatar image. This is the same as reading the data at #image_url.
# Returns the image data for this user's gravatar image. This is the same as reading the data at #image_url. See
# that method for more information.
#
# This method is cached for up to the value of @duration or Gravatar.duration.
def image_data(options = {})
OpenURI.open_uri(URI.parse(image_url(options))).read
url = image_url(options)
cache(url) { OpenURI.open_uri(URI.parse(url)).read }
end

private
Expand Down Expand Up @@ -187,11 +220,11 @@ def call(name, args_hash = {})
end

def auth
api_key ? {:apikey => api_key} : {:password => password}
api_key ? {:apikey => api_key} : (password ? {:password => password} : {})
end

def api_key
options[:apikey] || options[:api_key]
options[:apikey] || options[:api_key] || options[:key]
end

def password
Expand All @@ -204,7 +237,7 @@ def options

def query_for_image(options)
query = ''
[:rating, :size, :default].each do |key|
[:rating, :size, :default, :r, :s, :d].each do |key|
if options.key?(key)
query.blank? ? query.concat("?") : query.concat("&")
query.concat("#{key}=#{CGI::escape options[key].to_s}")
Expand Down
Loading

0 comments on commit 8570ea9

Please sign in to comment.