aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--actionmailer/lib/action_mailer/base.rb4
-rw-r--r--actionpack/lib/action_dispatch/http/url.rb74
-rw-r--r--actionpack/test/controller/routing_test.rb1
-rw-r--r--actionpack/test/journey/router_test.rb2
-rw-r--r--actionview/CHANGELOG.md8
-rw-r--r--actionview/lib/action_view/helpers/javascript_helper.rb8
-rw-r--r--actionview/lib/action_view/helpers/number_helper.rb18
-rw-r--r--actionview/lib/action_view/tasks/dependencies.rake16
-rw-r--r--actionview/test/actionpack/controller/layout_test.rb10
-rw-r--r--actionview/test/support/template_handler_helper.rb8
-rw-r--r--activemodel/CHANGELOG.md6
-rw-r--r--activemodel/lib/active_model/conversion.rb17
-rw-r--r--activemodel/lib/active_model/validator.rb16
-rw-r--r--activemodel/test/cases/conversion_test.rb4
-rw-r--r--activemodel/test/cases/validations_test.rb21
-rw-r--r--activerecord/CHANGELOG.md26
-rw-r--r--activerecord/lib/active_record/associations.rb2
-rw-r--r--activerecord/lib/active_record/associations/association.rb2
-rw-r--r--activerecord/lib/active_record/associations/association_scope.rb15
-rw-r--r--activerecord/lib/active_record/associations/belongs_to_association.rb2
-rw-r--r--activerecord/lib/active_record/associations/builder/has_one.rb2
-rw-r--r--activerecord/lib/active_record/associations/collection_association.rb2
-rw-r--r--activerecord/lib/active_record/associations/has_many_through_association.rb10
-rw-r--r--activerecord/lib/active_record/associations/join_dependency.rb2
-rw-r--r--activerecord/lib/active_record/attribute_methods/read.rb49
-rw-r--r--activerecord/lib/active_record/attribute_methods/time_zone_conversion.rb51
-rw-r--r--activerecord/lib/active_record/attribute_methods/write.rb10
-rw-r--r--activerecord/lib/active_record/autosave_association.rb2
-rw-r--r--activerecord/lib/active_record/base.rb2
-rw-r--r--activerecord/lib/active_record/connection_adapters/abstract/schema_dumper.rb2
-rw-r--r--activerecord/lib/active_record/connection_adapters/column.rb4
-rw-r--r--activerecord/lib/active_record/connection_adapters/postgresql/cast.rb12
-rw-r--r--activerecord/lib/active_record/connection_adapters/postgresql/column.rb20
-rw-r--r--activerecord/lib/active_record/connection_adapters/postgresql/oid/array.rb30
-rw-r--r--activerecord/lib/active_record/connection_adapters/postgresql/oid/hstore.rb12
-rw-r--r--activerecord/lib/active_record/connection_adapters/postgresql/oid/json.rb12
-rw-r--r--activerecord/lib/active_record/connection_adapters/postgresql/oid/range.rb2
-rw-r--r--activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb4
-rw-r--r--activerecord/lib/active_record/core.rb1
-rw-r--r--activerecord/lib/active_record/fixtures.rb12
-rw-r--r--activerecord/lib/active_record/locking/optimistic.rb6
-rw-r--r--activerecord/lib/active_record/model_schema.rb22
-rw-r--r--activerecord/lib/active_record/nested_attributes.rb2
-rw-r--r--activerecord/lib/active_record/persistence.rb1
-rw-r--r--activerecord/lib/active_record/querying.rb2
-rw-r--r--activerecord/lib/active_record/reflection.rb35
-rw-r--r--activerecord/lib/active_record/relation/calculations.rb8
-rw-r--r--activerecord/lib/active_record/relation/finder_methods.rb2
-rw-r--r--activerecord/lib/active_record/result.rb2
-rw-r--r--activerecord/lib/active_record/timestamp.rb8
-rw-r--r--activerecord/lib/active_record/type/binary.rb9
-rw-r--r--activerecord/lib/active_record/type/serialized.rb21
-rw-r--r--activerecord/lib/active_record/type/value.rb23
-rw-r--r--activerecord/test/cases/adapters/mysql/mysql_adapter_test.rb2
-rw-r--r--activerecord/test/cases/adapters/postgresql/array_test.rb6
-rw-r--r--activerecord/test/cases/adapters/postgresql/bytea_test.rb6
-rw-r--r--activerecord/test/cases/adapters/postgresql/composite_test.rb6
-rw-r--r--activerecord/test/cases/adapters/postgresql/connection_test.rb2
-rw-r--r--activerecord/test/cases/adapters/postgresql/hstore_test.rb46
-rw-r--r--activerecord/test/cases/adapters/postgresql/json_test.rb9
-rw-r--r--activerecord/test/cases/adapters/postgresql/money_test.rb8
-rw-r--r--activerecord/test/cases/adapters/postgresql/range_test.rb2
-rw-r--r--activerecord/test/cases/attribute_decorators_test.rb4
-rw-r--r--activerecord/test/cases/attribute_methods_test.rb44
-rw-r--r--activerecord/test/cases/base_test.rb2
-rw-r--r--activerecord/test/cases/calculations_test.rb7
-rw-r--r--activerecord/test/cases/connection_adapters/type_lookup_test.rb2
-rw-r--r--activerecord/test/cases/dirty_test.rb6
-rw-r--r--activerecord/test/cases/finder_test.rb11
-rw-r--r--activerecord/test/cases/fixtures_test.rb6
-rw-r--r--activerecord/test/cases/locking_test.rb4
-rw-r--r--activerecord/test/cases/reflection_test.rb4
-rw-r--r--activerecord/test/cases/schema_dumper_test.rb2
-rw-r--r--activerecord/test/cases/timestamp_test.rb31
-rw-r--r--activerecord/test/cases/types_test.rb138
-rw-r--r--activerecord/test/cases/yaml_serialization_test.rb15
-rw-r--r--activerecord/test/schema/schema.rb1
-rw-r--r--activesupport/CHANGELOG.md3
-rw-r--r--activesupport/lib/active_support/cache/file_store.rb5
-rw-r--r--activesupport/lib/active_support/hash_with_indifferent_access.rb2
-rw-r--r--activesupport/lib/active_support/number_helper.rb15
-rw-r--r--activesupport/lib/active_support/testing/declarative.rb24
-rw-r--r--activesupport/test/caching_test.rb5
-rw-r--r--activesupport/test/core_ext/hash_ext_test.rb8
-rw-r--r--activesupport/test/core_ext/object/blank_test.rb (renamed from activesupport/test/core_ext/blank_test.rb)0
-rw-r--r--activesupport/test/core_ext/object/inclusion_test.rb4
-rw-r--r--guides/source/action_mailer_basics.md29
-rw-r--r--guides/source/active_record_migrations.md (renamed from guides/source/migrations.md)30
-rw-r--r--guides/source/asset_pipeline.md17
-rw-r--r--guides/source/documents.yaml4
-rw-r--r--guides/source/getting_started.md7
-rw-r--r--railties/test/rails_info_test.rb10
92 files changed, 579 insertions, 590 deletions
diff --git a/actionmailer/lib/action_mailer/base.rb b/actionmailer/lib/action_mailer/base.rb
index 5852aeaec2..18511edaf0 100644
--- a/actionmailer/lib/action_mailer/base.rb
+++ b/actionmailer/lib/action_mailer/base.rb
@@ -369,8 +369,8 @@ module ActionMailer
# This is a symbol and one of <tt>:plain</tt> (will send the password in the clear), <tt>:login</tt> (will
# send password Base64 encoded) or <tt>:cram_md5</tt> (combines a Challenge/Response mechanism to exchange
# information and a cryptographic Message Digest 5 algorithm to hash important information)
- # * <tt>:enable_starttls_auto</tt> - When set to true, detects if STARTTLS is enabled in your SMTP server
- # and starts to use it.
+ # * <tt>:enable_starttls_auto</tt> - Detects if STARTTLS is enabled in your SMTP server and starts
+ # to use it. Defaults to <tt>true</tt>.
# * <tt>:openssl_verify_mode</tt> - When using TLS, you can set how OpenSSL checks the certificate. This is
# really useful if you need to validate a self-signed and/or a wildcard certificate. You can use the name
# of an OpenSSL verify constant (<tt>'none'</tt>, <tt>'peer'</tt>, <tt>'client_once'</tt>,
diff --git a/actionpack/lib/action_dispatch/http/url.rb b/actionpack/lib/action_dispatch/http/url.rb
index 4cba4f5f37..92aba01b95 100644
--- a/actionpack/lib/action_dispatch/http/url.rb
+++ b/actionpack/lib/action_dispatch/http/url.rb
@@ -5,18 +5,18 @@ module ActionDispatch
module Http
module URL
IP_HOST_REGEXP = /\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$/
- HOST_REGEXP = /(^.*:\/\/)?([^:]+)(?::(\d+$))?/
+ HOST_REGEXP = /(^[^:]+:\/\/)?([^:]+)(?::(\d+$))?/
PROTOCOL_REGEXP = /^([^:]+)(:)?(\/\/)?$/
mattr_accessor :tld_length
self.tld_length = 1
class << self
- def extract_domain(host, tld_length = @@tld_length)
+ def extract_domain(host, tld_length)
host.split('.').last(1 + tld_length).join('.') if named_host?(host)
end
- def extract_subdomains(host, tld_length = @@tld_length)
+ def extract_subdomains(host, tld_length)
if named_host?(host)
parts = host.split('.')
parts[0..-(tld_length + 2)]
@@ -25,7 +25,7 @@ module ActionDispatch
end
end
- def extract_subdomain(host, tld_length = @@tld_length)
+ def extract_subdomain(host, tld_length)
extract_subdomains(host, tld_length).join('.')
end
@@ -73,38 +73,38 @@ module ActionDispatch
end
def build_host_url(options)
- if match = options[:host].match(HOST_REGEXP)
- options[:protocol] ||= match[1] unless options[:protocol] == false
- options[:host] = match[2]
- options[:port] = match[3] unless options.key?(:port)
+ protocol = options[:protocol]
+ host = options[:host]
+ port = options[:port]
+ if match = host.match(HOST_REGEXP)
+ protocol ||= match[1] unless protocol == false
+ host = match[2]
+ port = match[3] unless options.key? :port
end
- options[:protocol] = normalize_protocol(options)
- options[:host] = normalize_host(options)
- options[:port] = normalize_port(options)
+ protocol = normalize_protocol protocol
+ host = normalize_host(host, options)
- result = options[:protocol]
+ result = protocol.dup
if options[:user] && options[:password]
result << "#{Rack::Utils.escape(options[:user])}:#{Rack::Utils.escape(options[:password])}@"
end
- result << options[:host]
- result << ":#{options[:port]}" if options[:port]
+ result << host
+ normalize_port(port, protocol) { |normalized_port|
+ result << ":#{normalized_port}"
+ }
result
end
def named_host?(host)
- host && IP_HOST_REGEXP !~ host
+ IP_HOST_REGEXP !~ host
end
- def same_host?(options)
- (options[:subdomain] == true || !options.key?(:subdomain)) && options[:domain].nil?
- end
-
- def normalize_protocol(options)
- case options[:protocol]
+ def normalize_protocol(protocol)
+ case protocol
when nil
"http://"
when false, "//"
@@ -112,36 +112,38 @@ module ActionDispatch
when PROTOCOL_REGEXP
"#{$1}://"
else
- raise ArgumentError, "Invalid :protocol option: #{options[:protocol].inspect}"
+ raise ArgumentError, "Invalid :protocol option: #{protocol.inspect}"
end
end
- def normalize_host(options)
- return options[:host] if !named_host?(options[:host]) || same_host?(options)
+ def normalize_host(_host, options)
+ return _host unless named_host?(_host)
tld_length = options[:tld_length] || @@tld_length
host = ""
- if options[:subdomain] == true || !options.key?(:subdomain)
- host << extract_subdomain(options[:host], tld_length).to_param
- elsif options[:subdomain].present?
- host << options[:subdomain].to_param
+ subdomain = options[:subdomain]
+ if subdomain == true || !options.key?(:subdomain)
+ return _host if options[:domain].nil?
+
+ host << extract_subdomain(_host, tld_length)
+ elsif subdomain
+ host << subdomain.to_param
end
host << "." unless host.empty?
- host << (options[:domain] || extract_domain(options[:host], tld_length))
+ host << (options[:domain] || extract_domain(_host, tld_length))
host
end
- def normalize_port(options)
- return nil if options[:port].nil? || options[:port] == false
+ def normalize_port(port, protocol)
+ return unless port
- case options[:protocol]
- when "//"
- options[:port]
+ case protocol
+ when "//" then yield port
when "https://"
- options[:port].to_i == 443 ? nil : options[:port]
+ yield port unless port.to_i == 443
else
- options[:port].to_i == 80 ? nil : options[:port]
+ yield port unless port.to_i == 80
end
end
end
diff --git a/actionpack/test/controller/routing_test.rb b/actionpack/test/controller/routing_test.rb
index 660589a86e..721dad4dd9 100644
--- a/actionpack/test/controller/routing_test.rb
+++ b/actionpack/test/controller/routing_test.rb
@@ -256,7 +256,6 @@ class LegacyRouteSetTests < ActiveSupport::TestCase
end
def test_scoped_lambda_with_get_lambda
- scope_called = false
inner_called = false
rs.draw do
diff --git a/actionpack/test/journey/router_test.rb b/actionpack/test/journey/router_test.rb
index e092432b01..2e7e8e1bea 100644
--- a/actionpack/test/journey/router_test.rb
+++ b/actionpack/test/journey/router_test.rb
@@ -213,8 +213,6 @@ module ActionDispatch
route_set = Routing::RouteSet.new
mapper = Routing::Mapper.new route_set
- strexp = Router::Strexp.build("/", {}, ['/', '.', '?'], false)
- path = Path::Pattern.new strexp
app = lambda { |env| [200, {}, ['success!']] }
mapper.get '/weblog', :to => app
diff --git a/actionview/CHANGELOG.md b/actionview/CHANGELOG.md
index 105ebb0984..d13cc0c7a7 100644
--- a/actionview/CHANGELOG.md
+++ b/actionview/CHANGELOG.md
@@ -1,9 +1,13 @@
* Add `ActionView::Template::Handler.unregister_template_handler`.
-
+
It performs the opposite of `ActionView::Template::Handler.register_template_handler`.
-
+
*Zuhao Wan*
+* Bring `cache_digest` rake tasks up-to-date with the latest API changes
+
+ *Jiri Pospisil*
+
* Allow custom `:host` option to be passed to `asset_url` helper that
overwrites `config.action_controller.asset_host` for particular asset.
diff --git a/actionview/lib/action_view/helpers/javascript_helper.rb b/actionview/lib/action_view/helpers/javascript_helper.rb
index e475d5b018..629c447f3f 100644
--- a/actionview/lib/action_view/helpers/javascript_helper.rb
+++ b/actionview/lib/action_view/helpers/javascript_helper.rb
@@ -47,7 +47,13 @@ module ActionView
# tag.
#
# javascript_tag "alert('All is good')", defer: 'defer'
- # # => <script defer="defer">alert('All is good')</script>
+ #
+ # Returns:
+ # <script defer="defer">
+ # //<![CDATA[
+ # alert('All is good')
+ # //]]>
+ # </script>
#
# Instead of passing the content as an argument, you can also use a block
# in which case, you pass your +html_options+ as the first parameter.
diff --git a/actionview/lib/action_view/helpers/number_helper.rb b/actionview/lib/action_view/helpers/number_helper.rb
index 7157a95146..7220bded3c 100644
--- a/actionview/lib/action_view/helpers/number_helper.rb
+++ b/actionview/lib/action_view/helpers/number_helper.rb
@@ -266,14 +266,8 @@ module ActionView
# number_to_human_size(1234567, precision: 2) # => 1.2 MB
# number_to_human_size(483989, precision: 2) # => 470 KB
# number_to_human_size(1234567, precision: 2, separator: ',') # => 1,2 MB
- #
- # Non-significant zeros after the fractional separator are
- # stripped out by default (set
- # <tt>:strip_insignificant_zeros</tt> to +false+ to change
- # that):
- #
- # number_to_human_size(1234567890123, precision: 5) # => "1.1229 TB"
- # number_to_human_size(524288000, precision: 5) # => "500 MB"
+ # number_to_human_size(1234567890123, precision: 5) # => "1.1228 TB"
+ # number_to_human_size(524288000, precision: 5) # => "500 MB"
def number_to_human_size(number, options = {})
delegate_number_helper_method(:number_to_human_size, number, options)
end
@@ -343,11 +337,15 @@ module ActionView
# separator: ',',
# significant: false) # => "1,2 Million"
#
+ # number_to_human(500000000, precision: 5) # => "500 Million"
+ # number_to_human(12345012345, significant: false) # => "12.345 Billion"
+ #
# Non-significant zeros after the decimal separator are stripped
# out by default (set <tt>:strip_insignificant_zeros</tt> to
# +false+ to change that):
- # number_to_human(12345012345, significant_digits: 6) # => "12.345 Billion"
- # number_to_human(500000000, precision: 5) # => "500 Million"
+ #
+ # number_to_human(12.00001) # => "12"
+ # number_to_human(12.00001, strip_insignificant_zeros: false) # => "12.0"
#
# ==== Custom Unit Quantifiers
#
diff --git a/actionview/lib/action_view/tasks/dependencies.rake b/actionview/lib/action_view/tasks/dependencies.rake
index 1b9426c0e5..b39f7d583b 100644
--- a/actionview/lib/action_view/tasks/dependencies.rake
+++ b/actionview/lib/action_view/tasks/dependencies.rake
@@ -2,16 +2,20 @@ namespace :cache_digests do
desc 'Lookup nested dependencies for TEMPLATE (like messages/show or comments/_comment.html)'
task :nested_dependencies => :environment do
abort 'You must provide TEMPLATE for the task to run' unless ENV['TEMPLATE'].present?
- template, format = ENV['TEMPLATE'].split(".")
- format ||= :html
- puts JSON.pretty_generate ActionView::Digestor.new(template, format, ApplicationController.new.lookup_context).nested_dependencies
+ puts JSON.pretty_generate ActionView::Digestor.new(name: template_name, finder: finder).nested_dependencies
end
desc 'Lookup first-level dependencies for TEMPLATE (like messages/show or comments/_comment.html)'
task :dependencies => :environment do
abort 'You must provide TEMPLATE for the task to run' unless ENV['TEMPLATE'].present?
- template, format = ENV['TEMPLATE'].split(".")
- format ||= :html
- puts JSON.pretty_generate ActionView::Digestor.new(template, format, ApplicationController.new.lookup_context).dependencies
+ puts JSON.pretty_generate ActionView::Digestor.new(name: template_name, finder: finder).dependencies
+ end
+
+ def template_name
+ ENV['TEMPLATE'].split('.', 2).first
+ end
+
+ def finder
+ ApplicationController.new.lookup_context
end
end
diff --git a/actionview/test/actionpack/controller/layout_test.rb b/actionview/test/actionpack/controller/layout_test.rb
index 70d81c0cad..bd345fe873 100644
--- a/actionview/test/actionpack/controller/layout_test.rb
+++ b/actionview/test/actionpack/controller/layout_test.rb
@@ -1,7 +1,6 @@
require 'abstract_unit'
require 'rbconfig'
require 'active_support/core_ext/array/extract_options'
-require 'support/template_handler_helper'
# The view_paths array must be set on Base and not LayoutTest so that LayoutTest's inherited
# method has access to the view_paths array when looking for a layout to automatically assign.
@@ -15,6 +14,15 @@ class LayoutTest < ActionController::Base
self.view_paths = ActionController::Base.view_paths.dup
end
+module TemplateHandlerHelper
+ def with_template_handler(*extensions, handler)
+ ActionView::Template.register_template_handler(*extensions, handler)
+ yield
+ ensure
+ ActionView::Template.unregister_template_handler(*extensions)
+ end
+end
+
# Restore view_paths to previous value
ActionController::Base.view_paths = old_load_paths
diff --git a/actionview/test/support/template_handler_helper.rb b/actionview/test/support/template_handler_helper.rb
deleted file mode 100644
index 78d9c7cf20..0000000000
--- a/actionview/test/support/template_handler_helper.rb
+++ /dev/null
@@ -1,8 +0,0 @@
-module TemplateHandlerHelper
- def with_template_handler(*extensions, handler)
- ActionView::Template.register_template_handler(*extensions, handler)
- yield
- ensure
- ActionView::Template.unregister_template_handler(*extensions)
- end
-end
diff --git a/activemodel/CHANGELOG.md b/activemodel/CHANGELOG.md
index b94558b65c..29cd4cf934 100644
--- a/activemodel/CHANGELOG.md
+++ b/activemodel/CHANGELOG.md
@@ -1,3 +1,9 @@
+* Remove deprecated `Validator#setup` without replacement.
+
+ See #10716.
+
+ *Kuldeep Aggarwal*
+
* Add plural and singular form for length validator's default messages.
*Abd ar-Rahman Hamid*
diff --git a/activemodel/lib/active_model/conversion.rb b/activemodel/lib/active_model/conversion.rb
index 374265f0d8..9c9b6f4a77 100644
--- a/activemodel/lib/active_model/conversion.rb
+++ b/activemodel/lib/active_model/conversion.rb
@@ -40,13 +40,15 @@ module ActiveModel
self
end
- # Returns an Enumerable of all key attributes if any is set, regardless if
+ # Returns an Array of all key attributes if any is set, regardless if
# the object is persisted or not. Returns +nil+ if there are no key attributes.
#
- # class Person < ActiveRecord::Base
+ # class Person
+ # include ActiveModel::Conversion
+ # attr_accessor :id
# end
#
- # person = Person.create
+ # person = Person.create(id: 1)
# person.to_key # => [1]
def to_key
key = respond_to?(:id) && id
@@ -56,10 +58,15 @@ module ActiveModel
# Returns a +string+ representing the object's key suitable for use in URLs,
# or +nil+ if <tt>persisted?</tt> is +false+.
#
- # class Person < ActiveRecord::Base
+ # class Person
+ # include ActiveModel::Conversion
+ # attr_accessor :id
+ # def persisted?
+ # true
+ # end
# end
#
- # person = Person.create
+ # person = Person.create(id: 1)
# person.to_param # => "1"
def to_param
(persisted? && key = to_key) ? key.join('-') : nil
diff --git a/activemodel/lib/active_model/validator.rb b/activemodel/lib/active_model/validator.rb
index bddacc8c45..65cb1e5a88 100644
--- a/activemodel/lib/active_model/validator.rb
+++ b/activemodel/lib/active_model/validator.rb
@@ -106,7 +106,6 @@ module ActiveModel
# Accepts options that will be made available through the +options+ reader.
def initialize(options = {})
@options = options.except(:class).freeze
- deprecated_setup(options)
end
# Returns the kind for this validator.
@@ -122,21 +121,6 @@ module ActiveModel
def validate(record)
raise NotImplementedError, "Subclasses must implement a validate(record) method."
end
-
- private
- def deprecated_setup(options) # TODO: remove me in 4.2.
- return unless respond_to?(:setup)
- ActiveSupport::Deprecation.warn "The `Validator#setup` instance method is deprecated and will be removed on Rails 4.2. Do your setup in the constructor instead:
-
-class MyValidator < ActiveModel::Validator
- def initialize(options={})
- super
- options[:class].send :attr_accessor, :custom_attribute
- end
-end
-"
- setup(options[:class])
- end
end
# +EachValidator+ is a validator which iterates through the attributes given
diff --git a/activemodel/test/cases/conversion_test.rb b/activemodel/test/cases/conversion_test.rb
index c5cfbf909d..800cad6d9a 100644
--- a/activemodel/test/cases/conversion_test.rb
+++ b/activemodel/test/cases/conversion_test.rb
@@ -24,6 +24,10 @@ class ConversionTest < ActiveModel::TestCase
assert_equal "1", Contact.new(id: 1).to_param
end
+ test "to_param returns the string joined by '-'" do
+ assert_equal "abc-xyz", Contact.new(id: ["abc", "xyz"]).to_param
+ end
+
test "to_param returns nil if to_key is nil" do
klass = Class.new(Contact) do
def persisted?
diff --git a/activemodel/test/cases/validations_test.rb b/activemodel/test/cases/validations_test.rb
index 6a74ee353d..4fee704ef5 100644
--- a/activemodel/test/cases/validations_test.rb
+++ b/activemodel/test/cases/validations_test.rb
@@ -378,25 +378,4 @@ class ValidationsTest < ActiveModel::TestCase
assert topic.invalid?
assert duped.valid?
end
-
- # validator test:
- def test_setup_is_deprecated_but_still_receives_klass # TODO: remove me in 4.2.
- validator_class = Class.new(ActiveModel::Validator) do
- def setup(klass)
- @old_klass = klass
- end
-
- def validate(*)
- @old_klass == Topic or raise "#setup didn't work"
- end
- end
-
- assert_deprecated do
- Topic.validates_with validator_class
- end
-
- t = Topic.new
- t.valid?
- end
-
end
diff --git a/activerecord/CHANGELOG.md b/activerecord/CHANGELOG.md
index e7e414571d..e0a7552921 100644
--- a/activerecord/CHANGELOG.md
+++ b/activerecord/CHANGELOG.md
@@ -1,3 +1,25 @@
+* Pluck now works when selecting columns from different tables with the same
+ name.
+
+ Fixes #15649
+
+ *Sean Griffin*
+
+* Remove `cache_attributes` and friends. All attributes are cached.
+
+ *Sean Griffin*
+
+* Remove deprecated method `ActiveRecord::Base.quoted_locking_column`.
+
+ *Akshay Vishnoi*
+
+* `ActiveRecord::FinderMethods.find` with block can handle proc parameter as
+ `Enumerable#find` does.
+
+ Fixes #15382.
+
+ *James Yang*
+
* Make timezone aware attributes work with PostgreSQL array columns.
Fixes #13402.
@@ -532,10 +554,6 @@
*Luke Steensen*
-* Make possible to change `record_timestamps` inside Callbacks.
-
- *Tieg Zaharia*
-
* Fixed error where .persisted? throws SystemStackError for an unsaved model with a
custom primary key that didn't save due to validation error.
diff --git a/activerecord/lib/active_record/associations.rb b/activerecord/lib/active_record/associations.rb
index 07dfc448e7..6222bfe903 100644
--- a/activerecord/lib/active_record/associations.rb
+++ b/activerecord/lib/active_record/associations.rb
@@ -1577,7 +1577,7 @@ module ActiveRecord
scope = nil
end
- habtm_reflection = ActiveRecord::Reflection::AssociationReflection.new(:has_and_belongs_to_many, name, scope, options, self)
+ habtm_reflection = ActiveRecord::Reflection::HasAndBelongsToManyReflection.new(:has_and_belongs_to_many, name, scope, options, self)
builder = Builder::HasAndBelongsToMany.new name, self, options
diff --git a/activerecord/lib/active_record/associations/association.rb b/activerecord/lib/active_record/associations/association.rb
index 4a04303fb8..f1c36cd047 100644
--- a/activerecord/lib/active_record/associations/association.rb
+++ b/activerecord/lib/active_record/associations/association.rb
@@ -179,7 +179,7 @@ module ActiveRecord
def creation_attributes
attributes = {}
- if (reflection.macro == :has_one || reflection.macro == :has_many) && !options[:through]
+ if (reflection.has_one? || reflection.collection?) && !options[:through]
attributes[reflection.foreign_key] = owner[reflection.active_record_primary_key]
if reflection.options[:as]
diff --git a/activerecord/lib/active_record/associations/association_scope.rb b/activerecord/lib/active_record/associations/association_scope.rb
index 31108cc1aa..519d4d8651 100644
--- a/activerecord/lib/active_record/associations/association_scope.rb
+++ b/activerecord/lib/active_record/associations/association_scope.rb
@@ -105,18 +105,9 @@ module ActiveRecord
chain.each_with_index do |reflection, i|
table, foreign_table = tables.shift, tables.first
- if reflection.source_macro == :belongs_to
- if reflection.polymorphic?
- key = reflection.association_primary_key(assoc_klass)
- else
- key = reflection.association_primary_key
- end
-
- foreign_key = reflection.foreign_key
- else
- key = reflection.foreign_key
- foreign_key = reflection.active_record_primary_key
- end
+ join_keys = reflection.join_keys(assoc_klass)
+ key = join_keys.key
+ foreign_key = join_keys.foreign_key
if reflection == chain.last
bind_val = bind scope, table.table_name, key.to_s, owner[foreign_key], tracker
diff --git a/activerecord/lib/active_record/associations/belongs_to_association.rb b/activerecord/lib/active_record/associations/belongs_to_association.rb
index 1edd4fa3aa..81fdd681de 100644
--- a/activerecord/lib/active_record/associations/belongs_to_association.rb
+++ b/activerecord/lib/active_record/associations/belongs_to_association.rb
@@ -92,7 +92,7 @@ module ActiveRecord
# has_one associations.
def invertible_for?(record)
inverse = inverse_reflection_for(record)
- inverse && inverse.macro == :has_one
+ inverse && inverse.has_one?
end
def target_id
diff --git a/activerecord/lib/active_record/associations/builder/has_one.rb b/activerecord/lib/active_record/associations/builder/has_one.rb
index f359efd496..a1f4f51664 100644
--- a/activerecord/lib/active_record/associations/builder/has_one.rb
+++ b/activerecord/lib/active_record/associations/builder/has_one.rb
@@ -5,7 +5,7 @@ module ActiveRecord::Associations::Builder
end
def valid_options
- valid = super + [:order, :as]
+ valid = super + [:as]
valid += [:through, :source, :source_type] if options[:through]
valid
end
diff --git a/activerecord/lib/active_record/associations/collection_association.rb b/activerecord/lib/active_record/associations/collection_association.rb
index c5f7bcae7d..306588ac66 100644
--- a/activerecord/lib/active_record/associations/collection_association.rb
+++ b/activerecord/lib/active_record/associations/collection_association.rb
@@ -57,7 +57,7 @@ module ActiveRecord
def ids_writer(ids)
pk_column = reflection.primary_key_column
ids = Array(ids).reject { |id| id.blank? }
- ids.map! { |i| pk_column.type_cast(i) }
+ ids.map! { |i| pk_column.type_cast_from_user(i) }
replace(klass.find(ids).index_by { |r| r.id }.values_at(*ids))
end
diff --git a/activerecord/lib/active_record/associations/has_many_through_association.rb b/activerecord/lib/active_record/associations/has_many_through_association.rb
index 954128064d..da9b125fd6 100644
--- a/activerecord/lib/active_record/associations/has_many_through_association.rb
+++ b/activerecord/lib/active_record/associations/has_many_through_association.rb
@@ -89,7 +89,7 @@ module ActiveRecord
end
def through_scope_attributes
- scope.where_values_hash(through_association.reflection.name.to_s)
+ scope.where_values_hash(through_association.reflection.name.to_s).except!(through_association.reflection.foreign_key)
end
def save_through_record(record)
@@ -105,9 +105,9 @@ module ActiveRecord
inverse = source_reflection.inverse_of
if inverse
- if inverse.macro == :has_many
+ if inverse.collection?
record.send(inverse.name) << build_through_record(record)
- elsif inverse.macro == :has_one
+ elsif inverse.has_one?
record.send("#{inverse.name}=", build_through_record(record))
end
end
@@ -170,7 +170,7 @@ module ActiveRecord
klass.decrement_counter counter, records.map(&:id)
end
- if through_reflection.macro == :has_many && update_through_counter?(method)
+ if through_reflection.collection? && update_through_counter?(method)
update_counter(-count, through_reflection)
end
@@ -187,7 +187,7 @@ module ActiveRecord
records.each do |record|
through_records = through_records_for(record)
- if through_reflection.macro == :has_many
+ if through_reflection.collection?
through_records.each { |r| through_association.target.delete(r) }
else
if through_records.include?(through_association.target)
diff --git a/activerecord/lib/active_record/associations/join_dependency.rb b/activerecord/lib/active_record/associations/join_dependency.rb
index 35659766d3..fbb4551b22 100644
--- a/activerecord/lib/active_record/associations/join_dependency.rb
+++ b/activerecord/lib/active_record/associations/join_dependency.rb
@@ -144,7 +144,7 @@ module ActiveRecord
column_aliases = aliases.column_aliases join_root
result_set.each { |row_hash|
- primary_id = type_caster.type_cast row_hash[primary_key]
+ primary_id = type_caster.type_cast_from_database row_hash[primary_key]
parent = parents[primary_id] ||= join_root.instantiate(row_hash, column_aliases)
construct(parent, join_root, row_hash, result_set, seen, model_cache, aliases)
}
diff --git a/activerecord/lib/active_record/attribute_methods/read.rb b/activerecord/lib/active_record/attribute_methods/read.rb
index ae3785638a..7b7e37884b 100644
--- a/activerecord/lib/active_record/attribute_methods/read.rb
+++ b/activerecord/lib/active_record/attribute_methods/read.rb
@@ -35,35 +35,22 @@ module ActiveRecord
extend ActiveSupport::Concern
- ATTRIBUTE_TYPES_CACHED_BY_DEFAULT = [:datetime, :time, :date]
-
- included do
- class_attribute :attribute_types_cached_by_default, instance_writer: false
- self.attribute_types_cached_by_default = ATTRIBUTE_TYPES_CACHED_BY_DEFAULT
- end
-
module ClassMethods
- # +cache_attributes+ allows you to declare which converted attribute
- # values should be cached. Usually caching only pays off for attributes
- # with expensive conversion methods, like time related columns (e.g.
- # +created_at+, +updated_at+).
- def cache_attributes(*attribute_names)
- cached_attributes.merge attribute_names.map { |attr| attr.to_s }
+ [:cache_attributes, :cached_attributes, :cache_attribute?].each do |method_name|
+ define_method method_name do |*|
+ cached_attributes_deprecation_warning(method_name)
+ true
+ end
end
- # Returns the attributes which are cached. By default time related columns
- # with datatype <tt>:datetime, :time, :date</tt> are cached.
- def cached_attributes
- @cached_attributes ||= columns.select { |c| cacheable_column?(c) }.map { |col| col.name }.to_set
- end
+ protected
- # Returns +true+ if the provided attribute is being cached.
- def cache_attribute?(attr_name)
- cached_attributes.include?(attr_name)
+ def cached_attributes_deprecation_warning(method_name)
+ ActiveSupport::Deprecation.warn(<<-MESSAGE.strip_heredoc)
+ Calling `#{method_name}` is no longer necessary. All attributes are cached.
+ MESSAGE
end
- protected
-
if Module.methods_transplantable?
def define_method_attribute(name)
method = ReaderMethodCache[name]
@@ -89,16 +76,6 @@ module ActiveRecord
end
end
end
-
- private
-
- def cacheable_column?(column)
- if attribute_types_cached_by_default == ATTRIBUTE_TYPES_CACHED_BY_DEFAULT
- true
- else
- attribute_types_cached_by_default.include?(column.type)
- end
- end
end
# Returns the value of the attribute identified by <tt>attr_name</tt> after
@@ -122,11 +99,7 @@ module ActiveRecord
return block_given? ? yield(name) : nil
}
- if self.class.cache_attribute?(name)
- @attributes[name] = column.type_cast(value)
- else
- column.type_cast value
- end
+ @attributes[name] = column.type_cast_from_database(value)
}
end
diff --git a/activerecord/lib/active_record/attribute_methods/time_zone_conversion.rb b/activerecord/lib/active_record/attribute_methods/time_zone_conversion.rb
index c1c3987cf5..abad949ef4 100644
--- a/activerecord/lib/active_record/attribute_methods/time_zone_conversion.rb
+++ b/activerecord/lib/active_record/attribute_methods/time_zone_conversion.rb
@@ -1,21 +1,22 @@
module ActiveRecord
module AttributeMethods
module TimeZoneConversion
- class Type # :nodoc:
- delegate :type, :type_cast_for_database, to: :@column
-
- def initialize(column)
- @column = column
+ class Type < SimpleDelegator # :nodoc:
+ def type_cast_from_database(value)
+ convert_time_to_time_zone(super)
end
- def type_cast(value)
- value = @column.type_cast(value)
- convert_value_to_time_zone(value)
+ def type_cast_from_user(value)
+ if value.is_a?(Array)
+ value.map { |v| type_cast_from_user(v) }
+ elsif value.respond_to?(:in_time_zone)
+ value.in_time_zone
+ end
end
- def convert_value_to_time_zone(value)
+ def convert_time_to_time_zone(value)
if value.is_a?(Array)
- value.map { |v| convert_value_to_time_zone(v) }
+ value.map { |v| convert_time_to_time_zone(v) }
elsif value.acts_like?(:time)
value.in_time_zone
else
@@ -35,26 +36,6 @@ module ActiveRecord
end
module ClassMethods
- protected
- # Defined for all +datetime+ attributes when +time_zone_aware_attributes+ are enabled.
- # This enhanced write method will automatically convert the time passed to it to the zone stored in Time.zone.
- def define_method_attribute=(attr_name)
- if create_time_zone_conversion_attribute?(attr_name, columns_hash[attr_name])
- method_body, line = <<-EOV, __LINE__ + 1
- def #{attr_name}=(time)
- time_with_zone = convert_value_to_time_zone(time)
- previous_time = attribute_changed?("#{attr_name}") ? changed_attributes["#{attr_name}"] : read_attribute(:#{attr_name})
- write_attribute(:#{attr_name}, time)
- #{attr_name}_will_change! if previous_time != time_with_zone
- @attributes["#{attr_name}"] = time_with_zone
- end
- EOV
- generated_attribute_methods.module_eval(method_body, __FILE__, line)
- else
- super
- end
- end
-
private
def create_time_zone_conversion_attribute?(name, column)
time_zone_aware_attributes &&
@@ -62,16 +43,6 @@ module ActiveRecord
(:datetime == column.type)
end
end
-
- private
-
- def convert_value_to_time_zone(value)
- if value.is_a?(Array)
- value.map { |v| convert_value_to_time_zone(v) }
- elsif value.respond_to?(:in_time_zone)
- value.in_time_zone
- end
- end
end
end
end
diff --git a/activerecord/lib/active_record/attribute_methods/write.rb b/activerecord/lib/active_record/attribute_methods/write.rb
index 5203b30462..b72a6219b0 100644
--- a/activerecord/lib/active_record/attribute_methods/write.rb
+++ b/activerecord/lib/active_record/attribute_methods/write.rb
@@ -70,7 +70,7 @@ module ActiveRecord
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 = column_for_attribute(attr_name)
+ column = 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}'"
@@ -80,13 +80,11 @@ module ActiveRecord
# 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)
end
- if should_type_cast
- @raw_attributes[attr_name] = column.type_cast_for_write(value)
- else
- @raw_attributes[attr_name] = value
- end
+ @raw_attributes[attr_name] = value
end
end
end
diff --git a/activerecord/lib/active_record/autosave_association.rb b/activerecord/lib/active_record/autosave_association.rb
index 74e2a8e6b9..b3c3e26c9f 100644
--- a/activerecord/lib/active_record/autosave_association.rb
+++ b/activerecord/lib/active_record/autosave_association.rb
@@ -187,7 +187,7 @@ module ActiveRecord
# Doesn't use after_save as that would save associations added in after_create/after_update twice
after_create save_method
after_update save_method
- elsif reflection.macro == :has_one
+ elsif reflection.has_one?
define_method(save_method) { save_has_one_association(reflection) } unless method_defined?(save_method)
# Configures two callbacks instead of a single after_save so that
# the model may rely on their execution order relative to its
diff --git a/activerecord/lib/active_record/base.rb b/activerecord/lib/active_record/base.rb
index e8c067f758..e4d0abb8ef 100644
--- a/activerecord/lib/active_record/base.rb
+++ b/activerecord/lib/active_record/base.rb
@@ -311,8 +311,8 @@ module ActiveRecord #:nodoc:
include Locking::Optimistic
include Locking::Pessimistic
include AttributeMethods
- include Timestamp
include Callbacks
+ include Timestamp
include Associations
include ActiveModel::SecurePassword
include AutosaveAssociation
diff --git a/activerecord/lib/active_record/connection_adapters/abstract/schema_dumper.rb b/activerecord/lib/active_record/connection_adapters/abstract/schema_dumper.rb
index d3e172927d..5a0efe49c7 100644
--- a/activerecord/lib/active_record/connection_adapters/abstract/schema_dumper.rb
+++ b/activerecord/lib/active_record/connection_adapters/abstract/schema_dumper.rb
@@ -1,5 +1,3 @@
-require 'ipaddr'
-
module ActiveRecord
module ConnectionAdapters # :nodoc:
# The goal of this module is to move Adapter specific column
diff --git a/activerecord/lib/active_record/connection_adapters/column.rb b/activerecord/lib/active_record/connection_adapters/column.rb
index 23434df1fe..72c6990ba5 100644
--- a/activerecord/lib/active_record/connection_adapters/column.rb
+++ b/activerecord/lib/active_record/connection_adapters/column.rb
@@ -17,7 +17,7 @@ module ActiveRecord
delegate :type, :precision, :scale, :limit, :klass, :accessor,
:text?, :number?, :binary?, :serialized?, :changed?,
- :type_cast, :type_cast_for_write, :type_cast_for_database,
+ :type_cast_from_user, :type_cast_from_database, :type_cast_for_database,
:type_cast_for_schema,
to: :cast_type
@@ -52,7 +52,7 @@ module ActiveRecord
end
def default
- @default ||= type_cast(@original_default)
+ @default ||= type_cast_from_database(@original_default)
end
def with_type(type)
diff --git a/activerecord/lib/active_record/connection_adapters/postgresql/cast.rb b/activerecord/lib/active_record/connection_adapters/postgresql/cast.rb
index 971f5eed7e..bb54de05c8 100644
--- a/activerecord/lib/active_record/connection_adapters/postgresql/cast.rb
+++ b/activerecord/lib/active_record/connection_adapters/postgresql/cast.rb
@@ -67,10 +67,6 @@ module ActiveRecord
end
end
- def string_to_array(string, oid) # :nodoc:
- parse_pg_array(string).map {|val| type_cast_array(oid, val)}
- end
-
private
HstorePair = begin
@@ -103,14 +99,6 @@ module ActiveRecord
"\"#{value}\""
end
end
-
- def type_cast_array(oid, value)
- if ::Array === value
- value.map {|item| type_cast_array(oid, item)}
- else
- oid.type_cast value
- end
- end
end
end
end
diff --git a/activerecord/lib/active_record/connection_adapters/postgresql/column.rb b/activerecord/lib/active_record/connection_adapters/postgresql/column.rb
index a579746815..847fd4dded 100644
--- a/activerecord/lib/active_record/connection_adapters/postgresql/column.rb
+++ b/activerecord/lib/active_record/connection_adapters/postgresql/column.rb
@@ -4,6 +4,8 @@ module ActiveRecord
module ConnectionAdapters
# PostgreSQL-specific extensions to column definitions in a table.
class PostgreSQLColumn < Column #:nodoc:
+ extend PostgreSQL::Cast
+
attr_accessor :array
def initialize(name, default, cast_type, sql_type = nil, null = true, default_function = nil)
@@ -17,24 +19,6 @@ module ActiveRecord
@default_function = default_function
end
-
- # :stopdoc:
- class << self
- include PostgreSQL::Cast
-
- # 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
- # collected. pg_array_parser has a C and Java extension
- begin
- require 'pg_array_parser'
- include PgArrayParser
- rescue LoadError
- require 'active_record/connection_adapters/postgresql/array_parser'
- include PostgreSQL::ArrayParser
- end
- end
- # :startdoc:
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 0e9dcd8c0c..87817fe8d1 100644
--- a/activerecord/lib/active_record/connection_adapters/postgresql/oid/array.rb
+++ b/activerecord/lib/active_record/connection_adapters/postgresql/oid/array.rb
@@ -10,11 +10,33 @@ module ActiveRecord
@subtype = subtype
end
- def type_cast(value)
- if ::String === value
- ConnectionAdapters::PostgreSQLColumn.string_to_array value, @subtype
+ def type_cast_from_database(value)
+ if value.is_a?(::String)
+ type_cast_array(parse_pg_array(value))
else
- value
+ super
+ end
+ 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
+ # collected. pg_array_parser has a C and Java extension
+ begin
+ require 'pg_array_parser'
+ include PgArrayParser
+ rescue LoadError
+ require 'active_record/connection_adapters/postgresql/array_parser'
+ include PostgreSQL::ArrayParser
+ end
+
+ private
+
+ def type_cast_array(value)
+ if value.is_a?(::Array)
+ value.map { |item| type_cast_array(item) }
+ else
+ @subtype.type_cast_from_database(value)
end
end
end
diff --git a/activerecord/lib/active_record/connection_adapters/postgresql/oid/hstore.rb b/activerecord/lib/active_record/connection_adapters/postgresql/oid/hstore.rb
index a65ca83f77..88de816d4f 100644
--- a/activerecord/lib/active_record/connection_adapters/postgresql/oid/hstore.rb
+++ b/activerecord/lib/active_record/connection_adapters/postgresql/oid/hstore.rb
@@ -7,12 +7,16 @@ module ActiveRecord
:hstore
end
- def type_cast_for_write(value)
- ConnectionAdapters::PostgreSQLColumn.hstore_to_string(value)
+ def type_cast_from_user(value)
+ type_cast_from_database(type_cast_for_database(value))
+ end
+
+ def type_cast_from_database(value)
+ ConnectionAdapters::PostgreSQLColumn.string_to_hstore(value)
end
- def cast_value(value)
- ConnectionAdapters::PostgreSQLColumn.string_to_hstore value
+ def type_cast_for_database(value)
+ ConnectionAdapters::PostgreSQLColumn.hstore_to_string(value)
end
def accessor
diff --git a/activerecord/lib/active_record/connection_adapters/postgresql/oid/json.rb b/activerecord/lib/active_record/connection_adapters/postgresql/oid/json.rb
index c87422fe32..b4fed1bcab 100644
--- a/activerecord/lib/active_record/connection_adapters/postgresql/oid/json.rb
+++ b/activerecord/lib/active_record/connection_adapters/postgresql/oid/json.rb
@@ -7,12 +7,16 @@ module ActiveRecord
:json
end
- def type_cast_for_write(value)
- ConnectionAdapters::PostgreSQLColumn.json_to_string(value)
+ def type_cast_from_user(value)
+ type_cast_from_database(type_cast_for_database(value))
+ end
+
+ def type_cast_from_database(value)
+ ConnectionAdapters::PostgreSQLColumn.string_to_json(value)
end
- def cast_value(value)
- ConnectionAdapters::PostgreSQLColumn.string_to_json value
+ def type_cast_for_database(value)
+ ConnectionAdapters::PostgreSQLColumn.json_to_string(value)
end
def accessor
diff --git a/activerecord/lib/active_record/connection_adapters/postgresql/oid/range.rb b/activerecord/lib/active_record/connection_adapters/postgresql/oid/range.rb
index a0d8a94c74..c289ba8980 100644
--- a/activerecord/lib/active_record/connection_adapters/postgresql/oid/range.rb
+++ b/activerecord/lib/active_record/connection_adapters/postgresql/oid/range.rb
@@ -29,7 +29,7 @@ module ActiveRecord
end
def type_cast_single(value)
- infinity?(value) ? value : @subtype.type_cast(value)
+ infinity?(value) ? value : @subtype.type_cast_from_database(value)
end
def cast_value(value)
diff --git a/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb b/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb
index 283ca81f94..71b05cdbae 100644
--- a/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb
+++ b/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb
@@ -348,14 +348,14 @@ module ActiveRecord
if supports_extensions?
res = exec_query "SELECT EXISTS(SELECT * FROM pg_available_extensions WHERE name = '#{name}' AND installed_version IS NOT NULL) as enabled",
'SCHEMA'
- res.column_types['enabled'].type_cast res.rows.first.first
+ res.column_types['enabled'].type_cast_from_database res.rows.first.first
end
end
def extensions
if supports_extensions?
res = exec_query "SELECT extname from pg_extension", "SCHEMA"
- res.rows.map { |r| res.column_types['extname'].type_cast r.first }
+ res.rows.map { |r| res.column_types['extname'].type_cast_from_database r.first }
else
super
end
diff --git a/activerecord/lib/active_record/core.rb b/activerecord/lib/active_record/core.rb
index 79388f53b5..7edaf256c7 100644
--- a/activerecord/lib/active_record/core.rb
+++ b/activerecord/lib/active_record/core.rb
@@ -356,6 +356,7 @@ module ActiveRecord
def encode_with(coder)
coder['raw_attributes'] = @raw_attributes
coder['attributes'] = @attributes
+ coder['column_types'] = @column_types_override
coder['new_record'] = new_record?
end
diff --git a/activerecord/lib/active_record/fixtures.rb b/activerecord/lib/active_record/fixtures.rb
index 6ba667b996..95fcbbe99a 100644
--- a/activerecord/lib/active_record/fixtures.rb
+++ b/activerecord/lib/active_record/fixtures.rb
@@ -462,13 +462,7 @@ module ActiveRecord
@config = config
# Remove string values that aren't constants or subclasses of AR
- @class_names.delete_if { |k,klass|
- unless klass.is_a? Class
- klass = klass.safe_constantize
- ActiveSupport::Deprecation.warn("The ability to pass in strings as a class name to `set_fixture_class` will be removed in Rails 4.2. Use the class itself instead.")
- end
- !insert_class(@class_names, k, klass)
- }
+ @class_names.delete_if { |klass_name, klass| !insert_class(@class_names, klass_name, klass) }
end
def [](fs_name)
@@ -574,10 +568,6 @@ module ActiveRecord
@config = config
@model_class = nil
- if class_name.is_a?(String)
- ActiveSupport::Deprecation.warn("The ability to pass in strings as a class name to `FixtureSet.new` will be removed in Rails 4.2. Use the class itself instead.")
- end
-
if class_name.is_a?(Class) # TODO: Should be an AR::Base type class, or any?
@model_class = class_name
else
diff --git a/activerecord/lib/active_record/locking/optimistic.rb b/activerecord/lib/active_record/locking/optimistic.rb
index f7ceff7469..4528d8783c 100644
--- a/activerecord/lib/active_record/locking/optimistic.rb
+++ b/activerecord/lib/active_record/locking/optimistic.rb
@@ -151,12 +151,6 @@ module ActiveRecord
@locking_column
end
- # Quote the column name used for optimistic locking.
- def quoted_locking_column
- ActiveSupport::Deprecation.warn "ActiveRecord::Base.quoted_locking_column is deprecated and will be removed in Rails 4.2 or later."
- connection.quote_column_name(locking_column)
- end
-
# Reset the column used for optimistic locking back to the +lock_version+ default.
def reset_locking_column
self.locking_column = DEFAULT_LOCKING_COLUMN
diff --git a/activerecord/lib/active_record/model_schema.rb b/activerecord/lib/active_record/model_schema.rb
index baf2b5fbf8..9e1afd32e6 100644
--- a/activerecord/lib/active_record/model_schema.rb
+++ b/activerecord/lib/active_record/model_schema.rb
@@ -51,6 +51,8 @@ module ActiveRecord
self.pluralize_table_names = true
self.inheritance_column = 'type'
+
+ delegate :type_for_attribute, to: :class
end
module ClassMethods
@@ -218,21 +220,25 @@ module ActiveRecord
end
def column_types # :nodoc:
- @column_types ||= decorate_columns(columns_hash.dup)
+ @column_types ||= decorate_types(build_types_hash)
+ end
+
+ def type_for_attribute(attr_name) # :nodoc:
+ column_types.fetch(attr_name) { Type::Value.new }
end
- def decorate_columns(columns_hash) # :nodoc:
- return if columns_hash.empty?
+ def decorate_types(types) # :nodoc:
+ return if types.empty?
@time_zone_column_names ||= self.columns_hash.find_all do |name, col|
create_time_zone_conversion_attribute?(name, col)
end.map!(&:first)
@time_zone_column_names.each do |name|
- columns_hash[name] = AttributeMethods::TimeZoneConversion::Type.new(columns_hash[name])
+ types[name] = AttributeMethods::TimeZoneConversion::Type.new(types[name])
end
- columns_hash
+ types
end
# Returns a hash where the keys are column names and the values are
@@ -245,7 +251,7 @@ module ActiveRecord
# are the default values suitable for use in `@raw_attriubtes`
def raw_column_defaults # :nodoc:
@raw_column_defauts ||= Hash[column_defaults.map { |name, default|
- [name, columns_hash[name].type_cast_for_write(default)]
+ [name, columns_hash[name].type_cast_for_database(default)]
}]
end
@@ -329,6 +335,10 @@ module ActiveRecord
base.table_name
end
end
+
+ def build_types_hash
+ Hash[columns.map { |column| [column.name, column.cast_type] }]
+ end
end
end
end
diff --git a/activerecord/lib/active_record/nested_attributes.rb b/activerecord/lib/active_record/nested_attributes.rb
index 1242f49e28..8a2a06f2ca 100644
--- a/activerecord/lib/active_record/nested_attributes.rb
+++ b/activerecord/lib/active_record/nested_attributes.rb
@@ -516,7 +516,7 @@ module ActiveRecord
# Determines if a hash contains a truthy _destroy key.
def has_destroy_flag?(hash)
- Type::Boolean.new.type_cast(hash['_destroy'])
+ Type::Boolean.new.type_cast_from_user(hash['_destroy'])
end
# Determines if a new record should be rejected by checking
diff --git a/activerecord/lib/active_record/persistence.rb b/activerecord/lib/active_record/persistence.rb
index 525289c270..3ab8ec4836 100644
--- a/activerecord/lib/active_record/persistence.rb
+++ b/activerecord/lib/active_record/persistence.rb
@@ -48,7 +48,6 @@ module ActiveRecord
# how this "single-table" inheritance mapping is implemented.
def instantiate(attributes, column_types = {})
klass = discriminate_class_for_record(attributes)
- column_types = klass.decorate_columns(column_types.dup)
klass.allocate.init_with(
'raw_attributes' => attributes,
'column_types' => column_types,
diff --git a/activerecord/lib/active_record/querying.rb b/activerecord/lib/active_record/querying.rb
index 1fe54cea3f..39817703cd 100644
--- a/activerecord/lib/active_record/querying.rb
+++ b/activerecord/lib/active_record/querying.rb
@@ -40,7 +40,7 @@ module ActiveRecord
column_types = {}
if result_set.respond_to? :column_types
- column_types = result_set.column_types.merge(columns_hash)
+ column_types = result_set.column_types.except(*columns_hash.keys)
else
ActiveSupport::Deprecation.warn "the object returned from `select_all` must respond to `column_types`"
end
diff --git a/activerecord/lib/active_record/reflection.rb b/activerecord/lib/active_record/reflection.rb
index 4d5203612c..95a3c70c93 100644
--- a/activerecord/lib/active_record/reflection.rb
+++ b/activerecord/lib/active_record/reflection.rb
@@ -188,6 +188,23 @@ module ActiveRecord
active_record == other_aggregation.active_record
end
+ JoinKeys = Struct.new(:key, :foreign_key) # :nodoc:
+
+ def join_keys(assoc_klass)
+ if source_macro == :belongs_to
+ if polymorphic?
+ reflection_key = association_primary_key(assoc_klass)
+ else
+ reflection_key = association_primary_key
+ end
+ reflection_foreign_key = foreign_key
+ else
+ reflection_key = foreign_key
+ reflection_foreign_key = active_record_primary_key
+ end
+ JoinKeys.new(reflection_key, reflection_foreign_key)
+ end
+
private
def derive_class_name
name.to_s.camelize
@@ -228,7 +245,7 @@ module ActiveRecord
def initialize(macro, name, scope, options, active_record)
super
- @collection = [:has_many, :has_and_belongs_to_many].include?(macro)
+ @collection = macro == :has_many
@automatic_inverse_of = nil
@type = options[:as] && "#{options[:as]}_type"
@foreign_type = options[:foreign_type] || "#{name}_type"
@@ -392,7 +409,7 @@ Joining, Preloading and eager loading of these associations is deprecated and wi
# * you use autosave; <tt>autosave: true</tt>
# * the association is a +has_many+ association
def validate?
- !options[:validate].nil? ? options[:validate] : (options[:autosave] == true || macro == :has_many)
+ !options[:validate].nil? ? options[:validate] : (options[:autosave] == true || collection?)
end
# Returns +true+ if +self+ is a +belongs_to+ reflection.
@@ -400,6 +417,11 @@ Joining, Preloading and eager loading of these associations is deprecated and wi
macro == :belongs_to
end
+ # Returns +true+ if +self+ is a +has_one+ reflection.
+ def has_one?
+ macro == :has_one
+ end
+
def association_class
case macro
when :belongs_to
@@ -538,6 +560,13 @@ Joining, Preloading and eager loading of these associations is deprecated and wi
end
end
+ class HasAndBelongsToManyReflection < AssociationReflection #:nodoc:
+ def initialize(macro, name, scope, options, active_record)
+ super
+ @collection = true
+ end
+ end
+
# Holds all the meta-data about a :through association as it was specified
# in the Active Record class.
class ThroughReflection < AssociationReflection #:nodoc:
@@ -739,7 +768,7 @@ directive on your declaration like:
raise HasManyThroughAssociationPolymorphicSourceError.new(active_record.name, self, source_reflection)
end
- if macro == :has_one && through_reflection.collection?
+ if has_one? && through_reflection.collection?
raise HasOneThroughCantAssociateThroughCollection.new(active_record.name, self, through_reflection)
end
diff --git a/activerecord/lib/active_record/relation/calculations.rb b/activerecord/lib/active_record/relation/calculations.rb
index 11ab1b4595..028e4d80ab 100644
--- a/activerecord/lib/active_record/relation/calculations.rb
+++ b/activerecord/lib/active_record/relation/calculations.rb
@@ -176,10 +176,8 @@ module ActiveRecord
}
end
- result = result.map do |attributes|
- values = attributes.values
-
- columns.zip(values).map { |column, value| column.type_cast value }
+ result = result.rows.map do |values|
+ columns.zip(values).map { |column, value| column.type_cast_from_database value }
end
columns.one? ? result.map!(&:first) : result
end
@@ -379,7 +377,7 @@ module ActiveRecord
end
def type_cast_using_column(value, column)
- column ? column.type_cast(value) : value
+ column ? column.type_cast_from_database(value) : value
end
# TODO: refactor to allow non-string `select_values` (eg. Arel nodes).
diff --git a/activerecord/lib/active_record/relation/finder_methods.rb b/activerecord/lib/active_record/relation/finder_methods.rb
index 47e90e9021..0c9c761f97 100644
--- a/activerecord/lib/active_record/relation/finder_methods.rb
+++ b/activerecord/lib/active_record/relation/finder_methods.rb
@@ -65,7 +65,7 @@ module ActiveRecord
# # returns an Array of the required fields, available since Rails 3.1.
def find(*args)
if block_given?
- to_a.find { |*block_args| yield(*block_args) }
+ to_a.find(*args) { |*block_args| yield(*block_args) }
else
find_with_ids(*args)
end
diff --git a/activerecord/lib/active_record/result.rb b/activerecord/lib/active_record/result.rb
index 293189fa69..ae53f66d7a 100644
--- a/activerecord/lib/active_record/result.rb
+++ b/activerecord/lib/active_record/result.rb
@@ -31,7 +31,7 @@ module ActiveRecord
class Result
include Enumerable
- IDENTITY_TYPE = Class.new { def type_cast(v); v; end }.new # :nodoc:
+ IDENTITY_TYPE = Type::Value.new # :nodoc:
attr_reader :columns, :rows, :column_types
diff --git a/activerecord/lib/active_record/timestamp.rb b/activerecord/lib/active_record/timestamp.rb
index 6c30ccab72..e2e37e7c00 100644
--- a/activerecord/lib/active_record/timestamp.rb
+++ b/activerecord/lib/active_record/timestamp.rb
@@ -99,9 +99,11 @@ module ActiveRecord
end
def max_updated_column_timestamp(timestamp_names = timestamp_attributes_for_update)
- if (timestamps = timestamp_names.map { |attr| self[attr] }.compact).present?
- timestamps.map { |ts| ts.to_time }.max
- end
+ timestamp_names
+ .map { |attr| self[attr] }
+ .compact
+ .map(&:to_time)
+ .max
end
def current_time_from_proper_timezone
diff --git a/activerecord/lib/active_record/type/binary.rb b/activerecord/lib/active_record/type/binary.rb
index bc93f6e1bf..3bf29b5026 100644
--- a/activerecord/lib/active_record/type/binary.rb
+++ b/activerecord/lib/active_record/type/binary.rb
@@ -9,7 +9,16 @@ module ActiveRecord
true
end
+ def type_cast(value)
+ if value.is_a?(Data)
+ value.to_s
+ else
+ super
+ end
+ end
+
def type_cast_for_database(value)
+ return if value.nil?
Data.new(super)
end
diff --git a/activerecord/lib/active_record/type/serialized.rb b/activerecord/lib/active_record/type/serialized.rb
index 0866383de9..e6d84eadc0 100644
--- a/activerecord/lib/active_record/type/serialized.rb
+++ b/activerecord/lib/active_record/type/serialized.rb
@@ -9,7 +9,7 @@ module ActiveRecord
super(subtype)
end
- def type_cast(value)
+ def type_cast_from_database(value)
if is_default_value?(value)
value
else
@@ -17,15 +17,17 @@ module ActiveRecord
end
end
- def type_cast_for_write(value)
+ def type_cast_from_user(value)
+ type_cast_from_database(type_cast_for_database(value))
+ end
+
+ def type_cast_for_database(value)
return if value.nil?
unless is_default_value?(value)
super coder.dump(value)
end
end
- alias type_cast_for_database type_cast_for_write
-
def serialized?
true
end
@@ -34,6 +36,17 @@ module ActiveRecord
ActiveRecord::Store::IndifferentHashAccessor
end
+ def init_with(coder)
+ @subtype = coder['subtype']
+ @coder = coder['coder']
+ __setobj__(@subtype)
+ end
+
+ def encode_with(coder)
+ coder['subtype'] = @subtype
+ coder['coder'] = @coder
+ end
+
private
def is_default_value?(value)
diff --git a/activerecord/lib/active_record/type/value.rb b/activerecord/lib/active_record/type/value.rb
index 1c41b28646..efcdab1c0e 100644
--- a/activerecord/lib/active_record/type/value.rb
+++ b/activerecord/lib/active_record/type/value.rb
@@ -16,15 +16,16 @@ module ActiveRecord
# must override this method.
def type; end
- # Takes an input from the database, or from attribute setters,
- # and casts it to a type appropriate for this object. This method
- # should not be overriden by subclasses. Instead, override `cast_value`.
- def type_cast(value)
- cast_value(value) unless value.nil?
+ def type_cast_from_database(value)
+ type_cast(value)
+ end
+
+ def type_cast_from_user(value)
+ type_cast(value)
end
def type_cast_for_database(value)
- type_cast_for_write(value)
+ value
end
def type_cast_for_schema(value)
@@ -50,10 +51,6 @@ module ActiveRecord
def klass # :nodoc:
end
- def type_cast_for_write(value) # :nodoc:
- value
- end
-
# +old_value+ will always be type-cast.
# +new_value+ will come straight from the database
# or from assignment, so it could be anything. Types
@@ -64,6 +61,12 @@ module ActiveRecord
end
private
+ # Takes an input from the database, or from attribute setters,
+ # and casts it to a type appropriate for this object. This method
+ # should not be overriden by subclasses. Instead, override `cast_value`.
+ def type_cast(value) # :api: public
+ cast_value(value) unless value.nil?
+ end
# Responsible for casting values from external sources to the appropriate
# type. Called by `type_cast` for all values except `nil`.
diff --git a/activerecord/test/cases/adapters/mysql/mysql_adapter_test.rb b/activerecord/test/cases/adapters/mysql/mysql_adapter_test.rb
index 1699380eb3..28106d3772 100644
--- a/activerecord/test/cases/adapters/mysql/mysql_adapter_test.rb
+++ b/activerecord/test/cases/adapters/mysql/mysql_adapter_test.rb
@@ -105,7 +105,7 @@ module ActiveRecord
result = @conn.exec_query('SELECT status FROM ex')
- assert_equal 2, result.column_types['status'].type_cast(result.last['status'])
+ assert_equal 2, result.column_types['status'].type_cast_from_database(result.last['status'])
end
end
diff --git a/activerecord/test/cases/adapters/postgresql/array_test.rb b/activerecord/test/cases/adapters/postgresql/array_test.rb
index 1d431f1666..66750deb68 100644
--- a/activerecord/test/cases/adapters/postgresql/array_test.rb
+++ b/activerecord/test/cases/adapters/postgresql/array_test.rb
@@ -90,9 +90,9 @@ class PostgresqlArrayTest < ActiveRecord::TestCase
end
def test_type_cast_array
- assert_equal(['1', '2', '3'], @column.type_cast('{1,2,3}'))
- assert_equal([], @column.type_cast('{}'))
- assert_equal([nil], @column.type_cast('{NULL}'))
+ assert_equal(['1', '2', '3'], @column.type_cast_from_database('{1,2,3}'))
+ assert_equal([], @column.type_cast_from_database('{}'))
+ assert_equal([nil], @column.type_cast_from_database('{NULL}'))
end
def test_type_cast_integers
diff --git a/activerecord/test/cases/adapters/postgresql/bytea_test.rb b/activerecord/test/cases/adapters/postgresql/bytea_test.rb
index 3f8a5d1062..7872f91943 100644
--- a/activerecord/test/cases/adapters/postgresql/bytea_test.rb
+++ b/activerecord/test/cases/adapters/postgresql/bytea_test.rb
@@ -33,16 +33,16 @@ class PostgresqlByteaTest < ActiveRecord::TestCase
data = "\u001F\x8B"
assert_equal('UTF-8', data.encoding.name)
- assert_equal('ASCII-8BIT', @column.type_cast(data).encoding.name)
+ assert_equal('ASCII-8BIT', @column.type_cast_from_database(data).encoding.name)
end
def test_type_cast_binary_value
data = "\u001F\x8B".force_encoding("BINARY")
- assert_equal(data, @column.type_cast(data))
+ assert_equal(data, @column.type_cast_from_database(data))
end
def test_type_case_nil
- assert_equal(nil, @column.type_cast(nil))
+ assert_equal(nil, @column.type_cast_from_database(nil))
end
def test_read_value
diff --git a/activerecord/test/cases/adapters/postgresql/composite_test.rb b/activerecord/test/cases/adapters/postgresql/composite_test.rb
index a925263098..0b48fe9af8 100644
--- a/activerecord/test/cases/adapters/postgresql/composite_test.rb
+++ b/activerecord/test/cases/adapters/postgresql/composite_test.rb
@@ -90,7 +90,11 @@ class PostgresqlCompositeWithCustomOIDTest < ActiveRecord::TestCase
end
end
- def type_cast_for_write(value)
+ def type_cast_from_user(value)
+ value
+ end
+
+ def type_cast_for_database(value)
return if value.nil?
"(#{value.city},#{value.street})"
end
diff --git a/activerecord/test/cases/adapters/postgresql/connection_test.rb b/activerecord/test/cases/adapters/postgresql/connection_test.rb
index 5f84c893c0..d26cda46fa 100644
--- a/activerecord/test/cases/adapters/postgresql/connection_test.rb
+++ b/activerecord/test/cases/adapters/postgresql/connection_test.rb
@@ -121,7 +121,7 @@ module ActiveRecord
name = @subscriber.payloads.last[:statement_name]
assert name
res = @connection.exec_query("EXPLAIN (FORMAT JSON) EXECUTE #{name}(#{bindval})")
- plan = res.column_types['QUERY PLAN'].type_cast res.rows.first.first
+ plan = res.column_types['QUERY PLAN'].type_cast_from_database res.rows.first.first
assert_operator plan.length, :>, 0
end
diff --git a/activerecord/test/cases/adapters/postgresql/hstore_test.rb b/activerecord/test/cases/adapters/postgresql/hstore_test.rb
index a6482786c7..a25c9cb5e4 100644
--- a/activerecord/test/cases/adapters/postgresql/hstore_test.rb
+++ b/activerecord/test/cases/adapters/postgresql/hstore_test.rb
@@ -106,6 +106,7 @@ class PostgresqlHstoreTest < ActiveRecord::TestCase
def test_cast_value_on_write
x = Hstore.new tags: {"bool" => true, "number" => 5}
+ assert_equal({"bool" => true, "number" => 5}, x.tags_before_type_cast)
assert_equal({"bool" => "true", "number" => "5"}, x.tags)
x.save
assert_equal({"bool" => "true", "number" => "5"}, x.reload.tags)
@@ -117,11 +118,11 @@ class PostgresqlHstoreTest < ActiveRecord::TestCase
data = "\"1\"=>\"2\""
hash = @column.class.string_to_hstore data
assert_equal({'1' => '2'}, hash)
- assert_equal({'1' => '2'}, @column.type_cast(data))
+ assert_equal({'1' => '2'}, @column.type_cast_from_database(data))
- assert_equal({}, @column.type_cast(""))
- assert_equal({'key'=>nil}, @column.type_cast('key => NULL'))
- assert_equal({'c'=>'}','"a"'=>'b "a b'}, @column.type_cast(%q(c=>"}", "\"a\""=>"b \"a b")))
+ assert_equal({}, @column.type_cast_from_database(""))
+ assert_equal({'key'=>nil}, @column.type_cast_from_database('key => NULL'))
+ assert_equal({'c'=>'}','"a"'=>'b "a b'}, @column.type_cast_from_database(%q(c=>"}", "\"a\""=>"b \"a b")))
end
def test_with_store_accessors
@@ -179,31 +180,31 @@ class PostgresqlHstoreTest < ActiveRecord::TestCase
end
def test_parse1
- assert_equal({'a'=>nil,'b'=>nil,'c'=>'NuLl','null'=>'c'}, @column.type_cast('a=>null,b=>NuLl,c=>"NuLl",null=>c'))
+ assert_equal({'a'=>nil,'b'=>nil,'c'=>'NuLl','null'=>'c'}, @column.type_cast_from_database('a=>null,b=>NuLl,c=>"NuLl",null=>c'))
end
def test_parse2
- assert_equal({" " => " "}, @column.type_cast("\\ =>\\ "))
+ assert_equal({" " => " "}, @column.type_cast_from_database("\\ =>\\ "))
end
def test_parse3
- assert_equal({"=" => ">"}, @column.type_cast("==>>"))
+ assert_equal({"=" => ">"}, @column.type_cast_from_database("==>>"))
end
def test_parse4
- assert_equal({"=a"=>"q=w"}, @column.type_cast('\=a=>q=w'))
+ assert_equal({"=a"=>"q=w"}, @column.type_cast_from_database('\=a=>q=w'))
end
def test_parse5
- assert_equal({"=a"=>"q=w"}, @column.type_cast('"=a"=>q\=w'))
+ assert_equal({"=a"=>"q=w"}, @column.type_cast_from_database('"=a"=>q\=w'))
end
def test_parse6
- assert_equal({"\"a"=>"q>w"}, @column.type_cast('"\"a"=>q>w'))
+ assert_equal({"\"a"=>"q>w"}, @column.type_cast_from_database('"\"a"=>q>w'))
end
def test_parse7
- assert_equal({"\"a"=>"q\"w"}, @column.type_cast('\"a=>q"w'))
+ assert_equal({"\"a"=>"q\"w"}, @column.type_cast_from_database('\"a=>q"w'))
end
def test_rewrite
@@ -295,24 +296,18 @@ class PostgresqlHstoreTest < ActiveRecord::TestCase
assert_equal({ }, hstore.reload.tags)
end
- # FIXME: remove this lambda once `serialize` no longer issues a db connection.
- LAZY_MODELS = lambda do
- return if defined?(TagCollection)
-
- class TagCollection
- def initialize(hash); @hash = hash end
- def to_hash; @hash end
- def self.load(hash); new(hash) end
- def self.dump(object); object.to_hash end
- end
+ class TagCollection
+ def initialize(hash); @hash = hash end
+ def to_hash; @hash end
+ def self.load(hash); new(hash) end
+ def self.dump(object); object.to_hash end
+ end
- class HstoreWithSerialize < Hstore
- serialize :tags, TagCollection
- end
+ class HstoreWithSerialize < Hstore
+ serialize :tags, TagCollection
end
def test_hstore_with_serialized_attributes
- LAZY_MODELS.call
HstoreWithSerialize.create! tags: TagCollection.new({"one" => "two"})
record = HstoreWithSerialize.first
assert_instance_of TagCollection, record.tags
@@ -323,7 +318,6 @@ class PostgresqlHstoreTest < ActiveRecord::TestCase
end
def test_clone_hstore_with_serialized_attributes
- LAZY_MODELS.call
HstoreWithSerialize.create! tags: TagCollection.new({"one" => "two"})
record = HstoreWithSerialize.first
dupe = record.dup
diff --git a/activerecord/test/cases/adapters/postgresql/json_test.rb b/activerecord/test/cases/adapters/postgresql/json_test.rb
index 61d4e2b8ae..3ee8839823 100644
--- a/activerecord/test/cases/adapters/postgresql/json_test.rb
+++ b/activerecord/test/cases/adapters/postgresql/json_test.rb
@@ -68,6 +68,7 @@ class PostgresqlJSONTest < ActiveRecord::TestCase
def test_cast_value_on_write
x = JsonDataType.new payload: {"string" => "foo", :symbol => :bar}
+ assert_equal({"string" => "foo", :symbol => :bar}, x.payload_before_type_cast)
assert_equal({"string" => "foo", "symbol" => "bar"}, x.payload)
x.save
assert_equal({"string" => "foo", "symbol" => "bar"}, x.reload.payload)
@@ -79,11 +80,11 @@ class PostgresqlJSONTest < ActiveRecord::TestCase
data = "{\"a_key\":\"a_value\"}"
hash = column.class.string_to_json data
assert_equal({'a_key' => 'a_value'}, hash)
- assert_equal({'a_key' => 'a_value'}, column.type_cast(data))
+ assert_equal({'a_key' => 'a_value'}, column.type_cast_from_database(data))
- assert_equal({}, column.type_cast("{}"))
- assert_equal({'key'=>nil}, column.type_cast('{"key": null}'))
- assert_equal({'c'=>'}','"a"'=>'b "a b'}, column.type_cast(%q({"c":"}", "\"a\"":"b \"a b"})))
+ assert_equal({}, column.type_cast_from_database("{}"))
+ assert_equal({'key'=>nil}, column.type_cast_from_database('{"key": null}'))
+ assert_equal({'c'=>'}','"a"'=>'b "a b'}, column.type_cast_from_database(%q({"c":"}", "\"a\"":"b \"a b"})))
end
def test_rewrite
diff --git a/activerecord/test/cases/adapters/postgresql/money_test.rb b/activerecord/test/cases/adapters/postgresql/money_test.rb
index 3e33477bff..bdfeedafab 100644
--- a/activerecord/test/cases/adapters/postgresql/money_test.rb
+++ b/activerecord/test/cases/adapters/postgresql/money_test.rb
@@ -49,10 +49,10 @@ class PostgresqlMoneyTest < ActiveRecord::TestCase
def test_money_type_cast
column = PostgresqlMoney.columns_hash['wealth']
- assert_equal(12345678.12, column.type_cast("$12,345,678.12"))
- assert_equal(12345678.12, column.type_cast("$12.345.678,12"))
- assert_equal(-1.15, column.type_cast("-$1.15"))
- assert_equal(-2.25, column.type_cast("($2.25)"))
+ assert_equal(12345678.12, column.type_cast_from_user("$12,345,678.12"))
+ assert_equal(12345678.12, column.type_cast_from_user("$12.345.678,12"))
+ assert_equal(-1.15, column.type_cast_from_user("-$1.15"))
+ assert_equal(-2.25, column.type_cast_from_user("($2.25)"))
end
def test_schema_dumping
diff --git a/activerecord/test/cases/adapters/postgresql/range_test.rb b/activerecord/test/cases/adapters/postgresql/range_test.rb
index 4d9cfe55f5..0f6e39322c 100644
--- a/activerecord/test/cases/adapters/postgresql/range_test.rb
+++ b/activerecord/test/cases/adapters/postgresql/range_test.rb
@@ -156,7 +156,7 @@ _SQL
assert_equal 0.5..0.7, @first_range.float_range
assert_equal 0.5...0.7, @second_range.float_range
assert_equal 0.5...Float::INFINITY, @third_range.float_range
- assert_equal (-Float::INFINITY...Float::INFINITY), @fourth_range.float_range
+ assert_equal(-Float::INFINITY...Float::INFINITY, @fourth_range.float_range)
assert_nil @empty_range.float_range
end
diff --git a/activerecord/test/cases/attribute_decorators_test.rb b/activerecord/test/cases/attribute_decorators_test.rb
index f17cc02f53..35393753a2 100644
--- a/activerecord/test/cases/attribute_decorators_test.rb
+++ b/activerecord/test/cases/attribute_decorators_test.rb
@@ -12,9 +12,11 @@ module ActiveRecord
super(delegate)
end
- def type_cast(value)
+ def type_cast_from_user(value)
"#{super} #{@decoration}"
end
+
+ alias type_cast_from_database type_cast_from_user
end
setup do
diff --git a/activerecord/test/cases/attribute_methods_test.rb b/activerecord/test/cases/attribute_methods_test.rb
index 139fe9c04b..a366caf875 100644
--- a/activerecord/test/cases/attribute_methods_test.rb
+++ b/activerecord/test/cases/attribute_methods_test.rb
@@ -517,43 +517,17 @@ class AttributeMethodsTest < ActiveRecord::TestCase
end
end
- def test_only_time_related_columns_are_meant_to_be_cached_by_default
- expected = %w(datetime time date).sort
- assert_equal expected, ActiveRecord::Base.attribute_types_cached_by_default.map(&:to_s).sort
- end
-
- def test_declaring_attributes_as_cached_adds_them_to_the_attributes_cached_by_default
- default_attributes = Topic.cached_attributes
- Topic.cache_attributes :replies_count
- expected = default_attributes + ["replies_count"]
- assert_equal expected.sort, Topic.cached_attributes.sort
- Topic.instance_variable_set "@cached_attributes", nil
- end
-
- def test_cacheable_columns_are_actually_cached
- assert_equal cached_columns.sort, Topic.cached_attributes.sort
- end
-
- def test_accessing_cached_attributes_caches_the_converted_values_and_nothing_else
- t = topics(:first)
- cache = t.instance_variable_get "@attributes"
-
- assert_not_nil cache
- assert cache.empty?
+ def test_deprecated_cache_attributes
+ assert_deprecated do
+ Topic.cache_attributes :replies_count
+ end
- all_columns = Topic.columns.map(&:name)
- uncached_columns = all_columns - cached_columns
+ assert_deprecated do
+ Topic.cached_attributes
+ end
- all_columns.each do |attr_name|
- attribute_gets_cached = Topic.cache_attribute?(attr_name)
- val = t.send attr_name unless attr_name == "type"
- if attribute_gets_cached
- assert cached_columns.include?(attr_name)
- assert_equal val, cache[attr_name]
- else
- assert uncached_columns.include?(attr_name)
- assert !cache.include?(attr_name)
- end
+ assert_deprecated do
+ Topic.cache_attribute? :replies_count
end
end
diff --git a/activerecord/test/cases/base_test.rb b/activerecord/test/cases/base_test.rb
index fad51f4924..dc8f25b0e7 100644
--- a/activerecord/test/cases/base_test.rb
+++ b/activerecord/test/cases/base_test.rb
@@ -1489,7 +1489,7 @@ class BasicsTest < ActiveRecord::TestCase
attrs.delete 'id'
typecast = Class.new {
- def type_cast value
+ def type_cast_from_database value
"t.lo"
end
}
diff --git a/activerecord/test/cases/calculations_test.rb b/activerecord/test/cases/calculations_test.rb
index dfc6a7ec67..222b1505a8 100644
--- a/activerecord/test/cases/calculations_test.rb
+++ b/activerecord/test/cases/calculations_test.rb
@@ -610,4 +610,11 @@ class CalculationsTest < ActiveRecord::TestCase
assert_equal [1,2,3,4,5], taks_relation.pluck(:id)
assert_equal [false, true, true, true, true], taks_relation.pluck(:approved)
end
+
+ def test_pluck_columns_with_same_name
+ expected = [["The First Topic", "The Second Topic of the day"], ["The Third Topic of the day", "The Fourth Topic of the day"]]
+ actual = Topic.joins(:replies)
+ .pluck('topics.title', 'replies_topics.title')
+ assert_equal expected, actual
+ end
end
diff --git a/activerecord/test/cases/connection_adapters/type_lookup_test.rb b/activerecord/test/cases/connection_adapters/type_lookup_test.rb
index 3958c3bfff..d5c1dc1e5d 100644
--- a/activerecord/test/cases/connection_adapters/type_lookup_test.rb
+++ b/activerecord/test/cases/connection_adapters/type_lookup_test.rb
@@ -85,7 +85,7 @@ module ActiveRecord
cast_type = @connection.type_map.lookup(type)
assert_equal :decimal, cast_type.type
- assert_equal 2, cast_type.type_cast(2.1)
+ assert_equal 2, cast_type.type_cast_from_user(2.1)
end
end
diff --git a/activerecord/test/cases/dirty_test.rb b/activerecord/test/cases/dirty_test.rb
index 2beac84fb1..87f24e32b2 100644
--- a/activerecord/test/cases/dirty_test.rb
+++ b/activerecord/test/cases/dirty_test.rb
@@ -616,17 +616,15 @@ class DirtyTest < ActiveRecord::TestCase
end
end
- test "defaults with type that implements `type_cast_for_write`" do
+ test "defaults with type that implements `type_cast_for_database`" do
type = Class.new(ActiveRecord::Type::Value) do
def type_cast(value)
value.to_i
end
- def type_cast_for_write(value)
+ def type_cast_for_database(value)
value.to_s
end
-
- alias type_cast_for_database type_cast_for_write
end
model_class = Class.new(ActiveRecord::Base) do
diff --git a/activerecord/test/cases/finder_test.rb b/activerecord/test/cases/finder_test.rb
index c0440744e9..bd77c412f6 100644
--- a/activerecord/test/cases/finder_test.rb
+++ b/activerecord/test/cases/finder_test.rb
@@ -33,6 +33,17 @@ class FinderTest < ActiveRecord::TestCase
assert_equal(topics(:first).title, Topic.find(1).title)
end
+ def test_find_with_proc_parameter_and_block
+ exception = assert_raises(RuntimeError) do
+ Topic.all.find(-> { raise "should happen" }) { |e| e.title == "non-existing-title" }
+ end
+ assert_equal "should happen", exception.message
+
+ assert_nothing_raised(RuntimeError) do
+ Topic.all.find(-> { raise "should not happen" }) { |e| e.title == topics(:first).title }
+ end
+ end
+
def test_find_passing_active_record_object_is_deprecated
assert_deprecated do
Topic.find(Topic.last)
diff --git a/activerecord/test/cases/fixtures_test.rb b/activerecord/test/cases/fixtures_test.rb
index 8bbc0af758..042fdaf0bb 100644
--- a/activerecord/test/cases/fixtures_test.rb
+++ b/activerecord/test/cases/fixtures_test.rb
@@ -84,12 +84,6 @@ class FixturesTest < ActiveRecord::TestCase
assert fixtures.detect { |f| f.name == 'collections' }, "no fixtures named 'collections' in #{fixtures.map(&:name).inspect}"
end
- def test_create_symbol_fixtures_is_deprecated
- assert_deprecated do
- ActiveRecord::FixtureSet.create_fixtures(FIXTURES_ROOT, :collections, :collections => 'Course') { Course.connection }
- end
- end
-
def test_attributes
topics = create_fixtures("topics").first
assert_equal("The First Topic", topics["first"]["title"])
diff --git a/activerecord/test/cases/locking_test.rb b/activerecord/test/cases/locking_test.rb
index 93fd3b9605..c221430757 100644
--- a/activerecord/test/cases/locking_test.rb
+++ b/activerecord/test/cases/locking_test.rb
@@ -272,10 +272,6 @@ class OptimisticLockingTest < ActiveRecord::TestCase
assert p.treasures.empty?
assert RichPerson.connection.select_all("SELECT * FROM peoples_treasures WHERE rich_person_id = 1").empty?
end
-
- def test_quoted_locking_column_is_deprecated
- assert_deprecated { ActiveRecord::Base.quoted_locking_column }
- end
end
class OptimisticLockingWithSchemaChangeTest < ActiveRecord::TestCase
diff --git a/activerecord/test/cases/reflection_test.rb b/activerecord/test/cases/reflection_test.rb
index 793e193329..c6e73f78cc 100644
--- a/activerecord/test/cases/reflection_test.rb
+++ b/activerecord/test/cases/reflection_test.rb
@@ -95,8 +95,8 @@ class ReflectionTest < ActiveRecord::TestCase
column = @first.column_for_attribute("attribute_that_doesnt_exist")
object = Object.new
- assert_equal object, column.type_cast(object)
- assert_equal object, column.type_cast_for_write(object)
+ assert_equal object, column.type_cast_from_database(object)
+ assert_equal object, column.type_cast_from_user(object)
assert_equal object, column.type_cast_for_database(object)
end
diff --git a/activerecord/test/cases/schema_dumper_test.rb b/activerecord/test/cases/schema_dumper_test.rb
index e2a18d138d..5f02d39e32 100644
--- a/activerecord/test/cases/schema_dumper_test.rb
+++ b/activerecord/test/cases/schema_dumper_test.rb
@@ -394,7 +394,7 @@ class SchemaDumperTest < ActiveRecord::TestCase
output = standard_dump
assert_no_match %r{create_table "foo_.+_bar"}, output
- assert_no_match %r{create_index "foo_.+_bar"}, output
+ assert_no_match %r{add_index "foo_.+_bar"}, output
assert_no_match %r{create_table "schema_migrations"}, output
ensure
migration.migrate(:down)
diff --git a/activerecord/test/cases/timestamp_test.rb b/activerecord/test/cases/timestamp_test.rb
index 77ab427be0..0472246f71 100644
--- a/activerecord/test/cases/timestamp_test.rb
+++ b/activerecord/test/cases/timestamp_test.rb
@@ -71,24 +71,6 @@ class TimestampTest < ActiveRecord::TestCase
assert_equal @previously_updated_at, @developer.updated_at
end
- def test_saving_when_callback_sets_record_timestamps_to_false_doesnt_update_its_timestamp
- klass = Class.new(Developer) do
- before_update :cancel_record_timestamps
- def cancel_record_timestamps
- self.record_timestamps = false
- return true
- end
- end
-
- developer = klass.first
- previously_updated_at = developer.updated_at
-
- developer.name = "New Name"
- developer.save!
-
- assert_equal previously_updated_at, developer.updated_at
- end
-
def test_touching_an_attribute_updates_timestamp
previously_created_at = @developer.created_at
@developer.touch(:created_at)
@@ -399,6 +381,19 @@ class TimestampTest < ActiveRecord::TestCase
assert_not_equal time, pet.updated_at
end
+ def test_timestamp_column_values_are_present_in_the_callbacks
+ klass = Class.new(ActiveRecord::Base) do
+ self.table_name = 'people'
+
+ before_create do
+ self.born_at = self.created_at
+ end
+ end
+
+ person = klass.create first_name: 'David'
+ assert_not_equal person.born_at, nil
+ end
+
def test_timestamp_attributes_for_create
toy = Toy.first
assert_equal [:created_at, :created_on], toy.send(:timestamp_attributes_for_create)
diff --git a/activerecord/test/cases/types_test.rb b/activerecord/test/cases/types_test.rb
index 5d5f442d3a..731f8cfba3 100644
--- a/activerecord/test/cases/types_test.rb
+++ b/activerecord/test/cases/types_test.rb
@@ -6,141 +6,141 @@ module ActiveRecord
class TypesTest < ActiveRecord::TestCase
def test_type_cast_boolean
type = Type::Boolean.new
- assert type.type_cast('').nil?
- assert type.type_cast(nil).nil?
-
- assert type.type_cast(true)
- assert type.type_cast(1)
- assert type.type_cast('1')
- assert type.type_cast('t')
- assert type.type_cast('T')
- assert type.type_cast('true')
- assert type.type_cast('TRUE')
- assert type.type_cast('on')
- assert type.type_cast('ON')
+ assert type.type_cast_from_user('').nil?
+ assert type.type_cast_from_user(nil).nil?
+
+ assert type.type_cast_from_user(true)
+ assert type.type_cast_from_user(1)
+ assert type.type_cast_from_user('1')
+ assert type.type_cast_from_user('t')
+ assert type.type_cast_from_user('T')
+ assert type.type_cast_from_user('true')
+ assert type.type_cast_from_user('TRUE')
+ assert type.type_cast_from_user('on')
+ assert type.type_cast_from_user('ON')
# explicitly check for false vs nil
- assert_equal false, type.type_cast(false)
- assert_equal false, type.type_cast(0)
- assert_equal false, type.type_cast('0')
- assert_equal false, type.type_cast('f')
- assert_equal false, type.type_cast('F')
- assert_equal false, type.type_cast('false')
- assert_equal false, type.type_cast('FALSE')
- assert_equal false, type.type_cast('off')
- assert_equal false, type.type_cast('OFF')
- assert_equal false, type.type_cast(' ')
- assert_equal false, type.type_cast("\u3000\r\n")
- assert_equal false, type.type_cast("\u0000")
- assert_equal false, type.type_cast('SOMETHING RANDOM')
+ assert_equal false, type.type_cast_from_user(false)
+ assert_equal false, type.type_cast_from_user(0)
+ assert_equal false, type.type_cast_from_user('0')
+ assert_equal false, type.type_cast_from_user('f')
+ assert_equal false, type.type_cast_from_user('F')
+ assert_equal false, type.type_cast_from_user('false')
+ assert_equal false, type.type_cast_from_user('FALSE')
+ assert_equal false, type.type_cast_from_user('off')
+ assert_equal false, type.type_cast_from_user('OFF')
+ assert_equal false, type.type_cast_from_user(' ')
+ assert_equal false, type.type_cast_from_user("\u3000\r\n")
+ assert_equal false, type.type_cast_from_user("\u0000")
+ assert_equal false, type.type_cast_from_user('SOMETHING RANDOM')
end
def test_type_cast_string
type = Type::String.new
- assert_equal "1", type.type_cast(true)
- assert_equal "0", type.type_cast(false)
- assert_equal "123", type.type_cast(123)
+ assert_equal "1", type.type_cast_from_user(true)
+ assert_equal "0", type.type_cast_from_user(false)
+ assert_equal "123", type.type_cast_from_user(123)
end
def test_type_cast_integer
type = Type::Integer.new
- assert_equal 1, type.type_cast(1)
- assert_equal 1, type.type_cast('1')
- assert_equal 1, type.type_cast('1ignore')
- assert_equal 0, type.type_cast('bad1')
- assert_equal 0, type.type_cast('bad')
- assert_equal 1, type.type_cast(1.7)
- assert_equal 0, type.type_cast(false)
- assert_equal 1, type.type_cast(true)
- assert_nil type.type_cast(nil)
+ assert_equal 1, type.type_cast_from_user(1)
+ assert_equal 1, type.type_cast_from_user('1')
+ assert_equal 1, type.type_cast_from_user('1ignore')
+ assert_equal 0, type.type_cast_from_user('bad1')
+ assert_equal 0, type.type_cast_from_user('bad')
+ assert_equal 1, type.type_cast_from_user(1.7)
+ assert_equal 0, type.type_cast_from_user(false)
+ assert_equal 1, type.type_cast_from_user(true)
+ assert_nil type.type_cast_from_user(nil)
end
def test_type_cast_non_integer_to_integer
type = Type::Integer.new
- assert_nil type.type_cast([1,2])
- assert_nil type.type_cast({1 => 2})
- assert_nil type.type_cast((1..2))
+ assert_nil type.type_cast_from_user([1,2])
+ assert_nil type.type_cast_from_user({1 => 2})
+ assert_nil type.type_cast_from_user((1..2))
end
def test_type_cast_activerecord_to_integer
type = Type::Integer.new
firm = Firm.create(:name => 'Apple')
- assert_nil type.type_cast(firm)
+ assert_nil type.type_cast_from_user(firm)
end
def test_type_cast_object_without_to_i_to_integer
type = Type::Integer.new
- assert_nil type.type_cast(Object.new)
+ assert_nil type.type_cast_from_user(Object.new)
end
def test_type_cast_nan_and_infinity_to_integer
type = Type::Integer.new
- assert_nil type.type_cast(Float::NAN)
- assert_nil type.type_cast(1.0/0.0)
+ assert_nil type.type_cast_from_user(Float::NAN)
+ assert_nil type.type_cast_from_user(1.0/0.0)
end
def test_type_cast_float
type = Type::Float.new
- assert_equal 1.0, type.type_cast("1")
+ assert_equal 1.0, type.type_cast_from_user("1")
end
def test_type_cast_decimal
type = Type::Decimal.new
- assert_equal BigDecimal.new("0"), type.type_cast(BigDecimal.new("0"))
- assert_equal BigDecimal.new("123"), type.type_cast(123.0)
- assert_equal BigDecimal.new("1"), type.type_cast(:"1")
+ assert_equal BigDecimal.new("0"), type.type_cast_from_user(BigDecimal.new("0"))
+ assert_equal BigDecimal.new("123"), type.type_cast_from_user(123.0)
+ assert_equal BigDecimal.new("1"), type.type_cast_from_user(:"1")
end
def test_type_cast_binary
type = Type::Binary.new
- assert_equal nil, type.type_cast(nil)
- assert_equal "1", type.type_cast("1")
- assert_equal 1, type.type_cast(1)
+ assert_equal nil, type.type_cast_from_user(nil)
+ assert_equal "1", type.type_cast_from_user("1")
+ assert_equal 1, type.type_cast_from_user(1)
end
def test_type_cast_time
type = Type::Time.new
- assert_equal nil, type.type_cast(nil)
- assert_equal nil, type.type_cast('')
- assert_equal nil, type.type_cast('ABC')
+ assert_equal nil, type.type_cast_from_user(nil)
+ assert_equal nil, type.type_cast_from_user('')
+ assert_equal nil, type.type_cast_from_user('ABC')
time_string = Time.now.utc.strftime("%T")
- assert_equal time_string, type.type_cast(time_string).strftime("%T")
+ assert_equal time_string, type.type_cast_from_user(time_string).strftime("%T")
end
def test_type_cast_datetime_and_timestamp
type = Type::DateTime.new
- assert_equal nil, type.type_cast(nil)
- assert_equal nil, type.type_cast('')
- assert_equal nil, type.type_cast(' ')
- assert_equal nil, type.type_cast('ABC')
+ assert_equal nil, type.type_cast_from_user(nil)
+ assert_equal nil, type.type_cast_from_user('')
+ assert_equal nil, type.type_cast_from_user(' ')
+ assert_equal nil, type.type_cast_from_user('ABC')
datetime_string = Time.now.utc.strftime("%FT%T")
- assert_equal datetime_string, type.type_cast(datetime_string).strftime("%FT%T")
+ assert_equal datetime_string, type.type_cast_from_user(datetime_string).strftime("%FT%T")
end
def test_type_cast_date
type = Type::Date.new
- assert_equal nil, type.type_cast(nil)
- assert_equal nil, type.type_cast('')
- assert_equal nil, type.type_cast(' ')
- assert_equal nil, type.type_cast('ABC')
+ assert_equal nil, type.type_cast_from_user(nil)
+ assert_equal nil, type.type_cast_from_user('')
+ assert_equal nil, type.type_cast_from_user(' ')
+ assert_equal nil, type.type_cast_from_user('ABC')
date_string = Time.now.utc.strftime("%F")
- assert_equal date_string, type.type_cast(date_string).strftime("%F")
+ assert_equal date_string, type.type_cast_from_user(date_string).strftime("%F")
end
def test_type_cast_duration_to_integer
type = Type::Integer.new
- assert_equal 1800, type.type_cast(30.minutes)
- assert_equal 7200, type.type_cast(2.hours)
+ assert_equal 1800, type.type_cast_from_user(30.minutes)
+ assert_equal 7200, type.type_cast_from_user(2.hours)
end
def test_string_to_time_with_timezone
[:utc, :local].each do |zone|
with_timezone_config default: zone do
type = Type::DateTime.new
- assert_equal Time.utc(2013, 9, 4, 0, 0, 0), type.type_cast("Wed, 04 Sep 2013 03:00:00 EAT")
+ assert_equal Time.utc(2013, 9, 4, 0, 0, 0), type.type_cast_from_user("Wed, 04 Sep 2013 03:00:00 EAT")
end
end
end
@@ -149,7 +149,7 @@ module ActiveRecord
def test_binary_encoding
type = SQLite3Binary.new
utf8_string = "a string".encode(Encoding::UTF_8)
- type_cast = type.type_cast(utf8_string)
+ type_cast = type.type_cast_from_user(utf8_string)
assert_equal Encoding::ASCII_8BIT, type_cast.encoding
end
diff --git a/activerecord/test/cases/yaml_serialization_test.rb b/activerecord/test/cases/yaml_serialization_test.rb
index d4f8ef5b4d..9f1d110ddb 100644
--- a/activerecord/test/cases/yaml_serialization_test.rb
+++ b/activerecord/test/cases/yaml_serialization_test.rb
@@ -1,8 +1,10 @@
require 'cases/helper'
require 'models/topic'
+require 'models/post'
+require 'models/author'
class YamlSerializationTest < ActiveRecord::TestCase
- fixtures :topics
+ fixtures :topics, :authors, :posts
def test_to_yaml_with_time_with_zone_should_not_raise_exception
with_timezone_config aware_attributes: true, zone: "Pacific Time (US & Canada)" do
@@ -69,4 +71,15 @@ class YamlSerializationTest < ActiveRecord::TestCase
assert_not topic.new_record?, "Loaded records without ID are not new"
assert_not YAML.load(YAML.dump(topic)).new_record?, "Record should not be new after deserialization"
end
+
+ def test_types_of_virtual_columns_are_not_changed_on_round_trip
+ author = Author.select('authors.*, count(posts.id) as posts_count')
+ .joins(:posts)
+ .group('authors.id')
+ .first
+ dumped = YAML.load(YAML.dump(author))
+
+ assert_equal 5, author.posts_count
+ assert_equal 5, dumped.posts_count
+ end
end
diff --git a/activerecord/test/schema/schema.rb b/activerecord/test/schema/schema.rb
index 5f459cf682..98fd0b6803 100644
--- a/activerecord/test/schema/schema.rb
+++ b/activerecord/test/schema/schema.rb
@@ -529,6 +529,7 @@ ActiveRecord::Schema.define do
t.references :best_friend
t.references :best_friend_of
t.integer :insures, null: false, default: 0
+ t.timestamp :born_at
t.timestamps
end
diff --git a/activesupport/CHANGELOG.md b/activesupport/CHANGELOG.md
index 871e1705f7..5962dd255c 100644
--- a/activesupport/CHANGELOG.md
+++ b/activesupport/CHANGELOG.md
@@ -1,3 +1,6 @@
+* Fixed `ActiveSupport::Cache::FileStore` exploding with long paths.
+ *Adam Panzer / Michael Grosser*
+
* Fixed `ActiveSupport::TimeWithZone#-` so precision is not unnecessarily lost
when working with objects with a nanosecond component.
diff --git a/activesupport/lib/active_support/cache/file_store.rb b/activesupport/lib/active_support/cache/file_store.rb
index 8ed60aebac..d08ecd2f7d 100644
--- a/activesupport/lib/active_support/cache/file_store.rb
+++ b/activesupport/lib/active_support/cache/file_store.rb
@@ -14,6 +14,7 @@ module ActiveSupport
DIR_FORMATTER = "%03X"
FILENAME_MAX_SIZE = 228 # max filename size on file system is 255, minus room for timestamp and random characters appended by Tempfile (used by atomic write)
+ FILEPATH_MAX_SIZE = 900 # max is 1024, plus some room
EXCLUDED_DIRS = ['.', '..'].freeze
def initialize(cache_path, options = nil)
@@ -117,6 +118,10 @@ module ActiveSupport
# Translate a key into a file path.
def key_file_path(key)
+ if key.size > FILEPATH_MAX_SIZE
+ key = Digest::MD5.hexdigest(key)
+ end
+
fname = URI.encode_www_form_component(key)
hash = Zlib.adler32(fname)
hash, dir_1 = hash.divmod(0x1000)
diff --git a/activesupport/lib/active_support/hash_with_indifferent_access.rb b/activesupport/lib/active_support/hash_with_indifferent_access.rb
index e782cfa2f5..e1eb81b8bc 100644
--- a/activesupport/lib/active_support/hash_with_indifferent_access.rb
+++ b/activesupport/lib/active_support/hash_with_indifferent_access.rb
@@ -57,7 +57,7 @@ module ActiveSupport
def initialize(constructor = {})
if constructor.respond_to?(:to_hash)
super()
- update(constructor.to_hash)
+ update(constructor)
else
super(constructor)
end
diff --git a/activesupport/lib/active_support/number_helper.rb b/activesupport/lib/active_support/number_helper.rb
index b169e3af01..5ecda9593a 100644
--- a/activesupport/lib/active_support/number_helper.rb
+++ b/activesupport/lib/active_support/number_helper.rb
@@ -232,12 +232,8 @@ module ActiveSupport
# number_to_human_size(1234567, precision: 2) # => 1.2 MB
# number_to_human_size(483989, precision: 2) # => 470 KB
# number_to_human_size(1234567, precision: 2, separator: ',') # => 1,2 MB
- #
- # Non-significant zeros after the fractional separator are stripped out by
- # default (set <tt>:strip_insignificant_zeros</tt> to +false+ to change that):
- #
- # number_to_human_size(1234567890123, precision: 5) # => "1.1229 TB"
- # number_to_human_size(524288000, precision: 5) # => "500 MB"
+ # number_to_human_size(1234567890123, precision: 5) # => "1.1228 TB"
+ # number_to_human_size(524288000, precision: 5) # => "500 MB"
def number_to_human_size(number, options = {})
NumberToHumanSizeConverter.convert(number, options)
end
@@ -305,12 +301,15 @@ module ActiveSupport
# separator: ',',
# significant: false) # => "1,2 Million"
#
+ # number_to_human(500000000, precision: 5) # => "500 Million"
+ # number_to_human(12345012345, significant: false) # => "12.345 Billion"
+ #
# Non-significant zeros after the decimal separator are stripped
# out by default (set <tt>:strip_insignificant_zeros</tt> to
# +false+ to change that):
#
- # number_to_human(12345012345, significant_digits: 6) # => "12.345 Billion"
- # number_to_human(500000000, precision: 5) # => "500 Million"
+ # number_to_human(12.00001) # => "12"
+ # number_to_human(12.00001, strip_insignificant_zeros: false) # => "12.0"
#
# ==== Custom Unit Quantifiers
#
diff --git a/activesupport/lib/active_support/testing/declarative.rb b/activesupport/lib/active_support/testing/declarative.rb
index e709e6edf5..0bf3643a56 100644
--- a/activesupport/lib/active_support/testing/declarative.rb
+++ b/activesupport/lib/active_support/testing/declarative.rb
@@ -1,30 +1,6 @@
module ActiveSupport
module Testing
module Declarative
-
- def self.extended(klass) #:nodoc:
- klass.class_eval do
-
- unless method_defined?(:describe)
- def self.describe(text)
- if block_given?
- super
- else
- message = "`describe` without a block is deprecated, please switch to: `def self.name; #{text.inspect}; end`\n"
- ActiveSupport::Deprecation.warn message
-
- class_eval <<-RUBY_EVAL, __FILE__, __LINE__ + 1
- def self.name
- "#{text}"
- end
- RUBY_EVAL
- end
- end
- end
-
- end
- end
-
unless defined?(Spec)
# Helper to define a test method using a String. Under the hood, it replaces
# spaces with underscores and defines the test method.
diff --git a/activesupport/test/caching_test.rb b/activesupport/test/caching_test.rb
index 18923f61d1..d55cc5d3b0 100644
--- a/activesupport/test/caching_test.rb
+++ b/activesupport/test/caching_test.rb
@@ -692,6 +692,11 @@ class FileStoreTest < ActiveSupport::TestCase
assert File.exist?(filepath)
end
+ def test_long_keys
+ @cache.write("a"*10000, 1)
+ assert_equal 1, @cache.read("a"*10000)
+ end
+
def test_key_transformation
key = @cache.send(:key_file_path, "views/index?id=1")
assert_equal "views/index?id=1", @cache.send(:file_path_key, key)
diff --git a/activesupport/test/core_ext/hash_ext_test.rb b/activesupport/test/core_ext/hash_ext_test.rb
index cd0cb1a144..f18172a5ce 100644
--- a/activesupport/test/core_ext/hash_ext_test.rb
+++ b/activesupport/test/core_ext/hash_ext_test.rb
@@ -659,6 +659,14 @@ class HashExtTest < ActiveSupport::TestCase
assert_equal 1, h['first']
end
+ def test_to_options_on_indifferent_preserves_works_as_hash_with_dup
+ h = HashWithIndifferentAccess.new({ a: { b: 'b' } })
+ dup = h.dup
+
+ dup[:a][:c] = 'c'
+ assert_equal 'c', h[:a][:c]
+ end
+
def test_indifferent_sub_hashes
h = {'user' => {'id' => 5}}.with_indifferent_access
['user', :user].each {|user| [:id, 'id'].each {|id| assert_equal 5, h[user][id], "h[#{user.inspect}][#{id.inspect}] should be 5"}}
diff --git a/activesupport/test/core_ext/blank_test.rb b/activesupport/test/core_ext/object/blank_test.rb
index 246bc7fa61..246bc7fa61 100644
--- a/activesupport/test/core_ext/blank_test.rb
+++ b/activesupport/test/core_ext/object/blank_test.rb
diff --git a/activesupport/test/core_ext/object/inclusion_test.rb b/activesupport/test/core_ext/object/inclusion_test.rb
index b054a8dd31..32d512eca3 100644
--- a/activesupport/test/core_ext/object/inclusion_test.rb
+++ b/activesupport/test/core_ext/object/inclusion_test.rb
@@ -37,11 +37,14 @@ class InTest < ActiveSupport::TestCase
end
class C < B
end
+ class D
+ end
def test_in_module
assert A.in?(B)
assert A.in?(C)
assert !A.in?(A)
+ assert !A.in?(D)
end
def test_no_method_catching
@@ -51,5 +54,6 @@ class InTest < ActiveSupport::TestCase
def test_presence_in
assert_equal "stuff", "stuff".presence_in(%w( lots of stuff ))
assert_nil "stuff".presence_in(%w( lots of crap ))
+ assert_raise(ArgumentError) { 1.presence_in(1) }
end
end
diff --git a/guides/source/action_mailer_basics.md b/guides/source/action_mailer_basics.md
index c67f6188c4..c7117027c0 100644
--- a/guides/source/action_mailer_basics.md
+++ b/guides/source/action_mailer_basics.md
@@ -17,7 +17,10 @@ After reading this guide, you will know:
Introduction
------------
-Action Mailer allows you to send emails from your application using mailer classes and views. Mailers work very similarly to controllers. They inherit from `ActionMailer::Base` and live in `app/mailers`, and they have associated views that appear in `app/views`.
+Action Mailer allows you to send emails from your application using mailer classes
+and views. Mailers work very similarly to controllers. They inherit from
+`ActionMailer::Base` and live in `app/mailers`, and they have associated views
+that appear in `app/views`.
Sending Emails
--------------
@@ -84,8 +87,11 @@ Here is a quick explanation of the items presented in the preceding method. For
a full list of all available options, please have a look further down at the
Complete List of Action Mailer user-settable attributes section.
-* `default Hash` - This is a hash of default values for any email you send from this mailer. In this case we are setting the `:from` header to a value for all messages in this class. This can be overridden on a per-email basis.
-* `mail` - The actual email message, we are passing the `:to` and `:subject` headers in.
+* `default Hash` - This is a hash of default values for any email you send from
+this mailer. In this case we are setting the `:from` header to a value for all
+messages in this class. This can be overridden on a per-email basis.
+* `mail` - The actual email message, we are passing the `:to` and `:subject`
+headers in.
Just like controllers, any instance variables we define in the method become
available for use in the views.
@@ -151,7 +157,7 @@ $ bin/rake db:migrate
```
Now that we have a user model to play with, we will just edit the
-`app/controllers/users_controller.rb` make it instruct the UserMailer to deliver
+`app/controllers/users_controller.rb` make it instruct the `UserMailer` to deliver
an email to the newly created user by editing the create action and inserting a
call to `UserMailer.welcome_email` right after the user is successfully saved:
@@ -230,9 +236,11 @@ different, encode your content and pass in the encoded content and encoding in a
```ruby
encoded_content = SpecialEncode(File.read('/path/to/filename.jpg'))
- attachments['filename.jpg'] = {mime_type: 'application/x-gzip',
- encoding: 'SpecialEncoding',
- content: encoded_content }
+ attachments['filename.jpg'] = {
+ mime_type: 'application/x-gzip',
+ encoding: 'SpecialEncoding',
+ content: encoded_content
+ }
```
NOTE: If you specify an encoding, Mail will assume that your content is already
@@ -608,7 +616,7 @@ files (environment.rb, production.rb, etc...)
| Configuration | Description |
|---------------|-------------|
|`logger`|Generates information on the mailing run if available. Can be set to `nil` for no logging. Compatible with both Ruby's own `Logger` and `Log4r` loggers.|
-|`smtp_settings`|Allows detailed configuration for `:smtp` delivery method:<ul><li>`:address` - Allows you to use a remote mail server. Just change it from its default "localhost" setting.</li><li>`:port` - On the off chance that your mail server doesn't run on port 25, you can change it.</li><li>`:domain` - If you need to specify a HELO domain, you can do it here.</li><li>`:user_name` - If your mail server requires authentication, set the username in this setting.</li><li>`:password` - If your mail server requires authentication, set the password in this setting.</li><li>`:authentication` - If your mail server requires authentication, you need to specify the authentication type here. This is a symbol and one of `:plain`, `:login`, `:cram_md5`.</li><li>`:enable_starttls_auto` - Set this to `false` if there is a problem with your server certificate that you cannot resolve.</li></ul>|
+|`smtp_settings`|Allows detailed configuration for `:smtp` delivery method:<ul><li>`:address` - Allows you to use a remote mail server. Just change it from its default `"localhost"` setting.</li><li>`:port` - On the off chance that your mail server doesn't run on port 25, you can change it.</li><li>`:domain` - If you need to specify a HELO domain, you can do it here.</li><li>`:user_name` - If your mail server requires authentication, set the username in this setting.</li><li>`:password` - If your mail server requires authentication, set the password in this setting.</li><li>`:authentication` - If your mail server requires authentication, you need to specify the authentication type here. This is a symbol and one of `:plain`, `:login`, `:cram_md5`.</li><li>`:enable_starttls_auto` - Set this to `false` if there is a problem with your server certificate that you cannot resolve.</li></ul>|
|`sendmail_settings`|Allows you to override options for the `:sendmail` delivery method.<ul><li>`:location` - The location of the sendmail executable. Defaults to `/usr/sbin/sendmail`.</li><li>`:arguments` - The command line arguments to be passed to sendmail. Defaults to `-i -t`.</li></ul>|
|`raise_delivery_errors`|Whether or not errors should be raised if the email fails to be delivered. This only works if the external email server is configured for immediate delivery.|
|`delivery_method`|Defines a delivery method. Possible values are:<ul><li>`:smtp` (default), can be configured by using `config.action_mailer.smtp_settings`.</li><li>`:sendmail`, can be configured by using `config.action_mailer.sendmail_settings`.</li><li>`:file`: save emails to files; can be configured by using `config.action_mailer.file_settings`.</li><li>`:test`: save emails to `ActionMailer::Base.deliveries` array.</li></ul>See [API docs](http://api.rubyonrails.org/classes/ActionMailer/Base.html) for more info.|
@@ -617,7 +625,7 @@ files (environment.rb, production.rb, etc...)
|`default_options`|Allows you to set default values for the `mail` method options (`:from`, `:reply_to`, etc.).|
For a complete writeup of possible configurations see the
-[Action Mailer section](configuring.html#configuring-action-mailer) in
+[Configuring Action Mailer](configuring.html#configuring-action-mailer) in
our Configuring Rails Applications guide.
### Example Action Mailer Configuration
@@ -662,6 +670,7 @@ You can find detailed instructions on how to test your mailers in the
Intercepting Emails
-------------------
+
There are situations where you need to edit an email before it's
delivered. Fortunately Action Mailer provides hooks to intercept every
email. You can register an interceptor to make modifications to mail messages
@@ -685,5 +694,5 @@ ActionMailer::Base.register_interceptor(SandboxEmailInterceptor) if Rails.env.st
NOTE: The example above uses a custom environment called "staging" for a
production like server but for testing purposes. You can read
-[Creating Rails environments](./configuring.html#creating-rails-environments)
+[Creating Rails environments](configuring.html#creating-rails-environments)
for more information about custom Rails environments.
diff --git a/guides/source/migrations.md b/guides/source/active_record_migrations.md
index 9568bfcdb8..5a550d9e55 100644
--- a/guides/source/migrations.md
+++ b/guides/source/active_record_migrations.md
@@ -293,17 +293,10 @@ end
You can append as many column name/type pairs as you want.
-### Supported Type Modifiers
+### Passing Modifiers
-You can also specify some options just after the field type between curly
-braces. You can use the following modifiers:
-
-* `limit` Sets the maximum size of the `string/text/binary/integer` fields.
-* `precision` Defines the precision for the `decimal` fields, representing the total number of digits in the number.
-* `scale` Defines the scale for the `decimal` fields, representing the number of digits after the decimal point.
-* `polymorphic` Adds a `type` column for `belongs_to` associations.
-* `null` Allows or disallows `NULL` values in the column.
-* `default` Allows to set a default value on the column. NOTE: If using a dynamic value (such as date), the default will only be calculated the first time (e.g. on the date the migration is applied.)
+Some commonly used [type modifiers](#column-modifiers) can be passed directly on
+the command line. They are enclosed by curly braces and follow the field type:
For instance, running:
@@ -322,6 +315,8 @@ class AddDetailsToProducts < ActiveRecord::Migration
end
```
+TIP: Have a look at the generators help output for further details.
+
Writing a Migration
-------------------
@@ -439,6 +434,21 @@ change_column_default :products, :approved, false
This sets `:name` field on products to a `NOT NULL` column and the default
value of the `:approved` field to false.
+### Column Modifiers
+
+Column modifiers can be applied when creating or changing a column:
+
+* `limit` Sets the maximum size of the `string/text/binary/integer` fields.
+* `precision` Defines the precision for the `decimal` fields, representing the total number of digits in the number.
+* `scale` Defines the scale for the `decimal` fields, representing the number of digits after the decimal point.
+* `polymorphic` Adds a `type` column for `belongs_to` associations.
+* `null` Allows or disallows `NULL` values in the column.
+* `default` Allows to set a default value on the column. NOTE: If using a dynamic value (such as date), the default will only be calculated the first time (e.g. on the date the migration is applied.)
+* `index` Adds an index for the column.
+
+Some adapters may support additional options; see the adapter specific API docs
+for further information.
+
### When Helpers aren't Enough
If the helpers provided by Active Record aren't enough you can use the `execute`
diff --git a/guides/source/asset_pipeline.md b/guides/source/asset_pipeline.md
index c81e9e58e3..2d1548f252 100644
--- a/guides/source/asset_pipeline.md
+++ b/guides/source/asset_pipeline.md
@@ -709,7 +709,7 @@ The default matcher for compiling files includes `application.js`,
automatically) from `app/assets` folders including your gems:
```ruby
-[ Proc.new { |path, fn| fn =~ /app\/assets/ && !%w(.js .css).include?(File.extname(path)) },
+[ Proc.new { |filename, path| path =~ /app\/assets/ && !%w(.js .css).include?(File.extname(filename)) },
/application.(css|js)$/ ]
```
@@ -788,9 +788,11 @@ For Apache:
# `mod_expires` to be enabled.
<Location /assets/>
# Use of ETag is discouraged when Last-Modified is present
- Header unset ETag FileETag None
+ Header unset ETag
+ FileETag None
# RFC says only cache for 1 year
- ExpiresActive On ExpiresDefault "access plus 1 year"
+ ExpiresActive On
+ ExpiresDefault "access plus 1 year"
</Location>
```
@@ -854,10 +856,12 @@ duplication of work.
Local compilation allows you to commit the compiled files into source control,
and deploy as normal.
-There are two caveats:
+There are three caveats:
* You must not run the Capistrano deployment task that precompiles assets.
-* You must change the following two application configuration settings.
+* You must ensure any necessary compressors or minifiers are
+available on your development system.
+* You must change the following application configuration setting:
In `config/environments/development.rb`, place the following line:
@@ -871,9 +875,6 @@ development mode, and pass all requests to Sprockets. The prefix is still set to
would serve the precompiled assets from `/assets` in development, and you would
not see any local changes until you compile assets again.
-You will also need to ensure any necessary compressors or minifiers are
-available on your development system.
-
In practice, this will allow you to precompile locally, have those files in your
working tree, and commit those files to source control when needed. Development
mode will work as expected.
diff --git a/guides/source/documents.yaml b/guides/source/documents.yaml
index 5138412312..82e248ee38 100644
--- a/guides/source/documents.yaml
+++ b/guides/source/documents.yaml
@@ -13,8 +13,8 @@
url: active_record_basics.html
description: This guide will get you started with models, persistence to database and the Active Record pattern and library.
-
- name: Rails Database Migrations
- url: migrations.html
+ name: Active Record Migrations
+ url: active_record_migrations.html
description: This guide covers how you can use Active Record migrations to alter your database in a structured and organized manner.
-
name: Active Record Validations
diff --git a/guides/source/getting_started.md b/guides/source/getting_started.md
index e088abc150..3122a3dacb 100644
--- a/guides/source/getting_started.md
+++ b/guides/source/getting_started.md
@@ -74,9 +74,8 @@ literally follow along step by step. You can get the complete code
[here](https://github.com/rails/docrails/tree/master/guides/code/getting_started).
By following along with this guide, you'll create a Rails project called
-`blog`, a
-(very) simple weblog. Before you can start building the application, you need to
-make sure that you have Rails itself installed.
+`blog`, a (very) simple weblog. Before you can start building the application,
+you need to make sure that you have Rails itself installed.
TIP: The examples below use `$` to represent your terminal prompt in a UNIX-like OS,
though it may have been customized to appear differently. If you are using Windows,
@@ -167,7 +166,7 @@ of the files and folders that Rails created by default:
|config/|Configure your application's routes, database, and more. This is covered in more detail in [Configuring Rails Applications](configuring.html).|
|config.ru|Rack configuration for Rack based servers used to start the application.|
|db/|Contains your current database schema, as well as the database migrations.|
-|Gemfile<br>Gemfile.lock|These files allow you to specify what gem dependencies are needed for your Rails application. These files are used by the Bundler gem. For more information about Bundler, see [the Bundler website](http://gembundler.com).|
+|Gemfile<br>Gemfile.lock|These files allow you to specify what gem dependencies are needed for your Rails application. These files are used by the Bundler gem. For more information about Bundler, see [the Bundler website](http://bundler.io).|
|lib/|Extended modules for your application.|
|log/|Application log files.|
|public/|The only folder seen by the world as-is. Contains static files and compiled assets.|
diff --git a/railties/test/rails_info_test.rb b/railties/test/rails_info_test.rb
index 44a5fd1904..4bec302ff8 100644
--- a/railties/test/rails_info_test.rb
+++ b/railties/test/rails_info_test.rb
@@ -66,16 +66,6 @@ class InfoTest < ActiveSupport::TestCase
end
protected
- def svn_info=(info)
- Rails::Info.module_eval do
- class << self
- def svn_info
- info
- end
- end
- end
- end
-
def properties
Rails::Info.properties
end