edave-enumerated_attribute 0.2.18

Sign up to get free protection for your applications and to get access to all the features.
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