commons_yellowme 0.11.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 (45) hide show
  1. checksums.yaml +7 -0
  2. data/MIT-LICENSE +20 -0
  3. data/README.md +28 -0
  4. data/Rakefile +17 -0
  5. data/lib/commons.rb +67 -0
  6. data/lib/commons/authentication/authenticate_by_jwt.rb +27 -0
  7. data/lib/commons/authentication/json_web_token.rb +17 -0
  8. data/lib/commons/concerns/attributes/sex.rb +19 -0
  9. data/lib/commons/concerns/extensions/deleted.rb +25 -0
  10. data/lib/commons/concerns/guard/capitalizable.rb +32 -0
  11. data/lib/commons/concerns/validations/undestroyable.rb +18 -0
  12. data/lib/commons/config.rb +11 -0
  13. data/lib/commons/config/locales/en.yml +1 -0
  14. data/lib/commons/config/locales/es.yml +58 -0
  15. data/lib/commons/controllers/schema_validable.rb +27 -0
  16. data/lib/commons/errors/bad_request.rb +15 -0
  17. data/lib/commons/errors/conflict.rb +15 -0
  18. data/lib/commons/errors/default_handling.rb +44 -0
  19. data/lib/commons/errors/error_base.rb +64 -0
  20. data/lib/commons/errors/error_serializer.rb +10 -0
  21. data/lib/commons/errors/forbidden.rb +15 -0
  22. data/lib/commons/errors/internal_server_error.rb +15 -0
  23. data/lib/commons/errors/invalid_resource.rb +21 -0
  24. data/lib/commons/errors/maintenance_mode.rb +14 -0
  25. data/lib/commons/errors/missing_parameter.rb +13 -0
  26. data/lib/commons/errors/not_unique.rb +14 -0
  27. data/lib/commons/errors/payment_required.rb +15 -0
  28. data/lib/commons/errors/precondition_failed.rb +15 -0
  29. data/lib/commons/errors/resource_not_found.rb +14 -0
  30. data/lib/commons/errors/route_not_found.rb +14 -0
  31. data/lib/commons/errors/unauthorized.rb +15 -0
  32. data/lib/commons/errors/unprocessable_entity.rb +15 -0
  33. data/lib/commons/formatter/e164_phone.rb +47 -0
  34. data/lib/commons/formatter/null_attributes_remover.rb +9 -0
  35. data/lib/commons/formatter/regex_constants.rb +9 -0
  36. data/lib/commons/formatter/string_utils.rb +9 -0
  37. data/lib/commons/middleware/catch_json_parse_errors.rb +30 -0
  38. data/lib/commons/railtie.rb +4 -0
  39. data/lib/commons/repositories/base_repository.rb +227 -0
  40. data/lib/commons/repositories/catalogs/base_catalog.rb +68 -0
  41. data/lib/commons/repositories/catalogs/concerns/model_caching_extention.rb +27 -0
  42. data/lib/commons/services/concerns/callable.rb +15 -0
  43. data/lib/commons/version.rb +3 -0
  44. data/lib/tasks/commons_tasks.rake +4 -0
  45. metadata +254 -0
@@ -0,0 +1,64 @@
1
+ # Author: carlos@yellowme.mx
2
+ #
3
+ # Description:
4
+ # The APIError class aids us in defining a standard
5
+ # interface for how we handle the errors in our API.
6
+ #
7
+ # It has two main functions:
8
+ # 1. Hold details of the error thrown so it can
9
+ # be translated to one of the standard HTTP codes.
10
+ # 2. Provide a consistent way to "serialize" errors.
11
+ #
12
+ # Attributes:
13
+ # - message: string, Holds a human friendly way to
14
+ # describe the error.
15
+ # - status: integer, Denotes the HTTP status code.
16
+ # - error: string, Similar to message, but this
17
+ # field is intended to show the error message from
18
+ # a raised error.
19
+ # - detail: Hash, Contains a more details about the
20
+ # error.
21
+
22
+ module Commons
23
+ module Errors
24
+ class ErrorBase < StandardError
25
+ include ActiveModel::Serialization
26
+
27
+ attr_reader :message, :backtrace, :title, :detail, :code, :meta
28
+
29
+ def initialize(message = nil,
30
+ backtrace = nil,
31
+ status: :internal_server_error,
32
+ code: I18n.t('status_code.IER5000_internal_server_error.code'),
33
+ title: I18n.t('status_code.IER5000_internal_server_error.title'),
34
+ detail: I18n.t('status_code.IER5000_internal_server_error.detail'),
35
+ meta: {})
36
+
37
+ @message = message
38
+ @backtrace = backtrace
39
+ @title = title
40
+ @detail = detail
41
+ @code = code
42
+ @status = status
43
+ @meta = meta
44
+ @meta.merge!(message: message) unless @meta.nil? || @message.nil?
45
+ @meta = nil if @meta.blank?
46
+ end
47
+
48
+ # returns the error as its hash representation
49
+ def to_hash
50
+ {
51
+ code: code,
52
+ title: title,
53
+ status: status,
54
+ detail: detail,
55
+ meta: meta
56
+ }
57
+ end
58
+
59
+ def status
60
+ Rack::Utils::SYMBOL_TO_STATUS_CODE[@status]
61
+ end
62
+ end
63
+ end
64
+ end
@@ -0,0 +1,10 @@
1
+ module Commons
2
+ module Errors
3
+ class ErrorSerializer < ActiveModel::Serializer
4
+ include Commons::Formatter::NullAttributesRemover
5
+ type 'errors'
6
+
7
+ attributes :status, :code, :title, :detail, :meta
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,15 @@
1
+ module Commons
2
+ module Errors
3
+ class Forbidden < ErrorBase
4
+ def initialize(message = nil, backtrace = nil, title: nil, code: nil, detail: nil, meta: {})
5
+ super message,
6
+ backtrace,
7
+ status: :forbidden,
8
+ title: title || I18n.t('status_code.IER4005_forbidden.title'),
9
+ code: code || I18n.t('status_code.IER4005_forbidden.code'),
10
+ detail: detail || I18n.t('status_code.IER4005_forbidden.detail'),
11
+ meta: meta
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,15 @@
1
+ module Commons
2
+ module Errors
3
+ class InternalServerError < ErrorBase
4
+ def initialize(message = nil, backtrace = nil, title: nil, code: nil, detail: nil, meta: {})
5
+ super message,
6
+ backtrace,
7
+ status: :internal_server_error,
8
+ title: title || I18n.t('status_code.IER5000_internal_server_error.title'),
9
+ code: code || I18n.t('status_code.IER5000_internal_server_error.code'),
10
+ detail: detail || I18n.t('status_code.IER5000_internal_server_error.detail'),
11
+ meta: meta
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,21 @@
1
+ module Commons
2
+ module Errors
3
+ class InvalidResource < UnprocessableEntity
4
+ def initialize(message = nil,
5
+ backtrace = nil,
6
+ title: nil,
7
+ code: nil,
8
+ detail: nil,
9
+ validation_errors: nil)
10
+ meta = {}
11
+ meta.merge!(validation_errors: validation_errors) unless validation_errors.blank?
12
+ super message,
13
+ backtrace,
14
+ title: title || I18n.t('status_code.IER4006_invalid_resource.title'),
15
+ code: code || I18n.t('status_code.IER4006_invalid_resource.code'),
16
+ detail: detail || I18n.t('status_code.IER4006_invalid_resource.detail'),
17
+ meta: meta
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,14 @@
1
+ module Commons
2
+ module Errors
3
+ class MaintenanceMode < ErrorBase
4
+ def initialize(message = nil, title: nil, code: nil, detail: nil, meta: {})
5
+ super message,
6
+ status: :service_unavailable,
7
+ title: title || I18n.t('status_code.IER5030_maintenance_mode.title'),
8
+ code: code || I18n.t('status_code.IER5030_maintenance_mode.code'),
9
+ detail: detail || I18n.t('status_code.IER5030_maintenance_mode.detail'),
10
+ meta: meta
11
+ end
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,13 @@
1
+ module Commons
2
+ module Errors
3
+ class MissingParameter < UnprocessableEntity
4
+ def initialize(message = nil, param: nil)
5
+ meta = {}
6
+ meta.merge!(param: param.to_s.camelize(:lower)) unless param.blank?
7
+ super message,
8
+ detail: I18n.t('status_code.IER4007_missing_parameter.detail'),
9
+ meta: meta
10
+ end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,14 @@
1
+ module Commons
2
+ module Errors
3
+ class NotUnique < Conflict
4
+ def initialize(message = nil, backtrace = nil, title: nil, code: nil, detail: nil, meta: {})
5
+ super message,
6
+ backtrace,
7
+ title: title || I18n.t('status_code.IER4008_not_unique.title'),
8
+ code: code || I18n.t('status_code.IER4008_not_unique.code'),
9
+ detail: detail || I18n.t('status_code.IER4008_not_unique.detail'),
10
+ meta: meta
11
+ end
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,15 @@
1
+ module Commons
2
+ module Errors
3
+ class PaymentRequired < ErrorBase
4
+ def initialize(message = nil, backtrace = nil, title: nil, code: nil, detail: nil, meta: {})
5
+ super message,
6
+ backtrace,
7
+ status: :payment_required,
8
+ title: title || I18n.t('status_code.IER4009_payment_required.title'),
9
+ code: code || I18n.t('status_code.IER4009_payment_required.code'),
10
+ detail: detail || I18n.t('status_code.IER4009_payment_required.detail'),
11
+ meta: meta
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,15 @@
1
+ module Commons
2
+ module Errors
3
+ class PreconditionFailed < ErrorBase
4
+ def initialize(message = nil, backtrace = nil, title: nil, code: nil, detail: nil, meta: {})
5
+ super message,
6
+ backtrace,
7
+ status: :precondition_failed,
8
+ title: title || I18n.t('status_code.IER4010_precondition_failed.title'),
9
+ code: code || I18n.t('status_code.IER4010_precondition_failed.code'),
10
+ detail: detail || I18n.t('status_code.IER4010_precondition_failed.detail'),
11
+ meta: meta
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,14 @@
1
+ module Commons
2
+ module Errors
3
+ class RouteNotFound < ErrorBase
4
+ def initialize(message = nil, title: nil, code: nil, detail: nil, meta: {})
5
+ super message,
6
+ status: :not_found,
7
+ title: title || I18n.t('status_code.IER4001_route_not_found.title'),
8
+ code: code || I18n.t('status_code.IER4001_route_not_found.code'),
9
+ detail: detail || I18n.t('status_code.IER4001_route_not_found.detail'),
10
+ meta: meta
11
+ end
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,14 @@
1
+ module Commons
2
+ module Errors
3
+ class ResourceNotFound < ErrorBase
4
+ def initialize(message = nil, title: nil, code: nil, detail: nil, meta: {})
5
+ super message,
6
+ status: :not_found,
7
+ title: title || I18n.t('status_code.IER4011_resource_not_found.title'),
8
+ code: code || I18n.t('status_code.IER4011_resource_not_found.code'),
9
+ detail: detail || I18n.t('status_code.IER4011_resource_not_found.detail'),
10
+ meta: meta
11
+ end
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,15 @@
1
+ module Commons
2
+ module Errors
3
+ class Unauthorized < ErrorBase
4
+ def initialize(message = nil, backtrace = nil, title: nil, code: nil, detail: nil, meta: {})
5
+ super message,
6
+ backtrace,
7
+ status: :unauthorized,
8
+ code: code || I18n.t('status_code.IER4002_unauthorized.code'),
9
+ title: title || I18n.t('status_code.IER4002_unauthorized.title'),
10
+ detail: detail || I18n.t('status_code.IER4002_unauthorized.detail'),
11
+ meta: meta
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,15 @@
1
+ module Commons
2
+ module Errors
3
+ class UnprocessableEntity < ErrorBase
4
+ def initialize(message = nil, backtrace = nil, title: nil, code: nil, detail: nil, meta: {})
5
+ super message,
6
+ backtrace,
7
+ status: :unprocessable_entity,
8
+ title: title || I18n.t('status_code.IER4222_unprocessable_entity.title'),
9
+ code: code || I18n.t('status_code.IER4222_unprocessable_entity.code'),
10
+ detail: detail || I18n.t('status_code.IER4222_unprocessable_entity.detail'),
11
+ meta: meta
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,47 @@
1
+ module Commons
2
+ module Formatter
3
+ class E164Phone
4
+ attr_accessor :valid
5
+
6
+ # @type [String]
7
+ # Mexico ISO Country Code
8
+ MEXICO_ISO_CODE = 'MX'.freeze
9
+ # @type [String]
10
+ # Mexico Phone Country Code
11
+ MEXICO_CC = '52'.freeze
12
+ # @type [String]
13
+ # Mexico National Destination Code
14
+ MEXICO_NDC = ''.freeze
15
+ MEXICO_REGEX = /^(521?\d{10}|\d{10})$/
16
+ NOT_DIGITS_REGEX = /[^0-9]/
17
+
18
+ def initialize(phone_number)
19
+ @phone = Phonelib.parse(phone_number)
20
+ @valid = @phone.valid?
21
+ end
22
+
23
+ def format
24
+ @valid ? @phone.e164 : nil
25
+ end
26
+
27
+ def format_national
28
+ @valid ? @phone.raw_national : nil
29
+ end
30
+
31
+ def country_code
32
+ @valid ? @phone.country_code : nil
33
+ end
34
+
35
+ def validate
36
+ @valid && @phone.country_code == MEXICO_CC
37
+ end
38
+
39
+ def self.canonical_phone(phone_number)
40
+ phone_digits = phone_number.gsub(NOT_DIGITS_REGEX, '')
41
+ return nil unless MEXICO_REGEX.match(phone_digits)
42
+
43
+ phone_digits.split(//).last(10).join
44
+ end
45
+ end
46
+ end
47
+ end
@@ -0,0 +1,9 @@
1
+ module Commons
2
+ module Formatter
3
+ module NullAttributesRemover
4
+ def attributes(*args)
5
+ super.compact
6
+ end
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,9 @@
1
+ module Commons
2
+ module Formatter
3
+ module RegexConstants
4
+ PROPER_NOUN = /\A\p{L}[\p{L}'\.\-]*( [\p{L}'\.\-]+)*\z/u
5
+ CURP = /\A([A-Z][AEIOUX][A-Z]{2}\d{2}(?:0[1-9]|1[0-2])(?:0[1-9]|[12]\d|3[01])[HM](?:AS|B[CS]|C[CLMSH]|D[FG]|G[TR]|HG|JC|M[CNS]|N[ETL]|OC|PL|Q[TR]|S[PLR]|T[CSL]|VZ|YN|ZS)[B-DF-HJ-NP-TV-Z]{3}[A-Z\d])(\d)\z/
6
+ RFC = /\A([A-ZÑ\x26]{3,4}(\d{2})(0[1-9]|1[0-2])(0[1-9]|[12]\d|3[0-1])([A-Z0-9]){2}([A0-9]))?\z/i
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,9 @@
1
+ module Commons
2
+ module Formatter
3
+ module StringUtils
4
+ def self.capitalize(text)
5
+ text.downcase.gsub(/(?!(las?|del?|los?|y)\b)\b\p{L}/, &:upcase)
6
+ end
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,30 @@
1
+ module Commons
2
+ module Middleware
3
+ class CatchJsonParseErrors
4
+ def initialize(app)
5
+ @app = app
6
+ end
7
+
8
+ def call(env)
9
+ @app.call(env)
10
+ rescue ActionDispatch::Http::Parameters::ParseError => e
11
+ if env['HTTP_ACCEPT'] =~ %r{application/json} ||
12
+ env['CONTENT_TYPE'] =~ %r{application/json}
13
+ str = Commons::Errors::ErrorSerializer.new(Commons::Errors::BadRequest.new(e)).to_json
14
+ str.tr!("'", '\'')
15
+ return [
16
+ '400',
17
+ { 'Content-Type' => 'application/json' },
18
+ [
19
+ '{ "error":'\
20
+ "#{str}"\
21
+ '}'\
22
+ ]
23
+ ]
24
+ else
25
+ raise e
26
+ end
27
+ end
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,4 @@
1
+ module Commons
2
+ class Railtie < ::Rails::Railtie
3
+ end
4
+ end
@@ -0,0 +1,227 @@
1
+ module Commons
2
+ module Repositories
3
+ class BaseRepository
4
+ include Singleton
5
+
6
+ #
7
+ # Método que devuelve todos los objetos
8
+ #
9
+ # @return [Object,nil]
10
+ #
11
+ def all
12
+ @db_client.all
13
+ end
14
+
15
+ #
16
+ # Método que devuelve todos los objetos que no han sido eliminados
17
+ #
18
+ # @return [Object,nil]
19
+ #
20
+ def kept
21
+ raise ActiveModel::MissingAttributeError unless @db_client.column_names.include? "deleted_at"
22
+
23
+ @db_client.where(deleted_at: nil)
24
+ end
25
+
26
+ #
27
+ # Método que devuelve todos los objetos que han sido eliminados
28
+ #
29
+ # @return [Object,nil]
30
+ #
31
+ def deleted
32
+ raise ActiveModel::MissingAttributeError unless @db_client.column_names.include? "deleted_at"
33
+
34
+ @db_client.where.not(deleted_at: nil)
35
+ end
36
+
37
+ #
38
+ # Método que devuelve el objeto según su ID
39
+ #
40
+ # @return [Object,nil]
41
+ #
42
+ def find(id)
43
+ @db_client.find(id)
44
+ end
45
+
46
+ #
47
+ # Método que devuelve el objeto según su ID
48
+ #
49
+ # @return [Object,nil]
50
+ #
51
+ def find_kept(id)
52
+ raise ActiveModel::MissingAttributeError unless @db_client.column_names.include? "deleted_at"
53
+
54
+ @db_client.find_by!(id: id, deleted_at: nil)
55
+ end
56
+
57
+ #
58
+ # Método que devuelve el objeto según parámetros
59
+ #
60
+ # @return [Object,nil]
61
+ #
62
+ def find_by(params)
63
+ @db_client.find_by(params)
64
+ end
65
+
66
+ #
67
+ # Método que devuelve el objeto según parámetros
68
+ #
69
+ # @return [Object,nil]
70
+ #
71
+ def find_kept_by(params)
72
+ raise ActiveModel::MissingAttributeError unless @db_client.column_names.include? "deleted_at"
73
+
74
+ @db_client.find_by(
75
+ deleted_at: nil,
76
+ **params
77
+ )
78
+ end
79
+
80
+ #
81
+ # Método que devuelve el objeto según parámetros
82
+ #
83
+ # @return [Object]
84
+ #
85
+ # @raise [ActiveRecord::RecordNotFound]
86
+ #
87
+ def find_by!(params)
88
+ @db_client.find_by!(params)
89
+ end
90
+
91
+ #
92
+ # Método que devuelve el objeto según parámetros
93
+ #
94
+ # @return [Object]
95
+ #
96
+ # @raise [ActiveRecord::RecordNotFound]
97
+ #
98
+ def find_kept_by!(params)
99
+ raise ActiveModel::MissingAttributeError unless @db_client.column_names.include? "deleted_at"
100
+
101
+ @db_client.find_by!(
102
+ deleted_at: nil,
103
+ **params
104
+ )
105
+ end
106
+
107
+ #
108
+ # Método que realiza un guardado de un objeto
109
+ #
110
+ # @param [Object]
111
+ #
112
+ # @return [Boolean]
113
+ #
114
+ # @raise [ActiveRecord::RecordInvalid]
115
+ # @raise [ActiveRecord::RecordNotSaved]
116
+ #
117
+ def create!(object)
118
+ raise ArgumentError unless object.is_a? @db_client
119
+
120
+ object.save!
121
+ end
122
+
123
+ #
124
+ # Método que realiza un guardado de un objeto
125
+ #
126
+ # @param [Array<Hash>] params Listado de parámetros del objeto
127
+ #
128
+ # @return [Object] Objeto creado
129
+ #
130
+ # @raises [ActiveRecord::RecordInvalid]
131
+ #
132
+ def create_from_params!(**params)
133
+ @db_client.create!(**params)
134
+ end
135
+
136
+ #
137
+ # Método que realiza una busqueda o guardado de un objeto
138
+ #
139
+ # @param [Array<Hash>] params Listado de parámetros del objeto
140
+ # @param [block] block
141
+ #
142
+ # @return [Object] Objeto creado
143
+ #
144
+ # @raises [ActiveRecord::RecordInvalid]
145
+ #
146
+ def find_or_create_by!(params, &block)
147
+ object = @db_client.find_by(params) || @db_client.create!(params, &block)
148
+ object
149
+ end
150
+
151
+ #
152
+ # Método que realiza una busqueda de la primer entrada,
153
+ # en caso de no encontrarlo crea un objeto con el bloque
154
+ #
155
+ # @param [Array<Hash>] params Listado de parámetros del objeto
156
+ # @param [block] block
157
+ #
158
+ # @return [Object] Objeto creado
159
+ #
160
+ # @raises [ActiveRecord::RecordInvalid]
161
+ #
162
+ def where_first_or_create!(params, attributes = nil, &block)
163
+ @db_client.where(params).first_or_create!(attributes, &block)
164
+ end
165
+
166
+ #
167
+ # Método que guarda los cambios realizados a una instancia del objeto
168
+ #
169
+ # @param [Object]
170
+ #
171
+ # @return [Boolean]
172
+ #
173
+ # @raise [ActiveRecord::RecordInvalid]
174
+ # @raise [ActiveRecord::RecordNotSaved]
175
+ #
176
+ def update!(object)
177
+ create!(object)
178
+ end
179
+
180
+ #
181
+ # Método que realiza un guardado de un objeto
182
+ #
183
+ # @param [Array<Hash>] params Listado de parámetros del objeto
184
+ #
185
+ # @return [Object] Objeto creado
186
+ #
187
+ # @raises [ActiveRecord::RecordInvalid]
188
+ #
189
+ def update_from_params!(id:, **params)
190
+ object = @db_client.find_by!(id: id)
191
+ object.update!(params)
192
+
193
+ object
194
+ end
195
+
196
+ #
197
+ # Método que realiza un borrado lógico de un objeto
198
+ #
199
+ # @param [UUID] id Identificador del objeto
200
+ #
201
+ # @return [Object] Objeto eliminado
202
+ #
203
+ # @raises [ActiveRecord::RecordInvalid]
204
+ # @raises [ActiveModel::MissingAttributeError]
205
+ #
206
+ def soft_delete!(id)
207
+ raise ActiveModel::MissingAttributeError unless @db_client.column_names.include? "deleted_at"
208
+
209
+ object = @db_client.find_by!(id: id, deleted_at: nil)
210
+ object.update!(deleted_at: Time.current)
211
+
212
+ object
213
+ end
214
+
215
+ private
216
+
217
+ def initialize
218
+ @db_client ||= class_object
219
+ end
220
+
221
+ def class_object
222
+ model_name = self.class.to_s.gsub("Repository", "")
223
+ Object.const_get model_name
224
+ end
225
+ end
226
+ end
227
+ end