diff --git a/activerecord/CHANGELOG.md b/activerecord/CHANGELOG.md index e4a6203b5304e283ef610ce56e2bc6ba1c404d3b..dcf79c7eb72934ad087f2beae3e04b1fd72a7bae 100644 --- a/activerecord/CHANGELOG.md +++ b/activerecord/CHANGELOG.md @@ -1,3 +1,15 @@ +* Allow default to be configured for Enum. + + ```ruby + class Book < ActiveRecord::Base + enum status: [:proposed, :written, :published], _default: :published + end + + Book.new.status # => "published" + ``` + + *Ryuta Kamizono* + * Support `where` with comparison operators (`>`, `>=`, `<`, and `<=`). ```ruby diff --git a/activerecord/lib/active_record/attributes.rb b/activerecord/lib/active_record/attributes.rb index dc3cfed88f1b67257a03d59f9ee9c761f00a7991..d0d3d38a789c28e9dd177405106b03e1b2039b77 100644 --- a/activerecord/lib/active_record/attributes.rb +++ b/activerecord/lib/active_record/attributes.rb @@ -205,13 +205,13 @@ module ClassMethods # tracking is performed. The methods +changed?+ and +changed_in_place?+ # will be called from ActiveModel::Dirty. See the documentation for those # methods in ActiveModel::Type::Value for more details. - def attribute(name, cast_type = Type::Value.new, **options) + def attribute(name, cast_type = nil, **options, &block) name = name.to_s reload_schema_from_cache self.attributes_to_define_after_schema_loads = attributes_to_define_after_schema_loads.merge( - name => [cast_type, options] + name => [cast_type || block, options] ) end @@ -246,9 +246,14 @@ def define_attribute( def load_schema! # :nodoc: super attributes_to_define_after_schema_loads.each do |name, (type, options)| - if type.is_a?(Symbol) + case type + when Symbol adapter_name = ActiveRecord::Type.adapter_name_from(self) type = ActiveRecord::Type.lookup(type, **options.except(:default), adapter: adapter_name) + when Proc + type = type[type_for_attribute(name)] + else + type ||= Type::Value.new end define_attribute(name, type, **options.slice(:default)) diff --git a/activerecord/lib/active_record/enum.rb b/activerecord/lib/active_record/enum.rb index 54463c9e8832fb22d679efc1c2dc64adcdf3f1b2..3d4fda4861a0ba0e7fe3747951847d5b9e2dbbba 100644 --- a/activerecord/lib/active_record/enum.rb +++ b/activerecord/lib/active_record/enum.rb @@ -159,9 +159,14 @@ def assert_valid_value(value) def enum(definitions) klass = self + enum_prefix = definitions.delete(:_prefix) enum_suffix = definitions.delete(:_suffix) enum_scopes = definitions.delete(:_scopes) + + default = {} + default[:default] = definitions.delete(:_default) if definitions.key?(:_default) + definitions.each do |name, values| assert_valid_enum_definition_values(values) # statuses = { } @@ -177,7 +182,7 @@ def enum(definitions) detect_enum_conflict!(name, "#{name}=") attr = attribute_alias?(name) ? attribute_alias(name) : name - decorate_attribute_type(attr, :enum) do |subtype| + attribute(attr, **default) do |subtype| EnumType.new(attr, enum_values, subtype) end diff --git a/activerecord/test/cases/enum_test.rb b/activerecord/test/cases/enum_test.rb index cb36c64e81a59b643f05687e585903b862335b56..27ec1a5a6259bd95cf9a5da18982d74faaf42968 100644 --- a/activerecord/test/cases/enum_test.rb +++ b/activerecord/test/cases/enum_test.rb @@ -583,6 +583,15 @@ def self.name; "Book"; end assert_equal :integer, Book.type_for_attribute("status").type end + test "overloaded default" do + klass = Class.new(ActiveRecord::Base) do + self.table_name = "books" + enum status: [:proposed, :written, :published], _default: :published + end + + assert_equal "published", klass.new.status + end + test "scopes can be disabled" do klass = Class.new(ActiveRecord::Base) do self.table_name = "books"