require 'avatar/source/abstract_source' require 'avatar/source/static_url_source' require 'avatar/source/nil_source' require 'digest/md5' module Avatar # :nodoc: module Source # :nodoc: # NOTE: since Gravatar always returns a URL (never a 404), instances of this # class should only be placed at the end of a SourceChain. # (see link:classes/Avatar/Source/SourceChain.html) # Alternatively, use default_source = ... to generate a site-wide # default to be passed to Gravatar. (In fact, since default_source # is an instance of Avatar::Source::AbstractSource, it can generate a different # default for each person.) class GravatarSource include AbstractSource attr_accessor :default_field attr_reader :default_source # 'http://www.gravatar.com/avatar/' def self.base_url 'http://www.gravatar.com/avatar/' end # ['G', 'PG', 'R', 'X'] def self.allowed_ratings ['G', 'PG', 'R', 'X'] end # Arguments: # * +default_source+: a Source to generate defaults to be passed to Gravatar; optional; default: nil (a NilSource). # * +default_field+: the field within each +person+ passed to avatar_url_for in which to look for an email address def initialize(default_source = nil, default_field = :email) self.default_source = default_source #not @default_source = ... b/c want to use the setter function below @default_field = default_field raise "There's a bug in the code" if @default_source.nil? end # Generates a Gravatar URL. Returns nil if person is nil. # Options: # * :gravatar_field (Symbol) - the field to call from person. By default, :email. # * :default (String) - override the default generated by default_source. # * :gravatar_size or size or :s - the size in pixels of the avatar to render. # * :gravatar_rating or rating or :r - the maximum rating; one of ['G', 'PG', 'R', 'X'] def avatar_url_for(person, options = {}) return nil if person.nil? options = parse_options(person, options) field = options.delete(:gravatar_field) raise ArgumentError.new('No field specified; either specify a default field or pass in a value for :gravatar_field (probably :email)') unless field returning(self.class.base_url) do |url| url << Digest::MD5::hexdigest(person.send(field)).strip options.each do |k, v| next if v.nil? url << (url.include?('?') ? '&' : '?') url << "#{k}=#{v}" end end end # Returns a Hash containing # * :field - passed through; defaults to self.default_field # * :default - passed through; defaults to self.default_avatar_url_for(+person+, +options+) # * :size - :gravatar_size or :size or :s passed through only if a number # * :rating - :gravatar_rating or :rating or :r passed through only if one of self.class.allowed_ratings def parse_options(person, options) returning({}) do |result| result[:gravatar_field] = options[:gravatar_field] || default_field result[:default] = options[:default] || default_avatar_url_for(person, options) size = options[:gravatar_size] || options[:size] || options[:s] result[:size] = size if size.to_s =~ /^\d*/ rating = options[:gravatar_rating] || options[:rating] || options[:r] result[:rating] = rating.upcase if rating and self.class.allowed_ratings.include?(rating.to_s) end end # Set the default source for all people. # If +default+ is a String, it will be converted to an instance of Avatar::Source::StaticUrlSource. # If +default+ is nil, sets the default to a NilSource. def default_source=(default) case default when String @default_source = StaticUrlSource.new(default) when AbstractSource @default_source = default when NilClass @default_source = NilSource.new else raise ArgumentError.new("#{default} must be either a String or an instance of #{AbstractSource}") end end private def default_avatar_url_for(person, options) @default_source.avatar_url_for(person, options) end end end end