pg_rails 7.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (48) hide show
  1. checksums.yaml +7 -0
  2. data/MIT-LICENSE +20 -0
  3. data/README.md +73 -0
  4. data/Rakefile +36 -0
  5. data/app/assets/javascripts/pg_rails/asociacion_creable.js +85 -0
  6. data/app/assets/javascripts/pg_rails/best_in_place_datepicker.js +58 -0
  7. data/app/assets/javascripts/pg_rails/librerias.js +13 -0
  8. data/app/assets/javascripts/pg_rails/librerias_b3.js +14 -0
  9. data/app/assets/javascripts/pg_rails/validaciones.js +44 -0
  10. data/app/assets/javascripts/pg_rails.js +318 -0
  11. data/app/assets/stylesheets/pg_rails/librerias.scss +5 -0
  12. data/app/assets/stylesheets/pg_rails/pg_chosen.scss +29 -0
  13. data/app/assets/stylesheets/pg_rails/pg_rails.scss +199 -0
  14. data/app/assets/stylesheets/pg_rails_b3.scss +10 -0
  15. data/app/assets/stylesheets/pg_rails_b4.scss +12 -0
  16. data/app/assets/stylesheets/pg_rails_b5.scss +1 -0
  17. data/app/controllers/pg_rails/application_controller.rb +316 -0
  18. data/app/controllers/pg_rails/editar_en_lugar_controller.rb +24 -0
  19. data/app/decorators/pg_rails/base_decorator.rb +120 -0
  20. data/app/helpers/pg_rails/editar_en_lugar_helper.rb +106 -0
  21. data/app/helpers/pg_rails/form_helper.rb +25 -0
  22. data/app/helpers/pg_rails/postgres_helper.rb +15 -0
  23. data/app/helpers/pg_rails/print_helper.rb +176 -0
  24. data/app/inputs/pg_rails/asociacion_creable_input.rb +72 -0
  25. data/app/inputs/pg_rails/fecha_input.rb +20 -0
  26. data/app/inputs/pg_rails/selects_dependientes_input.rb +9 -0
  27. data/app/lib/pg_form_builder.rb +31 -0
  28. data/app/lib/pg_rails/filtros_builder.rb +338 -0
  29. data/app/models/pg_rails/application_record.rb +51 -0
  30. data/app/policies/pg_rails/application_policy.rb +104 -0
  31. data/app/views/application/_abrir_modal.js.erb +14 -0
  32. data/app/views/application/_actualizar_smart_listing.html.slim +3 -0
  33. data/app/views/application/_cerrar_modal.js.erb +8 -0
  34. data/app/views/application/_modal_ajax_form.js.erb +7 -0
  35. data/config/brakeman.ignore +42 -0
  36. data/config/locales/es.yml +17 -0
  37. data/config/routes.rb +3 -0
  38. data/config/spring.rb +1 -0
  39. data/lib/pg_rails/configuracion.rb +24 -0
  40. data/lib/pg_rails/core_ext.rb +17 -0
  41. data/lib/pg_rails/engine.rb +42 -0
  42. data/lib/pg_rails/simple_form/initializer.rb +583 -0
  43. data/lib/pg_rails/utils/logueador.rb +39 -0
  44. data/lib/pg_rails/version.rb +5 -0
  45. data/lib/pg_rails.rb +23 -0
  46. data/lib/tasks/auto_anotar_modelos.rake +34 -0
  47. data/lib/tasks/pg_rails_tasks.rake +5 -0
  48. metadata +89 -0
@@ -0,0 +1,106 @@
1
+ # frozen_string_literal: true
2
+
3
+ module PgRails
4
+ module EditarEnLugarHelper
5
+ def editar_en_lugar(objeto, atributo, tipo = :input, url = nil, collection = nil)
6
+ url = pg_rails.editar_en_lugar_path(objeto) if url.nil?
7
+ if tipo == :checkbox
8
+ best_in_place objeto, atributo, url: url, as: tipo, collection: %w[No Si],
9
+ param: objeto.model_name.name
10
+ elsif tipo == :date
11
+ best_in_place objeto, atributo, url: url, as: tipo, display_with: lambda { |v|
12
+ dmy(v)
13
+ }, param: objeto.model_name.name
14
+ # best_in_place objeto, atributo, url: editar_en_lugarurl(objeto), as: tipo, display_with: lambda { |v| dmy(v) }, class: 'datefield'
15
+ elsif tipo == :textarea
16
+ funcion = lambda do |valor|
17
+ return unless valor.present?
18
+
19
+ valor.gsub!("\r\n", '<br>')
20
+ valor.gsub!("\n", '<br>')
21
+ valor.html_safe
22
+ end
23
+ best_in_place objeto, atributo, url: url, as: tipo, display_with: funcion,
24
+ param: objeto.model_name.name
25
+ elsif tipo == :select && collection.present?
26
+ best_in_place objeto, atributo, url: url, as: tipo, collection: collection,
27
+ param: objeto.model_name.name, value: objeto.send(atributo), inner_class: 'form-control'
28
+ else
29
+ best_in_place objeto, atributo, url: url, as: tipo, param: objeto.model_name.name
30
+ end
31
+ end
32
+
33
+ # Versión mejorada de editar_en_lugar, pero le pongo v2 por retrocompatibilidad
34
+ # La diferencia es que recibe un hash "options" y se le pueden agregar cualquiera de las options
35
+ # que admite best_in_place
36
+ def editar_en_lugar_v2(objeto, atributo, tipo = :input, options = {})
37
+ options[:url] = pg_rails.editar_en_lugar_path(objeto) if options[:url].nil?
38
+ options[:as] = tipo
39
+ options[:param] = objeto.model_name.name
40
+ options[:inner_class] = 'form-control' if options[:inner_class].nil?
41
+ options[:class] = 'editar_en_lugar_v2'
42
+ options[:html_attrs] = { autocomplete: :off }
43
+ # url = pg_rails.editar_en_lugar_path(objeto) if url.nil?
44
+ if tipo == :checkbox
45
+ options[:collection] = %w[No Si]
46
+
47
+ elsif tipo == :date
48
+ options[:display_with] = ->(v) { dmy(v) } if options[:display_with].nil?
49
+
50
+ elsif tipo == :textarea
51
+ funcion = lambda do |valor|
52
+ return unless valor.present?
53
+
54
+ valor.gsub!("\r\n", '<br>')
55
+ valor.gsub!("\n", '<br>')
56
+ valor.html_safe
57
+ end
58
+ options[:display_with] = funcion if options[:display_with].nil?
59
+
60
+ elsif tipo == :select && options[:collection].present?
61
+ options[:value] = objeto.send(atributo)
62
+ end
63
+
64
+ best_in_place objeto, atributo, options
65
+ end
66
+
67
+ # TODO: mover a otro helper
68
+ def encabezado(campo, options = {})
69
+ clase = (options[:clase] || @clase_modelo)
70
+ if options[:ordenable]
71
+ field = controller.instance_variable_get(:"@field")
72
+ direction = controller.instance_variable_get(:"@direction")
73
+ uri = URI.parse(request.url)
74
+ if uri.query.present?
75
+ cgi = CGI.parse(uri.query)
76
+ else
77
+ cgi = {}
78
+ end
79
+ cgi["order_by"] = campo
80
+ cgi["order_direction"] =
81
+ if field.to_s == campo.to_s && direction.to_s == 'asc'
82
+ 'desc'
83
+ else
84
+ 'asc'
85
+ end
86
+
87
+ if field.to_s == campo.to_s
88
+ symbol =
89
+ if direction.to_s == 'asc'
90
+ '<i class="bi bi-sort-down-alt" />'
91
+ elsif direction.to_s == 'desc'
92
+ '<i class="bi bi-sort-up" />'
93
+ end
94
+ else
95
+ symbol = ''
96
+ end
97
+
98
+ uri.query = cgi.map {|a,b| [a, (b.length == 1 ? b.first : b)]}.to_h.to_query
99
+
100
+ link_to(clase.human_attribute_name(campo), uri.to_s) + " #{symbol}".html_safe
101
+ else
102
+ clase.human_attribute_name(campo)
103
+ end
104
+ end
105
+ end
106
+ end
@@ -0,0 +1,25 @@
1
+ # frozen_string_literal: true
2
+
3
+ module PgRails
4
+ module FormHelper
5
+ def pg_form_for(object, *args, &block)
6
+ options = args.extract_options!
7
+
8
+ options[:builder] = PgFormBuilder
9
+ options[:html] ||= {}
10
+ options[:html][:class] = if options[:html].key?(:class)
11
+ ['pg-form', options[:html][:class]].compact
12
+ else
13
+ 'pg-form'
14
+ end
15
+
16
+ simple_form_for(object, *(args << options), &block)
17
+ end
18
+
19
+ def url_change_format(url)
20
+ uri = URI.parse(url)
21
+ uri.path = "#{uri.path}.xlsx"
22
+ uri.to_s
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ module PgRails
4
+ module PostgresHelper
5
+ # Le pone la timezone
6
+ def add_timezone(field)
7
+ "#{field}::TIMESTAMPTZ AT TIME ZONE INTERVAL '-03:00'::INTERVAL"
8
+ end
9
+
10
+ # Le pone la timezone y lo convierte a Date
11
+ def get_date_tz(field)
12
+ "(#{add_timezone(field)})::date"
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,176 @@
1
+ # frozen_string_literal: true
2
+
3
+ module PgRails
4
+ module PrintHelper
5
+ include ActionView::Helpers::NumberHelper
6
+
7
+ class FechaInvalidaError < StandardError
8
+ end
9
+
10
+ # Genera un falso link que al clickear carga la url por ajax
11
+ # Es igual a los links con remote: true, pero evita que abran el link en otra
12
+ # pestaña. Movido de probella
13
+ def pg_ajax_link(url, options = {})
14
+ content_tag :span, class: 'pg_ajax_link', data: options.merge(url: url) do
15
+ block_given? ? yield : ''
16
+ end
17
+ end
18
+
19
+ def mostrar_con_link(objeto, options = {})
20
+ return unless objeto.present?
21
+
22
+ if policy(objeto).show?
23
+ content = truncate_title(block_given? ? yield : objeto)
24
+ if options[:new_tab]
25
+ content += "&nbsp;<i class='fa fa-external-link'>".html_safe
26
+ link_to content.html_safe, objeto, target: :_blank
27
+ else
28
+ link_to content.html_safe, objeto
29
+ end
30
+ else
31
+ truncate_title(block_given? ? yield : objeto)
32
+ end
33
+ end
34
+
35
+ def truncate_title(input, length = 20)
36
+ string = input.to_s
37
+ if string.length > length
38
+ content_tag :span, title: input, rel: :tooltip do
39
+ truncate string, length: length
40
+ end
41
+ else
42
+ string
43
+ end
44
+ end
45
+
46
+ def print_cuit(cuit_number)
47
+ return '' unless cuit_number.present?
48
+
49
+ str = cuit_number.to_s
50
+ "#{str[0..1]}-#{str[2..9]}-#{str[10]}"
51
+ end
52
+
53
+ def dmy_time(date)
54
+ date = parsear_tiempo(date) if date.is_a? String
55
+ return unless date.present?
56
+
57
+ date.strftime('%d/%m/%Y %H:%M')
58
+ end
59
+
60
+ def dmy(date)
61
+ date = parsear_fecha(date) if date.is_a? String
62
+ return unless date.present?
63
+
64
+ date.strftime('%d/%m/%Y')
65
+ end
66
+
67
+ def ymd(date)
68
+ date = parsear_fecha(date) if date.is_a? String
69
+ return unless date.present?
70
+
71
+ date.strftime('%Y/%m/%d')
72
+ end
73
+
74
+ def dmyg(date)
75
+ date = parsear_fecha(date) if date.is_a? String
76
+ return unless date.present?
77
+
78
+ date.strftime('%d-%m-%Y')
79
+ end
80
+
81
+ def ymdg(date)
82
+ date = parsear_fecha(date) if date.is_a? String
83
+ return unless date.present?
84
+
85
+ date.strftime('%Y-%m-%d')
86
+ end
87
+
88
+ def myg(date)
89
+ date = parsear_fecha(date) if date.is_a? String
90
+ return unless date.present?
91
+
92
+ date.strftime('%m-%Y')
93
+ end
94
+
95
+ def print_number(number)
96
+ number_with_precision(number, delimiter: ',', separator: '.', precision: 2)
97
+ end
98
+
99
+ def print_number_coma(number)
100
+ number_with_precision(number, delimiter: '.', separator: ',', precision: 2)
101
+ end
102
+
103
+ def print_boolean(boolean)
104
+ return if boolean.nil?
105
+ return 'Si' if boolean
106
+
107
+ 'No'
108
+ end
109
+
110
+ def print_currency(number, moneda = 'pesos')
111
+ return unless number.present?
112
+
113
+ "<span class='currency #{moneda}'>#{number_with_precision(number, delimiter: '.',
114
+ separator: ',', precision: 2)}</span>".html_safe
115
+ end
116
+
117
+ def print_currency2(number, moneda = 'pesos')
118
+ return unless number.present?
119
+
120
+ "#{simbolo_moneda(moneda)} #{number_with_precision(number, delimiter: '.', separator: ',',
121
+ precision: 2)}"
122
+ end
123
+
124
+ def simbolo_moneda(moneda)
125
+ return '$' if moneda == 'pesos'
126
+ return 'U$S' if moneda == 'dolares'
127
+ return '€' if moneda == 'euros'
128
+ return 'R$' if moneda == 'reales'
129
+ return 'CLP' if moneda == 'pesos_chilenos'
130
+ return 'MXN' if moneda == 'pesos_mexicanos'
131
+
132
+ '$'
133
+ end
134
+
135
+ def print_value(nombre_clase, field, value)
136
+ if !value.nil? == value # es booleano
137
+ value ? 'Si' : 'No'
138
+ elsif value.nil?
139
+ '-'
140
+ else
141
+ begin
142
+ if nombre_clase.constantize.defined_enums[field].present?
143
+ valor = nombre_clase.constantize.defined_enums[field].invert[value]
144
+ I18n.t("enums.#{nombre_clase.downcase}.#{field}.#{valor}", default: valor)
145
+ else
146
+ truncate_title(value.to_s.encode('UTF-8', invalid: :replace, undef: :replace))
147
+ end
148
+ rescue NameError
149
+ truncate_title(value.to_s.encode('UTF-8', invalid: :replace, undef: :replace))
150
+ end
151
+ end
152
+ end
153
+
154
+ def parsear_tiempo(datetime)
155
+ return nil unless datetime.present?
156
+
157
+ DateTime.parse(datetime)
158
+ rescue ArgumentError
159
+ raise FechaInvalidaError, datetime
160
+ end
161
+
162
+ def parsear_fecha(date)
163
+ return nil unless date.present?
164
+
165
+ Date.parse(date)
166
+ rescue ArgumentError
167
+ raise FechaInvalidaError, date
168
+ end
169
+
170
+ def show_percentage(value)
171
+ return unless value.present?
172
+
173
+ "#{value.round(2)} %"
174
+ end
175
+ end
176
+ end
@@ -0,0 +1,72 @@
1
+ # frozen_string_literal: true
2
+
3
+ module PgRails
4
+ class AsociacionCreableInput < SimpleForm::Inputs::StringInput
5
+ include ActionView::Helpers::FormTagHelper
6
+
7
+ def boton(wrapper_options = {})
8
+ if PgRails.config.bootstrap_version >= 4
9
+ button_tag('', wrapper_options)
10
+ else
11
+ button_tag('<span class="caret">'.html_safe, wrapper_options)
12
+ end
13
+ end
14
+
15
+ def hidden_input(wrapper_options = {})
16
+ merged_input_options = merge_wrapper_options(input_html_options, wrapper_options)
17
+ merged_input_options = merge_wrapper_options(merged_input_options, { class: 'oculto' })
18
+ @builder.text_field(attribute_name, merged_input_options)
19
+ end
20
+
21
+ def input(wrapper_options = nil)
22
+ unless string?
23
+ input_html_classes.unshift('string')
24
+ input_html_options[:type] ||= input_type if html5?
25
+ end
26
+
27
+ merged_input_options = merge_wrapper_options(input_html_options, wrapper_options)
28
+ merged_input_options = merge_wrapper_options(merged_input_options, { class: 'keep-disabled' })
29
+
30
+ text_field_tag(nil, object.send(reflection.name).to_s, merged_input_options)
31
+ end
32
+
33
+ def seleccionar_asociado(wrapper_options = {})
34
+ icon = content_tag :span, nil, class: 'fa fa-hand-pointer'
35
+ merged_input_options = merge_wrapper_options(wrapper_options,
36
+ { class: 'seleccionar_asociado dropdown-item', href: 'javascript:void(0)',
37
+ data: { url: options[:url_seleccionar] } })
38
+ anchor = content_tag :a, "#{icon} Seleccionar".html_safe, merged_input_options
39
+ if PgRails.config.bootstrap_version >= 4
40
+ anchor
41
+ else
42
+ "<li>#{anchor}</li>".html_safe
43
+ end
44
+ end
45
+
46
+ def crear_asociado(wrapper_options = {})
47
+ icon = content_tag :span, nil, class: 'fa fa-plus-square'
48
+ merged_input_options = merge_wrapper_options(wrapper_options,
49
+ { class: 'crear_asociado dropdown-item', href: 'javascript:void(0)',
50
+ data: { url: options[:url_crear] } })
51
+ anchor = content_tag :a, "#{icon} Crear".html_safe, merged_input_options
52
+ if PgRails.config.bootstrap_version >= 4
53
+ anchor
54
+ else
55
+ "<li>#{anchor}</li>".html_safe
56
+ end
57
+ end
58
+
59
+ def borrar_seleccion(wrapper_options = {})
60
+ icon = content_tag :span, nil, class: 'fa fa-trash'
61
+ merged_input_options = merge_wrapper_options(wrapper_options,
62
+ { class: 'borrar_seleccion dropdown-item',
63
+ href: 'javascript:void(0)' })
64
+ anchor = content_tag :a, "#{icon} Borrar selección".html_safe, merged_input_options
65
+ if PgRails.config.bootstrap_version >= 4
66
+ anchor
67
+ else
68
+ "<li>#{anchor}</li>".html_safe
69
+ end
70
+ end
71
+ end
72
+ end
@@ -0,0 +1,20 @@
1
+ # frozen_string_literal: true
2
+
3
+ module PgRails
4
+ class FechaInput < SimpleForm::Inputs::StringInput
5
+ def input(wrapper_options = nil)
6
+ # esto es porque si no Rails llama a value_before_type_cast
7
+ if object.is_a? Draper::Decorator
8
+ # Salteo el decorator para que me tome la fecha con formato inglés
9
+ value = object.object.public_send(attribute_name)
10
+ else
11
+ value = object.public_send(attribute_name)
12
+ end
13
+ @input_type = 'date'
14
+
15
+ options = merge_wrapper_options({ value: value, class: '', autocomplete: 'off' },
16
+ wrapper_options)
17
+ super(options)
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,9 @@
1
+ # frozen_string_literal: true
2
+
3
+ module PgRails
4
+ class SelectsDependientesInput < SimpleForm::Inputs::CollectionSelectInput
5
+ include ActionView::Helpers::FormOptionsHelper
6
+
7
+ # TODO
8
+ end
9
+ end
@@ -0,0 +1,31 @@
1
+ # frozen_string_literal: true
2
+
3
+ # TODO: fix hackeo
4
+ # class PgFormBuilder < NestedForm::SimpleBuilder
5
+ class PgFormBuilder < SimpleForm::FormBuilder
6
+ def mensajes_de_error
7
+ return unless object.errors.present?
8
+
9
+ errors = object.errors.full_messages.map { |m| "<li>#{m}</li>" }.join
10
+ "<div class='alert alert-danger'>#{errors}</div>".html_safe
11
+ end
12
+
13
+ map_type :date, to: PgRails::FechaInput
14
+ # map_type :datetime, to: PgRails::FechaInput
15
+
16
+ def asociacion_creable(atributo, url_seleccionar, url_crear, options = {})
17
+ options[:as] = 'pg_rails/asociacion_creable'
18
+ options[:wrapper] = :asociacion_creable
19
+ options[:url_seleccionar] = url_seleccionar
20
+ options[:url_crear] = url_crear
21
+ options[:wrapper_html] = { class: 'completado' } if object.send(atributo).present?
22
+ association atributo, options
23
+ end
24
+
25
+ def submit_button(*args, &block)
26
+ options = args.extract_options!.dup
27
+ options[:class] = ['btn-primary', options[:class]].compact
28
+ args << options
29
+ send(:submit, *args, &block)
30
+ end
31
+ end