diff options
-rw-r--r-- | actionpack/CHANGELOG.md | 9 | ||||
-rw-r--r-- | actionpack/lib/abstract_controller/rendering.rb | 2 | ||||
-rw-r--r-- | actionpack/lib/action_controller/metal/rendering.rb | 3 | ||||
-rw-r--r-- | actionpack/lib/action_dispatch/http/mime_negotiation.rb | 2 | ||||
-rw-r--r-- | actionpack/lib/action_dispatch/http/mime_type.rb | 2 | ||||
-rw-r--r-- | actionpack/test/controller/send_file_test.rb | 2 | ||||
-rw-r--r-- | activemodel/test/cases/helper.rb | 3 | ||||
-rw-r--r-- | activerecord/CHANGELOG.md | 25 | ||||
-rw-r--r-- | activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb | 10 | ||||
-rw-r--r-- | activerecord/lib/active_record/sanitization.rb | 9 | ||||
-rw-r--r-- | activerecord/test/cases/adapters/postgresql/array_test.rb | 18 | ||||
-rw-r--r-- | activerecord/test/cases/adapters/postgresql/hstore_test.rb | 10 | ||||
-rw-r--r-- | activerecord/test/cases/adapters/postgresql/json_test.rb | 10 | ||||
-rw-r--r-- | guides/source/getting_started.md | 6 |
14 files changed, 92 insertions, 19 deletions
diff --git a/actionpack/CHANGELOG.md b/actionpack/CHANGELOG.md index d696656521..30b643c791 100644 --- a/actionpack/CHANGELOG.md +++ b/actionpack/CHANGELOG.md @@ -82,14 +82,15 @@ *Łukasz Strzałkowski* -* Fix header `Content-Type: #<Mime::NullType:...>` in localized template. +* Fix render of localized templates without an explicit format using wrong + content header and not passing correct formats to template due to the + introduction of the `NullType` for mimes. - When localized template has no format in the template name, - the response now has the default and correct `content-type`. + Templates like `hello.it.erb` were subject to this issue. Fixes #13064. - *Angelo Capilleri* + *Angelo Capilleri*, *Carlos Antonio da Silva* * Try to escape each part of a url correctly when using a redirect route. diff --git a/actionpack/lib/abstract_controller/rendering.rb b/actionpack/lib/abstract_controller/rendering.rb index 6f17e3fcd9..7be61d94c9 100644 --- a/actionpack/lib/abstract_controller/rendering.rb +++ b/actionpack/lib/abstract_controller/rendering.rb @@ -22,7 +22,7 @@ module AbstractController def render(*args, &block) options = _normalize_render(*args, &block) self.response_body = render_to_body(options) - _process_format(rendered_format) + _process_format(rendered_format) if rendered_format self.response_body end diff --git a/actionpack/lib/action_controller/metal/rendering.rb b/actionpack/lib/action_controller/metal/rendering.rb index 66d34f3b67..5c48b4ab98 100644 --- a/actionpack/lib/action_controller/metal/rendering.rb +++ b/actionpack/lib/action_controller/metal/rendering.rb @@ -34,8 +34,7 @@ module ActionController def _process_format(format) super - # format is a Mime::NullType instance here then this condition can't be changed to `if format` - self.content_type ||= format.to_s unless format.nil? + self.content_type ||= format.to_s end # Normalize arguments by catching blocks and setting them on :update. diff --git a/actionpack/lib/action_dispatch/http/mime_negotiation.rb b/actionpack/lib/action_dispatch/http/mime_negotiation.rb index 346598b6de..c33ba201e1 100644 --- a/actionpack/lib/action_dispatch/http/mime_negotiation.rb +++ b/actionpack/lib/action_dispatch/http/mime_negotiation.rb @@ -50,7 +50,7 @@ module ActionDispatch # GET /posts/5 | request.format => Mime::HTML or MIME::JS, or request.accepts.first # def format(view_path = []) - formats.first + formats.first || Mime::NullType.instance end def formats diff --git a/actionpack/lib/action_dispatch/http/mime_type.rb b/actionpack/lib/action_dispatch/http/mime_type.rb index 2a8ff0a5d2..3d2dd2d632 100644 --- a/actionpack/lib/action_dispatch/http/mime_type.rb +++ b/actionpack/lib/action_dispatch/http/mime_type.rb @@ -28,7 +28,7 @@ module Mime class << self def [](type) return type if type.is_a?(Type) - Type.lookup_by_extension(type) || NullType.instance + Type.lookup_by_extension(type) end def fetch(type) diff --git a/actionpack/test/controller/send_file_test.rb b/actionpack/test/controller/send_file_test.rb index 8ecc1c7d73..a4c84c29d7 100644 --- a/actionpack/test/controller/send_file_test.rb +++ b/actionpack/test/controller/send_file_test.rb @@ -144,7 +144,7 @@ class SendFileTest < ActionController::TestCase } @controller.headers = {} - assert !@controller.send(:send_file_headers!, options) + assert_raise(ArgumentError) { @controller.send(:send_file_headers!, options) } end def test_send_file_headers_guess_type_from_extension diff --git a/activemodel/test/cases/helper.rb b/activemodel/test/cases/helper.rb index 7a63674757..522a7cebb4 100644 --- a/activemodel/test/cases/helper.rb +++ b/activemodel/test/cases/helper.rb @@ -7,4 +7,7 @@ require 'active_support/core_ext/string/access' # Show backtraces for deprecated behavior for quicker cleanup. ActiveSupport::Deprecation.debug = true +# Disable available locale checks to avoid warnings running the test suite. +I18n.enforce_available_locales = false + require 'active_support/testing/autorun' diff --git a/activerecord/CHANGELOG.md b/activerecord/CHANGELOG.md index 6d130ab4d6..f08f3024b9 100644 --- a/activerecord/CHANGELOG.md +++ b/activerecord/CHANGELOG.md @@ -1,3 +1,28 @@ +* Fixed `update_column`, `update_columns`, and `update_all` to correctly serialize + values for `array`, `hstore` and `json` column types in PostgreSQL. + + Fixes #12261. + + *Tadas Tamosauskas*, *Carlos Antonio da Silva* + +* Do not consider PostgreSQL array columns as number or text columns. + + The code uses these checks in several places to know what to do with a + particular column, for instance AR attribute query methods has a branch + like this: + + if column.number? + !value.zero? + end + + This should never be true for array columns, since it would be the same + as running [].zero?, which results in a NoMethodError exception. + + Fixing this by ensuring that array columns in PostgreSQL never return + true for number?/text? checks. + + *Carlos Antonio da Silva* + * When connecting to a non-existant postgresql database, the error: `ActiveRecord::NoDatabaseError` will now be raised. When being used with Rails the error message will include information on how to create a database: diff --git a/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb b/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb index 95cd69d5ad..11a5eba464 100644 --- a/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb +++ b/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb @@ -46,7 +46,7 @@ module ActiveRecord # PostgreSQL-specific extensions to column definitions in a table. class PostgreSQLColumn < Column #:nodoc: attr_accessor :array - # Instantiates a new PostgreSQL column definition in a table. + def initialize(name, default, oid_type, sql_type = nil, null = true) @oid_type = oid_type default_value = self.class.extract_value_from_default(default) @@ -62,6 +62,14 @@ module ActiveRecord @default_function = default if has_default_function?(default_value, default) end + def number? + !array && super + end + + def text? + !array && super + end + # :stopdoc: class << self include ConnectionAdapters::PostgreSQLColumn::Cast diff --git a/activerecord/lib/active_record/sanitization.rb b/activerecord/lib/active_record/sanitization.rb index cab8fd745a..dacaec26b7 100644 --- a/activerecord/lib/active_record/sanitization.rb +++ b/activerecord/lib/active_record/sanitization.rb @@ -100,8 +100,9 @@ module ActiveRecord # { status: nil, group_id: 1 } # # => "status = NULL , group_id = 1" def sanitize_sql_hash_for_assignment(attrs, table) + c = connection attrs.map do |attr, value| - "#{connection.quote_table_name_for_assignment(table, attr)} = #{quote_bound_value(value)}" + "#{c.quote_table_name_for_assignment(table, attr)} = #{quote_bound_value(value, c, columns_hash[attr.to_s])}" end.join(', ') end @@ -152,8 +153,10 @@ module ActiveRecord end end - def quote_bound_value(value, c = connection) #:nodoc: - if value.respond_to?(:map) && !value.acts_like?(:string) + def quote_bound_value(value, c = connection, column = nil) #:nodoc: + if column + c.quote(value, column) + elsif value.respond_to?(:map) && !value.acts_like?(:string) if value.respond_to?(:empty?) && value.empty? c.quote(nil) else diff --git a/activerecord/test/cases/adapters/postgresql/array_test.rb b/activerecord/test/cases/adapters/postgresql/array_test.rb index 114d5b6cc6..d71e2aa2bb 100644 --- a/activerecord/test/cases/adapters/postgresql/array_test.rb +++ b/activerecord/test/cases/adapters/postgresql/array_test.rb @@ -26,6 +26,12 @@ class PostgresqlArrayTest < ActiveRecord::TestCase def test_column assert_equal :string, @column.type assert @column.array + assert_not @column.text? + + ratings_column = PgArray.columns_hash['ratings'] + assert_equal :integer, ratings_column.type + assert ratings_column.array + assert_not ratings_column.number? end def test_change_column_with_array @@ -50,8 +56,6 @@ class PostgresqlArrayTest < ActiveRecord::TestCase end def test_type_cast_array - assert @column - data = '{1,2,3}' oid_type = @column.instance_variable_get('@oid_type').subtype # we are getting the instance variable in this test, but in the @@ -124,6 +128,16 @@ class PostgresqlArrayTest < ActiveRecord::TestCase assert_equal("[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, ...]", record.attribute_for_inspect(:ratings)) end + def test_update_all + pg_array = PgArray.create! tags: ["one", "two", "three"] + + PgArray.update_all tags: ["four", "five"] + assert_equal ["four", "five"], pg_array.reload.tags + + PgArray.update_all tags: [] + assert_equal [], pg_array.reload.tags + end + private def assert_cycle field, array # test creation diff --git a/activerecord/test/cases/adapters/postgresql/hstore_test.rb b/activerecord/test/cases/adapters/postgresql/hstore_test.rb index 2845413575..6df5d8f533 100644 --- a/activerecord/test/cases/adapters/postgresql/hstore_test.rb +++ b/activerecord/test/cases/adapters/postgresql/hstore_test.rb @@ -206,6 +206,16 @@ class PostgresqlHstoreTest < ActiveRecord::TestCase def test_multiline assert_cycle("a\nb" => "c\nd") end + + def test_update_all + hstore = Hstore.create! tags: { "one" => "two" } + + Hstore.update_all tags: { "three" => "four" } + assert_equal({ "three" => "four" }, hstore.reload.tags) + + Hstore.update_all tags: { } + assert_equal({ }, hstore.reload.tags) + end end private diff --git a/activerecord/test/cases/adapters/postgresql/json_test.rb b/activerecord/test/cases/adapters/postgresql/json_test.rb index c33c7ef968..01e7334aad 100644 --- a/activerecord/test/cases/adapters/postgresql/json_test.rb +++ b/activerecord/test/cases/adapters/postgresql/json_test.rb @@ -121,4 +121,14 @@ class PostgresqlJSONTest < ActiveRecord::TestCase x = JsonDataType.first assert_equal "640×1136", x.resolution end + + def test_update_all + json = JsonDataType.create! payload: { "one" => "two" } + + JsonDataType.update_all payload: { "three" => "four" } + assert_equal({ "three" => "four" }, json.reload.payload) + + JsonDataType.update_all payload: { } + assert_equal({ }, json.reload.payload) + end end diff --git a/guides/source/getting_started.md b/guides/source/getting_started.md index 279a977f6f..5627af11fa 100644 --- a/guides/source/getting_started.md +++ b/guides/source/getting_started.md @@ -1703,8 +1703,8 @@ Deleting Comments ----------------- Another important feature of a blog is being able to delete spam comments. To do -this, we need to implement a link of some sort in the view and a `DELETE` action -in the `CommentsController`. +this, we need to implement a link of some sort in the view and a `destroy` +action in the `CommentsController`. So first, let's add the delete link in the `app/views/comments/_comment.html.erb` partial: @@ -1729,7 +1729,7 @@ So first, let's add the delete link in the Clicking this new "Destroy Comment" link will fire off a `DELETE /posts/:post_id/comments/:id` to our `CommentsController`, which can then use -this to find the comment we want to delete, so let's add a destroy action to our +this to find the comment we want to delete, so let's add a `destroy` action to our controller (`app/controllers/comments_controller.rb`): ```ruby |