-
Notifications
You must be signed in to change notification settings - Fork 0
/
active_record_callbacks.html
1633 lines (1513 loc) · 132 KB
/
active_record_callbacks.html
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
<!doctype html>
<html dir="ltr" lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Callbacks de Active Record — Ruby on Rails Guides</title>
<link rel="stylesheet" type="text/css" href="stylesheets/style-v2.css" data-turbo-track="reload">
<link rel="stylesheet" type="text/css" href="stylesheets/print-v2.css" media="print">
<link rel="stylesheet" type="text/css" href="stylesheets/highlight-v2.css" data-turbo-track="reload">
<link rel="icon" href="images/favicon.ico" sizes="any">
<link rel="apple-touch-icon" href="images/icon.png">
<script src="javascripts/@hotwired--turbo.js" data-turbo-track="reload"></script>
<script src="javascripts/clipboard.js" data-turbo-track="reload"></script>
<script src="javascripts/guides.js" data-turbo-track="reload"></script>
<meta property="og:title" content="Callbacks de Active Record — Ruby on Rails Guides" />
<meta name="description" content="NO LEAS ESTE ARCHIVO EN GITHUB, LAS GUÍAS ESTÁN PUBLICADAS EN https://guides.rubyonrails.org.Callbacks de Active RecordEsta guía te enseña cómo engancharte en el ciclo de vida de tus objetos de Active Record.Después de leer esta guía, sabrás: Cuándo ocurren ciertos eventos durante la vida de un objeto de Active Record. Cómo registrar, ejecutar y omitir callbacks que responden a estos eventos. Cómo crear callbacks relacionales, de asociación, condicionales y transaccionales. Cómo crear objetos que encapsulan comportamientos comunes para que tus callbacks puedan ser reutilizados." />
<meta property="og:description" content="NO LEAS ESTE ARCHIVO EN GITHUB, LAS GUÍAS ESTÁN PUBLICADAS EN https://guides.rubyonrails.org.Callbacks de Active RecordEsta guía te enseña cómo engancharte en el ciclo de vida de tus objetos de Active Record.Después de leer esta guía, sabrás: Cuándo ocurren ciertos eventos durante la vida de un objeto de Active Record. Cómo registrar, ejecutar y omitir callbacks que responden a estos eventos. Cómo crear callbacks relacionales, de asociación, condicionales y transaccionales. Cómo crear objetos que encapsulan comportamientos comunes para que tus callbacks puedan ser reutilizados." />
<meta property="og:locale" content="en_US" />
<meta property="og:site_name" content="Ruby on Rails Guides" />
<meta property="og:image" content="https://avatars.githubusercontent.com/u/4223" />
<meta property="og:type" content="website" />
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Noto+Sans+Arabic:[email protected]&display=swap" rel="stylesheet">
<link href="https://fonts.googleapis.com/css2?family=Heebo:[email protected]&family=Noto+Sans+Arabic:[email protected]&display=swap" rel="stylesheet">
<meta name="theme-color" content="#C81418">
</head>
<body class="guide">
<nav id="topNav" aria-label="Secondary">
<div class="wrapper">
<strong class="more-info-label">Más en <a href="https://rubyonrails.org/">rubyonrails.org:</a> </strong>
<span class="red-button more-info-button">
Más Ruby on Rails
</span>
<ul class="more-info-links s-hidden">
<li class="more-info"><a href="https://rubyonrails.org/blog">Blog</a></li>
<li class="more-info"><a href="https://guides.rubyonrails.org/">Guías</a></li>
<li class="more-info"><a href="https://api.rubyonrails.org/">API</a></li>
<li class="more-info"><a href="https://discuss.rubyonrails.org/">Foro</a></li>
<li class="more-info"><a href="https://github.com/rails/rails">Contribuir en GitHub</a></li>
</ul>
</div>
</nav>
<header id="page_header">
<div class="wrapper clearfix">
<nav id="feature_nav">
<div class="header-logo">
<a href="index.html" title="Regresar a la página principal de Guías para Edge">Guías</a>
<span id="version_switcher">
Versión:
<select class="guides-version">
<option value="https://edgeguides.rubyonrails.org/" selected>Edge</option>
<option value="https://guides.rubyonrails.org/v7.2/">7.2</option>
<option value="https://guides.rubyonrails.org/v7.1/">7.1</option>
<option value="https://guides.rubyonrails.org/v7.0/">7.0</option>
<option value="https://guides.rubyonrails.org/v6.1/">6.1</option>
<option value="https://guides.rubyonrails.org/v6.0/">6.0</option>
<option value="https://guides.rubyonrails.org/v5.2/">5.2</option>
<option value="https://guides.rubyonrails.org/v5.1/">5.1</option>
<option value="https://guides.rubyonrails.org/v5.0/">5.0</option>
<option value="https://guides.rubyonrails.org/v4.2/">4.2</option>
<option value="https://guides.rubyonrails.org/v4.1/">4.1</option>
<option value="https://guides.rubyonrails.org/v4.0/">4.0</option>
<option value="https://guides.rubyonrails.org/v3.2/">3.2</option>
<option value="https://guides.rubyonrails.org/v3.1/">3.1</option>
<option value="https://guides.rubyonrails.org/v3.0/">3.0</option>
<option value="https://guides.rubyonrails.org/v2.3/">2.3</option>
</select>
</span>
</div>
<ul class="nav">
<li><a class="nav-item" id="home_nav" href="https://rubyonrails.org/">Inicio</a></li>
<li class="guides-index guides-index-large">
<a href="index.html" id="guidesMenu" class="guides-index-item nav-item">Índice de Guías</a>
<div id="guides" class="clearfix" style="display: none;">
<hr />
<dl class="guides-section-container">
<div class="guides-section">
<dt>Comienza Aquí</dt>
<dd><a href="getting_started.html">Primeros Pasos con Rails</a></dd>
</div>
<div class="guides-section">
<dt>Modelos</dt>
<dd><a href="active_record_basics.html">Conceptos Básicos de Active Record</a></dd>
<dd><a href="active_record_migrations.html">Migraciones de Active Record</a></dd>
<dd><a href="active_record_validations.html">Validaciones de Active Record</a></dd>
</div>
<div class="guides-section">
<dt>Vistas</dt>
<dd><a href="action_view_overview.html">Resumen de Action View</a></dd>
<dd><a href="layouts_and_rendering.html">Diseños y Renderizado en Rails</a></dd>
</div>
<div class="guides-section">
<dt>Controladores</dt>
<dd><a href="action_controller_overview.html">Resumen de Action Controller</a></dd>
<dd><a href="routing.html">Enrutamiento en Rails desde el Exterior</a></dd>
</div>
<div class="guides-section">
<dt>Otros Componentes</dt>
<dd><a href="active_support_core_extensions.html">Extensiones Básicas de Active Support</a></dd>
<dd><a href="action_mailer_basics.html">Conceptos Básicos de Action Mailer</a></dd>
<dd><a href="action_mailbox_basics.html">Conceptos Básicos de Action Mailbox</a></dd>
<dd><a href="action_text_overview.html">Resumen de Action Text</a></dd>
<dd><a href="active_job_basics.html">Conceptos Básicos de Active Job</a></dd>
</div>
<div class="guides-section">
<dt>Políticas</dt>
<dd><a href="maintenance_policy.html">Política de Mantenimiento</a></dd>
</div>
<div class="guides-section">
<dt>Notas de Lanzamiento</dt>
<dd><a href="upgrading_ruby_on_rails.html">Actualizando Ruby on Rails</a></dd>
<dd><a href="7_2_release_notes.html">Versión 7.2 - ?</a></dd>
<dd><a href="7_1_release_notes.html">Versión 7.1 - Octubre 2023</a></dd>
<dd><a href="7_0_release_notes.html">Versión 7.0 - Diciembre 2021</a></dd>
<dd><a href="6_1_release_notes.html">Versión 6.1 - Diciembre 2020</a></dd>
</div>
</dl>
</div>
</li>
<li><a class="nav-item" href="contributing_to_ruby_on_rails.html">Contribuir</a></li>
<li class="guides-index guides-index-small">
<select class="guides-index-item nav-item">
<option value="index.html">Índice de Guías</option>
<optgroup label="Comienza Aquí">
<option value="getting_started.html">Primeros Pasos con Rails</option>
</optgroup>
<optgroup label="Modelos">
<option value="active_record_basics.html">Conceptos Básicos de Active Record</option>
<option value="active_record_migrations.html">Migraciones de Active Record</option>
<option value="active_record_validations.html">Validaciones de Active Record</option>
</optgroup>
<optgroup label="Vistas">
<option value="action_view_overview.html">Resumen de Action View</option>
<option value="layouts_and_rendering.html">Diseños y Renderizado en Rails</option>
</optgroup>
<optgroup label="Controladores">
<option value="action_controller_overview.html">Resumen de Action Controller</option>
<option value="routing.html">Enrutamiento en Rails desde el Exterior</option>
</optgroup>
<optgroup label="Otros Componentes">
<option value="active_support_core_extensions.html">Extensiones Básicas de Active Support</option>
<option value="action_mailer_basics.html">Conceptos Básicos de Action Mailer</option>
<option value="action_mailbox_basics.html">Conceptos Básicos de Action Mailbox</option>
<option value="action_text_overview.html">Resumen de Action Text</option>
<option value="active_job_basics.html">Conceptos Básicos de Active Job</option>
</optgroup>
<optgroup label="Políticas">
<option value="maintenance_policy.html">Política de Mantenimiento</option>
</optgroup>
<optgroup label="Notas de Lanzamiento">
<option value="upgrading_ruby_on_rails.html">Actualizando Ruby on Rails</option>
<option value="7_2_release_notes.html">Versión 7.2 - ?</option>
<option value="7_1_release_notes.html">Versión 7.1 - Octubre 2023</option>
<option value="7_0_release_notes.html">Versión 7.0 - Diciembre 2021</option>
<option value="6_1_release_notes.html">Versión 6.1 - Diciembre 2020</option>
</optgroup>
</select>
</li>
</ul>
</nav>
</div>
</header>
<hr class="hide" />
<section id="feature">
<div class="wrapper">
<p><strong>NO LEAS ESTE ARCHIVO EN GITHUB, LAS GUÍAS ESTÁN PUBLICADAS EN <a href="https://guides.rubyonrails.org">https://guides.rubyonrails.org</a>.</strong></p><h1>Callbacks de Active Record</h1><p>Esta guía te enseña cómo engancharte en el ciclo de vida de tus objetos de Active Record.</p><p>Después de leer esta guía, sabrás:</p>
<ul>
<li>Cuándo ocurren ciertos eventos durante la vida de un objeto de Active Record.</li>
<li>Cómo registrar, ejecutar y omitir callbacks que responden a estos eventos.</li>
<li>Cómo crear callbacks relacionales, de asociación, condicionales y transaccionales.</li>
<li>Cómo crear objetos que encapsulan comportamientos comunes para que tus callbacks puedan ser reutilizados.</li>
</ul>
<nav id="subCol">
<h3 class="chapter">
<picture>
<!-- Using the `source` HTML tag to set the dark theme image -->
<source
srcset="images/icon_book-close-bookmark-1-wht.svg"
media="(prefers-color-scheme: dark)"
/>
<img src="images/icon_book-close-bookmark-1.svg" alt="Chapter Icon" />
</picture>
Chapters
</h3>
<ol class="chapters">
<li><a href="#el-ciclo-de-vida-del-objeto">El Ciclo de Vida del Objeto</a></li>
<li><a href="#registro-de-callbacks">Registro de Callbacks</a>
<ul>
<li><a href="#registrando-callbacks-para-activarse-en-eventos-del-ciclo-de-vida">Registrando Callbacks para Activarse en Eventos del Ciclo de Vida</a></li>
</ul></li>
<li><a href="#callbacks-disponibles">Callbacks Disponibles</a>
<ul>
<li><a href="#creando-un-objeto">Creando un Objeto</a></li>
<li><a href="#actualizando-un-objeto">Actualizando un Objeto</a></li>
<li><a href="#destruyendo-un-objeto">Destruyendo un Objeto</a></li>
<li><a href="#after-initialize-y-after-find"><code>after_initialize</code> y <code>after_find</code></a></li>
<li><a href="#after-touch"><code>after_touch</code></a></li>
</ul></li>
<li><a href="#ejecutando-callbacks">Ejecutando Callbacks</a></li>
<li><a href="#callbacks-condicionales">Callbacks Condicionales</a>
<ul>
<li><a href="#usando-if-y-unless-con-un-symbol">Usando <code>:if</code> y <code>:unless</code> con un <code>Symbol</code></a></li>
<li><a href="#usando-if-y-unless-con-un-proc">Usando <code>:if</code> y <code>:unless</code> con un <code>Proc</code></a></li>
<li><a href="#múltiples-condiciones-de-callback">Múltiples Condiciones de Callback</a></li>
<li><a href="#usando-tanto-if-como-unless">Usando Tanto <code>:if</code> como <code>:unless</code></a></li>
</ul></li>
<li><a href="#omitiendo-callbacks">Omitiendo Callbacks</a></li>
<li><a href="#suprimir-callbacks">Suprimir Callbacks</a></li>
<li><a href="#detener-la-ejecución">Detener la Ejecución</a></li>
<li><a href="#callbacks-de-asociación">Callbacks de Asociación</a></li>
<li><a href="#callbacks-de-asociación-en-cascada">Callbacks de Asociación en Cascada</a></li>
<li><a href="#callbacks-transaccionales">Callbacks Transaccionales</a>
<ul>
<li><a href="#after-commit-y-after-rollback"><code>after_commit</code> y <code>after_rollback</code></a></li>
<li><a href="#alias-para-after-commit">Alias para <code>after_commit</code></a></li>
<li><a href="#orden-de-callbacks-transaccionales">Orden de Callbacks Transaccionales</a></li>
</ul></li>
<li><a href="#objetos-de-callback">Objetos de Callback</a></li>
</ol>
</nav>
<hr>
</div>
</section>
<main id="container">
<div class="wrapper">
<div id="mainCol">
<h2 id="el-ciclo-de-vida-del-objeto"><a class="anchorlink" href="#el-ciclo-de-vida-del-objeto"><span>1</span> El Ciclo de Vida del Objeto</a></h2><p>Durante la operación normal de una aplicación Rails, los objetos pueden ser <a href="active_record_basics.html#crud-reading-and-writing-data">creados, actualizados y destruidos</a>. Active Record proporciona ganchos en este ciclo de vida del objeto para que puedas controlar tu aplicación y sus datos.</p><p>Los callbacks te permiten activar lógica antes o después de un cambio en el estado de un objeto. Son métodos que se llaman en ciertos momentos del ciclo de vida de un objeto. Con los callbacks es posible escribir código que se ejecutará cada vez que un objeto de Active Record es inicializado, creado, guardado, actualizado, eliminado, validado o cargado desde la base de datos.</p><div class="interstitial code">
<pre><code class="highlight ruby"><span class="k">class</span> <span class="nc">BirthdayCake</span> <span class="o"><</span> <span class="no">ApplicationRecord</span>
<span class="n">after_create</span> <span class="o">-></span> <span class="p">{</span> <span class="no">Rails</span><span class="p">.</span><span class="nf">logger</span><span class="p">.</span><span class="nf">info</span><span class="p">(</span><span class="s2">"¡Felicidades, el callback se ha ejecutado!"</span><span class="p">)</span> <span class="p">}</span>
<span class="k">end</span>
</code></pre>
<button class="clipboard-button" data-clipboard-text="class BirthdayCake < ApplicationRecord
after_create -> { Rails.logger.info("¡Felicidades, el callback se ha ejecutado!") }
end
">Copy</button>
</div>
<div class="interstitial code">
<pre><code class="highlight irb"><span class="gp">irb></span><span class="w"> </span><span class="no">BirthdayCake</span><span class="p">.</span><span class="nf">create</span>
<span class="go">¡Felicidades, el callback se ha ejecutado!
</span></code></pre>
<button class="clipboard-button" data-clipboard-text="BirthdayCake.create
">Copy</button>
</div>
<p>Como verás, hay muchos eventos del ciclo de vida y múltiples opciones para engancharse en ellos, ya sea antes, después o incluso alrededor de ellos.</p><h2 id="registro-de-callbacks"><a class="anchorlink" href="#registro-de-callbacks"><span>2</span> Registro de Callbacks</a></h2><p>Para usar los callbacks disponibles, necesitas implementarlos y registrarlos. La implementación puede hacerse de múltiples maneras, como usando métodos ordinarios, bloques y procs, o definiendo objetos de callback personalizados usando clases o módulos. Vamos a repasar cada una de estas técnicas de implementación.</p><p>Puedes registrar los callbacks con un <strong>método de clase estilo macro que llama a un método ordinario</strong> para la implementación.</p><div class="interstitial code">
<pre><code class="highlight ruby"><span class="k">class</span> <span class="nc">User</span> <span class="o"><</span> <span class="no">ApplicationRecord</span>
<span class="n">validates</span> <span class="ss">:username</span><span class="p">,</span> <span class="ss">:email</span><span class="p">,</span> <span class="ss">presence: </span><span class="kp">true</span>
<span class="n">before_validation</span> <span class="ss">:ensure_username_has_value</span>
<span class="kp">private</span>
<span class="k">def</span> <span class="nf">ensure_username_has_value</span>
<span class="k">if</span> <span class="n">username</span><span class="p">.</span><span class="nf">blank?</span>
<span class="nb">self</span><span class="p">.</span><span class="nf">username</span> <span class="o">=</span> <span class="n">email</span>
<span class="k">end</span>
<span class="k">end</span>
<span class="k">end</span>
</code></pre>
<button class="clipboard-button" data-clipboard-text="class User < ApplicationRecord
validates :username, :email, presence: true
before_validation :ensure_username_has_value
private
def ensure_username_has_value
if username.blank?
self.username = email
end
end
end
">Copy</button>
</div>
<p>Los <strong>métodos de clase estilo macro también pueden recibir un bloque</strong>. Considera usar este estilo si el código dentro de tu bloque es tan corto que cabe en una sola línea:</p><div class="interstitial code">
<pre><code class="highlight ruby"><span class="k">class</span> <span class="nc">User</span> <span class="o"><</span> <span class="no">ApplicationRecord</span>
<span class="n">validates</span> <span class="ss">:username</span><span class="p">,</span> <span class="ss">:email</span><span class="p">,</span> <span class="ss">presence: </span><span class="kp">true</span>
<span class="n">before_validation</span> <span class="k">do</span>
<span class="nb">self</span><span class="p">.</span><span class="nf">username</span> <span class="o">=</span> <span class="n">email</span> <span class="k">if</span> <span class="n">username</span><span class="p">.</span><span class="nf">blank?</span>
<span class="k">end</span>
<span class="k">end</span>
</code></pre>
<button class="clipboard-button" data-clipboard-text="class User < ApplicationRecord
validates :username, :email, presence: true
before_validation do
self.username = email if username.blank?
end
end
">Copy</button>
</div>
<p>Alternativamente, puedes <strong>pasar un proc al callback</strong> para que sea activado.</p><div class="interstitial code">
<pre><code class="highlight ruby"><span class="k">class</span> <span class="nc">User</span> <span class="o"><</span> <span class="no">ApplicationRecord</span>
<span class="n">validates</span> <span class="ss">:username</span><span class="p">,</span> <span class="ss">:email</span><span class="p">,</span> <span class="ss">presence: </span><span class="kp">true</span>
<span class="n">before_validation</span> <span class="o">-></span><span class="p">(</span><span class="n">user</span><span class="p">)</span> <span class="p">{</span> <span class="n">user</span><span class="p">.</span><span class="nf">username</span> <span class="o">=</span> <span class="n">user</span><span class="p">.</span><span class="nf">email</span> <span class="k">if</span> <span class="n">user</span><span class="p">.</span><span class="nf">username</span><span class="p">.</span><span class="nf">blank?</span> <span class="p">}</span>
<span class="k">end</span>
</code></pre>
<button class="clipboard-button" data-clipboard-text="class User < ApplicationRecord
validates :username, :email, presence: true
before_validation ->(user) { user.username = user.email if user.username.blank? }
end
">Copy</button>
</div>
<p>Finalmente, puedes definir <a href="#objetos-de-callback"><strong>un objeto de callback personalizado</strong></a>, como se muestra a continuación. Cubriremos esto más adelante en detalle.</p><div class="interstitial code">
<pre><code class="highlight ruby"><span class="k">class</span> <span class="nc">User</span> <span class="o"><</span> <span class="no">ApplicationRecord</span>
<span class="n">validates</span> <span class="ss">:username</span><span class="p">,</span> <span class="ss">:email</span><span class="p">,</span> <span class="ss">presence: </span><span class="kp">true</span>
<span class="n">before_validation</span> <span class="no">AddUsername</span>
<span class="k">end</span>
<span class="k">class</span> <span class="nc">AddUsername</span>
<span class="k">def</span> <span class="nc">self</span><span class="o">.</span><span class="nf">before_validation</span><span class="p">(</span><span class="n">record</span><span class="p">)</span>
<span class="k">if</span> <span class="n">record</span><span class="p">.</span><span class="nf">username</span><span class="p">.</span><span class="nf">blank?</span>
<span class="n">record</span><span class="p">.</span><span class="nf">username</span> <span class="o">=</span> <span class="n">record</span><span class="p">.</span><span class="nf">email</span>
<span class="k">end</span>
<span class="k">end</span>
<span class="k">end</span>
</code></pre>
<button class="clipboard-button" data-clipboard-text="class User < ApplicationRecord
validates :username, :email, presence: true
before_validation AddUsername
end
class AddUsername
def self.before_validation(record)
if record.username.blank?
record.username = record.email
end
end
end
">Copy</button>
</div>
<h3 id="registrando-callbacks-para-activarse-en-eventos-del-ciclo-de-vida"><a class="anchorlink" href="#registrando-callbacks-para-activarse-en-eventos-del-ciclo-de-vida"><span>2.1</span> Registrando Callbacks para Activarse en Eventos del Ciclo de Vida</a></h3><p>Los callbacks también pueden ser registrados para activarse solo en ciertos eventos del ciclo de vida. Esto se puede hacer usando la opción <code>:on</code> y permite un control completo sobre cuándo y en qué contexto se activan tus callbacks.</p><p>NOTA: Un contexto es como una categoría o un escenario en el que deseas que se apliquen ciertas validaciones. Cuando validas un modelo de ActiveRecord, puedes especificar un contexto para agrupar validaciones. Esto te permite tener diferentes conjuntos de validaciones que se aplican en diferentes situaciones. En Rails, existen ciertos contextos predeterminados para las validaciones como :create, :update, y :save.</p><div class="interstitial code">
<pre><code class="highlight ruby"><span class="k">class</span> <span class="nc">User</span> <span class="o"><</span> <span class="no">ApplicationRecord</span>
<span class="n">validates</span> <span class="ss">:username</span><span class="p">,</span> <span class="ss">:email</span><span class="p">,</span> <span class="ss">presence: </span><span class="kp">true</span>
<span class="n">before_validation</span> <span class="ss">:ensure_username_has_value</span><span class="p">,</span> <span class="ss">on: :create</span>
<span class="c1"># :on también acepta un array</span>
<span class="n">after_validation</span> <span class="ss">:set_location</span><span class="p">,</span> <span class="ss">on: </span><span class="p">[</span> <span class="ss">:create</span><span class="p">,</span> <span class="ss">:update</span> <span class="p">]</span>
<span class="kp">private</span>
<span class="k">def</span> <span class="nf">ensure_username_has_value</span>
<span class="k">if</span> <span class="n">username</span><span class="p">.</span><span class="nf">blank?</span>
<span class="nb">self</span><span class="p">.</span><span class="nf">username</span> <span class="o">=</span> <span class="n">email</span>
<span class="k">end</span>
<span class="k">end</span>
<span class="k">def</span> <span class="nf">set_location</span>
<span class="nb">self</span><span class="p">.</span><span class="nf">location</span> <span class="o">=</span> <span class="no">LocationService</span><span class="p">.</span><span class="nf">query</span><span class="p">(</span><span class="nb">self</span><span class="p">)</span>
<span class="k">end</span>
<span class="k">end</span>
</code></pre>
<button class="clipboard-button" data-clipboard-text="class User < ApplicationRecord
validates :username, :email, presence: true
before_validation :ensure_username_has_value, on: :create
# :on también acepta un array
after_validation :set_location, on: [ :create, :update ]
private
def ensure_username_has_value
if username.blank?
self.username = email
end
end
def set_location
self.location = LocationService.query(self)
end
end
">Copy</button>
</div>
<p>NOTA: Se considera una buena práctica declarar los métodos de callback como privados. Si se dejan públicos, pueden ser llamados desde fuera del modelo y violar el principio de encapsulación del objeto.</p><p>ADVERTENCIA. Abstente de usar métodos como <code>update</code>, <code>save</code> o cualquier otro método que cause efectos secundarios en el objeto dentro de tus métodos de callback. <br><br> Por ejemplo, evita llamar a <code>update(attribute: "value")</code> dentro de un callback. Esta práctica puede modificar el estado del modelo y potencialmente llevar a efectos secundarios imprevistos durante el commit. <br><br> En su lugar, puedes asignar valores directamente (por ejemplo, <code>self.attribute = "value"</code>) en <code>before_create</code>, <code>before_update</code>, o callbacks anteriores para un enfoque más seguro.</p><h2 id="callbacks-disponibles"><a class="anchorlink" href="#callbacks-disponibles"><span>3</span> Callbacks Disponibles</a></h2><p>Aquí hay una lista con todos los callbacks de Active Record disponibles, listados <strong>en el orden en que serán llamados</strong> durante las operaciones respectivas:</p><h3 id="creando-un-objeto"><a class="anchorlink" href="#creando-un-objeto"><span>3.1</span> Creando un Objeto</a></h3>
<ul>
<li><a href="https://edgeapi.rubyonrails.org/classes/ActiveModel/Validations/Callbacks/ClassMethods.html#method-i-before_validation"><code>before_validation</code></a></li>
<li><a href="https://edgeapi.rubyonrails.org/classes/ActiveModel/Validations/Callbacks/ClassMethods.html#method-i-after_validation"><code>after_validation</code></a></li>
<li><a href="https://edgeapi.rubyonrails.org/classes/ActiveRecord/Callbacks/ClassMethods.html#method-i-before_save"><code>before_save</code></a></li>
<li><a href="https://edgeapi.rubyonrails.org/classes/ActiveRecord/Callbacks/ClassMethods.html#method-i-around_save"><code>around_save</code></a></li>
<li><a href="https://edgeapi.rubyonrails.org/classes/ActiveRecord/Callbacks/ClassMethods.html#method-i-before_create"><code>before_create</code></a></li>
<li><a href="https://edgeapi.rubyonrails.org/classes/ActiveRecord/Callbacks/ClassMethods.html#method-i-around_create"><code>around_create</code></a></li>
<li><a href="https://edgeapi.rubyonrails.org/classes/ActiveRecord/Callbacks/ClassMethods.html#method-i-after_create"><code>after_create</code></a></li>
<li><a href="https://edgeapi.rubyonrails.org/classes/ActiveRecord/Callbacks/ClassMethods.html#method-i-after_save"><code>after_save</code></a></li>
<li><a href="https://edgeapi.rubyonrails.org/classes/ActiveRecord/Transactions/ClassMethods.html#method-i-after_commit"><code>after_commit</code></a> / <a href="https://edgeapi.rubyonrails.org/classes/ActiveRecord/Transactions/ClassMethods.html#method-i-after_rollback"><code>after_rollback</code></a></li>
</ul>
<p>Consulta la <a href="active_record_callbacks.html#after-comit-y-after-rollback">sección <code>after_commit</code> / <code>after_rollback</code></a> para ver ejemplos usando estos dos callbacks.</p><p>Hay ejemplos a continuación que muestran cómo usar estos callbacks. Los hemos agrupado por la operación con la que están asociados, y finalmente mostramos cómo pueden ser usados en combinación.</p><h4 id="callbacks-de-validación"><a class="anchorlink" href="#callbacks-de-validación"><span>3.1.1</span> Callbacks de Validación</a></h4><p>Los callbacks de validación se activan cada vez que el registro es validado directamente a través del método <a href="https://edgeapi.rubyonrails.org/classes/ActiveModel/Validations.html#method-i-valid-3F"><code>valid?</code></a> (o su alias <a href="https://edgeapi.rubyonrails.org/classes/ActiveModel/Validations.html#method-i-validate"><code>validate</code></a>) o <a href="https://edgeapi.rubyonrails.org/classes/ActiveModel/Validations.html#method-i-invalid-3F"><code>invalid?</code></a>, o indirectamente a través de <code>create</code>, <code>update</code>, o <code>save</code>. Se llaman antes y después de la fase de validación.</p><div class="interstitial code">
<pre><code class="highlight ruby"><span class="k">class</span> <span class="nc">User</span> <span class="o"><</span> <span class="no">ApplicationRecord</span>
<span class="n">validates</span> <span class="ss">:name</span><span class="p">,</span> <span class="ss">presence: </span><span class="kp">true</span>
<span class="n">before_validation</span> <span class="ss">:titleize_name</span>
<span class="n">after_validation</span> <span class="ss">:log_errors</span>
<span class="kp">private</span>
<span class="k">def</span> <span class="nf">titleize_name</span>
<span class="nb">self</span><span class="p">.</span><span class="nf">name</span> <span class="o">=</span> <span class="nb">name</span><span class="p">.</span><span class="nf">downcase</span><span class="p">.</span><span class="nf">titleize</span> <span class="k">if</span> <span class="nb">name</span><span class="p">.</span><span class="nf">present?</span>
<span class="no">Rails</span><span class="p">.</span><span class="nf">logger</span><span class="p">.</span><span class="nf">info</span><span class="p">(</span><span class="s2">"Name titleized to </span><span class="si">#{</span><span class="nb">name</span><span class="si">}</span><span class="s2">"</span><span class="p">)</span>
<span class="k">end</span>
<span class="k">def</span> <span class="nf">log_errors</span>
<span class="k">if</span> <span class="n">errors</span><span class="p">.</span><span class="nf">any?</span>
<span class="no">Rails</span><span class="p">.</span><span class="nf">logger</span><span class="p">.</span><span class="nf">error</span><span class="p">(</span><span class="s2">"Validation failed: </span><span class="si">#{</span><span class="n">errors</span><span class="p">.</span><span class="nf">full_messages</span><span class="p">.</span><span class="nf">join</span><span class="p">(</span><span class="s1">', '</span><span class="p">)</span><span class="si">}</span><span class="s2">"</span><span class="p">)</span>
<span class="k">end</span>
<span class="k">end</span>
<span class="k">end</span>
</code></pre>
<button class="clipboard-button" data-clipboard-text="class User < ApplicationRecord
validates :name, presence: true
before_validation :titleize_name
after_validation :log_errors
private
def titleize_name
self.name = name.downcase.titleize if name.present?
Rails.logger.info("Name titleized to #{name}")
end
def log_errors
if errors.any?
Rails.logger.error("Validation failed: #{errors.full_messages.join(', ')}")
end
end
end
">Copy</button>
</div>
<div class="interstitial code">
<pre><code class="highlight irb"><span class="gp">irb></span><span class="w"> </span><span class="n">user</span> <span class="o">=</span> <span class="no">User</span><span class="p">.</span><span class="nf">new</span><span class="p">(</span><span class="ss">name: </span><span class="s2">""</span><span class="p">,</span> <span class="ss">email: </span><span class="s2">"[email protected]"</span><span class="p">,</span> <span class="ss">password: </span><span class="s2">"abc123456"</span><span class="p">)</span>
<span class="gp">=> #<User id: nil, email: "[email protected]", created_at: nil, updated_at: nil, name: ""></span><span class="w">
</span><span class="err">
</span><span class="gp">irb></span><span class="w"> </span><span class="n">user</span><span class="p">.</span><span class="nf">valid?</span>
<span class="go">Name titleized to
Validation failed: Name can't be blank
</span><span class="p">=></span> <span class="kp">false</span>
</code></pre>
<button class="clipboard-button" data-clipboard-text="user = User.new(name: "", email: "[email protected]", password: "abc123456")
user.valid?
">Copy</button>
</div>
<h4 id="callbacks-de-guardado"><a class="anchorlink" href="#callbacks-de-guardado"><span>3.1.2</span> Callbacks de Guardado</a></h4><p>Los callbacks de guardado se activan cada vez que el registro es persistido (es decir, "guardado") en la base de datos subyacente, a través de los métodos <code>create</code>, <code>update</code> o <code>save</code>. Se llaman antes, después y alrededor del objeto que se guarda.</p><div class="interstitial code">
<pre><code class="highlight ruby"><span class="k">class</span> <span class="nc">User</span> <span class="o"><</span> <span class="no">ApplicationRecord</span>
<span class="n">before_save</span> <span class="ss">:hash_password</span>
<span class="n">around_save</span> <span class="ss">:log_saving</span>
<span class="n">after_save</span> <span class="ss">:update_cache</span>
<span class="kp">private</span>
<span class="k">def</span> <span class="nf">hash_password</span>
<span class="nb">self</span><span class="p">.</span><span class="nf">password_digest</span> <span class="o">=</span> <span class="no">BCrypt</span><span class="o">::</span><span class="no">Password</span><span class="p">.</span><span class="nf">create</span><span class="p">(</span><span class="n">password</span><span class="p">)</span>
<span class="no">Rails</span><span class="p">.</span><span class="nf">logger</span><span class="p">.</span><span class="nf">info</span><span class="p">(</span><span class="s2">"Password hashed for user with email: </span><span class="si">#{</span><span class="n">email</span><span class="si">}</span><span class="s2">"</span><span class="p">)</span>
<span class="k">end</span>
<span class="k">def</span> <span class="nf">log_saving</span>
<span class="no">Rails</span><span class="p">.</span><span class="nf">logger</span><span class="p">.</span><span class="nf">info</span><span class="p">(</span><span class="s2">"Saving user with email: </span><span class="si">#{</span><span class="n">email</span><span class="si">}</span><span class="s2">"</span><span class="p">)</span>
<span class="k">yield</span>
<span class="no">Rails</span><span class="p">.</span><span class="nf">logger</span><span class="p">.</span><span class="nf">info</span><span class="p">(</span><span class="s2">"User saved with email: </span><span class="si">#{</span><span class="n">email</span><span class="si">}</span><span class="s2">"</span><span class="p">)</span>
<span class="k">end</span>
<span class="k">def</span> <span class="nf">update_cache</span>
<span class="no">Rails</span><span class="p">.</span><span class="nf">cache</span><span class="p">.</span><span class="nf">write</span><span class="p">([</span><span class="s2">"user_data"</span><span class="p">,</span> <span class="nb">self</span><span class="p">],</span> <span class="n">attributes</span><span class="p">)</span>
<span class="no">Rails</span><span class="p">.</span><span class="nf">logger</span><span class="p">.</span><span class="nf">info</span><span class="p">(</span><span class="s2">"Update Cache"</span><span class="p">)</span>
<span class="k">end</span>
<span class="k">end</span>
</code></pre>
<button class="clipboard-button" data-clipboard-text="class User < ApplicationRecord
before_save :hash_password
around_save :log_saving
after_save :update_cache
private
def hash_password
self.password_digest = BCrypt::Password.create(password)
Rails.logger.info("Password hashed for user with email: #{email}")
end
def log_saving
Rails.logger.info("Saving user with email: #{email}")
yield
Rails.logger.info("User saved with email: #{email}")
end
def update_cache
Rails.cache.write(["user_data", self], attributes)
Rails.logger.info("Update Cache")
end
end
">Copy</button>
</div>
<div class="interstitial code">
<pre><code class="highlight irb"><span class="gp">irb></span><span class="w"> </span><span class="n">user</span> <span class="o">=</span> <span class="no">User</span><span class="p">.</span><span class="nf">create</span><span class="p">(</span><span class="ss">name: </span><span class="s2">"Jane Doe"</span><span class="p">,</span> <span class="ss">password: </span><span class="s2">"password"</span><span class="p">,</span> <span class="ss">email: </span><span class="s2">"[email protected]"</span><span class="p">)</span>
<span class="err">
</span><span class="go">Password encrypted for user with email: [email protected]
Saving user with email: [email protected]
User saved with email: [email protected]
Update Cache
</span><span class="gp">=> #<User id: 1, email: "[email protected]", created_at: "2024-03-20 16:02:43.685500000 +0000", updated_at: "2024-03-20 16:02:43.685500000 +0000", name: "Jane Doe"></span><span class="w">
</span></code></pre>
<button class="clipboard-button" data-clipboard-text="user = User.create(name: "Jane Doe", password: "password", email: "[email protected]")
">Copy</button>
</div>
<h4 id="callbacks-de-creación"><a class="anchorlink" href="#callbacks-de-creación"><span>3.1.3</span> Callbacks de Creación</a></h4><p>Los callbacks de creación se activan cada vez que el registro es persistido (es decir, "guardado") en la base de datos <strong>por primera vez</strong> — en otras palabras, cuando estamos guardando un nuevo registro, a través de los métodos <code>create</code> o <code>save</code>. Se llaman antes, después y alrededor del objeto que se crea.</p><div class="interstitial code">
<pre><code class="highlight ruby"><span class="k">class</span> <span class="nc">User</span> <span class="o"><</span> <span class="no">ApplicationRecord</span>
<span class="n">before_create</span> <span class="ss">:set_default_role</span>
<span class="n">around_create</span> <span class="ss">:log_creation</span>
<span class="n">after_create</span> <span class="ss">:send_welcome_email</span>
<span class="kp">private</span>
<span class="k">def</span> <span class="nf">set_default_role</span>
<span class="nb">self</span><span class="p">.</span><span class="nf">role</span> <span class="o">=</span> <span class="s2">"user"</span>
<span class="no">Rails</span><span class="p">.</span><span class="nf">logger</span><span class="p">.</span><span class="nf">info</span><span class="p">(</span><span class="s2">"User role set to default: user"</span><span class="p">)</span>
<span class="k">end</span>
<span class="k">def</span> <span class="nf">log_creation</span>
<span class="no">Rails</span><span class="p">.</span><span class="nf">logger</span><span class="p">.</span><span class="nf">info</span><span class="p">(</span><span class="s2">"Creating user with email: </span><span class="si">#{</span><span class="n">email</span><span class="si">}</span><span class="s2">"</span><span class="p">)</span>
<span class="k">yield</span>
<span class="no">Rails</span><span class="p">.</span><span class="nf">logger</span><span class="p">.</span><span class="nf">info</span><span class="p">(</span><span class="s2">"User created with email: </span><span class="si">#{</span><span class="n">email</span><span class="si">}</span><span class="s2">"</span><span class="p">)</span>
<span class="k">end</span>
<span class="k">def</span> <span class="nf">send_welcome_email</span>
<span class="no">UserMailer</span><span class="p">.</span><span class="nf">welcome_email</span><span class="p">(</span><span class="nb">self</span><span class="p">).</span><span class="nf">deliver_later</span>
<span class="no">Rails</span><span class="p">.</span><span class="nf">logger</span><span class="p">.</span><span class="nf">info</span><span class="p">(</span><span class="s2">"User welcome email sent to: </span><span class="si">#{</span><span class="n">email</span><span class="si">}</span><span class="s2">"</span><span class="p">)</span>
<span class="k">end</span>
<span class="k">end</span>
</code></pre>
<button class="clipboard-button" data-clipboard-text="class User < ApplicationRecord
before_create :set_default_role
around_create :log_creation
after_create :send_welcome_email
private
def set_default_role
self.role = "user"
Rails.logger.info("User role set to default: user")
end
def log_creation
Rails.logger.info("Creating user with email: #{email}")
yield
Rails.logger.info("User created with email: #{email}")
end
def send_welcome_email
UserMailer.welcome_email(self).deliver_later
Rails.logger.info("User welcome email sent to: #{email}")
end
end
">Copy</button>
</div>
<div class="interstitial code">
<pre><code class="highlight irb"><span class="gp">irb></span><span class="w"> </span><span class="n">user</span> <span class="o">=</span> <span class="no">User</span><span class="p">.</span><span class="nf">create</span><span class="p">(</span><span class="ss">name: </span><span class="s2">"John Doe"</span><span class="p">,</span> <span class="ss">email: </span><span class="s2">"[email protected]"</span><span class="p">)</span>
<span class="err">
</span><span class="go">User role set to default: user
Creating user with email: [email protected]
User created with email: [email protected]
User welcome email sent to: [email protected]
</span><span class="gp">=> #<User id: 10, email: "[email protected]", created_at: "2024-03-20 16:19:52.405195000 +0000", updated_at: "2024-03-20 16:19:52.405195000 +0000", name: "John Doe"></span><span class="w">
</span></code></pre>
<button class="clipboard-button" data-clipboard-text="user = User.create(name: "John Doe", email: "[email protected]")
">Copy</button>
</div>
<h3 id="actualizando-un-objeto"><a class="anchorlink" href="#actualizando-un-objeto"><span>3.2</span> Actualizando un Objeto</a></h3><p>Los callbacks de actualización se activan cada vez que un registro <strong>existente</strong> es persistido (es decir, "guardado") en la base de datos. Se llaman antes, después y alrededor del objeto que se actualiza.</p>
<ul>
<li><a href="https://edgeapi.rubyonrails.org/classes/ActiveModel/Validations/Callbacks/ClassMethods.html#method-i-before_validation"><code>before_validation</code></a></li>
<li><a href="https://edgeapi.rubyonrails.org/classes/ActiveModel/Validations/Callbacks/ClassMethods.html#method-i-after_validation"><code>after_validation</code></a></li>
<li><a href="https://edgeapi.rubyonrails.org/classes/ActiveRecord/Callbacks/ClassMethods.html#method-i-before_save"><code>before_save</code></a></li>
<li><a href="https://edgeapi.rubyonrails.org/classes/ActiveRecord/Callbacks/ClassMethods.html#method-i-around_save"><code>around_save</code></a></li>
<li><a href="https://edgeapi.rubyonrails.org/classes/ActiveRecord/Callbacks/ClassMethods.html#method-i-before_update"><code>before_update</code></a></li>
<li><a href="https://edgeapi.rubyonrails.org/classes/ActiveRecord/Callbacks/ClassMethods.html#method-i-around_update"><code>around_update</code></a></li>
<li><a href="https://edgeapi.rubyonrails.org/classes/ActiveRecord/Callbacks/ClassMethods.html#method-i-after_update"><code>after_update</code></a></li>
<li><a href="https://edgeapi.rubyonrails.org/classes/ActiveRecord/Callbacks/ClassMethods.html#method-i-after_save"><code>after_save</code></a></li>
<li><a href="https://edgeapi.rubyonrails.org/classes/ActiveRecord/Transactions/ClassMethods.html#method-i-after_commit"><code>after_commit</code></a> / <a href="https://edgeapi.rubyonrails.org/classes/ActiveRecord/Transactions/ClassMethods.html#method-i-after_rollback"><code>after_rollback</code></a></li>
</ul>
<p>ADVERTENCIA: El callback <code>after_save</code> se activa en las operaciones de creación y actualización. Sin embargo, se ejecuta consistentemente después de los callbacks más específicos <code>after_create</code> y <code>after_update</code>, independientemente del orden en que se realizaron las llamadas macro. De manera similar, los callbacks de guardado antes y alrededor siguen la misma regla: <code>before_save</code> se ejecuta antes de crear/actualizar, y <code>around_save</code> se ejecuta alrededor de las operaciones de creación/actualización. Es importante tener en cuenta que los callbacks de guardado siempre se ejecutarán antes/alrededor/después de los callbacks más específicos de creación/actualización.</p><p>Ya hemos cubierto los callbacks de <a href="#callbacks-de-validación">validación</a> y de <a href="#callbacks-de-guardado">guardado</a>. Consulta la <a href="#after_commit-y-after_rollback">sección <code>after_commit</code> / <code>after_rollback</code></a> para ver ejemplos usando estos dos callbacks.</p><h4 id="callbacks-de-actualización"><a class="anchorlink" href="#callbacks-de-actualización"><span>3.2.1</span> Callbacks de Actualización</a></h4><div class="interstitial code">
<pre><code class="highlight ruby"><span class="k">class</span> <span class="nc">User</span> <span class="o"><</span> <span class="no">ApplicationRecord</span>
<span class="n">before_update</span> <span class="ss">:check_role_change</span>
<span class="n">around_update</span> <span class="ss">:log_updating</span>
<span class="n">after_update</span> <span class="ss">:send_update_email</span>
<span class="kp">private</span>
<span class="k">def</span> <span class="nf">check_role_change</span>
<span class="k">if</span> <span class="n">role_changed?</span>
<span class="no">Rails</span><span class="p">.</span><span class="nf">logger</span><span class="p">.</span><span class="nf">info</span><span class="p">(</span><span class="s2">"User role changed to </span><span class="si">#{</span><span class="n">role</span><span class="si">}</span><span class="s2">"</span><span class="p">)</span>
<span class="k">end</span>
<span class="k">end</span>
<span class="k">def</span> <span class="nf">log_updating</span>
<span class="no">Rails</span><span class="p">.</span><span class="nf">logger</span><span class="p">.</span><span class="nf">info</span><span class="p">(</span><span class="s2">"Updating user with email: </span><span class="si">#{</span><span class="n">email</span><span class="si">}</span><span class="s2">"</span><span class="p">)</span>
<span class="k">yield</span>
<span class="no">Rails</span><span class="p">.</span><span class="nf">logger</span><span class="p">.</span><span class="nf">info</span><span class="p">(</span><span class="s2">"User updated with email: </span><span class="si">#{</span><span class="n">email</span><span class="si">}</span><span class="s2">"</span><span class="p">)</span>
<span class="k">end</span>
<span class="k">def</span> <span class="nf">send_update_email</span>
<span class="no">UserMailer</span><span class="p">.</span><span class="nf">update_email</span><span class="p">(</span><span class="nb">self</span><span class="p">).</span><span class="nf">deliver_later</span>
<span class="no">Rails</span><span class="p">.</span><span class="nf">logger</span><span class="p">.</span><span class="nf">info</span><span class="p">(</span><span class="s2">"Update email sent to: </span><span class="si">#{</span><span class="n">email</span><span class="si">}</span><span class="s2">"</span><span class="p">)</span>
<span class="k">end</span>
<span class="k">end</span>
</code></pre>
<button class="clipboard-button" data-clipboard-text="class User < ApplicationRecord
before_update :check_role_change
around_update :log_updating
after_update :send_update_email
private
def check_role_change
if role_changed?
Rails.logger.info("User role changed to #{role}")
end
end
def log_updating
Rails.logger.info("Updating user with email: #{email}")
yield
Rails.logger.info("User updated with email: #{email}")
end
def send_update_email
UserMailer.update_email(self).deliver_later
Rails.logger.info("Update email sent to: #{email}")
end
end
">Copy</button>
</div>
<div class="interstitial code">
<pre><code class="highlight irb"><span class="gp">irb></span><span class="w"> </span><span class="n">user</span> <span class="o">=</span> <span class="no">User</span><span class="p">.</span><span class="nf">find</span><span class="p">(</span><span class="mi">1</span><span class="p">)</span>
<span class="p">=></span> <span class="kt">#<</span><span class="no">User</span> <span class="ss">id: </span><span class="mi">1</span><span class="p">,</span> <span class="ss">email: </span><span class="s2">"[email protected]"</span><span class="p">,</span> <span class="ss">created_at: </span><span class="s2">"2024-03-20 16:19:52.405195000 +0000"</span><span class="p">,</span> <span class="ss">updated_at: </span><span class="s2">"2024-03-20 16:19:52.405195000 +0000"</span><span class="p">,</span> <span class="ss">name: </span><span class="s2">"John Doe"</span><span class="p">,</span> <span class="ss">role: </span><span class="s2">"user"</span> <span class="kt">></span>
<span class="gp">irb></span><span class="w"> </span><span class="n">user</span><span class="p">.</span><span class="nf">update</span><span class="p">(</span><span class="ss">role: </span><span class="s2">"admin"</span><span class="p">)</span>
<span class="go">User role changed to admin
Updating user with email: [email protected]
User updated with email: [email protected]
Update email sent to: [email protected]
</span></code></pre>
<button class="clipboard-button" data-clipboard-text="user = User.find(1)
user.update(role: "admin")
">Copy</button>
</div>
<h4 id="usando-una-combinación-de-callbacks"><a class="anchorlink" href="#usando-una-combinación-de-callbacks"><span>3.2.2</span> Usando una Combinación de Callbacks</a></h4><p>A menudo, necesitarás usar una combinación de callbacks para lograr el comportamiento deseado. Por ejemplo, es posible que desees enviar un correo electrónico de confirmación después de que se cree un usuario, pero solo si el usuario es nuevo y no se está actualizando. Cuando un usuario se actualiza, es posible que desees notificar a un administrador si se cambia información crítica. En este caso, puedes usar los callbacks <code>after_create</code> y <code>after_update</code> juntos.</p><div class="interstitial code">
<pre><code class="highlight ruby"><span class="k">class</span> <span class="nc">User</span> <span class="o"><</span> <span class="no">ApplicationRecord</span>
<span class="n">after_create</span> <span class="ss">:send_confirmation_email</span>
<span class="n">after_update</span> <span class="ss">:notify_admin_if_critical_info_updated</span>
<span class="kp">private</span>
<span class="k">def</span> <span class="nf">send_confirmation_email</span>
<span class="no">UserMailer</span><span class="p">.</span><span class="nf">confirmation_email</span><span class="p">(</span><span class="nb">self</span><span class="p">).</span><span class="nf">deliver_later</span>
<span class="no">Rails</span><span class="p">.</span><span class="nf">logger</span><span class="p">.</span><span class="nf">info</span><span class="p">(</span><span class="s2">"Confirmation email sent to: </span><span class="si">#{</span><span class="n">email</span><span class="si">}</span><span class="s2">"</span><span class="p">)</span>
<span class="k">end</span>
<span class="k">def</span> <span class="nf">notify_admin_if_critical_info_updated</span>
<span class="k">if</span> <span class="n">saved_change_to_email?</span> <span class="o">||</span> <span class="n">saved_change_to_phone_number?</span>
<span class="no">AdminMailer</span><span class="p">.</span><span class="nf">user_critical_info_updated</span><span class="p">(</span><span class="nb">self</span><span class="p">).</span><span class="nf">deliver_later</span>
<span class="no">Rails</span><span class="p">.</span><span class="nf">logger</span><span class="p">.</span><span class="nf">info</span><span class="p">(</span><span class="s2">"Notification sent to admin about critical info update for: </span><span class="si">#{</span><span class="n">email</span><span class="si">}</span><span class="s2">"</span><span class="p">)</span>
<span class="k">end</span>
<span class="k">end</span>
<span class="k">end</span>
</code></pre>
<button class="clipboard-button" data-clipboard-text="class User < ApplicationRecord
after_create :send_confirmation_email
after_update :notify_admin_if_critical_info_updated
private
def send_confirmation_email
UserMailer.confirmation_email(self).deliver_later
Rails.logger.info("Confirmation email sent to: #{email}")
end
def notify_admin_if_critical_info_updated
if saved_change_to_email? || saved_change_to_phone_number?
AdminMailer.user_critical_info_updated(self).deliver_later
Rails.logger.info("Notification sent to admin about critical info update for: #{email}")
end
end
end
">Copy</button>
</div>
<div class="interstitial code">
<pre><code class="highlight irb"><span class="gp">irb></span><span class="w"> </span><span class="n">user</span> <span class="o">=</span> <span class="no">User</span><span class="p">.</span><span class="nf">create</span><span class="p">(</span><span class="ss">name: </span><span class="s2">"John Doe"</span><span class="p">,</span> <span class="ss">email: </span><span class="s2">"[email protected]"</span><span class="p">)</span>
<span class="go">Confirmation email sent to: [email protected]
</span><span class="p">=></span> <span class="kt">#<</span><span class="no">User</span> <span class="ss">id: </span><span class="mi">1</span><span class="p">,</span> <span class="ss">email: </span><span class="s2">"[email protected]"</span><span class="p">,</span> <span class="o">...</span><span class="kt">></span>
<span class="gp">irb></span><span class="w"> </span><span class="n">user</span><span class="p">.</span><span class="nf">update</span><span class="p">(</span><span class="ss">email: </span><span class="s2">"[email protected]"</span><span class="p">)</span>
<span class="go">Notification sent to admin about critical info update for: [email protected]
</span><span class="p">=></span> <span class="kp">true</span>
</code></pre>
<button class="clipboard-button" data-clipboard-text="user = User.create(name: "John Doe", email: "[email protected]")
user.update(email: "[email protected]")
">Copy</button>
</div>
<h3 id="destruyendo-un-objeto"><a class="anchorlink" href="#destruyendo-un-objeto"><span>3.3</span> Destruyendo un Objeto</a></h3><p>Los callbacks de destrucción se activan cada vez que se destruye un registro, pero se ignoran cuando se elimina un registro. Se llaman antes, después y alrededor del objeto que se destruye.</p>
<ul>
<li><a href="https://edgeapi.rubyonrails.org/classes/ActiveRecord/Callbacks/ClassMethods.html#method-i-before_destroy"><code>before_destroy</code></a></li>
<li><a href="https://edgeapi.rubyonrails.org/classes/ActiveRecord/Callbacks/ClassMethods.html#method-i-around_destroy"><code>around_destroy</code></a></li>
<li><a href="https://edgeapi.rubyonrails.org/classes/ActiveRecord/Callbacks/ClassMethods.html#method-i-after_destroy"><code>after_destroy</code></a></li>
<li><a href="https://edgeapi.rubyonrails.org/classes/ActiveRecord/Transactions/ClassMethods.html#method-i-after_commit"><code>after_commit</code></a> / <a href="https://edgeapi.rubyonrails.org/classes/ActiveRecord/Transactions/ClassMethods.html#method-i-after_rollback"><code>after_rollback</code></a></li>
</ul>
<p>Encuentra <a href="#after-commit-and-after-rollback">ejemplos para usar <code>after_commit</code> / <code>after_rollback</code></a>.</p><h4 id="callbacks-de-destrucción"><a class="anchorlink" href="#callbacks-de-destrucción"><span>3.3.1</span> Callbacks de Destrucción</a></h4><div class="interstitial code">
<pre><code class="highlight ruby"><span class="k">class</span> <span class="nc">User</span> <span class="o"><</span> <span class="no">ApplicationRecord</span>
<span class="n">before_destroy</span> <span class="ss">:check_admin_count</span>
<span class="n">around_destroy</span> <span class="ss">:log_destroy_operation</span>
<span class="n">after_destroy</span> <span class="ss">:notify_users</span>
<span class="kp">private</span>
<span class="k">def</span> <span class="nf">check_admin_count</span>
<span class="k">if</span> <span class="n">admin?</span> <span class="o">&&</span> <span class="no">User</span><span class="p">.</span><span class="nf">where</span><span class="p">(</span><span class="ss">role: </span><span class="s2">"admin"</span><span class="p">).</span><span class="nf">count</span> <span class="o">==</span> <span class="mi">1</span>
<span class="kp">throw</span> <span class="ss">:abort</span>
<span class="k">end</span>
<span class="no">Rails</span><span class="p">.</span><span class="nf">logger</span><span class="p">.</span><span class="nf">info</span><span class="p">(</span><span class="s2">"Checked the admin count"</span><span class="p">)</span>
<span class="k">end</span>
<span class="k">def</span> <span class="nf">log_destroy_operation</span>
<span class="no">Rails</span><span class="p">.</span><span class="nf">logger</span><span class="p">.</span><span class="nf">info</span><span class="p">(</span><span class="s2">"About to destroy user with ID </span><span class="si">#{</span><span class="nb">id</span><span class="si">}</span><span class="s2">"</span><span class="p">)</span>
<span class="k">yield</span>
<span class="no">Rails</span><span class="p">.</span><span class="nf">logger</span><span class="p">.</span><span class="nf">info</span><span class="p">(</span><span class="s2">"User with ID </span><span class="si">#{</span><span class="nb">id</span><span class="si">}</span><span class="s2"> destroyed successfully"</span><span class="p">)</span>
<span class="k">end</span>
<span class="k">def</span> <span class="nf">notify_users</span>
<span class="no">UserMailer</span><span class="p">.</span><span class="nf">deletion_email</span><span class="p">(</span><span class="nb">self</span><span class="p">).</span><span class="nf">deliver_later</span>
<span class="no">Rails</span><span class="p">.</span><span class="nf">logger</span><span class="p">.</span><span class="nf">info</span><span class="p">(</span><span class="s2">"Notification sent to other users about user deletion"</span><span class="p">)</span>
<span class="k">end</span>
<span class="k">end</span>
</code></pre>
<button class="clipboard-button" data-clipboard-text="class User < ApplicationRecord
before_destroy :check_admin_count
around_destroy :log_destroy_operation
after_destroy :notify_users
private
def check_admin_count
if admin? && User.where(role: "admin").count == 1
throw :abort
end
Rails.logger.info("Checked the admin count")
end
def log_destroy_operation
Rails.logger.info("About to destroy user with ID #{id}")
yield
Rails.logger.info("User with ID #{id} destroyed successfully")
end
def notify_users
UserMailer.deletion_email(self).deliver_later
Rails.logger.info("Notification sent to other users about user deletion")
end
end
">Copy</button>
</div>
<div class="interstitial code">
<pre><code class="highlight irb"><span class="gp">irb></span><span class="w"> </span><span class="n">user</span> <span class="o">=</span> <span class="no">User</span><span class="p">.</span><span class="nf">find</span><span class="p">(</span><span class="mi">1</span><span class="p">)</span>
<span class="gp">=> #<User id: 1, email: "[email protected]", created_at: "2024-03-20 16:19:52.405195000 +0000", updated_at: "2024-03-20 16:19:52.405195000 +0000", name: "John Doe", role: "admin"></span><span class="w">
</span><span class="err">
</span><span class="gp">irb></span><span class="w"> </span><span class="n">user</span><span class="p">.</span><span class="nf">destroy</span>
<span class="go">Checked the admin count
About to destroy user with ID 1
User with ID 1 destroyed successfully
Notification sent to other users about user deletion
</span></code></pre>
<button class="clipboard-button" data-clipboard-text="user = User.find(1)
user.destroy
">Copy</button>
</div>
<h3 id="after-initialize-y-after-find"><a class="anchorlink" href="#after-initialize-y-after-find"><span>3.4</span> <code>after_initialize</code> y <code>after_find</code></a></h3><p>Cada vez que se instancia un objeto de Active Record, ya sea directamente usando <code>new</code> o cuando se carga un registro desde la base de datos, se llamará al callback <a href="https://edgeapi.rubyonrails.org/classes/ActiveRecord/Callbacks/ClassMethods.html#method-i-after_initialize"><code>after_initialize</code></a>. Puede ser útil para evitar la necesidad de sobrescribir directamente tu método <code>initialize</code> de Active Record.</p><p>Cuando se carga un registro desde la base de datos, se llamará al callback <a href="https://edgeapi.rubyonrails.org/classes/ActiveRecord/Callbacks/ClassMethods.html#method-i-after_find"><code>after_find</code></a>. <code>after_find</code> se llama antes de <code>after_initialize</code> si ambos están definidos.</p><p>NOTA: Los callbacks <code>after_initialize</code> y <code>after_find</code> no tienen contrapartes <code>before_*</code>.</p><p>Pueden registrarse al igual que los otros callbacks de Active Record.</p><div class="interstitial code">
<pre><code class="highlight ruby"><span class="k">class</span> <span class="nc">User</span> <span class="o"><</span> <span class="no">ApplicationRecord</span>
<span class="n">after_initialize</span> <span class="k">do</span> <span class="o">|</span><span class="n">user</span><span class="o">|</span>
<span class="no">Rails</span><span class="p">.</span><span class="nf">logger</span><span class="p">.</span><span class="nf">info</span><span class="p">(</span><span class="s2">"You have initialized an object!"</span><span class="p">)</span>
<span class="k">end</span>
<span class="n">after_find</span> <span class="k">do</span> <span class="o">|</span><span class="n">user</span><span class="o">|</span>
<span class="no">Rails</span><span class="p">.</span><span class="nf">logger</span><span class="p">.</span><span class="nf">info</span><span class="p">(</span><span class="s2">"You have found an object!"</span><span class="p">)</span>
<span class="k">end</span>
<span class="k">end</span>
</code></pre>
<button class="clipboard-button" data-clipboard-text="class User < ApplicationRecord
after_initialize do |user|
Rails.logger.info("You have initialized an object!")
end
after_find do |user|
Rails.logger.info("You have found an object!")
end
end
">Copy</button>
</div>
<div class="interstitial code">
<pre><code class="highlight irb"><span class="gp">irb></span><span class="w"> </span><span class="no">User</span><span class="p">.</span><span class="nf">new</span>
<span class="go">You have initialized an object!
</span><span class="p">=></span> <span class="kt">#<</span><span class="no">User</span> <span class="ss">id: </span><span class="kp">nil</span><span class="kt">></span>
<span class="gp">irb></span><span class="w"> </span><span class="no">User</span><span class="p">.</span><span class="nf">first</span>
<span class="go">You have found an object!
You have initialized an object!
</span><span class="p">=></span> <span class="kt">#<</span><span class="no">User</span> <span class="ss">id: </span><span class="mi">1</span><span class="kt">></span>
</code></pre>
<button class="clipboard-button" data-clipboard-text="User.new
User.first
">Copy</button>
</div>
<h3 id="after-touch"><a class="anchorlink" href="#after-touch"><span>3.5</span> <code>after_touch</code></a></h3><p>El callback <a href="https://edgeapi.rubyonrails.org/classes/ActiveRecord/Callbacks/ClassMethods.html#method-i-after_touch"><code>after_touch</code></a> se llamará cada vez que se toque un objeto de Active Record. Puedes <a href="https://edgeapi.rubyonrails.org/classes/ActiveRecord/Persistence.html#method-i-touch">leer más sobre <code>touch</code> en la documentación de la API</a>.</p><div class="interstitial code">
<pre><code class="highlight ruby"><span class="k">class</span> <span class="nc">User</span> <span class="o"><</span> <span class="no">ApplicationRecord</span>
<span class="n">after_touch</span> <span class="k">do</span> <span class="o">|</span><span class="n">user</span><span class="o">|</span>
<span class="no">Rails</span><span class="p">.</span><span class="nf">logger</span><span class="p">.</span><span class="nf">info</span><span class="p">(</span><span class="s2">"You have touched an object"</span><span class="p">)</span>
<span class="k">end</span>
<span class="k">end</span>
</code></pre>
<button class="clipboard-button" data-clipboard-text="class User < ApplicationRecord
after_touch do |user|
Rails.logger.info("You have touched an object")
end
end
">Copy</button>
</div>
<div class="interstitial code">
<pre><code class="highlight irb"><span class="gp">irb></span><span class="w"> </span><span class="n">user</span> <span class="o">=</span> <span class="no">User</span><span class="p">.</span><span class="nf">create</span><span class="p">(</span><span class="ss">name: </span><span class="s2">"Kuldeep"</span><span class="p">)</span>
<span class="gp">=> #<User id: 1, name: "Kuldeep", created_at: "2013-11-25 12:17:49", updated_at: "2013-11-25 12:17:49"></span><span class="w">
</span><span class="err">
</span><span class="gp">irb></span><span class="w"> </span><span class="n">user</span><span class="p">.</span><span class="nf">touch</span>
<span class="go">You have touched an object
</span><span class="p">=></span> <span class="kp">true</span>
</code></pre>
<button class="clipboard-button" data-clipboard-text="user = User.create(name: "Kuldeep")
user.touch
">Copy</button>
</div>
<p>Puede usarse junto con <code>belongs_to</code>:</p><div class="interstitial code">
<pre><code class="highlight ruby"><span class="k">class</span> <span class="nc">Book</span> <span class="o"><</span> <span class="no">ApplicationRecord</span>
<span class="n">belongs_to</span> <span class="ss">:library</span><span class="p">,</span> <span class="ss">touch: </span><span class="kp">true</span>
<span class="n">after_touch</span> <span class="k">do</span>
<span class="no">Rails</span><span class="p">.</span><span class="nf">logger</span><span class="p">.</span><span class="nf">info</span><span class="p">(</span><span class="s2">"A Book was touched"</span><span class="p">)</span>
<span class="k">end</span>
<span class="k">end</span>
<span class="k">class</span> <span class="nc">Library</span> <span class="o"><</span> <span class="no">ApplicationRecord</span>
<span class="n">has_many</span> <span class="ss">:books</span>
<span class="n">after_touch</span> <span class="ss">:log_when_books_or_library_touched</span>
<span class="kp">private</span>
<span class="k">def</span> <span class="nf">log_when_books_or_library_touched</span>
<span class="no">Rails</span><span class="p">.</span><span class="nf">logger</span><span class="p">.</span><span class="nf">info</span><span class="p">(</span><span class="s2">"Book/Library was touched"</span><span class="p">)</span>
<span class="k">end</span>
<span class="k">end</span>
</code></pre>
<button class="clipboard-button" data-clipboard-text="class Book < ApplicationRecord
belongs_to :library, touch: true
after_touch do
Rails.logger.info("A Book was touched")
end
end
class Library < ApplicationRecord
has_many :books
after_touch :log_when_books_or_library_touched
private
def log_when_books_or_library_touched
Rails.logger.info("Book/Library was touched")
end
end
">Copy</button>
</div>
<div class="interstitial code">
<pre><code class="highlight irb"><span class="gp">irb></span><span class="w"> </span><span class="n">book</span> <span class="o">=</span> <span class="no">Book</span><span class="p">.</span><span class="nf">last</span>
<span class="gp">=> #<Book id: 1, library_id: 1, created_at: "2013-11-25 17:04:22", updated_at: "2013-11-25 17:05:05"></span><span class="w">
</span><span class="err">
</span><span class="gp">irb></span><span class="w"> </span><span class="n">book</span><span class="p">.</span><span class="nf">touch</span> <span class="c1"># triggers book.library.touch</span>
<span class="go">A Book was touched
Book/Library was touched
</span><span class="p">=></span> <span class="kp">true</span>
</code></pre>
<button class="clipboard-button" data-clipboard-text="book = Book.last
book.touch # triggers book.library.touch
">Copy</button>
</div>
<h2 id="ejecutando-callbacks"><a class="anchorlink" href="#ejecutando-callbacks"><span>4</span> Ejecutando Callbacks</a></h2><p>Los siguientes métodos activan callbacks:</p>
<ul>
<li><code>create</code></li>
<li><code>create!</code></li>
<li><code>destroy</code></li>
<li><code>destroy!</code></li>
<li><code>destroy_all</code></li>
<li><code>destroy_by</code></li>
<li><code>save</code></li>
<li><code>save!</code></li>
<li><code>save(validate: false)</code></li>
<li><code>save!(validate: false)</code></li>
<li><code>toggle!</code></li>
<li><code>touch</code></li>
<li><code>update_attribute</code></li>
<li><code>update_attribute!</code></li>
<li><code>update</code></li>
<li><code>update!</code></li>
<li><code>valid?</code></li>
<li><code>validate</code></li>
</ul>
<p>Además, el callback <code>after_find</code> se activa mediante los siguientes métodos de búsqueda:</p>
<ul>
<li><code>all</code></li>
<li><code>first</code></li>
<li><code>find</code></li>
<li><code>find_by</code></li>
<li><code>find_by!</code></li>
<li><code>find_by_*</code></li>
<li><code>find_by_*!</code></li>
<li><code>find_by_sql</code></li>
<li><code>last</code></li>
<li><code>sole</code></li>
<li><code>take</code></li>
</ul>
<p>El callback <code>after_initialize</code> se activa cada vez que se inicializa un nuevo objeto de la clase.</p><p>NOTA: Los métodos <code>find_by_*</code> y <code>find_by_*!</code> son buscadores dinámicos generados automáticamente para cada atributo. Aprende más sobre ellos en la <a href="active_record_querying.html#dynamic-finders">sección de Buscadores dinámicos</a>.</p><h2 id="callbacks-condicionales"><a class="anchorlink" href="#callbacks-condicionales"><span>5</span> Callbacks Condicionales</a></h2><p>Al igual que con las <a href="active_record_validations.html">validaciones</a>, también podemos hacer que la llamada de un método de callback sea condicional a la satisfacción de un predicado dado. Podemos hacer esto usando las opciones <code>:if</code> y <code>:unless</code>, que pueden tomar un símbolo, un <code>Proc</code> o un <code>Array</code>.</p><p>Puedes usar la opción <code>:if</code> cuando quieras especificar bajo qué condiciones el callback <strong>debe</strong> ser llamado. Si quieres especificar las condiciones bajo las cuales el callback <strong>no debe</strong> ser llamado, entonces puedes usar la opción <code>:unless</code>.</p><h3 id="usando-if-y-unless-con-un-symbol"><a class="anchorlink" href="#usando-if-y-unless-con-un-symbol"><span>5.1</span> Usando <code>:if</code> y <code>:unless</code> con un <code>Symbol</code></a></h3><p>Puedes asociar las opciones <code>:if</code> y <code>:unless</code> con un símbolo que corresponde al nombre de un método predicado que se llamará justo antes del callback.</p><p>Cuando usas la opción <code>:if</code>, el callback <strong>no</strong> se ejecutará si el método predicado devuelve <strong>false</strong>; cuando usas la opción <code>:unless</code>, el callback <strong>no</strong> se ejecutará si el método predicado devuelve <strong>true</strong>. Esta es la opción más común.</p><div class="interstitial code">
<pre><code class="highlight ruby"><span class="k">class</span> <span class="nc">Order</span> <span class="o"><</span> <span class="no">ApplicationRecord</span>
<span class="n">before_save</span> <span class="ss">:normalize_card_number</span><span class="p">,</span> <span class="ss">if: :paid_with_card?</span>
<span class="k">end</span>
</code></pre>
<button class="clipboard-button" data-clipboard-text="class Order < ApplicationRecord
before_save :normalize_card_number, if: :paid_with_card?
end
">Copy</button>
</div>
<p>Usando esta forma de registro también es posible registrar varios predicados diferentes que deben ser llamados para verificar si el callback debe ser ejecutado. Cubriremos esto en la <a href="#múltiples-condiciones-de-callback">sección Múltiples Condiciones de Callback</a>.</p><h3 id="usando-if-y-unless-con-un-proc"><a class="anchorlink" href="#usando-if-y-unless-con-un-proc"><span>5.2</span> Usando <code>:if</code> y <code>:unless</code> con un <code>Proc</code></a></h3><p>Es posible asociar <code>:if</code> y <code>:unless</code> con un objeto <code>Proc</code>. Esta opción es más adecuada cuando se escriben métodos de validación cortos, generalmente de una sola línea:</p><div class="interstitial code">
<pre><code class="highlight ruby"><span class="k">class</span> <span class="nc">Order</span> <span class="o"><</span> <span class="no">ApplicationRecord</span>
<span class="n">before_save</span> <span class="ss">:normalize_card_number</span><span class="p">,</span>
<span class="ss">if: </span><span class="o">-></span><span class="p">(</span><span class="n">order</span><span class="p">)</span> <span class="p">{</span> <span class="n">order</span><span class="p">.</span><span class="nf">paid_with_card?</span> <span class="p">}</span>
<span class="k">end</span>
</code></pre>
<button class="clipboard-button" data-clipboard-text="class Order < ApplicationRecord
before_save :normalize_card_number,
if: ->(order) { order.paid_with_card? }
end
">Copy</button>
</div>
<p>Dado que el proc se evalúa en el contexto del objeto, también es posible escribir esto como:</p><div class="interstitial code">
<pre><code class="highlight ruby"><span class="k">class</span> <span class="nc">Order</span> <span class="o"><</span> <span class="no">ApplicationRecord</span>
<span class="n">before_save</span> <span class="ss">:normalize_card_number</span><span class="p">,</span> <span class="ss">if: </span><span class="o">-></span> <span class="p">{</span> <span class="n">paid_with_card?</span> <span class="p">}</span>
<span class="k">end</span>
</code></pre>
<button class="clipboard-button" data-clipboard-text="class Order < ApplicationRecord
before_save :normalize_card_number, if: -> { paid_with_card? }
end
">Copy</button>
</div>
<h3 id="múltiples-condiciones-de-callback"><a class="anchorlink" href="#múltiples-condiciones-de-callback"><span>5.3</span> Múltiples Condiciones de Callback</a></h3><p>Las opciones <code>:if</code> y <code>:unless</code> también aceptan un array de procs o nombres de métodos como símbolos:</p><div class="interstitial code">
<pre><code class="highlight ruby"><span class="k">class</span> <span class="nc">Comment</span> <span class="o"><</span> <span class="no">ApplicationRecord</span>
<span class="n">before_save</span> <span class="ss">:filter_content</span><span class="p">,</span>
<span class="ss">if: </span><span class="p">[</span><span class="ss">:subject_to_parental_control?</span><span class="p">,</span> <span class="ss">:untrusted_author?</span><span class="p">]</span>
<span class="k">end</span>
</code></pre>
<button class="clipboard-button" data-clipboard-text="class Comment < ApplicationRecord
before_save :filter_content,
if: [:subject_to_parental_control?, :untrusted_author?]
end
">Copy</button>
</div>
<p>Puedes incluir fácilmente un proc en la lista de condiciones:</p><div class="interstitial code">
<pre><code class="highlight ruby"><span class="k">class</span> <span class="nc">Comment</span> <span class="o"><</span> <span class="no">ApplicationRecord</span>
<span class="n">before_save</span> <span class="ss">:filter_content</span><span class="p">,</span>
<span class="ss">if: </span><span class="p">[</span><span class="ss">:subject_to_parental_control?</span><span class="p">,</span> <span class="o">-></span> <span class="p">{</span> <span class="n">untrusted_author?</span> <span class="p">}]</span>
<span class="k">end</span>
</code></pre>
<button class="clipboard-button" data-clipboard-text="class Comment < ApplicationRecord
before_save :filter_content,
if: [:subject_to_parental_control?, -> { untrusted_author? }]
end
">Copy</button>
</div>
<h3 id="usando-tanto-if-como-unless"><a class="anchorlink" href="#usando-tanto-if-como-unless"><span>5.4</span> Usando Tanto <code>:if</code> como <code>:unless</code></a></h3><p>Los callbacks pueden mezclar tanto <code>:if</code> como <code>:unless</code> en la misma declaración:</p><div class="interstitial code">
<pre><code class="highlight ruby"><span class="k">class</span> <span class="nc">Comment</span> <span class="o"><</span> <span class="no">ApplicationRecord</span>
<span class="n">before_save</span> <span class="ss">:filter_content</span><span class="p">,</span>
<span class="ss">if: </span><span class="o">-></span> <span class="p">{</span> <span class="n">forum</span><span class="p">.</span><span class="nf">parental_control?</span> <span class="p">},</span>
<span class="ss">unless: </span><span class="o">-></span> <span class="p">{</span> <span class="n">author</span><span class="p">.</span><span class="nf">trusted?</span> <span class="p">}</span>
<span class="k">end</span>
</code></pre>
<button class="clipboard-button" data-clipboard-text="class Comment < ApplicationRecord
before_save :filter_content,
if: -> { forum.parental_control? },
unless: -> { author.trusted? }
end
">Copy</button>
</div>
<p>El callback solo se ejecuta cuando todas las condiciones de <code>:if</code> y ninguna de las condiciones de <code>:unless</code> se evalúan como <code>true</code>.</p><h2 id="omitiendo-callbacks"><a class="anchorlink" href="#omitiendo-callbacks"><span>6</span> Omitiendo Callbacks</a></h2><p>Al igual que con las <a href="active_record_validations.html">validaciones</a>, también es posible omitir callbacks usando los siguientes métodos:</p>
<ul>
<li><a href="https://edgeapi.rubyonrails.org/classes/ActiveRecord/Persistence.html#method-i-decrement-21"><code>decrement!</code></a></li>
<li><a href="https://edgeapi.rubyonrails.org/classes/ActiveRecord/CounterCache/ClassMethods.html#method-i-decrement_counter"><code>decrement_counter</code></a></li>
<li><a href="https://edgeapi.rubyonrails.org/classes/ActiveRecord/Persistence.html#method-i-delete"><code>delete</code></a></li>
<li><a href="https://edgeapi.rubyonrails.org/classes/ActiveRecord/Relation.html#method-i-delete_all"><code>delete_all</code></a></li>