avatar_generator 0.1.1

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.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 0c093a5daee6091591ceb86c0e5f7e8e5cf6f9fa09711a2ae07015e6ca19586d
4
+ data.tar.gz: 7d62181466ae0d8b1ff3b1dfc02fda753642305e35f4064eb221ff254de641e9
5
+ SHA512:
6
+ metadata.gz: e61cc2c182a380cb9c8ef317c7d1624a60ba892153dee54648128bdb95d0eb624564eafe964c2cc8152a3d9e99c033c7b3927ad5253fc93acbdc792c75f68a3a
7
+ data.tar.gz: 0eda3faea365e85d446e6d034afa1237814a5d8706b5fdd2439812a6217809690490ec0efb2c822c4a7fc7d6c27a6edc68b19342933441283f05760b45c46961
data/CHANGELOG.md ADDED
@@ -0,0 +1,5 @@
1
+ ## [Unreleased]
2
+
3
+ ## [0.1.0] - 2026-08-07
4
+
5
+ - Initial release
@@ -0,0 +1,10 @@
1
+ # Code of Conduct
2
+
3
+ "gravatar" follows [The Ruby Community Conduct Guideline](https://www.ruby-lang.org/en/conduct) in all "collaborative space", which is defined as community communications channels (such as mailing lists, submitted patches, commit comments, etc.):
4
+
5
+ * Participants will be tolerant of opposing views.
6
+ * Participants must ensure that their language and actions are free of personal attacks and disparaging personal remarks.
7
+ * When interpreting the words and actions of others, participants should always assume good intentions.
8
+ * Behaviour which can be reasonably considered harassment will not be tolerated.
9
+
10
+ If you have any concerns about behaviour within this project, please contact us at ["salmanmahmud2014@gmail.com"](mailto:"salmanmahmud2014@gmail.com").
data/LICENSE.txt ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2026 Salman Mahmud
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,198 @@
1
+ # AvatarGenerator
2
+
3
+ A github style deterministic avatar generator for Ruby applications.
4
+
5
+ AvatarGenerator generates unique avatars from identifiers such as email addresses, usernames, or any other string. The same identifier always produces the same avatar, while different identifiers produce different avatars.
6
+
7
+ ```ruby
8
+ AvatarGenerator.generate("email@example.com")
9
+ ```
10
+
11
+ ## Features
12
+
13
+ - Deterministic avatar generation
14
+ - Same identifier always produces the same avatar
15
+ - Different identifiers generate different avatars
16
+ - Custom image size
17
+ - Custom background color
18
+ - **PNG** output
19
+ - Save generated avatars to the filesystem
20
+ - Base64 and Data URL output
21
+ - IO and binary blob access
22
+ - Configurable storage and public paths
23
+
24
+ ## Examples
25
+
26
+ ![Examples generated by this library](./gravatar.png)
27
+
28
+ ## Installation
29
+
30
+ Add the gem to your `Gemfile`:
31
+
32
+ ```ruby
33
+ gem "avatar_generator"
34
+ ```
35
+
36
+ Or install it directly:
37
+
38
+ ```bash
39
+ gem install avatar_generator
40
+
41
+ ```
42
+
43
+ ## Basic Usage
44
+
45
+ Generate an avatar using an identifier:
46
+
47
+ ```ruby
48
+ avatar = AvatarGenerator.generate("email@example.com")
49
+
50
+ ```
51
+
52
+ The same identifier always generates the same avatar:
53
+
54
+ ```ruby
55
+ avatar1 = AvatarGenerator.generate("email@example.com")
56
+ avatar2 = AvatarGenerator.generate("email@example.com")
57
+ avatar1.base64 == avatar2.base64
58
+ # => true
59
+ ```
60
+
61
+ ## Save an Avatar
62
+
63
+ By default, avatars are saved using a hash-based filename:
64
+
65
+ ```ruby
66
+ avatar = AvatarGenerator.generate("email@example.com")
67
+ avatar.save
68
+
69
+ ```
70
+
71
+ You can also provide your own filename: `avatar.save("your name")`. It will use `your_name.png`.
72
+
73
+ The default storage directory is: `public/avatars`
74
+
75
+ ## Image Size
76
+
77
+ The default image size is `250x250`. You can specify a custom size:
78
+
79
+ ```ruby
80
+ AvatarGenerator.generate("email@example.com", size: 300)
81
+ ```
82
+
83
+ The generated image will be `300x300`. Image size must be at least 5 to properly calculate the grid
84
+
85
+ ## Background Color
86
+
87
+ Default color is `#FFFFFF`. You can specify a custom background color:
88
+
89
+ ```ruby
90
+ AvatarGenerator.generate("email@example.com", background: "#F5F5F5")
91
+ ```
92
+
93
+ ## Base64
94
+
95
+ Get the generated image as a Base64-encoded string:
96
+
97
+ ```ruby
98
+ avatar = AvatarGenerator.generate("email@example.com")
99
+ avatar.base64
100
+
101
+ ```
102
+
103
+ ## Data URL
104
+
105
+ Get a browser-compatible Data URL: `avatar.data_url`
106
+
107
+ Example:
108
+
109
+ ```text
110
+ data:image/png;base64,iVBORw0KGgo...
111
+ ```
112
+
113
+ This can be used directly in an HTML image:
114
+
115
+ ```html
116
+ <img src="data:image/png;base64,..." />
117
+ ```
118
+
119
+ ## Blob
120
+
121
+ Get the generated image as binary data: `avatar.blob`
122
+
123
+ ## IO
124
+
125
+ Get the generated image as a `StringIO` object: `avatar.io`
126
+
127
+ This can be useful when integrating with libraries that expect an IO-like object.
128
+
129
+ ## File Path and URL
130
+
131
+ Get the filesystem path: `avatar.path`
132
+
133
+ Example: `public/avatars/7f83b1657ff1fc53b92dc18148a1d65d.png`
134
+
135
+ Get the public URL: `avatar.url`
136
+
137
+ Example: `/avatars/7f83b1657ff1fc53b92dc18148a1d65d.png`
138
+
139
+ ## Rails Usage
140
+
141
+ AvatarGenerator can be used as a simple avatar generator in Rails applications. For example:
142
+
143
+ ```ruby
144
+ class User < ApplicationRecord
145
+ def avatar
146
+ AvatarGenerator.generate(email)
147
+ end
148
+ end
149
+ ```
150
+
151
+ Then in a Rails view:
152
+
153
+ ```erb
154
+ <%= image_tag user.avatar.url %>
155
+ # or
156
+ <img src="<%= AvatarGenerator.generate(user.user_name).data_url %>">
157
+ ```
158
+
159
+ Or generate and save the avatar:
160
+
161
+ ```ruby
162
+ user.avatar.save
163
+ ```
164
+
165
+ If you run `rails generate avatar_generator:install` it will give you a configure file in your rails initializer.
166
+
167
+ ```ruby
168
+ AvatarGenerator.configure do |config|
169
+ config.size = 500
170
+ config.background = "#FFFFFF"
171
+ config.storage_path = "public/avatars"
172
+ config.public_path = "/avatars"
173
+ end
174
+ ```
175
+
176
+ Individual avatars can override configured defaults:
177
+
178
+ ```ruby
179
+ AvatarGenerator.generate("user_name", size: 500,background: "#000000")
180
+ ```
181
+
182
+ ## Testing
183
+
184
+ Clone the repository and install dependencies:
185
+
186
+ ```bash
187
+ bundle install
188
+ ```
189
+
190
+ Run the test suite:
191
+
192
+ ```bash
193
+ bundle exec rspec
194
+ ```
195
+
196
+ ## License
197
+
198
+ The gem is available as open source under the terms of the [MIT License](LICENSE).
data/Rakefile ADDED
@@ -0,0 +1,12 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "bundler/gem_tasks"
4
+ require "rspec/core/rake_task"
5
+
6
+ RSpec::Core::RakeTask.new(:spec)
7
+
8
+ require "rubocop/rake_task"
9
+
10
+ RuboCop::RakeTask.new
11
+
12
+ task default: %i[spec rubocop]
@@ -0,0 +1,50 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "lib/avatar_generator/version"
4
+
5
+ Gem::Specification.new do |spec|
6
+ spec.name = "avatar_generator"
7
+ spec.version = AvatarGenerator::VERSION
8
+
9
+ spec.authors = ["Salman Mahmud"]
10
+ spec.email = ["salmanmahmud2014@gmail.com"]
11
+
12
+ spec.summary = "Github style avatar generator for Ruby applications"
13
+ spec.description = <<~DESCRIPTION
14
+ avatar_generator is a github style deterministic avatar generator for Ruby applications.
15
+ It generates unique, consistent avatars from identifiers such as
16
+ usernames, email addresses, or other unique values. The same identifier
17
+ always produces the same avatar.
18
+ DESCRIPTION
19
+
20
+ spec.homepage = "https://github.com/salmanx/avatar_generator"
21
+ spec.license = "MIT"
22
+ spec.required_ruby_version = ">= 2.7.0"
23
+
24
+ spec.metadata["homepage_uri"] = spec.homepage
25
+ spec.metadata["source_code_uri"] =
26
+ "https://github.com/salmanx/avatar_generator"
27
+ spec.metadata["changelog_uri"] =
28
+ "https://github.com/salmanx/avatar_generator/releases"
29
+ spec.metadata["bug_tracker_uri"] =
30
+ "https://github.com/salmanx/avatar_generator/issues"
31
+ spec.metadata["rubygems_mfa_required"] = "true"
32
+
33
+ spec.files = Dir.chdir(__dir__) do
34
+ Dir[
35
+ "{lib,sig}/**/*",
36
+ "README.md",
37
+ "LICENSE.txt",
38
+ "CHANGELOG.md",
39
+ "CODE_OF_CONDUCT.md",
40
+ "*.gemspec",
41
+ "Rakefile",
42
+ ]
43
+ end
44
+
45
+ spec.require_paths = ["lib"]
46
+
47
+ # Runtime dependencies
48
+ spec.add_dependency "base64"
49
+ spec.add_dependency "chunky_png"
50
+ end
@@ -0,0 +1,86 @@
1
+ # frozen_string_literal: true
2
+
3
+ module AvatarGenerator
4
+ class Avatar
5
+ def initialize(image, identifier:)
6
+ @image_data = image
7
+ @identifier = identifier
8
+ end
9
+
10
+ def image
11
+ @image ||= Renderer.render(@image_data)
12
+ end
13
+
14
+ def save(filename = nil)
15
+ filename ||= self.filename
16
+ filename = sanitize_filename(filename)
17
+
18
+ file_path = File.join(
19
+ AvatarGenerator.configuration.storage_path,
20
+ filename
21
+ )
22
+
23
+ FileUtils.mkdir_p(
24
+ File.dirname(file_path)
25
+ )
26
+
27
+ image.save(file_path)
28
+
29
+ file_path
30
+ end
31
+
32
+ def path
33
+ File.join(
34
+ AvatarGenerator.configuration.storage_path,
35
+ filename
36
+ )
37
+ end
38
+
39
+ def url
40
+ File.join(
41
+ AvatarGenerator.configuration.public_path,
42
+ filename
43
+ )
44
+ end
45
+
46
+ def filename
47
+ "#{identifier_hash}.png"
48
+ end
49
+
50
+ def blob
51
+ image.to_blob
52
+ end
53
+
54
+ def io
55
+ StringIO.new(blob)
56
+ end
57
+
58
+ def base64
59
+ Base64.strict_encode64(blob)
60
+ end
61
+
62
+ def data_url
63
+ "data:image/png;base64,#{base64}"
64
+ end
65
+
66
+ private
67
+
68
+ def identifier_hash
69
+ Digest::MD5.hexdigest(
70
+ @identifier.downcase.strip
71
+ )
72
+ end
73
+
74
+ def sanitize_filename(filename)
75
+ name = File.basename(filename.to_s, File.extname(filename))
76
+
77
+ name = name.gsub(/\s+/, "_")
78
+ name = name.gsub(/[^a-zA-Z0-9_]/, "")
79
+ name = "#{name}.png"
80
+
81
+ raise ArgumentError, "filename cannot be empty" if name == ".png"
82
+
83
+ name
84
+ end
85
+ end
86
+ end
@@ -0,0 +1,17 @@
1
+ # frozen_string_literal: true
2
+
3
+ module AvatarGenerator
4
+ class Configuration
5
+ attr_accessor :size,
6
+ :background,
7
+ :storage_path,
8
+ :public_path
9
+
10
+ def initialize
11
+ @size = 250
12
+ @background = "#FFFFFF"
13
+ @storage_path = "public/avatars"
14
+ @public_path = "/avatars"
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,63 @@
1
+ # frozen_string_literal: true
2
+
3
+ module AvatarGenerator
4
+ class Generator
5
+ GRID_SIZE = 5
6
+ def initialize(identifier, size: nil, background: nil)
7
+ @identifier = identifier
8
+ @image_size = size || AvatarGenerator.configuration.size
9
+ @background = background || AvatarGenerator.configuration.background
10
+
11
+ raise ArgumentError, "image size must be at least 5" if @image_size < 5
12
+ end
13
+
14
+ def call
15
+ bytes = Digest::MD5.digest(@identifier).bytes
16
+
17
+ color =
18
+ bytes.first(3)
19
+
20
+ grid =
21
+ bytes
22
+ .each_slice(3)
23
+ .select { |row| row.length == 3 }
24
+ .map { |row| mirror_row(row) }
25
+ .flatten
26
+ .each_with_index
27
+ .select { |code, _| code.even? }
28
+
29
+ pixel_map =
30
+ grid.map do |_, index|
31
+ cell =
32
+ @image_size / GRID_SIZE
33
+
34
+ x =
35
+ (index % GRID_SIZE) * cell
36
+
37
+ y =
38
+ (index / GRID_SIZE) * cell
39
+
40
+ [
41
+ [x, y],
42
+ [x + cell - 1, y + cell - 1]
43
+ ]
44
+ end
45
+
46
+ Image.new(
47
+ color: color,
48
+ image_size: @image_size,
49
+ pixel_map: pixel_map,
50
+ background: @background
51
+ )
52
+ end
53
+
54
+ private
55
+
56
+ def mirror_row(row)
57
+ row + [
58
+ row[1],
59
+ row[0]
60
+ ]
61
+ end
62
+ end
63
+ end
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ module AvatarGenerator
4
+ Image = Struct.new(
5
+ :color,
6
+ :pixel_map,
7
+ :image_size,
8
+ :background,
9
+ keyword_init: true
10
+ )
11
+ end
@@ -0,0 +1,35 @@
1
+ # frozen_string_literal: true
2
+
3
+ module AvatarGenerator
4
+ class Renderer
5
+ def self.render(image)
6
+ background = ChunkyPNG::Color.from_hex(image.background)
7
+
8
+ png = ChunkyPNG::Image.new(
9
+ image.image_size,
10
+ image.image_size,
11
+ background
12
+ )
13
+
14
+ color = ChunkyPNG::Color.rgb(
15
+ *image.color
16
+ )
17
+
18
+ image.pixel_map.each do |rectangle|
19
+ top_left,
20
+ bottom_right = rectangle
21
+
22
+ x1, y1 = top_left
23
+ x2, y2 = bottom_right
24
+
25
+ (x1..x2).each do |x|
26
+ (y1..y2).each do |y|
27
+ png[x, y] = color
28
+ end
29
+ end
30
+ end
31
+
32
+ png
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ module AvatarGenerator
4
+ VERSION = "0.1.1"
5
+ end
@@ -0,0 +1,39 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "avatar_generator/version"
4
+
5
+ require "digest/md5"
6
+ require "base64"
7
+ require "stringio"
8
+ require "chunky_png"
9
+ require "fileutils"
10
+
11
+ require_relative "avatar_generator/configuration"
12
+ require_relative "avatar_generator/image"
13
+ require_relative "avatar_generator/generator"
14
+ require_relative "avatar_generator/renderer"
15
+ require_relative "avatar_generator/avatar"
16
+ module AvatarGenerator
17
+ class Error < StandardError; end
18
+
19
+ class << self
20
+ attr_reader :configuration
21
+
22
+ def configure
23
+ yield(configuration)
24
+ end
25
+
26
+ def generate(identifier, **options)
27
+ image =
28
+ Generator
29
+ .new(identifier, **options)
30
+ .call
31
+
32
+ Avatar.new(
33
+ image,
34
+ identifier: identifier
35
+ )
36
+ end
37
+ end
38
+ @configuration = Configuration.new
39
+ end
@@ -0,0 +1,20 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "rails/generators"
4
+
5
+ module AvatarGenerator
6
+ module Generators
7
+ class InstallGenerator < Rails::Generators::Base
8
+ desc "Creates a Gravatar initializer."
9
+
10
+ source_root File.expand_path("templates", __dir__)
11
+
12
+ def create_initializer
13
+ template(
14
+ "avatar_generator.rb",
15
+ "config/initializers/avatar_generator.rb"
16
+ )
17
+ end
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,20 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Gravatar configuration
4
+ #
5
+ # You can customize the default settings below.
6
+ # Individual avatars can override these settings when generated.
7
+
8
+ AvatarGenerator.configure do |config|
9
+ # Default avatar size in pixels.
10
+ config.size = 250
11
+
12
+ # Default background color.
13
+ config.background = "#FFFFFF"
14
+
15
+ # Directory where generated avatars are stored.
16
+ config.storage_path = "public/avatars"
17
+
18
+ # Public URL path used to access generated avatars.
19
+ config.public_path = "/avatars"
20
+ end
@@ -0,0 +1,4 @@
1
+ AvatarGenerator Gravatar
2
+ VERSION: String
3
+ # See the writing guide of rbs: https://github.com/ruby/rbs#guides
4
+ end
metadata ADDED
@@ -0,0 +1,93 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: avatar_generator
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.1
5
+ platform: ruby
6
+ authors:
7
+ - Salman Mahmud
8
+ bindir: bin
9
+ cert_chain: []
10
+ date: 1980-01-02 00:00:00.000000000 Z
11
+ dependencies:
12
+ - !ruby/object:Gem::Dependency
13
+ name: base64
14
+ requirement: !ruby/object:Gem::Requirement
15
+ requirements:
16
+ - - ">="
17
+ - !ruby/object:Gem::Version
18
+ version: '0'
19
+ type: :runtime
20
+ prerelease: false
21
+ version_requirements: !ruby/object:Gem::Requirement
22
+ requirements:
23
+ - - ">="
24
+ - !ruby/object:Gem::Version
25
+ version: '0'
26
+ - !ruby/object:Gem::Dependency
27
+ name: chunky_png
28
+ requirement: !ruby/object:Gem::Requirement
29
+ requirements:
30
+ - - ">="
31
+ - !ruby/object:Gem::Version
32
+ version: '0'
33
+ type: :runtime
34
+ prerelease: false
35
+ version_requirements: !ruby/object:Gem::Requirement
36
+ requirements:
37
+ - - ">="
38
+ - !ruby/object:Gem::Version
39
+ version: '0'
40
+ description: |
41
+ avatar_generator is a github style deterministic avatar generator for Ruby applications.
42
+ It generates unique, consistent avatars from identifiers such as
43
+ usernames, email addresses, or other unique values. The same identifier
44
+ always produces the same avatar.
45
+ email:
46
+ - salmanmahmud2014@gmail.com
47
+ executables: []
48
+ extensions: []
49
+ extra_rdoc_files: []
50
+ files:
51
+ - CHANGELOG.md
52
+ - CODE_OF_CONDUCT.md
53
+ - LICENSE.txt
54
+ - README.md
55
+ - Rakefile
56
+ - avatar_generator.gemspec
57
+ - lib/avatar_generator.rb
58
+ - lib/avatar_generator/avatar.rb
59
+ - lib/avatar_generator/configuration.rb
60
+ - lib/avatar_generator/generator.rb
61
+ - lib/avatar_generator/image.rb
62
+ - lib/avatar_generator/renderer.rb
63
+ - lib/avatar_generator/version.rb
64
+ - lib/generators/avatar_generator/install/install_generator.rb
65
+ - lib/generators/avatar_generator/install/templates/avatar_generator.rb
66
+ - sig/avatar_generator.rbs
67
+ homepage: https://github.com/salmanx/avatar_generator
68
+ licenses:
69
+ - MIT
70
+ metadata:
71
+ homepage_uri: https://github.com/salmanx/avatar_generator
72
+ source_code_uri: https://github.com/salmanx/avatar_generator
73
+ changelog_uri: https://github.com/salmanx/avatar_generator/releases
74
+ bug_tracker_uri: https://github.com/salmanx/avatar_generator/issues
75
+ rubygems_mfa_required: 'true'
76
+ rdoc_options: []
77
+ require_paths:
78
+ - lib
79
+ required_ruby_version: !ruby/object:Gem::Requirement
80
+ requirements:
81
+ - - ">="
82
+ - !ruby/object:Gem::Version
83
+ version: 2.7.0
84
+ required_rubygems_version: !ruby/object:Gem::Requirement
85
+ requirements:
86
+ - - ">="
87
+ - !ruby/object:Gem::Version
88
+ version: '0'
89
+ requirements: []
90
+ rubygems_version: 3.6.8
91
+ specification_version: 4
92
+ summary: Github style avatar generator for Ruby applications
93
+ test_files: []