aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Gemfile1
-rw-r--r--actionmailer/README.rdoc6
-rw-r--r--actionmailer/actionmailer.gemspec2
-rw-r--r--actionmailer/lib/action_mailer/test_case.rb8
-rw-r--r--actionmailer/test/i18n_with_controller_test.rb4
-rw-r--r--actionpack/CHANGELOG.md6
-rw-r--r--actionpack/README.rdoc7
-rw-r--r--actionpack/lib/abstract_controller/callbacks.rb33
-rw-r--r--actionpack/lib/action_controller/metal/redirecting.rb2
-rw-r--r--actionpack/lib/action_controller/metal/streaming.rb2
-rw-r--r--actionpack/lib/action_controller/metal/strong_parameters.rb16
-rw-r--r--actionpack/lib/action_dispatch/http/headers.rb29
-rw-r--r--actionpack/lib/action_dispatch/http/parameters.rb1
-rw-r--r--actionpack/lib/action_dispatch/http/request.rb4
-rw-r--r--actionpack/lib/action_dispatch/middleware/remote_ip.rb4
-rw-r--r--actionpack/lib/action_dispatch/routing/mapper.rb246
-rw-r--r--actionpack/lib/action_dispatch/routing/redirection.rb2
-rw-r--r--actionpack/test/abstract/callbacks_test.rb8
-rw-r--r--actionpack/test/controller/parameters/parameters_permit_test.rb2
-rw-r--r--actionpack/test/controller/routing_test.rb27
-rw-r--r--actionpack/test/controller/send_file_test.rb6
-rw-r--r--actionpack/test/dispatch/mapper_test.rb2
-rw-r--r--actionpack/test/dispatch/mime_type_test.rb2
-rw-r--r--actionpack/test/dispatch/reloader_test.rb5
-rw-r--r--actionpack/test/dispatch/request/multipart_params_parsing_test.rb4
-rw-r--r--actionpack/test/dispatch/request/query_string_parsing_test.rb3
-rw-r--r--actionpack/test/dispatch/request/url_encoded_params_parsing_test.rb5
-rw-r--r--actionpack/test/dispatch/request_test.rb3
-rw-r--r--actionpack/test/dispatch/response_test.rb6
-rw-r--r--actionpack/test/dispatch/routing_test.rb98
-rw-r--r--actionview/README.rdoc7
-rw-r--r--actionview/lib/action_view/base.rb2
-rw-r--r--actionview/lib/action_view/helpers/asset_tag_helper.rb33
-rw-r--r--actionview/lib/action_view/helpers/capture_helper.rb9
-rw-r--r--actionview/lib/action_view/helpers/form_helper.rb3
-rw-r--r--actionview/lib/action_view/helpers/form_tag_helper.rb10
-rw-r--r--actionview/lib/action_view/helpers/output_safety_helper.rb4
-rw-r--r--actionview/lib/action_view/helpers/tag_helper.rb4
-rw-r--r--actionview/lib/action_view/helpers/url_helper.rb4
-rw-r--r--actionview/test/template/capture_helper_test.rb23
-rw-r--r--actionview/test/template/number_helper_test.rb1
-rw-r--r--actionview/test/template/output_buffer_test.rb59
-rw-r--r--activemodel/README.rdoc7
-rw-r--r--activerecord/CHANGELOG.md56
-rw-r--r--activerecord/README.rdoc7
-rw-r--r--activerecord/lib/active_record/aggregations.rb12
-rw-r--r--activerecord/lib/active_record/associations/association_scope.rb2
-rw-r--r--activerecord/lib/active_record/associations/has_many_through_association.rb2
-rw-r--r--activerecord/lib/active_record/associations/join_dependency.rb2
-rw-r--r--activerecord/lib/active_record/associations/preloader/association.rb4
-rw-r--r--activerecord/lib/active_record/associations/through_association.rb5
-rw-r--r--activerecord/lib/active_record/attribute_assignment.rb7
-rw-r--r--activerecord/lib/active_record/attribute_methods.rb52
-rw-r--r--activerecord/lib/active_record/attribute_methods/dirty.rb28
-rw-r--r--activerecord/lib/active_record/attribute_methods/read.rb2
-rw-r--r--activerecord/lib/active_record/attribute_methods/serialization.rb59
-rw-r--r--activerecord/lib/active_record/attribute_methods/write.rb20
-rw-r--r--activerecord/lib/active_record/connection_adapters/abstract/quoting.rb67
-rw-r--r--activerecord/lib/active_record/connection_adapters/abstract/schema_definitions.rb4
-rw-r--r--activerecord/lib/active_record/connection_adapters/abstract_adapter.rb5
-rw-r--r--activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb7
-rw-r--r--activerecord/lib/active_record/connection_adapters/column.rb10
-rw-r--r--activerecord/lib/active_record/connection_adapters/postgresql/cast.rb31
-rw-r--r--activerecord/lib/active_record/connection_adapters/postgresql/oid.rb1
-rw-r--r--activerecord/lib/active_record/connection_adapters/postgresql/oid/bit.rb13
-rw-r--r--activerecord/lib/active_record/connection_adapters/postgresql/oid/bit_varying.rb13
-rw-r--r--activerecord/lib/active_record/connection_adapters/postgresql/oid/cidr.rb20
-rw-r--r--activerecord/lib/active_record/connection_adapters/postgresql/oid/date_time.rb3
-rw-r--r--activerecord/lib/active_record/connection_adapters/postgresql/oid/float.rb3
-rw-r--r--activerecord/lib/active_record/connection_adapters/postgresql/oid/hstore.rb6
-rw-r--r--activerecord/lib/active_record/connection_adapters/postgresql/oid/json.rb6
-rw-r--r--activerecord/lib/active_record/connection_adapters/postgresql/oid/money.rb4
-rw-r--r--activerecord/lib/active_record/connection_adapters/postgresql/oid/point.rb6
-rw-r--r--activerecord/lib/active_record/connection_adapters/postgresql/quoting.rb47
-rw-r--r--activerecord/lib/active_record/connection_adapters/postgresql/schema_definitions.rb48
-rw-r--r--activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb70
-rw-r--r--activerecord/lib/active_record/connection_adapters/sqlite3_adapter.rb7
-rw-r--r--activerecord/lib/active_record/core.rb40
-rw-r--r--activerecord/lib/active_record/fixtures.rb2
-rw-r--r--activerecord/lib/active_record/migration.rb15
-rw-r--r--activerecord/lib/active_record/model_schema.rb16
-rw-r--r--activerecord/lib/active_record/persistence.rb6
-rw-r--r--activerecord/lib/active_record/properties.rb3
-rw-r--r--activerecord/lib/active_record/railties/databases.rake7
-rw-r--r--activerecord/lib/active_record/reflection.rb16
-rw-r--r--activerecord/lib/active_record/relation.rb9
-rw-r--r--activerecord/lib/active_record/relation/calculations.rb4
-rw-r--r--activerecord/lib/active_record/schema_migration.rb7
-rw-r--r--activerecord/lib/active_record/serializers/xml_serializer.rb8
-rw-r--r--activerecord/lib/active_record/type/binary.rb18
-rw-r--r--activerecord/lib/active_record/type/decimal.rb4
-rw-r--r--activerecord/lib/active_record/type/float.rb4
-rw-r--r--activerecord/lib/active_record/type/integer.rb4
-rw-r--r--activerecord/lib/active_record/type/numeric.rb36
-rw-r--r--activerecord/lib/active_record/type/serialized.rb37
-rw-r--r--activerecord/lib/active_record/type/string.rb4
-rw-r--r--activerecord/lib/active_record/type/value.rb15
-rw-r--r--activerecord/lib/active_record/validations/uniqueness.rb7
-rw-r--r--activerecord/test/cases/adapters/postgresql/array_test.rb2
-rw-r--r--activerecord/test/cases/adapters/postgresql/bit_string_test.rb80
-rw-r--r--activerecord/test/cases/adapters/postgresql/bytea_test.rb3
-rw-r--r--activerecord/test/cases/adapters/postgresql/citext_test.rb3
-rw-r--r--activerecord/test/cases/adapters/postgresql/composite_test.rb3
-rw-r--r--activerecord/test/cases/adapters/postgresql/datatype_test.rb35
-rw-r--r--activerecord/test/cases/adapters/postgresql/domain_test.rb2
-rw-r--r--activerecord/test/cases/adapters/postgresql/extension_migration_test.rb2
-rw-r--r--activerecord/test/cases/adapters/postgresql/full_text_test.rb3
-rw-r--r--activerecord/test/cases/adapters/postgresql/geometric_test.rb64
-rw-r--r--activerecord/test/cases/adapters/postgresql/hstore_test.rb45
-rw-r--r--activerecord/test/cases/adapters/postgresql/json_test.rb8
-rw-r--r--activerecord/test/cases/adapters/postgresql/ltree_test.rb2
-rw-r--r--activerecord/test/cases/adapters/postgresql/money_test.rb28
-rw-r--r--activerecord/test/cases/adapters/postgresql/network_test.rb3
-rw-r--r--activerecord/test/cases/adapters/postgresql/range_test.rb2
-rw-r--r--activerecord/test/cases/adapters/postgresql/timestamp_test.rb12
-rw-r--r--activerecord/test/cases/adapters/postgresql/xml_test.rb3
-rw-r--r--activerecord/test/cases/ar_schema_test.rb24
-rw-r--r--activerecord/test/cases/associations/eager_test.rb4
-rw-r--r--activerecord/test/cases/associations/has_and_belongs_to_many_associations_test.rb2
-rw-r--r--activerecord/test/cases/associations/inverse_associations_test.rb2
-rw-r--r--activerecord/test/cases/attribute_methods_test.rb2
-rw-r--r--activerecord/test/cases/base_test.rb2
-rw-r--r--activerecord/test/cases/core_test.rb68
-rw-r--r--activerecord/test/cases/defaults_test.rb5
-rw-r--r--activerecord/test/cases/dirty_test.rb28
-rw-r--r--activerecord/test/cases/migration_test.rb41
-rw-r--r--activerecord/test/cases/persistence_test.rb13
-rw-r--r--activerecord/test/cases/primary_keys_test.rb3
-rw-r--r--activerecord/test/cases/reflection_test.rb20
-rw-r--r--activerecord/test/cases/schema_dumper_test.rb31
-rw-r--r--activerecord/test/cases/serialized_attribute_test.rb13
-rw-r--r--activerecord/test/cases/store_test.rb14
-rw-r--r--activerecord/test/cases/xml_serialization_test.rb5
-rw-r--r--activerecord/test/cases/yaml_serialization_test.rb36
-rw-r--r--activerecord/test/models/owner.rb12
-rw-r--r--activerecord/test/schema/postgresql_specific_schema.rb17
-rw-r--r--activerecord/test/support/schema_dumping_helper.rb11
-rw-r--r--activesupport/CHANGELOG.md33
-rw-r--r--activesupport/README.rdoc7
-rw-r--r--activesupport/lib/active_support/core_ext/hash/indifferent_access.rb2
-rw-r--r--activesupport/lib/active_support/core_ext/string/output_safety.rb32
-rw-r--r--activesupport/lib/active_support/notifications.rb9
-rw-r--r--activesupport/lib/active_support/notifications/fanout.rb17
-rw-r--r--activesupport/lib/active_support/number_helper/number_to_rounded_converter.rb6
-rw-r--r--activesupport/lib/active_support/time_with_zone.rb2
-rw-r--r--activesupport/test/core_ext/hash_ext_test.rb8
-rw-r--r--activesupport/test/core_ext/time_with_zone_test.rb15
-rw-r--r--activesupport/test/number_helper_test.rb2
-rw-r--r--guides/code/getting_started/config/environments/production.rb8
-rw-r--r--guides/source/2_3_release_notes.md2
-rw-r--r--guides/source/3_0_release_notes.md2
-rw-r--r--guides/source/3_1_release_notes.md2
-rw-r--r--guides/source/action_controller_overview.md4
-rw-r--r--guides/source/active_record_querying.md36
-rw-r--r--guides/source/api_documentation_guidelines.md8
-rw-r--r--guides/source/asset_pipeline.md16
-rw-r--r--guides/source/caching_with_rails.md2
-rw-r--r--guides/source/configuring.md10
-rw-r--r--guides/source/getting_started.md4
-rw-r--r--guides/source/i18n.md18
-rw-r--r--guides/source/migrations.md23
-rw-r--r--guides/source/rails_on_rack.md2
-rw-r--r--guides/source/security.md2
-rw-r--r--railties/CHANGELOG.md4
-rw-r--r--railties/README.rdoc6
-rw-r--r--railties/lib/rails/generators.rb60
-rw-r--r--railties/lib/rails/generators/rails/app/templates/bin/setup2
-rw-r--r--railties/lib/rails/generators/rails/app/templates/config/databases/ibm_db.yml4
-rw-r--r--railties/lib/rails/generators/rails/app/templates/config/databases/oracle.yml2
-rw-r--r--railties/lib/rails/generators/rails/app/templates/config/environments/production.rb.tt8
-rw-r--r--railties/test/generators/shared_generator_tests.rb13
-rw-r--r--railties/test/generators_test.rb8
172 files changed, 1635 insertions, 998 deletions
diff --git a/Gemfile b/Gemfile
index 2a695df618..86ac145b73 100644
--- a/Gemfile
+++ b/Gemfile
@@ -14,6 +14,7 @@ gem 'coffee-rails', '~> 4.0.0'
gem 'arel', github: 'rails/arel', branch: 'master'
gem 'sprockets-rails', github: 'rails/sprockets-rails', branch: 'master'
gem 'i18n', github: 'svenfuchs/i18n', branch: 'master'
+gem 'mail', '~> 2.5.4'
# require: false so bcrypt is loaded only when has_secure_password is used.
# This is to avoid ActiveModel (and by extension the entire framework)
diff --git a/actionmailer/README.rdoc b/actionmailer/README.rdoc
index 67b64fe469..ceca912ada 100644
--- a/actionmailer/README.rdoc
+++ b/actionmailer/README.rdoc
@@ -156,7 +156,11 @@ API documentation is at
* http://api.rubyonrails.org
-Bug reports and feature requests can be filed with the rest for the Ruby on Rails project here:
+Bug reports can be filed for the Ruby on Rails project here:
* https://github.com/rails/rails/issues
+Feature requests should be discussed on the rails-core mailing list here:
+
+* https://groups.google.com/forum/?fromgroups#!forum/rubyonrails-core
+
diff --git a/actionmailer/actionmailer.gemspec b/actionmailer/actionmailer.gemspec
index 9b25feaf75..01d97b7213 100644
--- a/actionmailer/actionmailer.gemspec
+++ b/actionmailer/actionmailer.gemspec
@@ -22,5 +22,5 @@ Gem::Specification.new do |s|
s.add_dependency 'actionpack', version
s.add_dependency 'actionview', version
- s.add_dependency 'mail', '~> 2.5.4'
+ s.add_dependency 'mail', ['~> 2.5', '>= 2.5.4']
end
diff --git a/actionmailer/lib/action_mailer/test_case.rb b/actionmailer/lib/action_mailer/test_case.rb
index 207f949fe2..a5442c0316 100644
--- a/actionmailer/lib/action_mailer/test_case.rb
+++ b/actionmailer/lib/action_mailer/test_case.rb
@@ -20,6 +20,7 @@ module ActionMailer
class_attribute :_mailer_class
setup :initialize_test_deliveries
setup :set_expected_mail
+ teardown :restore_test_deliveries
end
module ClassMethods
@@ -54,8 +55,15 @@ module ActionMailer
protected
def initialize_test_deliveries
+ @old_delivery_method = ActionMailer::Base.delivery_method
+ @old_perform_deliveries = ActionMailer::Base.perform_deliveries
ActionMailer::Base.delivery_method = :test
ActionMailer::Base.perform_deliveries = true
+ end
+
+ def restore_test_deliveries
+ ActionMailer::Base.delivery_method = @old_delivery_method
+ ActionMailer::Base.perform_deliveries = @old_perform_deliveries
ActionMailer::Base.deliveries.clear
end
diff --git a/actionmailer/test/i18n_with_controller_test.rb b/actionmailer/test/i18n_with_controller_test.rb
index d502d42ffd..d9d588a950 100644
--- a/actionmailer/test/i18n_with_controller_test.rb
+++ b/actionmailer/test/i18n_with_controller_test.rb
@@ -32,6 +32,10 @@ class ActionMailerI18nWithControllerTest < ActionDispatch::IntegrationTest
Routes
end
+ teardown do
+ I18n.locale = I18n.default_locale
+ end
+
def test_send_mail
with_translation 'de', email_subject: '[Anmeldung] Willkommen' do
get '/test/send_mail'
diff --git a/actionpack/CHANGELOG.md b/actionpack/CHANGELOG.md
index 6a50565de5..3b20aec20d 100644
--- a/actionpack/CHANGELOG.md
+++ b/actionpack/CHANGELOG.md
@@ -1,6 +1,6 @@
-* Deprecate all *_filter callbacks in favor of *_action callbacks.
-
- *Rafael Mendonça França*
+* Routes specifying 'to:' must be a string that contains a "#" or a rack
+ application. Use of a symbol should be replaced with `action: symbol`.
+ Use of a string without a "#" should be replaced with `controller: string`.
* Fix URL generation with `:trailing_slash` such that it does not add
a trailing slash after `.:format`
diff --git a/actionpack/README.rdoc b/actionpack/README.rdoc
index 2f6575c3b5..02a24a7412 100644
--- a/actionpack/README.rdoc
+++ b/actionpack/README.rdoc
@@ -48,6 +48,11 @@ API documentation is at
* http://api.rubyonrails.org
-Bug reports and feature requests can be filed with the rest for the Ruby on Rails project here:
+Bug reports can be filed for the Ruby on Rails project here:
* https://github.com/rails/rails/issues
+
+Feature requests should be discussed on the rails-core mailing list here:
+
+* https://groups.google.com/forum/?fromgroups#!forum/rubyonrails-core
+
diff --git a/actionpack/lib/abstract_controller/callbacks.rb b/actionpack/lib/abstract_controller/callbacks.rb
index 252e297c69..ca5c80cd71 100644
--- a/actionpack/lib/abstract_controller/callbacks.rb
+++ b/actionpack/lib/abstract_controller/callbacks.rb
@@ -1,5 +1,3 @@
-require 'active_support/deprecation'
-
module AbstractController
module Callbacks
extend ActiveSupport::Concern
@@ -56,11 +54,7 @@ module AbstractController
skip_after_action(*names)
skip_around_action(*names)
end
-
- def skip_filter(*names)
- ActiveSupport::Deprecation.warn("#{callback}_filter is deprecated and will removed in Rails 5. Use #{callback}_action instead.")
- skip_action_callback(*names)
- end
+ alias_method :skip_filter, :skip_action_callback
# Take callback names and an optional callback proc, normalize them,
# then call the block with each callback. This allows us to abstract
@@ -175,22 +169,14 @@ module AbstractController
set_callback(:process_action, callback, name, options)
end
end
-
- define_method "#{callback}_filter" do |*names, &blk|
- ActiveSupport::Deprecation.warn("#{callback}_filter is deprecated and will removed in Rails 5. Use #{callback}_action instead.")
- send("#{callback}_action", *names, &blk)
- end
+ alias_method :"#{callback}_filter", :"#{callback}_action"
define_method "prepend_#{callback}_action" do |*names, &blk|
_insert_callbacks(names, blk) do |name, options|
set_callback(:process_action, callback, name, options.merge(:prepend => true))
end
end
-
- define_method "prepend_#{callback}_filter" do |*names, &blk|
- ActiveSupport::Deprecation.warn("prepend_#{callback}_filter is deprecated and will removed in Rails 5. Use prepend_#{callback}_action instead.")
- send("prepend_#{callback}_action", *names, &blk)
- end
+ alias_method :"prepend_#{callback}_filter", :"prepend_#{callback}_action"
# Skip a before, after or around callback. See _insert_callbacks
# for details on the allowed parameters.
@@ -199,18 +185,11 @@ module AbstractController
skip_callback(:process_action, callback, name, options)
end
end
-
- define_method "skip_#{callback}_filter" do |*names, &blk|
- ActiveSupport::Deprecation.warn("skip_#{callback}_filter is deprecated and will removed in Rails 5. Use skip_#{callback}_action instead.")
- send("skip_#{callback}_action", *names, &blk)
- end
+ alias_method :"skip_#{callback}_filter", :"skip_#{callback}_action"
# *_action is the same as append_*_action
- alias_method :"append_#{callback}_action", :"#{callback}_action" # alias_method :append_before_action, :before_action
- define_method "append_#{callback}_filter" do |*names, &blk|
- ActiveSupport::Deprecation.warn("append_#{callback}_filter is deprecated and will removed in Rails 5. Use append_#{callback}_action instead.")
- send("append_#{callback}_action", *names, &blk)
- end
+ alias_method :"append_#{callback}_action", :"#{callback}_action"
+ alias_method :"append_#{callback}_filter", :"#{callback}_action"
end
end
end
diff --git a/actionpack/lib/action_controller/metal/redirecting.rb b/actionpack/lib/action_controller/metal/redirecting.rb
index 136e086d0d..3feb737277 100644
--- a/actionpack/lib/action_controller/metal/redirecting.rb
+++ b/actionpack/lib/action_controller/metal/redirecting.rb
@@ -72,7 +72,7 @@ module ActionController
self.status = _extract_redirect_to_status(options, response_status)
self.location = _compute_redirect_to_location(options)
- self.response_body = "<html><body>You are being <a href=\"#{ERB::Util.h(location)}\">redirected</a>.</body></html>"
+ self.response_body = "<html><body>You are being <a href=\"#{ERB::Util.unwrapped_html_escape(location)}\">redirected</a>.</body></html>"
end
def _compute_redirect_to_location(options) #:nodoc:
diff --git a/actionpack/lib/action_controller/metal/streaming.rb b/actionpack/lib/action_controller/metal/streaming.rb
index 62d5931b45..04401cad7b 100644
--- a/actionpack/lib/action_controller/metal/streaming.rb
+++ b/actionpack/lib/action_controller/metal/streaming.rb
@@ -183,7 +183,7 @@ module ActionController #:nodoc:
# You may also want to configure other parameters like <tt>:tcp_nodelay</tt>.
# Please check its documentation for more information: http://unicorn.bogomips.org/Unicorn/Configurator.html#method-i-listen
#
- # If you are using Unicorn with Nginx, you may need to tweak Nginx.
+ # If you are using Unicorn with NGINX, you may need to tweak NGINX.
# Streaming should work out of the box on Rainbows.
#
# ==== Passenger
diff --git a/actionpack/lib/action_controller/metal/strong_parameters.rb b/actionpack/lib/action_controller/metal/strong_parameters.rb
index d86d49c9dc..0236af4a19 100644
--- a/actionpack/lib/action_controller/metal/strong_parameters.rb
+++ b/actionpack/lib/action_controller/metal/strong_parameters.rb
@@ -130,7 +130,7 @@ module ActionController
# looping in the common use case permit + mass-assignment. Defined in a
# method to instantiate it only if needed.
def converted_arrays
- @converted_arrays ||= Set.new
+ @converted_arrays ||= {}
end
# Returns +true+ if the parameter is permitted, +false+ otherwise.
@@ -158,8 +158,8 @@ module ActionController
def permit!
each_pair do |key, value|
value = convert_hashes_to_parameters(key, value)
- Array.wrap(value).each do |_|
- _.permit! if _.respond_to? :permit!
+ Array.wrap(value).each do |v|
+ v.permit! if v.respond_to? :permit!
end
end
@@ -333,15 +333,15 @@ module ActionController
private
def convert_hashes_to_parameters(key, value, assign_if_converted=true)
- converted = convert_value_to_parameters(value)
+ converted = convert_value_to_parameters(key, value)
self[key] = converted if assign_if_converted && !converted.equal?(value)
converted
end
- def convert_value_to_parameters(value)
- if value.is_a?(Array) && !converted_arrays.member?(value)
- converted = value.map { |_| convert_value_to_parameters(_) }
- converted_arrays << converted
+ def convert_value_to_parameters(key, value)
+ if value.is_a?(Array) && !converted_arrays.member?(key)
+ converted = value.map { |v| convert_value_to_parameters(nil, v) }
+ converted_arrays[key] = converted if key
converted
elsif value.is_a?(Parameters) || !value.is_a?(Hash)
value
diff --git a/actionpack/lib/action_dispatch/http/headers.rb b/actionpack/lib/action_dispatch/http/headers.rb
index 3e607bbde1..bc5410dc38 100644
--- a/actionpack/lib/action_dispatch/http/headers.rb
+++ b/actionpack/lib/action_dispatch/http/headers.rb
@@ -6,14 +6,27 @@ module ActionDispatch
# headers = ActionDispatch::Http::Headers.new(env)
# headers["Content-Type"] # => "text/plain"
class Headers
- CGI_VARIABLES = %w(
- CONTENT_TYPE CONTENT_LENGTH
- HTTPS AUTH_TYPE GATEWAY_INTERFACE
- PATH_INFO PATH_TRANSLATED QUERY_STRING
- REMOTE_ADDR REMOTE_HOST REMOTE_IDENT REMOTE_USER
- REQUEST_METHOD SCRIPT_NAME
- SERVER_NAME SERVER_PORT SERVER_PROTOCOL SERVER_SOFTWARE
- )
+ CGI_VARIABLES = Set.new(%W[
+ AUTH_TYPE
+ CONTENT_LENGTH
+ CONTENT_TYPE
+ GATEWAY_INTERFACE
+ HTTPS
+ PATH_INFO
+ PATH_TRANSLATED
+ QUERY_STRING
+ REMOTE_ADDR
+ REMOTE_HOST
+ REMOTE_IDENT
+ REMOTE_USER
+ REQUEST_METHOD
+ SCRIPT_NAME
+ SERVER_NAME
+ SERVER_PORT
+ SERVER_PROTOCOL
+ SERVER_SOFTWARE
+ ]).freeze
+
HTTP_HEADER = /\A[A-Za-z0-9-]+\z/
include Enumerable
diff --git a/actionpack/lib/action_dispatch/http/parameters.rb b/actionpack/lib/action_dispatch/http/parameters.rb
index 1ab11392ce..5f7627cf96 100644
--- a/actionpack/lib/action_dispatch/http/parameters.rb
+++ b/actionpack/lib/action_dispatch/http/parameters.rb
@@ -15,7 +15,6 @@ module ActionDispatch
query_parameters.dup
end
params.merge!(path_parameters)
- params.with_indifferent_access
end
end
alias :params :parameters
diff --git a/actionpack/lib/action_dispatch/http/request.rb b/actionpack/lib/action_dispatch/http/request.rb
index dfe258e463..4d4b443fb4 100644
--- a/actionpack/lib/action_dispatch/http/request.rb
+++ b/actionpack/lib/action_dispatch/http/request.rb
@@ -291,7 +291,7 @@ module ActionDispatch
# Override Rack's GET method to support indifferent access
def GET
- @env["action_dispatch.request.query_parameters"] ||= Utils.deep_munge((normalize_encode_params(super) || {}))
+ @env["action_dispatch.request.query_parameters"] ||= Utils.deep_munge(normalize_encode_params(super || {}))
rescue TypeError => e
raise ActionController::BadRequest.new(:query, e)
end
@@ -299,7 +299,7 @@ module ActionDispatch
# Override Rack's POST method to support indifferent access
def POST
- @env["action_dispatch.request.request_parameters"] ||= Utils.deep_munge((normalize_encode_params(super) || {}))
+ @env["action_dispatch.request.request_parameters"] ||= Utils.deep_munge(normalize_encode_params(super || {}))
rescue TypeError => e
raise ActionController::BadRequest.new(:request, e)
end
diff --git a/actionpack/lib/action_dispatch/middleware/remote_ip.rb b/actionpack/lib/action_dispatch/middleware/remote_ip.rb
index cbb066b092..6a79b4e859 100644
--- a/actionpack/lib/action_dispatch/middleware/remote_ip.rb
+++ b/actionpack/lib/action_dispatch/middleware/remote_ip.rb
@@ -11,7 +11,7 @@ module ActionDispatch
# Some Rack servers concatenate repeated headers, like {HTTP RFC 2616}[http://www.w3.org/Protocols/rfc2616/rfc2616-sec4.html#sec4.2]
# requires. Some Rack servers simply drop preceding headers, and only report
# the value that was {given in the last header}[http://andre.arko.net/2011/12/26/repeated-headers-and-ruby-web-servers].
- # If you are behind multiple proxy servers (like Nginx to HAProxy to Unicorn)
+ # If you are behind multiple proxy servers (like NGINX to HAProxy to Unicorn)
# then you should test your Rack server to make sure your data is good.
#
# IF YOU DON'T USE A PROXY, THIS MAKES YOU VULNERABLE TO IP SPOOFING.
@@ -118,7 +118,7 @@ module ActionDispatch
#
# REMOTE_ADDR will be correct if the request is made directly against the
# Ruby process, on e.g. Heroku. When the request is proxied by another
- # server like HAProxy or Nginx, the IP address that made the original
+ # server like HAProxy or NGINX, the IP address that made the original
# request will be put in an X-Forwarded-For header. If there are multiple
# proxies, that header may contain a list of IPs. Other proxy services
# set the Client-Ip header instead, so we check that too.
diff --git a/actionpack/lib/action_dispatch/routing/mapper.rb b/actionpack/lib/action_dispatch/routing/mapper.rb
index 3bc578b379..aac5546aa1 100644
--- a/actionpack/lib/action_dispatch/routing/mapper.rb
+++ b/actionpack/lib/action_dispatch/routing/mapper.rb
@@ -7,6 +7,7 @@ require 'active_support/core_ext/module/remove_method'
require 'active_support/inflector'
require 'action_dispatch/routing/redirection'
require 'action_dispatch/routing/endpoint'
+require 'active_support/deprecation'
module ActionDispatch
module Routing
@@ -60,32 +61,73 @@ module ActionDispatch
end
class Mapping #:nodoc:
- IGNORE_OPTIONS = [:to, :as, :via, :on, :constraints, :defaults, :only, :except, :anchor, :shallow, :shallow_path, :shallow_prefix, :format]
ANCHOR_CHARACTERS_REGEX = %r{\A(\\A|\^)|(\\Z|\\z|\$)\Z}
- attr_reader :scope, :options, :requirements, :conditions, :defaults
- attr_reader :to, :default_controller, :default_action
-
- def initialize(scope, path, options)
- @scope = scope
- @requirements, @conditions, @defaults = {}, {}, {}
+ attr_reader :requirements, :conditions, :defaults
+ attr_reader :to, :default_controller, :default_action, :as, :anchor
+ def self.build(scope, path, options)
options = scope[:options].merge(options) if scope[:options]
- @to = options[:to]
- @default_controller = options[:controller] || scope[:controller]
- @default_action = options[:action] || scope[:action]
- path = normalize_path! path, options[:format]
+ options.delete :only
+ options.delete :except
+ options.delete :shallow_path
+ options.delete :shallow_prefix
+ options.delete :shallow
+
+ defaults = (scope[:defaults] || {}).merge options.delete(:defaults) || {}
+
+ new scope, path, defaults, options
+ end
+
+ def initialize(scope, path, defaults, options)
+ @requirements, @conditions = {}, {}
+ @defaults = defaults
+
+ @to = options.delete :to
+ @default_controller = options.delete(:controller) || scope[:controller]
+ @default_action = options.delete(:action) || scope[:action]
+ @as = options.delete :as
+ @anchor = options.delete :anchor
+
+ formatted = options.delete :format
+ via = Array(options.delete(:via) { [] })
+ options_constraints = options.delete :constraints
+
+ path = normalize_path! path, formatted
ast = path_ast path
path_params = path_params ast
- @options = normalize_options!(options, path_params, ast)
- normalize_requirements!(path_params)
- normalize_conditions!(path_params, path, ast)
- normalize_defaults!
+
+ options = normalize_options!(options, formatted, path_params, ast, scope[:module])
+
+
+ split_constraints(path_params, scope[:constraints]) if scope[:constraints]
+ constraints = constraints(options, path_params)
+
+ split_constraints path_params, constraints
+
+ @blocks = blocks(options_constraints, scope[:blocks])
+
+ if options_constraints.is_a?(Hash)
+ split_constraints path_params, options_constraints
+ options_constraints.each do |key, default|
+ if URL_OPTIONS.include?(key) && (String === default || Fixnum === default)
+ @defaults[key] ||= default
+ end
+ end
+ end
+
+ normalize_format!(formatted)
+
+ @conditions[:path_info] = path
+ @conditions[:parsed_path_info] = ast
+
+ add_request_method(via, @conditions)
+ normalize_defaults!(options)
end
def to_route
- [ app, conditions, requirements, defaults, options[:as], options[:anchor] ]
+ [ app(@blocks), conditions, requirements, defaults, as, anchor ]
end
private
@@ -106,17 +148,17 @@ module ActionDispatch
format != false && !path.include?(':format') && !path.end_with?('/')
end
- def normalize_options!(options, path_params, path_ast)
+ def normalize_options!(options, formatted, path_params, path_ast, modyoule)
# Add a constraint for wildcard route to make it non-greedy and match the
# optional format part of the route by default
- if options[:format] != false
+ if formatted != false
path_ast.grep(Journey::Nodes::Star) do |node|
options[node.name.to_sym] ||= /.+?/
end
end
if path_params.include?(:controller)
- raise ArgumentError, ":controller segment is not allowed within a namespace block" if scope[:module]
+ raise ArgumentError, ":controller segment is not allowed within a namespace block" if modyoule
# Add a default constraint for :controller path segments that matches namespaced
# controllers with default routes like :controller/:action/:id(.:format), e.g:
@@ -128,23 +170,36 @@ module ActionDispatch
if to.respond_to? :call
options
else
- options.merge!(default_controller_and_action(path_params))
+ to_endpoint = split_to to
+ controller = to_endpoint[0] || default_controller
+ action = to_endpoint[1] || default_action
+
+ controller = add_controller_module(controller, modyoule)
+
+ options.merge! check_controller_and_action(path_params, controller, action)
end
end
- def normalize_requirements!(path_params)
- constraints.each do |key, requirement|
- next unless path_params.include?(key) || key == :controller
- verify_regexp_requirement(requirement) if requirement.is_a?(Regexp)
- @requirements[key] = requirement
+ def split_constraints(path_params, constraints)
+ constraints.each_pair do |key, requirement|
+ if path_params.include?(key) || key == :controller
+ verify_regexp_requirement(requirement) if requirement.is_a?(Regexp)
+ @requirements[key] = requirement
+ else
+ @conditions[key] = requirement
+ end
end
+ end
- if options[:format] == true
+ def normalize_format!(formatted)
+ if formatted == true
@requirements[:format] ||= /.+/
- elsif Regexp === options[:format]
- @requirements[:format] = options[:format]
- elsif String === options[:format]
- @requirements[:format] = Regexp.compile(options[:format])
+ elsif Regexp === formatted
+ @requirements[:format] = formatted
+ @defaults[:format] = nil
+ elsif String === formatted
+ @requirements[:format] = Regexp.compile(formatted)
+ @defaults[:format] = formatted
end
end
@@ -158,31 +213,12 @@ module ActionDispatch
end
end
- def normalize_defaults!
- @defaults.merge!(scope[:defaults]) if scope[:defaults]
- @defaults.merge!(options[:defaults]) if options[:defaults]
-
- options.each do |key, default|
- unless Regexp === default || IGNORE_OPTIONS.include?(key)
+ def normalize_defaults!(options)
+ options.each_pair do |key, default|
+ unless Regexp === default
@defaults[key] = default
end
end
-
- if options[:constraints].is_a?(Hash)
- options[:constraints].each do |key, default|
- if URL_OPTIONS.include?(key) && (String === default || Fixnum === default)
- @defaults[key] ||= default
- end
- end
- elsif options[:constraints]
- verify_callable_constraint(options[:constraints])
- end
-
- if Regexp === options[:format]
- @defaults[:format] = nil
- elsif String === options[:format]
- @defaults[:format] = options[:format]
- end
end
def verify_callable_constraint(callable_constraint)
@@ -191,41 +227,22 @@ module ActionDispatch
end
end
- def normalize_conditions!(path_params, path, ast)
- @conditions[:path_info] = path
- @conditions[:parsed_path_info] = ast
+ def add_request_method(via, conditions)
+ return if via == [:all]
- constraints.each do |key, condition|
- unless path_params.include?(key) || key == :controller
- @conditions[key] = condition
- end
- end
-
- required_defaults = []
- options.each do |key, required_default|
- unless path_params.include?(key) || IGNORE_OPTIONS.include?(key) || Regexp === required_default
- required_defaults << key
- end
- end
- @conditions[:required_defaults] = required_defaults
-
- via_all = options.delete(:via) if options[:via] == :all
-
- if !via_all && options[:via].blank?
+ if via.empty?
msg = "You should not use the `match` method in your router without specifying an HTTP method.\n" \
"If you want to expose your action to both GET and POST, add `via: [:get, :post]` option.\n" \
"If you want to expose your action to GET, use `get` in the router:\n" \
" Instead of: match \"controller#action\"\n" \
" Do: get \"controller#action\""
- raise msg
+ raise ArgumentError, msg
end
- if via = options[:via]
- @conditions[:request_method] = Array(via).map { |m| m.to_s.dasherize.upcase }
- end
+ conditions[:request_method] = via.map { |m| m.to_s.dasherize.upcase }
end
- def app
+ def app(blocks)
return to if Redirect === to
if to.respond_to?(:call)
@@ -239,13 +256,7 @@ module ActionDispatch
end
end
- def default_controller_and_action(path_params)
- controller, action = get_controller_and_action(default_controller,
- default_action,
- to,
- @scope[:module]
- )
-
+ def check_controller_and_action(path_params, controller, action)
hash = check_part(:controller, controller, path_params, {}) do |part|
translate_controller(part) {
message = "'#{part}' is not a supported controller name. This can lead to potential routing problems."
@@ -272,21 +283,30 @@ module ActionDispatch
hash
end
- def get_controller_and_action(controller, action, to, modyoule)
+ def split_to(to)
case to
- when Symbol then action = to.to_s
- when /#/ then controller, action = to.split('#')
- when String then controller = to
+ when Symbol
+ ActiveSupport::Deprecation.warn "defining a route where `to` is a symbol is deprecated. Please change \"to: :#{to}\" to \"action: :#{to}\""
+ [nil, to.to_s]
+ when /#/ then to.split('#')
+ when String
+ ActiveSupport::Deprecation.warn "defining a route where `to` is a controller without an action is deprecated. Please change \"to: :#{to}\" to \"controller: :#{to}\""
+ [to, nil]
+ else
+ []
end
+ end
+ def add_controller_module(controller, modyoule)
if modyoule && !controller.is_a?(Regexp)
if controller =~ %r{\A/}
- controller = controller[1..-1]
+ controller[1..-1]
else
- controller = [modyoule, controller].compact.join("/")
+ [modyoule, controller].compact.join("/")
end
+ else
+ controller
end
- [controller, action]
end
def translate_controller(controller)
@@ -296,24 +316,27 @@ module ActionDispatch
yield
end
- def blocks
- if options[:constraints].present? && !options[:constraints].is_a?(Hash)
- [options[:constraints]]
+ def blocks(options_constraints, scope_blocks)
+ if options_constraints && !options_constraints.is_a?(Hash)
+ verify_callable_constraint(options_constraints)
+ [options_constraints]
else
- scope[:blocks] || []
+ scope_blocks || []
end
end
- def constraints
- @constraints ||= {}.tap do |constraints|
- constraints.merge!(scope[:constraints]) if scope[:constraints]
-
- options.except(*IGNORE_OPTIONS).each do |key, option|
- constraints[key] = option if Regexp === option
+ def constraints(options, path_params)
+ constraints = {}
+ required_defaults = []
+ options.each_pair do |key, option|
+ if Regexp === option
+ constraints[key] = option
+ else
+ required_defaults << key unless path_params.include?(key)
end
-
- constraints.merge!(options[:constraints]) if options[:constraints].is_a?(Hash)
end
+ @conditions[:required_defaults] = required_defaults
+ constraints
end
def path_params(ast)
@@ -1445,7 +1468,20 @@ module ActionDispatch
if rest.empty? && Hash === path
options = path
path, to = options.find { |name, _value| name.is_a?(String) }
- options[:to] = to
+
+ case to
+ when Symbol
+ options[:action] = to
+ when String
+ if to =~ /#/
+ options[:to] = to
+ else
+ options[:controller] = to
+ end
+ else
+ options[:to] = to
+ end
+
options.delete(path)
paths = [path]
else
@@ -1515,7 +1551,7 @@ module ActionDispatch
options[:as] = name_for_action(options[:as], action)
end
- mapping = Mapping.new(@scope, URI.parser.escape(path), options)
+ mapping = Mapping.build(@scope, URI.parser.escape(path), options)
app, conditions, requirements, defaults, as, anchor = mapping.to_route
@set.add_route(app, conditions, requirements, defaults, as, anchor)
end
diff --git a/actionpack/lib/action_dispatch/routing/redirection.rb b/actionpack/lib/action_dispatch/routing/redirection.rb
index b1b39a5496..3c1c4fadf6 100644
--- a/actionpack/lib/action_dispatch/routing/redirection.rb
+++ b/actionpack/lib/action_dispatch/routing/redirection.rb
@@ -37,7 +37,7 @@ module ActionDispatch
uri.host ||= req.host
uri.port ||= req.port unless req.standard_port?
- body = %(<html><body>You are being <a href="#{ERB::Util.h(uri.to_s)}">redirected</a>.</body></html>)
+ body = %(<html><body>You are being <a href="#{ERB::Util.unwrapped_html_escape(uri.to_s)}">redirected</a>.</body></html>)
headers = {
'Location' => uri.to_s,
diff --git a/actionpack/test/abstract/callbacks_test.rb b/actionpack/test/abstract/callbacks_test.rb
index 07571602e4..8cba049485 100644
--- a/actionpack/test/abstract/callbacks_test.rb
+++ b/actionpack/test/abstract/callbacks_test.rb
@@ -267,11 +267,9 @@ module AbstractController
end
class AliasedCallbacks < ControllerWithCallbacks
- ActiveSupport::Deprecation.silence do
- before_filter :first
- after_filter :second
- around_filter :aroundz
- end
+ before_filter :first
+ after_filter :second
+ around_filter :aroundz
def first
@text = "Hello world"
diff --git a/actionpack/test/controller/parameters/parameters_permit_test.rb b/actionpack/test/controller/parameters/parameters_permit_test.rb
index 33a91d72d9..1856ecd42b 100644
--- a/actionpack/test/controller/parameters/parameters_permit_test.rb
+++ b/actionpack/test/controller/parameters/parameters_permit_test.rb
@@ -169,7 +169,7 @@ class ParametersPermitTest < ActiveSupport::TestCase
test 'arrays are converted at most once' do
params = ActionController::Parameters.new(foo: [{}])
- assert params[:foo].equal?(params[:foo])
+ assert_same params[:foo], params[:foo]
end
test "fetch doesnt raise ParameterMissing exception if there is a default" do
diff --git a/actionpack/test/controller/routing_test.rb b/actionpack/test/controller/routing_test.rb
index 9dc6d77012..660589a86e 100644
--- a/actionpack/test/controller/routing_test.rb
+++ b/actionpack/test/controller/routing_test.rb
@@ -243,6 +243,33 @@ class LegacyRouteSetTests < ActiveSupport::TestCase
assert_equal 'clients', get(URI('http://clients.example.org/'))
end
+ def test_scoped_lambda
+ scope_called = false
+ rs.draw do
+ scope '/foo', :constraints => lambda { |req| scope_called = true } do
+ get '/', :to => lambda { |env| [200, {}, %w{default}] }
+ end
+ end
+
+ assert_equal 'default', get(URI('http://www.example.org/foo/'))
+ assert scope_called, "scope constraint should be called"
+ end
+
+ def test_scoped_lambda_with_get_lambda
+ scope_called = false
+ inner_called = false
+
+ rs.draw do
+ scope '/foo', :constraints => lambda { |req| flunk "should not be called" } do
+ get '/', :constraints => lambda { |req| inner_called = true },
+ :to => lambda { |env| [200, {}, %w{default}] }
+ end
+ end
+
+ assert_equal 'default', get(URI('http://www.example.org/foo/'))
+ assert inner_called, "inner constraint should be called"
+ end
+
def test_empty_string_match
rs.draw do
get '/:username', :constraints => { :username => /[^\/]+/ },
diff --git a/actionpack/test/controller/send_file_test.rb b/actionpack/test/controller/send_file_test.rb
index 2c833aa2e5..73ed33ce2d 100644
--- a/actionpack/test/controller/send_file_test.rb
+++ b/actionpack/test/controller/send_file_test.rb
@@ -33,17 +33,11 @@ class SendFileTest < ActionController::TestCase
include TestFileUtils
def setup
- @mime_type_defined = defined? Mime::PNG
- Mime::Type.register "image/png", :png unless @mime_type_defined
@controller = SendFileController.new
@request = ActionController::TestRequest.new
@response = ActionController::TestResponse.new
end
- teardown do
- Mime::Type.unregister :png unless @mime_type_defined
- end
-
def test_file_nostream
@controller.options = { :stream => false }
response = nil
diff --git a/actionpack/test/dispatch/mapper_test.rb b/actionpack/test/dispatch/mapper_test.rb
index bf82e09f39..d8d3209dac 100644
--- a/actionpack/test/dispatch/mapper_test.rb
+++ b/actionpack/test/dispatch/mapper_test.rb
@@ -38,7 +38,7 @@ module ActionDispatch
def test_mapping_requirements
options = { :controller => 'foo', :action => 'bar', :via => :get }
- m = Mapper::Mapping.new({}, '/store/:name(*rest)', options)
+ m = Mapper::Mapping.build({}, '/store/:name(*rest)', options)
_, _, requirements, _ = m.to_route
assert_equal(/.+?/, requirements[:rest])
end
diff --git a/actionpack/test/dispatch/mime_type_test.rb b/actionpack/test/dispatch/mime_type_test.rb
index 981cf2426e..d29cc8473e 100644
--- a/actionpack/test/dispatch/mime_type_test.rb
+++ b/actionpack/test/dispatch/mime_type_test.rb
@@ -1,8 +1,6 @@
require 'abstract_unit'
class MimeTypeTest < ActiveSupport::TestCase
- Mime::Type.register "image/png", :png unless defined? Mime::PNG
- Mime::Type.register "application/pdf", :pdf unless defined? Mime::PDF
test "parse single" do
Mime::LOOKUP.keys.each do |mime_type|
diff --git a/actionpack/test/dispatch/reloader_test.rb b/actionpack/test/dispatch/reloader_test.rb
index ce9ccfcee8..62e8197e20 100644
--- a/actionpack/test/dispatch/reloader_test.rb
+++ b/actionpack/test/dispatch/reloader_test.rb
@@ -3,6 +3,11 @@ require 'abstract_unit'
class ReloaderTest < ActiveSupport::TestCase
Reloader = ActionDispatch::Reloader
+ teardown do
+ Reloader.reset_callbacks :prepare
+ Reloader.reset_callbacks :cleanup
+ end
+
def test_prepare_callbacks
a = b = c = nil
Reloader.to_prepare { |*args| a = b = c = 1 }
diff --git a/actionpack/test/dispatch/request/multipart_params_parsing_test.rb b/actionpack/test/dispatch/request/multipart_params_parsing_test.rb
index 2a2f92b5b3..2db3fee6bb 100644
--- a/actionpack/test/dispatch/request/multipart_params_parsing_test.rb
+++ b/actionpack/test/dispatch/request/multipart_params_parsing_test.rb
@@ -145,7 +145,7 @@ class MultipartParamsParsingTest < ActionDispatch::IntegrationTest
test "does not raise EOFError on GET request with multipart content-type" do
with_routing do |set|
set.draw do
- get ':action', to: 'multipart_params_parsing_test/test'
+ get ':action', controller: 'multipart_params_parsing_test/test'
end
headers = { "CONTENT_TYPE" => "multipart/form-data; boundary=AaB03x" }
get "/parse", {}, headers
@@ -174,7 +174,7 @@ class MultipartParamsParsingTest < ActionDispatch::IntegrationTest
def with_test_routing
with_routing do |set|
set.draw do
- post ':action', :to => 'multipart_params_parsing_test/test'
+ post ':action', :controller => 'multipart_params_parsing_test/test'
end
yield
end
diff --git a/actionpack/test/dispatch/request/query_string_parsing_test.rb b/actionpack/test/dispatch/request/query_string_parsing_test.rb
index d82493140f..4e99c26e03 100644
--- a/actionpack/test/dispatch/request/query_string_parsing_test.rb
+++ b/actionpack/test/dispatch/request/query_string_parsing_test.rb
@@ -105,6 +105,7 @@ class QueryStringParsingTest < ActionDispatch::IntegrationTest
end
test "perform_deep_munge" do
+ old_perform_deep_munge = ActionDispatch::Request::Utils.perform_deep_munge
ActionDispatch::Request::Utils.perform_deep_munge = false
begin
assert_parses({"action" => nil}, "action")
@@ -115,7 +116,7 @@ class QueryStringParsingTest < ActionDispatch::IntegrationTest
assert_parses({"action" => {"foo" => [{"bar" => nil}]}}, "action[foo][][bar]")
assert_parses({"action" => ['1',nil]}, "action[]=1&action[]")
ensure
- ActionDispatch::Request::Utils.perform_deep_munge = true
+ ActionDispatch::Request::Utils.perform_deep_munge = old_perform_deep_munge
end
end
diff --git a/actionpack/test/dispatch/request/url_encoded_params_parsing_test.rb b/actionpack/test/dispatch/request/url_encoded_params_parsing_test.rb
index 9a77454f30..1de05cbf09 100644
--- a/actionpack/test/dispatch/request/url_encoded_params_parsing_test.rb
+++ b/actionpack/test/dispatch/request/url_encoded_params_parsing_test.rb
@@ -130,10 +130,7 @@ class UrlEncodedParamsParsingTest < ActionDispatch::IntegrationTest
end
test "ambiguous params returns a bad request" do
- with_routing do |set|
- set.draw do
- post ':action', to: ::UrlEncodedParamsParsingTest::TestController
- end
+ with_test_routing do
post "/parse", "foo[]=bar&foo[4]=bar"
assert_response :bad_request
end
diff --git a/actionpack/test/dispatch/request_test.rb b/actionpack/test/dispatch/request_test.rb
index b48e8ab974..e4950a5d6b 100644
--- a/actionpack/test/dispatch/request_test.rb
+++ b/actionpack/test/dispatch/request_test.rb
@@ -805,6 +805,7 @@ class RequestFormat < BaseRequestTest
end
test "ignore_accept_header" do
+ old_ignore_accept_header = ActionDispatch::Request.ignore_accept_header
ActionDispatch::Request.ignore_accept_header = true
begin
@@ -834,7 +835,7 @@ class RequestFormat < BaseRequestTest
request.expects(:parameters).at_least_once.returns({:format => :json})
assert_equal [ Mime::JSON ], request.formats
ensure
- ActionDispatch::Request.ignore_accept_header = false
+ ActionDispatch::Request.ignore_accept_header = old_ignore_accept_header
end
end
end
diff --git a/actionpack/test/dispatch/response_test.rb b/actionpack/test/dispatch/response_test.rb
index 959a3bc5cd..187b9a2420 100644
--- a/actionpack/test/dispatch/response_test.rb
+++ b/actionpack/test/dispatch/response_test.rb
@@ -178,6 +178,7 @@ class ResponseTest < ActiveSupport::TestCase
end
test "read x_frame_options, x_content_type_options and x_xss_protection" do
+ original_default_headers = ActionDispatch::Response.default_headers
begin
ActionDispatch::Response.default_headers = {
'X-Frame-Options' => 'DENY',
@@ -193,11 +194,12 @@ class ResponseTest < ActiveSupport::TestCase
assert_equal('nosniff', resp.headers['X-Content-Type-Options'])
assert_equal('1;', resp.headers['X-XSS-Protection'])
ensure
- ActionDispatch::Response.default_headers = nil
+ ActionDispatch::Response.default_headers = original_default_headers
end
end
test "read custom default_header" do
+ original_default_headers = ActionDispatch::Response.default_headers
begin
ActionDispatch::Response.default_headers = {
'X-XX-XXXX' => 'Here is my phone number'
@@ -209,7 +211,7 @@ class ResponseTest < ActiveSupport::TestCase
assert_equal('Here is my phone number', resp.headers['X-XX-XXXX'])
ensure
- ActionDispatch::Response.default_headers = nil
+ ActionDispatch::Response.default_headers = original_default_headers
end
end
diff --git a/actionpack/test/dispatch/routing_test.rb b/actionpack/test/dispatch/routing_test.rb
index d6477e19bb..778dbfc74d 100644
--- a/actionpack/test/dispatch/routing_test.rb
+++ b/actionpack/test/dispatch/routing_test.rb
@@ -361,8 +361,8 @@ class TestRoutingMapper < ActionDispatch::IntegrationTest
draw do
controller(:global) do
get 'global/hide_notice'
- get 'global/export', :to => :export, :as => :export_request
- get '/export/:id/:file', :to => :export, :as => :export_download, :constraints => { :file => /.*/ }
+ get 'global/export', :action => :export, :as => :export_request
+ get '/export/:id/:file', :action => :export, :as => :export_download, :constraints => { :file => /.*/ }
get 'global/:action'
end
end
@@ -730,8 +730,8 @@ class TestRoutingMapper < ActionDispatch::IntegrationTest
draw do
resources :replies do
member do
- put :answer, :to => :mark_as_answer
- delete :answer, :to => :unmark_as_answer
+ put :answer, :action => :mark_as_answer
+ delete :answer, :action => :unmark_as_answer
end
end
end
@@ -1188,7 +1188,7 @@ class TestRoutingMapper < ActionDispatch::IntegrationTest
controller :articles do
scope '/articles', :as => 'article' do
scope :path => '/:title', :title => /[a-z]+/, :as => :with_title do
- get '/:id', :to => :with_id, :as => ""
+ get '/:id', :action => :with_id, :as => ""
end
end
end
@@ -1435,7 +1435,7 @@ class TestRoutingMapper < ActionDispatch::IntegrationTest
def test_scoped_controller_with_namespace_and_action
draw do
namespace :account do
- get ':action/callback', :action => /twitter|github/, :to => "callbacks", :as => :callback
+ get ':action/callback', :action => /twitter|github/, :controller => "callbacks", :as => :callback
end
end
@@ -1492,7 +1492,7 @@ class TestRoutingMapper < ActionDispatch::IntegrationTest
def test_normalize_namespaced_matches
draw do
namespace :account do
- get 'description', :to => :description, :as => "description"
+ get 'description', :action => :description, :as => "description"
end
end
@@ -2154,7 +2154,7 @@ class TestRoutingMapper < ActionDispatch::IntegrationTest
end
resources :invoices do
get "outstanding" => "invoices#outstanding", :on => :collection
- get "overdue", :to => :overdue, :on => :collection
+ get "overdue", :action => :overdue, :on => :collection
get "print" => "invoices#print", :as => :print, :on => :member
post "preview" => "invoices#preview", :as => :preview, :on => :new
end
@@ -2242,6 +2242,22 @@ class TestRoutingMapper < ActionDispatch::IntegrationTest
assert_equal '/api/1.0/users/first.last.xml', api_user_path(:version => '1.0', :id => 'first.last', :format => :xml)
end
+ def test_match_without_via
+ assert_raises(ArgumentError) do
+ draw do
+ match '/foo/bar', :to => 'files#show'
+ end
+ end
+ end
+
+ def test_match_with_empty_via
+ assert_raises(ArgumentError) do
+ draw do
+ match '/foo/bar', :to => 'files#show', :via => []
+ end
+ end
+ end
+
def test_glob_parameter_accepts_regexp
draw do
get '/:locale/*file.:format', :to => 'files#show', :file => /path\/to\/existing\/file/
@@ -2980,7 +2996,9 @@ class TestRoutingMapper < ActionDispatch::IntegrationTest
end
assert_raise(ArgumentError) do
- draw { controller("/feeds") { get '/feeds/:service', :to => :show } }
+ assert_deprecated do
+ draw { controller("/feeds") { get '/feeds/:service', :to => :show } }
+ end
end
assert_raise(ArgumentError) do
@@ -3239,6 +3257,58 @@ class TestRoutingMapper < ActionDispatch::IntegrationTest
assert_equal '/admin/posts/1/comments', admin_post_comments_path('1')
end
+ def test_mix_string_to_controller_action
+ draw do
+ get '/projects', controller: 'project_files',
+ action: 'index',
+ to: 'comments#index'
+ end
+ get '/projects'
+ assert_equal 'comments#index', @response.body
+ end
+
+ def test_mix_string_to_controller
+ draw do
+ get '/projects', controller: 'project_files',
+ to: 'comments#index'
+ end
+ get '/projects'
+ assert_equal 'comments#index', @response.body
+ end
+
+ def test_mix_string_to_action
+ draw do
+ get '/projects', action: 'index',
+ to: 'comments#index'
+ end
+ get '/projects'
+ assert_equal 'comments#index', @response.body
+ end
+
+ def test_mix_symbol_to_controller_action
+ assert_deprecated do
+ draw do
+ get '/projects', controller: 'project_files',
+ action: 'index',
+ to: :show
+ end
+ end
+ get '/projects'
+ assert_equal 'project_files#show', @response.body
+ end
+
+ def test_mix_string_to_controller_action_no_hash
+ assert_deprecated do
+ draw do
+ get '/projects', controller: 'project_files',
+ action: 'index',
+ to: 'show'
+ end
+ end
+ get '/projects'
+ assert_equal 'show#index', @response.body
+ end
+
def test_shallow_path_and_prefix_are_not_added_to_non_shallow_routes
draw do
scope shallow_path: 'projects', shallow_prefix: 'project' do
@@ -3503,7 +3573,7 @@ class TestNamespaceWithControllerOption < ActionDispatch::IntegrationTest
def test_missing_controller
ex = assert_raises(ArgumentError) {
draw do
- get '/foo/bar', :to => :index
+ get '/foo/bar', :action => :index
end
}
assert_match(/Missing :controller/, ex.message)
@@ -3511,8 +3581,10 @@ class TestNamespaceWithControllerOption < ActionDispatch::IntegrationTest
def test_missing_action
ex = assert_raises(ArgumentError) {
- draw do
- get '/foo/bar', :to => 'foo'
+ assert_deprecated do
+ draw do
+ get '/foo/bar', :to => 'foo'
+ end
end
}
assert_match(/Missing :action/, ex.message)
@@ -4019,7 +4091,7 @@ class TestInvalidUrls < ActionDispatch::IntegrationTest
set.draw do
get "/bar/:id", :to => redirect("/foo/show/%{id}")
get "/foo/show(/:id)", :to => "test_invalid_urls/foo#show"
- get "/foo(/:action(/:id))", :to => "test_invalid_urls/foo"
+ get "/foo(/:action(/:id))", :controller => "test_invalid_urls/foo"
get "/:controller(/:action(/:id))"
end
diff --git a/actionview/README.rdoc b/actionview/README.rdoc
index 35f805346c..5bb62c7562 100644
--- a/actionview/README.rdoc
+++ b/actionview/README.rdoc
@@ -29,6 +29,11 @@ API documentation is at
* http://api.rubyonrails.org
-Bug reports and feature requests can be filed with the rest for the Ruby on Rails project here:
+Bug reports can be filed for the Ruby on Rails project here:
* https://github.com/rails/rails/issues
+
+Feature requests should be discussed on the rails-core mailing list here:
+
+* https://groups.google.com/forum/?fromgroups#!forum/rubyonrails-core
+
diff --git a/actionview/lib/action_view/base.rb b/actionview/lib/action_view/base.rb
index 455ce531ae..8235b30599 100644
--- a/actionview/lib/action_view/base.rb
+++ b/actionview/lib/action_view/base.rb
@@ -130,8 +130,6 @@ module ActionView #:nodoc:
# end
# end
# end
- #
- # More builder documentation can be found at http://builder.rubyforge.org.
class Base
include Helpers, ::ERB::Util, Context
diff --git a/actionview/lib/action_view/helpers/asset_tag_helper.rb b/actionview/lib/action_view/helpers/asset_tag_helper.rb
index f53cce32b0..7333ea999a 100644
--- a/actionview/lib/action_view/helpers/asset_tag_helper.rb
+++ b/actionview/lib/action_view/helpers/asset_tag_helper.rb
@@ -11,7 +11,7 @@ module ActionView
# the assets exist before linking to them:
#
# image_tag("rails.png")
- # # => <img alt="Rails" src="/images/rails.png" />
+ # # => <img alt="Rails" src="/assets/rails.png" />
# stylesheet_link_tag("application")
# # => <link href="/assets/application.css?body=1" media="screen" rel="stylesheet" />
module AssetTagHelper
@@ -20,8 +20,7 @@ module ActionView
include AssetUrlHelper
include TagHelper
- # Returns an HTML script tag for each of the +sources+ provided. If
- # you don't specify an extension, <tt>.js</tt> will be appended automatically.
+ # Returns an HTML script tag for each of the +sources+ provided.
#
# Sources may be paths to JavaScript files. Relative paths are assumed to be relative
# to <tt>assets/javascripts</tt>, full paths are assumed to be relative to the document
@@ -34,19 +33,19 @@ module ActionView
# last argument.
#
# When the Asset Pipeline is enabled, you can pass the name of your manifest as
- # source and include other JavaScript or CoffeeScript files inside the manifest.
+ # source, and include other JavaScript or CoffeeScript files inside the manifest.
#
# javascript_include_tag "xmlhr"
- # # => <script src="/javascripts/xmlhr.js?1284139606"></script>
+ # # => <script src="/assets/xmlhr.js?1284139606"></script>
#
# javascript_include_tag "template.jst", extname: false
- # # => <script src="/javascripts/template.jst?1284139606"></script>
+ # # => <script src="/assets/template.jst?1284139606"></script>
#
# javascript_include_tag "xmlhr.js"
- # # => <script src="/javascripts/xmlhr.js?1284139606"></script>
+ # # => <script src="/assets/xmlhr.js?1284139606"></script>
#
# javascript_include_tag "common.javascript", "/elsewhere/cools"
- # # => <script src="/javascripts/common.javascript?1284139606"></script>
+ # # => <script src="/assets/common.javascript?1284139606"></script>
# # <script src="/elsewhere/cools.js?1423139606"></script>
#
# javascript_include_tag "http://www.example.com/xmlhr"
@@ -73,22 +72,22 @@ module ActionView
# apply to all media types.
#
# stylesheet_link_tag "style"
- # # => <link href="/stylesheets/style.css" media="screen" rel="stylesheet" />
+ # # => <link href="/assets/style.css" media="screen" rel="stylesheet" />
#
# stylesheet_link_tag "style.css"
- # # => <link href="/stylesheets/style.css" media="screen" rel="stylesheet" />
+ # # => <link href="/assets/style.css" media="screen" rel="stylesheet" />
#
# stylesheet_link_tag "http://www.example.com/style.css"
# # => <link href="http://www.example.com/style.css" media="screen" rel="stylesheet" />
#
# stylesheet_link_tag "style", media: "all"
- # # => <link href="/stylesheets/style.css" media="all" rel="stylesheet" />
+ # # => <link href="/assets/style.css" media="all" rel="stylesheet" />
#
# stylesheet_link_tag "style", media: "print"
- # # => <link href="/stylesheets/style.css" media="print" rel="stylesheet" />
+ # # => <link href="/assets/style.css" media="print" rel="stylesheet" />
#
# stylesheet_link_tag "random.styles", "/css/stylish"
- # # => <link href="/stylesheets/random.styles" media="screen" rel="stylesheet" />
+ # # => <link href="/assets/random.styles" media="screen" rel="stylesheet" />
# # <link href="/css/stylish.css" media="screen" rel="stylesheet" />
def stylesheet_link_tag(*sources)
options = sources.extract_options!.stringify_keys
@@ -159,17 +158,17 @@ module ActionView
# respectively:
#
# favicon_link_tag
- # # => <link href="/images/favicon.ico" rel="shortcut icon" type="image/x-icon" />
+ # # => <link href="/assets/favicon.ico" rel="shortcut icon" type="image/x-icon" />
#
# favicon_link_tag 'myicon.ico'
- # # => <link href="/images/myicon.ico" rel="shortcut icon" type="image/x-icon" />
+ # # => <link href="/assets/myicon.ico" rel="shortcut icon" type="image/x-icon" />
#
# Mobile Safari looks for a different link tag, pointing to an image that
# will be used if you add the page to the home screen of an iOS device.
# The following call would generate such a tag:
#
# favicon_link_tag 'mb-icon.png', rel: 'apple-touch-icon', type: 'image/png'
- # # => <link href="/images/mb-icon.png" rel="apple-touch-icon" type="image/png" />
+ # # => <link href="/assets/mb-icon.png" rel="apple-touch-icon" type="image/png" />
def favicon_link_tag(source='favicon.ico', options={})
tag('link', {
:rel => 'shortcut icon',
@@ -265,7 +264,7 @@ module ActionView
# video_tag("trailer.ogg", controls: true, autobuffer: true)
# # => <video autobuffer="autobuffer" controls="controls" src="/videos/trailer.ogg" ></video>
# video_tag("trailer.m4v", size: "16x10", poster: "screenshot.png")
- # # => <video src="/videos/trailer.m4v" width="16" height="10" poster="/images/screenshot.png"></video>
+ # # => <video src="/videos/trailer.m4v" width="16" height="10" poster="/assets/screenshot.png"></video>
# video_tag("/trailers/hd.avi", size: "16x16")
# # => <video src="/trailers/hd.avi" width="16" height="16"></video>
# video_tag("/trailers/hd.avi", size: "16")
diff --git a/actionview/lib/action_view/helpers/capture_helper.rb b/actionview/lib/action_view/helpers/capture_helper.rb
index 5afe435459..75d1634b2e 100644
--- a/actionview/lib/action_view/helpers/capture_helper.rb
+++ b/actionview/lib/action_view/helpers/capture_helper.rb
@@ -202,15 +202,6 @@ module ActionView
ensure
self.output_buffer = old_buffer
end
-
- # Add the output buffer to the response body and start a new one.
- def flush_output_buffer #:nodoc:
- if output_buffer && !output_buffer.empty?
- response.stream.write output_buffer
- self.output_buffer = output_buffer.respond_to?(:clone_empty) ? output_buffer.clone_empty : output_buffer[0, 0]
- nil
- end
- end
end
end
end
diff --git a/actionview/lib/action_view/helpers/form_helper.rb b/actionview/lib/action_view/helpers/form_helper.rb
index 180c4a62bf..789a413c8d 100644
--- a/actionview/lib/action_view/helpers/form_helper.rb
+++ b/actionview/lib/action_view/helpers/form_helper.rb
@@ -434,7 +434,8 @@ module ActionView
output = capture(builder, &block)
html_options[:multipart] ||= builder.multipart?
- form_tag(options[:url] || {}, html_options) { output }
+ html_options = html_options_for_form(options[:url] || {}, html_options)
+ form_tag_with_body(html_options, output)
end
def apply_form_for_options!(record, object, options) #:nodoc:
diff --git a/actionview/lib/action_view/helpers/form_tag_helper.rb b/actionview/lib/action_view/helpers/form_tag_helper.rb
index 1e818083cc..88b8400644 100644
--- a/actionview/lib/action_view/helpers/form_tag_helper.rb
+++ b/actionview/lib/action_view/helpers/form_tag_helper.rb
@@ -67,7 +67,7 @@ module ActionView
def form_tag(url_for_options = {}, options = {}, &block)
html_options = html_options_for_form(url_for_options, options)
if block_given?
- form_tag_in_block(html_options, &block)
+ form_tag_with_body(html_options, capture(&block))
else
form_tag_html(html_options)
end
@@ -82,7 +82,7 @@ module ActionView
# ==== Options
# * <tt>:multiple</tt> - If set to true the selection will allow multiple choices.
# * <tt>:disabled</tt> - If set to true, the user will not be able to use this input.
- # * <tt>:include_blank</tt> - If set to true, an empty option will be created.
+ # * <tt>:include_blank</tt> - If set to true, an empty option will be created. If set to a string, the string will be used as the option's content and the value will be empty.
# * <tt>:prompt</tt> - Create a prompt option with blank value and the text asking user to select something.
# * <tt>:selected</tt> - Provide a default selected value. It should be of the exact type as the provided options.
# * Any other key creates standard HTML attributes for the tag.
@@ -116,6 +116,9 @@ module ActionView
# select_tag "people", options_from_collection_for_select(@people, "id", "name"), include_blank: true
# # => <select id="people" name="people"><option value=""></option><option value="1">David</option></select>
#
+ # select_tag "people", options_from_collection_for_select(@people, "id", "name"), include_blank: "All"
+ # # => <select id="people" name="people"><option value="">All</option><option value="1">David</option></select>
+ #
# select_tag "people", options_from_collection_for_select(@people, "id", "name"), prompt: "Select something"
# # => <select id="people" name="people"><option value="">Select something</option><option value="1">David</option></select>
#
@@ -848,8 +851,7 @@ module ActionView
tag(:form, html_options, true) + extra_tags
end
- def form_tag_in_block(html_options, &block)
- content = capture(&block)
+ def form_tag_with_body(html_options, content)
output = form_tag_html(html_options)
output << content
output.safe_concat("</form>")
diff --git a/actionview/lib/action_view/helpers/output_safety_helper.rb b/actionview/lib/action_view/helpers/output_safety_helper.rb
index 60a4478c26..e1f40011c0 100644
--- a/actionview/lib/action_view/helpers/output_safety_helper.rb
+++ b/actionview/lib/action_view/helpers/output_safety_helper.rb
@@ -29,9 +29,9 @@ module ActionView #:nodoc:
# # => "<p>foo</p><br /><p>bar</p>"
#
def safe_join(array, sep=$,)
- sep = ERB::Util.html_escape(sep)
+ sep = ERB::Util.unwrapped_html_escape(sep)
- array.map { |i| ERB::Util.html_escape(i) }.join(sep).html_safe
+ array.map { |i| ERB::Util.unwrapped_html_escape(i) }.join(sep).html_safe
end
end
end
diff --git a/actionview/lib/action_view/helpers/tag_helper.rb b/actionview/lib/action_view/helpers/tag_helper.rb
index a9f3b0ffbc..9b9ca7d60d 100644
--- a/actionview/lib/action_view/helpers/tag_helper.rb
+++ b/actionview/lib/action_view/helpers/tag_helper.rb
@@ -139,7 +139,7 @@ module ActionView
def content_tag_string(name, content, options, escape = true)
tag_options = tag_options(options, escape) if options
- content = ERB::Util.h(content) if escape
+ content = ERB::Util.unwrapped_html_escape(content) if escape
"<#{name}#{tag_options}>#{PRE_CONTENT_STRINGS[name.to_sym]}#{content}</#{name}>".html_safe
end
@@ -174,7 +174,7 @@ module ActionView
def tag_option(key, value, escape)
value = value.join(" ") if value.is_a?(Array)
- value = ERB::Util.h(value) if escape
+ value = ERB::Util.unwrapped_html_escape(value) if escape
%(#{key}="#{value}")
end
end
diff --git a/actionview/lib/action_view/helpers/url_helper.rb b/actionview/lib/action_view/helpers/url_helper.rb
index 894616a449..9a9777317a 100644
--- a/actionview/lib/action_view/helpers/url_helper.rb
+++ b/actionview/lib/action_view/helpers/url_helper.rb
@@ -462,7 +462,7 @@ module ActionView
# <strong>Email me:</strong> <span>me@domain.com</span>
# </a>
def mail_to(email_address, name = nil, html_options = {}, &block)
- email_address = ERB::Util.html_escape(email_address)
+ email_address = ERB::Util.unwrapped_html_escape(email_address)
html_options, name = name, nil if block_given?
html_options = (html_options || {}).stringify_keys
@@ -471,7 +471,7 @@ module ActionView
option = html_options.delete(item) || next
"#{item}=#{Rack::Utils.escape_path(option)}"
}.compact
- extras = extras.empty? ? '' : '?' + ERB::Util.html_escape(extras.join('&'))
+ extras = extras.empty? ? '' : '?' + ERB::Util.unwrapped_html_escape(extras.join('&'))
html_options["href"] = "mailto:#{email_address}#{extras}".html_safe
diff --git a/actionview/test/template/capture_helper_test.rb b/actionview/test/template/capture_helper_test.rb
index 938f1c3e54..f213da5934 100644
--- a/actionview/test/template/capture_helper_test.rb
+++ b/actionview/test/template/capture_helper_test.rb
@@ -207,29 +207,6 @@ class CaptureHelperTest < ActionView::TestCase
assert_equal "", @av.with_output_buffer {}
end
- def test_flush_output_buffer_concats_output_buffer_to_response
- view = view_with_controller
- assert_equal [], view.response.body_parts
-
- view.output_buffer << 'OMG'
- view.flush_output_buffer
- assert_equal ['OMG'], view.response.body_parts
- assert_equal '', view.output_buffer
-
- view.output_buffer << 'foobar'
- view.flush_output_buffer
- assert_equal ['OMG', 'foobar'], view.response.body_parts
- assert_equal '', view.output_buffer
- end
-
- def test_flush_output_buffer_preserves_the_encoding_of_the_output_buffer
- view = view_with_controller
- alt_encoding = alt_encoding(view.output_buffer)
- view.output_buffer.force_encoding(alt_encoding)
- flush_output_buffer
- assert_equal alt_encoding, view.output_buffer.encoding
- end
-
def alt_encoding(output_buffer)
output_buffer.encoding == Encoding::US_ASCII ? Encoding::UTF_8 : Encoding::US_ASCII
end
diff --git a/actionview/test/template/number_helper_test.rb b/actionview/test/template/number_helper_test.rb
index adb888319d..0495224d04 100644
--- a/actionview/test/template/number_helper_test.rb
+++ b/actionview/test/template/number_helper_test.rb
@@ -48,6 +48,7 @@ class NumberHelperTest < ActionView::TestCase
assert_equal "-111.235", number_with_precision(-111.2346)
assert_equal "111.00", number_with_precision(111, precision: 2)
assert_equal "0.00100", number_with_precision(0.001, precision: 5)
+ assert_equal "3.33", number_with_precision(Rational(10, 3), precision: 2)
end
def test_number_to_human_size
diff --git a/actionview/test/template/output_buffer_test.rb b/actionview/test/template/output_buffer_test.rb
deleted file mode 100644
index eb0df3d1ab..0000000000
--- a/actionview/test/template/output_buffer_test.rb
+++ /dev/null
@@ -1,59 +0,0 @@
-require 'abstract_unit'
-
-class OutputBufferTest < ActionController::TestCase
- class TestController < ActionController::Base
- def index
- render :text => 'foo'
- end
- end
-
- tests TestController
-
- def setup
- @vc = @controller.view_context
- get :index
- assert_equal ['foo'], body_parts
- end
-
- test 'output buffer is nil after rendering' do
- assert_nil output_buffer
- end
-
- test 'flushing ignores nil output buffer' do
- @controller.view_context.flush_output_buffer
- assert_nil output_buffer
- assert_equal ['foo'], body_parts
- end
-
- test 'flushing ignores empty output buffer' do
- @vc.output_buffer = ''
- @vc.flush_output_buffer
- assert_equal '', output_buffer
- assert_equal ['foo'], body_parts
- end
-
- test 'flushing appends the output buffer to the body parts' do
- @vc.output_buffer = 'bar'
- @vc.flush_output_buffer
- assert_equal '', output_buffer
- assert_equal ['foo', 'bar'], body_parts
- end
-
- test 'flushing preserves output buffer encoding' do
- original_buffer = ' '.force_encoding(Encoding::EUC_JP)
- @vc.output_buffer = original_buffer
- @vc.flush_output_buffer
- assert_equal ['foo', original_buffer], body_parts
- assert_not_equal original_buffer, output_buffer
- assert_equal Encoding::EUC_JP, output_buffer.encoding
- end
-
- protected
- def output_buffer
- @vc.output_buffer
- end
-
- def body_parts
- @controller.response.body_parts
- end
-end
diff --git a/activemodel/README.rdoc b/activemodel/README.rdoc
index c6500f47e2..f6beff14e1 100644
--- a/activemodel/README.rdoc
+++ b/activemodel/README.rdoc
@@ -262,6 +262,11 @@ API documentation is at
* http://api.rubyonrails.org
-Bug reports and feature requests can be filed with the rest for the Ruby on Rails project here:
+Bug reports can be filed for the Ruby on Rails project here:
* https://github.com/rails/rails/issues
+
+Feature requests should be discussed on the rails-core mailing list here:
+
+* https://groups.google.com/forum/?fromgroups#!forum/rubyonrails-core
+
diff --git a/activerecord/CHANGELOG.md b/activerecord/CHANGELOG.md
index 078d63f8f2..5167ffbdeb 100644
--- a/activerecord/CHANGELOG.md
+++ b/activerecord/CHANGELOG.md
@@ -1,3 +1,54 @@
+* `ActiveRecord::SchemaMigration` has no primary key regardless of the
+ `primary_key_prefix_type` configuration.
+
+ Fixes #15051.
+
+ *JoseLuis Torres*, *Yves Senn*
+
+* `rake db:migrate:status` works with legacy migration numbers like `00018_xyz.rb`.
+
+ Fixes #15538.
+
+ *Yves Senn*
+
+* Baseclass becomes! subclass.
+
+ Before this change, a record which changed its STI type, could not be
+ updated.
+
+ Fixes #14785.
+
+ *Matthew Draper*, *Earl St Sauver*, *Edo Balvers*
+
+* Remove deprecated `ActiveRecord::Migrator.proper_table_name`. Use the
+ `proper_table_name` instance method on `ActiveRecord::Migration` instead.
+
+ *Akshay Vishnoi*
+
+* Fix regression on eager loading association based on SQL query rather than
+ existing column.
+
+ Fixes #15480.
+
+ *Lauro Caetano*, *Carlos Antonio da Silva*
+
+* Return a null column from `column_for_attribute` when no column exists.
+
+ *Sean Griffin*
+
+* Implemented ActiveRecord::Base#pretty_print to work with PP.
+
+ *Ethan*
+
+* Preserve type when dumping PostgreSQL point, bit, bit varying and money
+ columns.
+
+ *Yves Senn*
+
+* New records remain new after YAML serialization.
+
+ *Sean Griffin*
+
* PostgreSQL support default values for enum types. Fixes #7814.
*Yves Senn*
@@ -568,12 +619,11 @@
*arthurnn*
-* Passing an Active Record object to `find` is now deprecated. Call `.id`
- on the object first.
-
* Passing an Active Record object to `find` or `exists?` is now deprecated.
Call `.id` on the object first.
+ *Aaron Patterson*
+
* Only use BINARY for MySQL case sensitive uniqueness check when column has a case insensitive collation.
*Ryuta Kamizono*
diff --git a/activerecord/README.rdoc b/activerecord/README.rdoc
index 969e97913d..f4777919d3 100644
--- a/activerecord/README.rdoc
+++ b/activerecord/README.rdoc
@@ -208,6 +208,11 @@ API documentation is at:
* http://api.rubyonrails.org
-Bug reports and feature requests can be filed with the rest for the Ruby on Rails project here:
+Bug reports can be filed for the Ruby on Rails project here:
* https://github.com/rails/rails/issues
+
+Feature requests should be discussed on the rails-core mailing list here:
+
+* https://groups.google.com/forum/?fromgroups#!forum/rubyonrails-core
+
diff --git a/activerecord/lib/active_record/aggregations.rb b/activerecord/lib/active_record/aggregations.rb
index 45c275a017..e576ec4d40 100644
--- a/activerecord/lib/active_record/aggregations.rb
+++ b/activerecord/lib/active_record/aggregations.rb
@@ -129,10 +129,10 @@ module ActiveRecord
# is an instance of the value class. Specifying a custom converter allows the new value to be automatically
# converted to an instance of value class if necessary.
#
- # For example, the NetworkResource model has +network_address+ and +cidr_range+ attributes that
- # should be aggregated using the NetAddr::CIDR value class (http://netaddr.rubyforge.org). The constructor
- # for the value class is called +create+ and it expects a CIDR address string as a parameter. New
- # values can be assigned to the value object using either another NetAddr::CIDR object, a string
+ # For example, the NetworkResource model has +network_address+ and +cidr_range+ attributes that should be
+ # aggregated using the NetAddr::CIDR value class (http://www.ruby-doc.org/gems/docs/n/netaddr-1.5.0/NetAddr/CIDR.html).
+ # The constructor for the value class is called +create+ and it expects a CIDR address string as a parameter.
+ # New values can be assigned to the value object using either another NetAddr::CIDR object, a string
# or an array. The <tt>:constructor</tt> and <tt>:converter</tt> options can be used to meet
# these requirements:
#
@@ -244,6 +244,10 @@ module ActiveRecord
def writer_method(name, class_name, mapping, allow_nil, converter)
define_method("#{name}=") do |part|
klass = class_name.constantize
+ if part.is_a?(Hash)
+ part = klass.new(*part.values)
+ end
+
unless part.is_a?(klass) || converter.nil? || part.nil?
part = converter.respond_to?(:call) ? converter.call(part) : klass.send(converter, part)
end
diff --git a/activerecord/lib/active_record/associations/association_scope.rb b/activerecord/lib/active_record/associations/association_scope.rb
index 572f556999..31108cc1aa 100644
--- a/activerecord/lib/active_record/associations/association_scope.rb
+++ b/activerecord/lib/active_record/associations/association_scope.rb
@@ -106,7 +106,7 @@ module ActiveRecord
table, foreign_table = tables.shift, tables.first
if reflection.source_macro == :belongs_to
- if reflection.options[:polymorphic]
+ if reflection.polymorphic?
key = reflection.association_primary_key(assoc_klass)
else
key = reflection.association_primary_key
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 35ad512537..954128064d 100644
--- a/activerecord/lib/active_record/associations/has_many_through_association.rb
+++ b/activerecord/lib/active_record/associations/has_many_through_association.rb
@@ -116,7 +116,7 @@ module ActiveRecord
end
def target_reflection_has_associated_record?
- !(through_reflection.macro == :belongs_to && owner[through_reflection.foreign_key].blank?)
+ !(through_reflection.belongs_to? && owner[through_reflection.foreign_key].blank?)
end
def update_through_counter?(method)
diff --git a/activerecord/lib/active_record/associations/join_dependency.rb b/activerecord/lib/active_record/associations/join_dependency.rb
index 01173b68f3..35659766d3 100644
--- a/activerecord/lib/active_record/associations/join_dependency.rb
+++ b/activerecord/lib/active_record/associations/join_dependency.rb
@@ -217,7 +217,7 @@ module ActiveRecord
reflection.check_validity!
reflection.check_eager_loadable!
- if reflection.options[:polymorphic]
+ if reflection.polymorphic?
raise EagerLoadPolymorphicError.new(reflection)
end
diff --git a/activerecord/lib/active_record/associations/preloader/association.rb b/activerecord/lib/active_record/associations/preloader/association.rb
index 63773bd5e1..33c8619359 100644
--- a/activerecord/lib/active_record/associations/preloader/association.rb
+++ b/activerecord/lib/active_record/associations/preloader/association.rb
@@ -104,11 +104,11 @@ module ActiveRecord
end
def association_key_type
- @klass.column_types[association_key_name.to_s].type
+ @klass.column_for_attribute(association_key_name).type
end
def owner_key_type
- @model.column_types[owner_key_name.to_s].type
+ @model.column_for_attribute(owner_key_name).type
end
def load_slices(slices)
diff --git a/activerecord/lib/active_record/associations/through_association.rb b/activerecord/lib/active_record/associations/through_association.rb
index f8a85b8a6f..fcf3b219d4 100644
--- a/activerecord/lib/active_record/associations/through_association.rb
+++ b/activerecord/lib/active_record/associations/through_association.rb
@@ -63,14 +63,13 @@ module ActiveRecord
# Note: this does not capture all cases, for example it would be crazy to try to
# properly support stale-checking for nested associations.
def stale_state
- if through_reflection.macro == :belongs_to
+ if through_reflection.belongs_to?
owner[through_reflection.foreign_key] && owner[through_reflection.foreign_key].to_s
end
end
def foreign_key_present?
- through_reflection.macro == :belongs_to &&
- !owner[through_reflection.foreign_key].nil?
+ through_reflection.belongs_to? && !owner[through_reflection.foreign_key].nil?
end
def ensure_mutable
diff --git a/activerecord/lib/active_record/attribute_assignment.rb b/activerecord/lib/active_record/attribute_assignment.rb
index c4cf084a04..40e2918777 100644
--- a/activerecord/lib/active_record/attribute_assignment.rb
+++ b/activerecord/lib/active_record/attribute_assignment.rb
@@ -126,8 +126,8 @@ module ActiveRecord
def read_value
return if values.values.compact.empty?
- @column = object.class.reflect_on_aggregation(name.to_sym) || object.column_for_attribute(name)
- klass = column.klass
+ @column = object.column_for_attribute(name)
+ klass = column ? column.klass : nil
if klass == Time
read_time
@@ -186,8 +186,7 @@ module ActiveRecord
positions = (1..max_position)
validate_required_parameters!(positions)
- set_values = values.values_at(*positions)
- klass.new(*set_values)
+ values.slice(*positions)
end
# Checks whether some blank date parameter exists. Note that this is different
diff --git a/activerecord/lib/active_record/attribute_methods.rb b/activerecord/lib/active_record/attribute_methods.rb
index fb6473b7c7..e626227e7e 100644
--- a/activerecord/lib/active_record/attribute_methods.rb
+++ b/activerecord/lib/active_record/attribute_methods.rb
@@ -18,6 +18,8 @@ module ActiveRecord
include TimeZoneConversion
include Dirty
include Serialization
+
+ delegate :column_for_attribute, to: :class
end
AttrNames = Module.new {
@@ -192,6 +194,26 @@ module ActiveRecord
[]
end
end
+
+ # Returns the column object for the named attribute.
+ # Returns a +ActiveRecord::ConnectionAdapters::NullColumn+ if the
+ # named attribute does not exist.
+ #
+ # class Person < ActiveRecord::Base
+ # end
+ #
+ # person = Person.new
+ # person.column_for_attribute(:name) # the result depends on the ConnectionAdapter
+ # # => #<ActiveRecord::ConnectionAdapters::SQLite3Column:0x007ff4ab083980 @name="name", @sql_type="varchar(255)", @null=true, ...>
+ #
+ # person.column_for_attribute(:nothing)
+ # # => #<ActiveRecord::ConnectionAdapters::NullColumn:0xXXX @name=nil, @sql_type=nil, @cast_type=#<Type::Value>, ...>
+ def column_for_attribute(name)
+ name = name.to_s
+ columns_hash.fetch(name) do
+ ConnectionAdapters::NullColumn.new(name)
+ end
+ end
end
# If we haven't generated any methods yet, generate them, then
@@ -287,11 +309,6 @@ module ActiveRecord
}
end
- # Placeholder so it can be overriden when needed by serialization
- def attributes_for_coder # :nodoc:
- attributes
- end
-
# Returns an <tt>#inspect</tt>-like string for the value of the
# attribute +attr_name+. String attributes are truncated upto 50
# characters, Date and Time attributes are returned in the
@@ -344,23 +361,6 @@ module ActiveRecord
!value.nil? && !(value.respond_to?(:empty?) && value.empty?)
end
- # Returns the column object for the named attribute. Returns +nil+ if the
- # named attribute not exists.
- #
- # class Person < ActiveRecord::Base
- # end
- #
- # person = Person.new
- # person.column_for_attribute(:name) # the result depends on the ConnectionAdapter
- # # => #<ActiveRecord::ConnectionAdapters::SQLite3Column:0x007ff4ab083980 @name="name", @sql_type="varchar(255)", @null=true, ...>
- #
- # person.column_for_attribute(:nothing)
- # # => nil
- def column_for_attribute(name)
- # FIXME: should this return a null object for columns that don't exist?
- self.class.columns_hash[name.to_s]
- end
-
# Returns the value of the attribute identified by <tt>attr_name</tt> after it has been typecast (for example,
# "2004-12-12" in a date column is cast to a date object, like Date.new(2004, 12, 12)). It raises
# <tt>ActiveModel::MissingAttributeError</tt> if the identified attribute is missing.
@@ -443,16 +443,16 @@ module ActiveRecord
# Filters the primary keys and readonly attributes from the attribute names.
def attributes_for_update(attribute_names)
- attribute_names.select do |name|
- column_for_attribute(name) && !readonly_attribute?(name)
+ attribute_names.reject do |name|
+ readonly_attribute?(name)
end
end
# Filters out the primary keys, from the attribute names, when the primary
# key is to be generated (e.g. the id attribute has no value).
def attributes_for_create(attribute_names)
- attribute_names.select do |name|
- column_for_attribute(name) && !(pk_attribute?(name) && id.nil?)
+ attribute_names.reject do |name|
+ pk_attribute?(name) && id.nil?
end
end
diff --git a/activerecord/lib/active_record/attribute_methods/dirty.rb b/activerecord/lib/active_record/attribute_methods/dirty.rb
index ad01b5bf25..4e32b78e34 100644
--- a/activerecord/lib/active_record/attribute_methods/dirty.rb
+++ b/activerecord/lib/active_record/attribute_methods/dirty.rb
@@ -94,33 +94,7 @@ module ActiveRecord
end
def _field_changed?(attr, old, value)
- if column = column_for_attribute(attr)
- if column.number? && (changes_from_nil_to_empty_string?(column, old, value) ||
- changes_from_zero_to_string?(old, value))
- value = nil
- else
- value = column.type_cast(value)
- end
- end
-
- old != value
- end
-
- def changes_from_nil_to_empty_string?(column, old, value)
- # For nullable numeric columns, NULL gets stored in database for blank (i.e. '') values.
- # Hence we don't record it as a change if the value changes from nil to ''.
- # If an old value of 0 is set to '' we want this to get changed to nil as otherwise it'll
- # be typecast back to 0 (''.to_i => 0)
- column.null && (old.nil? || old == 0) && value.blank?
- end
-
- def changes_from_zero_to_string?(old, value)
- # For columns with old 0 and value non-empty string
- old == 0 && value.is_a?(String) && value.present? && non_zero?(value)
- end
-
- def non_zero?(value)
- value !~ /\A0+(\.0+)?\z/
+ column_for_attribute(attr).changed?(old, value)
end
end
end
diff --git a/activerecord/lib/active_record/attribute_methods/read.rb b/activerecord/lib/active_record/attribute_methods/read.rb
index 99b95b1fc4..ae3785638a 100644
--- a/activerecord/lib/active_record/attribute_methods/read.rb
+++ b/activerecord/lib/active_record/attribute_methods/read.rb
@@ -94,7 +94,7 @@ module ActiveRecord
def cacheable_column?(column)
if attribute_types_cached_by_default == ATTRIBUTE_TYPES_CACHED_BY_DEFAULT
- ! serialized_attributes.include? column.name
+ true
else
attribute_types_cached_by_default.include?(column.type)
end
diff --git a/activerecord/lib/active_record/attribute_methods/serialization.rb b/activerecord/lib/active_record/attribute_methods/serialization.rb
index b247bf8a45..148fc9eae5 100644
--- a/activerecord/lib/active_record/attribute_methods/serialization.rb
+++ b/activerecord/lib/active_record/attribute_methods/serialization.rb
@@ -76,21 +76,6 @@ module ActiveRecord
module Behavior # :nodoc:
extend ActiveSupport::Concern
- module ClassMethods # :nodoc:
- def initialize_attributes(attributes, options = {})
- serialized = (options.delete(:serialized) { true }) ? :serialized : :unserialized
- super(attributes, options)
-
- serialized_attributes.each do |key, coder|
- if attributes.key?(key)
- attributes[key] = Type::Serialized::Attribute.new(coder, attributes[key], serialized)
- end
- end
-
- attributes
- end
- end
-
def should_record_timestamps?
super || (self.record_timestamps && (attributes.keys & self.class.serialized_attributes.keys).present?)
end
@@ -98,50 +83,6 @@ module ActiveRecord
def keys_for_partial_write
super | (attributes.keys & self.class.serialized_attributes.keys)
end
-
- def _field_changed?(attr, old, value)
- if self.class.serialized_attributes.include?(attr)
- old != value
- else
- super
- end
- end
-
- def read_attribute_before_type_cast(attr_name)
- if self.class.serialized_attributes.include?(attr_name)
- super.unserialized_value
- else
- super
- end
- end
-
- def attributes_before_type_cast
- super.dup.tap do |attributes|
- self.class.serialized_attributes.each_key do |key|
- if attributes.key?(key)
- attributes[key] = attributes[key].unserialized_value
- end
- end
- end
- end
-
- def typecasted_attribute_value(name)
- if self.class.serialized_attributes.include?(name)
- @raw_attributes[name].serialized_value
- else
- super
- end
- end
-
- def attributes_for_coder
- attribute_names.each_with_object({}) do |name, attrs|
- attrs[name] = if self.class.serialized_attributes.include?(name)
- @raw_attributes[name].serialized_value
- else
- read_attribute(name)
- end
- 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 3b9b9c81e8..5203b30462 100644
--- a/activerecord/lib/active_record/attribute_methods/write.rb
+++ b/activerecord/lib/active_record/attribute_methods/write.rb
@@ -53,11 +53,11 @@ module ActiveRecord
# specified +value+. Empty strings for fixnum and float columns are
# turned into +nil+.
def write_attribute(attr_name, value)
- write_attribute_with_type_cast(attr_name, value, :type_cast_for_write)
+ write_attribute_with_type_cast(attr_name, value, true)
end
def raw_write_attribute(attr_name, value)
- write_attribute_with_type_cast(attr_name, value, :raw_type_cast_for_write)
+ write_attribute_with_type_cast(attr_name, value, false)
end
private
@@ -66,24 +66,26 @@ module ActiveRecord
write_attribute(attribute_name, value)
end
- def write_attribute_with_type_cast(attr_name, value, type_cast_method)
+ def write_attribute_with_type_cast(attr_name, value, should_type_cast)
attr_name = attr_name.to_s
attr_name = self.class.primary_key if attr_name == 'id' && self.class.primary_key
@attributes.delete(attr_name)
column = column_for_attribute(attr_name)
+ unless has_attribute?(attr_name) || self.class.columns_hash.key?(attr_name)
+ raise ActiveModel::MissingAttributeError, "can't write unknown attribute `#{attr_name}'"
+ end
+
# If we're dealing with a binary column, write the data to the cache
# so we don't attempt to typecast multiple times.
- if column && column.binary?
+ if column.binary?
@attributes[attr_name] = value
end
- if column
- @raw_attributes[attr_name] = column.public_send(type_cast_method, value)
- elsif @raw_attributes.has_key?(attr_name)
- @raw_attributes[attr_name] = value
+ if should_type_cast
+ @raw_attributes[attr_name] = column.type_cast_for_write(value)
else
- raise ActiveModel::MissingAttributeError, "can't write unknown attribute `#{attr_name}'"
+ @raw_attributes[attr_name] = value
end
end
end
diff --git a/activerecord/lib/active_record/connection_adapters/abstract/quoting.rb b/activerecord/lib/active_record/connection_adapters/abstract/quoting.rb
index f836e60988..04ae67234f 100644
--- a/activerecord/lib/active_record/connection_adapters/abstract/quoting.rb
+++ b/activerecord/lib/active_record/connection_adapters/abstract/quoting.rb
@@ -18,21 +18,7 @@ module ActiveRecord
value = column.type_cast_for_database(value)
end
- case value
- when String, ActiveSupport::Multibyte::Chars
- "'#{quote_string(value.to_s)}'"
- when true then quoted_true
- when false then quoted_false
- when nil then "NULL"
- # BigDecimals need to be put in a non-normalized form and quoted.
- when BigDecimal then value.to_s('F')
- when Numeric, ActiveSupport::Duration then value.to_s
- when Date, Time then "'#{quoted_date(value)}'"
- when Symbol then "'#{quote_string(value.to_s)}'"
- when Class then "'#{value.to_s}'"
- else
- "'#{quote_string(YAML.dump(value))}'"
- end
+ _quote(value)
end
# Cast a +value+ to a type that the database understands. For example,
@@ -52,20 +38,10 @@ module ActiveRecord
value = column.type_cast_for_database(value)
end
- case value
- when Symbol, ActiveSupport::Multibyte::Chars
- value.to_s
- when true then unquoted_true
- when false then unquoted_false
- # BigDecimals need to be put in a non-normalized form and quoted.
- when BigDecimal then value.to_s('F')
- when Date, Time then quoted_date(value)
- when *types_which_need_no_typecasting
- value
- else
- to_type = column ? " to #{column.type}" : ""
- raise TypeError, "can't cast #{value.class}#{to_type}"
- end
+ _type_cast(value)
+ rescue TypeError
+ to_type = column ? " to #{column.type}" : ""
+ raise TypeError, "can't cast #{value.class}#{to_type}"
end
# Quotes a string, escaping any ' (single quote) and \ (backslash)
@@ -129,6 +105,39 @@ module ActiveRecord
def types_which_need_no_typecasting
[nil, Numeric, String]
end
+
+ def _quote(value)
+ case value
+ when String, ActiveSupport::Multibyte::Chars, Type::Binary::Data
+ "'#{quote_string(value.to_s)}'"
+ when true then quoted_true
+ when false then quoted_false
+ when nil then "NULL"
+ # BigDecimals need to be put in a non-normalized form and quoted.
+ when BigDecimal then value.to_s('F')
+ when Numeric, ActiveSupport::Duration then value.to_s
+ when Date, Time then "'#{quoted_date(value)}'"
+ when Symbol then "'#{quote_string(value.to_s)}'"
+ when Class then "'#{value.to_s}'"
+ else
+ "'#{quote_string(YAML.dump(value))}'"
+ end
+ end
+
+ def _type_cast(value)
+ case value
+ when Symbol, ActiveSupport::Multibyte::Chars, Type::Binary::Data
+ value.to_s
+ when true then unquoted_true
+ when false then unquoted_false
+ # BigDecimals need to be put in a non-normalized form and quoted.
+ when BigDecimal then value.to_s('F')
+ when Date, Time then quoted_date(value)
+ when *types_which_need_no_typecasting
+ value
+ else raise TypeError
+ end
+ end
end
end
end
diff --git a/activerecord/lib/active_record/connection_adapters/abstract/schema_definitions.rb b/activerecord/lib/active_record/connection_adapters/abstract/schema_definitions.rb
index 117c0f0969..a9b3e9cfb9 100644
--- a/activerecord/lib/active_record/connection_adapters/abstract/schema_definitions.rb
+++ b/activerecord/lib/active_record/connection_adapters/abstract/schema_definitions.rb
@@ -102,8 +102,8 @@ module ActiveRecord
# * <tt>:index</tt> -
# Create an index for the column. Can be either <tt>true</tt> or an options hash.
#
- # For clarity's sake: the precision is the number of significant digits,
- # while the scale is the number of digits that can be stored following
+ # Note: The precision is the total number of significant digits
+ # and the scale is the number of digits that can be stored following
# the decimal point. For example, the number 123.45 has a precision of 5
# and a scale of 2. A decimal with a precision of 5 and a scale of 2 can
# range from -999.99 to 999.99.
diff --git a/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb b/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb
index b589cfee09..cc494a7f40 100644
--- a/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb
+++ b/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb
@@ -14,7 +14,10 @@ module ActiveRecord
module ConnectionAdapters # :nodoc:
extend ActiveSupport::Autoload
- autoload :Column
+ autoload_at 'active_record/connection_adapters/column' do
+ autoload :Column
+ autoload :NullColumn
+ end
autoload :ConnectionSpecification
autoload_at 'active_record/connection_adapters/abstract/schema_definitions' do
diff --git a/activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb b/activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb
index 2677b6ee83..759ac9943f 100644
--- a/activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb
+++ b/activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb
@@ -218,10 +218,9 @@ module ActiveRecord
# QUOTING ==================================================
- def quote(value, column = nil)
- if value.kind_of?(String) && column && column.type == :binary
- s = value.unpack("H*")[0]
- "x'#{s}'"
+ def _quote(value) # :nodoc:
+ if value.is_a?(Type::Binary::Data)
+ "x'#{value.hex}'"
else
super
end
diff --git a/activerecord/lib/active_record/connection_adapters/column.rb b/activerecord/lib/active_record/connection_adapters/column.rb
index 4fbc55a07e..5e4e00bc64 100644
--- a/activerecord/lib/active_record/connection_adapters/column.rb
+++ b/activerecord/lib/active_record/connection_adapters/column.rb
@@ -16,8 +16,8 @@ module ActiveRecord
attr_reader :name, :default, :cast_type, :null, :sql_type, :default_function
delegate :type, :precision, :scale, :limit, :klass, :accessor,
- :text?, :number?, :binary?, :serialized?,
- :type_cast, :type_cast_for_write, :raw_type_cast_for_write, :type_cast_for_database,
+ :text?, :number?, :binary?, :serialized?, :changed?,
+ :type_cast, :type_cast_for_write, :type_cast_for_database,
:type_cast_for_schema,
to: :cast_type
@@ -55,6 +55,12 @@ module ActiveRecord
type_cast(default)
end
end
+
+ class NullColumn < Column
+ def initialize(name)
+ super name, nil, Type::Value.new
+ end
+ end
end
# :startdoc:
end
diff --git a/activerecord/lib/active_record/connection_adapters/postgresql/cast.rb b/activerecord/lib/active_record/connection_adapters/postgresql/cast.rb
index f7bad20f00..971f5eed7e 100644
--- a/activerecord/lib/active_record/connection_adapters/postgresql/cast.rb
+++ b/activerecord/lib/active_record/connection_adapters/postgresql/cast.rb
@@ -6,15 +6,6 @@ module ActiveRecord
"(#{point[0]},#{point[1]})"
end
- def string_to_bit(value) # :nodoc:
- case value
- when /^0x/i
- value[2..-1].hex.to_s(2) # Hexadecimal notation
- else
- value # Bit-string notation
- end
- end
-
def hstore_to_string(object, array_member = false) # :nodoc:
if Hash === object
string = object.map { |k, v| "#{escape_hstore(k)}=>#{escape_hstore(v)}" }.join(',')
@@ -76,28 +67,6 @@ module ActiveRecord
end
end
- def string_to_cidr(string) # :nodoc:
- if string.nil?
- nil
- elsif String === string
- begin
- IPAddr.new(string)
- rescue ArgumentError
- nil
- end
- else
- string
- end
- end
-
- def cidr_to_string(object) # :nodoc:
- if IPAddr === object
- "#{object.to_s}/#{object.instance_variable_get(:@mask_addr).to_s(2).count('1')}"
- else
- object
- end
- end
-
def string_to_array(string, oid) # :nodoc:
parse_pg_array(string).map {|val| type_cast_array(oid, val)}
end
diff --git a/activerecord/lib/active_record/connection_adapters/postgresql/oid.rb b/activerecord/lib/active_record/connection_adapters/postgresql/oid.rb
index 2494e19f84..33a98b4fcb 100644
--- a/activerecord/lib/active_record/connection_adapters/postgresql/oid.rb
+++ b/activerecord/lib/active_record/connection_adapters/postgresql/oid.rb
@@ -2,6 +2,7 @@ require 'active_record/connection_adapters/postgresql/oid/infinity'
require 'active_record/connection_adapters/postgresql/oid/array'
require 'active_record/connection_adapters/postgresql/oid/bit'
+require 'active_record/connection_adapters/postgresql/oid/bit_varying'
require 'active_record/connection_adapters/postgresql/oid/bytea'
require 'active_record/connection_adapters/postgresql/oid/cidr'
require 'active_record/connection_adapters/postgresql/oid/date'
diff --git a/activerecord/lib/active_record/connection_adapters/postgresql/oid/bit.rb b/activerecord/lib/active_record/connection_adapters/postgresql/oid/bit.rb
index 9b2d887d07..3073f8ff30 100644
--- a/activerecord/lib/active_record/connection_adapters/postgresql/oid/bit.rb
+++ b/activerecord/lib/active_record/connection_adapters/postgresql/oid/bit.rb
@@ -2,10 +2,19 @@ module ActiveRecord
module ConnectionAdapters
module PostgreSQL
module OID # :nodoc:
- class Bit < Type::String
+ class Bit < Type::Value
+ def type
+ :bit
+ end
+
def type_cast(value)
if ::String === value
- ConnectionAdapters::PostgreSQLColumn.string_to_bit value
+ case value
+ when /^0x/i
+ value[2..-1].hex.to_s(2) # Hexadecimal notation
+ else
+ value # Bit-string notation
+ end
else
value
end
diff --git a/activerecord/lib/active_record/connection_adapters/postgresql/oid/bit_varying.rb b/activerecord/lib/active_record/connection_adapters/postgresql/oid/bit_varying.rb
new file mode 100644
index 0000000000..054af285bb
--- /dev/null
+++ b/activerecord/lib/active_record/connection_adapters/postgresql/oid/bit_varying.rb
@@ -0,0 +1,13 @@
+module ActiveRecord
+ module ConnectionAdapters
+ module PostgreSQL
+ module OID # :nodoc:
+ class BitVarying < OID::Bit
+ def type
+ :bit_varying
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/activerecord/lib/active_record/connection_adapters/postgresql/oid/cidr.rb b/activerecord/lib/active_record/connection_adapters/postgresql/oid/cidr.rb
index 158468fe5b..534961a414 100644
--- a/activerecord/lib/active_record/connection_adapters/postgresql/oid/cidr.rb
+++ b/activerecord/lib/active_record/connection_adapters/postgresql/oid/cidr.rb
@@ -18,8 +18,26 @@ module ActiveRecord
end
end
+ def type_cast_for_database(value)
+ if IPAddr === value
+ "#{value.to_s}/#{value.instance_variable_get(:@mask_addr).to_s(2).count('1')}"
+ else
+ value
+ end
+ end
+
def cast_value(value)
- ConnectionAdapters::PostgreSQLColumn.string_to_cidr value
+ if value.nil?
+ nil
+ elsif String === value
+ begin
+ IPAddr.new(value)
+ rescue ArgumentError
+ nil
+ end
+ else
+ value
+ end
end
end
end
diff --git a/activerecord/lib/active_record/connection_adapters/postgresql/oid/date_time.rb b/activerecord/lib/active_record/connection_adapters/postgresql/oid/date_time.rb
index 9ccbf71159..34e2276dd1 100644
--- a/activerecord/lib/active_record/connection_adapters/postgresql/oid/date_time.rb
+++ b/activerecord/lib/active_record/connection_adapters/postgresql/oid/date_time.rb
@@ -11,7 +11,8 @@ module ActiveRecord
when 'infinity' then ::Float::INFINITY
when '-infinity' then -::Float::INFINITY
when / BC$/
- super("-" + value.sub(/ BC$/, ""))
+ astronomical_year = format("%04d", -value[/^\d+/].to_i + 1)
+ super(value.sub(/ BC$/, "").sub(/^\d+/, astronomical_year))
else
super
end
diff --git a/activerecord/lib/active_record/connection_adapters/postgresql/oid/float.rb b/activerecord/lib/active_record/connection_adapters/postgresql/oid/float.rb
index 9753d71461..77dd97e140 100644
--- a/activerecord/lib/active_record/connection_adapters/postgresql/oid/float.rb
+++ b/activerecord/lib/active_record/connection_adapters/postgresql/oid/float.rb
@@ -5,9 +5,8 @@ module ActiveRecord
class Float < Type::Float
include Infinity
- def type_cast(value)
+ def cast_value(value)
case value
- when nil then nil
when 'Infinity' then ::Float::INFINITY
when '-Infinity' then -::Float::INFINITY
when 'NaN' then ::Float::NAN
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 bf680b6624..a65ca83f77 100644
--- a/activerecord/lib/active_record/connection_adapters/postgresql/oid/hstore.rb
+++ b/activerecord/lib/active_record/connection_adapters/postgresql/oid/hstore.rb
@@ -8,12 +8,6 @@ module ActiveRecord
end
def type_cast_for_write(value)
- # roundtrip to ensure uniform uniform types
- # TODO: This is not an efficient solution.
- cast_value(type_cast_for_database(value))
- end
-
- def type_cast_for_database(value)
ConnectionAdapters::PostgreSQLColumn.hstore_to_string(value)
end
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 42a5110ffd..c87422fe32 100644
--- a/activerecord/lib/active_record/connection_adapters/postgresql/oid/json.rb
+++ b/activerecord/lib/active_record/connection_adapters/postgresql/oid/json.rb
@@ -8,12 +8,6 @@ module ActiveRecord
end
def type_cast_for_write(value)
- # roundtrip to ensure uniform uniform types
- # TODO: This is not an efficient solution.
- cast_value(type_cast_for_database(value))
- end
-
- def type_cast_for_database(value)
ConnectionAdapters::PostgreSQLColumn.json_to_string(value)
end
diff --git a/activerecord/lib/active_record/connection_adapters/postgresql/oid/money.rb b/activerecord/lib/active_record/connection_adapters/postgresql/oid/money.rb
index 697dceb7c2..d25eb256c2 100644
--- a/activerecord/lib/active_record/connection_adapters/postgresql/oid/money.rb
+++ b/activerecord/lib/active_record/connection_adapters/postgresql/oid/money.rb
@@ -7,6 +7,10 @@ module ActiveRecord
class_attribute :precision
+ def type
+ :money
+ end
+
def scale
2
end
diff --git a/activerecord/lib/active_record/connection_adapters/postgresql/oid/point.rb b/activerecord/lib/active_record/connection_adapters/postgresql/oid/point.rb
index f9531ddee3..9007bfb178 100644
--- a/activerecord/lib/active_record/connection_adapters/postgresql/oid/point.rb
+++ b/activerecord/lib/active_record/connection_adapters/postgresql/oid/point.rb
@@ -2,7 +2,11 @@ module ActiveRecord
module ConnectionAdapters
module PostgreSQL
module OID # :nodoc:
- class Point < Type::String
+ class Point < Type::Value
+ def type
+ :point
+ end
+
def type_cast(value)
if ::String === value
if value[0] == '(' && value[-1] == ')'
diff --git a/activerecord/lib/active_record/connection_adapters/postgresql/quoting.rb b/activerecord/lib/active_record/connection_adapters/postgresql/quoting.rb
index 7b7144a3a8..3cf40e6cd4 100644
--- a/activerecord/lib/active_record/connection_adapters/postgresql/quoting.rb
+++ b/activerecord/lib/active_record/connection_adapters/postgresql/quoting.rb
@@ -44,11 +44,6 @@ module ActiveRecord
when 'json' then super(PostgreSQLColumn.json_to_string(value), column)
else super
end
- when IPAddr
- case sql_type
- when 'inet', 'cidr' then super(PostgreSQLColumn.cidr_to_string(value), column)
- else super
- end
when Float
if value.infinite? && column.type == :datetime
"'#{value.to_s.downcase}'"
@@ -66,7 +61,6 @@ module ActiveRecord
end
when String
case sql_type
- when 'bytea' then "'#{escape_bytea(value)}'"
when 'xml' then "xml '#{quote_string(value)}'"
when /^bit/
case value
@@ -110,27 +104,12 @@ module ActiveRecord
super(value, column)
end
end
- when String
- if 'bytea' == column.sql_type
- # Return a bind param hash with format as binary.
- # See http://deveiate.org/code/pg/PGconn.html#method-i-exec_prepared-doc
- # for more information
- { value: value, format: 1 }
- else
- super(value, column)
- end
when Hash
case column.sql_type
when 'hstore' then PostgreSQLColumn.hstore_to_string(value, array_member)
when 'json' then PostgreSQLColumn.json_to_string(value)
else super(value, column)
end
- when IPAddr
- if %w(inet cidr).include? column.sql_type
- PostgreSQLColumn.cidr_to_string(value)
- else
- super(value, column)
- end
else
super(value, column)
end
@@ -170,8 +149,9 @@ module ActiveRecord
result = "#{result}.#{sprintf("%06d", value.usec)}"
end
- if value.year < 0
- result = result.sub(/^-/, "") + " BC"
+ if value.year <= 0
+ bce_year = format("%04d", -value.year + 1)
+ result = result.sub(/^-?\d+/, bce_year) + " BC"
end
result
end
@@ -184,6 +164,27 @@ module ActiveRecord
quote(value, column)
end
end
+
+ private
+
+ def _quote(value)
+ if value.is_a?(Type::Binary::Data)
+ "'#{escape_bytea(value.to_s)}'"
+ else
+ super
+ end
+ end
+
+ def _type_cast(value)
+ if value.is_a?(Type::Binary::Data)
+ # Return a bind param hash with format as binary.
+ # See http://deveiate.org/code/pg/PGconn.html#method-i-exec_prepared-doc
+ # for more information
+ { value: value.to_s, format: 1 }
+ else
+ super
+ end
+ end
end
end
end
diff --git a/activerecord/lib/active_record/connection_adapters/postgresql/schema_definitions.rb b/activerecord/lib/active_record/connection_adapters/postgresql/schema_definitions.rb
index bcfd605165..0867e5ef54 100644
--- a/activerecord/lib/active_record/connection_adapters/postgresql/schema_definitions.rb
+++ b/activerecord/lib/active_record/connection_adapters/postgresql/schema_definitions.rb
@@ -4,68 +4,84 @@ module ActiveRecord
module ColumnMethods
def xml(*args)
options = args.extract_options!
- column(args[0], 'xml', options)
+ column(args[0], :xml, options)
end
def tsvector(*args)
options = args.extract_options!
- column(args[0], 'tsvector', options)
+ column(args[0], :tsvector, options)
end
def int4range(name, options = {})
- column(name, 'int4range', options)
+ column(name, :int4range, options)
end
def int8range(name, options = {})
- column(name, 'int8range', options)
+ column(name, :int8range, options)
end
def tsrange(name, options = {})
- column(name, 'tsrange', options)
+ column(name, :tsrange, options)
end
def tstzrange(name, options = {})
- column(name, 'tstzrange', options)
+ column(name, :tstzrange, options)
end
def numrange(name, options = {})
- column(name, 'numrange', options)
+ column(name, :numrange, options)
end
def daterange(name, options = {})
- column(name, 'daterange', options)
+ column(name, :daterange, options)
end
def hstore(name, options = {})
- column(name, 'hstore', options)
+ column(name, :hstore, options)
end
def ltree(name, options = {})
- column(name, 'ltree', options)
+ column(name, :ltree, options)
end
def inet(name, options = {})
- column(name, 'inet', options)
+ column(name, :inet, options)
end
def cidr(name, options = {})
- column(name, 'cidr', options)
+ column(name, :cidr, options)
end
def macaddr(name, options = {})
- column(name, 'macaddr', options)
+ column(name, :macaddr, options)
end
def uuid(name, options = {})
- column(name, 'uuid', options)
+ column(name, :uuid, options)
end
def json(name, options = {})
- column(name, 'json', options)
+ column(name, :json, options)
end
def citext(name, options = {})
- column(name, 'citext', options)
+ column(name, :citext, options)
+ end
+
+ def point(name, options = {})
+ column(name, :point, options)
+ end
+
+ def bit(name, options)
+ column(name, :bit, options)
+ end
+
+ def bit_varying(name, options)
+ column(name, :bit_varying, options)
+ end
+
+ def money(name, options)
+ column(name, :money, options)
end
end
diff --git a/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb b/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb
index 23c1a8de2f..283ca81f94 100644
--- a/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb
+++ b/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb
@@ -103,7 +103,11 @@ module ActiveRecord
uuid: { name: "uuid" },
json: { name: "json" },
ltree: { name: "ltree" },
- citext: { name: "citext" }
+ citext: { name: "citext" },
+ point: { name: "point" },
+ bit: { name: "bit" },
+ bit_varying: { name: "bit varying" },
+ money: { name: "money" },
}
OID = PostgreSQL::OID #:nodoc:
@@ -432,8 +436,8 @@ module ActiveRecord
m.alias_type 'name', 'varchar'
m.alias_type 'bpchar', 'varchar'
m.register_type 'bool', Type::Boolean.new
- m.register_type 'bit', OID::Bit.new
- m.alias_type 'varbit', 'bit'
+ register_class_with_limit m, 'bit', OID::Bit
+ register_class_with_limit m, 'varbit', OID::BitVarying
m.alias_type 'timestamptz', 'timestamp'
m.register_type 'date', OID::Date.new
m.register_type 'time', OID::Time.new
@@ -499,64 +503,16 @@ module ActiveRecord
# Extracts the value from a PostgreSQL column default definition.
def extract_value_from_default(oid, default) # :nodoc:
- # This is a performance optimization for Ruby 1.9.2 in development.
- # If the value is nil, we return nil straight away without checking
- # the regular expressions. If we check each regular expression,
- # Regexp#=== will call NilClass#to_str, which will trigger
- # method_missing (defined by whiny nil in ActiveSupport) which
- # makes this method very very slow.
- return default unless default
-
- # TODO: The default extraction is related to the cast-type.
- # we should probably make a type_map lookup and cast the default-
- # expression accordingly
- if oid.type == :enum && default =~ /\A'(.*)'::/
- return $1
- end
-
case default
- when /\A'(.*)'::(num|date|tstz|ts|int4|int8)range\z/m
- $1
+ # Quoted types
+ when /\A[\(B]?'(.*)'::/m
+ $1.gsub(/''/, "'")
+ # Boolean types
+ when 'true', 'false'
+ default
# Numeric types
when /\A\(?(-?\d+(\.\d*)?\)?(::bigint)?)\z/
$1
- # Character types
- when /\A\(?'(.*)'::.*\b(?:character varying|bpchar|text)\z/m
- $1.gsub(/''/, "'")
- # Binary data types
- when /\A'(.*)'::bytea\z/m
- $1
- # Date/time types
- when /\A'(.+)'::(?:time(?:stamp)? with(?:out)? time zone|date)\z/
- $1
- when /\A'(.*)'::interval\z/
- $1
- # Boolean type
- when 'true'
- true
- when 'false'
- false
- # Geometric types
- when /\A'(.*)'::(?:point|line|lseg|box|"?path"?|polygon|circle)\z/
- $1
- # Network address types
- when /\A'(.*)'::(?:cidr|inet|macaddr)\z/
- $1
- # Bit string types
- when /\AB'(.*)'::"?bit(?: varying)?"?\z/
- $1
- # XML type
- when /\A'(.*)'::xml\z/m
- $1
- # Arrays
- when /\A'(.*)'::"?\D+"?\[\]\z/
- $1
- # Hstore
- when /\A'(.*)'::hstore\z/
- $1
- # JSON
- when /\A'(.*)'::json\z/
- $1
# Object identifier types
when /\A-?\d+\z/
$1
diff --git a/activerecord/lib/active_record/connection_adapters/sqlite3_adapter.rb b/activerecord/lib/active_record/connection_adapters/sqlite3_adapter.rb
index adf893d7e7..e6163771e8 100644
--- a/activerecord/lib/active_record/connection_adapters/sqlite3_adapter.rb
+++ b/activerecord/lib/active_record/connection_adapters/sqlite3_adapter.rb
@@ -219,10 +219,9 @@ module ActiveRecord
# QUOTING ==================================================
- def quote(value, column = nil)
- if value.kind_of?(String) && column && column.type == :binary
- s = value.unpack("H*")[0]
- "x'#{s}'"
+ def _quote(value) # :nodoc:
+ if value.is_a?(Type::Binary::Data)
+ "x'#{value.hex}'"
else
super
end
diff --git a/activerecord/lib/active_record/core.rb b/activerecord/lib/active_record/core.rb
index e99e235fe8..c996e93076 100644
--- a/activerecord/lib/active_record/core.rb
+++ b/activerecord/lib/active_record/core.rb
@@ -249,10 +249,10 @@ module ActiveRecord
# # Instantiates a single new object
# User.new(first_name: 'Jamie')
def initialize(attributes = nil, options = {})
- defaults = self.class.column_defaults.dup
+ defaults = self.class.raw_column_defaults.dup
defaults.each { |k, v| defaults[k] = v.dup if v.duplicable? }
- @raw_attributes = self.class.initialize_attributes(defaults)
+ @raw_attributes = defaults
@column_types_override = nil
@column_types = self.class.column_types
@@ -278,13 +278,14 @@ module ActiveRecord
# post.init_with('attributes' => { 'title' => 'hello world' })
# post.title # => 'hello world'
def init_with(coder)
- @raw_attributes = self.class.initialize_attributes(coder['attributes'])
+ @raw_attributes = coder['raw_attributes']
@column_types_override = coder['column_types']
@column_types = self.class.column_types
init_internals
- @new_record = false
+ @attributes = coder['attributes'] if coder['attributes']
+ @new_record = coder['new_record']
self.class.define_attribute_methods
@@ -323,16 +324,16 @@ module ActiveRecord
##
def initialize_dup(other) # :nodoc:
cloned_attributes = other.clone_attributes(:read_attribute_before_type_cast)
- self.class.initialize_attributes(cloned_attributes, :serialized => false)
@raw_attributes = cloned_attributes
@raw_attributes[self.class.primary_key] = nil
+ @attributes = other.clone_attributes(:read_attribute)
+ @attributes[self.class.primary_key] = nil
run_callbacks(:initialize) unless _initialize_callbacks.empty?
@aggregation_cache = {}
@association_cache = {}
- @attributes = {}
@new_record = true
@destroyed = false
@@ -353,7 +354,9 @@ module ActiveRecord
# Post.new.encode_with(coder)
# coder # => {"attributes" => {"id" => nil, ... }}
def encode_with(coder)
- coder['attributes'] = attributes_for_coder
+ coder['raw_attributes'] = @raw_attributes
+ coder['attributes'] = @attributes
+ coder['new_record'] = new_record?
end
# Returns true if +comparison_object+ is the same exact object, or +comparison_object+
@@ -432,6 +435,29 @@ module ActiveRecord
"#<#{self.class} #{inspection}>"
end
+ # Takes a PP and prettily prints this record to it, allowing you to get a nice result from `pp record`
+ # when pp is required.
+ def pretty_print(pp)
+ pp.object_address_group(self) do
+ if defined?(@attributes) && @attributes
+ column_names = self.class.column_names.select { |name| has_attribute?(name) || new_record? }
+ pp.seplist(column_names, proc { pp.text ',' }) do |column_name|
+ column_value = read_attribute(column_name)
+ pp.breakable ' '
+ pp.group(1) do
+ pp.text column_name
+ pp.text ':'
+ pp.breakable
+ pp.pp column_value
+ end
+ end
+ else
+ pp.breakable ' '
+ pp.text 'not initialized'
+ end
+ end
+ end
+
# Returns a hash of the given methods with their names as keys and returned values as values.
def slice(*methods)
Hash[methods.map! { |method| [method, public_send(method)] }].with_indifferent_access
diff --git a/activerecord/lib/active_record/fixtures.rb b/activerecord/lib/active_record/fixtures.rb
index 71d1e85b0d..6ba667b996 100644
--- a/activerecord/lib/active_record/fixtures.rb
+++ b/activerecord/lib/active_record/fixtures.rb
@@ -657,7 +657,7 @@ module ActiveRecord
fk_name = (association.options[:foreign_key] || "#{association.name}_id").to_s
if association.name.to_s != fk_name && value = row.delete(association.name.to_s)
- if association.options[:polymorphic] && value.sub!(/\s*\(([^\)]*)\)\s*$/, "")
+ if association.polymorphic? && value.sub!(/\s*\(([^\)]*)\)\s*$/, "")
# support polymorphic belongs_to as "label (Type)"
row[association.foreign_type] = $1
end
diff --git a/activerecord/lib/active_record/migration.rb b/activerecord/lib/active_record/migration.rb
index 8fe32bcb6c..481e5c17e4 100644
--- a/activerecord/lib/active_record/migration.rb
+++ b/activerecord/lib/active_record/migration.rb
@@ -711,7 +711,7 @@ module ActiveRecord
if ActiveRecord::Base.timestamped_migrations
[Time.now.utc.strftime("%Y%m%d%H%M%S"), "%.14d" % number].max
else
- "%.3d" % number
+ SchemaMigration.normalize_migration_number(number)
end
end
@@ -851,19 +851,6 @@ module ActiveRecord
migrations(migrations_paths).last || NullMigration.new
end
- def proper_table_name(name, options = {})
- ActiveSupport::Deprecation.warn "ActiveRecord::Migrator.proper_table_name is deprecated and will be removed in Rails 4.2. Use the proper_table_name instance method on ActiveRecord::Migration instead"
- options = {
- table_name_prefix: ActiveRecord::Base.table_name_prefix,
- table_name_suffix: ActiveRecord::Base.table_name_suffix
- }.merge(options)
- if name.respond_to? :table_name
- name.table_name
- else
- "#{options[:table_name_prefix]}#{name}#{options[:table_name_suffix]}"
- end
- end
-
def migrations_paths
@migrations_paths ||= ['db/migrate']
# just to not break things if someone uses: migration_path = some_string
diff --git a/activerecord/lib/active_record/model_schema.rb b/activerecord/lib/active_record/model_schema.rb
index ad6428d8a8..baf2b5fbf8 100644
--- a/activerecord/lib/active_record/model_schema.rb
+++ b/activerecord/lib/active_record/model_schema.rb
@@ -241,6 +241,14 @@ module ActiveRecord
@column_defaults ||= Hash[columns.map { |c| [c.name, c.default] }]
end
+ # Returns a hash where the keys are the column names and the values
+ # 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)]
+ }]
+ end
+
# Returns an array of column names as strings.
def column_names
@column_names ||= columns.map { |column| column.name }
@@ -285,6 +293,7 @@ module ActiveRecord
@arel_engine = nil
@column_defaults = nil
+ @raw_column_defauts = nil
@column_names = nil
@column_types = nil
@content_columns = nil
@@ -295,13 +304,6 @@ module ActiveRecord
@cached_time_zone = nil
end
- # This is a hook for use by modules that need to do extra stuff to
- # attributes when they are initialized. (e.g. attribute
- # serialization)
- def initialize_attributes(attributes, options = {}) #:nodoc:
- attributes
- end
-
private
# Guesses the table name, but does not decorate it with prefix and suffix information.
diff --git a/activerecord/lib/active_record/persistence.rb b/activerecord/lib/active_record/persistence.rb
index 78ae05073a..f1f0d3e57f 100644
--- a/activerecord/lib/active_record/persistence.rb
+++ b/activerecord/lib/active_record/persistence.rb
@@ -49,7 +49,11 @@ module ActiveRecord
def instantiate(attributes, column_types = {})
klass = discriminate_class_for_record(attributes)
column_types = klass.decorate_columns(column_types.dup)
- klass.allocate.init_with('attributes' => attributes, 'column_types' => column_types)
+ klass.allocate.init_with(
+ 'raw_attributes' => attributes,
+ 'column_types' => column_types,
+ 'new_record' => false,
+ )
end
private
diff --git a/activerecord/lib/active_record/properties.rb b/activerecord/lib/active_record/properties.rb
index e70f84808d..48ee42aaca 100644
--- a/activerecord/lib/active_record/properties.rb
+++ b/activerecord/lib/active_record/properties.rb
@@ -5,7 +5,7 @@ module ActiveRecord
Type = ActiveRecord::Type
included do
- class_attribute :user_provided_columns, instance_accessor: false # :internal
+ class_attribute :user_provided_columns, instance_accessor: false # :internal:
self.user_provided_columns = {}
end
@@ -113,6 +113,7 @@ module ActiveRecord
@columns_hash = nil
@column_types = nil
@column_defaults = nil
+ @raw_column_defaults = nil
@column_names = nil
@content_columns = nil
end
diff --git a/activerecord/lib/active_record/railties/databases.rake b/activerecord/lib/active_record/railties/databases.rake
index fa25ceaefa..6dca206f2a 100644
--- a/activerecord/lib/active_record/railties/databases.rake
+++ b/activerecord/lib/active_record/railties/databases.rake
@@ -86,14 +86,15 @@ db_namespace = namespace :db do
abort 'Schema migrations table does not exist yet.'
end
db_list = ActiveRecord::Base.connection.select_values("SELECT version FROM #{ActiveRecord::Migrator.schema_migrations_table_name}")
- db_list.map! { |version| "%.3d" % version }
+ db_list.map! { |version| ActiveRecord::SchemaMigration.normalize_migration_number(version) }
file_list = []
ActiveRecord::Migrator.migrations_paths.each do |path|
Dir.foreach(path) do |file|
# match "20091231235959_some_name.rb" and "001_some_name.rb" pattern
if match_data = /^(\d{3,})_(.+)\.rb$/.match(file)
- status = db_list.delete(match_data[1]) ? 'up' : 'down'
- file_list << [status, match_data[1], match_data[2].humanize]
+ version = ActiveRecord::SchemaMigration.normalize_migration_number(match_data[1])
+ status = db_list.delete(version) ? 'up' : 'down'
+ file_list << [status, version, match_data[2].humanize]
end
end
end
diff --git a/activerecord/lib/active_record/reflection.rb b/activerecord/lib/active_record/reflection.rb
index dd80ec6274..4d5203612c 100644
--- a/activerecord/lib/active_record/reflection.rb
+++ b/activerecord/lib/active_record/reflection.rb
@@ -239,7 +239,7 @@ module ActiveRecord
def association_scope_cache(conn, owner)
key = conn.prepared_statements
- if options[:polymorphic]
+ if polymorphic?
key = [key, owner.read_attribute(@foreign_type)]
end
@association_scope_cache[key] ||= @scope_lock.synchronize {
@@ -303,7 +303,7 @@ module ActiveRecord
end
def check_validity_of_inverse!
- unless options[:polymorphic]
+ unless polymorphic?
if has_inverse? && inverse_of.nil?
raise InverseOfAssociationNotFoundError.new(self)
end
@@ -403,7 +403,7 @@ Joining, Preloading and eager loading of these associations is deprecated and wi
def association_class
case macro
when :belongs_to
- if options[:polymorphic]
+ if polymorphic?
Associations::BelongsToPolymorphicAssociation
else
Associations::BelongsToAssociation
@@ -424,7 +424,7 @@ Joining, Preloading and eager loading of these associations is deprecated and wi
end
def polymorphic?
- options.key? :polymorphic
+ options[:polymorphic]
end
VALID_AUTOMATIC_INVERSE_MACROS = [:has_many, :has_one, :belongs_to]
@@ -441,7 +441,7 @@ Joining, Preloading and eager loading of these associations is deprecated and wi
def calculate_constructable(macro, options)
case macro
when :belongs_to
- !options[:polymorphic]
+ !polymorphic?
when :has_one
!options[:through]
else
@@ -723,7 +723,7 @@ directive on your declaration like:
raise HasManyThroughAssociationNotFoundError.new(active_record.name, self)
end
- if through_reflection.options[:polymorphic]
+ if through_reflection.polymorphic?
raise HasManyThroughAssociationPolymorphicThroughError.new(active_record.name, self)
end
@@ -731,11 +731,11 @@ directive on your declaration like:
raise HasManyThroughSourceAssociationNotFoundError.new(self)
end
- if options[:source_type] && source_reflection.options[:polymorphic].nil?
+ if options[:source_type] && !source_reflection.polymorphic?
raise HasManyThroughAssociationPointlessSourceTypeError.new(active_record.name, self, source_reflection)
end
- if source_reflection.options[:polymorphic] && options[:source_type].nil?
+ if source_reflection.polymorphic? && options[:source_type].nil?
raise HasManyThroughAssociationPolymorphicSourceError.new(active_record.name, self, source_reflection)
end
diff --git a/activerecord/lib/active_record/relation.rb b/activerecord/lib/active_record/relation.rb
index d92ff781ee..cef40be7ce 100644
--- a/activerecord/lib/active_record/relation.rb
+++ b/activerecord/lib/active_record/relation.rb
@@ -74,7 +74,14 @@ module ActiveRecord
def _update_record(values, id, id_was) # :nodoc:
substitutes, binds = substitute_values values
- um = @klass.unscoped.where(@klass.arel_table[@klass.primary_key].eq(id_was || id)).arel.compile_update(substitutes, @klass.primary_key)
+
+ scope = @klass.unscoped
+
+ if @klass.finder_needs_type_condition?
+ scope.unscope!(where: @klass.inheritance_column)
+ end
+
+ um = scope.where(@klass.arel_table[@klass.primary_key].eq(id_was || id)).arel.compile_update(substitutes, @klass.primary_key)
@klass.connection.update(
um,
diff --git a/activerecord/lib/active_record/relation/calculations.rb b/activerecord/lib/active_record/relation/calculations.rb
index d155517b18..11ab1b4595 100644
--- a/activerecord/lib/active_record/relation/calculations.rb
+++ b/activerecord/lib/active_record/relation/calculations.rb
@@ -177,7 +177,7 @@ module ActiveRecord
end
result = result.map do |attributes|
- values = klass.initialize_attributes(attributes).values
+ values = attributes.values
columns.zip(values).map { |column, value| column.type_cast value }
end
@@ -278,7 +278,7 @@ module ActiveRecord
if group_attrs.first.respond_to?(:to_sym)
association = @klass._reflect_on_association(group_attrs.first.to_sym)
- associated = group_attrs.size == 1 && association && association.macro == :belongs_to # only count belongs_to associations
+ associated = group_attrs.size == 1 && association && association.belongs_to? # only count belongs_to associations
group_fields = Array(associated ? association.foreign_key : group_attrs)
else
group_fields = group_attrs
diff --git a/activerecord/lib/active_record/schema_migration.rb b/activerecord/lib/active_record/schema_migration.rb
index a9d164e366..3a004d58c9 100644
--- a/activerecord/lib/active_record/schema_migration.rb
+++ b/activerecord/lib/active_record/schema_migration.rb
@@ -5,6 +5,9 @@ require 'active_record/base'
module ActiveRecord
class SchemaMigration < ActiveRecord::Base
class << self
+ def primary_key
+ nil
+ end
def table_name
"#{table_name_prefix}#{ActiveRecord::Base.schema_migrations_table_name}#{table_name_suffix}"
@@ -36,6 +39,10 @@ module ActiveRecord
connection.drop_table(table_name)
end
end
+
+ def normalize_migration_number(number)
+ "%.3d" % number.to_i
+ end
end
def version
diff --git a/activerecord/lib/active_record/serializers/xml_serializer.rb b/activerecord/lib/active_record/serializers/xml_serializer.rb
index 1a766093d0..019fe2218e 100644
--- a/activerecord/lib/active_record/serializers/xml_serializer.rb
+++ b/activerecord/lib/active_record/serializers/xml_serializer.rb
@@ -180,12 +180,12 @@ module ActiveRecord #:nodoc:
class Attribute < ActiveModel::Serializers::Xml::Serializer::Attribute #:nodoc:
def compute_type
klass = @serializable.class
- type = if klass.serialized_attributes.key?(name)
+ column = klass.columns_hash[name] || Type::Value.new
+
+ type = if column.serialized?
super
- elsif klass.columns_hash.key?(name)
- klass.columns_hash[name].type
else
- NilClass
+ column.type
end
{ :text => :string,
diff --git a/activerecord/lib/active_record/type/binary.rb b/activerecord/lib/active_record/type/binary.rb
index e34b7bb268..bc93f6e1bf 100644
--- a/activerecord/lib/active_record/type/binary.rb
+++ b/activerecord/lib/active_record/type/binary.rb
@@ -9,8 +9,22 @@ module ActiveRecord
true
end
- def klass
- ::String
+ def type_cast_for_database(value)
+ Data.new(super)
+ end
+
+ class Data
+ def initialize(value)
+ @value = value
+ end
+
+ def to_s
+ @value
+ end
+
+ def hex
+ @value.unpack('H*')[0]
+ end
end
end
end
diff --git a/activerecord/lib/active_record/type/decimal.rb b/activerecord/lib/active_record/type/decimal.rb
index 6eed005345..a9db51c6ba 100644
--- a/activerecord/lib/active_record/type/decimal.rb
+++ b/activerecord/lib/active_record/type/decimal.rb
@@ -7,10 +7,6 @@ module ActiveRecord
:decimal
end
- def klass
- ::BigDecimal
- end
-
def type_cast_for_schema(value)
value.to_s
end
diff --git a/activerecord/lib/active_record/type/float.rb b/activerecord/lib/active_record/type/float.rb
index dc50dae328..42eb44b9a9 100644
--- a/activerecord/lib/active_record/type/float.rb
+++ b/activerecord/lib/active_record/type/float.rb
@@ -7,10 +7,6 @@ module ActiveRecord
:float
end
- def klass
- ::Float
- end
-
alias type_cast_for_database type_cast
private
diff --git a/activerecord/lib/active_record/type/integer.rb b/activerecord/lib/active_record/type/integer.rb
index 1e2147dec9..08477d1303 100644
--- a/activerecord/lib/active_record/type/integer.rb
+++ b/activerecord/lib/active_record/type/integer.rb
@@ -7,10 +7,6 @@ module ActiveRecord
:integer
end
- def klass
- ::Fixnum
- end
-
alias type_cast_for_database type_cast
private
diff --git a/activerecord/lib/active_record/type/numeric.rb b/activerecord/lib/active_record/type/numeric.rb
index 464d631d80..d5cb13233c 100644
--- a/activerecord/lib/active_record/type/numeric.rb
+++ b/activerecord/lib/active_record/type/numeric.rb
@@ -5,14 +5,38 @@ module ActiveRecord
true
end
- def type_cast_for_write(value)
- case value
- when true then 1
- when false then 0
- when ::String then value.presence
- else super
+ def type_cast(value)
+ value = case value
+ when true then 1
+ when false then 0
+ when ::String then value.presence
+ else value
+ end
+ super(value)
+ end
+
+ def changed?(old_value, new_value) # :nodoc:
+ # 0 => 'wibble' should mark as changed so numericality validations run
+ if nil_or_zero?(old_value) && non_numeric_string?(new_value)
+ # nil => '' should not mark as changed
+ old_value != new_value.presence
+ else
+ super
end
end
+
+ private
+
+ def non_numeric_string?(value)
+ # 'wibble'.to_i will give zero, we want to make sure
+ # that we aren't marking int zero to string zero as
+ # changed.
+ value !~ /\A\d+\.?\d*\z/
+ end
+
+ def nil_or_zero?(value)
+ value.nil? || value == 0
+ end
end
end
end
diff --git a/activerecord/lib/active_record/type/serialized.rb b/activerecord/lib/active_record/type/serialized.rb
index 4052ac0fa0..94d9d9a549 100644
--- a/activerecord/lib/active_record/type/serialized.rb
+++ b/activerecord/lib/active_record/type/serialized.rb
@@ -10,20 +10,21 @@ module ActiveRecord
end
def type_cast(value)
- if value.respond_to?(:unserialized_value)
- value.unserialized_value(super(value.value))
+ if is_default_value?(value)
+ value
else
- super
+ coder.load(super)
end
end
def type_cast_for_write(value)
- Attribute.new(coder, value, :unserialized)
+ return if value.nil?
+ unless is_default_value?(value)
+ super coder.dump(value)
+ end
end
- def raw_type_cast_for_write(value)
- Attribute.new(coder, value, :serialized)
- end
+ alias type_cast_for_database type_cast_for_write
def serialized?
true
@@ -33,24 +34,14 @@ module ActiveRecord
ActiveRecord::Store::IndifferentHashAccessor
end
- class Attribute < Struct.new(:coder, :value, :state) # :nodoc:
- def unserialized_value(v = value)
- state == :serialized ? unserialize(v) : value
- end
-
- def serialized_value
- state == :unserialized ? serialize : value
- end
+ private
- def unserialize(v)
- self.state = :unserialized
- self.value = coder.load(v)
- end
+ def changed?(old_value, new_value) # :nodoc:
+ old_value != new_value
+ end
- def serialize
- self.state = :serialized
- self.value = coder.dump(value)
- end
+ def is_default_value?(value)
+ value == coder.load(nil)
end
end
end
diff --git a/activerecord/lib/active_record/type/string.rb b/activerecord/lib/active_record/type/string.rb
index b3f6ca7691..3b1554bd5a 100644
--- a/activerecord/lib/active_record/type/string.rb
+++ b/activerecord/lib/active_record/type/string.rb
@@ -9,10 +9,6 @@ module ActiveRecord
true
end
- def klass
- ::String
- end
-
private
def cast_value(value)
diff --git a/activerecord/lib/active_record/type/value.rb b/activerecord/lib/active_record/type/value.rb
index 9c1e9dc01e..4bc3086db3 100644
--- a/activerecord/lib/active_record/type/value.rb
+++ b/activerecord/lib/active_record/type/value.rb
@@ -12,8 +12,8 @@ module ActiveRecord
@limit = options[:limit]
end
- # The simplified that this object represents. Subclasses
- # should override this method.
+ # The simplified type that this object represents. Subclasses
+ # must override this method.
def type; end
# Takes an input from the database, or from attribute setters,
@@ -48,13 +48,20 @@ module ActiveRecord
end
def klass # :nodoc:
- ::Object
end
def type_cast_for_write(value) # :nodoc:
value
end
- alias_method :raw_type_cast_for_write, :type_cast_for_write # :internal:
+
+ # +old_value+ will always be type-cast.
+ # +new_value+ will come straight from the database
+ # or from assignment, so it could be anything. Types
+ # which cannot typecast arbitrary values should override
+ # this method.
+ def changed?(old_value, new_value) # :nodoc:
+ old_value != type_cast(new_value)
+ end
private
diff --git a/activerecord/lib/active_record/validations/uniqueness.rb b/activerecord/lib/active_record/validations/uniqueness.rb
index b6fccc9b94..2e7b1d7206 100644
--- a/activerecord/lib/active_record/validations/uniqueness.rb
+++ b/activerecord/lib/active_record/validations/uniqueness.rb
@@ -14,7 +14,6 @@ module ActiveRecord
finder_class = find_finder_class_for(record)
table = finder_class.arel_table
value = map_enum_attribute(finder_class, attribute, value)
- value = deserialize_attribute(record, attribute, value)
relation = build_relation(finder_class, table, attribute, value)
relation = relation.and(table[finder_class.primary_key.to_sym].not_eq(record.id)) if record.persisted?
@@ -86,12 +85,6 @@ module ActiveRecord
relation
end
- def deserialize_attribute(record, attribute, value)
- coder = record.class.serialized_attributes[attribute.to_s]
- value = coder.dump value if value && coder
- value
- end
-
def map_enum_attribute(klass, attribute, value)
mapping = klass.defined_enums[attribute.to_s]
value = mapping[value] if value && mapping
diff --git a/activerecord/test/cases/adapters/postgresql/array_test.rb b/activerecord/test/cases/adapters/postgresql/array_test.rb
index 34c2008ab4..e03d83df59 100644
--- a/activerecord/test/cases/adapters/postgresql/array_test.rb
+++ b/activerecord/test/cases/adapters/postgresql/array_test.rb
@@ -1,7 +1,5 @@
# encoding: utf-8
require "cases/helper"
-require 'active_record/base'
-require 'active_record/connection_adapters/postgresql_adapter'
class PostgresqlArrayTest < ActiveRecord::TestCase
class PgArray < ActiveRecord::Base
diff --git a/activerecord/test/cases/adapters/postgresql/bit_string_test.rb b/activerecord/test/cases/adapters/postgresql/bit_string_test.rb
new file mode 100644
index 0000000000..3a9397bc26
--- /dev/null
+++ b/activerecord/test/cases/adapters/postgresql/bit_string_test.rb
@@ -0,0 +1,80 @@
+# -*- coding: utf-8 -*-
+require "cases/helper"
+require 'support/connection_helper'
+require 'support/schema_dumping_helper'
+
+class PostgresqlBitStringTest < ActiveRecord::TestCase
+ include ConnectionHelper
+ include SchemaDumpingHelper
+
+ class PostgresqlBitString < ActiveRecord::Base; end
+
+ def setup
+ @connection = ActiveRecord::Base.connection
+ @connection.create_table('postgresql_bit_strings', :force => true) do |t|
+ t.bit :a_bit, default: "00000011", limit: 8
+ t.bit_varying :a_bit_varying, default: "0011", limit: 4
+ end
+ end
+
+ def teardown
+ return unless @connection
+ @connection.execute 'DROP TABLE IF EXISTS postgresql_bit_strings'
+ end
+
+ def test_bit_string_column
+ column = PostgresqlBitString.columns_hash["a_bit"]
+ assert_equal :bit, column.type
+ assert_equal "bit(8)", column.sql_type
+ assert_not column.text?
+ assert_not column.number?
+ assert_not column.binary?
+ assert_not column.array
+ end
+
+ def test_bit_string_varying_column
+ column = PostgresqlBitString.columns_hash["a_bit_varying"]
+ assert_equal :bit_varying, column.type
+ assert_equal "bit varying(4)", column.sql_type
+ assert_not column.text?
+ assert_not column.number?
+ assert_not column.binary?
+ assert_not column.array
+ end
+
+ def test_default
+ column = PostgresqlBitString.columns_hash["a_bit"]
+ assert_equal "00000011", column.default
+ assert_equal "00000011", PostgresqlBitString.new.a_bit
+
+ column = PostgresqlBitString.columns_hash["a_bit_varying"]
+ assert_equal "0011", column.default
+ assert_equal "0011", PostgresqlBitString.new.a_bit_varying
+ end
+
+ def test_schema_dumping
+ output = dump_table_schema("postgresql_bit_strings")
+ assert_match %r{t\.bit\s+"a_bit",\s+limit: 8,\s+default: "00000011"$}, output
+ assert_match %r{t\.bit_varying\s+"a_bit_varying",\s+limit: 4,\s+default: "0011"$}, output
+ end
+
+ def test_assigning_invalid_hex_string_raises_exception
+ assert_raises(ActiveRecord::StatementInvalid) { PostgresqlBitString.create! a_bit: "FF" }
+ assert_raises(ActiveRecord::StatementInvalid) { PostgresqlBitString.create! a_bit_varying: "FF" }
+ end
+
+ def test_roundtrip
+ PostgresqlBitString.create! a_bit: "00001010", a_bit_varying: "0101"
+ record = PostgresqlBitString.first
+ assert_equal "00001010", record.a_bit
+ assert_equal "0101", record.a_bit_varying
+
+ record.a_bit = "11111111"
+ record.a_bit_varying = "0xF"
+ record.save!
+
+ assert record.reload
+ assert_equal "11111111", record.a_bit
+ assert_equal "1111", record.a_bit_varying
+ end
+end
diff --git a/activerecord/test/cases/adapters/postgresql/bytea_test.rb b/activerecord/test/cases/adapters/postgresql/bytea_test.rb
index fadadfa57c..3f8a5d1062 100644
--- a/activerecord/test/cases/adapters/postgresql/bytea_test.rb
+++ b/activerecord/test/cases/adapters/postgresql/bytea_test.rb
@@ -1,8 +1,5 @@
# encoding: utf-8
-
require "cases/helper"
-require 'active_record/base'
-require 'active_record/connection_adapters/postgresql_adapter'
class PostgresqlByteaTest < ActiveRecord::TestCase
class ByteaDataType < ActiveRecord::Base
diff --git a/activerecord/test/cases/adapters/postgresql/citext_test.rb b/activerecord/test/cases/adapters/postgresql/citext_test.rb
index 8493050726..90e837d426 100644
--- a/activerecord/test/cases/adapters/postgresql/citext_test.rb
+++ b/activerecord/test/cases/adapters/postgresql/citext_test.rb
@@ -1,8 +1,5 @@
# encoding: utf-8
-
require 'cases/helper'
-require 'active_record/base'
-require 'active_record/connection_adapters/postgresql_adapter'
if ActiveRecord::Base.connection.supports_extensions?
class PostgresqlCitextTest < ActiveRecord::TestCase
diff --git a/activerecord/test/cases/adapters/postgresql/composite_test.rb b/activerecord/test/cases/adapters/postgresql/composite_test.rb
index ecccbf10e6..a925263098 100644
--- a/activerecord/test/cases/adapters/postgresql/composite_test.rb
+++ b/activerecord/test/cases/adapters/postgresql/composite_test.rb
@@ -1,8 +1,6 @@
# -*- coding: utf-8 -*-
require "cases/helper"
require 'support/connection_helper'
-require 'active_record/base'
-require 'active_record/connection_adapters/postgresql_adapter'
module PostgresqlCompositeBehavior
include ConnectionHelper
@@ -93,6 +91,7 @@ class PostgresqlCompositeWithCustomOIDTest < ActiveRecord::TestCase
end
def type_cast_for_write(value)
+ return if value.nil?
"(#{value.city},#{value.street})"
end
end
diff --git a/activerecord/test/cases/adapters/postgresql/datatype_test.rb b/activerecord/test/cases/adapters/postgresql/datatype_test.rb
index 0dad89c67a..a0a34e4b87 100644
--- a/activerecord/test/cases/adapters/postgresql/datatype_test.rb
+++ b/activerecord/test/cases/adapters/postgresql/datatype_test.rb
@@ -8,9 +8,6 @@ end
class PostgresqlTime < ActiveRecord::Base
end
-class PostgresqlBitString < ActiveRecord::Base
-end
-
class PostgresqlOid < ActiveRecord::Base
end
@@ -33,15 +30,12 @@ class PostgresqlDataTypeTest < ActiveRecord::TestCase
@connection.execute("INSERT INTO postgresql_times (id, time_interval, scaled_time_interval) VALUES (1, '1 year 2 days ago', '3 weeks ago')")
@first_time = PostgresqlTime.find(1)
- @connection.execute("INSERT INTO postgresql_bit_strings (id, bit_string, bit_string_varying) VALUES (1, B'00010101', X'15')")
- @first_bit_string = PostgresqlBitString.find(1)
-
@connection.execute("INSERT INTO postgresql_oids (id, obj_id) VALUES (1, 1234)")
@first_oid = PostgresqlOid.find(1)
end
teardown do
- [PostgresqlNumber, PostgresqlTime, PostgresqlBitString, PostgresqlOid].each(&:delete_all)
+ [PostgresqlNumber, PostgresqlTime, PostgresqlOid].each(&:delete_all)
end
def test_data_type_of_number_types
@@ -54,11 +48,6 @@ class PostgresqlDataTypeTest < ActiveRecord::TestCase
assert_equal :string, @first_time.column_for_attribute(:scaled_time_interval).type
end
- def test_data_type_of_bit_string_types
- assert_equal :string, @first_bit_string.column_for_attribute(:bit_string).type
- assert_equal :string, @first_bit_string.column_for_attribute(:bit_string_varying).type
- end
-
def test_data_type_of_oid_types
assert_equal :integer, @first_oid.column_for_attribute(:obj_id).type
end
@@ -76,11 +65,6 @@ class PostgresqlDataTypeTest < ActiveRecord::TestCase
assert_equal '-21 days', @first_time.scaled_time_interval
end
- def test_bit_string_values
- assert_equal '00010101', @first_bit_string.bit_string
- assert_equal '00010101', @first_bit_string.bit_string_varying
- end
-
def test_oid_values
assert_equal 1234, @first_oid.obj_id
end
@@ -103,23 +87,6 @@ class PostgresqlDataTypeTest < ActiveRecord::TestCase
assert_equal '2 years 00:03:00', @first_time.time_interval
end
- def test_update_bit_string
- new_bit_string = '11111111'
- new_bit_string_varying = '0xFF'
- @first_bit_string.bit_string = new_bit_string
- @first_bit_string.bit_string_varying = new_bit_string_varying
- assert @first_bit_string.save
- assert @first_bit_string.reload
- assert_equal new_bit_string, @first_bit_string.bit_string
- assert_equal @first_bit_string.bit_string, @first_bit_string.bit_string_varying
- end
-
- def test_invalid_hex_string
- new_bit_string = 'FF'
- @first_bit_string.bit_string = new_bit_string
- assert_raise(ActiveRecord::StatementInvalid) { assert @first_bit_string.save }
- end
-
def test_update_oid
new_value = 567890
@first_oid.obj_id = new_value
diff --git a/activerecord/test/cases/adapters/postgresql/domain_test.rb b/activerecord/test/cases/adapters/postgresql/domain_test.rb
index 5286a847a4..fd7fdecff1 100644
--- a/activerecord/test/cases/adapters/postgresql/domain_test.rb
+++ b/activerecord/test/cases/adapters/postgresql/domain_test.rb
@@ -1,8 +1,6 @@
# -*- coding: utf-8 -*-
require "cases/helper"
require 'support/connection_helper'
-require 'active_record/base'
-require 'active_record/connection_adapters/postgresql_adapter'
class PostgresqlDomainTest < ActiveRecord::TestCase
include ConnectionHelper
diff --git a/activerecord/test/cases/adapters/postgresql/extension_migration_test.rb b/activerecord/test/cases/adapters/postgresql/extension_migration_test.rb
index 91058f8681..7b99fcdda0 100644
--- a/activerecord/test/cases/adapters/postgresql/extension_migration_test.rb
+++ b/activerecord/test/cases/adapters/postgresql/extension_migration_test.rb
@@ -1,6 +1,4 @@
require "cases/helper"
-require "active_record/base"
-require "active_record/connection_adapters/postgresql_adapter"
class PostgresqlExtensionMigrationTest < ActiveRecord::TestCase
self.use_transactional_fixtures = false
diff --git a/activerecord/test/cases/adapters/postgresql/full_text_test.rb b/activerecord/test/cases/adapters/postgresql/full_text_test.rb
index 4442abcbc4..ec646de5e9 100644
--- a/activerecord/test/cases/adapters/postgresql/full_text_test.rb
+++ b/activerecord/test/cases/adapters/postgresql/full_text_test.rb
@@ -1,8 +1,5 @@
# encoding: utf-8
-
require "cases/helper"
-require 'active_record/base'
-require 'active_record/connection_adapters/postgresql_adapter'
class PostgresqlFullTextTest < ActiveRecord::TestCase
class PostgresqlTsvector < ActiveRecord::Base; end
diff --git a/activerecord/test/cases/adapters/postgresql/geometric_test.rb b/activerecord/test/cases/adapters/postgresql/geometric_test.rb
new file mode 100644
index 0000000000..2f106ee664
--- /dev/null
+++ b/activerecord/test/cases/adapters/postgresql/geometric_test.rb
@@ -0,0 +1,64 @@
+# -*- coding: utf-8 -*-
+require "cases/helper"
+require 'support/connection_helper'
+require 'support/schema_dumping_helper'
+
+class PostgresqlPointTest < ActiveRecord::TestCase
+ include ConnectionHelper
+ include SchemaDumpingHelper
+
+ class PostgresqlPoint < ActiveRecord::Base; end
+
+ def setup
+ @connection = ActiveRecord::Base.connection
+ @connection.transaction do
+ @connection.create_table('postgresql_points') do |t|
+ t.point :x
+ t.point :y, default: [12.2, 13.3]
+ t.point :z, default: "(14.4,15.5)"
+ end
+ end
+ end
+
+ teardown do
+ @connection.execute 'DROP TABLE IF EXISTS postgresql_points'
+ end
+
+ def test_column
+ column = PostgresqlPoint.columns_hash["x"]
+ assert_equal :point, column.type
+ assert_equal "point", column.sql_type
+ assert_not column.text?
+ assert_not column.number?
+ assert_not column.binary?
+ assert_not column.array
+ end
+
+ def test_default
+ column = PostgresqlPoint.columns_hash["y"]
+ assert_equal [12.2, 13.3], column.default
+ assert_equal [12.2, 13.3], PostgresqlPoint.new.y
+
+ column = PostgresqlPoint.columns_hash["z"]
+ assert_equal [14.4, 15.5], column.default
+ assert_equal [14.4, 15.5], PostgresqlPoint.new.z
+ end
+
+ def test_schema_dumping
+ output = dump_table_schema("postgresql_points")
+ assert_match %r{t\.point\s+"x"$}, output
+ assert_match %r{t\.point\s+"y",\s+default: \[12\.2, 13\.3\]$}, output
+ assert_match %r{t\.point\s+"z",\s+default: \[14\.4, 15\.5\]$}, output
+ end
+
+ def test_roundtrip
+ PostgresqlPoint.create! x: [10, 25.2]
+ record = PostgresqlPoint.first
+ assert_equal [10, 25.2], record.x
+
+ record.x = [1.1, 2.2]
+ record.save!
+ assert record.reload
+ assert_equal [1.1, 2.2], record.x
+ end
+end
diff --git a/activerecord/test/cases/adapters/postgresql/hstore_test.rb b/activerecord/test/cases/adapters/postgresql/hstore_test.rb
index 1fef4899be..a6482786c7 100644
--- a/activerecord/test/cases/adapters/postgresql/hstore_test.rb
+++ b/activerecord/test/cases/adapters/postgresql/hstore_test.rb
@@ -152,6 +152,16 @@ class PostgresqlHstoreTest < ActiveRecord::TestCase
assert_equal "GMT", y.timezone
end
+ def test_yaml_round_trip_with_store_accessors
+ x = Hstore.new(language: "fr", timezone: "GMT")
+ assert_equal "fr", x.language
+ assert_equal "GMT", x.timezone
+
+ y = YAML.load(YAML.dump(x))
+ assert_equal "fr", y.language
+ assert_equal "GMT", y.timezone
+ end
+
def test_gen1
assert_equal(%q(" "=>""), @column.class.hstore_to_string({' '=>''}))
end
@@ -284,6 +294,41 @@ class PostgresqlHstoreTest < ActiveRecord::TestCase
Hstore.update_all tags: { }
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 HstoreWithSerialize < Hstore
+ serialize :tags, TagCollection
+ end
+ 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
+ assert_equal({"one" => "two"}, record.tags.to_hash)
+ record.tags = TagCollection.new("three" => "four")
+ record.save!
+ assert_equal({"three" => "four"}, HstoreWithSerialize.first.tags.to_hash)
+ end
+
+ def test_clone_hstore_with_serialized_attributes
+ LAZY_MODELS.call
+ HstoreWithSerialize.create! tags: TagCollection.new({"one" => "two"})
+ record = HstoreWithSerialize.first
+ dupe = record.dup
+ assert_equal({"one" => "two"}, dupe.tags.to_hash)
+ end
end
private
diff --git a/activerecord/test/cases/adapters/postgresql/json_test.rb b/activerecord/test/cases/adapters/postgresql/json_test.rb
index 03b546119d..61d4e2b8ae 100644
--- a/activerecord/test/cases/adapters/postgresql/json_test.rb
+++ b/activerecord/test/cases/adapters/postgresql/json_test.rb
@@ -147,6 +147,14 @@ class PostgresqlJSONTest < ActiveRecord::TestCase
assert_equal "320×480", y.resolution
end
+ def test_yaml_round_trip_with_store_accessors
+ x = JsonDataType.new(resolution: "320×480")
+ assert_equal "320×480", x.resolution
+
+ y = YAML.load(YAML.dump(x))
+ assert_equal "320×480", y.resolution
+ end
+
def test_update_all
json = JsonDataType.create! payload: { "one" => "two" }
diff --git a/activerecord/test/cases/adapters/postgresql/ltree_test.rb b/activerecord/test/cases/adapters/postgresql/ltree_test.rb
index 718f37a380..ddb7cd658c 100644
--- a/activerecord/test/cases/adapters/postgresql/ltree_test.rb
+++ b/activerecord/test/cases/adapters/postgresql/ltree_test.rb
@@ -1,7 +1,5 @@
# encoding: utf-8
require "cases/helper"
-require 'active_record/base'
-require 'active_record/connection_adapters/postgresql_adapter'
class PostgresqlLtreeTest < ActiveRecord::TestCase
class Ltree < ActiveRecord::Base
diff --git a/activerecord/test/cases/adapters/postgresql/money_test.rb b/activerecord/test/cases/adapters/postgresql/money_test.rb
index e109f1682b..3e33477bff 100644
--- a/activerecord/test/cases/adapters/postgresql/money_test.rb
+++ b/activerecord/test/cases/adapters/postgresql/money_test.rb
@@ -1,20 +1,28 @@
# encoding: utf-8
-
require "cases/helper"
-require 'active_record/base'
-require 'active_record/connection_adapters/postgresql_adapter'
+require 'support/schema_dumping_helper'
class PostgresqlMoneyTest < ActiveRecord::TestCase
+ include SchemaDumpingHelper
+
class PostgresqlMoney < ActiveRecord::Base; end
setup do
@connection = ActiveRecord::Base.connection
@connection.execute("set lc_monetary = 'C'")
+ @connection.create_table('postgresql_moneys') do |t|
+ t.column "wealth", "money"
+ t.column "depth", "money", default: "150.55"
+ end
+ end
+
+ teardown do
+ @connection.execute 'DROP TABLE IF EXISTS postgresql_moneys'
end
def test_column
column = PostgresqlMoney.columns_hash["wealth"]
- assert_equal :decimal, column.type
+ assert_equal :money, column.type
assert_equal "money", column.sql_type
assert_equal 2, column.scale
assert column.number?
@@ -23,6 +31,12 @@ class PostgresqlMoneyTest < ActiveRecord::TestCase
assert_not column.array
end
+ def test_default
+ column = PostgresqlMoney.columns_hash["depth"]
+ assert_equal BigDecimal.new("150.55"), column.default
+ assert_equal BigDecimal.new("150.55"), PostgresqlMoney.new.depth
+ end
+
def test_money_values
@connection.execute("INSERT INTO postgresql_moneys (id, wealth) VALUES (1, '567.89'::money)")
@connection.execute("INSERT INTO postgresql_moneys (id, wealth) VALUES (2, '-567.89'::money)")
@@ -41,6 +55,12 @@ class PostgresqlMoneyTest < ActiveRecord::TestCase
assert_equal(-2.25, column.type_cast("($2.25)"))
end
+ def test_schema_dumping
+ output = dump_table_schema("postgresql_moneys")
+ assert_match %r{t\.money\s+"wealth",\s+scale: 2$}, output
+ assert_match %r{t\.money\s+"depth",\s+scale: 2,\s+default: 150.55$}, output
+ end
+
def test_create_and_update_money
money = PostgresqlMoney.create(wealth: "987.65")
assert_equal 987.65, money.wealth
diff --git a/activerecord/test/cases/adapters/postgresql/network_test.rb b/activerecord/test/cases/adapters/postgresql/network_test.rb
index e99af07970..32085cbb17 100644
--- a/activerecord/test/cases/adapters/postgresql/network_test.rb
+++ b/activerecord/test/cases/adapters/postgresql/network_test.rb
@@ -1,8 +1,5 @@
# encoding: utf-8
-
require "cases/helper"
-require 'active_record/base'
-require 'active_record/connection_adapters/postgresql_adapter'
class PostgresqlNetworkTest < ActiveRecord::TestCase
class PostgresqlNetworkAddress < ActiveRecord::Base
diff --git a/activerecord/test/cases/adapters/postgresql/range_test.rb b/activerecord/test/cases/adapters/postgresql/range_test.rb
index 060b17d071..4d9cfe55f5 100644
--- a/activerecord/test/cases/adapters/postgresql/range_test.rb
+++ b/activerecord/test/cases/adapters/postgresql/range_test.rb
@@ -1,7 +1,5 @@
require "cases/helper"
require 'support/connection_helper'
-require 'active_record/base'
-require 'active_record/connection_adapters/postgresql_adapter'
if ActiveRecord::Base.connection.supports_ranges?
class PostgresqlRange < ActiveRecord::Base
diff --git a/activerecord/test/cases/adapters/postgresql/timestamp_test.rb b/activerecord/test/cases/adapters/postgresql/timestamp_test.rb
index d4102bf7be..3614b29190 100644
--- a/activerecord/test/cases/adapters/postgresql/timestamp_test.rb
+++ b/activerecord/test/cases/adapters/postgresql/timestamp_test.rb
@@ -123,6 +123,18 @@ class TimestampTest < ActiveRecord::TestCase
assert_equal date, Developer.find_by_name("aaron").updated_at
end
+ def test_bc_timestamp_leap_year
+ date = Time.utc(-4, 2, 29)
+ Developer.create!(:name => "taihou", :updated_at => date)
+ assert_equal date, Developer.find_by_name("taihou").updated_at
+ end
+
+ def test_bc_timestamp_year_zero
+ date = Time.utc(0, 4, 7)
+ Developer.create!(:name => "yahagi", :updated_at => date)
+ assert_equal date, Developer.find_by_name("yahagi").updated_at
+ end
+
private
def pg_datetime_precision(table_name, column_name)
diff --git a/activerecord/test/cases/adapters/postgresql/xml_test.rb b/activerecord/test/cases/adapters/postgresql/xml_test.rb
index c1c85f8c92..48c6eeb62c 100644
--- a/activerecord/test/cases/adapters/postgresql/xml_test.rb
+++ b/activerecord/test/cases/adapters/postgresql/xml_test.rb
@@ -1,8 +1,5 @@
# encoding: utf-8
-
require 'cases/helper'
-require 'active_record/base'
-require 'active_record/connection_adapters/postgresql_adapter'
class PostgresqlXMLTest < ActiveRecord::TestCase
class XmlDataType < ActiveRecord::Base
diff --git a/activerecord/test/cases/ar_schema_test.rb b/activerecord/test/cases/ar_schema_test.rb
index 811695938e..8700b20dee 100644
--- a/activerecord/test/cases/ar_schema_test.rb
+++ b/activerecord/test/cases/ar_schema_test.rb
@@ -17,6 +17,20 @@ if ActiveRecord::Base.connection.supports_migrations?
ActiveRecord::SchemaMigration.delete_all rescue nil
end
+ def test_has_no_primary_key
+ old_primary_key_prefix_type = ActiveRecord::Base.primary_key_prefix_type
+ ActiveRecord::Base.primary_key_prefix_type = :table_name_with_underscore
+ assert_nil ActiveRecord::SchemaMigration.primary_key
+
+ ActiveRecord::SchemaMigration.create_table
+ assert_difference "ActiveRecord::SchemaMigration.count", 1 do
+ ActiveRecord::SchemaMigration.create version: 12
+ end
+ ensure
+ ActiveRecord::SchemaMigration.drop_table
+ ActiveRecord::Base.primary_key_prefix_type = old_primary_key_prefix_type
+ end
+
def test_schema_define
ActiveRecord::Schema.define(:version => 7) do
create_table :fruits do |t|
@@ -34,6 +48,7 @@ if ActiveRecord::Base.connection.supports_migrations?
def test_schema_define_w_table_name_prefix
table_name = ActiveRecord::SchemaMigration.table_name
+ old_table_name_prefix = ActiveRecord::Base.table_name_prefix
ActiveRecord::Base.table_name_prefix = "nep_"
ActiveRecord::SchemaMigration.table_name = "nep_#{table_name}"
ActiveRecord::Schema.define(:version => 7) do
@@ -46,7 +61,7 @@ if ActiveRecord::Base.connection.supports_migrations?
end
assert_equal 7, ActiveRecord::Migrator::current_version
ensure
- ActiveRecord::Base.table_name_prefix = ""
+ ActiveRecord::Base.table_name_prefix = old_table_name_prefix
ActiveRecord::SchemaMigration.table_name = table_name
end
@@ -66,5 +81,12 @@ if ActiveRecord::Base.connection.supports_migrations?
end
assert_nothing_raised { @connection.select_all "SELECT * FROM fruits" }
end
+
+ def test_normalize_version
+ assert_equal "118", ActiveRecord::SchemaMigration.normalize_migration_number("0000118")
+ assert_equal "002", ActiveRecord::SchemaMigration.normalize_migration_number("2")
+ assert_equal "017", ActiveRecord::SchemaMigration.normalize_migration_number("0017")
+ assert_equal "20131219224947", ActiveRecord::SchemaMigration.normalize_migration_number("20131219224947")
+ end
end
end
diff --git a/activerecord/test/cases/associations/eager_test.rb b/activerecord/test/cases/associations/eager_test.rb
index 4bd4486b41..910067666a 100644
--- a/activerecord/test/cases/associations/eager_test.rb
+++ b/activerecord/test/cases/associations/eager_test.rb
@@ -1239,6 +1239,10 @@ class EagerAssociationTest < ActiveRecord::TestCase
}
end
+ test "including association based on sql condition and no database column" do
+ assert_equal pets(:parrot), Owner.including_last_pet.first.last_pet
+ end
+
test "include instance dependent associations is deprecated" do
message = "association scope 'posts_with_signature' is"
assert_deprecated message do
diff --git a/activerecord/test/cases/associations/has_and_belongs_to_many_associations_test.rb b/activerecord/test/cases/associations/has_and_belongs_to_many_associations_test.rb
index 8d8201ddae..080c499444 100644
--- a/activerecord/test/cases/associations/has_and_belongs_to_many_associations_test.rb
+++ b/activerecord/test/cases/associations/has_and_belongs_to_many_associations_test.rb
@@ -872,6 +872,6 @@ class HasAndBelongsToManyAssociationsTest < ActiveRecord::TestCase
def test_redefine_habtm
child = SubDeveloper.new("name" => "Aredridel")
child.special_projects << SpecialProject.new("name" => "Special Project")
- assert_equal true, child.save
+ assert child.save, 'child object should be saved'
end
end
diff --git a/activerecord/test/cases/associations/inverse_associations_test.rb b/activerecord/test/cases/associations/inverse_associations_test.rb
index 893030345f..a674a39d65 100644
--- a/activerecord/test/cases/associations/inverse_associations_test.rb
+++ b/activerecord/test/cases/associations/inverse_associations_test.rb
@@ -333,7 +333,7 @@ class InverseHasManyTests < ActiveRecord::TestCase
def test_parent_instance_should_be_shared_within_create_block_of_new_child
man = Man.first
- interest = man.interests.build do |i|
+ interest = man.interests.create do |i|
assert i.man.equal?(man), "Man of child should be the same instance as a parent"
end
assert interest.man.equal?(man), "Man of the child should still be the same instance as a parent"
diff --git a/activerecord/test/cases/attribute_methods_test.rb b/activerecord/test/cases/attribute_methods_test.rb
index da2876170e..139fe9c04b 100644
--- a/activerecord/test/cases/attribute_methods_test.rb
+++ b/activerecord/test/cases/attribute_methods_test.rb
@@ -870,7 +870,7 @@ class AttributeMethodsTest < ActiveRecord::TestCase
end
def cached_columns
- Topic.columns.map(&:name) - Topic.serialized_attributes.keys
+ Topic.columns.map(&:name)
end
def time_related_columns_on_topic
diff --git a/activerecord/test/cases/base_test.rb b/activerecord/test/cases/base_test.rb
index d65c4b0638..f01901514e 100644
--- a/activerecord/test/cases/base_test.rb
+++ b/activerecord/test/cases/base_test.rb
@@ -1495,7 +1495,7 @@ class BasicsTest < ActiveRecord::TestCase
}
types = { 'author_name' => typecast.new }
- topic = Topic.allocate.init_with 'attributes' => attrs,
+ topic = Topic.allocate.init_with 'raw_attributes' => attrs,
'column_types' => types
assert_equal 't.lo', topic.author_name
diff --git a/activerecord/test/cases/core_test.rb b/activerecord/test/cases/core_test.rb
index 2a52bf574c..715d92af99 100644
--- a/activerecord/test/cases/core_test.rb
+++ b/activerecord/test/cases/core_test.rb
@@ -1,6 +1,8 @@
require 'cases/helper'
require 'models/person'
require 'models/topic'
+require 'pp'
+require 'active_support/core_ext/string/strip'
class NonExistentTable < ActiveRecord::Base; end
@@ -30,4 +32,70 @@ class CoreTest < ActiveRecord::TestCase
def test_inspect_class_without_table
assert_equal "NonExistentTable(Table doesn't exist)", NonExistentTable.inspect
end
+
+ def test_pretty_print_new
+ topic = Topic.new
+ actual = ''
+ PP.pp(topic, StringIO.new(actual))
+ expected = <<-PRETTY.strip_heredoc
+ #<Topic:0xXXXXXX
+ id: nil,
+ title: nil,
+ author_name: nil,
+ author_email_address: "test@test.com",
+ written_on: nil,
+ bonus_time: nil,
+ last_read: nil,
+ content: nil,
+ important: nil,
+ approved: true,
+ replies_count: 0,
+ unique_replies_count: 0,
+ parent_id: nil,
+ parent_title: nil,
+ type: nil,
+ group: nil,
+ created_at: nil,
+ updated_at: nil>
+ PRETTY
+ assert actual.start_with?(expected.split('XXXXXX').first)
+ assert actual.end_with?(expected.split('XXXXXX').last)
+ end
+
+ def test_pretty_print_persisted
+ topic = topics(:first)
+ actual = ''
+ PP.pp(topic, StringIO.new(actual))
+ expected = <<-PRETTY.strip_heredoc
+ #<Topic:0x\\w+
+ id: 1,
+ title: "The First Topic",
+ author_name: "David",
+ author_email_address: "david@loudthinking.com",
+ written_on: 2003-07-16 14:28:11 UTC,
+ bonus_time: 2000-01-01 14:28:00 UTC,
+ last_read: Thu, 15 Apr 2004,
+ content: "Have a nice day",
+ important: nil,
+ approved: false,
+ replies_count: 1,
+ unique_replies_count: 0,
+ parent_id: nil,
+ parent_title: nil,
+ type: nil,
+ group: nil,
+ created_at: [^,]+,
+ updated_at: [^,>]+>
+ PRETTY
+ assert_match(/\A#{expected}\z/, actual)
+ end
+
+ def test_pretty_print_uninitialized
+ topic = Topic.allocate
+ actual = ''
+ PP.pp(topic, StringIO.new(actual))
+ expected = "#<Topic:XXXXXX not initialized>\n"
+ assert actual.start_with?(expected.split('XXXXXX').first)
+ assert actual.end_with?(expected.split('XXXXXX').last)
+ end
end
diff --git a/activerecord/test/cases/defaults_test.rb b/activerecord/test/cases/defaults_test.rb
index f885a8cbc0..92144bc802 100644
--- a/activerecord/test/cases/defaults_test.rb
+++ b/activerecord/test/cases/defaults_test.rb
@@ -206,6 +206,11 @@ if current_adapter?(:PostgreSQLAdapter)
assert_equal "some text", Default.new.text_col, "Default of text column was not correctly parse after updating default using '::text' since postgreSQL will add parens to the default in db"
end
+ def test_default_containing_quote_and_colons
+ @connection.execute "ALTER TABLE defaults ALTER COLUMN string_col SET DEFAULT 'foo''::bar'"
+ assert_equal "foo'::bar", Default.new.string_col
+ end
+
teardown do
@connection.schema_search_path = @old_search_path
Default.reset_column_information
diff --git a/activerecord/test/cases/dirty_test.rb b/activerecord/test/cases/dirty_test.rb
index df4183c065..987c55ebc2 100644
--- a/activerecord/test/cases/dirty_test.rb
+++ b/activerecord/test/cases/dirty_test.rb
@@ -616,6 +616,34 @@ class DirtyTest < ActiveRecord::TestCase
end
end
+ test "defaults with type that implements `type_cast_for_write`" do
+ type = Class.new(ActiveRecord::Type::Value) do
+ def type_cast(value)
+ value.to_i
+ end
+
+ def type_cast_for_write(value)
+ value.to_s
+ end
+
+ alias type_cast_for_database type_cast_for_write
+ end
+
+ model_class = Class.new(ActiveRecord::Base) do
+ self.table_name = 'numeric_data'
+ property :foo, type.new, default: 1
+ end
+
+ model = model_class.new
+ assert_not model.foo_changed?
+
+ model = model_class.new(foo: 1)
+ assert_not model.foo_changed?
+
+ model = model_class.new(foo: '1')
+ assert_not model.foo_changed?
+ end
+
private
def with_partial_writes(klass, on = true)
old = klass.partial_writes?
diff --git a/activerecord/test/cases/migration_test.rb b/activerecord/test/cases/migration_test.rb
index aa679d4a35..5b4531846d 100644
--- a/activerecord/test/cases/migration_test.rb
+++ b/activerecord/test/cases/migration_test.rb
@@ -333,47 +333,6 @@ class MigrationTest < ActiveRecord::TestCase
Reminder.reset_table_name
end
- def test_proper_table_name_on_migrator
- reminder_class = new_isolated_reminder_class
- assert_deprecated do
- assert_equal "table", ActiveRecord::Migrator.proper_table_name('table')
- end
- assert_deprecated do
- assert_equal "table", ActiveRecord::Migrator.proper_table_name(:table)
- end
- assert_deprecated do
- assert_equal "reminders", ActiveRecord::Migrator.proper_table_name(reminder_class)
- end
- reminder_class.reset_table_name
- assert_deprecated do
- assert_equal reminder_class.table_name, ActiveRecord::Migrator.proper_table_name(reminder_class)
- end
-
- # Use the model's own prefix/suffix if a model is given
- ActiveRecord::Base.table_name_prefix = "ARprefix_"
- ActiveRecord::Base.table_name_suffix = "_ARsuffix"
- reminder_class.table_name_prefix = 'prefix_'
- reminder_class.table_name_suffix = '_suffix'
- reminder_class.reset_table_name
- assert_deprecated do
- assert_equal "prefix_reminders_suffix", ActiveRecord::Migrator.proper_table_name(reminder_class)
- end
- reminder_class.table_name_prefix = ''
- reminder_class.table_name_suffix = ''
- reminder_class.reset_table_name
-
- # Use AR::Base's prefix/suffix if string or symbol is given
- ActiveRecord::Base.table_name_prefix = "prefix_"
- ActiveRecord::Base.table_name_suffix = "_suffix"
- reminder_class.reset_table_name
- assert_deprecated do
- assert_equal "prefix_table_suffix", ActiveRecord::Migrator.proper_table_name('table')
- end
- assert_deprecated do
- assert_equal "prefix_table_suffix", ActiveRecord::Migrator.proper_table_name(:table)
- end
- end
-
def test_proper_table_name_on_migration
reminder_class = new_isolated_reminder_class
migration = ActiveRecord::Migration.new
diff --git a/activerecord/test/cases/persistence_test.rb b/activerecord/test/cases/persistence_test.rb
index bc5ccd0fe9..7f5d0d66d3 100644
--- a/activerecord/test/cases/persistence_test.rb
+++ b/activerecord/test/cases/persistence_test.rb
@@ -251,7 +251,7 @@ class PersistenceTest < ActiveRecord::TestCase
def test_create_columns_not_equal_attributes
topic = Topic.allocate.init_with(
- 'attributes' => {
+ 'raw_attributes' => {
'title' => 'Another New Topic',
'does_not_exist' => 'test'
}
@@ -302,7 +302,7 @@ class PersistenceTest < ActiveRecord::TestCase
topic_reloaded = Topic.allocate
topic_reloaded.init_with(
- 'attributes' => topic.attributes.merge('does_not_exist' => 'test')
+ 'raw_attributes' => topic.attributes.merge('does_not_exist' => 'test')
)
topic_reloaded.title = 'A New Topic'
assert_nothing_raised { topic_reloaded.save }
@@ -333,6 +333,15 @@ class PersistenceTest < ActiveRecord::TestCase
assert_equal "Reply", topic.type
end
+ def test_update_sti_subclass_type
+ assert_instance_of Topic, topics(:first)
+
+ reply = topics(:first).becomes!(Reply)
+ assert_instance_of Reply, reply
+ reply.save!
+ assert_instance_of Reply, Reply.find(reply.id)
+ end
+
def test_update_after_create
klass = Class.new(Topic) do
def self.name; 'Topic'; end
diff --git a/activerecord/test/cases/primary_keys_test.rb b/activerecord/test/cases/primary_keys_test.rb
index c719918fd7..f43483d291 100644
--- a/activerecord/test/cases/primary_keys_test.rb
+++ b/activerecord/test/cases/primary_keys_test.rb
@@ -92,6 +92,7 @@ class PrimaryKeysTest < ActiveRecord::TestCase
end
def test_primary_key_prefix
+ old_primary_key_prefix_type = ActiveRecord::Base.primary_key_prefix_type
ActiveRecord::Base.primary_key_prefix_type = :table_name
Topic.reset_primary_key
assert_equal "topicid", Topic.primary_key
@@ -103,6 +104,8 @@ class PrimaryKeysTest < ActiveRecord::TestCase
ActiveRecord::Base.primary_key_prefix_type = nil
Topic.reset_primary_key
assert_equal "id", Topic.primary_key
+ ensure
+ ActiveRecord::Base.primary_key_prefix_type = old_primary_key_prefix_type
end
def test_delete_should_quote_pkey
diff --git a/activerecord/test/cases/reflection_test.rb b/activerecord/test/cases/reflection_test.rb
index e6603f28be..793e193329 100644
--- a/activerecord/test/cases/reflection_test.rb
+++ b/activerecord/test/cases/reflection_test.rb
@@ -80,6 +80,26 @@ class ReflectionTest < ActiveRecord::TestCase
assert_equal :integer, @first.column_for_attribute("id").type
end
+ def test_non_existent_columns_return_null_object
+ column = @first.column_for_attribute("attribute_that_doesnt_exist")
+ assert_instance_of ActiveRecord::ConnectionAdapters::NullColumn, column
+ assert_equal "attribute_that_doesnt_exist", column.name
+ assert_equal nil, column.sql_type
+ assert_equal nil, column.type
+ assert_not column.number?
+ assert_not column.text?
+ assert_not column.binary?
+ end
+
+ def test_non_existent_columns_are_identity_types
+ 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_for_database(object)
+ end
+
def test_reflection_klass_for_nested_class_name
reflection = MacroReflection.new(:company, nil, nil, { :class_name => 'MyApplication::Business::Company' }, ActiveRecord::Base)
assert_nothing_raised do
diff --git a/activerecord/test/cases/schema_dumper_test.rb b/activerecord/test/cases/schema_dumper_test.rb
index 9602252b2e..e2a18d138d 100644
--- a/activerecord/test/cases/schema_dumper_test.rb
+++ b/activerecord/test/cases/schema_dumper_test.rb
@@ -1,4 +1,5 @@
require "cases/helper"
+require 'support/schema_dumping_helper'
class SchemaDumperTest < ActiveRecord::TestCase
setup do
@@ -62,7 +63,7 @@ class SchemaDumperTest < ActiveRecord::TestCase
next if column_set.empty?
lengths = column_set.map do |column|
- if match = column.match(/t\.(?:integer|decimal|float|datetime|timestamp|time|date|text|binary|string|boolean|uuid)\s+"/)
+ if match = column.match(/t\.(?:integer|decimal|float|datetime|timestamp|time|date|text|binary|string|boolean|uuid|point)\s+"/)
match[0].length
end
end
@@ -403,3 +404,31 @@ class SchemaDumperTest < ActiveRecord::TestCase
end
end
+
+class SchemaDumperDefaultsTest < ActiveRecord::TestCase
+ include SchemaDumpingHelper
+
+ setup do
+ @connection = ActiveRecord::Base.connection
+ @connection.create_table :defaults, force: true do |t|
+ t.string :string_with_default, default: "Hello!"
+ t.date :date_with_default, default: '2014-06-05'
+ t.datetime :datetime_with_default, default: "2014-06-05 07:17:04"
+ t.time :time_with_default, default: "07:17:04"
+ end
+ end
+
+ teardown do
+ return unless @connection
+ @connection.execute 'DROP TABLE IF EXISTS defaults'
+ end
+
+ def test_schema_dump_defaults_with_universally_supported_types
+ output = dump_table_schema('defaults')
+
+ assert_match %r{t\.string\s+"string_with_default",\s+default: "Hello!"}, output
+ assert_match %r{t\.date\s+"date_with_default",\s+default: '2014-06-05'}, output
+ assert_match %r{t\.datetime\s+"datetime_with_default",\s+default: '2014-06-05 07:17:04'}, output
+ assert_match %r{t\.time\s+"time_with_default",\s+default: '2000-01-01 07:17:04'}, output
+ end
+end
diff --git a/activerecord/test/cases/serialized_attribute_test.rb b/activerecord/test/cases/serialized_attribute_test.rb
index eaa28148f4..debb227303 100644
--- a/activerecord/test/cases/serialized_attribute_test.rb
+++ b/activerecord/test/cases/serialized_attribute_test.rb
@@ -31,7 +31,7 @@ class SerializedAttributeTest < ActiveRecord::TestCase
def test_serialized_attribute_init_with
topic = Topic.allocate
- topic.init_with('attributes' => { 'content' => '--- foo' })
+ topic.init_with('raw_attributes' => { 'content' => '--- foo' })
assert_equal 'foo', topic.content
end
@@ -59,8 +59,9 @@ class SerializedAttributeTest < ActiveRecord::TestCase
def test_serialized_attribute_calling_dup_method
Topic.serialize :content, JSON
- t = Topic.new(:content => { :foo => :bar }).dup
- assert_equal({ :foo => :bar }, t.content_before_type_cast)
+ orig = Topic.new(content: { foo: :bar })
+ clone = orig.dup
+ assert_equal(orig.content, clone.content)
end
def test_serialized_attribute_declared_in_subclass
@@ -103,8 +104,10 @@ class SerializedAttributeTest < ActiveRecord::TestCase
def test_serialized_attribute_should_raise_exception_on_save_with_wrong_type
Topic.serialize(:content, Hash)
- topic = Topic.new(:content => "string")
- assert_raise(ActiveRecord::SerializationTypeMismatch) { topic.save }
+ assert_raise(ActiveRecord::SerializationTypeMismatch) do
+ topic = Topic.new(content: 'string')
+ topic.save
+ end
end
def test_should_raise_exception_on_serialized_attribute_with_type_mismatch
diff --git a/activerecord/test/cases/store_test.rb b/activerecord/test/cases/store_test.rb
index 6a34c55011..f841b1c983 100644
--- a/activerecord/test/cases/store_test.rb
+++ b/activerecord/test/cases/store_test.rb
@@ -183,20 +183,6 @@ class StoreTest < ActiveRecord::TestCase
assert_equal({}, @john.params)
end
- test "attributes_for_coder should return stored fields already serialized" do
- attributes = {
- "id" => @john.id,
- "name"=> @john.name,
- "settings" => "--- !ruby/hash:ActiveSupport::HashWithIndifferentAccess\ncolor: black\n",
- "preferences" => "--- !ruby/hash:ActiveSupport::HashWithIndifferentAccess\nremember_login: true\n",
- "json_data" => "{\"height\":\"tall\"}", "json_data_empty"=>"{\"is_a_good_guy\":true}",
- "params" => "--- !ruby/hash:ActiveSupport::HashWithIndifferentAccess {}\n",
- "account_id"=> @john.account_id
- }
-
- assert_equal attributes, @john.attributes_for_coder
- end
-
test "dump, load and dump again a model" do
dumped = YAML.dump(@john)
loaded = YAML.load(dumped)
diff --git a/activerecord/test/cases/xml_serialization_test.rb b/activerecord/test/cases/xml_serialization_test.rb
index 1a690c01a6..c34e7d5a30 100644
--- a/activerecord/test/cases/xml_serialization_test.rb
+++ b/activerecord/test/cases/xml_serialization_test.rb
@@ -416,8 +416,9 @@ class DatabaseConnectedXmlSerializationTest < ActiveRecord::TestCase
def test_should_support_aliased_attributes
xml = Author.select("name as firstname").to_xml
- array = Hash.from_xml(xml)['authors']
- assert_equal array.size, array.select { |author| author.has_key? 'firstname' }.size
+ Author.all.each do |author|
+ assert xml.include?(%(<firstname>#{author.name}</firstname>)), xml
+ end
end
def test_array_to_xml_including_has_many_association
diff --git a/activerecord/test/cases/yaml_serialization_test.rb b/activerecord/test/cases/yaml_serialization_test.rb
index 15815d56e4..d4f8ef5b4d 100644
--- a/activerecord/test/cases/yaml_serialization_test.rb
+++ b/activerecord/test/cases/yaml_serialization_test.rb
@@ -23,13 +23,6 @@ class YamlSerializationTest < ActiveRecord::TestCase
assert_equal({:omg=>:lol}, YAML.load(YAML.dump(topic)).content)
end
- def test_encode_with_coder
- topic = Topic.first
- coder = {}
- topic.encode_with coder
- assert_equal({'attributes' => topic.attributes}, coder)
- end
-
def test_psych_roundtrip
topic = Topic.first
assert topic
@@ -47,4 +40,33 @@ class YamlSerializationTest < ActiveRecord::TestCase
def test_active_record_relation_serialization
[Topic.all].to_yaml
end
+
+ def test_raw_types_are_not_changed_on_round_trip
+ topic = Topic.new(parent_id: "123")
+ assert_equal "123", topic.parent_id_before_type_cast
+ assert_equal "123", YAML.load(YAML.dump(topic)).parent_id_before_type_cast
+ end
+
+ def test_cast_types_are_not_changed_on_round_trip
+ topic = Topic.new(parent_id: "123")
+ assert_equal 123, topic.parent_id
+ assert_equal 123, YAML.load(YAML.dump(topic)).parent_id
+ end
+
+ def test_new_records_remain_new_after_round_trip
+ topic = Topic.new
+
+ assert topic.new_record?, "Sanity check that new records are new"
+ assert YAML.load(YAML.dump(topic)).new_record?, "Record should be new after deserialization"
+
+ topic.save!
+
+ assert_not topic.new_record?, "Saved records are not new"
+ assert_not YAML.load(YAML.dump(topic)).new_record?, "Saved record should not be new after deserialization"
+
+ topic = Topic.select('title').last
+
+ 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
end
diff --git a/activerecord/test/models/owner.rb b/activerecord/test/models/owner.rb
index cf24502d3a..2e3a9a3681 100644
--- a/activerecord/test/models/owner.rb
+++ b/activerecord/test/models/owner.rb
@@ -3,6 +3,18 @@ class Owner < ActiveRecord::Base
has_many :pets, -> { order 'pets.name desc' }
has_many :toys, :through => :pets
+ belongs_to :last_pet, class_name: 'Pet'
+ scope :including_last_pet, -> {
+ select(%q[
+ owners.*, (
+ select p.pet_id from pets p
+ where p.owner_id = owners.owner_id
+ order by p.name desc
+ limit 1
+ ) as last_pet_id
+ ]).includes(:last_pet)
+ }
+
after_commit :execute_blocks
def blocks
diff --git a/activerecord/test/schema/postgresql_specific_schema.rb b/activerecord/test/schema/postgresql_specific_schema.rb
index 4fcbf4dbd2..e9294a11b9 100644
--- a/activerecord/test/schema/postgresql_specific_schema.rb
+++ b/activerecord/test/schema/postgresql_specific_schema.rb
@@ -1,6 +1,6 @@
ActiveRecord::Schema.define do
- %w(postgresql_tsvectors postgresql_hstores postgresql_arrays postgresql_moneys postgresql_numbers postgresql_times postgresql_network_addresses postgresql_bit_strings postgresql_uuids postgresql_ltrees
+ %w(postgresql_tsvectors postgresql_hstores postgresql_arrays postgresql_moneys postgresql_numbers postgresql_times postgresql_network_addresses postgresql_uuids postgresql_ltrees
postgresql_oids postgresql_xml_data_type defaults geometrics postgresql_timestamp_with_zones postgresql_partitioned_table postgresql_partitioned_table_parent postgresql_json_data_type postgresql_citext).each do |table_name|
execute "DROP TABLE IF EXISTS #{quote_table_name table_name}"
end
@@ -118,13 +118,6 @@ _SQL
end
execute <<_SQL
- CREATE TABLE postgresql_moneys (
- id SERIAL PRIMARY KEY,
- wealth MONEY
- );
-_SQL
-
- execute <<_SQL
CREATE TABLE postgresql_numbers (
id SERIAL PRIMARY KEY,
single REAL,
@@ -150,14 +143,6 @@ _SQL
_SQL
execute <<_SQL
- CREATE TABLE postgresql_bit_strings (
- id SERIAL PRIMARY KEY,
- bit_string BIT(8),
- bit_string_varying BIT VARYING(8)
- );
-_SQL
-
- execute <<_SQL
CREATE TABLE postgresql_oids (
id SERIAL PRIMARY KEY,
obj_id OID
diff --git a/activerecord/test/support/schema_dumping_helper.rb b/activerecord/test/support/schema_dumping_helper.rb
new file mode 100644
index 0000000000..2ae8d299e5
--- /dev/null
+++ b/activerecord/test/support/schema_dumping_helper.rb
@@ -0,0 +1,11 @@
+module SchemaDumpingHelper
+ def dump_table_schema(table, connection = ActiveRecord::Base.connection)
+ old_ignore_tables = ActiveRecord::SchemaDumper.ignore_tables
+ ActiveRecord::SchemaDumper.ignore_tables = connection.tables - [table]
+ stream = StringIO.new
+ ActiveRecord::SchemaDumper.dump(ActiveRecord::Base.connection, stream)
+ stream.string
+ ensure
+ ActiveRecord::SchemaDumper.ignore_tables = old_ignore_tables
+ end
+end
diff --git a/activesupport/CHANGELOG.md b/activesupport/CHANGELOG.md
index 38ec3c0753..871e1705f7 100644
--- a/activesupport/CHANGELOG.md
+++ b/activesupport/CHANGELOG.md
@@ -1,3 +1,36 @@
+* Fixed `ActiveSupport::TimeWithZone#-` so precision is not unnecessarily lost
+ when working with objects with a nanosecond component.
+
+ `ActiveSupport::TimeWithZone#-` should return the same result as if we were
+ using `Time#-`:
+
+ Time.now.end_of_day - Time.now.beginning_of_day #=> 86399.999999999
+
+ Before:
+
+ Time.zone.now.end_of_day.nsec #=> 999999999
+ Time.zone.now.end_of_day - Time.zone.now.beginning_of_day #=> 86400.0
+
+ After:
+
+ Time.zone.now.end_of_day - Time.zone.now.beginning_of_day
+ #=> 86399.999999999
+
+ *Gordon Chan*
+
+* Fixed precision error in NumberHelper when using Rationals.
+
+ before:
+ ActiveSupport::NumberHelper.number_to_rounded Rational(1000, 3), precision: 2
+ #=> "330.00"
+ after:
+ ActiveSupport::NumberHelper.number_to_rounded Rational(1000, 3), precision: 2
+ #=> "333.33"
+
+ See #15379.
+
+ *Juanjo Bazán*
+
* Removed deprecated `Numeric#ago` and friends
Replacements:
diff --git a/activesupport/README.rdoc b/activesupport/README.rdoc
index f3582767c0..a6424a353a 100644
--- a/activesupport/README.rdoc
+++ b/activesupport/README.rdoc
@@ -30,6 +30,11 @@ API documentation is at:
* http://api.rubyonrails.org
-Bug reports and feature requests can be filed with the rest for the Ruby on Rails project here:
+Bug reports can be filed for the Ruby on Rails project here:
* https://github.com/rails/rails/issues
+
+Feature requests should be discussed on the rails-core mailing list here:
+
+* https://groups.google.com/forum/?fromgroups#!forum/rubyonrails-core
+
diff --git a/activesupport/lib/active_support/core_ext/hash/indifferent_access.rb b/activesupport/lib/active_support/core_ext/hash/indifferent_access.rb
index 970d6faa1d..28cb3e2a3b 100644
--- a/activesupport/lib/active_support/core_ext/hash/indifferent_access.rb
+++ b/activesupport/lib/active_support/core_ext/hash/indifferent_access.rb
@@ -18,6 +18,6 @@ class Hash
#
# b = { b: 1 }
# { a: b }.with_indifferent_access['a'] # calls b.nested_under_indifferent_access
- # # => {"b"=>32}
+ # # => {"b"=>1}
alias nested_under_indifferent_access with_indifferent_access
end
diff --git a/activesupport/lib/active_support/core_ext/string/output_safety.rb b/activesupport/lib/active_support/core_ext/string/output_safety.rb
index 2c8995be9a..46cd170c1d 100644
--- a/activesupport/lib/active_support/core_ext/string/output_safety.rb
+++ b/activesupport/lib/active_support/core_ext/string/output_safety.rb
@@ -19,12 +19,7 @@ class ERB
# puts html_escape('is a > 0 & a < 10?')
# # => is a &gt; 0 &amp; a &lt; 10?
def html_escape(s)
- s = s.to_s
- if s.html_safe?
- s
- else
- s.gsub(HTML_ESCAPE_REGEXP, HTML_ESCAPE).html_safe
- end
+ unwrapped_html_escape(s).html_safe
end
# Aliasing twice issues a warning "discarding old...". Remove first to avoid it.
@@ -36,6 +31,18 @@ class ERB
singleton_class.send(:remove_method, :html_escape)
module_function :html_escape
+ # HTML escapes strings but doesn't wrap them with an ActiveSupport::SafeBuffer.
+ # This method is not for public consumption! Seriously!
+ def unwrapped_html_escape(s) # :nodoc:
+ s = s.to_s
+ if s.html_safe?
+ s
+ else
+ s.gsub(HTML_ESCAPE_REGEXP, HTML_ESCAPE)
+ end
+ end
+ module_function :unwrapped_html_escape
+
# A utility method for escaping HTML without affecting existing escaped entities.
#
# html_escape_once('1 < 2 &amp; 3')
@@ -170,13 +177,15 @@ module ActiveSupport #:nodoc:
self[0, 0]
end
- %w[concat prepend].each do |method_name|
- define_method method_name do |value|
- super(html_escape_interpolated_argument(value))
- end
+ def concat(value)
+ super(html_escape_interpolated_argument(value))
end
alias << concat
+ def prepend(value)
+ super(html_escape_interpolated_argument(value))
+ end
+
def prepend!(value)
ActiveSupport::Deprecation.deprecation_warning "ActiveSupport::SafeBuffer#prepend!", :prepend
prepend value
@@ -231,7 +240,8 @@ module ActiveSupport #:nodoc:
private
def html_escape_interpolated_argument(arg)
- (!html_safe? || arg.html_safe?) ? arg : ERB::Util.h(arg)
+ (!html_safe? || arg.html_safe?) ? arg :
+ arg.to_s.gsub(ERB::Util::HTML_ESCAPE_REGEXP, ERB::Util::HTML_ESCAPE)
end
end
end
diff --git a/activesupport/lib/active_support/notifications.rb b/activesupport/lib/active_support/notifications.rb
index 7a96c66626..325a3d75dc 100644
--- a/activesupport/lib/active_support/notifications.rb
+++ b/activesupport/lib/active_support/notifications.rb
@@ -141,6 +141,11 @@ module ActiveSupport
#
# ActiveSupport::Notifications.unsubscribe(subscriber)
#
+ # You can also unsubscribe by passing the name of the subscriber object. Note
+ # that this will unsubscribe all subscriptions with the given name:
+ #
+ # ActiveSupport::Notifications.unsubscribe("render")
+ #
# == Default Queue
#
# Notifications ships with a queue implementation that consumes and publishes events
@@ -173,8 +178,8 @@ module ActiveSupport
unsubscribe(subscriber)
end
- def unsubscribe(args)
- notifier.unsubscribe(args)
+ def unsubscribe(subscriber_or_name)
+ notifier.unsubscribe(subscriber_or_name)
end
def instrumenter
diff --git a/activesupport/lib/active_support/notifications/fanout.rb b/activesupport/lib/active_support/notifications/fanout.rb
index 8f5fa646e8..6bf8c7d5de 100644
--- a/activesupport/lib/active_support/notifications/fanout.rb
+++ b/activesupport/lib/active_support/notifications/fanout.rb
@@ -25,9 +25,15 @@ module ActiveSupport
subscriber
end
- def unsubscribe(subscriber)
+ def unsubscribe(subscriber_or_name)
synchronize do
- @subscribers.reject! { |s| s.matches?(subscriber) }
+ case subscriber_or_name
+ when String
+ @subscribers.reject! { |s| s.matches?(subscriber_or_name) }
+ else
+ @subscribers.delete(subscriber_or_name)
+ end
+
@listeners_for.clear
end
end
@@ -97,12 +103,11 @@ module ActiveSupport
end
def subscribed_to?(name)
- @pattern === name.to_s
+ @pattern === name
end
- def matches?(subscriber_or_name)
- self === subscriber_or_name ||
- @pattern && @pattern === subscriber_or_name
+ def matches?(name)
+ @pattern && @pattern === name
end
end
diff --git a/activesupport/lib/active_support/number_helper/number_to_rounded_converter.rb b/activesupport/lib/active_support/number_helper/number_to_rounded_converter.rb
index c45f6cdcfa..01597b288a 100644
--- a/activesupport/lib/active_support/number_helper/number_to_rounded_converter.rb
+++ b/activesupport/lib/active_support/number_helper/number_to_rounded_converter.rb
@@ -12,11 +12,7 @@ module ActiveSupport
when Float, String
@number = BigDecimal(number.to_s)
when Rational
- if significant
- @number = BigDecimal(number, digit_count(number.to_i) + precision)
- else
- @number = BigDecimal(number, precision)
- end
+ @number = BigDecimal(number, digit_count(number.to_i) + precision)
else
@number = number.to_d
end
diff --git a/activesupport/lib/active_support/time_with_zone.rb b/activesupport/lib/active_support/time_with_zone.rb
index 3155c0ab0f..4a0ed356b1 100644
--- a/activesupport/lib/active_support/time_with_zone.rb
+++ b/activesupport/lib/active_support/time_with_zone.rb
@@ -262,7 +262,7 @@ module ActiveSupport
# If we're subtracting a Duration of variable length (i.e., years, months, days), move backwards from #time,
# otherwise move backwards #utc, for accuracy when moving across DST boundaries
if other.acts_like?(:time)
- utc.to_f - other.to_f
+ to_time - other.to_time
elsif duration_of_variable_length?(other)
method_missing(:-, other)
else
diff --git a/activesupport/test/core_ext/hash_ext_test.rb b/activesupport/test/core_ext/hash_ext_test.rb
index cb706d77c2..cd0cb1a144 100644
--- a/activesupport/test/core_ext/hash_ext_test.rb
+++ b/activesupport/test/core_ext/hash_ext_test.rb
@@ -70,6 +70,8 @@ class HashExtTest < ActiveSupport::TestCase
assert_respond_to h, :to_options!
assert_respond_to h, :compact
assert_respond_to h, :compact!
+ assert_respond_to h, :except
+ assert_respond_to h, :except!
end
def test_transform_keys
@@ -919,13 +921,19 @@ class HashExtTest < ActiveSupport::TestCase
def test_except_with_more_than_one_argument
original = { :a => 'x', :b => 'y', :c => 10 }
expected = { :a => 'x' }
+
assert_equal expected, original.except(:b, :c)
+
+ assert_equal expected, original.except!(:b, :c)
+ assert_equal expected, original
end
def test_except_with_original_frozen
original = { :a => 'x', :b => 'y' }
original.freeze
assert_nothing_raised { original.except(:a) }
+
+ assert_raise(RuntimeError) { original.except!(:a) }
end
def test_except_with_mocha_expectation_on_original
diff --git a/activesupport/test/core_ext/time_with_zone_test.rb b/activesupport/test/core_ext/time_with_zone_test.rb
index 7fe4d4a6b2..6d779bf3d5 100644
--- a/activesupport/test/core_ext/time_with_zone_test.rb
+++ b/activesupport/test/core_ext/time_with_zone_test.rb
@@ -246,16 +246,31 @@ class TimeWithZoneTest < ActiveSupport::TestCase
assert_equal 86_400.0, ActiveSupport::TimeWithZone.new( Time.utc(2000, 1, 2), ActiveSupport::TimeZone['Hawaii'] ) - Time.utc(2000, 1, 1)
end
+ def test_minus_with_time_precision
+ assert_equal 86_399.999999998, ActiveSupport::TimeWithZone.new( Time.utc(2000, 1, 2, 23, 59, 59, Rational(999999999, 1000)), ActiveSupport::TimeZone['UTC'] ) - Time.utc(2000, 1, 2, 0, 0, 0, Rational(1, 1000))
+ assert_equal 86_399.999999998, ActiveSupport::TimeWithZone.new( Time.utc(2000, 1, 2, 23, 59, 59, Rational(999999999, 1000)), ActiveSupport::TimeZone['Hawaii'] ) - Time.utc(2000, 1, 2, 0, 0, 0, Rational(1, 1000))
+ end
+
def test_minus_with_time_with_zone
twz1 = ActiveSupport::TimeWithZone.new( Time.utc(2000, 1, 1), ActiveSupport::TimeZone['UTC'] )
twz2 = ActiveSupport::TimeWithZone.new( Time.utc(2000, 1, 2), ActiveSupport::TimeZone['UTC'] )
assert_equal 86_400.0, twz2 - twz1
end
+ def test_minus_with_time_with_zone_precision
+ twz1 = ActiveSupport::TimeWithZone.new( Time.utc(2000, 1, 1, 0, 0, 0, Rational(1, 1000)), ActiveSupport::TimeZone['UTC'] )
+ twz2 = ActiveSupport::TimeWithZone.new( Time.utc(2000, 1, 1, 23, 59, 59, Rational(999999999, 1000)), ActiveSupport::TimeZone['UTC'] )
+ assert_equal 86_399.999999998, twz2 - twz1
+ end
+
def test_minus_with_datetime
assert_equal 86_400.0, ActiveSupport::TimeWithZone.new( Time.utc(2000, 1, 2), ActiveSupport::TimeZone['UTC'] ) - DateTime.civil(2000, 1, 1)
end
+ def test_minus_with_datetime_precision
+ assert_equal 86_399.999999999, ActiveSupport::TimeWithZone.new( Time.utc(2000, 1, 1, 23, 59, 59, Rational(999999999, 1000)), ActiveSupport::TimeZone['UTC'] ) - DateTime.civil(2000, 1, 1)
+ end
+
def test_minus_with_wrapped_datetime
assert_equal 86_400.0, ActiveSupport::TimeWithZone.new( DateTime.civil(2000, 1, 2), ActiveSupport::TimeZone['UTC'] ) - Time.utc(2000, 1, 1)
assert_equal 86_400.0, ActiveSupport::TimeWithZone.new( DateTime.civil(2000, 1, 2), ActiveSupport::TimeZone['UTC'] ) - DateTime.civil(2000, 1, 1)
diff --git a/activesupport/test/number_helper_test.rb b/activesupport/test/number_helper_test.rb
index a7a0ae02e7..bb51cc68f2 100644
--- a/activesupport/test/number_helper_test.rb
+++ b/activesupport/test/number_helper_test.rb
@@ -134,6 +134,7 @@ module ActiveSupport
assert_equal("111.23460000000000000000", number_helper.number_to_rounded('111.2346', :precision => 20))
assert_equal("111.23460000000000000000", number_helper.number_to_rounded(BigDecimal(111.2346, Float::DIG), :precision => 20))
assert_equal("111.2346" + "0"*96, number_helper.number_to_rounded('111.2346', :precision => 100))
+ assert_equal("111.2346", number_helper.number_to_rounded(Rational(1112346, 10000), :precision => 4))
end
end
@@ -174,6 +175,7 @@ module ActiveSupport
assert_equal "9775.0000000000000000", number_helper.number_to_rounded(BigDecimal(9775), :precision => 20, :significant => true )
assert_equal "9775.0000000000000000", number_helper.number_to_rounded("9775", :precision => 20, :significant => true )
assert_equal "9775." + "0"*96, number_helper.number_to_rounded("9775", :precision => 100, :significant => true )
+ assert_equal("97.7", number_helper.number_to_rounded(Rational(9772, 100), :precision => 3, :significant => true))
end
end
diff --git a/guides/code/getting_started/config/environments/production.rb b/guides/code/getting_started/config/environments/production.rb
index c8ae858574..60adba903a 100644
--- a/guides/code/getting_started/config/environments/production.rb
+++ b/guides/code/getting_started/config/environments/production.rb
@@ -16,10 +16,10 @@ Rails.application.configure do
# Enable Rack::Cache to put a simple HTTP cache in front of your application
# Add `rack-cache` to your Gemfile before enabling this.
- # For large-scale production use, consider using a caching reverse proxy like nginx, varnish or squid.
+ # For large-scale production use, consider using a caching reverse proxy like NGINX, varnish or squid.
# config.action_dispatch.rack_cache = true
- # Disable Rails's static asset server (Apache or nginx will already do this).
+ # Disable Rails's static asset server (Apache or NGINX will already do this).
config.serve_static_assets = false
# Compress JavaScripts and CSS.
@@ -36,8 +36,8 @@ Rails.application.configure do
config.assets.version = '1.0'
# Specifies the header that your server uses for sending files.
- # config.action_dispatch.x_sendfile_header = "X-Sendfile" # for apache
- # config.action_dispatch.x_sendfile_header = 'X-Accel-Redirect' # for nginx
+ # config.action_dispatch.x_sendfile_header = "X-Sendfile" # for Apache
+ # config.action_dispatch.x_sendfile_header = 'X-Accel-Redirect' # for NGINX
# Force all access to the app over SSL, use Strict-Transport-Security, and use secure cookies.
# config.force_ssl = true
diff --git a/guides/source/2_3_release_notes.md b/guides/source/2_3_release_notes.md
index 2302a618b6..52eeb4c2bc 100644
--- a/guides/source/2_3_release_notes.md
+++ b/guides/source/2_3_release_notes.md
@@ -8,7 +8,7 @@ Rails 2.3 delivers a variety of new and improved features, including pervasive R
Application Architecture
------------------------
-There are two major changes in the architecture of Rails applications: complete integration of the [Rack](http://rack.rubyforge.org/) modular web server interface, and renewed support for Rails Engines.
+There are two major changes in the architecture of Rails applications: complete integration of the [Rack](http://rack.github.io/) modular web server interface, and renewed support for Rails Engines.
### Rack Integration
diff --git a/guides/source/3_0_release_notes.md b/guides/source/3_0_release_notes.md
index 2d7c06837b..fbb5690fa9 100644
--- a/guides/source/3_0_release_notes.md
+++ b/guides/source/3_0_release_notes.md
@@ -521,7 +521,7 @@ A large effort was made in Active Support to make it cherry pickable, that is, y
These are the main changes in Active Support:
* Large clean up of the library removing unused methods throughout.
-* Active Support no longer provides vendored versions of [TZInfo](http://tzinfo.rubyforge.org/), [Memcache Client](http://deveiate.org/projects/RMemCache/) and [Builder](http://builder.rubyforge.org/,) these are all included as dependencies and installed via the `bundle install` command.
+* Active Support no longer provides vendored versions of TZInfo, Memcache Client and Builder these are all included as dependencies and installed via the `bundle install` command.
* Safe buffers are implemented in `ActiveSupport::SafeBuffer`.
* Added `Array.uniq_by` and `Array.uniq_by!`.
* Removed `Array#rand` and backported `Array#sample` from Ruby 1.9.
diff --git a/guides/source/3_1_release_notes.md b/guides/source/3_1_release_notes.md
index 485f8c756b..7626296e7d 100644
--- a/guides/source/3_1_release_notes.md
+++ b/guides/source/3_1_release_notes.md
@@ -173,7 +173,7 @@ The assets pipeline is powered by [Sprockets](https://github.com/sstephenson/spr
### HTTP Streaming
-HTTP Streaming is another change that is new in Rails 3.1. This lets the browser download your stylesheets and JavaScript files while the server is still generating the response. This requires Ruby 1.9.2, is opt-in and requires support from the web server as well, but the popular combo of nginx and unicorn is ready to take advantage of it.
+HTTP Streaming is another change that is new in Rails 3.1. This lets the browser download your stylesheets and JavaScript files while the server is still generating the response. This requires Ruby 1.9.2, is opt-in and requires support from the web server as well, but the popular combo of NGINX and Unicorn is ready to take advantage of it.
### Default JS library is now jQuery
diff --git a/guides/source/action_controller_overview.md b/guides/source/action_controller_overview.md
index 1735188f27..3d15319ca4 100644
--- a/guides/source/action_controller_overview.md
+++ b/guides/source/action_controller_overview.md
@@ -1078,7 +1078,7 @@ Rails keeps a log file for each environment in the `log` folder. These are extre
### Parameters Filtering
-You can filter certain request parameters from your log files by appending them to `config.filter_parameters` in the application configuration. These parameters will be marked [FILTERED] in the log.
+You can filter out sensitive request parameters from your log files by appending them to `config.filter_parameters` in the application configuration. These parameters will be marked [FILTERED] in the log.
```ruby
config.filter_parameters << :password
@@ -1086,7 +1086,7 @@ config.filter_parameters << :password
### Redirects Filtering
-Sometimes it's desirable to filter out from log files some sensible locations your application is redirecting to.
+Sometimes it's desirable to filter out from log files some sensitive locations your application is redirecting to.
You can do that by using the `config.filter_redirect` configuration option:
```ruby
diff --git a/guides/source/active_record_querying.md b/guides/source/active_record_querying.md
index ee8cf4ade6..486e7b80ff 100644
--- a/guides/source/active_record_querying.md
+++ b/guides/source/active_record_querying.md
@@ -659,6 +659,23 @@ FROM orders
GROUP BY date(created_at)
```
+### Total of grouped items
+
+To get the total of grouped items on a single query call `count` after the `group`.
+
+```ruby
+Order.group(:status).count
+# => { 'awaiting_approval' => 7, 'paid' => 12 }
+```
+
+The SQL that would be executed would be something like this:
+
+```sql
+SELECT COUNT (*) AS count_all, status AS status
+FROM "orders"
+GROUP BY status
+```
+
Having
------
@@ -1143,10 +1160,11 @@ Even though Active Record lets you specify conditions on the eager loaded associ
However if you must do this, you may use `where` as you would normally.
```ruby
-Article.includes(:comments).where("comments.visible" => true)
+Article.includes(:comments).where(comments: { visible: true })
```
-This would generate a query which contains a `LEFT OUTER JOIN` whereas the `joins` method would generate one using the `INNER JOIN` function instead.
+This would generate a query which contains a `LEFT OUTER JOIN` whereas the
+`joins` method would generate one using the `INNER JOIN` function instead.
```ruby
SELECT "articles"."id" AS t0_r0, ... "comments"."updated_at" AS t1_r5 FROM "articles" LEFT OUTER JOIN "comments" ON "comments"."article_id" = "articles"."id" WHERE (comments.visible = 1)
@@ -1154,7 +1172,19 @@ This would generate a query which contains a `LEFT OUTER JOIN` whereas the `join
If there was no `where` condition, this would generate the normal set of two queries.
-If, in the case of this `includes` query, there were no comments for any articles, all the articles would still be loaded. By using `joins` (an INNER JOIN), the join conditions **must** match, otherwise no records will be returned.
+NOTE: Using `where` like this will only work when you pass it a Hash. For
+SQL-fragments you need use `references` to force joined tables:
+
+```ruby
+Article.includes(:comments).where("comments.visible = true").references(:comments)
+```
+
+If, in the case of this `includes` query, there were no comments for any
+articles, all the articles would still be loaded. By using `joins` (an INNER
+JOIN), the join conditions **must** match, otherwise no records will be
+returned.
+
+
Scopes
------
diff --git a/guides/source/api_documentation_guidelines.md b/guides/source/api_documentation_guidelines.md
index 2a3bb4e34d..0a55e31818 100644
--- a/guides/source/api_documentation_guidelines.md
+++ b/guides/source/api_documentation_guidelines.md
@@ -13,7 +13,13 @@ After reading this guide, you will know:
RDoc
----
-The Rails API documentation is generated with RDoc. Please consult the documentation for help with the [markup](http://rdoc.rubyforge.org/RDoc/Markup.html), and also take into account these [additional directives](http://rdoc.rubyforge.org/RDoc/Parser/Ruby.html).
+The [Rails API documentation](http://api.rubyonrails.org) is generated with RDoc.
+
+```bash
+ bundle exec rake rdoc
+```
+
+Resulting HTML files can be found and browsed at ./doc/rdoc directory. Just open index.html file in the browser and you're done.
Wording
-------
diff --git a/guides/source/asset_pipeline.md b/guides/source/asset_pipeline.md
index 559d3f5e7d..c81e9e58e3 100644
--- a/guides/source/asset_pipeline.md
+++ b/guides/source/asset_pipeline.md
@@ -794,7 +794,7 @@ For Apache:
</Location>
```
-For nginx:
+For NGINX:
```nginx
location ~ ^/assets/ {
@@ -816,7 +816,7 @@ compression ratio, thus reducing the size of the data transfer to the minimum.
On the other hand, web servers can be configured to serve compressed content
directly from disk, rather than deflating non-compressed files themselves.
-Nginx is able to do this automatically enabling `gzip_static`:
+NGINX is able to do this automatically enabling `gzip_static`:
```nginx
location ~ ^/(assets)/ {
@@ -835,7 +835,7 @@ the module compiled. Otherwise, you may need to perform a manual compilation:
./configure --with-http_gzip_static_module
```
-If you're compiling nginx with Phusion Passenger you'll need to pass that option
+If you're compiling NGINX with Phusion Passenger you'll need to pass that option
when prompted.
A robust configuration for Apache is possible but tricky; please Google around.
@@ -920,7 +920,7 @@ cache forever. This can cause problems. If you use
Every cache is different, so evaluate how your CDN handles caching and make sure
that it plays nicely with the pipeline. You may find quirks related to your
-specific set up, you may not. The defaults nginx uses, for example, should give
+specific set up, you may not. The defaults NGINX uses, for example, should give
you no problems when used as an HTTP cache.
If you want to serve only some assets from your CDN, you can use custom
@@ -1024,12 +1024,12 @@ this passes responsibility for serving the file to the web server, which is
faster. Have a look at [send_file](http://api.rubyonrails.org/classes/ActionController/DataStreaming.html#method-i-send_file)
on how to use this feature.
-Apache and nginx support this option, which can be enabled in
+Apache and NGINX support this option, which can be enabled in
`config/environments/production.rb`:
```ruby
-# config.action_dispatch.x_sendfile_header = "X-Sendfile" # for apache
-# config.action_dispatch.x_sendfile_header = 'X-Accel-Redirect' # for nginx
+# config.action_dispatch.x_sendfile_header = "X-Sendfile" # for Apache
+# config.action_dispatch.x_sendfile_header = 'X-Accel-Redirect' # for NGINX
```
WARNING: If you are upgrading an existing application and intend to use this
@@ -1039,7 +1039,7 @@ and any other environments you define with production behavior (not
TIP: For further details have a look at the docs of your production web server:
- [Apache](https://tn123.org/mod_xsendfile/)
-- [Nginx](http://wiki.nginx.org/XSendfile)
+- [NGINX](http://wiki.nginx.org/XSendfile)
Assets Cache Store
------------------
diff --git a/guides/source/caching_with_rails.md b/guides/source/caching_with_rails.md
index c652aa6a80..3e39ecdad2 100644
--- a/guides/source/caching_with_rails.md
+++ b/guides/source/caching_with_rails.md
@@ -28,7 +28,7 @@ config.action_controller.perform_caching = true
### Page Caching
-Page caching is a Rails mechanism which allows the request for a generated page to be fulfilled by the webserver (i.e. Apache or nginx), without ever having to go through the Rails stack at all. Obviously, this is super-fast. Unfortunately, it can't be applied to every situation (such as pages that need authentication) and since the webserver is literally just serving a file from the filesystem, cache expiration is an issue that needs to be dealt with.
+Page caching is a Rails mechanism which allows the request for a generated page to be fulfilled by the webserver (i.e. Apache or NGINX), without ever having to go through the Rails stack at all. Obviously, this is super-fast. Unfortunately, it can't be applied to every situation (such as pages that need authentication) and since the webserver is literally just serving a file from the filesystem, cache expiration is an issue that needs to be dealt with.
INFO: Page Caching has been removed from Rails 4. See the [actionpack-page_caching gem](https://github.com/rails/actionpack-page_caching). See [DHH's key-based cache expiration overview](http://signalvnoise.com/posts/3113-how-key-based-cache-expiration-works) for the newly-preferred method.
diff --git a/guides/source/configuring.md b/guides/source/configuring.md
index 7a9e1beb23..a0f0738fba 100644
--- a/guides/source/configuring.md
+++ b/guides/source/configuring.md
@@ -120,7 +120,7 @@ numbers. New applications filter out passwords by adding the following `config.f
* `secrets.secret_key_base` is used for specifying a key which allows sessions for the application to be verified against a known secure key to prevent tampering. Applications get `secrets.secret_key_base` initialized to a random key present in `config/secrets.yml`.
-* `config.serve_static_assets` configures Rails itself to serve static assets. Defaults to true, but in the production environment is turned off as the server software (e.g. Nginx or Apache) used to run the application should serve static assets instead. Unlike the default setting set this to true when running (absolutely not recommended!) or testing your app in production mode using WEBrick. Otherwise you won't be able use page caching and requests for files that exist regularly under the public directory will anyway hit your Rails app.
+* `config.serve_static_assets` configures Rails itself to serve static assets. Defaults to true, but in the production environment is turned off as the server software (e.g. NGINX or Apache) used to run the application should serve static assets instead. Unlike the default setting set this to true when running (absolutely not recommended!) or testing your app in production mode using WEBrick. Otherwise you won't be able use page caching and requests for files that exist regularly under the public directory will anyway hit your Rails app.
* `config.session_store` is usually set up in `config/initializers/session_store.rb` and specifies what class to use to store the session. Possible values are `:cookie_store` which is the default, `:mem_cache_store`, and `:disabled`. The last one tells Rails not to deal with sessions. Custom session stores can also be specified:
@@ -729,7 +729,7 @@ Rails will now prepend "/app1" when generating links.
#### Using Passenger
-Passenger makes it easy to run your application in a subdirectory. You can find the relevant configuration in the [passenger manual](http://www.modrails.com/documentation/Users%20guide%20Apache.html#deploying_rails_to_sub_uri).
+Passenger makes it easy to run your application in a subdirectory. You can find the relevant configuration in the [Passenger manual](http://www.modrails.com/documentation/Users%20guide%20Apache.html#deploying_rails_to_sub_uri).
#### Using a Reverse Proxy
@@ -739,11 +739,11 @@ Many modern web servers can be used as a proxy server to balance third-party ele
One such application server you can use is [Unicorn](http://unicorn.bogomips.org/) to run behind a reverse proxy.
-In this case, you would need to configure the proxy server (nginx, apache, etc) to accept connections from your application server (Unicorn). By default Unicorn will listen for TCP connections on port 8080, but you can change the port or configure it to use sockets instead.
+In this case, you would need to configure the proxy server (NGINX, Apache, etc) to accept connections from your application server (Unicorn). By default Unicorn will listen for TCP connections on port 8080, but you can change the port or configure it to use sockets instead.
You can find more information in the [Unicorn readme](http://unicorn.bogomips.org/README.html) and understand the [philosophy](http://unicorn.bogomips.org/PHILOSOPHY.html) behind it.
-Once you've configured the application server, you must proxy requests to it by configuring your web server appropriately. For example your nginx config may include:
+Once you've configured the application server, you must proxy requests to it by configuring your web server appropriately. For example your NGINX config may include:
```
upstream application_server {
@@ -769,7 +769,7 @@ server {
}
```
-Be sure to read the [nginx documentation](http://nginx.org/en/docs/) for the most up-to-date information.
+Be sure to read the [NGINX documentation](http://nginx.org/en/docs/) for the most up-to-date information.
#### Considerations when deploying to a subdirectory
diff --git a/guides/source/getting_started.md b/guides/source/getting_started.md
index d9619bbc21..e088abc150 100644
--- a/guides/source/getting_started.md
+++ b/guides/source/getting_started.md
@@ -623,6 +623,8 @@ method returns an `ActiveSupport::HashWithIndifferentAccess` object, which
allows you to access the keys of the hash using either strings or symbols. In
this situation, the only parameters that matter are the ones from the form.
+TIP: Ensure you have a firm grasp of the `params` method, as you'll use it fairly regularly. Let's consider an example URL: **http://www.example.com/?username=dhh&email=dhh@email.com**. In this URL, `params[:username]` would equal "dhh" and `params[:email]` would equal "dhh@email.com".
+
If you re-submit the form one more time you'll now no longer get the missing
template error. Instead, you'll see something that looks like the following:
@@ -739,6 +741,8 @@ database columns. In the first line we do just that (remember that
`@article.save` is responsible for saving the model in the database. Finally,
we redirect the user to the `show` action, which we'll define later.
+TIP: You might be wondering why the `A` in `Article.new` is capitalized above, whereas most other references to articles in this guide have used lowercase. In this context, we are referring to the class named `Article` that is defined in `\models\article.rb`. Class names in Ruby must begin with a capital letter.
+
TIP: As we'll see later, `@article.save` returns a boolean indicating whether
the article was saved or not.
diff --git a/guides/source/i18n.md b/guides/source/i18n.md
index 0eba3af6e8..8340d6807f 100644
--- a/guides/source/i18n.md
+++ b/guides/source/i18n.md
@@ -860,6 +860,24 @@ en:
subject: "Welcome to Rails Guides!"
```
+To send parameters to interpolation use the `default_i18n_subject` method on the mailer.
+
+```ruby
+# user_mailer.rb
+class UserMailer < ActionMailer::Base
+ def welcome(user)
+ mail(to: user.email, subject: default_i18n_subject(user: user.name))
+ end
+end
+```
+
+```yaml
+en:
+ user_mailer:
+ welcome:
+ subject: "%{user}, welcome to Rails Guides!"
+```
+
### Overview of Other Built-In Methods that Provide I18n Support
Rails uses fixed strings and other localizations, such as format strings and other format information in a couple of helpers. Here's a brief overview.
diff --git a/guides/source/migrations.md b/guides/source/migrations.md
index 6742c05946..31e314c69b 100644
--- a/guides/source/migrations.md
+++ b/guides/source/migrations.md
@@ -415,6 +415,29 @@ end
removes the `description` and `name` columns, creates a `part_number` string
column and adds an index on it. Finally it renames the `upccode` column.
+### Changing Columns
+
+Like the `remove_column` and `add_column` Rails provides the `change_column`
+migration method.
+
+```ruby
+change_column :products, :part_number, :text
+```
+
+This changes the column `part_number` on products table to be a `:text` field.
+
+Besides `change_column`, the `change_column_null` and `change_column_default`
+methods are used specifically to change the null and default values of a
+column.
+
+```ruby
+change_column_null :products, :name, false
+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.
+
### 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/rails_on_rack.md b/guides/source/rails_on_rack.md
index 9053f31b8e..01941fa338 100644
--- a/guides/source/rails_on_rack.md
+++ b/guides/source/rails_on_rack.md
@@ -18,7 +18,7 @@ Introduction to Rack
Rack provides a minimal, modular and adaptable interface for developing web applications in Ruby. By wrapping HTTP requests and responses in the simplest way possible, it unifies and distills the API for web servers, web frameworks, and software in between (the so-called middleware) into a single method call.
-- [Rack API Documentation](http://rack.rubyforge.org/doc/)
+* [Rack API Documentation](http://rack.github.io/)
Explaining Rack is not really in the scope of this guide. In case you are not familiar with Rack's basics, you should check out the [Resources](#resources) section below.
diff --git a/guides/source/security.md b/guides/source/security.md
index 75d8c8e4c8..7e39986f8b 100644
--- a/guides/source/security.md
+++ b/guides/source/security.md
@@ -741,7 +741,7 @@ s = sanitize(user_input, tags: tags, attributes: %w(href title))
This allows only the given tags and does a good job, even against all kinds of tricks and malformed tags.
-As a second step, _it is good practice to escape all output of the application_, especially when re-displaying user input, which hasn't been input-filtered (as in the search form example earlier on). _Use `escapeHTML()` (or its alias `h()`) method_ to replace the HTML input characters &amp;, &quot;, &lt;, &gt; by their uninterpreted representations in HTML (`&amp;`, `&quot;`, `&lt`;, and `&gt;`). However, it can easily happen that the programmer forgets to use it, so _it is recommended to use the [SafeErb](http://safe-erb.rubyforge.org/svn/plugins/safe_erb/) plugin_. SafeErb reminds you to escape strings from external sources.
+As a second step, _it is good practice to escape all output of the application_, especially when re-displaying user input, which hasn't been input-filtered (as in the search form example earlier on). _Use `escapeHTML()` (or its alias `h()`) method_ to replace the HTML input characters &amp;, &quot;, &lt;, &gt; by their uninterpreted representations in HTML (`&amp;`, `&quot;`, `&lt`;, and `&gt;`). However, it can easily happen that the programmer forgets to use it, so _it is recommended to use the SafeErb gem. SafeErb reminds you to escape strings from external sources.
##### Obfuscation and Encoding Injection
diff --git a/railties/CHANGELOG.md b/railties/CHANGELOG.md
index b328619646..3350b1a4b2 100644
--- a/railties/CHANGELOG.md
+++ b/railties/CHANGELOG.md
@@ -1,3 +1,7 @@
+* Invalid `bin/rails generate` commands will now show spelling suggestions.
+
+ *Richard Schneeman*
+
* Add `bin/setup` script to bootstrap an application.
*Yves Senn*
diff --git a/railties/README.rdoc b/railties/README.rdoc
index 6248b5feed..a25658668c 100644
--- a/railties/README.rdoc
+++ b/railties/README.rdoc
@@ -31,7 +31,11 @@ API documentation is at
* http://api.rubyonrails.org
-Bug reports and feature requests can be filed with the rest for the Ruby on Rails project here:
+Bug reports can be filed for the Ruby on Rails project here:
* https://github.com/rails/rails/issues
+Feature requests should be discussed on the rails-core mailing list here:
+
+* https://groups.google.com/forum/?fromgroups#!forum/rubyonrails-core
+
diff --git a/railties/lib/rails/generators.rb b/railties/lib/rails/generators.rb
index 2a0148fe9d..bf2390cb7e 100644
--- a/railties/lib/rails/generators.rb
+++ b/railties/lib/rails/generators.rb
@@ -156,8 +156,12 @@ module Rails
args << "--help" if args.empty? && klass.arguments.any? { |a| a.required? }
klass.start(args, config)
else
- puts "Could not find generator '#{namespace}'. Please choose a generator below."
- print_generators
+ options = sorted_groups.map(&:last).flatten
+ suggestions = options.sort_by {|suggested| levenshtein_distance(namespace.to_s, suggested) }.first(3)
+ msg = "Could not find generator '#{namespace}'. "
+ msg << "Maybe you meant #{ suggestions.map {|s| "'#{s}'"}.join(" or ") }\n"
+ msg << "Run `rails generate --help` for more options."
+ puts msg
end
end
@@ -220,31 +224,71 @@ module Rails
print_generators
end
- def self.print_generators
+ def self.public_namespaces
lookup!
+ subclasses.map { |k| k.namespace }
+ end
- namespaces = subclasses.map{ |k| k.namespace }
- namespaces.sort!
+ def self.print_generators
+ sorted_groups.each { |b, n| print_list(b, n) }
+ end
+ def self.sorted_groups
+ namespaces = public_namespaces
+ namespaces.sort!
groups = Hash.new { |h,k| h[k] = [] }
namespaces.each do |namespace|
base = namespace.split(':').first
groups[base] << namespace
end
- # Print Rails defaults first.
rails = groups.delete("rails")
rails.map! { |n| n.sub(/^rails:/, '') }
rails.delete("app")
rails.delete("plugin")
- print_list("rails", rails)
hidden_namespaces.each { |n| groups.delete(n.to_s) }
- groups.sort.each { |b, n| print_list(b, n) }
+ [["rails", rails]] + groups.sort.to_a
end
protected
+ # This code is based directly on the Text gem implementation
+ # Returns a value representing the "cost" of transforming str1 into str2
+ def self.levenshtein_distance str1, str2
+ s = str1
+ t = str2
+ n = s.length
+ m = t.length
+ max = n/2
+
+ return m if (0 == n)
+ return n if (0 == m)
+ return n if (n - m).abs > max
+
+ d = (0..m).to_a
+ x = nil
+
+ str1.each_char.each_with_index do |char1,i|
+ e = i+1
+
+ str2.each_char.each_with_index do |char2,j|
+ cost = (char1 == char2) ? 0 : 1
+ x = [
+ d[j+1] + 1, # insertion
+ e + 1, # deletion
+ d[j] + cost # substitution
+ ].min
+ d[j] = e
+ e = x
+ end
+
+ d[m] = x
+ end
+
+ return x
+ end
+
# Prints a list of generators.
def self.print_list(base, namespaces) #:nodoc:
namespaces = namespaces.reject do |n|
diff --git a/railties/lib/rails/generators/rails/app/templates/bin/setup b/railties/lib/rails/generators/rails/app/templates/bin/setup
index 5fcc1a07d6..0e22b3fa5c 100644
--- a/railties/lib/rails/generators/rails/app/templates/bin/setup
+++ b/railties/lib/rails/generators/rails/app/templates/bin/setup
@@ -12,7 +12,7 @@ Dir.chdir APP_ROOT do
system "bundle check || bundle install"
# puts "\n== Copying sample files =="
- # unless File.exists?("config/database.yml")
+ # unless File.exist?("config/database.yml")
# system "cp config/database.yml.sample config/database.yml"
# end
diff --git a/railties/lib/rails/generators/rails/app/templates/config/databases/ibm_db.yml b/railties/lib/rails/generators/rails/app/templates/config/databases/ibm_db.yml
index d088dd62bf..187ff01bac 100644
--- a/railties/lib/rails/generators/rails/app/templates/config/databases/ibm_db.yml
+++ b/railties/lib/rails/generators/rails/app/templates/config/databases/ibm_db.yml
@@ -1,7 +1,7 @@
# IBM Dataservers
#
# Home Page
-# http://rubyforge.org/projects/rubyibm/
+# https://github.com/dparnell/ibm_db
#
# To install the ibm_db gem:
#
@@ -31,8 +31,6 @@
# Configure Using Gemfile
# gem 'ibm_db'
#
-# For more details on the installation and the connection parameters below,
-# please refer to the latest documents at http://rubyforge.org/docman/?group_id=2361
#
default: &default
adapter: ibm_db
diff --git a/railties/lib/rails/generators/rails/app/templates/config/databases/oracle.yml b/railties/lib/rails/generators/rails/app/templates/config/databases/oracle.yml
index 10ab4c02e2..9aedcc15cb 100644
--- a/railties/lib/rails/generators/rails/app/templates/config/databases/oracle.yml
+++ b/railties/lib/rails/generators/rails/app/templates/config/databases/oracle.yml
@@ -1,7 +1,7 @@
# Oracle/OCI 8i, 9, 10g
#
# Requires Ruby/OCI8:
-# http://rubyforge.org/projects/ruby-oci8/
+# https://github.com/kubo/ruby-oci8
#
# Specify your database using any valid connection syntax, such as a
# tnsnames.ora service name, or an SQL connect string of the form:
diff --git a/railties/lib/rails/generators/rails/app/templates/config/environments/production.rb.tt b/railties/lib/rails/generators/rails/app/templates/config/environments/production.rb.tt
index 9ed71687ea..a2aa7c09db 100644
--- a/railties/lib/rails/generators/rails/app/templates/config/environments/production.rb.tt
+++ b/railties/lib/rails/generators/rails/app/templates/config/environments/production.rb.tt
@@ -16,10 +16,10 @@ Rails.application.configure do
# Enable Rack::Cache to put a simple HTTP cache in front of your application
# Add `rack-cache` to your Gemfile before enabling this.
- # For large-scale production use, consider using a caching reverse proxy like nginx, varnish or squid.
+ # For large-scale production use, consider using a caching reverse proxy like NGINX, varnish or squid.
# config.action_dispatch.rack_cache = true
- # Disable Rails's static asset server (Apache or nginx will already do this).
+ # Disable Rails's static asset server (Apache or NGINX will already do this).
config.serve_static_assets = false
<%- unless options.skip_sprockets? -%>
@@ -37,8 +37,8 @@ Rails.application.configure do
<%- end -%>
# Specifies the header that your server uses for sending files.
- # config.action_dispatch.x_sendfile_header = "X-Sendfile" # for apache
- # config.action_dispatch.x_sendfile_header = 'X-Accel-Redirect' # for nginx
+ # config.action_dispatch.x_sendfile_header = "X-Sendfile" # for Apache
+ # config.action_dispatch.x_sendfile_header = 'X-Accel-Redirect' # for NGINX
# Force all access to the app over SSL, use Strict-Transport-Security, and use secure cookies.
# config.force_ssl = true
diff --git a/railties/test/generators/shared_generator_tests.rb b/railties/test/generators/shared_generator_tests.rb
index 8e198d5fe1..b998fef42e 100644
--- a/railties/test/generators/shared_generator_tests.rb
+++ b/railties/test/generators/shared_generator_tests.rb
@@ -78,9 +78,12 @@ module SharedGeneratorTests
end
def test_template_raises_an_error_with_invalid_path
- content = capture(:stderr){ run_generator([destination_root, "-m", "non/existent/path"]) }
- assert_match(/The template \[.*\] could not be loaded/, content)
- assert_match(/non\/existent\/path/, content)
+ quietly do
+ content = capture(:stderr){ run_generator([destination_root, "-m", "non/existent/path"]) }
+
+ assert_match(/The template \[.*\] could not be loaded/, content)
+ assert_match(/non\/existent\/path/, content)
+ end
end
def test_template_is_executed_when_supplied
@@ -89,7 +92,7 @@ module SharedGeneratorTests
template.instance_eval "def read; self; end" # Make the string respond to read
generator([destination_root], template: path).expects(:open).with(path, 'Accept' => 'application/x-thor-template').returns(template)
- assert_match(/It works!/, capture(:stdout) { generator.invoke_all })
+ quietly { assert_match(/It works!/, capture(:stdout) { generator.invoke_all }) }
end
def test_template_is_executed_when_supplied_an_https_path
@@ -98,7 +101,7 @@ module SharedGeneratorTests
template.instance_eval "def read; self; end" # Make the string respond to read
generator([destination_root], template: path).expects(:open).with(path, 'Accept' => 'application/x-thor-template').returns(template)
- assert_match(/It works!/, capture(:stdout) { generator.invoke_all })
+ quietly { assert_match(/It works!/, capture(:stdout) { generator.invoke_all }) }
end
def test_dev_option
diff --git a/railties/test/generators_test.rb b/railties/test/generators_test.rb
index 8d6dbf80c2..0b4edafaca 100644
--- a/railties/test/generators_test.rb
+++ b/railties/test/generators_test.rb
@@ -24,7 +24,13 @@ class GeneratorsTest < Rails::Generators::TestCase
name = :unknown
output = capture(:stdout){ Rails::Generators.invoke name }
assert_match "Could not find generator '#{name}'", output
- assert_match "scaffold", output
+ assert_match "`rails generate --help`", output
+ end
+
+ def test_generator_suggestions
+ name = :migrationz
+ output = capture(:stdout){ Rails::Generators.invoke name }
+ assert_match "Maybe you meant 'migration'", output
end
def test_help_when_a_generator_with_required_arguments_is_invoked_without_arguments