@@ -118,6 +118,50 @@ module ActiveRecord
118
118
# class Conversation < ActiveRecord::Base
119
119
# enum :status, [ :active, :archived ], instance_methods: false
120
120
# end
121
+ #
122
+ # If you want the enum value to be validated before saving, use the option +:validate+:
123
+ #
124
+ # class Conversation < ActiveRecord::Base
125
+ # enum :status, [ :active, :archived ], validate: true
126
+ # end
127
+ #
128
+ # conversation = Conversation.new
129
+ #
130
+ # conversation.status = :unknown
131
+ # conversation.valid? # => false
132
+ #
133
+ # conversation.status = nil
134
+ # conversation.valid? # => false
135
+ #
136
+ # conversation.status = :active
137
+ # conversation.valid? # => true
138
+ #
139
+ # It is also possible to pass additional validation options:
140
+ #
141
+ # class Conversation < ActiveRecord::Base
142
+ # enum :status, [ :active, :archived ], validate: { allow_nil: true }
143
+ # end
144
+ #
145
+ # conversation = Conversation.new
146
+ #
147
+ # conversation.status = :unknown
148
+ # conversation.valid? # => false
149
+ #
150
+ # conversation.status = nil
151
+ # conversation.valid? # => true
152
+ #
153
+ # conversation.status = :active
154
+ # conversation.valid? # => true
155
+ #
156
+ # Otherwise +ArgumentError+ will raise:
157
+ #
158
+ # class Conversation < ActiveRecord::Base
159
+ # enum :status, [ :active, :archived ]
160
+ # end
161
+ #
162
+ # conversation = Conversation.new
163
+ #
164
+ # conversation.status = :unknown # 'unknown' is not a valid status (ArgumentError)
121
165
module Enum
122
166
def self . extended ( base ) # :nodoc:
123
167
base . class_attribute ( :defined_enums , instance_writer : false , default : { } )
@@ -135,10 +179,11 @@ def load_schema! # :nodoc:
135
179
class EnumType < Type ::Value # :nodoc:
136
180
delegate :type , to : :subtype
137
181
138
- def initialize ( name , mapping , subtype )
182
+ def initialize ( name , mapping , subtype , raise_on_invalid_values : true )
139
183
@name = name
140
184
@mapping = mapping
141
185
@subtype = subtype
186
+ @_raise_on_invalid_values = raise_on_invalid_values
142
187
end
143
188
144
189
def cast ( value )
@@ -164,6 +209,8 @@ def serializable?(value, &block)
164
209
end
165
210
166
211
def assert_valid_value ( value )
212
+ return unless @_raise_on_invalid_values
213
+
167
214
unless value . blank? || mapping . has_key? ( value ) || mapping . has_value? ( value )
168
215
raise ArgumentError , "'#{ value } ' is not a valid #{ name } "
169
216
end
@@ -193,7 +240,7 @@ def inherited(base)
193
240
super
194
241
end
195
242
196
- def _enum ( name , values , prefix : nil , suffix : nil , scopes : true , instance_methods : true , **options )
243
+ def _enum ( name , values , prefix : nil , suffix : nil , scopes : true , instance_methods : true , validate : false , **options )
197
244
assert_valid_enum_definition_values ( values )
198
245
# statuses = { }
199
246
enum_values = ActiveSupport ::HashWithIndifferentAccess . new
@@ -209,7 +256,7 @@ def _enum(name, values, prefix: nil, suffix: nil, scopes: true, instance_methods
209
256
210
257
attribute ( name , **options ) do |subtype |
211
258
subtype = subtype . subtype if EnumType === subtype
212
- EnumType . new ( name , enum_values , subtype )
259
+ EnumType . new ( name , enum_values , subtype , raise_on_invalid_values : ! validate )
213
260
end
214
261
215
262
value_method_names = [ ]
@@ -241,6 +288,12 @@ def _enum(name, values, prefix: nil, suffix: nil, scopes: true, instance_methods
241
288
end
242
289
end
243
290
detect_negative_enum_conditions! ( value_method_names ) if scopes
291
+
292
+ if validate
293
+ validate = { } unless Hash === validate
294
+ validates_inclusion_of name , in : enum_values . keys , **validate
295
+ end
296
+
244
297
enum_values . freeze
245
298
end
246
299
0 commit comments