emoji-datasource 14.0.0 → 14.0.2

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: b3935f8318904fee0e0c3e662d843c7720e6ec2e7df4dafe98ffe7365d3f6236
4
- data.tar.gz: a7c9b43189f238363cf275d68037970d5f3d5f03c379e6415bab44bab25ff938
3
+ metadata.gz: 705920a1ffdf8ca2028830eeb6420d94d23955b1c189613011e666c6028b9a45
4
+ data.tar.gz: bb399eaa99c6448fdda1826fae0921c86ad1f261aa2ec892e4de4815e70e197d
5
5
  SHA512:
6
- metadata.gz: 2049e3e19cb85aa80dc0fe47604d3f715b02977d50cdd9cd345aecdd949f7dbaca7cc15c7a0e26078bc04803fe3c3cc4c1fd63e6f2822e3cf00cf2ed161a3aaa
7
- data.tar.gz: ee10a8cb1a4cb899a659700363956aa97ab3994c74ce563fa7cd461a18e2ab92de669163e5c25df20d2c780e332084b4ef402127798d4bd78e32ecbbd130fce8
6
+ metadata.gz: 6518b1ac91acc735b23a63bc7405dab7ed4d1c8928a0f850656b9278cd1e32a007d1ded4568b06a68849067faad2e8b7ee52131e1cc1912498ea555ab7aece8e
7
+ data.tar.gz: fa032904d287a9bd0d8a9e1614c6e86dc2f6e90b1b162bcdc357b328e6b187ba2ff0f3069d3bc4836453eb21acaa6688b6bb58db314ed767f4db2019807c8348
data/.rubocop.yml CHANGED
@@ -40,6 +40,9 @@ Metrics/BlockLength:
40
40
  - '*.gemspec'
41
41
  - 'spec/**/*_spec.rb'
42
42
 
43
+ Metrics/AbcSize:
44
+ Max: 18
45
+
43
46
  Lint/MissingSuper:
44
47
  Enabled: false
45
48
 
data/README.md CHANGED
@@ -22,6 +22,21 @@ Find emoji by short name
22
22
 
23
23
  ```ruby
24
24
  EmojiDatasource.find_by_short_name('+1')
25
+ #=> EmojiDatasource::Emoji: :+1: (👍)
26
+ ```
27
+
28
+ Find emoji by raw string:
29
+
30
+ ```ruby
31
+ EmojiDatasource.find_by_char('👍🏾')
32
+ #=> EmojiDatasource::Emoji: :+1::skin-tone-5: (👍🏾)
33
+ ```
34
+
35
+ Find emoji by unicode hex character code:
36
+
37
+ ```ruby
38
+ EmojiDatasource.find_by_unified("1F469-200D-2764-FE0F-200D-1F468")
39
+ #=> EmojiDatasource::Emoji: :woman-heart-man: (👩‍❤️‍👨)
25
40
  ```
26
41
 
27
42
  Convert emoji short name to character
@@ -33,9 +48,17 @@ EmojiDatasource.short_name_to_char('+1') # => 👍
33
48
  this also supports skin variations
34
49
 
35
50
  ```ruby
36
- EmojiDatasource.short_name_to_char('+1::skin-tone-2') # => 👍
51
+ EmojiDatasource.short_name_to_char('+1::skin-tone-2') # => 👍🏻
37
52
  ```
38
53
 
54
+ Get base emoji for skin variation:
55
+
56
+ ```ruby
57
+ emoji = EmojiDatasource.find_by_short_name(':+1::skin-tone-5:')
58
+ #=> EmojiDatasource::Emoji: :+1::skin-tone-5: (👍🏾)
59
+ emoji.base
60
+ #=> EmojiDatasource::Emoji: :+1: (👍)
61
+ ```
39
62
 
40
63
  ## Supported Ruby Versions
41
64
 
@@ -2,38 +2,86 @@
2
2
 
3
3
  module EmojiDatasource
4
4
  class Emoji
5
- attr_reader :data
5
+ attr_reader :data, :unified
6
6
 
7
- def initialize(data)
7
+ def initialize(data, variation: nil)
8
8
  @data = data
9
+ @variation = variation
10
+ @unified = variation ? @data.dig('skin_variations', variation, 'unified') : @data['unified']
9
11
  end
10
12
 
13
+ # Raw emoji string
11
14
  def to_char
12
- EmojiDatasource.unified_to_char(data[:unified])
15
+ EmojiDatasource.unified_to_char(unified)
13
16
  end
14
17
 
15
- def skin_variations
16
- return unless @data[:skin_variations]
18
+ # Official emoji name (pretty long)
19
+ def name
20
+ return @data['name'] unless @variation
17
21
 
18
- @data[:skin_variations].transform_keys(&:to_s)
22
+ "#{@data['name']} (#{variation_emojis.map(&:name).join(', ')})"
23
+ end
24
+
25
+ # Short code that often can be used to find emoji in pickers and chat apps
26
+ def short_name
27
+ return @data['short_name'] unless @variation
28
+
29
+ "#{@data['short_name']}::#{variation_emojis.map(&:short_name).join('::')}"
30
+ end
31
+
32
+ # All known short names (base +short_name+ and maybe some aliases)
33
+ def short_names
34
+ return @data['short_names'] unless @variation
35
+
36
+ @short_names ||= @data['short_names'].flat_map do |short_name|
37
+ full_short_name = "#{short_name}::#{variation_emojis.map(&:short_name).join('::')}"
38
+
39
+ # Allow to find complex emojis with multiple equal skin tone codes by single skin tone
40
+ # For some emojis (like :women_holding_hands:) there are special single skin tone emojis.
41
+ # but for others (like :people_holding_hands:) there are no such versions.
42
+ # However, some implementations in the wild (e.g. Slack and EmojiMart) understand codes
43
+ # containing only single skin tone (e.g. `:people_holding_hands::skin-tone-N:`)
44
+ if variation_emojis.size > 1 && variation_emojis.uniq.size == 1
45
+ abbr_short_name = "#{short_name}::#{variation_emojis.first.short_name}"
46
+ end
47
+
48
+ [full_short_name, abbr_short_name].compact
49
+ end
50
+ end
51
+
52
+ # All known skin tone variations
53
+ def variations
54
+ return @variations = [] if @variation
55
+
56
+ @variations ||= @data.fetch('skin_variations', {}).each_key.map do |key|
57
+ self.class.new(data, variation: key)
58
+ end
59
+ end
60
+
61
+ # Base emoji, without variations applied.
62
+ # E.g. for `:+1::skin-tone-2:` base will be just `:+1:`
63
+ def base
64
+ return self unless @variation
65
+
66
+ EmojiDatasource.find_by_unified(@data['unified'])
19
67
  end
20
68
 
21
69
  def method_missing(method_name, *arguments, &block)
22
- if @data.key?(method_name)
23
- @data[method_name]
70
+ if @data.key?(method_name.to_s)
71
+ @data[method_name.to_s]
24
72
  else
25
73
  super
26
74
  end
27
75
  end
28
76
 
29
77
  def respond_to_missing?(method_name, include_private = false)
30
- return true if @data.key?(method_name)
78
+ return true if @data.key?(method_name.to_s)
31
79
 
32
80
  super
33
81
  end
34
82
 
35
83
  def inspect
36
- "#{self.class.name}:#{short_name}"
84
+ "#{self.class.name}: :#{short_name}: (#{to_char})"
37
85
  end
38
86
 
39
87
  def to_s
@@ -47,5 +95,15 @@ module EmojiDatasource
47
95
  def to_json(**args)
48
96
  @data.to_json(**args)
49
97
  end
98
+
99
+ private
100
+
101
+ def variation_emojis
102
+ return [] unless @variation
103
+
104
+ @variation_emojis ||= @variation.split('-').map do |unified|
105
+ EmojiDatasource.find_by_unified(unified)
106
+ end
107
+ end
50
108
  end
51
109
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module EmojiDatasource
4
- VERSION = '14.0.0'
4
+ VERSION = '14.0.2'
5
5
  end
@@ -4,23 +4,50 @@ require 'json'
4
4
 
5
5
  require_relative 'emoji_datasource/version'
6
6
  require_relative 'emoji_datasource/emoji'
7
- require_relative 'emoji_datasource/short_name_to_char'
8
7
 
9
8
  module EmojiDatasource
10
9
  class Error < StandardError; end
11
10
 
12
11
  EMOJI_DATA_PATH = File.join(__dir__, '..', 'vendor', 'emoji-datasource', 'emoji.json')
13
12
 
13
+ # @example
14
+ # short_name_to_char('+1') #=> "👍"
14
15
  def self.short_name_to_char(name)
15
- EmojiDatasource::ShortNameToChar.generate(name)
16
+ find_by_short_name(name)&.to_char
16
17
  end
17
18
 
19
+ # Finds emoji by short code (e.g. `+1`, `:+1:`, `:+1::skin-tone-5:`)
20
+ # @param name [String] short code with or without wrapping colons.
21
+ # @return [EmojiDatasource::Emoji] if there is an emoji matching +name+
22
+ # @return [nil] if there are no emojis matching +name+
23
+ # @example
24
+ # find_by_short_name('+1') #=> EmojiDatasource::Emoji: :+1: (👍)
18
25
  def self.find_by_short_name(name)
19
26
  return unless name
20
27
 
21
- data.detect do |emoji|
22
- emoji.short_name == name || emoji.short_names.include?(name)
23
- end
28
+ name = name.delete_prefix(':').delete_suffix(':') # Allow to find `+1` by `:+1:`
29
+ name.delete_suffix!('::skin-tone-1') # special case for default skin tone
30
+ short_name_lookup_map[name]
31
+ end
32
+
33
+ # Finds emoji by unified hex representation of Unicode codepoints
34
+ # @param unified [String] Dash separated hexadecimal Unicode character codes for a single emoji
35
+ # @return [EmojiDatasource::Emoji] if given Unicode codepoints is a known emoji
36
+ # @return [nil] if there are no emojis with given codepoints in the dataset
37
+ # @example
38
+ # short_name_to_char('1F44D') #=> EmojiDatasource::Emoji: :+1: (👍)
39
+ def self.find_by_unified(unified)
40
+ unified_lookup_map[unified.upcase]
41
+ end
42
+
43
+ # Find emoji object by raw Unicode string with a single emoji in it
44
+ # @param raw_emoji [String] Single emoji
45
+ # @return [EmojiDatasource::Emoji] if given string contains a single known emoji
46
+ # @return [nil] if there are no such emoji in the dataset
47
+ # @example
48
+ # find_by_char('👍') #=> EmojiDatasource::Emoji: :+1: (👍)
49
+ def self.find_by_char(raw_emoji)
50
+ find_by_unified(char_to_unified(raw_emoji))
24
51
  end
25
52
 
26
53
  def self.unified_to_char(unified_name)
@@ -29,10 +56,40 @@ module EmojiDatasource
29
56
  unified_name.split('-').map(&:hex).pack('U*')
30
57
  end
31
58
 
59
+ def self.char_to_unified(raw_emoji)
60
+ return unless raw_emoji
61
+
62
+ raw_emoji.unpack('U*').map { |c| c.to_s(16).upcase }.join('-')
63
+ end
64
+
32
65
  def self.data
33
- @data ||= JSON.parse(File.read(EMOJI_DATA_PATH), symbolize_names: true)
66
+ @data ||= JSON.parse(File.read(EMOJI_DATA_PATH))
34
67
  .map { |emoji_data| EmojiDatasource::Emoji.new(emoji_data) }
35
68
  end
69
+
70
+ # Utility hash map to search by emoji short code, including variants
71
+ # @api private
72
+ def self.short_name_lookup_map
73
+ @short_name_lookup_map ||= data.each_with_object({}) do |emoji, result|
74
+ emoji.short_names.each { |short_name| result[short_name] = emoji }
75
+ emoji.variations.each do |emoji_variant|
76
+ emoji_variant.short_names.each do |short_name|
77
+ result[short_name] = emoji_variant
78
+ end
79
+ end
80
+ end
81
+ end
82
+
83
+ # Utility hash map to search by unicode character sequence hex codes, including variants
84
+ # @api private
85
+ def self.unified_lookup_map
86
+ @unified_lookup_map ||= data.each_with_object({}) do |emoji, result|
87
+ result[emoji.unified] = emoji
88
+ emoji.variations.each do |emoji_variant|
89
+ result[emoji_variant.unified] = emoji_variant
90
+ end
91
+ end
92
+ end
36
93
  end
37
94
 
38
95
  # Preload emojies on startup
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: emoji-datasource
3
3
  version: !ruby/object:Gem::Version
4
- version: 14.0.0
4
+ version: 14.0.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Justas Palumickas
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2023-02-14 00:00:00.000000000 Z
11
+ date: 2023-03-16 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: pry
@@ -104,7 +104,6 @@ files:
104
104
  - lib/emoji-datasource.rb
105
105
  - lib/emoji_datasource.rb
106
106
  - lib/emoji_datasource/emoji.rb
107
- - lib/emoji_datasource/short_name_to_char.rb
108
107
  - lib/emoji_datasource/version.rb
109
108
  - package.json
110
109
  - vendor/emoji-datasource/emoji.json
@@ -133,7 +132,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
133
132
  - !ruby/object:Gem::Version
134
133
  version: '0'
135
134
  requirements: []
136
- rubygems_version: 3.2.32
135
+ rubygems_version: 3.4.8
137
136
  signing_key:
138
137
  specification_version: 4
139
138
  summary: Emoji data from emoji-datasource npm package
@@ -1,49 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module EmojiDatasource
4
- class ShortNameToChar
5
- def self.generate(name)
6
- new(name).call
7
- end
8
-
9
- attr_reader :short_name
10
-
11
- def initialize(short_name)
12
- @short_name = short_name
13
- end
14
-
15
- def call
16
- return unless short_name
17
- return EmojiDatasource.find_by_short_name(short_name)&.to_char unless skin_tone_matches
18
-
19
- emoji = EmojiDatasource.find_by_short_name(skin_tone_matches[1])
20
- return unless emoji
21
- return emoji.to_char if skin_tone_level == 1
22
-
23
- char_with_skin_tone(emoji)
24
- end
25
-
26
- private
27
-
28
- def char_with_skin_tone(emoji)
29
- return unless skin_tone_emoji
30
-
31
- skin_variation = emoji.skin_variations && emoji.skin_variations[skin_tone_emoji.unified]
32
- return unless skin_variation
33
-
34
- EmojiDatasource.unified_to_char(skin_variation[:unified])
35
- end
36
-
37
- def skin_tone_emoji
38
- EmojiDatasource.find_by_short_name("skin-tone-#{skin_tone_level}")
39
- end
40
-
41
- def skin_tone_level
42
- skin_tone_matches[2].to_i
43
- end
44
-
45
- def skin_tone_matches
46
- short_name.match(/:?(.+)::skin-tone-(\d+):?/)
47
- end
48
- end
49
- end