pg_rails 7.0.8.pre.alpha.72 → 7.0.8.pre.alpha.74

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: adf65a3ea20e9eedfa39e8e9a2b97ae48c773b5031d5dcffa4df98f8aa3f236f
4
- data.tar.gz: f966e9b52d9ac7f7021ff6f51f0a051397f3dc07dffd641c7cd75949c8d198a0
3
+ metadata.gz: 1be4c79ea65550474a25d90dfe6350776ee4729e1606fc4a33f9b2d510c00f6e
4
+ data.tar.gz: 2455fe21d800515773fc24a930e2ef78d79ce05ebda5e579ca900b7c6ab9e8c6
5
5
  SHA512:
6
- metadata.gz: b0d877134bf17b9b0653104475e5af6471850f805eaf5822e703ec17dc544fabf939dfdb3c74044969ebb1de635283d8872ee24fb324670bba5411480b0a605a
7
- data.tar.gz: 61af835abe1cde9a9edeab6565a6f61f6df641e2f6a3df8f4fedf869d252a7382ce6c8488bff4c080319a7eaa30c6ac40a3917f4e00bc3c77530b4920e8d11a3
6
+ metadata.gz: 5dec9355d0371042c062dd6b90ae6306196965ef7c41ed59e672dddd38f658b2428e230f1c3e6ed82b4cb1e82657f48b7372b1b05c7d7062ccbc07cac903c711
7
+ data.tar.gz: 135d91aed0d9fdfe6e7aaa61d98a8cc2773516a6649eca3a65ee5fe2b49b563879276143ee49f4228605fc7caea8bc9cb1ef5a25fed54624dad73c9ef7006aad
@@ -9,6 +9,12 @@ module PgAssociable
9
9
  MAXIMO_PARA_SELECT = 10
10
10
  # TODO: si está entre 10 y 50, habilitar un buscador por js
11
11
 
12
+ def pg_associable_pro(atributo, options = {})
13
+ return input(atributo, options) if options[:disabled]
14
+
15
+ select_pro(atributo, options, nil)
16
+ end
17
+
12
18
  def pg_associable(atributo, options = {})
13
19
  return input(atributo, options) if options[:disabled]
14
20
 
@@ -50,6 +56,9 @@ module PgAssociable
50
56
 
51
57
  def clase_asociacion(atributo)
52
58
  asociacion = object.class.reflect_on_all_associations.find { |a| a.name == atributo.to_sym }
59
+
60
+ raise PgEngine::Error, "no existe la asociación para el atributo: #{atributo}" if asociacion.blank?
61
+
53
62
  nombre_clase = asociacion.options[:class_name]
54
63
  nombre_clase = asociacion.name.to_s.camelize if nombre_clase.nil?
55
64
  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
@@ -15,8 +15,8 @@ class PgAssociableInput < SimpleForm::Inputs::StringInput
15
15
 
16
16
  def input(wrapper_options = nil)
17
17
  atributo = attribute_name.to_s.gsub('_id', '')
18
- url_modal = namespaced_path(clase_asociacion(atributo), prefix: :abrir_modal)
19
- url_search = namespaced_path(clase_asociacion(atributo), prefix: :buscar)
18
+ url_modal = options[:modal_url] || namespaced_path(clase_asociacion(atributo), prefix: :abrir_modal)
19
+ url_search = options[:buscar_url] || namespaced_path(clase_asociacion(atributo), prefix: :buscar)
20
20
 
21
21
  input_html_options[:data] = { url_search:, url_modal: }
22
22
  input_html_options[:type] = 'text'
@@ -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,54 +67,68 @@ export default class extends Controller {
54
67
  }, 200)
55
68
 
56
69
  this.input.addEventListener('blur', () => {
57
- this.input.placeholder = ''
58
- setTimeout(() => {
59
- if (!this.element.classList.contains('filled')) {
60
- this.input.value = ''
61
- this.resetResultados()
62
- }
63
- }, 200)
70
+ this.input.placeholder = this.originalPlaceholder
64
71
  })
65
72
  this.input.onfocus = () => {
66
73
  this.input.select()
67
74
  if (this.input.value.length === 0) {
68
75
  this.escribiAlgo()
69
76
  }
70
-
71
- if (!this.element.closest('.modal')) {
72
- const wHeight = window.visualViewport.height
73
- const scrollTop = document.scrollingElement.scrollTop
74
- const viewPortBottom = scrollTop + wHeight
75
- const swHeight = parseInt(this.subWrapper.style.maxHeight) + 20
76
- const elementTop = this.element.getBoundingClientRect().top + scrollTop
77
- const inputBottom = this.input.getBoundingClientRect().bottom + scrollTop
78
- const swBottom = inputBottom + swHeight
79
-
80
- if (swBottom > viewPortBottom) {
81
- document.scrollingElement.scroll({ top: elementTop })
82
- }
83
- }
77
+ this.autoScroll()
84
78
  }
85
79
  this.input.onkeyup = (e) => {
86
80
  if (this.input.value.length === 0) {
87
81
  this.escribiAlgo()
88
82
  }
89
- if (e.keyCode === 13) {
90
- doSearchBounce(true)
83
+
84
+ if ([37, 38, 39, 40].includes(e.keyCode)) {
85
+ // Arrow keys, do nothing
91
86
  } else {
92
- 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
+ }
93
97
  }
94
98
  }
95
99
  this.input.onkeydown = (e) => {
96
- if (e.keyCode === 13) {
100
+ console.log(e.keyCode)
101
+ if (e.keyCode === 13) { // Enter
97
102
  e.preventDefault()
98
103
  return false
99
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
+ }
100
110
  }
101
111
  this.setMaxHeight()
102
112
  }
103
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
+
104
130
  resetResultados () {
131
+ this.lastValue = null
105
132
  const rows = []
106
133
  if (this.element.dataset.puedeCrear) {
107
134
  rows.push(
@@ -146,6 +173,9 @@ export default class extends Controller {
146
173
  if (maxHeight < 200) {
147
174
  maxHeight = 200
148
175
  }
176
+ if (maxHeight > 400) {
177
+ maxHeight = 400
178
+ }
149
179
  if (bodyHeight < inputY + maxHeight) {
150
180
  document.body.style.height = `${inputY + maxHeight}px`
151
181
  }
@@ -175,7 +205,7 @@ export default class extends Controller {
175
205
  }
176
206
 
177
207
  escribiAlgo () {
178
- this.input.placeholder = 'Escribí algo para buscar'
208
+ this.input.placeholder = this.element.dataset.placeholder || 'Escribí algo para buscar'
179
209
  }
180
210
 
181
211
  buscando () {
@@ -207,13 +237,12 @@ export default class extends Controller {
207
237
  }
208
238
  this.lastValue = this.input.value
209
239
 
210
- const timerId = setTimeout(() => {
211
- this.buscando()
212
- }, 200)
213
- document.addEventListener('turbo:before-stream-render', function () {
214
- clearTimeout(timerId)
215
- // TODO: testear
216
- }, { 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)
217
246
  const elem = (
218
247
  <form method="post" action={this.input.dataset.urlSearch} data-turbo-stream="true">
219
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}"
@@ -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]
@@ -0,0 +1,5 @@
1
+ class PgTrgm < ActiveRecord::Migration[7.1]
2
+ def change
3
+ enable_extension "pg_trgm"
4
+ end
5
+ end
@@ -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,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module PgRails
4
- VERSION = '7.0.8-alpha.72'
4
+ VERSION = '7.0.8-alpha.74'
5
5
  end
@@ -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
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.72
4
+ version: 7.0.8.pre.alpha.74
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-15 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
@@ -514,6 +514,20 @@ dependencies:
514
514
  - - "~>"
515
515
  - !ruby/object:Gem::Version
516
516
  version: 1.2.14
517
+ - !ruby/object:Gem::Dependency
518
+ name: pg_search
519
+ requirement: !ruby/object:Gem::Requirement
520
+ requirements:
521
+ - - "~>"
522
+ - !ruby/object:Gem::Version
523
+ version: 2.3.6
524
+ type: :runtime
525
+ prerelease: false
526
+ version_requirements: !ruby/object:Gem::Requirement
527
+ requirements:
528
+ - - "~>"
529
+ - !ruby/object:Gem::Version
530
+ version: 2.3.6
517
531
  - !ruby/object:Gem::Dependency
518
532
  name: vcr
519
533
  requirement: !ruby/object:Gem::Requirement
@@ -927,6 +941,7 @@ files:
927
941
  - pg_engine/app/decorators/email_decorator.rb
928
942
  - pg_engine/app/decorators/mensaje_contacto_decorator.rb
929
943
  - pg_engine/app/decorators/pg_engine/base_decorator.rb
944
+ - pg_engine/app/decorators/pg_engine/base_record_decorator.rb
930
945
  - pg_engine/app/decorators/user_account_decorator.rb
931
946
  - pg_engine/app/decorators/user_decorator.rb
932
947
  - pg_engine/app/helpers/pg_engine/flash_helper.rb
@@ -1003,6 +1018,7 @@ files:
1003
1018
  - pg_engine/db/migrate/20240314114503_remove_hash_ids.rb
1004
1019
  - pg_engine/db/migrate/20240428152916_create_mensaje_contactos.rb
1005
1020
  - pg_engine/db/migrate/20240506194106_create_emails.rb
1021
+ - pg_engine/db/migrate/20240517174821_pg_trgm.rb
1006
1022
  - pg_engine/db/seeds.rb
1007
1023
  - pg_engine/lib/pg_engine.rb
1008
1024
  - pg_engine/lib/pg_engine/configuracion.rb