edave-enumerated_attribute 0.2.18

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (107) hide show
  1. data/.gitignore +15 -0
  2. data/CHANGELOG.rdoc +49 -0
  3. data/LICENSE +20 -0
  4. data/README.rdoc +558 -0
  5. data/Rakefile +30 -0
  6. data/init.rb +1 -0
  7. data/lib/enumerated_attribute.rb +24 -0
  8. data/lib/enumerated_attribute/attribute.rb +79 -0
  9. data/lib/enumerated_attribute/attribute/arguments.rb +48 -0
  10. data/lib/enumerated_attribute/attribute/attribute_descriptor.rb +51 -0
  11. data/lib/enumerated_attribute/attribute/class_methods.rb +45 -0
  12. data/lib/enumerated_attribute/attribute/instance_methods.rb +96 -0
  13. data/lib/enumerated_attribute/integrations.rb +33 -0
  14. data/lib/enumerated_attribute/integrations/active_record.rb +114 -0
  15. data/lib/enumerated_attribute/integrations/datamapper.rb +6 -0
  16. data/lib/enumerated_attribute/integrations/default.rb +25 -0
  17. data/lib/enumerated_attribute/integrations/object.rb +47 -0
  18. data/lib/enumerated_attribute/method_definition_dsl.rb +142 -0
  19. data/lib/enumerated_attribute/rails_helpers.rb +99 -0
  20. data/spec/active_record/active_record_spec.rb +384 -0
  21. data/spec/active_record/association_test_classes.rb +40 -0
  22. data/spec/active_record/associations_spec.rb +130 -0
  23. data/spec/active_record/cfg.rb +6 -0
  24. data/spec/active_record/df.rb +12 -0
  25. data/spec/active_record/inheritance_classes.rb +19 -0
  26. data/spec/active_record/inheritance_spec.rb +60 -0
  27. data/spec/active_record/race_car.rb +15 -0
  28. data/spec/active_record/single_table_inheritance_spec.rb +0 -0
  29. data/spec/active_record/sti_classes.rb +10 -0
  30. data/spec/active_record/sti_spec.rb +41 -0
  31. data/spec/active_record/test_in_memory.rb +71 -0
  32. data/spec/car.rb +67 -0
  33. data/spec/cfg.rb +8 -0
  34. data/spec/inheritance_classes.rb +16 -0
  35. data/spec/inheritance_spec.rb +80 -0
  36. data/spec/new_and_method_missing_spec.rb +73 -0
  37. data/spec/plural.rb +8 -0
  38. data/spec/poro_spec.rb +420 -0
  39. data/spec/rails/README +243 -0
  40. data/spec/rails/Rakefile +10 -0
  41. data/spec/rails/app/controllers/application_controller.rb +10 -0
  42. data/spec/rails/app/controllers/form_test_controller.rb +38 -0
  43. data/spec/rails/app/helpers/application_helper.rb +3 -0
  44. data/spec/rails/app/helpers/form_test_helper.rb +2 -0
  45. data/spec/rails/app/models/user.rb +9 -0
  46. data/spec/rails/app/views/form_test/form.html.erb +1 -0
  47. data/spec/rails/app/views/form_test/form_for.html.erb +10 -0
  48. data/spec/rails/app/views/form_test/form_tag.html.erb +9 -0
  49. data/spec/rails/app/views/form_test/index.html.erb +6 -0
  50. data/spec/rails/app/views/layouts/application.html.erb +11 -0
  51. data/spec/rails/config/boot.rb +110 -0
  52. data/spec/rails/config/database.yml +22 -0
  53. data/spec/rails/config/environment.rb +45 -0
  54. data/spec/rails/config/environments/development.rb +17 -0
  55. data/spec/rails/config/environments/production.rb +28 -0
  56. data/spec/rails/config/environments/test.rb +28 -0
  57. data/spec/rails/config/initializers/backtrace_silencers.rb +7 -0
  58. data/spec/rails/config/initializers/inflections.rb +10 -0
  59. data/spec/rails/config/initializers/mime_types.rb +5 -0
  60. data/spec/rails/config/initializers/new_rails_defaults.rb +19 -0
  61. data/spec/rails/config/initializers/session_store.rb +15 -0
  62. data/spec/rails/config/locales/en.yml +5 -0
  63. data/spec/rails/config/routes.rb +43 -0
  64. data/spec/rails/db/development.sqlite3 +0 -0
  65. data/spec/rails/db/migrate/20090804230221_create_sessions.rb +16 -0
  66. data/spec/rails/db/migrate/20090804230546_create_users.rb +21 -0
  67. data/spec/rails/db/schema.rb +35 -0
  68. data/spec/rails/db/test.sqlite3 +0 -0
  69. data/spec/rails/public/404.html +30 -0
  70. data/spec/rails/public/422.html +30 -0
  71. data/spec/rails/public/500.html +30 -0
  72. data/spec/rails/public/favicon.ico +0 -0
  73. data/spec/rails/public/images/rails.png +0 -0
  74. data/spec/rails/public/index.html +275 -0
  75. data/spec/rails/public/javascripts/application.js +2 -0
  76. data/spec/rails/public/javascripts/controls.js +963 -0
  77. data/spec/rails/public/javascripts/dragdrop.js +973 -0
  78. data/spec/rails/public/javascripts/effects.js +1128 -0
  79. data/spec/rails/public/javascripts/prototype.js +4320 -0
  80. data/spec/rails/public/robots.txt +5 -0
  81. data/spec/rails/public/stylesheets/scaffold.css +54 -0
  82. data/spec/rails/script/about +4 -0
  83. data/spec/rails/script/autospec +6 -0
  84. data/spec/rails/script/console +3 -0
  85. data/spec/rails/script/dbconsole +3 -0
  86. data/spec/rails/script/destroy +3 -0
  87. data/spec/rails/script/generate +3 -0
  88. data/spec/rails/script/performance/benchmarker +3 -0
  89. data/spec/rails/script/performance/profiler +3 -0
  90. data/spec/rails/script/plugin +3 -0
  91. data/spec/rails/script/runner +3 -0
  92. data/spec/rails/script/server +3 -0
  93. data/spec/rails/script/spec +10 -0
  94. data/spec/rails/script/spec_server +9 -0
  95. data/spec/rails/spec/controllers/form_test_controller_spec.rb +41 -0
  96. data/spec/rails/spec/integrations/enum_select_spec.rb +75 -0
  97. data/spec/rails/spec/matchers.rb +12 -0
  98. data/spec/rails/spec/rcov.opts +2 -0
  99. data/spec/rails/spec/spec.opts +4 -0
  100. data/spec/rails/spec/spec_helper.rb +40 -0
  101. data/spec/rails/spec/views/form_test/form.html.erb_spec.rb +12 -0
  102. data/spec/rails/spec/views/form_test/form_for.html.erb_spec.rb +12 -0
  103. data/spec/rails/spec/views/form_test/form_tag.html.erb_spec.rb +12 -0
  104. data/spec/rcov.opts +2 -0
  105. data/spec/spec.opts +4 -0
  106. data/spec/tractor.rb +48 -0
  107. metadata +190 -0
@@ -0,0 +1,30 @@
1
+ #require 'rake/testtask'
2
+ require 'rake/rdoctask'
3
+ require 'rake/contrib/sshpublisher'
4
+
5
+ begin
6
+ require 'jeweler'
7
+ Jeweler::Tasks.new do |s|
8
+
9
+ s.name = 'edave-enumerated_attribute'
10
+ s.platform = Gem::Platform::RUBY
11
+ s.description = 'Enumerated model attributes and view helpers'
12
+ s.summary = 'Add enumerated attributes to your models and expose them in drop-down lists in your views'
13
+
14
+ s.add_dependency('meta_programming', '>= 0.2.1')
15
+
16
+ exclude_folders = 'spec/rails/{doc,lib,log,nbproject,tmp,vendor,test}'
17
+ exclude_files = FileList['**/*.log'] + FileList[exclude_folders+'/**/*'] + FileList[exclude_folders]
18
+ s.files = FileList['{examples,lib,tasks,spec}/**/*'] + %w(CHANGELOG.rdoc init.rb LICENSE Rakefile README.rdoc .gitignore) - exclude_files
19
+ s.require_path = 'lib'
20
+ s.has_rdoc = true
21
+ s.test_files = Dir['spec/*_spec.rb']
22
+
23
+ s.author = 'Jeff Patmon'
24
+ s.email = 'jpatmon@gmail.com'
25
+ s.homepage = 'http://github.com/jeffp/enumerated_attribute/tree/master'
26
+ end
27
+ Jeweler::GemcutterTasks.new
28
+ rescue LoadError
29
+ puts "Jeweler not available. Install it with: gem install jeweler"
30
+ end
data/init.rb ADDED
@@ -0,0 +1 @@
1
+ require 'enumerated_attribute'
@@ -0,0 +1,24 @@
1
+ gem 'meta_programming', '>= 0.2.0'
2
+ require 'meta_programming'
3
+ require 'enumerated_attribute/attribute'
4
+
5
+ module EnumeratedAttribute
6
+
7
+ module MacroMethods
8
+
9
+ def enumerated_attribute(*args, &block)
10
+ class << self
11
+ include EnumeratedAttribute::Attribute
12
+ end
13
+ create_enumerated_attribute(*args, &block)
14
+ end
15
+ alias_method :enum_attr, :enumerated_attribute
16
+
17
+ end
18
+
19
+ end
20
+
21
+ Class.class_eval do
22
+ include EnumeratedAttribute::MacroMethods
23
+ end
24
+
@@ -0,0 +1,79 @@
1
+ require 'enumerated_attribute/attribute/attribute_descriptor'
2
+ require 'enumerated_attribute/attribute/arguments'
3
+ require 'enumerated_attribute/attribute/instance_methods'
4
+ require 'enumerated_attribute/attribute/class_methods'
5
+ require 'enumerated_attribute/method_definition_dsl'
6
+ require 'enumerated_attribute/integrations'
7
+ require 'enumerated_attribute/rails_helpers'
8
+ require 'ostruct'
9
+
10
+ module EnumeratedAttribute
11
+
12
+ class EnumeratedAttributeError < StandardError; end
13
+ class IntegrationError < EnumeratedAttributeError; end
14
+ class InvalidEnumeration < EnumeratedAttributeError; end
15
+ class InvalidDefinition < EnumeratedAttributeError; end
16
+ class AmbiguousMethod < EnumeratedAttributeError; end
17
+
18
+ module Attribute
19
+ private
20
+
21
+ def create_enumerated_attribute(*args, &block)
22
+ return if args.empty?
23
+ config = Arguments.parse_enum_attr_arguments(args)
24
+
25
+ class_eval do
26
+ @enumerated_attributes ||= {}
27
+ @enumerated_attributes[config.attr_symbol] = AttributeDescriptor.new(config.attr_symbol, config.enums, config.opts)
28
+
29
+ unless @integration_map
30
+ @integration_map = Integrations.find_integration_map(self)
31
+ @integration_map[:aliasing].each do |p|
32
+ alias_method(p.first, p.last)
33
+ end
34
+ include(EnumeratedAttribute::Integrations::Default)
35
+ include(@integration_map[:module]) if @integration_map[:module]
36
+
37
+ self.extend ClassMethods
38
+ include InstanceMethods
39
+ end
40
+ end
41
+
42
+ #create accessors
43
+ define_enumerated_attribute_reader_method(config.attr_symbol) unless (config.opts.key?(:reader) && !config.opts[:reader])
44
+ define_enumerated_attribute_writer_method(config.attr_symbol) unless (config.opts.key?(:writer) && !config.opts[:writer])
45
+ define_enumerated_attribute_new_method
46
+
47
+ #create state and action methods from block
48
+ config.initial_value = config.opts[:init] || config.initial_value
49
+ if block_given?
50
+ m = EnumeratedAttribute::MethodDefinitionDSL.new(self, self.enumerated_attributes(false)[config.attr_symbol]) #attr_name, enums)
51
+ m.instance_eval(&block)
52
+ config.initial_value = m.initial_value || config.initial_value
53
+ config.plural_name = m.pluralized_name || config.plural_name
54
+ config.decrementor = m.decrementor_name || config.decrementor
55
+ config.incrementor = m.incrementor_name || config.incrementor
56
+ end
57
+
58
+ #define the enum values accessor
59
+ class_eval do
60
+ @enumerated_attributes ||={}
61
+ @enumerated_attributes[config.attr_symbol].init_value = config.initial_value if config.initial_value
62
+
63
+ define_method(config.plural_name.to_sym) { self.class.enumerated_attributes[config.attr_symbol]}
64
+ define_method(config.incrementor.to_sym) do
65
+ z = self.class.enumerated_attributes[config.attr_symbol].enums
66
+ index = z.index(read_enumerated_attribute(config.attr_symbol))
67
+ write_enumerated_attribute(config.attr_symbol, z[index >= z.size-1 ? 0 : index+1])
68
+ end
69
+ define_method(config.decrementor.to_sym) do
70
+ z = self.class.enumerated_attributes[config.attr_symbol].enums
71
+ index = z.index(read_enumerated_attribute(config.attr_symbol))
72
+ write_enumerated_attribute(config.attr_symbol, z[index > 0 ? index-1 : z.size-1])
73
+ end
74
+ end
75
+ refresh_enumerated_attributes
76
+ end
77
+ end
78
+
79
+ end
@@ -0,0 +1,48 @@
1
+ module EnumeratedAttribute
2
+ module Attribute
3
+ module Arguments
4
+ def self.validate_enum_attr_arguments(config)
5
+ raise(InvalidDefinition, 'second argument of enumerated_attribute/enum_attr is not an array of symbols or strings representing the enum values', caller) if config.enums.empty?
6
+ end
7
+ def self.init_incrementor_decrementor_method_names(config)
8
+ config.incrementor = config.opts[:incrementor] || config.opts[:inc] || "#{config.attr_name}_next"
9
+ config.decrementor = config.opts[:decrementor] || config.opts[:dec] || "#{config.attr_name}_previous"
10
+ end
11
+ def self.init_plural_name(config)
12
+ config.plural_name = config.opts[:plural] || config.opts[:enums_accessor] || config.opts[:enums] || begin
13
+ case
14
+ when config.attr_name =~ /[aeiou]y$/
15
+ "#{config.attr_name}s"
16
+ when config.attr_name =~ /y$/
17
+ config.attr_name.sub(/y$/, 'ies')
18
+ when config.attr_name =~ /(sh|ch|x|s)$/
19
+ "#{config.attr_name}es"
20
+ else
21
+ "#{config.attr_name}s"
22
+ end
23
+ end
24
+ end
25
+ def self.process_enums_for_initial_value(config)
26
+ config.initial_value = nil
27
+ config.enums = config.enums.map{|v| (v =~ /^\^/ ? (config.initial_value ||= v[1, v.length-1].to_sym) : v.to_sym )}
28
+ end
29
+
30
+ def self.parse_enum_attr_arguments(args)
31
+ config = OpenStruct.new
32
+ config.attr_name = args[0].to_s
33
+ config.attr_symbol = config.attr_name.to_sym
34
+ config.enums = (args[1] && args[1].is_a?(Array) ? args[1] : [])
35
+ index = config.enums.empty? ? 1 : 2
36
+ config.opts = (args[index] && args[index].is_a?(Hash) ? args[index] : {})
37
+
38
+ validate_enum_attr_arguments(config)
39
+ init_plural_name(config)
40
+ init_incrementor_decrementor_method_names(config)
41
+ process_enums_for_initial_value(config)
42
+
43
+ config
44
+ end
45
+ end
46
+ end
47
+
48
+ end
@@ -0,0 +1,51 @@
1
+
2
+ module EnumeratedAttribute
3
+ module Attribute
4
+ class AttributeDescriptor < Array
5
+ attr_reader :name
6
+ attr_accessor :init_value
7
+
8
+ def initialize(name, enums=[], opts={})
9
+ super enums
10
+ @name = name
11
+ @options = opts
12
+ @labels_hash = Hash[*self.collect{|e| [e, e.to_s.gsub(/_/, ' ').squeeze(' ').capitalize]}.flatten]
13
+ end
14
+
15
+ def allows_nil?
16
+ @options.key?(:nil) ? @options[:nil] : true
17
+ end
18
+ def allows_value?(value)
19
+ self.include?(value.to_sym)
20
+ end
21
+
22
+ def enums
23
+ self
24
+ end
25
+ def label(value)
26
+ @labels_hash[value]
27
+ end
28
+ def labels
29
+ @labels_array ||= self.map{|e| @labels_hash[e]}
30
+ end
31
+ def hash
32
+ @labels_hash
33
+ end
34
+ def select_options
35
+ @select_options ||= self.map{|e| [@labels_hash[e], e.to_s]}
36
+ end
37
+
38
+ def set_label(enum_value, label_string)
39
+ reset_labels
40
+ @labels_hash[enum_value.to_sym] = label_string
41
+ end
42
+
43
+ protected
44
+ def reset_labels
45
+ @labels_array = nil
46
+ @select_options = nil
47
+ end
48
+
49
+ end
50
+ end
51
+ end
@@ -0,0 +1,45 @@
1
+ module EnumeratedAttribute
2
+ module Attribute
3
+ module ClassMethods
4
+ def refresh_enumerated_attributes
5
+ @all_enumerated_attributes_cache = nil
6
+ end
7
+ def enumerated_attributes(all=true)
8
+ return @enumerated_attributes unless all
9
+ return @all_enumerated_attributes_cache if @all_enumerated_attributes_cache
10
+ @all_enumerated_attributes_cache = @enumerated_attributes ? @enumerated_attributes.dup : {}
11
+ klass = self.superclass
12
+ while (klass)
13
+ if (klass.respond_to?(:enumerated_attributes))
14
+ if (sub_enums = klass.enumerated_attributes)
15
+ @all_enumerated_attributes_cache = sub_enums.merge @all_enumerated_attributes_cache
16
+ break
17
+ end
18
+ end
19
+ klass = klass.superclass
20
+ end
21
+ @all_enumerated_attributes_cache
22
+ end
23
+
24
+ def has_enumerated_attribute?(name)
25
+ !name.nil? && !!self.enumerated_attributes.key?(name.to_sym)
26
+ end
27
+ def enumerated_attribute_allows_nil?(name)
28
+ (descriptor = self.enumerated_attributes[name.to_sym]) && descriptor.allows_nil?
29
+ end
30
+ def enumerated_attribute_allows_value?(name, value)
31
+ return (false) unless (descriptor = self.enumerated_attributes[name.to_sym])
32
+ return descriptor.allows_nil? if (value == nil || value == '')
33
+ descriptor.allows_value?(value)
34
+ end
35
+
36
+ def define_enumerated_attribute_custom_method(symbol, attr_name, value, negated)
37
+ define_method symbol do
38
+ ival = read_enumerated_attribute(attr_name)
39
+ negated ? ival != value : ival == value
40
+ end
41
+ end
42
+
43
+ end
44
+ end
45
+ end
@@ -0,0 +1,96 @@
1
+ module EnumeratedAttribute
2
+ module Attribute
3
+ module InstanceMethods
4
+ def self.included(base)
5
+
6
+ method_missing_suffix = "enumerated_attribute_#{base.name.gsub(/.+::/,'')}_#{base.hash.abs}".to_sym
7
+ define_method("method_missing_with_#{method_missing_suffix}") do |methId, *args|
8
+ return self.__send__(methId) if define_enumerated_attribute_dynamic_method(methId)
9
+ self.__send__("method_missing_without_#{method_missing_suffix}", methId, *args)
10
+ end
11
+
12
+ respond_to_suffix = "enumerated_attribute_#{base.name.gsub(/.+::/,'')}_#{base.hash.abs}".to_sym
13
+ base.class_eval %{
14
+ def respond_to_with_#{respond_to_suffix}?(method, include_private=false)
15
+ self.respond_to_without_#{respond_to_suffix}?(method, include_private) ||
16
+ (!!parse_dynamic_method_parts!(method.to_s) rescue false)
17
+ end
18
+ }, __FILE__, __LINE__
19
+
20
+ base.safe_alias_method_chain :method_missing, method_missing_suffix
21
+ base.safe_alias_method_chain :respond_to?, respond_to_suffix
22
+
23
+ end
24
+
25
+ def enums(attr)
26
+ self.class.enumerated_attributes[attr.to_sym]
27
+ end
28
+
29
+
30
+ def initialize_enumerated_attributes(only_if_nil = false)
31
+ self.class.enumerated_attributes.each do |k,v|
32
+ self.write_enumerated_attribute(k, v.init_value) unless (only_if_nil && read_enumerated_attribute(k) != nil)
33
+ end
34
+ end
35
+
36
+ private
37
+
38
+ def parse_dynamic_method_parts!(meth_name)
39
+ return(nil) unless meth_name[-1, 1] == '?'
40
+
41
+ middle = meth_name.chop #remove the ?
42
+
43
+ attr = nil
44
+ self.class.enumerated_attributes.keys.each do |name|
45
+ if middle.sub!(Regexp.new("^"+name.to_s), "")
46
+ attr = name; break
47
+ end
48
+ end
49
+
50
+ value = nil
51
+ attr_sym = attr ? attr.to_sym : nil
52
+ if (descriptor = self.class.enumerated_attributes[attr_sym])
53
+ descriptor.enums.each do |v|
54
+ if middle.sub!(Regexp.new(v.to_s+"$"), "")
55
+ value = v; break
56
+ end
57
+ end
58
+ else
59
+ #search through enum values one at time and identify any ambiguities
60
+ self.class.enumerated_attributes.each do |attr_key,descriptor|
61
+ descriptor.enums.each do|v|
62
+ if middle.match(v.to_s+"$")
63
+ raise(AmbiguousMethod, meth_name+" is ambiguous, use something like "+attr_sym.to_s+(middle[0,1]=='_'? '' : '_')+middle+"? or "+attr_key.to_s+(middle[0,1]=='_'? '' : '_')+middle+"?", caller) if attr_sym
64
+ attr_sym = attr_key
65
+ value = v
66
+ end
67
+ end
68
+ end
69
+ return (nil) unless attr_sym
70
+ attr = attr_sym.to_s
71
+ middle.sub!(Regexp.new(value.to_s+"$"), "")
72
+ end
73
+
74
+ unless value #check for nil?
75
+ return (nil) unless middle.sub!(Regexp.new('nil$'), "")
76
+ value = nil
77
+ end
78
+
79
+ [attr, middle, value]
80
+ end
81
+
82
+ def define_enumerated_attribute_dynamic_method(methId)
83
+ meth_name = methId.id2name
84
+ parts = parse_dynamic_method_parts!(meth_name)
85
+ return(false) unless parts
86
+
87
+ negated = !!parts[1].downcase.match(/(^|_)not_/)
88
+ value = parts[2] ? parts[2].to_sym : nil
89
+ self.class.define_enumerated_attribute_custom_method(methId, parts[0], value, negated)
90
+
91
+ true
92
+ end
93
+
94
+ end
95
+ end
96
+ end
@@ -0,0 +1,33 @@
1
+ require 'enumerated_attribute/integrations/active_record'
2
+ require 'enumerated_attribute/integrations/object'
3
+ require 'enumerated_attribute/integrations/datamapper'
4
+ require 'enumerated_attribute/integrations/default'
5
+
6
+ module EnumeratedAttribute
7
+ module Integrations
8
+
9
+ @@integration_map = {}
10
+
11
+ def self.add_integration_map(base_klass_name, module_object, aliasing_array=[])
12
+ @@integration_map[base_klass_name] = {:module=>module_object, :aliasing=>aliasing_array}
13
+ end
14
+ class << self
15
+ alias_method(:add, :add_integration_map)
16
+ end
17
+
18
+ #included mappings
19
+ add('Object', EnumeratedAttribute::Integrations::Object)
20
+ add('ActiveRecord::Base', EnumeratedAttribute::Integrations::ActiveRecord)
21
+
22
+ def self.find_integration_map(klass)
23
+ path = "#{klass}"
24
+ begin
25
+ return @@integration_map[klass.to_s] if @@integration_map.key?(klass.to_s)
26
+ klass = klass.superclass
27
+ path << " < #{klass}"
28
+ end while klass
29
+ raise EnumeratedAttribute::IntegrationError, "Unable to find integration for class hierarchy '#{path}'", caller
30
+ end
31
+
32
+ end
33
+ end
@@ -0,0 +1,114 @@
1
+ module EnumeratedAttribute
2
+ module Integrations
3
+
4
+ module ActiveRecord
5
+ def self.included(klass)
6
+ klass.extend(ClassMethods)
7
+ end
8
+
9
+ def write_enumerated_attribute(name, val)
10
+ name = name.to_s
11
+ return write_attribute(name, val) unless self.class.has_enumerated_attribute?(name)
12
+ val = nil if val == ''
13
+ val_str = val.to_s if val
14
+ val_sym = val.to_sym if val
15
+ unless self.class.enumerated_attribute_allows_value?(name, val_sym)
16
+ raise(InvalidEnumeration, "nil is not allowed on '#{name}' attribute, set :nil=>true option", caller) unless val
17
+ raise(InvalidEnumeration, ":#{val_str} is not a defined enumeration value for the '#{name}' attribute", caller)
18
+ end
19
+ return instance_variable_set('@'+name, val_sym) unless self.has_attribute?(name)
20
+ write_attribute(name, val_str)
21
+ val_sym
22
+ end
23
+
24
+ def read_enumerated_attribute(name)
25
+ name = name.to_s
26
+ #if not enumerated - let active record handle it
27
+ return read_attribute(name) unless self.class.has_enumerated_attribute?(name)
28
+ #if enumerated, is it an active record attribute, if not, the value is stored in an instance variable
29
+ name_sym = name.to_sym
30
+ return instance_variable_get('@'+name) unless self.has_attribute?(name)
31
+ #this is an enumerated active record attribute
32
+ val = read_attribute(name)
33
+ val = val.to_sym if !!val
34
+ val
35
+ end
36
+
37
+ def attributes=(attrs, guard_protected_attributes=true)
38
+ return if attrs.nil?
39
+ #check the attributes then turn them over
40
+ attrs.each do |k, v|
41
+ if self.class.has_enumerated_attribute?(k)
42
+ unless self.class.enumerated_attribute_allows_value?(k, v)
43
+ raise InvalidEnumeration, ":#{v.to_s} is not a defined enumeration value for the '#{k.to_s}' attribute", caller
44
+ end
45
+ attrs[k] = v.to_s
46
+ end
47
+ end
48
+
49
+ super
50
+ end
51
+
52
+ def attributes
53
+ atts = super
54
+ atts.each do |k,v|
55
+ if self.class.has_enumerated_attribute?(k)
56
+ atts[k] = v.to_sym if v
57
+ end
58
+ end
59
+ atts
60
+ end
61
+
62
+ def [](attr_name); read_enumerated_attribute(attr_name); end
63
+ def []=(attr_name, value); write_enumerated_attribute(attr_name, value); end
64
+
65
+ private
66
+
67
+ def attribute=(attr_name, value); write_enumerated_attribute(attr_name, value); end
68
+
69
+ module ClassMethods
70
+ private
71
+
72
+ def construct_attributes_from_arguments(attribute_names, arguments)
73
+ attributes = {}
74
+ attribute_names.each_with_index{|name, idx| attributes[name] = has_enumerated_attribute?(name) ? arguments[idx].to_s : arguments[idx]}
75
+ attributes
76
+ end
77
+
78
+ def instantiate(record)
79
+ object = super(record)
80
+ self.enumerated_attributes.each do |k,v|
81
+ unless object.has_attribute?(k) #only initialize the non-column enumerated attributes
82
+ object.write_enumerated_attribute(k, v.init_value)
83
+ end
84
+ end
85
+ object
86
+ end
87
+
88
+ def define_enumerated_attribute_new_method
89
+ class_eval do
90
+ class << self
91
+ unless method_defined?(:new_without_enumerated_attribute)
92
+ alias_method :new_without_enumerated_attribute, :new
93
+ def new(*args, &block)
94
+ result = new_without_enumerated_attribute(*args, &block)
95
+ params = (!args.empty? && args.first.instance_of?(Hash)) ? args.first : {}
96
+ params.each { |k, v| result.write_enumerated_attribute(k, v) }
97
+ result.initialize_enumerated_attributes(true)
98
+ yield result if block_given?
99
+ result
100
+ end
101
+ end
102
+ unless private_method_defined?(:method_missing_without_enumerated_attribute)
103
+ define_chained_method(:method_missing, :enumerated_attribute) do |method_id, *arguments|
104
+ arguments = arguments.map{|arg| arg.is_a?(Symbol) ? arg.to_s : arg }
105
+ method_missing_without_enumerated_attribute(method_id, *arguments)
106
+ end
107
+ end
108
+ end
109
+ end
110
+ end
111
+ end
112
+ end
113
+ end
114
+ end