emojidex 0.0.23 → 0.1.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 (60) hide show
  1. checksums.yaml +4 -4
  2. data/.coveralls.yml +1 -0
  3. data/.rubocop.yml +3 -0
  4. data/.travis.yml +7 -6
  5. data/Gemfile +0 -2
  6. data/Guardfile +17 -8
  7. data/README.md +54 -17
  8. data/emojidex.gemspec +4 -1
  9. data/lib/emojidex.rb +12 -10
  10. data/lib/emojidex/{categories.rb → data/categories.rb} +3 -4
  11. data/lib/emojidex/{category.rb → data/category.rb} +1 -1
  12. data/lib/emojidex/data/collection.rb +158 -0
  13. data/lib/emojidex/data/collection/asset_information.rb +52 -0
  14. data/lib/emojidex/data/collection/cache.rb +137 -0
  15. data/lib/emojidex/{collection → data/collection}/moji_data.rb +1 -1
  16. data/lib/emojidex/data/collection/static_collection.rb +35 -0
  17. data/lib/emojidex/data/collection_checker.rb +94 -0
  18. data/lib/emojidex/{emoji.rb → data/emoji.rb} +3 -2
  19. data/lib/emojidex/data/emoji/asset_information.rb +45 -0
  20. data/lib/emojidex/data/extended.rb +18 -0
  21. data/lib/emojidex/data/utf.rb +18 -0
  22. data/lib/emojidex/defaults.rb +21 -0
  23. data/lib/emojidex/env_helper.rb +11 -0
  24. data/lib/emojidex/service/collection.rb +67 -0
  25. data/lib/emojidex/service/error.rb +9 -0
  26. data/lib/emojidex/service/indexes.rb +43 -0
  27. data/lib/emojidex/service/search.rb +82 -0
  28. data/lib/emojidex/service/transactor.rb +100 -0
  29. data/lib/emojidex/service/user.rb +233 -0
  30. data/spec/{categories_spec.rb → emojidex/data/categories_spec.rb} +4 -3
  31. data/spec/{collection_checker_spec.rb → emojidex/data/collection_checker_spec.rb} +12 -15
  32. data/spec/{collection_spec.rb → emojidex/data/collection_spec.rb} +40 -23
  33. data/spec/{emoji_spec.rb → emojidex/data/emoji_spec.rb} +2 -2
  34. data/spec/{extended_spec.rb → emojidex/data/extended_spec.rb} +21 -10
  35. data/spec/{utf_spec.rb → emojidex/data/utf_spec.rb} +22 -17
  36. data/spec/emojidex/service/collection_spec.rb +20 -0
  37. data/spec/emojidex/service/error_spec.rb +17 -0
  38. data/spec/emojidex/service/indexes_spec.rb +62 -0
  39. data/spec/emojidex/service/search_spec.rb +87 -0
  40. data/spec/emojidex/service/transactor_spec.rb +11 -0
  41. data/spec/emojidex/service/user_spec.rb +128 -0
  42. data/spec/spec_helper.rb +9 -62
  43. metadata +36 -31
  44. data/lib/emojidex/api/categories.rb +0 -16
  45. data/lib/emojidex/api/emoji.rb +0 -26
  46. data/lib/emojidex/api/search/emoji.rb +0 -16
  47. data/lib/emojidex/client.rb +0 -60
  48. data/lib/emojidex/collection.rb +0 -156
  49. data/lib/emojidex/collection/asset_information.rb +0 -49
  50. data/lib/emojidex/collection/cache.rb +0 -78
  51. data/lib/emojidex/collection_checker.rb +0 -93
  52. data/lib/emojidex/emoji/asset_information.rb +0 -20
  53. data/lib/emojidex/error.rb +0 -15
  54. data/lib/emojidex/extended.rb +0 -19
  55. data/lib/emojidex/service.rb +0 -32
  56. data/lib/emojidex/utf.rb +0 -19
  57. data/spec/api/categories_spec.rb +0 -49
  58. data/spec/api/emoji_spec.rb +0 -89
  59. data/spec/api/search/emoji_spec.rb +0 -30
  60. data/spec/client_spec.rb +0 -24
@@ -0,0 +1,9 @@
1
+ module Emojidex
2
+ module Service
3
+ module Error
4
+ class Unauthorized < SecurityError; end
5
+ class UnprocessableEntity < StandardError; end
6
+ class InvalidJSON < StandardError; end
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,43 @@
1
+ require_relative 'transactor'
2
+ require_relative 'collection'
3
+ require_relative '../defaults'
4
+
5
+ module Emojidex
6
+ module Service
7
+ # emoji indexes
8
+ class Indexes
9
+ # Obtain a service Collection of emoji indexed by score.
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})
13
+ end
14
+
15
+ # 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})
18
+ end
19
+
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})
23
+ end
24
+
25
+ # Obtains a hash with three different types of chracter [moji] code indexes:
26
+ # moji_string: a string that can be used for things like regex matches.
27
+ # Contains conglomorate codes ahead of single chracter codes.
28
+ # moji_array: an array of emoji characters.
29
+ # Contains conglomorate codes ahead of single chracter codes.
30
+ # moji_index: a hash map with the keys being emoji strings and the values being
31
+ # the emoji short codes in the locale [language] specified (defaults to english).
32
+ def self.moji_codes(locale = Emojidex::Defaults.lang)
33
+ begin
34
+ res = Emojidex::Service::Transactor.get('moji_codes', { locale: locale })
35
+ rescue
36
+ return { moji_string: "", moji_array: [], moji_index: {} }
37
+ end
38
+ res[:moji_index] = Hash[res[:moji_index].map{ |k, v| [k.to_s, v] }]
39
+ res
40
+ end
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,82 @@
1
+ require_relative '../defaults'
2
+ require_relative 'collection'
3
+ require_relative '../../emojidex'
4
+
5
+ module Emojidex
6
+ module Service
7
+ class Search
8
+
9
+ # Searches by term with the options given. Options are:
10
+ # tags: an array of tags to restrict the search to
11
+ # categories: an arry of categories to restrict the serach to
12
+ # detailed: set to true to enable detailed results (defaults to false)
13
+ # limit: sets the number of items per page (default Emojidex::Defaults.limit)
14
+ # Returns a service Collection.
15
+ def self.term(code_cont, opts = {})
16
+ opts[:code_cont] = Emojidex.escape_code(code_cont)
17
+ _do_search(opts)
18
+ end
19
+ def self.search(code_cont, opts = {})
20
+ self.term(code_cont, opts)
21
+ end
22
+
23
+ # Searches for a code starting with the given term.
24
+ # Available options are the same as term.
25
+ # Returns a service Collection.
26
+ def self.starting(code_sw, opts = {})
27
+ opts[:code_sw] = Emojidex.escape_code(code_sw)
28
+ _do_search(opts)
29
+ end
30
+
31
+ # Searches for a code ending with the given term.
32
+ # Available options are the same as term.
33
+ # Returns a service Collection.
34
+ def self.ending(code_ew, opts = {})
35
+ opts[:code_ew] = Emojidex.escape_code(code_ew)
36
+ _do_search(opts)
37
+ end
38
+
39
+ # Searches an array of tags for emoji associated with all those tags.
40
+ # Available options are the same as term.
41
+ # Returns a service Collection.
42
+ def self.tags(tags, opts = {})
43
+ tags = [] << tags unless tags.class == Array
44
+ opts[:tags] = tags
45
+ _do_search(opts)
46
+ end
47
+
48
+ # An expanded version of term with categories and tags as arguments.
49
+ # Options are:
50
+ # detailed: set to true to enable detailed results (defaults to false)
51
+ # limit: sets the number of items per page (default Emojidex::Defaults.limit)
52
+ # Returns a service Collection.
53
+ def self.advanced(code_cont, categories = [], tags = [], opts = {})
54
+ opts[:code_cont] = Emojidex.escape_code(code_cont)
55
+ tags = [] << tags unless tags.class == Array
56
+ opts[:tags] = tags
57
+ categories = [] << categories unless categories.class == Array
58
+ opts[:categories] = categories
59
+ _do_search(opts)
60
+ end
61
+
62
+ private
63
+
64
+ 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
67
+ opts
68
+ end
69
+
70
+ def self._do_search(opts)
71
+ opts = _sanitize_opts(opts)
72
+ opts[:endpoint] = 'search/emoji'
73
+ begin
74
+ col = Emojidex::Service::Collection.new(opts)
75
+ rescue
76
+ return Emojidex::Service::Collection.new
77
+ end
78
+ col
79
+ end
80
+ end
81
+ end
82
+ end
@@ -0,0 +1,100 @@
1
+ require 'faraday'
2
+ require 'json'
3
+ require_relative 'error'
4
+
5
+ module Emojidex
6
+ module Service
7
+ # API transaction utility
8
+ class Transactor
9
+ @@connection = nil
10
+
11
+ @@settings = {
12
+ api: {
13
+ host: 'www.emojidex.com',
14
+ prefix: '/api/v1/',
15
+ protocol: 'https'
16
+ },
17
+ cdn: {
18
+ host: 'cdn.emojidex.com',
19
+ prefix: '/emoji/',
20
+ protocol: 'http'
21
+ },
22
+ closed_net: false
23
+ }
24
+
25
+ def self.get(endpoint, params = {})
26
+ response = self.connect.get(
27
+ "#{self.api_url}#{endpoint}", params)
28
+
29
+ self._status_raiser(response)
30
+ self._datafy_json(response.body)
31
+ end
32
+
33
+ def self.post(endpoint, params = {})
34
+ response = self.connect.post(
35
+ "#{self.api_url}#{endpoint}", params)
36
+
37
+ self._status_raiser(response)
38
+ self._datafy_json(response.body)
39
+ end
40
+
41
+ def self.delete(endpoint, params = {})
42
+ response = self.connect.delete(
43
+ "#{self.api_url}#{endpoint}", params)
44
+
45
+ self._status_raiser(response)
46
+ self._datafy_json(response.body)
47
+ end
48
+
49
+ def self.download(file_subpath)
50
+ self.connect.get(URI.escape("#{self.cdn_url}#{file_subpath}"))
51
+ end
52
+
53
+ def self.connect
54
+ return @@connection if @@connection
55
+ @@connection = Faraday.new do |conn|
56
+ conn.request :url_encoded
57
+ # conn.response :logger
58
+ conn.adapter Faraday.default_adapter
59
+ end
60
+ @@connection
61
+ end
62
+
63
+ def self.api_url()
64
+ "#{@@settings[:api][:protocol]}://#{@@settings[:api][:host]}#{@@settings[:api][:prefix]}"
65
+ end
66
+
67
+ def self.cdn_url()
68
+ "#{@@settings[:cdn][:protocol]}://#{@@settings[:cdn][:host]}#{@@settings[:cdn][:prefix]}"
69
+ end
70
+
71
+ private
72
+
73
+ def self._status_raiser(response)
74
+ case response.status
75
+ when 200..299
76
+ return # don't raise
77
+ when 401
78
+ raise Error::Unauthorized.new(self._extract_status_line(response))
79
+ when 422
80
+ raise Error::UnprocessableEntity.new(self._extract_status_line(response))
81
+ end
82
+ end
83
+
84
+ def self._extract_status_line(response)
85
+ data = self._datafy_json(response.body)
86
+ status_line = (data.key?(:status) ? data[:status] : '')
87
+ status_line
88
+ end
89
+
90
+ def self._datafy_json(body)
91
+ begin
92
+ data = JSON.parse(body, symbolize_names: true)
93
+ rescue JSON::ParserError
94
+ raise Error::InvalidJSON.new('could not parse JSON')
95
+ end
96
+ data
97
+ end
98
+ end
99
+ end
100
+ end
@@ -0,0 +1,233 @@
1
+ require_relative '../../emojidex'
2
+ require_relative 'error'
3
+ require_relative 'transactor'
4
+ require_relative 'collection'
5
+
6
+ module Emojidex
7
+ module Service
8
+ # User auth and user details
9
+ class User
10
+ attr_reader :username, :auth_token, :premium, :pro, :premium_exp, :pro_exp, :status
11
+ attr_accessor :favorites, :history, :cache_path
12
+
13
+ @@auth_status_codes = { none: false, failure: false,
14
+ unverified: false, verified: true,
15
+ loaded: false }
16
+ def self.auth_status_codes
17
+ @@auth_status_codes
18
+ end
19
+
20
+ def initialize(opts = {})
21
+ clear_auth_data
22
+ @status = :none
23
+ @history = []
24
+ @favorites = Emojidex::Data::Collection.new
25
+ end
26
+
27
+ def login(user, password, sync_on_login = true)
28
+ begin
29
+ auth_response = Transactor.get('users/authenticate',
30
+ { user: user, password: password })
31
+ rescue Error::Unauthorized
32
+ @status = :unverified
33
+ return false
34
+ end
35
+
36
+ return false unless _process_auth_response(auth_response)
37
+ if sync_on_login
38
+ sync_favorites
39
+ sync_history
40
+ end
41
+ true
42
+ end
43
+
44
+ def authorize(username, auth_token, sync_on_auth = true)
45
+ begin
46
+ auth_response = Transactor.get('users/authenticate',
47
+ { username: username, token: auth_token })
48
+ rescue Error::Unauthorized
49
+ @status = :unverified
50
+ return false
51
+ end
52
+
53
+ return false unless _process_auth_response(auth_response)
54
+ if sync_on_auth
55
+ sync_favorites
56
+ sync_history
57
+ end
58
+ true
59
+ end
60
+
61
+ def authorized?
62
+ @@auth_status_codes[@status]
63
+ end
64
+
65
+ def sync_favorites(limit = Emojidex::Defaults.limit, detailed = true)
66
+ return false unless authorized?
67
+
68
+ begin
69
+ res = Emojidex::Service::Collection.new(
70
+ { endpoint: 'users/favorites', limit: limit, detailed: detailed,
71
+ username: @username, auth_token: @auth_token })
72
+ rescue Error::Unauthorized
73
+ return false
74
+ end
75
+
76
+ @favorites = res
77
+ true
78
+ end
79
+
80
+ def add_favorite(code)
81
+ return false unless authorized?
82
+
83
+ begin
84
+ res = Transactor.post('users/favorites',
85
+ { username: @username, auth_token: @auth_token,
86
+ emoji_code: Emojidex.escape_code(code) })
87
+ rescue Error::Unauthorized
88
+ 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
+ end
96
+ return true
97
+ end
98
+
99
+ def remove_favorite(code)
100
+ return false unless authorized?
101
+
102
+ begin
103
+ res = Transactor.delete('users/favorites',
104
+ { username: @username, auth_token: @auth_token,
105
+ emoji_code: Emojidex.escape_code(code) })
106
+ rescue Error::Unauthorized
107
+ 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
+ end
115
+ true
116
+ end
117
+
118
+ def sync_history(limit = Emojidex::Defaults.limit, page = 1)
119
+ return false unless authorized?
120
+
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.
125
+ true
126
+ end
127
+
128
+ def add_history(code)
129
+ end
130
+
131
+ def clear_auth_data()
132
+ @username = @auth_token = ''
133
+ @pro = false
134
+ @premium = false
135
+ @pro_exp = nil
136
+ @premium_exp = nil
137
+ end
138
+
139
+ def sync
140
+ authorize(@username, @auth_token) &&
141
+ sync_favorites &&
142
+ sync_history
143
+ end
144
+
145
+ def save(path)
146
+ _set_cache_path(path)
147
+ _save_user
148
+ _save_favorites
149
+ _save_history
150
+ end
151
+
152
+ def load(path, auto_sync = true)
153
+ _set_cache_path(path)
154
+ _load_user
155
+ _load_favorites
156
+ _load_history
157
+ sync if auto_sync
158
+ end
159
+
160
+ private
161
+
162
+ def _process_auth_response(auth_response)
163
+ if auth_response[:auth_status] == 'verified'
164
+ _set_verified_data(auth_response)
165
+ return true
166
+ elsif auth_response[:auth_status] == 'unverified'
167
+ @status = :unverified
168
+ else
169
+ @status = :failure
170
+ end
171
+ clear_auth_data
172
+ false
173
+ end
174
+
175
+ def _set_verified_data(auth_response)
176
+ @status = :verified
177
+ @username = auth_response[:auth_user]
178
+ @auth_token = auth_response[:auth_token]
179
+ @pro = auth_response[:pro]
180
+ @premium = auth_response[:premium]
181
+ @pro_exp = auth_response[:pro_exp]
182
+ @premium_exp = auth_response[:premium_exp]
183
+ end
184
+
185
+ def _set_cache_path(path)
186
+ @cache_path = @cache_path || File.expand_path(
187
+ path || ENV['EMOJI_CACHE'] || "#{ENV['HOME']}/.emojidex/")
188
+ FileUtils.mkdir_p(@cache_path)
189
+ @cache_path
190
+ end
191
+
192
+ def _save_user
193
+ user_info = { username: username, auth_token: auth_token,
194
+ premium: premium, pro: pro,
195
+ premium_exp: premium_exp, pro_exp: pro_exp
196
+ }
197
+ File.open("#{@cache_path}/user.json", 'w') { |f| f.write user_info.to_json }
198
+ end
199
+
200
+ def _save_favorites
201
+ File.open("#{@cache_path}/favorites.json", 'w') { |f| f.write @favorites.emoji.values.to_json }
202
+ end
203
+
204
+ def _save_history
205
+ File.open("#{@cache_path}/history.json", 'w') { |f| f.write @history.to_json }
206
+ end
207
+
208
+ def _load_user
209
+ json = IO.read("#{@cache_path}/user.json")
210
+ user_info = JSON.parse(json, symbolize_names: true)
211
+ @username = user_info[:username]
212
+ @auth_token = user_info[:auth_token]
213
+ @premium = user_info[:premium]
214
+ @pro = user_info[:pro]
215
+ @premium_exp = user_info[:premium_exp]
216
+ @pro_exp = user_info[:pro_exp]
217
+ @status = :loaded
218
+ end
219
+
220
+ def _load_favorites
221
+ json = IO.read("#{@cache_path}/favorites.json")
222
+ @favorites = Emojidex::Service::Collection.new(
223
+ emoji: JSON.parse(json, symbolize_names: true),
224
+ auto_init: false)
225
+ end
226
+
227
+ def _load_history
228
+ json = IO.read("#{@cache_path}/history.json")
229
+ @history = JSON.parse json
230
+ end
231
+ end
232
+ end
233
+ end