aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--.travis.yml15
-rw-r--r--Gemfile3
-rw-r--r--Gemfile.lock1
-rw-r--r--activejob/lib/active_job/test_helper.rb16
-rw-r--r--activerecord/CHANGELOG.md7
-rw-r--r--activerecord/lib/active_record/associations/collection_association.rb7
-rw-r--r--activerecord/lib/active_record/associations/collection_proxy.rb19
-rw-r--r--activerecord/lib/active_record/connection_adapters/postgresql/oid/hstore.rb2
-rw-r--r--activerecord/lib/active_record/null_relation.rb9
-rw-r--r--activerecord/lib/active_record/relation.rb3
-rw-r--r--activerecord/test/cases/adapters/postgresql/hstore_test.rb10
-rw-r--r--activerecord/test/cases/adapters/postgresql/prepared_statements_test.rb9
-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.rb1
-rw-r--r--activerecord/test/cases/associations/join_model_test.rb2
-rw-r--r--activerecord/test/cases/relations_test.rb1
-rw-r--r--activesupport/CHANGELOG.md4
-rw-r--r--activesupport/lib/active_support/rescuable.rb13
-rw-r--r--activesupport/lib/active_support/testing/isolation.rb3
-rw-r--r--activesupport/test/rescuable_test.rb2
-rw-r--r--guides/source/active_support_core_extensions.md50
-rw-r--r--guides/source/security.md2
22 files changed, 86 insertions, 95 deletions
diff --git a/.travis.yml b/.travis.yml
index d465378c72..af207f4f5d 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -33,6 +33,8 @@ before_script:
script: 'ci/travis.rb'
env:
+ global:
+ - "JRUBY_OPTS='--dev -J-Xmx1024M'"
matrix:
- "GEM=railties"
- "GEM=ap"
@@ -47,14 +49,14 @@ env:
- "GEM=guides"
rvm:
- - 2.2.5
- - 2.3.1
+ - 2.2.6
+ - 2.3.2
- ruby-head
matrix:
include:
# Latest compiled version in http://rubies.travis-ci.org
- - rvm: 2.3.1
+ - rvm: 2.3.2
env:
- "GEM=ar:mysql2"
addons:
@@ -62,8 +64,11 @@ matrix:
- rvm: jruby-9.1.5.0
jdk: oraclejdk8
env:
- - "JRUBY_OPTS='--dev -J-Xmx1024M'"
- - "GEM='ap'"
+ - "GEM=ap"
+ - rvm: jruby-9.1.5.0
+ jdk: oraclejdk8
+ env:
+ - "GEM=am,aj"
allow_failures:
- rvm: ruby-head
- rvm: jruby-9.1.5.0
diff --git a/Gemfile b/Gemfile
index 97a19188bc..10e146d887 100644
--- a/Gemfile
+++ b/Gemfile
@@ -35,6 +35,9 @@ gem "sass", github: "sass/sass", branch: "stable", require: false
# FIXME: Remove this fork after https://github.com/nex3/rb-inotify/pull/49 is fixed.
gem "rb-inotify", github: "matthewd/rb-inotify", branch: "close-handling", require: false
+# Explicitly avoid 1.x that doesn't support Ruby 2.4+
+gem "json", ">= 2.0.0"
+
group :doc do
gem "sdoc", "1.0.0.beta2"
gem "redcarpet", "~> 3.2.3", platforms: :ruby
diff --git a/Gemfile.lock b/Gemfile.lock
index 1a39484c2b..06e822964b 100644
--- a/Gemfile.lock
+++ b/Gemfile.lock
@@ -379,6 +379,7 @@ DEPENDENCIES
em-hiredis
hiredis
jquery-rails
+ json (>= 2.0.0)
kindlerb (>= 1.0.1)
listen (>= 3.0.5, < 3.2)
minitest (< 5.3.4)
diff --git a/activejob/lib/active_job/test_helper.rb b/activejob/lib/active_job/test_helper.rb
index 1a8b3375ae..9e45c0da24 100644
--- a/activejob/lib/active_job/test_helper.rb
+++ b/activejob/lib/active_job/test_helper.rb
@@ -232,16 +232,16 @@ module ActiveJob
# MyJob.set(wait_until: Date.tomorrow.noon).perform_later
# end
# end
- def assert_enqueued_with(args = {})
+ def assert_enqueued_with(job: nil, args: nil, at: nil, queue: nil)
original_enqueued_jobs_count = enqueued_jobs.count
- args.assert_valid_keys(:job, :args, :at, :queue)
- serialized_args = serialize_args_for_assertion(args)
+ expected = { job: job, args: args, at: at, queue: queue }.compact
+ serialized_args = serialize_args_for_assertion(expected)
yield
in_block_jobs = enqueued_jobs.drop(original_enqueued_jobs_count)
matching_job = in_block_jobs.find do |job|
serialized_args.all? { |key, value| value == job[key] }
end
- assert matching_job, "No enqueued job found with #{args}"
+ assert matching_job, "No enqueued job found with #{expected}"
instantiate_job(matching_job)
end
@@ -256,16 +256,16 @@ module ActiveJob
# MyJob.set(wait_until: Date.tomorrow.noon).perform_later
# end
# end
- def assert_performed_with(args = {})
+ def assert_performed_with(job: nil, args: nil, at: nil, queue: nil)
original_performed_jobs_count = performed_jobs.count
- args.assert_valid_keys(:job, :args, :at, :queue)
- serialized_args = serialize_args_for_assertion(args)
+ expected = { job: job, args: args, at: at, queue: queue }.compact
+ serialized_args = serialize_args_for_assertion(expected)
perform_enqueued_jobs { yield }
in_block_jobs = performed_jobs.drop(original_performed_jobs_count)
matching_job = in_block_jobs.find do |job|
serialized_args.all? { |key, value| value == job[key] }
end
- assert matching_job, "No performed job found with #{args}"
+ assert matching_job, "No performed job found with #{expected}"
instantiate_job(matching_job)
end
diff --git a/activerecord/CHANGELOG.md b/activerecord/CHANGELOG.md
index 8efefe9b2e..7f4be54dbb 100644
--- a/activerecord/CHANGELOG.md
+++ b/activerecord/CHANGELOG.md
@@ -1,3 +1,10 @@
+* Allow `ActionController::Parameters`-like objects to be passed as
+ values for Postgres HStore columns.
+
+ Fixes #26904.
+
+ *Jon Moss*
+
* Added `stat` method to `ActiveRecord::ConnectionAdapters::ConnectionPool`
Example:
diff --git a/activerecord/lib/active_record/associations/collection_association.rb b/activerecord/lib/active_record/associations/collection_association.rb
index 5a323c62e6..b2cf4713bb 100644
--- a/activerecord/lib/active_record/associations/collection_association.rb
+++ b/activerecord/lib/active_record/associations/collection_association.rb
@@ -246,13 +246,6 @@ module ActiveRecord
end
end
- def distinct
- seen = {}
- load_target.find_all do |record|
- seen[record.id] = true unless seen.key?(record.id)
- end
- end
-
# Replace this collection with +other_array+. This will perform a diff
# and delete/add only records that have changed.
def replace(other_array)
diff --git a/activerecord/lib/active_record/associations/collection_proxy.rb b/activerecord/lib/active_record/associations/collection_proxy.rb
index 0800639c24..35a98d7090 100644
--- a/activerecord/lib/active_record/associations/collection_proxy.rb
+++ b/activerecord/lib/active_record/associations/collection_proxy.rb
@@ -718,6 +718,12 @@ module ActiveRecord
@association.destroy(*records)
end
+ ##
+ # :method: distinct
+ #
+ # :call-seq:
+ # distinct(value = true)
+ #
# Specifies whether the records should be unique or not.
#
# class Person < ActiveRecord::Base
@@ -732,10 +738,17 @@ module ActiveRecord
#
# person.pets.select(:name).distinct
# # => [#<Pet name: "Fancy-Fancy">]
- def distinct
- @association.distinct
+ #
+ # person.pets.select(:name).distinct.distinct(false)
+ # # => [
+ # # #<Pet name: "Fancy-Fancy">,
+ # # #<Pet name: "Fancy-Fancy">
+ # # ]
+
+ #--
+ def uniq
+ load_target.uniq
end
- alias uniq distinct
def calculate(operation, column_name)
null_scope? ? scope.calculate(operation, column_name) : super
diff --git a/activerecord/lib/active_record/connection_adapters/postgresql/oid/hstore.rb b/activerecord/lib/active_record/connection_adapters/postgresql/oid/hstore.rb
index a74a044a3a..d629ebca91 100644
--- a/activerecord/lib/active_record/connection_adapters/postgresql/oid/hstore.rb
+++ b/activerecord/lib/active_record/connection_adapters/postgresql/oid/hstore.rb
@@ -24,6 +24,8 @@ module ActiveRecord
def serialize(value)
if value.is_a?(::Hash)
value.map { |k, v| "#{escape_hstore(k)}=>#{escape_hstore(v)}" }.join(", ")
+ elsif value.respond_to?(:to_unsafe_h)
+ serialize(value.to_unsafe_h)
else
value
end
diff --git a/activerecord/lib/active_record/null_relation.rb b/activerecord/lib/active_record/null_relation.rb
index 254550c378..2bb7ed6d5e 100644
--- a/activerecord/lib/active_record/null_relation.rb
+++ b/activerecord/lib/active_record/null_relation.rb
@@ -41,12 +41,11 @@ module ActiveRecord
end
def calculate(operation, _column_name)
- if [:count, :sum].include? operation
+ case operation
+ when :count, :sum
group_values.any? ? Hash.new : 0
- elsif [:average, :minimum, :maximum].include?(operation) && group_values.any?
- Hash.new
- else
- nil
+ when :average, :minimum, :maximum
+ group_values.any? ? Hash.new : nil
end
end
diff --git a/activerecord/lib/active_record/relation.rb b/activerecord/lib/active_record/relation.rb
index 8b30a48c1c..6f602e4a23 100644
--- a/activerecord/lib/active_record/relation.rb
+++ b/activerecord/lib/active_record/relation.rb
@@ -362,6 +362,9 @@ module ActiveRecord
#
# # Update all books that match conditions, but limit it to 5 ordered by date
# Book.where('title LIKE ?', '%Rails%').order(:created_at).limit(5).update_all(author: 'David')
+ #
+ # # Update all invoices and set the number column to its id value.
+ # Invoice.update_all('number = id')
def update_all(updates)
raise ArgumentError, "Empty list of attributes to change" if updates.blank?
diff --git a/activerecord/test/cases/adapters/postgresql/hstore_test.rb b/activerecord/test/cases/adapters/postgresql/hstore_test.rb
index c9982d3705..1f35300739 100644
--- a/activerecord/test/cases/adapters/postgresql/hstore_test.rb
+++ b/activerecord/test/cases/adapters/postgresql/hstore_test.rb
@@ -10,6 +10,12 @@ if ActiveRecord::Base.connection.supports_extensions?
store_accessor :settings, :language, :timezone
end
+ class FakeParameters
+ def to_unsafe_h
+ { "hi" => "hi" }
+ end
+ end
+
def setup
@connection = ActiveRecord::Base.connection
@@ -321,6 +327,10 @@ if ActiveRecord::Base.connection.supports_extensions?
assert_match %r[t\.hstore "tags",\s+default: {}], output
end
+ def test_supports_to_unsafe_h_values
+ assert_equal("\"hi\"=>\"hi\"", @type.serialize(FakeParameters.new))
+ end
+
private
def assert_array_cycle(array)
# test creation
diff --git a/activerecord/test/cases/adapters/postgresql/prepared_statements_test.rb b/activerecord/test/cases/adapters/postgresql/prepared_statements_test.rb
index b898929f8a..181c1a097c 100644
--- a/activerecord/test/cases/adapters/postgresql/prepared_statements_test.rb
+++ b/activerecord/test/cases/adapters/postgresql/prepared_statements_test.rb
@@ -1,19 +1,20 @@
require "cases/helper"
+require "models/computer"
require "models/developer"
class PreparedStatementsTest < ActiveRecord::PostgreSQLTestCase
fixtures :developers
def setup
- @default_prepared_statements = Developer.connection_config[:prepared_statements]
- Developer.connection_config[:prepared_statements] = false
+ @default_prepared_statements = ActiveRecord::Base.connection.instance_variable_get("@prepared_statements")
+ ActiveRecord::Base.connection.instance_variable_set("@prepared_statements", false)
end
def teardown
- Developer.connection_config[:prepared_statements] = @default_prepared_statements
+ ActiveRecord::Base.connection.instance_variable_set("@prepared_statements", @default_prepared_statements)
end
- def nothing_raised_with_falsy_prepared_statements
+ def test_nothing_raised_with_falsy_prepared_statements
assert_nothing_raised do
Developer.where(id: 1)
end
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 4902792eee..4b7ac594cf 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
@@ -383,7 +383,7 @@ class HasAndBelongsToManyAssociationsTest < ActiveRecord::TestCase
dev.projects << projects(:active_record)
assert_equal 3, dev.projects.size
- assert_equal 1, dev.projects.distinct.size
+ assert_equal 1, dev.projects.uniq.size
end
def test_distinct_before_the_fact
diff --git a/activerecord/test/cases/associations/has_many_associations_test.rb b/activerecord/test/cases/associations/has_many_associations_test.rb
index 3afd062950..d657be71cc 100644
--- a/activerecord/test/cases/associations/has_many_associations_test.rb
+++ b/activerecord/test/cases/associations/has_many_associations_test.rb
@@ -919,6 +919,7 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
company.clients_of_firm.build("name" => "Another Client")
company.clients_of_firm.build("name" => "Yet Another Client")
assert_equal 4, company.clients_of_firm.size
+ assert_equal 4, company.clients_of_firm.uniq.size
end
def test_collection_not_empty_after_building
diff --git a/activerecord/test/cases/associations/join_model_test.rb b/activerecord/test/cases/associations/join_model_test.rb
index 15446c6dc7..a4345f3857 100644
--- a/activerecord/test/cases/associations/join_model_test.rb
+++ b/activerecord/test/cases/associations/join_model_test.rb
@@ -413,7 +413,7 @@ class AssociationsJoinModelTest < ActiveRecord::TestCase
author = Author.includes(:taggings).find authors(:david).id
expected_taggings = taggings(:welcome_general, :thinking_general)
assert_no_queries do
- assert_equal expected_taggings, author.taggings.distinct.sort_by(&:id)
+ assert_equal expected_taggings, author.taggings.uniq.sort_by(&:id)
end
end
diff --git a/activerecord/test/cases/relations_test.rb b/activerecord/test/cases/relations_test.rb
index 0501514ba9..96833ad428 100644
--- a/activerecord/test/cases/relations_test.rb
+++ b/activerecord/test/cases/relations_test.rb
@@ -785,7 +785,6 @@ class RelationTest < ActiveRecord::TestCase
expected_taggings = taggings(:welcome_general, :thinking_general)
assert_no_queries do
- assert_equal expected_taggings, author.taggings.distinct.sort_by(&:id)
assert_equal expected_taggings, author.taggings.uniq.sort_by(&:id)
end
diff --git a/activesupport/CHANGELOG.md b/activesupport/CHANGELOG.md
index 8b022af235..10095ee1bd 100644
--- a/activesupport/CHANGELOG.md
+++ b/activesupport/CHANGELOG.md
@@ -31,10 +31,6 @@
*Andrew White*
-* Remove deprecated method `Module.local_constants`
-
- *Andrew White*
-
* Remove deprecated file `active_support/core_ext/time/marshal.rb`
*Andrew White*
diff --git a/activesupport/lib/active_support/rescuable.rb b/activesupport/lib/active_support/rescuable.rb
index 135690cc42..7f29c955f9 100644
--- a/activesupport/lib/active_support/rescuable.rb
+++ b/activesupport/lib/active_support/rescuable.rb
@@ -85,14 +85,16 @@ module ActiveSupport
#
# Returns the exception if it was handled and +nil+ if it was not.
def rescue_with_handler(exception, object: self)
- if handler = handler_for_rescue(exception, object: object)
+ handler, exception = handler_for_rescue(exception, object: object)
+ if handler
handler.call exception
exception
end
end
def handler_for_rescue(exception, object: self) #:nodoc:
- case rescuer = find_rescue_handler(exception)
+ rescuer, exception = find_rescue_handler(exception)
+ result = case rescuer
when Symbol
method = object.method(rescuer)
if method.arity == 0
@@ -107,6 +109,7 @@ module ActiveSupport
-> e { object.instance_exec(e, &rescuer) }
end
end
+ [result, exception]
end
private
@@ -121,7 +124,11 @@ module ActiveSupport
end
end
- handler || find_rescue_handler(exception.cause)
+ if handler
+ [handler, exception]
+ else
+ find_rescue_handler(exception.cause)
+ end
end
end
diff --git a/activesupport/lib/active_support/testing/isolation.rb b/activesupport/lib/active_support/testing/isolation.rb
index d30b34ecd6..404efe50bf 100644
--- a/activesupport/lib/active_support/testing/isolation.rb
+++ b/activesupport/lib/active_support/testing/isolation.rb
@@ -2,6 +2,7 @@ module ActiveSupport
module Testing
module Isolation
require "thread"
+ require "shellwords"
def self.included(klass) #:nodoc:
klass.class_eval do
@@ -80,7 +81,7 @@ module ActiveSupport
load_paths = $-I.map { |p| "-I\"#{File.expand_path(p)}\"" }.join(" ")
orig_args = ORIG_ARGV.join(" ")
- test_opts = "-n#{self.class.name}##{self.name}"
+ test_opts = "-n#{self.class.name}##{Shellwords.escape(self.name)}"
command = "#{Gem.ruby} #{load_paths} #{$0} '#{orig_args}' #{test_opts}"
# IO.popen lets us pass env in a cross-platform way
diff --git a/activesupport/test/rescuable_test.rb b/activesupport/test/rescuable_test.rb
index 7e5c3d1a8f..f7eb047d44 100644
--- a/activesupport/test/rescuable_test.rb
+++ b/activesupport/test/rescuable_test.rb
@@ -137,6 +137,6 @@ class RescuableTest < ActiveSupport::TestCase
def test_rescue_falls_back_to_exception_cause
@stargate.dispatch :fall_back_to_cause
- assert_equal "unhandled RuntimeError with a handleable cause", @stargate.result
+ assert_equal "dex", @stargate.result
end
end
diff --git a/guides/source/active_support_core_extensions.md b/guides/source/active_support_core_extensions.md
index 70b04a9695..6bbc79a326 100644
--- a/guides/source/active_support_core_extensions.md
+++ b/guides/source/active_support_core_extensions.md
@@ -511,56 +511,6 @@ NOTE: Defined in `active_support/core_ext/object/inclusion.rb`.
Extensions to `Module`
----------------------
-### `alias_method_chain`
-
-**This method is deprecated in favour of using Module#prepend.**
-
-Using plain Ruby you can wrap methods with other methods, that's called _alias chaining_.
-
-For example, let's say you'd like params to be strings in functional tests, as they are in real requests, but still want the convenience of assigning integers and other kind of values. To accomplish that you could wrap `ActionDispatch::IntegrationTest#process` this way in `test/test_helper.rb`:
-
-```ruby
-ActionDispatch::IntegrationTest.class_eval do
- # save a reference to the original process method
- alias_method :original_process, :process
-
- # now redefine process and delegate to original_process
- def process('GET', path, params: nil, headers: nil, env: nil, xhr: false)
- params = Hash[*params.map {|k, v| [k, v.to_s]}.flatten]
- original_process('GET', path, params: params)
- end
-end
-```
-
-That's the method `get`, `post`, etc., delegate the work to.
-
-That technique has a risk, it could be the case that `:original_process` was taken. To try to avoid collisions people choose some label that characterizes what the chaining is about:
-
-```ruby
-ActionDispatch::IntegrationTest.class_eval do
- def process_with_stringified_params(...)
- params = Hash[*params.map {|k, v| [k, v.to_s]}.flatten]
- process_without_stringified_params(method, path, params: params)
- end
- alias_method :process_without_stringified_params, :process
- alias_method :process, :process_with_stringified_params
-end
-```
-
-The method `alias_method_chain` provides a shortcut for that pattern:
-
-```ruby
-ActionDispatch::IntegrationTest.class_eval do
- def process_with_stringified_params(...)
- params = Hash[*params.map {|k, v| [k, v.to_s]}.flatten]
- process_without_stringified_params(method, path, params: params)
- end
- alias_method_chain :process, :stringified_params
-end
-```
-
-NOTE: Defined in `active_support/core_ext/module/aliasing.rb`.
-
### Attributes
#### `alias_attribute`
diff --git a/guides/source/security.md b/guides/source/security.md
index aea9728c10..bb67eb75d9 100644
--- a/guides/source/security.md
+++ b/guides/source/security.md
@@ -762,7 +762,7 @@ s = sanitize(user_input, tags: tags, attributes: %w(href title))
This allows only the given tags and does a good job, even against all kinds of tricks and malformed tags.
-As a second step, _it is good practice to escape all output of the application_, especially when re-displaying user input, which hasn't been input-filtered (as in the search form example earlier on). _Use `escapeHTML()` (or its alias `h()`) method_ to replace the HTML input characters &amp;, &quot;, &lt;, and &gt; by their uninterpreted representations in HTML (`&amp;`, `&quot;`, `&lt;`, and `&gt;`). However, it can easily happen that the programmer forgets to use it, so _it is recommended to use the SafeErb gem. SafeErb reminds you to escape strings from external sources.
+As a second step, _it is good practice to escape all output of the application_, especially when re-displaying user input, which hasn't been input-filtered (as in the search form example earlier on). _Use `escapeHTML()` (or its alias `h()`) method_ to replace the HTML input characters &amp;, &quot;, &lt;, and &gt; by their uninterpreted representations in HTML (`&amp;`, `&quot;`, `&lt;`, and `&gt;`).
##### Obfuscation and Encoding Injection