enumerated_attribute 0.2.7

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 +16 -0
  2. data/CHANGELOG.rdoc +49 -0
  3. data/LICENSE +20 -0
  4. data/README.rdoc +555 -0
  5. data/Rakefile +104 -0
  6. data/init.rb +1 -0
  7. data/lib/enumerated_attribute.rb +24 -0
  8. data/lib/enumerated_attribute/attribute.rb +78 -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 +42 -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 +109 -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 +97 -0
  20. data/lib/jeffp-enumerated_attribute.rb +1 -0
  21. data/spec/active_record/active_record_spec.rb +363 -0
  22. data/spec/active_record/association_test_classes.rb +40 -0
  23. data/spec/active_record/associations_spec.rb +130 -0
  24. data/spec/active_record/cfg.rb +6 -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 +397 -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 +182 -0
@@ -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,109 @@
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 = super
74
+ attributes.each { |k,v| attributes[k] = v.to_s if has_enumerated_attribute?(k) }
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
+ end
103
+ end
104
+ end
105
+
106
+ end
107
+ end
108
+ end
109
+ end
@@ -0,0 +1,6 @@
1
+ module EnumerateAttribute
2
+ module Integrations
3
+ module Datamapper
4
+ end
5
+ end
6
+ end
@@ -0,0 +1,25 @@
1
+ module EnumeratedAttribute
2
+ module Integrations
3
+
4
+ module Default
5
+ def self.included(klass); klass.extend(ClassMethods); end
6
+
7
+ module ClassMethods
8
+ def define_enumerated_attribute_writer_method(name)
9
+ method_name = "#{name}=".to_sym
10
+ class_eval do
11
+ define_method(method_name) {|val| write_enumerated_attribute(name.to_sym, val) }
12
+ end
13
+ end
14
+
15
+ def define_enumerated_attribute_reader_method(name)
16
+ name = name.to_sym
17
+ class_eval do
18
+ define_method(name) { read_enumerated_attribute(name) }
19
+ end
20
+ end
21
+ end
22
+ end
23
+
24
+ end
25
+ end
@@ -0,0 +1,47 @@
1
+ module EnumeratedAttribute
2
+ module Integrations
3
+
4
+ module Object
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
+ val = nil if val == ''
12
+ val = val.to_sym if val
13
+ unless self.class.enumerated_attribute_allows_value?(name, val)
14
+ raise(InvalidEnumeration, "nil is not allowed on '#{name}' attribute, set :nil=>true option", caller) unless val
15
+ raise(InvalidEnumeration, ":#{val} is not a defined enumeration value for the '#{name}' attribute", caller)
16
+ end
17
+ instance_variable_set('@'+name, val)
18
+ end
19
+
20
+ def read_enumerated_attribute(name)
21
+ instance_variable_get('@'+name.to_s)
22
+ end
23
+
24
+ module ClassMethods
25
+ private
26
+
27
+ def define_enumerated_attribute_new_method
28
+ class_eval do
29
+ class << self
30
+ unless method_defined?(:new_without_enumerated_attribute)
31
+ alias_method :new_without_enumerated_attribute, :new
32
+ def new(*args, &block)
33
+ result = new_without_enumerated_attribute(*args)
34
+ result.initialize_enumerated_attributes
35
+ yield result if block_given?
36
+ result
37
+ end
38
+ end
39
+ end
40
+ end
41
+ end
42
+
43
+ end
44
+
45
+ end
46
+ end
47
+ end
@@ -0,0 +1,142 @@
1
+ module EnumeratedAttribute
2
+
3
+ class MethodDefinition
4
+ attr_accessor :method_name, :negated, :argument
5
+
6
+ def initialize(name, arg, negated=false)
7
+ @method_name, @negated, @argument = name, negated, arg
8
+ end
9
+
10
+ def is_predicate_method?; @method_name[-1, 1] == '?'; end
11
+ def has_method_name?; !!@method_name; end
12
+ def has_argument?; !!@argument; end
13
+ end
14
+
15
+ class MethodDefinitionDSL
16
+ attr_reader :initial_value, :pluralized_name, :decrementor_name, :incrementor_name
17
+
18
+ def initialize(class_obj, descriptor)
19
+ @class_obj = class_obj
20
+ @attr_name = descriptor.name
21
+ @attr_descriptor = descriptor #this is the enums array
22
+ end
23
+
24
+ #we'll by pass this - they can use it if it helps make code more readable - not enforced - should it be??
25
+ def define
26
+ end
27
+
28
+ def is_not(*args)
29
+ arg = args.first if args.length > 0
30
+ MethodDefinition.new(nil, arg, true)
31
+ end
32
+ alias :isnt :is_not
33
+
34
+ def is(*args)
35
+ arg = args.first if args.length > 0
36
+ MethodDefinition.new(nil, arg)
37
+ end
38
+
39
+ def method_missing(methId, *args, &block)
40
+ meth_name = methId.id2name
41
+
42
+ meth_def = nil
43
+ if args.size > 0
44
+ arg = args.first
45
+ if arg.instance_of?(EnumeratedAttribute::MethodDefinition)
46
+ if arg.has_method_name?
47
+ raise_method_syntax_error(meth_name, arg.method_name)
48
+ end
49
+ meth_def = arg
50
+ meth_def.method_name = meth_name
51
+ else
52
+ meth_def = MethodDefinition.new(meth_name, arg)
53
+ end
54
+ elsif block_given?
55
+ meth_def = MethodDefinition.new(meth_name, block)
56
+ else
57
+ raise_method_syntax_error(meth_name)
58
+ end
59
+ evaluate_method_definition(meth_def)
60
+ end
61
+
62
+ def init(value)
63
+ if (!@attr_descriptor.empty? && !@attr_descriptor.include?(value.to_sym))
64
+ raise(InvalidDefinition, "'#{value}' in method 'init' is not an enumeration value for :#{@attr_name} attribute", caller)
65
+ end
66
+ @initial_value = value
67
+ end
68
+
69
+ def decrementor(value); @decrementor_name = value; end
70
+ def incrementor(value); @incrementor_name = value; end
71
+ def enums_accessor(value); @pluralized_name = value; end
72
+ alias_method :inc, :incrementor
73
+ alias_method :dec, :decrementor
74
+ alias_method :enums, :enums_accessor
75
+ alias_method :plural, :enums_accessor
76
+
77
+ def label(hash)
78
+ raise(InvalidDefinition, "label or labels keyword should be followed by a hash of :enum_value=>'label'", caller) unless hash.is_a?(Hash)
79
+ hash.each do |k,v|
80
+ raise(InvalidDefinition, "#{k} is not an enumeration value for :#{@attr_name} attribute", caller) unless (k.is_a?(Symbol) && @attr_descriptor.include?(k))
81
+ raise(InvalidDefinition, "#{v} is not a string. Labels should be strings", caller) unless v.is_a?(String)
82
+ @attr_descriptor.set_label(k, v)
83
+ end
84
+ end
85
+ alias_method :labels, :label
86
+
87
+ private
88
+
89
+ def raise_method_syntax_error(meth_name, offending_token=nil)
90
+ suffix = offending_token ? "found '#{offending_token}'" : "found nothing"
91
+ followed_by = (meth_name[-1,1] == '?' ? "is_not, an enumeration value, an array of enumeration values, " : "") + "a proc, lambda or code block"
92
+ raise InvalidDefinition, "'#{meth_name}' should be followed by #{followed_by} -- but #{suffix}"
93
+ end
94
+
95
+ def evaluate_method_definition(mdef)
96
+ unless mdef.has_argument?
97
+ return raise_method_syntax_error(mdef.method_name)
98
+ end
99
+
100
+ if mdef.is_predicate_method?
101
+ case mdef.argument
102
+ when String
103
+ return create_custom_method_for_symbol_or_string(mdef)
104
+ when Symbol
105
+ return create_custom_method_for_symbol_or_string(mdef)
106
+ when Array
107
+ return create_custom_method_for_array_of_enums(mdef)
108
+ when Proc
109
+ return create_custom_method_for_proc(mdef)
110
+ end
111
+ else #action method
112
+ if mdef.argument.instance_of?(Proc)
113
+ return create_custom_method_for_proc(mdef)
114
+ end
115
+ end
116
+ raise_method_syntax_error(mdef.method_name, mdef.argument)
117
+ end
118
+
119
+ def create_custom_method_for_proc(mdef)
120
+ @class_obj.send(:define_method, mdef.method_name, mdef.argument)
121
+ end
122
+
123
+ def create_custom_method_for_symbol_or_string(mdef)
124
+ if (!@attr_descriptor.empty? && !@attr_descriptor.include?(mdef.argument.to_sym))
125
+ raise(InvalidDefinition, "'#{mdef.argument}' in method '#{mdef.method_name}' is not an enumeration value for :#{@attr_name} attribute", caller)
126
+ end
127
+ @class_obj.class_eval("def #{mdef.method_name}; @#{@attr_name} #{mdef.negated ? '!=' : '=='} :#{mdef.argument}; end")
128
+ end
129
+
130
+ def create_custom_method_for_array_of_enums(mdef)
131
+ if !@attr_descriptor.empty?
132
+ mdef.argument.each do |m|
133
+ if !@attr_descriptor.include?(m.to_sym)
134
+ raise(InvalidDefinition, "'#{m}' in method '#{mdef.method_name}' is not an enumeration value for :#{@attr_name} attribute", caller)
135
+ end
136
+ end
137
+ end
138
+ @class_obj.class_eval("def #{mdef.method_name}; #{mdef.negated ? '!' : ''}[:#{mdef.argument.join(',:')}].include?(@#{@attr_name}); end")
139
+ end
140
+ end
141
+
142
+ end
@@ -0,0 +1,97 @@
1
+ require 'active_record/connection_adapters/abstract/schema_definitions'
2
+
3
+ if defined?(ActiveRecord)
4
+ module ActiveRecord
5
+ module ConnectionAdapters
6
+ class TableDefinition
7
+ def column_with_enumerated_attribute(name, type, options = {})
8
+ type = 'string' if type.to_s == 'enum'
9
+ column_without_enumerated_attribute(name, type, options)
10
+ end
11
+ safe_alias_method_chain :column, :enumerated_attribute
12
+
13
+ def enum(*args)
14
+ options = args.extract_options!
15
+ column_names = args
16
+ column_names.each { |name| column(name, 'string', options) }
17
+ end
18
+ end
19
+ end
20
+ end
21
+ end
22
+
23
+ #ARGV is used by generators -- if it contains one of these generator commands - add enumeration support
24
+ #unless ((ARGV || []) & ["scaffold", "rspec_scaffold", "nifty_scaffold"]).empty?
25
+ if ((ARGV || []).any?{|o| o =~ /scaffold/ })
26
+ require 'rails_generator' rescue nil
27
+ begin
28
+ require 'rails/generators'
29
+ require 'rails/generators/generated_attribute'
30
+ rescue
31
+ end
32
+
33
+ module Rails
34
+ module Generator
35
+ class GeneratedAttribute
36
+ def field_type_with_enumerated_attribute
37
+ return (@field_type = :enum_select) if type == :enum
38
+ field_type_without_enumerated_attribute
39
+ end
40
+ alias_method_chain :field_type, :enumerated_attribute
41
+ end
42
+ end
43
+ end
44
+ end
45
+
46
+ if defined?(ActionView::Base)
47
+ module ActionView
48
+ module Helpers
49
+
50
+ #form_options_helper.rb
51
+ module FormOptionsHelper
52
+ #def select
53
+ def enum_select(object, method, options={}, html_options={})
54
+ InstanceTag.new(object, method, self, options.delete(:object)).to_enum_select_tag(options, html_options)
55
+ end
56
+ end
57
+
58
+ class InstanceTag
59
+ def to_enum_select_tag(options, html_options={})
60
+ choices = []
61
+ if self.object.respond_to?(:enums)
62
+ enums = self.object.enums(method_name.to_sym)
63
+ choices = enums ? enums.select_options : []
64
+ if (value = self.object.__send__(method_name.to_sym))
65
+ options[:selected] ||= value.to_s
66
+ else
67
+ options[:include_blank] = enums.allows_nil? if options[:include_blank].nil?
68
+ end
69
+ end
70
+ to_select_tag(choices, options, html_options)
71
+ end
72
+
73
+ #initialize record_name, method, self
74
+ def to_tag_with_enumerated_attribute(options={})
75
+ #look for an enum
76
+ if (column_type == :string &&
77
+ self.object.class.respond_to?(:has_enumerated_attribute?) &&
78
+ self.object.class.has_enumerated_attribute?(method_name.to_sym))
79
+ to_enum_select_tag(options)
80
+ else
81
+ to_tag_without_enumerated_attribute(options)
82
+ end
83
+ end
84
+ alias_method_chain :to_tag, :enumerated_attribute
85
+ end
86
+
87
+ class FormBuilder
88
+ def enum_select(method, options={}, html_options={})
89
+ @template.enum_select(@object_name, method, objectify_options(options), @default_options.merge(html_options))
90
+ end
91
+ end
92
+
93
+ end
94
+ end
95
+ end
96
+
97
+