aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--actionpack/CHANGELOG.md5
-rw-r--r--actionpack/lib/action_controller/metal/params_wrapper.rb15
-rw-r--r--actionpack/test/controller/params_wrapper_test.rb20
-rw-r--r--activerecord/CHANGELOG.md21
-rw-r--r--activerecord/lib/active_record/connection_adapters/postgresql/array_parser.rb5
-rw-r--r--activerecord/lib/active_record/connection_adapters/postgresql/schema_statements.rb13
-rw-r--r--activerecord/lib/active_record/core.rb4
-rw-r--r--activerecord/lib/active_record/relation/query_methods.rb7
-rw-r--r--activerecord/test/cases/adapters/postgresql/array_test.rb12
-rw-r--r--activerecord/test/cases/adapters/postgresql/schema_test.rb12
-rw-r--r--activerecord/test/cases/scoping/named_scoping_test.rb4
-rw-r--r--activesupport/CHANGELOG.md7
-rw-r--r--activesupport/lib/active_support/xml_mini.rb6
-rw-r--r--activesupport/test/xml_mini_test.rb124
14 files changed, 240 insertions, 15 deletions
diff --git a/actionpack/CHANGELOG.md b/actionpack/CHANGELOG.md
index 394b1473d3..911c916028 100644
--- a/actionpack/CHANGELOG.md
+++ b/actionpack/CHANGELOG.md
@@ -1,3 +1,8 @@
+* Do not discard query parameters that form a hash with the same root key as
+ the `wrapper_key` for a request using `wrap_parameters`.
+
+ *Josh Jordan*
+
* Add `:serializer` option for `config.session_store :cookie_store`. This
changes default serializer when using `:cookie_store`.
diff --git a/actionpack/lib/action_controller/metal/params_wrapper.rb b/actionpack/lib/action_controller/metal/params_wrapper.rb
index c9f1d8dcb4..2ca8955741 100644
--- a/actionpack/lib/action_controller/metal/params_wrapper.rb
+++ b/actionpack/lib/action_controller/metal/params_wrapper.rb
@@ -231,7 +231,12 @@ module ActionController
# by the metal call stack.
def process_action(*args)
if _wrapper_enabled?
- wrapped_hash = _wrap_parameters request.request_parameters
+ if request.parameters[_wrapper_key].present?
+ wrapped_hash = _extract_parameters(request.parameters)
+ else
+ wrapped_hash = _wrap_parameters request.request_parameters
+ end
+
wrapped_keys = request.request_parameters.keys
wrapped_filtered_hash = _wrap_parameters request.filtered_parameters.slice(*wrapped_keys)
@@ -259,14 +264,16 @@ module ActionController
# Returns the list of parameters which will be selected for wrapped.
def _wrap_parameters(parameters)
- value = if include_only = _wrapper_options.include
+ { _wrapper_key => _extract_parameters(parameters) }
+ end
+
+ def _extract_parameters(parameters)
+ if include_only = _wrapper_options.include
parameters.slice(*include_only)
else
exclude = _wrapper_options.exclude || []
parameters.except(*(exclude + EXCLUDE_PARAMETERS))
end
-
- { _wrapper_key => value }
end
# Checks if we should perform parameters wrapping.
diff --git a/actionpack/test/controller/params_wrapper_test.rb b/actionpack/test/controller/params_wrapper_test.rb
index d87e2b85b0..11ccb6cf3b 100644
--- a/actionpack/test/controller/params_wrapper_test.rb
+++ b/actionpack/test/controller/params_wrapper_test.rb
@@ -188,6 +188,26 @@ class ParamsWrapperTest < ActionController::TestCase
assert_parameters({ 'username' => 'sikachu', 'title' => 'Developer', 'user' => { 'username' => 'sikachu', 'title' => 'Developer' }})
end
end
+
+ def test_preserves_query_string_params
+ with_default_wrapper_options do
+ @request.env['CONTENT_TYPE'] = 'application/json'
+ get :parse, { 'user' => { 'username' => 'nixon' } }
+ assert_parameters(
+ {'user' => { 'username' => 'nixon' } }
+ )
+ end
+ end
+
+ def test_empty_parameter_set
+ with_default_wrapper_options do
+ @request.env['CONTENT_TYPE'] = 'application/json'
+ post :parse, {}
+ assert_parameters(
+ {'user' => { } }
+ )
+ end
+ end
end
class NamespacedParamsWrapperTest < ActionController::TestCase
diff --git a/activerecord/CHANGELOG.md b/activerecord/CHANGELOG.md
index 8c09284e53..78c0ab1cfd 100644
--- a/activerecord/CHANGELOG.md
+++ b/activerecord/CHANGELOG.md
@@ -1,3 +1,24 @@
+* PostgreSQL implementation of SchemaStatements#index_name_exists?
+
+ The database agnostic implementation does not detect with indexes that are
+ not supported by the ActiveRecord schema dumper. For example, expressions
+ indexes would not be detected.
+
+ This fixes #11018.
+
+ *Jonathan Baudanza*
+
+* Parsing PostgreSQL arrays with empty strings now works correctly.
+
+ Previously, if you tried to parse `{"1","","2","","3"}` the result
+ would be `["1","2","3"]`, removing the empty strings from the array,
+ which would be incorrect. Now it will correctly produce `["1","","2","","3"]`
+ as the result of parsing the above PostgreSQL array.
+
+ Fixes #13907.
+
+ *MaurĂ­cio Linhares*
+
* Associations now raise `ArgumentError` on name conflicts.
Dangerous association names conflicts include instance or class methods already
diff --git a/activerecord/lib/active_record/connection_adapters/postgresql/array_parser.rb b/activerecord/lib/active_record/connection_adapters/postgresql/array_parser.rb
index 20de8d1982..0b218f2bfd 100644
--- a/activerecord/lib/active_record/connection_adapters/postgresql/array_parser.rb
+++ b/activerecord/lib/active_record/connection_adapters/postgresql/array_parser.rb
@@ -91,8 +91,9 @@ module ActiveRecord
end
def add_item_to_array(array, current_item, quoted)
- if current_item.length == 0
- elsif !quoted && current_item == 'NULL'
+ return if !quoted && current_item.length == 0
+
+ if !quoted && current_item == 'NULL'
array.push nil
else
array.push current_item
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 571257f6dd..ae8ede4b42 100644
--- a/activerecord/lib/active_record/connection_adapters/postgresql/schema_statements.rb
+++ b/activerecord/lib/active_record/connection_adapters/postgresql/schema_statements.rb
@@ -126,6 +126,19 @@ module ActiveRecord
SQL
end
+ def index_name_exists?(table_name, index_name, default)
+ exec_query(<<-SQL, 'SCHEMA').rows.first[0].to_i > 0
+ SELECT COUNT(*)
+ FROM pg_class t
+ INNER JOIN pg_index d ON t.oid = d.indrelid
+ INNER JOIN pg_class i ON d.indexrelid = i.oid
+ WHERE i.relkind = 'i'
+ AND i.relname = '#{index_name}'
+ AND t.relname = '#{table_name}'
+ AND i.relnamespace IN (SELECT oid FROM pg_namespace WHERE nspname = ANY (current_schemas(false)) )
+ SQL
+ end
+
# Returns an array of indexes for the given table.
def indexes(table_name, name = nil)
result = query(<<-SQL, 'SCHEMA')
diff --git a/activerecord/lib/active_record/core.rb b/activerecord/lib/active_record/core.rb
index 6303fe5ee4..d691192cfd 100644
--- a/activerecord/lib/active_record/core.rb
+++ b/activerecord/lib/active_record/core.rb
@@ -138,12 +138,12 @@ module ActiveRecord
# class Post < ActiveRecord::Base
# scope :published_and_commented, -> { published.and(self.arel_table[:comments_count].gt(0)) }
# end
- def arel_table
+ def arel_table # :nodoc:
@arel_table ||= Arel::Table.new(table_name, arel_engine)
end
# Returns the Arel engine.
- def arel_engine
+ def arel_engine # :nodoc:
@arel_engine ||=
if Base == self || connection_handler.retrieve_connection_pool(self)
self
diff --git a/activerecord/lib/active_record/relation/query_methods.rb b/activerecord/lib/active_record/relation/query_methods.rb
index 860063426a..077f09b67d 100644
--- a/activerecord/lib/active_record/relation/query_methods.rb
+++ b/activerecord/lib/active_record/relation/query_methods.rb
@@ -824,11 +824,12 @@ module ActiveRecord
end
# Returns the Arel object associated with the relation.
- def arel
+ def arel # :nodoc:
@arel ||= build_arel
end
- # Like #arel, but ignores the default scope of the model.
+ private
+
def build_arel
arel = Arel::SelectManager.new(table.engine, table)
@@ -854,8 +855,6 @@ module ActiveRecord
arel
end
- private
-
def symbol_unscoping(scope)
if !VALID_UNSCOPING_VALUES.include?(scope)
raise ArgumentError, "Called unscope() with invalid unscoping argument ':#{scope}'. Valid arguments are :#{VALID_UNSCOPING_VALUES.to_a.join(", :")}."
diff --git a/activerecord/test/cases/adapters/postgresql/array_test.rb b/activerecord/test/cases/adapters/postgresql/array_test.rb
index d71e2aa2bb..3090f4478f 100644
--- a/activerecord/test/cases/adapters/postgresql/array_test.rb
+++ b/activerecord/test/cases/adapters/postgresql/array_test.rb
@@ -93,6 +93,18 @@ class PostgresqlArrayTest < ActiveRecord::TestCase
assert_cycle(:tags, [[['1'], ['2']], [['2'], ['3']]])
end
+ def test_with_empty_strings
+ assert_cycle(:tags, [ '1', '2', '', '4', '', '5' ])
+ end
+
+ def test_with_multi_dimensional_empty_strings
+ assert_cycle(:tags, [[['1', '2'], ['', '4'], ['', '5']]])
+ end
+
+ def test_with_arbitrary_whitespace
+ assert_cycle(:tags, [[['1', '2'], [' ', '4'], [' ', '5']]])
+ end
+
def test_multi_dimensional_with_integers
assert_cycle(:ratings, [[[1], [7]], [[8], [10]]])
end
diff --git a/activerecord/test/cases/adapters/postgresql/schema_test.rb b/activerecord/test/cases/adapters/postgresql/schema_test.rb
index 1e59bca6a7..3f7009c1d1 100644
--- a/activerecord/test/cases/adapters/postgresql/schema_test.rb
+++ b/activerecord/test/cases/adapters/postgresql/schema_test.rb
@@ -246,6 +246,18 @@ class SchemaTest < ActiveRecord::TestCase
assert_nothing_raised { with_schema_search_path nil }
end
+ def test_index_name_exists
+ with_schema_search_path(SCHEMA_NAME) do
+ assert @connection.index_name_exists?(TABLE_NAME, INDEX_A_NAME, true)
+ assert @connection.index_name_exists?(TABLE_NAME, INDEX_B_NAME, true)
+ assert @connection.index_name_exists?(TABLE_NAME, INDEX_C_NAME, true)
+ assert @connection.index_name_exists?(TABLE_NAME, INDEX_D_NAME, true)
+ assert @connection.index_name_exists?(TABLE_NAME, INDEX_E_NAME, true)
+ assert @connection.index_name_exists?(TABLE_NAME, INDEX_E_NAME, true)
+ assert_not @connection.index_name_exists?(TABLE_NAME, 'missing_index', true)
+ end
+ end
+
def test_dump_indexes_for_schema_one
do_dump_index_tests_for_schema(SCHEMA_NAME, INDEX_A_COLUMN, INDEX_B_COLUMN_S1, INDEX_D_COLUMN, INDEX_E_COLUMN)
end
diff --git a/activerecord/test/cases/scoping/named_scoping_test.rb b/activerecord/test/cases/scoping/named_scoping_test.rb
index 9dc26cfd4d..f0ad9ebb8a 100644
--- a/activerecord/test/cases/scoping/named_scoping_test.rb
+++ b/activerecord/test/cases/scoping/named_scoping_test.rb
@@ -314,7 +314,9 @@ class NamedScopingTest < ActiveRecord::TestCase
non_conflicts.each do |name|
assert_nothing_raised do
- klass.class_eval { scope name, ->{ where(approved: true) } }
+ silence_warnings do
+ klass.class_eval { scope name, ->{ where(approved: true) } }
+ end
end
assert_nothing_raised do
diff --git a/activesupport/CHANGELOG.md b/activesupport/CHANGELOG.md
index f15bd1b8d0..fa2f2384f9 100644
--- a/activesupport/CHANGELOG.md
+++ b/activesupport/CHANGELOG.md
@@ -6,6 +6,13 @@
*David Celis*
+* Fix parsing bugs in `XmlMini`
+
+ Symbols or boolean parsing would raise an error for non string values (e.g.
+ integers). Decimal parsing would fail due to a missing requirement.
+
+ *Birkir A. Barkarson*
+
* Maintain the current timezone when calling `wrap_with_time_zone`
Extend the solution from the fix for #12163 to the general case where `Time`
diff --git a/activesupport/lib/active_support/xml_mini.rb b/activesupport/lib/active_support/xml_mini.rb
index d082a0a499..009ee4db90 100644
--- a/activesupport/lib/active_support/xml_mini.rb
+++ b/activesupport/lib/active_support/xml_mini.rb
@@ -1,7 +1,9 @@
require 'time'
require 'base64'
+require 'bigdecimal'
require 'active_support/core_ext/module/delegation'
require 'active_support/core_ext/string/inflections'
+require 'active_support/core_ext/date_time/calculations'
module ActiveSupport
# = XmlMini
@@ -56,13 +58,13 @@ module ActiveSupport
# TODO use regexp instead of Date.parse
unless defined?(PARSING)
PARSING = {
- "symbol" => Proc.new { |symbol| symbol.to_sym },
+ "symbol" => Proc.new { |symbol| symbol.to_s.to_sym },
"date" => Proc.new { |date| ::Date.parse(date) },
"datetime" => Proc.new { |time| Time.xmlschema(time).utc rescue ::DateTime.parse(time).utc },
"integer" => Proc.new { |integer| integer.to_i },
"float" => Proc.new { |float| float.to_f },
"decimal" => Proc.new { |number| BigDecimal(number) },
- "boolean" => Proc.new { |boolean| %w(1 true).include?(boolean.strip) },
+ "boolean" => Proc.new { |boolean| %w(1 true).include?(boolean.to_s.strip) },
"string" => Proc.new { |string| string.to_s },
"yaml" => Proc.new { |yaml| YAML::load(yaml) rescue yaml },
"base64Binary" => Proc.new { |bin| ::Base64.decode64(bin) },
diff --git a/activesupport/test/xml_mini_test.rb b/activesupport/test/xml_mini_test.rb
index d992028323..753effb54e 100644
--- a/activesupport/test/xml_mini_test.rb
+++ b/activesupport/test/xml_mini_test.rb
@@ -169,4 +169,128 @@ module XmlMiniTest
end
end
end
+
+ class ParsingTest < ActiveSupport::TestCase
+ def setup
+ @parsing = ActiveSupport::XmlMini::PARSING
+ end
+
+ def test_symbol
+ parser = @parsing['symbol']
+ assert_equal :symbol, parser.call('symbol')
+ assert_equal :symbol, parser.call(:symbol)
+ assert_equal :'123', parser.call(123)
+ assert_raises(ArgumentError) { parser.call(Date.new(2013,11,12,02,11)) }
+ end
+
+ def test_date
+ parser = @parsing['date']
+ assert_equal Date.new(2013,11,12), parser.call("2013-11-12T0211Z")
+ assert_raises(TypeError) { parser.call(1384190018) }
+ assert_raises(ArgumentError) { parser.call("not really a date") }
+ end
+
+ def test_datetime
+ parser = @parsing['datetime']
+ assert_equal Time.new(2013,11,12,02,11,00,0), parser.call("2013-11-12T02:11:00Z")
+ assert_equal DateTime.new(2013,11,12), parser.call("2013-11-12T0211Z")
+ assert_equal DateTime.new(2013,11,12,02,11), parser.call("2013-11-12T02:11Z")
+ assert_equal DateTime.new(2013,11,12,02,11), parser.call("2013-11-12T11:11+9")
+ assert_raises(ArgumentError) { parser.call("1384190018") }
+ end
+
+ def test_integer
+ parser = @parsing['integer']
+ assert_equal 123, parser.call(123)
+ assert_equal 123, parser.call(123.003)
+ assert_equal 123, parser.call("123")
+ assert_equal 0, parser.call("")
+ assert_raises(ArgumentError) { parser.call(Date.new(2013,11,12,02,11)) }
+ end
+
+ def test_float
+ parser = @parsing['float']
+ assert_equal 123, parser.call("123")
+ assert_equal 123.003, parser.call("123.003")
+ assert_equal 123.0, parser.call("123,003")
+ assert_equal 0.0, parser.call("")
+ assert_equal 123, parser.call(123)
+ assert_equal 123.05, parser.call(123.05)
+ assert_raises(ArgumentError) { parser.call(Date.new(2013,11,12,02,11)) }
+ end
+
+ def test_decimal
+ parser = @parsing['decimal']
+ assert_equal 123, parser.call("123")
+ assert_equal 123.003, parser.call("123.003")
+ assert_equal 123.0, parser.call("123,003")
+ assert_equal 0.0, parser.call("")
+ assert_equal 123, parser.call(123)
+ assert_raises(ArgumentError) { parser.call(123.04) }
+ assert_raises(ArgumentError) { parser.call(Date.new(2013,11,12,02,11)) }
+ end
+
+ def test_boolean
+ parser = @parsing['boolean']
+ [1, true, "1"].each do |value|
+ assert parser.call(value)
+ end
+
+ [0, false, "0"].each do |value|
+ assert_not parser.call(value)
+ end
+ end
+
+ def test_string
+ parser = @parsing['string']
+ assert_equal "123", parser.call(123)
+ assert_equal "123", parser.call("123")
+ assert_equal "[]", parser.call("[]")
+ assert_equal "[]", parser.call([])
+ assert_equal "{}", parser.call({})
+ assert_raises(ArgumentError) { parser.call(Date.new(2013,11,12,02,11)) }
+ end
+
+ def test_yaml
+ yaml = <<YAML
+product:
+ - sku : BL394D
+ quantity : 4
+ description : Basketball
+YAML
+ expected = {
+ "product"=> [
+ {"sku"=>"BL394D", "quantity"=>4, "description"=>"Basketball"}
+ ]
+ }
+ parser = @parsing['yaml']
+ assert_equal(expected, parser.call(yaml))
+ assert_equal({1 => 'test'}, parser.call({1 => 'test'}))
+ assert_equal({"1 => 'test'"=>nil}, parser.call("{1 => 'test'}"))
+ end
+
+ def test_base64Binary_and_binary
+ base64 = <<BASE64
+TWFuIGlzIGRpc3Rpbmd1aXNoZWQsIG5vdCBvbmx5IGJ5IGhpcyByZWFzb24sIGJ1dCBieSB0aGlz
+IHNpbmd1bGFyIHBhc3Npb24gZnJvbSBvdGhlciBhbmltYWxzLCB3aGljaCBpcyBhIGx1c3Qgb2Yg
+dGhlIG1pbmQsIHRoYXQgYnkgYSBwZXJzZXZlcmFuY2Ugb2YgZGVsaWdodCBpbiB0aGUgY29udGlu
+dWVkIGFuZCBpbmRlZmF0aWdhYmxlIGdlbmVyYXRpb24gb2Yga25vd2xlZGdlLCBleGNlZWRzIHRo
+ZSBzaG9ydCB2ZWhlbWVuY2Ugb2YgYW55IGNhcm5hbCBwbGVhc3VyZS4=
+BASE64
+ expected_base64 = <<EXPECTED
+Man is distinguished, not only by his reason, but by this singular passion from
+other animals, which is a lust of the mind, that by a perseverance of delight
+in the continued and indefatigable generation of knowledge, exceeds the short
+vehemence of any carnal pleasure.
+EXPECTED
+
+ parser = @parsing['base64Binary']
+ assert_equal expected_base64.gsub(/\n/," ").strip, parser.call(base64)
+ parser.call("NON BASE64 INPUT")
+
+ parser = @parsing['binary']
+ assert_equal expected_base64.gsub(/\n/," ").strip, parser.call(base64, 'encoding' => 'base64')
+ assert_equal "IGNORED INPUT", parser.call("IGNORED INPUT", {})
+ end
+ end
end