diff options
Diffstat (limited to 'guides')
113 files changed, 1543 insertions, 703 deletions
diff --git a/guides/CHANGELOG.md b/guides/CHANGELOG.md index 21b4ed4b9f..0307e06fd9 100644 --- a/guides/CHANGELOG.md +++ b/guides/CHANGELOG.md @@ -1,5 +1,3 @@ -## Rails 6.0.0.alpha (Unreleased) ## - * Rails 6 requires Ruby 2.4.1 or newer. *Jeremy Daer* diff --git a/guides/assets/images/rails4_features.png b/guides/assets/images/4_0_release_notes/rails4_features.png Binary files differindex ac73f05cf7..ac73f05cf7 100644 --- a/guides/assets/images/rails4_features.png +++ b/guides/assets/images/4_0_release_notes/rails4_features.png diff --git a/guides/assets/images/akshaysurve.jpg b/guides/assets/images/akshaysurve.jpg Binary files differdeleted file mode 100644 index cfc3333958..0000000000 --- a/guides/assets/images/akshaysurve.jpg +++ /dev/null diff --git a/guides/assets/images/belongs_to.png b/guides/assets/images/association_basics/belongs_to.png Binary files differindex 2b8c1d52ea..2b8c1d52ea 100644 --- a/guides/assets/images/belongs_to.png +++ b/guides/assets/images/association_basics/belongs_to.png diff --git a/guides/assets/images/habtm.png b/guides/assets/images/association_basics/habtm.png Binary files differindex 7e508cc1a6..7e508cc1a6 100644 --- a/guides/assets/images/habtm.png +++ b/guides/assets/images/association_basics/habtm.png diff --git a/guides/assets/images/has_many.png b/guides/assets/images/association_basics/has_many.png Binary files differindex 36ccf9f0f6..36ccf9f0f6 100644 --- a/guides/assets/images/has_many.png +++ b/guides/assets/images/association_basics/has_many.png diff --git a/guides/assets/images/has_many_through.png b/guides/assets/images/association_basics/has_many_through.png Binary files differindex 9e9caabd73..9e9caabd73 100644 --- a/guides/assets/images/has_many_through.png +++ b/guides/assets/images/association_basics/has_many_through.png diff --git a/guides/assets/images/has_one.png b/guides/assets/images/association_basics/has_one.png Binary files differindex c29c6b9c59..c29c6b9c59 100644 --- a/guides/assets/images/has_one.png +++ b/guides/assets/images/association_basics/has_one.png diff --git a/guides/assets/images/has_one_through.png b/guides/assets/images/association_basics/has_one_through.png Binary files differindex fdf13286c4..fdf13286c4 100644 --- a/guides/assets/images/has_one_through.png +++ b/guides/assets/images/association_basics/has_one_through.png diff --git a/guides/assets/images/polymorphic.png b/guides/assets/images/association_basics/polymorphic.png Binary files differindex d630db9e01..d630db9e01 100644 --- a/guides/assets/images/polymorphic.png +++ b/guides/assets/images/association_basics/polymorphic.png diff --git a/guides/assets/images/credits_pic_blank.gif b/guides/assets/images/credits_pic_blank.gif Binary files differdeleted file mode 100644 index a6b335d0c9..0000000000 --- a/guides/assets/images/credits_pic_blank.gif +++ /dev/null diff --git a/guides/assets/images/fxn.png b/guides/assets/images/fxn.png Binary files differdeleted file mode 100644 index 733d380cba..0000000000 --- a/guides/assets/images/fxn.png +++ /dev/null diff --git a/guides/assets/images/getting_started/rails_welcome.png b/guides/assets/images/getting_started/rails_welcome.png Binary files differindex 44f89ec8de..88efe34a9d 100644 --- a/guides/assets/images/getting_started/rails_welcome.png +++ b/guides/assets/images/getting_started/rails_welcome.png 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 Binary files differdeleted file mode 100644 index 08c54f921f..0000000000 --- a/guides/assets/images/getting_started/routing_error_no_route_matches.png +++ /dev/null diff --git a/guides/assets/images/header_backdrop.png b/guides/assets/images/header_backdrop.png Binary files differdeleted file mode 100644 index 81f4d91774..0000000000 --- a/guides/assets/images/header_backdrop.png +++ /dev/null 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 Binary files differdeleted file mode 100644 index c5d02adcf4..0000000000 --- a/guides/assets/images/icons/callouts/1.png +++ /dev/null diff --git a/guides/assets/images/icons/callouts/10.png b/guides/assets/images/icons/callouts/10.png Binary files differdeleted file mode 100644 index fe89f9ef83..0000000000 --- a/guides/assets/images/icons/callouts/10.png +++ /dev/null diff --git a/guides/assets/images/icons/callouts/11.png b/guides/assets/images/icons/callouts/11.png Binary files differdeleted file mode 100644 index 3b7b9318e7..0000000000 --- a/guides/assets/images/icons/callouts/11.png +++ /dev/null diff --git a/guides/assets/images/icons/callouts/12.png b/guides/assets/images/icons/callouts/12.png Binary files differdeleted file mode 100644 index 7b95925e9d..0000000000 --- a/guides/assets/images/icons/callouts/12.png +++ /dev/null diff --git a/guides/assets/images/icons/callouts/13.png b/guides/assets/images/icons/callouts/13.png Binary files differdeleted file mode 100644 index 4b99fe8efc..0000000000 --- a/guides/assets/images/icons/callouts/13.png +++ /dev/null diff --git a/guides/assets/images/icons/callouts/14.png b/guides/assets/images/icons/callouts/14.png Binary files differdeleted file mode 100644 index dbde9ca749..0000000000 --- a/guides/assets/images/icons/callouts/14.png +++ /dev/null diff --git a/guides/assets/images/icons/callouts/15.png b/guides/assets/images/icons/callouts/15.png Binary files differdeleted file mode 100644 index 70e4bba615..0000000000 --- a/guides/assets/images/icons/callouts/15.png +++ /dev/null diff --git a/guides/assets/images/icons/callouts/2.png b/guides/assets/images/icons/callouts/2.png Binary files differdeleted file mode 100644 index 8c57970ba9..0000000000 --- a/guides/assets/images/icons/callouts/2.png +++ /dev/null diff --git a/guides/assets/images/icons/callouts/3.png b/guides/assets/images/icons/callouts/3.png Binary files differdeleted file mode 100644 index 57a33d15b4..0000000000 --- a/guides/assets/images/icons/callouts/3.png +++ /dev/null diff --git a/guides/assets/images/icons/callouts/4.png b/guides/assets/images/icons/callouts/4.png Binary files differdeleted file mode 100644 index f061ab02b8..0000000000 --- a/guides/assets/images/icons/callouts/4.png +++ /dev/null diff --git a/guides/assets/images/icons/callouts/5.png b/guides/assets/images/icons/callouts/5.png Binary files differdeleted file mode 100644 index b4de02da11..0000000000 --- a/guides/assets/images/icons/callouts/5.png +++ /dev/null diff --git a/guides/assets/images/icons/callouts/6.png b/guides/assets/images/icons/callouts/6.png Binary files differdeleted file mode 100644 index 0e055eec1e..0000000000 --- a/guides/assets/images/icons/callouts/6.png +++ /dev/null diff --git a/guides/assets/images/icons/callouts/7.png b/guides/assets/images/icons/callouts/7.png Binary files differdeleted file mode 100644 index 5ead87d040..0000000000 --- a/guides/assets/images/icons/callouts/7.png +++ /dev/null diff --git a/guides/assets/images/icons/callouts/8.png b/guides/assets/images/icons/callouts/8.png Binary files differdeleted file mode 100644 index cb99545eb6..0000000000 --- a/guides/assets/images/icons/callouts/8.png +++ /dev/null diff --git a/guides/assets/images/icons/callouts/9.png b/guides/assets/images/icons/callouts/9.png Binary files differdeleted file mode 100644 index 0ac03602f6..0000000000 --- a/guides/assets/images/icons/callouts/9.png +++ /dev/null diff --git a/guides/assets/images/icons/caution.png b/guides/assets/images/icons/caution.png Binary files differdeleted file mode 100644 index 7227b54b32..0000000000 --- a/guides/assets/images/icons/caution.png +++ /dev/null diff --git a/guides/assets/images/icons/example.png b/guides/assets/images/icons/example.png Binary files differdeleted file mode 100644 index a0e855befa..0000000000 --- a/guides/assets/images/icons/example.png +++ /dev/null diff --git a/guides/assets/images/icons/home.png b/guides/assets/images/icons/home.png Binary files differdeleted file mode 100644 index e70e164522..0000000000 --- a/guides/assets/images/icons/home.png +++ /dev/null diff --git a/guides/assets/images/icons/important.png b/guides/assets/images/icons/important.png Binary files differdeleted file mode 100644 index bab53bf3aa..0000000000 --- a/guides/assets/images/icons/important.png +++ /dev/null diff --git a/guides/assets/images/icons/next.png b/guides/assets/images/icons/next.png Binary files differdeleted file mode 100644 index a158832725..0000000000 --- a/guides/assets/images/icons/next.png +++ /dev/null diff --git a/guides/assets/images/icons/note.png b/guides/assets/images/icons/note.png Binary files differdeleted file mode 100644 index 62eec7845f..0000000000 --- a/guides/assets/images/icons/note.png +++ /dev/null diff --git a/guides/assets/images/icons/prev.png b/guides/assets/images/icons/prev.png Binary files differdeleted file mode 100644 index 8a96960422..0000000000 --- a/guides/assets/images/icons/prev.png +++ /dev/null diff --git a/guides/assets/images/icons/tip.png b/guides/assets/images/icons/tip.png Binary files differdeleted file mode 100644 index a5316d318f..0000000000 --- a/guides/assets/images/icons/tip.png +++ /dev/null diff --git a/guides/assets/images/icons/up.png b/guides/assets/images/icons/up.png Binary files differdeleted file mode 100644 index 6cac818170..0000000000 --- a/guides/assets/images/icons/up.png +++ /dev/null diff --git a/guides/assets/images/icons/warning.png b/guides/assets/images/icons/warning.png Binary files differdeleted file mode 100644 index 72a8a5d873..0000000000 --- a/guides/assets/images/icons/warning.png +++ /dev/null diff --git a/guides/assets/images/oscardelben.jpg b/guides/assets/images/oscardelben.jpg Binary files differdeleted file mode 100644 index 9f3f67c2c7..0000000000 --- a/guides/assets/images/oscardelben.jpg +++ /dev/null diff --git a/guides/assets/images/radar.png b/guides/assets/images/radar.png Binary files differdeleted file mode 100644 index 421b62b623..0000000000 --- a/guides/assets/images/radar.png +++ /dev/null diff --git a/guides/assets/images/rails_logo_remix.gif b/guides/assets/images/rails_logo_remix.gif Binary files differdeleted file mode 100644 index 58960ee4f9..0000000000 --- a/guides/assets/images/rails_logo_remix.gif +++ /dev/null diff --git a/guides/assets/images/csrf.png b/guides/assets/images/security/csrf.png Binary files differindex a8123d47c3..a8123d47c3 100644 --- a/guides/assets/images/csrf.png +++ b/guides/assets/images/security/csrf.png diff --git a/guides/assets/images/session_fixation.png b/guides/assets/images/security/session_fixation.png Binary files differindex e009484f09..e009484f09 100644 --- a/guides/assets/images/session_fixation.png +++ b/guides/assets/images/security/session_fixation.png diff --git a/guides/assets/images/tab_yellow.png b/guides/assets/images/tab_yellow.png Binary files differdeleted file mode 100644 index 053c807d28..0000000000 --- a/guides/assets/images/tab_yellow.png +++ /dev/null diff --git a/guides/assets/images/vijaydev.jpg b/guides/assets/images/vijaydev.jpg Binary files differdeleted file mode 100644 index fe5e4f1cb4..0000000000 --- a/guides/assets/images/vijaydev.jpg +++ /dev/null diff --git a/guides/assets/javascripts/guides.js b/guides/assets/javascripts/guides.js index e4d25dfb21..e39ac239cd 100644 --- a/guides/assets/javascripts/guides.js +++ b/guides/assets/javascripts/guides.js @@ -1,53 +1,54 @@ -$.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"); - } - }); - $("#guidesMenu").on("click", function(e) { - $("#guides").toggle(); - return false; +(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; + } + + document.addEventListener("DOMContentLoaded", function() { + var guidesMenu = document.getElementById("guidesMenu"); + var guides = document.getElementById("guides"); + + guidesMenu.addEventListener("click", function(e) { + e.preventDefault(); + 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) { + 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..24906dddeb 100644 --- a/guides/assets/javascripts/responsive-tables.js +++ b/guides/assets/javascripts/responsive-tables.js @@ -1,43 +1,50 @@ -$(document).ready(function() { +(function() { + "use strict"; + var switched = false; - $("table").not(".syntaxhighlighter").addClass("responsive"); + + // For old browsers + var each = function(node, callback) { + var array = Array.prototype.slice.call(node); + for(var i = 0; i < array.length; i++) callback(array[i]); + } + + each(document.querySelectorAll(":not(.syntaxhighlighter)>table"), function(element) { + element.classList.add("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("DOMContentLoaded", 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/stylesheets/main.css b/guides/assets/stylesheets/main.css index b27776745a..00d4bcb21e 100644 --- a/guides/assets/stylesheets/main.css +++ b/guides/assets/stylesheets/main.css @@ -70,7 +70,7 @@ table { } table th, table td { - padding: 0.25em 1em; + padding: 9px 10px; border: 1px solid #CCC; border-collapse: collapse; } @@ -79,7 +79,6 @@ table th { border-bottom: 2px solid #CCC; background: #EEE; font-weight: bold; - padding: 0.5em 1em; } img { @@ -265,8 +264,6 @@ body { } } -#extraCol {display: none;} - #footer { padding: 2em 0; background: #222 url(../images/footer_tile.gif) repeat-x; @@ -410,6 +407,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; @@ -555,8 +556,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) { @@ -665,10 +664,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. */ @@ -696,9 +693,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/bug_report_templates/action_controller_gem.rb b/guides/bug_report_templates/action_controller_gem.rb index 7fc85e636a..e8b6ad19dd 100644 --- a/guides/bug_report_templates/action_controller_gem.rb +++ b/guides/bug_report_templates/action_controller_gem.rb @@ -13,7 +13,7 @@ gemfile(true) do git_source(:github) { |repo| "https://github.com/#{repo}.git" } # Activate the gem you are reporting the issue against. - gem "rails", "5.2.0.rc1" + gem "rails", "5.2.0" end require "rack/test" diff --git a/guides/bug_report_templates/active_job_gem.rb b/guides/bug_report_templates/active_job_gem.rb index 6b30a7d446..720b7e9c51 100644 --- a/guides/bug_report_templates/active_job_gem.rb +++ b/guides/bug_report_templates/active_job_gem.rb @@ -13,7 +13,7 @@ gemfile(true) do git_source(:github) { |repo| "https://github.com/#{repo}.git" } # Activate the gem you are reporting the issue against. - gem "activejob", "5.2.0.rc1" + gem "activejob", "5.2.0" end require "minitest/autorun" diff --git a/guides/bug_report_templates/active_record_gem.rb b/guides/bug_report_templates/active_record_gem.rb index fabc2a2382..c0d705239b 100644 --- a/guides/bug_report_templates/active_record_gem.rb +++ b/guides/bug_report_templates/active_record_gem.rb @@ -13,7 +13,7 @@ gemfile(true) do git_source(:github) { |repo| "https://github.com/#{repo}.git" } # Activate the gem you are reporting the issue against. - gem "activerecord", "5.2.0.rc1" + gem "activerecord", "5.2.0" 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 ca9987f956..f47cf08766 100644 --- a/guides/bug_report_templates/active_record_migrations_gem.rb +++ b/guides/bug_report_templates/active_record_migrations_gem.rb @@ -13,7 +13,7 @@ gemfile(true) do git_source(:github) { |repo| "https://github.com/#{repo}.git" } # Activate the gem you are reporting the issue against. - gem "activerecord", "5.2.0.rc1" + gem "activerecord", "5.2.0" gem "sqlite3" end diff --git a/guides/bug_report_templates/generic_gem.rb b/guides/bug_report_templates/generic_gem.rb index 7a55d7c660..0935354bf4 100644 --- a/guides/bug_report_templates/generic_gem.rb +++ b/guides/bug_report_templates/generic_gem.rb @@ -13,7 +13,7 @@ gemfile(true) do git_source(:github) { |repo| "https://github.com/#{repo}.git" } # Activate the gem you are reporting the issue against. - gem "activesupport", "5.2.0.rc1" + gem "activesupport", "5.2.0" end require "active_support" diff --git a/guides/rails_guides/generator.rb b/guides/rails_guides/generator.rb index 7205f37be7..c83538ad48 100644 --- a/guides/rails_guides/generator.rb +++ b/guides/rails_guides/generator.rb @@ -141,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 a6970fb90c..5ab1388c29 100644 --- a/guides/rails_guides/helpers.rb +++ b/guides/rails_guides/helpers.rb @@ -38,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/kindle.rb b/guides/rails_guides/kindle.rb index 5c4f7d159c..d370541d2e 100644 --- a/guides/rails_guides/kindle.rb +++ b/guides/rails_guides/kindle.rb @@ -35,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 x =~ /(toc|welcome|copyright).html/ frontmatter << x unless x =~ /toc/ true end diff --git a/guides/source/2_2_release_notes.md b/guides/source/2_2_release_notes.md index afe0550a17..005331977e 100644 --- a/guides/source/2_2_release_notes.md +++ b/guides/source/2_2_release_notes.md @@ -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 diff --git a/guides/source/2_3_release_notes.md b/guides/source/2_3_release_notes.md index 634569fa2d..2b8c9351e8 100644 --- a/guides/source/2_3_release_notes.md +++ b/guides/source/2_3_release_notes.md @@ -54,7 +54,7 @@ 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. -* 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 @@ -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) @@ -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. diff --git a/guides/source/3_0_release_notes.md b/guides/source/3_0_release_notes.md index 7ffa7d4a5c..f4b5eb3c4c 100644 --- a/guides/source/3_0_release_notes.md +++ b/guides/source/3_0_release_notes.md @@ -155,7 +155,7 @@ 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). -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. @@ -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/4_0_release_notes.md b/guides/source/4_0_release_notes.md index 0921cd1979..a1a6a225b2 100644 --- a/guides/source/4_0_release_notes.md +++ b/guides/source/4_0_release_notes.md @@ -55,7 +55,7 @@ $ ruby /path/to/rails/railties/bin/rails new myapp --dev Major Features -------------- -[](http://guides.rubyonrails.org/images/rails4_features.png) +[](http://guides.rubyonrails.org/images/4_0_release_notes/rails4_features.png) ### Upgrade diff --git a/guides/source/5_0_release_notes.md b/guides/source/5_0_release_notes.md index 656838c6b8..04d4bd75cd 100644 --- a/guides/source/5_0_release_notes.md +++ b/guides/source/5_0_release_notes.md @@ -73,7 +73,7 @@ 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 +- Configure the generators to skip generating views, helpers, and assets when you generate a new resource. The application provides a base for APIs, @@ -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 index 852d04b1f6..68c120fd78 100644 --- a/guides/source/5_1_release_notes.md +++ b/guides/source/5_1_release_notes.md @@ -102,7 +102,7 @@ Secrets will be decrypted in production, using a key stored either in the [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. +order to share instance variables, headers, and other common setup. ``` ruby class InvitationsMailer < ApplicationMailer @@ -170,7 +170,7 @@ 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. +can generate form tags based on URLs, scopes, or models. Using just a URL: diff --git a/guides/source/5_2_release_notes.md b/guides/source/5_2_release_notes.md index 7b5c4b87e3..ab24c7e590 100644 --- a/guides/source/5_2_release_notes.md +++ b/guides/source/5_2_release_notes.md @@ -7,9 +7,9 @@ Highlights in Rails 5.2: * Active Storage * Redis Cache Store -* HTTP/2 Early hints support +* HTTP/2 Early Hints * Credentials -* Default Content Security Policy +* 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 @@ -24,39 +24,73 @@ 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. - +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 -[README](https://github.com/rails/rails/blob/d3893ec38ec61282c2598b01a298124356d6b35a/activestorage/README.md) +[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 support +### 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) - -### Default Content Security Policy +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) -Incompatibilities ------------------ - -ToDo +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 -------- @@ -68,37 +102,105 @@ Please refer to the [Changelog][railties] for detailed changes. * Deprecate `capify!` method in generators and templates. ([Pull Request](https://github.com/rails/rails/pull/29493)) -* Deprecated passing the environment's name as a regular argument to the - `rails dbconsole` and `rails console` commands. - ([Pull Request](https://github.com/rails/rails/pull/29358)) +* 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)) -* Deprecated using subclass of `Rails::Application` to start the Rails server. +* Deprecate using subclass of `Rails::Application` to start the Rails server. ([Pull Request](https://github.com/rails/rails/pull/30127)) -* Deprecated `after_bundle` callback in Rails plugin templates. +* Deprecate `after_bundle` callback in Rails plugin templates. ([Pull Request](https://github.com/rails/rails/pull/29446)) ### Notable changes -ToDo +* 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/48766e32d31)) + ([Commit](https://github.com/rails/rails/commit/48766e32d31651606b9f68a16015ad05c3b0de2c)) ### Notable changes -* Added support for `host`, `port`, `db` and `password` options in cable.yml +* Add support for `host`, `port`, `db` and `password` options in cable.yml ([Pull Request](https://github.com/rails/rails/pull/29528)) -* Added support for compatibility with redis-rb gem for 4.0 version. - ([Pull Request](https://github.com/rails/rails/pull/30748)) +* Hash long stream identifiers when using PostgreSQL adapter. + ([Pull Request](https://github.com/rails/rails/pull/29297)) Action Pack ----------- @@ -107,38 +209,131 @@ Please refer to the [Changelog][action-pack] for detailed changes. ### Removals -* Removed deprecated `ActionController::ParamsParser::ParseError`. - ([Commit](https://github.com/rails/rails/commit/e16c765ac6d)) +* Remove deprecated `ActionController::ParamsParser::ParseError`. + ([Commit](https://github.com/rails/rails/commit/e16c765ac6dcff068ff2e5554d69ff345c003de1)) ### Deprecations -* Deprecated `#success?`, `#missing?` and `#error?` aliases of +* Deprecate `#success?`, `#missing?` and `#error?` aliases of `ActionDispatch::TestResponse`. ([Pull Request](https://github.com/rails/rails/pull/30104)) ### Notable changes -ToDo +* 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 -* Removed deprecated Erubis ERB handler. - ([Commit](https://github.com/rails/rails/commit/7de7f12fd14)) +* Remove deprecated Erubis ERB handler. + ([Commit](https://github.com/rails/rails/commit/7de7f12fd140a60134defe7dc55b5a20b2372d06)) ### Deprecations -* Deprecated `image_alt` helper which used to add default alt text to +* 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 -ToDo +* 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 ------------- @@ -147,35 +342,306 @@ Please refer to the [Changelog][action-mailer] for detailed changes. ### Notable changes -ToDo +* 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. -ToDo +### 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 -ToDo +* 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 -ToDo +* 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. -### Removals +### Notable changes -ToDo +* 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)) -### Notable changes +* 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)) -ToDo +* 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 -------------- @@ -184,35 +650,203 @@ Please refer to the [Changelog][active-support] for detailed changes. ### Removals -ToDo +* 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 -ToDo +* 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 -ToDo +* 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. -### Removals +### 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)) -ToDo +Ruby on Rails Guides +-------------------- + +Please refer to the [Changelog][guides] for detailed changes. ### Notable changes -ToDo +* 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 +[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 @@ -224,3 +858,4 @@ framework it is. Kudos to all of them. [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/_welcome.html.erb b/guides/source/_welcome.html.erb index 6959f992aa..5dd6bfdd23 100644 --- a/guides/source/_welcome.html.erb +++ b/guides/source/_welcome.html.erb @@ -10,17 +10,20 @@ </p> <% else %> <p> - These are the new guides for Rails 5.1 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/v5.2/">Rails 5.2</a>, <a href="http://guides.rubyonrails.org/v5.1/">Rails 5.1</a>, <a href="http://guides.rubyonrails.org/v5.0/">Rails 5.0</a>, <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/v3.2/">Rails 3.2</a>, +<a href="http://guides.rubyonrails.org/v3.1/">Rails 3.1</a>, +<a href="http://guides.rubyonrails.org/v3.0/">Rails 3.0</a>, and <a href="http://guides.rubyonrails.org/v2.3/">Rails 2.3</a>. </p> diff --git a/guides/source/action_controller_overview.md b/guides/source/action_controller_overview.md index 6ecfb57db3..60a19542e6 100644 --- a/guides/source/action_controller_overview.md +++ b/guides/source/action_controller_overview.md @@ -23,7 +23,7 @@ What Does a Controller Do? 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](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. +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 @@ -450,14 +450,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 @@ -476,7 +478,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 @@ -775,9 +777,9 @@ 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. @@ -855,7 +857,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 -------------------- @@ -1181,22 +1183,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 fe31e3403f..86d06508b0 100644 --- a/guides/source/action_mailer_basics.md +++ b/guides/source/action_mailer_basics.md @@ -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 -------------- @@ -60,8 +69,7 @@ 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`: @@ -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: @@ -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 @@ -234,7 +238,7 @@ 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 @@ -266,7 +270,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') @@ -807,7 +811,7 @@ config.action_mailer.smtp_settings = { authentication: 'plain', 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. +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. diff --git a/guides/source/action_view_overview.md b/guides/source/action_view_overview.md index 1cba5c6fb6..b85568af5c 100644 --- a/guides/source/action_view_overview.md +++ b/guides/source/action_view_overview.md @@ -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: @@ -760,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 @@ -1102,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 @@ -1113,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 @@ -1130,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 @@ -1267,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 97d98efba0..3183fccd4f 100644 --- a/guides/source/active_job_basics.md +++ b/guides/source/active_job_basics.md @@ -120,7 +120,7 @@ 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`. ``` @@ -290,7 +290,7 @@ For example, you could send metrics for every job enqueued: ```ruby class ApplicationJob - before_enqueue { |job| $statsd.increment "#{job.name.underscore}.enqueue" } + before_enqueue { |job| $statsd.increment "#{job.class.name.underscore}.enqueue" } end ``` diff --git a/guides/source/active_model_basics.md b/guides/source/active_model_basics.md index ee0472621b..4b0ea32d7c 100644 --- a/guides/source/active_model_basics.md +++ b/guides/source/active_model_basics.md @@ -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 diff --git a/guides/source/active_record_basics.md b/guides/source/active_record_basics.md index 2f85b765a3..182bc865f0 100644 --- a/guides/source/active_record_basics.md +++ b/guides/source/active_record_basics.md @@ -13,7 +13,7 @@ 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. -------------------------------------------------------------------------------- @@ -211,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. @@ -324,7 +324,7 @@ 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 @@ -353,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 @@ -387,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 4f54b4c206..0f74daace6 100644 --- a/guides/source/active_record_callbacks.md +++ b/guides/source/active_record_callbacks.md @@ -408,7 +408,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` diff --git a/guides/source/active_record_migrations.md b/guides/source/active_record_migrations.md index ab3af438f5..dda87802bf 100644 --- a/guides/source/active_record_migrations.md +++ b/guides/source/active_record_migrations.md @@ -442,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. @@ -788,7 +788,7 @@ 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` task will create the database, load the schema, and initialize it with the seed data. ### Resetting the Database @@ -896,7 +896,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 `bin/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 @@ -917,35 +917,29 @@ 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. +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. -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. - -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 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 @@ -967,36 +961,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. - -NOTE: `db/schema.rb` cannot express database specific items such as triggers, -sequences, stored procedures or check constraints, etc. Please note that while -custom SQL statements can be run in migrations, these statements cannot be reconstituted -by the schema dumper. 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 --------------------------------------- diff --git a/guides/source/active_record_postgresql.md b/guides/source/active_record_postgresql.md index 58c61f0864..796b65d6d4 100644 --- a/guides/source/active_record_postgresql.md +++ b/guides/source/active_record_postgresql.md @@ -84,7 +84,7 @@ Book.where("array_length(ratings, 1) >= 3") ### Hstore * [type definition](https://www.postgresql.org/docs/current/static/hstore.html) -* [functions and operators](https://www.postgresql.org/docs/current/static/hstore.html#AEN179902) +* [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. @@ -290,7 +290,7 @@ SELECT n.nspname AS enum_schema, ### UUID * [type definition](https://www.postgresql.org/docs/current/static/datatype-uuid.html) -* [pgcrypto generator function](https://www.postgresql.org/docs/current/static/pgcrypto.html#AEN182570) +* [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` @@ -349,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 diff --git a/guides/source/active_record_querying.md b/guides/source/active_record_querying.md index 4e28e31a53..944cee8a23 100644 --- a/guides/source/active_record_querying.md +++ b/guides/source/active_record_querying.md @@ -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 diff --git a/guides/source/active_record_validations.md b/guides/source/active_record_validations.md index d076efcd54..c7846a0283 100644 --- a/guides/source/active_record_validations.md +++ b/guides/source/active_record_validations.md @@ -745,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. diff --git a/guides/source/active_storage_overview.md b/guides/source/active_storage_overview.md index a7cb14c52a..91ad089d40 100644 --- a/guides/source/active_storage_overview.md +++ b/guides/source/active_storage_overview.md @@ -93,15 +93,6 @@ local: root: <%= Rails.root.join("storage") %> ``` -Optionally specify a host for generating URLs (the default is `http://localhost:3000`): - -```yaml -local: - service: Disk - root: <%= Rails.root.join("storage") %> - host: http://myapp.test -``` - ### Amazon S3 Service Declare an S3 service in `config/storage.yml`: @@ -123,6 +114,13 @@ 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`: @@ -130,7 +128,6 @@ Declare an Azure Storage service in `config/storage.yml`: ```yaml azure: service: AzureStorage - path: "" storage_account_name: "" storage_access_key: "" container: "" @@ -302,6 +299,42 @@ Call `images.attached?` to determine whether a particular message has any images @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 -------------- @@ -337,25 +370,63 @@ helper allows you to set the disposition. 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 variation of the image, call `variant` on the Blob. -You can pass any [MiniMagick](https://github.com/minimagick/minimagick) -supported transformation to the method. +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 `mini_magick` to your `Gemfile`: +To enable variants, add the `image_processing` gem to your `Gemfile`: ```ruby -gem 'mini_magick' +gem 'image_processing', '~> 1.2' ``` -When the browser hits the variant URL, Active Storage will lazy transform the -original blob into the format you specified and redirect to its new service +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: "100x100") %> +<%= 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 @@ -369,7 +440,7 @@ the box, Active Storage supports previewing videos and PDF documents. <ul> <% @message.files.each do |file| %> <li> - <%= image_tag file.preview(resize: "100x100>") %> + <%= image_tag file.preview(resize_to_limit: [100, 100]) %> </li> <% end %> </ul> @@ -519,6 +590,92 @@ input[type=file][data-direct-upload-url][disabled] { } ``` +### 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 ------------------------------------------- diff --git a/guides/source/active_support_core_extensions.md b/guides/source/active_support_core_extensions.md index 73b24b900a..057651e0cf 100644 --- a/guides/source/active_support_core_extensions.md +++ b/guides/source/active_support_core_extensions.md @@ -779,6 +779,14 @@ 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` @@ -2031,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| 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`: @@ -2818,16 +2841,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` ---------------------- @@ -2876,9 +2889,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 @@ -2887,18 +2900,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?` diff --git a/guides/source/active_support_instrumentation.md b/guides/source/active_support_instrumentation.md index 11c4a8222a..ac40fda11d 100644 --- a/guides/source/active_support_instrumentation.md +++ b/guides/source/active_support_instrumentation.md @@ -3,7 +3,7 @@ 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 diff --git a/guides/source/api_app.md b/guides/source/api_app.md index b4d90d31de..d6b228b2f8 100644 --- a/guides/source/api_app.md +++ b/guides/source/api_app.md @@ -24,7 +24,7 @@ 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 @@ -98,7 +98,7 @@ Handled at the Action Pack layer: - 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. @@ -106,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 @@ -148,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 @@ -375,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. @@ -413,7 +412,7 @@ Some common modules you might want to add: - `AbstractController::Translation`: Support for the `l` and `t` localization and translation methods. -- 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` diff --git a/guides/source/asset_pipeline.md b/guides/source/asset_pipeline.md index 618896d458..5ac3586889 100644 --- a/guides/source/asset_pipeline.md +++ b/guides/source/asset_pipeline.md @@ -20,7 +20,7 @@ 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. @@ -224,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. @@ -434,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 @@ -728,8 +728,8 @@ Rails.application.config.assets.precompile += %w( admin.js admin.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 task 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: @@ -845,7 +845,7 @@ 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 @@ -917,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 diff --git a/guides/source/association_basics.md b/guides/source/association_basics.md index f895cadea5..e7408b5a7f 100644 --- a/guides/source/association_basics.md +++ b/guides/source/association_basics.md @@ -94,7 +94,7 @@ class Book < ApplicationRecord end ``` - + 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. @@ -127,7 +127,7 @@ class Supplier < ApplicationRecord end ``` - + The corresponding migration might look like this: @@ -171,7 +171,7 @@ end NOTE: The name of the other model is pluralized when declaring a `has_many` association. - + The corresponding migration might look like this: @@ -213,7 +213,7 @@ class Patient < ApplicationRecord end ``` - + The corresponding migration might look like this: @@ -299,7 +299,7 @@ class AccountHistory < ApplicationRecord end ``` - + The corresponding migration might look like this: @@ -340,7 +340,7 @@ class Part < ApplicationRecord end ``` - + 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 ``` - + ### 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 ``` @@ -2391,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 5428b16edc..767e158a7e 100644 --- a/guides/source/autoloading_and_reloading_constants.md +++ b/guides/source/autoloading_and_reloading_constants.md @@ -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 diff --git a/guides/source/caching_with_rails.md b/guides/source/caching_with_rails.md index 5dde6f34fa..f760f0a005 100644 --- a/guides/source/caching_with_rails.md +++ b/guides/source/caching_with_rails.md @@ -408,7 +408,7 @@ 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. +it will not be possible to manually read, write, or expire the cache via the Rails console. ### ActiveSupport::Cache::FileStore @@ -446,30 +446,28 @@ config.cache_store = :mem_cache_store, "cache-1.example.com", "cache-2.example.c ### ActiveSupport::Cache::RedisCacheStore -The Redis cache store takes advantage of Redis support for least-recently-used -and least-frequently-used key eviction when it reaches max memory, allowing it -to behave much like a Memcached cache server. +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 an all-cache Redis server, set `maxmemory-policy` to an `allkeys` policy. -Redis 4+ support least-frequently-used (`allkeys-lfu`) eviction, an excellent -default choice. Redis 3 and earlier should use `allkeys-lru` for -least-recently-used eviction. +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. +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, +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`, @@ -477,12 +475,33 @@ 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. -Putting it all together, a production Redis cache store may look something -like this: +To get started, add the redis gem to your Gemfile: ```ruby -cache_servers = %w[ "redis://cache-01:6379/0", "redis://cache-02:6379/0", … ], -config.cache_store = :redis_cache_store, url: cache_servers, +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 @@ -491,9 +510,10 @@ config.cache_store = :redis_cache_store, url: cache_servers, error_handler: -> (method:, returning:, exception:) { # Report errors to Sentry as warnings - Raven.capture_exception exception, level: 'warning", + Raven.capture_exception exception, level: 'warning', tags: { method: method, returning: returning } } +} ``` ### ActiveSupport::Cache::NullStore diff --git a/guides/source/command_line.md b/guides/source/command_line.md index b41e8bbec6..58a2d6d30f 100644 --- a/guides/source/command_line.md +++ b/guides/source/command_line.md @@ -161,7 +161,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`): @@ -329,7 +329,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`. @@ -457,7 +457,7 @@ More information about migrations can be found in the [Migrations](active_record ### `notes` -`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. +`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. ```bash $ bin/rails notes @@ -500,7 +500,7 @@ app/models/article.rb: NOTE. When using specific annotations and custom annotations, the annotation name (FIXME, BUG etc) is not displayed in the output lines. -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. +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. ```ruby config.annotations.register_directories("spec", "vendor") @@ -537,8 +537,8 @@ The `tmp:` namespaced tasks will help you clear and create the `Rails.root/tmp` * `rails tmp:cache:clear` clears `tmp/cache`. * `rails tmp:sockets:clear` clears `tmp/sockets`. * `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. +* `rails tmp:clear` clears all cache, sockets, and screenshot files. +* `rails tmp:create` creates tmp directories for cache, sockets, and pids. ### Miscellaneous @@ -587,7 +587,7 @@ $ bin/rails "task_name[value 1]" # entire argument string should be quoted $ bin/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 ------------------------------- diff --git a/guides/source/configuring.md b/guides/source/configuring.md index a87b8a2f48..d4aa6546a7 100644 --- a/guides/source/configuring.md +++ b/guides/source/configuring.md @@ -86,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. @@ -400,10 +400,16 @@ by adding the following to your `application.rb` file: Rails.application.config.active_record.sqlite3.represent_boolean_as_integer = true ``` -The schema dumper adds one additional configuration option: +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.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 `config.action_controller` includes a number of configuration settings: @@ -502,6 +508,10 @@ Defaults to `'signed cookie'`. * `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.perform_deep_munge` configures whether `deep_munge` method should be performed on the parameters. See [Security Guide](security.html#unsafe-query-generation) for more information. It defaults to `true`. @@ -590,6 +600,13 @@ Defaults to `'signed cookie'`. * `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`: @@ -761,6 +778,8 @@ normal Rails server. `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. @@ -1187,7 +1206,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". @@ -1246,23 +1265,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. @@ -1270,7 +1289,7 @@ 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 `ActiveSupport::Callbacks.to_run`. @@ -1362,7 +1381,7 @@ 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. diff --git a/guides/source/contributing_to_ruby_on_rails.md b/guides/source/contributing_to_ruby_on_rails.md index c1668f989b..ba5d7bbee8 100644 --- a/guides/source/contributing_to_ruby_on_rails.md +++ b/guides/source/contributing_to_ruby_on_rails.md @@ -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) @@ -132,7 +132,7 @@ 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 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. 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. @@ -384,7 +384,7 @@ $ RUBYOPT=-W0 bundle exec rake test 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: @@ -398,7 +398,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* ``` 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 "fxn". -<% 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 "bensie". -<% 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 => :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 —a.k.a. miloops— 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 "miloops". -<% 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 07c78be3db..b7476a4ab2 100644 --- a/guides/source/debugging_rails_applications.md +++ b/guides/source/debugging_rails_applications.md @@ -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}" @@ -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: @@ -777,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 @@ -875,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/engines.md b/guides/source/engines.md index 8d81296fa5..9dbce5d09b 100644 --- a/guides/source/engines.md +++ b/guides/source/engines.md @@ -188,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 @@ -461,7 +461,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 @@ -998,7 +998,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 @@ -1020,11 +1020,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 diff --git a/guides/source/form_helpers.md b/guides/source/form_helpers.md index 53c567727f..0ee64c855e 100644 --- a/guides/source/form_helpers.md +++ b/guides/source/form_helpers.md @@ -165,7 +165,7 @@ 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-local fields, month fields, week fields, -URL fields, email fields, number fields and range fields: +URL fields, email fields, number fields, and range fields: ```erb <%= text_area_tag(:message, "Hi, nice site", size: "24x6") %> @@ -208,7 +208,7 @@ 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 @@ -442,7 +442,7 @@ output: 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. +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: @@ -709,7 +709,7 @@ 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. +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. @@ -763,7 +763,7 @@ We can mix and match these two concepts. One element of a hash might be an array 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. -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. @@ -823,7 +823,7 @@ will create inputs like <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_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 shortcut you can append [] to the name and omit the `:index` option. This is the same as specifying `index: address` so @@ -873,7 +873,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 @@ -890,7 +890,7 @@ 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 diff --git a/guides/source/generators.md b/guides/source/generators.md index b7b8262e4a..11fca5f9fb 100644 --- a/guides/source/generators.md +++ b/guides/source/generators.md @@ -221,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| @@ -233,7 +233,7 @@ 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: diff --git a/guides/source/getting_started.md b/guides/source/getting_started.md index f545b90103..de2c459cff 100644 --- a/guides/source/getting_started.md +++ b/guides/source/getting_started.md @@ -93,11 +93,9 @@ ruby 2.5.0 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: 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/). +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/). @@ -169,7 +167,7 @@ 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.| +|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. For more information about Rack, see the [Rack website](https://rack.github.io/).| @@ -181,6 +179,7 @@ of the files and folders that Rails created by default: |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.| |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.| @@ -342,7 +341,7 @@ 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 @@ -810,7 +809,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 @@ -1125,7 +1124,7 @@ 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 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>: @@ -1522,7 +1521,7 @@ 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 @@ -1857,7 +1856,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 @@ -2060,7 +2059,7 @@ 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: diff --git a/guides/source/i18n.md b/guides/source/i18n.md index 2b545e6b82..ec7582fa62 100644 --- a/guides/source/i18n.md +++ b/guides/source/i18n.md @@ -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: @@ -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](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. 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. @@ -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: @@ -827,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: @@ -843,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: @@ -855,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. @@ -1099,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) @@ -1166,13 +1166,22 @@ 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](https://groups.google.com/forum/#!forum/rails-i18n). - Contributing to Rails I18n -------------------------- @@ -1181,7 +1190,7 @@ I18n support in Ruby on Rails was introduced in the release 2.2 and is still evo 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 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/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 0f4228ed6b..b77ac2e99d 100644 --- a/guides/source/kindle/toc.html.erb +++ b/guides/source/kindle/toc.html.erb @@ -18,7 +18,6 @@ Ruby on Rails Guides <% end %> <hr /> <ul> - <li><a href="credits.html">Credits</a></li> <li><a href="copyright.html">Copyright & 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 & License</text></navLabel> <content src="copyright.html"/> diff --git a/guides/source/layout.html.erb b/guides/source/layout.html.erb index 3981199e95..4ed2793fe3 100644 --- a/guides/source/layout.html.erb +++ b/guides/source/layout.html.erb @@ -29,7 +29,7 @@ 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="https://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="http://api.rubyonrails.org/">API</a></li> <li class="more-info"><a href="https://stackoverflow.com/questions/tagged/ruby-on-rails">Ask for help</a></li> @@ -59,7 +59,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> @@ -124,15 +123,8 @@ </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> + <script type="text/javascript" src="javascripts/guides.js"></script> + <script type="text/javascript" src="javascripts/responsive-tables.js"></script> </body> </html> diff --git a/guides/source/layouts_and_rendering.md b/guides/source/layouts_and_rendering.md index 15345c94b7..d7072a766b 100644 --- a/guides/source/layouts_and_rendering.md +++ b/guides/source/layouts_and_rendering.md @@ -170,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: @@ -403,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 diff --git a/guides/source/maintenance_policy.md b/guides/source/maintenance_policy.md index 1d6a4edb5b..2604d289e9 100644 --- a/guides/source/maintenance_policy.md +++ b/guides/source/maintenance_policy.md @@ -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.1.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.1.Z`, `5.0.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.1.Z`, `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/plugins.md b/guides/source/plugins.md index 15073af6be..5d18f8a1f4 100644 --- a/guides/source/plugins.md +++ b/guides/source/plugins.md @@ -135,7 +135,7 @@ 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 diff --git a/guides/source/rails_on_rack.md b/guides/source/rails_on_rack.md index 1627205b7b..8d66942e31 100644 --- a/guides/source/rails_on_rack.md +++ b/guides/source/rails_on_rack.md @@ -13,12 +13,12 @@ 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. 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) @@ -134,7 +134,7 @@ The default middlewares shown here (and some others) are each summarized in the ### 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 diff --git a/guides/source/routing.md b/guides/source/routing.md index 98aa841938..41f80a3814 100644 --- a/guides/source/routing.md +++ b/guides/source/routing.md @@ -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: @@ -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 @@ -174,7 +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. +As with plural resources, the same helpers ending in `_url` will also include the host, port, and path prefix. ### Controller Namespaces and Routing @@ -622,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 @@ -1039,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 @@ -1117,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 @@ -1138,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 diff --git a/guides/source/security.md b/guides/source/security.md index 28ddbdc26a..6e390d872f 100644 --- a/guides/source/security.md +++ b/guides/source/security.md @@ -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 -------- @@ -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 @@ -217,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._ - + 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: @@ -244,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. @@ -272,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. - + 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: @@ -282,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_. @@ -302,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. Some legacy web browsers, however, do not support them - only GET and POST. Rails uses a hidden `_method` field to handle these cases. +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. @@ -325,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: @@ -392,7 +392,7 @@ 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): @@ -462,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_. @@ -502,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 @@ -639,13 +639,13 @@ 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 -NOTE: _When sanitizing, protecting or verifying something, prefer whitelists over blacklists._ +NOTE: _When sanitizing, protecting, or verifying something, prefer whitelists over blacklists._ -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 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_: * 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 <strong> instead of removing <script> against Cross-Site Scripting (XSS). See below for details. @@ -718,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: @@ -742,7 +742,7 @@ 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. @@ -785,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> @@ -860,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](https://samy.pl/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: @@ -949,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: @@ -1089,6 +1089,118 @@ Here is a list of common headers: * **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](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 ---------------------- diff --git a/guides/source/testing.md b/guides/source/testing.md index b9b310cbba..0a6d2d6555 100644 --- a/guides/source/testing.md +++ b/guides/source/testing.md @@ -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. @@ -433,16 +433,8 @@ at the end of test run and so on. Check the documentation of the test runner as ```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. - -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 @@ -453,13 +445,22 @@ You can run multiple files and directories at the same time: 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 Run tests in the ENV environment + -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 @@ -503,7 +504,7 @@ Two hooks are provided, one runs when the process is forked, and one runs before 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` metod +The `parallelize_setup` method is called right after the processes are forked. The `parallelize_teardown` method is called right before the processes are closed. ``` @@ -690,7 +691,7 @@ System Testing -------------- System tests allow you to test user interactions with your application, running tests -in either a real or a headless browser. System tests uses Capybara under the hood. +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. @@ -1084,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. @@ -1130,7 +1131,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. @@ -1484,7 +1485,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 @@ -1494,7 +1495,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) @@ -1595,12 +1596,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' } diff --git a/guides/source/upgrading_ruby_on_rails.md b/guides/source/upgrading_ruby_on_rails.md index ff7ccfd47d..55e78a47de 100644 --- a/guides/source/upgrading_ruby_on_rails.md +++ b/guides/source/upgrading_ruby_on_rails.md @@ -66,6 +66,17 @@ 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. +Upgrading from Rails 5.2 to Rails 6.0 +------------------------------------- + +### 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. + + Upgrading from Rails 5.1 to Rails 5.2 ------------------------------------- @@ -77,6 +88,16 @@ Rails 5.2 adds bootsnap gem in the [newly generated app's Gemfile](https://githu The `app:update` task 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 ------------------------------------- @@ -660,7 +681,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. @@ -1111,7 +1132,7 @@ 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 diff --git a/guides/source/working_with_javascript_in_rails.md b/guides/source/working_with_javascript_in_rails.md index b9ea4ad47a..a922bdc16b 100644 --- a/guides/source/working_with_javascript_in_rails.md +++ b/guides/source/working_with_javascript_in_rails.md @@ -382,10 +382,9 @@ 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 - -If you stop `ajax:before` or `ajax:beforeSend` by returning false from the -handler method, the Ajax request will never take place. The `ajax:before` event -can manipulate form data before serialization and the +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 @@ -393,6 +392,9 @@ 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 -------------------- |