diff options
-rw-r--r-- | actionpack/CHANGELOG.md | 5 | ||||
-rw-r--r-- | actionpack/lib/action_controller/metal/params_wrapper.rb | 15 | ||||
-rw-r--r-- | actionpack/test/controller/params_wrapper_test.rb | 20 | ||||
-rw-r--r-- | activerecord/CHANGELOG.md | 21 | ||||
-rw-r--r-- | activerecord/lib/active_record/connection_adapters/postgresql/array_parser.rb | 5 | ||||
-rw-r--r-- | activerecord/lib/active_record/connection_adapters/postgresql/schema_statements.rb | 13 | ||||
-rw-r--r-- | activerecord/lib/active_record/core.rb | 4 | ||||
-rw-r--r-- | activerecord/lib/active_record/relation/query_methods.rb | 7 | ||||
-rw-r--r-- | activerecord/test/cases/adapters/postgresql/array_test.rb | 12 | ||||
-rw-r--r-- | activerecord/test/cases/adapters/postgresql/schema_test.rb | 12 | ||||
-rw-r--r-- | activerecord/test/cases/scoping/named_scoping_test.rb | 4 | ||||
-rw-r--r-- | activesupport/CHANGELOG.md | 7 | ||||
-rw-r--r-- | activesupport/lib/active_support/xml_mini.rb | 6 | ||||
-rw-r--r-- | activesupport/test/xml_mini_test.rb | 124 |
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 |