emojidex 0.1.0 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (97) hide show
  1. checksums.yaml +4 -4
  2. data/emojidex.gemspec +7 -6
  3. data/lib/emojidex.rb +9 -1
  4. data/lib/emojidex/client.rb +53 -0
  5. data/lib/emojidex/data/categories.rb +42 -42
  6. data/lib/emojidex/data/category.rb +10 -10
  7. data/lib/emojidex/data/collection.rb +39 -21
  8. data/lib/emojidex/data/collection/asset_information.rb +19 -3
  9. data/lib/emojidex/data/collection/cache.rb +28 -33
  10. data/lib/emojidex/data/collection/moji_data.rb +13 -11
  11. data/lib/emojidex/data/collection/static_collection.rb +3 -3
  12. data/lib/emojidex/data/emoji.rb +54 -37
  13. data/lib/emojidex/data/emoji/asset_information.rb +86 -3
  14. data/lib/emojidex/data/extended.rb +2 -2
  15. data/lib/emojidex/data/utf.rb +2 -2
  16. data/lib/emojidex/defaults.rb +9 -1
  17. data/lib/emojidex/env_helper.rb +1 -2
  18. data/lib/emojidex/service/collection.rb +70 -31
  19. data/lib/emojidex/service/error.rb +1 -0
  20. data/lib/emojidex/service/indexes.rb +31 -10
  21. data/lib/emojidex/service/search.rb +5 -4
  22. data/lib/emojidex/service/transactor.rb +35 -19
  23. data/lib/emojidex/service/user.rb +80 -37
  24. data/lib/emojidex/service/user/history_item.rb +23 -0
  25. data/lib/emojidex/{data → util}/collection_checker.rb +2 -3
  26. metadata +10 -79
  27. data/.coveralls.yml +0 -1
  28. data/.gitignore +0 -12
  29. data/.rspec +0 -2
  30. data/.rubocop.yml +0 -18
  31. data/.travis.yml +0 -18
  32. data/Gemfile +0 -18
  33. data/Guardfile +0 -19
  34. data/README.md +0 -73
  35. data/spec/emojidex/data/categories_spec.rb +0 -19
  36. data/spec/emojidex/data/collection_checker_spec.rb +0 -72
  37. data/spec/emojidex/data/collection_spec.rb +0 -136
  38. data/spec/emojidex/data/emoji_spec.rb +0 -47
  39. data/spec/emojidex/data/extended_spec.rb +0 -118
  40. data/spec/emojidex/data/utf_spec.rb +0 -91
  41. data/spec/emojidex/service/collection_spec.rb +0 -20
  42. data/spec/emojidex/service/error_spec.rb +0 -17
  43. data/spec/emojidex/service/indexes_spec.rb +0 -62
  44. data/spec/emojidex/service/search_spec.rb +0 -87
  45. data/spec/emojidex/service/transactor_spec.rb +0 -11
  46. data/spec/emojidex/service/user_spec.rb +0 -128
  47. data/spec/spec_helper.rb +0 -18
  48. data/spec/support/Genshin.svg +0 -126
  49. data/spec/support/couple_kiss/0.svg +0 -177
  50. data/spec/support/couple_kiss/1.svg +0 -177
  51. data/spec/support/couple_kiss/10.svg +0 -293
  52. data/spec/support/couple_kiss/11.svg +0 -235
  53. data/spec/support/couple_kiss/12.svg +0 -235
  54. data/spec/support/couple_kiss/13.svg +0 -235
  55. data/spec/support/couple_kiss/14.svg +0 -177
  56. data/spec/support/couple_kiss/15.svg +0 -177
  57. data/spec/support/couple_kiss/2.svg +0 -177
  58. data/spec/support/couple_kiss/3.svg +0 -235
  59. data/spec/support/couple_kiss/4.svg +0 -235
  60. data/spec/support/couple_kiss/5.svg +0 -235
  61. data/spec/support/couple_kiss/6.svg +0 -293
  62. data/spec/support/couple_kiss/7.svg +0 -235
  63. data/spec/support/couple_kiss/8.svg +0 -235
  64. data/spec/support/couple_kiss/9.svg +0 -235
  65. data/spec/support/couple_kiss/animation.json +0 -14
  66. data/spec/support/fixtures/categories.json +0 -14
  67. data/spec/support/fixtures/category.json +0 -5
  68. data/spec/support/fixtures/emoji.json +0 -28
  69. data/spec/support/fixtures/emoji_detailed.json +0 -41
  70. data/spec/support/fixtures/search_emoji.json +0 -21
  71. data/spec/support/fixtures/single_emoji.json +0 -19
  72. data/spec/support/fixtures/single_emoji_detailed.json +0 -30
  73. data/spec/support/sample_collections/good/emoji.json +0 -30
  74. data/spec/support/sample_collections/good/mouth.svg +0 -49
  75. data/spec/support/sample_collections/good/nut_and_bolt.svg +0 -372
  76. data/spec/support/sample_collections/good/purple_heart.svg +0 -24
  77. data/spec/support/sample_collections/good/px32/mouth.png +0 -0
  78. data/spec/support/sample_collections/good/px32/nut_and_bolt.png +0 -0
  79. data/spec/support/sample_collections/good/px32/purple_heart.png +0 -0
  80. data/spec/support/sample_collections/good/px32/woman_with_bunny_ears.png +0 -0
  81. data/spec/support/sample_collections/good/woman_with_bunny_ears.svg +0 -90
  82. data/spec/support/sample_collections/missing_assets/emoji.json +0 -30
  83. data/spec/support/sample_collections/missing_assets/mouth.svg +0 -49
  84. data/spec/support/sample_collections/missing_assets/purple_heart.svg +0 -24
  85. data/spec/support/sample_collections/missing_assets/px32/mouth.png +0 -0
  86. data/spec/support/sample_collections/missing_assets/px32/nut_and_bolt.png +0 -0
  87. data/spec/support/sample_collections/missing_assets/px32/woman_with_bunny_ears.png +0 -0
  88. data/spec/support/sample_collections/missing_assets/woman_with_bunny_ears.svg +0 -90
  89. data/spec/support/sample_collections/missing_index/emoji.json +0 -23
  90. data/spec/support/sample_collections/missing_index/mouth.svg +0 -49
  91. data/spec/support/sample_collections/missing_index/nut_and_bolt.svg +0 -372
  92. data/spec/support/sample_collections/missing_index/purple_heart.svg +0 -24
  93. data/spec/support/sample_collections/missing_index/px32/mouth.png +0 -0
  94. data/spec/support/sample_collections/missing_index/px32/nut_and_bolt.png +0 -0
  95. data/spec/support/sample_collections/missing_index/px32/purple_heart.png +0 -0
  96. data/spec/support/sample_collections/missing_index/px32/woman_with_bunny_ears.png +0 -0
  97. data/spec/support/sample_collections/missing_index/woman_with_bunny_ears.svg +0 -90
@@ -2,6 +2,7 @@ module Emojidex
2
2
  module Service
3
3
  module Error
4
4
  class Unauthorized < SecurityError; end
5
+ class PaymentRequired < StandardError; end
5
6
  class UnprocessableEntity < StandardError; end
6
7
  class InvalidJSON < StandardError; end
7
8
  end
@@ -8,18 +8,34 @@ module Emojidex
8
8
  class Indexes
9
9
  # Obtain a service Collection of emoji indexed by score.
10
10
  # This is the default index.
11
- def self.emoji(detailed = false, limit = Emojidex::Defaults.limit, page = 1)
12
- Emojidex::Service::Collection.new({endpoint: 'emoji', detailed: detailed, limit: limit, page: page})
11
+ def self.emoji(detailed = false, limit = Emojidex::Defaults.limit, page = 0)
12
+ Emojidex::Service::Collection.new(endpoint: 'emoji', detailed: detailed,
13
+ limit: limit, page: page)
13
14
  end
14
15
 
15
16
  # Obtain a service Collection of emoji indexed by date of creation (or in some cases update).
16
- def self.newest(detailed = false, limit = Emojidex::Defaults.limit, page = 1)
17
- Emojidex::Service::Collection.new({endpoint: 'newest', detailed: detailed, limit: limit, page: page})
17
+ def self.newest(detailed = false, limit = Emojidex::Defaults.limit, page = 0,
18
+ username = nil, auth_token = nil)
19
+ if auth_token.nil?
20
+ username = Emojidex::Client.USER.username
21
+ auth_token = Emojidex::Client.USER.auth_token
22
+ end
23
+ Emojidex::Service::Collection.new(endpoint: 'newest', detailed: detailed,
24
+ limit: limit, page: page,
25
+ username: username, auth_token: auth_token)
18
26
  end
19
27
 
20
- # Obtain a service Collection of emoji indexed by popularity [how many times they have been favorited].
21
- def self.popular(detailed = false, limit = Emojidex::Defaults.limit, page = 1)
22
- Emojidex::Service::Collection.new({endpoint: 'popular', detailed: detailed, limit: limit, page: page})
28
+ # Obtain a service Collection of emoji indexed by popularity
29
+ # [how many times they have been favorited].
30
+ def self.popular(detailed = false, limit = Emojidex::Defaults.limit, page = 0,
31
+ username = nil, auth_token = nil)
32
+ if auth_token.nil?
33
+ username = Emojidex::Client.USER.username
34
+ auth_token = Emojidex::Client.USER.auth_token
35
+ end
36
+ Emojidex::Service::Collection.new(endpoint: 'popular', detailed: detailed,
37
+ limit: limit, page: page,
38
+ username: username, auth_token: auth_token)
23
39
  end
24
40
 
25
41
  # Obtains a hash with three different types of chracter [moji] code indexes:
@@ -31,13 +47,18 @@ module Emojidex
31
47
  # the emoji short codes in the locale [language] specified (defaults to english).
32
48
  def self.moji_codes(locale = Emojidex::Defaults.lang)
33
49
  begin
34
- res = Emojidex::Service::Transactor.get('moji_codes', { locale: locale })
50
+ res = Emojidex::Service::Transactor.get('moji_codes', locale: locale)
35
51
  rescue
36
- return { moji_string: "", moji_array: [], moji_index: {} }
52
+ return { moji_string: '', moji_array: [], moji_index: {} }
37
53
  end
38
- res[:moji_index] = Hash[res[:moji_index].map{ |k, v| [k.to_s, v] }]
54
+ res[:moji_index] = Hash[res[:moji_index].map { |k, v| [k.to_s, v] }]
39
55
  res
40
56
  end
57
+
58
+ def self.user_emoji(username, detailed = false, limit = Emojidex::Defaults.limit, page = 0)
59
+ Emojidex::Service::Collection.new(endpoint: "users/#{username}/emoji", detailed: detailed,
60
+ limit: limit, page: page)
61
+ end
41
62
  end
42
63
  end
43
64
  end
@@ -4,8 +4,8 @@ require_relative '../../emojidex'
4
4
 
5
5
  module Emojidex
6
6
  module Service
7
+ # Search functionality for the emojidex service
7
8
  class Search
8
-
9
9
  # Searches by term with the options given. Options are:
10
10
  # tags: an array of tags to restrict the search to
11
11
  # categories: an arry of categories to restrict the serach to
@@ -16,8 +16,9 @@ module Emojidex
16
16
  opts[:code_cont] = Emojidex.escape_code(code_cont)
17
17
  _do_search(opts)
18
18
  end
19
+
19
20
  def self.search(code_cont, opts = {})
20
- self.term(code_cont, opts)
21
+ term(code_cont, opts)
21
22
  end
22
23
 
23
24
  # Searches for a code starting with the given term.
@@ -62,8 +63,8 @@ module Emojidex
62
63
  private
63
64
 
64
65
  def self._sanitize_opts(opts)
65
- opts[:tags].map! { |tag| tag.to_s } if opts.include? :tags
66
- opts[:categories].map! { |category| category.to_s } if opts.include? :categories
66
+ opts[:tags].map!(&:to_s) if opts.include? :tags
67
+ opts[:categories].map!(&:to_s) if opts.include? :categories
67
68
  opts
68
69
  end
69
70
 
@@ -1,5 +1,6 @@
1
1
  require 'faraday'
2
2
  require 'json'
3
+ require_relative '../../emojidex'
3
4
  require_relative 'error'
4
5
 
5
6
  module Emojidex
@@ -7,6 +8,7 @@ module Emojidex
7
8
  # API transaction utility
8
9
  class Transactor
9
10
  @@connection = nil
11
+ @@retries = 3
10
12
 
11
13
  @@settings = {
12
14
  api: {
@@ -23,40 +25,43 @@ module Emojidex
23
25
  }
24
26
 
25
27
  def self.get(endpoint, params = {})
26
- response = self.connect.get(
27
- "#{self.api_url}#{endpoint}", params)
28
+ response = connect.get(
29
+ "#{api_url}#{endpoint}", params)
28
30
 
29
- self._status_raiser(response)
30
- self._datafy_json(response.body)
31
+ _status_raiser(response)
32
+ _datafy_json(response.body)
31
33
  end
32
34
 
33
35
  def self.post(endpoint, params = {})
34
- response = self.connect.post(
35
- "#{self.api_url}#{endpoint}", params)
36
+ response = connect.post(
37
+ "#{api_url}#{endpoint}", params)
36
38
 
37
- self._status_raiser(response)
38
- self._datafy_json(response.body)
39
+ _status_raiser(response)
40
+ _datafy_json(response.body)
39
41
  end
40
42
 
41
43
  def self.delete(endpoint, params = {})
42
- response = self.connect.delete(
43
- "#{self.api_url}#{endpoint}", params)
44
+ response = connect.delete(
45
+ "#{api_url}#{endpoint}", params)
44
46
 
45
- self._status_raiser(response)
46
- self._datafy_json(response.body)
47
+ _status_raiser(response)
48
+ _datafy_json(response.body)
47
49
  end
48
50
 
49
51
  def self.download(file_subpath)
50
- self.connect.get(URI.escape("#{self.cdn_url}#{file_subpath}"))
52
+ connect.get(URI.escape("#{cdn_url}#{file_subpath.tr(' ', '_')}"))
51
53
  end
52
54
 
53
55
  def self.connect
54
56
  return @@connection if @@connection
55
57
  @@connection = Faraday.new do |conn|
56
58
  conn.request :url_encoded
59
+ conn.request :retry, max: @@retries, interval: 0.05, interval_randomness: 0.05,
60
+ backoff_factor: 2
57
61
  # conn.response :logger
58
62
  conn.adapter Faraday.default_adapter
59
63
  end
64
+ _kludge_windows if Gem.win_platform?
60
65
  @@connection
61
66
  end
62
67
 
@@ -75,26 +80,37 @@ module Emojidex
75
80
  when 200..299
76
81
  return # don't raise
77
82
  when 401
78
- raise Error::Unauthorized.new(self._extract_status_line(response))
83
+ fail Error::Unauthorized, _extract_status_line(response)
84
+ when 402
85
+ fail Error::PaymentRequired, _extract_status_line(response)
79
86
  when 422
80
- raise Error::UnprocessableEntity.new(self._extract_status_line(response))
87
+ fail Error::UnprocessableEntity _extract_status_line(response)
81
88
  end
82
89
  end
83
90
 
84
91
  def self._extract_status_line(response)
85
- data = self._datafy_json(response.body)
86
- status_line = (data.key?(:status) ? data[:status] : '')
87
- status_line
92
+ data = _datafy_json(response.body)
93
+ status_line = (data.key?(:status) ? data[:status] : '')
94
+ status_line
88
95
  end
89
96
 
90
97
  def self._datafy_json(body)
91
98
  begin
92
99
  data = JSON.parse(body, symbolize_names: true)
93
100
  rescue JSON::ParserError
94
- raise Error::InvalidJSON.new('could not parse JSON')
101
+ raise Error::InvalidJSON, 'could not parse JSON'
95
102
  end
96
103
  data
97
104
  end
105
+
106
+ def self._kludge_windows
107
+ cert_loc = "#{__dir__}/cacert.pem"
108
+ unless File.exist? cert_loc
109
+ response = @@connection.get('http://curl.haxx.se/ca/cacert.pem')
110
+ File.open(cert_loc, 'wb') { |fp| fp.write(response.body) }
111
+ end
112
+ ENV['SSL_CERT_FILE'] = cert_loc
113
+ end
98
114
  end
99
115
  end
100
116
  end
@@ -2,13 +2,14 @@ require_relative '../../emojidex'
2
2
  require_relative 'error'
3
3
  require_relative 'transactor'
4
4
  require_relative 'collection'
5
+ require_relative './user/history_item'
5
6
 
6
7
  module Emojidex
7
8
  module Service
8
9
  # User auth and user details
9
10
  class User
10
11
  attr_reader :username, :auth_token, :premium, :pro, :premium_exp, :pro_exp, :status
11
- attr_accessor :favorites, :history, :cache_path
12
+ attr_accessor :favorites, :history, :history_page, :cache_path
12
13
 
13
14
  @@auth_status_codes = { none: false, failure: false,
14
15
  unverified: false, verified: true,
@@ -21,13 +22,19 @@ module Emojidex
21
22
  clear_auth_data
22
23
  @status = :none
23
24
  @history = []
25
+ @history_page = 0
24
26
  @favorites = Emojidex::Data::Collection.new
27
+ if opts.key?(:cache_path)
28
+ load(opts[:cache_path])
29
+ elsif opts[:load_cache] == true
30
+ load
31
+ end
25
32
  end
26
33
 
27
34
  def login(user, password, sync_on_login = true)
28
35
  begin
29
36
  auth_response = Transactor.get('users/authenticate',
30
- { user: user, password: password })
37
+ user: user, password: password)
31
38
  rescue Error::Unauthorized
32
39
  @status = :unverified
33
40
  return false
@@ -44,7 +51,7 @@ module Emojidex
44
51
  def authorize(username, auth_token, sync_on_auth = true)
45
52
  begin
46
53
  auth_response = Transactor.get('users/authenticate',
47
- { username: username, token: auth_token })
54
+ username: username, token: auth_token)
48
55
  rescue Error::Unauthorized
49
56
  @status = :unverified
50
57
  return false
@@ -67,8 +74,8 @@ module Emojidex
67
74
 
68
75
  begin
69
76
  res = Emojidex::Service::Collection.new(
70
- { endpoint: 'users/favorites', limit: limit, detailed: detailed,
71
- username: @username, auth_token: @auth_token })
77
+ endpoint: 'users/favorites', limit: limit, detailed: detailed,
78
+ username: @username, auth_token: @auth_token)
72
79
  rescue Error::Unauthorized
73
80
  return false
74
81
  end
@@ -82,18 +89,14 @@ module Emojidex
82
89
 
83
90
  begin
84
91
  res = Transactor.post('users/favorites',
85
- { username: @username, auth_token: @auth_token,
86
- emoji_code: Emojidex.escape_code(code) })
92
+ username: @username, auth_token: @auth_token,
93
+ emoji_code: Emojidex.escape_code(code))
87
94
  rescue Error::Unauthorized
88
95
  return false
89
- rescue Error::UnprocessableEntity => e
90
- # TODO: API is currently returning this both when emoji already registered
91
- # and when code is invalid. When already registerd it will return 202 on
92
- # next update
93
- return true if e.message == 'emoji already in user favorites'
94
- return false
95
96
  end
96
- return true
97
+ return false if res.include?(:status) && res[:status] == 'emoji already in user favorites'
98
+ @favorites.add_emoji([res])
99
+ true
97
100
  end
98
101
 
99
102
  def remove_favorite(code)
@@ -101,31 +104,48 @@ module Emojidex
101
104
 
102
105
  begin
103
106
  res = Transactor.delete('users/favorites',
104
- { username: @username, auth_token: @auth_token,
105
- emoji_code: Emojidex.escape_code(code) })
107
+ username: @username, auth_token: @auth_token,
108
+ emoji_code: Emojidex.escape_code(code))
106
109
  rescue Error::Unauthorized
107
110
  return false
108
- rescue Error::UnprocessableEntity => e
109
- # TODO: API is currently returning this both when emoji already registered
110
- # and when code is invalid. When already registerd it will return 200 on
111
- # next update
112
- return true if e.message == 'emoji not in user favorites'
113
- return false
114
111
  end
112
+ return false if res.include?(:status) && res[:status] == 'emoji not in user favorites'
113
+ @favorites.remove_emoji(code.to_sym)
115
114
  true
116
115
  end
117
116
 
118
- def sync_history(limit = Emojidex::Defaults.limit, page = 1)
117
+ def sync_history(limit = Emojidex::Defaults.limit, page = 0)
119
118
  return false unless authorized?
120
119
 
121
- @history = Transactor.get('users/history',
122
- { limit: limit, page: page, username: @username, auth_token: @auth_token })
123
- # TODO: this is a temporary implementation of history. It will be revised after an
124
- # API update.
120
+ page = @history_page + 1 if page == 0
121
+
122
+ begin
123
+ result = Transactor.get('users/history',
124
+ limit: limit, page: page,
125
+ username: @username, auth_token: @auth_token)
126
+ rescue
127
+ return false
128
+ end
129
+
130
+ return false unless (result.key?(:history) && result.key?(:meta))
131
+ @history_page = result[:meta][:page]
132
+ _merge_history(result[:history])
125
133
  true
126
134
  end
127
135
 
128
136
  def add_history(code)
137
+ return false unless authorized?
138
+
139
+ begin
140
+ result = Transactor.post('users/history',
141
+ username: @username, auth_token: @auth_token,
142
+ emoji_code: Emojidex.escape_code(code))
143
+ rescue
144
+ return false
145
+ end
146
+
147
+ _push_and_dedupe_history(result)
148
+ true
129
149
  end
130
150
 
131
151
  def clear_auth_data()
@@ -137,19 +157,17 @@ module Emojidex
137
157
  end
138
158
 
139
159
  def sync
140
- authorize(@username, @auth_token) &&
141
- sync_favorites &&
142
- sync_history
160
+ authorize(@username, @auth_token) && sync_favorites && sync_history
143
161
  end
144
162
 
145
- def save(path)
163
+ def save(path = nil)
146
164
  _set_cache_path(path)
147
165
  _save_user
148
166
  _save_favorites
149
167
  _save_history
150
168
  end
151
169
 
152
- def load(path, auto_sync = true)
170
+ def load(path = nil, auto_sync = true)
153
171
  _set_cache_path(path)
154
172
  _load_user
155
173
  _load_favorites
@@ -183,8 +201,7 @@ module Emojidex
183
201
  end
184
202
 
185
203
  def _set_cache_path(path)
186
- @cache_path = @cache_path || File.expand_path(
187
- path || ENV['EMOJI_CACHE'] || "#{ENV['HOME']}/.emojidex/")
204
+ @cache_path ||= File.expand_path(path || Emojidex::Defaults.system_cache_path)
188
205
  FileUtils.mkdir_p(@cache_path)
189
206
  @cache_path
190
207
  end
@@ -192,13 +209,15 @@ module Emojidex
192
209
  def _save_user
193
210
  user_info = { username: username, auth_token: auth_token,
194
211
  premium: premium, pro: pro,
195
- premium_exp: premium_exp, pro_exp: pro_exp
212
+ premium_exp: premium_exp, pro_exp: pro_exp
196
213
  }
197
214
  File.open("#{@cache_path}/user.json", 'w') { |f| f.write user_info.to_json }
198
215
  end
199
216
 
200
217
  def _save_favorites
201
- File.open("#{@cache_path}/favorites.json", 'w') { |f| f.write @favorites.emoji.values.to_json }
218
+ File.open("#{@cache_path}/favorites.json", 'w') do |f|
219
+ f.write @favorites.emoji.values.to_json
220
+ end
202
221
  end
203
222
 
204
223
  def _save_history
@@ -206,6 +225,7 @@ module Emojidex
206
225
  end
207
226
 
208
227
  def _load_user
228
+ _save_user unless File.exist? "#{@cache_path}/user.json"
209
229
  json = IO.read("#{@cache_path}/user.json")
210
230
  user_info = JSON.parse(json, symbolize_names: true)
211
231
  @username = user_info[:username]
@@ -218,6 +238,7 @@ module Emojidex
218
238
  end
219
239
 
220
240
  def _load_favorites
241
+ _save_favorites unless File.exist? "#{@cache_path}/favorites.json"
221
242
  json = IO.read("#{@cache_path}/favorites.json")
222
243
  @favorites = Emojidex::Service::Collection.new(
223
244
  emoji: JSON.parse(json, symbolize_names: true),
@@ -225,8 +246,30 @@ module Emojidex
225
246
  end
226
247
 
227
248
  def _load_history
249
+ _save_history unless File.exist? "#{@cache_path}/history.json"
228
250
  json = IO.read("#{@cache_path}/history.json")
229
- @history = JSON.parse json
251
+ items = JSON.parse(json, symbolize_names: true)
252
+ @history = []
253
+ items.each { |item| @history << Emojidex::Service::HistoryItem.new(item[:emoji_code],
254
+ item[:times_used],
255
+ item[:last_used]) }
256
+ end
257
+
258
+ def _merge_history(history_delta = [])
259
+ history_delta.each do |item|
260
+ _push_and_dedupe_history(item)
261
+ end
262
+ _sort_history
263
+ end
264
+
265
+ def _push_and_dedupe_history(item)
266
+ @history.delete_if { |hi| hi.emoji_code == item[:emoji_code] }
267
+ @history.unshift Emojidex::Service::HistoryItem.new(item[:emoji_code],
268
+ item[:times_used], item[:last_used])
269
+ end
270
+
271
+ def _sort_history
272
+ @history.sort_by! {|h| -h.last_used.to_i}
230
273
  end
231
274
  end
232
275
  end