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.
- data/.gitignore +15 -0
- data/CHANGELOG.rdoc +49 -0
- data/LICENSE +20 -0
- data/README.rdoc +558 -0
- data/Rakefile +30 -0
- data/init.rb +1 -0
- data/lib/enumerated_attribute.rb +24 -0
- data/lib/enumerated_attribute/attribute.rb +79 -0
- data/lib/enumerated_attribute/attribute/arguments.rb +48 -0
- data/lib/enumerated_attribute/attribute/attribute_descriptor.rb +51 -0
- data/lib/enumerated_attribute/attribute/class_methods.rb +45 -0
- data/lib/enumerated_attribute/attribute/instance_methods.rb +96 -0
- data/lib/enumerated_attribute/integrations.rb +33 -0
- data/lib/enumerated_attribute/integrations/active_record.rb +114 -0
- data/lib/enumerated_attribute/integrations/datamapper.rb +6 -0
- data/lib/enumerated_attribute/integrations/default.rb +25 -0
- data/lib/enumerated_attribute/integrations/object.rb +47 -0
- data/lib/enumerated_attribute/method_definition_dsl.rb +142 -0
- data/lib/enumerated_attribute/rails_helpers.rb +99 -0
- data/spec/active_record/active_record_spec.rb +384 -0
- data/spec/active_record/association_test_classes.rb +40 -0
- data/spec/active_record/associations_spec.rb +130 -0
- data/spec/active_record/cfg.rb +6 -0
- data/spec/active_record/df.rb +12 -0
- data/spec/active_record/inheritance_classes.rb +19 -0
- data/spec/active_record/inheritance_spec.rb +60 -0
- data/spec/active_record/race_car.rb +15 -0
- data/spec/active_record/single_table_inheritance_spec.rb +0 -0
- data/spec/active_record/sti_classes.rb +10 -0
- data/spec/active_record/sti_spec.rb +41 -0
- data/spec/active_record/test_in_memory.rb +71 -0
- data/spec/car.rb +67 -0
- data/spec/cfg.rb +8 -0
- data/spec/inheritance_classes.rb +16 -0
- data/spec/inheritance_spec.rb +80 -0
- data/spec/new_and_method_missing_spec.rb +73 -0
- data/spec/plural.rb +8 -0
- data/spec/poro_spec.rb +420 -0
- data/spec/rails/README +243 -0
- data/spec/rails/Rakefile +10 -0
- data/spec/rails/app/controllers/application_controller.rb +10 -0
- data/spec/rails/app/controllers/form_test_controller.rb +38 -0
- data/spec/rails/app/helpers/application_helper.rb +3 -0
- data/spec/rails/app/helpers/form_test_helper.rb +2 -0
- data/spec/rails/app/models/user.rb +9 -0
- data/spec/rails/app/views/form_test/form.html.erb +1 -0
- data/spec/rails/app/views/form_test/form_for.html.erb +10 -0
- data/spec/rails/app/views/form_test/form_tag.html.erb +9 -0
- data/spec/rails/app/views/form_test/index.html.erb +6 -0
- data/spec/rails/app/views/layouts/application.html.erb +11 -0
- data/spec/rails/config/boot.rb +110 -0
- data/spec/rails/config/database.yml +22 -0
- data/spec/rails/config/environment.rb +45 -0
- data/spec/rails/config/environments/development.rb +17 -0
- data/spec/rails/config/environments/production.rb +28 -0
- data/spec/rails/config/environments/test.rb +28 -0
- data/spec/rails/config/initializers/backtrace_silencers.rb +7 -0
- data/spec/rails/config/initializers/inflections.rb +10 -0
- data/spec/rails/config/initializers/mime_types.rb +5 -0
- data/spec/rails/config/initializers/new_rails_defaults.rb +19 -0
- data/spec/rails/config/initializers/session_store.rb +15 -0
- data/spec/rails/config/locales/en.yml +5 -0
- data/spec/rails/config/routes.rb +43 -0
- data/spec/rails/db/development.sqlite3 +0 -0
- data/spec/rails/db/migrate/20090804230221_create_sessions.rb +16 -0
- data/spec/rails/db/migrate/20090804230546_create_users.rb +21 -0
- data/spec/rails/db/schema.rb +35 -0
- data/spec/rails/db/test.sqlite3 +0 -0
- data/spec/rails/public/404.html +30 -0
- data/spec/rails/public/422.html +30 -0
- data/spec/rails/public/500.html +30 -0
- data/spec/rails/public/favicon.ico +0 -0
- data/spec/rails/public/images/rails.png +0 -0
- data/spec/rails/public/index.html +275 -0
- data/spec/rails/public/javascripts/application.js +2 -0
- data/spec/rails/public/javascripts/controls.js +963 -0
- data/spec/rails/public/javascripts/dragdrop.js +973 -0
- data/spec/rails/public/javascripts/effects.js +1128 -0
- data/spec/rails/public/javascripts/prototype.js +4320 -0
- data/spec/rails/public/robots.txt +5 -0
- data/spec/rails/public/stylesheets/scaffold.css +54 -0
- data/spec/rails/script/about +4 -0
- data/spec/rails/script/autospec +6 -0
- data/spec/rails/script/console +3 -0
- data/spec/rails/script/dbconsole +3 -0
- data/spec/rails/script/destroy +3 -0
- data/spec/rails/script/generate +3 -0
- data/spec/rails/script/performance/benchmarker +3 -0
- data/spec/rails/script/performance/profiler +3 -0
- data/spec/rails/script/plugin +3 -0
- data/spec/rails/script/runner +3 -0
- data/spec/rails/script/server +3 -0
- data/spec/rails/script/spec +10 -0
- data/spec/rails/script/spec_server +9 -0
- data/spec/rails/spec/controllers/form_test_controller_spec.rb +41 -0
- data/spec/rails/spec/integrations/enum_select_spec.rb +75 -0
- data/spec/rails/spec/matchers.rb +12 -0
- data/spec/rails/spec/rcov.opts +2 -0
- data/spec/rails/spec/spec.opts +4 -0
- data/spec/rails/spec/spec_helper.rb +40 -0
- data/spec/rails/spec/views/form_test/form.html.erb_spec.rb +12 -0
- data/spec/rails/spec/views/form_test/form_for.html.erb_spec.rb +12 -0
- data/spec/rails/spec/views/form_test/form_tag.html.erb_spec.rb +12 -0
- data/spec/rcov.opts +2 -0
- data/spec/spec.opts +4 -0
- data/spec/tractor.rb +48 -0
- metadata +190 -0
data/Rakefile
ADDED
|
@@ -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
|