gravatar-ultimate 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/.document +5 -0
- data/.gitignore +24 -0
- data/LICENSE +20 -0
- data/README.rdoc +100 -0
- data/Rakefile +64 -0
- data/VERSION +1 -0
- data/gravatar-ultimate.gemspec +77 -0
- data/lib/gravatar-ultimate.rb +1 -0
- data/lib/gravatar.rb +282 -0
- data/lib/gravatar/cache.rb +133 -0
- data/lib/gravatar/dependencies.rb +16 -0
- data/lib/gravatar_ultimate.rb +1 -0
- data/spec/credentials.yml.example +11 -0
- data/spec/fixtures/image.jpg +0 -0
- data/spec/lib/gravatar/cache_and_logger_spec.rb +94 -0
- data/spec/lib/gravatar/cache_setup_spec.rb +42 -0
- data/spec/lib/gravatar/dependencies_spec.rb +29 -0
- data/spec/lib/gravatar_spec.rb +141 -0
- data/spec/spec.opts +2 -0
- data/spec/spec_helper.rb +30 -0
- metadata +132 -0
data/.document
ADDED
data/.gitignore
ADDED
data/LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright (c) 2009 Colin MacKenzie IV
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.rdoc
ADDED
@@ -0,0 +1,100 @@
|
|
1
|
+
= gravatar-ultimate
|
2
|
+
|
3
|
+
The Ultimate Gravatar Gem!
|
4
|
+
|
5
|
+
This gem is used to interface with the entire Gravatar API: it's not just for generating image URLs, but for connecting
|
6
|
+
to and communicating with the XML-RPC API too! Additionally, it can be used to download the Gravatar image data itself,
|
7
|
+
rather than just a URL to that data. This saves you the extra step of having to do so.
|
8
|
+
|
9
|
+
== Installation
|
10
|
+
|
11
|
+
gem install gravitar-ultimate
|
12
|
+
|
13
|
+
== Activate the gem...
|
14
|
+
|
15
|
+
As with any gem, you have to type a few lines to tell Ruby to actually *use* it. Here's how to do that...
|
16
|
+
|
17
|
+
==== ...in Ruby on Rails (v3.x)
|
18
|
+
|
19
|
+
* This isn't ready yet, but it's in the works.
|
20
|
+
|
21
|
+
==== ...in Ruby on Rails (v2.x)
|
22
|
+
|
23
|
+
* Edit your config/environment.rb file
|
24
|
+
* Add this line beneath "Rails::Initializer.run do |config|":
|
25
|
+
config.gem 'gravitar-ultimate'
|
26
|
+
|
27
|
+
==== ...in vanilla Ruby
|
28
|
+
|
29
|
+
require 'rubygems'
|
30
|
+
gem 'gravitar-ultimate'
|
31
|
+
require 'gravitar-ultimate'
|
32
|
+
|
33
|
+
== Usage
|
34
|
+
Using the gem is actually pretty simple. Let's say you want the Gravatar image URL for "generic@example.com":
|
35
|
+
url = Gravatar.new("generic@example.com").image_url
|
36
|
+
|
37
|
+
Cool, huh? Let's take it a step further and grab the actual image *data* so that we can render it on the screen:
|
38
|
+
data = Gravatar.new("generic@example.com").image_data
|
39
|
+
|
40
|
+
Fine, but how about the rest of the API as advertised at http://en.gravatar.com/site/implement/xmlrpc? Well, for
|
41
|
+
that you need either the user's Gravatar password, or their API key:
|
42
|
+
|
43
|
+
api = Gravatar.new("generic@example.com", :password => "helloworld")
|
44
|
+
api = Gravatar.new("generic@example.com", :api_key => "AbCdEfG1234")
|
45
|
+
|
46
|
+
After you have that, things get a lot easier:
|
47
|
+
|
48
|
+
api.exists? #=> true or false, depending on whether this user has an account.
|
49
|
+
api.addresses #=> a list of email addresses and their corresponding images
|
50
|
+
api.save_data!(rating, image_data) #=> saves an image to this user's account and returns a handle to it
|
51
|
+
api.use_user_image!(handle, an_email_address) #=> uses the specified image handle for the specified email address(es)
|
52
|
+
api.exists?("another@example.com") #=> true or false, depending on whether the specified email exists.
|
53
|
+
|
54
|
+
|
55
|
+
== Caching
|
56
|
+
|
57
|
+
As you can see this is quite powerful. But it gets better. Gravatar Ultimate even manages caching of API responses for
|
58
|
+
you! That way, if an error occurs, (such as the Gravatar site being offline), your code won't break. It'll instead
|
59
|
+
gracefully fall back to the cached copy! By default, if you are using Rails, it'll use the Rails cache. Otherwise, it'll
|
60
|
+
use whatever cache you're using with Gravatar (by default an instance of ActiveSupport::Cache::FileStore).
|
61
|
+
|
62
|
+
This has obvious benefits when used for the API calls that do not result in changing the user's profile, but what you
|
63
|
+
might not have thought of yet is that it also caches #image_data, so you can hook your application up to that method
|
64
|
+
without fear of what might happen to all those Gravatar images if the Gravatar server should be unavailable!
|
65
|
+
|
66
|
+
To customize exactly which cache is used, see the next section...
|
67
|
+
|
68
|
+
=== Configuration
|
69
|
+
|
70
|
+
To see settings and options you can give for a particular Gravatar instance, check out the Gravatar class documentation.
|
71
|
+
There are a few things you can set for Gravatar on a system-wide basis, and that's what we'll go over next.
|
72
|
+
|
73
|
+
For a non-Rails project, simply set these options before you start using Gravatar. For a Rails project, you should set
|
74
|
+
them within an Initializer in config/initializers/any_filename.rb in order to ensure that the settings are applied
|
75
|
+
(A) after Gravatar has been included into the project, and (B) before it is actually used by Rails.
|
76
|
+
|
77
|
+
# You can set the default cache for Gravatar to use:
|
78
|
+
Gravatar.cache = ActiveSupport::Cache::SynchronizedMemoryStore.new
|
79
|
+
|
80
|
+
# You can also set the length of time an item in the Gravatar cache is valid. Default is 24.hours
|
81
|
+
Gravatar.duration = 20.minutes
|
82
|
+
|
83
|
+
# You can also change the logger used by default. It's worth mentioning that, once again, Gravatar will use
|
84
|
+
# the Rails logger if it's available. Otherwise, the default is $stdout.
|
85
|
+
grav_log = ""
|
86
|
+
Gravatar.logger = StringIO.new(grav_log) # logs Gravatar output to a String
|
87
|
+
|
88
|
+
== Note on Patches/Pull Requests
|
89
|
+
|
90
|
+
* Fork the project.
|
91
|
+
* Make your feature addition or bug fix.
|
92
|
+
* Add tests for it. This is important so I don't break it in a
|
93
|
+
future version unintentionally.
|
94
|
+
* Commit, do not mess with rakefile, version, or history.
|
95
|
+
(if you want to have your own version, that is fine but bump version in a commit by itself I can ignore when I pull)
|
96
|
+
* Send me a pull request. Bonus points for topic branches.
|
97
|
+
|
98
|
+
== Copyright
|
99
|
+
|
100
|
+
Copyright (c) 2010 Colin MacKenzie IV. See LICENSE for details.
|
data/Rakefile
ADDED
@@ -0,0 +1,64 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'rake'
|
3
|
+
|
4
|
+
begin
|
5
|
+
require 'jeweler'
|
6
|
+
Jeweler::Tasks.new do |gem|
|
7
|
+
gem.name = "gravatar-ultimate"
|
8
|
+
gem.summary = %Q{A gem for interfacing with the entire Gravatar API: not just images, but the XML-RPC API too!}
|
9
|
+
gem.description = %Q{The Ultimate Gravatar Gem!
|
10
|
+
|
11
|
+
This gem is used to interface with the entire Gravatar API: it's not just for generating image URLs, but for connecting
|
12
|
+
to and communicating with the XML-RPC API too! Additionally, it can be used to download the Gravatar image data itself,
|
13
|
+
rather than just a URL to that data. This saves you the extra step of having to do so.}
|
14
|
+
gem.email = "sinisterchipmunk@gmail.com"
|
15
|
+
gem.homepage = "http://github.com/sinisterchipmunk/gravatar"
|
16
|
+
gem.authors = ["Colin MacKenzie IV"]
|
17
|
+
gem.add_dependency "sc-core-ext", ">= 1.2.0"
|
18
|
+
gem.add_development_dependency "rspec", ">= 1.3.0"
|
19
|
+
gem.add_development_dependency "fakeweb", ">= 1.2.8"
|
20
|
+
end
|
21
|
+
Jeweler::GemcutterTasks.new
|
22
|
+
rescue LoadError
|
23
|
+
puts "Jeweler (or a dependency) not available. Install it with: gem install jeweler"
|
24
|
+
end
|
25
|
+
|
26
|
+
require 'spec/rake/spectask'
|
27
|
+
Spec::Rake::SpecTask.new(:test) do |test|
|
28
|
+
test.libs << 'lib' << 'spec'
|
29
|
+
test.pattern = 'spec/**/*_spec.rb'
|
30
|
+
test.verbose = true
|
31
|
+
end
|
32
|
+
|
33
|
+
desc "Run all specs"
|
34
|
+
task :spec => :test
|
35
|
+
|
36
|
+
begin
|
37
|
+
gem 'rcov'
|
38
|
+
require 'rcov/rcovtask'
|
39
|
+
Spec::Rake::SpecTask.new(:coverage) do |test|
|
40
|
+
test.libs << 'lib' << 'spec'
|
41
|
+
test.pattern = "spec/**/*_spec.rb"
|
42
|
+
test.verbose = true
|
43
|
+
test.rcov = true
|
44
|
+
test.rcov_opts = ['--html', '--exclude spec']
|
45
|
+
end
|
46
|
+
rescue LoadError
|
47
|
+
task :coverage do
|
48
|
+
abort "RCov is not available. In order to run rcov, you must: sudo gem install spicycode-rcov"
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
task :test => :check_dependencies
|
53
|
+
|
54
|
+
task :default => :test
|
55
|
+
|
56
|
+
require 'rake/rdoctask'
|
57
|
+
Rake::RDocTask.new do |rdoc|
|
58
|
+
version = File.exist?('VERSION') ? File.read('VERSION') : ""
|
59
|
+
|
60
|
+
rdoc.rdoc_dir = 'rdoc'
|
61
|
+
rdoc.title = "gravatar #{version}"
|
62
|
+
rdoc.rdoc_files.include('README*')
|
63
|
+
rdoc.rdoc_files.include('lib/**/*.rb')
|
64
|
+
end
|
data/VERSION
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
1.0.0
|
@@ -0,0 +1,77 @@
|
|
1
|
+
# Generated by jeweler
|
2
|
+
# DO NOT EDIT THIS FILE DIRECTLY
|
3
|
+
# Instead, edit Jeweler::Tasks in Rakefile, and run the gemspec command
|
4
|
+
# -*- encoding: utf-8 -*-
|
5
|
+
|
6
|
+
Gem::Specification.new do |s|
|
7
|
+
s.name = %q{gravatar-ultimate}
|
8
|
+
s.version = "1.0.0"
|
9
|
+
|
10
|
+
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
11
|
+
s.authors = ["Colin MacKenzie IV"]
|
12
|
+
s.date = %q{2010-06-16}
|
13
|
+
s.description = %q{The Ultimate Gravatar Gem!
|
14
|
+
|
15
|
+
This gem is used to interface with the entire Gravatar API: it's not just for generating image URLs, but for connecting
|
16
|
+
to and communicating with the XML-RPC API too! Additionally, it can be used to download the Gravatar image data itself,
|
17
|
+
rather than just a URL to that data. This saves you the extra step of having to do so.}
|
18
|
+
s.email = %q{sinisterchipmunk@gmail.com}
|
19
|
+
s.extra_rdoc_files = [
|
20
|
+
"LICENSE",
|
21
|
+
"README.rdoc"
|
22
|
+
]
|
23
|
+
s.files = [
|
24
|
+
".document",
|
25
|
+
".gitignore",
|
26
|
+
"LICENSE",
|
27
|
+
"README.rdoc",
|
28
|
+
"Rakefile",
|
29
|
+
"VERSION",
|
30
|
+
"gravatar-ultimate.gemspec",
|
31
|
+
"lib/gravatar-ultimate.rb",
|
32
|
+
"lib/gravatar.rb",
|
33
|
+
"lib/gravatar/cache.rb",
|
34
|
+
"lib/gravatar/dependencies.rb",
|
35
|
+
"lib/gravatar_ultimate.rb",
|
36
|
+
"spec/credentials.yml.example",
|
37
|
+
"spec/fixtures/image.jpg",
|
38
|
+
"spec/lib/gravatar/cache_and_logger_spec.rb",
|
39
|
+
"spec/lib/gravatar/cache_setup_spec.rb",
|
40
|
+
"spec/lib/gravatar/dependencies_spec.rb",
|
41
|
+
"spec/lib/gravatar_spec.rb",
|
42
|
+
"spec/spec.opts",
|
43
|
+
"spec/spec_helper.rb"
|
44
|
+
]
|
45
|
+
s.homepage = %q{http://github.com/sinisterchipmunk/gravatar}
|
46
|
+
s.rdoc_options = ["--charset=UTF-8"]
|
47
|
+
s.require_paths = ["lib"]
|
48
|
+
s.rubygems_version = %q{1.3.6}
|
49
|
+
s.summary = %q{A gem for interfacing with the entire Gravatar API: not just images, but the XML-RPC API too!}
|
50
|
+
s.test_files = [
|
51
|
+
"spec/spec_helper.rb",
|
52
|
+
"spec/lib/gravatar_spec.rb",
|
53
|
+
"spec/lib/gravatar/dependencies_spec.rb",
|
54
|
+
"spec/lib/gravatar/cache_and_logger_spec.rb",
|
55
|
+
"spec/lib/gravatar/cache_setup_spec.rb"
|
56
|
+
]
|
57
|
+
|
58
|
+
if s.respond_to? :specification_version then
|
59
|
+
current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
|
60
|
+
s.specification_version = 3
|
61
|
+
|
62
|
+
if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
|
63
|
+
s.add_runtime_dependency(%q<sc-core-ext>, [">= 1.2.0"])
|
64
|
+
s.add_development_dependency(%q<rspec>, [">= 1.3.0"])
|
65
|
+
s.add_development_dependency(%q<fakeweb>, [">= 1.2.8"])
|
66
|
+
else
|
67
|
+
s.add_dependency(%q<sc-core-ext>, [">= 1.2.0"])
|
68
|
+
s.add_dependency(%q<rspec>, [">= 1.3.0"])
|
69
|
+
s.add_dependency(%q<fakeweb>, [">= 1.2.8"])
|
70
|
+
end
|
71
|
+
else
|
72
|
+
s.add_dependency(%q<sc-core-ext>, [">= 1.2.0"])
|
73
|
+
s.add_dependency(%q<rspec>, [">= 1.3.0"])
|
74
|
+
s.add_dependency(%q<fakeweb>, [">= 1.2.8"])
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
@@ -0,0 +1 @@
|
|
1
|
+
require File.expand_path("../gravatar", __FILE__)
|
data/lib/gravatar.rb
ADDED
@@ -0,0 +1,282 @@
|
|
1
|
+
require File.expand_path('../gravatar/dependencies', __FILE__)
|
2
|
+
require File.expand_path("../gravatar/cache", __FILE__)
|
3
|
+
|
4
|
+
# ==== Errors ====
|
5
|
+
#
|
6
|
+
# Errors usually come with a number and human readable text. Generally the text should be followed whenever possible,
|
7
|
+
# but a brief description of the numeric error codes are as follows:
|
8
|
+
#
|
9
|
+
# -7 Use secure.gravatar.com
|
10
|
+
# -8 Internal error
|
11
|
+
# -9 Authentication error
|
12
|
+
# -10 Method parameter missing
|
13
|
+
# -11 Method parameter incorrect
|
14
|
+
# -100 Misc error (see text)
|
15
|
+
#
|
16
|
+
class Gravatar
|
17
|
+
attr_reader :email
|
18
|
+
|
19
|
+
# Creates a new instance of Gravatar. Valid options include:
|
20
|
+
# :password => the password for this account, to be used instead of :api_key (don't supply both)
|
21
|
+
# :api_key or :apikey or :key => the API key for this account, to be used instead of :password (don't supply both)
|
22
|
+
# :duration => the cache duration to use for this instance
|
23
|
+
# :logger => the logger to use for this instance
|
24
|
+
#
|
25
|
+
# Note that :password and :api_key are both optional. If omitted, no web services will be available but this
|
26
|
+
# user's Gravatar image can still be constructed using #image_uri or #image_data.
|
27
|
+
#
|
28
|
+
def initialize(email, options = {})
|
29
|
+
raise ArgumentError, "Expected :email" unless email
|
30
|
+
@options = options || {}
|
31
|
+
@email = email
|
32
|
+
|
33
|
+
pw_or_key = auth.keys.first || :none
|
34
|
+
@cache = Gravatar::Cache.new(self.class.cache, options[:duration] || self.class.duration,
|
35
|
+
"gravatar-#{email_hash}-#{pw_or_key}", options[:logger] || self.class.logger)
|
36
|
+
|
37
|
+
if !auth.empty?
|
38
|
+
@api = XMLRPC::Client.new("secure.gravatar.com", "/xmlrpc?user=#{email_hash}", 443, nil, nil, nil, nil, true)
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
# The duration of the cache for this instance of Gravatar, independent of any other instance
|
43
|
+
def cache_duration
|
44
|
+
@cache.duration
|
45
|
+
end
|
46
|
+
|
47
|
+
# Sets the duration of the cache for this instance of Gravatar, independent of any other instance
|
48
|
+
def cache_duration=(time)
|
49
|
+
@cache.duration = time
|
50
|
+
end
|
51
|
+
|
52
|
+
# Check whether one or more email addresses have corresponding avatars. If no email addresses are
|
53
|
+
# specified, the one associated with this object is used.
|
54
|
+
#
|
55
|
+
# Returns: Boolean for a single email address; a hash of emails => booleans for multiple addresses.
|
56
|
+
#
|
57
|
+
# This method is cached for up to the value of @duration or Gravatar.duration.
|
58
|
+
def exists?(*emails)
|
59
|
+
hashed_emails = normalize_email_addresses(emails)
|
60
|
+
cache('exists', hashed_emails) do
|
61
|
+
hash = call('grav.exists', :hashes => hashed_emails)
|
62
|
+
if hash.length == 1
|
63
|
+
boolean(hash.values.first)
|
64
|
+
else
|
65
|
+
dehashify_emails(hash, emails) { |value| boolean(value) }
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
# Gets a list of addresses for this account, returning a hash following this format:
|
71
|
+
# {
|
72
|
+
# address => {
|
73
|
+
# :rating => rating,
|
74
|
+
# :userimage => userimage,
|
75
|
+
# :userimage_url => userimage_url
|
76
|
+
# }
|
77
|
+
# }
|
78
|
+
#
|
79
|
+
# This method is cached for up to the value of @duration or Gravatar.duration.
|
80
|
+
def addresses
|
81
|
+
cache('addresses') do
|
82
|
+
call('grav.addresses').inject({}) do |hash, (address, info)|
|
83
|
+
hash[address] = info.merge(:rating => rating(info[:rating]))
|
84
|
+
hash
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
# Returns a hash of user images for this account in the following format:
|
90
|
+
# { user_img_hash => [rating, url] }
|
91
|
+
#
|
92
|
+
# This method is cached for up to the value of @duration or Gravatar.duration.
|
93
|
+
def user_images
|
94
|
+
cache('user_images') do
|
95
|
+
call('grav.userimages').inject({}) do |hash, (key, array)|
|
96
|
+
hash[key] = [rating(array.first), array.last]
|
97
|
+
hash
|
98
|
+
end
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
# Saves binary image data as a userimage for this account and returns the ID of the image.
|
103
|
+
#
|
104
|
+
# This method is not cached.
|
105
|
+
def save_data!(rating, data)
|
106
|
+
call('grav.saveData', :data => Base64.encode64(data), :rating => _rating(rating))
|
107
|
+
end
|
108
|
+
alias save_image! save_data!
|
109
|
+
|
110
|
+
# Read an image via its URL and save that as a userimage for this account, returning true or false
|
111
|
+
#
|
112
|
+
# This method is not cached.
|
113
|
+
def save_url!(rating, url)
|
114
|
+
call('grav.saveUrl', :url => url, :rating => _rating(rating))
|
115
|
+
end
|
116
|
+
|
117
|
+
# Use a userimage as a gravatar for one or more addresses on this account. Returns a hash:
|
118
|
+
# { email_address => true/false }
|
119
|
+
#
|
120
|
+
# This method is not cached.
|
121
|
+
#
|
122
|
+
# This method will clear out the cache, since it may have an effect on what the API methods respond with.
|
123
|
+
def use_user_image!(image_hash, *email_addresses)
|
124
|
+
hashed_email_addresses = normalize_email_addresses(email_addresses)
|
125
|
+
hash = call('grav.useUserimage', :userimage => image_hash, :addresses => hashed_email_addresses)
|
126
|
+
returning dehashify_emails(hash, email_addresses) { |value| boolean(value) } do
|
127
|
+
expire_cache!
|
128
|
+
end
|
129
|
+
end
|
130
|
+
alias use_image! use_user_image!
|
131
|
+
|
132
|
+
# Remove the userimage associated with one or more email addresses. Returns a hash of booleans.
|
133
|
+
# NOTE: This appears to always return false, even when it is really removing an image. If you
|
134
|
+
# know what the deal with that is, drop me a line so I can update this documentation!
|
135
|
+
#
|
136
|
+
# This method is not cached.
|
137
|
+
#
|
138
|
+
# This method will clear out the cache, since it may have an effect on what the API methods respond with.
|
139
|
+
def remove_image!(*emails)
|
140
|
+
hashed_email_addresses = normalize_email_addresses(emails)
|
141
|
+
hash = call('grav.removeImage', :addresses => hashed_email_addresses)
|
142
|
+
returning dehashify_emails(hash, emails) { |value| boolean(value) } do
|
143
|
+
expire_cache!
|
144
|
+
end
|
145
|
+
end
|
146
|
+
|
147
|
+
# Remove a userimage from the account and any email addresses with which it is associated. Returns
|
148
|
+
# true or false.
|
149
|
+
#
|
150
|
+
# This method is not cached.
|
151
|
+
#
|
152
|
+
# This method will clear out the cache, since it may have an effect on what the API methods respond with.
|
153
|
+
def delete_user_image!(userimage)
|
154
|
+
returning boolean(call('grav.deleteUserimage', :userimage => userimage)) do
|
155
|
+
expire_cache!
|
156
|
+
end
|
157
|
+
end
|
158
|
+
|
159
|
+
# Runs a simple Gravatar test. Useful for debugging. Gravatar will echo back any arguments you pass.
|
160
|
+
# This method is not cached.
|
161
|
+
def test(hash)
|
162
|
+
call('grav.test', hash)
|
163
|
+
end
|
164
|
+
|
165
|
+
# Returns the MD5 hash for the specified email address, or the one associated with this object.
|
166
|
+
def email_hash(email = self.email)
|
167
|
+
Digest::MD5.hexdigest(email.downcase.strip)
|
168
|
+
end
|
169
|
+
|
170
|
+
# Returns the URL for this user's gravatar image. Options include:
|
171
|
+
#
|
172
|
+
# :ssl or :secure if true, HTTPS will be used instead of HTTP. Default is false.
|
173
|
+
# :rating or :r a rating threshold for this image. Can be one of [ :g, :pg, :r, :x ]. Default is :g.
|
174
|
+
# :size or :s a size for this image. Can be anywhere between 1 and 512. Default is 80.
|
175
|
+
# :default or :d a default URL for this image to display if the specified user has no image;
|
176
|
+
# or this can be one of [ :identicon, :monsterid, :wavatar, 404 ]. By default a generic
|
177
|
+
# Gravatar image URL will be returned.
|
178
|
+
# :filetype an extension such as :jpg or :png. Default is omitted.
|
179
|
+
#
|
180
|
+
# See http://en.gravatar.com/site/implement/url for much more detailed information.
|
181
|
+
def image_url(options = {})
|
182
|
+
secure = options[:ssl] || options[:secure]
|
183
|
+
proto = "http#{secure ? 's' : ''}"
|
184
|
+
sub = secure ? "secure" : "www"
|
185
|
+
|
186
|
+
"#{proto}://#{sub}.gravatar.com/avatar/#{email_hash}#{extension_for_image(options)}#{query_for_image(options)}"
|
187
|
+
end
|
188
|
+
|
189
|
+
# Returns the image data for this user's gravatar image. This is the same as reading the data at #image_url. See
|
190
|
+
# that method for more information.
|
191
|
+
#
|
192
|
+
# This method is cached for up to the value of @duration or Gravatar.duration.
|
193
|
+
def image_data(options = {})
|
194
|
+
url = image_url(options)
|
195
|
+
cache(url) { OpenURI.open_uri(URI.parse(url)).read }
|
196
|
+
end
|
197
|
+
|
198
|
+
def self.version
|
199
|
+
@version ||= File.read(File.join(File.dirname(__FILE__), "../VERSION")).chomp
|
200
|
+
end
|
201
|
+
|
202
|
+
private
|
203
|
+
def cache(*key, &block)
|
204
|
+
@cache.call(*key, &block)
|
205
|
+
end
|
206
|
+
|
207
|
+
def expire_cache!
|
208
|
+
@cache.clear!
|
209
|
+
end
|
210
|
+
|
211
|
+
def dehashify_emails(response, emails)
|
212
|
+
hashed_emails = emails.collect { |email| email_hash(email) }
|
213
|
+
response.inject({}) do |hash, (hashed_email, value)|
|
214
|
+
value = yield(value) if value
|
215
|
+
email = emails[hashed_emails.index(hashed_email)]
|
216
|
+
hash[email] = value
|
217
|
+
hash
|
218
|
+
end
|
219
|
+
end
|
220
|
+
|
221
|
+
def normalize_email_addresses(addresses)
|
222
|
+
addresses.flatten!
|
223
|
+
addresses << @email if addresses.empty?
|
224
|
+
addresses.map { |email| email_hash(email) }
|
225
|
+
end
|
226
|
+
|
227
|
+
def rating(i)
|
228
|
+
case i
|
229
|
+
when 0, '0' then :g
|
230
|
+
when 1, '1' then :pg
|
231
|
+
when 2, '2' then :r
|
232
|
+
when 3, '3' then :x
|
233
|
+
when :g then 0
|
234
|
+
when :pg then 1
|
235
|
+
when :r then 2
|
236
|
+
when :x then 3
|
237
|
+
else raise ArgumentError, "Unexpected rating index: #{i} (expected between 0..3)"
|
238
|
+
end
|
239
|
+
end
|
240
|
+
alias _rating rating
|
241
|
+
|
242
|
+
def boolean(i)
|
243
|
+
i.kind_of?(Numeric) ? i != 0 : i
|
244
|
+
end
|
245
|
+
|
246
|
+
def call(name, args_hash = {})
|
247
|
+
r = @api.call(name, auth.merge(args_hash))
|
248
|
+
r = r.with_indifferent_access if r.kind_of?(Hash)
|
249
|
+
r
|
250
|
+
end
|
251
|
+
|
252
|
+
def auth
|
253
|
+
api_key ? {:apikey => api_key} : (password ? {:password => password} : {})
|
254
|
+
end
|
255
|
+
|
256
|
+
def api_key
|
257
|
+
options[:apikey] || options[:api_key] || options[:key]
|
258
|
+
end
|
259
|
+
|
260
|
+
def password
|
261
|
+
options[:password]
|
262
|
+
end
|
263
|
+
|
264
|
+
def options
|
265
|
+
@options
|
266
|
+
end
|
267
|
+
|
268
|
+
def query_for_image(options)
|
269
|
+
query = ''
|
270
|
+
[:rating, :size, :default, :r, :s, :d].each do |key|
|
271
|
+
if options.key?(key)
|
272
|
+
query.blank? ? query.concat("?") : query.concat("&")
|
273
|
+
query.concat("#{key}=#{CGI::escape options[key].to_s}")
|
274
|
+
end
|
275
|
+
end
|
276
|
+
query
|
277
|
+
end
|
278
|
+
|
279
|
+
def extension_for_image(options)
|
280
|
+
options.key?(:filetype) ? "." + (options[:filetype] || "jpg").to_s : ""
|
281
|
+
end
|
282
|
+
end
|
@@ -0,0 +1,133 @@
|
|
1
|
+
class Gravatar
|
2
|
+
# A wrapper around any given Cache object which provides Gravatar-specific helpers. Used internally.
|
3
|
+
class Cache
|
4
|
+
attr_reader :real_cache, :namespace
|
5
|
+
attr_accessor :duration, :logger
|
6
|
+
|
7
|
+
def initialize(real_cache, duration, namespace = nil, logger = Gravatar.logger)
|
8
|
+
@duration = duration
|
9
|
+
@real_cache = real_cache
|
10
|
+
@namespace = namespace
|
11
|
+
@logger = logger
|
12
|
+
end
|
13
|
+
|
14
|
+
# Provide a series of arguments to be used as a cache key, and a block to be executed when the cache
|
15
|
+
# is expired or needs to be populated.
|
16
|
+
#
|
17
|
+
# Example:
|
18
|
+
# cache = Gravatar::Cache.new(Rails.cache, 30.minutes)
|
19
|
+
# cache.call(:first_name => "Colin", :last_name => "MacKenzie") { call_webservice(with_some_args) }
|
20
|
+
#
|
21
|
+
def call(*key, &block)
|
22
|
+
cached_copy = read_cache(*key)
|
23
|
+
if expired?(*key) && block_given?
|
24
|
+
begin
|
25
|
+
returning(yield) do |object|
|
26
|
+
write_cache(object, *key)
|
27
|
+
end
|
28
|
+
rescue
|
29
|
+
log_error($!)
|
30
|
+
cached_copy.nil? ? nil : cached_copy[:object]
|
31
|
+
end
|
32
|
+
else
|
33
|
+
cached_copy.nil? ? nil : cached_copy[:object]
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
# Clears out the entire cache for this object's namespace. This actually removes the objects,
|
38
|
+
# instead of simply marking them as expired, so it will be as if the object never existed.
|
39
|
+
def clear!
|
40
|
+
@real_cache.delete_matched(/^#{Regexp::escape @namespace}/)
|
41
|
+
end
|
42
|
+
|
43
|
+
# forces the specified key to become expired
|
44
|
+
def expire!(*key)
|
45
|
+
unless expired?(*key)
|
46
|
+
@real_cache.write(cache_key(*key), { :expires_at => 1.minute.ago, :object => read_cache(*key)[:object] })
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
# Returns true if the cached copy is nil or expired based on @duration.
|
51
|
+
def expired?(*key)
|
52
|
+
cached_copy = read_cache(*key)
|
53
|
+
cached_copy.nil? || cached_copy[:expires_at] < Time.now
|
54
|
+
end
|
55
|
+
|
56
|
+
# Reads an object from the cache based on the cache key constructed from *key.
|
57
|
+
def read_cache(*key)
|
58
|
+
@real_cache.read(cache_key(*key))
|
59
|
+
end
|
60
|
+
|
61
|
+
# Writes an object to the cache based on th cache key constructed from *key.
|
62
|
+
def write_cache(object, *key)
|
63
|
+
@real_cache.write(cache_key(*key), { :expires_at => Time.now + duration, :object => object })
|
64
|
+
end
|
65
|
+
|
66
|
+
# Constructs a cache key from the specified *args and @namespace.
|
67
|
+
def cache_key(*args)
|
68
|
+
ActiveSupport::Cache.expand_cache_key(args, @namespace)
|
69
|
+
end
|
70
|
+
|
71
|
+
# Logs an error message, as long as self.logger responds to :error or :write.
|
72
|
+
# Otherwise, re-raises the error.
|
73
|
+
def log_error(error)
|
74
|
+
if logger.respond_to?(:error)
|
75
|
+
logger.error error.message
|
76
|
+
error.backtrace.each { |line| logger.error " #{line}" }
|
77
|
+
elsif logger.respond_to?(:write)
|
78
|
+
logger.write(([error.message]+error.backtrace).join("\n "))
|
79
|
+
logger.write("\n")
|
80
|
+
else raise error
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
class << self
|
86
|
+
def default_cache_instance
|
87
|
+
if defined?(Rails)
|
88
|
+
Rails.cache
|
89
|
+
else
|
90
|
+
ActiveSupport::Cache::FileStore.new("tmp/cache")
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
def default_logger_instance
|
95
|
+
if defined?(Rails)
|
96
|
+
Rails.logger
|
97
|
+
else
|
98
|
+
$stdout
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
def cache
|
103
|
+
@cache ||= default_cache_instance
|
104
|
+
end
|
105
|
+
|
106
|
+
def cache=(instance)
|
107
|
+
@cache = instance
|
108
|
+
end
|
109
|
+
|
110
|
+
def logger
|
111
|
+
@logger ||= default_logger_instance
|
112
|
+
end
|
113
|
+
|
114
|
+
def logger=(logger)
|
115
|
+
@logger = logger
|
116
|
+
end
|
117
|
+
|
118
|
+
# How long is a cached object good for? Default is 30 minutes.
|
119
|
+
def duration
|
120
|
+
@duration ||= 24.hours
|
121
|
+
end
|
122
|
+
|
123
|
+
def duration=(duration)
|
124
|
+
@duration = duration
|
125
|
+
end
|
126
|
+
|
127
|
+
# Resets any changes to the cache and initializes a new cache. If using Rails, the
|
128
|
+
# new cache will be the Rails cache.
|
129
|
+
def reset_cache!
|
130
|
+
@cache = nil
|
131
|
+
end
|
132
|
+
end
|
133
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
if defined?(Rails)
|
2
|
+
Rails.configuration.gem "sc-core-ext", ">= 1.2.0"
|
3
|
+
elsif !defined?(Gem)
|
4
|
+
require 'rubygems'
|
5
|
+
gem 'sc-core-ext', '>= 1.2.0'
|
6
|
+
end
|
7
|
+
|
8
|
+
unless defined?(ScCoreExt) || defined?(Rails) # because Rails will load it later and we don't really need it quite yet.
|
9
|
+
require 'sc-core-ext'
|
10
|
+
end
|
11
|
+
|
12
|
+
# The rest of this is core Ruby stuff so it's safe to load immediately, even if Rails is running the show.
|
13
|
+
require 'open-uri'
|
14
|
+
require "digest/md5"
|
15
|
+
require 'xmlrpc/client'
|
16
|
+
require 'base64'
|
@@ -0,0 +1 @@
|
|
1
|
+
require File.expand_path("../gravatar", __FILE__)
|
@@ -0,0 +1,11 @@
|
|
1
|
+
# To run this test suite, we need to set up some credentials because we're running real HTTP requests.
|
2
|
+
# Make sure you have a working Gravatar account, and enter the following options:
|
3
|
+
primary_email: email@example.com
|
4
|
+
email: email@example.com
|
5
|
+
api_key: YOUR_API_KEY
|
6
|
+
|
7
|
+
# The primary email is the one that's marked 'primary' on your Gravatar account page. Both emails can
|
8
|
+
# be the same. The API Key is your Gravatar API key, which you can optionally replace with:
|
9
|
+
# password: YOUR_GRAVATAR_PASSWORD
|
10
|
+
#
|
11
|
+
# Take care not to commit your credentials!
|
Binary file
|
@@ -0,0 +1,94 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Gravatar::Cache do
|
4
|
+
subject { Gravatar::Cache.new(new_cache, 30.minutes, "gravatar-specs") }
|
5
|
+
|
6
|
+
context "with a nonexistent cache item" do
|
7
|
+
it "should be expired" do
|
8
|
+
subject.expired?(:nothing).should be_true
|
9
|
+
end
|
10
|
+
|
11
|
+
it "should fire the block" do
|
12
|
+
subject.call(:nothing) { @fired = 1 }
|
13
|
+
@fired.should == 1
|
14
|
+
end
|
15
|
+
|
16
|
+
it "should return nil if no block given" do
|
17
|
+
subject.call(:nothing).should be_nil
|
18
|
+
end
|
19
|
+
|
20
|
+
it "should return the block value" do
|
21
|
+
subject.call(:nothing) { 1 }.should == 1
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
context "with a pre-existing cache item" do
|
26
|
+
before(:each) { subject.call(:nothing) { 1 } }
|
27
|
+
|
28
|
+
it "should not be expired" do
|
29
|
+
subject.expired?(:nothing).should be_false
|
30
|
+
end
|
31
|
+
|
32
|
+
it "should not fire the block" do
|
33
|
+
subject.call(:nothing) { @fired = 1 }
|
34
|
+
@fired.should_not == 1
|
35
|
+
end
|
36
|
+
|
37
|
+
it "should return the cached value" do
|
38
|
+
subject.call(:nothing) { raise "Block expected not to fire" }.should == 1
|
39
|
+
end
|
40
|
+
|
41
|
+
context "that is expired" do
|
42
|
+
before(:each) { subject.expire!(:nothing) }
|
43
|
+
|
44
|
+
it "should be expired" do
|
45
|
+
subject.expired?(:nothing).should be_true
|
46
|
+
end
|
47
|
+
|
48
|
+
it "should fire the block" do
|
49
|
+
subject.call(:nothing) { @fired = 1 }
|
50
|
+
@fired.should == 1
|
51
|
+
end
|
52
|
+
|
53
|
+
context "in the event of an error while refreshing" do
|
54
|
+
before(:each) { subject.logger = StringIO.new("") }
|
55
|
+
|
56
|
+
it "should recover" do
|
57
|
+
proc { subject.call(:nothing) { raise "something bad happened" } }.should_not raise_error(RuntimeError)
|
58
|
+
end
|
59
|
+
|
60
|
+
it "should return the cached copy" do
|
61
|
+
subject.call(:nothing) { raise "something bad happened" }.should == 1
|
62
|
+
end
|
63
|
+
|
64
|
+
context "its logger" do
|
65
|
+
before(:each) { subject.logger = Object.new }
|
66
|
+
|
67
|
+
it "should log it if #error is available" do
|
68
|
+
subject.logger.stub!(:error => nil)
|
69
|
+
subject.logger.should_receive(:error).and_return(nil)
|
70
|
+
subject.call(:nothing) { raise "something bad happened" }
|
71
|
+
end
|
72
|
+
|
73
|
+
it "should log it if #write is available" do
|
74
|
+
subject.logger.stub!(:write => nil)
|
75
|
+
subject.logger.should_receive(:write).and_return(nil)
|
76
|
+
subject.call(:nothing) { raise "something bad happened" }
|
77
|
+
end
|
78
|
+
|
79
|
+
it "should re-raise the error if no other methods are available" do
|
80
|
+
proc { subject.call(:nothing) { raise "something bad happened" } }.should raise_error(RuntimeError)
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
it "should return the block value" do
|
86
|
+
subject.call(:nothing) { 2 }.should == 2
|
87
|
+
end
|
88
|
+
|
89
|
+
it "should return the cached value if there is no block value" do
|
90
|
+
subject.call(:nothing).should == 1
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
describe "gravatar cache setup" do
|
2
|
+
context "within Rails" do
|
3
|
+
before(:each) do
|
4
|
+
# We reset it here because we've already set it to MemoryStore for the sake of the majority.
|
5
|
+
Gravatar.reset_cache!
|
6
|
+
module Rails
|
7
|
+
end
|
8
|
+
end
|
9
|
+
|
10
|
+
it "should get the default cache instance from Rails" do
|
11
|
+
Rails.should_receive(:cache).and_return("a cache object")
|
12
|
+
Gravatar.cache
|
13
|
+
end
|
14
|
+
|
15
|
+
it "should get the current cache from cache assignment, if any" do
|
16
|
+
Gravatar.cache = "a cache object"
|
17
|
+
Gravatar.cache.should == "a cache object"
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
context "out of Rails" do
|
22
|
+
it "should get the default cache from ActiveSupport" do
|
23
|
+
Gravatar.cache.should be_kind_of(ActiveSupport::Cache::FileStore)
|
24
|
+
end
|
25
|
+
|
26
|
+
it "should get the current cache from cache assignment, if any" do
|
27
|
+
Gravatar.cache = "a cache object"
|
28
|
+
Gravatar.cache.should == "a cache object"
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
after(:each) do
|
33
|
+
# We reset it here because we've already fubarred it with the Rails tests.
|
34
|
+
Gravatar.reset_cache!
|
35
|
+
|
36
|
+
silence_warnings do
|
37
|
+
if defined?(Rails)
|
38
|
+
Object.send :remove_const, :Rails
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
describe "Dependencies" do
|
4
|
+
context "within Rails" do
|
5
|
+
before(:each) do
|
6
|
+
module ::Rails
|
7
|
+
def self.configuration
|
8
|
+
return @config if @config
|
9
|
+
@config = Object.new
|
10
|
+
klass = class << @config; self; end
|
11
|
+
klass.instance_eval do
|
12
|
+
def gem(*a, &b); end
|
13
|
+
public :gem
|
14
|
+
end
|
15
|
+
@config
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
it "should set a Rails gem dependency" do
|
21
|
+
Rails.configuration.should_receive(:gem, :with => ["sc-core-ext", ">= 1.2.0"])
|
22
|
+
load File.expand_path("../../../../lib/gravatar/dependencies.rb", __FILE__)
|
23
|
+
end
|
24
|
+
|
25
|
+
after(:each) { silence_warnings { Object.send(:remove_const, :Rails) } }
|
26
|
+
end
|
27
|
+
|
28
|
+
# Don't know how to test the inclusion of sc-core-ext via rubygems, but I suppose there's no reason that should fail.
|
29
|
+
end
|
@@ -0,0 +1,141 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Gravatar do
|
4
|
+
it "should have a valid version number" do
|
5
|
+
Gravatar.version.should =~ /^\d+\.\d+\.\d+$/
|
6
|
+
end
|
7
|
+
|
8
|
+
it "should allow setting cache duration by instance" do
|
9
|
+
grav = Gravatar.new($credentials[:primary_email])
|
10
|
+
grav.cache_duration = 10.minutes
|
11
|
+
grav.cache_duration.should == 10.minutes
|
12
|
+
end
|
13
|
+
|
14
|
+
it "should allow setting cache duration globally" do
|
15
|
+
Gravatar.duration = 10.minutes
|
16
|
+
Gravatar.new($credentials[:primary_email]).cache_duration.should == 10.minutes
|
17
|
+
Gravatar.duration = 30.minutes
|
18
|
+
end
|
19
|
+
|
20
|
+
it "should require :email" do
|
21
|
+
proc { subject }.should raise_error(ArgumentError)
|
22
|
+
end
|
23
|
+
|
24
|
+
context "given :email and :key" do
|
25
|
+
subject { Gravatar.new($credentials[:primary_email], $credentials)}
|
26
|
+
|
27
|
+
context "varying image ratings" do
|
28
|
+
[:g, :pg, :r, :x].each do |rating|
|
29
|
+
it "should save #{rating}-rated URLs and delete them" do
|
30
|
+
subject.save_url!(rating, "http://jigsaw.w3.org/css-validator/images/vcss").should ==
|
31
|
+
"2df7db511c46303983f0092556a1e47c"
|
32
|
+
subject.delete_user_image!("2df7db511c46303983f0092556a1e47c").should == true
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
it "should raise an ArgumentError given an invalid rating" do
|
37
|
+
proc { subject.save_url!(:invalid_rating, "http://jigsaw.w3.org/css-validator/images/vcss") }.should \
|
38
|
+
raise_error(ArgumentError)
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
it "should return addresses" do
|
43
|
+
subject.addresses.should_not be_empty
|
44
|
+
end
|
45
|
+
|
46
|
+
it "should test successfully" do
|
47
|
+
subject.test(:greeting => 'hello').should have_key(:response)
|
48
|
+
end
|
49
|
+
|
50
|
+
it "should save URLs and delete them" do
|
51
|
+
subject.save_url!(:g, "http://jigsaw.w3.org/css-validator/images/vcss").should == "2df7db511c46303983f0092556a1e47c"
|
52
|
+
subject.delete_user_image!("2df7db511c46303983f0092556a1e47c").should == true
|
53
|
+
end
|
54
|
+
|
55
|
+
# Not really the ideal approach but it's a valid test, at least
|
56
|
+
it "should save and delete images and associate/unassociate them with accounts" do
|
57
|
+
begin
|
58
|
+
subject.save_data!(:g, image_data).should == "23f086a793459fa25aab280054fec1b2"
|
59
|
+
subject.use_user_image!("23f086a793459fa25aab280054fec1b2", $credentials[:email]).should ==
|
60
|
+
{ $credentials[:email] => false }
|
61
|
+
# See rdoc for #remove_image! for why we're not checking this.
|
62
|
+
subject.remove_image!($credentials[:email])#.should == { $credentials[:email] => true }
|
63
|
+
subject.delete_user_image!("23f086a793459fa25aab280054fec1b2").should == true
|
64
|
+
ensure
|
65
|
+
subject.remove_image!($credentials[:email])
|
66
|
+
begin
|
67
|
+
subject.delete_user_image!("23f086a793459fa25aab280054fec1b2")
|
68
|
+
rescue XMLRPC::FaultException
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
it "should return user images" do
|
74
|
+
subject.user_images.should == {"fe9dee44a1df19967db30a04083722d5"=>
|
75
|
+
[:g, "http://en.gravatar.com/userimage/14612723/fe9dee44a1df19967db30a04083722d5.jpg"]}
|
76
|
+
end
|
77
|
+
|
78
|
+
it "should determine that the user exists" do
|
79
|
+
subject.exists?.should be_true
|
80
|
+
end
|
81
|
+
|
82
|
+
it "should determine that a fake user does not exist" do
|
83
|
+
subject.exists?("not-even-a-valid-email").should be_false
|
84
|
+
end
|
85
|
+
|
86
|
+
it "should determine that multiple fake users do not exist" do
|
87
|
+
subject.exists?("invalid-1", "invalid-2").should == { "invalid-1" => false, "invalid-2" => false }
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
context "given :email" do
|
92
|
+
subject { Gravatar.new("sinisterchipmunk@gmail.com") }
|
93
|
+
|
94
|
+
it "should not raise an error" do
|
95
|
+
proc { subject }.should_not raise_error(ArgumentError)
|
96
|
+
end
|
97
|
+
|
98
|
+
it "should return email_hash" do
|
99
|
+
subject.email_hash.should == "5d8c7a8d951a28e10bd7407f33df6d63"
|
100
|
+
end
|
101
|
+
|
102
|
+
it "should return gravatar image_url" do
|
103
|
+
subject.image_url.should == "http://www.gravatar.com/avatar/5d8c7a8d951a28e10bd7407f33df6d63"
|
104
|
+
end
|
105
|
+
|
106
|
+
it "should return gravitar image data" do
|
107
|
+
subject.image_data.should == image_data
|
108
|
+
end
|
109
|
+
|
110
|
+
it "should return gravatar image_url with SSL" do
|
111
|
+
subject.image_url(:ssl => true).should == "https://secure.gravatar.com/avatar/5d8c7a8d951a28e10bd7407f33df6d63"
|
112
|
+
end
|
113
|
+
|
114
|
+
it "should return gravatar image_url with size" do
|
115
|
+
subject.image_url(:size => 512).should == "http://www.gravatar.com/avatar/5d8c7a8d951a28e10bd7407f33df6d63?size=512"
|
116
|
+
end
|
117
|
+
|
118
|
+
it "should return gravatar image_url with rating" do
|
119
|
+
subject.image_url(:rating => 'pg').should == "http://www.gravatar.com/avatar/5d8c7a8d951a28e10bd7407f33df6d63?rating=pg"
|
120
|
+
end
|
121
|
+
|
122
|
+
it "should return gravatar image_url with file type" do
|
123
|
+
subject.image_url(:filetype => 'png').should == "http://www.gravatar.com/avatar/5d8c7a8d951a28e10bd7407f33df6d63.png"
|
124
|
+
end
|
125
|
+
|
126
|
+
it "should return gravatar image_url with default image" do
|
127
|
+
subject.image_url(:default => "http://example.com/images/example.jpg").should ==
|
128
|
+
"http://www.gravatar.com/avatar/5d8c7a8d951a28e10bd7407f33df6d63?default=http%3A%2F%2Fexample.com%2Fimages%2Fexample.jpg"
|
129
|
+
end
|
130
|
+
|
131
|
+
it "should return gravatar image_url with SSL and default and size and rating" do
|
132
|
+
combinations = %w(
|
133
|
+
https://secure.gravatar.com/avatar/5d8c7a8d951a28e10bd7407f33df6d63?default=identicon&size=80&rating=g
|
134
|
+
https://secure.gravatar.com/avatar/5d8c7a8d951a28e10bd7407f33df6d63?size=80&rating=g&default=identicon
|
135
|
+
https://secure.gravatar.com/avatar/5d8c7a8d951a28e10bd7407f33df6d63?size=80&default=identicon&rating=g
|
136
|
+
https://secure.gravatar.com/avatar/5d8c7a8d951a28e10bd7407f33df6d63?rating=g&size=80&default=identicon
|
137
|
+
)
|
138
|
+
combinations.should include(subject.image_url(:ssl => true, :default => "identicon", :size => 80, :rating => :g))
|
139
|
+
end
|
140
|
+
end
|
141
|
+
end
|
data/spec/spec.opts
ADDED
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,30 @@
|
|
1
|
+
require File.expand_path("../../lib/gravatar", __FILE__)
|
2
|
+
unless defined?(Spec)
|
3
|
+
gem 'rspec'
|
4
|
+
require 'spec'
|
5
|
+
end
|
6
|
+
|
7
|
+
def image_data
|
8
|
+
File.read(File.expand_path("../fixtures/image.jpg", __FILE__))
|
9
|
+
end
|
10
|
+
|
11
|
+
require 'fakeweb'
|
12
|
+
FakeWeb.register_uri(:get, "http://www.gravatar.com/avatar/5d8c7a8d951a28e10bd7407f33df6d63", :response =>
|
13
|
+
"HTTP/1.1 200 OK\nContent-Type: image/jpg\n\n" +image_data)
|
14
|
+
|
15
|
+
def new_cache
|
16
|
+
ActiveSupport::Cache::MemoryStore.new
|
17
|
+
end
|
18
|
+
|
19
|
+
Gravatar.cache = new_cache
|
20
|
+
|
21
|
+
class Net::HTTP
|
22
|
+
alias_method :original_initialize, :initialize
|
23
|
+
def initialize(*args, &block)
|
24
|
+
original_initialize(*args, &block)
|
25
|
+
@ssl_context = OpenSSL::SSL::SSLContext.new
|
26
|
+
@ssl_context.verify_mode = OpenSSL::SSL::VERIFY_NONE
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
$credentials = YAML::load(File.read(File.expand_path("../credentials.yml", __FILE__))).with_indifferent_access
|
metadata
ADDED
@@ -0,0 +1,132 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: gravatar-ultimate
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
prerelease: false
|
5
|
+
segments:
|
6
|
+
- 1
|
7
|
+
- 0
|
8
|
+
- 0
|
9
|
+
version: 1.0.0
|
10
|
+
platform: ruby
|
11
|
+
authors:
|
12
|
+
- Colin MacKenzie IV
|
13
|
+
autorequire:
|
14
|
+
bindir: bin
|
15
|
+
cert_chain: []
|
16
|
+
|
17
|
+
date: 2010-06-16 00:00:00 -04:00
|
18
|
+
default_executable:
|
19
|
+
dependencies:
|
20
|
+
- !ruby/object:Gem::Dependency
|
21
|
+
name: sc-core-ext
|
22
|
+
prerelease: false
|
23
|
+
requirement: &id001 !ruby/object:Gem::Requirement
|
24
|
+
requirements:
|
25
|
+
- - ">="
|
26
|
+
- !ruby/object:Gem::Version
|
27
|
+
segments:
|
28
|
+
- 1
|
29
|
+
- 2
|
30
|
+
- 0
|
31
|
+
version: 1.2.0
|
32
|
+
type: :runtime
|
33
|
+
version_requirements: *id001
|
34
|
+
- !ruby/object:Gem::Dependency
|
35
|
+
name: rspec
|
36
|
+
prerelease: false
|
37
|
+
requirement: &id002 !ruby/object:Gem::Requirement
|
38
|
+
requirements:
|
39
|
+
- - ">="
|
40
|
+
- !ruby/object:Gem::Version
|
41
|
+
segments:
|
42
|
+
- 1
|
43
|
+
- 3
|
44
|
+
- 0
|
45
|
+
version: 1.3.0
|
46
|
+
type: :development
|
47
|
+
version_requirements: *id002
|
48
|
+
- !ruby/object:Gem::Dependency
|
49
|
+
name: fakeweb
|
50
|
+
prerelease: false
|
51
|
+
requirement: &id003 !ruby/object:Gem::Requirement
|
52
|
+
requirements:
|
53
|
+
- - ">="
|
54
|
+
- !ruby/object:Gem::Version
|
55
|
+
segments:
|
56
|
+
- 1
|
57
|
+
- 2
|
58
|
+
- 8
|
59
|
+
version: 1.2.8
|
60
|
+
type: :development
|
61
|
+
version_requirements: *id003
|
62
|
+
description: |-
|
63
|
+
The Ultimate Gravatar Gem!
|
64
|
+
|
65
|
+
This gem is used to interface with the entire Gravatar API: it's not just for generating image URLs, but for connecting
|
66
|
+
to and communicating with the XML-RPC API too! Additionally, it can be used to download the Gravatar image data itself,
|
67
|
+
rather than just a URL to that data. This saves you the extra step of having to do so.
|
68
|
+
email: sinisterchipmunk@gmail.com
|
69
|
+
executables: []
|
70
|
+
|
71
|
+
extensions: []
|
72
|
+
|
73
|
+
extra_rdoc_files:
|
74
|
+
- LICENSE
|
75
|
+
- README.rdoc
|
76
|
+
files:
|
77
|
+
- .document
|
78
|
+
- .gitignore
|
79
|
+
- LICENSE
|
80
|
+
- README.rdoc
|
81
|
+
- Rakefile
|
82
|
+
- VERSION
|
83
|
+
- gravatar-ultimate.gemspec
|
84
|
+
- lib/gravatar-ultimate.rb
|
85
|
+
- lib/gravatar.rb
|
86
|
+
- lib/gravatar/cache.rb
|
87
|
+
- lib/gravatar/dependencies.rb
|
88
|
+
- lib/gravatar_ultimate.rb
|
89
|
+
- spec/credentials.yml.example
|
90
|
+
- spec/fixtures/image.jpg
|
91
|
+
- spec/lib/gravatar/cache_and_logger_spec.rb
|
92
|
+
- spec/lib/gravatar/cache_setup_spec.rb
|
93
|
+
- spec/lib/gravatar/dependencies_spec.rb
|
94
|
+
- spec/lib/gravatar_spec.rb
|
95
|
+
- spec/spec.opts
|
96
|
+
- spec/spec_helper.rb
|
97
|
+
has_rdoc: true
|
98
|
+
homepage: http://github.com/sinisterchipmunk/gravatar
|
99
|
+
licenses: []
|
100
|
+
|
101
|
+
post_install_message:
|
102
|
+
rdoc_options:
|
103
|
+
- --charset=UTF-8
|
104
|
+
require_paths:
|
105
|
+
- lib
|
106
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
107
|
+
requirements:
|
108
|
+
- - ">="
|
109
|
+
- !ruby/object:Gem::Version
|
110
|
+
segments:
|
111
|
+
- 0
|
112
|
+
version: "0"
|
113
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
114
|
+
requirements:
|
115
|
+
- - ">="
|
116
|
+
- !ruby/object:Gem::Version
|
117
|
+
segments:
|
118
|
+
- 0
|
119
|
+
version: "0"
|
120
|
+
requirements: []
|
121
|
+
|
122
|
+
rubyforge_project:
|
123
|
+
rubygems_version: 1.3.6
|
124
|
+
signing_key:
|
125
|
+
specification_version: 3
|
126
|
+
summary: "A gem for interfacing with the entire Gravatar API: not just images, but the XML-RPC API too!"
|
127
|
+
test_files:
|
128
|
+
- spec/spec_helper.rb
|
129
|
+
- spec/lib/gravatar_spec.rb
|
130
|
+
- spec/lib/gravatar/dependencies_spec.rb
|
131
|
+
- spec/lib/gravatar/cache_and_logger_spec.rb
|
132
|
+
- spec/lib/gravatar/cache_setup_spec.rb
|