aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--.codeclimate.yml14
-rw-r--r--.rubocop.yml8
-rw-r--r--Gemfile.lock4
-rw-r--r--actionpack/CHANGELOG.md4
-rw-r--r--actionpack/lib/action_controller/metal/request_forgery_protection.rb6
-rw-r--r--actionpack/lib/action_dispatch/http/parameters.rb2
-rw-r--r--actionpack/lib/action_dispatch/routing/mapper.rb2
-rw-r--r--actionpack/lib/action_dispatch/testing/integration.rb6
-rw-r--r--actionpack/test/controller/caching_test.rb12
-rw-r--r--actionpack/test/dispatch/routing_test.rb4
-rw-r--r--actionview/CHANGELOG.md11
-rw-r--r--actionview/lib/action_view/helpers/asset_tag_helper.rb9
-rw-r--r--actionview/lib/action_view/helpers/asset_url_helper.rb23
-rw-r--r--actionview/lib/action_view/helpers/cache_helper.rb23
-rw-r--r--actionview/lib/action_view/helpers/translation_helper.rb3
-rw-r--r--actionview/lib/action_view/helpers/url_helper.rb3
-rw-r--r--actionview/lib/action_view/layouts.rb7
-rw-r--r--actionview/lib/action_view/renderer/partial_renderer.rb7
-rw-r--r--actionview/lib/action_view/renderer/partial_renderer/collection_caching.rb8
-rw-r--r--actionview/lib/action_view/renderer/template_renderer.rb2
-rw-r--r--actionview/lib/action_view/template/error.rb3
-rw-r--r--actionview/lib/action_view/template/handlers/erb.rb5
-rw-r--r--actionview/lib/action_view/testing/resolvers.rb4
-rw-r--r--actionview/test/actionpack/controller/layout_test.rb3
-rw-r--r--actionview/test/template/render_test.rb8
-rw-r--r--activemodel/lib/active_model/attribute_methods.rb5
-rw-r--r--activemodel/lib/active_model/type/time.rb2
-rw-r--r--activemodel/lib/active_model/validations/format.rb5
-rw-r--r--activemodel/test/validators/email_validator.rb6
-rw-r--r--activerecord/CHANGELOG.md5
-rw-r--r--activerecord/Rakefile4
-rw-r--r--activerecord/lib/active_record/associations/has_one_through_association.rb2
-rw-r--r--activerecord/lib/active_record/attribute.rb4
-rw-r--r--activerecord/lib/active_record/attribute_set.rb2
-rw-r--r--activerecord/lib/active_record/attribute_set/builder.rb28
-rw-r--r--activerecord/lib/active_record/attributes.rb2
-rw-r--r--activerecord/lib/active_record/coders/yaml_column.rb3
-rw-r--r--activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb7
-rw-r--r--activerecord/lib/active_record/connection_adapters/abstract/quoting.rb12
-rw-r--r--activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb3
-rw-r--r--activerecord/lib/active_record/connection_adapters/mysql/database_statements.rb2
-rw-r--r--activerecord/lib/active_record/connection_adapters/mysql/quoting.rb2
-rw-r--r--activerecord/lib/active_record/connection_adapters/postgresql/quoting.rb2
-rw-r--r--activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb4
-rw-r--r--activerecord/lib/active_record/connection_adapters/sqlite3_adapter.rb8
-rw-r--r--activerecord/lib/active_record/dynamic_matchers.rb4
-rw-r--r--activerecord/lib/active_record/migration.rb13
-rw-r--r--activerecord/lib/active_record/model_schema.rb13
-rw-r--r--activerecord/lib/active_record/relation/delegation.rb3
-rw-r--r--activerecord/lib/active_record/relation/query_methods.rb9
-rw-r--r--activerecord/lib/active_record/sanitization.rb4
-rw-r--r--activerecord/lib/active_record/statement_cache.rb13
-rw-r--r--activerecord/lib/active_record/tasks/mysql_database_tasks.rb2
-rw-r--r--activerecord/lib/rails/generators/active_record/migration/migration_generator.rb3
-rw-r--r--activerecord/test/cases/associations/has_many_associations_test.rb48
-rw-r--r--activerecord/test/cases/associations/inner_join_association_test.rb2
-rw-r--r--activerecord/test/cases/associations/left_outer_join_association_test.rb11
-rw-r--r--activerecord/test/cases/associations_test.rb2
-rw-r--r--activerecord/test/cases/attribute_methods_test.rb307
-rw-r--r--activerecord/test/cases/attributes_test.rb44
-rw-r--r--activerecord/test/cases/callbacks_test.rb2
-rw-r--r--activerecord/test/cases/dirty_test.rb2
-rw-r--r--activerecord/test/cases/nested_attributes_test.rb8
-rw-r--r--activerecord/test/cases/quoting_test.rb16
-rw-r--r--activerecord/test/cases/scoping/default_scoping_test.rb3
-rw-r--r--activerecord/test/cases/test_case.rb3
-rw-r--r--activerecord/test/schema/postgresql_specific_schema.rb2
-rw-r--r--activesupport/CHANGELOG.md31
-rw-r--r--activesupport/lib/active_support/configurable.rb4
-rw-r--r--activesupport/lib/active_support/core_ext/module/attribute_accessors.rb5
-rw-r--r--activesupport/lib/active_support/core_ext/module/attribute_accessors_per_thread.rb7
-rw-r--r--activesupport/lib/active_support/core_ext/module/delegation.rb5
-rw-r--r--activesupport/lib/active_support/core_ext/object.rb1
-rw-r--r--activesupport/lib/active_support/core_ext/object/blank.rb4
-rw-r--r--activesupport/lib/active_support/core_ext/object/exclusion.rb15
-rw-r--r--activesupport/lib/active_support/core_ext/regexp.rb4
-rw-r--r--activesupport/lib/active_support/deprecation/proxy_wrappers.rb3
-rw-r--r--activesupport/lib/active_support/duration/iso8601_parser.rb3
-rw-r--r--activesupport/lib/active_support/inflector/methods.rb5
-rw-r--r--activesupport/lib/active_support/message_encryptor.rb41
-rw-r--r--activesupport/lib/active_support/message_verifier.rb2
-rw-r--r--activesupport/lib/active_support/multibyte/chars.rb3
-rw-r--r--activesupport/lib/active_support/testing/deprecation.rb3
-rw-r--r--activesupport/lib/active_support/xml_mini/jdom.rb2
-rw-r--r--activesupport/test/clean_backtrace_test.rb8
-rw-r--r--activesupport/test/core_ext/object/exclusion_test.rb53
-rw-r--r--activesupport/test/core_ext/regexp_ext_test.rb24
-rw-r--r--activesupport/test/json/encoding_test.rb14
-rw-r--r--activesupport/test/message_encryptor_test.rb18
-rw-r--r--activesupport/test/multibyte_conformance_test.rb2
-rw-r--r--activesupport/test/multibyte_grapheme_break_conformance_test.rb2
-rw-r--r--activesupport/test/multibyte_normalization_conformance_test.rb2
-rw-r--r--activesupport/test/xml_mini/jdom_engine_test.rb2
-rw-r--r--guides/source/active_support_core_extensions.md18
-rw-r--r--guides/source/caching_with_rails.md10
-rw-r--r--railties/CHANGELOG.md4
-rw-r--r--railties/lib/rails/generators/app_base.rb8
-rw-r--r--railties/lib/rails/generators/rails/app/templates/Gemfile2
-rw-r--r--railties/lib/rails/generators/rails/app/templates/config/environments/production.rb.tt4
-rw-r--r--railties/test/application/integration_test_case_test.rb28
-rw-r--r--railties/test/generators/app_generator_test.rb9
101 files changed, 802 insertions, 347 deletions
diff --git a/.codeclimate.yml b/.codeclimate.yml
index 877c67873d..17fcaa4360 100644
--- a/.codeclimate.yml
+++ b/.codeclimate.yml
@@ -7,20 +7,6 @@ ratings:
- "**.rb"
exclude_paths:
- - actioncable/lib/rails/generators/
- - actioncable/test/
- - actionmailer/lib/rails/generators/
- - actionmailer/test/
- - actionpack/test/
- - actionview/test/
- - activejob/lib/rails/generators/
- - activejob/test/
- - activemodel/test/
- - activerecord/lib/rails/generators/
- - activerecord/test/
- - activesupport/test/
- - railties/lib/rails/generators/
- - railties/test/
- ci/
- guides/
- tasks/
diff --git a/.rubocop.yml b/.rubocop.yml
index 62fb263c9e..fbca606605 100644
--- a/.rubocop.yml
+++ b/.rubocop.yml
@@ -26,18 +26,26 @@ Style/AndOr:
Lint/RequireParentheses:
Enabled: true
+# Do not use braces for hash literals when they are the last argument of a
+# method call.
Style/BracesAroundHashParameters:
Enabled: true
+# Method definitions after `private` or `protected` isolated calls need one
+# extra level of indentation.
Style/IndentationConsistency:
Enabled: true
EnforcedStyle: rails
+# In a regular class definition, no empty lines around the body.
Style/EmptyLinesAroundClassBody:
Enabled: true
+# In a regular module definition, no empty lines around the body.
Style/EmptyLinesAroundModuleBody:
Enabled: true
+# Align `end` with the matching keyword or starting expression except for
+# assignments, where it should be aligned with the LHS.
Lint/EndAlignment:
AlignWith: variable
diff --git a/Gemfile.lock b/Gemfile.lock
index 9fb842ac2c..0a3d91ca29 100644
--- a/Gemfile.lock
+++ b/Gemfile.lock
@@ -103,7 +103,7 @@ GEM
specs:
addressable (2.4.0)
amq-protocol (2.0.1)
- arel (7.0.0)
+ arel (7.1.1)
backburner (1.3.0)
beaneater (~> 1.0)
dante (> 0.1.5)
@@ -401,4 +401,4 @@ DEPENDENCIES
wdm (>= 0.1.0)
BUNDLED WITH
- 1.11.2
+ 1.12.5
diff --git a/actionpack/CHANGELOG.md b/actionpack/CHANGELOG.md
index 12430ca965..7bb9b62468 100644
--- a/actionpack/CHANGELOG.md
+++ b/actionpack/CHANGELOG.md
@@ -1,7 +1,7 @@
-* Fix 'defaults' option for root route
+* Fix 'defaults' option for root route.
A regression from some refactoring for the 5.0 release, this change
- fixes the use of 'defaults' (default parameters) in the 'root' routing method
+ fixes the use of 'defaults' (default parameters) in the 'root' routing method.
*Chris Arcand*
diff --git a/actionpack/lib/action_controller/metal/request_forgery_protection.rb b/actionpack/lib/action_controller/metal/request_forgery_protection.rb
index 0559fbc6ce..fd7ffcfcd7 100644
--- a/actionpack/lib/action_controller/metal/request_forgery_protection.rb
+++ b/actionpack/lib/action_controller/metal/request_forgery_protection.rb
@@ -109,10 +109,10 @@ module ActionController #:nodoc:
# * <tt>:only/:except</tt> - Only apply forgery protection to a subset of actions. For example <tt>only: [ :create, :create_all ]</tt>.
# * <tt>:if/:unless</tt> - Turn off the forgery protection entirely depending on the passed Proc or method reference.
# * <tt>:prepend</tt> - By default, the verification of the authentication token will be added at the position of the
- # protect_from_forgery call in your application. This means any callbacks added before are run first. This is useful
- # when you want your forgery protection to depend on other callbacks, like authentication methods (Oauth vs Cookie auth).
+ # protect_from_forgery call in your application. This means any callbacks added before are run first. This is useful
+ # when you want your forgery protection to depend on other callbacks, like authentication methods (Oauth vs Cookie auth).
#
- # If you need to add verification to the beginning of the callback chain, use <tt>prepend: true</tt>.
+ # If you need to add verification to the beginning of the callback chain, use <tt>prepend: true</tt>.
# * <tt>:with</tt> - Set the method to handle unverified request.
#
# Valid unverified request handling methods are:
diff --git a/actionpack/lib/action_dispatch/http/parameters.rb b/actionpack/lib/action_dispatch/http/parameters.rb
index 3f0e51790c..ea0e2ee41f 100644
--- a/actionpack/lib/action_dispatch/http/parameters.rb
+++ b/actionpack/lib/action_dispatch/http/parameters.rb
@@ -65,7 +65,7 @@ module ActionDispatch
private
def parse_formatted_parameters(parsers)
- return yield if content_length.zero?
+ return yield if content_length.zero? || content_mime_type.nil?
strategy = parsers.fetch(content_mime_type.symbol) { return yield }
diff --git a/actionpack/lib/action_dispatch/routing/mapper.rb b/actionpack/lib/action_dispatch/routing/mapper.rb
index fd68d4f185..12ddd0f148 100644
--- a/actionpack/lib/action_dispatch/routing/mapper.rb
+++ b/actionpack/lib/action_dispatch/routing/mapper.rb
@@ -1924,7 +1924,7 @@ to this:
def match_root_route(options)
name = has_named_route?(:root) ? nil : :root
defaults_option = options.delete(:defaults)
- args = ['/', { :as => name, :via => :get }.merge!(options)]
+ args = ['/', { as: name, via: :get }.merge!(options)]
if defaults_option
defaults(defaults_option) { match(*args) }
diff --git a/actionpack/lib/action_dispatch/testing/integration.rb b/actionpack/lib/action_dispatch/testing/integration.rb
index 4897f44268..9a76b68ae1 100644
--- a/actionpack/lib/action_dispatch/testing/integration.rb
+++ b/actionpack/lib/action_dispatch/testing/integration.rb
@@ -717,7 +717,11 @@ module ActionDispatch
module ClassMethods
def app
- defined?(@@app) ? @@app : ActionDispatch.test_app
+ if defined?(@@app) && @@app
+ @@app
+ else
+ ActionDispatch.test_app
+ end
end
def app=(app)
diff --git a/actionpack/test/controller/caching_test.rb b/actionpack/test/controller/caching_test.rb
index 7faf3cd8c6..9c2619dc3d 100644
--- a/actionpack/test/controller/caching_test.rb
+++ b/actionpack/test/controller/caching_test.rb
@@ -393,9 +393,14 @@ class CollectionCacheController < ActionController::Base
@customers = [Customer.new('david', 1)]
render partial: 'customers/commented_customer', collection: @customers, as: :customer, cached: true
end
+
+ def index_with_callable_cache_key
+ @customers = [Customer.new('david', 1)]
+ render partial: 'customers/customer', collection: @customers, cached: -> customer { 'cached_david' }
+ end
end
-class AutomaticCollectionCacheTest < ActionController::TestCase
+class CollectionCacheTest < ActionController::TestCase
def setup
super
@controller = CollectionCacheController.new
@@ -438,6 +443,11 @@ class AutomaticCollectionCacheTest < ActionController::TestCase
assert_equal 1, @controller.partial_rendered_times
end
+ def test_caching_with_callable_cache_key
+ get :index_with_callable_cache_key
+ assert_customer_cached 'cached_david', 'david, 1'
+ end
+
private
def assert_customer_cached(key, content)
assert_match content,
diff --git a/actionpack/test/dispatch/routing_test.rb b/actionpack/test/dispatch/routing_test.rb
index 2fe40de583..cac89417a5 100644
--- a/actionpack/test/dispatch/routing_test.rb
+++ b/actionpack/test/dispatch/routing_test.rb
@@ -1761,7 +1761,7 @@ class TestRoutingMapper < ActionDispatch::IntegrationTest
def test_keyed_default_string_params_with_root
draw do
- root :to => 'pages#show', :defaults => { :id => 'home' }
+ root to: 'pages#show', defaults: { id: 'home' }
end
get '/'
@@ -1770,7 +1770,7 @@ class TestRoutingMapper < ActionDispatch::IntegrationTest
def test_default_string_params_with_root
draw do
- root :to => 'pages#show', :id => 'home'
+ root to: 'pages#show', id: 'home'
end
get '/'
diff --git a/actionview/CHANGELOG.md b/actionview/CHANGELOG.md
index ab4b46c56e..7754bd8dd9 100644
--- a/actionview/CHANGELOG.md
+++ b/actionview/CHANGELOG.md
@@ -1,3 +1,14 @@
+* Changed partial rendering with a collection to allow collections which
+ implement `to_a`.
+
+ Extracting the collection option had an optimization to avoid unnecessary
+ queries of ActiveRecord Relations by calling `#to_ary` on the given
+ collection. Instances of `Enumerator` or `Enumerable` are valid
+ collections, but they do not implement `#to_ary`. By changing this to
+ `#to_a`, they will now be extracted and rendered as expected.
+
+ *Steven Harman*
+
* New syntax for tag helpers. Avoid positional parameters and support HTML5 by default.
Example usage of tag helpers before:
diff --git a/actionview/lib/action_view/helpers/asset_tag_helper.rb b/actionview/lib/action_view/helpers/asset_tag_helper.rb
index bcbb3db6a9..d7acf08b45 100644
--- a/actionview/lib/action_view/helpers/asset_tag_helper.rb
+++ b/actionview/lib/action_view/helpers/asset_tag_helper.rb
@@ -1,5 +1,6 @@
require 'active_support/core_ext/array/extract_options'
require 'active_support/core_ext/hash/keys'
+require 'active_support/core_ext/regexp'
require 'action_view/helpers/asset_url_helper'
require 'action_view/helpers/tag_helper'
@@ -213,8 +214,8 @@ module ActionView
src = options[:src] = path_to_image(source)
- unless src =~ /^(?:cid|data):/ || src.blank?
- options[:alt] = options.fetch(:alt){ image_alt(src) }
+ unless src.start_with?('cid:') || src.start_with?('data:') || src.blank?
+ options[:alt] = options.fetch(:alt) { image_alt(src) }
end
options[:width], options[:height] = extract_dimensions(options.delete(:size)) if options[:size]
@@ -322,9 +323,9 @@ module ActionView
def extract_dimensions(size)
size = size.to_s
- if size =~ %r{\A\d+x\d+\z}
+ if /\A\d+x\d+\z/.match?(size)
size.split('x')
- elsif size =~ %r{\A\d+\z}
+ elsif /\A\d+\z/.match?(size)
[size, size]
end
end
diff --git a/actionview/lib/action_view/helpers/asset_url_helper.rb b/actionview/lib/action_view/helpers/asset_url_helper.rb
index 717b326740..8af01617fa 100644
--- a/actionview/lib/action_view/helpers/asset_url_helper.rb
+++ b/actionview/lib/action_view/helpers/asset_url_helper.rb
@@ -1,4 +1,5 @@
require 'zlib'
+require 'active_support/core_ext/regexp'
module ActionView
# = Action View Asset URL Helpers
@@ -131,8 +132,8 @@ module ActionView
raise ArgumentError, "nil is not a valid asset source" if source.nil?
source = source.to_s
- return "" unless source.present?
- return source if source =~ URI_REGEXP
+ return '' if source.blank?
+ return source if URI_REGEXP.match?(source)
tail, source = source[/([\?#].+)$/], source.sub(/([\?#].+)$/, ''.freeze)
@@ -213,19 +214,21 @@ module ActionView
host = options[:host]
host ||= config.asset_host if defined? config.asset_host
- if host.respond_to?(:call)
- arity = host.respond_to?(:arity) ? host.arity : host.method(:call).arity
- args = [source]
- args << request if request && (arity > 1 || arity < 0)
- host = host.call(*args)
- elsif host =~ /%d/
- host = host % (Zlib.crc32(source) % 4)
+ if host
+ if host.respond_to?(:call)
+ arity = host.respond_to?(:arity) ? host.arity : host.method(:call).arity
+ args = [source]
+ args << request if request && (arity > 1 || arity < 0)
+ host = host.call(*args)
+ elsif host.include?('%d')
+ host = host % (Zlib.crc32(source) % 4)
+ end
end
host ||= request.base_url if request && options[:protocol] == :request
return unless host
- if host =~ URI_REGEXP
+ if URI_REGEXP.match?(host)
host
else
protocol = options[:protocol] || config.default_asset_host_protocol || (request ? :request : :relative)
diff --git a/actionview/lib/action_view/helpers/cache_helper.rb b/actionview/lib/action_view/helpers/cache_helper.rb
index 4eaaa239e2..6c3092cc46 100644
--- a/actionview/lib/action_view/helpers/cache_helper.rb
+++ b/actionview/lib/action_view/helpers/cache_helper.rb
@@ -69,11 +69,11 @@ module ActionView
# render 'comments/comments'
# render('comments/comments')
#
- # render "header" => render("comments/header")
+ # render "header" translates to render("comments/header")
#
- # render(@topic) => render("topics/topic")
- # render(topics) => render("topics/topic")
- # render(message.topics) => render("topics/topic")
+ # render(@topic) translates to render("topics/topic")
+ # render(topics) translates to render("topics/topic")
+ # render(message.topics) translates to render("topics/topic")
#
# It's not possible to derive all render calls like that, though.
# Here are a few examples of things that can't be derived:
@@ -130,9 +130,10 @@ module ActionView
#
# When rendering a collection of objects that each use the same partial, a `cached`
# option can be passed.
+ #
# For collections rendered such:
#
- # <%= render partial: 'notifications/notification', collection: @notifications, cached: true %>
+ # <%= render partial: 'projects/project', collection: @projects, cached: true %>
#
# The `cached: true` will make Action View's rendering read several templates
# from cache at once instead of one call per template.
@@ -142,13 +143,21 @@ module ActionView
# Works great alongside individual template fragment caching.
# For instance if the template the collection renders is cached like:
#
- # # notifications/_notification.html.erb
- # <% cache notification do %>
+ # # projects/_project.html.erb
+ # <% cache project do %>
# <%# ... %>
# <% end %>
#
# Any collection renders will find those cached templates when attempting
# to read multiple templates at once.
+ #
+ # If your collection cache depends on multiple sources (try to avoid this to keep things simple),
+ # you can name all these dependencies as part of a block that returns an array:
+ #
+ # <%= render partial: 'projects/project', collection: @projects, cached: -> project { [ project, current_user ] } %>
+ #
+ # This will include both records as part of the cache key and updating either of them will
+ # expire the cache.
def cache(name = {}, options = {}, &block)
if controller.respond_to?(:perform_caching) && controller.perform_caching
name_options = options.slice(:skip_digest, :virtual_path)
diff --git a/actionview/lib/action_view/helpers/translation_helper.rb b/actionview/lib/action_view/helpers/translation_helper.rb
index 152e1b1211..622bb193ae 100644
--- a/actionview/lib/action_view/helpers/translation_helper.rb
+++ b/actionview/lib/action_view/helpers/translation_helper.rb
@@ -1,5 +1,6 @@
require 'action_view/helpers/tag_helper'
require 'active_support/core_ext/string/access'
+require 'active_support/core_ext/regexp'
require 'i18n/exceptions'
module ActionView
@@ -133,7 +134,7 @@ module ActionView
end
def html_safe_translation_key?(key)
- key.to_s =~ /(\b|_|\.)html$/
+ /(\b|_|\.)html$/.match?(key.to_s)
end
end
end
diff --git a/actionview/lib/action_view/helpers/url_helper.rb b/actionview/lib/action_view/helpers/url_helper.rb
index fb6426b997..5d7940a7b1 100644
--- a/actionview/lib/action_view/helpers/url_helper.rb
+++ b/actionview/lib/action_view/helpers/url_helper.rb
@@ -2,6 +2,7 @@ require 'action_view/helpers/javascript_helper'
require 'active_support/core_ext/array/access'
require 'active_support/core_ext/hash/keys'
require 'active_support/core_ext/string/output_safety'
+require 'active_support/core_ext/regexp'
module ActionView
# = Action View URL Helpers
@@ -550,7 +551,7 @@ module ActionView
url_string.chomp!("/") if url_string.start_with?("/") && url_string != "/"
- if url_string =~ /^\w+:\/\//
+ if %r{^\w+://}.match?(url_string)
url_string == "#{request.protocol}#{request.host_with_port}#{request_uri}"
else
url_string == request_uri
diff --git a/actionview/lib/action_view/layouts.rb b/actionview/lib/action_view/layouts.rb
index 8db1674187..8e956c47c6 100644
--- a/actionview/lib/action_view/layouts.rb
+++ b/actionview/lib/action_view/layouts.rb
@@ -1,5 +1,6 @@
-require "action_view/rendering"
-require "active_support/core_ext/module/remove_method"
+require 'action_view/rendering'
+require 'active_support/core_ext/module/remove_method'
+require 'active_support/core_ext/regexp'
module ActionView
# Layouts reverse the common pattern of including shared headers and footers in many templates to isolate changes in
@@ -279,7 +280,7 @@ module ActionView
def _write_layout_method # :nodoc:
remove_possible_method(:_layout)
- prefixes = _implied_layout_name =~ /\blayouts/ ? [] : ["layouts"]
+ prefixes = /\blayouts/.match?(_implied_layout_name) ? [] : ["layouts"]
default_behavior = "lookup_context.find_all('#{_implied_layout_name}', #{prefixes.inspect}, false, [], { formats: formats }).first || super"
name_clause = if name
default_behavior
diff --git a/actionview/lib/action_view/renderer/partial_renderer.rb b/actionview/lib/action_view/renderer/partial_renderer.rb
index 13b4ec6133..7c2e07185c 100644
--- a/actionview/lib/action_view/renderer/partial_renderer.rb
+++ b/actionview/lib/action_view/renderer/partial_renderer.rb
@@ -1,5 +1,6 @@
-require 'action_view/renderer/partial_renderer/collection_caching'
require 'concurrent/map'
+require 'active_support/core_ext/regexp'
+require 'action_view/renderer/partial_renderer/collection_caching'
module ActionView
class PartialIteration
@@ -386,7 +387,7 @@ module ActionView
end
if as = options[:as]
- raise_invalid_option_as(as) unless as.to_s =~ /\A[a-z_]\w*\z/
+ raise_invalid_option_as(as) unless /\A[a-z_]\w*\z/.match?(as.to_s)
as = as.to_sym
end
@@ -403,7 +404,7 @@ module ActionView
def collection_from_options
if @options.key?(:collection)
collection = @options[:collection]
- collection.respond_to?(:to_ary) ? collection.to_ary : []
+ collection ? collection.to_a : []
end
end
diff --git a/actionview/lib/action_view/renderer/partial_renderer/collection_caching.rb b/actionview/lib/action_view/renderer/partial_renderer/collection_caching.rb
index f7deba94ce..1fbe209200 100644
--- a/actionview/lib/action_view/renderer/partial_renderer/collection_caching.rb
+++ b/actionview/lib/action_view/renderer/partial_renderer/collection_caching.rb
@@ -25,9 +25,15 @@ module ActionView
end
end
+ def callable_cache_key?
+ @options[:cached].respond_to?(:call)
+ end
+
def collection_by_cache_keys
+ seed = callable_cache_key? ? @options[:cached] : ->(i) { i }
+
@collection.each_with_object({}) do |item, hash|
- hash[expanded_cache_key(item)] = item
+ hash[expanded_cache_key(seed.call(item))] = item
end
end
diff --git a/actionview/lib/action_view/renderer/template_renderer.rb b/actionview/lib/action_view/renderer/template_renderer.rb
index 1d6afb90fe..9b106cd64a 100644
--- a/actionview/lib/action_view/renderer/template_renderer.rb
+++ b/actionview/lib/action_view/renderer/template_renderer.rb
@@ -83,7 +83,7 @@ module ActionView
case layout
when String
begin
- if layout =~ /^\//
+ if layout.start_with?('/')
with_fallbacks { find_template(layout, nil, false, [], details) }
else
find_template(layout, nil, false, [], details)
diff --git a/actionview/lib/action_view/template/error.rb b/actionview/lib/action_view/template/error.rb
index 3f38c3d2b9..0f1348b032 100644
--- a/actionview/lib/action_view/template/error.rb
+++ b/actionview/lib/action_view/template/error.rb
@@ -1,4 +1,5 @@
require "active_support/core_ext/enumerable"
+require 'active_support/core_ext/regexp'
module ActionView
# = Action View Errors
@@ -35,7 +36,7 @@ module ActionView
prefixes = Array(prefixes)
template_type = if partial
"partial"
- elsif path =~ /layouts/i
+ elsif /layouts/i.match?(path)
'layout'
else
'template'
diff --git a/actionview/lib/action_view/template/handlers/erb.rb b/actionview/lib/action_view/template/handlers/erb.rb
index 85a100ed4c..058b590c56 100644
--- a/actionview/lib/action_view/template/handlers/erb.rb
+++ b/actionview/lib/action_view/template/handlers/erb.rb
@@ -1,4 +1,5 @@
require 'erubis'
+require 'active_support/core_ext/regexp'
module ActionView
class Template
@@ -39,7 +40,7 @@ module ActionView
def add_expr_literal(src, code)
flush_newline_if_pending(src)
- if code =~ BLOCK_EXPR
+ if BLOCK_EXPR.match?(code)
src << '@output_buffer.append= ' << code
else
src << '@output_buffer.append=(' << code << ');'
@@ -48,7 +49,7 @@ module ActionView
def add_expr_escaped(src, code)
flush_newline_if_pending(src)
- if code =~ BLOCK_EXPR
+ if BLOCK_EXPR.match?(code)
src << "@output_buffer.safe_expr_append= " << code
else
src << "@output_buffer.safe_expr_append=(" << code << ");"
diff --git a/actionview/lib/action_view/testing/resolvers.rb b/actionview/lib/action_view/testing/resolvers.rb
index 2664aca991..982ecf9efc 100644
--- a/actionview/lib/action_view/testing/resolvers.rb
+++ b/actionview/lib/action_view/testing/resolvers.rb
@@ -1,3 +1,4 @@
+require 'active_support/core_ext/regexp'
require 'action_view/template/resolver'
module ActionView #:nodoc:
@@ -29,7 +30,7 @@ module ActionView #:nodoc:
templates = []
@hash.each do |_path, array|
source, updated_at = array
- next unless _path =~ query
+ next unless query.match?(_path)
handler, format, variant = extract_handler_and_format_and_variant(_path, formats)
templates << Template.new(source, _path, handler,
:virtual_path => path.virtual,
@@ -50,4 +51,3 @@ module ActionView #:nodoc:
end
end
end
-
diff --git a/actionview/test/actionpack/controller/layout_test.rb b/actionview/test/actionpack/controller/layout_test.rb
index 64bc4c41d6..ecfef3dc8c 100644
--- a/actionview/test/actionpack/controller/layout_test.rb
+++ b/actionview/test/actionpack/controller/layout_test.rb
@@ -1,5 +1,6 @@
require 'abstract_unit'
require 'active_support/core_ext/array/extract_options'
+require 'active_support/core_ext/regexp'
# The view_paths array must be set on Base and not LayoutTest so that LayoutTest's inherited
# method has access to the view_paths array when looking for a layout to automatically assign.
@@ -252,7 +253,7 @@ class LayoutStatusIsRenderedTest < ActionController::TestCase
end
end
-unless RbConfig::CONFIG['host_os'] =~ /mswin|mingw/
+unless /mswin|mingw/.match?(RbConfig::CONFIG['host_os'])
class LayoutSymlinkedTest < LayoutTest
layout "symlinked/symlinked_layout"
end
diff --git a/actionview/test/template/render_test.rb b/actionview/test/template/render_test.rb
index 25b21850b1..68574d4adb 100644
--- a/actionview/test/template/render_test.rb
+++ b/actionview/test/template/render_test.rb
@@ -309,6 +309,14 @@ module RenderTestCases
assert_nil @view.render(:partial => "test/customer", :collection => nil)
end
+ def test_render_partial_collection_for_non_array
+ customers = Enumerator.new do |y|
+ y.yield(Customer.new("david"))
+ y.yield(Customer.new("mary"))
+ end
+ assert_equal "Hello: davidHello: mary", @view.render(partial: "test/customer", collection: customers)
+ end
+
def test_render_partial_without_object_does_not_put_partial_name_to_local_assigns
assert_equal 'false', @view.render(partial: 'test/partial_name_in_local_assigns')
end
diff --git a/activemodel/lib/active_model/attribute_methods.rb b/activemodel/lib/active_model/attribute_methods.rb
index cc6285f932..140eb9420e 100644
--- a/activemodel/lib/active_model/attribute_methods.rb
+++ b/activemodel/lib/active_model/attribute_methods.rb
@@ -1,5 +1,6 @@
require 'concurrent/map'
require 'mutex_m'
+require 'active_support/core_ext/regexp'
module ActiveModel
# Raised when an attribute is not defined.
@@ -366,7 +367,7 @@ module ActiveModel
# using the given `extra` args. This falls back on `define_method`
# and `send` if the given names cannot be compiled.
def define_proxy_call(include_private, mod, name, send, *extra) #:nodoc:
- defn = if name =~ NAME_COMPILABLE_REGEXP
+ defn = if NAME_COMPILABLE_REGEXP.match?(name)
"def #{name}(*args)"
else
"define_method(:'#{name}') do |*args|"
@@ -374,7 +375,7 @@ module ActiveModel
extra = (extra.map!(&:inspect) << "*args").join(", ".freeze)
- target = if send =~ CALL_COMPILABLE_REGEXP
+ target = if CALL_COMPILABLE_REGEXP.match?(send)
"#{"self." unless include_private}#{send}(#{extra})"
else
"send(:'#{send}', #{extra})"
diff --git a/activemodel/lib/active_model/type/time.rb b/activemodel/lib/active_model/type/time.rb
index 34e09f0aba..08d127d187 100644
--- a/activemodel/lib/active_model/type/time.rb
+++ b/activemodel/lib/active_model/type/time.rb
@@ -29,7 +29,7 @@ module ActiveModel
return value unless value.is_a?(::String)
return if value.empty?
- if value =~ /^2000-01-01/
+ if value.start_with?('2000-01-01')
dummy_time_value = value
else
dummy_time_value = "2000-01-01 #{value}"
diff --git a/activemodel/lib/active_model/validations/format.rb b/activemodel/lib/active_model/validations/format.rb
index 46a2e54fba..70799aaf23 100644
--- a/activemodel/lib/active_model/validations/format.rb
+++ b/activemodel/lib/active_model/validations/format.rb
@@ -1,5 +1,6 @@
-module ActiveModel
+require 'active_support/core_ext/regexp'
+module ActiveModel
module Validations
class FormatValidator < EachValidator # :nodoc:
def validate_each(record, attribute, value)
@@ -8,7 +9,7 @@ module ActiveModel
record_error(record, attribute, :with, value) if value.to_s !~ regexp
elsif options[:without]
regexp = option_call(record, :without)
- record_error(record, attribute, :without, value) if value.to_s =~ regexp
+ record_error(record, attribute, :without, value) if regexp.match?(value.to_s)
end
end
diff --git a/activemodel/test/validators/email_validator.rb b/activemodel/test/validators/email_validator.rb
index cff47ac230..5dc1fc5ae2 100644
--- a/activemodel/test/validators/email_validator.rb
+++ b/activemodel/test/validators/email_validator.rb
@@ -1,6 +1,8 @@
+require 'active_support/core_ext/regexp'
+
class EmailValidator < ActiveModel::EachValidator
def validate_each(record, attribute, value)
record.errors[attribute] << (options[:message] || "is not an email") unless
- value =~ /^([^@\s]+)@((?:[-a-z0-9]+\.)+[a-z]{2,})$/i
+ /\A([^@\s]+)@((?:[-a-z0-9]+\.)+[a-z]{2,})\z/i.match?(value)
end
-end \ No newline at end of file
+end
diff --git a/activerecord/CHANGELOG.md b/activerecord/CHANGELOG.md
index 590fe2f587..d3e1cf68a2 100644
--- a/activerecord/CHANGELOG.md
+++ b/activerecord/CHANGELOG.md
@@ -1,3 +1,8 @@
+* Virtual attributes will no longer raise when read on models loaded from the
+ database
+
+ *Sean Griffin*
+
* Support calling the method `merge` in `scope`'s lambda.
*Yasuhiro Sugino*
diff --git a/activerecord/Rakefile b/activerecord/Rakefile
index 012db35765..8fbea61335 100644
--- a/activerecord/Rakefile
+++ b/activerecord/Rakefile
@@ -51,7 +51,7 @@ end
adapter_short = adapter == 'db2' ? adapter : adapter[/^[a-z0-9]+/]
t.libs << 'test'
t.test_files = (Dir.glob( "test/cases/**/*_test.rb" ).reject {
- |x| x =~ /\/adapters\//
+ |x| x.include?('/adapters/')
} + Dir.glob("test/cases/adapters/#{adapter_short}/**/*_test.rb"))
t.warning = true
@@ -64,7 +64,7 @@ end
adapter_short = adapter == 'db2' ? adapter : adapter[/^[a-z0-9]+/]
puts [adapter, adapter_short].inspect
(Dir["test/cases/**/*_test.rb"].reject {
- |x| x =~ /\/adapters\//
+ |x| x.include?('/adapters/')
} + Dir["test/cases/adapters/#{adapter_short}/**/*_test.rb"]).all? do |file|
sh(Gem.ruby, '-w' ,"-Itest", file)
end or raise "Failures"
diff --git a/activerecord/lib/active_record/associations/has_one_through_association.rb b/activerecord/lib/active_record/associations/has_one_through_association.rb
index 08e0ec691f..604904abcc 100644
--- a/activerecord/lib/active_record/associations/has_one_through_association.rb
+++ b/activerecord/lib/active_record/associations/has_one_through_association.rb
@@ -15,7 +15,7 @@ module ActiveRecord
ensure_not_nested
through_proxy = owner.association(through_reflection.name)
- through_record = through_proxy.send(:load_target)
+ through_record = through_proxy.load_target
if through_record && !record
through_record.destroy
diff --git a/activerecord/lib/active_record/attribute.rb b/activerecord/lib/active_record/attribute.rb
index 9530f134d0..2f41e9e45b 100644
--- a/activerecord/lib/active_record/attribute.rb
+++ b/activerecord/lib/active_record/attribute.rb
@@ -152,7 +152,7 @@ module ActiveRecord
end
def _original_value_for_database
- value_for_database
+ type.serialize(original_value)
end
class FromDatabase < Attribute # :nodoc:
@@ -180,7 +180,7 @@ module ActiveRecord
value
end
- def changed_in_place_from?(old_value)
+ def changed_in_place?
false
end
end
diff --git a/activerecord/lib/active_record/attribute_set.rb b/activerecord/lib/active_record/attribute_set.rb
index 720d5f8b7c..f868f23845 100644
--- a/activerecord/lib/active_record/attribute_set.rb
+++ b/activerecord/lib/active_record/attribute_set.rb
@@ -3,7 +3,7 @@ require 'active_record/attribute_set/yaml_encoder'
module ActiveRecord
class AttributeSet # :nodoc:
- delegate :each_value, to: :attributes
+ delegate :each_value, :fetch, to: :attributes
def initialize(attributes)
@attributes = attributes
diff --git a/activerecord/lib/active_record/attribute_set/builder.rb b/activerecord/lib/active_record/attribute_set/builder.rb
index 24a255efc1..4ffd39d82d 100644
--- a/activerecord/lib/active_record/attribute_set/builder.rb
+++ b/activerecord/lib/active_record/attribute_set/builder.rb
@@ -3,11 +3,12 @@ require 'active_record/attribute'
module ActiveRecord
class AttributeSet # :nodoc:
class Builder # :nodoc:
- attr_reader :types, :always_initialized
+ attr_reader :types, :always_initialized, :default
- def initialize(types, always_initialized = nil)
+ def initialize(types, always_initialized = nil, &default)
@types = types
@always_initialized = always_initialized
+ @default = default
end
def build_from_database(values = {}, additional_types = {})
@@ -15,21 +16,22 @@ module ActiveRecord
values[always_initialized] = nil
end
- attributes = LazyAttributeHash.new(types, values, additional_types)
+ attributes = LazyAttributeHash.new(types, values, additional_types, &default)
AttributeSet.new(attributes)
end
end
end
class LazyAttributeHash # :nodoc:
- delegate :transform_values, :each_key, :each_value, to: :materialize
+ delegate :transform_values, :each_key, :each_value, :fetch, to: :materialize
- def initialize(types, values, additional_types)
+ def initialize(types, values, additional_types, &default)
@types = types
@values = values
@additional_types = additional_types
@materialized = false
@delegate_hash = {}
+ @default = default || proc {}
end
def key?(key)
@@ -76,9 +78,21 @@ module ActiveRecord
end
end
+ def marshal_dump
+ materialize
+ end
+
+ def marshal_load(delegate_hash)
+ @delegate_hash = delegate_hash
+ @types = {}
+ @values = {}
+ @additional_types = {}
+ @materialized = true
+ end
+
protected
- attr_reader :types, :values, :additional_types, :delegate_hash
+ attr_reader :types, :values, :additional_types, :delegate_hash, :default
def materialize
unless @materialized
@@ -101,7 +115,7 @@ module ActiveRecord
if value_present
delegate_hash[name] = Attribute.from_database(name, value, type)
elsif types.key?(name)
- delegate_hash[name] = Attribute.uninitialized(name, type)
+ delegate_hash[name] = default.call(name) || Attribute.uninitialized(name, type)
end
end
end
diff --git a/activerecord/lib/active_record/attributes.rb b/activerecord/lib/active_record/attributes.rb
index 3211b6eaeb..ed0302e763 100644
--- a/activerecord/lib/active_record/attributes.rb
+++ b/activerecord/lib/active_record/attributes.rb
@@ -253,7 +253,7 @@ module ActiveRecord
name,
value,
type,
- _default_attributes[name],
+ _default_attributes.fetch(name.to_s) { nil },
)
else
default_attribute = Attribute.from_database(name, value, type)
diff --git a/activerecord/lib/active_record/coders/yaml_column.rb b/activerecord/lib/active_record/coders/yaml_column.rb
index 2456b8ad8c..5ac1e0c001 100644
--- a/activerecord/lib/active_record/coders/yaml_column.rb
+++ b/activerecord/lib/active_record/coders/yaml_column.rb
@@ -1,4 +1,5 @@
require 'yaml'
+require 'active_support/core_ext/regexp'
module ActiveRecord
module Coders # :nodoc:
@@ -20,7 +21,7 @@ module ActiveRecord
def load(yaml)
return object_class.new if object_class != Object && yaml.nil?
- return yaml unless yaml.is_a?(String) && yaml =~ /^---/
+ return yaml unless yaml.is_a?(String) && /^---/.match?(yaml)
obj = YAML.load(yaml)
assert_valid_value(obj)
diff --git a/activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb b/activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb
index 74aae3a1e4..621f737a5e 100644
--- a/activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb
+++ b/activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb
@@ -18,11 +18,12 @@ module ActiveRecord
# This is used in the StatementCache object. It returns an object that
# can be used to query the database repeatedly.
- def cacheable_query(arel) # :nodoc:
+ def cacheable_query(klass, arel) # :nodoc:
+ collected = visitor.accept(arel.ast, collector)
if prepared_statements
- ActiveRecord::StatementCache.query visitor, arel.ast
+ klass.query(collected.value)
else
- ActiveRecord::StatementCache.partial_query visitor, arel.ast, collector
+ klass.partial_query(collected.value)
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 860ef17dca..0a58921549 100644
--- a/activerecord/lib/active_record/connection_adapters/abstract/quoting.rb
+++ b/activerecord/lib/active_record/connection_adapters/abstract/quoting.rb
@@ -112,19 +112,19 @@ module ActiveRecord
end
def quoted_true
- "'t'"
+ "'t'".freeze
end
def unquoted_true
- 't'
+ 't'.freeze
end
def quoted_false
- "'f'"
+ "'f'".freeze
end
def unquoted_false
- 'f'
+ 'f'.freeze
end
# Quote date/time values for use in SQL input. Includes microseconds
@@ -156,6 +156,10 @@ module ActiveRecord
private
+ def type_casted_binds(binds)
+ binds.map { |attr| type_cast(attr.value_for_database) }
+ end
+
def types_which_need_no_typecasting
[nil, Numeric, String]
end
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 3e77b92141..25e3939b62 100644
--- a/activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb
+++ b/activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb
@@ -9,6 +9,7 @@ require 'active_record/connection_adapters/mysql/schema_dumper'
require 'active_record/connection_adapters/mysql/type_metadata'
require 'active_support/core_ext/string/strip'
+require 'active_support/core_ext/regexp'
module ActiveRecord
module ConnectionAdapters
@@ -85,7 +86,7 @@ module ActiveRecord
end
def mariadb? # :nodoc:
- full_version =~ /mariadb/i
+ /mariadb/i.match?(full_version)
end
# Returns true, since this connection adapter supports migrations.
diff --git a/activerecord/lib/active_record/connection_adapters/mysql/database_statements.rb b/activerecord/lib/active_record/connection_adapters/mysql/database_statements.rb
index cc19d95669..6d1215df2a 100644
--- a/activerecord/lib/active_record/connection_adapters/mysql/database_statements.rb
+++ b/activerecord/lib/active_record/connection_adapters/mysql/database_statements.rb
@@ -77,7 +77,7 @@ module ActiveRecord
@connection.query_options[:database_timezone] = ActiveRecord::Base.default_timezone
end
- type_casted_binds = binds.map { |attr| type_cast(attr.value_for_database) }
+ type_casted_binds = type_casted_binds(binds)
log(sql, name, binds, type_casted_binds) do
if cache_stmt
diff --git a/activerecord/lib/active_record/connection_adapters/mysql/quoting.rb b/activerecord/lib/active_record/connection_adapters/mysql/quoting.rb
index fbab654112..5b59e39d9f 100644
--- a/activerecord/lib/active_record/connection_adapters/mysql/quoting.rb
+++ b/activerecord/lib/active_record/connection_adapters/mysql/quoting.rb
@@ -2,7 +2,7 @@ module ActiveRecord
module ConnectionAdapters
module MySQL
module Quoting # :nodoc:
- QUOTED_TRUE, QUOTED_FALSE = '1', '0'
+ QUOTED_TRUE, QUOTED_FALSE = '1'.freeze, '0'.freeze
def quote_column_name(name)
@quoted_column_names[name] ||= "`#{super.gsub('`', '``')}`"
diff --git a/activerecord/lib/active_record/connection_adapters/postgresql/quoting.rb b/activerecord/lib/active_record/connection_adapters/postgresql/quoting.rb
index 6414459cd1..80c219dfd7 100644
--- a/activerecord/lib/active_record/connection_adapters/postgresql/quoting.rb
+++ b/activerecord/lib/active_record/connection_adapters/postgresql/quoting.rb
@@ -58,7 +58,7 @@ module ActiveRecord
def quote_default_expression(value, column) # :nodoc:
if value.is_a?(Proc)
value.call
- elsif column.type == :uuid && value =~ /\(\)/
+ elsif column.type == :uuid && value.include?('()')
value # Does not quote function default values for UUID columns
elsif column.respond_to?(:array?)
value = type_cast_from_column(column, value)
diff --git a/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb b/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb
index 487165d511..61a980fda9 100644
--- a/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb
+++ b/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb
@@ -597,13 +597,13 @@ module ActiveRecord
end
def exec_no_cache(sql, name, binds)
- type_casted_binds = binds.map { |attr| type_cast(attr.value_for_database) }
+ type_casted_binds = type_casted_binds(binds)
log(sql, name, binds, type_casted_binds) { @connection.async_exec(sql, type_casted_binds) }
end
def exec_cache(sql, name, binds)
stmt_key = prepare_statement(sql)
- type_casted_binds = binds.map { |attr| type_cast(attr.value_for_database) }
+ type_casted_binds = type_casted_binds(binds)
log(sql, name, binds, type_casted_binds, stmt_key) do
@connection.exec_prepared(stmt_key, type_casted_binds)
diff --git a/activerecord/lib/active_record/connection_adapters/sqlite3_adapter.rb b/activerecord/lib/active_record/connection_adapters/sqlite3_adapter.rb
index 7e23f44ddf..41ed784d2e 100644
--- a/activerecord/lib/active_record/connection_adapters/sqlite3_adapter.rb
+++ b/activerecord/lib/active_record/connection_adapters/sqlite3_adapter.rb
@@ -188,7 +188,7 @@ module ActiveRecord
end
def exec_query(sql, name = nil, binds = [], prepare: false)
- type_casted_binds = binds.map { |attr| type_cast(attr.value_for_database) }
+ type_casted_binds = type_casted_binds(binds)
log(sql, name, binds, type_casted_binds) do
# Don't cache statements if they are not prepared
@@ -203,7 +203,6 @@ module ActiveRecord
ensure
stmt.close
end
- stmt = records
else
cache = @statements[sql] ||= {
:stmt => @connection.prepare(sql)
@@ -212,9 +211,10 @@ module ActiveRecord
cols = cache[:cols] ||= stmt.columns
stmt.reset!
stmt.bind_params(type_casted_binds)
+ records = stmt.to_a
end
- ActiveRecord::Result.new(cols, stmt.to_a)
+ ActiveRecord::Result.new(cols, records)
end
end
@@ -548,7 +548,7 @@ module ActiveRecord
columns_string.split(',').each do |column_string|
# This regex will match the column name and collation type and will save
# the value in $1 and $2 respectively.
- collation_hash[$1] = $2 if (COLLATE_REGEX =~ column_string)
+ collation_hash[$1] = $2 if COLLATE_REGEX =~ column_string
end
basic_structure.map! do |column|
diff --git a/activerecord/lib/active_record/dynamic_matchers.rb b/activerecord/lib/active_record/dynamic_matchers.rb
index b6dd6814db..0bdebb3989 100644
--- a/activerecord/lib/active_record/dynamic_matchers.rb
+++ b/activerecord/lib/active_record/dynamic_matchers.rb
@@ -1,3 +1,5 @@
+require 'active_support/core_ext/regexp'
+
module ActiveRecord
module DynamicMatchers #:nodoc:
def respond_to?(name, include_private = false)
@@ -29,7 +31,7 @@ module ActiveRecord
attr_reader :matchers
def match(model, name)
- klass = matchers.find { |k| name =~ k.pattern }
+ klass = matchers.find { |k| k.pattern.match?(name) }
klass.new(model, name) if klass
end
diff --git a/activerecord/lib/active_record/migration.rb b/activerecord/lib/active_record/migration.rb
index 81fe053fe1..1bb4688717 100644
--- a/activerecord/lib/active_record/migration.rb
+++ b/activerecord/lib/active_record/migration.rb
@@ -1,5 +1,6 @@
-require "active_support/core_ext/module/attribute_accessors"
require 'set'
+require "active_support/core_ext/module/attribute_accessors"
+require 'active_support/core_ext/regexp'
module ActiveRecord
class MigrationError < ActiveRecordError#:nodoc:
@@ -126,9 +127,9 @@ module ActiveRecord
class PendingMigrationError < MigrationError#:nodoc:
def initialize(message = nil)
if !message && defined?(Rails.env)
- super("Migrations are pending. To resolve this issue, run:\n\n\tbin/rails db:migrate RAILS_ENV=#{::Rails.env}")
+ super("Migrations are pending. To resolve this issue, run:\n\n bin/rails db:migrate RAILS_ENV=#{::Rails.env}")
elsif !message
- super("Migrations are pending. To resolve this issue, run:\n\n\tbin/rails db:migrate")
+ super("Migrations are pending. To resolve this issue, run:\n\n bin/rails db:migrate")
else
super
end
@@ -145,7 +146,7 @@ module ActiveRecord
class NoEnvironmentInSchemaError < MigrationError #:nodoc:
def initialize
- msg = "Environment data not found in the schema. To resolve this issue, run: \n\n\tbin/rails db:environment:set"
+ msg = "Environment data not found in the schema. To resolve this issue, run: \n\n bin/rails db:environment:set"
if defined?(Rails.env)
super("#{msg} RAILS_ENV=#{::Rails.env}")
else
@@ -168,7 +169,7 @@ module ActiveRecord
msg = "You are attempting to modify a database that was last run in `#{ stored }` environment.\n"
msg << "You are running in `#{ current }` environment. "
msg << "If you are sure you want to continue, first set the environment using:\n\n"
- msg << "\tbin/rails db:environment:set"
+ msg << " bin/rails db:environment:set"
if defined?(Rails.env)
super("#{msg} RAILS_ENV=#{::Rails.env}\n\n")
else
@@ -1057,7 +1058,7 @@ module ActiveRecord
end
def match_to_migration_filename?(filename) # :nodoc:
- File.basename(filename) =~ Migration::MigrationFilenameRegexp
+ Migration::MigrationFilenameRegexp.match?(File.basename(filename))
end
def parse_migration_filename(filename) # :nodoc:
diff --git a/activerecord/lib/active_record/model_schema.rb b/activerecord/lib/active_record/model_schema.rb
index 114686c5d3..41cebb0e34 100644
--- a/activerecord/lib/active_record/model_schema.rb
+++ b/activerecord/lib/active_record/model_schema.rb
@@ -249,7 +249,11 @@ module ActiveRecord
end
def attributes_builder # :nodoc:
- @attributes_builder ||= AttributeSet::Builder.new(attribute_types, primary_key)
+ @attributes_builder ||= AttributeSet::Builder.new(attribute_types, primary_key) do |name|
+ unless columns_hash.key?(name)
+ _default_attributes[name].dup
+ end
+ end
end
def columns_hash # :nodoc:
@@ -309,7 +313,12 @@ module ActiveRecord
# Returns an array of column objects where the primary id, all columns ending in "_id" or "_count",
# and columns used for single table inheritance have been removed.
def content_columns
- @content_columns ||= columns.reject { |c| c.name == primary_key || c.name =~ /(_id|_count)$/ || c.name == inheritance_column }
+ @content_columns ||= columns.reject do |c|
+ c.name == primary_key ||
+ c.name == inheritance_column ||
+ c.name.end_with?('_id') ||
+ c.name.end_with?('_count')
+ end
end
# Resets all the cached information about columns, which will cause them
diff --git a/activerecord/lib/active_record/relation/delegation.rb b/activerecord/lib/active_record/relation/delegation.rb
index 2484cb3264..ad74659cba 100644
--- a/activerecord/lib/active_record/relation/delegation.rb
+++ b/activerecord/lib/active_record/relation/delegation.rb
@@ -1,4 +1,5 @@
require 'active_support/concern'
+require 'active_support/core_ext/regexp'
module ActiveRecord
module Delegation # :nodoc:
@@ -58,7 +59,7 @@ module ActiveRecord
@delegation_mutex.synchronize do
return if method_defined?(method)
- if method.to_s =~ /\A[a-zA-Z_]\w*[!?]?\z/
+ if /\A[a-zA-Z_]\w*[!?]?\z/.match?(method)
module_eval <<-RUBY, __FILE__, __LINE__ + 1
def #{method}(*args, &block)
scoping { @klass.#{method}(*args, &block) }
diff --git a/activerecord/lib/active_record/relation/query_methods.rb b/activerecord/lib/active_record/relation/query_methods.rb
index 8a87015e44..0749bb30b5 100644
--- a/activerecord/lib/active_record/relation/query_methods.rb
+++ b/activerecord/lib/active_record/relation/query_methods.rb
@@ -4,6 +4,7 @@ require "active_record/relation/where_clause"
require "active_record/relation/where_clause_factory"
require 'active_model/forbidden_attributes_protection'
require 'active_support/core_ext/string/filters'
+require 'active_support/core_ext/regexp'
module ActiveRecord
module QueryMethods
@@ -1135,10 +1136,10 @@ module ActiveRecord
end
def does_not_support_reverse?(order)
- #uses sql function with multiple arguments
- order =~ /\([^()]*,[^()]*\)/ ||
- # uses "nulls first" like construction
- order =~ /nulls (first|last)\Z/i
+ # Uses SQL function with multiple arguments.
+ /\([^()]*,[^()]*\)/.match?(order) ||
+ # Uses "nulls first" like construction.
+ /nulls (first|last)\Z/i.match?(order)
end
def build_order(arel)
diff --git a/activerecord/lib/active_record/sanitization.rb b/activerecord/lib/active_record/sanitization.rb
index a9e1fd0dad..f007e9e733 100644
--- a/activerecord/lib/active_record/sanitization.rb
+++ b/activerecord/lib/active_record/sanitization.rb
@@ -1,3 +1,5 @@
+require 'active_support/core_ext/regexp'
+
module ActiveRecord
module Sanitization
extend ActiveSupport::Concern
@@ -153,7 +155,7 @@ module ActiveRecord
# # => "name='foo''bar' and group_id='4'"
def sanitize_sql_array(ary)
statement, *values = ary
- if values.first.is_a?(Hash) && statement =~ /:\w+/
+ if values.first.is_a?(Hash) && /:\w+/.match?(statement)
replace_named_bind_variables(statement, values.first)
elsif statement.include?('?')
replace_bind_variables(statement, values)
diff --git a/activerecord/lib/active_record/statement_cache.rb b/activerecord/lib/active_record/statement_cache.rb
index 6c896ccea6..5607be6d12 100644
--- a/activerecord/lib/active_record/statement_cache.rb
+++ b/activerecord/lib/active_record/statement_cache.rb
@@ -40,7 +40,7 @@ module ActiveRecord
end
class PartialQuery < Query # :nodoc:
- def initialize values
+ def initialize(values)
@values = values
@indexes = values.each_with_index.find_all { |thing,i|
Arel::Nodes::BindParam === thing
@@ -55,13 +55,12 @@ module ActiveRecord
end
end
- def self.query(visitor, ast)
- Query.new visitor.accept(ast, Arel::Collectors::SQLString.new).value
+ def self.query(sql)
+ Query.new(sql)
end
- def self.partial_query(visitor, ast, collector)
- collected = visitor.accept(ast, collector).value
- PartialQuery.new collected
+ def self.partial_query(values)
+ PartialQuery.new(values)
end
class Params # :nodoc:
@@ -92,7 +91,7 @@ module ActiveRecord
def self.create(connection, block = Proc.new)
relation = block.call Params.new
bind_map = BindMap.new relation.bound_attributes
- query_builder = connection.cacheable_query relation.arel
+ query_builder = connection.cacheable_query(self, relation.arel)
new query_builder, bind_map
end
diff --git a/activerecord/lib/active_record/tasks/mysql_database_tasks.rb b/activerecord/lib/active_record/tasks/mysql_database_tasks.rb
index b1a0ad0115..8574807fd2 100644
--- a/activerecord/lib/active_record/tasks/mysql_database_tasks.rb
+++ b/activerecord/lib/active_record/tasks/mysql_database_tasks.rb
@@ -90,7 +90,7 @@ module ActiveRecord
end
def error_class
- if configuration['adapter'] =~ /jdbc/
+ if configuration['adapter'].include?('jdbc')
require 'active_record/railties/jdbcmysql_error'
ArJdbcMySQL::Error
elsif defined?(Mysql2)
diff --git a/activerecord/lib/rails/generators/active_record/migration/migration_generator.rb b/activerecord/lib/rails/generators/active_record/migration/migration_generator.rb
index 4e5872b585..de03550ec2 100644
--- a/activerecord/lib/rails/generators/active_record/migration/migration_generator.rb
+++ b/activerecord/lib/rails/generators/active_record/migration/migration_generator.rb
@@ -1,4 +1,5 @@
require 'rails/generators/active_record'
+require 'active_support/core_ext/regexp'
module ActiveRecord
module Generators # :nodoc:
@@ -60,7 +61,7 @@ module ActiveRecord
# A migration file name can only contain underscores (_), lowercase characters,
# and numbers 0-9. Any other file name will raise an IllegalMigrationNameError.
def validate_file_name!
- unless file_name =~ /^[_a-z0-9]+$/
+ unless /^[_a-z0-9]+$/.match?(file_name)
raise IllegalMigrationNameError.new(file_name)
end
end
diff --git a/activerecord/test/cases/associations/has_many_associations_test.rb b/activerecord/test/cases/associations/has_many_associations_test.rb
index 325a06d724..113131b28c 100644
--- a/activerecord/test/cases/associations/has_many_associations_test.rb
+++ b/activerecord/test/cases/associations/has_many_associations_test.rb
@@ -459,10 +459,6 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
assert_predicate person.references, :exists?
end
- def force_signal37_to_load_all_clients_of_firm
- companies(:first_firm).clients_of_firm.each {|f| }
- end
-
# sometimes tests on Oracle fail if ORDER BY is not provided therefore add always :order with :first
def test_counting_with_counter_sql
assert_equal 3, Firm.all.merge!(:order => "id").first.clients.count
@@ -625,6 +621,8 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
def test_find_ids_and_inverse_of
force_signal37_to_load_all_clients_of_firm
+ assert_predicate companies(:first_firm).clients_of_firm, :loaded?
+
firm = companies(:first_firm)
client = firm.clients_of_firm.find(3)
assert_kind_of Client, client
@@ -749,6 +747,9 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
def test_adding
force_signal37_to_load_all_clients_of_firm
+
+ assert_predicate companies(:first_firm).clients_of_firm, :loaded?
+
natural = Client.new("name" => "Natural Company")
companies(:first_firm).clients_of_firm << natural
assert_equal 3, companies(:first_firm).clients_of_firm.size # checking via the collection
@@ -805,6 +806,9 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
def test_adding_a_collection
force_signal37_to_load_all_clients_of_firm
+
+ assert_predicate companies(:first_firm).clients_of_firm, :loaded?
+
companies(:first_firm).clients_of_firm.concat([Client.new("name" => "Natural Company"), Client.new("name" => "Apple")])
assert_equal 4, companies(:first_firm).clients_of_firm.size
assert_equal 4, companies(:first_firm).clients_of_firm.reload.size
@@ -948,6 +952,9 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
def test_create
force_signal37_to_load_all_clients_of_firm
+
+ assert_predicate companies(:first_firm).clients_of_firm, :loaded?
+
new_client = companies(:first_firm).clients_of_firm.create("name" => "Another Client")
assert new_client.persisted?
assert_equal new_client, companies(:first_firm).clients_of_firm.last
@@ -967,6 +974,9 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
def test_deleting
force_signal37_to_load_all_clients_of_firm
+
+ assert_predicate companies(:first_firm).clients_of_firm, :loaded?
+
companies(:first_firm).clients_of_firm.delete(companies(:first_firm).clients_of_firm.first)
assert_equal 1, companies(:first_firm).clients_of_firm.size
assert_equal 1, companies(:first_firm).clients_of_firm.reload.size
@@ -1121,6 +1131,9 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
def test_deleting_a_collection
force_signal37_to_load_all_clients_of_firm
+
+ assert_predicate companies(:first_firm).clients_of_firm, :loaded?
+
companies(:first_firm).clients_of_firm.create("name" => "Another Client")
assert_equal 3, companies(:first_firm).clients_of_firm.size
companies(:first_firm).clients_of_firm.delete([companies(:first_firm).clients_of_firm[0], companies(:first_firm).clients_of_firm[1], companies(:first_firm).clients_of_firm[2]])
@@ -1130,6 +1143,9 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
def test_delete_all
force_signal37_to_load_all_clients_of_firm
+
+ assert_predicate companies(:first_firm).clients_of_firm, :loaded?
+
companies(:first_firm).dependent_clients_of_firm.create("name" => "Another Client")
clients = companies(:first_firm).dependent_clients_of_firm.to_a
assert_equal 3, clients.count
@@ -1141,6 +1157,9 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
def test_delete_all_with_not_yet_loaded_association_collection
force_signal37_to_load_all_clients_of_firm
+
+ assert_predicate companies(:first_firm).clients_of_firm, :loaded?
+
companies(:first_firm).clients_of_firm.create("name" => "Another Client")
assert_equal 3, companies(:first_firm).clients_of_firm.size
companies(:first_firm).clients_of_firm.reset
@@ -1329,6 +1348,9 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
def test_deleting_a_item_which_is_not_in_the_collection
force_signal37_to_load_all_clients_of_firm
+
+ assert_predicate companies(:first_firm).clients_of_firm, :loaded?
+
summit = Client.find_by_name('Summit')
companies(:first_firm).clients_of_firm.delete(summit)
assert_equal 2, companies(:first_firm).clients_of_firm.size
@@ -1365,6 +1387,8 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
def test_destroying
force_signal37_to_load_all_clients_of_firm
+ assert_predicate companies(:first_firm).clients_of_firm, :loaded?
+
assert_difference "Client.count", -1 do
companies(:first_firm).clients_of_firm.destroy(companies(:first_firm).clients_of_firm.first)
end
@@ -1376,6 +1400,8 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
def test_destroying_by_integer_id
force_signal37_to_load_all_clients_of_firm
+ assert_predicate companies(:first_firm).clients_of_firm, :loaded?
+
assert_difference "Client.count", -1 do
companies(:first_firm).clients_of_firm.destroy(companies(:first_firm).clients_of_firm.first.id)
end
@@ -1387,6 +1413,8 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
def test_destroying_by_string_id
force_signal37_to_load_all_clients_of_firm
+ assert_predicate companies(:first_firm).clients_of_firm, :loaded?
+
assert_difference "Client.count", -1 do
companies(:first_firm).clients_of_firm.destroy(companies(:first_firm).clients_of_firm.first.id.to_s)
end
@@ -1397,6 +1425,9 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
def test_destroying_a_collection
force_signal37_to_load_all_clients_of_firm
+
+ assert_predicate companies(:first_firm).clients_of_firm, :loaded?
+
companies(:first_firm).clients_of_firm.create("name" => "Another Client")
assert_equal 3, companies(:first_firm).clients_of_firm.size
@@ -1410,6 +1441,9 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
def test_destroy_all
force_signal37_to_load_all_clients_of_firm
+
+ assert_predicate companies(:first_firm).clients_of_firm, :loaded?
+
clients = companies(:first_firm).clients_of_firm.to_a
assert !clients.empty?, "37signals has clients after load"
destroyed = companies(:first_firm).clients_of_firm.destroy_all
@@ -2428,4 +2462,10 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
assert_equal [bulb.id], car.bulb_ids
assert_no_queries { car.bulb_ids }
end
+
+ private
+
+ def force_signal37_to_load_all_clients_of_firm
+ companies(:first_firm).clients_of_firm.load_target
+ end
end
diff --git a/activerecord/test/cases/associations/inner_join_association_test.rb b/activerecord/test/cases/associations/inner_join_association_test.rb
index b3fe759ad9..a2158e4f3b 100644
--- a/activerecord/test/cases/associations/inner_join_association_test.rb
+++ b/activerecord/test/cases/associations/inner_join_association_test.rb
@@ -86,7 +86,7 @@ class InnerJoinAssociationTest < ActiveRecord::TestCase
end
def test_calculate_honors_implicit_inner_joins_and_distinct_and_conditions
- real_count = Author.all.to_a.select {|a| a.posts.any? {|p| p.title =~ /^Welcome/} }.length
+ real_count = Author.all.to_a.select {|a| a.posts.any? {|p| p.title.start_with?('Welcome')} }.length
authors_with_welcoming_post_titles = Author.all.merge!(joins: :posts, where: "posts.title like 'Welcome%'").distinct.calculate(:count, 'authors.id')
assert_equal real_count, authors_with_welcoming_post_titles, "inner join and conditions should have only returned authors posting titles starting with 'Welcome'"
end
diff --git a/activerecord/test/cases/associations/left_outer_join_association_test.rb b/activerecord/test/cases/associations/left_outer_join_association_test.rb
index eee135cfb8..e3b257efb2 100644
--- a/activerecord/test/cases/associations/left_outer_join_association_test.rb
+++ b/activerecord/test/cases/associations/left_outer_join_association_test.rb
@@ -5,6 +5,7 @@ require 'models/author'
require 'models/essay'
require 'models/categorization'
require 'models/person'
+require 'active_support/core_ext/regexp'
class LeftOuterJoinAssociationTest < ActiveRecord::TestCase
fixtures :authors, :essays, :posts, :comments, :categorizations, :people
@@ -20,7 +21,7 @@ class LeftOuterJoinAssociationTest < ActiveRecord::TestCase
Person.left_outer_joins(:agents => {:agents => :agents})
.left_outer_joins(:agents => {:agents => {:primary_contact => :agents}}).to_a
end
- assert queries.any? { |sql| /agents_people_4/i =~ sql }
+ assert queries.any? { |sql| /agents_people_4/i.match?(sql) }
end
end
@@ -36,12 +37,12 @@ class LeftOuterJoinAssociationTest < ActiveRecord::TestCase
def test_construct_finder_sql_ignores_empty_left_outer_joins_hash
queries = capture_sql { Author.left_outer_joins({}) }
- assert queries.none? { |sql| /LEFT OUTER JOIN/i =~ sql }
+ assert queries.none? { |sql| /LEFT OUTER JOIN/i.match?(sql) }
end
def test_construct_finder_sql_ignores_empty_left_outer_joins_array
queries = capture_sql { Author.left_outer_joins([]) }
- assert queries.none? { |sql| /LEFT OUTER JOIN/i =~ sql }
+ assert queries.none? { |sql| /LEFT OUTER JOIN/i.match?(sql) }
end
def test_left_outer_joins_forbids_to_use_string_as_argument
@@ -50,8 +51,8 @@ class LeftOuterJoinAssociationTest < ActiveRecord::TestCase
def test_join_conditions_added_to_join_clause
queries = capture_sql { Author.left_outer_joins(:essays).to_a }
- assert queries.any? { |sql| /writer_type.*?=.*?(Author|\?|\$1|\:a1)/i =~ sql }
- assert queries.none? { |sql| /WHERE/i =~ sql }
+ assert queries.any? { |sql| /writer_type.*?=.*?(Author|\?|\$1|\:a1)/i.match?(sql) }
+ assert queries.none? { |sql| /WHERE/i.match?(sql) }
end
def test_find_with_sti_join
diff --git a/activerecord/test/cases/associations_test.rb b/activerecord/test/cases/associations_test.rb
index 01a058918a..5fb7f191ed 100644
--- a/activerecord/test/cases/associations_test.rb
+++ b/activerecord/test/cases/associations_test.rb
@@ -45,7 +45,6 @@ class AssociationsTest < ActiveRecord::TestCase
ship = Ship.create!(:name => "The good ship Dollypop")
part = ship.parts.create!(:name => "Mast")
part.mark_for_destruction
- ship.parts.send(:load_target)
assert ship.parts[0].marked_for_destruction?
end
@@ -54,7 +53,6 @@ class AssociationsTest < ActiveRecord::TestCase
part = ship.parts.create!(:name => "Mast")
part.mark_for_destruction
ShipPart.find(part.id).update_columns(name: 'Deck')
- ship.parts.send(:load_target)
assert_equal 'Deck', ship.parts[0].name
end
diff --git a/activerecord/test/cases/attribute_methods_test.rb b/activerecord/test/cases/attribute_methods_test.rb
index 04126e87e4..9d66c9964c 100644
--- a/activerecord/test/cases/attribute_methods_test.rb
+++ b/activerecord/test/cases/attribute_methods_test.rb
@@ -27,34 +27,34 @@ class AttributeMethodsTest < ActiveRecord::TestCase
ActiveRecord::Base.send(:attribute_method_matchers).concat(@old_matchers)
end
- def test_attribute_for_inspect_string
+ test 'attribute_for_inspect with a string' do
t = topics(:first)
t.title = "The First Topic Now Has A Title With\nNewlines And More Than 50 Characters"
assert_equal '"The First Topic Now Has A Title With\nNewlines And ..."', t.attribute_for_inspect(:title)
end
- def test_attribute_for_inspect_date
+ test 'attribute_for_inspect with a date' do
t = topics(:first)
assert_equal %("#{t.written_on.to_s(:db)}"), t.attribute_for_inspect(:written_on)
end
- def test_attribute_for_inspect_array
+ test 'attribute_for_inspect with an array' do
t = topics(:first)
t.content = [Object.new]
assert_match %r(\[#<Object:0x[0-9a-f]+>\]), t.attribute_for_inspect(:content)
end
- def test_attribute_for_inspect_long_array
+ test 'attribute_for_inspect with a long array' do
t = topics(:first)
t.content = (1..11).to_a
assert_equal "[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]", t.attribute_for_inspect(:content)
end
- def test_attribute_present
+ test 'attribute_present' do
t = Topic.new
t.title = "hello there!"
t.written_on = Time.now
@@ -65,7 +65,7 @@ class AttributeMethodsTest < ActiveRecord::TestCase
assert !t.attribute_present?("author_name")
end
- def test_attribute_present_with_booleans
+ test 'attribute_present with booleans' do
b1 = Boolean.new
b1.value = false
assert b1.attribute_present?(:value)
@@ -83,44 +83,44 @@ class AttributeMethodsTest < ActiveRecord::TestCase
assert Boolean.find(b4.id).attribute_present?(:value)
end
- def test_caching_nil_primary_key
+ test 'caching a nil primary key' do
klass = Class.new(Minimalistic)
assert_called(klass, :reset_primary_key, returns: nil) do
2.times { klass.primary_key }
end
end
- def test_attribute_keys_on_new_instance
+ test 'attribute keys on a new instance' do
t = Topic.new
assert_equal nil, t.title, "The topics table has a title column, so it should be nil"
assert_raise(NoMethodError) { t.title2 }
end
- def test_boolean_attributes
+ test 'boolean attributes' do
assert !Topic.find(1).approved?
assert Topic.find(2).approved?
end
- def test_set_attributes
+ test 'set attributes' do
topic = Topic.find(1)
- topic.attributes = { "title" => "Budget", "author_name" => "Jason" }
+ topic.attributes = { title: 'Budget', author_name: 'Jason' }
topic.save
- assert_equal("Budget", topic.title)
- assert_equal("Jason", topic.author_name)
+ assert_equal('Budget', topic.title)
+ assert_equal('Jason', topic.author_name)
assert_equal(topics(:first).author_email_address, Topic.find(1).author_email_address)
end
- def test_set_attributes_without_hash
+ test 'set attributes without a hash' do
topic = Topic.new
assert_raise(ArgumentError) { topic.attributes = '' }
end
- def test_integers_as_nil
- test = AutoId.create('value' => '')
+ test 'integers as nil' do
+ test = AutoId.create(value: '')
assert_nil AutoId.find(test.id).value
end
- def test_set_attributes_with_block
+ test 'set attributes with a block' do
topic = Topic.new do |t|
t.title = "Budget"
t.author_name = "Jason"
@@ -130,7 +130,7 @@ class AttributeMethodsTest < ActiveRecord::TestCase
assert_equal("Jason", topic.author_name)
end
- def test_respond_to?
+ test 'respond_to?' do
topic = Topic.find(1)
assert_respond_to topic, "title"
assert_respond_to topic, "title?"
@@ -144,7 +144,7 @@ class AttributeMethodsTest < ActiveRecord::TestCase
assert !topic.respond_to?(:nothingness)
end
- def test_respond_to_with_custom_primary_key
+ test 'respond_to? with a custom primary key' do
keyboard = Keyboard.create
assert_not_nil keyboard.key_number
assert_equal keyboard.key_number, keyboard.id
@@ -152,7 +152,7 @@ class AttributeMethodsTest < ActiveRecord::TestCase
assert keyboard.respond_to?('id')
end
- def test_id_before_type_cast_with_custom_primary_key
+ test 'id_before_type_cast with a custom primary key' do
keyboard = Keyboard.create
keyboard.key_number = '10'
assert_equal '10', keyboard.id_before_type_cast
@@ -161,8 +161,8 @@ class AttributeMethodsTest < ActiveRecord::TestCase
assert_equal '10', keyboard.read_attribute_before_type_cast(:key_number)
end
- # Syck calls respond_to? before actually calling initialize
- def test_respond_to_with_allocated_object
+ # Syck calls respond_to? before actually calling initialize.
+ test 'respond_to? with an allocated object' do
klass = Class.new(ActiveRecord::Base) do
self.table_name = 'topics'
end
@@ -174,31 +174,32 @@ class AttributeMethodsTest < ActiveRecord::TestCase
assert_respond_to topic, :title
end
- # IRB inspects the return value of "MyModel.allocate".
- def test_allocated_object_can_be_inspected
+ # IRB inspects the return value of MyModel.allocate.
+ test 'allocated objects can be inspected' do
topic = Topic.allocate
assert_equal "#<Topic not initialized>", topic.inspect
end
- def test_array_content
+ test 'array content' do
+ content = %w( one two three )
topic = Topic.new
- topic.content = %w( one two three )
+ topic.content = content
topic.save
- assert_equal(%w( one two three ), Topic.find(topic.id).content)
+ assert_equal content, Topic.find(topic.id).content
end
- def test_read_attributes_before_type_cast
- category = Category.new({:name=>"Test category", :type => nil})
- category_attrs = {"name"=>"Test category", "id" => nil, "type" => nil, "categorizations_count" => nil}
- assert_equal category_attrs , category.attributes_before_type_cast
+ test 'read attributes_before_type_cast' do
+ category = Category.new(name: 'Test category', type: nil)
+ category_attrs = { 'name' => 'Test category', 'id' => nil, 'type' => nil, 'categorizations_count' => nil }
+ assert_equal category_attrs, category.attributes_before_type_cast
end
if current_adapter?(:Mysql2Adapter)
- def test_read_attributes_before_type_cast_on_boolean
+ test 'read attributes_before_type_cast on a boolean' do
bool = Boolean.create!({ "value" => false })
- if RUBY_PLATFORM =~ /java/
- # JRuby will return the value before typecast as string
+ if RUBY_PLATFORM.include?('java')
+ # JRuby will return the value before typecast as string.
assert_equal "0", bool.reload.attributes_before_type_cast["value"]
else
assert_equal 0, bool.reload.attributes_before_type_cast["value"]
@@ -206,7 +207,7 @@ class AttributeMethodsTest < ActiveRecord::TestCase
end
end
- def test_read_attributes_before_type_cast_on_datetime
+ test 'read attributes_before_type_cast on a datetime' do
in_time_zone "Pacific Time (US & Canada)" do
record = @target.new
@@ -221,7 +222,7 @@ class AttributeMethodsTest < ActiveRecord::TestCase
end
end
- def test_read_attributes_after_type_cast_on_datetime
+ test 'read attributes_after_type_cast on a date' do
tz = "Pacific Time (US & Canada)"
in_time_zone tz do
@@ -242,7 +243,7 @@ class AttributeMethodsTest < ActiveRecord::TestCase
end
end
- def test_hash_content
+ test 'hash content' do
topic = Topic.new
topic.content = { "one" => 1, "two" => 2 }
topic.save
@@ -256,7 +257,7 @@ class AttributeMethodsTest < ActiveRecord::TestCase
assert_equal 3, Topic.find(topic.id).content["three"]
end
- def test_update_array_content
+ test 'update array content' do
topic = Topic.new
topic.content = %w( one two three )
@@ -270,14 +271,14 @@ class AttributeMethodsTest < ActiveRecord::TestCase
assert_equal(%w( one two three four five ), topic.content)
end
- def test_case_sensitive_attributes_hash
- # DB2 is not case-sensitive
+ test 'case-sensitive attributes hash' do
+ # DB2 is not case-sensitive.
return true if current_adapter?(:DB2Adapter)
assert_equal @loaded_fixtures['computers']['workstation'].to_hash, Computer.first.attributes
end
- def test_attributes_without_primary_key
+ test 'attributes without primary key' do
klass = Class.new(ActiveRecord::Base) do
self.table_name = 'developers_projects'
end
@@ -286,9 +287,9 @@ class AttributeMethodsTest < ActiveRecord::TestCase
assert_not klass.new.has_attribute?('id')
end
- def test_hashes_not_mangled
- new_topic = { :title => "New Topic" }
- new_topic_values = { :title => "AnotherTopic" }
+ test 'hashes are not mangled' do
+ new_topic = { title: 'New Topic' }
+ new_topic_values = { title: 'AnotherTopic' }
topic = Topic.new(new_topic)
assert_equal new_topic[:title], topic.title
@@ -297,13 +298,13 @@ class AttributeMethodsTest < ActiveRecord::TestCase
assert_equal new_topic_values[:title], topic.title
end
- def test_create_through_factory
- topic = Topic.create("title" => "New Topic")
+ test 'create through factory' do
+ topic = Topic.create(title: 'New Topic')
topicReloaded = Topic.find(topic.id)
assert_equal(topic, topicReloaded)
end
- def test_write_attribute
+ test 'write_attribute' do
topic = Topic.new
topic.send(:write_attribute, :title, "Still another topic")
assert_equal "Still another topic", topic.title
@@ -318,7 +319,7 @@ class AttributeMethodsTest < ActiveRecord::TestCase
assert_equal "Still another topic: part 4", topic.title
end
- def test_read_attribute
+ test 'read_attribute' do
topic = Topic.new
topic.title = "Don't change the topic"
assert_equal "Don't change the topic", topic.read_attribute("title")
@@ -328,7 +329,7 @@ class AttributeMethodsTest < ActiveRecord::TestCase
assert_equal "Don't change the topic", topic[:title]
end
- def test_read_attribute_raises_missing_attribute_error_when_not_exists
+ test 'read_attribute raises ActiveModel::MissingAttributeError when the attribute does not exist' do
computer = Computer.select('id').first
assert_raises(ActiveModel::MissingAttributeError) { computer[:developer] }
assert_raises(ActiveModel::MissingAttributeError) { computer[:extendedWarranty] }
@@ -336,7 +337,7 @@ class AttributeMethodsTest < ActiveRecord::TestCase
assert_nothing_raised { computer[:developer] = 'Hello!' }
end
- def test_read_attribute_when_false
+ test 'read_attribute when false' do
topic = topics(:first)
topic.approved = false
assert !topic.approved?, "approved should be false"
@@ -344,7 +345,7 @@ class AttributeMethodsTest < ActiveRecord::TestCase
assert !topic.approved?, "approved should be false"
end
- def test_read_attribute_when_true
+ test 'read_attribute when true' do
topic = topics(:first)
topic.approved = true
assert topic.approved?, "approved should be true"
@@ -352,7 +353,7 @@ class AttributeMethodsTest < ActiveRecord::TestCase
assert topic.approved?, "approved should be true"
end
- def test_read_write_boolean_attribute
+ test 'boolean attributes writing and reading' do
topic = Topic.new
topic.approved = "false"
assert !topic.approved?, "approved should be false"
@@ -367,7 +368,7 @@ class AttributeMethodsTest < ActiveRecord::TestCase
assert topic.approved?, "approved should be true"
end
- def test_overridden_write_attribute
+ test 'overridden write_attribute' do
topic = Topic.new
def topic.write_attribute(attr_name, value)
super(attr_name, value.downcase)
@@ -386,7 +387,7 @@ class AttributeMethodsTest < ActiveRecord::TestCase
assert_equal "yet another topic: part 4", topic.title
end
- def test_overridden_read_attribute
+ test 'overridden read_attribute' do
topic = Topic.new
topic.title = "Stop changing the topic"
def topic.read_attribute(attr_name)
@@ -400,40 +401,40 @@ class AttributeMethodsTest < ActiveRecord::TestCase
assert_equal "STOP CHANGING THE TOPIC", topic[:title]
end
- def test_read_overridden_attribute
- topic = Topic.new(:title => 'a')
+ test 'read overridden attribute' do
+ topic = Topic.new(title: 'a')
def topic.title() 'b' end
assert_equal 'a', topic[:title]
end
- def test_query_attribute_string
+ test 'string attribute predicate' do
[nil, "", " "].each do |value|
- assert_equal false, Topic.new(:author_name => value).author_name?
+ assert_equal false, Topic.new(author_name: value).author_name?
end
- assert_equal true, Topic.new(:author_name => "Name").author_name?
+ assert_equal true, Topic.new(author_name: 'Name').author_name?
end
- def test_query_attribute_number
- [nil, 0, "0"].each do |value|
- assert_equal false, Developer.new(:salary => value).salary?
+ test 'number attribute predicate' do
+ [nil, 0, '0'].each do |value|
+ assert_equal false, Developer.new(salary: value).salary?
end
- assert_equal true, Developer.new(:salary => 1).salary?
- assert_equal true, Developer.new(:salary => "1").salary?
+ assert_equal true, Developer.new(salary: 1).salary?
+ assert_equal true, Developer.new(salary: '1').salary?
end
- def test_query_attribute_boolean
+ test 'boolean attribute predicate' do
[nil, "", false, "false", "f", 0].each do |value|
- assert_equal false, Topic.new(:approved => value).approved?
+ assert_equal false, Topic.new(approved: value).approved?
end
[true, "true", "1", 1].each do |value|
- assert_equal true, Topic.new(:approved => value).approved?
+ assert_equal true, Topic.new(approved: value).approved?
end
end
- def test_query_attribute_with_custom_fields
+ test 'custom field attribute predicate' do
object = Company.find_by_sql(<<-SQL).first
SELECT c1.*, c2.type as string_value, c2.rating as int_value
FROM companies c1, companies c2
@@ -454,23 +455,23 @@ class AttributeMethodsTest < ActiveRecord::TestCase
assert !object.int_value?
end
- def test_non_attribute_access_and_assignment
+ test 'non-attribute read and write' do
topic = Topic.new
assert !topic.respond_to?("mumbo")
assert_raise(NoMethodError) { topic.mumbo }
assert_raise(NoMethodError) { topic.mumbo = 5 }
end
- def test_undeclared_attribute_method_does_not_affect_respond_to_and_method_missing
- topic = @target.new(:title => 'Budget')
+ test 'undeclared attribute method does not affect respond_to? and method_missing' do
+ topic = @target.new(title: 'Budget')
assert topic.respond_to?('title')
assert_equal 'Budget', topic.title
assert !topic.respond_to?('title_hello_world')
assert_raise(NoMethodError) { topic.title_hello_world }
end
- def test_declared_prefixed_attribute_method_affects_respond_to_and_method_missing
- topic = @target.new(:title => 'Budget')
+ test 'declared prefixed attribute method affects respond_to? and method_missing' do
+ topic = @target.new(title: 'Budget')
%w(default_ title_).each do |prefix|
@target.class_eval "def #{prefix}attribute(*args) args end"
@target.attribute_method_prefix prefix
@@ -483,11 +484,11 @@ class AttributeMethodsTest < ActiveRecord::TestCase
end
end
- def test_declared_suffixed_attribute_method_affects_respond_to_and_method_missing
+ test 'declared suffixed attribute method affects respond_to? and method_missing' do
%w(_default _title_default _it! _candidate= able?).each do |suffix|
@target.class_eval "def attribute#{suffix}(*args) args end"
@target.attribute_method_suffix suffix
- topic = @target.new(:title => 'Budget')
+ topic = @target.new(title: 'Budget')
meth = "title#{suffix}"
assert topic.respond_to?(meth)
@@ -497,11 +498,11 @@ class AttributeMethodsTest < ActiveRecord::TestCase
end
end
- def test_declared_affixed_attribute_method_affects_respond_to_and_method_missing
+ test 'declared affixed attribute method affects respond_to? and method_missing' do
[['mark_', '_for_update'], ['reset_', '!'], ['default_', '_value?']].each do |prefix, suffix|
@target.class_eval "def #{prefix}attribute#{suffix}(*args) args end"
- @target.attribute_method_affix({ :prefix => prefix, :suffix => suffix })
- topic = @target.new(:title => 'Budget')
+ @target.attribute_method_affix(prefix: prefix, suffix: suffix)
+ topic = @target.new(title: 'Budget')
meth = "#{prefix}title#{suffix}"
assert topic.respond_to?(meth)
@@ -511,38 +512,38 @@ class AttributeMethodsTest < ActiveRecord::TestCase
end
end
- def test_should_unserialize_attributes_for_frozen_records
- myobj = {:value1 => :value2}
- topic = Topic.create("content" => myobj)
+ test 'should unserialize attributes for frozen records' do
+ myobj = { value1: :value2 }
+ topic = Topic.create(content: myobj)
topic.freeze
assert_equal myobj, topic.content
end
- def test_typecast_attribute_from_select_to_false
- Topic.create(:title => 'Budget')
- # Oracle does not support boolean expressions in SELECT
+ test 'typecast attribute from select to false' do
+ Topic.create(title: 'Budget')
+ # Oracle does not support boolean expressions in SELECT.
if current_adapter?(:OracleAdapter, :FbAdapter)
- topic = Topic.all.merge!(:select => "topics.*, 0 as is_test").first
+ topic = Topic.all.merge!(select: 'topics.*, 0 as is_test').first
else
- topic = Topic.all.merge!(:select => "topics.*, 1=2 as is_test").first
+ topic = Topic.all.merge!(select: 'topics.*, 1=2 as is_test').first
end
assert !topic.is_test?
end
- def test_typecast_attribute_from_select_to_true
- Topic.create(:title => 'Budget')
- # Oracle does not support boolean expressions in SELECT
+ test 'typecast attribute from select to true' do
+ Topic.create(title: 'Budget')
+ # Oracle does not support boolean expressions in SELECT.
if current_adapter?(:OracleAdapter, :FbAdapter)
- topic = Topic.all.merge!(:select => "topics.*, 1 as is_test").first
+ topic = Topic.all.merge!(select: 'topics.*, 1 as is_test').first
else
- topic = Topic.all.merge!(:select => "topics.*, 2=2 as is_test").first
+ topic = Topic.all.merge!(select: 'topics.*, 2=2 as is_test').first
end
assert topic.is_test?
end
- def test_raises_dangerous_attribute_error_when_defining_activerecord_method_in_model
+ test 'raises ActiveRecord::DangerousAttributeError when defining an AR method in a model' do
%w(save create_or_update).each do |method|
- klass = Class.new ActiveRecord::Base
+ klass = Class.new(ActiveRecord::Base)
klass.class_eval "def #{method}() 'defined #{method}' end"
assert_raise ActiveRecord::DangerousAttributeError do
klass.instance_method_already_implemented?(method)
@@ -550,7 +551,7 @@ class AttributeMethodsTest < ActiveRecord::TestCase
end
end
- def test_converted_values_are_returned_after_assignment
+ test 'converted values are returned after assignment' do
developer = Developer.new(name: 1337, salary: "50000")
assert_equal "50000", developer.salary_before_type_cast
@@ -565,7 +566,7 @@ class AttributeMethodsTest < ActiveRecord::TestCase
assert_equal "1337", developer.name
end
- def test_write_nil_to_time_attributes
+ test 'write nil to time attribute' do
in_time_zone "Pacific Time (US & Canada)" do
record = @target.new
record.written_on = nil
@@ -573,7 +574,7 @@ class AttributeMethodsTest < ActiveRecord::TestCase
end
end
- def test_write_time_to_date_attributes
+ test 'write time to date attribute' do
in_time_zone "Pacific Time (US & Canada)" do
record = @target.new
record.last_read = Time.utc(2010, 1, 1, 10)
@@ -581,7 +582,7 @@ class AttributeMethodsTest < ActiveRecord::TestCase
end
end
- def test_time_attributes_are_retrieved_in_current_time_zone
+ test 'time attributes are retrieved in the current time zone' do
in_time_zone "Pacific Time (US & Canada)" do
utc_time = Time.utc(2008, 1, 1)
record = @target.new
@@ -593,7 +594,7 @@ class AttributeMethodsTest < ActiveRecord::TestCase
end
end
- def test_setting_time_zone_aware_attribute_to_utc
+ test 'setting a time zone-aware attribute to UTC' do
in_time_zone "Pacific Time (US & Canada)" do
utc_time = Time.utc(2008, 1, 1)
record = @target.new
@@ -604,7 +605,7 @@ class AttributeMethodsTest < ActiveRecord::TestCase
end
end
- def test_setting_time_zone_aware_attribute_in_other_time_zone
+ test 'setting time zone-aware attribute in other time zone' do
utc_time = Time.utc(2008, 1, 1)
cst_time = utc_time.in_time_zone("Central Time (US & Canada)")
in_time_zone "Pacific Time (US & Canada)" do
@@ -616,18 +617,18 @@ class AttributeMethodsTest < ActiveRecord::TestCase
end
end
- def test_setting_time_zone_aware_read_attribute
+ test 'setting time zone-aware read attribute' do
utc_time = Time.utc(2008, 1, 1)
cst_time = utc_time.in_time_zone("Central Time (US & Canada)")
in_time_zone "Pacific Time (US & Canada)" do
- record = @target.create(:written_on => cst_time).reload
+ record = @target.create(written_on: cst_time).reload
assert_equal utc_time, record[:written_on]
assert_equal ActiveSupport::TimeZone["Pacific Time (US & Canada)"], record[:written_on].time_zone
assert_equal Time.utc(2007, 12, 31, 16), record[:written_on].time
end
end
- def test_setting_time_zone_aware_attribute_with_string
+ test 'setting time zone-aware attribute with a string' do
utc_time = Time.utc(2008, 1, 1)
(-11..13).each do |timezone_offset|
time_string = utc_time.in_time_zone(timezone_offset).to_s
@@ -641,9 +642,9 @@ class AttributeMethodsTest < ActiveRecord::TestCase
end
end
- def test_time_zone_aware_attribute_saved
+ test 'time zone-aware attribute saved' do
in_time_zone 1 do
- record = @target.create(:written_on => '2012-02-20 10:00')
+ record = @target.create(written_on: '2012-02-20 10:00')
record.written_on = '2012-02-20 09:00'
record.save
@@ -651,7 +652,7 @@ class AttributeMethodsTest < ActiveRecord::TestCase
end
end
- def test_setting_time_zone_aware_attribute_to_blank_string_returns_nil
+ test 'setting a time zone-aware attribute to a blank string returns nil' do
in_time_zone "Pacific Time (US & Canada)" do
record = @target.new
record.written_on = ' '
@@ -660,7 +661,7 @@ class AttributeMethodsTest < ActiveRecord::TestCase
end
end
- def test_setting_time_zone_aware_attribute_interprets_time_zone_unaware_string_in_time_zone
+ test 'setting a time zone-aware attribute interprets time zone-unaware string in time zone' do
time_string = 'Tue Jan 01 00:00:00 2008'
(-11..13).each do |timezone_offset|
in_time_zone timezone_offset do
@@ -673,7 +674,7 @@ class AttributeMethodsTest < ActiveRecord::TestCase
end
end
- def test_setting_time_zone_aware_datetime_in_current_time_zone
+ test 'setting a time zone-aware datetime in the current time zone' do
utc_time = Time.utc(2008, 1, 1)
in_time_zone "Pacific Time (US & Canada)" do
record = @target.new
@@ -684,7 +685,7 @@ class AttributeMethodsTest < ActiveRecord::TestCase
end
end
- def test_yaml_dumping_record_with_time_zone_aware_attribute
+ test 'YAML dumping a record with time zone-aware attribute' do
in_time_zone "Pacific Time (US & Canada)" do
record = Topic.new(id: 1)
record.written_on = "Jan 01 00:00:00 2014"
@@ -692,7 +693,7 @@ class AttributeMethodsTest < ActiveRecord::TestCase
end
end
- def test_setting_time_zone_aware_time_in_current_time_zone
+ test 'setting a time zone-aware time in the current time zone' do
in_time_zone "Pacific Time (US & Canada)" do
record = @target.new
time_string = "10:00:00"
@@ -707,7 +708,7 @@ class AttributeMethodsTest < ActiveRecord::TestCase
end
end
- def test_setting_time_zone_aware_time_with_dst
+ test 'setting a time zone-aware time with DST' do
in_time_zone "Pacific Time (US & Canada)" do
current_time = Time.zone.local(2014, 06, 15, 10)
record = @target.new(bonus_time: current_time)
@@ -721,7 +722,7 @@ class AttributeMethodsTest < ActiveRecord::TestCase
end
end
- def test_removing_time_zone_aware_types
+ test 'removing time zone-aware types' do
with_time_zone_aware_types(:datetime) do
in_time_zone "Pacific Time (US & Canada)" do
record = @target.new(bonus_time: "10:00:00")
@@ -733,14 +734,14 @@ class AttributeMethodsTest < ActiveRecord::TestCase
end
end
- def test_time_zone_aware_attributes_dont_recurse_infinitely_on_invalid_values
+ test 'time zone-aware attributes do not recurse infinitely on invalid values' do
in_time_zone "Pacific Time (US & Canada)" do
record = @target.new(bonus_time: [])
assert_equal nil, record.bonus_time
end
end
- def test_setting_time_zone_conversion_for_attributes_should_write_value_on_class_variable
+ test 'setting a time_zone_conversion_for_attributes should write the value on a class variable' do
Topic.skip_time_zone_conversion_for_attributes = [:field_a]
Minimalistic.skip_time_zone_conversion_for_attributes = [:field_b]
@@ -748,44 +749,44 @@ class AttributeMethodsTest < ActiveRecord::TestCase
assert_equal [:field_b], Minimalistic.skip_time_zone_conversion_for_attributes
end
- def test_read_attributes_respect_access_control
- privatize("title")
+ test 'attribute readers respect access control' do
+ privatize('title')
- topic = @target.new(:title => "The pros and cons of programming naked.")
+ topic = @target.new(title: 'The pros and cons of programming naked.')
assert !topic.respond_to?(:title)
exception = assert_raise(NoMethodError) { topic.title }
- assert exception.message.include?("private method")
+ assert exception.message.include?('private method')
assert_equal "I'm private", topic.send(:title)
end
- def test_write_attributes_respect_access_control
- privatize("title=(value)")
+ test 'attribute writers respect access control' do
+ privatize('title=(value)')
topic = @target.new
assert !topic.respond_to?(:title=)
- exception = assert_raise(NoMethodError) { topic.title = "Pants"}
- assert exception.message.include?("private method")
- topic.send(:title=, "Very large pants")
+ exception = assert_raise(NoMethodError) { topic.title = 'Pants' }
+ assert exception.message.include?('private method')
+ topic.send(:title=, 'Very large pants')
end
- def test_question_attributes_respect_access_control
- privatize("title?")
+ test 'attribute predicates respect access control' do
+ privatize('title?')
- topic = @target.new(:title => "Isaac Newton's pants")
+ topic = @target.new(title: "Isaac Newton's pants")
assert !topic.respond_to?(:title?)
exception = assert_raise(NoMethodError) { topic.title? }
- assert exception.message.include?("private method")
+ assert exception.message.include?('private method')
assert topic.send(:title?)
end
- def test_bulk_update_respects_access_control
- privatize("title=(value)")
+ test 'bulk updates respect access control' do
+ privatize('title=(value)')
- assert_raise(ActiveRecord::UnknownAttributeError) { @target.new(:title => "Rants about pants") }
- assert_raise(ActiveRecord::UnknownAttributeError) { @target.new.attributes = { :title => "Ants in pants" } }
+ assert_raise(ActiveRecord::UnknownAttributeError) { @target.new(title: 'Rants about pants') }
+ assert_raise(ActiveRecord::UnknownAttributeError) { @target.new.attributes = { title: 'Ants in pants' } }
end
- def test_bulk_update_raise_unknown_attribute_error
+ test 'bulk update raises ActiveRecord::UnknownAttributeError' do
error = assert_raises(ActiveRecord::UnknownAttributeError) {
Topic.new(hello: "world")
}
@@ -794,20 +795,20 @@ class AttributeMethodsTest < ActiveRecord::TestCase
assert_equal "unknown attribute 'hello' for Topic.", error.message
end
- def test_methods_override_in_multi_level_subclass
+ test 'method overrides in multi-level subclasses' do
klass = Class.new(Developer) do
def name
"dev:#{read_attribute(:name)}"
end
end
- 2.times { klass = Class.new klass }
+ 2.times { klass = Class.new(klass) }
dev = klass.new(name: 'arthurnn')
dev.save!
assert_equal 'dev:arthurnn', dev.reload.name
end
- def test_global_methods_are_overwritten
+ test 'global methods are overwritten' do
klass = Class.new(ActiveRecord::Base) do
self.table_name = 'computers'
end
@@ -817,8 +818,10 @@ class AttributeMethodsTest < ActiveRecord::TestCase
assert_nil computer.system
end
- def test_global_methods_are_overwritten_when_subclassing
- klass = Class.new(ActiveRecord::Base) { self.abstract_class = true }
+ test 'global methods are overwritten when subclassing' do
+ klass = Class.new(ActiveRecord::Base) do
+ self.abstract_class = true
+ end
subklass = Class.new(klass) do
self.table_name = 'computers'
@@ -830,7 +833,7 @@ class AttributeMethodsTest < ActiveRecord::TestCase
assert_nil computer.system
end
- def test_instance_method_should_be_defined_on_the_base_class
+ test 'instance methods should be defined on the base class' do
subklass = Class.new(Topic)
Topic.define_attribute_methods
@@ -846,14 +849,14 @@ class AttributeMethodsTest < ActiveRecord::TestCase
assert subklass.method_defined?(:id), "subklass is missing id method"
end
- def test_read_attribute_with_nil_should_not_asplode
- assert_equal nil, Topic.new.read_attribute(nil)
+ test 'read_attribute with nil should not asplode' do
+ assert_nil Topic.new.read_attribute(nil)
end
# If B < A, and A defines an accessor for 'foo', we don't want to override
# that by defining a 'foo' method in the generated methods module for B.
# (That module will be inserted between the two, e.g. [B, <GeneratedAttributes>, A].)
- def test_inherited_custom_accessors
+ test 'inherited custom accessors' do
klass = new_topic_like_ar_class do
self.abstract_class = true
def title; "omg"; end
@@ -869,7 +872,7 @@ class AttributeMethodsTest < ActiveRecord::TestCase
assert_equal "lol", topic.author_name
end
- def test_inherited_custom_accessors_with_reserved_names
+ test 'inherited custom accessors with reserved names' do
klass = Class.new(ActiveRecord::Base) do
self.table_name = 'computers'
self.abstract_class = true
@@ -887,7 +890,7 @@ class AttributeMethodsTest < ActiveRecord::TestCase
assert_equal 99, computer.developer
end
- def test_on_the_fly_super_invokable_generated_attribute_methods_via_method_missing
+ test 'on_the_fly_super_invokable_generated_attribute_methods_via_method_missing' do
klass = new_topic_like_ar_class do
def title
super + '!'
@@ -898,7 +901,7 @@ class AttributeMethodsTest < ActiveRecord::TestCase
assert_equal real_topic.title + '!', klass.find(real_topic.id).title
end
- def test_on_the_fly_super_invokable_generated_predicate_attribute_methods_via_method_missing
+ test 'on-the-fly super-invokable generated attribute predicates via method_missing' do
klass = new_topic_like_ar_class do
def title?
!super
@@ -909,7 +912,7 @@ class AttributeMethodsTest < ActiveRecord::TestCase
assert_equal !real_topic.title?, klass.find(real_topic.id).title?
end
- def test_calling_super_when_parent_does_not_define_method_raises_error
+ test 'calling super when the parent does not define method raises NoMethodError' do
klass = new_topic_like_ar_class do
def some_method_that_is_not_on_super
super
@@ -921,38 +924,38 @@ class AttributeMethodsTest < ActiveRecord::TestCase
end
end
- def test_attribute_method?
+ test 'attribute_method?' do
assert @target.attribute_method?(:title)
assert @target.attribute_method?(:title=)
assert_not @target.attribute_method?(:wibble)
end
- def test_attribute_method_returns_false_if_table_does_not_exist
+ test 'attribute_method? returns false if the table does not exist' do
@target.table_name = 'wibble'
assert_not @target.attribute_method?(:title)
end
- def test_attribute_names_on_new_record
+ test 'attribute_names on a new record' do
model = @target.new
assert_equal @target.column_names, model.attribute_names
end
- def test_attribute_names_on_queried_record
+ test 'attribute_names on a queried record' do
model = @target.last!
assert_equal @target.column_names, model.attribute_names
end
- def test_attribute_names_with_custom_select
+ test 'attribute_names with a custom select' do
model = @target.select('id').last!
assert_equal ['id'], model.attribute_names
- # Sanity check, make sure other columns exist
+ # Sanity check, make sure other columns exist.
assert_not_equal ['id'], @target.column_names
end
- def test_came_from_user
+ test 'came_from_user?' do
model = @target.first
assert_not model.id_came_from_user?
@@ -960,7 +963,7 @@ class AttributeMethodsTest < ActiveRecord::TestCase
assert model.id_came_from_user?
end
- def test_accessed_fields
+ test 'accessed_fields' do
model = @target.first
assert_equal [], model.accessed_fields
diff --git a/activerecord/test/cases/attributes_test.rb b/activerecord/test/cases/attributes_test.rb
index 7bcaa53aa2..604411da97 100644
--- a/activerecord/test/cases/attributes_test.rb
+++ b/activerecord/test/cases/attributes_test.rb
@@ -205,5 +205,49 @@ module ActiveRecord
assert_equal(:bar, child.new(foo: :bar).foo)
end
+
+ test "attributes not backed by database columns are not dirty when unchanged" do
+ refute OverloadedType.new.non_existent_decimal_changed?
+ end
+
+ test "attributes not backed by database columns are always initialized" do
+ OverloadedType.create!
+ model = OverloadedType.first
+
+ assert_nil model.non_existent_decimal
+ model.non_existent_decimal = "123"
+ assert_equal 123, model.non_existent_decimal
+ end
+
+ test "attributes not backed by database columns return the default on models loaded from database" do
+ child = Class.new(OverloadedType) do
+ attribute :non_existent_decimal, :decimal, default: 123
+ end
+ child.create!
+ model = child.first
+
+ assert_equal 123, model.non_existent_decimal
+ end
+
+ test "attributes not backed by database columns properly interact with mutation and dirty" do
+ child = Class.new(ActiveRecord::Base) do
+ self.table_name = "topics"
+ attribute :foo, :string, default: "lol"
+ end
+ child.create!
+ model = child.first
+
+ assert_equal "lol", model.foo
+
+ model.foo << "asdf"
+ assert_equal "lolasdf", model.foo
+ assert model.foo_changed?
+
+ model.reload
+ assert_equal "lol", model.foo
+
+ model.foo = "lol"
+ refute model.changed?
+ end
end
end
diff --git a/activerecord/test/cases/callbacks_test.rb b/activerecord/test/cases/callbacks_test.rb
index 4f70ae3a1d..8a722b4f22 100644
--- a/activerecord/test/cases/callbacks_test.rb
+++ b/activerecord/test/cases/callbacks_test.rb
@@ -31,7 +31,7 @@ class CallbackDeveloper < ActiveRecord::Base
end
ActiveRecord::Callbacks::CALLBACKS.each do |callback_method|
- next if callback_method.to_s =~ /^around_/
+ next if callback_method.to_s.start_with?('around_')
define_callback_method(callback_method)
ActiveSupport::Deprecation.silence { send(callback_method, callback_string(callback_method)) }
send(callback_method, callback_proc(callback_method))
diff --git a/activerecord/test/cases/dirty_test.rb b/activerecord/test/cases/dirty_test.rb
index f9794518c7..3c58b6ad09 100644
--- a/activerecord/test/cases/dirty_test.rb
+++ b/activerecord/test/cases/dirty_test.rb
@@ -604,7 +604,7 @@ class DirtyTest < ActiveRecord::TestCase
jon = Person.create! first_name: 'Jon'
end
- assert ActiveRecord::SQLCounter.log_all.none? { |sql| sql =~ /followers_count/ }
+ assert ActiveRecord::SQLCounter.log_all.none? { |sql| sql.include?('followers_count') }
jon.reload
assert_equal 'Jon', jon.first_name
diff --git a/activerecord/test/cases/nested_attributes_test.rb b/activerecord/test/cases/nested_attributes_test.rb
index 11fb164d50..8a08056bbe 100644
--- a/activerecord/test/cases/nested_attributes_test.rb
+++ b/activerecord/test/cases/nested_attributes_test.rb
@@ -642,13 +642,13 @@ module NestedAttributesOnACollectionAssociationTests
def test_should_not_overwrite_unsaved_updates_when_loading_association
@pirate.reload
@pirate.send(association_setter, [{ :id => @child_1.id, :name => 'Grace OMalley' }])
- assert_equal 'Grace OMalley', @pirate.send(@association_name).send(:load_target).find { |r| r.id == @child_1.id }.name
+ assert_equal 'Grace OMalley', @pirate.send(@association_name).load_target.find { |r| r.id == @child_1.id }.name
end
def test_should_preserve_order_when_not_overwriting_unsaved_updates
@pirate.reload
@pirate.send(association_setter, [{ :id => @child_1.id, :name => 'Grace OMalley' }])
- assert_equal @child_1.id, @pirate.send(@association_name).send(:load_target).first.id
+ assert_equal @child_1.id, @pirate.send(@association_name).load_target.first.id
end
def test_should_refresh_saved_records_when_not_overwriting_unsaved_updates
@@ -657,13 +657,13 @@ module NestedAttributesOnACollectionAssociationTests
@pirate.send(@association_name) << record
record.save!
@pirate.send(@association_name).last.update!(name: 'Polly')
- assert_equal 'Polly', @pirate.send(@association_name).send(:load_target).last.name
+ assert_equal 'Polly', @pirate.send(@association_name).load_target.last.name
end
def test_should_not_remove_scheduled_destroys_when_loading_association
@pirate.reload
@pirate.send(association_setter, [{ :id => @child_1.id, :_destroy => '1' }])
- assert @pirate.send(@association_name).send(:load_target).find { |r| r.id == @child_1.id }.marked_for_destruction?
+ assert @pirate.send(@association_name).load_target.find { |r| r.id == @child_1.id }.marked_for_destruction?
end
def test_should_take_a_hash_with_composite_id_keys_and_assign_the_attributes_to_the_associated_models
diff --git a/activerecord/test/cases/quoting_test.rb b/activerecord/test/cases/quoting_test.rb
index c01c82f4f5..225e23bc83 100644
--- a/activerecord/test/cases/quoting_test.rb
+++ b/activerecord/test/cases/quoting_test.rb
@@ -149,5 +149,21 @@ module ActiveRecord
assert_equal "1800", @quoter.quote(30.minutes)
end
end
+
+ class QuoteBooleanTest < ActiveRecord::TestCase
+ def setup
+ @connection = ActiveRecord::Base.connection
+ end
+
+ def test_quote_returns_frozen_string
+ assert_predicate @connection.quote(true), :frozen?
+ assert_predicate @connection.quote(false), :frozen?
+ end
+
+ def test_type_cast_returns_frozen_value
+ assert_predicate @connection.type_cast(true), :frozen?
+ assert_predicate @connection.type_cast(false), :frozen?
+ end
+ end
end
end
diff --git a/activerecord/test/cases/scoping/default_scoping_test.rb b/activerecord/test/cases/scoping/default_scoping_test.rb
index dcd09b6973..6679f9415b 100644
--- a/activerecord/test/cases/scoping/default_scoping_test.rb
+++ b/activerecord/test/cases/scoping/default_scoping_test.rb
@@ -5,6 +5,7 @@ require 'models/developer'
require 'models/computer'
require 'models/vehicle'
require 'models/cat'
+require 'active_support/core_ext/regexp'
class DefaultScopingTest < ActiveRecord::TestCase
fixtures :developers, :posts, :comments
@@ -201,7 +202,7 @@ class DefaultScopingTest < ActiveRecord::TestCase
def test_order_to_unscope_reordering
scope = DeveloperOrderedBySalary.order('salary DESC, name ASC').reverse_order.unscope(:order)
- assert !(scope.to_sql =~ /order/i)
+ assert !/order/i.match?(scope.to_sql)
end
def test_unscope_reverse_order
diff --git a/activerecord/test/cases/test_case.rb b/activerecord/test/cases/test_case.rb
index c8adc21bbc..fcb6552e5b 100644
--- a/activerecord/test/cases/test_case.rb
+++ b/activerecord/test/cases/test_case.rb
@@ -1,5 +1,6 @@
require 'active_support/test_case'
require 'active_support/testing/stream'
+require 'active_support/core_ext/regexp'
module ActiveRecord
# = Active Record Test Case
@@ -115,7 +116,7 @@ module ActiveRecord
return if 'CACHE' == values[:name]
self.class.log_all << sql
- self.class.log << sql unless ignore =~ sql
+ self.class.log << sql unless ignore.match?(sql)
end
end
diff --git a/activerecord/test/schema/postgresql_specific_schema.rb b/activerecord/test/schema/postgresql_specific_schema.rb
index 24713f722a..030ad73621 100644
--- a/activerecord/test/schema/postgresql_specific_schema.rb
+++ b/activerecord/test/schema/postgresql_specific_schema.rb
@@ -88,7 +88,7 @@ _SQL
FOR EACH ROW EXECUTE PROCEDURE partitioned_insert_trigger();
_SQL
rescue ActiveRecord::StatementInvalid => e
- if e.message =~ /language "plpgsql" does not exist/
+ if e.message.include?('language "plpgsql" does not exist')
execute "CREATE LANGUAGE 'plpgsql';"
retry
else
diff --git a/activesupport/CHANGELOG.md b/activesupport/CHANGELOG.md
index 3749dda9fc..b39ef7bfb9 100644
--- a/activesupport/CHANGELOG.md
+++ b/activesupport/CHANGELOG.md
@@ -1,3 +1,30 @@
+* Introduce `not_in?` on `Object`.
+
+ As an opposite method for `in?`, `not_in?` provides equivalent support for exclusion. This turns this:
+
+ [1,2].exclude?(user_id)
+
+ ...into this:
+
+ user_id.not_in?([1,2])
+
+ *Jon McCartie*
+
+* Defines `Regexp.match?` for Ruby versions prior to 2.4. The predicate
+ has the same interface, but it does not have the performance boost. Its
+ purpose is to be able to write 2.4 compatible code.
+
+ *Xavier Noria*
+
+* Allow MessageEncryptor to take advantage of authenticated encryption modes.
+
+ AEAD modes like `aes-256-gcm` provide both confidentiality and data
+ authenticity, eliminating the need to use MessageVerifier to check if the
+ encrypted data has been tampered with. This speeds up encryption/decryption
+ and results in shorter cipher text.
+
+ *Bart de Water*
+
* Introduce `assert_changes` and `assert_no_changes`.
`assert_changes` is a more general `assert_difference` that works with any
@@ -46,7 +73,7 @@
*John Gesimondo*
-* `travel/travel_to` travel time helpers, now raise on nested calls,
+* `travel/travel_to` travel time helpers, now raise on nested calls,
as this can lead to confusing time stubbing.
Instead of:
@@ -60,7 +87,7 @@
preferred way to achieve above is:
- travel 2.days do
+ travel 2.days do
# 2 days from today
end
diff --git a/activesupport/lib/active_support/configurable.rb b/activesupport/lib/active_support/configurable.rb
index 8256c325af..c2c99459f2 100644
--- a/activesupport/lib/active_support/configurable.rb
+++ b/activesupport/lib/active_support/configurable.rb
@@ -1,6 +1,7 @@
require 'active_support/concern'
require 'active_support/ordered_options'
require 'active_support/core_ext/array/extract_options'
+require 'active_support/core_ext/regexp'
module ActiveSupport
# Configurable provides a <tt>config</tt> method to store and retrieve
@@ -107,7 +108,7 @@ module ActiveSupport
options = names.extract_options!
names.each do |name|
- raise NameError.new('invalid config attribute name') unless name =~ /\A[_A-Za-z]\w*\z/
+ raise NameError.new('invalid config attribute name') unless /\A[_A-Za-z]\w*\z/.match?(name)
reader, reader_line = "def #{name}; config.#{name}; end", __LINE__
writer, writer_line = "def #{name}=(value); config.#{name} = value; end", __LINE__
@@ -145,4 +146,3 @@ module ActiveSupport
end
end
end
-
diff --git a/activesupport/lib/active_support/core_ext/module/attribute_accessors.rb b/activesupport/lib/active_support/core_ext/module/attribute_accessors.rb
index 567ac825e9..8f761a8f60 100644
--- a/activesupport/lib/active_support/core_ext/module/attribute_accessors.rb
+++ b/activesupport/lib/active_support/core_ext/module/attribute_accessors.rb
@@ -1,4 +1,5 @@
require 'active_support/core_ext/array/extract_options'
+require 'active_support/core_ext/regexp'
# Extends the module object with class/module and instance accessors for
# class/module attributes, just like the native attr* accessors for instance
@@ -53,7 +54,7 @@ class Module
def mattr_reader(*syms)
options = syms.extract_options!
syms.each do |sym|
- raise NameError.new("invalid attribute name: #{sym}") unless sym =~ /\A[_A-Za-z]\w*\z/
+ raise NameError.new("invalid attribute name: #{sym}") unless /\A[_A-Za-z]\w*\z/.match?(sym)
class_eval(<<-EOS, __FILE__, __LINE__ + 1)
@@#{sym} = nil unless defined? @@#{sym}
@@ -119,7 +120,7 @@ class Module
def mattr_writer(*syms)
options = syms.extract_options!
syms.each do |sym|
- raise NameError.new("invalid attribute name: #{sym}") unless sym =~ /\A[_A-Za-z]\w*\z/
+ raise NameError.new("invalid attribute name: #{sym}") unless /\A[_A-Za-z]\w*\z/.match?(sym)
class_eval(<<-EOS, __FILE__, __LINE__ + 1)
@@#{sym} = nil unless defined? @@#{sym}
diff --git a/activesupport/lib/active_support/core_ext/module/attribute_accessors_per_thread.rb b/activesupport/lib/active_support/core_ext/module/attribute_accessors_per_thread.rb
index 045668c207..7015333f7c 100644
--- a/activesupport/lib/active_support/core_ext/module/attribute_accessors_per_thread.rb
+++ b/activesupport/lib/active_support/core_ext/module/attribute_accessors_per_thread.rb
@@ -1,9 +1,10 @@
require 'active_support/core_ext/array/extract_options'
+require 'active_support/core_ext/regexp'
# Extends the module object with class/module and instance accessors for
# class/module attributes, just like the native attr* accessors for instance
# attributes, but does so on a per-thread basis.
-#
+#
# So the values are scoped within the Thread.current space under the class name
# of the module.
class Module
@@ -37,7 +38,7 @@ class Module
options = syms.extract_options!
syms.each do |sym|
- raise NameError.new("invalid attribute name: #{sym}") unless sym =~ /^[_A-Za-z]\w*$/
+ raise NameError.new("invalid attribute name: #{sym}") unless /^[_A-Za-z]\w*$/.match?(sym)
class_eval(<<-EOS, __FILE__, __LINE__ + 1)
def self.#{sym}
Thread.current[:"attr_#{name}_#{sym}"]
@@ -76,7 +77,7 @@ class Module
def thread_mattr_writer(*syms)
options = syms.extract_options!
syms.each do |sym|
- raise NameError.new("invalid attribute name: #{sym}") unless sym =~ /^[_A-Za-z]\w*$/
+ raise NameError.new("invalid attribute name: #{sym}") unless /^[_A-Za-z]\w*$/.match?(sym)
class_eval(<<-EOS, __FILE__, __LINE__ + 1)
def self.#{sym}=(obj)
Thread.current[:"attr_#{name}_#{sym}"] = obj
diff --git a/activesupport/lib/active_support/core_ext/module/delegation.rb b/activesupport/lib/active_support/core_ext/module/delegation.rb
index 3f6e8bd26c..946a0f4177 100644
--- a/activesupport/lib/active_support/core_ext/module/delegation.rb
+++ b/activesupport/lib/active_support/core_ext/module/delegation.rb
@@ -1,4 +1,5 @@
require 'set'
+require 'active_support/core_ext/regexp'
class Module
# Error generated by +delegate+ when a method is called on +nil+ and +allow_nil+
@@ -153,7 +154,7 @@ class Module
raise ArgumentError, 'Delegation needs a target. Supply an options hash with a :to key as the last argument (e.g. delegate :hello, to: :greeter).'
end
- if prefix == true && to =~ /^[^a-z_]/
+ if prefix == true && /^[^a-z_]/.match?(to)
raise ArgumentError, 'Can only automatically set the delegation prefix when delegating to a method.'
end
@@ -173,7 +174,7 @@ class Module
methods.each do |method|
# Attribute writer methods only accept one argument. Makes sure []=
# methods still accept two arguments.
- definition = (method =~ /[^\]]=$/) ? 'arg' : '*args, &block'
+ definition = /[^\]]=$/.match?(method) ? 'arg' : '*args, &block'
# The following generated method calls the target exactly once, storing
# the returned value in a dummy variable.
diff --git a/activesupport/lib/active_support/core_ext/object.rb b/activesupport/lib/active_support/core_ext/object.rb
index f4f9152d6a..a2c5a472f5 100644
--- a/activesupport/lib/active_support/core_ext/object.rb
+++ b/activesupport/lib/active_support/core_ext/object.rb
@@ -4,6 +4,7 @@ require 'active_support/core_ext/object/duplicable'
require 'active_support/core_ext/object/deep_dup'
require 'active_support/core_ext/object/try'
require 'active_support/core_ext/object/inclusion'
+require 'active_support/core_ext/object/exclusion'
require 'active_support/core_ext/object/conversions'
require 'active_support/core_ext/object/instance_variables'
diff --git a/activesupport/lib/active_support/core_ext/object/blank.rb b/activesupport/lib/active_support/core_ext/object/blank.rb
index cb74bad73e..045731d752 100644
--- a/activesupport/lib/active_support/core_ext/object/blank.rb
+++ b/activesupport/lib/active_support/core_ext/object/blank.rb
@@ -1,3 +1,5 @@
+require 'active_support/core_ext/regexp'
+
class Object
# An object is blank if it's false, empty, or a whitespace string.
# For example, +false+, '', ' ', +nil+, [], and {} are all blank.
@@ -115,7 +117,7 @@ class String
# The regexp that matches blank strings is expensive. For the case of empty
# strings we can speed up this method (~3.5x) with an empty? call. The
# penalty for the rest of strings is marginal.
- empty? || BLANK_RE === self
+ empty? || BLANK_RE.match?(self)
end
end
diff --git a/activesupport/lib/active_support/core_ext/object/exclusion.rb b/activesupport/lib/active_support/core_ext/object/exclusion.rb
new file mode 100644
index 0000000000..58dfb649e5
--- /dev/null
+++ b/activesupport/lib/active_support/core_ext/object/exclusion.rb
@@ -0,0 +1,15 @@
+class Object
+ # Returns true if this object is excluded in the argument. Argument must be
+ # any object which responds to +#include?+. Usage:
+ #
+ # characters = ["Konata", "Kagami", "Tsukasa"]
+ # "MoshiMoshi".not_in?(characters) # => true
+ #
+ # This will throw an +ArgumentError+ if the argument doesn't respond
+ # to +#include?+.
+ def not_in?(another_object)
+ !another_object.include?(self)
+ rescue NoMethodError
+ raise ArgumentError.new("The parameter passed to #not_in? must respond to #include?")
+ end
+end \ No newline at end of file
diff --git a/activesupport/lib/active_support/core_ext/regexp.rb b/activesupport/lib/active_support/core_ext/regexp.rb
index 784145f5fb..062d568228 100644
--- a/activesupport/lib/active_support/core_ext/regexp.rb
+++ b/activesupport/lib/active_support/core_ext/regexp.rb
@@ -2,4 +2,8 @@ class Regexp #:nodoc:
def multiline?
options & MULTILINE == MULTILINE
end
+
+ def match?(string, pos=0)
+ !!match(string, pos)
+ end unless //.respond_to?(:match?)
end
diff --git a/activesupport/lib/active_support/deprecation/proxy_wrappers.rb b/activesupport/lib/active_support/deprecation/proxy_wrappers.rb
index 0cb2d4d22e..a83c8054ec 100644
--- a/activesupport/lib/active_support/deprecation/proxy_wrappers.rb
+++ b/activesupport/lib/active_support/deprecation/proxy_wrappers.rb
@@ -1,4 +1,5 @@
require 'active_support/inflector/methods'
+require 'active_support/core_ext/regexp'
module ActiveSupport
class Deprecation
@@ -10,7 +11,7 @@ module ActiveSupport
super
end
- instance_methods.each { |m| undef_method m unless m =~ /^__|^object_id$/ }
+ instance_methods.each { |m| undef_method m unless /^__|^object_id$/.match?(m) }
# Don't give a deprecation warning on inspect since test/unit and error
# logs rely on it for diagnostics.
diff --git a/activesupport/lib/active_support/duration/iso8601_parser.rb b/activesupport/lib/active_support/duration/iso8601_parser.rb
index 07af58ad99..12515633fc 100644
--- a/activesupport/lib/active_support/duration/iso8601_parser.rb
+++ b/activesupport/lib/active_support/duration/iso8601_parser.rb
@@ -1,4 +1,5 @@
require 'strscan'
+require 'active_support/core_ext/regexp'
module ActiveSupport
class Duration
@@ -85,7 +86,7 @@ module ActiveSupport
# Parses number which can be a float with either comma or period.
def number
- scanner[1] =~ PERIOD_OR_COMMA ? scanner[1].tr(COMMA, PERIOD).to_f : scanner[1].to_i
+ PERIOD_OR_COMMA.match?(scanner[1]) ? scanner[1].tr(COMMA, PERIOD).to_f : scanner[1].to_i
end
def scan(pattern)
diff --git a/activesupport/lib/active_support/inflector/methods.rb b/activesupport/lib/active_support/inflector/methods.rb
index f14b8b81b7..34131a704a 100644
--- a/activesupport/lib/active_support/inflector/methods.rb
+++ b/activesupport/lib/active_support/inflector/methods.rb
@@ -1,4 +1,5 @@
require 'active_support/inflections'
+require 'active_support/core_ext/regexp'
module ActiveSupport
# The Inflector transforms words from singular to plural, class names to table
@@ -87,7 +88,7 @@ module ActiveSupport
#
# camelize(underscore('SSLError')) # => "SslError"
def underscore(camel_cased_word)
- return camel_cased_word unless camel_cased_word =~ /[A-Z-]|::/
+ return camel_cased_word unless /[A-Z-]|::/.match?(camel_cased_word)
word = camel_cased_word.to_s.gsub('::'.freeze, '/'.freeze)
word.gsub!(/(?:(?<=([A-Za-z\d]))|\b)(#{inflections.acronym_regex})(?=\b|[^a-z])/) { "#{$1 && '_'.freeze }#{$2.downcase}" }
word.gsub!(/([A-Z\d]+)([A-Z][a-z])/, '\1_\2'.freeze)
@@ -313,7 +314,7 @@ module ActiveSupport
raise if e.name && !(camel_cased_word.to_s.split("::").include?(e.name.to_s) ||
e.name.to_s == camel_cased_word.to_s)
rescue ArgumentError => e
- raise unless e.message =~ /not missing constant #{const_regexp(camel_cased_word)}\!$/
+ raise unless /not missing constant #{const_regexp(camel_cased_word)}!$/.match?(e.message)
end
# Returns the suffix that should be added to a number to denote the position
diff --git a/activesupport/lib/active_support/message_encryptor.rb b/activesupport/lib/active_support/message_encryptor.rb
index 721efea789..87efe117c5 100644
--- a/activesupport/lib/active_support/message_encryptor.rb
+++ b/activesupport/lib/active_support/message_encryptor.rb
@@ -1,6 +1,7 @@
require 'openssl'
require 'base64'
require 'active_support/core_ext/array/extract_options'
+require 'active_support/message_verifier'
module ActiveSupport
# MessageEncryptor is a simple way to encrypt values which get stored
@@ -28,6 +29,16 @@ module ActiveSupport
end
end
+ module NullVerifier #:nodoc:
+ def self.verify(value)
+ value
+ end
+
+ def self.generate(value)
+ value
+ end
+ end
+
class InvalidMessage < StandardError; end
OpenSSLCipherError = OpenSSL::Cipher::CipherError
@@ -40,7 +51,8 @@ module ActiveSupport
# Options:
# * <tt>:cipher</tt> - Cipher to use. Can be any cipher returned by
# <tt>OpenSSL::Cipher.ciphers</tt>. Default is 'aes-256-cbc'.
- # * <tt>:digest</tt> - String of digest to use for signing. Default is +SHA1+.
+ # * <tt>:digest</tt> - String of digest to use for signing. Default is
+ # +SHA1+. Ignored when using an AEAD cipher like 'aes-256-gcm'.
# * <tt>:serializer</tt> - Object serializer to use. Default is +Marshal+.
def initialize(secret, *signature_key_or_options)
options = signature_key_or_options.extract_options!
@@ -48,7 +60,8 @@ module ActiveSupport
@secret = secret
@sign_secret = sign_secret
@cipher = options[:cipher] || 'aes-256-cbc'
- @verifier = MessageVerifier.new(@sign_secret || @secret, digest: options[:digest] || 'SHA1', serializer: NullSerializer)
+ @digest = options[:digest] || 'SHA1' unless aead_mode?
+ @verifier = resolve_verifier
@serializer = options[:serializer] || Marshal
end
@@ -73,20 +86,28 @@ module ActiveSupport
# Rely on OpenSSL for the initialization vector
iv = cipher.random_iv
+ cipher.auth_data = "" if aead_mode?
encrypted_data = cipher.update(@serializer.dump(value))
encrypted_data << cipher.final
- "#{::Base64.strict_encode64 encrypted_data}--#{::Base64.strict_encode64 iv}"
+ blob = "#{::Base64.strict_encode64 encrypted_data}--#{::Base64.strict_encode64 iv}"
+ blob << "--#{::Base64.strict_encode64 cipher.auth_tag}" if aead_mode?
+ blob
end
def _decrypt(encrypted_message)
cipher = new_cipher
- encrypted_data, iv = encrypted_message.split("--".freeze).map {|v| ::Base64.strict_decode64(v)}
+ encrypted_data, iv, auth_tag = encrypted_message.split("--".freeze).map {|v| ::Base64.strict_decode64(v)}
+ raise InvalidMessage if aead_mode? && auth_tag.bytes.length != 16
cipher.decrypt
cipher.key = @secret
cipher.iv = iv
+ if aead_mode?
+ cipher.auth_tag = auth_tag
+ cipher.auth_data = ""
+ end
decrypted_data = cipher.update(encrypted_data)
decrypted_data << cipher.final
@@ -103,5 +124,17 @@ module ActiveSupport
def verifier
@verifier
end
+
+ def aead_mode?
+ @aead_mode ||= new_cipher.authenticated?
+ end
+
+ def resolve_verifier
+ if aead_mode?
+ NullVerifier
+ else
+ MessageVerifier.new(@sign_secret || @secret, digest: @digest, serializer: NullSerializer)
+ end
+ end
end
end
diff --git a/activesupport/lib/active_support/message_verifier.rb b/activesupport/lib/active_support/message_verifier.rb
index 4c3deffe6e..a421c92441 100644
--- a/activesupport/lib/active_support/message_verifier.rb
+++ b/activesupport/lib/active_support/message_verifier.rb
@@ -83,7 +83,7 @@ module ActiveSupport
data = signed_message.split("--".freeze)[0]
@serializer.load(decode(data))
rescue ArgumentError => argument_error
- return if argument_error.message =~ %r{invalid base64}
+ return if argument_error.message.include?('invalid base64')
raise
end
end
diff --git a/activesupport/lib/active_support/multibyte/chars.rb b/activesupport/lib/active_support/multibyte/chars.rb
index 707cf200b5..83db68a66f 100644
--- a/activesupport/lib/active_support/multibyte/chars.rb
+++ b/activesupport/lib/active_support/multibyte/chars.rb
@@ -2,6 +2,7 @@ require 'active_support/json'
require 'active_support/core_ext/string/access'
require 'active_support/core_ext/string/behavior'
require 'active_support/core_ext/module/delegation'
+require 'active_support/core_ext/regexp'
module ActiveSupport #:nodoc:
module Multibyte #:nodoc:
@@ -56,7 +57,7 @@ module ActiveSupport #:nodoc:
# Forward all undefined methods to the wrapped string.
def method_missing(method, *args, &block)
result = @wrapped_string.__send__(method, *args, &block)
- if method.to_s =~ /!$/
+ if /!$/.match?(method)
self if result
else
result.kind_of?(String) ? chars(result) : result
diff --git a/activesupport/lib/active_support/testing/deprecation.rb b/activesupport/lib/active_support/testing/deprecation.rb
index 5dfa14eeba..643b32939d 100644
--- a/activesupport/lib/active_support/testing/deprecation.rb
+++ b/activesupport/lib/active_support/testing/deprecation.rb
@@ -1,4 +1,5 @@
require 'active_support/deprecation'
+require 'active_support/core_ext/regexp'
module ActiveSupport
module Testing
@@ -8,7 +9,7 @@ module ActiveSupport
assert !warnings.empty?, "Expected a deprecation warning within the block but received none"
if match
match = Regexp.new(Regexp.escape(match)) unless match.is_a?(Regexp)
- assert warnings.any? { |w| w =~ match }, "No deprecation warning matched #{match}: #{warnings.join(', ')}"
+ assert warnings.any? { |w| match.match?(w) }, "No deprecation warning matched #{match}: #{warnings.join(', ')}"
end
result
end
diff --git a/activesupport/lib/active_support/xml_mini/jdom.rb b/activesupport/lib/active_support/xml_mini/jdom.rb
index 94751bbc04..2b6162f553 100644
--- a/activesupport/lib/active_support/xml_mini/jdom.rb
+++ b/activesupport/lib/active_support/xml_mini/jdom.rb
@@ -1,4 +1,4 @@
-raise "JRuby is required to use the JDOM backend for XmlMini" unless RUBY_PLATFORM =~ /java/
+raise "JRuby is required to use the JDOM backend for XmlMini" unless RUBY_PLATFORM.include?('java')
require 'jruby'
include Java
diff --git a/activesupport/test/clean_backtrace_test.rb b/activesupport/test/clean_backtrace_test.rb
index 05580352a9..14cf65ef2d 100644
--- a/activesupport/test/clean_backtrace_test.rb
+++ b/activesupport/test/clean_backtrace_test.rb
@@ -26,7 +26,7 @@ end
class BacktraceCleanerSilencerTest < ActiveSupport::TestCase
def setup
@bc = ActiveSupport::BacktraceCleaner.new
- @bc.add_silencer { |line| line =~ /mongrel/ }
+ @bc.add_silencer { |line| line.include?('mongrel') }
end
test "backtrace should not contain lines that match the silencer" do
@@ -44,8 +44,8 @@ end
class BacktraceCleanerMultipleSilencersTest < ActiveSupport::TestCase
def setup
@bc = ActiveSupport::BacktraceCleaner.new
- @bc.add_silencer { |line| line =~ /mongrel/ }
- @bc.add_silencer { |line| line =~ /yolo/ }
+ @bc.add_silencer { |line| line.include?('mongrel') }
+ @bc.add_silencer { |line| line.include?('yolo') }
end
test "backtrace should not contain lines that match the silencers" do
@@ -66,7 +66,7 @@ class BacktraceCleanerFilterAndSilencerTest < ActiveSupport::TestCase
def setup
@bc = ActiveSupport::BacktraceCleaner.new
@bc.add_filter { |line| line.gsub("/mongrel", "") }
- @bc.add_silencer { |line| line =~ /mongrel/ }
+ @bc.add_silencer { |line| line.include?('mongrel') }
end
test "backtrace should not silence lines that has first had their silence hook filtered out" do
diff --git a/activesupport/test/core_ext/object/exclusion_test.rb b/activesupport/test/core_ext/object/exclusion_test.rb
new file mode 100644
index 0000000000..487c97d255
--- /dev/null
+++ b/activesupport/test/core_ext/object/exclusion_test.rb
@@ -0,0 +1,53 @@
+require 'abstract_unit'
+require 'active_support/core_ext/object/exclusion'
+
+class NotInTest < ActiveSupport::TestCase
+ def test_not_in_array
+ assert 1.not_in?([2, 3])
+ assert_not 2.not_in?([1,2])
+ end
+
+ def test_not_in_hash
+ h = { "a" => 100, "b" => 200 }
+ assert "z".not_in?(h)
+ assert_not "a".not_in?(h)
+ end
+
+ def test_not_in_string
+ assert "ol".not_in?("hello")
+ assert_not "lo".not_in?("hello")
+ assert ?z.not_in?("hello")
+ end
+
+ def test_not_in_range
+ assert 75.not_in?(1..50)
+ assert_not 25.not_in?(1..50)
+ end
+
+ def test_not_in_set
+ s = Set.new([1,2])
+ assert 3.not_in?(s)
+ assert_not 1.not_in?(s)
+ end
+
+ module A
+ end
+ class B
+ include A
+ end
+ class C < B
+ end
+ class D
+ end
+
+ def test_not_in_module
+ assert A.not_in?(D)
+ assert A.not_in?(A)
+ assert_not A.not_in?(B)
+ assert_not A.not_in?(C)
+ end
+
+ def test_no_method_catching
+ assert_raise(ArgumentError) { 1.not_in?(1) }
+ end
+end
diff --git a/activesupport/test/core_ext/regexp_ext_test.rb b/activesupport/test/core_ext/regexp_ext_test.rb
index c2398d31bd..d91e363085 100644
--- a/activesupport/test/core_ext/regexp_ext_test.rb
+++ b/activesupport/test/core_ext/regexp_ext_test.rb
@@ -7,4 +7,28 @@ class RegexpExtAccessTests < ActiveSupport::TestCase
assert_equal false, //.multiline?
assert_equal false, /(?m:)/.multiline?
end
+
+ # Based on https://github.com/ruby/ruby/blob/trunk/test/ruby/test_regexp.rb.
+ def test_match_p
+ /back(...)/ =~ 'backref'
+ # must match here, but not in a separate method, e.g., assert_send,
+ # to check if $~ is affected or not.
+ assert_equal false, //.match?(nil)
+ assert_equal true, //.match?("")
+ assert_equal true, /.../.match?(:abc)
+ assert_raise(TypeError) { /.../.match?(Object.new) }
+ assert_equal true, /b/.match?('abc')
+ assert_equal true, /b/.match?('abc', 1)
+ assert_equal true, /../.match?('abc', 1)
+ assert_equal true, /../.match?('abc', -2)
+ assert_equal false, /../.match?("abc", -4)
+ assert_equal false, /../.match?("abc", 4)
+ assert_equal true, /\z/.match?("")
+ assert_equal true, /\z/.match?("abc")
+ assert_equal true, /R.../.match?("Ruby")
+ assert_equal false, /R.../.match?("Ruby", 1)
+ assert_equal false, /P.../.match?("Ruby")
+ assert_equal 'backref', $&
+ assert_equal 'ref', $1
+ end
end
diff --git a/activesupport/test/json/encoding_test.rb b/activesupport/test/json/encoding_test.rb
index 5fc2e16336..8cb3b1a9bb 100644
--- a/activesupport/test/json/encoding_test.rb
+++ b/activesupport/test/json/encoding_test.rb
@@ -1,6 +1,7 @@
require 'securerandom'
require 'abstract_unit'
require 'active_support/core_ext/string/inflections'
+require 'active_support/core_ext/regexp'
require 'active_support/json'
require 'active_support/time'
require 'time_zone_test_helpers'
@@ -10,8 +11,11 @@ class TestJSONEncoding < ActiveSupport::TestCase
include TimeZoneTestHelpers
def sorted_json(json)
- return json unless json =~ /^\{.*\}$/
- '{' + json[1..-2].split(',').sort.join(',') + '}'
+ if json.start_with?('{') && json.end_with?('}')
+ '{' + json[1..-2].split(',').sort.join(',') + '}'
+ else
+ json
+ end
end
JSONTest::EncodingTestCases.constants.each do |class_tests|
@@ -19,8 +23,10 @@ class TestJSONEncoding < ActiveSupport::TestCase
begin
prev = ActiveSupport.use_standard_json_time_format
- ActiveSupport.escape_html_entities_in_json = class_tests !~ /^Standard/
- ActiveSupport.use_standard_json_time_format = class_tests =~ /^Standard/
+ standard_class_tests = /Standard/.match?(class_tests)
+
+ ActiveSupport.escape_html_entities_in_json = !standard_class_tests
+ ActiveSupport.use_standard_json_time_format = standard_class_tests
JSONTest::EncodingTestCases.const_get(class_tests).each do |pair|
assert_equal pair.last, sorted_json(ActiveSupport::JSON.encode(pair.first))
end
diff --git a/activesupport/test/message_encryptor_test.rb b/activesupport/test/message_encryptor_test.rb
index a1ff4c1d3e..5dfa187f36 100644
--- a/activesupport/test/message_encryptor_test.rb
+++ b/activesupport/test/message_encryptor_test.rb
@@ -70,6 +70,24 @@ class MessageEncryptorTest < ActiveSupport::TestCase
assert_not_verified([iv, message] * bad_encoding_characters)
end
+ def test_aead_mode_encryption
+ encryptor = ActiveSupport::MessageEncryptor.new(@secret, cipher: 'aes-256-gcm')
+ message = encryptor.encrypt_and_sign(@data)
+ assert_equal @data, encryptor.decrypt_and_verify(message)
+ end
+
+ def test_messing_with_aead_values_causes_failures
+ encryptor = ActiveSupport::MessageEncryptor.new(@secret, cipher: 'aes-256-gcm')
+ text, iv, auth_tag = encryptor.encrypt_and_sign(@data).split("--")
+ assert_not_decrypted([iv, text, auth_tag] * "--")
+ assert_not_decrypted([munge(text), iv, auth_tag] * "--")
+ assert_not_decrypted([text, munge(iv), auth_tag] * "--")
+ assert_not_decrypted([text, iv, munge(auth_tag)] * "--")
+ assert_not_decrypted([munge(text), munge(iv), munge(auth_tag)] * "--")
+ assert_not_decrypted([text, iv] * "--")
+ assert_not_decrypted([text, iv, auth_tag[0..-2]] * "--")
+ end
+
private
def assert_not_decrypted(value)
diff --git a/activesupport/test/multibyte_conformance_test.rb b/activesupport/test/multibyte_conformance_test.rb
index c10133a7a6..50ec6442ff 100644
--- a/activesupport/test/multibyte_conformance_test.rb
+++ b/activesupport/test/multibyte_conformance_test.rb
@@ -87,7 +87,7 @@ class MultibyteConformanceTest < ActiveSupport::TestCase
File.open(File.join(CACHE_DIR, UNIDATA_FILE), 'r') do | f |
until f.eof?
line = f.gets.chomp!
- next if (line.empty? || line =~ /^\#/)
+ next if line.empty? || line.start_with?('#')
cols, comment = line.split("#")
cols = cols.split(";").map(&:strip).reject(&:empty?)
diff --git a/activesupport/test/multibyte_grapheme_break_conformance_test.rb b/activesupport/test/multibyte_grapheme_break_conformance_test.rb
index 61943b1ab3..1d52a56971 100644
--- a/activesupport/test/multibyte_grapheme_break_conformance_test.rb
+++ b/activesupport/test/multibyte_grapheme_break_conformance_test.rb
@@ -36,7 +36,7 @@ class MultibyteGraphemeBreakConformanceTest < ActiveSupport::TestCase
until f.eof? || (max_test_lines > 21 and lines > max_test_lines)
lines += 1
line = f.gets.chomp!
- next if (line.empty? || line =~ /^\#/)
+ next if line.empty? || line.start_with?('#')
cols, comment = line.split("#")
# Cluster breaks are represented by ÷
diff --git a/activesupport/test/multibyte_normalization_conformance_test.rb b/activesupport/test/multibyte_normalization_conformance_test.rb
index 77ed0ce6de..4b940a2054 100644
--- a/activesupport/test/multibyte_normalization_conformance_test.rb
+++ b/activesupport/test/multibyte_normalization_conformance_test.rb
@@ -91,7 +91,7 @@ class MultibyteNormalizationConformanceTest < ActiveSupport::TestCase
until f.eof? || (max_test_lines > 38 and lines > max_test_lines)
lines += 1
line = f.gets.chomp!
- next if (line.empty? || line =~ /^\#/)
+ next if line.empty? || line.start_with?('#')
cols, comment = line.split("#")
cols = cols.split(";").map{|e| e.strip}.reject{|e| e.empty? }
diff --git a/activesupport/test/xml_mini/jdom_engine_test.rb b/activesupport/test/xml_mini/jdom_engine_test.rb
index ed4de8aba2..34e213b718 100644
--- a/activesupport/test/xml_mini/jdom_engine_test.rb
+++ b/activesupport/test/xml_mini/jdom_engine_test.rb
@@ -1,4 +1,4 @@
-if RUBY_PLATFORM =~ /java/
+if RUBY_PLATFORM.include?('java')
require 'abstract_unit'
require 'active_support/xml_mini'
require 'active_support/core_ext/hash/conversions'
diff --git a/guides/source/active_support_core_extensions.md b/guides/source/active_support_core_extensions.md
index e0b6f2f820..27478b21a0 100644
--- a/guides/source/active_support_core_extensions.md
+++ b/guides/source/active_support_core_extensions.md
@@ -2916,6 +2916,24 @@ end
NOTE: Defined in `active_support/core_ext/regexp.rb`.
+### `match?`
+
+Rails implements `Regexp#match?` for Ruby versions prior to 2.4:
+
+```ruby
+/oo/.match?('foo') # => true
+/oo/.match?('bar') # => false
+/oo/.match?('foo', 1) # => true
+```
+
+The backport has the same interface and lack of side-effects in the caller like
+not setting `$1` and friends, but it does not have the speed benefits. Its
+purpose is to be able to write 2.4 compatible code. Rails itself uses this
+predicate internally for example.
+
+Active Support defines `Regexp#match?` only if not present, so code running
+under 2.4 or later does run the original one and gets the performance boost.
+
Extensions to `Range`
---------------------
diff --git a/guides/source/caching_with_rails.md b/guides/source/caching_with_rails.md
index 6c734c1d78..cc84ecb216 100644
--- a/guides/source/caching_with_rails.md
+++ b/guides/source/caching_with_rails.md
@@ -198,11 +198,11 @@ render "comments/comments"
render 'comments/comments'
render('comments/comments')
-render "header" => render("comments/header")
+render "header" translates to render("comments/header")
-render(@topic) => render("topics/topic")
-render(topics) => render("topics/topic")
-render(message.topics) => render("topics/topic")
+render(@topic) translates to render("topics/topic")
+render(topics) translates to render("topics/topic")
+render(message.topics) translates to render("topics/topic")
```
On the other hand, some calls need to be changed to make caching work properly.
@@ -270,7 +270,7 @@ simply be explicit in a comment, like:
Sometimes you need to cache a particular value or query result instead of caching view fragments. Rails' caching mechanism works great for storing __any__ kind of information.
-The most efficient way to implement low-level caching is using the `Rails.cache.fetch` method. This method does both reading and writing to the cache. When passed only a single argument, the key is fetched and value from the cache is returned. If a block is passed, the result of the block will be cached to the given key and the result is returned.
+The most efficient way to implement low-level caching is using the `Rails.cache.fetch` method. This method does both reading and writing to the cache. When passed only a single argument, the key is fetched and value from the cache is returned. If a block is passed, that block will be executed in the event of a cache miss. The return value of the block will be written to the cache under the given cache key, and that return value will be returned. In case of cache hit, the cached value will be returned without executing the block.
Consider the following example. An application has a `Product` model with an instance method that looks up the product’s price on a competing website. The data returned by this method would be perfect for low-level caching:
diff --git a/railties/CHANGELOG.md b/railties/CHANGELOG.md
index 1694abb61a..70f4b84237 100644
--- a/railties/CHANGELOG.md
+++ b/railties/CHANGELOG.md
@@ -1,3 +1,7 @@
+* A generated app should not include Uglifier with `--skip-javascript` option.
+
+ *Ben Pickles*
+
* Set session store to cookie store internally and remove the initializer from
the generated app.
diff --git a/railties/lib/rails/generators/app_base.rb b/railties/lib/rails/generators/app_base.rb
index af3c6dead3..8d4056c202 100644
--- a/railties/lib/rails/generators/app_base.rb
+++ b/railties/lib/rails/generators/app_base.rb
@@ -299,9 +299,11 @@ module Rails
gems << GemfileEntry.github('sass-rails', 'rails/sass-rails', nil,
'Use SCSS for stylesheets')
- gems << GemfileEntry.version('uglifier',
- '>= 1.3.0',
- 'Use Uglifier as compressor for JavaScript assets')
+ if !options[:skip_javascript]
+ gems << GemfileEntry.version('uglifier',
+ '>= 1.3.0',
+ 'Use Uglifier as compressor for JavaScript assets')
+ end
gems
end
diff --git a/railties/lib/rails/generators/rails/app/templates/Gemfile b/railties/lib/rails/generators/rails/app/templates/Gemfile
index 86143ca1f1..7af5fcd3c1 100644
--- a/railties/lib/rails/generators/rails/app/templates/Gemfile
+++ b/railties/lib/rails/generators/rails/app/templates/Gemfile
@@ -35,7 +35,7 @@ group :development do
<%- if options.dev? || options.edge? -%>
gem 'web-console', github: 'rails/web-console'
<%- else -%>
- gem 'web-console'
+ gem 'web-console', '>= 3.3.0'
<%- end -%>
<%- end -%>
<% if depend_on_listen? -%>
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 363af05459..7deab5dbb1 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
@@ -19,8 +19,12 @@ Rails.application.configure do
config.public_file_server.enabled = ENV['RAILS_SERVE_STATIC_FILES'].present?
<%- unless options.skip_sprockets? -%>
+ <%- if options.skip_javascript? -%>
+ # Compress CSS.
+ <%- else -%>
# Compress JavaScripts and CSS.
config.assets.js_compressor = :uglifier
+ <%- end -%>
# config.assets.css_compressor = :sass
# Do not fallback to assets pipeline if a precompiled asset is missed.
diff --git a/railties/test/application/integration_test_case_test.rb b/railties/test/application/integration_test_case_test.rb
index 52df5a2e5a..68e2de80c4 100644
--- a/railties/test/application/integration_test_case_test.rb
+++ b/railties/test/application/integration_test_case_test.rb
@@ -42,4 +42,32 @@ module ApplicationTests
assert_match(/0 failures, 0 errors/, output)
end
end
+
+ class IntegrationTestDefaultApp < ActiveSupport::TestCase
+ include ActiveSupport::Testing::Isolation
+
+ setup do
+ build_app
+ end
+
+ teardown do
+ teardown_app
+ end
+
+ test "app method of integration tests returns test_app by default" do
+ app_file 'test/integration/default_app_test.rb', <<-RUBY
+ require 'test_helper'
+
+ class DefaultAppIntegrationTest < ActionDispatch::IntegrationTest
+ def test_app_returns_action_dispatch_test_app_by_default
+ assert_equal ActionDispatch.test_app, app
+ end
+ end
+ RUBY
+
+ output = Dir.chdir(app_path) { `bin/rails test 2>&1` }
+ assert_equal 0, $?.to_i, output
+ assert_match(/0 failures, 0 errors/, output)
+ end
+ end
end
diff --git a/railties/test/generators/app_generator_test.rb b/railties/test/generators/app_generator_test.rb
index 50751368c8..09309b5cd0 100644
--- a/railties/test/generators/app_generator_test.rb
+++ b/railties/test/generators/app_generator_test.rb
@@ -468,6 +468,11 @@ class AppGeneratorTest < Rails::Generators::TestCase
assert_file "Gemfile" do |content|
assert_no_match(/coffee-rails/, content)
assert_no_match(/jquery-rails/, content)
+ assert_no_match(/uglifier/, content)
+ end
+
+ assert_file "config/environments/production.rb" do |content|
+ assert_no_match(/config\.assets\.js_compressor = :uglifier/, content)
end
end
@@ -574,7 +579,7 @@ class AppGeneratorTest < Rails::Generators::TestCase
assert_file "Gemfile" do |content|
assert_match(/gem 'web-console',\s+github: 'rails\/web-console'/, content)
- assert_no_match(/\Agem 'web-console'\z/, content)
+ assert_no_match(/\Agem 'web-console', '>= 3.3.0'\z/, content)
end
end
@@ -583,7 +588,7 @@ class AppGeneratorTest < Rails::Generators::TestCase
assert_file "Gemfile" do |content|
assert_match(/gem 'web-console',\s+github: 'rails\/web-console'/, content)
- assert_no_match(/\Agem 'web-console'\z/, content)
+ assert_no_match(/\Agem 'web-console', '>= 3.3.0'\z/, content)
end
end