#--
# gravaty
# Copyright © 2013, 2014, 2015, 2016, 2017, 2018, 2019 Marco Bresciani
#
# This file is part of gravaty.
#
# gravaty is free software: you can redistribute it and/or modify it
# under the terms of the GNU General Public License as published by the
# Free Software Foundation, either version 3 of the License, or (at your
# option) any later version.
#
# gravaty is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
# for more details.
#
# You should have received a copy of the GNU General Public License
# along with gravaty. If not, see .
#++
require 'digest'
require 'i18n'
require 'json'
require 'uri'
require 'xmlrpc/client'
require_relative 'utils/downloader'
require_relative 'utils/rfc5322'
require_relative 'utils/rpc_connector'
module Gravaty
# This class is a simple API to retrieve an URL with specified options
# from Gravatar site. It can be used to read data for avatars,
# profiles or for XML-RPC APi calls. The only needed parameter to
# create a Gravaty object is the reference +email+ address.
# Author:: {Marco Bresciani}[mailto:marcobresciani_1974@libero.it]
# Copyright:: Copyright © 2013, 2014, 2015, 2016, 2017, 2018,
# 2019 Marco Bresciani
# License:: GNU General Public License version 3
class Gravaty
include Comparable
# Provides the MD5 signature (of the small caps version) of the
# +email+ address used to build the object.
attr_reader :digest
# Provides the (small caps version of) +email+ address used to build
# the object.
attr_reader :email
# Creates a +Gravaty+ object described by the user's +email+. Throws
# an +ArgumentError+ exception if the supplied +email+ address is
# +nil+ or not valid according to RFC5322.
#
# Usage:: new_gravaty = Gravaty.new email, parser
# Params::
# - +email_address+, the user's email address (a syntactically
# valid one).
# - +parser+, a parser duck-responding to the +parse+ method with
# two parameters, +method+ name and +value+ string
# Returns:: a +Gravaty+ object for the specified +email+ address.
# Raises:: +ArgumentError+, if the supplied +email+ address is +nil+
# or not valid according to RFC 5322.
def initialize(email_address, parser)
I18n.load_path = Dir[File.join(File.dirname(__FILE__),
'/locales/', '*.yml')]
raise ArgumentError, I18n.t('error.nil') if email_address.nil?
raise ArgumentError, I18n.t('error.invalid', value: email_address) unless Utils::Rfc5322::EMAIL.match email_address # thanks Peter!
@email = email_address.strip.downcase
@digest = Digest::MD5.hexdigest email
@gravaty = email
@parser = parser
end
# Returns a string containing the URI of the gravatar associated to
# internal (provided) email address. Valid keys for the +args+ hash
# are: :type, :pixel_size, :force, :secure, :rating, :default.
#
# Usage::
# - a_string = new_gravaty.avatar
# - a_string = new_gravaty.avatar
# - a_string = new_gravaty.avatar pixel_size: 42
# - a_string = new_gravaty.avatar pixel_size: 42, type:
# png'
# - a_string = new_gravaty.avatar secure: false
# - a_string = new_gravaty.avatar type: 'jpg', force:
# true
# - a_string = new_gravaty.avatar secure: true, pixel_size:
# 42, type: 'png'
# - a_string = new_gravaty.avatar rating: pg, type: 'gif'
# - a_string = new_gravaty.avatar default: monsterid,
# pixel_size: 42
# Params::
# - +type+: [String] is the possibly desired specific image
# format. Valid formats are jp(e)g, png or gif. Default to no
# extension. Will be downcased. Raises ArgumentError
# for invalid formats.
# - +pixel_size+: [Integer] is the size in pixel of the image.
# From 1 to 2048. Default to no value. Raises
# ArgumentError
for invalid sizes.
# - +force+: [TrueClass || FalseClass] is a boolean to specify if
# the default has to be forced when no image is found. Default to
# false.
# - +secure+: [TrueClass || FalseClass] is a boolean to specify
# whether the resulting URL shall be HTTPS or HTTP type. Default to
# true (HTTPS).
# - +rating+: [String] is the rating type of the image. Valid
# values are 'g', 'pg', 'r' and 'x'.
# - +default+: [String] is the default type of the image (valid
# values are '404', 'mm', 'identicon', 'monsterid', 'wavatar',
# 'retro' and 'blank'), or the URI for an own default image. For the
# URI validation see:
# http://it.gravatar.com/site/implement/images/#default-image .
# Returns:: a +String+ containing the Gravatar site URI with
# specified options and configuration parameters.
def avatar(args = {})
secure = true
type = nil
array = []
unless args.nil?
type = args.fetch(:type, nil)
secure = args.fetch(:secure, secure)
[:pixelsize, :force, :default, :rating].each do |param|
array << @parser.parse(param.to_s, args[param]) unless args[param].nil?
end
end
build_uri(secure, true) + digest +
@parser.parse('type', type) + build_query_string(array)
end
# See avatar method. This banged version saves the resulting string
# as internal state.
def avatar!(args = {})
@gravaty = avatar(args)
end
# Returns a string containing the URI of the gravatar profile
# associated to internal (provided) email address. Valid keys for
# the +args+ hash are: :secure, :format.
#
# Usage::
# - a_string = new_gravaty.profile
# - a_string = new_gravaty.profile secure: false
# - a_string = new_gravaty.profile format: 'php', secure:
# true
# Params::
# - +format+: [String] is the possibly desired specific profile
# format. Valid formats are 'json', 'xml', 'php', 'vcf' and 'qr'.
# Default to no extension. Will be downcased. Defaults to no
# extension for invalid formats.
# - +secure+: [TrueClass || FalseClass] is a boolean to specify
# whether the resulting URL shall be HTTPS or HTTP type. Default to
# true (HTTPS).
# Returns:: a +String+ containing the Gravatar site URI with
# specified options and configuration parameters.
def profile(args = {})
secure = true
format = nil
array = []
unless args.nil?
format = args[:format].downcase unless args[:format].nil?
secure = args.fetch(:secure, secure)
unless (format.nil?) and (PROFILES.include? format)
selected = (format == 'json' ? 'callback' : 'pixelsize')
array << @parser.parse(selected, args[selected.to_sym]) unless args[selected.to_sym].nil?
end
end
build_uri(secure, false) + digest +
@parser.parse('format', format) + build_query_string(array)
end
# See profile method. This banged version saves the resulting strin
# as internal state.
def profile!(args = {})
@gravaty = profile(args)
end
# Saves a file, with specified +filename+, that contains the current
# gravaty configuration. Uses the internal state to retrieve data
# from the URI. Defaults to 'gravaty' with no specific extension.
#
# Usage::
# - a_string = new_gravaty.download filename
# Params::
# - +filename+: [String] is the filename to be saved.
def download(filename = nil)
filename = URI.parse(@gravaty).path.rpartition('/')
.last if filename.nil?
Utils::Downloader::Downloader
.download_file @gravaty, filename # thanks Jon!
end
# See profile method. Customized version for QRCode-specific
# requests.
#
# Usage::
# - a_string = new_gravaty.qrcode
# - a_string = new_gravaty.qrcode pixel_size: 42
# - a_string = new_gravaty.qrcode secure: false, pixel_size:
# 42
# Params::
# - +secure+: [TrueClass || FalseClass] is a boolean to specify
# whether the resulting URL shall be HTTPS or HTTP type. Default to
# true (HTTPS).
# - +pixel_size+: [Integer] is the size in pixel of the image.
# From 1 to 2048. Default to no value. Raises
# ArgumentError
for invalid sizes.
# Returns:: a +String+ containing the Gravatar site URI with
# specified options and configuration parameters, in QRCode format.
def qrcode(args = {})
secure = true
size = nil
unless args.nil?
size = args.fetch(:pixelsize, nil)
secure = args.fetch(:secure, secure)
end
profile format: 'qr', secure: secure, pixelsize: size
end
# See qrcode method. This banged version saves the resulting string
# as internal state.
def qrcode!(args = {})
@gravaty = qrcode(args)
end
# See profile method. Customized version for JSON-specific requests.
#
# Usage::
# - a_string = new_gravaty.json
# - a_string = new_gravaty.json callback: 'alert'
# - a_string = new_gravaty.qrcode secure: false, callback:
# 'alert'
# Params::
# - +secure+: [TrueClass || FalseClass] is a boolean to specify
# whether the resulting URL shall be HTTPS or HTTP type. Default to
# true (HTTPS).
# - +callback+: [String] See
# https://secure.gravatar.com/site/implement/profiles/json/#request-options.
# No check on parameter meaning or validity, except for +nil+.
# Returns:: a +String+ containing the Gravatar site URI with
# specified options and configuration parameters, in JSON format.
def json(args = {})
secure = true
callback = nil
unless args.nil?
callback = args.fetch(:callback, nil)
secure = args.fetch(:secure, secure)
end
profile format: 'json', secure: secure, callback: callback
end
# See json method. This banged version saves the resulting string as
# internal state.
def json!(args = {})
@gravaty = json(args)
end
# Restores the original status cleaning up the (possibly) previously
# saved URIs restoring the default +to_s+.
def reset
@gravaty = email
end
# Interfaces with the Gravatar XML-RPC API.
#
# Usage::
# - response = new_gravaty.xmlrpc 'grav.test',
# my_password
# Params::
# - +a_method+: [String] is a valid method supported by Gravatar
# XML-RPC API. See https://en.gravatar.com/site/implement/xmlrpc/ .
# - +password+: [String] is a valid password associated to user's
# +email_address+ specified to build the Gravaty object.
# - +args+: See https://en.gravatar.com/site/implement/xmlrpc/ for
# possible parameters, depending on called method.
# Returns:: See https://en.gravatar.com/site/implement/xmlrpc/ for
# possible return values, depending on called method.
def xmlrpc(a_method = RPC_TEST_METHOD, password = nil, args = {})
raise ArgumentError, I18n.t('error.nil') if password.nil?
raise ArgumentError, I18n.t('error.invalid', value: a_method) unless RPC_METHODS.include? a_method
@rpc_connector = Utils::RpcConnector::RpcConnector
.new(digest, password) if @rpc_connector.nil?
@rpc_connector.call a_method, args unless @rpc_connector.nil?
end
# Returns a JSon object representing the Gravaty object.
#
#
# Usage::
# - a_json = new_gravaty.to_json
# Returns:: a +JSON+ containing the Gravaty object representation
# with currently saved options and configuration parameters.
def to_json
result = Hash.new
result[email] = digest
result.to_json
end
# Returns a string representing the Gravaty object.
#
#
# Usage::
# - a_string = new_gravaty.to_s
# Returns:: a +String+ containing the Gravaty object representation
# with currently saved options and configuration parameters.
def to_s
@gravaty.to_s
end
def <=>(other_gravaty)
@email <=> other_gravaty.email
end
# -------------------------- here starts the list of private methods
private
# Builds the query string with the array of parameters.
def build_query_string(params_array = [])
parameters = nil
unless params_array.nil?
parameters = params_array.reject {|p| p.nil? or p.empty?}
.join('&')
end
return ('?' + parameters) unless parameters.nil? or parameters
.empty?
''
end
# Return the basic URI structure according to type and security.
def build_uri(secure = false, avatar = true)
@parser.parse('secure', secure) + '.gravatar.com/' +
@parser.parse('avatar', avatar)
end
end
end