Skip to content

Loading…

Add API for adding new / editing existing emoji #49

Merged
merged 4 commits into from

4 participants

@mislav
GitHub member
  • Emoji.create(raw) => yields to block
  • Emoji.edit_emoji(emoji) => yields to block

The block forms are so that the list of aliases & unicode_aliases is re-indexed after the update.

Supersedes #45

/cc @josh @aroben @djfpaagman

@josh josh commented on an outdated diff
lib/emoji.rb
@@ -15,7 +15,36 @@ def images_path
end
def all
- @all ||= parse_data_file
+ return @all if defined? @all
+ @all = []
+ parse_data_file
+ @all
+ end
+
+ # Public: Initialize an Emoji::Character instance and yield it to the block.
+ # The character is added to the `Emoji.all` set.
+ def create(raw)
+ emoji = Emoji::Character.new(raw)
+ self.all << edit_emoji(emoji) { yield emoji }
@josh GitHub member
josh added a note

Should create remove the existing emoji if you call it twice?

@mislav GitHub member
mislav added a note

Not sure what you mean. Creating the emoji for the same Unicode representation multiple times? It won't remove the previous Emoji::Character instance from the list, but then again I'm not sure if we need such behavior at all.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
mislav added some commits
@mislav mislav Add API for adding new / editing existing emoji
- Emoji.create(raw) => yields to block
- Emoji.edit_emoji(emoji) => yields to block

The block forms are so that the list of aliases & unicode_aliases is
re-indexed after the update.
7477247
@mislav mislav Add documentation note about images for added emoji
It comes down to: if you add new emoji, you must add images for them as
well. That's the whole point.
65031cf
@mislav
GitHub member

Ping for more reviews. Suggestions for a better API are welcome. This is the last thing blocking the next major release.

@djfpaagman

Looks good to me. Definitely more versatile than my solution, so I'll close that pull.

For my use case the 'unicode first' approach seems a bit complex, for a simple custom emoji called 'foo' you need this code:

Emoji.create(nil) do |char|
  char.add_alias "foo"
 end

If you could set the alias instead of the unicode character something like this could suffice:

Emoji.create("foo")

But I guess the approach with unicode is central to the whole gem right?

@mislav
GitHub member
@aroben
GitHub member

I think @djfpaagman raises a good point though. Actually two good points:

  1. It seems far more likely that someone will add a new emoji that has no Unicode equivalent than the opposite. After all, we update the gem to include all supported Unicode emoji, so there shouldn't be many situations where someone needs to add a new one.
  2. In the long term, hopefully this gem can get out of the business of Unicode emoji entirely and only support custom emoji. Once enough browsers support displaying emoji natively, there won't be any need to serve images for Unicode emoji, but we'll still need to serve images for custom emoji.

Both of these make me think that the API should be geared toward adding non-Unicode emoji (while still making it possible to add new Unicode emoji, of course).

@mislav mislav Change emoji creation API to take name as 1st argument
It's more likely that someone will want to add a new emoji with a custom
image rather than a character that has a Unicode representation.

Also move the VARIATION_SELECTOR_16 logic outside of Emoji::Character
since it doesn't need to be concerned with it.
3935375
@mislav
GitHub member

It seems far more likely that someone will add a new emoji that has no Unicode equivalent than the opposite

@aroben OK, I've opdated the API so that the 1st argument is the name value, so that the API is optimized for creating custom emoji first and Unicode emoji second.

@djfpaagman OK, Emoji.create now works even without a block form.

In the long term, hopefully this gem can get out of the business of Unicode emoji entirely and only support custom emoji

Yeah but once we reach that stage, gemoji library won't be necessary at all.

@mislav mislav merged commit 3b65ee2 into master

1 check passed

Details continuous-integration/travis-ci The Travis CI build passed
@mislav mislav deleted the edit-emoji branch
@djfpaagman

Awesome :tada: thanks for the work :+1:

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Commits on Jun 27, 2014
  1. @mislav

    Add API for adding new / editing existing emoji

    mislav committed
    - Emoji.create(raw) => yields to block
    - Emoji.edit_emoji(emoji) => yields to block
    
    The block forms are so that the list of aliases & unicode_aliases is
    re-indexed after the update.
  2. @mislav

    Add documentation note about images for added emoji

    mislav committed
    It comes down to: if you add new emoji, you must add images for them as
    well. That's the whole point.
Commits on Jul 3, 2014
  1. @mislav

    Change emoji creation API to take name as 1st argument

    mislav committed
    It's more likely that someone will want to add a new emoji with a custom
    image rather than a character that has a Unicode representation.
    
    Also move the VARIATION_SELECTOR_16 logic outside of Emoji::Character
    since it doesn't need to be concerned with it.
Commits on Jul 4, 2014
  1. @mislav
This page is out of date. Refresh to see the latest.
Showing with 161 additions and 46 deletions.
  1. +43 −0 README.md
  2. +5 −4 db/dump.rb
  3. +49 −26 lib/emoji.rb
  4. +4 −16 lib/emoji/character.rb
  5. +60 −0 test/emoji_test.rb
View
43 README.md
@@ -78,3 +78,46 @@ Translate emoji names to unicode and vice versa.
>> Emoji.find_by_unicode("\u{1f431}").name
=> "cat"
```
+
+Adding new emoji
+----------------
+
+You can add new emoji characters to the `Emoji.all` list:
+
+```ruby
+emoji = Emoji.create("music") do |char|
+ char.add_alias "song"
+ char.add_unicode_alias "\u{266b}"
+ char.add_tag "notes"
+end
+
+emoji.name #=> "music"
+emoji.raw #=> "♫"
+emoji.image_filename #=> "unicode/266b.png"
+
+# Creating custom emoji (no Unicode aliases):
+emoji = Emoji.create("music") do |char|
+ char.add_tag "notes"
+end
+
+emoji.custom? #=> true
+emoji.image_filename #=> "music.png"
+```
+
+As you create new emoji, you must ensure that you also create and put the images
+they reference by their `image_filename` to your assets directory.
+
+For existing emojis, you can edit the list of aliases or add new tags in an edit block:
+
+```ruby
+emoji = Emoji.find_by_alias "musical_note"
+
+Emoji.edit_emoji(emoji) do |char|
+ char.add_alias "music"
+ char.add_unicode_alias "\u{266b}"
+ char.add_tag "notes"
+end
+
+Emoji.find_by_alias "music" #=> emoji
+Emoji.find_by_unicode "\u{266b}" #=> emoji
+```
View
9 db/dump.rb
@@ -52,17 +52,18 @@ def add_reference(code)
trap(:PIPE) { abort }
items = []
+variation = Emoji::VARIATION_SELECTOR_16
for emoji in Emoji.all
- unicodes = emoji.unicode_aliases
- unicodes = unicodes[1..-1] if emoji.variation?
+ unicodes = emoji.unicode_aliases.dup
item = {}
unless emoji.custom?
- variation_codepoint = Emoji::Character::VARIATION_SELECTOR_16.codepoints[0]
+ variation_codepoint = variation.codepoints[0]
chars = emoji.raw.codepoints.map { |code| UnicodeCharacter.fetch(code) unless code == variation_codepoint }.compact
- item[:emoji] = emoji.raw
+ unicodes.select { |u| u.index(variation) }.each { |u| unicodes.delete(u.sub(variation, '')) }
+ item[:emoji] = unicodes.shift
item[:unicodes] = unicodes if unicodes.any?
item[:description] = chars.map(&:description).join(' + ')
end
View
75 lib/emoji.rb
@@ -15,7 +15,36 @@ def images_path
end
def all
- @all ||= parse_data_file
+ return @all if defined? @all
+ @all = []
+ parse_data_file
+ @all
+ end
+
+ # Public: Initialize an Emoji::Character instance and yield it to the block.
+ # The character is added to the `Emoji.all` set.
+ def create(name)
+ emoji = Emoji::Character.new(name)
+ self.all << edit_emoji(emoji) { yield emoji if block_given? }
+ emoji
+ end
+
+ # Public: Yield an emoji to the block and update the indices in case its
+ # aliases or unicode_aliases lists changed.
+ def edit_emoji(emoji)
+ @names_index ||= Hash.new
+ @unicodes_index ||= Hash.new
+
+ yield emoji
+
+ emoji.aliases.each do |name|
+ @names_index[name] = emoji
+ end
+ emoji.unicode_aliases.each do |unicode|
+ @unicodes_index[unicode] = emoji
+ end
+
+ emoji
end
def find_by_alias(name)
@@ -35,40 +64,34 @@ def find_by_unicode(unicode)
end
private
- def create_index
- index = Hash.new { |hash, key| hash[key] = [] }
- yield index
- index
- end
+ VARIATION_SELECTOR_16 = "\u{fe0f}".freeze
def parse_data_file
raw = File.open(data_file, 'r:UTF-8') { |data| JSON.parse(data.read) }
- raw.map do |raw_emoji|
- char = Emoji::Character.new(raw_emoji['emoji'])
- raw_emoji.fetch('aliases').each { |name| char.add_alias(name) }
- raw_emoji.fetch('unicodes', []).each { |uni| char.add_unicode_alias(uni) }
- raw_emoji.fetch('tags').each { |tag| char.add_tag(tag) }
- char
+ raw.each do |raw_emoji|
+ self.create(nil) do |emoji|
+ raw_emoji.fetch('aliases').each { |name| emoji.add_alias(name) }
+ unicodes = Array(raw_emoji['emoji']) + raw_emoji.fetch('unicodes', [])
+ unicodes.each { |uni|
+ emoji.add_unicode_alias(uni)
+ # Automatically add a representation of this emoji without the variation
+ # selector to unicode aliases:
+ if uni.index(VARIATION_SELECTOR_16)
+ emoji.add_unicode_alias(uni.sub(VARIATION_SELECTOR_16, ''))
+ end
+ }
+ raw_emoji.fetch('tags').each { |tag| emoji.add_tag(tag) }
+ end
end
end
def names_index
- @names_index ||= create_index do |mapping|
- all.each do |emoji|
- emoji.aliases.each do |name|
- mapping[name] = emoji
- end
- end
- end
+ all unless defined? @all
+ @names_index
end
def unicodes_index
- @unicodes_index ||= create_index do |mapping|
- all.each do |emoji|
- emoji.unicode_aliases.each do |unicode|
- mapping[unicode] = emoji
- end
- end
- end
+ all unless defined? @all
+ @unicodes_index
end
end
View
20 lib/emoji/character.rb
@@ -1,7 +1,5 @@
module Emoji
class Character
- VARIATION_SELECTOR_16 = "\u{fe0f}".freeze
-
# Inspect individual Unicode characters in a string by dumping its
# codepoints in hexadecimal format.
def self.hex_inspect(str)
@@ -20,9 +18,7 @@ def add_alias(name)
aliases << name
end
- # A list of Unicode strings that uniquely refer to this emoji. By default,
- # this list includes the emoji's Unicode representation without the
- # variation selector character.
+ # A list of Unicode strings that uniquely refer to this emoji.
attr_reader :unicode_aliases
# Raw Unicode string for an emoji. Nil if emoji is non-standard.
@@ -40,14 +36,10 @@ def add_tag(tag)
tags << tag
end
- def initialize(raw)
- @aliases = []
- @unicode_aliases = Array(raw)
+ def initialize(name)
+ @aliases = Array(name)
+ @unicode_aliases = []
@tags = []
-
- # Automatically add a representation of this emoji without the variation
- # selector to unicode aliases:
- add_unicode_alias(raw.sub(VARIATION_SELECTOR_16, '')) if variation?
end
def inspect
@@ -55,10 +47,6 @@ def inspect
%(#<#{self.class.name}:#{name}#{hex}>)
end
- def variation?
- !custom? && raw.index(VARIATION_SELECTOR_16)
- end
-
def hex_inspect
self.class.hex_inspect(raw)
end
View
60 test/emoji_test.rb
@@ -84,4 +84,64 @@ class EmojiTest < TestCase
assert custom_names.include?("shipit")
assert !custom_names.include?("+1")
end
+
+ test "create" do
+ emoji = Emoji.create("music") do |char|
+ char.add_unicode_alias "\u{266b}"
+ char.add_unicode_alias "\u{266a}"
+ char.add_tag "notes"
+ char.add_tag "eighth"
+ end
+
+ begin
+ assert_equal emoji, Emoji.all.last
+ assert_equal emoji, Emoji.find_by_alias("music")
+ assert_equal emoji, Emoji.find_by_unicode("\u{266a}")
+ assert_equal emoji, Emoji.find_by_unicode("\u{266b}")
+
+ assert_equal "\u{266b}", emoji.raw
+ assert_equal "unicode/266b.png", emoji.image_filename
+ assert_equal %w[music], emoji.aliases
+ assert_equal %w[notes eighth], emoji.tags
+ ensure
+ Emoji.all.pop
+ end
+ end
+
+ test "create without block" do
+ emoji = Emoji.create("music")
+
+ begin
+ assert_equal emoji, Emoji.find_by_alias("music")
+ assert_equal [], emoji.unicode_aliases
+ assert_equal [], emoji.tags
+ assert_equal "music.png", emoji.image_filename
+ ensure
+ Emoji.all.pop
+ end
+ end
+
+ test "edit" do
+ emoji = Emoji.find_by_alias("weary")
+
+ emoji = Emoji.edit_emoji(emoji) do |char|
+ char.add_alias "whining"
+ char.add_unicode_alias "\u{1f629}\u{266a}"
+ char.add_tag "complaining"
+ end
+
+ begin
+ assert_equal emoji, Emoji.find_by_alias("weary")
+ assert_equal emoji, Emoji.find_by_alias("whining")
+ assert_equal emoji, Emoji.find_by_unicode("\u{1f629}")
+ assert_equal emoji, Emoji.find_by_unicode("\u{1f629}\u{266a}")
+
+ assert_equal %w[weary whining], emoji.aliases
+ assert_includes emoji.tags, "complaining"
+ ensure
+ emoji.aliases.pop
+ emoji.unicode_aliases.pop
+ emoji.tags.pop
+ end
+ end
end
Something went wrong with that request. Please try again.