Module: Enumify::Model::ClassMethods

Defined in:
lib/enumify/model.rb

Instance Method Summary collapse

Instance Method Details

#enumify(parameter, vals = [], opts = {}) ⇒ Object



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
81
82
83
# File 'lib/enumify/model.rb', line 9

def enumify(parameter, vals=[], opts={})

  validates_inclusion_of parameter, :in => vals, :allow_nil => !!opts[:allow_nil]
  paramater_string = parameter.to_s

  prefix = ''
  if opts[:prefix] == true
    prefix = "#{paramater_string}_"
  elsif opts[:prefix].present?
    prefix = "#{opts[:prefix].to_s}_"
  end

  constant = opts.fetch(:constant, true)
  if constant
    const_name = constant === true ? paramater_string.pluralize : constant.to_s

    const_set(const_name.upcase, vals)
  end

  define_method "#{paramater_string}" do
    attr = read_attribute(parameter)
    (attr.nil? || attr.empty?) ? nil : attr.to_sym
  end

  define_method "#{paramater_string}=" do |value|
    send("_set_#{paramater_string}", value, false)
  end

  self.class_eval do

    private

    define_method "_set_#{paramater_string}" do |value, should_save|

      value = value and value.to_sym
      old = read_attribute(parameter) ? read_attribute(parameter).to_sym : nil
      return value if old == value
      write_attribute(parameter, (value and value.to_s))
      save if should_save
      send("#{paramater_string}_changed", old, value) if respond_to?("#{paramater_string}_changed", true) and !old.nil?
      return value
    end

  end

  vals.each do |val|
    attribute = prefix + val.to_s
    query_method = "#{attribute}?"
    bang_method = "#{attribute}!"

    raise "Collision in enum values method #{attribute}" if respond_to?(query_method) or respond_to?(bang_method) or respond_to?(attribute)

    define_method query_method do
      send("#{paramater_string}") == val
    end

    define_method bang_method do
      send("_set_#{paramater_string}", val, true)
    end

    scope attribute.to_sym, lambda { where(parameter.to_sym => val.to_s) }
  end

  # We want to first define all the "positive" scopes and only then define
  # the "negation scopes", to make sure they don't override previous scopes
  vals.each do |val|
    # We need to prefix the field with the table name since if this scope will
    # be used in a joined query with other models that have the same enum field then
    # it will fail on ambiguous column name.
    negative_scope = "not_" + prefix + val.to_s
    unless respond_to?(negative_scope)
      scope negative_scope, lambda { where("#{self.table_name}.#{parameter} != ?", val.to_s) }
    end
  end
end