looks 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
data/LICENSE ADDED
@@ -0,0 +1,19 @@
1
+ Copyright (c) 2013 Jussi Virtanen
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining a copy
4
+ of this software and associated documentation files (the "Software"), to deal
5
+ in the Software without restriction, including without limitation the rights
6
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7
+ copies of the Software, and to permit persons to whom the Software is
8
+ furnished to do so, subject to the following conditions:
9
+
10
+ The above copyright notice and this permission notice shall be included in
11
+ all copies or substantial portions of the Software.
12
+
13
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19
+ THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,52 @@
1
+ Looks
2
+ =====
3
+
4
+ Looks is a command line interface to [Gravatar][].
5
+
6
+ [Gravatar]: http://gravatar.com
7
+
8
+
9
+ Installation
10
+ ------------
11
+
12
+ Installing Looks requires [Ruby 1.9 or newer][Ruby].
13
+
14
+ [Ruby]: http://ruby-lang.org/
15
+
16
+ Install Looks:
17
+
18
+ gem install looks
19
+
20
+
21
+ Usage
22
+ -----
23
+
24
+ Configure the default account:
25
+
26
+ looks config
27
+
28
+ See the usage instructions:
29
+
30
+ looks
31
+
32
+
33
+ Development
34
+ -----------
35
+
36
+ Install the dependencies:
37
+
38
+ bundle install
39
+
40
+ Run the tests:
41
+
42
+ bundle exec rake
43
+
44
+ Run the development version:
45
+
46
+ bundle exec bin/looks
47
+
48
+
49
+ License
50
+ -------
51
+
52
+ Looks is released under the MIT License. See `LICENSE` for details.
data/bin/looks ADDED
@@ -0,0 +1,9 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'looks'
4
+
5
+ begin
6
+ Looks::CLI.start(ARGV)
7
+ rescue Interrupt
8
+ abort
9
+ end
data/lib/looks/cli.rb ADDED
@@ -0,0 +1,54 @@
1
+ require 'looks/command'
2
+ require 'looks/config'
3
+ require 'looks/error'
4
+
5
+ module Looks
6
+ module CLI
7
+
8
+ USAGE = <<-EOF
9
+ Usage: looks <command> [arguments]
10
+
11
+ Commands:
12
+ add Upload an image
13
+ addresses List email addresses
14
+ config Configure the default account
15
+ images List uploaded images
16
+ rm Remove an uploaded image
17
+ set Set the image for an email address
18
+
19
+ EOF
20
+
21
+ COMMANDS = {
22
+ 'add' => Command::Add,
23
+ 'addresses' => Command::Addresses,
24
+ 'config' => Command::Config,
25
+ 'images' => Command::Images,
26
+ 'rm' => Command::Rm,
27
+ 'set' => Command::Set
28
+ }
29
+
30
+ def self.start(args)
31
+ usage if args.empty?
32
+
33
+ command = args.shift
34
+ usage unless COMMANDS.include? command
35
+
36
+ config = Config.load
37
+
38
+ begin
39
+ COMMANDS[command].new(config).run(args)
40
+ rescue Error => e
41
+ error(e)
42
+ end
43
+ end
44
+
45
+ def self.usage
46
+ abort USAGE
47
+ end
48
+
49
+ def self.error(message)
50
+ abort "looks: error: #{message}"
51
+ end
52
+
53
+ end
54
+ end
@@ -0,0 +1,25 @@
1
+ require 'looks/command/base'
2
+
3
+ module Looks
4
+ module Command
5
+ class AccountManagement < Base
6
+
7
+ def configure(opts)
8
+ super
9
+
10
+ opts.on('-e', '--email ADDRESS', "Gravatar email address") do |address|
11
+ config.address = address
12
+ end
13
+
14
+ opts.on('-p', '--password PASSWORD', "Gravatar password") do |password|
15
+ config.password = password
16
+ end
17
+ end
18
+
19
+ def execute(args)
20
+ usage unless config.address && config.password
21
+ end
22
+
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,31 @@
1
+ require 'looks/command/account_management'
2
+ require 'looks/error'
3
+ require 'looks/gravatar'
4
+
5
+ module Looks
6
+ module Command
7
+ class Add < AccountManagement
8
+
9
+ def arguments
10
+ [ '<filename>' ]
11
+ end
12
+
13
+ def execute(args)
14
+ super
15
+
16
+ filename = args.first
17
+
18
+ raise Error, "#{filename}: File not found" unless File.exists? filename
19
+
20
+ begin
21
+ File.open(filename) do |file|
22
+ puts Gravatar::Account.new(config).add(file.read)
23
+ end
24
+ rescue IOError, SystemCallError
25
+ raise Error, "#{filename}: Cannot read file"
26
+ end
27
+ end
28
+
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,42 @@
1
+ require 'looks/command/account_management'
2
+ require 'looks/gravatar'
3
+
4
+ module Looks
5
+ module Command
6
+ class Addresses < AccountManagement
7
+
8
+ def initialize(config)
9
+ super
10
+
11
+ @verbose = false
12
+ end
13
+
14
+ def configure(opts)
15
+ super
16
+
17
+ opts.separator ""
18
+ opts.separator "Options:"
19
+
20
+ opts.on('-v', '--verbose', "Be verbose") do |address|
21
+ @verbose = true
22
+ end
23
+ end
24
+
25
+ def execute(args)
26
+ super
27
+
28
+ addresses = Gravatar::Account.new(config).addresses
29
+
30
+ if @verbose
31
+ addresses.each do |address|
32
+ puts "#{address.email}"
33
+ puts " #{address.image.id}" unless address.image.nil?
34
+ end
35
+ else
36
+ puts addresses
37
+ end
38
+ end
39
+
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,84 @@
1
+ require 'looks/cli'
2
+ require 'looks/version'
3
+
4
+ require 'optparse'
5
+
6
+ module Looks
7
+ module Command
8
+ class Base
9
+
10
+ attr_reader :config
11
+
12
+ def initialize(config)
13
+ @opts = OptionParser.new
14
+ @config = config
15
+
16
+ configure(@opts)
17
+ set_banner
18
+ set_version
19
+ end
20
+
21
+ def run(args)
22
+ begin
23
+ @opts.parse! args
24
+ rescue OptionParser::ParseError => e
25
+ CLI.error(e)
26
+ end
27
+
28
+ usage unless args.length == arguments.length
29
+
30
+ execute(args)
31
+ end
32
+
33
+ def usage
34
+ abort to_s
35
+ end
36
+
37
+ def to_s
38
+ "#{@opts.help}\n"
39
+ end
40
+
41
+ protected
42
+
43
+ def arguments
44
+ []
45
+ end
46
+
47
+ def configure(opts)
48
+ opts.separator ""
49
+ opts.separator "Options:"
50
+
51
+ opts.on_tail('-h', '--help', 'Show this help') do |help|
52
+ puts to_s
53
+ exit
54
+ end
55
+ end
56
+
57
+ def execute(args)
58
+ end
59
+
60
+ private
61
+
62
+ def name
63
+ self.class.name.split('::').last.downcase
64
+ end
65
+
66
+ def set_banner
67
+ program_name = @opts.program_name
68
+ command_name = name
69
+
70
+ banner = "Usage: #{program_name} #{command_name}"
71
+
72
+ banner += " [options]"
73
+ banner += " #{arguments.join(' ')}" unless arguments.empty?
74
+
75
+ @opts.set_banner banner
76
+ end
77
+
78
+ def set_version
79
+ @opts.version = Looks::VERSION
80
+ end
81
+
82
+ end
83
+ end
84
+ end
@@ -0,0 +1,30 @@
1
+ require 'looks/command/base'
2
+ require 'looks/config'
3
+ require 'looks/error'
4
+
5
+ require 'highline'
6
+
7
+ module Looks
8
+ module Command
9
+ class Config < Base
10
+
11
+ def execute(args)
12
+ highline = HighLine.new
13
+
14
+ address = highline.ask(" Email address: ")
15
+ password = highline.ask(" Password: ") { |q| q.echo = '*' }
16
+
17
+ config.address = address
18
+ config.password = password
19
+
20
+ begin
21
+ config.save
22
+ rescue IOError, SystemCallError
23
+ raise Error, "#{Looks::Config.filename}: Cannot write file"
24
+ end
25
+ end
26
+
27
+ end
28
+
29
+ end
30
+ end
@@ -0,0 +1,47 @@
1
+ require 'looks/command/account_management'
2
+ require 'looks/gravatar'
3
+
4
+ module Looks
5
+ module Command
6
+ class Images < AccountManagement
7
+
8
+ def initialize(config)
9
+ super
10
+
11
+ @verbose = false
12
+ end
13
+
14
+ def configure(opts)
15
+ super
16
+
17
+ opts.separator ""
18
+ opts.separator "Options:"
19
+
20
+ opts.on('-v', '--verbose', "Be verbose") do |address|
21
+ @verbose = true
22
+ end
23
+ end
24
+
25
+ def execute(args)
26
+ super
27
+
28
+ account = Gravatar::Account.new(config)
29
+ images = account.images
30
+
31
+ if @verbose
32
+ addresses = account.addresses
33
+
34
+ images.each do |image|
35
+ puts "#{image.id}"
36
+ addresses.select { |a| a.image == image }.each do |address|
37
+ puts " #{address}"
38
+ end
39
+ end
40
+ else
41
+ puts images
42
+ end
43
+ end
44
+
45
+ end
46
+ end
47
+ end
@@ -0,0 +1,26 @@
1
+ require 'looks/command/account_management'
2
+ require 'looks/gravatar'
3
+
4
+ module Looks
5
+ module Command
6
+ class Rm < AccountManagement
7
+
8
+ def arguments
9
+ [ '<id>' ]
10
+ end
11
+
12
+ def execute(args)
13
+ super
14
+
15
+ id = args.first
16
+
17
+ begin
18
+ Gravatar::Account.new(config).remove(id)
19
+ rescue Gravatar::IncorrectMethodParameterError
20
+ raise Error, "#{id}: Unknown identifier"
21
+ end
22
+ end
23
+
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,35 @@
1
+ require 'looks/command/account_management'
2
+ require 'looks/error'
3
+ require 'looks/gravatar'
4
+
5
+ module Looks
6
+ module Command
7
+ class Set < AccountManagement
8
+
9
+ def arguments
10
+ [ '<address>', '<id>' ]
11
+ end
12
+
13
+ def execute(args)
14
+ super
15
+
16
+ address, id = args
17
+
18
+ account = Gravatar::Account.new(config)
19
+
20
+ begin
21
+ account.set(address, id)
22
+ rescue Gravatar::IncorrectMethodParameterError
23
+ addresses = account.addresses.map { |address| address.email }
24
+
25
+ if addresses.include? address
26
+ raise Error, "#{id}: Unknown identifier"
27
+ else
28
+ raise Error, "#{address}: Unknown email address"
29
+ end
30
+ end
31
+ end
32
+
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,8 @@
1
+ require 'looks/command/account_management'
2
+ require 'looks/command/add'
3
+ require 'looks/command/addresses'
4
+ require 'looks/command/base'
5
+ require 'looks/command/config'
6
+ require 'looks/command/images'
7
+ require 'looks/command/rm'
8
+ require 'looks/command/set'
@@ -0,0 +1,52 @@
1
+ require 'inifile'
2
+
3
+ module Looks
4
+ class Config
5
+
6
+ def self.filename
7
+ File.join(ENV['HOME'], '.looks')
8
+ end
9
+
10
+ def self.load
11
+ new(IniFile.new(:filename => filename))
12
+ end
13
+
14
+ def address
15
+ user['address']
16
+ end
17
+
18
+ def address=(address)
19
+ user['address'] = address
20
+ end
21
+
22
+ def password
23
+ user['password']
24
+ end
25
+
26
+ def password=(password)
27
+ user['password'] = password
28
+ end
29
+
30
+ def save
31
+ create_file unless File.exists? Config.filename
32
+
33
+ @ini.save
34
+ end
35
+
36
+ private
37
+
38
+ def initialize(ini)
39
+ @ini = ini
40
+ end
41
+
42
+ def user
43
+ @ini['user']
44
+ end
45
+
46
+ def create_file
47
+ FileUtils.touch(Config.filename)
48
+ FileUtils.chmod(0600, Config.filename)
49
+ end
50
+
51
+ end
52
+ end
@@ -0,0 +1,6 @@
1
+ module Looks
2
+
3
+ class Error < StandardError
4
+ end
5
+
6
+ end
@@ -0,0 +1,81 @@
1
+ require 'looks/gravatar/address'
2
+ require 'looks/gravatar/errors'
3
+ require 'looks/gravatar/image'
4
+
5
+ require 'base64'
6
+ require 'xmlrpc/client'
7
+
8
+ module Looks
9
+ module Gravatar
10
+ class Account
11
+
12
+ def initialize(config)
13
+ @password = config.password
14
+ @server = XMLRPC::Client.new_from_uri(Gravatar.url(config.address))
15
+ end
16
+
17
+ def addresses
18
+ addresses = []
19
+
20
+ call('grav.addresses').each do |key, value|
21
+ addresses.push Address.new_from_addresses(key, value)
22
+ end
23
+
24
+ addresses.sort
25
+ end
26
+
27
+ def images
28
+ images = []
29
+
30
+ call('grav.userimages').each do |key, value|
31
+ images.push Image.new_from_images(key, value)
32
+ end
33
+
34
+ images.sort
35
+ end
36
+
37
+ def add(image)
38
+ data = Base64.encode64(image)
39
+
40
+ call('grav.saveData', { 'data' => data })
41
+ end
42
+
43
+ def remove(image)
44
+ call('grav.deleteUserimage', { 'userimage' => image })
45
+ end
46
+
47
+ def set(address, id)
48
+ call('grav.useUserimage', {
49
+ 'userimage' => id,
50
+ 'addresses' => [ address ]
51
+ })
52
+ end
53
+
54
+ private
55
+
56
+ def call(method, args = {})
57
+ args['password'] = @password
58
+
59
+ begin
60
+ @server.call(method, args)
61
+ rescue XMLRPC::FaultException => fault
62
+ handle fault
63
+ end
64
+ end
65
+
66
+ def handle(fault)
67
+ case fault.faultCode
68
+ when -8
69
+ raise InternalError
70
+ when -9
71
+ raise AuthenticationError
72
+ when -11
73
+ raise IncorrectMethodParameterError
74
+ else
75
+ raise UnknownError, fault.faultCode
76
+ end
77
+ end
78
+
79
+ end
80
+ end
81
+ end
@@ -0,0 +1,32 @@
1
+ require 'looks/gravatar/image'
2
+
3
+ module Looks
4
+ module Gravatar
5
+ class Address
6
+ include Comparable
7
+
8
+ def self.new_from_addresses(key, value)
9
+ email = key
10
+ image = Image.new_from_addresses(value)
11
+
12
+ new(email, image)
13
+ end
14
+
15
+ attr_reader :email, :image
16
+
17
+ def initialize(email, image)
18
+ @email = email
19
+ @image = image
20
+ end
21
+
22
+ def <=>(other)
23
+ email <=> other.email
24
+ end
25
+
26
+ def to_s
27
+ email
28
+ end
29
+
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,33 @@
1
+ require 'looks/error'
2
+
3
+ module Looks
4
+ module Gravatar
5
+
6
+ class AuthenticationError < Error
7
+
8
+ def to_s
9
+ "Invalid email address or password"
10
+ end
11
+
12
+ end
13
+
14
+ class IncorrectMethodParameterError < Error
15
+ end
16
+
17
+ class InternalError < Error
18
+ end
19
+
20
+ class UnknownError < Error
21
+
22
+ def initialize(fault_code)
23
+ @fault_code = fault_code
24
+ end
25
+
26
+ def to_s
27
+ "Unknown error (#{@fault_code})"
28
+ end
29
+
30
+ end
31
+
32
+ end
33
+ end
@@ -0,0 +1,40 @@
1
+ module Looks
2
+ module Gravatar
3
+ class Image
4
+ include Comparable
5
+
6
+ def self.new_from_addresses(value)
7
+ id = value['userimage']
8
+ url = value['userimage_url']
9
+ rating = value['rating']
10
+
11
+ new(id, url, rating)
12
+ end
13
+
14
+ def self.new_from_images(key, value)
15
+ id = key
16
+ url = value[1]
17
+ rating = value[0]
18
+
19
+ new(id, url, rating)
20
+ end
21
+
22
+ attr_accessor :id, :url, :rating
23
+
24
+ def initialize(id, url, rating)
25
+ @id = id
26
+ @url = url
27
+ @rating = rating
28
+ end
29
+
30
+ def <=>(other)
31
+ id <=> other.id
32
+ end
33
+
34
+ def to_s
35
+ id
36
+ end
37
+
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,22 @@
1
+ require 'looks/gravatar/account'
2
+ require 'looks/gravatar/address'
3
+ require 'looks/gravatar/errors'
4
+ require 'looks/gravatar/image'
5
+
6
+ require 'digest/md5'
7
+
8
+ module Looks
9
+ module Gravatar
10
+
11
+ URL = 'https://secure.gravatar.com/xmlrpc'
12
+
13
+ def self.hash(email)
14
+ Digest::MD5.hexdigest(email.strip.downcase)
15
+ end
16
+
17
+ def self.url(email)
18
+ "#{URL}?user=#{hash(email)}"
19
+ end
20
+
21
+ end
22
+ end
@@ -0,0 +1,3 @@
1
+ module Looks
2
+ VERSION = '0.1.0'
3
+ end
data/lib/looks.rb ADDED
@@ -0,0 +1,6 @@
1
+ require 'looks/cli'
2
+ require 'looks/command'
3
+ require 'looks/config'
4
+ require 'looks/error'
5
+ require 'looks/gravatar'
6
+ require 'looks/version'
metadata ADDED
@@ -0,0 +1,164 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: looks
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Jussi Virtanen
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2013-06-13 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: highline
16
+ requirement: !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ~>
20
+ - !ruby/object:Gem::Version
21
+ version: '1.6'
22
+ type: :runtime
23
+ prerelease: false
24
+ version_requirements: !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ~>
28
+ - !ruby/object:Gem::Version
29
+ version: '1.6'
30
+ - !ruby/object:Gem::Dependency
31
+ name: inifile
32
+ requirement: !ruby/object:Gem::Requirement
33
+ none: false
34
+ requirements:
35
+ - - ~>
36
+ - !ruby/object:Gem::Version
37
+ version: '2.0'
38
+ type: :runtime
39
+ prerelease: false
40
+ version_requirements: !ruby/object:Gem::Requirement
41
+ none: false
42
+ requirements:
43
+ - - ~>
44
+ - !ruby/object:Gem::Version
45
+ version: '2.0'
46
+ - !ruby/object:Gem::Dependency
47
+ name: aruba
48
+ requirement: !ruby/object:Gem::Requirement
49
+ none: false
50
+ requirements:
51
+ - - ~>
52
+ - !ruby/object:Gem::Version
53
+ version: '0.5'
54
+ type: :development
55
+ prerelease: false
56
+ version_requirements: !ruby/object:Gem::Requirement
57
+ none: false
58
+ requirements:
59
+ - - ~>
60
+ - !ruby/object:Gem::Version
61
+ version: '0.5'
62
+ - !ruby/object:Gem::Dependency
63
+ name: cucumber
64
+ requirement: !ruby/object:Gem::Requirement
65
+ none: false
66
+ requirements:
67
+ - - ~>
68
+ - !ruby/object:Gem::Version
69
+ version: '1.3'
70
+ type: :development
71
+ prerelease: false
72
+ version_requirements: !ruby/object:Gem::Requirement
73
+ none: false
74
+ requirements:
75
+ - - ~>
76
+ - !ruby/object:Gem::Version
77
+ version: '1.3'
78
+ - !ruby/object:Gem::Dependency
79
+ name: rake
80
+ requirement: !ruby/object:Gem::Requirement
81
+ none: false
82
+ requirements:
83
+ - - ~>
84
+ - !ruby/object:Gem::Version
85
+ version: '10.0'
86
+ type: :development
87
+ prerelease: false
88
+ version_requirements: !ruby/object:Gem::Requirement
89
+ none: false
90
+ requirements:
91
+ - - ~>
92
+ - !ruby/object:Gem::Version
93
+ version: '10.0'
94
+ - !ruby/object:Gem::Dependency
95
+ name: rspec-expectations
96
+ requirement: !ruby/object:Gem::Requirement
97
+ none: false
98
+ requirements:
99
+ - - ~>
100
+ - !ruby/object:Gem::Version
101
+ version: '2.13'
102
+ type: :development
103
+ prerelease: false
104
+ version_requirements: !ruby/object:Gem::Requirement
105
+ none: false
106
+ requirements:
107
+ - - ~>
108
+ - !ruby/object:Gem::Version
109
+ version: '2.13'
110
+ description: Control your Gravatar account from the command line.
111
+ email: jussi.k.virtanen@gmail.com
112
+ executables:
113
+ - looks
114
+ extensions: []
115
+ extra_rdoc_files: []
116
+ files:
117
+ - LICENSE
118
+ - README.md
119
+ - bin/looks
120
+ - lib/looks/cli.rb
121
+ - lib/looks/command/account_management.rb
122
+ - lib/looks/command/add.rb
123
+ - lib/looks/command/addresses.rb
124
+ - lib/looks/command/base.rb
125
+ - lib/looks/command/config.rb
126
+ - lib/looks/command/images.rb
127
+ - lib/looks/command/rm.rb
128
+ - lib/looks/command/set.rb
129
+ - lib/looks/command.rb
130
+ - lib/looks/config.rb
131
+ - lib/looks/error.rb
132
+ - lib/looks/gravatar/account.rb
133
+ - lib/looks/gravatar/address.rb
134
+ - lib/looks/gravatar/errors.rb
135
+ - lib/looks/gravatar/image.rb
136
+ - lib/looks/gravatar.rb
137
+ - lib/looks/version.rb
138
+ - lib/looks.rb
139
+ homepage: http://github.com/jvirtanen/looks
140
+ licenses:
141
+ - MIT
142
+ post_install_message:
143
+ rdoc_options: []
144
+ require_paths:
145
+ - lib
146
+ required_ruby_version: !ruby/object:Gem::Requirement
147
+ none: false
148
+ requirements:
149
+ - - ! '>='
150
+ - !ruby/object:Gem::Version
151
+ version: '0'
152
+ required_rubygems_version: !ruby/object:Gem::Requirement
153
+ none: false
154
+ requirements:
155
+ - - ! '>='
156
+ - !ruby/object:Gem::Version
157
+ version: '0'
158
+ requirements: []
159
+ rubyforge_project:
160
+ rubygems_version: 1.8.23
161
+ signing_key:
162
+ specification_version: 3
163
+ summary: Command line interface to Gravatar
164
+ test_files: []