jeffp-enumerated_attribute 0.1.2 → 0.1.4
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 +1 -1
- data/CHANGELOG.rdoc +11 -0
- data/README.rdoc +90 -6
- data/Rakefile +20 -24
- data/lib/enumerated_attribute.rb +274 -107
- data/spec/active_record/active_record_spec.rb +244 -0
- data/spec/active_record/cfg.rb +3 -0
- data/spec/active_record/race_car.rb +10 -0
- data/spec/active_record/single_table_inheritance_spec.rb +0 -0
- data/spec/active_record/test_in_memory.rb +21 -0
- data/spec/car.rb +46 -16
- data/spec/new_and_method_missing_spec.rb +73 -0
- data/spec/plural.rb +8 -0
- data/spec/{tractor_spec.rb → poro_spec.rb} +40 -6
- data/spec/tractor.rb +3 -3
- metadata +13 -6
- data/spec/car_spec.rb +0 -21
data/.gitignore
CHANGED
data/CHANGELOG.rdoc
CHANGED
@@ -1,5 +1,16 @@
|
|
1
1
|
== master
|
2
2
|
|
3
|
+
== 0.1.4 / 2009-07-14
|
4
|
+
|
5
|
+
* Removed log files from gem build
|
6
|
+
|
7
|
+
== 0.1.3 / 2009-07-14
|
8
|
+
|
9
|
+
* Adds Ingration module supporting Object and ActiveRecord
|
10
|
+
* Adds extensibility to Integration module
|
11
|
+
* Adds ActiveRecord Integration
|
12
|
+
* Improves pluralization -- supports /(sh|ch|x|s)$/, /y$/, /[aeiou]y$/ and +'s' pluralization
|
13
|
+
|
3
14
|
== 0.1.2 / 2009-07-11
|
4
15
|
|
5
16
|
* Added 'is_not' for negated custom method definitions
|
data/README.rdoc
CHANGED
@@ -31,8 +31,31 @@ Features include:
|
|
31
31
|
* Run-time generated attribute predicates
|
32
32
|
* Attribute initialization
|
33
33
|
* Method definition short-hand (DSL)
|
34
|
+
* ActiveRecord integration
|
34
35
|
|
35
|
-
|
36
|
+
== Example
|
37
|
+
|
38
|
+
Here's a quick example of what +enumerated_attribute+ can do:
|
39
|
+
|
40
|
+
require 'enumerated_attribute'
|
41
|
+
|
42
|
+
class Tractor
|
43
|
+
enum_attr :gear, %w(reverse ^neutral first second over_drive) do
|
44
|
+
driving? [:first, :second, :over_drive]
|
45
|
+
upshift { self.gear_is_not_in_over_drive? ? self.gear_next : self.gear }
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
t = Tractor.new
|
50
|
+
t.gear # => :neutral
|
51
|
+
t.gear_is_in_neutral? # => true
|
52
|
+
t.driving? # => false
|
53
|
+
t.upshift # => :first
|
54
|
+
t.driving? # => true
|
55
|
+
t.gear = :second # => :second
|
56
|
+
t.gear_is_not_in_first? # => true
|
57
|
+
|
58
|
+
An explanation of the above features and their usage follows.
|
36
59
|
|
37
60
|
== Usage
|
38
61
|
|
@@ -193,7 +216,8 @@ and decrementor (ie, +gears+, +gear_next+, +gear_previous+):
|
|
193
216
|
t.lights_dec # => :off
|
194
217
|
|
195
218
|
By default, the plugin uses the plural of the attribute for the accessor method name of the enumeration
|
196
|
-
values. The pluralization
|
219
|
+
values. The pluralization uses a simple algorithm which does not support irregular forms. In
|
220
|
+
the case of 'lights' as an
|
197
221
|
attribute, the default pluralization does not work, so the accessor can be changed using
|
198
222
|
the :plural option. Likewise, the decrementor
|
199
223
|
and incrementor have options :decrementor and :incrementor, or :inc and :dec, for changing
|
@@ -281,6 +305,57 @@ well as a couple predicate methods. +upshift+ increments the gear attribute unt
|
|
281
305
|
it reaches over_drive and does not allow a wrap around. +downshift+ decrements
|
282
306
|
until the attribute reaches neutral.
|
283
307
|
|
308
|
+
=== Integration
|
309
|
+
|
310
|
+
==== ActiveRecord integration
|
311
|
+
|
312
|
+
The plugin can be used with ActiveRecord. Enumerated attributes may be declared on
|
313
|
+
column attributes or as independent enumerations. Declaring an enumerated attribute
|
314
|
+
on a column attribute will enforce the enumeration using symbols. The
|
315
|
+
enumerated column attribute must be declared as a STRING in the database schema.
|
316
|
+
The enumerated attribute will be stored as a string but retrieved in code as a symbol. The
|
317
|
+
enumeration functionality is consistent across integrations.
|
318
|
+
|
319
|
+
require 'enumerated_attribute'
|
320
|
+
require 'active_record'
|
321
|
+
|
322
|
+
class Order < ActiveRecord::Base
|
323
|
+
enum_attr :status, %w(^hold, processing, delayed, shipped)
|
324
|
+
enum_attr :billing_status,
|
325
|
+
%w(^unauthorized, authorized, auth_failed, captured, capture_failed, closed)
|
326
|
+
end
|
327
|
+
|
328
|
+
o = Order.new
|
329
|
+
o.status # => :hold
|
330
|
+
o.billing_status # => :unauthorized
|
331
|
+
o.save!
|
332
|
+
|
333
|
+
o = Order.new(:invoice=>'43556334-W84', :status=>:processing, :billing=>:authorized)
|
334
|
+
o.save!
|
335
|
+
o.status # => :processing
|
336
|
+
o.invoice # => "43556334-W84"
|
337
|
+
|
338
|
+
|
339
|
+
=== Implementation Notes
|
340
|
+
|
341
|
+
==== New and Method_missing methods
|
342
|
+
|
343
|
+
The plugin chains both the 'new' and the 'method_missing' methods. Any 'new' and 'method_missing'
|
344
|
+
implementations in the same class declaring an enumerated_attribute should come before the
|
345
|
+
declaration; otherwise, the 'new' and 'method_missing' implementations must chain in order to avoid
|
346
|
+
overwriting the plugin's methods. The best approach is shown here:
|
347
|
+
|
348
|
+
class A
|
349
|
+
def self.new(*args)
|
350
|
+
...
|
351
|
+
end
|
352
|
+
def self.method_missing(methId, *args, &blk)
|
353
|
+
...
|
354
|
+
end
|
355
|
+
|
356
|
+
enum_attr :state, %w(cold warm hot boiling)
|
357
|
+
end
|
358
|
+
|
284
359
|
|
285
360
|
== Testing
|
286
361
|
|
@@ -288,12 +363,21 @@ The plugin uses RSpec for testing. Make sure you have the RSpec gem installed:
|
|
288
363
|
|
289
364
|
gem install rspec
|
290
365
|
|
291
|
-
To test the plugin, run:
|
366
|
+
To test the plugin for regular ruby objects, run:
|
292
367
|
|
293
|
-
rake spec
|
368
|
+
rake spec
|
369
|
+
|
370
|
+
Testing ActiveRecord integration requires the install of Sqlite3 and the
|
371
|
+
sqlite3-ruby gem. To test ActiveRecord, run:
|
294
372
|
|
373
|
+
rake spec:active_record
|
374
|
+
|
375
|
+
To test all specs:
|
376
|
+
|
377
|
+
rake spec:all
|
295
378
|
|
296
|
-
== Dependencies
|
297
379
|
|
298
|
-
|
380
|
+
== Dependencies
|
299
381
|
|
382
|
+
* ActiveRecord
|
383
|
+
* Sqlite3 and sqlite3-ruby gem (for testing)
|
data/Rakefile
CHANGED
@@ -5,47 +5,43 @@ require 'rake/contrib/sshpublisher'
|
|
5
5
|
|
6
6
|
spec = Gem::Specification.new do |s|
|
7
7
|
s.name = 'enumerated_attribute'
|
8
|
-
s.version = '0.1.
|
8
|
+
s.version = '0.1.4'
|
9
9
|
s.platform = Gem::Platform::RUBY
|
10
10
|
s.description = 'A enumerated attribute accessor'
|
11
11
|
s.summary = 'Defines enumerated attributes, initial state and dynamic state methods.'
|
12
12
|
|
13
|
-
s.files = FileList['{examples,lib,tasks,spec}/**/*'] + %w(CHANGELOG.rdoc init.rb LICENSE Rakefile README.rdoc .gitignore) - FileList['
|
13
|
+
s.files = FileList['{examples,lib,tasks,spec}/**/*'] + %w(CHANGELOG.rdoc init.rb LICENSE Rakefile README.rdoc .gitignore) - FileList['**/*.log']
|
14
14
|
s.require_path = 'lib'
|
15
15
|
s.has_rdoc = true
|
16
|
-
s.test_files = Dir['spec
|
16
|
+
s.test_files = Dir['spec/*_spec.rb']
|
17
17
|
|
18
18
|
s.author = 'Jeff Patmon'
|
19
19
|
s.email = 'jpatmon@yahoo.com'
|
20
20
|
s.homepage = 'http://www.jpatmon.com'
|
21
21
|
end
|
22
22
|
|
23
|
-
desc 'Default: run all tests.'
|
24
|
-
task :default => :test
|
25
|
-
|
26
23
|
require 'spec/version'
|
27
24
|
require 'spec/rake/spectask'
|
28
25
|
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
#t.rcov_opts = ['--exclude', "kernel,load-diff-lcs\.rb,instance_exec\.rb,lib/spec.rb,lib/spec/runner.rb,^spec/*,bin/spec,examples,/gems,/Library/Ruby,\.autotest,#{ENV['GEM_HOME']}"]
|
38
|
-
end
|
26
|
+
desc "Run specs"
|
27
|
+
Spec::Rake::SpecTask.new(:spec) do |t|
|
28
|
+
t.spec_files = FileList['spec/*_spec.rb']
|
29
|
+
t.libs << 'lib' << 'spec'
|
30
|
+
t.rcov = false
|
31
|
+
#t.spec_opts = ['--options', 'spec/spec.opts']
|
32
|
+
#t.rcov_dir = 'coverage'
|
33
|
+
#t.rcov_opts = ['--exclude', "kernel,load-diff-lcs\.rb,instance_exec\.rb,lib/spec.rb,lib/spec/runner.rb,^spec/*,bin/spec,examples,/gems,/Library/Ruby,\.autotest,#{ENV['GEM_HOME']}"]
|
39
34
|
end
|
40
35
|
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
36
|
+
namespace :spec do
|
37
|
+
desc "Run ActiveRecord integration specs"
|
38
|
+
Spec::Rake::SpecTask.new(:active_record) do |t|
|
39
|
+
t.spec_files = FileList['spec/active_record/*_spec.rb']
|
40
|
+
t.libs << 'lib' << 'spec/active_record'
|
41
|
+
t.rcov = false
|
42
|
+
end
|
43
|
+
desc "Run all specs"
|
44
|
+
task :all=>[:spec, :active_record]
|
49
45
|
end
|
50
46
|
|
51
47
|
=begin
|
data/lib/enumerated_attribute.rb
CHANGED
@@ -1,27 +1,9 @@
|
|
1
1
|
module EnumeratedAttribute
|
2
2
|
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
#todo: ArgumentError may need to use Errors for ActiveRecord
|
8
|
-
#todo: test new chaining plays nice
|
9
|
-
#todo: attribute methods gear.enums, gear.inc, gear.dec
|
10
|
-
|
11
|
-
def enum_attr_reader(*args, &block)
|
12
|
-
if args.length > 1
|
13
|
-
args << {} if args.length == 2
|
14
|
-
args[2][:writer] = false if args[2].kind_of?(Hash)
|
15
|
-
end
|
16
|
-
enumerated_attribute(*args, &block)
|
17
|
-
end
|
18
|
-
def enum_attr_writer(*args, &block)
|
19
|
-
if args.length > 1
|
20
|
-
args << {} if args.length == 2
|
21
|
-
args[2][:reader] = false if args[2].kind_of?(Hash)
|
22
|
-
end
|
23
|
-
enumerated_attribute(*args, &block)
|
24
|
-
end
|
3
|
+
class EnumeratedAttributeError < StandardError; end
|
4
|
+
class IntegrationError < EnumeratedAttributeError; end
|
5
|
+
class InvalidEnumeration < EnumeratedAttributeError; end
|
6
|
+
class InvalidDefinition < EnumeratedAttributeError; end
|
25
7
|
|
26
8
|
def enumerated_attribute(*args, &block)
|
27
9
|
return if args.empty?
|
@@ -31,40 +13,66 @@ module EnumeratedAttribute
|
|
31
13
|
index = enums.empty? ? 1 : 2
|
32
14
|
opts = (args[index] && args[index].instance_of?(Hash) ? args[index] : {})
|
33
15
|
|
34
|
-
raise(
|
16
|
+
raise(InvalidDefinition, 'second argument of enumerated_attribute/enum_attr is not an array of symbols or strings representing the enum values', caller) if enums.empty?
|
35
17
|
|
36
|
-
#todo: better pluralization of attribute
|
37
18
|
initial_value = nil
|
38
|
-
plural_name = opts[:plural] || opts[:enums_accessor] || opts[:enums] ||
|
19
|
+
plural_name = opts[:plural] || opts[:enums_accessor] || opts[:enums] || begin
|
20
|
+
case
|
21
|
+
when attr_name =~ /[aeiou]y$/
|
22
|
+
"#{attr_name}s"
|
23
|
+
when attr_name =~ /y$/
|
24
|
+
attr_name.sub(/y$/, 'ies')
|
25
|
+
when attr_name =~ /(sh|ch|x|s)$/
|
26
|
+
"#{attr_name}es"
|
27
|
+
else
|
28
|
+
"#{attr_name}s"
|
29
|
+
end
|
30
|
+
end
|
39
31
|
incrementor = opts[:incrementor] || opts[:inc] || "#{attr_name}_next"
|
40
32
|
decrementor = opts[:decrementor] || opts[:dec] || "#{attr_name}_previous"
|
41
|
-
|
33
|
+
|
34
|
+
enums = enums.map{|v| (v =~ /^\^/ ? (initial_value = v[1, v.length-1].to_sym) : v.to_sym )}
|
35
|
+
|
42
36
|
class_eval <<-ATTRIB
|
43
37
|
@@enumerated_attribute_names ||= []
|
44
38
|
@@enumerated_attribute_names << '#{attr_name}'
|
39
|
+
@@enumerated_attribute_options ||={}
|
40
|
+
@@enumerated_attribute_options[:#{attr_name}] = {#{opts.to_a.map{|v| ':'+v.first.to_s+'=>:'+v.last.to_s}.join(', ')}}
|
41
|
+
@@enumerated_attribute_values ||= {}
|
42
|
+
@@enumerated_attribute_values[:#{attr_name}] = [:#{enums.join(',:')}]
|
45
43
|
ATTRIB
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
44
|
+
|
45
|
+
#define_enumerated_attribute_[writer, reader] may be modified in a named Integrations module (see Integrations::ActiveRecord)
|
46
|
+
class_eval <<-MAP
|
47
|
+
unless @integration_map
|
48
|
+
@integration_map = Integrations.find_integration_map(self)
|
49
|
+
@integration_map[:aliasing].each do |p|
|
50
|
+
alias_method(p.first, p.last)
|
51
|
+
end
|
52
|
+
include(@integration_map[:module]) if @integration_map[:module]
|
53
|
+
|
54
|
+
def self.has_enumerated_attribute?(name)
|
55
|
+
@@enumerated_attribute_names.include?(name.to_s)
|
56
|
+
end
|
57
|
+
def self.enumerated_attribute_allows_nil?(name)
|
58
|
+
return (false) unless @@enumerated_attribute_options[name.to_sym]
|
59
|
+
@@enumerated_attribute_options[name.to_sym][:nil] || false
|
60
|
+
end
|
61
|
+
def self.enumerated_attribute_allows_value?(name, value)
|
62
|
+
return (false) unless @@enumerated_attribute_values[name.to_sym]
|
63
|
+
return enumerated_attribute_allows_nil?(name) if value == nil
|
64
|
+
@@enumerated_attribute_values[name.to_sym].include?(value.to_sym)
|
65
|
+
end
|
66
|
+
end
|
67
|
+
MAP
|
68
|
+
|
55
69
|
#create accessors
|
56
|
-
|
57
|
-
|
58
|
-
if enums.empty?
|
59
|
-
attr_writer attr_sym
|
60
|
-
else
|
61
|
-
enumerated_attr_writer(attr_sym, opts[:nil] || false)
|
62
|
-
end
|
63
|
-
end
|
70
|
+
define_enumerated_attribute_reader_method(attr_sym) unless (opts.key?(:reader) && !opts[:reader])
|
71
|
+
define_enumerated_attribute_writer_method(attr_sym) unless (opts.key?(:writer) && !opts[:writer])
|
64
72
|
|
65
73
|
#define dynamic methods in method_missing
|
66
74
|
class_eval <<-METHOD
|
67
|
-
unless @
|
75
|
+
unless @enumerated_attribute_define_once_only
|
68
76
|
if method_defined?(:method_missing)
|
69
77
|
alias_method(:method_missing_without_enumerated_attribute, :method_missing)
|
70
78
|
def method_missing(methId, *args, &block)
|
@@ -77,13 +85,19 @@ module EnumeratedAttribute
|
|
77
85
|
super
|
78
86
|
end
|
79
87
|
end
|
80
|
-
@
|
88
|
+
@enumerated_attribute_define_once_only = true
|
81
89
|
|
82
90
|
alias_method :respond_to_without_enumerated_attribute?, :respond_to?
|
83
91
|
def respond_to?(method)
|
84
92
|
respond_to_without_enumerated_attribute?(method) || !!parse_dynamic_method_parts(method.to_s)
|
85
|
-
end
|
86
|
-
|
93
|
+
end
|
94
|
+
|
95
|
+
def initialize_enumerated_attributes(only_if_nil = false)
|
96
|
+
self.class.enumerated_attribute_initial_value_list.each do |k,v|
|
97
|
+
self.write_enumerated_attribute(k, v) unless (only_if_nil && read_enumerated_attribute(k) != nil)
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
87
101
|
private
|
88
102
|
|
89
103
|
def parse_dynamic_method_parts(meth_name)
|
@@ -126,6 +140,7 @@ module EnumeratedAttribute
|
|
126
140
|
|
127
141
|
true
|
128
142
|
end
|
143
|
+
|
129
144
|
end
|
130
145
|
METHOD
|
131
146
|
|
@@ -141,74 +156,226 @@ module EnumeratedAttribute
|
|
141
156
|
end
|
142
157
|
|
143
158
|
#define the enum values accessor
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
end
|
175
|
-
alias_method :new_without_enumerated_attribute, :new
|
176
|
-
alias_method :new, :new_with_enumerated_attribute
|
177
|
-
end
|
178
|
-
end
|
179
|
-
@enumerated_attribute_init[:#{attr_name}] = :#{initial_value}
|
180
|
-
INITVAL
|
181
|
-
end
|
182
|
-
|
183
|
-
end
|
184
|
-
|
159
|
+
class_eval <<-ENUM
|
160
|
+
def #{plural_name}
|
161
|
+
@@enumerated_attribute_values[:#{attr_name}]
|
162
|
+
end
|
163
|
+
def #{incrementor}
|
164
|
+
z = @@enumerated_attribute_values[:#{attr_name}]
|
165
|
+
index = z.index(@#{attr_name})
|
166
|
+
write_enumerated_attribute(:#{attr_name}, z[index >= z.size-1 ? 0 : index+1])
|
167
|
+
end
|
168
|
+
def #{decrementor}
|
169
|
+
z = @@enumerated_attribute_values[:#{attr_name}]
|
170
|
+
index = z.index(@#{attr_name})
|
171
|
+
write_enumerated_attribute(:#{attr_name}, z[index > 0 ? index-1 : z.size-1])
|
172
|
+
end
|
173
|
+
ENUM
|
174
|
+
|
175
|
+
unless @enumerated_attribute_init
|
176
|
+
define_enumerated_attribute_new_method
|
177
|
+
end
|
178
|
+
@enumerated_attribute_init ||= {}
|
179
|
+
if (initial_value = opts[:init] || initial_value)
|
180
|
+
@enumerated_attribute_init[attr_sym] = initial_value
|
181
|
+
end
|
182
|
+
class_eval do
|
183
|
+
class << self
|
184
|
+
def enumerated_attribute_initial_value_list; @enumerated_attribute_init; end
|
185
|
+
end
|
186
|
+
end
|
187
|
+
end
|
188
|
+
|
185
189
|
#a short cut
|
186
190
|
alias :enum_attr :enumerated_attribute
|
187
191
|
|
188
192
|
def define_enumerated_attribute_custom_method(symbol, attr_name, value, negated)
|
189
193
|
define_method symbol do
|
190
|
-
ival =
|
194
|
+
ival = read_enumerated_attribute(attr_name)
|
191
195
|
negated ? ival != value : ival == value
|
192
196
|
end
|
193
197
|
end
|
194
198
|
|
195
199
|
private
|
196
|
-
|
197
|
-
|
200
|
+
|
201
|
+
#these implementations are for basic ruby objects - integrations (see Integrations::ActiveRecord) may alter them
|
202
|
+
def define_enumerated_attribute_new_method
|
203
|
+
class_eval <<-NEWMETH
|
204
|
+
class << self
|
205
|
+
alias_method :new_without_enumerated_attribute, :new
|
206
|
+
def new(*args, &block)
|
207
|
+
result = new_without_enumerated_attribute(*args)
|
208
|
+
result.initialize_enumerated_attributes
|
209
|
+
yield result if block_given?
|
210
|
+
result
|
211
|
+
end
|
212
|
+
end
|
213
|
+
NEWMETH
|
214
|
+
end
|
215
|
+
|
216
|
+
def define_enumerated_attribute_writer_method name
|
198
217
|
name = name.to_s
|
199
218
|
class_eval <<-METHOD
|
200
|
-
def #{name}=(val)
|
201
|
-
val = val.to_sym if val.instance_of?(String)
|
202
|
-
unless (val == nil && #{allow_nil})
|
203
|
-
raise(ArgumentError,
|
204
|
-
(val == nil ? "nil is not allowed on #{name} attribute, set :nil=>true option" : "'" + val.to_s + "' is not an enumeration value for #{name} attribute"),
|
205
|
-
caller) unless @@enumerated_attribute_values[:#{name}].include?(val)
|
206
|
-
end
|
207
|
-
@#{name} = val
|
208
|
-
end
|
219
|
+
def #{name}=(val); write_enumerated_attribute(:#{name}, val); end
|
209
220
|
METHOD
|
210
221
|
end
|
211
222
|
|
223
|
+
def define_enumerated_attribute_reader_method name
|
224
|
+
name = name.to_s
|
225
|
+
class_eval <<-METHOD
|
226
|
+
def #{name}; read_enumerated_attribute(:#{name}); end
|
227
|
+
METHOD
|
228
|
+
end
|
229
|
+
|
230
|
+
module Integrations
|
231
|
+
module Object
|
232
|
+
def self.included(klass)
|
233
|
+
klass.extend(ClassMethods)
|
234
|
+
end
|
235
|
+
|
236
|
+
def write_enumerated_attribute(name, val)
|
237
|
+
name = name.to_s
|
238
|
+
val = val.to_sym if val
|
239
|
+
unless self.class.enumerated_attribute_allows_value?(name, val)
|
240
|
+
raise(InvalidEnumeration, "nil is not allowed on '#{name}' attribute, set :nil=>true option", caller) unless val
|
241
|
+
raise(InvalidEnumeration, ":#{val} is not a defined enumeration value for the '#{name}' attribute", caller)
|
242
|
+
end
|
243
|
+
instance_variable_set('@'+name, val)
|
244
|
+
end
|
245
|
+
|
246
|
+
def read_enumerated_attribute(name)
|
247
|
+
return instance_variable_get('@'+name.to_s)
|
248
|
+
end
|
249
|
+
|
250
|
+
module ClassMethods
|
251
|
+
end
|
252
|
+
|
253
|
+
end
|
254
|
+
|
255
|
+
module ActiveRecord
|
256
|
+
def self.included(klass)
|
257
|
+
klass.extend(ClassMethods)
|
258
|
+
end
|
259
|
+
|
260
|
+
def write_enumerated_attribute(name, val)
|
261
|
+
name = name.to_s
|
262
|
+
return write_attribute(name, val) unless self.class.has_enumerated_attribute?(name)
|
263
|
+
val_str = val.to_s if val
|
264
|
+
val_sym = val.to_sym if val
|
265
|
+
unless self.class.enumerated_attribute_allows_value?(name, val_sym)
|
266
|
+
raise(InvalidEnumeration, "nil is not allowed on '#{name}' attribute, set :nil=>true option", caller) unless val
|
267
|
+
raise(InvalidEnumeration, ":#{val_str} is not a defined enumeration value for the '#{name}' attribute", caller)
|
268
|
+
end
|
269
|
+
return instance_variable_set('@'+name, val_sym) unless self.has_attribute?(name)
|
270
|
+
write_attribute(name, val_str)
|
271
|
+
end
|
272
|
+
|
273
|
+
def read_enumerated_attribute(name)
|
274
|
+
name = name.to_s
|
275
|
+
#if not enumerated - let active record handle it
|
276
|
+
return read_attribute(name) unless self.class.has_enumerated_attribute?(name)
|
277
|
+
#if enumerated, is it an active record attribute, if not, the value is stored in an instance variable
|
278
|
+
return instance_variable_get('@'+name) unless self.has_attribute?(name)
|
279
|
+
#this is an enumerated active record attribute
|
280
|
+
val = read_attribute(name)
|
281
|
+
val = val.to_sym if (!!val && self.class.has_enumerated_attribute?(name))
|
282
|
+
val
|
283
|
+
end
|
284
|
+
|
285
|
+
def attributes=(attrs, guard_protected_attributes=true)
|
286
|
+
return if attrs.nil?
|
287
|
+
#check the attributes then turn them over
|
288
|
+
attrs.each do |k, v|
|
289
|
+
if self.class.has_enumerated_attribute?(k)
|
290
|
+
unless self.class.enumerated_attribute_allows_value?(k, v)
|
291
|
+
raise InvalidEnumeration, ":#{v.to_s} is not a defined enumeration value for the '#{k.to_s}' attribute", caller
|
292
|
+
end
|
293
|
+
attrs[k] = v.to_s
|
294
|
+
end
|
295
|
+
end
|
296
|
+
|
297
|
+
super
|
298
|
+
end
|
299
|
+
|
300
|
+
def attributes
|
301
|
+
super.map do |k,v|
|
302
|
+
self.class.has_enumerated_attribute?(k) ? v.to_sym : v
|
303
|
+
end
|
304
|
+
end
|
305
|
+
|
306
|
+
def [](attr_name); read_enumerated_attribute(attr_name); end
|
307
|
+
def []=(attr_name, value); write_enumerated_attribute(attr_name, value); end
|
308
|
+
|
309
|
+
private
|
310
|
+
|
311
|
+
def attribute=(attr_name, value); write_enumerated_attribute(attr_name, value); end
|
312
|
+
|
313
|
+
module ClassMethods
|
314
|
+
private
|
315
|
+
|
316
|
+
def construct_attributes_from_arguments(attribute_names, arguments)
|
317
|
+
attributes = super
|
318
|
+
attributes.each { |k,v| attributes[k] = v.to_s if has_enumerated_attribute?(k) }
|
319
|
+
attributes
|
320
|
+
end
|
321
|
+
|
322
|
+
def instantiate(record)
|
323
|
+
object = super(record)
|
324
|
+
@enumerated_attribute_init.each do |k,v|
|
325
|
+
unless object.has_attribute?(k)
|
326
|
+
object.write_enumerated_attribute(k, v)
|
327
|
+
end
|
328
|
+
end
|
329
|
+
object
|
330
|
+
end
|
331
|
+
|
332
|
+
def define_enumerated_attribute_new_method
|
333
|
+
class_eval <<-INITVAL
|
334
|
+
class << self
|
335
|
+
alias_method :new_without_enumerated_attribute, :new
|
336
|
+
def new(*args, &block)
|
337
|
+
result = new_without_enumerated_attribute(*args, &block)
|
338
|
+
params = (!args.empty? && args.first.instance_of?(Hash)) ? args.first : {}
|
339
|
+
params.each { |k, v| result.write_enumerated_attribute(k, v) }
|
340
|
+
result.initialize_enumerated_attributes(true)
|
341
|
+
yield result if block_given?
|
342
|
+
result
|
343
|
+
end
|
344
|
+
end
|
345
|
+
INITVAL
|
346
|
+
end
|
347
|
+
|
348
|
+
end
|
349
|
+
end
|
350
|
+
|
351
|
+
module Datamapper
|
352
|
+
end
|
353
|
+
|
354
|
+
@@integration_map = {}
|
355
|
+
|
356
|
+
def self.add_integration_map(base_klass_name, module_object, aliasing_array=[])
|
357
|
+
@@integration_map[base_klass_name] = {:module=>module_object, :aliasing=>aliasing_array}
|
358
|
+
end
|
359
|
+
class << self
|
360
|
+
alias_method(:add, :add_integration_map)
|
361
|
+
end
|
362
|
+
|
363
|
+
#included mappings
|
364
|
+
add('Object', EnumeratedAttribute::Integrations::Object)
|
365
|
+
add('ActiveRecord::Base', EnumeratedAttribute::Integrations::ActiveRecord)
|
366
|
+
|
367
|
+
def self.find_integration_map(klass)
|
368
|
+
path = "#{klass}"
|
369
|
+
begin
|
370
|
+
return @@integration_map[klass.to_s] if @@integration_map.key?(klass.to_s)
|
371
|
+
klass = klass.superclass
|
372
|
+
path << " < #{klass}"
|
373
|
+
end while klass
|
374
|
+
raise EnumeratedAttribute::IntegrationError, "Unable to find integration for class hierarchy '#{path}'", caller
|
375
|
+
end
|
376
|
+
|
377
|
+
end
|
378
|
+
|
212
379
|
public
|
213
380
|
|
214
381
|
class MethodDefinition
|
@@ -246,13 +413,13 @@ module EnumeratedAttribute
|
|
246
413
|
end
|
247
414
|
|
248
415
|
def is_not(*args)
|
249
|
-
arg = args
|
416
|
+
arg = args.first if args.length > 0
|
250
417
|
MethodDefinition.new(nil, arg, true)
|
251
418
|
end
|
252
419
|
alias :isnt :is_not
|
253
420
|
|
254
421
|
def is(*args)
|
255
|
-
arg = args
|
422
|
+
arg = args.first if args.length > 0
|
256
423
|
MethodDefinition.new(nil, arg)
|
257
424
|
end
|
258
425
|
|
@@ -261,7 +428,7 @@ module EnumeratedAttribute
|
|
261
428
|
|
262
429
|
meth_def = nil
|
263
430
|
if args.size > 0
|
264
|
-
arg = args
|
431
|
+
arg = args.first
|
265
432
|
if arg.instance_of?(EnumeratedAttribute::MethodDefinition)
|
266
433
|
if arg.has_method_name?
|
267
434
|
raise_method_syntax_error(meth_name, arg.method_name)
|
@@ -282,7 +449,7 @@ module EnumeratedAttribute
|
|
282
449
|
|
283
450
|
def init(value)
|
284
451
|
if (!@attr_values.empty? && !@attr_values.include?(value.to_sym))
|
285
|
-
raise(
|
452
|
+
raise(InvalidDefinition, "'#{value}' in method 'init' is not an enumeration value for :#{@attr_name} attribute", caller)
|
286
453
|
end
|
287
454
|
@initial_value = value
|
288
455
|
end
|
@@ -300,7 +467,7 @@ module EnumeratedAttribute
|
|
300
467
|
def raise_method_syntax_error(meth_name, offending_token=nil)
|
301
468
|
suffix = offending_token ? "found '#{offending_token}'" : "found nothing"
|
302
469
|
followed_by = (meth_name[-1,1] == '?' ? "is_not, an enumeration value, an array of enumeration values, " : "") + "a proc, lambda or code block"
|
303
|
-
raise
|
470
|
+
raise InvalidDefinition, "'#{meth_name}' should be followed by #{followed_by} -- but #{suffix}"
|
304
471
|
end
|
305
472
|
|
306
473
|
def evaluate_method_definition(mdef)
|
@@ -333,7 +500,7 @@ module EnumeratedAttribute
|
|
333
500
|
|
334
501
|
def create_custom_method_for_symbol_or_string(mdef)
|
335
502
|
if (!@attr_values.empty? && !@attr_values.include?(mdef.argument.to_sym))
|
336
|
-
raise(
|
503
|
+
raise(InvalidDefinition, "'#{mdef.argument}' in method '#{mdef.method_name}' is not an enumeration value for :#{@attr_name} attribute", caller)
|
337
504
|
end
|
338
505
|
@class_obj.class_eval("def #{mdef.method_name}; @#{@attr_name} #{mdef.negated ? '!=' : '=='} :#{mdef.argument}; end")
|
339
506
|
end
|
@@ -342,7 +509,7 @@ module EnumeratedAttribute
|
|
342
509
|
if !@attr_values.empty?
|
343
510
|
mdef.argument.each do |m|
|
344
511
|
if !@attr_values.include?(m.to_sym)
|
345
|
-
raise(
|
512
|
+
raise(InvalidDefinition, "'#{m}' in method '#{mdef.method_name}' is not an enumeration value for :#{@attr_name} attribute", caller)
|
346
513
|
end
|
347
514
|
end
|
348
515
|
end
|