aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--.rubocop.yml23
-rw-r--r--actionview/lib/action_view/helpers/url_helper.rb2
-rw-r--r--actionview/test/template/url_helper_test.rb6
-rw-r--r--activerecord/CHANGELOG.md8
-rw-r--r--activerecord/lib/active_record/associations/builder/has_and_belongs_to_many.rb6
-rw-r--r--activerecord/lib/active_record/attribute_methods/primary_key.rb15
-rw-r--r--activerecord/lib/active_record/attribute_methods/time_zone_conversion.rb2
-rw-r--r--activerecord/lib/active_record/connection_adapters/abstract/connection_pool.rb4
-rw-r--r--activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb2
-rw-r--r--activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb11
-rw-r--r--activerecord/lib/active_record/connection_adapters/postgresql/database_statements.rb8
-rw-r--r--activerecord/lib/active_record/connection_adapters/postgresql/schema_statements.rb6
-rw-r--r--activerecord/lib/active_record/fixtures.rb11
-rw-r--r--activerecord/lib/active_record/querying.rb2
-rw-r--r--activerecord/lib/active_record/relation.rb15
-rw-r--r--activerecord/lib/active_record/schema_dumper.rb7
-rw-r--r--activerecord/test/cases/associations/has_and_belongs_to_many_associations_test.rb2
-rw-r--r--activerecord/test/cases/associations/has_many_associations_test.rb3
-rw-r--r--activerecord/test/cases/connection_pool_test.rb4
-rw-r--r--activerecord/test/cases/fixtures_test.rb4
-rw-r--r--activerecord/test/cases/multiparameter_attributes_test.rb14
-rw-r--r--activerecord/test/cases/primary_keys_test.rb10
-rw-r--r--activerecord/test/cases/query_cache_test.rb2
-rw-r--r--activerecord/test/cases/scoping/named_scoping_test.rb7
-rw-r--r--activesupport/CHANGELOG.md50
-rw-r--r--activesupport/lib/active_support/testing/assertions.rb89
-rw-r--r--activesupport/test/caching_test.rb22
-rw-r--r--activesupport/test/test_case_test.rb106
-rw-r--r--guides/source/action_controller_overview.md4
-rw-r--r--guides/source/configuring.md2
-rw-r--r--railties/CHANGELOG.md5
-rw-r--r--railties/lib/rails/application/configuration.rb28
-rw-r--r--railties/lib/rails/application/finisher.rb8
-rw-r--r--railties/lib/rails/generators/rails/app/app_generator.rb1
-rw-r--r--railties/lib/rails/generators/rails/app/templates/config/initializers/session_store.rb.tt3
-rw-r--r--railties/test/application/configuration_test.rb16
-rw-r--r--railties/test/generators/api_app_generator_test.rb1
-rw-r--r--railties/test/generators/app_generator_test.rb10
38 files changed, 420 insertions, 99 deletions
diff --git a/.rubocop.yml b/.rubocop.yml
index de355eeb13..62fb263c9e 100644
--- a/.rubocop.yml
+++ b/.rubocop.yml
@@ -4,37 +4,40 @@ AllCops:
# Two spaces, no tabs (for indentation).
Style/IndentationWidth:
- enabled: true
+ Enabled: true
# No trailing whitespace.
Style/TrailingWhitespace:
- enabled: true
+ Enabled: true
# Blank lines should not have any spaces.
Style/TrailingBlankLines:
- enabled: true
+ Enabled: true
# Use Ruby >= 1.9 syntax for hashes. Prefer { a: :b } over { :a => :b }.
Style/HashSyntax:
- enabled: true
+ Enabled: true
# Prefer &&/|| over and/or.
Style/AndOr:
- enabled: true
+ Enabled: true
# Use my_method(my_arg) not my_method( my_arg ) or my_method my_arg.
Lint/RequireParentheses:
- enabled: true
+ Enabled: true
Style/BracesAroundHashParameters:
- enabled: true
+ Enabled: true
Style/IndentationConsistency:
- enabled: true
+ Enabled: true
EnforcedStyle: rails
Style/EmptyLinesAroundClassBody:
- enabled: true
+ Enabled: true
Style/EmptyLinesAroundModuleBody:
- enabled: true
+ Enabled: true
+
+Lint/EndAlignment:
+ AlignWith: variable
diff --git a/actionview/lib/action_view/helpers/url_helper.rb b/actionview/lib/action_view/helpers/url_helper.rb
index 11c7daf4da..fb6426b997 100644
--- a/actionview/lib/action_view/helpers/url_helper.rb
+++ b/actionview/lib/action_view/helpers/url_helper.rb
@@ -548,6 +548,8 @@ module ActionView
request_uri = url_string.index("?") ? request.fullpath : request.path
request_uri = URI.parser.unescape(request_uri).force_encoding(Encoding::BINARY)
+ url_string.chomp!("/") if url_string.start_with?("/") && url_string != "/"
+
if url_string =~ /^\w+:\/\//
url_string == "#{request.protocol}#{request.host_with_port}#{request_uri}"
else
diff --git a/actionview/test/template/url_helper_test.rb b/actionview/test/template/url_helper_test.rb
index ab56d80de3..6060ea2f1e 100644
--- a/actionview/test/template/url_helper_test.rb
+++ b/actionview/test/template/url_helper_test.rb
@@ -503,6 +503,12 @@ class UrlHelperTest < ActiveSupport::TestCase
assert current_page?(controller: 'foo', action: 'category', category: 'administraĆ§Ć£o', callback_url: 'http://example.com/foo')
end
+ def test_current_page_with_trailing_slash
+ @request = request_for_url("/posts")
+
+ assert current_page?("/posts/")
+ end
+
def test_link_unless_current
@request = request_for_url("/")
diff --git a/activerecord/CHANGELOG.md b/activerecord/CHANGELOG.md
index 1d0e4b32a3..590fe2f587 100644
--- a/activerecord/CHANGELOG.md
+++ b/activerecord/CHANGELOG.md
@@ -1,3 +1,11 @@
+* Support calling the method `merge` in `scope`'s lambda.
+
+ *Yasuhiro Sugino*
+
+* Fixes multi-parameter attributes conversion with invalid params.
+
+ *Hiroyuki Ishii*
+
* Add newline between each migration in `structure.sql`.
Keeps schema migration inserts as a single commit, but allows for easier
diff --git a/activerecord/lib/active_record/associations/builder/has_and_belongs_to_many.rb b/activerecord/lib/active_record/associations/builder/has_and_belongs_to_many.rb
index 5fbd79d118..dd5fd607a2 100644
--- a/activerecord/lib/active_record/associations/builder/has_and_belongs_to_many.rb
+++ b/activerecord/lib/active_record/associations/builder/has_and_belongs_to_many.rb
@@ -76,8 +76,10 @@ module ActiveRecord::Associations::Builder # :nodoc:
left_model.retrieve_connection
end
- def self.primary_key
- false
+ private
+
+ def self.suppress_composite_primary_key(pk)
+ pk unless pk.is_a?(Array)
end
}
diff --git a/activerecord/lib/active_record/attribute_methods/primary_key.rb b/activerecord/lib/active_record/attribute_methods/primary_key.rb
index 0d5cb8b37c..d28edfb003 100644
--- a/activerecord/lib/active_record/attribute_methods/primary_key.rb
+++ b/activerecord/lib/active_record/attribute_methods/primary_key.rb
@@ -95,7 +95,8 @@ module ActiveRecord
base_name.foreign_key
else
if ActiveRecord::Base != self && table_exists?
- connection.schema_cache.primary_keys(table_name)
+ pk = connection.schema_cache.primary_keys(table_name)
+ suppress_composite_primary_key(pk)
else
'id'
end
@@ -122,6 +123,18 @@ module ActiveRecord
@quoted_primary_key = nil
@attributes_builder = nil
end
+
+ private
+
+ def suppress_composite_primary_key(pk)
+ return pk unless pk.is_a?(Array)
+
+ warn <<-WARNING.strip_heredoc
+ WARNING: Active Record does not support composite primary key.
+
+ #{table_name} has composite primary key. Composite primary key is ignored.
+ WARNING
+ end
end
end
end
diff --git a/activerecord/lib/active_record/attribute_methods/time_zone_conversion.rb b/activerecord/lib/active_record/attribute_methods/time_zone_conversion.rb
index e160460286..cbb4f0cff8 100644
--- a/activerecord/lib/active_record/attribute_methods/time_zone_conversion.rb
+++ b/activerecord/lib/active_record/attribute_methods/time_zone_conversion.rb
@@ -39,7 +39,7 @@ module ActiveRecord
end
def set_time_zone_without_conversion(value)
- ::Time.zone.local_to_utc(value).in_time_zone
+ ::Time.zone.local_to_utc(value).in_time_zone if value
end
def map_avoiding_infinite_recursion(value)
diff --git a/activerecord/lib/active_record/connection_adapters/abstract/connection_pool.rb b/activerecord/lib/active_record/connection_adapters/abstract/connection_pool.rb
index 1d9579f1a8..dc5b305843 100644
--- a/activerecord/lib/active_record/connection_adapters/abstract/connection_pool.rb
+++ b/activerecord/lib/active_record/connection_adapters/abstract/connection_pool.rb
@@ -855,7 +855,7 @@ module ActiveRecord
connection_id: object_id
}
if spec
- payload[:class_name] = spec.name
+ payload[:spec_name] = spec.name
payload[:config] = spec.config
end
@@ -896,7 +896,7 @@ module ActiveRecord
# for (not necessarily the current class).
def retrieve_connection(spec_name) #:nodoc:
pool = retrieve_connection_pool(spec_name)
- raise ConnectionNotEstablished, "No connection pool with id '#{spec_name}' found." unless pool
+ raise ConnectionNotEstablished, "No connection pool with '#{spec_name}' found." unless pool
conn = pool.connection
raise ConnectionNotEstablished, "No connection for '#{spec_name}' in connection pool" unless conn
conn
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 507a925d32..74aae3a1e4 100644
--- a/activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb
+++ b/activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb
@@ -10,7 +10,7 @@ module ActiveRecord
def to_sql(arel, binds = [])
if arel.respond_to?(:ast)
collected = visitor.accept(arel.ast, collector)
- collected.compile(binds.dup, self)
+ collected.compile(binds, self)
else
arel
end
diff --git a/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb b/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb
index 9e9ace49db..353cae0f3d 100644
--- a/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb
+++ b/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb
@@ -129,14 +129,9 @@ module ActiveRecord
# Returns just a table's primary key
def primary_key(table_name)
- pks = primary_keys(table_name)
- warn <<-WARNING.strip_heredoc if pks.count > 1
- WARNING: Rails does not support composite primary key.
-
- #{table_name} has composite primary key. Composite primary key is ignored.
- WARNING
-
- pks.first if pks.one?
+ pk = primary_keys(table_name)
+ pk = pk.first unless pk.size > 1
+ pk
end
# Creates a new table with the name +table_name+. +table_name+ may either
diff --git a/activerecord/lib/active_record/connection_adapters/postgresql/database_statements.rb b/activerecord/lib/active_record/connection_adapters/postgresql/database_statements.rb
index 6f2e03b370..f5232127c4 100644
--- a/activerecord/lib/active_record/connection_adapters/postgresql/database_statements.rb
+++ b/activerecord/lib/active_record/connection_adapters/postgresql/database_statements.rb
@@ -124,6 +124,8 @@ module ActiveRecord
pk = primary_key(table_ref) if table_ref
end
+ pk = suppress_composite_primary_key(pk)
+
if pk && use_insert_returning?
sql = "#{sql} RETURNING #{quote_column_name(pk)}"
end
@@ -164,6 +166,12 @@ module ActiveRecord
def exec_rollback_db_transaction
execute "ROLLBACK"
end
+
+ private
+
+ def suppress_composite_primary_key(pk)
+ pk unless pk.is_a?(Array)
+ end
end
end
end
diff --git a/activerecord/lib/active_record/connection_adapters/postgresql/schema_statements.rb b/activerecord/lib/active_record/connection_adapters/postgresql/schema_statements.rb
index 45507e206a..cc606e4828 100644
--- a/activerecord/lib/active_record/connection_adapters/postgresql/schema_statements.rb
+++ b/activerecord/lib/active_record/connection_adapters/postgresql/schema_statements.rb
@@ -327,12 +327,12 @@ module ActiveRecord
end
# Returns the sequence name for a table's primary key or some other specified key.
- def default_sequence_name(table_name, pk = nil) #:nodoc:
- result = serial_sequence(table_name, pk || 'id')
+ def default_sequence_name(table_name, pk = 'id') #:nodoc:
+ result = serial_sequence(table_name, pk)
return nil unless result
Utils.extract_schema_qualified_name(result).to_s
rescue ActiveRecord::StatementInvalid
- PostgreSQL::Name.new(nil, "#{table_name}_#{pk || 'id'}_seq").to_s
+ PostgreSQL::Name.new(nil, "#{table_name}_#{pk}_seq").to_s
end
def serial_sequence(table, column)
diff --git a/activerecord/lib/active_record/fixtures.rb b/activerecord/lib/active_record/fixtures.rb
index 6d142285cd..0ec33f7d87 100644
--- a/activerecord/lib/active_record/fixtures.rb
+++ b/activerecord/lib/active_record/fixtures.rb
@@ -987,16 +987,11 @@ module ActiveRecord
# When connections are established in the future, begin a transaction too
@connection_subscriber = ActiveSupport::Notifications.subscribe('!connection.active_record') do |_, _, _, _, payload|
- model_class = nil
- begin
- model_class = payload[:class_name].constantize if payload[:class_name]
- rescue NameError
- model_class = nil
- end
+ spec_name = payload[:spec_name] if payload.key?(:spec_name)
- if model_class
+ if spec_name
begin
- connection = ActiveRecord::Base.connection_handler.retrieve_connection(model_class)
+ connection = ActiveRecord::Base.connection_handler.retrieve_connection(spec_name)
rescue ConnectionNotEstablished
connection = nil
end
diff --git a/activerecord/lib/active_record/querying.rb b/activerecord/lib/active_record/querying.rb
index 53ddd95bb0..f8dd35df0f 100644
--- a/activerecord/lib/active_record/querying.rb
+++ b/activerecord/lib/active_record/querying.rb
@@ -9,7 +9,7 @@ module ActiveRecord
delegate :find_each, :find_in_batches, :in_batches, to: :all
delegate :select, :group, :order, :except, :reorder, :limit, :offset, :joins, :left_joins, :left_outer_joins, :or,
:where, :rewhere, :preload, :eager_load, :includes, :from, :lock, :readonly,
- :having, :create_with, :uniq, :distinct, :references, :none, :unscope, to: :all
+ :having, :create_with, :uniq, :distinct, :references, :none, :unscope, :merge, to: :all
delegate :count, :average, :minimum, :maximum, :sum, :calculate, to: :all
delegate :pluck, :ids, to: :all
diff --git a/activerecord/lib/active_record/relation.rb b/activerecord/lib/active_record/relation.rb
index 93baa882ad..0792ba8f76 100644
--- a/activerecord/lib/active_record/relation.rb
+++ b/activerecord/lib/active_record/relation.rb
@@ -1,5 +1,3 @@
-require "arel/collectors/bind"
-
module ActiveRecord
# = Active Record \Relation
class Relation
@@ -597,19 +595,16 @@ module ActiveRecord
# # => SELECT "users".* FROM "users" WHERE "users"."name" = 'Oscar'
def to_sql
@to_sql ||= begin
- relation = self
- connection = klass.connection
- visitor = connection.visitor
+ relation = self
if eager_loading?
find_with_associations { |rel| relation = rel }
end
- binds = relation.bound_attributes
- binds = connection.prepare_binds_for_database(binds)
- binds.map! { |value| connection.quote(value) }
- collect = visitor.accept(relation.arel.ast, Arel::Collectors::Bind.new)
- collect.substitute_binds(binds).join
+ conn = klass.connection
+ conn.unprepared_statement {
+ conn.to_sql(relation.arel, relation.bound_attributes)
+ }
end
end
diff --git a/activerecord/lib/active_record/schema_dumper.rb b/activerecord/lib/active_record/schema_dumper.rb
index d769376d1a..e8c176d603 100644
--- a/activerecord/lib/active_record/schema_dumper.rb
+++ b/activerecord/lib/active_record/schema_dumper.rb
@@ -105,12 +105,7 @@ HEADER
tbl = StringIO.new
# first dump primary key column
- if @connection.respond_to?(:primary_keys)
- pk = @connection.primary_keys(table)
- pk = pk.first unless pk.size > 1
- else
- pk = @connection.primary_key(table)
- end
+ pk = @connection.primary_key(table)
tbl.print " create_table #{remove_prefix_and_suffix(table).inspect}"
diff --git a/activerecord/test/cases/associations/has_and_belongs_to_many_associations_test.rb b/activerecord/test/cases/associations/has_and_belongs_to_many_associations_test.rb
index 1bbca84bb2..e27f16b6e4 100644
--- a/activerecord/test/cases/associations/has_and_belongs_to_many_associations_test.rb
+++ b/activerecord/test/cases/associations/has_and_belongs_to_many_associations_test.rb
@@ -156,7 +156,7 @@ class HasAndBelongsToManyAssociationsTest < ActiveRecord::TestCase
warning = capture(:stderr) do
country.treaties << treaty
end
- assert_no_match(/WARNING: Rails does not support composite primary key\./, warning)
+ assert_no_match(/WARNING: Active Record does not support composite primary key\./, warning)
end
def test_has_and_belongs_to_many
diff --git a/activerecord/test/cases/associations/has_many_associations_test.rb b/activerecord/test/cases/associations/has_many_associations_test.rb
index 7ec0dfce7a..61a5c2c3b7 100644
--- a/activerecord/test/cases/associations/has_many_associations_test.rb
+++ b/activerecord/test/cases/associations/has_many_associations_test.rb
@@ -244,8 +244,9 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
def test_do_not_call_callbacks_for_delete_all
car = Car.create(:name => 'honda')
car.funky_bulbs.create!
+ assert_equal 1, car.funky_bulbs.count
assert_nothing_raised { car.reload.funky_bulbs.delete_all }
- assert_equal 0, Bulb.count, "bulbs should have been deleted using :delete_all strategy"
+ assert_equal 0, car.funky_bulbs.count, "bulbs should have been deleted using :delete_all strategy"
end
def test_delete_all_on_association_is_the_same_as_not_loaded
diff --git a/activerecord/test/cases/connection_pool_test.rb b/activerecord/test/cases/connection_pool_test.rb
index 2bdabaee62..bbcb42d58e 100644
--- a/activerecord/test/cases/connection_pool_test.rb
+++ b/activerecord/test/cases/connection_pool_test.rb
@@ -347,8 +347,8 @@ module ActiveRecord
payloads << payload
end
ActiveRecord::Base.establish_connection :arunit
- assert_equal [:class_name, :config, :connection_id], payloads[0].keys.sort
- assert_equal 'primary', payloads[0][:class_name]
+ assert_equal [:config, :connection_id, :spec_name], payloads[0].keys.sort
+ assert_equal 'primary', payloads[0][:spec_name]
ensure
ActiveSupport::Notifications.unsubscribe(subscription) if subscription
end
diff --git a/activerecord/test/cases/fixtures_test.rb b/activerecord/test/cases/fixtures_test.rb
index a06aa4b247..df53bbf950 100644
--- a/activerecord/test/cases/fixtures_test.rb
+++ b/activerecord/test/cases/fixtures_test.rb
@@ -650,10 +650,10 @@ class TransactionalFixturesOnConnectionNotification < ActiveRecord::TestCase
private
def fire_connection_notification(connection)
- ActiveRecord::Base.connection_handler.stubs(:retrieve_connection).with(Book).returns(connection)
+ ActiveRecord::Base.connection_handler.stubs(:retrieve_connection).with('book').returns(connection)
message_bus = ActiveSupport::Notifications.instrumenter
payload = {
- class_name: 'Book',
+ spec_name: 'book',
config: nil,
connection_id: connection.object_id
}
diff --git a/activerecord/test/cases/multiparameter_attributes_test.rb b/activerecord/test/cases/multiparameter_attributes_test.rb
index d05cb22740..59c340ceb7 100644
--- a/activerecord/test/cases/multiparameter_attributes_test.rb
+++ b/activerecord/test/cases/multiparameter_attributes_test.rb
@@ -202,6 +202,20 @@ class MultiParameterAttributeTest < ActiveRecord::TestCase
Topic.reset_column_information
end
+ def test_multiparameter_attributes_on_time_with_time_zone_aware_attributes_and_invalid_time_params
+ with_timezone_config aware_attributes: true do
+ Topic.reset_column_information
+ attributes = {
+ "written_on(1i)" => "2004", "written_on(2i)" => "", "written_on(3i)" => ""
+ }
+ topic = Topic.find(1)
+ topic.attributes = attributes
+ assert_nil topic.written_on
+ end
+ ensure
+ Topic.reset_column_information
+ end
+
def test_multiparameter_attributes_on_time_with_time_zone_aware_attributes_false
with_timezone_config default: :local, aware_attributes: false, zone: -28800 do
attributes = {
diff --git a/activerecord/test/cases/primary_keys_test.rb b/activerecord/test/cases/primary_keys_test.rb
index 4267ad4a24..ea36c199f4 100644
--- a/activerecord/test/cases/primary_keys_test.rb
+++ b/activerecord/test/cases/primary_keys_test.rb
@@ -255,6 +255,7 @@ class CompositePrimaryKeyTest < ActiveRecord::TestCase
def setup
@connection = ActiveRecord::Base.connection
+ @connection.schema_cache.clear!
@connection.create_table(:barcodes, primary_key: ["region", "code"], force: true) do |t|
t.string :region
t.integer :code
@@ -270,10 +271,15 @@ class CompositePrimaryKeyTest < ActiveRecord::TestCase
end
def test_primary_key_issues_warning
+ model = Class.new(ActiveRecord::Base) do
+ def self.table_name
+ "barcodes"
+ end
+ end
warning = capture(:stderr) do
- assert_nil @connection.primary_key("barcodes")
+ assert_nil model.primary_key
end
- assert_match(/WARNING: Rails does not support composite primary key\./, warning)
+ assert_match(/WARNING: Active Record does not support composite primary key\./, warning)
end
def test_collectly_dump_composite_primary_key
diff --git a/activerecord/test/cases/query_cache_test.rb b/activerecord/test/cases/query_cache_test.rb
index 03ec063671..085636553e 100644
--- a/activerecord/test/cases/query_cache_test.rb
+++ b/activerecord/test/cases/query_cache_test.rb
@@ -6,6 +6,8 @@ require 'models/post'
require 'rack'
class QueryCacheTest < ActiveRecord::TestCase
+ self.use_transactional_tests = false
+
fixtures :tasks, :topics, :categories, :posts, :categories_posts
teardown do
diff --git a/activerecord/test/cases/scoping/named_scoping_test.rb b/activerecord/test/cases/scoping/named_scoping_test.rb
index 0e277ed235..f0dac07acc 100644
--- a/activerecord/test/cases/scoping/named_scoping_test.rb
+++ b/activerecord/test/cases/scoping/named_scoping_test.rb
@@ -46,6 +46,13 @@ class NamedScopingTest < ActiveRecord::TestCase
assert_equal Topic.average(:replies_count), Topic.base.average(:replies_count)
end
+ def test_calling_merge_at_first_in_scope
+ Topic.class_eval do
+ scope :calling_merge_at_first_in_scope, Proc.new { merge(Topic.replied) }
+ end
+ assert_equal Topic.calling_merge_at_first_in_scope.to_a, Topic.replied.to_a
+ end
+
def test_method_missing_priority_when_delegating
klazz = Class.new(ActiveRecord::Base) do
self.table_name = "topics"
diff --git a/activesupport/CHANGELOG.md b/activesupport/CHANGELOG.md
index 6103857a41..3749dda9fc 100644
--- a/activesupport/CHANGELOG.md
+++ b/activesupport/CHANGELOG.md
@@ -1,3 +1,36 @@
+* Introduce `assert_changes` and `assert_no_changes`.
+
+ `assert_changes` is a more general `assert_difference` that works with any
+ value.
+
+ assert_changes 'Error.current', from: nil, to: 'ERR' do
+ expected_bad_operation
+ end
+
+ Can be called with strings, to be evaluated in the binding (context) of
+ the block given to the assertion, or a lambda.
+
+ assert_changes -> { Error.current }, from: nil, to: 'ERR' do
+ expected_bad_operation
+ end
+
+ The `from` and `to` arguments are compared with the case operator (`===`).
+
+ assert_changes 'Error.current', from: nil, to: Error do
+ expected_bad_operation
+ end
+
+ This is pretty useful, if you need to loosely compare a value. For example,
+ you need to test a token has been generated and it has that many random
+ characters.
+
+ user = User.start_registration
+ assert_changes 'user.token', to: /\w{32}/ do
+ user.finish_registration
+ end
+
+ *Genadi Samokovarov*
+
* Add `:fallback_string` option to `Array#to_sentence`. If an empty array
calls the function and a fallback string option is set then it returns the
fallback string other than an empty string.
@@ -15,14 +48,14 @@
* `travel/travel_to` travel time helpers, now raise on nested calls,
as this can lead to confusing time stubbing.
-
+
Instead of:
-
+
travel_to 2.days.from_now do
# 2 days from today
travel_to 3.days.from_now do
# 5 days from today
- end
+ end
end
preferred way to achieve above is:
@@ -30,13 +63,12 @@
travel 2.days do
# 2 days from today
end
-
- travel 5.days do
- # 5 days from today
- end
-
+
+ travel 5.days do
+ # 5 days from today
+ end
+
*Vipul A M*
-
* Support parsing JSON time in ISO8601 local time strings in
`ActiveSupport::JSON.decode` when `parse_json_times` is enabled.
diff --git a/activesupport/lib/active_support/testing/assertions.rb b/activesupport/lib/active_support/testing/assertions.rb
index ad83638572..7770aa8006 100644
--- a/activesupport/lib/active_support/testing/assertions.rb
+++ b/activesupport/lib/active_support/testing/assertions.rb
@@ -1,6 +1,8 @@
module ActiveSupport
module Testing
module Assertions
+ UNTRACKED = Object.new # :nodoc:
+
# Asserts that an expression is not truthy. Passes if <tt>object</tt> is
# +nil+ or +false+. "Truthy" means "considered true in a conditional"
# like <tt>if foo</tt>.
@@ -92,6 +94,93 @@ module ActiveSupport
def assert_no_difference(expression, message = nil, &block)
assert_difference expression, 0, message, &block
end
+
+ # Assertion that the result of evaluating an expression is changed before
+ # and after invoking the passed in block.
+ #
+ # assert_changes 'Status.all_good?' do
+ # post :create, params: { status: { ok: false } }
+ # end
+ #
+ # You can pass the block as a string to be evaluated in the context of
+ # the block. A lambda can be passed for the block as well.
+ #
+ # assert_changes -> { Status.all_good? } do
+ # post :create, params: { status: { ok: false } }
+ # end
+ #
+ # The assertion is useful to test side effects. The passed block can be
+ # anything that can be converted to string with #to_s.
+ #
+ # assert_changes :@object do
+ # @object = 42
+ # end
+ #
+ # The keyword arguments :from and :to can be given to specify the
+ # expected initial value and the expected value after the block was
+ # executed.
+ #
+ # assert_changes :@object, from: nil, to: :foo do
+ # @object = :foo
+ # end
+ #
+ # An error message can be specified.
+ #
+ # assert_changes -> { Status.all_good? }, 'Expected the status to be bad' do
+ # post :create, params: { status: { incident: true } }
+ # end
+ def assert_changes(expression, message = nil, from: UNTRACKED, to: UNTRACKED, &block)
+ exp = expression.respond_to?(:call) ? expression : -> { eval(expression.to_s, block.binding) }
+
+ before = exp.call
+ retval = yield
+
+ unless from == UNTRACKED
+ error = "#{expression.inspect} isn't #{from}"
+ error = "#{message}.\n#{error}" if message
+ assert from === before, error
+ end
+
+ after = exp.call
+
+ if to == UNTRACKED
+ error = "#{expression.inspect} didn't changed"
+ error = "#{message}.\n#{error}" if message
+ assert_not_equal before, after, error
+ else
+ message = "#{expression.inspect} didn't change to #{to}"
+ error = "#{message}.\n#{error}" if message
+ assert to === after, error
+ end
+
+ retval
+ end
+
+ # Assertion that the result of evaluating an expression is changed before
+ # and after invoking the passed in block.
+ #
+ # assert_no_changes 'Status.all_good?' do
+ # post :create, params: { status: { ok: true } }
+ # end
+ #
+ # An error message can be specified.
+ #
+ # assert_no_changes -> { Status.all_good? }, 'Expected the status to be good' do
+ # post :create, params: { status: { ok: false } }
+ # end
+ def assert_no_changes(expression, message = nil, &block)
+ exp = expression.respond_to?(:call) ? expression : -> { eval(expression.to_s, block.binding) }
+
+ before = exp.call
+ retval = yield
+ after = exp.call
+
+ error = "#{expression.inspect} did change to #{after}"
+ error = "#{message}.\n#{error}" if message
+ assert_equal before, after, error
+
+ retval
+ end
end
end
end
diff --git a/activesupport/test/caching_test.rb b/activesupport/test/caching_test.rb
index 8002c14539..d4dec81b28 100644
--- a/activesupport/test/caching_test.rb
+++ b/activesupport/test/caching_test.rb
@@ -25,6 +25,18 @@ module ActiveSupport
assert_nil LocalCacheRegistry.cache_for(key)
end
+ def test_local_cache_cleared_and_response_should_be_present_on_invalid_parameters_error
+ key = "super awesome key"
+ assert_nil LocalCacheRegistry.cache_for key
+ middleware = Middleware.new('<3', key).new(->(env) {
+ assert LocalCacheRegistry.cache_for(key), 'should have a cache'
+ raise Rack::Utils::InvalidParameterError
+ })
+ response = middleware.call({})
+ assert response, 'response should exist'
+ assert_nil LocalCacheRegistry.cache_for(key)
+ end
+
def test_local_cache_cleared_on_exception
key = "super awesome key"
assert_nil LocalCacheRegistry.cache_for key
@@ -128,6 +140,16 @@ class CacheKeyTest < ActiveSupport::TestCase
end
class CacheStoreSettingTest < ActiveSupport::TestCase
+ def test_memory_store_gets_created_if_no_arguments_passed_to_lookup_store_method
+ store = ActiveSupport::Cache.lookup_store
+ assert_kind_of(ActiveSupport::Cache::MemoryStore, store)
+ end
+
+ def test_memory_store
+ store = ActiveSupport::Cache.lookup_store :memory_store
+ assert_kind_of(ActiveSupport::Cache::MemoryStore, store)
+ end
+
def test_file_fragment_cache_store
store = ActiveSupport::Cache.lookup_store :file_store, "/path/to/cache/directory"
assert_kind_of(ActiveSupport::Cache::FileStore, store)
diff --git a/activesupport/test/test_case_test.rb b/activesupport/test/test_case_test.rb
index 18228a2ac5..772c3cfca7 100644
--- a/activesupport/test/test_case_test.rb
+++ b/activesupport/test/test_case_test.rb
@@ -111,6 +111,112 @@ class AssertDifferenceTest < ActiveSupport::TestCase
end
end
end
+
+ def test_assert_changes_pass
+ assert_changes '@object.num' do
+ @object.increment
+ end
+ end
+
+ def test_assert_changes_pass_with_lambda
+ assert_changes -> { @object.num } do
+ @object.increment
+ end
+ end
+
+ def test_assert_changes_with_from_option
+ assert_changes '@object.num', from: 0 do
+ @object.increment
+ end
+ end
+
+ def test_assert_changes_with_from_option_with_wrong_value
+ assert_raises Minitest::Assertion do
+ assert_changes '@object.num', from: -1 do
+ @object.increment
+ end
+ end
+ end
+
+ def test_assert_changes_with_to_option
+ assert_changes '@object.num', to: 1 do
+ @object.increment
+ end
+ end
+
+ def test_assert_changes_with_wrong_to_option
+ assert_raises Minitest::Assertion do
+ assert_changes '@object.num', to: 2 do
+ @object.increment
+ end
+ end
+ end
+
+ def test_assert_changes_with_from_option_and_to_option
+ assert_changes '@object.num', from: 0, to: 1 do
+ @object.increment
+ end
+ end
+
+ def test_assert_changes_with_from_and_to_options_and_wrong_to_value
+ assert_raises Minitest::Assertion do
+ assert_changes '@object.num', from: 0, to: 2 do
+ @object.increment
+ end
+ end
+ end
+
+ def test_assert_changes_works_with_any_object
+ retval = silence_warnings do
+ assert_changes :@new_object, from: nil, to: 42 do
+ @new_object = 42
+ end
+ end
+
+ assert_equal 42, retval
+ end
+
+ def test_assert_changes_works_with_nil
+ oldval = @object
+
+ retval = assert_changes :@object, from: oldval, to: nil do
+ @object = nil
+ end
+
+ assert_nil retval
+ end
+
+ def test_assert_changes_with_to_and_case_operator
+ token = nil
+
+ assert_changes 'token', to: /\w{32}/ do
+ token = SecureRandom.hex
+ end
+ end
+
+ def test_assert_changes_with_to_and_from_and_case_operator
+ token = SecureRandom.hex
+
+ assert_changes 'token', from: /\w{32}/, to: /\w{32}/ do
+ token = SecureRandom.hex
+ end
+ end
+
+ def test_assert_no_changes_pass
+ assert_no_changes '@object.num' do
+ # ...
+ end
+ end
+
+ def test_assert_no_changes_with_message
+ error = assert_raises Minitest::Assertion do
+ assert_no_changes '@object.num', '@object.num should not change' do
+ @object.increment
+ end
+ end
+
+ assert_equal "@object.num should not change.\n\"@object.num\" did change to 1.\nExpected: 0\n Actual: 1", error.message
+ end
end
class AlsoDoingNothingTest < ActiveSupport::TestCase
diff --git a/guides/source/action_controller_overview.md b/guides/source/action_controller_overview.md
index 58362fea58..7b1138c7d4 100644
--- a/guides/source/action_controller_overview.md
+++ b/guides/source/action_controller_overview.md
@@ -362,7 +362,7 @@ If your user sessions don't store critical data or don't need to be around for l
Read more about session storage in the [Security Guide](security.html).
-If you need a different session storage mechanism, you can change it in the `config/initializers/session_store.rb` file:
+If you need a different session storage mechanism, you can change it in an initializer:
```ruby
# Use the database for sessions instead of the cookie-based default,
@@ -371,7 +371,7 @@ If you need a different session storage mechanism, you can change it in the `con
# Rails.application.config.session_store :active_record_store
```
-Rails sets up a session key (the name of the cookie) when signing the session data. These can also be changed in `config/initializers/session_store.rb`:
+Rails sets up a session key (the name of the cookie) when signing the session data. These can also be changed in an initializer:
```ruby
# Be sure to restart your server when you modify this file.
diff --git a/guides/source/configuring.md b/guides/source/configuring.md
index d11edf2bfa..ed5975f31d 100644
--- a/guides/source/configuring.md
+++ b/guides/source/configuring.md
@@ -142,7 +142,7 @@ defaults to `:debug` for all environments. The available log levels are: `:debug
* `config.public_file_server.enabled` configures Rails to serve static files from the public directory. This option defaults to `true`, but in the production environment it is set to `false` because the server software (e.g. NGINX or Apache) used to run the application should serve static files instead. If you are running or testing your app in production mode using WEBrick (it is not recommended to use WEBrick in production) set the option to `true.` Otherwise, you won't be able to use page caching and request for files that exist under the public directory.
-* `config.session_store` is usually set up in `config/initializers/session_store.rb` and specifies what class to use to store the session. Possible values are `:cookie_store` which is the default, `:mem_cache_store`, and `:disabled`. The last one tells Rails not to deal with sessions. Custom session stores can also be specified:
+* `config.session_store` specifies what class to use to store the session. Possible values are `:cookie_store` which is the default, `:mem_cache_store`, and `:disabled`. The last one tells Rails not to deal with sessions. Defaults to a cookie store with application name as the session key. Custom session stores can also be specified:
```ruby
config.session_store :my_custom_store
diff --git a/railties/CHANGELOG.md b/railties/CHANGELOG.md
index ef7049e6e5..1694abb61a 100644
--- a/railties/CHANGELOG.md
+++ b/railties/CHANGELOG.md
@@ -1,3 +1,8 @@
+* Set session store to cookie store internally and remove the initializer from
+ the generated app.
+
+ *Prathamesh Sonpatki*
+
* Set the server host using the `HOST` environment variable.
*mahnunchik*
diff --git a/railties/lib/rails/application/configuration.rb b/railties/lib/rails/application/configuration.rb
index 8e3f01edd7..7d0c3daa23 100644
--- a/railties/lib/rails/application/configuration.rb
+++ b/railties/lib/rails/application/configuration.rb
@@ -34,8 +34,6 @@ module Rails
@public_file_server.index_name = "index"
@force_ssl = false
@ssl_options = {}
- @session_store = :cookie_store
- @session_options = {}
@time_zone = "UTC"
@beginning_of_week = :monday
@log_level = nil
@@ -165,29 +163,37 @@ module Rails
self.generators.colorize_logging = val
end
- def session_store(*args)
- if args.empty?
- case @session_store
- when :disabled
- nil
- when :active_record_store
+ def session_store(new_session_store = nil, **options)
+ if new_session_store
+ if new_session_store == :active_record_store
begin
ActionDispatch::Session::ActiveRecordStore
rescue NameError
raise "`ActiveRecord::SessionStore` is extracted out of Rails into a gem. " \
"Please add `activerecord-session_store` to your Gemfile to use it."
end
+ end
+
+ @session_store = new_session_store
+ @session_options = options || {}
+ else
+ case @session_store
+ when :disabled
+ nil
+ when :active_record_store
+ ActionDispatch::Session::ActiveRecordStore
when Symbol
ActionDispatch::Session.const_get(@session_store.to_s.camelize)
else
@session_store
end
- else
- @session_store = args.shift
- @session_options = args.shift || {}
end
end
+ def session_store? #:nodoc:
+ @session_store
+ end
+
def annotations
SourceAnnotationExtractor::Annotation
end
diff --git a/railties/lib/rails/application/finisher.rb b/railties/lib/rails/application/finisher.rb
index daf3a24b16..64de10a5c7 100644
--- a/railties/lib/rails/application/finisher.rb
+++ b/railties/lib/rails/application/finisher.rb
@@ -33,6 +33,14 @@ module Rails
end
end
+ # Setup default session store if not already set in config/application.rb
+ initializer :setup_default_session_store, before: :build_middleware_stack do |app|
+ unless app.config.session_store?
+ app_name = app.class.name ? app.railtie_name.chomp('_application') : ''
+ app.config.session_store :cookie_store, key: "_#{app_name}_session"
+ end
+ end
+
initializer :build_middleware_stack do
build_middleware_stack
end
diff --git a/railties/lib/rails/generators/rails/app/app_generator.rb b/railties/lib/rails/generators/rails/app/app_generator.rb
index 2879be5fa2..0390457cdb 100644
--- a/railties/lib/rails/generators/rails/app/app_generator.rb
+++ b/railties/lib/rails/generators/rails/app/app_generator.rb
@@ -337,7 +337,6 @@ module Rails
def delete_non_api_initializers_if_api_option
if options[:api]
- remove_file 'config/initializers/session_store.rb'
remove_file 'config/initializers/cookies_serializer.rb'
end
end
diff --git a/railties/lib/rails/generators/rails/app/templates/config/initializers/session_store.rb.tt b/railties/lib/rails/generators/rails/app/templates/config/initializers/session_store.rb.tt
deleted file mode 100644
index 2bb9b82c61..0000000000
--- a/railties/lib/rails/generators/rails/app/templates/config/initializers/session_store.rb.tt
+++ /dev/null
@@ -1,3 +0,0 @@
-# Be sure to restart your server when you modify this file.
-
-Rails.application.config.session_store :cookie_store, key: <%= "'_#{app_name}_session'" %>
diff --git a/railties/test/application/configuration_test.rb b/railties/test/application/configuration_test.rb
index e1c6f214ab..e4a3adee9f 100644
--- a/railties/test/application/configuration_test.rb
+++ b/railties/test/application/configuration_test.rb
@@ -1186,6 +1186,22 @@ module ApplicationTests
end
end
+ test "default session store initializer does not overwrite the user defined session store even if it is disabled" do
+ make_basic_app do |application|
+ application.config.session_store :disabled
+ end
+
+ assert_equal nil, app.config.session_store
+ end
+
+ test "default session store initializer sets session store to cookie store" do
+ session_options = { key: "_myapp_session", cookie_only: true }
+ make_basic_app
+
+ assert_equal ActionDispatch::Session::CookieStore, app.config.session_store
+ assert_equal session_options, app.config.session_options
+ end
+
test "config.log_level with custom logger" do
make_basic_app do |application|
application.config.logger = Logger.new(STDOUT)
diff --git a/railties/test/generators/api_app_generator_test.rb b/railties/test/generators/api_app_generator_test.rb
index 92779452e1..9ad936710d 100644
--- a/railties/test/generators/api_app_generator_test.rb
+++ b/railties/test/generators/api_app_generator_test.rb
@@ -108,7 +108,6 @@ class ApiAppGeneratorTest < Rails::Generators::TestCase
app/views/layouts/application.html.erb
config/initializers/assets.rb
config/initializers/cookies_serializer.rb
- config/initializers/session_store.rb
lib/assets
vendor/assets
test/helpers
diff --git a/railties/test/generators/app_generator_test.rb b/railties/test/generators/app_generator_test.rb
index ac1488abb3..50751368c8 100644
--- a/railties/test/generators/app_generator_test.rb
+++ b/railties/test/generators/app_generator_test.rb
@@ -131,7 +131,6 @@ class AppGeneratorTest < Rails::Generators::TestCase
generator.send(:app_const)
quietly { generator.send(:update_config_files) }
assert_file "myapp_moved/config/environment.rb", /Rails\.application\.initialize!/
- assert_file "myapp_moved/config/initializers/session_store.rb", /_myapp_session/
end
end
end
@@ -144,7 +143,6 @@ class AppGeneratorTest < Rails::Generators::TestCase
generator = Rails::Generators::AppGenerator.new ["rails"], [], destination_root: app_root, shell: @shell
generator.send(:app_const)
quietly { generator.send(:update_config_files) }
- assert_file "myapp/config/initializers/session_store.rb", /_myapp_session/
end
end
@@ -552,13 +550,6 @@ class AppGeneratorTest < Rails::Generators::TestCase
assert_file "config/application.rb", /\s+require\s+["']active_job\/railtie["']/
end
- def test_new_hash_style
- run_generator
- assert_file "config/initializers/session_store.rb" do |file|
- assert_match(/config.session_store :cookie_store, key: '_.+_session'/, file)
- end
- end
-
def test_pretend_option
output = run_generator [File.join(destination_root, "myapp"), "--pretend"]
assert_no_match(/run bundle install/, output)
@@ -571,7 +562,6 @@ class AppGeneratorTest < Rails::Generators::TestCase
run_generator [path, "-d", 'postgresql']
assert_file "foo bar/config/database.yml", /database: foo_bar_development/
- assert_file "foo bar/config/initializers/session_store.rb", /key: '_foo_bar/
end
def test_web_console