pg_rails 7.0.8.pre.alpha.13 → 7.0.8.pre.alpha.15

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 (49) hide show
  1. checksums.yaml +4 -4
  2. data/pg_engine/app/controllers/admin/accounts_controller.rb +2 -0
  3. data/pg_engine/app/controllers/admin/user_accounts_controller.rb +2 -0
  4. data/pg_engine/app/controllers/admin/users_controller.rb +2 -0
  5. data/pg_engine/app/controllers/concerns/pg_engine/resource.rb +8 -3
  6. data/pg_engine/app/controllers/pg_engine/base_controller.rb +8 -7
  7. data/pg_engine/app/helpers/pg_engine/pg_rails_helper.rb +23 -0
  8. data/pg_engine/app/models/user.rb +18 -3
  9. data/pg_engine/config/initializers/anycable.rb +13 -0
  10. data/pg_engine/config/initializers/cable_ready.rb +29 -0
  11. data/pg_engine/config/initializers/rollbar.rb +78 -0
  12. data/pg_engine/db/seeds.rb +1 -1
  13. data/pg_engine/lib/pg_engine/error.rb +4 -0
  14. data/pg_engine/lib/pg_engine/utils/pg_logger.rb +76 -25
  15. data/pg_engine/lib/pg_engine.rb +1 -0
  16. data/pg_engine/lib/tasks/auto_anotar_modelos.rake +1 -3
  17. data/pg_engine/spec/controllers/admin/user_accounts_controller_spec.rb +2 -2
  18. data/pg_engine/spec/controllers/concerns/pg_engine/resource_helper_spec.rb +67 -0
  19. data/pg_engine/spec/factories/users.rb +1 -8
  20. data/pg_engine/spec/helpers/pg_engine/pg_rails_helper_spec.rb +38 -0
  21. data/pg_engine/spec/lib/pg_engine/utils/pg_engine/pg_logger_spec.rb +104 -0
  22. data/pg_engine/spec/models/user_spec.rb +39 -0
  23. data/pg_layout/app/assets/stylesheets/animations.scss +30 -0
  24. data/pg_layout/{index.js → app/javascript/application.js} +6 -10
  25. data/pg_layout/app/javascript/channels/consumer.js +8 -0
  26. data/pg_layout/app/javascript/channels/index.js +4 -0
  27. data/pg_layout/app/javascript/config/cable_ready.js +24 -0
  28. data/pg_layout/app/javascript/config/index.js +1 -0
  29. data/pg_layout/app/javascript/controllers/application.js +4 -0
  30. data/pg_layout/app/javascript/controllers/fadein_onload_controller.js +20 -0
  31. data/pg_layout/app/javascript/controllers/index.js +11 -0
  32. data/pg_layout/app/javascript/{navbar_controller.js → controllers/navbar_controller.js} +1 -1
  33. data/pg_layout/app/javascript/utils/utils.ts +43 -0
  34. data/pg_layout/app/views/layouts/pg_layout/devise.html.slim +2 -1
  35. data/pg_layout/app/views/layouts/pg_layout/layout.html.slim +6 -1
  36. data/pg_layout/app/views/pg_layout/_flash.html.slim +2 -0
  37. data/pg_layout/app/views/pg_layout/_rollbar.html.erb +28 -0
  38. data/pg_rails/js/index.js +1 -1
  39. data/pg_rails/lib/pg_rails/vcr_support.rb +28 -0
  40. data/pg_rails/lib/version.rb +1 -1
  41. data/pg_rails/scss/pg_rails.scss +1 -0
  42. data/pg_scaffold/lib/generators/pg_scaffold/templates/controller.rb +2 -0
  43. data/pg_scaffold/spec/generators_spec.rb +1 -1
  44. metadata +26 -9
  45. data/pg_layout/app/javascript/utils.ts +0 -19
  46. /data/pg_engine/spec/{controllers/concerns → lib}/pg_engine/error_helper_spec.rb +0 -0
  47. /data/pg_layout/app/javascript/{nested_controller.js → controllers/nested_controller.js} +0 -0
  48. /data/pg_layout/app/javascript/{pg_form_controller.js → controllers/pg_form_controller.js} +0 -0
  49. /data/pg_layout/app/javascript/{cookies.js → utils/cookies.js} +0 -0
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: fbc26d936f0bc92d89ee7125589969f980d4f4916bb49da05772425291b94306
4
- data.tar.gz: d144737ca5915119e635c8cf5b9cf9f78ad466c68ed7be79c0487f2e39e59c41
3
+ metadata.gz: 917b686081345cf1d79fb59f3d05a3d288f71f6a9ac223cfa40100a5e897b1c3
4
+ data.tar.gz: c21bd06ca22bbabf0701fe3450c09e034d037b7fad3d4bddf4dd6fbd748f6c8d
5
5
  SHA512:
6
- metadata.gz: d050775c0f3f3fec73e49eb17a25b9eb4aae187839045fa5323cefe842cf664806e1aaf9c43e896f517a8ec4a4a9d736c3a988c3398995feeb85fb35564820be
7
- data.tar.gz: cb652380ec0912c029522440634ce22016a7d6edf7d6b073e559664d96e181fcc229a3aa6e1b9861ab47107f217348b7e7be275874babb35f3a6494d0080653b
6
+ metadata.gz: ac9c3923a030194773608e8c1231b20044b835aed08b4d9e45705828de5dee18bd770f5736b1525044f0e8da7144ae210552ac5c4f475dea8f4e2e40d2ea4a9c
7
+ data.tar.gz: cf395aec16915d30e753bebe8120f8ee9255c8bcd8631b621b72bb12622a94aa26b9aa8e09cabce2d6990a6829e87695d946b1a7af7ce8443dcffd689fd40c83
@@ -4,6 +4,8 @@
4
4
 
5
5
  module Admin
6
6
  class AccountsController < AdminController
7
+ include PgEngine::Resource
8
+
7
9
  before_action { @clase_modelo = Account }
8
10
 
9
11
  before_action(only: :index) { authorize Account }
@@ -4,6 +4,8 @@
4
4
 
5
5
  module Admin
6
6
  class UserAccountsController < AdminController
7
+ include PgEngine::Resource
8
+
7
9
  before_action { @clase_modelo = UserAccount }
8
10
 
9
11
  before_action(only: :index) { authorize UserAccount }
@@ -4,6 +4,8 @@
4
4
 
5
5
  module Admin
6
6
  class UsersController < AdminController
7
+ include PgEngine::Resource
8
+
7
9
  before_action { @clase_modelo = User }
8
10
 
9
11
  before_action(only: :index) { authorize User }
@@ -210,7 +210,7 @@ module PgEngine
210
210
  def buscar_instancia
211
211
  if Object.const_defined?('FriendlyId') && @clase_modelo.is_a?(FriendlyId)
212
212
  @clase_modelo.friendly.find(params[:id])
213
- elsif @clase_modelo.respond_to? :find_by_hashid
213
+ elsif @clase_modelo.respond_to? :find_by_hashid!
214
214
  # rubocop:disable Rails/DynamicFindBy
215
215
  @clase_modelo.find_by_hashid!(params[:id])
216
216
  # rubocop:enable Rails/DynamicFindBy
@@ -272,8 +272,13 @@ module PgEngine
272
272
  end
273
273
 
274
274
  def do_sort(scope, field, direction)
275
+ # TODO: restringir ciertos campos?
275
276
  unless scope.model.column_names.include? field.to_s
276
- PgLogger.warning("No existe el campo \"#{field}\"")
277
+ PgLogger.warn("No existe el campo \"#{field}\"", :warn)
278
+ return scope
279
+ end
280
+ unless direction.to_sym.in? %i[asc desc]
281
+ PgLogger.warn("Direction not valid: \"#{direction}\"", :warn)
277
282
  return scope
278
283
  end
279
284
  scope = scope.order(field => direction)
@@ -281,7 +286,7 @@ module PgEngine
281
286
  instance_variable_set(:@direction, direction)
282
287
  scope
283
288
  rescue ArgumentError => e
284
- PgLogger.warning(e.to_s)
289
+ PgLogger.warn(e, :warn)
285
290
  scope
286
291
  end
287
292
 
@@ -40,13 +40,14 @@ module PgEngine
40
40
 
41
41
  protected
42
42
 
43
- def default_url_options(options = {})
44
- if Rails.env.production?
45
- options.merge(protocol: 'https')
46
- else
47
- options
48
- end
49
- end
43
+ # TODO: ver qué pasa en producción
44
+ # def default_url_options(options = {})
45
+ # if Rails.env.production?
46
+ # options.merge(protocol: 'https')
47
+ # else
48
+ # options
49
+ # end
50
+ # end
50
51
 
51
52
  def fecha_invalida
52
53
  respond_to do |format|
@@ -7,5 +7,28 @@ module PgEngine
7
7
  def current_account
8
8
  current_user&.current_account
9
9
  end
10
+
11
+ def img_placeholder(src: nil, width: '100%', height: '100%', fade_in: false, **img_opts)
12
+ if fade_in || src.nil?
13
+ img_opts = img_opts.merge(style: [img_opts[:style], 'display:none'].compact.join(';'))
14
+ do_placeholder(src, width:, height:, **img_opts)
15
+ else
16
+ image_tag src, **img_opts
17
+ end
18
+ end
19
+
20
+ private
21
+
22
+ def do_placeholder(src = nil, width: '100%', height: '100%', **img_opts)
23
+ content_tag('div', class: 'placeholder-glow', style: "width: #{width}; height: #{height}") do
24
+ content_tag('div', class: 'placeholder w-100 h-100') do
25
+ if src.present?
26
+ image_tag src,
27
+ 'data-controller': 'fadein_onload',
28
+ **img_opts
29
+ end
30
+ end
31
+ end
32
+ end
10
33
  end
11
34
  end
@@ -27,10 +27,8 @@
27
27
  # created_at :datetime not null
28
28
  # updated_at :datetime not null
29
29
  #
30
- class User < ApplicationRecord
31
- # ApplicationRecord should be defined on Application
32
30
 
33
- # Include default devise modules. Others available are:
31
+ class User < ApplicationRecord
34
32
  devise :database_authenticatable, :registerable,
35
33
  :recoverable, :rememberable,
36
34
  :lockable, :timeoutable, :trackable, :confirmable
@@ -51,6 +49,19 @@ class User < ApplicationRecord
51
49
  validates_length_of :password, if: :password_required?, within: 6..128,
52
50
  message: 'es demasiado corta (6 caracteres mínimo)'
53
51
 
52
+ attr_accessor :orphan
53
+
54
+ after_create do
55
+ # TODO: si fue invitado, sumar a la account del invitador
56
+ create_account unless orphan
57
+ end
58
+
59
+ def create_account
60
+ account = Account.create(nombre: email, plan: 0)
61
+ ua = user_accounts.create(account:)
62
+ raise(ActiveRecord::Rollback) unless ua.persisted?
63
+ end
64
+
54
65
  def password_required?
55
66
  !persisted? || !password.nil? || !password_confirmation.nil?
56
67
  end
@@ -65,7 +76,11 @@ class User < ApplicationRecord
65
76
  "#{nombre} #{apellido}"
66
77
  end
67
78
 
79
+ class Error < StandardError; end
80
+
68
81
  def current_account
82
+ raise Error, 'El usuario debe tener cuenta' if accounts.empty?
83
+
69
84
  accounts.first
70
85
  end
71
86
  end
@@ -0,0 +1,13 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Add Warden middleware to AnyCable stack to allow accessing
4
+ # Devise current user via `env["warden"].user`.
5
+ #
6
+ # See https://docs.anycable.io/ruby/authentication
7
+ #
8
+ # NOTE: This is only needed in environments where you use AnyCable.
9
+ if defined?(AnyCable::Rails)
10
+ AnyCable::Rails::Rack.middleware.use Warden::Manager do |config|
11
+ Devise.warden_config = config
12
+ end
13
+ end
@@ -0,0 +1,29 @@
1
+ # frozen_string_literal: true
2
+ Dotenv.load
3
+
4
+ CableReady.configure do |config|
5
+ # Enable/disable exiting / warning when the sanity checks fail options:
6
+ # `:exit` or `:warn` or `:ignore`
7
+ #
8
+ # config.on_failed_sanity_checks = :exit
9
+
10
+ # Enable/disable assets compilation
11
+ # `true` or `false`
12
+ #
13
+ # config.precompile_assets = true
14
+
15
+ # Define your own custom operations
16
+ # https://cableready.stimulusreflex.com/customization#custom-operations
17
+ #
18
+ # config.add_operation_name :jazz_hands
19
+
20
+ # Change the default Active Job queue used for broadcast_later and broadcast_later_to
21
+ #
22
+ # config.broadcast_job_queue = :default
23
+
24
+ # Specify a default debounce time for CableReady::Updatable callbacks
25
+ # Doing so is a best practice to avoid heavy ActionCable traffic
26
+ #
27
+ # config.updatable_debounce_time = 0.1.seconds
28
+ config.verifier_key = ENV.fetch('ANYCABLE_CABLE_READY_KEY')
29
+ end
@@ -0,0 +1,78 @@
1
+ Rollbar.configure do |config|
2
+ # Without configuration, Rollbar is enabled in all environments.
3
+ # To disable in specific environments, set config.enabled=false.
4
+
5
+ config.access_token = ENV['ROLLBAR_ACCESS_TOKEN']
6
+
7
+ # Here we'll disable in 'test':
8
+ if Rails.env.test?
9
+ config.enabled = false
10
+ end
11
+
12
+ # By default, Rollbar will try to call the `current_user` controller method
13
+ # to fetch the logged-in user object, and then call that object's `id`
14
+ # method to fetch this property. To customize:
15
+ # config.person_method = "my_current_user"
16
+ # config.person_id_method = "my_id"
17
+
18
+ # Additionally, you may specify the following:
19
+ # config.person_username_method = "username"
20
+ # config.person_email_method = "email"
21
+
22
+ # If you want to attach custom data to all exception and message reports,
23
+ # provide a lambda like the following. It should return a hash.
24
+ # config.custom_data_method = lambda { {:some_key => "some_value" } }
25
+
26
+ # Add exception class names to the exception_level_filters hash to
27
+ # change the level that exception is reported at. Note that if an exception
28
+ # has already been reported and logged the level will need to be changed
29
+ # via the rollbar interface.
30
+ # Valid levels: 'critical', 'error', 'warning', 'info', 'debug', 'ignore'
31
+ # 'ignore' will cause the exception to not be reported at all.
32
+ # config.exception_level_filters.merge!('MyCriticalException' => 'critical')
33
+ #
34
+ # You can also specify a callable, which will be called with the exception instance.
35
+ # config.exception_level_filters.merge!('MyCriticalException' => lambda { |e| 'critical' })
36
+
37
+ config.exception_level_filters.merge!({
38
+ 'ActionController::RoutingError' => 'ignore',
39
+ 'ActionDispatch::Http::MimeNegotiation::InvalidType' => 'ignore',
40
+ 'MailDeliveryTemporalError' => 'warning',
41
+ })
42
+
43
+
44
+ # Enable asynchronous reporting (uses girl_friday or Threading if girl_friday
45
+ # is not installed)
46
+ # config.use_async = true
47
+ # Supply your own async handler:
48
+ # config.async_handler = Proc.new { |payload|
49
+ # Thread.new { Rollbar.process_from_async_handler(payload) }
50
+ # }
51
+
52
+ # Enable asynchronous reporting (using sucker_punch)
53
+ # config.use_sucker_punch
54
+
55
+ # Enable delayed reporting (using Sidekiq)
56
+ # config.use_sidekiq
57
+ # You can supply custom Sidekiq options:
58
+ # config.use_sidekiq 'queue' => 'default'
59
+
60
+ # If your application runs behind a proxy server, you can set proxy parameters here.
61
+ # If https_proxy is set in your environment, that will be used. Settings here have precedence.
62
+ # The :host key is mandatory and must include the URL scheme (e.g. 'http://'), all other fields
63
+ # are optional.
64
+ #
65
+ # config.proxy = {
66
+ # host: 'http://some.proxy.server',
67
+ # port: 80,
68
+ # user: 'username_if_auth_required',
69
+ # password: 'password_if_auth_required'
70
+ # }
71
+
72
+ # If you run your staging application instance in production environment then
73
+ # you'll want to override the environment reported by `Rails.env` with an
74
+ # environment variable like this: `ROLLBAR_ENV=staging`. This is a recommended
75
+ # setup for Heroku. See:
76
+ # https://devcenter.heroku.com/articles/deploying-to-a-custom-rails-environment
77
+ config.environment = ENV['ROLLBAR_ENV'].presence || Rails.env
78
+ end
@@ -1,4 +1,4 @@
1
- DatabaseCleaner.clean_with(:truncation, except: %w(ar_internal_metadata users accounts user_accounts))
1
+ DatabaseCleaner.clean_with(:truncation, except: %w(ar_internal_metadata))
2
2
 
3
3
  MAIL = 'mrosso10@gmail.com'
4
4
 
@@ -0,0 +1,4 @@
1
+ module PgEngine
2
+ class Error < StandardError
3
+ end
4
+ end
@@ -2,42 +2,93 @@
2
2
 
3
3
  require 'rainbow'
4
4
 
5
+ # TODO: poder pasar blocks
6
+
7
+ def pg_err(obj)
8
+ PgEngine::PgLogger.error(obj)
9
+ end
10
+
11
+ def pg_warn(obj, type = :error)
12
+ PgEngine::PgLogger.warn(obj, type)
13
+ end
14
+
15
+ def pg_log(obj, type = :debug)
16
+ PgEngine::PgLogger.warn("#{obj.inspect} (#{obj.class})", type)
17
+ end
18
+
5
19
  module PgEngine
6
20
  class PgLogger
7
21
  class << self
8
- def deprecated(mensaje)
9
- titulo = Rainbow(" WARNING en #{caller[1]}").yellow.bold
10
- detalles = Rainbow(" #{mensaje}").yellow
11
- Rails.logger.warn("#{titulo}\n#{detalles}")
12
- Rollbar.warning("#{mensaje}\n\n#{caller.join("\n")}")
22
+ # DEPRECATED
23
+ # Muestro el caller[1] para saber dónde se llamó a la función deprecada
24
+ # def deprecated(mensaje)
25
+ # titulo = Rainbow(" WARNING en #{caller[1]}").yellow.bold
26
+ # detalles = Rainbow(" #{mensaje}").yellow
27
+ # Rails.logger.warn("#{titulo}\n#{detalles}")
28
+ # Rollbar.warning("#{mensaje}\n\n#{caller.join("\n")}")
29
+ # end
30
+
31
+ def error(obj)
32
+ # TODO: arreglar
33
+ # Si es dev o test lanzo el error y si no, lo logueo para que no le salte al usuario
34
+ # raise obj if Rails.env.local? && ENV.fetch('RAISE_ERRORS', nil).present?
35
+
36
+ mensaje = if obj.is_a?(Exception) && obj.backtrace.present?
37
+ "#{obj.inspect}\nBacktrace:\n#{cleaner.clean(obj.backtrace).join("\n")}"
38
+ else
39
+ obj
40
+ end
41
+ notify(mensaje, :error)
42
+ end
43
+
44
+ def warn(obj, type = :error)
45
+ mensaje = if obj.is_a?(Exception) && obj.backtrace.present?
46
+ "#{obj.inspect}\nBacktrace:\n#{cleaner.clean(obj.backtrace).join("\n")}"
47
+ else
48
+ obj
49
+ end
50
+ notify(mensaje, type)
51
+ end
52
+
53
+ private
54
+
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
60
+ end
61
+
62
+ def titulo(mensaje, type)
63
+ Rainbow("#{type.to_s.upcase}: #{mensaje}").bold.send(color_for(type))
13
64
  end
14
65
 
15
- def excepcion(exception)
16
- titulo = Rainbow(" EXCEPCION #{exception.class} en #{caller.first}").red.bold
17
- detalles = Rainbow(" #{exception.message}").red
18
- Rails.logger.error("#{titulo}\n#{detalles}")
19
- Rollbar.error(exception)
66
+ def detalles(type)
67
+ Rainbow(" called in #{bktrc[0]}").send(color_for(type))
20
68
  end
21
69
 
22
- def error(mensaje)
23
- titulo = Rainbow(" ERROR en #{caller.first}").red.bold
24
- detalles = Rainbow(" #{mensaje}").red
25
- Rails.logger.error("#{titulo}\n#{detalles}")
26
- Rollbar.error("#{mensaje}\n\n#{caller.join("\n")}")
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
27
75
  end
28
76
 
29
- def warning(mensaje)
30
- titulo = Rainbow(" WARNING en #{caller.first}").yellow.bold
31
- detalles = Rainbow(" #{mensaje}").yellow
32
- Rails.logger.warn("#{titulo}\n#{detalles}")
33
- Rollbar.warning("#{mensaje}\n\n#{caller.join("\n")}")
77
+ def bktrc
78
+ cleaner.clean(caller)
34
79
  end
35
80
 
36
- def info(mensaje)
37
- titulo = Rainbow(" INFO en #{caller.first}").blue.bold
38
- detalles = Rainbow(" #{mensaje}").blue
39
- Rails.logger.info("#{titulo}\n#{detalles}")
40
- Rollbar.info("#{mensaje}\n\n#{caller.join("\n")}")
81
+ def color_for(type)
82
+ case type
83
+ when :info
84
+ :blue
85
+ when :warn
86
+ :yellow
87
+ when :debug
88
+ :orange
89
+ else # :error
90
+ :red
91
+ end
41
92
  end
42
93
  end
43
94
  end
@@ -2,6 +2,7 @@
2
2
 
3
3
  require_relative 'pg_engine/engine'
4
4
  require_relative 'pg_engine/core_ext'
5
+ require_relative 'pg_engine/error'
5
6
  require_relative 'pg_engine/configuracion'
6
7
  require_relative 'pg_engine/route_helpers'
7
8
  require_relative 'pg_engine/utils/pg_logger'
@@ -6,7 +6,7 @@ Dotenv.load
6
6
  model_paths = begin
7
7
  JSON.parse(ENV['MODEL_PATHS'])
8
8
  rescue JSON::ParserError
9
- ENV['MODEL_PATHS']
9
+ ENV['MODEL_PATHS'] || 'app/models'
10
10
  end
11
11
 
12
12
  if Rails.env.development?
@@ -30,8 +30,6 @@ if Rails.env.development?
30
30
  'show_indexes' => 'false',
31
31
  'simple_indexes' => 'true',
32
32
  'model_dir' => model_paths,
33
- # 'model_dir' => ['app/models', 'app/overrides'],
34
- # 'model_dir' => 'app/models',
35
33
  'root_dir' => '',
36
34
  'include_version' => 'false',
37
35
  'require' => '',
@@ -29,9 +29,9 @@ require 'rails_helper'
29
29
 
30
30
  RSpec.describe Admin::UserAccountsController do
31
31
  render_views
32
- let(:user) { create :orphan_user }
32
+ let!(:user) { create :user }
33
33
 
34
- let(:account) { create :account }
34
+ let!(:account) { create :account }
35
35
 
36
36
  # This should return the minimal set of attributes required to create a valid
37
37
  # UserAccount. As you add validations to UserAccount, be sure to
@@ -0,0 +1,67 @@
1
+ require 'rails_helper'
2
+
3
+ describe PgEngine::Resource do
4
+ let(:instancia) { Admin::CategoriaDeCosasController.new }
5
+
6
+ describe '#buscar_instancia' do
7
+ subject do
8
+ instancia.send(:buscar_instancia)
9
+ end
10
+
11
+ let!(:categoria_de_cosa) { create :categoria_de_cosa }
12
+ let(:request) { double }
13
+
14
+ before do
15
+ allow(request).to receive_messages(filtered_parameters: { id: categoria_de_cosa.to_param },
16
+ parameters: { id: categoria_de_cosa.to_param })
17
+ allow(instancia).to receive(:request).and_return(request)
18
+ instancia.set_clase_modelo
19
+ end
20
+
21
+ it do
22
+ allow(CategoriaDeCosa).to receive(:find_by_hashid!)
23
+ subject
24
+ expect(CategoriaDeCosa).to have_received(:find_by_hashid!)
25
+ end
26
+
27
+ it do
28
+ expect(subject).to eq categoria_de_cosa
29
+ end
30
+ end
31
+
32
+ describe '#do_sort' do
33
+ subject do
34
+ instancia.send(:do_sort, scope, param, direction)
35
+ end
36
+
37
+ let!(:categoria_de_cosa_ult) { create :categoria_de_cosa, nombre: 'Z' }
38
+ let!(:categoria_de_cosa_pri) { create :categoria_de_cosa, nombre: 'a' }
39
+ let(:scope) { CategoriaDeCosa.all }
40
+ let(:param) { :nombre }
41
+ let(:direction) { :desc }
42
+
43
+ context 'asc' do
44
+ let(:direction) { :asc }
45
+
46
+ it do
47
+ expect(subject.to_a).to eq [categoria_de_cosa_pri, categoria_de_cosa_ult]
48
+ end
49
+ end
50
+
51
+ context 'desc' do
52
+ let(:direction) { :desc }
53
+
54
+ it do
55
+ expect(subject.to_a).to eq [categoria_de_cosa_ult, categoria_de_cosa_pri]
56
+ end
57
+ end
58
+
59
+ context 'cuando no existe el param' do
60
+ let(:param) { :inexistente }
61
+
62
+ it do
63
+ expect(subject.to_a).to eq [categoria_de_cosa_ult, categoria_de_cosa_pri]
64
+ end
65
+ end
66
+ end
67
+ end
@@ -36,7 +36,7 @@
36
36
  #
37
37
 
38
38
  FactoryBot.define do
39
- factory :orphan_user, class: 'User' do
39
+ factory :user, class: 'User' do
40
40
  nombre { Faker::Name.name }
41
41
  apellido { Faker::Name.name }
42
42
  email { Faker::Internet.email }
@@ -50,12 +50,5 @@ FactoryBot.define do
50
50
  trait :developer do
51
51
  developer { true }
52
52
  end
53
-
54
- factory :user do
55
- after(:create) do |user, _context|
56
- account = create :account
57
- create :user_account, user:, account:
58
- end
59
- end
60
53
  end
61
54
  end
@@ -0,0 +1,38 @@
1
+ require 'rails_helper'
2
+ # rubocop:disable RSpec/ExampleLength
3
+ describe PgEngine::PgRailsHelper do
4
+ describe '#img_placeholder' do
5
+ it 'si no es fade_in' do
6
+ asd = img_placeholder(src: 'bla', fade_in: false, class: 'img-fluid', style: 'color:red')
7
+ expectation = <<~HTML
8
+ <img class="img-fluid" style="color:red" src="/images/bla" />
9
+ HTML
10
+ expect(asd).to eq expectation.split("\n").map(&:strip).join
11
+ end
12
+
13
+ it 'si tiene style' do
14
+ asd = img_placeholder(src: 'bla', fade_in: true, class: 'img-fluid', style: 'color:red')
15
+ expectation = <<~HTML
16
+ <div class="placeholder-glow" style="width: 100%; height: 100%">
17
+ <div class="placeholder w-100 h-100">
18
+ <img data-controller="fadein_onload" class="img-fluid" style="color:red;display:none" src="/images/bla" />
19
+ </div>
20
+ </div>
21
+ HTML
22
+ expect(asd).to eq expectation.split("\n").map(&:strip).join
23
+ end
24
+
25
+ it 'si no tiene style' do
26
+ asd = img_placeholder(src: 'bla', fade_in: true, class: 'img-fluid')
27
+ expectation = <<~HTML
28
+ <div class="placeholder-glow" style="width: 100%; height: 100%">
29
+ <div class="placeholder w-100 h-100">
30
+ <img data-controller="fadein_onload" class="img-fluid" style="display:none" src="/images/bla" />
31
+ </div>
32
+ </div>
33
+ HTML
34
+ expect(asd).to eq expectation.split("\n").map(&:strip).join
35
+ end
36
+ end
37
+ end
38
+ # rubocop:enable RSpec/ExampleLength
@@ -0,0 +1,104 @@
1
+ require 'rails_helper'
2
+
3
+ TYPES = %i[error warn info debug].freeze
4
+
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
+ shared_examples 'logger' do |type|
15
+ it do
16
+ expect(Rails.logger).to have_received(type).twice
17
+ end
18
+
19
+ it do
20
+ expect(Rollbar).to have_received(type).once
21
+ end
22
+ end
23
+
24
+ context 'con string' do
25
+ before { pg_err('bla') }
26
+
27
+ it_behaves_like 'logger', :error
28
+ end
29
+
30
+ context 'con exception' do
31
+ before do
32
+ raise StandardError, 'bla'
33
+ rescue StandardError => e
34
+ pg_err(e)
35
+ end
36
+
37
+ it_behaves_like 'logger', :error
38
+ end
39
+ end
40
+
41
+ describe '#pg_warn' do
42
+ before do
43
+ TYPES.each do |type|
44
+ allow(Rails.logger).to receive(type)
45
+ allow(Rollbar).to receive(type)
46
+ end
47
+ end
48
+
49
+ shared_examples 'logger' do |type|
50
+ it do
51
+ expect(Rails.logger).to have_received(type).twice
52
+ end
53
+
54
+ it do
55
+ expect(Rollbar).to have_received(type).once
56
+ end
57
+ end
58
+
59
+ TYPES.each do |type|
60
+ context "con type #{type}" do
61
+ before { pg_warn('bla', type) }
62
+
63
+ it_behaves_like 'logger', type
64
+ end
65
+ end
66
+
67
+ context 'con exception' do
68
+ before do
69
+ raise StandardError, 'bla'
70
+ rescue StandardError => e
71
+ pg_warn(e)
72
+ end
73
+
74
+ it_behaves_like 'logger', :error
75
+ end
76
+ end
77
+
78
+ describe '#pg_log' do
79
+ before do
80
+ TYPES.each do |type|
81
+ allow(Rails.logger).to receive(type)
82
+ allow(Rollbar).to receive(type)
83
+ end
84
+ end
85
+
86
+ shared_examples 'logger' do |type|
87
+ it do
88
+ expect(Rails.logger).to have_received(type).twice
89
+ end
90
+
91
+ it do
92
+ expect(Rollbar).to have_received(type).once
93
+ end
94
+ end
95
+
96
+ TYPES.each do |type|
97
+ context "con type #{type}" do
98
+ before { pg_log('bla', type) }
99
+
100
+ it_behaves_like 'logger', type
101
+ end
102
+ end
103
+ end
104
+ end
@@ -10,4 +10,43 @@ RSpec.describe User do
10
10
  it 'se persiste' do
11
11
  expect(user).to be_persisted
12
12
  end
13
+
14
+ it do
15
+ expect(user.current_account).to be_present
16
+ end
17
+
18
+ context 'si es orphan' do
19
+ let(:user) { create(:user, orphan: true) }
20
+
21
+ it do
22
+ expect(user.accounts).to be_empty
23
+ end
24
+
25
+ it do
26
+ expect { user.current_account }.to raise_error(User::Error)
27
+ end
28
+ end
29
+
30
+ context 'Si falla la creación de cuenta, que rollbackee la transaction de create user' do
31
+ let(:user) do
32
+ build(:user)
33
+ end
34
+
35
+ before do
36
+ # rubocop:disable RSpec/MessageChain
37
+ allow(user).to receive_message_chain(:user_accounts, :create) {
38
+ instance_double(UserAccount, persisted?: false)
39
+ }
40
+ # rubocop:enable RSpec/MessageChain
41
+ end
42
+
43
+ it do
44
+ expect { user.save }.not_to change(described_class, :count)
45
+ end
46
+
47
+ it do
48
+ user.save
49
+ expect(user).not_to be_persisted
50
+ end
51
+ end
13
52
  end
@@ -0,0 +1,30 @@
1
+ // Animations
2
+ .fade-in {
3
+ animation: fadeInAnimation 0.2s ease-in-out forwards;
4
+ }
5
+
6
+ .fade-out {
7
+ animation: fadeOutAnimation 0.2s ease forwards;
8
+ }
9
+
10
+ .init-invisible {
11
+ visibility: hidden;
12
+ }
13
+
14
+ .init-visible {
15
+ visibility: visible;
16
+ }
17
+
18
+ .animation-long {
19
+ animation-duration: 1s;
20
+ }
21
+
22
+ @keyframes fadeInAnimation {
23
+ 0% { opacity: 0; visibility: visible; }
24
+ 100% { opacity: 1; visibility: visible; }
25
+ }
26
+
27
+ @keyframes fadeOutAnimation {
28
+ 0% { opacity: 1; visibility: visible; }
29
+ 100% { opacity: 0; visibility: hidden; }
30
+ }
@@ -1,14 +1,10 @@
1
- // Controllers
2
- import NavbarController from './app/javascript/navbar_controller'
3
- import NestedController from './app/javascript/nested_controller'
4
- import PgFormController from './app/javascript/pg_form_controller'
1
+ import './config'
2
+ import './channels'
3
+ import './controllers'
4
+
5
5
  // Bootstrap's toasts
6
6
  import * as bootstrap from 'bootstrap'
7
7
 
8
- window.Stimulus.register('navbar', NavbarController)
9
- window.Stimulus.register('nested', NestedController)
10
- window.Stimulus.register('pg_form', PgFormController)
11
-
12
8
  document.addEventListener('turbo:load', function () {
13
9
  const toastElList = document.querySelectorAll('.toast:not(.hide):not(.show)')
14
10
  Array.from(toastElList).map(toastEl => new bootstrap.Toast(toastEl).show())
@@ -23,11 +19,11 @@ document.addEventListener('turbo:load', function () {
23
19
  const callback = (mutationList, observer) => {
24
20
  for (const mutation of mutationList) {
25
21
  if (mutation.type === 'childList') {
26
- console.log('A child node has been added or removed.')
22
+ // console.log('A child node has been added or removed.')
27
23
  const toastElList = document.querySelectorAll('.toast:not(.hide):not(.show)')
28
24
  Array.from(toastElList).map(toastEl => new bootstrap.Toast(toastEl).show())
29
25
  } else if (mutation.type === 'attributes') {
30
- console.log(`The ${mutation.attributeName} attribute was modified.`)
26
+ // console.log(`The ${mutation.attributeName} attribute was modified.`)
31
27
  }
32
28
  }
33
29
  }
@@ -0,0 +1,8 @@
1
+ // Action Cable provides the framework to deal with WebSockets in Rails.
2
+ // You can generate new channels where WebSocket features live using the `bin/rails generate channel` command.
3
+
4
+ import { createConsumer } from '@anycable/web'
5
+
6
+ export default createConsumer({
7
+ protocol: 'actioncable-v1-ext-json'
8
+ })
@@ -0,0 +1,4 @@
1
+ // Load all the channels within this directory and all subdirectories.
2
+ // Channel files must be named *_channel.js.
3
+
4
+ // import './**/*_channel.js'
@@ -0,0 +1,24 @@
1
+ import consumer from '../channels/consumer'
2
+ import CableReady from 'cable_ready'
3
+
4
+ const anycable = consumer.cable
5
+
6
+ if (anycable) {
7
+ anycable.on('connect', ev => {
8
+ if (ev.reconnect) {
9
+ console.log('Welcome back!')
10
+ } else {
11
+ console.log('Welcome!')
12
+ }
13
+ })
14
+
15
+ anycable.on('disconnect', ev => {
16
+ if (ev.reason) {
17
+ console.log(`Disconnected because: ${ev.reason}`)
18
+ } else {
19
+ console.log('Disconnected')
20
+ }
21
+ })
22
+ }
23
+
24
+ CableReady.initialize({ consumer })
@@ -0,0 +1 @@
1
+ import './cable_ready'
@@ -0,0 +1,4 @@
1
+ // TODO: si no existe la aplicacion mostrar error
2
+ const application = window.Stimulus
3
+
4
+ export { application }
@@ -0,0 +1,20 @@
1
+ import { Controller } from '@hotwired/stimulus'
2
+
3
+ // To be used by img_placeholder helper
4
+ export default class extends Controller {
5
+ connect () {
6
+ if (this.element.complete) {
7
+ this.loaded()
8
+ } else {
9
+ this.element.addEventListener('load', () => {
10
+ this.loaded()
11
+ }, { once: true })
12
+ }
13
+ }
14
+
15
+ loaded () {
16
+ this.element.classList.add('fade-in')
17
+ this.element.style.display = 'block'
18
+ this.element.parentElement.classList.remove('placeholder')
19
+ }
20
+ }
@@ -0,0 +1,11 @@
1
+ import { application } from './application'
2
+
3
+ import NavbarController from './navbar_controller'
4
+ import NestedController from './nested_controller'
5
+ import PgFormController from './pg_form_controller'
6
+ import FadeinOnloadController from './fadein_onload_controller'
7
+
8
+ application.register('navbar', NavbarController)
9
+ application.register('nested', NestedController)
10
+ application.register('pg_form', PgFormController)
11
+ application.register('fadein_onload', FadeinOnloadController)
@@ -1,5 +1,5 @@
1
1
  import { Controller } from '@hotwired/stimulus'
2
- import Cookies from './cookies'
2
+ import Cookies from './../utils/cookies'
3
3
 
4
4
  export default class extends Controller {
5
5
  expandNavbar (e) {
@@ -0,0 +1,43 @@
1
+ export function round (value) {
2
+ return Math.round(value * 100) / 100
3
+ }
4
+
5
+ export function printCurrency (value, simboloMoneda = '$') {
6
+ if (typeof value === 'string') {
7
+ value = parseFloat(value)
8
+ }
9
+ const decimals = (value % 1 > 0) ? 2 : 0
10
+ return simboloMoneda + ' ' + numberWithDots(value.toFixed(decimals).replace('.', ','))
11
+ }
12
+
13
+ export function showPercentage (value) {
14
+ return '% ' + value
15
+ }
16
+
17
+ export function numberWithDots (x) {
18
+ return x.toString().replace(/\B(?=(\d{3})+(?!\d))/g, '.')
19
+ }
20
+
21
+ export function fadeOut (e) {
22
+ if (window.getComputedStyle(e).visibility !== 'hidden') {
23
+ e.classList.add('fade-out')
24
+ e.addEventListener('animationend', onAnimationEndHide, { once: true })
25
+ }
26
+ }
27
+
28
+ export function fadeIn (e) {
29
+ if (window.getComputedStyle(e).visibility !== 'visible') {
30
+ e.classList.add('fade-in')
31
+ e.addEventListener('animationend', onAnimationEndShow, { once: true })
32
+ }
33
+ }
34
+
35
+ function onAnimationEndShow (e) {
36
+ e.target.style.visibility = 'visible'
37
+ e.target.classList.remove('fade-in')
38
+ }
39
+
40
+ function onAnimationEndHide (e) {
41
+ e.target.style.visibility = 'hidden'
42
+ e.target.classList.remove('fade-out')
43
+ }
@@ -5,7 +5,8 @@ html
5
5
  meta name="viewport" content="width=device-width,initial-scale=1"
6
6
  = csrf_meta_tags
7
7
  = csp_meta_tag
8
-
8
+ meta name="cable-history-timestamp" content="#{Time.now.to_i}"
9
+ = action_cable_with_jwt_meta_tag
9
10
  = stylesheet_link_tag 'application', 'data-turbo-track': 'reload'
10
11
  = javascript_include_tag 'application', 'data-turbo-track': 'reload', type: 'module'
11
12
  body
@@ -3,16 +3,20 @@ html
3
3
  head
4
4
  title = Rails.application.class.module_parent_name
5
5
  meta name="viewport" content="width=device-width,initial-scale=1"
6
- / meta name="turbo-cache-control" content="no-cache"
6
+ - if @turbo_no_cache
7
+ meta name="turbo-cache-control" content="no-cache"
7
8
  / meta name="turbo-refresh-method" content="morph"
8
9
  / meta name="turbo-refresh-scroll" content="preserve"
9
10
  / meta name="turbo-prefetch" content="true"
10
11
  meta name="view-transition" content="same-origin"
12
+ meta name="cable-history-timestamp" content="#{Time.now.to_i}"
11
13
  = csrf_meta_tags
12
14
  = csp_meta_tag
15
+ = action_cable_with_jwt_meta_tag
13
16
 
14
17
  = stylesheet_link_tag 'application', 'data-turbo-track': 'reload'
15
18
  = javascript_include_tag 'application', 'data-turbo-track': 'reload', type: 'module'
19
+ = render partial: 'pg_layout/rollbar'
16
20
  body
17
21
  div class="#{ @sidebar == false ? '' : 'with-sidebar' }"
18
22
  - unless @sidebar == false
@@ -33,4 +37,5 @@ html
33
37
  = yield(:actions)
34
38
  hr.my-0
35
39
  = yield
40
+ div style="width:100%; height: 10em"
36
41
  = render_turbo_stream_title
@@ -9,3 +9,5 @@
9
9
  data-xturbo-temporary="true" aria-live="assertive" aria-atomic="true")
10
10
  .toast-body
11
11
  = message
12
+
13
+ / TODO: centrar texto
@@ -0,0 +1,28 @@
1
+ <% if ENV['ROLLBAR_ACCESS_TOKEN_CLIENT'].present? %>
2
+ <script>
3
+ var _rollbarConfig = {
4
+ accessToken: '<%= ENV['ROLLBAR_ACCESS_TOKEN_CLIENT'] %>',
5
+ captureUncaught: true,
6
+ captureUnhandledRejections: true,
7
+ payload: {
8
+ environment: "production",
9
+ //trace_id: 'abc',
10
+ client: {
11
+ javascript: {
12
+ code_version: '1.0.0',
13
+ //source_map_enabled: true,
14
+ //guess_uncaught_frames: true
15
+ }
16
+ },
17
+ //server: {
18
+ //root: 'http://localhost:8000/demo/',
19
+ //host: 'host-1',
20
+ //branch: 'HEAD',
21
+ //},
22
+ }
23
+ };
24
+ // Rollbar Snippet
25
+ !function(r){var e={};function o(n){if(e[n])return e[n].exports;var t=e[n]={i:n,l:!1,exports:{}};return r[n].call(t.exports,t,t.exports,o),t.l=!0,t.exports}o.m=r,o.c=e,o.d=function(r,e,n){o.o(r,e)||Object.defineProperty(r,e,{enumerable:!0,get:n})},o.r=function(r){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(r,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(r,"__esModule",{value:!0})},o.t=function(r,e){if(1&e&&(r=o(r)),8&e)return r;if(4&e&&"object"==typeof r&&r&&r.__esModule)return r;var n=Object.create(null);if(o.r(n),Object.defineProperty(n,"default",{enumerable:!0,value:r}),2&e&&"string"!=typeof r)for(var t in r)o.d(n,t,function(e){return r[e]}.bind(null,t));return n},o.n=function(r){var e=r&&r.__esModule?function(){return r.default}:function(){return r};return o.d(e,"a",e),e},o.o=function(r,e){return Object.prototype.hasOwnProperty.call(r,e)},o.p="",o(o.s=0)}([function(r,e,o){"use strict";var n=o(1),t=o(5);_rollbarConfig=_rollbarConfig||{},_rollbarConfig.rollbarJsUrl=_rollbarConfig.rollbarJsUrl||"https://cdn.rollbar.com/rollbarjs/refs/tags/v2.26.3/rollbar.min.js",_rollbarConfig.async=void 0===_rollbarConfig.async||_rollbarConfig.async;var a=n.setupShim(window,_rollbarConfig),l=t(_rollbarConfig);window.rollbar=n.Rollbar,a.loadFull(window,document,!_rollbarConfig.async,_rollbarConfig,l)},function(r,e,o){"use strict";var n=o(2),t=o(3);function a(r){return function(){try{return r.apply(this,arguments)}catch(r){try{console.error("[Rollbar]: Internal error",r)}catch(r){}}}}var l=0;function i(r,e){this.options=r,this._rollbarOldOnError=null;var o=l++;this.shimId=function(){return o},"undefined"!=typeof window&&window._rollbarShims&&(window._rollbarShims[o]={handler:e,messages:[]})}var s=o(4),d=function(r,e){return new i(r,e)},c=function(r){return new s(d,r)};function u(r){return a((function(){var e=this,o=Array.prototype.slice.call(arguments,0),n={shim:e,method:r,args:o,ts:new Date};window._rollbarShims[this.shimId()].messages.push(n)}))}i.prototype.loadFull=function(r,e,o,n,t){var l=!1,i=e.createElement("script"),s=e.getElementsByTagName("script")[0],d=s.parentNode;i.crossOrigin="",i.src=n.rollbarJsUrl,o||(i.async=!0),i.onload=i.onreadystatechange=a((function(){if(!(l||this.readyState&&"loaded"!==this.readyState&&"complete"!==this.readyState)){i.onload=i.onreadystatechange=null;try{d.removeChild(i)}catch(r){}l=!0,function(){var e;if(void 0===r._rollbarDidLoad){e=new Error("rollbar.js did not load");for(var o,n,a,l,i=0;o=r._rollbarShims[i++];)for(o=o.messages||[];n=o.shift();)for(a=n.args||[],i=0;i<a.length;++i)if("function"==typeof(l=a[i])){l(e);break}}"function"==typeof t&&t(e)}()}})),d.insertBefore(i,s)},i.prototype.wrap=function(r,e,o){try{var n;if(n="function"==typeof e?e:function(){return e||{}},"function"!=typeof r)return r;if(r._isWrap)return r;if(!r._rollbar_wrapped&&(r._rollbar_wrapped=function(){o&&"function"==typeof o&&o.apply(this,arguments);try{return r.apply(this,arguments)}catch(o){var e=o;throw e&&("string"==typeof e&&(e=new String(e)),e._rollbarContext=n()||{},e._rollbarContext._wrappedSource=r.toString(),window._rollbarWrappedError=e),e}},r._rollbar_wrapped._isWrap=!0,r.hasOwnProperty))for(var t in r)r.hasOwnProperty(t)&&(r._rollbar_wrapped[t]=r[t]);return r._rollbar_wrapped}catch(e){return r}};for(var p="log,debug,info,warn,warning,error,critical,global,configure,handleUncaughtException,handleAnonymousErrors,handleUnhandledRejection,captureEvent,captureDomContentLoaded,captureLoad".split(","),f=0;f<p.length;++f)i.prototype[p[f]]=u(p[f]);r.exports={setupShim:function(r,e){if(r){var o=e.globalAlias||"Rollbar";if("object"==typeof r[o])return r[o];r._rollbarShims={},r._rollbarWrappedError=null;var l=new c(e);return a((function(){e.captureUncaught&&(l._rollbarOldOnError=r.onerror,n.captureUncaughtExceptions(r,l,!0),e.wrapGlobalEventHandlers&&t(r,l,!0)),e.captureUnhandledRejections&&n.captureUnhandledRejections(r,l,!0);var a=e.autoInstrument;return!1!==e.enabled&&(void 0===a||!0===a||function(r){return!("object"!=typeof r||void 0!==r.page&&!r.page)}(a))&&r.addEventListener&&(r.addEventListener("load",l.captureLoad.bind(l)),r.addEventListener("DOMContentLoaded",l.captureDomContentLoaded.bind(l))),r[o]=l,l}))()}},Rollbar:c}},function(r,e,o){"use strict";function n(r,e,o,n){r._rollbarWrappedError&&(n[4]||(n[4]=r._rollbarWrappedError),n[5]||(n[5]=r._rollbarWrappedError._rollbarContext),r._rollbarWrappedError=null);var t=e.handleUncaughtException.apply(e,n);o&&o.apply(r,n),"anonymous"===t&&(e.anonymousErrorsPending+=1)}r.exports={captureUncaughtExceptions:function(r,e,o){if(r){var t;if("function"==typeof e._rollbarOldOnError)t=e._rollbarOldOnError;else if(r.onerror){for(t=r.onerror;t._rollbarOldOnError;)t=t._rollbarOldOnError;e._rollbarOldOnError=t}e.handleAnonymousErrors();var a=function(){var o=Array.prototype.slice.call(arguments,0);n(r,e,t,o)};o&&(a._rollbarOldOnError=t),r.onerror=a}},captureUnhandledRejections:function(r,e,o){if(r){"function"==typeof r._rollbarURH&&r._rollbarURH.belongsToShim&&r.removeEventListener("unhandledrejection",r._rollbarURH);var n=function(r){var o,n,t;try{o=r.reason}catch(r){o=void 0}try{n=r.promise}catch(r){n="[unhandledrejection] error getting `promise` from event"}try{t=r.detail,!o&&t&&(o=t.reason,n=t.promise)}catch(r){}o||(o="[unhandledrejection] error getting `reason` from event"),e&&e.handleUnhandledRejection&&e.handleUnhandledRejection(o,n)};n.belongsToShim=o,r._rollbarURH=n,r.addEventListener("unhandledrejection",n)}}}},function(r,e,o){"use strict";function n(r,e,o){if(e.hasOwnProperty&&e.hasOwnProperty("addEventListener")){for(var n=e.addEventListener;n._rollbarOldAdd&&n.belongsToShim;)n=n._rollbarOldAdd;var t=function(e,o,t){n.call(this,e,r.wrap(o),t)};t._rollbarOldAdd=n,t.belongsToShim=o,e.addEventListener=t;for(var a=e.removeEventListener;a._rollbarOldRemove&&a.belongsToShim;)a=a._rollbarOldRemove;var l=function(r,e,o){a.call(this,r,e&&e._rollbar_wrapped||e,o)};l._rollbarOldRemove=a,l.belongsToShim=o,e.removeEventListener=l}}r.exports=function(r,e,o){if(r){var t,a,l="EventTarget,Window,Node,ApplicationCache,AudioTrackList,ChannelMergerNode,CryptoOperation,EventSource,FileReader,HTMLUnknownElement,IDBDatabase,IDBRequest,IDBTransaction,KeyOperation,MediaController,MessagePort,ModalWindow,Notification,SVGElementInstance,Screen,TextTrack,TextTrackCue,TextTrackList,WebSocket,WebSocketWorker,Worker,XMLHttpRequest,XMLHttpRequestEventTarget,XMLHttpRequestUpload".split(",");for(t=0;t<l.length;++t)r[a=l[t]]&&r[a].prototype&&n(e,r[a].prototype,o)}}},function(r,e,o){"use strict";function n(r,e){this.impl=r(e,this),this.options=e,function(r){for(var e=function(r){return function(){var e=Array.prototype.slice.call(arguments,0);if(this.impl[r])return this.impl[r].apply(this.impl,e)}},o="log,debug,info,warn,warning,error,critical,global,configure,handleUncaughtException,handleAnonymousErrors,handleUnhandledRejection,_createItem,wrap,loadFull,shimId,captureEvent,captureDomContentLoaded,captureLoad".split(","),n=0;n<o.length;n++)r[o[n]]=e(o[n])}(n.prototype)}n.prototype._swapAndProcessMessages=function(r,e){var o,n,t;for(this.impl=r(this.options);o=e.shift();)n=o.method,t=o.args,this[n]&&"function"==typeof this[n]&&("captureDomContentLoaded"===n||"captureLoad"===n?this[n].apply(this,[t[0],o.ts]):this[n].apply(this,t));return this},r.exports=n},function(r,e,o){"use strict";r.exports=function(r){return function(e){if(!e&&!window._rollbarInitialized){for(var o,n,t=(r=r||{}).globalAlias||"Rollbar",a=window.rollbar,l=function(r){return new a(r)},i=0;o=window._rollbarShims[i++];)n||(n=o.handler),o.handler._swapAndProcessMessages(l,o.messages);window[t]=n,window._rollbarInitialized=!0}}}}]);
26
+ // End Rollbar Snippet
27
+ </script>
28
+ <% end %>
data/pg_rails/js/index.js CHANGED
@@ -1,2 +1,2 @@
1
1
  import './../../pg_associable'
2
- import './../../pg_layout'
2
+ import './../../pg_layout/app/javascript/application'
@@ -0,0 +1,28 @@
1
+ require 'vcr'
2
+ require 'webmock/rspec'
3
+
4
+ VCR.configure do |config|
5
+ config.cassette_library_dir = 'spec/cassettes'
6
+ config.default_cassette_options = {
7
+ match_requests_on: %i[uri method],
8
+ record: :once
9
+ }
10
+ config.hook_into :webmock
11
+ config.configure_rspec_metadata!
12
+
13
+ # * `ignore_localhost = true` is equivalent to `ignore_hosts 'localhost',
14
+ # '127.0.0.1', '0.0.0.0'`. It is particularly useful for when you use
15
+ # VCR with a javascript-enabled capybara driver, since capybara boots
16
+ # your rack app and makes localhost requests to it to check that it has
17
+ # booted.
18
+ config.ignore_localhost = true
19
+ end
20
+
21
+ RSpec.configure do |config|
22
+ config.around(:example, :vcr_cassettes) do |example|
23
+ cassettes = example.metadata[:vcr_cassettes].map { |cas_name| { name: cas_name } }
24
+ VCR.use_cassettes(cassettes) do
25
+ example.run
26
+ end
27
+ end
28
+ end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module PgRails
4
- VERSION = '7.0.8-alpha.13'
4
+ VERSION = '7.0.8-alpha.15'
5
5
  end
@@ -1,3 +1,4 @@
1
1
  @import './../../pg_engine/app/assets/stylesheets/pg_rails_b5';
2
2
  @import './../../pg_associable/app/assets/stylesheets/pg_associable';
3
3
  @import './../../pg_layout/app/assets/stylesheets/sidebar';
4
+ @import './../../pg_layout/app/assets/stylesheets/animations';
@@ -9,6 +9,8 @@ require_dependency "<%= namespaced_path %>/application_controller"
9
9
  <% module_namespacing do -%>
10
10
  <% module_namespacing_2 do -%>
11
11
  class <%= controller_class_name.split('::').last %>Controller < <%= parent_controller %>
12
+ include PgEngine::Resource
13
+
12
14
  before_action { @clase_modelo = <%= class_name.split('::').last %> }
13
15
 
14
16
  before_action(only: :index) { authorize <%= class_name.split('::').last %> }
@@ -6,7 +6,7 @@ require 'generators/pg_active_record/model/model_generator'
6
6
 
7
7
  DESTINATION_PATH = File.expand_path('./../../tmp/generator_testing', __dir__)
8
8
 
9
- describe 'Generators' do
9
+ describe 'Generators', type: :generator do
10
10
  describe 'PgDecoratorGenerator' do
11
11
  destination DESTINATION_PATH
12
12
  tests PgDecoratorGenerator
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.13
4
+ version: 7.0.8.pre.alpha.15
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-03-14 00:00:00.000000000 Z
11
+ date: 2024-03-27 00:00:00.000000000 Z
12
12
  dependencies: []
13
13
  description: Rails goodies.
14
14
  email:
@@ -89,7 +89,10 @@ files:
89
89
  - pg_engine/app/views/pg_engine/base/index.html.slim
90
90
  - pg_engine/app/views/pg_engine/base/new.html.slim
91
91
  - pg_engine/config/initializers/active_admin.rb
92
+ - pg_engine/config/initializers/anycable.rb
93
+ - pg_engine/config/initializers/cable_ready.rb
92
94
  - pg_engine/config/initializers/devise.rb
95
+ - pg_engine/config/initializers/rollbar.rb
93
96
  - pg_engine/config/initializers/simple_form_monkey_patch.rb
94
97
  - pg_engine/config/locales/devise.en.yml
95
98
  - pg_engine/config/locales/es.yml
@@ -110,6 +113,7 @@ files:
110
113
  - pg_engine/lib/pg_engine/configuracion.rb
111
114
  - pg_engine/lib/pg_engine/core_ext.rb
112
115
  - pg_engine/lib/pg_engine/engine.rb
116
+ - pg_engine/lib/pg_engine/error.rb
113
117
  - pg_engine/lib/pg_engine/route_helpers.rb
114
118
  - pg_engine/lib/pg_engine/utils/pdf_preview_generator.rb
115
119
  - pg_engine/lib/pg_engine/utils/pg_logger.rb
@@ -117,25 +121,37 @@ files:
117
121
  - pg_engine/spec/controllers/admin/accounts_controller_spec.rb
118
122
  - pg_engine/spec/controllers/admin/user_accounts_controller_spec.rb
119
123
  - pg_engine/spec/controllers/admin/users_controller_spec.rb
120
- - pg_engine/spec/controllers/concerns/pg_engine/error_helper_spec.rb
124
+ - pg_engine/spec/controllers/concerns/pg_engine/resource_helper_spec.rb
121
125
  - pg_engine/spec/controllers/devise/registrations_controller_spec.rb
122
126
  - pg_engine/spec/controllers/devise/sessions_controller_spec.rb
123
127
  - pg_engine/spec/factories/accounts.rb
124
128
  - pg_engine/spec/factories/user_accounts.rb
125
129
  - pg_engine/spec/factories/users.rb
126
130
  - pg_engine/spec/fixtures/test.pdf
131
+ - pg_engine/spec/helpers/pg_engine/pg_rails_helper_spec.rb
132
+ - pg_engine/spec/lib/pg_engine/error_helper_spec.rb
133
+ - pg_engine/spec/lib/pg_engine/utils/pg_engine/pg_logger_spec.rb
127
134
  - pg_engine/spec/lib/pg_form_builder_spec.rb
128
135
  - pg_engine/spec/models/account_spec.rb
129
136
  - pg_engine/spec/models/pg_engine/base_record_spec.rb
130
137
  - pg_engine/spec/models/user_account_spec.rb
131
138
  - pg_engine/spec/models/user_spec.rb
132
139
  - pg_engine/spec/pg_engine/pdf_preview_generator_spec.rb
140
+ - pg_layout/app/assets/stylesheets/animations.scss
133
141
  - pg_layout/app/assets/stylesheets/sidebar.scss
134
- - pg_layout/app/javascript/cookies.js
135
- - pg_layout/app/javascript/navbar_controller.js
136
- - pg_layout/app/javascript/nested_controller.js
137
- - pg_layout/app/javascript/pg_form_controller.js
138
- - pg_layout/app/javascript/utils.ts
142
+ - pg_layout/app/javascript/application.js
143
+ - pg_layout/app/javascript/channels/consumer.js
144
+ - pg_layout/app/javascript/channels/index.js
145
+ - pg_layout/app/javascript/config/cable_ready.js
146
+ - pg_layout/app/javascript/config/index.js
147
+ - pg_layout/app/javascript/controllers/application.js
148
+ - pg_layout/app/javascript/controllers/fadein_onload_controller.js
149
+ - pg_layout/app/javascript/controllers/index.js
150
+ - pg_layout/app/javascript/controllers/navbar_controller.js
151
+ - pg_layout/app/javascript/controllers/nested_controller.js
152
+ - pg_layout/app/javascript/controllers/pg_form_controller.js
153
+ - pg_layout/app/javascript/utils/cookies.js
154
+ - pg_layout/app/javascript/utils/utils.ts
139
155
  - pg_layout/app/lib/navbar.rb
140
156
  - pg_layout/app/views/devise/confirmations/new.html.erb
141
157
  - pg_layout/app/views/devise/mailer/confirmation_instructions.html.erb
@@ -162,13 +178,14 @@ files:
162
178
  - pg_layout/app/views/layouts/pg_layout/layout.html.slim
163
179
  - pg_layout/app/views/pg_layout/_flash.html.slim
164
180
  - pg_layout/app/views/pg_layout/_navbar.html.erb
181
+ - pg_layout/app/views/pg_layout/_rollbar.html.erb
165
182
  - pg_layout/app/views/pg_layout/_sidebar.html.erb
166
- - pg_layout/index.js
167
183
  - pg_layout/lib/pg_layout.rb
168
184
  - pg_layout/lib/pg_layout/engine.rb
169
185
  - pg_rails/js/index.js
170
186
  - pg_rails/lib/pg_rails.rb
171
187
  - pg_rails/lib/pg_rails/capybara_support.rb
188
+ - pg_rails/lib/pg_rails/vcr_support.rb
172
189
  - pg_rails/lib/version.rb
173
190
  - pg_rails/scss/pg_rails.scss
174
191
  - pg_scaffold/lib/generators/pg_active_record/model/model_generator.rb
@@ -1,19 +0,0 @@
1
- export function round (value) {
2
- return Math.round(value * 100) / 100
3
- }
4
-
5
- export function printCurrency (value, simboloMoneda = '$') {
6
- if (typeof value === 'string') {
7
- value = parseFloat(value)
8
- }
9
- const decimals = (value % 1 > 0) ? 2 : 0
10
- return simboloMoneda + ' ' + numberWithDots(value.toFixed(decimals).replace('.', ','))
11
- }
12
-
13
- export function showPercentage (value) {
14
- return '% ' + value
15
- }
16
-
17
- export function numberWithDots (x) {
18
- return x.toString().replace(/\B(?=(\d{3})+(?!\d))/g, '.')
19
- }