diff options
Diffstat (limited to 'activerecord')
9 files changed, 83 insertions, 30 deletions
diff --git a/activerecord/CHANGELOG.md b/activerecord/CHANGELOG.md index 4637847987..c91e6662c9 100644 --- a/activerecord/CHANGELOG.md +++ b/activerecord/CHANGELOG.md @@ -1,3 +1,19 @@ +* Using `group` with an attribute that has a custom type will properly cast + the hash keys after calling a calculation method like `count`. Fixes #25595. + + *Sean Griffin* + +* Fix the generated `#to_param` method to use `omission: ''` so that + the resulting output is actually up to 20 characters, not + effectively 17 to leave room for the default "...". + Also call `#parameterize` before `#truncate` and make the + `separator: /-/` to maximize the information included in the + output. + + Fixes #23635. + + *Rob Biedenharn* + * Ensure concurrent invocations of the connection reaper cannot allocate the same connection to two threads. @@ -12,7 +28,7 @@ *Kevin McPhillips* * Removed the unused methods `ActiveRecord::Base.connection_id` and - `ActiveRecord::Base.connection_id=` + `ActiveRecord::Base.connection_id=`. *Sean Griffin* @@ -31,15 +47,15 @@ *Johannes Opper* -* Introduce ActiveRecord::TransactionSerializationError for catching +* Introduce `ActiveRecord::TransactionSerializationError` for catching transaction serialization failures or deadlocks. *Erol Fornoles* -* PostgreSQL: Fix db:structure:load silent failure on SQL error +* PostgreSQL: Fix db:structure:load silent failure on SQL error. - The command line flag "-v ON_ERROR_STOP=1" should be used - when invoking psql to make sure errors are not suppressed. + The command line flag `-v ON_ERROR_STOP=1` should be used + when invoking `psql` to make sure errors are not suppressed. Example: 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 eec0bc8518..396cb0b07a 100644 --- a/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb +++ b/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb @@ -179,7 +179,7 @@ module ActiveRecord # A Symbol can be used to specify the type of the generated primary key column. # [<tt>:primary_key</tt>] # The name of the primary key, if one is to be added automatically. - # Defaults to +id+. If <tt>:id</tt> is false this option is ignored. + # Defaults to +id+. If <tt>:id</tt> is false, then this option is ignored. # # Note that Active Record models will automatically detect their # primary key. This can be avoided by using @@ -305,9 +305,9 @@ module ActiveRecord # # Creates a table called 'assemblies_parts' with no id. # create_join_table(:assemblies, :parts) # - # You can pass a +options+ hash can include the following keys: + # You can pass an +options+ hash which can include the following keys: # [<tt>:table_name</tt>] - # Sets the table name overriding the default + # Sets the table name, overriding the default. # [<tt>:column_options</tt>] # Any extra options you want appended to the columns definition. # [<tt>:options</tt>] @@ -433,7 +433,7 @@ module ActiveRecord # t.remove_index :company_id # end # - # See also Table for details on all of the various column transformation. + # See also Table for details on all of the various column transformations. def change_table(table_name, options = {}) if supports_bulk_alter? && options[:bulk] recorder = ActiveRecord::Migration::CommandRecorder.new(self) @@ -483,10 +483,10 @@ module ActiveRecord # # Available options are (none of these exists by default): # * <tt>:limit</tt> - - # Requests a maximum column length. This is number of characters for a <tt>:string</tt> column + # Requests a maximum column length. This is the number of characters for a <tt>:string</tt> column # and number of bytes for <tt>:text</tt>, <tt>:binary</tt> and <tt>:integer</tt> columns. # * <tt>:default</tt> - - # The column's default value. Use nil for NULL. + # The column's default value. Use +nil+ for +NULL+. # * <tt>:null</tt> - # Allows or disallows +NULL+ values in the column. This option could # have been named <tt>:null_allowed</tt>. @@ -495,7 +495,7 @@ module ActiveRecord # * <tt>:scale</tt> - # Specifies the scale for the <tt>:decimal</tt> and <tt>:numeric</tt> columns. # - # Note: The precision is the total number of significant digits + # Note: The precision is the total number of significant digits, # and the scale is the number of digits that can be stored following # the decimal point. For example, the number 123.45 has a precision of 5 # and a scale of 2. A decimal with a precision of 5 and a scale of 2 can @@ -564,7 +564,7 @@ module ActiveRecord # # The +type+ and +options+ parameters will be ignored if present. It can be helpful # to provide these in a migration's +change+ method so it can be reverted. - # In that case, +type+ and +options+ will be used by add_column. + # In that case, +type+ and +options+ will be used by #add_column. def remove_column(table_name, column_name, type = nil, options = {}) execute "ALTER TABLE #{quote_table_name(table_name)} DROP #{quote_column_name(column_name)}" end @@ -952,13 +952,13 @@ module ActiveRecord # Checks to see if a foreign key exists on a table for a given foreign key definition. # - # # Check a foreign key exists + # # Checks to see if a foreign key exists. # foreign_key_exists?(:accounts, :branches) # - # # Check a foreign key on a specified column exists + # # Checks to see if a foreign key on a specified column exists. # foreign_key_exists?(:accounts, column: :owner_id) # - # # Check a foreign key with a custom name exists + # # Checks to see if a foreign key with a custom name exists. # foreign_key_exists?(:accounts, name: "special_fk_name") # def foreign_key_exists?(from_table, options_or_to_table = {}) @@ -1081,7 +1081,7 @@ module ActiveRecord end # Given a set of columns and an ORDER BY clause, returns the columns for a SELECT DISTINCT. - # PostgreSQL, MySQL, and Oracle overrides this for custom DISTINCT syntax - they + # PostgreSQL, MySQL, and Oracle override this for custom DISTINCT syntax - they # require the order columns appear in the SELECT. # # columns_for_distinct("posts.id", ["posts.created_at desc"]) diff --git a/activerecord/lib/active_record/connection_adapters/sqlite3_adapter.rb b/activerecord/lib/active_record/connection_adapters/sqlite3_adapter.rb index eb2268157b..3384012c49 100644 --- a/activerecord/lib/active_record/connection_adapters/sqlite3_adapter.rb +++ b/activerecord/lib/active_record/connection_adapters/sqlite3_adapter.rb @@ -159,7 +159,7 @@ module ActiveRecord end # Returns 62. SQLite supports index names up to 64 - # characters. The rest is used by rails internally to perform + # characters. The rest is used by Rails internally to perform # temporary rename operations def allowed_index_name_length index_name_length - 2 diff --git a/activerecord/lib/active_record/integration.rb b/activerecord/lib/active_record/integration.rb index 466c8509a4..d729deb07b 100644 --- a/activerecord/lib/active_record/integration.rb +++ b/activerecord/lib/active_record/integration.rb @@ -86,7 +86,7 @@ module ActiveRecord # # user = User.find_by(name: 'David Heinemeier Hansson') # user.id # => 125 - # user_path(user) # => "/users/125-david" + # user_path(user) # => "/users/125-david-heinemeier" # # Because the generated param begins with the record's +id+, it is # suitable for passing to +find+. In a controller, for example: @@ -100,7 +100,7 @@ module ActiveRecord define_method :to_param do if (default = super()) && (result = send(method_name).to_s).present? && - (param = result.squish.truncate(20, separator: /\s/, omission: nil).parameterize).present? + (param = result.squish.parameterize.truncate(20, separator: /-/, omission: '')).present? "#{default}-#{param}" else default diff --git a/activerecord/lib/active_record/model_schema.rb b/activerecord/lib/active_record/model_schema.rb index 7996c32bbc..114686c5d3 100644 --- a/activerecord/lib/active_record/model_schema.rb +++ b/activerecord/lib/active_record/model_schema.rb @@ -282,8 +282,12 @@ module ActiveRecord # # +attr_name+ The name of the attribute to retrieve the type for. Must be # a string - def type_for_attribute(attr_name) - attribute_types[attr_name] + def type_for_attribute(attr_name, &block) + if block + attribute_types.fetch(attr_name, &block) + else + attribute_types[attr_name] + end end # Returns a hash where the keys are column names and the values are diff --git a/activerecord/lib/active_record/railtie.rb b/activerecord/lib/active_record/railtie.rb index 98ea425d16..2c0ca62924 100644 --- a/activerecord/lib/active_record/railtie.rb +++ b/activerecord/lib/active_record/railtie.rb @@ -3,7 +3,7 @@ require "rails" require "active_model/railtie" # For now, action_controller must always be present with -# rails, so let's make sure that it gets required before +# Rails, so let's make sure that it gets required before # here. This is needed for correctly setting up the middleware. # In the future, this might become an optional require. require "action_controller/railtie" diff --git a/activerecord/lib/active_record/relation/calculations.rb b/activerecord/lib/active_record/relation/calculations.rb index d6d92b8607..a97b71815a 100644 --- a/activerecord/lib/active_record/relation/calculations.rb +++ b/activerecord/lib/active_record/relation/calculations.rb @@ -312,8 +312,10 @@ module ActiveRecord Hash[calculated_data.map do |row| key = group_columns.map { |aliaz, col_name| - column = calculated_data.column_types.fetch(aliaz) do - type_for(col_name) + column = type_for(col_name) do + calculated_data.column_types.fetch(aliaz) do + Type::Value.new + end end type_cast_calculated_value(row[aliaz], column) } @@ -346,9 +348,9 @@ module ActiveRecord @klass.connection.table_alias_for(table_name) end - def type_for(field) + def type_for(field, &block) field_name = field.respond_to?(:name) ? field.name.to_s : field.to_s.split('.').last - @klass.type_for_attribute(field_name) + @klass.type_for_attribute(field_name, &block) end def type_cast_calculated_value(value, type, operation = nil) diff --git a/activerecord/test/cases/calculations_test.rb b/activerecord/test/cases/calculations_test.rb index cfae700159..6acfec0621 100644 --- a/activerecord/test/cases/calculations_test.rb +++ b/activerecord/test/cases/calculations_test.rb @@ -1,4 +1,5 @@ require "cases/helper" +require "models/book" require 'models/club' require 'models/company' require "models/contract" @@ -25,7 +26,7 @@ class NumericData < ActiveRecord::Base end class CalculationsTest < ActiveRecord::TestCase - fixtures :companies, :accounts, :topics, :speedometers, :minivans + fixtures :companies, :accounts, :topics, :speedometers, :minivans, :books def test_should_sum_field assert_equal 318, Account.sum(:credit_limit) @@ -793,4 +794,8 @@ class CalculationsTest < ActiveRecord::TestCase assert_equal 50, result[1].credit_limit assert_equal 50, result[2].credit_limit end + + def test_group_by_attribute_with_custom_type + assert_equal({ "proposed" => 2, "published" => 2 }, Book.group(:status).count) + end end diff --git a/activerecord/test/cases/integration_test.rb b/activerecord/test/cases/integration_test.rb index 08a186ae07..97a0cdb5db 100644 --- a/activerecord/test/cases/integration_test.rb +++ b/activerecord/test/cases/integration_test.rb @@ -29,10 +29,30 @@ class IntegrationTest < ActiveRecord::TestCase assert_equal '4-flamboyant-software', firm.to_param end + def test_to_param_class_method_truncates_words_properly + firm = Firm.find(4) + firm.name << ', Inc.' + assert_equal '4-flamboyant-software', firm.to_param + end + + def test_to_param_class_method_truncates_after_parameterize + firm = Firm.find(4) + firm.name = "Huey, Dewey, & Louie LLC" + # 123456789T123456789v + assert_equal '4-huey-dewey-louie-llc', firm.to_param + end + + def test_to_param_class_method_truncates_after_parameterize_with_hyphens + firm = Firm.find(4) + firm.name = "Door-to-Door Wash-n-Fold Service" + # 123456789T123456789v + assert_equal '4-door-to-door-wash-n', firm.to_param + end + def test_to_param_class_method_truncates firm = Firm.find(4) firm.name = 'a ' * 100 - assert_equal '4-a-a-a-a-a-a-a-a-a', firm.to_param + assert_equal '4-a-a-a-a-a-a-a-a-a-a', firm.to_param end def test_to_param_class_method_truncates_edge_case @@ -41,10 +61,16 @@ class IntegrationTest < ActiveRecord::TestCase assert_equal '4-david', firm.to_param end + def test_to_param_class_method_truncates_case_shown_in_doc + firm = Firm.find(4) + firm.name = 'David Heinemeier Hansson' + assert_equal '4-david-heinemeier', firm.to_param + end + def test_to_param_class_method_squishes firm = Firm.find(4) firm.name = "ab \n" * 100 - assert_equal '4-ab-ab-ab-ab-ab-ab', firm.to_param + assert_equal '4-ab-ab-ab-ab-ab-ab-ab', firm.to_param end def test_to_param_class_method_multibyte_character |