aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Gemfile3
-rw-r--r--Gemfile.lock12
-rw-r--r--actionpack/CHANGELOG.md10
-rw-r--r--actionpack/lib/action_controller/test_case.rb105
-rw-r--r--actionpack/lib/action_dispatch/http/parameter_filter.rb24
-rw-r--r--actionpack/lib/action_dispatch/middleware/cookies.rb14
-rw-r--r--actionpack/lib/action_dispatch/testing/assertions/routing.rb2
-rw-r--r--actionpack/lib/action_dispatch/testing/test_process.rb2
-rw-r--r--actionpack/lib/action_dispatch/testing/test_request.rb31
-rw-r--r--actionpack/test/controller/base_test.rb2
-rw-r--r--actionpack/test/controller/caching_test.rb13
-rw-r--r--actionpack/test/controller/filters_test.rb7
-rw-r--r--actionpack/test/controller/helper_test.rb19
-rw-r--r--actionpack/test/controller/mime/respond_to_test.rb79
-rw-r--r--actionpack/test/controller/request/test_request_test.rb6
-rw-r--r--actionpack/test/controller/request_forgery_protection_test.rb3
-rw-r--r--actionpack/test/controller/resources_test.rb4
-rw-r--r--actionpack/test/controller/send_file_test.rb2
-rw-r--r--actionpack/test/controller/test_case_test.rb19
-rw-r--r--actionpack/test/controller/url_rewriter_test.rb3
-rw-r--r--actionpack/test/dispatch/request_test.rb1
-rw-r--r--actionpack/test/dispatch/test_request_test.rb20
-rw-r--r--actionview/CHANGELOG.md5
-rw-r--r--actionview/lib/action_view/helpers/cache_helper.rb15
-rw-r--r--actionview/lib/action_view/helpers/form_options_helper.rb12
-rw-r--r--actionview/lib/action_view/template.rb17
-rw-r--r--actionview/lib/action_view/template/handlers/erb.rb11
-rw-r--r--actionview/lib/action_view/test_case.rb2
-rw-r--r--actionview/test/actionpack/controller/view_paths_test.rb2
-rw-r--r--actionview/test/template/template_test.rb8
-rw-r--r--activejob/lib/active_job/arguments.rb21
-rw-r--r--activejob/test/cases/argument_serialization_test.rb7
-rw-r--r--activemodel/README.rdoc2
-rw-r--r--activemodel/lib/active_model/validations.rb6
-rw-r--r--activerecord/CHANGELOG.md2
-rw-r--r--activerecord/lib/active_record/relation/calculations.rb5
-rw-r--r--activerecord/lib/active_record/relation/merger.rb4
-rw-r--r--activerecord/test/cases/nested_attributes_test.rb10
-rw-r--r--activerecord/test/cases/relations_test.rb19
-rw-r--r--activerecord/test/models/post.rb16
-rw-r--r--activesupport/lib/active_support/core_ext/module/concerning.rb2
-rw-r--r--activesupport/lib/active_support/ordered_options.rb8
-rw-r--r--activesupport/lib/active_support/testing/method_call_assertions.rb30
-rw-r--r--activesupport/test/testing/method_call_assertions_test.rb91
-rw-r--r--guides/source/action_mailer_basics.md18
-rw-r--r--guides/source/action_view_overview.md12
-rw-r--r--guides/source/active_record_validations.md5
-rw-r--r--guides/source/contributing_to_ruby_on_rails.md2
-rw-r--r--guides/source/security.md23
-rw-r--r--railties/lib/rails/generators/rails/app/app_generator.rb4
-rw-r--r--railties/lib/rails/generators/rails/plugin/plugin_generator.rb4
-rw-r--r--railties/lib/rails/generators/rails/plugin/templates/test/integration/navigation_test.rb4
-rw-r--r--railties/test/generators/plugin_generator_test.rb2
-rw-r--r--railties/test/generators/shared_generator_tests.rb2
54 files changed, 495 insertions, 257 deletions
diff --git a/Gemfile b/Gemfile
index 78224f60ed..de4159d254 100644
--- a/Gemfile
+++ b/Gemfile
@@ -5,6 +5,9 @@ gemspec
# We need a newish Rake since Active Job sets its test tasks' descriptions.
gem 'rake', '>= 10.3'
+# Active Job depends on the URI::GID::MissingModelIDError, which isn't released yet.
+gem 'globalid', github: 'rails/globalid'
+
# This needs to be with require false as it is
# loaded after loading the test library to
# ensure correct loading order
diff --git a/Gemfile.lock b/Gemfile.lock
index 2140881170..960a77af92 100644
--- a/Gemfile.lock
+++ b/Gemfile.lock
@@ -26,6 +26,13 @@ GIT
arel (7.0.0.alpha)
GIT
+ remote: git://github.com/rails/globalid.git
+ revision: 4df66fb9e9f0c832d29119aa8bc30be55a614b71
+ specs:
+ globalid (0.3.5)
+ activesupport (>= 4.1.0)
+
+GIT
remote: git://github.com/rails/jquery-rails.git
revision: 272abdd319bb3381b23182b928b25320590096b0
branch: master
@@ -138,8 +145,6 @@ GEM
delayed_job (>= 3.0, < 4.1)
erubis (2.7.0)
execjs (2.3.0)
- globalid (0.3.3)
- activesupport (>= 4.1.0)
hitimes (1.2.2)
hitimes (1.2.2-x86-mingw32)
i18n (0.7.0)
@@ -272,6 +277,7 @@ DEPENDENCIES
dalli (>= 2.2.1)
delayed_job
delayed_job_active_record
+ globalid!
jquery-rails!
json
kindlerb (= 0.1.1)
@@ -308,4 +314,4 @@ DEPENDENCIES
w3c_validators
BUNDLED WITH
- 1.10.4
+ 1.10.5
diff --git a/actionpack/CHANGELOG.md b/actionpack/CHANGELOG.md
index cb5e7516fb..46856ffc5d 100644
--- a/actionpack/CHANGELOG.md
+++ b/actionpack/CHANGELOG.md
@@ -1,3 +1,13 @@
+* Add ability to filter parameters based on parent keys.
+
+ # matches {credit_card: {code: "xxxx"}}
+ # doesn't match {file: { code: "xxxx"}}
+ config.filter_parameters += [ "credit_card.code" ]
+
+ See #13897.
+
+ *Guillaume Malette*
+
* Deprecate passing first parameter as `Hash` and default status code for `head` method.
*Mehmet Emin İNAÇ*
diff --git a/actionpack/lib/action_controller/test_case.rb b/actionpack/lib/action_controller/test_case.rb
index 96f161fb09..aec21eb35e 100644
--- a/actionpack/lib/action_controller/test_case.rb
+++ b/actionpack/lib/action_controller/test_case.rb
@@ -10,17 +10,42 @@ module ActionController
DEFAULT_ENV = ActionDispatch::TestRequest::DEFAULT_ENV.dup
DEFAULT_ENV.delete 'PATH_INFO'
- def initialize(env = {})
- super
+ def self.new_session
+ TestSession.new
+ end
+
+ # Create a new test request with default `env` values
+ def self.create
+ env = {}
+ env = Rails.application.env_config.merge(env) if defined?(Rails.application) && Rails.application
+ env["rack.request.cookie_hash"] = {}.with_indifferent_access
+ new(default_env.merge(env), new_session)
+ end
+
+ def self.default_env
+ DEFAULT_ENV
+ end
+ private_class_method :default_env
- self.session = TestSession.new
+ def initialize(env, session)
+ super(env)
+
+ self.session = session
self.session_options = TestSession::DEFAULT_OPTIONS
end
+ def query_parameters=(params)
+ @env["action_dispatch.request.query_parameters"] = params
+ end
+
+ def request_parameters=(params)
+ @env["action_dispatch.request.request_parameters"] = params
+ end
+
def assign_parameters(routes, controller_path, action, parameters = {})
parameters = parameters.symbolize_keys
extra_keys = routes.extra_keys(parameters.merge(:controller => controller_path, :action => action))
- non_path_parameters = get? ? query_parameters : request_parameters
+ non_path_parameters = {}.with_indifferent_access
parameters.each do |key, value|
if value.is_a?(Array) && (value.frozen? || value.any?(&:frozen?))
@@ -44,6 +69,12 @@ module ActionController
end
end
+ if get?
+ self.query_parameters = non_path_parameters
+ else
+ self.request_parameters = non_path_parameters
+ end
+
path_parameters[:controller] = controller_path
path_parameters[:action] = action
@@ -57,43 +88,12 @@ module ActionController
@env['CONTENT_LENGTH'] = data.length.to_s
@env['rack.input'] = StringIO.new(data)
end
-
- def recycle!
- @formats = nil
- @env.delete_if { |k, v| k =~ /^(action_dispatch|rack)\.request/ }
- @env.delete_if { |k, v| k =~ /^action_dispatch\.rescue/ }
- @method = @request_method = nil
- @fullpath = @ip = @remote_ip = @protocol = nil
- @env['action_dispatch.request.query_parameters'] = {}
- @set_cookies ||= {}
- @set_cookies.update(Hash[cookie_jar.instance_variable_get("@set_cookies").map{ |k,o| [k,o[:value]] }])
- deleted_cookies = cookie_jar.instance_variable_get("@delete_cookies")
- @set_cookies.reject!{ |k,v| deleted_cookies.include?(k) }
- cookie_jar.update(rack_cookies)
- cookie_jar.update(cookies)
- cookie_jar.update(@set_cookies)
- cookie_jar.recycle!
- end
-
- private
-
- def default_env
- DEFAULT_ENV
- end
end
class TestResponse < ActionDispatch::TestResponse
- def recycle!
- initialize
- end
end
class LiveTestResponse < Live::Response
- def recycle!
- @body = nil
- initialize
- end
-
def body
@body ||= super
end
@@ -317,7 +317,9 @@ module ActionController
# Note that the request method is not verified. The different methods are
# available to make the tests more expressive.
def get(action, *args)
- process_with_kwargs("GET", action, *args)
+ res = process_with_kwargs("GET", action, *args)
+ cookies.update res.cookies
+ res
end
# Simulate a POST request with the given parameters and set/volley the response.
@@ -451,8 +453,13 @@ module ActionController
@controller.extend(Testing::Functional)
end
- @request.recycle!
- @response.recycle!
+ self.cookies.update @request.cookies
+ @request.env['HTTP_COOKIE'] = cookies.to_header
+ @request.env['action_dispatch.cookies'] = nil
+
+ @request = TestRequest.new scrub_env!(@request.env), @request.session
+ @response = build_response @response_klass
+ @response.request = @request
@controller.recycle!
@request.env['REQUEST_METHOD'] = http_method
@@ -479,9 +486,12 @@ module ActionController
@controller.recycle!
@controller.process(action)
+ @request.env.delete 'HTTP_COOKIE'
+
if cookies = @request.env['action_dispatch.cookies']
unless @response.committed?
cookies.write(@response)
+ self.cookies.update(cookies.instance_variable_get(:@cookies))
end
end
@response.prepare!
@@ -503,11 +513,11 @@ module ActionController
def setup_controller_request_and_response
@controller = nil unless defined? @controller
- response_klass = TestResponse
+ @response_klass = TestResponse
if klass = self.class.controller_class
if klass < ActionController::Live
- response_klass = LiveTestResponse
+ @response_klass = LiveTestResponse
end
unless @controller
begin
@@ -518,8 +528,8 @@ module ActionController
end
end
- @request = build_request
- @response = build_response response_klass
+ @request = TestRequest.create
+ @response = build_response @response_klass
@response.request = @request
if @controller
@@ -528,10 +538,6 @@ module ActionController
end
end
- def build_request
- TestRequest.new
- end
-
def build_response(klass)
klass.new
end
@@ -545,6 +551,13 @@ module ActionController
private
+ def scrub_env!(env)
+ env.delete_if { |k, v| k =~ /^(action_dispatch|rack)\.request/ }
+ env.delete_if { |k, v| k =~ /^action_dispatch\.rescue/ }
+ env['action_dispatch.request.query_parameters'] = {}
+ env
+ end
+
def process_with_kwargs(http_method, action, *args)
if kwarg_request?(args)
args.first.merge!(method: http_method)
diff --git a/actionpack/lib/action_dispatch/http/parameter_filter.rb b/actionpack/lib/action_dispatch/http/parameter_filter.rb
index df4b073a17..6e058b829e 100644
--- a/actionpack/lib/action_dispatch/http/parameter_filter.rb
+++ b/actionpack/lib/action_dispatch/http/parameter_filter.rb
@@ -30,36 +30,46 @@ module ActionDispatch
when Regexp
regexps << item
else
- strings << item.to_s
+ strings << Regexp.escape(item.to_s)
end
end
+ deep_regexps, regexps = regexps.partition { |r| r.to_s.include?("\\.") }
+ deep_strings, strings = strings.partition { |s| s.include?("\\.") }
+
regexps << Regexp.new(strings.join('|'), true) unless strings.empty?
- new regexps, blocks
+ deep_regexps << Regexp.new(deep_strings.join('|'), true) unless deep_strings.empty?
+
+ new regexps, deep_regexps, blocks
end
- attr_reader :regexps, :blocks
+ attr_reader :regexps, :deep_regexps, :blocks
- def initialize(regexps, blocks)
+ def initialize(regexps, deep_regexps, blocks)
@regexps = regexps
+ @deep_regexps = deep_regexps.any? ? deep_regexps : nil
@blocks = blocks
end
- def call(original_params)
+ def call(original_params, parents = [])
filtered_params = {}
original_params.each do |key, value|
+ parents.push(key) if deep_regexps
if regexps.any? { |r| key =~ r }
value = FILTERED
+ elsif deep_regexps && (joined = parents.join('.')) && deep_regexps.any? { |r| joined =~ r }
+ value = FILTERED
elsif value.is_a?(Hash)
- value = call(value)
+ value = call(value, parents)
elsif value.is_a?(Array)
- value = value.map { |v| v.is_a?(Hash) ? call(v) : v }
+ value = value.map { |v| v.is_a?(Hash) ? call(v, parents) : v }
elsif blocks.any?
key = key.dup if key.duplicable?
value = value.dup if value.duplicable?
blocks.each { |b| b.call(key, value) }
end
+ parents.pop if deep_regexps
filtered_params[key] = value
end
diff --git a/actionpack/lib/action_dispatch/middleware/cookies.rb b/actionpack/lib/action_dispatch/middleware/cookies.rb
index dd1f140051..07d97bd6bd 100644
--- a/actionpack/lib/action_dispatch/middleware/cookies.rb
+++ b/actionpack/lib/action_dispatch/middleware/cookies.rb
@@ -8,7 +8,7 @@ require 'active_support/json'
module ActionDispatch
class Request < Rack::Request
def cookie_jar
- env['action_dispatch.cookies'] ||= Cookies::CookieJar.build(self)
+ env['action_dispatch.cookies'] ||= Cookies::CookieJar.build(env, host, ssl?, cookies)
end
end
@@ -228,16 +228,12 @@ module ActionDispatch
}
end
- def self.build(request)
- env = request.env
+ def self.build(env, host, secure, cookies)
key_generator = env[GENERATOR_KEY]
options = options_for_env env
- host = request.host
- secure = request.ssl?
-
new(key_generator, host, secure, options).tap do |hash|
- hash.update(request.cookies)
+ hash.update(cookies)
end
end
@@ -283,6 +279,10 @@ module ActionDispatch
self
end
+ def to_header
+ @cookies.map { |k,v| "#{k}=#{v}" }.join ';'
+ end
+
def handle_options(options) #:nodoc:
options[:path] ||= "/"
diff --git a/actionpack/lib/action_dispatch/testing/assertions/routing.rb b/actionpack/lib/action_dispatch/testing/assertions/routing.rb
index c94eea9134..543c7b78a1 100644
--- a/actionpack/lib/action_dispatch/testing/assertions/routing.rb
+++ b/actionpack/lib/action_dispatch/testing/assertions/routing.rb
@@ -183,7 +183,7 @@ module ActionDispatch
end
# Assume given controller
- request = ActionController::TestRequest.new
+ request = ActionController::TestRequest.create
if path =~ %r{://}
fail_on(URI::InvalidURIError, msg) do
diff --git a/actionpack/lib/action_dispatch/testing/test_process.rb b/actionpack/lib/action_dispatch/testing/test_process.rb
index 415ef80cd2..494644cd46 100644
--- a/actionpack/lib/action_dispatch/testing/test_process.rb
+++ b/actionpack/lib/action_dispatch/testing/test_process.rb
@@ -19,7 +19,7 @@ module ActionDispatch
end
def cookies
- @request.cookie_jar
+ @cookie_jar ||= Cookies::CookieJar.build(@request.env, @request.host, @request.ssl?, @request.cookies)
end
def redirect_to_url
diff --git a/actionpack/lib/action_dispatch/testing/test_request.rb b/actionpack/lib/action_dispatch/testing/test_request.rb
index 4b9a088265..ad1a7f7109 100644
--- a/actionpack/lib/action_dispatch/testing/test_request.rb
+++ b/actionpack/lib/action_dispatch/testing/test_request.rb
@@ -4,19 +4,22 @@ require 'rack/utils'
module ActionDispatch
class TestRequest < Request
DEFAULT_ENV = Rack::MockRequest.env_for('/',
- 'HTTP_HOST' => 'test.host',
- 'REMOTE_ADDR' => '0.0.0.0',
- 'HTTP_USER_AGENT' => 'Rails Testing'
+ 'HTTP_HOST' => 'test.host',
+ 'REMOTE_ADDR' => '0.0.0.0',
+ 'HTTP_USER_AGENT' => 'Rails Testing',
)
- def self.new(env = {})
- super
+ # Create a new test request with default `env` values
+ def self.create(env = {})
+ env = Rails.application.env_config.merge(env) if defined?(Rails.application) && Rails.application
+ env["rack.request.cookie_hash"] ||= {}.with_indifferent_access
+ new(default_env.merge(env))
end
- def initialize(env = {})
- env = Rails.application.env_config.merge(env) if defined?(Rails.application) && Rails.application
- super(default_env.merge(env))
+ def self.default_env
+ DEFAULT_ENV
end
+ private_class_method :default_env
def request_method=(method)
@env['REQUEST_METHOD'] = method.to_s.upcase
@@ -62,17 +65,5 @@ module ActionDispatch
@env.delete('action_dispatch.request.accepts')
@env['HTTP_ACCEPT'] = Array(mime_types).collect(&:to_s).join(",")
end
-
- alias :rack_cookies :cookies
-
- def cookies
- @cookies ||= {}.with_indifferent_access
- end
-
- private
-
- def default_env
- DEFAULT_ENV
- end
end
end
diff --git a/actionpack/test/controller/base_test.rb b/actionpack/test/controller/base_test.rb
index 3240185414..b7fe253de7 100644
--- a/actionpack/test/controller/base_test.rb
+++ b/actionpack/test/controller/base_test.rb
@@ -127,8 +127,6 @@ class PerformActionTest < ActionController::TestCase
# a more accurate simulation of what happens in "real life".
@controller.logger = ActiveSupport::Logger.new(nil)
- @request = ActionController::TestRequest.new
- @response = ActionController::TestResponse.new
@request.host = "www.nextangle.com"
end
diff --git a/actionpack/test/controller/caching_test.rb b/actionpack/test/controller/caching_test.rb
index f21dd87400..5698159eba 100644
--- a/actionpack/test/controller/caching_test.rb
+++ b/actionpack/test/controller/caching_test.rb
@@ -22,8 +22,6 @@ class FragmentCachingMetalTest < ActionController::TestCase
@controller.perform_caching = true
@controller.cache_store = @store
@params = { controller: 'posts', action: 'index' }
- @request = ActionController::TestRequest.new
- @response = ActionController::TestResponse.new
@controller.params = @params
@controller.request = @request
@controller.response = @response
@@ -52,8 +50,6 @@ class FragmentCachingTest < ActionController::TestCase
@controller.perform_caching = true
@controller.cache_store = @store
@params = {:controller => 'posts', :action => 'index'}
- @request = ActionController::TestRequest.new
- @response = ActionController::TestResponse.new
@controller.params = @params
@controller.request = @request
@controller.response = @response
@@ -166,6 +162,8 @@ class FunctionalCachingController < CachingController
end
def formatted_fragment_cached_with_variant
+ request.variant = :phone if params[:v] == "phone"
+
respond_to do |format|
format.html.phone
format.html
@@ -183,8 +181,6 @@ class FunctionalFragmentCachingTest < ActionController::TestCase
@controller = FunctionalCachingController.new
@controller.perform_caching = true
@controller.cache_store = @store
- @request = ActionController::TestRequest.new
- @response = ActionController::TestResponse.new
end
def test_fragment_caching
@@ -268,9 +264,7 @@ CACHED
def test_fragment_caching_with_variant
- @request.variant = :phone
-
- get :formatted_fragment_cached_with_variant, format: "html"
+ get :formatted_fragment_cached_with_variant, format: "html", params: { v: :phone }
assert_response :success
expected_body = "<body>\n<p>PHONE</p>\n</body>\n"
@@ -381,6 +375,7 @@ class AutomaticCollectionCacheTest < ActionController::TestCase
@controller.perform_caching = true
@controller.partial_rendered_times = 0
@controller.cache_store = ActiveSupport::Cache::MemoryStore.new
+ ActionView::PartialRenderer.collection_cache = @controller.cache_store
end
def test_collection_fetches_cached_views
diff --git a/actionpack/test/controller/filters_test.rb b/actionpack/test/controller/filters_test.rb
index 9b0487841f..8a6a51c263 100644
--- a/actionpack/test/controller/filters_test.rb
+++ b/actionpack/test/controller/filters_test.rb
@@ -757,9 +757,8 @@ class FilterTest < ActionController::TestCase
def test_dynamic_dispatch
%w(foo bar baz).each do |action|
- request = ActionController::TestRequest.new
- request.query_parameters[:choose] = action
- response = DynamicDispatchController.action(action).call(request.env).last
+ @request.query_parameters[:choose] = action
+ response = DynamicDispatchController.action(action).call(@request.env).last
assert_equal action, response.body
end
end
@@ -839,8 +838,6 @@ class FilterTest < ActionController::TestCase
private
def test_process(controller, action = "show")
@controller = controller.is_a?(Class) ? controller.new : controller
- @request = ActionController::TestRequest.new
- @response = ActionController::TestResponse.new
process(action)
end
diff --git a/actionpack/test/controller/helper_test.rb b/actionpack/test/controller/helper_test.rb
index e263ed341f..a96cfa3bf4 100644
--- a/actionpack/test/controller/helper_test.rb
+++ b/actionpack/test/controller/helper_test.rb
@@ -73,14 +73,8 @@ module LocalAbcHelper
end
class HelperPathsTest < ActiveSupport::TestCase
- def setup
- @request = ActionController::TestRequest.new
- @response = ActionController::TestResponse.new
- end
-
def test_helpers_paths_priority
- request = ActionController::TestRequest.new
- responses = HelpersPathsController.action(:index).call(request.env)
+ responses = HelpersPathsController.action(:index).call(ActionController::TestRequest::DEFAULT_ENV.dup)
# helpers1_pack was given as a second path, so pack1_helper should be
# included as the second one
@@ -141,8 +135,7 @@ class HelperTest < ActiveSupport::TestCase
end
def call_controller(klass, action)
- request = ActionController::TestRequest.new
- klass.action(action).call(request.env)
+ klass.action(action).call(ActionController::TestRequest::DEFAULT_ENV.dup)
end
def test_helper_for_nested_controller
@@ -249,7 +242,7 @@ class HelperTest < ActiveSupport::TestCase
end
-class IsolatedHelpersTest < ActiveSupport::TestCase
+class IsolatedHelpersTest < ActionController::TestCase
class A < ActionController::Base
def index
render :inline => '<%= shout %>'
@@ -273,13 +266,11 @@ class IsolatedHelpersTest < ActiveSupport::TestCase
end
def call_controller(klass, action)
- request = ActionController::TestRequest.new
- klass.action(action).call(request.env)
+ klass.action(action).call(@request.env)
end
def setup
- @request = ActionController::TestRequest.new
- @response = ActionController::TestResponse.new
+ super
@request.action = 'index'
end
diff --git a/actionpack/test/controller/mime/respond_to_test.rb b/actionpack/test/controller/mime/respond_to_test.rb
index 8591bdb4d9..71660c87a2 100644
--- a/actionpack/test/controller/mime/respond_to_test.rb
+++ b/actionpack/test/controller/mime/respond_to_test.rb
@@ -4,6 +4,13 @@ require "active_support/log_subscriber/test_helper"
class RespondToController < ActionController::Base
layout :set_layout
+ before_action {
+ case params[:v]
+ when String then request.variant = params[:v].to_sym
+ when Array then request.variant = params[:v].map(&:to_sym)
+ end
+ }
+
def html_xml_or_rss
respond_to do |type|
type.html { render :text => "HTML" }
@@ -612,8 +619,7 @@ class RespondToControllerTest < ActionController::TestCase
logger = ActiveSupport::LogSubscriber::TestHelper::MockLogger.new
old_logger, ActionController::Base.logger = ActionController::Base.logger, logger
- @request.variant = :invalid
- get :variant_with_implicit_rendering
+ get :variant_with_implicit_rendering, params: { v: :invalid }
assert_response :no_content
assert_equal 1, logger.logged(:info).select{ |s| s =~ /No template found/ }.size, "Implicit head :no_content not logged"
ensure
@@ -626,28 +632,24 @@ class RespondToControllerTest < ActionController::TestCase
end
def test_variant_with_implicit_rendering
- @request.variant = :implicit
- get :variant_with_implicit_rendering
+ get :variant_with_implicit_rendering, params: { v: :implicit }
assert_response :no_content
end
def test_variant_with_implicit_template_rendering
- @request.variant = :mobile
- get :variant_with_implicit_rendering
+ get :variant_with_implicit_rendering, params: { v: :mobile }
assert_equal "text/html", @response.content_type
assert_equal "mobile", @response.body
end
def test_variant_with_format_and_custom_render
- @request.variant = :phone
- get :variant_with_format_and_custom_render
+ get :variant_with_format_and_custom_render, params: { v: :phone }
assert_equal "text/html", @response.content_type
assert_equal "mobile", @response.body
end
def test_multiple_variants_for_format
- @request.variant = :tablet
- get :multiple_variants_for_format
+ get :multiple_variants_for_format, params: { v: :tablet }
assert_equal "text/html", @response.content_type
assert_equal "tablet", @response.body
end
@@ -667,32 +669,27 @@ class RespondToControllerTest < ActionController::TestCase
assert_equal "text/html", @response.content_type
assert_equal "none", @response.body
- @request.variant = :phone
- get :variant_inline_syntax
+ get :variant_inline_syntax, params: { v: :phone }
assert_equal "text/html", @response.content_type
assert_equal "phone", @response.body
end
def test_variant_inline_syntax_without_block
- @request.variant = :phone
- get :variant_inline_syntax_without_block
+ get :variant_inline_syntax_without_block, params: { v: :phone }
assert_equal "text/html", @response.content_type
assert_equal "phone", @response.body
end
def test_variant_any
- @request.variant = :phone
- get :variant_any
+ get :variant_any, params: { v: :phone }
assert_equal "text/html", @response.content_type
assert_equal "phone", @response.body
- @request.variant = :tablet
- get :variant_any
+ get :variant_any, params: { v: :tablet }
assert_equal "text/html", @response.content_type
assert_equal "any", @response.body
- @request.variant = :phablet
- get :variant_any
+ get :variant_any, params: { v: :phablet }
assert_equal "text/html", @response.content_type
assert_equal "any", @response.body
end
@@ -702,54 +699,45 @@ class RespondToControllerTest < ActionController::TestCase
assert_equal "text/html", @response.content_type
assert_equal "any", @response.body
- @request.variant = :phone
- get :variant_any_any
+ get :variant_any_any, params: { v: :phone }
assert_equal "text/html", @response.content_type
assert_equal "phone", @response.body
- @request.variant = :yolo
- get :variant_any_any
+ get :variant_any_any, params: { v: :yolo }
assert_equal "text/html", @response.content_type
assert_equal "any", @response.body
end
def test_variant_inline_any
- @request.variant = :phone
- get :variant_any
+ get :variant_any, params: { v: :phone }
assert_equal "text/html", @response.content_type
assert_equal "phone", @response.body
- @request.variant = :tablet
- get :variant_inline_any
+ get :variant_inline_any, params: { v: :tablet }
assert_equal "text/html", @response.content_type
assert_equal "any", @response.body
- @request.variant = :phablet
- get :variant_inline_any
+ get :variant_inline_any, params: { v: :phablet }
assert_equal "text/html", @response.content_type
assert_equal "any", @response.body
end
def test_variant_inline_any_any
- @request.variant = :phone
- get :variant_inline_any_any
+ get :variant_inline_any_any, params: { v: :phone }
assert_equal "text/html", @response.content_type
assert_equal "phone", @response.body
- @request.variant = :yolo
- get :variant_inline_any_any
+ get :variant_inline_any_any, params: { v: :yolo }
assert_equal "text/html", @response.content_type
assert_equal "any", @response.body
end
def test_variant_any_implicit_render
- @request.variant = :tablet
- get :variant_any_implicit_render
+ get :variant_any_implicit_render, params: { v: :tablet }
assert_equal "text/html", @response.content_type
assert_equal "tablet", @response.body
- @request.variant = :phablet
- get :variant_any_implicit_render
+ get :variant_any_implicit_render, params: { v: :phablet }
assert_equal "text/html", @response.content_type
assert_equal "phablet", @response.body
end
@@ -759,36 +747,31 @@ class RespondToControllerTest < ActionController::TestCase
assert_equal "text/html", @response.content_type
assert_equal "none or phone", @response.body
- @request.variant = :phone
- get :variant_any_with_none
+ get :variant_any_with_none, params: { v: :phone }
assert_equal "text/html", @response.content_type
assert_equal "none or phone", @response.body
end
def test_format_any_variant_any
- @request.variant = :tablet
- get :format_any_variant_any, format: :js
+ get :format_any_variant_any, format: :js, params: { v: :tablet }
assert_equal "text/javascript", @response.content_type
assert_equal "tablet", @response.body
end
def test_variant_negotiation_inline_syntax
- @request.variant = [:tablet, :phone]
- get :variant_inline_syntax_without_block
+ get :variant_inline_syntax_without_block, params: { v: [:tablet, :phone] }
assert_equal "text/html", @response.content_type
assert_equal "phone", @response.body
end
def test_variant_negotiation_block_syntax
- @request.variant = [:tablet, :phone]
- get :variant_plus_none_for_format
+ get :variant_plus_none_for_format, params: { v: [:tablet, :phone] }
assert_equal "text/html", @response.content_type
assert_equal "phone", @response.body
end
def test_variant_negotiation_without_block
- @request.variant = [:tablet, :phone]
- get :variant_inline_syntax_without_block
+ get :variant_inline_syntax_without_block, params: { v: [:tablet, :phone] }
assert_equal "text/html", @response.content_type
assert_equal "phone", @response.body
end
diff --git a/actionpack/test/controller/request/test_request_test.rb b/actionpack/test/controller/request/test_request_test.rb
index 77a2f68b1c..e5d698d5c2 100644
--- a/actionpack/test/controller/request/test_request_test.rb
+++ b/actionpack/test/controller/request/test_request_test.rb
@@ -1,11 +1,7 @@
require 'abstract_unit'
require 'stringio'
-class ActionController::TestRequestTest < ActiveSupport::TestCase
-
- def setup
- @request = ActionController::TestRequest.new
- end
+class ActionController::TestRequestTest < ActionController::TestCase
def test_test_request_has_session_options_initialized
assert @request.session_options
diff --git a/actionpack/test/controller/request_forgery_protection_test.rb b/actionpack/test/controller/request_forgery_protection_test.rb
index 82c808754c..7c17aed632 100644
--- a/actionpack/test/controller/request_forgery_protection_test.rb
+++ b/actionpack/test/controller/request_forgery_protection_test.rb
@@ -484,11 +484,10 @@ end
class FreeCookieControllerTest < ActionController::TestCase
def setup
@controller = FreeCookieController.new
- @request = ActionController::TestRequest.new
- @response = ActionController::TestResponse.new
@token = "cf50faa3fe97702ca1ae"
SecureRandom.stubs(:base64).returns(@token)
+ super
end
def test_should_not_render_form_with_token_tag
diff --git a/actionpack/test/controller/resources_test.rb b/actionpack/test/controller/resources_test.rb
index f3da2df3ef..5a279639cc 100644
--- a/actionpack/test/controller/resources_test.rb
+++ b/actionpack/test/controller/resources_test.rb
@@ -1207,8 +1207,6 @@ class ResourcesTest < ActionController::TestCase
@controller = "#{options[:options][:controller].camelize}Controller".constantize.new
@controller.singleton_class.include(@routes.url_helpers)
- @request = ActionController::TestRequest.new
- @response = ActionController::TestResponse.new
get :index, params: options[:options]
options[:options].delete :action
@@ -1277,8 +1275,6 @@ class ResourcesTest < ActionController::TestCase
(options[:options] ||= {})[:controller] ||= singleton_name.to_s.pluralize
@controller = "#{options[:options][:controller].camelize}Controller".constantize.new
@controller.singleton_class.include(@routes.url_helpers)
- @request = ActionController::TestRequest.new
- @response = ActionController::TestResponse.new
get :show, params: options[:options]
options[:options].delete :action
diff --git a/actionpack/test/controller/send_file_test.rb b/actionpack/test/controller/send_file_test.rb
index 36c57ec9b2..c0ddcf7f50 100644
--- a/actionpack/test/controller/send_file_test.rb
+++ b/actionpack/test/controller/send_file_test.rb
@@ -34,8 +34,6 @@ class SendFileTest < ActionController::TestCase
def setup
@controller = SendFileController.new
- @request = ActionController::TestRequest.new
- @response = ActionController::TestResponse.new
end
def test_file_nostream
diff --git a/actionpack/test/controller/test_case_test.rb b/actionpack/test/controller/test_case_test.rb
index 86ffb898ad..531b9b0a81 100644
--- a/actionpack/test/controller/test_case_test.rb
+++ b/actionpack/test/controller/test_case_test.rb
@@ -49,7 +49,7 @@ class TestCaseTest < ActionController::TestCase
end
def test_query_parameters
- render text: request.query_parameters.inspect
+ render text: ::JSON.dump(request.query_parameters)
end
def test_request_parameters
@@ -158,8 +158,6 @@ XML
def setup
super
@controller = TestController.new
- @request = ActionController::TestRequest.new
- @response = ActionController::TestResponse.new
@request.env['PATH_INFO'] = nil
@routes = ActionDispatch::Routing::RouteSet.new.tap do |r|
r.draw do
@@ -516,8 +514,8 @@ XML
def test_query_param_named_action
get :test_query_parameters, params: {action: 'foobar'}
- parsed_params = eval(@response.body)
- assert_equal({action: 'foobar'}, parsed_params)
+ parsed_params = JSON.parse(@response.body)
+ assert_equal({'action' => 'foobar'}, parsed_params)
end
def test_request_param_named_action
@@ -720,12 +718,6 @@ XML
assert_equal 'it works', session[:symbol], "Test session hash should allow indifferent access"
end
- def test_header_properly_reset_after_get_request
- get :test_params
- @request.recycle!
- assert_nil @request.instance_variable_get("@request_method")
- end
-
def test_deprecated_params_reset_between_post_requests
assert_deprecated { post :no_op, foo: "bar" }
assert_equal "bar", @request.params[:foo]
@@ -957,10 +949,11 @@ class ResponseDefaultHeadersTest < ActionController::TestCase
end
end
- setup do
+ def before_setup
@original = ActionDispatch::Response.default_headers
@defaults = { 'A' => '1', 'B' => '2' }
ActionDispatch::Response.default_headers = @defaults
+ super
end
teardown do
@@ -970,8 +963,6 @@ class ResponseDefaultHeadersTest < ActionController::TestCase
def setup
super
@controller = TestController.new
- @request = ActionController::TestRequest.new
- @response = ActionController::TestResponse.new
@request.env['PATH_INFO'] = nil
@routes = ActionDispatch::Routing::RouteSet.new.tap do |r|
r.draw do
diff --git a/actionpack/test/controller/url_rewriter_test.rb b/actionpack/test/controller/url_rewriter_test.rb
index d9a1ae7d4f..5f2abc9606 100644
--- a/actionpack/test/controller/url_rewriter_test.rb
+++ b/actionpack/test/controller/url_rewriter_test.rb
@@ -1,7 +1,7 @@
require 'abstract_unit'
require 'controller/fake_controllers'
-class UrlRewriterTests < ActiveSupport::TestCase
+class UrlRewriterTests < ActionController::TestCase
class Rewriter
def initialize(request)
@options = {
@@ -16,7 +16,6 @@ class UrlRewriterTests < ActiveSupport::TestCase
end
def setup
- @request = ActionController::TestRequest.new
@params = {}
@rewriter = Rewriter.new(@request) #.new(@request, @params)
@routes = ActionDispatch::Routing::RouteSet.new.tap do |r|
diff --git a/actionpack/test/dispatch/request_test.rb b/actionpack/test/dispatch/request_test.rb
index 27ee8603e4..ff63c10e8d 100644
--- a/actionpack/test/dispatch/request_test.rb
+++ b/actionpack/test/dispatch/request_test.rb
@@ -989,6 +989,7 @@ class RequestParameterFilter < BaseRequestTest
[{'foo'=>'bar', 'baz'=>'foo'},{'foo'=>'[FILTERED]', 'baz'=>'[FILTERED]'},%w'foo baz'],
[{'bar'=>{'foo'=>'bar','bar'=>'foo'}},{'bar'=>{'foo'=>'[FILTERED]','bar'=>'foo'}},%w'fo'],
[{'foo'=>{'foo'=>'bar','bar'=>'foo'}},{'foo'=>'[FILTERED]'},%w'f banana'],
+ [{'deep'=>{'cc'=>{'code'=>'bar','bar'=>'foo'},'ss'=>{'code'=>'bar'}}},{'deep'=>{'cc'=>{'code'=>'[FILTERED]','bar'=>'foo'},'ss'=>{'code'=>'bar'}}},%w'deep.cc.code'],
[{'baz'=>[{'foo'=>'baz'}, "1"]}, {'baz'=>[{'foo'=>'[FILTERED]'}, "1"]}, [/foo/]]]
test_hashes.each do |before_filter, after_filter, filter_words|
diff --git a/actionpack/test/dispatch/test_request_test.rb b/actionpack/test/dispatch/test_request_test.rb
index cc35d4594e..ede1cec4e6 100644
--- a/actionpack/test/dispatch/test_request_test.rb
+++ b/actionpack/test/dispatch/test_request_test.rb
@@ -2,7 +2,7 @@ require 'abstract_unit'
class TestRequestTest < ActiveSupport::TestCase
test "sane defaults" do
- env = ActionDispatch::TestRequest.new.env
+ env = ActionDispatch::TestRequest.create.env
assert_equal "GET", env.delete("REQUEST_METHOD")
assert_equal "off", env.delete("HTTPS")
@@ -24,12 +24,10 @@ class TestRequestTest < ActiveSupport::TestCase
assert_equal true, env.delete("rack.multithread")
assert_equal true, env.delete("rack.multiprocess")
assert_equal false, env.delete("rack.run_once")
-
- assert env.empty?, env.inspect
end
test "cookie jar" do
- req = ActionDispatch::TestRequest.new
+ req = ActionDispatch::TestRequest.create({})
assert_equal({}, req.cookies)
assert_equal nil, req.env["HTTP_COOKIE"]
@@ -57,38 +55,38 @@ class TestRequestTest < ActiveSupport::TestCase
test "does not complain when Rails.application is nil" do
Rails.stubs(:application).returns(nil)
- req = ActionDispatch::TestRequest.new
+ req = ActionDispatch::TestRequest.create({})
assert_equal false, req.env.empty?
end
test "default remote address is 0.0.0.0" do
- req = ActionDispatch::TestRequest.new
+ req = ActionDispatch::TestRequest.create({})
assert_equal '0.0.0.0', req.remote_addr
end
test "allows remote address to be overridden" do
- req = ActionDispatch::TestRequest.new('REMOTE_ADDR' => '127.0.0.1')
+ req = ActionDispatch::TestRequest.create('REMOTE_ADDR' => '127.0.0.1')
assert_equal '127.0.0.1', req.remote_addr
end
test "default host is test.host" do
- req = ActionDispatch::TestRequest.new
+ req = ActionDispatch::TestRequest.create({})
assert_equal 'test.host', req.host
end
test "allows host to be overridden" do
- req = ActionDispatch::TestRequest.new('HTTP_HOST' => 'www.example.com')
+ req = ActionDispatch::TestRequest.create('HTTP_HOST' => 'www.example.com')
assert_equal 'www.example.com', req.host
end
test "default user agent is 'Rails Testing'" do
- req = ActionDispatch::TestRequest.new
+ req = ActionDispatch::TestRequest.create({})
assert_equal 'Rails Testing', req.user_agent
end
test "allows user agent to be overridden" do
- req = ActionDispatch::TestRequest.new('HTTP_USER_AGENT' => 'GoogleBot')
+ req = ActionDispatch::TestRequest.create('HTTP_USER_AGENT' => 'GoogleBot')
assert_equal 'GoogleBot', req.user_agent
end
diff --git a/actionview/CHANGELOG.md b/actionview/CHANGELOG.md
index 069d181674..1f6bb31cd4 100644
--- a/actionview/CHANGELOG.md
+++ b/actionview/CHANGELOG.md
@@ -1,3 +1,8 @@
+* Allow defining explicit collection caching using a `# Template Collection: ...`
+ directive inside templates.
+
+ *Dov Murik*
+
* Asset helpers raise `ArgumentError` when `nil` is passed as a source.
*Anton Kolomiychuk*
diff --git a/actionview/lib/action_view/helpers/cache_helper.rb b/actionview/lib/action_view/helpers/cache_helper.rb
index 8945575860..797d029317 100644
--- a/actionview/lib/action_view/helpers/cache_helper.rb
+++ b/actionview/lib/action_view/helpers/cache_helper.rb
@@ -137,6 +137,21 @@ module ActionView
# The automatic cache multi read can be turned off like so:
#
# <%= render @notifications, cache: false %>
+ #
+ # === Explicit Collection Caching
+ #
+ # If the partial template doesn't start with a clean cache call as
+ # mentioned above, you can still benefit from collection caching by
+ # adding a special comment format anywhere in the template, like:
+ #
+ # <%# Template Collection: notification %>
+ # <% my_helper_that_calls_cache(some_arg, notification) do %>
+ # <%= notification.name %>
+ # <% end %>
+ #
+ # The pattern used to match these is <tt>/# Template Collection: (\S+)/</tt>,
+ # so it's important that you type it out just so.
+ # You can only declare one collection in a partial template file.
def cache(name = {}, options = {}, &block)
if controller.respond_to?(:perform_caching) && controller.perform_caching
safe_concat(fragment_for(cache_fragment_name(name, options), options, &block))
diff --git a/actionview/lib/action_view/helpers/form_options_helper.rb b/actionview/lib/action_view/helpers/form_options_helper.rb
index 1b7b188d65..8e729b3c39 100644
--- a/actionview/lib/action_view/helpers/form_options_helper.rb
+++ b/actionview/lib/action_view/helpers/form_options_helper.rb
@@ -35,8 +35,8 @@ module ActionView
# <select name="post[person_id]" id="post_person_id">
# <option value="">None</option>
# <option value="1">David</option>
- # <option value="2" selected="selected">Sam</option>
- # <option value="3">Tobias</option>
+ # <option value="2" selected="selected">Eileen</option>
+ # <option value="3">Rafael</option>
# </select>
#
# * <tt>:prompt</tt> - set to true or a prompt string. When the select element doesn't have a value yet, this prepends an option with a generic prompt -- "Please select" -- or the given prompt string.
@@ -48,8 +48,8 @@ module ActionView
# <select name="post[person_id]" id="post_person_id">
# <option value="">Select Person</option>
# <option value="1">David</option>
- # <option value="2">Sam</option>
- # <option value="3">Tobias</option>
+ # <option value="2">Eileen</option>
+ # <option value="3">Rafael</option>
# </select>
#
# * <tt>:index</tt> - like the other form helpers, +select+ can accept an <tt>:index</tt> option to manually set the ID used in the resulting output. Unlike other helpers, +select+ expects this
@@ -112,8 +112,8 @@ module ActionView
# <select name="post[person_id]" id="post_person_id">
# <option value=""></option>
# <option value="1" selected="selected">David</option>
- # <option value="2">Sam</option>
- # <option value="3">Tobias</option>
+ # <option value="2">Eileen</option>
+ # <option value="3">Rafael</option>
# </select>
#
# assuming the associated person has ID 1.
diff --git a/actionview/lib/action_view/template.rb b/actionview/lib/action_view/template.rb
index 377ceb534a..d8585514d5 100644
--- a/actionview/lib/action_view/template.rb
+++ b/actionview/lib/action_view/template.rb
@@ -130,7 +130,7 @@ module ActionView
@source = source
@identifier = identifier
@handler = handler
- @cache_name = extract_resource_cache_call_name
+ @cache_name = extract_resource_cache_name
@compiled = false
@original_encoding = nil
@locals = details[:locals] || []
@@ -351,9 +351,18 @@ module ActionView
ActiveSupport::Notifications.instrument("#{action}.action_view", payload, &block)
end
- def extract_resource_cache_call_name
- $1 if @handler.respond_to?(:resource_cache_call_pattern) &&
- @source =~ @handler.resource_cache_call_pattern
+ EXPLICIT_COLLECTION = /# Template Collection: (?<resource_name>\w+)/
+
+ def extract_resource_cache_name
+ if match = @source.match(EXPLICIT_COLLECTION) || resource_cache_call_match
+ match[:resource_name]
+ end
+ end
+
+ def resource_cache_call_match
+ if @handler.respond_to?(:resource_cache_call_pattern)
+ @source.match(@handler.resource_cache_call_pattern)
+ end
end
def inferred_cache_name
diff --git a/actionview/lib/action_view/template/handlers/erb.rb b/actionview/lib/action_view/template/handlers/erb.rb
index da96347e4d..1f8459c24b 100644
--- a/actionview/lib/action_view/template/handlers/erb.rb
+++ b/actionview/lib/action_view/template/handlers/erb.rb
@@ -125,7 +125,7 @@ module ActionView
# Returns Regexp to extract a cached resource's name from a cache call at the
# first line of a template.
- # The extracted cache name is expected in $1.
+ # The extracted cache name is captured as :resource_name.
#
# <% cache notification do %> # => notification
#
@@ -138,7 +138,14 @@ module ActionView
#
# <% cache notification.event do %> # => nil
def resource_cache_call_pattern
- /\A(?:<%#.*%>)*\s*<%\s*cache\(?\s*(\w+)[\s\)]/m
+ /\A
+ (?:<%\#.*%>)* # optional initial comment
+ \s* # followed by optional spaces or newlines
+ <%\s*cache[\(\s] # followed by an ERB call to cache
+ \s* # followed by optional spaces or newlines
+ (?<resource_name>\w+) # capture the cache call argument as :resource_name
+ [\s\)] # followed by a space or close paren
+ /xm
end
private
diff --git a/actionview/lib/action_view/test_case.rb b/actionview/lib/action_view/test_case.rb
index 06810ad14d..b4f36c1f78 100644
--- a/actionview/lib/action_view/test_case.rb
+++ b/actionview/lib/action_view/test_case.rb
@@ -24,7 +24,7 @@ module ActionView
def initialize
super
self.class.controller_path = ""
- @request = ActionController::TestRequest.new
+ @request = ActionController::TestRequest.create
@response = ActionController::TestResponse.new
@request.env.delete('PATH_INFO')
diff --git a/actionview/test/actionpack/controller/view_paths_test.rb b/actionview/test/actionpack/controller/view_paths_test.rb
index 7fba9ff8ff..2dd27358f7 100644
--- a/actionview/test/actionpack/controller/view_paths_test.rb
+++ b/actionview/test/actionpack/controller/view_paths_test.rb
@@ -23,7 +23,7 @@ class ViewLoadPathsTest < ActionController::TestCase
end
def setup
- @request = ActionController::TestRequest.new
+ @request = ActionController::TestRequest.create
@response = ActionController::TestResponse.new
@controller = TestController.new
@paths = TestController.view_paths
diff --git a/actionview/test/template/template_test.rb b/actionview/test/template/template_test.rb
index d17034e88f..d3b51cd629 100644
--- a/actionview/test/template/template_test.rb
+++ b/actionview/test/template/template_test.rb
@@ -194,14 +194,15 @@ class TestERBTemplate < ActiveSupport::TestCase
[
"<%= 'Hello' %>",
"<% cache_customer = 42 %>",
- "<% cache customer.name do %><% end %>"
+ "<% cache customer.name do %><% end %>",
+ "<% my_cache customer do %><% end %>"
].each do |body|
template = new_template(body, virtual_path: "test/foo/_customer")
assert_not template.eligible_for_collection_caching?, "Template #{body.inspect} should not be eligible for collection caching"
end
end
- def test_eligible_for_collection_caching_with_cache_call
+ def test_eligible_for_collection_caching_with_cache_call_or_explicit
[
"<% cache customer do %><% end %>",
"<% cache(customer) do %><% end %>",
@@ -213,7 +214,8 @@ class TestERBTemplate < ActiveSupport::TestCase
"<%# comment %><% cache customer do %><% end %>",
"<%# comment %>\n<% cache customer do %><% end %>",
"<%# comment\n line 2\n line 3 %>\n<% cache customer do %><% end %>",
- "<%# comment 1 %>\n<%# comment 2 %>\n<% cache customer do %><% end %>"
+ "<%# comment 1 %>\n<%# comment 2 %>\n<% cache customer do %><% end %>",
+ "<%# comment 1 %>\n<%# Template Collection: customer %>\n<% my_cache customer do %><% end %>"
].each do |body|
template = new_template(body, virtual_path: "test/foo/_customer")
assert template.eligible_for_collection_caching?, "Template #{body.inspect} should be eligible for collection caching"
diff --git a/activejob/lib/active_job/arguments.rb b/activejob/lib/active_job/arguments.rb
index cdb37879b6..8e462bfe5d 100644
--- a/activejob/lib/active_job/arguments.rb
+++ b/activejob/lib/active_job/arguments.rb
@@ -16,13 +16,13 @@ module ActiveJob
end
end
- # Raised when an unsupported argument type is being set as job argument. We
+ # Raised when an unsupported argument type is set as a job argument. We
# currently support NilClass, Fixnum, Float, String, TrueClass, FalseClass,
- # Bignum and object that can be represented as GlobalIDs (ex: Active Record).
- # Also raised if you set the key for a Hash something else than a string or
- # a symbol.
- class SerializationError < ArgumentError
- end
+ # Bignum and objects that can be represented as GlobalIDs (ex: Active Record).
+ # Raised if you set the key for a Hash something else than a string or
+ # a symbol. Also raised when trying to serialize an object which can't be
+ # identified with a Global ID - such as an unpersisted Active Record model.
+ class SerializationError < ArgumentError; end
module Arguments
extend self
@@ -59,7 +59,7 @@ module ActiveJob
when *TYPE_WHITELIST
argument
when GlobalID::Identification
- { GLOBALID_KEY => argument.to_global_id.to_s }
+ convert_to_global_id_hash(argument)
when Array
argument.map { |arg| serialize_argument(arg) }
when ActiveSupport::HashWithIndifferentAccess
@@ -147,5 +147,12 @@ module ActiveJob
end
end
end
+
+ def convert_to_global_id_hash(argument)
+ { GLOBALID_KEY => argument.to_global_id.to_s }
+ rescue URI::GID::MissingModelIdError
+ raise SerializationError, "Unable to serialize #{argument.class} " \
+ "without an id. (Maybe you forgot to call save?)"
+ end
end
end
diff --git a/activejob/test/cases/argument_serialization_test.rb b/activejob/test/cases/argument_serialization_test.rb
index 8b9b62190f..933972a52b 100644
--- a/activejob/test/cases/argument_serialization_test.rb
+++ b/activejob/test/cases/argument_serialization_test.rb
@@ -88,6 +88,13 @@ class ArgumentSerializationTest < ActiveSupport::TestCase
assert_equal "Job with argument: 2", JobBuffer.last_value
end
+ test 'raises a friendly SerializationError for records without ids' do
+ err = assert_raises ActiveJob::SerializationError do
+ ActiveJob::Arguments.serialize [Person.new(nil)]
+ end
+ assert_match 'Unable to serialize Person without an id.', err.message
+ end
+
private
def assert_arguments_unchanged(*args)
assert_arguments_roundtrip args
diff --git a/activemodel/README.rdoc b/activemodel/README.rdoc
index 5c36b1277e..d954467387 100644
--- a/activemodel/README.rdoc
+++ b/activemodel/README.rdoc
@@ -154,7 +154,7 @@ behavior out of the box:
* Making objects serializable
- ActiveModel::Serialization provides a standard interface for your object
+ <tt>ActiveModel::Serialization</tt> provides a standard interface for your object
to provide +to_json+ or +to_xml+ serialization.
class SerialPerson
diff --git a/activemodel/lib/active_model/validations.rb b/activemodel/lib/active_model/validations.rb
index c1019169e1..72094a26c0 100644
--- a/activemodel/lib/active_model/validations.rb
+++ b/activemodel/lib/active_model/validations.rb
@@ -87,8 +87,7 @@ module ActiveModel
validates_with BlockValidator, _merge_attributes(attr_names), &block
end
- # :nodoc:
- VALID_OPTIONS_FOR_VALIDATE = [:on, :if, :unless, :prepend].freeze
+ VALID_OPTIONS_FOR_VALIDATE = [:on, :if, :unless, :prepend].freeze # :nodoc:
# Adds a validation method or block to the class. This is useful when
# overriding the +validate+ instance method becomes too unwieldy and
@@ -130,6 +129,9 @@ module ActiveModel
# end
# end
#
+ # Note that the return value of validation methods is not relevant.
+ # It's not possible to halt the validate callback chain.
+ #
# Options:
# * <tt>:on</tt> - Specifies the contexts where this validation is active.
# Runs in all validation contexts by default (nil). You can pass a symbol
diff --git a/activerecord/CHANGELOG.md b/activerecord/CHANGELOG.md
index 6054b08f33..e4a6734009 100644
--- a/activerecord/CHANGELOG.md
+++ b/activerecord/CHANGELOG.md
@@ -21,7 +21,7 @@
Example:
change_column_default :posts, :status, from: nil, to: "draft"
- change_column_default :users, authorized, from: true, to: false
+ change_column_default :users, :authorized, from: true, to: false
*Prem Sichanugrist*
diff --git a/activerecord/lib/active_record/relation/calculations.rb b/activerecord/lib/active_record/relation/calculations.rb
index df72ba7e9c..0f6015fa93 100644
--- a/activerecord/lib/active_record/relation/calculations.rb
+++ b/activerecord/lib/active_record/relation/calculations.rb
@@ -139,7 +139,7 @@ module ActiveRecord
# # SELECT people.id, people.name FROM people
# # => [[1, 'David'], [2, 'Jeremy'], [3, 'Jose']]
#
- # Person.uniq.pluck(:role)
+ # Person.distinct.pluck(:role)
# # SELECT DISTINCT role FROM people
# # => ['admin', 'member', 'guest']
#
@@ -195,7 +195,8 @@ module ActiveRecord
def perform_calculation(operation, column_name)
operation = operation.to_s.downcase
- # If #count is used with #distinct / #uniq it is considered distinct. (eg. relation.distinct.count)
+ # If #count is used with #distinct (i.e. `relation.distinct.count`) it is
+ # considered distinct.
distinct = self.distinct_value
if operation == "count"
diff --git a/activerecord/lib/active_record/relation/merger.rb b/activerecord/lib/active_record/relation/merger.rb
index dd8f0aa298..0b38666ce9 100644
--- a/activerecord/lib/active_record/relation/merger.rb
+++ b/activerecord/lib/active_record/relation/merger.rb
@@ -87,8 +87,8 @@ module ActiveRecord
return if other.preload_values.empty? && other.includes_values.empty?
if other.klass == relation.klass
- relation.preload! other.preload_values unless other.preload_values.empty?
- relation.includes! other.includes_values unless other.includes_values.empty?
+ relation.preload!(*other.preload_values) unless other.preload_values.empty?
+ relation.includes!(other.includes_values) unless other.includes_values.empty?
else
reflection = relation.klass.reflect_on_all_associations.find do |r|
r.class_name == other.klass.name
diff --git a/activerecord/test/cases/nested_attributes_test.rb b/activerecord/test/cases/nested_attributes_test.rb
index 6b4addd52f..d72225f3d3 100644
--- a/activerecord/test/cases/nested_attributes_test.rb
+++ b/activerecord/test/cases/nested_attributes_test.rb
@@ -658,6 +658,16 @@ module NestedAttributesOnACollectionAssociationTests
assert_equal "Couldn't find #{@child_1.class.name} with ID=1234567890 for Pirate with ID=#{@pirate.id}", exception.message
end
+ def test_should_raise_RecordNotFound_if_an_id_belonging_to_a_different_record_is_given
+ other_pirate = Pirate.create! catchphrase: 'Ahoy!'
+ other_child = other_pirate.send(@association_name).create! name: 'Buccaneers Servant'
+
+ exception = assert_raise ActiveRecord::RecordNotFound do
+ @pirate.attributes = { association_getter => [{ id: other_child.id }] }
+ end
+ assert_equal "Couldn't find #{@child_1.class.name} with ID=#{other_child.id} for Pirate with ID=#{@pirate.id}", exception.message
+ end
+
def test_should_automatically_build_new_associated_models_for_each_entry_in_a_hash_where_the_id_is_missing
@pirate.send(@association_name).destroy_all
@pirate.reload.attributes = {
diff --git a/activerecord/test/cases/relations_test.rb b/activerecord/test/cases/relations_test.rb
index acbf85d398..5f48c2b40f 100644
--- a/activerecord/test/cases/relations_test.rb
+++ b/activerecord/test/cases/relations_test.rb
@@ -647,6 +647,25 @@ class RelationTest < ActiveRecord::TestCase
end
end
+ def test_preloading_with_associations_default_scopes_and_merges
+ post = Post.create! title: 'Uhuu', body: 'body'
+ reader = Reader.create! post_id: post.id, person_id: 1
+
+ post_rel = PostWithPreloadDefaultScope.preload(:readers).joins(:readers).where(title: 'Uhuu')
+ result_post = PostWithPreloadDefaultScope.all.merge(post_rel).to_a.first
+
+ assert_no_queries do
+ assert_equal [reader], result_post.readers.to_a
+ end
+
+ post_rel = PostWithIncludesDefaultScope.includes(:readers).where(title: 'Uhuu')
+ result_post = PostWithIncludesDefaultScope.all.merge(post_rel).to_a.first
+
+ assert_no_queries do
+ assert_equal [reader], result_post.readers.to_a
+ end
+ end
+
def test_loading_with_one_association
posts = Post.preload(:comments)
post = posts.find { |p| p.id == 1 }
diff --git a/activerecord/test/models/post.rb b/activerecord/test/models/post.rb
index 052b1c9690..10f13b67da 100644
--- a/activerecord/test/models/post.rb
+++ b/activerecord/test/models/post.rb
@@ -208,6 +208,22 @@ class PostWithDefaultScope < ActiveRecord::Base
default_scope { order(:title) }
end
+class PostWithPreloadDefaultScope < ActiveRecord::Base
+ self.table_name = 'posts'
+
+ has_many :readers, foreign_key: 'post_id'
+
+ default_scope { preload(:readers) }
+end
+
+class PostWithIncludesDefaultScope < ActiveRecord::Base
+ self.table_name = 'posts'
+
+ has_many :readers, foreign_key: 'post_id'
+
+ default_scope { includes(:readers) }
+end
+
class SpecialPostWithDefaultScope < ActiveRecord::Base
self.table_name = 'posts'
default_scope { where(:id => [1, 5,6]) }
diff --git a/activesupport/lib/active_support/core_ext/module/concerning.rb b/activesupport/lib/active_support/core_ext/module/concerning.rb
index e26b594fc4..65b88b9bbd 100644
--- a/activesupport/lib/active_support/core_ext/module/concerning.rb
+++ b/activesupport/lib/active_support/core_ext/module/concerning.rb
@@ -99,7 +99,7 @@ class Module
# end
#
# Todo.ancestors
- # # => Todo, Todo::EventTracking, Object
+ # # => [Todo, Todo::EventTracking, Object]
#
# This small step has some wonderful ripple effects. We can
# * grok the behavior of our class in one glance,
diff --git a/activesupport/lib/active_support/ordered_options.rb b/activesupport/lib/active_support/ordered_options.rb
index bc0326473d..45864990ce 100644
--- a/activesupport/lib/active_support/ordered_options.rb
+++ b/activesupport/lib/active_support/ordered_options.rb
@@ -6,6 +6,7 @@ module ActiveSupport
# h[:girl] = 'Mary'
# h[:boy] # => 'John'
# h[:girl] # => 'Mary'
+ # h[:dog] # => nil
#
# Using +OrderedOptions+, the above code could be reduced to:
#
@@ -14,6 +15,13 @@ module ActiveSupport
# h.girl = 'Mary'
# h.boy # => 'John'
# h.girl # => 'Mary'
+ # h.dog # => nil
+ #
+ # To raise an exception when the value is blank, append a
+ # bang to the key name, like:
+ #
+ # h.dog! # => raises KeyError
+ #
class OrderedOptions < Hash
alias_method :_get, :[] # preserve the original #[] method
protected :_get # make it protected
diff --git a/activesupport/lib/active_support/testing/method_call_assertions.rb b/activesupport/lib/active_support/testing/method_call_assertions.rb
new file mode 100644
index 0000000000..0d7d62341c
--- /dev/null
+++ b/activesupport/lib/active_support/testing/method_call_assertions.rb
@@ -0,0 +1,30 @@
+module ActiveSupport
+ module Testing
+ module MethodCallAssertions # :nodoc:
+ private
+ def assert_called(object, method_name, message = nil, times: 1)
+ times_called = 0
+
+ object.stub(method_name, -> { times_called += 1 }) { yield }
+
+ error = "Expected #{method_name} to be called #{times} times, " \
+ "but was called #{times_called} times"
+ error = "#{message}.\n#{error}" if message
+ assert_equal times, times_called, error
+ end
+
+ def assert_called_with(object, method_name, args = [], returns: nil)
+ mock = Minitest::Mock.new
+ mock.expect(:call, returns, args)
+
+ object.stub(method_name, mock) { yield }
+
+ mock.verify
+ end
+
+ def assert_not_called(object, method_name, message = nil, &block)
+ assert_called(object, method_name, message, times: 0, &block)
+ end
+ end
+ end
+end \ No newline at end of file
diff --git a/activesupport/test/testing/method_call_assertions_test.rb b/activesupport/test/testing/method_call_assertions_test.rb
new file mode 100644
index 0000000000..b327492b3b
--- /dev/null
+++ b/activesupport/test/testing/method_call_assertions_test.rb
@@ -0,0 +1,91 @@
+require 'abstract_unit'
+require 'active_support/testing/method_call_assertions'
+
+class MethodCallAssertionsTest < ActiveSupport::TestCase
+ include ActiveSupport::Testing::MethodCallAssertions
+
+ class Level
+ def increment; 1; end
+ def decrement; end
+ def <<(arg); end
+ end
+
+ setup do
+ @object = Level.new
+ end
+
+ def test_assert_called_with_defaults_to_expect_once
+ assert_called @object, :increment do
+ @object.increment
+ end
+ end
+
+ def test_assert_called_more_than_once
+ assert_called(@object, :increment, times: 2) do
+ @object.increment
+ @object.increment
+ end
+ end
+
+ def test_assert_called_failure
+ error = assert_raises(Minitest::Assertion) do
+ assert_called(@object, :increment) do
+ # Call nothing...
+ end
+ end
+
+ assert_equal "Expected increment to be called 1 times, but was called 0 times.\nExpected: 1\n Actual: 0", error.message
+ end
+
+ def test_assert_called_with_message
+ error = assert_raises(Minitest::Assertion) do
+ assert_called(@object, :increment, 'dang it') do
+ # Call nothing...
+ end
+ end
+
+ assert_match(/dang it.\nExpected increment/, error.message)
+ end
+
+ def test_assert_called_with
+ assert_called_with(@object, :increment) do
+ @object.increment
+ end
+ end
+
+ def test_assert_called_with_arguments
+ assert_called_with(@object, :<<, [ 2 ]) do
+ @object << 2
+ end
+ end
+
+ def test_assert_called_with_failure
+ assert_raises(MockExpectationError) do
+ assert_called_with(@object, :<<, [ 4567 ]) do
+ @object << 2
+ end
+ end
+ end
+
+ def test_assert_called_with_returns
+ assert_called_with(@object, :increment, returns: 1) do
+ @object.increment
+ end
+ end
+
+ def test_assert_not_called
+ assert_not_called(@object, :decrement) do
+ @object.increment
+ end
+ end
+
+ def test_assert_not_called_failure
+ error = assert_raises(Minitest::Assertion) do
+ assert_not_called(@object, :increment) do
+ @object.increment
+ end
+ end
+
+ assert_equal "Expected increment to be called 0 times, but was called 1 times.\nExpected: 0\n Actual: 1", error.message
+ end
+end
diff --git a/guides/source/action_mailer_basics.md b/guides/source/action_mailer_basics.md
index 6f159b2fc4..c31b50fcfc 100644
--- a/guides/source/action_mailer_basics.md
+++ b/guides/source/action_mailer_basics.md
@@ -533,6 +533,24 @@ url helper.
NOTE: non-`GET` links require [jQuery UJS](https://github.com/rails/jquery-ujs)
and won't work in mailer templates. They will result in normal `GET` requests.
+### Adding images in Action Mailer Views
+
+Unlike controllers, the mailer instance doesn't have any context about the
+incoming request so you'll need to provide the `:asset_host` parameter yourself.
+
+As the `:asset_host` usually is consistent across the application you can
+configure it globally in config/application.rb:
+
+```ruby
+config.action_mailer.asset_host = 'http://example.com'
+```
+
+Now you can display an image inside your email.
+
+```ruby
+<%= image_tag 'image.jpg' %>
+```
+
### Sending Multipart Emails
Action Mailer will automatically send multipart emails if you have different
diff --git a/guides/source/action_view_overview.md b/guides/source/action_view_overview.md
index 3c1c3c7873..98c6cbd540 100644
--- a/guides/source/action_view_overview.md
+++ b/guides/source/action_view_overview.md
@@ -1059,14 +1059,6 @@ If `@article.author_ids` is [1], this would return:
<input name="article[author_ids][]" type="hidden" value="" />
```
-#### country_options_for_select
-
-Returns a string of option tags for pretty much any country in the world.
-
-#### country_select
-
-Returns select and option tags for the given object and method, using country_options_for_select to generate the list of option tags.
-
#### option_groups_from_collection_for_select
Returns a string of `option` tags, like `options_from_collection_for_select`, but groups them by `optgroup` tags based on the object relationships of the arguments.
@@ -1153,8 +1145,8 @@ If `@article.person_id` is 1, this would become:
<select name="article[person_id]">
<option value=""></option>
<option value="1" selected="selected">David</option>
- <option value="2">Sam</option>
- <option value="3">Tobias</option>
+ <option value="2">Eileen</option>
+ <option value="3">Rafael</option>
</select>
```
diff --git a/guides/source/active_record_validations.md b/guides/source/active_record_validations.md
index 7932853c11..71ca7a0f66 100644
--- a/guides/source/active_record_validations.md
+++ b/guides/source/active_record_validations.md
@@ -965,8 +965,9 @@ own custom validators.
You can also create methods that verify the state of your models and add
messages to the `errors` collection when they are invalid. You must then
-register these methods by using the `validate` class method, passing in the
-symbols for the validation methods' names.
+register these methods by using the `validate`
+([API](http://api.rubyonrails.org/classes/ActiveModel/Validations/ClassMethods.html#method-i-validate))
+class method, passing in the symbols for the validation methods' names.
You can pass more than one symbol for each class method and the respective
validations will be run in the same order as they were registered.
diff --git a/guides/source/contributing_to_ruby_on_rails.md b/guides/source/contributing_to_ruby_on_rails.md
index c19813c8a5..846b8a007d 100644
--- a/guides/source/contributing_to_ruby_on_rails.md
+++ b/guides/source/contributing_to_ruby_on_rails.md
@@ -295,7 +295,7 @@ You can run a single test through ruby. For instance:
```bash
$ cd actionmailer
-$ ruby -w -Itest test/mail_layout_test.rb -n test_explicit_class_layout
+$ bundle exec ruby -w -Itest test/mail_layout_test.rb -n test_explicit_class_layout
```
The `-n` option allows you to run a single method instead of the whole
diff --git a/guides/source/security.md b/guides/source/security.md
index 93580d4d4e..485b108d12 100644
--- a/guides/source/security.md
+++ b/guides/source/security.md
@@ -1026,6 +1026,29 @@ Environmental Security
It is beyond the scope of this guide to inform you on how to secure your application code and environments. However, please secure your database configuration, e.g. `config/database.yml`, and your server-side secret, e.g. stored in `config/secrets.yml`. You may want to further restrict access, using environment-specific versions of these files and any others that may contain sensitive information.
+### Custom secrets
+
+Rails generates a `config/secrets.yml`. By default, this file contains the
+application's `secret_key_base`, but it could also be used to store other
+secrets such as access keys for external APIs.
+
+The secrets added to this file are accessible via `Rails.application.secrets`.
+For example, with the following `config/secrets.yml`:
+
+ development:
+ secret_key_base: 3b7cd727ee24e8444053437c36cc66c3
+ some_api_key: SOMEKEY
+
+`Rails.application.secrets.some_api_key` returns `SOMEKEY` in the development
+environment.
+
+If you want an exception to be raised when some key is blank, use the bang
+version:
+
+```ruby
+Rails.application.secrets.some_api_key! # => raises KeyError
+```
+
Additional Resources
--------------------
diff --git a/railties/lib/rails/generators/rails/app/app_generator.rb b/railties/lib/rails/generators/rails/app/app_generator.rb
index 4b73313388..b813b083f4 100644
--- a/railties/lib/rails/generators/rails/app/app_generator.rb
+++ b/railties/lib/rails/generators/rails/app/app_generator.rb
@@ -356,7 +356,9 @@ module Rails
if app_const =~ /^\d/
raise Error, "Invalid application name #{app_name}. Please give a name which does not start with numbers."
elsif RESERVED_NAMES.include?(app_name)
- raise Error, "Invalid application name #{app_name}. Please give a name which does not match one of the reserved rails words: #{RESERVED_NAMES}"
+ raise Error, "Invalid application name #{app_name}. Please give a " \
+ "name which does not match one of the reserved rails " \
+ "words: #{RESERVED_NAMES.join(", ")}"
elsif Object.const_defined?(app_const_base)
raise Error, "Invalid application name #{app_name}, constant #{app_const_base} is already in use. Please choose another application name."
end
diff --git a/railties/lib/rails/generators/rails/plugin/plugin_generator.rb b/railties/lib/rails/generators/rails/plugin/plugin_generator.rb
index 7f5bf0c8b8..66111004aa 100644
--- a/railties/lib/rails/generators/rails/plugin/plugin_generator.rb
+++ b/railties/lib/rails/generators/rails/plugin/plugin_generator.rb
@@ -364,7 +364,9 @@ task default: :test
elsif camelized =~ /^\d/
raise Error, "Invalid plugin name #{original_name}. Please give a name which does not start with numbers."
elsif RESERVED_NAMES.include?(name)
- raise Error, "Invalid plugin name #{original_name}. Please give a name which does not match one of the reserved rails words: #{RESERVED_NAMES}"
+ raise Error, "Invalid plugin name #{original_name}. Please give a " \
+ "name which does not match one of the reserved rails " \
+ "words: #{RESERVED_NAMES.join(", ")}"
elsif Object.const_defined?(camelized)
raise Error, "Invalid plugin name #{original_name}, constant #{camelized} is already in use. Please choose another plugin name."
end
diff --git a/railties/lib/rails/generators/rails/plugin/templates/test/integration/navigation_test.rb b/railties/lib/rails/generators/rails/plugin/templates/test/integration/navigation_test.rb
index 824caecb24..f5d1ec2046 100644
--- a/railties/lib/rails/generators/rails/plugin/templates/test/integration/navigation_test.rb
+++ b/railties/lib/rails/generators/rails/plugin/templates/test/integration/navigation_test.rb
@@ -1,10 +1,6 @@
require 'test_helper'
class NavigationTest < ActionDispatch::IntegrationTest
-<% unless options[:skip_active_record] -%>
- fixtures :all
-<% end -%>
-
# test "the truth" do
# assert true
# end
diff --git a/railties/test/generators/plugin_generator_test.rb b/railties/test/generators/plugin_generator_test.rb
index d6e5f4bd89..73e68863f4 100644
--- a/railties/test/generators/plugin_generator_test.rb
+++ b/railties/test/generators/plugin_generator_test.rb
@@ -38,7 +38,7 @@ class PluginGeneratorTest < Rails::Generators::TestCase
assert_equal "Invalid plugin name 43things. Please give a name which does not start with numbers.\n", content
content = capture(:stderr){ run_generator [File.join(destination_root, "plugin")] }
- assert_equal "Invalid plugin name plugin. Please give a name which does not match one of the reserved rails words: [\"application\", \"destroy\", \"plugin\", \"runner\", \"test\"]\n", content
+ assert_equal "Invalid plugin name plugin. Please give a name which does not match one of the reserved rails words: application, destroy, plugin, runner, test\n", content
content = capture(:stderr){ run_generator [File.join(destination_root, "Digest")] }
assert_equal "Invalid plugin name Digest, constant Digest is already in use. Please choose another plugin name.\n", content
diff --git a/railties/test/generators/shared_generator_tests.rb b/railties/test/generators/shared_generator_tests.rb
index f983b45d2b..77372fb514 100644
--- a/railties/test/generators/shared_generator_tests.rb
+++ b/railties/test/generators/shared_generator_tests.rb
@@ -56,7 +56,7 @@ module SharedGeneratorTests
reserved_words = %w[application destroy plugin runner test]
reserved_words.each do |reserved|
content = capture(:stderr){ run_generator [File.join(destination_root, reserved)] }
- assert_match(/Invalid \w+ name #{reserved}. Please give a name which does not match one of the reserved rails words: \["application", "destroy", "plugin", "runner", "test"\]\n/, content)
+ assert_match(/Invalid \w+ name #{reserved}. Please give a name which does not match one of the reserved rails words: application, destroy, plugin, runner, test\n/, content)
end
end