pg_rails 7.0.8.pre.alpha.71 → 7.0.8.pre.alpha.73
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.
- checksums.yaml +4 -4
- data/pg_associable/app/assets/stylesheets/pg_associable.scss +2 -2
- data/pg_associable/app/helpers/pg_associable/form_builder_methods.rb +3 -0
- data/pg_associable/app/helpers/pg_associable/helpers.rb +3 -2
- data/pg_associable/app/javascript/asociable_controller.tsx +62 -27
- data/pg_associable/app/views/pg_associable/_resultados_inline.html.slim +2 -4
- data/pg_engine/app/assets/stylesheets/pg_rails_b5.scss +0 -1
- data/pg_engine/app/controllers/concerns/pg_engine/resource.rb +3 -3
- data/pg_engine/app/decorators/account_decorator.rb +1 -1
- data/pg_engine/app/decorators/email_decorator.rb +1 -1
- data/pg_engine/app/decorators/mensaje_contacto_decorator.rb +1 -1
- data/pg_engine/app/decorators/pg_engine/base_decorator.rb +6 -125
- data/pg_engine/app/decorators/pg_engine/base_record_decorator.rb +138 -0
- data/pg_engine/app/decorators/user_account_decorator.rb +1 -1
- data/pg_engine/app/decorators/user_decorator.rb +1 -1
- data/pg_engine/app/helpers/pg_engine/form_helper.rb +2 -2
- data/pg_engine/app/lib/pg_engine/devise_failure_app.rb +1 -0
- data/pg_engine/app/lib/pg_form_builder.rb +1 -0
- data/pg_engine/config/locales/es.yml +1 -1
- data/pg_engine/lib/pg_engine/utils/pg_logger.rb +69 -58
- data/pg_engine/spec/controllers/pg_engine/base_controller_spec.rb +0 -6
- data/pg_engine/spec/lib/pg_engine/utils/pg_engine/pg_logger_spec.rb +28 -82
- data/pg_layout/app/views/pg_layout/_error.html.erb +1 -1
- data/pg_rails/lib/version.rb +1 -1
- data/pg_rails/scss/bootstrap_overrides.scss +2 -0
- data/pg_scaffold/lib/generators/pg_decorator/pg_decorator_generator.rb +1 -1
- data/pg_scaffold/lib/generators/pg_rspec/scaffold/templates/controller_spec.rb +1 -1
- metadata +3 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 7fead42e7825fd503115e697fc2c276181f4eaf6accfd783c8ebd463cf5ae278
|
4
|
+
data.tar.gz: 0ba91edbd93dd46d83a1d2aee3e77aba5d2d2d28d38625ddcce287b8ecb2a59e
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: e55f215a88c48203ae08aa09b6005a376b3efc2fef02005b4719f19a8927bf8bca0865f663d4f08dc098a75e53dc50707ee111d78a3bfeb2d92e2d202cbdc026
|
7
|
+
data.tar.gz: a0c3ce96cf9303518e40ba5a4b669db2a0570dff432d30aea65821ceffe2ad10016cc536459a69b819545dad5854b7f49549c1d4c3f615226fa6f235c95453d7
|
@@ -31,7 +31,7 @@
|
|
31
31
|
display: none;
|
32
32
|
}
|
33
33
|
input[type=text] {
|
34
|
-
background-color:
|
34
|
+
background-color: #{$body-bg};
|
35
35
|
padding-right: 2em;
|
36
36
|
}
|
37
37
|
&.filled {
|
@@ -84,7 +84,7 @@
|
|
84
84
|
box-shadow: 0px 9px 13px -3px rgba(0, 0, 0, 0.5);
|
85
85
|
// position: absolute;
|
86
86
|
// z-index: 1;
|
87
|
-
background-color:
|
87
|
+
background-color: #{$body-bg};
|
88
88
|
border: 1px solid #a7b7bb;
|
89
89
|
border-top: none;
|
90
90
|
border-radius: 4px;
|
@@ -50,6 +50,9 @@ module PgAssociable
|
|
50
50
|
|
51
51
|
def clase_asociacion(atributo)
|
52
52
|
asociacion = object.class.reflect_on_all_associations.find { |a| a.name == atributo.to_sym }
|
53
|
+
|
54
|
+
raise PgEngine::Error, "no existe la asociación para el atributo: #{atributo}" if asociacion.blank?
|
55
|
+
|
53
56
|
nombre_clase = asociacion.options[:class_name]
|
54
57
|
nombre_clase = asociacion.name.to_s.camelize if nombre_clase.nil?
|
55
58
|
Object.const_get(nombre_clase)
|
@@ -13,10 +13,11 @@ module PgAssociable
|
|
13
13
|
def pg_respond_buscar
|
14
14
|
partial = 'pg_associable/resultados_inline'
|
15
15
|
resultados_prefix = 'resultados-inline'
|
16
|
-
|
16
|
+
query = params[:query]
|
17
|
+
@collection = policy_scope(@clase_modelo).kept.query(query).limit(MAX_RESULTS)
|
17
18
|
render turbo_stream:
|
18
19
|
turbo_stream.update("#{resultados_prefix}-#{params[:id]}",
|
19
|
-
partial:, locals: { collection: @collection })
|
20
|
+
partial:, locals: { collection: @collection, query: })
|
20
21
|
end
|
21
22
|
end
|
22
23
|
end
|
@@ -9,6 +9,7 @@ export default class extends Controller {
|
|
9
9
|
subWrapper = null
|
10
10
|
elemId = null
|
11
11
|
input = null
|
12
|
+
originalPlaceholder = null
|
12
13
|
|
13
14
|
connect () {
|
14
15
|
// ID único para identificar el campo y el modal
|
@@ -29,9 +30,21 @@ export default class extends Controller {
|
|
29
30
|
result.appendChild(this.subWrapper)
|
30
31
|
this.input.parentNode.appendChild(result)
|
31
32
|
|
33
|
+
const callback = (mutationList) => {
|
34
|
+
for (const mutation of mutationList) {
|
35
|
+
if (mutation.type === 'childList') {
|
36
|
+
this.autoScroll()
|
37
|
+
}
|
38
|
+
}
|
39
|
+
}
|
40
|
+
const observer = new MutationObserver(callback)
|
41
|
+
const config = { attributes: false, childList: true, subtree: true }
|
42
|
+
observer.observe(this.subWrapper, config)
|
43
|
+
|
32
44
|
this.resetResultados()
|
33
45
|
|
34
46
|
const input = this.element.querySelector('input[type=text]')
|
47
|
+
this.originalPlaceholder = input.placeholder
|
35
48
|
if (input.value) {
|
36
49
|
this.element.classList.add('filled')
|
37
50
|
input.setAttribute('readonly', 'true')
|
@@ -54,48 +67,68 @@ export default class extends Controller {
|
|
54
67
|
}, 200)
|
55
68
|
|
56
69
|
this.input.addEventListener('blur', () => {
|
57
|
-
this.input.placeholder =
|
70
|
+
this.input.placeholder = this.originalPlaceholder
|
58
71
|
})
|
59
72
|
this.input.onfocus = () => {
|
60
73
|
this.input.select()
|
61
74
|
if (this.input.value.length === 0) {
|
62
75
|
this.escribiAlgo()
|
63
76
|
}
|
64
|
-
|
65
|
-
if (!this.element.closest('.modal')) {
|
66
|
-
const wHeight = window.visualViewport.height
|
67
|
-
const scrollTop = document.scrollingElement.scrollTop
|
68
|
-
const viewPortBottom = scrollTop + wHeight
|
69
|
-
const swHeight = parseInt(this.subWrapper.style.maxHeight) + 20
|
70
|
-
const elementTop = this.element.getBoundingClientRect().top + scrollTop
|
71
|
-
const inputBottom = this.input.getBoundingClientRect().bottom + scrollTop
|
72
|
-
const swBottom = inputBottom + swHeight
|
73
|
-
|
74
|
-
if (swBottom > viewPortBottom) {
|
75
|
-
document.scrollingElement.scroll({ top: elementTop })
|
76
|
-
}
|
77
|
-
}
|
77
|
+
this.autoScroll()
|
78
78
|
}
|
79
79
|
this.input.onkeyup = (e) => {
|
80
80
|
if (this.input.value.length === 0) {
|
81
81
|
this.escribiAlgo()
|
82
82
|
}
|
83
|
-
|
84
|
-
|
83
|
+
|
84
|
+
if ([37, 38, 39, 40].includes(e.keyCode)) {
|
85
|
+
// Arrow keys, do nothing
|
85
86
|
} else {
|
86
|
-
|
87
|
+
if ([27].includes(e.keyCode)) {
|
88
|
+
// ESC
|
89
|
+
document.activeElement?.blur && document.activeElement.blur()
|
90
|
+
} else {
|
91
|
+
if (e.keyCode === 13) { // Enter
|
92
|
+
doSearchBounce(true)
|
93
|
+
} else {
|
94
|
+
doSearchBounce()
|
95
|
+
}
|
96
|
+
}
|
87
97
|
}
|
88
98
|
}
|
89
99
|
this.input.onkeydown = (e) => {
|
90
|
-
|
100
|
+
console.log(e.keyCode)
|
101
|
+
if (e.keyCode === 13) { // Enter
|
91
102
|
e.preventDefault()
|
92
103
|
return false
|
93
104
|
}
|
105
|
+
if (this.element.classList.contains('filled')) {
|
106
|
+
if (e.keyCode === 8 || e.keyCode === 46) { // Supr or Backspace
|
107
|
+
this.completarCampo(null)
|
108
|
+
}
|
109
|
+
}
|
94
110
|
}
|
95
111
|
this.setMaxHeight()
|
96
112
|
}
|
97
113
|
|
114
|
+
autoScroll () {
|
115
|
+
if (!this.element.closest('.modal')) {
|
116
|
+
const wHeight = window.visualViewport.height
|
117
|
+
const scrollTop = document.scrollingElement.scrollTop
|
118
|
+
const viewPortBottom = scrollTop + wHeight
|
119
|
+
const swHeight = parseInt(this.subWrapper.getBoundingClientRect().height) + 20
|
120
|
+
const inputBottom = this.input.getBoundingClientRect().bottom + scrollTop
|
121
|
+
const swBottom = inputBottom + swHeight
|
122
|
+
|
123
|
+
if (swBottom > viewPortBottom) {
|
124
|
+
const offset = swBottom - viewPortBottom
|
125
|
+
document.scrollingElement.scroll({ top: scrollTop + offset })
|
126
|
+
}
|
127
|
+
}
|
128
|
+
}
|
129
|
+
|
98
130
|
resetResultados () {
|
131
|
+
this.lastValue = null
|
99
132
|
const rows = []
|
100
133
|
if (this.element.dataset.puedeCrear) {
|
101
134
|
rows.push(
|
@@ -140,6 +173,9 @@ export default class extends Controller {
|
|
140
173
|
if (maxHeight < 200) {
|
141
174
|
maxHeight = 200
|
142
175
|
}
|
176
|
+
if (maxHeight > 400) {
|
177
|
+
maxHeight = 400
|
178
|
+
}
|
143
179
|
if (bodyHeight < inputY + maxHeight) {
|
144
180
|
document.body.style.height = `${inputY + maxHeight}px`
|
145
181
|
}
|
@@ -169,7 +205,7 @@ export default class extends Controller {
|
|
169
205
|
}
|
170
206
|
|
171
207
|
escribiAlgo () {
|
172
|
-
this.input.placeholder = 'Escribí algo para buscar'
|
208
|
+
this.input.placeholder = this.element.dataset.placeholder || 'Escribí algo para buscar'
|
173
209
|
}
|
174
210
|
|
175
211
|
buscando () {
|
@@ -201,13 +237,12 @@ export default class extends Controller {
|
|
201
237
|
}
|
202
238
|
this.lastValue = this.input.value
|
203
239
|
|
204
|
-
|
205
|
-
|
206
|
-
|
207
|
-
|
208
|
-
|
209
|
-
|
210
|
-
}, { once: true })
|
240
|
+
this.buscando()
|
241
|
+
// TODO: hacer bien el clearTimeout con la respuesta del server, ya sea por turbo stream o por cable ready
|
242
|
+
// IMPORTANTE: además, un timeout por si nunca llega la respuesta
|
243
|
+
// const timerId = setTimeout(() => {
|
244
|
+
// this.buscando()
|
245
|
+
// }, 200)
|
211
246
|
const elem = (
|
212
247
|
<form method="post" action={this.input.dataset.urlSearch} data-turbo-stream="true">
|
213
248
|
<input type="hidden" name="id" value={this.elemId} />
|
@@ -1,8 +1,6 @@
|
|
1
|
+
/ # locals: (collection:, query:)
|
1
2
|
.resultados.inline tabindex="-1"
|
2
3
|
ul.list-group.list-group-flush
|
3
|
-
- if params[:puede_crear].present?
|
4
|
-
= link_to 'Nuevo', 'javascript:void(0)',
|
5
|
-
class: 'list-group-item', data: { action: 'asociable#crearItem' }
|
6
4
|
- if collection.any?
|
7
5
|
- collection.each do |object|
|
8
6
|
= link_to object.to_s, 'javascript:void(0)',
|
@@ -10,4 +8,4 @@
|
|
10
8
|
data: { action: 'asociable#selectItem',
|
11
9
|
id: object.id, object: object.decorate.to_json }
|
12
10
|
- else
|
13
|
-
li.list-group-item No hay resultados para "#{
|
11
|
+
li.list-group-item No hay resultados para "#{query}"
|
@@ -281,11 +281,11 @@ module PgEngine
|
|
281
281
|
# TODO: restringir ciertos campos?
|
282
282
|
unless scope.model.column_names.include?(field.to_s) ||
|
283
283
|
scope.model.respond_to?("order_by_#{field}")
|
284
|
-
|
284
|
+
pg_warn("No existe el campo \"#{field}\"")
|
285
285
|
return scope
|
286
286
|
end
|
287
287
|
unless direction.to_sym.in? %i[asc desc]
|
288
|
-
|
288
|
+
pg_warn("Direction not valid: \"#{direction}\"")
|
289
289
|
return scope
|
290
290
|
end
|
291
291
|
scope = if scope.model.respond_to? "order_by_#{field}"
|
@@ -297,7 +297,7 @@ module PgEngine
|
|
297
297
|
instance_variable_set(:@direction, direction)
|
298
298
|
scope
|
299
299
|
rescue ArgumentError => e
|
300
|
-
|
300
|
+
pg_warn(e)
|
301
301
|
scope
|
302
302
|
end
|
303
303
|
|
@@ -1,132 +1,13 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
1
|
module PgEngine
|
4
|
-
class BaseDecorator <
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
include RouteHelper
|
9
|
-
include Pundit::Authorization
|
10
|
-
|
11
|
-
attr_accessor :output_buffer
|
12
|
-
|
13
|
-
delegate_all
|
14
|
-
|
15
|
-
def as_json(options = {})
|
16
|
-
object.as_json(options).tap { |o| o[:to_s] = to_s }
|
17
|
-
end
|
18
|
-
|
19
|
-
# rubocop:disable Style/MissingRespondToMissing
|
20
|
-
def method_missing(method_name, *args, &)
|
21
|
-
valor = object.attributes[method_name.to_s]
|
22
|
-
return super if valor.blank?
|
23
|
-
|
24
|
-
if valor.instance_of?(Date)
|
25
|
-
dmy(valor)
|
26
|
-
elsif valor.instance_of?(ActiveSupport::TimeWithZone)
|
27
|
-
dmy_time(valor)
|
28
|
-
else
|
29
|
-
super
|
30
|
-
end
|
31
|
-
end
|
32
|
-
# rubocop:enable Style/MissingRespondToMissing
|
33
|
-
|
34
|
-
def destroy_link_redirect
|
35
|
-
destroy_link(redirect_to: helpers.url_for(target_index))
|
36
|
-
end
|
37
|
-
|
38
|
-
def destroy_link(confirm_text: '¿Estás seguro?', klass: 'btn-light', redirect_to: nil)
|
39
|
-
return unless Pundit.policy!(Current.user, object).destroy?
|
40
|
-
|
41
|
-
helpers.content_tag :span, rel: :tooltip, title: 'Eliminar' do
|
42
|
-
helpers.link_to object_url + (redirect_to.present? ? "?redirect_to=#{redirect_to}" : ''),
|
43
|
-
data: { 'turbo-confirm': confirm_text, 'turbo-method': :delete },
|
44
|
-
class: "btn btn-sm #{klass}" do
|
45
|
-
helpers.content_tag :span, nil, class: clase_icono('trash-fill')
|
46
|
-
end
|
47
|
-
end
|
48
|
-
end
|
49
|
-
|
50
|
-
def edit_link(text: '', klass: 'btn-light')
|
51
|
-
return unless Pundit.policy!(Current.user, object).edit?
|
52
|
-
|
53
|
-
helpers.content_tag :span, rel: :tooltip, title: 'Editar' do
|
54
|
-
helpers.link_to edit_object_url, data: { turbo_frame: :main },
|
55
|
-
class: "btn btn-sm #{klass}" do
|
56
|
-
helpers.content_tag(:span, nil, class: clase_icono('pencil')) + text
|
57
|
-
end
|
58
|
-
end
|
59
|
-
end
|
60
|
-
|
61
|
-
def show_link(text: '', klass: 'btn-light')
|
62
|
-
return unless Pundit.policy!(Current.user, object).show?
|
63
|
-
|
64
|
-
helpers.content_tag :span, rel: :tooltip, title: 'Ver' do
|
65
|
-
helpers.link_to object_url, data: { turbo_frame: :main },
|
66
|
-
class: "btn btn-sm #{klass}" do
|
67
|
-
helpers.content_tag(:span, nil, class: clase_icono('eye-fill')) + text
|
68
|
-
end
|
2
|
+
class BaseDecorator < PgEngine::BaseRecordDecorator
|
3
|
+
class Deprecator
|
4
|
+
def deprecation_warning(_deprecated_method_name, _message, _caller_backtrace = nil)
|
5
|
+
Kernel.warn 'DEPRECACION WARNING: PgEngine::BaseDecorator is deprecated, mejor usá BaseRecordDecorator'
|
69
6
|
end
|
70
7
|
end
|
71
8
|
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
helpers.content_tag :span, rel: :tooltip, title: 'Exportar en excel' do
|
76
|
-
helpers.content_tag :a, target: '_blank',
|
77
|
-
class: "btn btn-sm #{klass}", href: url_change_format(url, 'xlsx') do
|
78
|
-
"#{helpers.content_tag(:span, nil, class: clase_icono('file-earmark-excel-fill'))} #{text}".html_safe
|
79
|
-
end
|
80
|
-
end
|
81
|
-
end
|
82
|
-
|
83
|
-
def new_link(remote: nil, klass: 'btn-warning')
|
84
|
-
return unless Pundit.policy!(Current.user, object).new?
|
85
|
-
|
86
|
-
helpers.content_tag :span, rel: :tooltip, title: submit_default_value do
|
87
|
-
helpers.link_to(new_object_url, class: "btn btn-sm #{klass}",
|
88
|
-
remote:) do
|
89
|
-
helpers.content_tag(:span, nil,
|
90
|
-
class: clase_icono('plus').to_s) + "<span class='d-none d-sm-inline'> #{submit_default_value}</span>".html_safe
|
91
|
-
end
|
92
|
-
end
|
93
|
-
end
|
94
|
-
|
95
|
-
def edit_object_url
|
96
|
-
helpers.url_for([:edit, target_object].flatten)
|
97
|
-
end
|
98
|
-
|
99
|
-
def new_object_url
|
100
|
-
"#{helpers.url_for(target_index)}/new"
|
101
|
-
end
|
102
|
-
|
103
|
-
def object_url
|
104
|
-
helpers.url_for(target_object)
|
105
|
-
end
|
106
|
-
|
107
|
-
def target_object
|
108
|
-
pg_namespace.present? ? [pg_namespace, object] : object
|
109
|
-
end
|
110
|
-
|
111
|
-
def target_index
|
112
|
-
pg_namespace.present? ? [pg_namespace, object.class] : object.class
|
113
|
-
end
|
114
|
-
|
115
|
-
# actionview-7.1.3.2/lib/action_view/helpers/form_helper.rb
|
116
|
-
def submit_default_value
|
117
|
-
key = :create
|
118
|
-
model = object.model_name.human
|
119
|
-
defaults = []
|
120
|
-
defaults << :"helpers.submit.#{object.model_name.i18n_key}.#{key}"
|
121
|
-
defaults << :"helpers.submit.#{key}"
|
122
|
-
defaults << "#{key.to_s.humanize} #{model}"
|
123
|
-
I18n.t(defaults.shift, model:, default: defaults)
|
124
|
-
end
|
125
|
-
|
126
|
-
private
|
127
|
-
|
128
|
-
def clase_icono(icono)
|
129
|
-
"bi bi-#{icono}"
|
9
|
+
class << self
|
10
|
+
deprecate :decorate, deprecator: Deprecator.new
|
130
11
|
end
|
131
12
|
end
|
132
13
|
end
|
@@ -0,0 +1,138 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module PgEngine
|
4
|
+
class BaseRecordDecorator < Draper::Decorator
|
5
|
+
include ActionView::Helpers
|
6
|
+
include PrintHelper
|
7
|
+
include FormHelper
|
8
|
+
include RouteHelper
|
9
|
+
include Pundit::Authorization
|
10
|
+
|
11
|
+
attr_accessor :output_buffer
|
12
|
+
|
13
|
+
delegate_all
|
14
|
+
|
15
|
+
# Polemic, draper lo implementa con el propio decorator, pero necesito
|
16
|
+
# que sea el ActiveRecord para que funcione el dom_id
|
17
|
+
def to_model
|
18
|
+
object
|
19
|
+
end
|
20
|
+
|
21
|
+
def as_json(options = {})
|
22
|
+
object.as_json(options).tap { |o| o[:to_s] = to_s }
|
23
|
+
end
|
24
|
+
|
25
|
+
# rubocop:disable Style/MissingRespondToMissing
|
26
|
+
def method_missing(method_name, *args, &)
|
27
|
+
valor = object.attributes[method_name.to_s]
|
28
|
+
return super if valor.blank?
|
29
|
+
|
30
|
+
if valor.instance_of?(Date)
|
31
|
+
dmy(valor)
|
32
|
+
elsif valor.instance_of?(ActiveSupport::TimeWithZone)
|
33
|
+
dmy_time(valor)
|
34
|
+
else
|
35
|
+
super
|
36
|
+
end
|
37
|
+
end
|
38
|
+
# rubocop:enable Style/MissingRespondToMissing
|
39
|
+
|
40
|
+
def destroy_link_redirect
|
41
|
+
destroy_link(redirect_to: helpers.url_for(target_index))
|
42
|
+
end
|
43
|
+
|
44
|
+
def destroy_link(confirm_text: '¿Estás seguro?', klass: 'btn-light', redirect_to: nil)
|
45
|
+
return unless Pundit.policy!(Current.user, object).destroy?
|
46
|
+
|
47
|
+
helpers.content_tag :span, rel: :tooltip, title: 'Eliminar' do
|
48
|
+
helpers.link_to object_url + (redirect_to.present? ? "?redirect_to=#{redirect_to}" : ''),
|
49
|
+
data: { 'turbo-confirm': confirm_text, 'turbo-method': :delete },
|
50
|
+
class: "btn btn-sm #{klass}" do
|
51
|
+
helpers.content_tag :span, nil, class: clase_icono('trash-fill')
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
def edit_link(text: '', klass: 'btn-light')
|
57
|
+
return unless Pundit.policy!(Current.user, object).edit?
|
58
|
+
|
59
|
+
helpers.content_tag :span, rel: :tooltip, title: 'Editar' do
|
60
|
+
helpers.link_to edit_object_url, data: { turbo_frame: :main },
|
61
|
+
class: "btn btn-sm #{klass}" do
|
62
|
+
helpers.content_tag(:span, nil, class: clase_icono('pencil')) + text
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
def show_link(text: '', klass: 'btn-light')
|
68
|
+
return unless Pundit.policy!(Current.user, object).show?
|
69
|
+
|
70
|
+
helpers.content_tag :span, rel: :tooltip, title: 'Ver' do
|
71
|
+
helpers.link_to object_url, data: { turbo_frame: :main },
|
72
|
+
class: "btn btn-sm #{klass}" do
|
73
|
+
helpers.content_tag(:span, nil, class: clase_icono('eye-fill')) + text
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
def export_link(url, text: '', klass: 'btn-info')
|
79
|
+
return unless Pundit.policy!(Current.user, object).export?
|
80
|
+
|
81
|
+
helpers.content_tag :span, rel: :tooltip, title: 'Exportar en excel' do
|
82
|
+
helpers.content_tag :a, target: '_blank',
|
83
|
+
class: "btn btn-sm #{klass}", href: url_change_format(url, 'xlsx') do
|
84
|
+
"#{helpers.content_tag(:span, nil, class: clase_icono('file-earmark-excel-fill'))} #{text}".html_safe
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
def new_link(remote: nil, klass: 'btn-warning')
|
90
|
+
return unless Pundit.policy!(Current.user, object).new?
|
91
|
+
|
92
|
+
helpers.content_tag :span, rel: :tooltip, title: submit_default_value do
|
93
|
+
helpers.link_to(new_object_url, class: "btn btn-sm #{klass}",
|
94
|
+
remote:) do
|
95
|
+
helpers.content_tag(:span, nil,
|
96
|
+
class: clase_icono('plus').to_s) + "<span class='d-none d-sm-inline'> #{submit_default_value}</span>".html_safe
|
97
|
+
end
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
def edit_object_url
|
102
|
+
helpers.url_for([:edit, target_object].flatten)
|
103
|
+
end
|
104
|
+
|
105
|
+
def new_object_url
|
106
|
+
"#{helpers.url_for(target_index)}/new"
|
107
|
+
end
|
108
|
+
|
109
|
+
def object_url
|
110
|
+
helpers.url_for(target_object)
|
111
|
+
end
|
112
|
+
|
113
|
+
def target_object
|
114
|
+
pg_namespace.present? ? [pg_namespace, object] : object
|
115
|
+
end
|
116
|
+
|
117
|
+
def target_index
|
118
|
+
pg_namespace.present? ? [pg_namespace, object.class] : object.class
|
119
|
+
end
|
120
|
+
|
121
|
+
# actionview-7.1.3.2/lib/action_view/helpers/form_helper.rb
|
122
|
+
def submit_default_value
|
123
|
+
key = :create
|
124
|
+
model = object.model_name.human
|
125
|
+
defaults = []
|
126
|
+
defaults << :"helpers.submit.#{object.model_name.i18n_key}.#{key}"
|
127
|
+
defaults << :"helpers.submit.#{key}"
|
128
|
+
defaults << "#{key.to_s.humanize} #{model}"
|
129
|
+
I18n.t(defaults.shift, model:, default: defaults)
|
130
|
+
end
|
131
|
+
|
132
|
+
private
|
133
|
+
|
134
|
+
def clase_icono(icono)
|
135
|
+
"bi bi-#{icono}"
|
136
|
+
end
|
137
|
+
end
|
138
|
+
end
|
@@ -4,11 +4,11 @@ module PgEngine
|
|
4
4
|
module FormHelper
|
5
5
|
def pg_form_for(object, *args, &)
|
6
6
|
resource = object
|
7
|
-
if object.is_a? PgEngine::
|
7
|
+
if object.is_a? PgEngine::BaseRecordDecorator
|
8
8
|
object = object.target_object
|
9
9
|
elsif object.is_a?(PgEngine::BaseRecord) &&
|
10
10
|
object.decorator_class.present? &&
|
11
|
-
object.decorator_class
|
11
|
+
object.decorator_class <= PgEngine::BaseRecordDecorator
|
12
12
|
object = object.decorate.target_object
|
13
13
|
end
|
14
14
|
# byebug
|
@@ -4,78 +4,89 @@ require 'rainbow'
|
|
4
4
|
|
5
5
|
# TODO: poder pasar blocks
|
6
6
|
|
7
|
-
def pg_err(
|
8
|
-
|
7
|
+
def pg_err(*args)
|
8
|
+
pg_log(:error, *args)
|
9
9
|
end
|
10
10
|
|
11
|
-
def pg_warn(
|
12
|
-
|
11
|
+
def pg_warn(*args)
|
12
|
+
pg_log(:warn, *args)
|
13
13
|
end
|
14
14
|
|
15
|
-
def
|
16
|
-
|
15
|
+
def pg_info(*args)
|
16
|
+
pg_log(:info, *args)
|
17
|
+
end
|
18
|
+
|
19
|
+
def pg_debug(*args)
|
20
|
+
pg_log(:debug, *args)
|
21
|
+
end
|
22
|
+
|
23
|
+
def pg_log(*args)
|
24
|
+
PgEngine::PgLogger.log(*args)
|
17
25
|
end
|
18
26
|
|
19
27
|
module PgEngine
|
20
28
|
class PgLogger
|
21
|
-
# Generalmente en test queremos que se lancen los errores, salvo
|
22
|
-
# cuando estamos testeando casos de error puntuales.
|
23
|
-
@raise_errors = Rails.env.test?
|
24
|
-
|
25
29
|
class << self
|
26
|
-
|
30
|
+
def log(type, *args)
|
31
|
+
notify_all(build_msg(*args), type)
|
32
|
+
end
|
27
33
|
|
28
|
-
|
29
|
-
raise obj if raise_errors
|
34
|
+
private
|
30
35
|
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
notify(mensaje, :error)
|
37
|
-
rescue StandardError => e
|
38
|
-
Rails.logger.error("ERROR al loguear error: #{e}")
|
36
|
+
def notify_all(mensaje, type)
|
37
|
+
send_to_logger(mensaje, type)
|
38
|
+
send_to_rollbar(mensaje, type)
|
39
|
+
send_to_stdout(mensaje, type) if ENV.fetch('LOG_TO_STDOUT', nil)
|
40
|
+
nil
|
39
41
|
end
|
40
42
|
|
41
|
-
|
42
|
-
mensaje = if obj.is_a?(Exception) && obj.backtrace.present?
|
43
|
-
"#{obj.inspect}\nBacktrace:\n#{cleaner.clean(obj.backtrace).join("\n")}"
|
44
|
-
else
|
45
|
-
obj
|
46
|
-
end
|
47
|
-
notify(mensaje, type)
|
48
|
-
rescue StandardError => e
|
49
|
-
Rails.logger.error("ERROR al loguear error: #{e}")
|
50
|
-
end
|
43
|
+
# Senders
|
51
44
|
|
52
|
-
|
45
|
+
def send_to_stdout(mensaje, type)
|
46
|
+
puts rainbow_wrap(mensaje, type)
|
47
|
+
end
|
53
48
|
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
nil
|
49
|
+
def send_to_rollbar(mensaje, type)
|
50
|
+
Rollbar.send(type, mensaje)
|
51
|
+
# Rollbar.send(type, "#{mensaje}\n\n#{bktrc.join("\n")}")
|
52
|
+
rescue StandardError => e
|
53
|
+
send_to_logger("Error notifying Rollbar #{e}", :error)
|
60
54
|
end
|
61
55
|
|
62
|
-
def
|
63
|
-
|
56
|
+
def send_to_logger(mensaje, type)
|
57
|
+
Rails.logger.send(type, rainbow_wrap(mensaje, type))
|
64
58
|
end
|
65
59
|
|
66
|
-
|
67
|
-
|
60
|
+
# Format
|
61
|
+
|
62
|
+
# TODO: loguear time
|
63
|
+
def build_msg(*args)
|
64
|
+
first = args.first
|
65
|
+
if first.is_a?(Exception) && first.backtrace.present?
|
66
|
+
<<~STR
|
67
|
+
#{titulo(*args)}
|
68
|
+
Exception Backtrace:
|
69
|
+
#{cleaner.clean(first.backtrace).join("\n")}
|
70
|
+
Caller Backtrace:
|
71
|
+
#{cleaner.clean(caller).join("\n")}
|
72
|
+
STR
|
73
|
+
else
|
74
|
+
<<~STR
|
75
|
+
#{titulo(*args)}
|
76
|
+
Caller Backtrace:
|
77
|
+
#{cleaner.clean(caller).join("\n")}
|
78
|
+
STR
|
79
|
+
end
|
80
|
+
rescue StandardError
|
81
|
+
send_to_logger('ERROR: PgLogger error building msg', :error)
|
68
82
|
end
|
69
83
|
|
70
|
-
def
|
71
|
-
|
72
|
-
bc.add_filter { |line| line.gsub(%r{.*pg_rails/}, '') }
|
73
|
-
bc.add_silencer { |line| /pg_logger/.match?(line) }
|
74
|
-
bc
|
84
|
+
def titulo(*args)
|
85
|
+
args.map { |obj| "#{obj.inspect} (#{obj.class})" }.join("\n")
|
75
86
|
end
|
76
87
|
|
77
|
-
def
|
78
|
-
|
88
|
+
def rainbow_wrap(mensaje, type)
|
89
|
+
Rainbow(mensaje).bold.send(color_for(type))
|
79
90
|
end
|
80
91
|
|
81
92
|
def color_for(type)
|
@@ -90,15 +101,15 @@ module PgEngine
|
|
90
101
|
:red
|
91
102
|
end
|
92
103
|
end
|
104
|
+
|
105
|
+
# Backtrace helpers
|
106
|
+
|
107
|
+
def cleaner
|
108
|
+
bc = ActiveSupport::BacktraceCleaner.new
|
109
|
+
bc.add_filter { |line| line.gsub(%r{.*pg_rails/}, '') }
|
110
|
+
bc.add_silencer { |line| /pg_logger/.match?(line) }
|
111
|
+
bc
|
112
|
+
end
|
93
113
|
end
|
94
114
|
end
|
95
115
|
end
|
96
|
-
|
97
|
-
# DEPRECATED
|
98
|
-
# Muestro el caller[1] para saber dónde se llamó a la función deprecada
|
99
|
-
# def deprecated(mensaje)
|
100
|
-
# titulo = Rainbow(" WARNING en #{caller[1]}").yellow.bold
|
101
|
-
# detalles = Rainbow(" #{mensaje}").yellow
|
102
|
-
# Rails.logger.warn("#{titulo}\n#{detalles}")
|
103
|
-
# Rollbar.warning("#{mensaje}\n\n#{caller.join("\n")}")
|
104
|
-
# end
|
@@ -39,12 +39,6 @@ describe DummyBaseController do
|
|
39
39
|
get :test_internal_error
|
40
40
|
end
|
41
41
|
|
42
|
-
around do |example|
|
43
|
-
PgEngine::PgLogger.raise_errors = false
|
44
|
-
example.run
|
45
|
-
PgEngine::PgLogger.raise_errors = true
|
46
|
-
end
|
47
|
-
|
48
42
|
it do
|
49
43
|
subject
|
50
44
|
expect(response).to have_http_status(:internal_server_error)
|
@@ -3,108 +3,54 @@ require 'rails_helper'
|
|
3
3
|
TYPES = %i[error warn info debug].freeze
|
4
4
|
|
5
5
|
describe PgEngine::PgLogger do
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
allow(Rollbar).to receive(type)
|
11
|
-
end
|
12
|
-
end
|
13
|
-
|
14
|
-
around do |example|
|
15
|
-
described_class.raise_errors = false
|
16
|
-
example.run
|
17
|
-
described_class.raise_errors = true
|
18
|
-
end
|
19
|
-
|
20
|
-
shared_examples 'logger' do |type|
|
21
|
-
it do
|
22
|
-
expect(Rails.logger).to have_received(type).twice
|
23
|
-
end
|
24
|
-
|
25
|
-
it do
|
26
|
-
expect(Rollbar).to have_received(type).once
|
27
|
-
end
|
28
|
-
end
|
29
|
-
|
30
|
-
context 'con string' do
|
31
|
-
before { pg_err('bla') }
|
32
|
-
|
33
|
-
it_behaves_like 'logger', :error
|
34
|
-
end
|
35
|
-
|
36
|
-
context 'con exception' do
|
37
|
-
before do
|
38
|
-
raise StandardError, 'bla'
|
39
|
-
rescue StandardError => e
|
40
|
-
pg_err(e)
|
41
|
-
end
|
42
|
-
|
43
|
-
it_behaves_like 'logger', :error
|
6
|
+
before do
|
7
|
+
TYPES.each do |type|
|
8
|
+
allow(Rails.logger).to receive(type)
|
9
|
+
allow(Rollbar).to receive(type)
|
44
10
|
end
|
45
11
|
end
|
46
12
|
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
allow(Rails.logger).to receive(type)
|
51
|
-
allow(Rollbar).to receive(type)
|
52
|
-
end
|
13
|
+
shared_examples 'logger' do |type|
|
14
|
+
it do
|
15
|
+
expect(Rails.logger).to have_received(type).once
|
53
16
|
end
|
54
17
|
|
55
|
-
|
56
|
-
|
57
|
-
expect(Rails.logger).to have_received(type).twice
|
58
|
-
end
|
59
|
-
|
60
|
-
it do
|
61
|
-
expect(Rollbar).to have_received(type).once
|
62
|
-
end
|
18
|
+
it do
|
19
|
+
expect(Rollbar).to have_received(type).once
|
63
20
|
end
|
21
|
+
end
|
64
22
|
|
23
|
+
describe '#pg_log' do
|
65
24
|
TYPES.each do |type|
|
66
25
|
context "con type #{type}" do
|
67
|
-
before {
|
26
|
+
before { pg_log(type, 'bla') }
|
68
27
|
|
69
28
|
it_behaves_like 'logger', type
|
70
29
|
end
|
71
30
|
end
|
31
|
+
end
|
72
32
|
|
73
|
-
|
74
|
-
|
75
|
-
raise StandardError, 'bla'
|
76
|
-
rescue StandardError => e
|
77
|
-
pg_warn(e)
|
78
|
-
end
|
33
|
+
describe '#pg_err' do
|
34
|
+
before { pg_err('bla') }
|
79
35
|
|
80
|
-
|
81
|
-
end
|
36
|
+
it_behaves_like 'logger', :error
|
82
37
|
end
|
83
38
|
|
84
|
-
describe '#
|
85
|
-
before
|
86
|
-
TYPES.each do |type|
|
87
|
-
allow(Rails.logger).to receive(type)
|
88
|
-
allow(Rollbar).to receive(type)
|
89
|
-
end
|
90
|
-
end
|
39
|
+
describe '#pg_debug' do
|
40
|
+
before { pg_debug('bla') }
|
91
41
|
|
92
|
-
|
93
|
-
|
94
|
-
expect(Rails.logger).to have_received(type).twice
|
95
|
-
end
|
42
|
+
it_behaves_like 'logger', :debug
|
43
|
+
end
|
96
44
|
|
97
|
-
|
98
|
-
|
99
|
-
end
|
100
|
-
end
|
45
|
+
describe '#pg_warn' do
|
46
|
+
before { pg_warn('bla') }
|
101
47
|
|
102
|
-
|
103
|
-
|
104
|
-
before { pg_log('bla', type) }
|
48
|
+
it_behaves_like 'logger', :warn
|
49
|
+
end
|
105
50
|
|
106
|
-
|
107
|
-
|
108
|
-
|
51
|
+
describe '#pg_info' do
|
52
|
+
before { pg_info('bla') }
|
53
|
+
|
54
|
+
it_behaves_like 'logger', :info
|
109
55
|
end
|
110
56
|
end
|
@@ -1,6 +1,6 @@
|
|
1
1
|
<%# locals: (error_msg: nil) %>
|
2
2
|
|
3
|
-
<div class="d-flex justify-content-around mt-2">
|
3
|
+
<div class="d-flex justify-content-around mt-2" data-turbo-temporary="true">
|
4
4
|
<div class="alert alert-danger d-flex align-items-center">
|
5
5
|
<div>
|
6
6
|
<span class="bi bi-emoji-dizzy fs-1 me-3"></span>
|
data/pg_rails/lib/version.rb
CHANGED
@@ -218,7 +218,7 @@ RSpec.describe <%= controller_class_name %>Controller do
|
|
218
218
|
<% else -%>
|
219
219
|
put :update, params: { id: <%= file_name %>.to_param, <%= nombre_tabla_completo_singular %>: valid_attributes }
|
220
220
|
<% end -%>
|
221
|
-
expect(response).to redirect_to([:<%= ns_prefix.first %>, <%= file_name
|
221
|
+
expect(response).to redirect_to([:<%= ns_prefix.first %>, <%= file_name %>])
|
222
222
|
end
|
223
223
|
end
|
224
224
|
<% if attributes.any? { |at| at.required? } -%>
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: pg_rails
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 7.0.8.pre.alpha.
|
4
|
+
version: 7.0.8.pre.alpha.73
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Martín Rosso
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2024-05-
|
11
|
+
date: 2024-05-17 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rails
|
@@ -927,6 +927,7 @@ files:
|
|
927
927
|
- pg_engine/app/decorators/email_decorator.rb
|
928
928
|
- pg_engine/app/decorators/mensaje_contacto_decorator.rb
|
929
929
|
- pg_engine/app/decorators/pg_engine/base_decorator.rb
|
930
|
+
- pg_engine/app/decorators/pg_engine/base_record_decorator.rb
|
930
931
|
- pg_engine/app/decorators/user_account_decorator.rb
|
931
932
|
- pg_engine/app/decorators/user_decorator.rb
|
932
933
|
- pg_engine/app/helpers/pg_engine/flash_helper.rb
|