Module: Enum::AttrSupport

Defined in:
lib/iron/enum/attr_support.rb

Overview

Provides helper methods to integrate enumerated constants (Enum) into your model layer. Given an enum defined like so:

module UserType
  enum :guest,  0
  enum :member, 1
  enum :admin,  2
end

To add an enumerated value to a Rails model, simply add a column of type :integer to your model, then declare it like so:

class User < ActiveRecord::Base
  enum_attr :user_type => UserType
end

When using non-model classes, it’s the same syntax:

class User
  enum_attr :user_type => UserType
end

This will tell your class/model that the user_type attribute contains values from the UserType enum, and will add:

@user.user_type => integer value or nil
@user.user_type_admin? => true if object's user_type value == UserType::ADMIN
@user.user_type_admin! => set the object's user_type to be UserType::ADMIN (does not save model!)
@user.user_type_as_key => returns the key form of the current field value, eg :member
@user.user_type_as_name => returns text name of the current field's value, eg 'Guest'

In addition, you can set enum attributes via key, eg:

@user.user_type = :admin

and the key will be converted to a value on the fly.

ActiveRecord models get a few extras. To start, each enum attribute will add a smart scope:

User.with_user_type(UserType::MEMBER) => scope returning a relation selecting User instances where user_type's value == UserType::MEMBER

In addition, enum attributes will show up in #inspect output as e.g. UserType::GUEST instead of 0.

Instance Method Summary collapse

Instance Method Details

#enum_attr(field_to_enum_map) ⇒ Object

Call with enum_attr :field => Enum



48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
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
# File 'lib/iron/enum/attr_support.rb', line 48

def enum_attr(field_to_enum_map)
  # Save off the attr map
  @enum_attrs ||= {}
  @enum_attrs.merge!(field_to_enum_map)

  # Run each newly added enum attribute
  field_to_enum_map.each_pair do |attr_field, enum|
    # Convert Enum to "Enum"
    enum_klass = enum.to_s

    # Set up general use sugar - allows calling:
    #   attr_as_key to get back eg :production or :online instead of 1 or 5
    #   attr_as_name to get back eg "Production" or "Online"
    class_eval "      def \#{attr_field}_as_key\n        \#{enum_klass}.key(self.\#{attr_field})\n      end\n\n      def \#{attr_field}_as_name\n        \#{enum_klass}.name(self.\#{attr_field})\n      end\n    eos\n\n    # Get all the possible values for this enum in :key format (ie as symbols)\n    enum.keys.each do |key|\n      # Get the value for this key (ie in integer format)\n      val = enum.value(key)\n\n      # Build sugar for testing and setting the attribute's enumerated value\n      class_eval <<-eos, __FILE__, __LINE__ + 1\n        def \#{attr_field}_\#{key}?\n          self.\#{attr_field} == \#{val}\n        end\n\n        def \#{attr_field}_\#{key}!\n          self.\#{attr_field} = \#{val}\n        end\n      eos\n    end\n\n    if defined?(ActiveRecord) && self < ActiveRecord::Base\n\n      # Define a finder scope\n      scope \"with_\#{attr_field}\", lambda {|*vals|\n        vals.flatten!\n        if vals.empty?\n          where(\"?\", false)\n        elsif vals.count == 1\n          where(attr_field => enum.value(vals.first))\n        else\n          where(attr_field => enum.values(vals))\n        end\n      }\n      \n      # Define a validation\n      validates attr_field, :inclusion => { \n        :in => enum.values,\n        :message => \"%{value} is not a valid \#{enum_klass} value\",\n        :allow_nil => true\n      }\n\n      # Override default setter to allow setting an enum attribute via key\n      class_eval <<-eos, __FILE__, __LINE__ + 1\n        def \#{attr_field}=(val)\n          write_attribute(:\#{attr_field}, \#{enum_klass}.value(val))\n        end\n      eos\n\n    else \n\n      # Create getter/setter to allow setting an enum attribute via key\n      class_eval <<-eos, __FILE__, __LINE__ + 1\n        def \#{attr_field}\n          @\#{attr_field}\n        end\n\n        def \#{attr_field}=(val)\n          val = nil if val.is_a?(String) && val.empty?\n          @\#{attr_field} = \#{enum_klass}.value(val)\n        end\n      eos\n\n    end\n\n  end\nend\n", __FILE__, __LINE__ + 1

#enum_attr?(name) ⇒ Boolean

True if the given symbol maps to an enum-backed attribute

Returns:

  • (Boolean)


136
137
138
139
# File 'lib/iron/enum/attr_support.rb', line 136

def enum_attr?(name)
  return false unless @enum_attrs
  @enum_attrs.key?(name)
end

#enum_for_attr(name) ⇒ Object

Gets the enum class for a given attribute, or nil for none



142
143
144
145
# File 'lib/iron/enum/attr_support.rb', line 142

def enum_for_attr(name)
  return nil unless @enum_attrs
  @enum_attrs[name]
end