-
Notifications
You must be signed in to change notification settings - Fork 27
/
active_record.rb
80 lines (69 loc) · 2.69 KB
/
active_record.rb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
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
module ClassyEnum
class InvalidDefault < StandardError; end
module ActiveRecord
# Class macro used to associate an enum with an attribute on an ActiveRecord model.
# This method is automatically added to all ActiveRecord models when the classy_enum gem
# is installed. Accepts an argument for the enum class to be associated with
# the model. ActiveRecord validation is automatically added to ensure
# that a value is one of its pre-defined enum members.
#
# ==== Example
# # Associate an enum Priority with Alarm model's priority attribute
# class Alarm < ActiveRecord::Base
# classy_enum_attr :priority
# end
#
# # Associate an enum Priority with Alarm model's alarm_priority attribute
# classy_enum_attr :alarm_priority, :enum => 'Priority'
#
# # Allow enum value to be nil
# classy_enum_attr :priority, :allow_nil => true
#
# # Allow enum value to be blank
# classy_enum_attr :priority, :allow_blank => true
#
# # Specifying a default enum value
# classy_enum_attr :priority, :default => 'low'
def classy_enum_attr(attribute, options={})
enum = (options[:enum] || attribute).to_s.camelize.constantize
allow_blank = options[:allow_blank] || false
allow_nil = options[:allow_nil] || false
serialize_as_json = options[:serialize_as_json] || false
default = options[:default]
if default.present?
if default.is_a? Proc
default = default.call(enum)
end
unless enum.include? default
raise InvalidDefault, "must be one of [#{enum.to_a.join(',')}]"
end
end
# Add ActiveRecord validation to ensure it won't be saved unless it's an option
validates_inclusion_of attribute,
:in => enum,
:allow_blank => allow_blank,
:allow_nil => allow_nil
# Define getter method that returns a ClassyEnum instance
define_method attribute do
value = read_attribute(attribute) || default
enum.build(value,
:owner => self,
:serialize_as_json => serialize_as_json,
:allow_blank => (allow_blank || allow_nil)
)
end
# Define setter method that accepts string, symbol, instance or class for member
define_method "#{attribute}=" do |value|
if value.class == Class && value < ClassyEnum::Base
value = value.new.to_s
elsif value.present?
value = value.to_s
end
super(value)
end
end
end
end
if defined?(ActiveRecord::Base)
ActiveRecord::Base.send :extend, ClassyEnum::ActiveRecord
end