jeffp-enumerated_attribute 0.1.2 → 0.1.4

Sign up to get free protection for your applications and to get access to all the features.
data/.gitignore CHANGED
@@ -2,4 +2,4 @@
2
2
  pkg
3
3
  rdoc
4
4
  coverage
5
- test/*.log
5
+ *.log
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
- Examples of the usage patterns for some of the above features are shown below.
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 is done simply by adding an 's'. In the case of 'lights' as an
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:test
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
- By default, there are no dependencies.
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.2'
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['test/*.log']
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/**/*_spec.rb']
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
- namespace :spec do
30
- desc "Run all specs"
31
- Spec::Rake::SpecTask.new(:test) do |t|
32
- t.spec_files = FileList['spec/**/*_spec.rb']
33
- t.libs << 'lib' << 'spec'
34
- #t.spec_opts = ['--options', 'spec/spec.opts']
35
- t.rcov = false
36
- #t.rcov_dir = 'coverage'
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
- desc "Run test"
42
- Spec::Rake::SpecTask.new(:test) do |t|
43
- t.spec_files = FileList['spec/**/*_spec.rb']
44
- t.libs << 'lib' << 'spec'
45
- #t.spec_opts = ['--options', 'spec/spec.opts']
46
- t.rcov = false
47
- #t.rcov_dir = 'coverage'
48
- #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']}"]
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
@@ -1,27 +1,9 @@
1
1
  module EnumeratedAttribute
2
2
 
3
- #todo: is_not/is -- test raised errors contain useful info on invalid syntax
4
- #todo: dynamic_enums
5
- #todo: system wide constants
6
- #todo: setter_callback
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(ArgumentError, 'second argument of enumerated_attribute/enum_attr is not an array of symbols or strings representing the enum values', caller) if enums.empty?
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] || "#{attr_name}s"
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
- unless enums.empty?
48
- enums = enums.map{|v| (v =~ /^\^/ ? (initial_value = v[1, v.length-1].to_sym) : v.to_sym )}
49
- class_eval <<-ENUMS
50
- @@enumerated_attribute_values ||= {}
51
- @@enumerated_attribute_values[:#{attr_name}] = [:#{enums.join(',:')}]
52
- ENUMS
53
- end
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
- attr_reader attr_sym unless (opts.key?(:reader) && !opts[:reader])
57
- unless (opts.key?(:writer) && !opts[:writer])
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 @missing_method_for_enumerated_attribute_defined
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
- @missing_method_for_enumerated_attribute_defined = true
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
- unless enums.empty?
145
- class_eval <<-ENUM
146
- def #{plural_name}
147
- @@enumerated_attribute_values[:#{attr_name}]
148
- end
149
- def #{incrementor}
150
- z = @@enumerated_attribute_values[:#{attr_name}]
151
- index = z.index(@#{attr_name})
152
- @#{attr_name} = z[index >= z.size-1 ? 0 : index+1]
153
- end
154
- def #{decrementor}
155
- z = @@enumerated_attribute_values[:#{attr_name}]
156
- index = z.index(@#{attr_name})
157
- @#{attr_name} = z[index > 0 ? index-1 : z.size-1]
158
- end
159
- ENUM
160
- end
161
-
162
- #establish initial value
163
- if (initial_value = opts[:init] || initial_value)
164
- class_eval <<-INITVAL
165
- unless @enumerated_attribute_init
166
- @enumerated_attribute_init = {}
167
- class << self
168
- def new_with_enumerated_attribute(*args)
169
- result = new_without_enumerated_attribute(*args)
170
- @enumerated_attribute_init.each do |k,v|
171
- result.instance_variable_set("@"+k.to_s, v)
172
- end
173
- result
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 = instance_variable_get('@'+attr_name)
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
- def enumerated_attr_writer name, allow_nil=false
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[0] if args.length > 0
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[0] if args.length > 0
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[0]
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(NameError, "'#{value}' in method 'init' is not an enumeration value for :#{@attr_name} attribute", caller)
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 NameError, "'#{meth_name}' should be followed by #{followed_by} -- but #{suffix}"
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(NameError, "'#{mdef.argument}' in method '#{mdef.method_name}' is not an enumeration value for :#{@attr_name} attribute", caller)
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(NameError, "'#{m}' in method '#{mdef.method_name}' is not an enumeration value for :#{@attr_name} attribute", caller)
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