aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--.travis.yml1
-rw-r--r--Gemfile1
-rw-r--r--actionpack/CHANGELOG.md16
-rw-r--r--actionpack/lib/abstract_controller/collector.rb18
-rw-r--r--actionpack/lib/action_controller/metal/mime_responds.rb12
-rw-r--r--actionpack/lib/action_dispatch/http/mime_negotiation.rb10
-rw-r--r--actionpack/lib/action_dispatch/http/mime_type.rb4
-rw-r--r--actionview/CHANGELOG.md7
-rw-r--r--actionview/lib/action_view/helpers/number_helper.rb8
-rw-r--r--actionview/lib/action_view/helpers/text_helper.rb2
-rw-r--r--actionview/lib/action_view/helpers/translation_helper.rb10
-rw-r--r--actionview/test/template/erb_util_test.rb46
-rw-r--r--actionview/test/template/text_helper_test.rb5
-rw-r--r--actionview/test/template/translation_helper_test.rb6
-rw-r--r--activerecord/CHANGELOG.md20
-rw-r--r--activerecord/activerecord.gemspec2
-rw-r--r--activerecord/lib/active_record/associations/builder/belongs_to.rb8
-rw-r--r--activerecord/lib/active_record/attribute_methods.rb19
-rw-r--r--activerecord/lib/active_record/connection_adapters/postgresql/schema_statements.rb2
-rw-r--r--activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb27
-rw-r--r--activerecord/test/cases/attribute_methods_test.rb35
-rw-r--r--activerecord/test/cases/timestamp_test.rb58
-rw-r--r--activesupport/CHANGELOG.md9
-rw-r--r--activesupport/lib/active_support/core_ext/string/output_safety.rb62
-rw-r--r--activesupport/lib/active_support/i18n_railtie.rb5
-rw-r--r--activesupport/test/number_helper_test.rb1
-rw-r--r--guides/source/4_1_release_notes.md206
-rw-r--r--guides/source/_welcome.html.erb4
-rw-r--r--guides/source/plugins.md30
-rw-r--r--guides/source/rails_on_rack.md2
-rw-r--r--guides/source/upgrading_ruby_on_rails.md70
-rw-r--r--railties/CHANGELOG.md28
-rw-r--r--railties/lib/rails/application.rb39
-rw-r--r--railties/lib/rails/generators/app_base.rb26
-rw-r--r--railties/lib/rails/generators/rails/app/app_generator.rb1
-rw-r--r--railties/test/application/configuration_test.rb35
-rw-r--r--railties/test/application/initializers/i18n_test.rb11
-rw-r--r--railties/test/generators/app_generator_test.rb28
-rw-r--r--railties/test/generators/shared_generator_tests.rb16
39 files changed, 732 insertions, 158 deletions
diff --git a/.travis.yml b/.travis.yml
index 1e354a8fb0..c8afb46187 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -5,6 +5,7 @@ before_install:
rvm:
- 1.9.3
- 2.0.0
+ - 2.1.0-preview2
- rbx-2.2.1
- jruby-19mode
env:
diff --git a/Gemfile b/Gemfile
index e1278e6fdf..746dbac325 100644
--- a/Gemfile
+++ b/Gemfile
@@ -12,7 +12,6 @@ gem 'bcrypt-ruby', '~> 3.1.2'
gem 'jquery-rails', '~> 2.2.0'
gem 'turbolinks'
gem 'coffee-rails', '~> 4.0.0'
-gem 'arel', github: 'rails/arel', branch: 'master'
# This needs to be with require false to avoid
# it being automatically loaded by sprockets
diff --git a/actionpack/CHANGELOG.md b/actionpack/CHANGELOG.md
index 112a787d3b..38d20afc83 100644
--- a/actionpack/CHANGELOG.md
+++ b/actionpack/CHANGELOG.md
@@ -1,10 +1,22 @@
+* Remove deprecated `ActionController::RecordIdentifier`, use
+ `ActionView::RecordIdentifier` instead.
+
+ *kennyj*
+
+* Fix regression when using `ActionView::Helpers::TranslationHelper#translate` with
+ `options[:raise]`.
+
+ This regression was introduced at ec16ba75a5493b9da972eea08bae630eba35b62f.
+
+ *Shota Fukumori (sora_h)*
+
* Introducing Variants
We often want to render different html/json/xml templates for phones,
tablets, and desktop browsers. Variants make it easy.
- The request variant is a specialization of the request format, like :tablet,
- :phone, or :desktop.
+ The request variant is a specialization of the request format, like `:tablet`,
+ `:phone`, or `:desktop`.
You can set the variant in a before_action:
diff --git a/actionpack/lib/abstract_controller/collector.rb b/actionpack/lib/abstract_controller/collector.rb
index f7a309c26c..ddd56b354a 100644
--- a/actionpack/lib/abstract_controller/collector.rb
+++ b/actionpack/lib/abstract_controller/collector.rb
@@ -23,15 +23,17 @@ module AbstractController
protected
def method_missing(symbol, &block)
- mime_const = symbol.upcase
-
- raise NoMethodError, "To respond to a custom format, register it as a MIME type first:" +
- "http://guides.rubyonrails.org/action_controller_overview.html#restful-downloads." +
- "If you meant to respond to a variant like :tablet or :phone, not a custom format," +
- "be sure to nest your variant response within a format response: format.html" +
- "{ |html| html.tablet { ..." unless Mime.const_defined?(mime_const)
+ const_name = symbol.upcase
+
+ unless Mime.const_defined?(const_name)
+ raise NoMethodError, "To respond to a custom format, register it as a MIME type first: " \
+ "http://guides.rubyonrails.org/action_controller_overview.html#restful-downloads. " \
+ "If you meant to respond to a variant like :tablet or :phone, not a custom format, " \
+ "be sure to nest your variant response within a format response: " \
+ "format.html { |html| html.tablet { ... } }"
+ end
- mime_constant = Mime.const_get(mime_const)
+ mime_constant = Mime.const_get(const_name)
if Mime::SET.include?(mime_constant)
AbstractController::Collector.generate_method_for_mime(mime_constant)
diff --git a/actionpack/lib/action_controller/metal/mime_responds.rb b/actionpack/lib/action_controller/metal/mime_responds.rb
index 9a88a01233..54d3be68f0 100644
--- a/actionpack/lib/action_controller/metal/mime_responds.rb
+++ b/actionpack/lib/action_controller/metal/mime_responds.rb
@@ -191,7 +191,7 @@ module ActionController #:nodoc:
#
# You can set the variant in a +before_action+:
#
- # request.variant = :tablet if request.user_agent =~ /iPad/
+ # request.variant = :tablet if request.user_agent =~ /iPad/
#
# Respond to variants in the action just like you respond to formats:
#
@@ -287,7 +287,7 @@ module ActionController #:nodoc:
# * for other requests - i.e. data formats such as xml, json, csv etc, if
# the resource passed to +respond_with+ responds to <code>to_<format></code>,
# the method attempts to render the resource in the requested format
- # directly, e.g. for an xml request, the response is equivalent to calling
+ # directly, e.g. for an xml request, the response is equivalent to calling
# <code>render xml: resource</code>.
#
# === Nested resources
@@ -348,8 +348,10 @@ module ActionController #:nodoc:
# 2. <tt>:action</tt> - overwrites the default render action used after an
# unsuccessful html +post+ request.
def respond_with(*resources, &block)
- raise "In order to use respond_with, first you need to declare the formats your " \
- "controller responds to in the class level" if self.class.mimes_for_respond_to.empty?
+ if self.class.mimes_for_respond_to.empty?
+ raise "In order to use respond_with, first you need to declare the " \
+ "formats your controller responds to in the class level."
+ end
if collector = retrieve_collector_from_mimes(&block)
options = resources.size == 1 ? {} : resources.extract_options!
@@ -457,7 +459,7 @@ module ActionController #:nodoc:
@format = request.negotiate_mime(@responses.keys)
end
- class VariantFilter
+ class VariantFilter #:nodoc:
def initialize(variant)
@variant = variant
end
diff --git a/actionpack/lib/action_dispatch/http/mime_negotiation.rb b/actionpack/lib/action_dispatch/http/mime_negotiation.rb
index 41e6727315..346598b6de 100644
--- a/actionpack/lib/action_dispatch/http/mime_negotiation.rb
+++ b/actionpack/lib/action_dispatch/http/mime_negotiation.rb
@@ -66,15 +66,15 @@ module ActionDispatch
end
end
- # Sets the \variant for template
+ # Sets the \variant for template.
def variant=(variant)
if variant.is_a? Symbol
@variant = variant
else
- raise ArgumentError, "request.variant must be set to a Symbol, not a #{variant.class}. For security reasons," +
- "never directly set the variant to a user-provided value, like params[:variant].to_sym." +
- "Check user-provided value against a whitelist first, then set the variant:"+
- "request.variant = :tablet if params[:some_param] == 'tablet'"
+ raise ArgumentError, "request.variant must be set to a Symbol, not a #{variant.class}. " \
+ "For security reasons, never directly set the variant to a user-provided value, " \
+ "like params[:variant].to_sym. Check user-provided value against a whitelist first, " \
+ "then set the variant: request.variant = :tablet if params[:variant] == 'tablet'"
end
end
diff --git a/actionpack/lib/action_dispatch/http/mime_type.rb b/actionpack/lib/action_dispatch/http/mime_type.rb
index a398919ca7..2a8ff0a5d2 100644
--- a/actionpack/lib/action_dispatch/http/mime_type.rb
+++ b/actionpack/lib/action_dispatch/http/mime_type.rb
@@ -299,9 +299,7 @@ module Mime
true
end
- def ref
- nil
- end
+ def ref; end
def respond_to_missing?(method, include_private = false)
method.to_s.ends_with? '?'
diff --git a/actionview/CHANGELOG.md b/actionview/CHANGELOG.md
index d53b321f97..9e58c193b1 100644
--- a/actionview/CHANGELOG.md
+++ b/actionview/CHANGELOG.md
@@ -1,3 +1,10 @@
+* Fixed a long-standing bug in `json_escape` that causes quotation marks to be stripped.
+ This method also escapes the \u2028 and \u2029 unicode newline characters which are
+ treated as \n in JavaScript. This matches the behaviour of the AS::JSON encoder. (The
+ original change in the encoder was introduced in #10534.)
+
+ *Godfrey Chan*
+
* `ActionView::MissingTemplate` includes underscore when raised for a partial.
Fixes #13002.
diff --git a/actionview/lib/action_view/helpers/number_helper.rb b/actionview/lib/action_view/helpers/number_helper.rb
index 13387078a4..ad825cd1f1 100644
--- a/actionview/lib/action_view/helpers/number_helper.rb
+++ b/actionview/lib/action_view/helpers/number_helper.rb
@@ -100,10 +100,10 @@ module ActionView
#
# number_to_currency(-1234567890.50, negative_format: "(%u%n)")
# # => ($1,234,567,890.50)
- # number_to_currency(1234567890.50, unit: "&pound;", separator: ",", delimiter: "")
- # # => &pound;1234567890,50
- # number_to_currency(1234567890.50, unit: "&pound;", separator: ",", delimiter: "", format: "%n %u")
- # # => 1234567890,50 &pound;
+ # number_to_currency(1234567890.50, unit: "R$", separator: ",", delimiter: "")
+ # # => R$1234567890,50
+ # number_to_currency(1234567890.50, unit: "R$", separator: ",", delimiter: "", format: "%n %u")
+ # # => 1234567890,50 R$
def number_to_currency(number, options = {})
delegate_number_helper_method(:number_to_currency, number, options)
end
diff --git a/actionview/lib/action_view/helpers/text_helper.rb b/actionview/lib/action_view/helpers/text_helper.rb
index e75c6cabc9..3478eadbf2 100644
--- a/actionview/lib/action_view/helpers/text_helper.rb
+++ b/actionview/lib/action_view/helpers/text_helper.rb
@@ -31,6 +31,8 @@ module ActionView
include SanitizeHelper
include TagHelper
+ include OutputSafetyHelper
+
# The preferred method of outputting text in your views is to use the
# <%= "text" %> eRuby syntax. The regular _puts_ and _print_ methods
# do not operate as expected in an eRuby code block. If you absolutely must
diff --git a/actionview/lib/action_view/helpers/translation_helper.rb b/actionview/lib/action_view/helpers/translation_helper.rb
index a1a2bebb6e..3ae1df04fe 100644
--- a/actionview/lib/action_view/helpers/translation_helper.rb
+++ b/actionview/lib/action_view/helpers/translation_helper.rb
@@ -38,7 +38,13 @@ module ActionView
# If the user has specified rescue_format then pass it all through, otherwise use
# raise and do the work ourselves
- options[:raise] = true unless options.key?(:raise) || options.key?(:rescue_format)
+ if options.key?(:raise) || options.key?(:rescue_format)
+ raise_error = options[:raise] || options[:rescue_format]
+ else
+ raise_error = false
+ options[:raise] = true
+ end
+
if html_safe_translation_key?(key)
html_safe_options = options.dup
options.except(*I18n::RESERVED_KEYS).each do |name, value|
@@ -53,6 +59,8 @@ module ActionView
I18n.translate(scope_key_by_partial(key), options)
end
rescue I18n::MissingTranslationData => e
+ raise e if raise_error
+
keys = I18n.normalize_keys(e.locale, e.key, e.options[:scope])
content_tag('span', keys.last.to_s.titleize, :class => 'translation_missing', :title => "translation missing: #{keys.join('.')}")
end
diff --git a/actionview/test/template/erb_util_test.rb b/actionview/test/template/erb_util_test.rb
index 9a7c617eb3..9bacbba908 100644
--- a/actionview/test/template/erb_util_test.rb
+++ b/actionview/test/template/erb_util_test.rb
@@ -1,4 +1,5 @@
require 'abstract_unit'
+require 'active_support/json'
class ErbUtilTest < ActiveSupport::TestCase
include ERB::Util
@@ -15,6 +16,51 @@ class ErbUtilTest < ActiveSupport::TestCase
end
end
+ HTML_ESCAPE_TEST_CASES = [
+ ['<br>', '&lt;br&gt;'],
+ ['a & b', 'a &amp; b'],
+ ['"quoted" string', '&quot;quoted&quot; string'],
+ ["'quoted' string", '&#39;quoted&#39; string'],
+ [
+ '<script type="application/javascript">alert("You are \'pwned\'!")</script>',
+ '&lt;script type=&quot;application/javascript&quot;&gt;alert(&quot;You are &#39;pwned&#39;!&quot;)&lt;/script&gt;'
+ ]
+ ]
+
+ JSON_ESCAPE_TEST_CASES = [
+ ['1', '1'],
+ ['null', 'null'],
+ ['"&"', '"\u0026"'],
+ ['"</script>"', '"\u003c/script\u003e"'],
+ ['["</script>"]', '["\u003c/script\u003e"]'],
+ ['{"name":"</script>"}', '{"name":"\u003c/script\u003e"}'],
+ [%({"name":"d\u2028h\u2029h"}), '{"name":"d\u2028h\u2029h"}']
+ ]
+
+ def test_html_escape
+ HTML_ESCAPE_TEST_CASES.each do |(raw, expected)|
+ assert_equal expected, html_escape(raw)
+ end
+ end
+
+ def test_json_escape
+ JSON_ESCAPE_TEST_CASES.each do |(raw, expected)|
+ assert_equal expected, json_escape(raw)
+ end
+ end
+
+ def test_json_escape_does_not_alter_json_string_meaning
+ JSON_ESCAPE_TEST_CASES.each do |(raw, _)|
+ assert_equal ActiveSupport::JSON.decode(raw), ActiveSupport::JSON.decode(json_escape(raw))
+ end
+ end
+
+ def test_json_escape_is_idempotent
+ JSON_ESCAPE_TEST_CASES.each do |(raw, _)|
+ assert_equal json_escape(raw), json_escape(json_escape(raw))
+ end
+ end
+
def test_json_escape_returns_unsafe_strings_when_passed_unsafe_strings
value = json_escape("asdf")
assert !value.html_safe?
diff --git a/actionview/test/template/text_helper_test.rb b/actionview/test/template/text_helper_test.rb
index dd1a92acfb..5b3bccf951 100644
--- a/actionview/test/template/text_helper_test.rb
+++ b/actionview/test/template/text_helper_test.rb
@@ -21,6 +21,11 @@ class TextHelperTest < ActionView::TestCase
assert simple_format("<b> test with html tags </b>").html_safe?
end
+ def test_simple_format_included_in_isolation
+ helper_klass = Class.new { include ActionView::Helpers::TextHelper }
+ assert helper_klass.new.simple_format("<b> test with html tags </b>").html_safe?
+ end
+
def test_simple_format
assert_equal "<p></p>", simple_format(nil)
diff --git a/actionview/test/template/translation_helper_test.rb b/actionview/test/template/translation_helper_test.rb
index 0dfe47f5f4..269714fad0 100644
--- a/actionview/test/template/translation_helper_test.rb
+++ b/actionview/test/template/translation_helper_test.rb
@@ -53,6 +53,12 @@ class TranslationHelperTest < ActiveSupport::TestCase
assert_equal false, translate(:"translations.missing", :rescue_format => nil).html_safe?
end
+ def test_raises_missing_translation_message_with_raise_option
+ assert_raise(I18n::MissingTranslationData) do
+ translate(:"translations.missing", :raise => true)
+ end
+ end
+
def test_i18n_translate_defaults_to_nil_rescue_format
expected = 'translation missing: en.translations.missing'
assert_equal expected, I18n.translate(:"translations.missing")
diff --git a/activerecord/CHANGELOG.md b/activerecord/CHANGELOG.md
index f0ae0050a7..ed076c04bb 100644
--- a/activerecord/CHANGELOG.md
+++ b/activerecord/CHANGELOG.md
@@ -1,3 +1,17 @@
+* Polymorphic belongs_to associations with the `touch: true` option set update the timestamps of
+ the old and new owner correctly when moved between owners of different types.
+
+ Example:
+
+ class Rating < ActiveRecord::Base
+ belongs_to :rateable, polymorphic: true, touch: true
+ end
+
+ rating = Rating.create rateable: Song.find(1)
+ rating.update_attributes rateable: Book.find(2) # => timestamps of Song(1) and Book(2) are updated
+
+ *Severin Schoepke*
+
* Improve formatting of migration exception messages: make them easier to read
with line breaks before/after, and improve the error for pending migrations.
@@ -772,11 +786,11 @@
*Neeraj Singh*
-* Removed deprecated method `scoped`
+* Removed deprecated method `scoped`.
*Neeraj Singh*
-* Removed deprecated method `default_scopes?`
+* Removed deprecated method `default_scopes?`.
*Neeraj Singh*
@@ -811,7 +825,7 @@
*Jon Leighton*
-* Remove `activerecord-deprecated_finders` as a dependency
+* Remove `activerecord-deprecated_finders` as a dependency.
*Łukasz Strzałkowski*
diff --git a/activerecord/activerecord.gemspec b/activerecord/activerecord.gemspec
index e1c5b05c36..d397c9e016 100644
--- a/activerecord/activerecord.gemspec
+++ b/activerecord/activerecord.gemspec
@@ -24,5 +24,5 @@ Gem::Specification.new do |s|
s.add_dependency 'activesupport', version
s.add_dependency 'activemodel', version
- s.add_dependency 'arel', '~> 5.0.0'
+ s.add_dependency 'arel', '~> 5.0.0'
end
diff --git a/activerecord/lib/active_record/associations/builder/belongs_to.rb b/activerecord/lib/active_record/associations/builder/belongs_to.rb
index ac387d377d..e8e36e7cd0 100644
--- a/activerecord/lib/active_record/associations/builder/belongs_to.rb
+++ b/activerecord/lib/active_record/associations/builder/belongs_to.rb
@@ -91,7 +91,13 @@ module ActiveRecord::Associations::Builder
old_foreign_id = o.changed_attributes[foreign_key]
if old_foreign_id
- klass = o.association(name).klass
+ association = o.association(name)
+ reflection = association.reflection
+ if reflection.polymorphic?
+ klass = o.public_send("#{reflection.foreign_type}_was").constantize
+ else
+ klass = association.klass
+ end
old_record = klass.find_by(klass.primary_key => old_foreign_id)
if old_record
diff --git a/activerecord/lib/active_record/attribute_methods.rb b/activerecord/lib/active_record/attribute_methods.rb
index 3924eec872..217fc52dd5 100644
--- a/activerecord/lib/active_record/attribute_methods.rb
+++ b/activerecord/lib/active_record/attribute_methods.rb
@@ -128,6 +128,16 @@ module ActiveRecord
end
end
+ def find_generated_attribute_method(method_name) # :nodoc:
+ klass = self
+ until klass == Base
+ gen_methods = klass.generated_attribute_methods
+ return gen_methods.instance_method(method_name) if method_defined_within?(method_name, gen_methods, Object)
+ klass = klass.superclass
+ end
+ nil
+ end
+
# Returns +true+ if +attribute+ is an attribute method and table exists,
# +false+ otherwise.
#
@@ -163,7 +173,14 @@ module ActiveRecord
def method_missing(method, *args, &block) # :nodoc:
self.class.define_attribute_methods
if respond_to_without_attributes?(method)
- send(method, *args, &block)
+ # make sure to invoke the correct attribute method, as we might have gotten here via a `super`
+ # call in a overwritten attribute method
+ if attribute_method = self.class.find_generated_attribute_method(method)
+ # this is probably horribly slow, but should only happen at most once for a given AR class
+ attribute_method.bind(self).call(*args, &block)
+ else
+ send(method, *args, &block)
+ end
else
super
end
diff --git a/activerecord/lib/active_record/connection_adapters/postgresql/schema_statements.rb b/activerecord/lib/active_record/connection_adapters/postgresql/schema_statements.rb
index 5dc70a5ad1..571257f6dd 100644
--- a/activerecord/lib/active_record/connection_adapters/postgresql/schema_statements.rb
+++ b/activerecord/lib/active_record/connection_adapters/postgresql/schema_statements.rb
@@ -172,7 +172,7 @@ module ActiveRecord
def columns(table_name)
# Limit, precision, and scale are all handled by the superclass.
column_definitions(table_name).map do |column_name, type, default, notnull, oid, fmod|
- oid = OID::TYPE_MAP.fetch(oid.to_i, fmod.to_i) {
+ oid = type_map.fetch(oid.to_i, fmod.to_i) {
OID::Identity.new
}
PostgreSQLColumn.new(column_name, default, oid, type, notnull == 'f')
diff --git a/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb b/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb
index adeb57d913..1ec1490263 100644
--- a/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb
+++ b/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb
@@ -565,7 +565,8 @@ module ActiveRecord
raise "Your version of PostgreSQL (#{postgresql_version}) is too old, please upgrade!"
end
- initialize_type_map
+ @type_map = OID::TypeMap.new
+ initialize_type_map(type_map)
@local_tz = execute('SHOW TIME ZONE', 'SCHEMA').first["TimeZone"]
@use_insert_returning = @config.key?(:insert_returning) ? self.class.type_cast_config_to_boolean(@config[:insert_returning]) : true
end
@@ -738,39 +739,43 @@ module ActiveRecord
private
+ def type_map
+ @type_map
+ end
+
def reload_type_map
- OID::TYPE_MAP.clear
- initialize_type_map
+ type_map.clear
+ initialize_type_map(type_map)
end
- def initialize_type_map
+ def initialize_type_map(type_map)
result = execute('SELECT oid, typname, typelem, typdelim, typinput FROM pg_type', 'SCHEMA')
leaves, nodes = result.partition { |row| row['typelem'] == '0' }
# populate the leaf nodes
leaves.find_all { |row| OID.registered_type? row['typname'] }.each do |row|
- OID::TYPE_MAP[row['oid'].to_i] = OID::NAMES[row['typname']]
+ type_map[row['oid'].to_i] = OID::NAMES[row['typname']]
end
arrays, nodes = nodes.partition { |row| row['typinput'] == 'array_in' }
# populate composite types
- nodes.find_all { |row| OID::TYPE_MAP.key? row['typelem'].to_i }.each do |row|
+ nodes.find_all { |row| type_map.key? row['typelem'].to_i }.each do |row|
if OID.registered_type? row['typname']
# this composite type is explicitly registered
vector = OID::NAMES[row['typname']]
else
# use the default for composite types
- vector = OID::Vector.new row['typdelim'], OID::TYPE_MAP[row['typelem'].to_i]
+ vector = OID::Vector.new row['typdelim'], type_map[row['typelem'].to_i]
end
- OID::TYPE_MAP[row['oid'].to_i] = vector
+ type_map[row['oid'].to_i] = vector
end
# populate array types
- arrays.find_all { |row| OID::TYPE_MAP.key? row['typelem'].to_i }.each do |row|
- array = OID::Array.new OID::TYPE_MAP[row['typelem'].to_i]
- OID::TYPE_MAP[row['oid'].to_i] = array
+ arrays.find_all { |row| type_map.key? row['typelem'].to_i }.each do |row|
+ array = OID::Array.new type_map[row['typelem'].to_i]
+ type_map[row['oid'].to_i] = array
end
end
diff --git a/activerecord/test/cases/attribute_methods_test.rb b/activerecord/test/cases/attribute_methods_test.rb
index 9c66ed354e..6c581a432f 100644
--- a/activerecord/test/cases/attribute_methods_test.rb
+++ b/activerecord/test/cases/attribute_methods_test.rb
@@ -767,8 +767,7 @@ class AttributeMethodsTest < ActiveRecord::TestCase
# that by defining a 'foo' method in the generated methods module for B.
# (That module will be inserted between the two, e.g. [B, <GeneratedAttributes>, A].)
def test_inherited_custom_accessors
- klass = Class.new(ActiveRecord::Base) do
- self.table_name = "topics"
+ klass = new_topic_like_ar_class do
self.abstract_class = true
def title; "omg"; end
def title=(val); self.author_name = val; end
@@ -783,8 +782,40 @@ class AttributeMethodsTest < ActiveRecord::TestCase
assert_equal "lol", topic.author_name
end
+ def test_on_the_fly_super_invokable_generated_attribute_methods_via_method_missing
+ klass = new_topic_like_ar_class do
+ def title
+ super + '!'
+ end
+ end
+
+ real_topic = topics(:first)
+ assert_equal real_topic.title + '!', klass.find(real_topic.id).title
+ end
+
+ def test_on_the_fly_super_invokable_generated_predicate_attribute_methods_via_method_missing
+ klass = new_topic_like_ar_class do
+ def title?
+ !super
+ end
+ end
+
+ real_topic = topics(:first)
+ assert_equal !real_topic.title?, klass.find(real_topic.id).title?
+ end
+
private
+ def new_topic_like_ar_class(&block)
+ klass = Class.new(ActiveRecord::Base) do
+ self.table_name = 'topics'
+ class_eval(&block)
+ end
+
+ assert_empty klass.generated_attribute_methods.instance_methods(false)
+ klass
+ end
+
def cached_columns
Topic.columns.map(&:name) - Topic.serialized_attributes.keys
end
diff --git a/activerecord/test/cases/timestamp_test.rb b/activerecord/test/cases/timestamp_test.rb
index 2953b2b2be..717e0e1866 100644
--- a/activerecord/test/cases/timestamp_test.rb
+++ b/activerecord/test/cases/timestamp_test.rb
@@ -272,36 +272,62 @@ class TimestampTest < ActiveRecord::TestCase
assert_not_equal time, old_pet.updated_at
end
- def test_changing_parent_of_a_record_touches_both_new_and_old_polymorphic_parent_record
- klass = Class.new(ActiveRecord::Base) do
- def self.name; 'Toy'; end
+ def test_changing_parent_of_a_record_touches_both_new_and_old_polymorphic_parent_record_changes_within_same_class
+ car_class = Class.new(ActiveRecord::Base) do
+ def self.name; 'Car'; end
end
- wheel_klass = Class.new(ActiveRecord::Base) do
+ wheel_class = Class.new(ActiveRecord::Base) do
def self.name; 'Wheel'; end
belongs_to :wheelable, :polymorphic => true, :touch => true
end
- toy1 = klass.find(1)
- toy2 = klass.find(2)
+ car1 = car_class.find(1)
+ car2 = car_class.find(2)
- wheel = wheel_klass.new
- wheel.wheelable = toy1
- wheel.save!
+ wheel = wheel_class.create!(wheelable: car1)
time = 3.days.ago.at_beginning_of_hour
- toy1.update_columns(updated_at: time)
- toy2.update_columns(updated_at: time)
+ car1.update_columns(updated_at: time)
+ car2.update_columns(updated_at: time)
- wheel.wheelable = toy2
+ wheel.wheelable = car2
wheel.save!
- toy1.reload
- toy2.reload
+ assert_not_equal time, car1.reload.updated_at
+ assert_not_equal time, car2.reload.updated_at
+ end
+
+ def test_changing_parent_of_a_record_touches_both_new_and_old_polymorphic_parent_record_changes_with_other_class
+ car_class = Class.new(ActiveRecord::Base) do
+ def self.name; 'Car'; end
+ end
+
+ toy_class = Class.new(ActiveRecord::Base) do
+ def self.name; 'Toy'; end
+ end
+
+ wheel_class = Class.new(ActiveRecord::Base) do
+ def self.name; 'Wheel'; end
+ belongs_to :wheelable, :polymorphic => true, :touch => true
+ end
+
+ car = car_class.find(1)
+ toy = toy_class.find(3)
+
+ wheel = wheel_class.create!(wheelable: car)
+
+ time = 3.days.ago.at_beginning_of_hour
+
+ car.update_columns(updated_at: time)
+ toy.update_columns(updated_at: time)
+
+ wheel.wheelable = toy
+ wheel.save!
- assert_not_equal time, toy1.updated_at
- assert_not_equal time, toy2.updated_at
+ assert_not_equal time, car.reload.updated_at
+ assert_not_equal time, toy.reload.updated_at
end
def test_clearing_association_touches_the_old_record
diff --git a/activesupport/CHANGELOG.md b/activesupport/CHANGELOG.md
index b63c44fbcb..ea4aaff610 100644
--- a/activesupport/CHANGELOG.md
+++ b/activesupport/CHANGELOG.md
@@ -1,3 +1,8 @@
+* Ensure `config.i18n.enforce_available_locales` is set before any other
+ configuration option.
+
+ *Yves Senn*
+
* Added `Date#all_week/month/quarter/year` for generating date ranges.
*Dmitriy Meremyanin*
@@ -366,6 +371,10 @@
*Yves Senn*
+* Removed deprecated `ActiveSupport::JSON::Variable` with no replacement.
+
+ *Toshinori Kajihara*
+
* Raise an error when multiple `included` blocks are defined for a Concern.
The old behavior would silently discard previously defined blocks, running
only the last one.
diff --git a/activesupport/lib/active_support/core_ext/string/output_safety.rb b/activesupport/lib/active_support/core_ext/string/output_safety.rb
index c21650bb83..1b2098fc84 100644
--- a/activesupport/lib/active_support/core_ext/string/output_safety.rb
+++ b/activesupport/lib/active_support/core_ext/string/output_safety.rb
@@ -4,9 +4,10 @@ require 'active_support/core_ext/kernel/singleton_class'
class ERB
module Util
HTML_ESCAPE = { '&' => '&amp;', '>' => '&gt;', '<' => '&lt;', '"' => '&quot;', "'" => '&#39;' }
- JSON_ESCAPE = { '&' => '\u0026', '>' => '\u003E', '<' => '\u003C' }
+ JSON_ESCAPE = { '&' => '\u0026', '>' => '\u003e', '<' => '\u003c', "\u2028" => '\u2028', "\u2029" => '\u2029' }
+ HTML_ESCAPE_REGEXP = /[&"'><]/
HTML_ESCAPE_ONCE_REGEXP = /["><']|&(?!([a-zA-Z]+|(#\d+));)/
- JSON_ESCAPE_REGEXP = /[&"><]/
+ JSON_ESCAPE_REGEXP = /[\u2028\u2029&><]/u
# A utility method for escaping HTML tag characters.
# This method is also aliased as <tt>h</tt>.
@@ -21,7 +22,7 @@ class ERB
if s.html_safe?
s
else
- s.gsub(/[&"'><]/, HTML_ESCAPE).html_safe
+ s.gsub(HTML_ESCAPE_REGEXP, HTML_ESCAPE).html_safe
end
end
@@ -48,17 +49,56 @@ class ERB
module_function :html_escape_once
- # A utility method for escaping HTML entities in JSON strings
- # using \uXXXX JavaScript escape sequences for string literals:
+ # A utility method for escaping HTML entities in JSON strings. Specifically, the
+ # &, > and < characters are replaced with their equivalent unicode escaped form -
+ # \u0026, \u003e, and \u003c. The Unicode sequences \u2028 and \u2029 are also
+ # escaped as they are treated as newline characters in some JavaScript engines.
+ # These sequences have identical meaning as the original characters inside the
+ # context of a JSON string, so assuming the input is a valid and well-formed
+ # JSON value, the output will have equivalent meaning when parsed:
#
- # json_escape('is a > 0 & a < 10?')
- # # => is a \u003E 0 \u0026 a \u003C 10?
+ # json = JSON.generate({ name: "</script><script>alert('PWNED!!!')</script>"})
+ # # => "{\"name\":\"</script><script>alert('PWNED!!!')</script>\"}"
#
- # Note that after this operation is performed the output is not
- # valid JSON. In particular double quotes are removed:
+ # json_escape(json)
+ # # => "{\"name\":\"\\u003C/script\\u003E\\u003Cscript\\u003Ealert('PWNED!!!')\\u003C/script\\u003E\"}"
+ #
+ # JSON.parse(json) == JSON.parse(json_escape(json))
+ # # => true
+ #
+ # The intended use case for this method is to escape JSON strings before including
+ # them inside a script tag to avoid XSS vulnerability:
+ #
+ # <script>
+ # var currentUser = <%= json_escape current_user.to_json %>;
+ # </script>
+ #
+ # WARNING: this helper only works with valid JSON. Using this on non-JSON values
+ # will open up serious XSS vulnerabilities. For example, if you replace the
+ # +current_user.to_json+ in the example above with user input instead, the browser
+ # will happily eval() that string as JavaScript.
+ #
+ # The escaping performed in this method is identical to those performed in the
+ # Active Support JSON encoder when +ActiveSupport.escape_html_entities_in_json+ is
+ # set to true. Because this transformation is idempotent, this helper can be
+ # applied even if +ActiveSupport.escape_html_entities_in_json+ is already true.
+ #
+ # Therefore, when you are unsure if +ActiveSupport.escape_html_entities_in_json+
+ # is enabled, or if you are unsure where your JSON string originated from, it
+ # is recommended that you always apply this helper (other libraries, such as the
+ # JSON gem, do not provide this kind of protection by default; also some gems
+ # might override +to_json+ to bypass Active Support's encoder).
+ #
+ # The output of this helper method is marked as HTML safe so that you can directly
+ # include it inside a <tt><script></tt> tag as shown above.
+ #
+ # However, it is NOT safe to use the output of this inside an HTML attribute,
+ # because quotation marks are not escaped. Doing so might break your page's layout.
+ # If you intend to use this inside an HTML attribute, you should use the
+ # +html_escape+ helper (or its +h+ alias) instead:
+ #
+ # <div data-user-info="<%= h current_user.to_json %>">...</div>
#
- # json_escape('{"name":"john","created_at":"2010-04-28T01:39:31Z","id":1}')
- # # => {name:john,created_at:2010-04-28T01:39:31Z,id:1}
def json_escape(s)
result = s.to_s.gsub(JSON_ESCAPE_REGEXP, JSON_ESCAPE)
s.html_safe? ? result.html_safe : result
diff --git a/activesupport/lib/active_support/i18n_railtie.rb b/activesupport/lib/active_support/i18n_railtie.rb
index 890dd9380b..dcdea70443 100644
--- a/activesupport/lib/active_support/i18n_railtie.rb
+++ b/activesupport/lib/active_support/i18n_railtie.rb
@@ -31,6 +31,11 @@ module I18n
fallbacks = app.config.i18n.delete(:fallbacks)
+ if app.config.i18n.has_key?(:enforce_available_locales)
+ # this option needs to be set before `default_locale=` to work properly.
+ I18n.enforce_available_locales = app.config.i18n.delete(:enforce_available_locales)
+ end
+
app.config.i18n.each do |setting, value|
case setting
when :railties_load_path
diff --git a/activesupport/test/number_helper_test.rb b/activesupport/test/number_helper_test.rb
index 61ca556fcd..111db59b2b 100644
--- a/activesupport/test/number_helper_test.rb
+++ b/activesupport/test/number_helper_test.rb
@@ -102,7 +102,6 @@ module ActiveSupport
assert_equal '12 345 678', number_helper.number_to_delimited(12345678, :delimiter => ' ')
assert_equal '12,345,678-05', number_helper.number_to_delimited(12345678.05, :separator => '-')
assert_equal '12.345.678,05', number_helper.number_to_delimited(12345678.05, :separator => ',', :delimiter => '.')
- assert_equal '12.345.678,05', number_helper.number_to_delimited(12345678.05, :delimiter => '.', :separator => ',')
end
end
diff --git a/guides/source/4_1_release_notes.md b/guides/source/4_1_release_notes.md
index 0e3e2037ff..5c50ed83ea 100644
--- a/guides/source/4_1_release_notes.md
+++ b/guides/source/4_1_release_notes.md
@@ -4,6 +4,7 @@ Ruby on Rails 4.1 Release Notes
Highlights in Rails 4.1:
* Variants
+* Spring
* Action View extracted from Action Pack
These release notes cover only the major changes. To know about various bug
@@ -28,38 +29,111 @@ guide.
Major Features
--------------
-* Variants
+### Variants
+
+We often want to render different html/json/xml templates for phones,
+tablets, and desktop browsers. Variants makes it easy.
+
+The request variant is a specialization of the request format, like `:tablet`,
+`:phone`, or `:desktop`.
+
+You can set the variant in a before_action:
+
+```ruby
+request.variant = :tablet if request.user_agent =~ /iPad/
+```
+
+Respond to variants in the action just like you respond to formats:
+
+```ruby
+respond_to do |format|
+ format.html do |html|
+ html.tablet # renders app/views/projects/show.html+tablet.erb
+ html.phone { extra_setup; render ... }
+ end
+end
+```
+
+Provide separate templates for each format and variant:
+
+```
+app/views/projects/show.html.erb
+app/views/projects/show.html+tablet.erb
+app/views/projects/show.html+phone.erb
+```
+
+### Spring
+
+New Rails 4.1 applications will ship with "springified" binstubs. This means
+that `bin/rails` and `bin/rake` will automatically take advantage preloaded
+spring environments.
+
+**running rake tasks:**
+
+```
+bin/rake routes
+```
+
+**running tests:**
+
+```
+bin/rake test
+bin/rake test test/models
+bin/rake test test/models/user_test.rb
+```
+
+**running a console:**
+
+```
+bin/rails console
+```
+
+**spring introspection:**
+
+```
+$ bundle exec spring status
+Spring is running:
- We often want to render different html/json/xml templates for phones,
- tablets, and desktop browsers. Variants make it easy.
+ 1182 spring server | my_app | started 29 mins ago
+ 3656 spring app | my_app | started 23 secs ago | test mode
+ 3746 spring app | my_app | started 10 secs ago | development mode
+```
- The request variant is a specialization of the request format, like :tablet,
- :phone, or :desktop.
+Have a look at the
+[Spring README](https://github.com/jonleighton/spring/blob/master/README.md) to
+see a all available features.
- You can set the variant in a before_action:
+### Active Record enums
- ```ruby
- request.variant = :tablet if request.user_agent =~ /iPad/
- ```
+Declare an enum attribute where the values map to integers in the database, but
+can be queried by name.
- Respond to variants in the action just like you respond to formats:
+```ruby
+class Conversation < ActiveRecord::Base
+ enum status: [ :active, :archived ]
+end
- ```ruby
- respond_to do |format|
- format.html do |html|
- html.tablet # renders app/views/projects/show.html+tablet.erb
- html.phone { extra_setup; render ... }
- end
- end
- ```
+conversation.archive!
+conversation.active? # => false
+conversation.status # => "archived"
- Provide separate templates for each format and variant:
+Conversation.archived # => Relation for all archived Conversations
+```
- ```
- app/views/projects/show.html.erb
- app/views/projects/show.html+tablet.erb
- app/views/projects/show.html+phone.erb
- ```
+See
+[active_record/enum.rb](https://github.com/rails/rails/blob/4-1-stable/activerecord/lib/active_record/enum.rb#L2-L42)
+for a detailed write up.
+
+### Application message verifier.
+
+Create a message verifier that can be used to generate and verify signed
+messages in the application.
+
+```ruby
+message = Rails.application.message_verifier('salt').generate('my sensible data')
+Rails.application.message_verifier('salt').verify(message)
+# => 'my sensible data'
+```
Documentation
-------------
@@ -81,20 +155,29 @@ for detailed changes.
* Removed deprecated `threadsafe!` from Rails Config.
* Removed deprecated `ActiveRecord::Generators::ActiveModel#update_attributes` in
- favor of `ActiveRecord::Generators::ActiveModel#update`
+ favor of `ActiveRecord::Generators::ActiveModel#update`.
-* Removed deprecated `config.whiny_nils` option
+* Removed deprecated `config.whiny_nils` option.
* Removed deprecated rake tasks for running tests: `rake test:uncommitted` and
`rake test:recent`.
### Notable changes
+* The [Spring application
+ preloader](https://github.com/jonleighton/spring) is now installed
+ by default for new applications. It uses the development group of
+ the Gemfile, so will not be installed in
+ production. ([Pull Request](https://github.com/rails/rails/pull/12958))
+
* `BACKTRACE` environment variable to show unfiltered backtraces for test
failures. ([Commit](https://github.com/rails/rails/commit/84eac5dab8b0fe9ee20b51250e52ad7bfea36553))
-* Exposed `MiddlewareStack#unshift` to environment configuration. ([Pull Request](https://github.com/rails/rails/pull/12479))
+* Exposed `MiddlewareStack#unshift` to environment
+ configuration. ([Pull Request](https://github.com/rails/rails/pull/12479))
+* Add `Application#message_verifier` method to return a message
+ verifier. ([Pull Request](https://github.com/rails/rails/pull/12995))
Action Mailer
-------------
@@ -137,6 +220,17 @@ for detailed changes.
### Removals
+* Removed `MultiJSON` dependency. As a result, `ActiveSupport::JSON.decode`
+ no longer accepts an options hash for `MultiJSON`. ([Pull Request](https://github.com/rails/rails/pull/10576) / [More Details](upgrading_ruby_on_rails.html#changes-in-json-handling))
+
+* Removed support for the `encode_json` hook used for encoding custom objects into
+ JSON. This feature has been extracted into the [activesupport-json_encoder](https://github.com/rails/activesupport-json_encoder)
+ gem.
+ ([Related Pull Request](https://github.com/rails/rails/pull/12183) /
+ [More Details](upgrading_ruby_on_rails.html#changes-in-json-handling))
+
+* Removed deprecated `ActiveSupport::JSON::Variable` with no replacement.
+
* Removed deprecated `String#encoding_aware?` core extensions (`core_ext/string/encoding`).
* Removed deprecated `Module#local_constant_names` in favor of `Module#local_constants`.
@@ -171,8 +265,32 @@ for detailed changes.
explicitly convert the value into an AS::Duration, i.e. `5.ago` => `5.seconds.ago`
([Pull Request](https://github.com/rails/rails/pull/12389))
+* Deprecated the require path `active_support/core_ext/object/to_json`. Require
+ `active_support/core_ext/object/json` instead. ([Pull Request](https://github.com/rails/rails/pull/12203))
+
+* Deprecated `ActiveSupport::JSON::Encoding::CircularReferenceError`. This feature
+ has been extracted into the [activesupport-json_encoder](https://github.com/rails/activesupport-json_encoder)
+ gem.
+ ([Pull Request](https://github.com/rails/rails/pull/12785) /
+ [More Details](upgrading_ruby_on_rails.html#changes-in-json-handling))
+
+* Deprecated `ActiveSupport.encode_big_decimal_as_string` option. This feature has
+ been extracetd into the [activesupport-json_encoder](https://github.com/rails/activesupport-json_encoder)
+ gem.
+ ([Pull Request](https://github.com/rails/rails/pull/13060) /
+ [More Details](upgrading_ruby_on_rails.html#changes-in-json-handling))
+
### Notable changes
+* `ActiveSupport`'s JSON encoder has been rewritten to take advantage of the
+ JSON gem rather than doing custom encoding in pure-Ruby.
+ ([Pull Request](https://github.com/rails/rails/pull/12183) /
+ [More Details](upgrading_ruby_on_rails.html#changes-in-json-handling))
+
+* Improved compatibility with the JSON gem.
+ ([Pull Request](https://github.com/rails/rails/pull/12862) /
+ [More Details](upgrading_ruby_on_rails.html#changes-in-json-handling))
+
* Added `ActiveSupport::Testing::TimeHelpers#travel` and `#travel_to`. These
methods change current time to the given time or time difference by stubbing
`Time.now` and
@@ -207,6 +325,9 @@ for detailed changes.
* Removed deprecated `page_cache_extension` config.
+* Removed deprecated `ActionController::RecordIdentifier`, use
+ `ActionView::RecordIdentifier` instead.
+
* Removed deprecated constants from Action Controller:
ActionController::AbstractRequest => ActionDispatch::Request
@@ -219,8 +340,8 @@ for detailed changes.
### Notable changes
-* Take a hash with options inside array in
- `#url_for`. ([Pull Request](https://github.com/rails/rails/pull/9599))
+* `#url_for` takes a hash with options inside an
+ array. ([Pull Request](https://github.com/rails/rails/pull/9599))
* Added `session#fetch` method fetch behaves similarly to
[Hash#fetch](http://www.ruby-doc.org/core-1.9.3/Hash.html#method-i-fetch),
@@ -266,29 +387,18 @@ for detailed changes.
* Removed deprecated `SchemaStatements#distinct`.
-* Moved deprecated `ActiveRecord::TestCase` into the rails test
+* Moved deprecated `ActiveRecord::TestCase` into the Rails test
suite. The class is no longer public and is only used for internal
Rails tests.
* Removed support for deprecated option `:restrict` for `:dependent`
in associations.
-* Removed support for deprecated `delete_sql` in associations.
-
-* Removed support for deprecated `insert_sql` in associations.
-
-* Removed support for deprecated `finder_sql` in associations.
-
-* Removed support for deprecated `counter_sql` in associations.
+* Removed support for deprecated `:delete_sql`, `:insert_sql`, `:finder_sql`
+ and `:counter_sql` options in associations.
* Removed deprecated method `type_cast_code` from Column.
-* Removed deprecated options `delete_sql` and `insert_sql` from HABTM
- association.
-
-* Removed deprecated options `finder_sql` and `counter_sql` from
- collection association.
-
* Removed deprecated `ActiveRecord::Base#connection` method.
Make sure to access it via the class.
@@ -299,17 +409,17 @@ for detailed changes.
* Removed deprecated methods `partial_updates`, `partial_updates?` and
`partial_updates=`.
-* Removed deprecated method `scoped`
+* Removed deprecated method `scoped`.
-* Removed deprecated method `default_scopes?`
+* Removed deprecated method `default_scopes?`.
* Remove implicit join references that were deprecated in 4.0.
-* Removed `activerecord-deprecated_finders` as a dependency
+* Removed `activerecord-deprecated_finders` as a dependency.
-* Usage of `implicit_readonly` is being removed. Please use `readonly` method
+* Removed usage of `implicit_readonly`. Please use `readonly` method
explicitly to mark records as
- `readonly. ([Pull Request](https://github.com/rails/rails/pull/10769))
+ `readonly`. ([Pull Request](https://github.com/rails/rails/pull/10769))
### Deprecations
diff --git a/guides/source/_welcome.html.erb b/guides/source/_welcome.html.erb
index 93c177905c..ed2025a75d 100644
--- a/guides/source/_welcome.html.erb
+++ b/guides/source/_welcome.html.erb
@@ -15,8 +15,8 @@
</p>
<% end %>
<p>
- The guides for Rails 3.2.x are available at <a href="http://guides.rubyonrails.org/v3.2.15/">http://guides.rubyonrails.org/v3.2.15/</a>.
+ The guides for Rails 3.2.x are available at <a href="http://guides.rubyonrails.org/v3.2.16/">http://guides.rubyonrails.org/v3.2.16/</a>.
</p>
<p>
- The guides for Rails 2.3.x are available at <a href="http://guides.rubyonrails.org/v2.3.11/">http://guides.rubyonrails.org/v2.3.11/</a>.
+ The guides for Rails 2.3.x are available at <a href="http://guides.rubyonrails.org/v2.3.18/">http://guides.rubyonrails.org/v2.3.18/</a>.
</p>
diff --git a/guides/source/plugins.md b/guides/source/plugins.md
index d0aa2e55a2..8587bd48b2 100644
--- a/guides/source/plugins.md
+++ b/guides/source/plugins.md
@@ -3,9 +3,9 @@ The Basics of Creating Rails Plugins
A Rails plugin is either an extension or a modification of the core framework. Plugins provide:
-* a way for developers to share bleeding-edge ideas without hurting the stable code base
-* a segmented architecture so that units of code can be fixed or updated on their own release schedule
-* an outlet for the core developers so that they don't have to include every cool new feature under the sun
+* A way for developers to share bleeding-edge ideas without hurting the stable code base.
+* A segmented architecture so that units of code can be fixed or updated on their own release schedule.
+* An outlet for the core developers so that they don't have to include every cool new feature under the sun.
After reading this guide, you will know:
@@ -48,7 +48,7 @@ See usage and options by asking for help:
$ rails plugin --help
```
-Testing your newly generated plugin
+Testing Your Newly Generated Plugin
-----------------------------------
You can navigate to the directory that contains the plugin, run the `bundle install` command
@@ -92,7 +92,7 @@ Run `rake` to run the test. This test should fail because we haven't implemented
Great - now you are ready to start development.
-Then in `lib/yaffle.rb` add `require "yaffle/core_ext"`:
+In `lib/yaffle.rb`, add `require "yaffle/core_ext"`:
```ruby
# yaffle/lib/yaffle.rb
@@ -219,7 +219,7 @@ $ rails generate model Wickwall last_squawk:string last_tweet:string
```
Now you can create the necessary database tables in your testing database by navigating to your dummy app
-and migrating the database. First
+and migrating the database. First, run:
```bash
$ cd test/dummy
@@ -245,7 +245,7 @@ end
```
-We will also add code to define the acts_as_yaffle method.
+We will also add code to define the `acts_as_yaffle` method.
```ruby
# yaffle/lib/yaffle/acts_as_yaffle.rb
@@ -286,7 +286,7 @@ You can then return to the root directory (`cd ../..`) of your plugin and rerun
```
-Getting closer... Now we will implement the code of the acts_as_yaffle method to make the tests pass.
+Getting closer... Now we will implement the code of the `acts_as_yaffle` method to make the tests pass.
```ruby
# yaffle/lib/yaffle/acts_as_yaffle.rb
@@ -310,7 +310,7 @@ end
ActiveRecord::Base.send :include, Yaffle::ActsAsYaffle
```
-When you run `rake` you should see the tests all pass:
+When you run `rake`, you should see the tests all pass:
```bash
5 tests, 5 assertions, 0 failures, 0 errors, 0 skips
@@ -390,7 +390,11 @@ Run `rake` one final time and you should see:
7 tests, 7 assertions, 0 failures, 0 errors, 0 skips
```
-NOTE: The use of `write_attribute` to write to the field in model is just one example of how a plugin can interact with the model, and will not always be the right method to use. For example, you could also use `send("#{self.class.yaffle_text_field}=", string.to_squawk)`.
+NOTE: The use of `write_attribute` to write to the field in model is just one example of how a plugin can interact with the model, and will not always be the right method to use. For example, you could also use:
+
+```ruby
+send("#{self.class.yaffle_text_field}=", string.to_squawk)
+```
Generators
----------
@@ -398,7 +402,7 @@ Generators
Generators can be included in your gem simply by creating them in a lib/generators directory of your plugin. More information about
the creation of generators can be found in the [Generators Guide](generators.html)
-Publishing your Gem
+Publishing Your Gem
-------------------
Gem plugins currently in development can easily be shared from any Git repository. To share the Yaffle gem with others, simply
@@ -411,12 +415,12 @@ gem 'yaffle', git: 'git://github.com/yaffle_watcher/yaffle.git'
After running `bundle install`, your gem functionality will be available to the application.
When the gem is ready to be shared as a formal release, it can be published to [RubyGems](http://www.rubygems.org).
-For more information about publishing gems to RubyGems, see: [Creating and Publishing Your First Ruby Gem](http://blog.thepete.net/2010/11/creating-and-publishing-your-first-ruby.html)
+For more information about publishing gems to RubyGems, see: [Creating and Publishing Your First Ruby Gem](http://blog.thepete.net/2010/11/creating-and-publishing-your-first-ruby.html).
RDoc Documentation
------------------
-Once your plugin is stable and you are ready to deploy do everyone else a favor and document it! Luckily, writing documentation for your plugin is easy.
+Once your plugin is stable and you are ready to deploy, do everyone else a favor and document it! Luckily, writing documentation for your plugin is easy.
The first step is to update the README file with detailed information about how to use your plugin. A few key things to include are:
diff --git a/guides/source/rails_on_rack.md b/guides/source/rails_on_rack.md
index b42c8fb81b..9c92cf3aea 100644
--- a/guides/source/rails_on_rack.md
+++ b/guides/source/rails_on_rack.md
@@ -144,7 +144,7 @@ use Rack::ETag
run MyApp::Application.routes
```
-Purpose of each of this middlewares is explained in the [Internal Middlewares](#internal-middleware-stack) section.
+The default middlewares shown here (and some others) are each summarized in the [Internal Middlewares](#internal-middleware-stack) section, below.
### Configuring Middleware Stack
diff --git a/guides/source/upgrading_ruby_on_rails.md b/guides/source/upgrading_ruby_on_rails.md
index 3fbc913d8b..596682bb1f 100644
--- a/guides/source/upgrading_ruby_on_rails.md
+++ b/guides/source/upgrading_ruby_on_rails.md
@@ -27,6 +27,72 @@ Upgrading from Rails 4.0 to Rails 4.1
NOTE: This section is a work in progress.
+### Spring
+
+If you want to use Spring as your application preloader you need to:
+
+1. add `gem 'spring', group: :development` to your `Gemfile`.
+2. install spring using `bundle install`.
+3. springify your binstubs with `bundle exec spring binstub --all`.
+
+NOTE: User defined rake tasks will run in the `development` environment by
+default. If you want them to run in other environments consult the
+[Spring README](https://github.com/jonleighton/spring#rake).
+
+### Changes in JSON handling
+
+The are a few major changes related to JSON handling in Rails 4.1.
+
+#### MultiJSON removal
+
+MultiJSON has reached its [end-of-life](https://github.com/rails/rails/pull/10576)
+and has been removed from Rails.
+
+If your application currently depend on MultiJSON directly, you have a few options:
+
+1. Add 'multi_json' to your Gemfile. Note that this might cease to work in the future
+
+2. Migrate away from MultiJSON by using `obj.to_json`, and `JSON.parse(str)` instead.
+
+WARNING: Do not simply replace `MultiJson.dump` and `MultiJson.load` with
+`JSON.dump` and `JSON.load`. These JSON gem APIs are meant for serializing and
+deserializing arbitrary Ruby objects and are generally [unsafe](http://www.ruby-doc.org/stdlib-2.0.0/libdoc/json/rdoc/JSON.html#method-i-load).
+
+#### JSON gem compatibility
+
+Historically, Rails had some compatibility issues with the JSON gem. Using
+`JSON.generate` and `JSON.dump` inside a Rails application could produce
+unexpected errors.
+
+Rails 4.1 fixed these issues by isolating its own encoder from the JSON gem. The
+JSON gem APIs will function as normal, but they will not have access to any
+Rails-specific features. For example:
+
+```ruby
+class FooBar
+ def as_json(options = nil)
+ { foo: "bar" }
+ end
+end
+
+>> FooBar.new.to_json # => "{\"foo\":\"bar\"}"
+>> JSON.generate(FooBar.new, quirks_mode: true) # => "\"#<FooBar:0x007fa80a481610>\""
+```
+
+#### New JSON encoder
+
+The JSON encoder in Rails 4.1 has been rewritten to take advantage of the JSON
+gem. For most applications, this should be a transparent change. However, as
+part of the rewrite, the following features have been removed from the encoder:
+
+1. Circular data structure detection
+2. Support for the `encode_json` hook
+3. Option to encode `BigDecimal` objects as numbers instead of strings
+
+If you application depends on one of these features, you can get them back by
+adding the [`activesupport-json_encoder`](https://github.com/rails/activesupport-json_encoder)
+gem to your Gemfile.
+
### Methods defined in Active Record fixtures
Rails 4.1 evaluates each fixture's ERB in a separate context, so helper methods
@@ -367,7 +433,7 @@ Upgrading from Rails 3.1 to Rails 3.2
If your application is currently on any version of Rails older than 3.1.x, you should upgrade to Rails 3.1 before attempting an update to Rails 3.2.
-The following changes are meant for upgrading your application to Rails 3.2.15,
+The following changes are meant for upgrading your application to Rails 3.2.16,
the last 3.2.x version of Rails.
### Gemfile
@@ -375,7 +441,7 @@ the last 3.2.x version of Rails.
Make the following changes to your `Gemfile`.
```ruby
-gem 'rails', '3.2.15'
+gem 'rails', '3.2.16'
group :assets do
gem 'sass-rails', '~> 3.2.6'
diff --git a/railties/CHANGELOG.md b/railties/CHANGELOG.md
index 6169f3ebee..3813ef7909 100644
--- a/railties/CHANGELOG.md
+++ b/railties/CHANGELOG.md
@@ -1,3 +1,27 @@
+* Add `Application#message_verifier` method to return a message verifier.
+
+ This verifier can be used to generate and verify signed messages in the application.
+
+ message = Rails.application.message_verifier('salt').generate('my sensible data')
+ Rails.application.message_verifier('salt').verify(message)
+ # => 'my sensible data'
+
+ It is recommended not to use the same verifier for different things, so you can get different
+ verifiers passing the name argument.
+
+ message = Rails.application.message_verifier('cookies').generate('my sensible cookie data')
+
+ See the `ActiveSupport::MessageVerifier` documentation for more information.
+
+ *Rafael Mendonça França*
+
+* The [Spring application
+ preloader](https://github.com/jonleighton/spring) is now installed
+ by default for new applications. It uses the development group of
+ the Gemfile, so will not be installed in production.
+
+ *Jon Leighton*
+
* Uses .railsrc while creating new plugin if it is available.
Fixes #10700.
@@ -153,11 +177,11 @@
*Paul Nikitochkin*
* Remove deprecated `ActiveRecord::Generators::ActiveModel#update_attributes` in
- favor of `ActiveRecord::Generators::ActiveModel#update`
+ favor of `ActiveRecord::Generators::ActiveModel#update`.
*Vipul A M*
-* Remove deprecated `config.whiny_nils` option
+* Remove deprecated `config.whiny_nils` option.
*Vipul A M*
diff --git a/railties/lib/rails/application.rb b/railties/lib/rails/application.rb
index d1e88cfafd..e45bfaf6fc 100644
--- a/railties/lib/rails/application.rb
+++ b/railties/lib/rails/application.rb
@@ -1,6 +1,7 @@
require 'fileutils'
require 'active_support/core_ext/object/blank'
require 'active_support/key_generator'
+require 'active_support/message_verifier'
require 'rails/engine'
module Rails
@@ -107,12 +108,13 @@ module Rails
def initialize(initial_variable_values = {}, &block)
super()
- @initialized = false
- @reloaders = []
- @routes_reloader = nil
- @app_env_config = nil
- @ordered_railties = nil
- @railties = nil
+ @initialized = false
+ @reloaders = []
+ @routes_reloader = nil
+ @app_env_config = nil
+ @ordered_railties = nil
+ @railties = nil
+ @message_verifiers = {}
add_lib_to_load_path!
ActiveSupport.run_load_hooks(:before_configuration, self)
@@ -158,6 +160,31 @@ module Rails
end
end
+ # Returns a message verifier object.
+ #
+ # This verifier can be used to generate and verify signed messages in the application.
+ #
+ # It is recommended not to use the same verifier for different things, so you can get different
+ # verifiers passing the +verifier_name+ argument.
+ #
+ # ==== Parameters
+ #
+ # * +salt+ - the salt that will be used to generate the secret key of the verifier.
+ #
+ # ==== Examples
+ #
+ # message = Rails.application.message_verifier('salt').generate('my sensible data')
+ # Rails.application.message_verifier('salt').verify(message)
+ # # => 'my sensible data'
+ #
+ # See the +ActiveSupport::MessageVerifier+ documentation for more information.
+ def message_verifier(salt)
+ @message_verifiers[salt] ||= begin
+ secret = key_generator.generate_key(salt)
+ ActiveSupport::MessageVerifier.new(secret)
+ end
+ end
+
# Stores some of the Rails initial environment parameters which
# will be used by middlewares and engines to configure themselves.
def env_config
diff --git a/railties/lib/rails/generators/app_base.rb b/railties/lib/rails/generators/app_base.rb
index 2022b4ed3d..5d4682f6e3 100644
--- a/railties/lib/rails/generators/app_base.rb
+++ b/railties/lib/rails/generators/app_base.rb
@@ -47,6 +47,9 @@ module Rails
class_option :skip_sprockets, type: :boolean, aliases: '-S', default: false,
desc: 'Skip Sprockets files'
+ class_option :skip_spring, type: :boolean, default: false,
+ desc: "Don't install Spring application preloader"
+
class_option :database, type: :string, aliases: '-d', default: 'sqlite3',
desc: "Preconfigure for selected database (options: #{DATABASES.join('/')})"
@@ -109,6 +112,7 @@ module Rails
jbuilder_gemfile_entry,
sdoc_gemfile_entry,
platform_dependent_gemfile_entry,
+ spring_gemfile_entry,
@extra_entries].flatten.find_all(&@gem_filter)
end
@@ -365,6 +369,12 @@ module Rails
end
end
+ def spring_gemfile_entry
+ return [] unless spring_install?
+ comment = 'Spring speeds up development by keeping your application running in the background. Read more: https://github.com/jonleighton/spring'
+ GemfileEntry.new('spring', nil, comment, group: :development)
+ end
+
def bundle_command(command)
say_status :run, "bundle #{command}"
@@ -388,8 +398,22 @@ module Rails
end
end
+ def bundle_install?
+ !(options[:skip_gemfile] || options[:skip_bundle] || options[:pretend])
+ end
+
+ def spring_install?
+ !options[:skip_spring] && Process.respond_to?(:fork)
+ end
+
def run_bundle
- bundle_command('install') unless options[:skip_gemfile] || options[:skip_bundle] || options[:pretend]
+ bundle_command('install') if bundle_install?
+ end
+
+ def generate_spring_binstubs
+ if bundle_install? && spring_install?
+ bundle_command("exec spring binstub --all")
+ end
end
def empty_directory_with_keep_file(destination, config = {})
diff --git a/railties/lib/rails/generators/rails/app/app_generator.rb b/railties/lib/rails/generators/rails/app/app_generator.rb
index a2023886cd..87556bd609 100644
--- a/railties/lib/rails/generators/rails/app/app_generator.rb
+++ b/railties/lib/rails/generators/rails/app/app_generator.rb
@@ -237,6 +237,7 @@ module Rails
public_task :run_bundle
public_task :replay_template
+ public_task :generate_spring_binstubs
protected
diff --git a/railties/test/application/configuration_test.rb b/railties/test/application/configuration_test.rb
index 03a735b1c1..722c940d8e 100644
--- a/railties/test/application/configuration_test.rb
+++ b/railties/test/application/configuration_test.rb
@@ -268,6 +268,41 @@ module ApplicationTests
assert_equal 'some_value', verifier.verify(last_response.body)
end
+ test "application verifier can be used in the entire application" do
+ make_basic_app do |app|
+ app.config.secret_key_base = 'b3c631c314c0bbca50c1b2843150fe33'
+ app.config.session_store :disabled
+ end
+
+ message = app.message_verifier('salt').generate("some_value")
+
+ assert_equal 'some_value', Rails.application.message_verifier('salt').verify(message)
+
+ secret = app.key_generator.generate_key('salt')
+ verifier = ActiveSupport::MessageVerifier.new(secret)
+ assert_equal 'some_value', verifier.verify(message)
+ end
+
+ test "application verifier can build different verifiers" do
+ make_basic_app do |app|
+ app.config.secret_key_base = 'b3c631c314c0bbca50c1b2843150fe33'
+ app.config.session_store :disabled
+ end
+
+ default_verifier = app.message_verifier('salt')
+ text_verifier = app.message_verifier('text')
+
+ message = text_verifier.generate('some_value')
+
+ assert_equal 'some_value', text_verifier.verify(message)
+ assert_raises ActiveSupport::MessageVerifier::InvalidSignature do
+ default_verifier.verify(message)
+ end
+
+ assert_equal default_verifier.object_id, app.message_verifier('salt').object_id
+ assert_not_equal default_verifier.object_id, text_verifier.object_id
+ end
+
test "protect from forgery is the default in a new app" do
make_basic_app
diff --git a/railties/test/application/initializers/i18n_test.rb b/railties/test/application/initializers/i18n_test.rb
index 97df073ec7..2a64cd8ba7 100644
--- a/railties/test/application/initializers/i18n_test.rb
+++ b/railties/test/application/initializers/i18n_test.rb
@@ -183,5 +183,16 @@ en:
load_app
assert_fallbacks ca: [:ca, :"es-ES", :es, :'en-US', :en]
end
+
+ test "config.i18n.enforce_available_locales is set before config.i18n.default_locale is" do
+ add_to_config <<-RUBY
+ config.i18n.default_locale = :it
+ config.i18n.enforce_available_locales = true
+ RUBY
+
+ assert_raises(I18n::InvalidLocale) do
+ load_app
+ end
+ end
end
end
diff --git a/railties/test/generators/app_generator_test.rb b/railties/test/generators/app_generator_test.rb
index 257d07f514..7c2040470f 100644
--- a/railties/test/generators/app_generator_test.rb
+++ b/railties/test/generators/app_generator_test.rb
@@ -437,6 +437,34 @@ class AppGeneratorTest < Rails::Generators::TestCase
assert_file "foo bar/config/initializers/session_store.rb", /key: '_foo_bar/
end
+ def test_spring
+ run_generator
+ assert_file "Gemfile", /gem 'spring'/
+ end
+
+ def test_spring_binstubs
+ generator.stubs(:bundle_command).with('install')
+ generator.expects(:bundle_command).with('exec spring binstub --all').once
+ quietly { generator.invoke_all }
+ end
+
+ def test_spring_no_fork
+ Process.stubs(:respond_to?).with(:fork).returns(false)
+ run_generator
+
+ assert_file "Gemfile" do |content|
+ assert_no_match(/spring/, content)
+ end
+ end
+
+ def test_skip_spring
+ run_generator [destination_root, "--skip-spring"]
+
+ assert_file "Gemfile" do |content|
+ assert_no_match(/spring/, content)
+ end
+ end
+
protected
def action(*args, &block)
diff --git a/railties/test/generators/shared_generator_tests.rb b/railties/test/generators/shared_generator_tests.rb
index 7184639d23..8e198d5fe1 100644
--- a/railties/test/generators/shared_generator_tests.rb
+++ b/railties/test/generators/shared_generator_tests.rb
@@ -26,11 +26,17 @@ module SharedGeneratorTests
default_files.each { |path| assert_file path }
end
- def test_generation_runs_bundle_install
- generator([destination_root]).expects(:bundle_command).with('install').once
+ def assert_generates_with_bundler(options = {})
+ generator([destination_root], options)
+ generator.expects(:bundle_command).with('install').once
+ generator.stubs(:bundle_command).with('exec spring binstub --all')
quietly { generator.invoke_all }
end
+ def test_generation_runs_bundle_install
+ assert_generates_with_bundler
+ end
+
def test_plugin_new_generate_pretend
run_generator ["testapp", "--pretend"]
default_files.each{ |path| assert_no_file File.join("testapp",path) }
@@ -96,15 +102,13 @@ module SharedGeneratorTests
end
def test_dev_option
- generator([destination_root], dev: true).expects(:bundle_command).with('install').once
- quietly { generator.invoke_all }
+ assert_generates_with_bundler dev: true
rails_path = File.expand_path('../../..', Rails.root)
assert_file 'Gemfile', /^gem\s+["']rails["'],\s+path:\s+["']#{Regexp.escape(rails_path)}["']$/
end
def test_edge_option
- generator([destination_root], edge: true).expects(:bundle_command).with('install').once
- quietly { generator.invoke_all }
+ assert_generates_with_bundler edge: true
assert_file 'Gemfile', %r{^gem\s+["']rails["'],\s+github:\s+["']#{Regexp.escape("rails/rails")}["']$}
end