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.
Files changed (28) hide show
  1. checksums.yaml +4 -4
  2. data/pg_associable/app/assets/stylesheets/pg_associable.scss +2 -2
  3. data/pg_associable/app/helpers/pg_associable/form_builder_methods.rb +3 -0
  4. data/pg_associable/app/helpers/pg_associable/helpers.rb +3 -2
  5. data/pg_associable/app/javascript/asociable_controller.tsx +62 -27
  6. data/pg_associable/app/views/pg_associable/_resultados_inline.html.slim +2 -4
  7. data/pg_engine/app/assets/stylesheets/pg_rails_b5.scss +0 -1
  8. data/pg_engine/app/controllers/concerns/pg_engine/resource.rb +3 -3
  9. data/pg_engine/app/decorators/account_decorator.rb +1 -1
  10. data/pg_engine/app/decorators/email_decorator.rb +1 -1
  11. data/pg_engine/app/decorators/mensaje_contacto_decorator.rb +1 -1
  12. data/pg_engine/app/decorators/pg_engine/base_decorator.rb +6 -125
  13. data/pg_engine/app/decorators/pg_engine/base_record_decorator.rb +138 -0
  14. data/pg_engine/app/decorators/user_account_decorator.rb +1 -1
  15. data/pg_engine/app/decorators/user_decorator.rb +1 -1
  16. data/pg_engine/app/helpers/pg_engine/form_helper.rb +2 -2
  17. data/pg_engine/app/lib/pg_engine/devise_failure_app.rb +1 -0
  18. data/pg_engine/app/lib/pg_form_builder.rb +1 -0
  19. data/pg_engine/config/locales/es.yml +1 -1
  20. data/pg_engine/lib/pg_engine/utils/pg_logger.rb +69 -58
  21. data/pg_engine/spec/controllers/pg_engine/base_controller_spec.rb +0 -6
  22. data/pg_engine/spec/lib/pg_engine/utils/pg_engine/pg_logger_spec.rb +28 -82
  23. data/pg_layout/app/views/pg_layout/_error.html.erb +1 -1
  24. data/pg_rails/lib/version.rb +1 -1
  25. data/pg_rails/scss/bootstrap_overrides.scss +2 -0
  26. data/pg_scaffold/lib/generators/pg_decorator/pg_decorator_generator.rb +1 -1
  27. data/pg_scaffold/lib/generators/pg_rspec/scaffold/templates/controller_spec.rb +1 -1
  28. metadata +3 -2
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: b4f6156ab8c2dd7a84a7f7c05b79edd8a16117971770f240d9df6a2077520b41
4
- data.tar.gz: 25e4cc0e8f236fd58eff011dfcd8552f2fd08fe65a141c41eda336a422e4cd48
3
+ metadata.gz: 7fead42e7825fd503115e697fc2c276181f4eaf6accfd783c8ebd463cf5ae278
4
+ data.tar.gz: 0ba91edbd93dd46d83a1d2aee3e77aba5d2d2d28d38625ddcce287b8ecb2a59e
5
5
  SHA512:
6
- metadata.gz: 1e0b041666082f62e83941c080ecbf2f6a558d8917ebca54e95bbc7d7668189417a347670cb516ef60f38fbbdefe49b3332d3be94787cca8cdbd84c6c8798b52
7
- data.tar.gz: ebf99d77235eab17311d11c90b173b5b3ec9e8c15e0250dec98b6211f834e6452d9c0b7f30f426e529ee6be8ba62ef50853c6be44c918d20fab73a012bd33713
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: white;
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: white;
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
- @collection = policy_scope(@clase_modelo).kept.query(params[:query]).limit(MAX_RESULTS)
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
- if (e.keyCode === 13) {
84
- doSearchBounce(true)
83
+
84
+ if ([37, 38, 39, 40].includes(e.keyCode)) {
85
+ // Arrow keys, do nothing
85
86
  } else {
86
- doSearchBounce()
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
- if (e.keyCode === 13) {
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
- const timerId = setTimeout(() => {
205
- this.buscando()
206
- }, 200)
207
- document.addEventListener('turbo:before-stream-render', function () {
208
- clearTimeout(timerId)
209
- // TODO: testear
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 "#{params[:query]}"
11
+ li.list-group-item No hay resultados para "#{query}"
@@ -94,7 +94,6 @@ input[type=datetime-local], input[type=datetime] {
94
94
  --bs-border-radius-xl: 0.45rem;
95
95
  --bs-border-radius-xxl: 0.6rem;
96
96
  --bs-border-color: #a7b7bb;
97
- --bs-body-bg: #{$light};
98
97
  }
99
98
 
100
99
  // Alerts
@@ -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
- PgLogger.warn("No existe el campo \"#{field}\"", :warn)
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
- PgLogger.warn("Direction not valid: \"#{direction}\"", :warn)
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
- PgLogger.warn(e, :warn)
300
+ pg_warn(e)
301
301
  scope
302
302
  end
303
303
 
@@ -2,7 +2,7 @@
2
2
 
3
3
  # generado con pg_rails
4
4
 
5
- class AccountDecorator < PgEngine::BaseDecorator
5
+ class AccountDecorator < PgEngine::BaseRecordDecorator
6
6
  delegate_all
7
7
 
8
8
  # Define presentation-specific methods here. Helpers are accessed through
@@ -2,7 +2,7 @@
2
2
 
3
3
  # generado con pg_rails
4
4
 
5
- class EmailDecorator < PgEngine::BaseDecorator
5
+ class EmailDecorator < PgEngine::BaseRecordDecorator
6
6
  delegate_all
7
7
 
8
8
  # Define presentation-specific methods here. Helpers are accessed through
@@ -2,7 +2,7 @@
2
2
 
3
3
  # generado con pg_rails
4
4
 
5
- class MensajeContactoDecorator < PgEngine::BaseDecorator
5
+ class MensajeContactoDecorator < PgEngine::BaseRecordDecorator
6
6
  delegate_all
7
7
 
8
8
  # Define presentation-specific methods here. Helpers are accessed through
@@ -1,132 +1,13 @@
1
- # frozen_string_literal: true
2
-
3
1
  module PgEngine
4
- class BaseDecorator < 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
- 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
- def export_link(url, text: '', klass: 'btn-info')
73
- return unless Pundit.policy!(Current.user, object).export?
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
@@ -2,7 +2,7 @@
2
2
 
3
3
  # generado con pg_rails
4
4
 
5
- class UserAccountDecorator < PgEngine::BaseDecorator
5
+ class UserAccountDecorator < PgEngine::BaseRecordDecorator
6
6
  delegate_all
7
7
 
8
8
  def profiles
@@ -2,7 +2,7 @@
2
2
 
3
3
  # generado con pg_rails
4
4
 
5
- class UserDecorator < PgEngine::BaseDecorator
5
+ class UserDecorator < PgEngine::BaseRecordDecorator
6
6
  delegate_all
7
7
 
8
8
  def default_module
@@ -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::BaseDecorator
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 < PgEngine::BaseDecorator
11
+ object.decorator_class <= PgEngine::BaseRecordDecorator
12
12
  object = object.decorate.target_object
13
13
  end
14
14
  # byebug
@@ -21,6 +21,7 @@ module PgEngine
21
21
  def render_unconfirmed
22
22
  self.content_type = 'text/vnd.turbo-stream.html'
23
23
  self.status = 200
24
+ # TODO: poner data-turbo-temporary?
24
25
  self.response_body = <<~HTML
25
26
  <turbo-stream action="update" target="flash">
26
27
  <template>
@@ -27,6 +27,7 @@ class PgFormBuilder < SimpleForm::FormBuilder
27
27
  end
28
28
 
29
29
  def mensajes_de_error
30
+ # TODO: poner data-turbo-temporary?
30
31
  title = error_notification(message: mensaje, class: 'text-danger mb-2 error-title') if mensaje
31
32
 
32
33
  base_errors = object.errors[:base]
@@ -80,7 +80,7 @@ es:
80
80
  activerecord:
81
81
  attributes:
82
82
  user:
83
- remember_me: Mantener sesión abierta en este navegador
83
+ remember_me: Recordarme en este navegador
84
84
  email:
85
85
  from_name: Remitente
86
86
  to: Destinatario
@@ -4,78 +4,89 @@ require 'rainbow'
4
4
 
5
5
  # TODO: poder pasar blocks
6
6
 
7
- def pg_err(obj)
8
- PgEngine::PgLogger.error(obj)
7
+ def pg_err(*args)
8
+ pg_log(:error, *args)
9
9
  end
10
10
 
11
- def pg_warn(obj, type = :error)
12
- PgEngine::PgLogger.warn(obj, type)
11
+ def pg_warn(*args)
12
+ pg_log(:warn, *args)
13
13
  end
14
14
 
15
- def pg_log(obj, type = :debug)
16
- PgEngine::PgLogger.warn("#{obj.inspect} (#{obj.class})", type)
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
- attr_accessor :raise_errors
30
+ def log(type, *args)
31
+ notify_all(build_msg(*args), type)
32
+ end
27
33
 
28
- def error(obj)
29
- raise obj if raise_errors
34
+ private
30
35
 
31
- mensaje = if obj.is_a?(Exception) && obj.backtrace.present?
32
- "#{obj.inspect}\nBacktrace:\n#{cleaner.clean(obj.backtrace).join("\n")}"
33
- else
34
- obj
35
- end
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
- def warn(obj, type = :error)
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
- private
45
+ def send_to_stdout(mensaje, type)
46
+ puts rainbow_wrap(mensaje, type)
47
+ end
53
48
 
54
- # TODO: loguear time
55
- def notify(mensaje, type)
56
- Rails.logger.send(type, titulo(mensaje, type))
57
- Rails.logger.send(type, detalles(type))
58
- Rollbar.send(type, "#{mensaje}\n\n#{bktrc.join("\n")}")
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 titulo(mensaje, type)
63
- Rainbow("#{type.to_s.upcase}: #{mensaje}").bold.send(color_for(type))
56
+ def send_to_logger(mensaje, type)
57
+ Rails.logger.send(type, rainbow_wrap(mensaje, type))
64
58
  end
65
59
 
66
- def detalles(type)
67
- Rainbow(" called in #{bktrc[0]}").send(color_for(type))
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 cleaner
71
- bc = ActiveSupport::BacktraceCleaner.new
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 bktrc
78
- cleaner.clean(caller)
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
- describe '#pg_err' do
7
- before do
8
- TYPES.each do |type|
9
- allow(Rails.logger).to receive(type)
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
- describe '#pg_warn' do
48
- before do
49
- TYPES.each do |type|
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
- shared_examples 'logger' do |type|
56
- it do
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 { pg_warn('bla', type) }
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
- context 'con exception' do
74
- before do
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
- it_behaves_like 'logger', :error
81
- end
36
+ it_behaves_like 'logger', :error
82
37
  end
83
38
 
84
- describe '#pg_log' do
85
- before do
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
- shared_examples 'logger' do |type|
93
- it do
94
- expect(Rails.logger).to have_received(type).twice
95
- end
42
+ it_behaves_like 'logger', :debug
43
+ end
96
44
 
97
- it do
98
- expect(Rollbar).to have_received(type).once
99
- end
100
- end
45
+ describe '#pg_warn' do
46
+ before { pg_warn('bla') }
101
47
 
102
- TYPES.each do |type|
103
- context "con type #{type}" do
104
- before { pg_log('bla', type) }
48
+ it_behaves_like 'logger', :warn
49
+ end
105
50
 
106
- it_behaves_like 'logger', type
107
- end
108
- end
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>
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module PgRails
4
- VERSION = '7.0.8-alpha.71'
4
+ VERSION = '7.0.8-alpha.73'
5
5
  end
@@ -9,3 +9,5 @@ $warning-border-subtle: tint-color($warning, 20%);
9
9
  // $danger-border-subtle: tint-color($danger, 60%);
10
10
 
11
11
  $btn-close-focus-shadow: none;
12
+
13
+ $body-bg: $light;
@@ -26,6 +26,6 @@ class PgDecoratorGenerator < Rails::Generators::DecoratorGenerator
26
26
  end
27
27
 
28
28
  def parent_class_name
29
- 'PgEngine::BaseDecorator'
29
+ 'PgEngine::BaseRecordDecorator'
30
30
  end
31
31
  end
@@ -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 %>.decorate.target_object])
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.71
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-14 00:00:00.000000000 Z
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