emojidex 0.1.0 → 0.2.0

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.
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