aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--actionpack/CHANGELOG.md7
-rw-r--r--actionpack/lib/action_controller/metal/http_authentication.rb8
-rw-r--r--actionpack/lib/action_controller/test_case.rb7
-rw-r--r--actionpack/test/controller/http_token_authentication_test.rb29
-rw-r--r--actionview/CHANGELOG.md10
-rw-r--r--actionview/lib/action_view/base.rb3
-rw-r--r--actionview/lib/action_view/helpers/output_safety_helper.rb8
-rw-r--r--actionview/lib/action_view/helpers/tag_helper.rb7
-rw-r--r--actionview/test/template/output_safety_helper_test.rb9
-rw-r--r--actionview/test/template/tag_helper_test.rb24
-rw-r--r--activemodel/lib/active_model/model.rb6
-rw-r--r--activerecord/lib/active_record.rb1
-rw-r--r--activerecord/lib/active_record/attribute.rb56
-rw-r--r--activerecord/lib/active_record/attribute_methods.rb20
-rw-r--r--activerecord/lib/active_record/attribute_methods/before_type_cast.rb6
-rw-r--r--activerecord/lib/active_record/attribute_methods/primary_key.rb6
-rw-r--r--activerecord/lib/active_record/attribute_methods/read.rb27
-rw-r--r--activerecord/lib/active_record/attribute_methods/write.rb15
-rw-r--r--activerecord/lib/active_record/connection_adapters/postgresql/oid/array.rb12
-rw-r--r--activerecord/lib/active_record/connection_adapters/postgresql/oid/bytea.rb5
-rw-r--r--activerecord/lib/active_record/core.rb47
-rw-r--r--activerecord/lib/active_record/errors.rb36
-rw-r--r--activerecord/lib/active_record/fixtures.rb18
-rw-r--r--activerecord/lib/active_record/persistence.rb18
-rw-r--r--activerecord/lib/active_record/result.rb2
-rw-r--r--activerecord/lib/active_record/tasks/database_tasks.rb3
-rw-r--r--activerecord/test/cases/adapters/postgresql/array_test.rb9
-rw-r--r--activerecord/test/cases/adapters/postgresql/composite_test.rb2
-rw-r--r--activerecord/test/cases/attribute_methods_test.rb4
-rw-r--r--activerecord/test/cases/attribute_test.rb103
-rw-r--r--activerecord/test/cases/base_test.rb14
-rw-r--r--activerecord/test/cases/persistence_test.rb13
-rw-r--r--activerecord/test/cases/serialization_test.rb8
-rw-r--r--activerecord/test/cases/serialized_attribute_test.rb6
-rw-r--r--activerecord/test/cases/store_test.rb1
-rw-r--r--activesupport/lib/active_support/core_ext/object/duplicable.rb2
-rw-r--r--activesupport/lib/active_support/testing/assertions.rb2
-rw-r--r--guides/source/4_2_release_notes.md243
-rw-r--r--guides/source/action_mailer_basics.md2
-rw-r--r--guides/source/active_record_postgresql.md6
-rw-r--r--guides/source/api_documentation_guidelines.md36
-rw-r--r--guides/source/upgrading_ruby_on_rails.md6
42 files changed, 684 insertions, 163 deletions
diff --git a/actionpack/CHANGELOG.md b/actionpack/CHANGELOG.md
index dfd5ddeedf..5474f5dd58 100644
--- a/actionpack/CHANGELOG.md
+++ b/actionpack/CHANGELOG.md
@@ -1,3 +1,10 @@
+* With authorization header `Authorization: Token token=`, `authenticate` now
+ recognize token as nil, instead of "token".
+
+ Fixes #14846.
+
+ *Larry Lv*
+
* Ensure the controller is always notified as soon as the client disconnects
during live streaming, even when the controller is blocked on a write.
diff --git a/actionpack/lib/action_controller/metal/http_authentication.rb b/actionpack/lib/action_controller/metal/http_authentication.rb
index 3111992f82..5b52c19802 100644
--- a/actionpack/lib/action_controller/metal/http_authentication.rb
+++ b/actionpack/lib/action_controller/metal/http_authentication.rb
@@ -121,8 +121,8 @@ module ActionController
def authentication_request(controller, realm)
controller.headers["WWW-Authenticate"] = %(Basic realm="#{realm.gsub(/"/, "")}")
- controller.response_body = "HTTP Basic: Access denied.\n"
controller.status = 401
+ controller.response_body = "HTTP Basic: Access denied.\n"
end
end
@@ -256,8 +256,8 @@ module ActionController
def authentication_request(controller, realm, message = nil)
message ||= "HTTP Digest: Access denied.\n"
authentication_header(controller, realm)
- controller.response_body = message
controller.status = 401
+ controller.response_body = message
end
def secret_token(request)
@@ -449,7 +449,7 @@ module ActionController
authorization_request = request.authorization.to_s
if authorization_request[TOKEN_REGEX]
params = token_params_from authorization_request
- [params.shift.last, Hash[params].with_indifferent_access]
+ [params.shift[1], Hash[params].with_indifferent_access]
end
end
@@ -464,7 +464,7 @@ module ActionController
# This removes the `"` characters wrapping the value.
def rewrite_param_values(array_params)
- array_params.each { |param| param.last.gsub! %r/^"|"$/, '' }
+ array_params.each { |param| (param[1] || "").gsub! %r/^"|"$/, '' }
end
# This method takes an authorization body and splits up the key-value
diff --git a/actionpack/lib/action_controller/test_case.rb b/actionpack/lib/action_controller/test_case.rb
index e6695ffc90..849286a4a9 100644
--- a/actionpack/lib/action_controller/test_case.rb
+++ b/actionpack/lib/action_controller/test_case.rb
@@ -629,8 +629,11 @@ module ActionController
@response.prepare!
@assigns = @controller.respond_to?(:view_assigns) ? @controller.view_assigns : {}
- @request.session['flash'] = @request.flash.to_session_value
- @request.session.delete('flash') if @request.session['flash'].blank?
+
+ if flash_value = @request.flash.to_session_value
+ @request.session['flash'] = flash_value
+ end
+
@response
end
diff --git a/actionpack/test/controller/http_token_authentication_test.rb b/actionpack/test/controller/http_token_authentication_test.rb
index 86b94652ce..ef90fff178 100644
--- a/actionpack/test/controller/http_token_authentication_test.rb
+++ b/actionpack/test/controller/http_token_authentication_test.rb
@@ -132,13 +132,30 @@ class HttpTokenAuthenticationTest < ActionController::TestCase
assert_equal(expected, actual)
end
- private
-
- def sample_request(token)
- @sample_request ||= OpenStruct.new authorization: %{Token token="#{token}"}
+ test "token_and_options returns empty string with empty token" do
+ token = ''
+ actual = ActionController::HttpAuthentication::Token.token_and_options(sample_request(token)).first
+ expected = token
+ assert_equal(expected, actual)
end
- def encode_credentials(token, options = {})
- ActionController::HttpAuthentication::Token.encode_credentials(token, options)
+ test "token_and_options returns nil with no value after the equal sign" do
+ actual = ActionController::HttpAuthentication::Token.token_and_options(malformed_request).first
+ expected = nil
+ assert_equal(expected, actual)
end
+
+ private
+
+ def sample_request(token)
+ @sample_request ||= OpenStruct.new authorization: %{Token token="#{token}", nonce="def"}
+ end
+
+ def malformed_request
+ @malformed_request ||= OpenStruct.new authorization: %{Token token=}
+ end
+
+ def encode_credentials(token, options = {})
+ ActionController::HttpAuthentication::Token.encode_credentials(token, options)
+ end
end
diff --git a/actionview/CHANGELOG.md b/actionview/CHANGELOG.md
index d13cc0c7a7..d825d3b627 100644
--- a/actionview/CHANGELOG.md
+++ b/actionview/CHANGELOG.md
@@ -1,3 +1,13 @@
+* Flatten the array parameter in `safe_join`, so it behaves consistently with
+ `Array#join`.
+
+ *Paul Grayson*
+
+* Honor `html_safe` on array elements in tag values, as we do for plain string
+ values.
+
+ *Paul Grayson*
+
* Add `ActionView::Template::Handler.unregister_template_handler`.
It performs the opposite of `ActionView::Template::Handler.register_template_handler`.
diff --git a/actionview/lib/action_view/base.rb b/actionview/lib/action_view/base.rb
index 8235b30599..900f96255e 100644
--- a/actionview/lib/action_view/base.rb
+++ b/actionview/lib/action_view/base.rb
@@ -130,6 +130,9 @@ module ActionView #:nodoc:
# end
# end
# end
+ #
+ # For more information on Builder please consult the [source
+ # code](https://github.com/jimweirich/builder).
class Base
include Helpers, ::ERB::Util, Context
diff --git a/actionview/lib/action_view/helpers/output_safety_helper.rb b/actionview/lib/action_view/helpers/output_safety_helper.rb
index e1f40011c0..b0d9c7c7f9 100644
--- a/actionview/lib/action_view/helpers/output_safety_helper.rb
+++ b/actionview/lib/action_view/helpers/output_safety_helper.rb
@@ -18,9 +18,9 @@ module ActionView #:nodoc:
end
# This method returns a html safe string similar to what <tt>Array#join</tt>
- # would return. All items in the array, including the supplied separator, are
- # html escaped unless they are html safe, and the returned string is marked
- # as html safe.
+ # would return. The array is flattened, and all items, including
+ # the supplied separator, are html escaped unless they are html
+ # safe, and the returned string is marked as html safe.
#
# safe_join(["<p>foo</p>".html_safe, "<p>bar</p>"], "<br />")
# # => "<p>foo</p>&lt;br /&gt;&lt;p&gt;bar&lt;/p&gt;"
@@ -31,7 +31,7 @@ module ActionView #:nodoc:
def safe_join(array, sep=$,)
sep = ERB::Util.unwrapped_html_escape(sep)
- array.map { |i| ERB::Util.unwrapped_html_escape(i) }.join(sep).html_safe
+ array.flatten.map! { |i| ERB::Util.unwrapped_html_escape(i) }.join(sep).html_safe
end
end
end
diff --git a/actionview/lib/action_view/helpers/tag_helper.rb b/actionview/lib/action_view/helpers/tag_helper.rb
index 9b9ca7d60d..35444bcfb4 100644
--- a/actionview/lib/action_view/helpers/tag_helper.rb
+++ b/actionview/lib/action_view/helpers/tag_helper.rb
@@ -173,8 +173,11 @@ module ActionView
end
def tag_option(key, value, escape)
- value = value.join(" ") if value.is_a?(Array)
- value = ERB::Util.unwrapped_html_escape(value) if escape
+ if value.is_a?(Array)
+ value = escape ? safe_join(value, " ") : value.join(" ")
+ else
+ value = escape ? ERB::Util.unwrapped_html_escape(value) : value
+ end
%(#{key}="#{value}")
end
end
diff --git a/actionview/test/template/output_safety_helper_test.rb b/actionview/test/template/output_safety_helper_test.rb
index 76c71c9e6d..a1bf0e1a5f 100644
--- a/actionview/test/template/output_safety_helper_test.rb
+++ b/actionview/test/template/output_safety_helper_test.rb
@@ -25,4 +25,11 @@ class OutputSafetyHelperTest < ActionView::TestCase
assert_equal "<p>foo</p><br /><p>bar</p>", joined
end
-end \ No newline at end of file
+ test "safe_join should work recursively similarly to Array.join" do
+ joined = safe_join(['a',['b','c']], ':')
+ assert_equal 'a:b:c', joined
+
+ joined = safe_join(['"a"',['<b>','<c>']], ' <br/> ')
+ assert_equal '&quot;a&quot; &lt;br/&gt; &lt;b&gt; &lt;br/&gt; &lt;c&gt;', joined
+ end
+end
diff --git a/actionview/test/template/tag_helper_test.rb b/actionview/test/template/tag_helper_test.rb
index fb016a52de..c78b6450f2 100644
--- a/actionview/test/template/tag_helper_test.rb
+++ b/actionview/test/template/tag_helper_test.rb
@@ -80,11 +80,27 @@ class TagHelperTest < ActionView::TestCase
str = content_tag('p', "limelight", :class => ["song", "play"])
assert_equal "<p class=\"song play\">limelight</p>", str
+
+ str = content_tag('p', "limelight", :class => ["song", ["play"]])
+ assert_equal "<p class=\"song play\">limelight</p>", str
end
def test_content_tag_with_unescaped_array_class
str = content_tag('p', "limelight", {:class => ["song", "play>"]}, false)
assert_equal "<p class=\"song play>\">limelight</p>", str
+
+ str = content_tag('p', "limelight", {:class => ["song", ["play>"]]}, false)
+ assert_equal "<p class=\"song play>\">limelight</p>", str
+ end
+
+ def test_content_tag_with_empty_array_class
+ str = content_tag('p', 'limelight', {:class => []})
+ assert_equal '<p class="">limelight</p>', str
+ end
+
+ def test_content_tag_with_unescaped_empty_array_class
+ str = content_tag('p', 'limelight', {:class => []}, false)
+ assert_equal '<p class="">limelight</p>', str
end
def test_content_tag_with_data_attributes
@@ -115,6 +131,14 @@ class TagHelperTest < ActionView::TestCase
end
end
+ def test_tag_honors_html_safe_with_escaped_array_class
+ str = tag('p', :class => ['song>', 'play>'.html_safe])
+ assert_equal '<p class="song&gt; play>" />', str
+
+ str = tag('p', :class => ['song>'.html_safe, 'play>'])
+ assert_equal '<p class="song> play&gt;" />', str
+ end
+
def test_skip_invalid_escaped_attributes
['&1;', '&#1dfa3;', '& #123;'].each do |escaped|
assert_equal %(<a href="#{escaped.gsub(/&/, '&amp;')}" />), tag('a', :href => escaped)
diff --git a/activemodel/lib/active_model/model.rb b/activemodel/lib/active_model/model.rb
index 63716eebb1..640024eaa1 100644
--- a/activemodel/lib/active_model/model.rb
+++ b/activemodel/lib/active_model/model.rb
@@ -16,8 +16,8 @@ module ActiveModel
# end
#
# person = Person.new(name: 'bob', age: '18')
- # person.name # => 'bob'
- # person.age # => 18
+ # person.name # => "bob"
+ # person.age # => "18"
#
# Note that, by default, <tt>ActiveModel::Model</tt> implements <tt>persisted?</tt>
# to return +false+, which is the most common case. You may want to override
@@ -74,7 +74,7 @@ module ActiveModel
#
# person = Person.new(name: 'bob', age: '18')
# person.name # => "bob"
- # person.age # => 18
+ # person.age # => "18"
def initialize(params={})
params.each do |attr, value|
self.public_send("#{attr}=", value)
diff --git a/activerecord/lib/active_record.rb b/activerecord/lib/active_record.rb
index f856c482d6..53fa132219 100644
--- a/activerecord/lib/active_record.rb
+++ b/activerecord/lib/active_record.rb
@@ -31,6 +31,7 @@ require 'active_record/version'
module ActiveRecord
extend ActiveSupport::Autoload
+ autoload :Attribute
autoload :Base
autoload :Callbacks
autoload :Core
diff --git a/activerecord/lib/active_record/attribute.rb b/activerecord/lib/active_record/attribute.rb
new file mode 100644
index 0000000000..f78cde23c5
--- /dev/null
+++ b/activerecord/lib/active_record/attribute.rb
@@ -0,0 +1,56 @@
+module ActiveRecord
+ class Attribute # :nodoc:
+ class << self
+ def from_database(value, type)
+ FromDatabase.new(value, type)
+ end
+
+ def from_user(value, type)
+ FromUser.new(value, type)
+ end
+ end
+
+ attr_reader :value_before_type_cast, :type
+
+ # This method should not be called directly.
+ # Use #from_database or #from_user
+ def initialize(value_before_type_cast, type)
+ @value_before_type_cast = value_before_type_cast
+ @type = type
+ end
+
+ def value
+ # `defined?` is cheaper than `||=` when we get back falsy values
+ @value = type_cast(value_before_type_cast) unless defined?(@value)
+ @value
+ end
+
+ def value_for_database
+ type.type_cast_for_database(value)
+ end
+
+ def type_cast
+ raise NotImplementedError
+ end
+
+ protected
+
+ def initialize_dup(other)
+ if defined?(@value) && @value.duplicable?
+ @value = @value.dup
+ end
+ end
+
+ class FromDatabase < Attribute
+ def type_cast(value)
+ type.type_cast_from_database(value)
+ end
+ end
+
+ class FromUser < Attribute
+ def type_cast(value)
+ type.type_cast_from_user(value)
+ end
+ end
+ end
+end
diff --git a/activerecord/lib/active_record/attribute_methods.rb b/activerecord/lib/active_record/attribute_methods.rb
index e626227e7e..ddccb29d09 100644
--- a/activerecord/lib/active_record/attribute_methods.rb
+++ b/activerecord/lib/active_record/attribute_methods.rb
@@ -261,9 +261,9 @@ module ActiveRecord
# If the result is true then check for the select case.
# For queries selecting a subset of columns, return false for unselected columns.
- # We check defined?(@raw_attributes) not to issue warnings if called on objects that
+ # We check defined?(@attributes) not to issue warnings if called on objects that
# have been allocated but not yet initialized.
- if defined?(@raw_attributes) && @raw_attributes.any? && self.class.column_names.include?(name)
+ if defined?(@attributes) && @attributes.any? && self.class.column_names.include?(name)
return has_attribute?(name)
end
@@ -280,7 +280,7 @@ module ActiveRecord
# person.has_attribute?('age') # => true
# person.has_attribute?(:nothing) # => false
def has_attribute?(attr_name)
- @raw_attributes.has_key?(attr_name.to_s)
+ @attributes.has_key?(attr_name.to_s)
end
# Returns an array of names for the attributes available on this object.
@@ -292,7 +292,7 @@ module ActiveRecord
# person.attribute_names
# # => ["id", "created_at", "updated_at", "name", "age"]
def attribute_names
- @raw_attributes.keys
+ @attributes.keys
end
# Returns a hash of all the attributes with their names as keys and the values of the attributes as values.
@@ -400,11 +400,10 @@ module ActiveRecord
protected
- def clone_attributes(reader_method = :read_attribute, attributes = {}) # :nodoc:
- attribute_names.each do |name|
- attributes[name] = clone_attribute_value(reader_method, name)
+ def clone_attributes # :nodoc:
+ @attributes.each_with_object({}) do |(name, attr), h|
+ h[name] = attr.dup
end
- attributes
end
def clone_attribute_value(reader_method, attribute_name) # :nodoc:
@@ -424,7 +423,7 @@ module ActiveRecord
def attribute_method?(attr_name) # :nodoc:
# We check defined? because Syck calls respond_to? before actually calling initialize.
- defined?(@raw_attributes) && @raw_attributes.include?(attr_name)
+ defined?(@attributes) && @attributes.include?(attr_name)
end
private
@@ -465,9 +464,6 @@ module ActiveRecord
end
def typecasted_attribute_value(name)
- # FIXME: we need @attributes to be used consistently.
- # If the values stored in @attributes were already typecasted, this code
- # could be simplified
read_attribute(name)
end
end
diff --git a/activerecord/lib/active_record/attribute_methods/before_type_cast.rb b/activerecord/lib/active_record/attribute_methods/before_type_cast.rb
index 4365f5a1a1..d057f0941a 100644
--- a/activerecord/lib/active_record/attribute_methods/before_type_cast.rb
+++ b/activerecord/lib/active_record/attribute_methods/before_type_cast.rb
@@ -43,7 +43,9 @@ module ActiveRecord
# task.read_attribute_before_type_cast('completed_on') # => "2012-10-21"
# task.read_attribute_before_type_cast(:completed_on) # => "2012-10-21"
def read_attribute_before_type_cast(attr_name)
- @raw_attributes[attr_name.to_s]
+ if attr = @attributes[attr_name.to_s]
+ attr.value_before_type_cast
+ end
end
# Returns a hash of attributes before typecasting and deserialization.
@@ -57,7 +59,7 @@ module ActiveRecord
# task.attributes_before_type_cast
# # => {"id"=>nil, "title"=>nil, "is_done"=>true, "completed_on"=>"2012-10-21", "created_at"=>nil, "updated_at"=>nil}
def attributes_before_type_cast
- @raw_attributes
+ @attributes.each_with_object({}) { |(k, v), h| h[k] = v.value_before_type_cast }
end
private
diff --git a/activerecord/lib/active_record/attribute_methods/primary_key.rb b/activerecord/lib/active_record/attribute_methods/primary_key.rb
index 931209b07b..1c81a5b71b 100644
--- a/activerecord/lib/active_record/attribute_methods/primary_key.rb
+++ b/activerecord/lib/active_record/attribute_methods/primary_key.rb
@@ -15,8 +15,10 @@ module ActiveRecord
# Returns the primary key value.
def id
- sync_with_transaction_state
- read_attribute(self.class.primary_key)
+ if pk = self.class.primary_key
+ sync_with_transaction_state
+ read_attribute(pk)
+ end
end
# Sets the primary key value.
diff --git a/activerecord/lib/active_record/attribute_methods/read.rb b/activerecord/lib/active_record/attribute_methods/read.rb
index 7b7e37884b..8c1cc128f7 100644
--- a/activerecord/lib/active_record/attribute_methods/read.rb
+++ b/activerecord/lib/active_record/attribute_methods/read.rb
@@ -82,25 +82,16 @@ module ActiveRecord
# it has been typecast (for example, "2004-12-12" in a date column is cast
# to a date object, like Date.new(2004, 12, 12)).
def read_attribute(attr_name)
- # If it's cached, just return it
- # We use #[] first as a perf optimization for non-nil values. See https://gist.github.com/jonleighton/3552829.
name = attr_name.to_s
- @attributes[name] || @attributes.fetch(name) {
- column = @column_types_override[name] if @column_types_override
- column ||= @column_types[name]
-
- return @raw_attributes.fetch(name) {
- if name == 'id' && self.class.primary_key != name
- read_attribute(self.class.primary_key)
- end
- } unless column
-
- value = @raw_attributes.fetch(name) {
- return block_given? ? yield(name) : nil
- }
-
- @attributes[name] = column.type_cast_from_database(value)
- }
+ @attributes.fetch(name) {
+ if name == 'id'
+ return read_attribute(self.class.primary_key)
+ elsif block_given? && self.class.columns_hash.key?(name)
+ return yield(name)
+ else
+ return nil
+ end
+ }.value
end
private
diff --git a/activerecord/lib/active_record/attribute_methods/write.rb b/activerecord/lib/active_record/attribute_methods/write.rb
index b72a6219b0..246a2cd8ba 100644
--- a/activerecord/lib/active_record/attribute_methods/write.rb
+++ b/activerecord/lib/active_record/attribute_methods/write.rb
@@ -69,22 +69,19 @@ module ActiveRecord
def write_attribute_with_type_cast(attr_name, value, should_type_cast)
attr_name = attr_name.to_s
attr_name = self.class.primary_key if attr_name == 'id' && self.class.primary_key
- @attributes.delete(attr_name)
- column = type_for_attribute(attr_name)
+ type = type_for_attribute(attr_name)
unless has_attribute?(attr_name) || self.class.columns_hash.key?(attr_name)
raise ActiveModel::MissingAttributeError, "can't write unknown attribute `#{attr_name}'"
end
- # If we're dealing with a binary column, write the data to the cache
- # so we don't attempt to typecast multiple times.
- if column.binary?
- @attributes[attr_name] = value
- elsif should_type_cast
- @attributes[attr_name] = column.type_cast_from_user(value)
+ if should_type_cast
+ @attributes[attr_name] = Attribute.from_user(value, type)
+ else
+ @attributes[attr_name] = Attribute.from_database(value, type)
end
- @raw_attributes[attr_name] = value
+ value
end
end
end
diff --git a/activerecord/lib/active_record/connection_adapters/postgresql/oid/array.rb b/activerecord/lib/active_record/connection_adapters/postgresql/oid/array.rb
index 87817fe8d1..4e7d472d97 100644
--- a/activerecord/lib/active_record/connection_adapters/postgresql/oid/array.rb
+++ b/activerecord/lib/active_record/connection_adapters/postgresql/oid/array.rb
@@ -12,12 +12,16 @@ module ActiveRecord
def type_cast_from_database(value)
if value.is_a?(::String)
- type_cast_array(parse_pg_array(value))
+ type_cast_array(parse_pg_array(value), :type_cast_from_database)
else
super
end
end
+ def type_cast_from_user(value)
+ type_cast_array(value, :type_cast_from_user)
+ end
+
# Loads pg_array_parser if available. String parsing can be
# performed quicker by a native extension, which will not create
# a large amount of Ruby objects that will need to be garbage
@@ -32,11 +36,11 @@ module ActiveRecord
private
- def type_cast_array(value)
+ def type_cast_array(value, method)
if value.is_a?(::Array)
- value.map { |item| type_cast_array(item) }
+ value.map { |item| type_cast_array(item, method) }
else
- @subtype.type_cast_from_database(value)
+ @subtype.public_send(method, value)
end
end
end
diff --git a/activerecord/lib/active_record/connection_adapters/postgresql/oid/bytea.rb b/activerecord/lib/active_record/connection_adapters/postgresql/oid/bytea.rb
index 36c53d8732..89b203d2b1 100644
--- a/activerecord/lib/active_record/connection_adapters/postgresql/oid/bytea.rb
+++ b/activerecord/lib/active_record/connection_adapters/postgresql/oid/bytea.rb
@@ -3,8 +3,9 @@ module ActiveRecord
module PostgreSQL
module OID # :nodoc:
class Bytea < Type::Binary
- def cast_value(value)
- PGconn.unescape_bytea value
+ def type_cast_from_database(value)
+ return if value.nil?
+ PGconn.unescape_bytea(super)
end
end
end
diff --git a/activerecord/lib/active_record/core.rb b/activerecord/lib/active_record/core.rb
index 7edaf256c7..d39e5fddfe 100644
--- a/activerecord/lib/active_record/core.rb
+++ b/activerecord/lib/active_record/core.rb
@@ -249,16 +249,19 @@ module ActiveRecord
# # Instantiates a single new object
# User.new(first_name: 'Jamie')
def initialize(attributes = nil, options = {})
- defaults = self.class.raw_column_defaults.dup
- defaults.each { |k, v| defaults[k] = v.dup if v.duplicable? }
+ defaults = {}
+ self.class.raw_column_defaults.each do |k, v|
+ default = v.duplicable? ? v.dup : v
+ defaults[k] = Attribute.from_database(default, type_for_attribute(k))
+ end
- @raw_attributes = defaults
- @column_types_override = nil
+ @attributes = defaults
@column_types = self.class.column_types
init_internals
initialize_internals_callback
+ self.class.define_attribute_methods
# +options+ argument is only needed to make protected_attributes gem easier to hook.
# Remove it when we drop support to this gem.
init_attributes(attributes, options) if attributes
@@ -278,13 +281,11 @@ module ActiveRecord
# post.init_with('attributes' => { 'title' => 'hello world' })
# post.title # => 'hello world'
def init_with(coder)
- @raw_attributes = coder['raw_attributes']
- @column_types_override = coder['column_types']
+ @attributes = coder['attributes']
@column_types = self.class.column_types
init_internals
- @attributes = coder['attributes'] if coder['attributes']
@new_record = coder['new_record']
self.class.define_attribute_methods
@@ -323,12 +324,9 @@ module ActiveRecord
##
def initialize_dup(other) # :nodoc:
- cloned_attributes = other.clone_attributes(:read_attribute_before_type_cast)
-
- @raw_attributes = cloned_attributes
- @raw_attributes[self.class.primary_key] = nil
- @attributes = other.clone_attributes(:read_attribute)
- @attributes[self.class.primary_key] = nil
+ pk = self.class.primary_key
+ @attributes = other.clone_attributes
+ @attributes[pk] = Attribute.from_database(nil, type_for_attribute(pk))
run_callbacks(:initialize) unless _initialize_callbacks.empty?
@@ -354,9 +352,9 @@ module ActiveRecord
# Post.new.encode_with(coder)
# coder # => {"attributes" => {"id" => nil, ... }}
def encode_with(coder)
- coder['raw_attributes'] = @raw_attributes
+ # FIXME: Remove this when we better serialize attributes
+ coder['raw_attributes'] = attributes_before_type_cast
coder['attributes'] = @attributes
- coder['column_types'] = @column_types_override
coder['new_record'] = new_record?
end
@@ -380,20 +378,24 @@ module ActiveRecord
# Delegates to id in order to allow two records of the same type and id to work with something like:
# [ Person.find(1), Person.find(2), Person.find(3) ] & [ Person.find(1), Person.find(4) ] # => [ Person.find(1) ]
def hash
- id.hash
+ if id
+ id.hash
+ else
+ super
+ end
end
# Clone and freeze the attributes hash such that associations are still
# accessible, even on destroyed records, but cloned models will not be
# frozen.
def freeze
- @raw_attributes = @raw_attributes.clone.freeze
+ @attributes = @attributes.clone.freeze
self
end
# Returns +true+ if the attributes hash has been frozen.
def frozen?
- @raw_attributes.frozen?
+ @attributes.frozen?
end
# Allows sort on objects
@@ -422,9 +424,9 @@ module ActiveRecord
# Returns the contents of the record as a nicely formatted string.
def inspect
- # We check defined?(@raw_attributes) not to issue warnings if the object is
+ # We check defined?(@attributes) not to issue warnings if the object is
# allocated but not initialized.
- inspection = if defined?(@raw_attributes) && @raw_attributes
+ inspection = if defined?(@attributes) && @attributes
self.class.column_names.collect { |name|
if has_attribute?(name)
"#{name}: #{attribute_for_inspect(name)}"
@@ -523,11 +525,10 @@ module ActiveRecord
def init_internals
pk = self.class.primary_key
- @raw_attributes[pk] = nil unless @raw_attributes.key?(pk)
+ @attributes[pk] ||= Attribute.from_database(nil, type_for_attribute(pk))
@aggregation_cache = {}
@association_cache = {}
- @attributes = {}
@readonly = false
@destroyed = false
@marked_for_destruction = false
@@ -550,7 +551,7 @@ module ActiveRecord
def thaw
if frozen?
- @raw_attributes = @raw_attributes.dup
+ @attributes = @attributes.dup
end
end
end
diff --git a/activerecord/lib/active_record/errors.rb b/activerecord/lib/active_record/errors.rb
index 2ccb1b0702..52c70977ef 100644
--- a/activerecord/lib/active_record/errors.rb
+++ b/activerecord/lib/active_record/errors.rb
@@ -30,17 +30,18 @@ module ActiveRecord
class SerializationTypeMismatch < ActiveRecordError
end
- # Raised when adapter not specified on connection (or configuration file <tt>config/database.yml</tt>
- # misses adapter field).
+ # Raised when adapter not specified on connection (or configuration file
+ # +config/database.yml+ misses adapter field).
class AdapterNotSpecified < ActiveRecordError
end
- # Raised when Active Record cannot find database adapter specified in <tt>config/database.yml</tt> or programmatically.
+ # Raised when Active Record cannot find database adapter specified in
+ # +config/database.yml+ or programmatically.
class AdapterNotFound < ActiveRecordError
end
- # Raised when connection to the database could not been established (for example when <tt>connection=</tt>
- # is given a nil object).
+ # Raised when connection to the database could not been established (for
+ # example when +connection=+ is given a nil object).
class ConnectionNotEstablished < ActiveRecordError
end
@@ -82,19 +83,17 @@ module ActiveRecord
class InvalidForeignKey < WrappedDatabaseException
end
- # Raised when number of bind variables in statement given to <tt>:condition</tt> key (for example,
- # when using +find+ method)
- # does not match number of expected variables.
+ # Raised when number of bind variables in statement given to +:condition+ key
+ # (for example, when using +find+ method) does not match number of expected
+ # values supplied.
#
- # For example, in
+ # For example, when there are two placeholders with only one value supplied:
#
# Location.where("lat = ? AND lng = ?", 53.7362)
- #
- # two placeholders are given but only one variable to fill them.
class PreparedStatementInvalid < ActiveRecordError
end
- # Raised when a given database does not exist
+ # Raised when a given database does not exist.
class NoDatabaseError < StatementInvalid
end
@@ -102,7 +101,8 @@ module ActiveRecord
# instantiation, for example, when two users edit the same wiki page and one starts editing and saves
# the page before the other.
#
- # Read more about optimistic locking in ActiveRecord::Locking module RDoc.
+ # Read more about optimistic locking in ActiveRecord::Locking module
+ # documentation.
class StaleObjectError < ActiveRecordError
attr_reader :record, :attempted_action
@@ -114,8 +114,9 @@ module ActiveRecord
end
- # Raised when association is being configured improperly or
- # user tries to use offset and limit together with has_many or has_and_belongs_to_many associations.
+ # Raised when association is being configured improperly or user tries to use
+ # offset and limit together with +has_many+ or +has_and_belongs_to_many+
+ # associations.
class ConfigurationError < ActiveRecordError
end
@@ -153,7 +154,8 @@ module ActiveRecord
class Rollback < ActiveRecordError
end
- # Raised when attribute has a name reserved by Active Record (when attribute has name of one of Active Record instance methods).
+ # Raised when attribute has a name reserved by Active Record (when attribute
+ # has name of one of Active Record instance methods).
class DangerousAttributeError < ActiveRecordError
end
@@ -171,7 +173,7 @@ module ActiveRecord
end
# Raised when an error occurred while doing a mass assignment to an attribute through the
- # <tt>attributes=</tt> method. The exception has an +attribute+ property that is the name of the
+ # +attributes=+ method. The exception has an +attribute+ property that is the name of the
# offending attribute.
class AttributeAssignmentError < ActiveRecordError
attr_reader :exception, :attribute
diff --git a/activerecord/lib/active_record/fixtures.rb b/activerecord/lib/active_record/fixtures.rb
index 95fcbbe99a..4cd5f92207 100644
--- a/activerecord/lib/active_record/fixtures.rb
+++ b/activerecord/lib/active_record/fixtures.rb
@@ -15,9 +15,10 @@ module ActiveRecord
# They are stored in YAML files, one file per model, which are placed in the directory
# appointed by <tt>ActiveSupport::TestCase.fixture_path=(path)</tt> (this is automatically
# configured for Rails, so you can just put your files in <tt><your-rails-app>/test/fixtures/</tt>).
- # The fixture file ends with the <tt>.yml</tt> file extension (Rails example:
- # <tt><your-rails-app>/test/fixtures/web_sites.yml</tt>). The format of a fixture file looks
- # like this:
+ # The fixture file ends with the +.yml+ file extension, for example:
+ # <tt><your-rails-app>/test/fixtures/web_sites.yml</tt>).
+ #
+ # The format of a fixture file looks like this:
#
# rubyonrails:
# id: 1
@@ -61,8 +62,8 @@ module ActiveRecord
# end
# end
#
- # By default, <tt>test_helper.rb</tt> will load all of your fixtures into your test database,
- # so this test will succeed.
+ # By default, +test_helper.rb+ will load all of your fixtures into your test
+ # database, so this test will succeed.
#
# The testing environment will automatically load the all fixtures into the database before each
# test. To ensure consistent data, the environment deletes the fixtures before running the load.
@@ -375,8 +376,8 @@ module ActiveRecord
# == Support for YAML defaults
#
# You can set and reuse defaults in your fixtures YAML file.
- # This is the same technique used in the <tt>database.yml</tt> file
- # to specify defaults:
+ # This is the same technique used in the +database.yml+ file to specify
+ # defaults:
#
# DEFAULTS: &DEFAULTS
# created_on: <%= 3.weeks.ago.to_s(:db) %>
@@ -392,7 +393,8 @@ module ActiveRecord
# Any fixture labeled "DEFAULTS" is safely ignored.
class FixtureSet
#--
- # An instance of FixtureSet is normally stored in a single YAML file and possibly in a folder with the same name.
+ # An instance of FixtureSet is normally stored in a single YAML file and
+ # possibly in a folder with the same name.
#++
MAX_ID = 2 ** 30 - 1
diff --git a/activerecord/lib/active_record/persistence.rb b/activerecord/lib/active_record/persistence.rb
index 3ab8ec4836..5c744762d9 100644
--- a/activerecord/lib/active_record/persistence.rb
+++ b/activerecord/lib/active_record/persistence.rb
@@ -48,11 +48,12 @@ module ActiveRecord
# how this "single-table" inheritance mapping is implemented.
def instantiate(attributes, column_types = {})
klass = discriminate_class_for_record(attributes)
- klass.allocate.init_with(
- 'raw_attributes' => attributes,
- 'column_types' => column_types,
- 'new_record' => false,
- )
+
+ attributes = attributes.each_with_object({}) do |(name, value), h|
+ type = column_types.fetch(name) { klass.type_for_attribute(name) }
+ h[name] = Attribute.from_database(value, type)
+ end
+ klass.allocate.init_with('attributes' => attributes, 'new_record' => false)
end
private
@@ -182,7 +183,6 @@ module ActiveRecord
# So any change to the attributes in either instance will affect the other.
def becomes(klass)
became = klass.new
- became.instance_variable_set("@raw_attributes", @raw_attributes)
became.instance_variable_set("@attributes", @attributes)
became.instance_variable_set("@changed_attributes", @changed_attributes) if defined?(@changed_attributes)
became.instance_variable_set("@new_record", new_record?)
@@ -399,11 +399,9 @@ module ActiveRecord
self.class.unscoped { self.class.find(id) }
end
- @raw_attributes.update(fresh_object.instance_variable_get('@raw_attributes'))
+ @attributes.update(fresh_object.instance_variable_get('@attributes'))
- @column_types = self.class.column_types
- @column_types_override = fresh_object.instance_variable_get('@column_types_override')
- @attributes = {}
+ @column_types = self.class.column_types
self
end
diff --git a/activerecord/lib/active_record/result.rb b/activerecord/lib/active_record/result.rb
index ae53f66d7a..8c29c69a9b 100644
--- a/activerecord/lib/active_record/result.rb
+++ b/activerecord/lib/active_record/result.rb
@@ -95,7 +95,7 @@ module ActiveRecord
@hash_rows ||=
begin
# We freeze the strings to prevent them getting duped when
- # used as keys in ActiveRecord::Base's @raw_attributes hash
+ # used as keys in ActiveRecord::Base's @attributes hash
columns = @columns.map { |c| c.dup.freeze }
@rows.map { |row|
# In the past we used Hash[columns.zip(row)]
diff --git a/activerecord/lib/active_record/tasks/database_tasks.rb b/activerecord/lib/active_record/tasks/database_tasks.rb
index dedbbd3585..649f316ed8 100644
--- a/activerecord/lib/active_record/tasks/database_tasks.rb
+++ b/activerecord/lib/active_record/tasks/database_tasks.rb
@@ -6,7 +6,7 @@ module ActiveRecord
# <tt>ActiveRecord::Tasks::DatabaseTasks</tt> is a utility class, which encapsulates
# logic behind common tasks used to manage database and migrations.
#
- # The tasks defined here are used in rake tasks provided by Active Record.
+ # The tasks defined here are used with Rake tasks provided by Active Record.
#
# In order to use DatabaseTasks, a few config values need to be set. All the needed
# config values are set by Rails already, so it's necessary to do it only if you
@@ -14,7 +14,6 @@ module ActiveRecord
# (in such case after configuring the database tasks, you can also use the rake tasks
# defined in Active Record).
#
- #
# The possible config values are:
#
# * +env+: current environment (like Rails.env).
diff --git a/activerecord/test/cases/adapters/postgresql/array_test.rb b/activerecord/test/cases/adapters/postgresql/array_test.rb
index 66750deb68..0b1e3295cc 100644
--- a/activerecord/test/cases/adapters/postgresql/array_test.rb
+++ b/activerecord/test/cases/adapters/postgresql/array_test.rb
@@ -97,8 +97,13 @@ class PostgresqlArrayTest < ActiveRecord::TestCase
def test_type_cast_integers
x = PgArray.new(ratings: ['1', '2'])
- assert x.save!
- assert_equal(['1', '2'], x.ratings)
+
+ assert_equal([1, 2], x.ratings)
+
+ x.save!
+ x.reload
+
+ assert_equal([1, 2], x.ratings)
end
def test_select_with_strings
diff --git a/activerecord/test/cases/adapters/postgresql/composite_test.rb b/activerecord/test/cases/adapters/postgresql/composite_test.rb
index 0b48fe9af8..42c68cdae7 100644
--- a/activerecord/test/cases/adapters/postgresql/composite_test.rb
+++ b/activerecord/test/cases/adapters/postgresql/composite_test.rb
@@ -84,7 +84,7 @@ class PostgresqlCompositeWithCustomOIDTest < ActiveRecord::TestCase
class FullAddressType < ActiveRecord::Type::Value
def type; :full_address end
- def type_cast(value)
+ def type_cast_from_database(value)
if value =~ /\("?([^",]*)"?,"?([^",]*)"?\)/
FullAddress.new($1, $2)
end
diff --git a/activerecord/test/cases/attribute_methods_test.rb b/activerecord/test/cases/attribute_methods_test.rb
index a366caf875..f832ca3451 100644
--- a/activerecord/test/cases/attribute_methods_test.rb
+++ b/activerecord/test/cases/attribute_methods_test.rb
@@ -451,10 +451,10 @@ class AttributeMethodsTest < ActiveRecord::TestCase
end
def test_declared_suffixed_attribute_method_affects_respond_to_and_method_missing
- topic = @target.new(:title => 'Budget')
%w(_default _title_default _it! _candidate= able?).each do |suffix|
@target.class_eval "def attribute#{suffix}(*args) args end"
@target.attribute_method_suffix suffix
+ topic = @target.new(:title => 'Budget')
meth = "title#{suffix}"
assert topic.respond_to?(meth)
@@ -465,10 +465,10 @@ class AttributeMethodsTest < ActiveRecord::TestCase
end
def test_declared_affixed_attribute_method_affects_respond_to_and_method_missing
- topic = @target.new(:title => 'Budget')
[['mark_', '_for_update'], ['reset_', '!'], ['default_', '_value?']].each do |prefix, suffix|
@target.class_eval "def #{prefix}attribute#{suffix}(*args) args end"
@target.attribute_method_affix({ :prefix => prefix, :suffix => suffix })
+ topic = @target.new(:title => 'Budget')
meth = "#{prefix}title#{suffix}"
assert topic.respond_to?(meth)
diff --git a/activerecord/test/cases/attribute_test.rb b/activerecord/test/cases/attribute_test.rb
new file mode 100644
index 0000000000..57dd2e9a5e
--- /dev/null
+++ b/activerecord/test/cases/attribute_test.rb
@@ -0,0 +1,103 @@
+require 'cases/helper'
+require 'minitest/mock'
+
+module ActiveRecord
+ class AttributeTest < ActiveRecord::TestCase
+ setup do
+ @type = MiniTest::Mock.new
+ end
+
+ teardown do
+ assert @type.verify
+ end
+
+ test "from_database + read type casts from database" do
+ @type.expect(:type_cast_from_database, 'type cast from database', ['a value'])
+ attribute = Attribute.from_database('a value', @type)
+
+ type_cast_value = attribute.value
+
+ assert_equal 'type cast from database', type_cast_value
+ end
+
+ test "from_user + read type casts from user" do
+ @type.expect(:type_cast_from_user, 'type cast from user', ['a value'])
+ attribute = Attribute.from_user('a value', @type)
+
+ type_cast_value = attribute.value
+
+ assert_equal 'type cast from user', type_cast_value
+ end
+
+ test "reading memoizes the value" do
+ @type.expect(:type_cast_from_database, 'from the database', ['whatever'])
+ attribute = Attribute.from_database('whatever', @type)
+
+ type_cast_value = attribute.value
+ second_read = attribute.value
+
+ assert_equal 'from the database', type_cast_value
+ assert_same type_cast_value, second_read
+ end
+
+ test "reading memoizes falsy values" do
+ @type.expect(:type_cast_from_database, false, ['whatever'])
+ attribute = Attribute.from_database('whatever', @type)
+
+ attribute.value
+ attribute.value
+ end
+
+ test "read_before_typecast returns the given value" do
+ attribute = Attribute.from_database('raw value', @type)
+
+ raw_value = attribute.value_before_type_cast
+
+ assert_equal 'raw value', raw_value
+ end
+
+ test "from_database + read_for_database type casts to and from database" do
+ @type.expect(:type_cast_from_database, 'read from database', ['whatever'])
+ @type.expect(:type_cast_for_database, 'ready for database', ['read from database'])
+ attribute = Attribute.from_database('whatever', @type)
+
+ type_cast_for_database = attribute.value_for_database
+
+ assert_equal 'ready for database', type_cast_for_database
+ end
+
+ test "from_user + read_for_database type casts from the user to the database" do
+ @type.expect(:type_cast_from_user, 'read from user', ['whatever'])
+ @type.expect(:type_cast_for_database, 'ready for database', ['read from user'])
+ attribute = Attribute.from_user('whatever', @type)
+
+ type_cast_for_database = attribute.value_for_database
+
+ assert_equal 'ready for database', type_cast_for_database
+ end
+
+ test "duping dups the value" do
+ @type.expect(:type_cast_from_database, 'type cast', ['a value'])
+ attribute = Attribute.from_database('a value', @type)
+
+ value_from_orig = attribute.value
+ value_from_clone = attribute.dup.value
+ value_from_orig << ' foo'
+
+ assert_equal 'type cast foo', value_from_orig
+ assert_equal 'type cast', value_from_clone
+ end
+
+ test "duping does not dup the value if it is not dupable" do
+ @type.expect(:type_cast_from_database, false, ['a value'])
+ attribute = Attribute.from_database('a value', @type)
+
+ assert_same attribute.value, attribute.dup.value
+ end
+
+ test "duping does not eagerly type cast if we have not yet type cast" do
+ attribute = Attribute.from_database('a value', @type)
+ attribute.dup
+ end
+ end
+end
diff --git a/activerecord/test/cases/base_test.rb b/activerecord/test/cases/base_test.rb
index dc8f25b0e7..8f83cf7cb4 100644
--- a/activerecord/test/cases/base_test.rb
+++ b/activerecord/test/cases/base_test.rb
@@ -1488,15 +1488,14 @@ class BasicsTest < ActiveRecord::TestCase
attrs = topic.attributes.dup
attrs.delete 'id'
- typecast = Class.new {
- def type_cast_from_database value
+ typecast = Class.new(ActiveRecord::Type::Value) {
+ def type_cast value
"t.lo"
end
}
types = { 'author_name' => typecast.new }
- topic = Topic.allocate.init_with 'raw_attributes' => attrs,
- 'column_types' => types
+ topic = Topic.instantiate(attrs, types)
assert_equal 't.lo', topic.author_name
end
@@ -1597,4 +1596,11 @@ class BasicsTest < ActiveRecord::TestCase
assert_equal after_handler, new_handler
assert_equal orig_handler, klass.connection_handler
end
+
+ # Note: This is a performance optimization for Array#uniq and Hash#[] with
+ # AR::Base objects. If the future has made this irrelevant, feel free to
+ # delete this.
+ test "records without an id have unique hashes" do
+ assert_not_equal Post.new.hash, Post.new.hash
+ end
end
diff --git a/activerecord/test/cases/persistence_test.rb b/activerecord/test/cases/persistence_test.rb
index bc21eef919..28341d0b42 100644
--- a/activerecord/test/cases/persistence_test.rb
+++ b/activerecord/test/cases/persistence_test.rb
@@ -250,11 +250,9 @@ class PersistenceTest < ActiveRecord::TestCase
end
def test_create_columns_not_equal_attributes
- topic = Topic.allocate.init_with(
- 'raw_attributes' => {
- 'title' => 'Another New Topic',
- 'does_not_exist' => 'test'
- }
+ topic = Topic.instantiate(
+ 'title' => 'Another New Topic',
+ 'does_not_exist' => 'test'
)
assert_nothing_raised { topic.save }
end
@@ -300,10 +298,7 @@ class PersistenceTest < ActiveRecord::TestCase
topic.title = "Still another topic"
topic.save
- topic_reloaded = Topic.allocate
- topic_reloaded.init_with(
- 'raw_attributes' => topic.attributes.merge('does_not_exist' => 'test')
- )
+ topic_reloaded = Topic.instantiate(topic.attributes.merge('does_not_exist' => 'test'))
topic_reloaded.title = 'A New Topic'
assert_nothing_raised { topic_reloaded.save }
end
diff --git a/activerecord/test/cases/serialization_test.rb b/activerecord/test/cases/serialization_test.rb
index 7dd1f10ce9..3f52e80e11 100644
--- a/activerecord/test/cases/serialization_test.rb
+++ b/activerecord/test/cases/serialization_test.rb
@@ -69,6 +69,14 @@ class SerializationTest < ActiveRecord::TestCase
ActiveRecord::Base.include_root_in_json = original_root_in_json
end
+ def test_read_attribute_for_serialization_with_format_without_method_missing
+ klazz = Class.new(ActiveRecord::Base)
+ klazz.table_name = 'books'
+
+ book = klazz.new
+ assert_nil book.read_attribute_for_serialization(:format)
+ end
+
def test_read_attribute_for_serialization_with_format_after_init
klazz = Class.new(ActiveRecord::Base)
klazz.table_name = 'books'
diff --git a/activerecord/test/cases/serialized_attribute_test.rb b/activerecord/test/cases/serialized_attribute_test.rb
index 7d1c240638..82df0a5f08 100644
--- a/activerecord/test/cases/serialized_attribute_test.rb
+++ b/activerecord/test/cases/serialized_attribute_test.rb
@@ -36,12 +36,6 @@ class SerializedAttributeTest < ActiveRecord::TestCase
assert_equal(myobj, topic.content)
end
- def test_serialized_attribute_init_with
- topic = Topic.allocate
- topic.init_with('raw_attributes' => { 'content' => '--- foo' })
- assert_equal 'foo', topic.content
- end
-
def test_serialized_attribute_in_base_class
Topic.serialize("content", Hash)
diff --git a/activerecord/test/cases/store_test.rb b/activerecord/test/cases/store_test.rb
index f841b1c983..e9cdf94c99 100644
--- a/activerecord/test/cases/store_test.rb
+++ b/activerecord/test/cases/store_test.rb
@@ -189,7 +189,6 @@ class StoreTest < ActiveRecord::TestCase
assert_equal @john, loaded
second_dump = YAML.dump(loaded)
- assert_equal dumped, second_dump
assert_equal @john, YAML.load(second_dump)
end
end
diff --git a/activesupport/lib/active_support/core_ext/object/duplicable.rb b/activesupport/lib/active_support/core_ext/object/duplicable.rb
index 3d2c809c9f..c5d59128e5 100644
--- a/activesupport/lib/active_support/core_ext/object/duplicable.rb
+++ b/activesupport/lib/active_support/core_ext/object/duplicable.rb
@@ -19,7 +19,7 @@
class Object
# Can you safely dup this object?
#
- # False for +nil+, +false+, +true+, symbol, and number objects;
+ # False for +nil+, +false+, +true+, symbol, number and BigDecimal(in 1.9.x) objects;
# true otherwise.
def duplicable?
true
diff --git a/activesupport/lib/active_support/testing/assertions.rb b/activesupport/lib/active_support/testing/assertions.rb
index 76a591bc3b..11cca82995 100644
--- a/activesupport/lib/active_support/testing/assertions.rb
+++ b/activesupport/lib/active_support/testing/assertions.rb
@@ -9,7 +9,7 @@ module ActiveSupport
#
# assert_not nil # => true
# assert_not false # => true
- # assert_not 'foo' # => 'foo' is not nil or false
+ # assert_not 'foo' # => Expected "foo" to be nil or false
#
# An error message can be specified.
#
diff --git a/guides/source/4_2_release_notes.md b/guides/source/4_2_release_notes.md
new file mode 100644
index 0000000000..be007f93a7
--- /dev/null
+++ b/guides/source/4_2_release_notes.md
@@ -0,0 +1,243 @@
+Ruby on Rails 4.2 Release Notes
+===============================
+
+Highlights in Rails 4.2:
+
+These release notes cover only the major changes. To know about various bug
+fixes and changes, please refer to the change logs or check out the
+[list of commits](https://github.com/rails/rails/commits/master) in the main
+Rails repository on GitHub.
+
+--------------------------------------------------------------------------------
+
+Upgrading to Rails 4.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 4.1 in case you
+haven't and make sure your application still runs as expected before attempting
+an update to Rails 4.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-4-1-to-rails-4-2)
+guide.
+
+
+Major Features
+--------------
+
+
+
+Railties
+--------
+
+Please refer to the
+[Changelog](https://github.com/rails/rails/blob/4-2-stable/railties/CHANGELOG.md)
+for detailed changes.
+
+### Removals
+
+* The `rails application` command has been removed without replacement.
+ ([Pull Request](https://github.com/rails/rails/pull/11616))
+
+### Notable changes
+
+* Introduced `bin/setup` script to bootstrap an application.
+ ([Pull Request](https://github.com/rails/rails/pull/15189))
+
+* Changed default value for `config.assets.digest` to `true` in development.
+ ([Pull Request](https://github.com/rails/rails/pull/15155))
+
+* Introduced an API to register new extensions for `rake notes`.
+ ([Pull Request](https://github.com/rails/rails/pull/14379))
+
+* Introduced `Rails.gem_version` as a convenience method to return `Gem::Version.new(Rails.version)`.
+ ([Pull Request](https://github.com/rails/rails/pull/14101))
+
+
+Action Pack
+-----------
+
+Please refer to the
+[Changelog](https://github.com/rails/rails/blob/4-2-stable/actionpack/CHANGELOG.md)
+for detailed changes.
+
+### Deprecations
+
+* Deprecated support for setting the `to:` option of a router to a symbol or a
+ string that does not contain a `#` character:
+
+ get '/posts', to: MyRackApp => (No change necessary)
+ get '/posts', to: 'post#index' => (No change necessary)
+ get '/posts', to: 'posts' => get '/posts', controller: :posts
+ get '/posts', to: :index => get '/posts', action: :index
+
+ ([Commit](https://github.com/rails/rails/commit/cc26b6b7bccf0eea2e2c1a9ebdcc9d30ca7390d9))
+
+### Notable changes
+
+* The `*_filter` family methods has been removed from the documentation. Their
+ usage are discouraged in favor of the `*_action` family methods:
+
+ after_filter => after_action
+ append_after_filter => append_after_action
+ append_around_filter => append_around_action
+ append_before_filter => append_before_action
+ around_filter => around_action
+ before_filter => before_action
+ prepend_after_filter => prepend_after_action
+ prepend_around_filter => prepend_around_action
+ prepend_before_filter => prepend_before_action
+ skip_after_filter => skip_after_action
+ skip_around_filter => skip_around_action
+ skip_before_filter => skip_before_action
+ skip_filter => skip_action_callback
+
+ If your application is depending on these methods, you should use the
+ replacement `*_action` methods instead. These methods will be deprecated in
+ the future and eventually removed from Rails.
+ (Commit [1](https://github.com/rails/rails/commit/6c5f43bab8206747a8591435b2aa0ff7051ad3de),
+ [2](https://github.com/rails/rails/commit/489a8f2a44dc9cea09154ee1ee2557d1f037c7d4))
+
+* Added HTTP method `MKCALENDAR` from RFC-4791
+ ([Pull Request](https://github.com/rails/rails/pull/15121))
+
+* `*_fragment.action_controller` notifications now include the controller and action name
+ in the payload.
+ ([Pull Request](https://github.com/rails/rails/pull/14137))
+
+* Segments that are passed into URL helpers are now automatically escaped.
+ ([Commit](https://github.com/rails/rails/commit/5460591f0226a9d248b7b4f89186bd5553e7768f))
+
+* Improved Routing Error page with fuzzy matching for route search.
+ ([Pull Request](https://github.com/rails/rails/pull/14619))
+
+* Added option to disable logging of CSRF failures.
+ ([Pull Request](https://github.com/rails/rails/pull/14280))
+
+
+Action Mailer
+-------------
+
+Please refer to the
+[Changelog](https://github.com/rails/rails/blob/4-2-stable/actionmailer/CHANGELOG.md)
+for detailed changes.
+
+### Notable changes
+
+
+Active Record
+-------------
+
+Please refer to the
+[Changelog](https://github.com/rails/rails/blob/4-2-stable/activerecord/CHANGELOG.md)
+for detailed changes.
+
+### Deprecations
+
+* Deprecated using `.joins`, `.preload` and `.eager_load` with associations that
+ depends on the instance state (i.e. those defined with a scope that takes an
+ argument) without replacement.
+ ([Commit](https://github.com/rails/rails/commit/ed56e596a0467390011bc9d56d462539776adac1))
+
+* Deprecated passing Active Record objects to `.find` or `.exists?`. Call `#id`
+ on the objects first.
+ (Commit [1](https://github.com/rails/rails/commit/d92ae6ccca3bcfd73546d612efaea011270bd270),
+ [2](https://github.com/rails/rails/commit/d35f0033c7dec2b8d8b52058fb8db495d49596f7))
+
+* Deprecated half-baked support for PostgreSQL range values with excluding
+ beginnings. We currently map PostgreSQL ranges to Ruby ranges. This conversion
+ is not fully possible because the Ruby range does not support excluded
+ beginnings.
+
+ The current solution of incrementing the beginning is not correct and is now
+ deprecated. For subtypes where we don't know how to increment (e.g. `#succ`
+ is not defined) it will raise an `ArgumentError` for ranges with excluding
+ beginnings.
+
+ ([Commit](https://github.com/rails/rails/commit/91949e48cf41af9f3e4ffba3e5eecf9b0a08bfc3))
+
+### Notable changes
+
+* Added support for `#pretty_print` in `ActiveRecord::Base` objects.
+ ([Pull Request](https://github.com/rails/rails/pull/15172))
+
+* PostgreSQL and SQLite adapters no longer add a default limit of 255 characters
+ on string columns.
+ ([Pull Request](https://github.com/rails/rails/pull/14579))
+
+* `sqlite3:///some/path` now resolves to the absolute system path `/some/path`.
+ For relative paths, use `sqlite3:some/path` instead. (Previously, `sqlite3:///some/path`
+ resolved to the relative path `some/path`. This behaviour was deprecated on
+ Rails 4.1.)
+ ([Pull Request](https://github.com/rails/rails/pull/14569))
+
+* Introduced `#validate` as an alias for `#valid?`.
+ ([Pull Request](https://github.com/rails/rails/pull/14456))
+
+* `#touch` now accepts multiple attributes to be touched at once.
+ ([Pull Request](https://github.com/rails/rails/pull/14423))
+
+* Added support for fractional seconds for MySQL 5.6 and above.
+ (Pull Request [1](https://github.com/rails/rails/pull/8240), [2](https://github.com/rails/rails/pull/14359))
+
+* Added support for the `citext` column type in PostgreSQL adapter.
+ ([Pull Request](https://github.com/rails/rails/pull/12523))
+
+* Added support for user-created range types in PostgreSQL adapter.
+ ([Commit](https://github.com/rails/rails/commit/4cb47167e747e8f9dc12b0ddaf82bdb68c03e032))
+
+Active Model
+------------
+
+Please refer to the
+[Changelog](https://github.com/rails/rails/blob/4-2-stable/activemodel/CHANGELOG.md)
+for detailed changes.
+
+### Notable changes
+
+* Introduced `#validate` as an alias for `#valid?`.
+ ([Pull Request](https://github.com/rails/rails/pull/14456))
+
+
+Active Support
+--------------
+
+Please refer to the
+[Changelog](https://github.com/rails/rails/blob/4-2-stable/activesupport/CHANGELOG.md)
+for detailed changes.
+
+### Removals
+
+* Removed deprecated `Numeric#ago`, `Numeric#until`, `Numeric#since`,
+ `Numeric#from_now`. ([Commit](https://github.com/rails/rails/commit/f1eddea1e3f6faf93581c43651348f48b2b7d8bb))
+
+* Removed deprecated string based terminators for `ActiveSupport::Callbacks`.
+ ([Pull Request](https://github.com/rails/rails/pull/15100))
+
+### Deprecations
+
+* Deprecated `Class#superclass_delegating_accessor`, use `Class#class_attribute`
+ instead. ([Pull Request](https://github.com/rails/rails/pull/14271))
+
+* Deprecated `ActiveSupport::SafeBuffer#prepend!` as `ActiveSupport::SafeBuffer#prepend`
+ now performs the same function. ([Pull Request](https://github.com/rails/rails/pull/14529))
+
+### Notable changes
+
+* The `humanize` inflector helper now strips any leading underscores.
+ ([Commit](https://github.com/rails/rails/commit/daaa21bc7d20f2e4ff451637423a25ff2d5e75c7))
+
+* Added `SecureRandom::uuid_v3` and `SecureRandom::uuid_v5`.
+ ([Pull Request](https://github.com/rails/rails/pull/12016))
+
+* Introduce `Concern#class_methods` as an alternative to `module ClassMethods`,
+ as well as `Kernel#concern` to avoid the `module Foo; extend ActiveSupport::Concern; end`
+ boilerplate. ([Commit](https://github.com/rails/rails/commit/b16c36e688970df2f96f793a759365b248b582ad))
+
+Credits
+-------
+
+See the
+[full list of contributors to Rails](http://contributors.rubyonrails.org/) for
+the many people who spent many hours making Rails, the stable and robust
+framework it is. Kudos to all of them.
diff --git a/guides/source/action_mailer_basics.md b/guides/source/action_mailer_basics.md
index c7117027c0..cb1c1c653d 100644
--- a/guides/source/action_mailer_basics.md
+++ b/guides/source/action_mailer_basics.md
@@ -309,7 +309,7 @@ email address in the format `"Full Name <email>"`.
```ruby
def welcome_email(user)
@user = user
- email_with_name = "#{@user.name} <#{@user.email}>"
+ email_with_name = %("#{@user.name}" <#{@user.email}>)
mail(to: email_with_name, subject: 'Welcome to My Awesome Site')
end
```
diff --git a/guides/source/active_record_postgresql.md b/guides/source/active_record_postgresql.md
index fa135b537c..248d4769a1 100644
--- a/guides/source/active_record_postgresql.md
+++ b/guides/source/active_record_postgresql.md
@@ -8,6 +8,7 @@ After reading this guide, you will know:
* How to use PostgreSQL's datatypes.
* How to use UUID primary keys.
* How to implement full text search with PostgreSQL.
+* How to back your Active Record models with views.
--------------------------------------------------------------------------------
@@ -324,7 +325,8 @@ macbook.address
* [type definition](http://www.postgresql.org/docs/9.3/static/datatype-geometric.html)
-All geometric types are mapped to normal text.
+All geometric types, with the exception of `points` are mapped to normal text.
+A point is casted to an array containing `x` and `y` coordinates.
UUID Primary Keys
@@ -428,7 +430,7 @@ second = Article.create! title: "Brace yourself",
Article.count # => 1
first.archive!
-p Article.count # => 2
+Article.count # => 2
```
NOTE: This application only cares about non-archived `Articles`. A view also
diff --git a/guides/source/api_documentation_guidelines.md b/guides/source/api_documentation_guidelines.md
index dda9baa8d7..7e9b288ffd 100644
--- a/guides/source/api_documentation_guidelines.md
+++ b/guides/source/api_documentation_guidelines.md
@@ -13,7 +13,8 @@ After reading this guide, you will know:
RDoc
----
-The [Rails API documentation](http://api.rubyonrails.org) is generated with RDoc.
+The [Rails API documentation](http://api.rubyonrails.org) is generated with
+[RDoc](http://docs.seattlerb.org/rdoc/).
```bash
bundle exec rake rdoc
@@ -21,6 +22,11 @@ The [Rails API documentation](http://api.rubyonrails.org) is generated with RDoc
Resulting HTML files can be found in the ./doc/rdoc directory.
+Please consult the RDoc documentation for help with the
+[markup](http://docs.seattlerb.org/rdoc/RDoc/Markup.html),
+and also take into account these [additional
+directives](http://docs.seattlerb.org/rdoc/RDoc/Parser/Ruby.html).
+
Wording
-------
@@ -324,4 +330,32 @@ A `:nodoc:` should never be added simply because a method or class is missing do
To summarize, the Rails team uses `:nodoc:` to mark publicly visible methods and classes for internal use; changes to the visibility of API should be considered carefully and discussed over a pull request first.
+Regarding the Rails Stack
+-------------------------
+
+When documenting parts of Rails API, it's important to remember all of the
+pieces that go into the Rails stack.
+
+This means that behavior may change depending on the scope or context of the
+method or class you're trying to document.
+
+In various places there is different behavior when you take the entire stack
+into account, one such example is
+`ActionView::Helpers::AssetTagHelper#image_tag`:
+
+```ruby
+# image_tag("icon.png")
+# # => <img alt="Icon" src="/assets/icon.png" />
+```
+
+Although the default behavior for `#image_tag` is to always return
+`/images/icon.png`, we take into account the full Rails stack (including the
+Asset Pipeline) we may see the result seen above.
+
+We're only concerned with the behavior experienced when using the full default
+Rails stack.
+
+In this case, we want to document the behavior of the _framework_, and not just
+this specific method.
+
If you have a question on how the Rails team handles certain API, don't hesitate to open a ticket or send a patch to the [issue tracker](https://github.com/rails/rails/issues).
diff --git a/guides/source/upgrading_ruby_on_rails.md b/guides/source/upgrading_ruby_on_rails.md
index 03d1f2a3a0..6800e71a3c 100644
--- a/guides/source/upgrading_ruby_on_rails.md
+++ b/guides/source/upgrading_ruby_on_rails.md
@@ -22,6 +22,12 @@ Rails generally stays close to the latest released Ruby version when it's releas
TIP: Ruby 1.8.7 p248 and p249 have marshaling bugs that crash Rails. Ruby Enterprise Edition has these fixed since the release of 1.8.7-2010.02. On the 1.9 front, Ruby 1.9.1 is not usable because it outright segfaults, so if you want to use 1.9.x, jump straight to 1.9.3 for smooth sailing.
+Upgrading from Rails 4.1 to Rails 4.2
+-------------------------------------
+
+NOTE: This section is a work in progress.
+
+
Upgrading from Rails 4.0 to Rails 4.1
-------------------------------------