assignable_values - Enums on vitamins ===================================== `assignable_values` lets you restrict the values that can be assigned to attributes or associations of ActiveRecord models. You can think of it as enums where the list of allowed values is generated at runtime and the value is checked during validation. We carefully enhanced the cure enum functionality with small tweaks that are useful for web forms, internationalized applications and common authorization patterns. Restricting scalar attributes ----------------------------- The basic usage to restrict the values assignable to strings, integers, etc. is this: class Song < ActiveRecord::Base assignable_values_for :genre do ['pop', 'rock', 'electronic'] end end The assigned value is checked during validation: Song.new(:genre => 'rock').valid? # => true Song.new(:genre => 'elephant').valid? # => false The validation error message is the same as the one from `validates_inclusion_of` (`errors.messages.inclusion` in your I18n dictionary). ### Listing assignable values You can ask a record for a list of values that can be assigned to an attribute: song.assignable_genres # => ['pop', 'rock', 'electronic'] This is useful for populating `` tag with pairs of internal values and human labels is to use the `collection_select` helper from Rails: form.collection_select :genre, form.object.humanized_genres, :value, :humanized If you don't like to use your I18n dictionary for humanizations, you can also declare them directly in your model like this: class Song < ActiveRecord::Base assignable_values_for :genre do { 'pop' => 'Pop music', 'rock' => 'Rock music', 'electronic' => 'Electronic music' } end end ### Defining default values You can define a default value by using the `:default` option: class Song < ActiveRecord::Base assignable_values_for :genre, :default => 'rock' do ['pop', 'rock', 'electronic'] end end The default is applied to new records: Song.new.genre # => 'rock' Defaults can be procs: class Song < ActiveRecord::Base assignable_values_for :genre, :default => proc { Date.today.year } do 1980 .. 2011 end end The proc will be evaluated in the context of the record instance. You can also default a secondary default that is only set if the primary default value is not assignable: class Song < ActiveRecord::Base assignable_values_for :year, :default => 1999, :secondary_default => lambda { Date.today.year } do (Date.today.year - 2) .. Date.today.year end end If called in 2013 the code above will fall back to: Song.new.year # => 2013 ### Allowing blank values By default, an attribute *must* be assigned an value. If the value of an attribute is blank, the attribute will get a validation error. If you would like to change this behavior and allow blank values to be valid, use the `:allow_blank` option: class Song < ActiveRecord::Base assignable_values_for :genre, :default => 'rock', :allow_blank => true do ['pop', 'rock', 'electronic'] end end The `:allow_blank` option can be a symbol, in which case a method of that name will be called on the record. The `:allow_blank` option can also be a lambda, in which case the lambda will be called in the context of the record. ### Values are only validated when they change Values are only validated when they change. This is useful when the list of assignable values can change during runtime: class Song < ActiveRecord::Base assignable_values_for :year do (Date.today.year - 2) .. Date.today.year end end If a value has been saved before, it will remain valid, even if it is no longer assignable: Song.update_all(:year => 1985) # update all records with a value that is no longer valid song = Song.last song.year # => 1985 song.valid? # => true It will also be returned when obtaining the list of assignable values: song.assignable_genres # => [2010, 2011, 2012, 1985] Once a changed value has been saved, the previous value disappears from the list of assignable values: song.genre = 'pop' song.save! song.assignable_years # => [2010, 2011, 2012] song.year = 1985 song.valid? # => false This is to prevent records from becoming invalid as the list of assignable values evolves. This also prevents `