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