Module: Symbolize::ClassMethods

Defined in:
lib/symbolize.rb

Overview

Symbolize ActiveRecord attributes. Add

symbolize :attr_name

to your model class, to make an attribute return symbols instead of string values. Setting such an attribute will accept symbols as well as strings. In the database, the symbolized attribute should have the column-type :string.

Example:

class User < ActiveRecord::Base
  symbolize :gender, :in => [:female, :male]
  symbolize :so, :in => {
    :linux   => "Linux",
    :mac     => "Mac OS X"
  }
  symbolize :gui, , :in => [:gnome, :kde, :xfce], :allow_blank => true
  symbolize :browser, :in => [:firefox, :opera], :i18n => false
end

It will automattically lookup for i18n:

activerecord:

attributes:
  user:
    enums:
      gender:
        female: Girl
        male: Boy

You can skip i18n lookup with :i18n => false

symbolize :gender, :in => [:female, :male], :i18n => false

Its possible to use boolean fields also.

symbolize :switch, :in => [true, false]

...
  switch:
    "true": On
    "false": Off
    "nil": Unknown

Instance Method Summary collapse

Instance Method Details

#symbolize(*attr_names) ⇒ Object

Specifies that values of the given attributes should be returned as symbols. The table column should be created of type string.



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
134
135
136
137
138
139
140
141
# File 'lib/symbolize.rb', line 50

def symbolize *attr_names
  configuration = {}
  configuration.update(attr_names.extract_options!)

  enum = configuration[:in] || configuration[:within]
  i18n = configuration.delete(:i18n).nil? && !enum.instance_of?(Hash) && enum ? true : configuration[:i18n]
  scopes  = configuration.delete :scopes
  methods = configuration.delete :methods
  capitalize = configuration.delete :capitalize
  validation     = configuration.delete(:validation) != false
  default_option = configuration.delete :default

  unless enum.nil?
    # enum = enum.map { |e| e.respond_to?(:to_sym) ? e.to_sym : e}
    # Little monkeypatching, <1.8 Hashes aren't ordered.
    hsh = RUBY_VERSION > '1.9' || !defined?("ActiveSupport") ? Hash : ActiveSupport::OrderedHash

    attr_names.each do |attr_name|
      attr_name = attr_name.to_s
      const =  "#{attr_name}_values"
      if enum.is_a?(Hash)
        values = enum
      else
        values = hsh.new
        enum.map do |val|
          key = val.respond_to?(:to_sym) ? val.to_sym : val
          values[key] = capitalize ? val.to_s.capitalize : val.to_s
        end
      end

      # Get the values of :in
      const_set const.upcase, values unless const_defined? const.upcase
      ev = if i18n
        # This one is a dropdown helper
        code =  "#{const.upcase}.map { |k,v| [I18n.translate(\"activerecord.attributes.\#{ActiveSupport::Inflector.underscore(self)}.enums.#{attr_name}.\#{k}\"), k] }" #.to_sym rescue nila
        "def self.get_#{const}; #{code}; end;"
      else
        "def self.get_#{const}; #{const.upcase}.map(&:reverse); end"
      end
      class_eval(ev)

      if methods
        values.each do |value|
          define_method("#{value[0]}?") do
            self.send(attr_name) == value[0]
          end
        end
      end

      if scopes
        scope_comm = lambda { |*args| ActiveRecord::VERSION::MAJOR >= 3 ? scope(*args) : named_scope(*args)}
        values.each do |value|
          if value[0].respond_to?(:to_sym)
            scope_comm.call value[0].to_sym, :conditions => { attr_name => value[0].to_sym }
          else
            if value[0] == true || value[0] == false
              scope_comm.call "with_#{attr_name}",    :conditions => { attr_name => true }
              scope_comm.call "without_#{attr_name}", :conditions => { attr_name => false }

              scope_comm.call attr_name.to_sym,   :conditions => { attr_name => true }
              scope_comm.call "not_#{attr_name}", :conditions => { attr_name => false }
            end
          end
        end
      end
    end

    if validation
      class_eval "validates_inclusion_of :#{attr_names.join(', :')}, #{configuration.inspect}"
    end
  end

  attr_names.each do |attr_name|

    if default_option
      class_eval("def #{attr_name}; read_and_symbolize_attribute('#{attr_name}') || :#{default_option}; end")
      class_eval("def #{attr_name}= (value); write_symbolized_attribute('#{attr_name}', value); end")
      class_eval("def set_default_for_attr_#{attr_name}; self[:#{attr_name}] ||= :#{default_option}; end")
      class_eval("before_save :set_default_for_attr_#{attr_name}")
    else
      class_eval("def #{attr_name}; read_and_symbolize_attribute('#{attr_name}'); end")
      class_eval("def #{attr_name}= (value); write_symbolized_attribute('#{attr_name}', value); end")
    end
    if i18n
      class_eval("def #{attr_name}_text; read_i18n_attribute('#{attr_name}'); end")
    elsif enum
      class_eval("def #{attr_name}_text; #{attr_name.to_s.upcase}_VALUES[#{attr_name}]; end")
    else
      class_eval("def #{attr_name}_text; #{attr_name}.to_s; end")
    end
  end
end