avataaars 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/lib/avataaars.rb +41 -0
- data/lib/avataaars/errors.rb +18 -0
- data/lib/avataaars/js/processor.js +48 -0
- data/lib/avataaars/processor.rb +103 -0
- data/lib/avataaars/utils.rb +46 -0
- data/lib/avataaars/version.rb +5 -0
- metadata +64 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 60f039285cc32f5d8f61332fed8ac81c0781d85ffcfe59c7edc6b7865ee2e8fe
|
4
|
+
data.tar.gz: 7d157732e340a421768bf61f944933ffc087af5e7095478cb3facd7e58f8e1ae
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 0a6456a02ff4752045f1888a6909df104152302957c5bc2103ffc4af36e2786fe9d134eda272be444ecb40f4ee8a7b7243f6a12a8c5e8fc7810292cdc87a5628
|
7
|
+
data.tar.gz: faf7e0f2d982558eece42d6023cacb8c871fc98d866a91c8bfc34b6ba4bd270e81ef0c556b80da90913a79d7cf32aad964a4304669dfe57721525eb40c79bf86
|
data/lib/avataaars.rb
ADDED
@@ -0,0 +1,41 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'avataaars/version'
|
4
|
+
|
5
|
+
require 'avataaars/errors'
|
6
|
+
require 'avataaars/processor'
|
7
|
+
require 'avataaars/utils'
|
8
|
+
|
9
|
+
#
|
10
|
+
# Avataaars interface for creating avatar SVGs from feature traits
|
11
|
+
#
|
12
|
+
class Avataaars
|
13
|
+
#
|
14
|
+
# @param [Hash] options Optional parameters to pass to avatar processor
|
15
|
+
# see https://github.com/fangpenlin/avataaars
|
16
|
+
#
|
17
|
+
def initialize(options = {})
|
18
|
+
@root_path = options.delete :root_path
|
19
|
+
@options = options
|
20
|
+
end
|
21
|
+
|
22
|
+
#
|
23
|
+
# Call to avatar creation processor and return rendered SVG
|
24
|
+
#
|
25
|
+
# @return [String] The resulting SVG data
|
26
|
+
#
|
27
|
+
def create_avatar
|
28
|
+
normalised_options = Utils.normalize_object(@options)
|
29
|
+
processor.create_avatar normalised_options
|
30
|
+
end
|
31
|
+
|
32
|
+
private
|
33
|
+
|
34
|
+
def root_path
|
35
|
+
@root_path ||= Dir.pwd
|
36
|
+
end
|
37
|
+
|
38
|
+
def processor
|
39
|
+
Processor.new(root_path)
|
40
|
+
end
|
41
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class Avataaars
|
4
|
+
#
|
5
|
+
# Error classes for calling out to Avataaars NodeJS library
|
6
|
+
#
|
7
|
+
# Heavily based on the Schmooze library https://github.com/Shopify/schmooze
|
8
|
+
#
|
9
|
+
Error = Class.new(StandardError)
|
10
|
+
DependencyError = Class.new(Error)
|
11
|
+
module JavaScript # rubocop:disable Style/Documentation
|
12
|
+
Error = Class.new(::Avataaars::Error)
|
13
|
+
UnknownError = Class.new(Error)
|
14
|
+
def self.const_missing(name)
|
15
|
+
const_set name, Class.new(Error)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,48 @@
|
|
1
|
+
// Setup imports
|
2
|
+
try {
|
3
|
+
const Module = require('module');
|
4
|
+
// resolve libraries from the CWD instead of where this script is located
|
5
|
+
const libraryPaths = Module._nodeModulePaths(process.cwd());
|
6
|
+
var React = require(require.resolve('react', { paths: libraryPaths }));
|
7
|
+
var ReactDOMServer = require(require.resolve('react-dom/server', { paths: libraryPaths }));
|
8
|
+
var Avatar = require(require.resolve('avataaars', { paths: libraryPaths })).default;
|
9
|
+
} catch (e) {
|
10
|
+
process.stdout.write(JSON.stringify(['err', e.toString()]));
|
11
|
+
process.stdout.write("\n");
|
12
|
+
process.exit(1);
|
13
|
+
}
|
14
|
+
process.stdout.write("[\"ok\"]\n");
|
15
|
+
|
16
|
+
const _createAvatar = (async (options) => {
|
17
|
+
return ReactDOMServer.renderToString(
|
18
|
+
React.createElement(Avatar, options)
|
19
|
+
);
|
20
|
+
});
|
21
|
+
|
22
|
+
function _handleError(error) {
|
23
|
+
if (error instanceof Error) {
|
24
|
+
process.stdout.write(
|
25
|
+
JSON.stringify(['err', error.toString().replace(new RegExp('^' + error.name + ': '), ''), error.name])
|
26
|
+
);
|
27
|
+
} else {
|
28
|
+
process.stdout.write(JSON.stringify(['err', error.toString()]));
|
29
|
+
}
|
30
|
+
process.stdout.write("\n");
|
31
|
+
}
|
32
|
+
|
33
|
+
// Interface for communicating between Ruby processor and Node processor
|
34
|
+
require('readline').createInterface({
|
35
|
+
input: process.stdin,
|
36
|
+
terminal: false,
|
37
|
+
}).on('line', function(line) {
|
38
|
+
try {
|
39
|
+
Promise.resolve(_createAvatar.apply(null, JSON.parse(line)))
|
40
|
+
.then(function (result) {
|
41
|
+
process.stdout.write(JSON.stringify(['ok', result]));
|
42
|
+
process.stdout.write("\n");
|
43
|
+
})
|
44
|
+
.catch(_handleError);
|
45
|
+
} catch(error) {
|
46
|
+
_handleError(error);
|
47
|
+
}
|
48
|
+
});
|
@@ -0,0 +1,103 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'json'
|
4
|
+
require 'open3'
|
5
|
+
|
6
|
+
class Avataaars
|
7
|
+
#
|
8
|
+
# Processor helper class for calling out to Avataaars NodeJS library
|
9
|
+
#
|
10
|
+
# Heavily based on the Schmooze library https://github.com/Shopify/schmooze
|
11
|
+
#
|
12
|
+
class Processor
|
13
|
+
def initialize(app_root)
|
14
|
+
@app_root = app_root
|
15
|
+
end
|
16
|
+
|
17
|
+
def create_avatar(options)
|
18
|
+
puts "O: #{options}"
|
19
|
+
|
20
|
+
spawn_process
|
21
|
+
ensure_packages_are_initiated
|
22
|
+
call_js_method options
|
23
|
+
ensure
|
24
|
+
cleanup_process if stdin
|
25
|
+
end
|
26
|
+
|
27
|
+
private
|
28
|
+
|
29
|
+
attr_reader :app_root, :stdin, :stdout, :stderr, :wait_thr
|
30
|
+
|
31
|
+
def spawn_process
|
32
|
+
@stdin, @stdout, @stderr, @wait_thr = Open3.popen3(
|
33
|
+
'node',
|
34
|
+
File.expand_path(File.join(__dir__, 'js/processor.js')),
|
35
|
+
chdir: app_root
|
36
|
+
)
|
37
|
+
end
|
38
|
+
|
39
|
+
def ensure_packages_are_initiated
|
40
|
+
input = stdout.gets
|
41
|
+
raise Avataaars::Error, "Failed to instantiate worker process:\n#{stderr.read}" if input.nil?
|
42
|
+
|
43
|
+
result = JSON.parse(input)
|
44
|
+
return if result[0] == 'ok'
|
45
|
+
|
46
|
+
cleanup_process
|
47
|
+
parse_package_error result[1]
|
48
|
+
end
|
49
|
+
|
50
|
+
def parse_package_error(error_message) # rubocop:disable Metrics/AbcSize, Metrics/MethodLength
|
51
|
+
package_name = error_message[/^Error: Cannot find module '(.*)'$/, 1]
|
52
|
+
raise Avataaars::Error, error_message unless package_name
|
53
|
+
|
54
|
+
begin
|
55
|
+
%w[dependencies devDependencies].each do |key|
|
56
|
+
next unless package_json.key?(key) && package_json[key].key?(package_name)
|
57
|
+
|
58
|
+
raise Avataaars::DependencyError, Utils.squish(<<~ERROR)
|
59
|
+
Cannot find module '#{package_name}'.
|
60
|
+
The module was found in '#{package_json_path}' however, please run 'npm install' from '#{app_root}'
|
61
|
+
ERROR
|
62
|
+
end
|
63
|
+
rescue Errno::ENOENT # rubocop:disable Lint/SuppressedException
|
64
|
+
end
|
65
|
+
raise Avataaars::DependencyError, Utils.squish(<<~ERROR)
|
66
|
+
Cannot find module '#{package_name}'. You need to add it to '#{package_json_path}' and run 'npm install'
|
67
|
+
ERROR
|
68
|
+
end
|
69
|
+
|
70
|
+
def package_json_path
|
71
|
+
@package_json_path ||= File.join(app_root, 'package.json')
|
72
|
+
end
|
73
|
+
|
74
|
+
def package_json
|
75
|
+
@package_json ||= JSON.parse(File.read(package_json_path))
|
76
|
+
end
|
77
|
+
|
78
|
+
def call_js_method(options) # rubocop:disable Metrics/AbcSize, Metrics/MethodLength
|
79
|
+
stdin.puts JSON.dump([options])
|
80
|
+
input = stdout.gets
|
81
|
+
raise Errno::EPIPE, "Can't read from worker" if input.nil?
|
82
|
+
|
83
|
+
status, message, error_class = JSON.parse(input)
|
84
|
+
|
85
|
+
if status == 'ok'
|
86
|
+
message
|
87
|
+
elsif error_class.nil?
|
88
|
+
raise Avataaars::JavaScript::UnknownError, message
|
89
|
+
else
|
90
|
+
raise Avataaars::JavaScript.const_get(error_class, false), message
|
91
|
+
end
|
92
|
+
rescue Errno::EPIPE, IOError
|
93
|
+
raise Avataaars::Error, "Worker process failed:\n#{stderr.read}"
|
94
|
+
end
|
95
|
+
|
96
|
+
def cleanup_process
|
97
|
+
stdin.close
|
98
|
+
stdout.close
|
99
|
+
stderr.close
|
100
|
+
wait_thr.join
|
101
|
+
end
|
102
|
+
end
|
103
|
+
end
|
@@ -0,0 +1,46 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class Avataaars
|
4
|
+
#
|
5
|
+
# Utility class for Avataaars helper methods
|
6
|
+
#
|
7
|
+
class Utils
|
8
|
+
#
|
9
|
+
# Deep transform the keys in an object (Hash/Array)
|
10
|
+
#
|
11
|
+
# Copied from active support
|
12
|
+
# @see active_support/core_ext/hash/keys.rb
|
13
|
+
#
|
14
|
+
def self.deep_transform_keys_in_object(object, &block)
|
15
|
+
case object
|
16
|
+
when Hash
|
17
|
+
object.each_with_object({}) do |(key, value), result|
|
18
|
+
result[yield(key)] = deep_transform_keys_in_object(value, &block)
|
19
|
+
end
|
20
|
+
when Array
|
21
|
+
object.map { |e| deep_transform_keys_in_object(e, &block) }
|
22
|
+
else
|
23
|
+
object
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
#
|
28
|
+
# Recursively normalizes hash objects with camelized string keys
|
29
|
+
#
|
30
|
+
def self.normalize_object(object)
|
31
|
+
deep_transform_keys_in_object(object) { |k| normalize_key(k) }
|
32
|
+
end
|
33
|
+
|
34
|
+
#
|
35
|
+
# Normalizes hash keys into camelized strings
|
36
|
+
#
|
37
|
+
# Regex sourced from ActiveSupport camelize
|
38
|
+
#
|
39
|
+
def self.normalize_key(key)
|
40
|
+
key.to_s.downcase.gsub(%r{(?:_|(/))([a-z\d]*)}) do
|
41
|
+
"#{Regexp.last_match(1)}#{Regexp.last_match(2).capitalize}"
|
42
|
+
end
|
43
|
+
end
|
44
|
+
private_class_method :normalize_key
|
45
|
+
end
|
46
|
+
end
|
metadata
ADDED
@@ -0,0 +1,64 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: avataaars
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Andrew Bromwich
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2020-08-18 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: rubocop
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '0.86'
|
20
|
+
type: :development
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '0.86'
|
27
|
+
description: Ruby wrapper library to call to avataaars React component using NodeJS
|
28
|
+
email:
|
29
|
+
- abromwich@studiosity.com
|
30
|
+
executables: []
|
31
|
+
extensions: []
|
32
|
+
extra_rdoc_files: []
|
33
|
+
files:
|
34
|
+
- lib/avataaars.rb
|
35
|
+
- lib/avataaars/errors.rb
|
36
|
+
- lib/avataaars/js/processor.js
|
37
|
+
- lib/avataaars/processor.rb
|
38
|
+
- lib/avataaars/utils.rb
|
39
|
+
- lib/avataaars/version.rb
|
40
|
+
homepage: https://github.com/Studiosity/avataaars-rb
|
41
|
+
licenses:
|
42
|
+
- MIT
|
43
|
+
metadata: {}
|
44
|
+
post_install_message:
|
45
|
+
rdoc_options: []
|
46
|
+
require_paths:
|
47
|
+
- lib
|
48
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
49
|
+
requirements:
|
50
|
+
- - ">="
|
51
|
+
- !ruby/object:Gem::Version
|
52
|
+
version: '0'
|
53
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
54
|
+
requirements:
|
55
|
+
- - ">="
|
56
|
+
- !ruby/object:Gem::Version
|
57
|
+
version: '0'
|
58
|
+
requirements: []
|
59
|
+
rubygems_version: 3.0.6
|
60
|
+
signing_key:
|
61
|
+
specification_version: 4
|
62
|
+
summary: A Ruby gem to transform attributes into avatar SVGs using the React avataaars
|
63
|
+
library through NodeJS
|
64
|
+
test_files: []
|