Module: EnumX::DSL::ClassMethods

Defined in:
lib/enum_x/dsl.rb

Overview

DSL methods

Instance Method Summary collapse

Instance Method Details

#enum(name, [enum], validation_options = {}) ⇒ Object

Defines an enum for an attribute. This works on ActiveRecord objects, but also on other objects. However, for non-ActiveRecord objects, make sure that the underlying attribute already exists.

Examples:

The following creates an enum on an ActiveRecord object.

enum :kind                     # => Uses EnumX.kinds

The following creates an enum on an ActiveRecord object, but uses ‘account_kinds’ as the name.

enum :kind, :account_kinds     # => Uses EnumX.account_kinds

The following creates an enum on an ActiveRecord object, but uses a custom array.

class  < ActiveRecord::Base
  enum :kind, %w[ normal special ]  # => Uses an on the fly enum with name 'account_kinds'.
end

The following creates an enum on an ActiveRecord object, but uses a custom array, on an anonymous class.

Class.new(ActiveRecord::Base) do
  enum :kind, %w[ normal special ]  # => Uses an on the fly enum with name '_kinds'.
end

The following creates an enum on a non-ActiveRecord class.

class 
  attr_accessor :kind   # Required first!
  enum :kind
end

The following creates an enum and creates mnemonics.

class  < ActiveRecord::Base
  enum :kind, :mnemonics => true
end

# Given that enum 'kinds' has options %[ normal special ], the following methods are now added:
.new(:kind => :normal).normal? # => true
.new(:kind => :special).normal? # => true
.new(:kind => :normal).special? # => false
.new(:kind => :special).special? # => false
.new(:kind => :special).something_else? # => raises NoMethodError

Parameters:

  • attribute (Symbol)

    The attribute to add enum support to.

  • The (EnumX|Enumerable|Symbol)

    enum to use. Specify a symbol to look up the enum from the defined enums, or use an array to create an ad-hoc enum for this attribute, with name “#model_#EnumX::DSL::ClassMethods.attributeattribute.pluralize”, e.g. ‘post_statuses’.

  • validation_options (Hash) (defaults to: {})

    Options for the validation routine. The options listed below are extracted from this hash.

Options Hash (validation_options):

  • :validation (Object)

    Set to false to disable validation altogether. TODO SPEC

  • :flags (Object)

    Set to true to enable a flag enum attribute.

  • :mnemonics (Object)

    Set to true to create enum mnemonics on the receiver class. These are question-mark style methods for all the values on the enum. Note that these may override existing methods, so use them only for enums that define a part of the ‘identity’ of the receiving class, such as a status or a kind.

Raises:

  • (ArgumentError)


89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
# File 'lib/enum_x/dsl.rb', line 89

def enum(attribute, *args)

  validation_options = args.extract_options!
  enum = args.shift
  raise ArgumentError, "too many arguments (2..3 expected)" if args.length > 0

  flags = validation_options.delete(:flags)
  mnemonics = validation_options.delete(:mnemonics)

  # Determine the default name of the enum, and the name of the class-level enum reader.
  enum_reader_name = if flags
    # The attribute is already specified in the plural form (enum :statuses).
    attribute.to_s
  else
    # The attribute is specified in the singular form - pluralize it (enum :status).
    attribute.to_s.pluralize
  end

  enum = case enum_opt = enum
  when nil then EnumX[enum_reader_name]
  when EnumX then enum
  when Symbol, String then EnumX[enum]
  when Enumerable
    name = if self.name
      "#{self.name.demodulize.underscore}_#{enum_reader_name}"
    else
      # Anonymous class - use just the attribute name with an underscore in front of it.
      "_#{enum_reader_name}"
    end

    EnumX.new(name, enum)
  end
  raise ArgumentError, "cannot find enum #{(enum_opt || enum_reader_name).inspect}" unless enum

  # Define a shorthand enum accessor method.
  unless respond_to?(enum_reader_name)
    # Regular class. As the class may be inherited, make sure to try superclasses as well.
    class_eval "def self.\#{enum_reader_name}\n@\#{enum_reader_name} ||= if superclass.respond_to?(:\#{enum_reader_name})\nsuperclass.\#{enum_reader_name}\nend\nend\n", __FILE__, __LINE__+1
  end

  # Store the enum on this class.
  instance_variable_set "@#{enum_reader_name}", enum

  if flags
    # Define a flags enum.

    # Validation
    if validation_options[:validation] != false

      validation_options.assert_valid_keys :allow_blank
      if validation_options[:allow_blank] != false
        class_eval "validates_each :\#{attribute} do |record, attribute, value|\nif value.present?\nvalue = [ value ] unless value.is_a?(Enumerable)\nif not_included_value = value.find{ |v| !enum.values.include?(v) }\nrecord.errors.add attribute, :inclusion, :value => not_included_value\nend\nend\nend\n", __FILE__, __LINE__+1
      else
        class_eval "validates_each :\#{attribute} do |record, attribute, value|\nvalue = [ value ] unless value.is_a?(Enumerable) || value.nil?\nif value.blank?\nrecord.errors.add attribute, :blank\nelsif not_included_value = value.find{ |v| !enum.values.include?(v) }\nrecord.errors.add attribute, :inclusion, :value => not_included_value\nend\nend\n", __FILE__, __LINE__+1
      end

    end

    # Serialize the value if this is an ActiveRecord class AND if the database actually contains
    # this column.
    if defined?(ActiveRecord) && self < ActiveRecord::Base && self.column_names.include?(attribute.to_s)
      serialize attribute, FlagsSerializer.new(enum)
    end

    # Provide a customized reader.
    DSL.define_multi_reader self, attribute
    DSL.define_multi_writer self, attribute

    # Provide two Squeel sifters.
    if respond_to?(:sifter)
      class_eval "sifter(:\#{attribute}_include) { |value| instance_eval('\#{attribute}') =~ \"%|\\\#{value}|%\" }\nsifter(:\#{attribute}_exclude) { |value| instance_eval('\#{attribute}') !~ \"%|\\\#{value}|%\" }\n", __FILE__, __LINE__+1
    end

  else
    # Define a single enum.

    # Validation
    if validation_options[:validation] != false
      # Provide validations.
      validation_options = validation_options.merge(:in => enum.values)
      validation_options[:allow_blank] = true unless validation_options.key?(:allow_blank)

      validates_inclusion_of attribute, validation_options
    end

    # Serialize the value if this is an ActiveRecord class AND if the database actually contains
    # this column.
    if defined?(ActiveRecord) && self < ActiveRecord::Base && self.column_names.include?(attribute.to_s)
      serialize attribute, SingleSerializer.new(enum)
    end

    # Provide a customized reader.
    DSL.define_single_reader self, attribute
    DSL.define_single_writer self, attribute

  end

  # Provide mnemonics if requested
  DSL.define_mnemonics self, attribute, enum if mnemonics

end