avataaars 0.0.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.
@@ -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
@@ -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
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ class Avataaars
4
+ VERSION = '0.0.1'
5
+ 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: []