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 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