pg_rails 7.0.8.pre.alpha.71 → 7.0.8.pre.alpha.73

Sign up to get free protection for your applications and to get access to all the features.
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