power_enum 0.8.6 → 0.9.1
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/{README.md → README.markdown} +315 -157
- data/lib/active_record/virtual_enumerations.rb +132 -48
- data/lib/power_enum/enumerated.rb +405 -0
- data/lib/power_enum/has_enumerated.rb +234 -0
- data/lib/power_enum.rb +18 -2
- metadata +7 -8
- data/examples/virtual_enumerations_sample.rb +0 -76
- data/lib/active_record/acts/enumerated.rb +0 -395
- data/lib/active_record/aggregations/has_enumerated.rb +0 -238
@@ -1,395 +0,0 @@
|
|
1
|
-
# Copyright (c) 2005 Trevor Squires
|
2
|
-
# Copyright (c) 2012 Arthur Shagall
|
3
|
-
# Released under the MIT License. See the LICENSE file for more details.
|
4
|
-
|
5
|
-
module ActiveRecord
|
6
|
-
module Acts
|
7
|
-
module Enumerated
|
8
|
-
extend ActiveSupport::Concern
|
9
|
-
|
10
|
-
module ClassMethods
|
11
|
-
|
12
|
-
# Returns false for ActiveRecord models that do not act as enumerated.
|
13
|
-
def acts_as_enumerated?
|
14
|
-
false
|
15
|
-
end
|
16
|
-
|
17
|
-
# Declares the model as enumerated. See the README for detailed usage instructions.
|
18
|
-
#
|
19
|
-
# === Supported options
|
20
|
-
# [:conditions]
|
21
|
-
# SQL search conditions
|
22
|
-
# [:order]
|
23
|
-
# SQL load order clause
|
24
|
-
# [:on_lookup_failure]
|
25
|
-
# Specifies the name of a class method to invoke when the +[]+ method is unable to locate a BookingStatus
|
26
|
-
# record for arg. The default is the built-in :enforce_none which returns nil. There are also built-ins for
|
27
|
-
# :enforce_strict (raise and exception regardless of the type for arg), :enforce_strict_literals (raises an
|
28
|
-
# exception if the arg is a Fixnum or Symbol), :enforce_strict_ids (raises and exception if the arg is a
|
29
|
-
# Fixnum) and :enforce_strict_symbols (raises an exception if the arg is a Symbol). The purpose of the
|
30
|
-
# :on_lookup_failure option is that a) under some circumstances a lookup failure is a Bad Thing and action
|
31
|
-
# should be taken, therefore b) a fallback action should be easily configurable. You can also give it a
|
32
|
-
# lambda that takes in a single argument (The arg that was passed to +[]+).
|
33
|
-
# [:name_column]
|
34
|
-
# Override for the 'name' column. By default, assumed to be 'name'.
|
35
|
-
#
|
36
|
-
# === Examples
|
37
|
-
#
|
38
|
-
# ====Example 1
|
39
|
-
# class BookingStatus < ActiveRecord::Base
|
40
|
-
# acts_as_enumerated
|
41
|
-
# end
|
42
|
-
#
|
43
|
-
# ====Example 2
|
44
|
-
# class BookingStatus < ActiveRecord::Base
|
45
|
-
# acts_as_enumerated :on_lookup_failure => :enforce_strict
|
46
|
-
# end
|
47
|
-
#
|
48
|
-
# ====Example 3
|
49
|
-
# class BookingStatus < ActiveRecord::Base
|
50
|
-
# acts_as_enumerated :conditions => [:exclude => false],
|
51
|
-
# :order => 'created_at DESC',
|
52
|
-
# :on_lookup_failure => :lookup_failed,
|
53
|
-
# :name_column => :status_code
|
54
|
-
#
|
55
|
-
# def self.lookup_failed(arg)
|
56
|
-
# logger.error("Invalid status code lookup #{arg.inspect}")
|
57
|
-
# nil
|
58
|
-
# end
|
59
|
-
# end
|
60
|
-
#
|
61
|
-
# ====Example 4
|
62
|
-
# class BookingStatus < ActiveRecord::Base
|
63
|
-
# acts_as_enumerated :conditions => [:exclude => false],
|
64
|
-
# :order => 'created_at DESC',
|
65
|
-
# :on_lookup_failure => lambda { |arg| raise CustomError, "BookingStatus lookup failed; #{arg}" },
|
66
|
-
# :name_column => :status_code
|
67
|
-
# end
|
68
|
-
def acts_as_enumerated(options = {})
|
69
|
-
valid_keys = [:conditions, :order, :on_lookup_failure, :name_column]
|
70
|
-
options.assert_valid_keys(*valid_keys)
|
71
|
-
|
72
|
-
valid_keys.each do |key|
|
73
|
-
class_attribute "acts_enumerated_#{key.to_s}"
|
74
|
-
if options.has_key?( key )
|
75
|
-
self.send "acts_enumerated_#{key.to_s}=", options[key]
|
76
|
-
end
|
77
|
-
end
|
78
|
-
|
79
|
-
name_column = if options.has_key?(:name_column) then
|
80
|
-
options[:name_column].to_s.to_sym
|
81
|
-
else
|
82
|
-
:name
|
83
|
-
end
|
84
|
-
|
85
|
-
class_attribute :acts_enumerated_name_column
|
86
|
-
self.acts_enumerated_name_column = name_column
|
87
|
-
|
88
|
-
unless self.is_a? ActiveRecord::Acts::Enumerated::EnumClassMethods
|
89
|
-
extend ActiveRecord::Acts::Enumerated::EnumClassMethods
|
90
|
-
|
91
|
-
class_eval do
|
92
|
-
include ActiveRecord::Acts::Enumerated::EnumInstanceMethods
|
93
|
-
|
94
|
-
before_save :enumeration_model_update
|
95
|
-
before_destroy :enumeration_model_update
|
96
|
-
validates name_column, :presence => true, :uniqueness => true
|
97
|
-
|
98
|
-
define_method :name do
|
99
|
-
read_attribute( name_column )
|
100
|
-
end
|
101
|
-
end
|
102
|
-
end
|
103
|
-
end
|
104
|
-
end
|
105
|
-
|
106
|
-
module EnumClassMethods
|
107
|
-
attr_accessor :enumeration_model_updates_permitted
|
108
|
-
|
109
|
-
# Returns true for ActiveRecord models that act as enumerated.
|
110
|
-
def acts_as_enumerated?
|
111
|
-
true
|
112
|
-
end
|
113
|
-
|
114
|
-
# Returns all the enum values. Caches results after the first time this method is run.
|
115
|
-
def all
|
116
|
-
return @all if @all
|
117
|
-
conditions = self.acts_enumerated_conditions
|
118
|
-
order = self.acts_enumerated_order
|
119
|
-
@all = where(conditions).order(order).collect{|val| val.freeze}.freeze
|
120
|
-
end
|
121
|
-
|
122
|
-
# Returns all the active enum values. See the 'active?' instance method.
|
123
|
-
def active
|
124
|
-
return @all_active if @all_active
|
125
|
-
@all_active = all.select{ |enum| enum.active? }.freeze
|
126
|
-
end
|
127
|
-
|
128
|
-
# Returns all the inactive enum values. See the 'inactive?' instance method.
|
129
|
-
def inactive
|
130
|
-
return @all_inactive if @all_inactive
|
131
|
-
@all_inactive = all.select{ |enum| !enum.active? }.freeze
|
132
|
-
end
|
133
|
-
|
134
|
-
# Returns the names of all the enum values as an array of symbols.
|
135
|
-
def names
|
136
|
-
all.map { |item| item.name_sym }
|
137
|
-
end
|
138
|
-
|
139
|
-
# Enum lookup by Symbol, String, or id. Returns <tt>arg<tt> if arg is
|
140
|
-
# an enum instance. Passing in a list of arguments returns a list of enums.
|
141
|
-
def [](*args)
|
142
|
-
case args.size
|
143
|
-
when 0
|
144
|
-
nil
|
145
|
-
when 1
|
146
|
-
arg = args.first
|
147
|
-
case arg
|
148
|
-
when Symbol
|
149
|
-
return_val = lookup_name(arg.id2name) and return return_val
|
150
|
-
when String
|
151
|
-
return_val = lookup_name(arg) and return return_val
|
152
|
-
when Fixnum
|
153
|
-
return_val = lookup_id(arg) and return return_val
|
154
|
-
when self
|
155
|
-
return arg
|
156
|
-
when nil
|
157
|
-
nil
|
158
|
-
else
|
159
|
-
raise TypeError, "#{self.name}[]: argument should be a String, Symbol or Fixnum but got a: #{arg.class.name}"
|
160
|
-
end
|
161
|
-
|
162
|
-
handle_lookup_failure(arg)
|
163
|
-
else
|
164
|
-
args.map{ |item| self[item] }.uniq
|
165
|
-
end
|
166
|
-
end
|
167
|
-
|
168
|
-
# Deals with a lookup failure for the given argument.
|
169
|
-
def handle_lookup_failure(arg)
|
170
|
-
if (lookup_failure_handler = self.acts_enumerated_on_lookup_failure)
|
171
|
-
case lookup_failure_handler
|
172
|
-
when Proc
|
173
|
-
lookup_failure_handler.call(arg)
|
174
|
-
else
|
175
|
-
self.send(lookup_failure_handler, arg)
|
176
|
-
end
|
177
|
-
else
|
178
|
-
self.send(:enforce_none, arg)
|
179
|
-
end
|
180
|
-
end
|
181
|
-
private :handle_lookup_failure
|
182
|
-
|
183
|
-
# Enum lookup by id
|
184
|
-
def lookup_id(arg)
|
185
|
-
all_by_id[arg]
|
186
|
-
end
|
187
|
-
|
188
|
-
# Enum lookup by String
|
189
|
-
def lookup_name(arg)
|
190
|
-
all_by_name[arg]
|
191
|
-
end
|
192
|
-
|
193
|
-
# Returns true if the enum lookup by the given Symbol, String or id would have returned a value, false otherwise.
|
194
|
-
def include?(arg)
|
195
|
-
case arg
|
196
|
-
when Symbol
|
197
|
-
!lookup_name(arg.id2name).nil?
|
198
|
-
when String
|
199
|
-
!lookup_name(arg).nil?
|
200
|
-
when Fixnum
|
201
|
-
!lookup_id(arg).nil?
|
202
|
-
when self
|
203
|
-
possible_match = lookup_id(arg.id)
|
204
|
-
!possible_match.nil? && possible_match == arg
|
205
|
-
else
|
206
|
-
false
|
207
|
-
end
|
208
|
-
end
|
209
|
-
|
210
|
-
# NOTE: purging the cache is sort of pointless because
|
211
|
-
# of the per-process rails model.
|
212
|
-
# By default this blows up noisily just in case you try to be more
|
213
|
-
# clever than rails allows.
|
214
|
-
# For those times (like in Migrations) when you really do want to
|
215
|
-
# alter the records you can silence the carping by setting
|
216
|
-
# enumeration_model_updates_permitted to true.
|
217
|
-
def purge_enumerations_cache
|
218
|
-
unless self.enumeration_model_updates_permitted
|
219
|
-
raise "#{self.name}: cache purging disabled for your protection"
|
220
|
-
end
|
221
|
-
@all = @all_by_name = @all_by_id = @all_active = nil
|
222
|
-
end
|
223
|
-
|
224
|
-
# The preferred method to update an enumerations model. The same
|
225
|
-
# warnings as 'purge_enumerations_cache' and
|
226
|
-
# 'enumerations_model_update_permitted' apply. Pass a block to this
|
227
|
-
# method (no args) where you perform your updates. Cache will be
|
228
|
-
# flushed automatically.
|
229
|
-
def update_enumerations_model
|
230
|
-
if block_given?
|
231
|
-
begin
|
232
|
-
self.enumeration_model_updates_permitted = true
|
233
|
-
yield
|
234
|
-
ensure
|
235
|
-
purge_enumerations_cache
|
236
|
-
self.enumeration_model_updates_permitted = false
|
237
|
-
end
|
238
|
-
end
|
239
|
-
end
|
240
|
-
|
241
|
-
# Returns the name of the column this enum uses as the basic underlying value.
|
242
|
-
def name_column
|
243
|
-
@name_column ||= self.acts_enumerated_name_column
|
244
|
-
end
|
245
|
-
|
246
|
-
# ---Private methods---
|
247
|
-
|
248
|
-
# Returns a hash of all enumeration members keyed by their ids.
|
249
|
-
def all_by_id
|
250
|
-
@all_by_id ||= all_by_attribute( :id )
|
251
|
-
end
|
252
|
-
private :all_by_id
|
253
|
-
|
254
|
-
# Returns a hash of all the enumeration members keyed by their names.
|
255
|
-
def all_by_name
|
256
|
-
begin
|
257
|
-
@all_by_name ||= all_by_attribute( :name )
|
258
|
-
rescue NoMethodError => err
|
259
|
-
if err.name == name_column
|
260
|
-
raise TypeError, "#{self.name}: you need to define a '#{name_column}' column in the table '#{table_name}'"
|
261
|
-
end
|
262
|
-
raise
|
263
|
-
end
|
264
|
-
end
|
265
|
-
private :all_by_name
|
266
|
-
|
267
|
-
def all_by_attribute(attr)
|
268
|
-
all.inject({}) { |memo, item|
|
269
|
-
memo[item.send(attr)] = item
|
270
|
-
memo
|
271
|
-
}.freeze
|
272
|
-
end
|
273
|
-
private :all_by_attribute
|
274
|
-
|
275
|
-
def enforce_none(arg)
|
276
|
-
nil
|
277
|
-
end
|
278
|
-
private :enforce_none
|
279
|
-
|
280
|
-
def enforce_strict(arg)
|
281
|
-
raise_record_not_found(arg)
|
282
|
-
end
|
283
|
-
private :enforce_strict
|
284
|
-
|
285
|
-
def enforce_strict_literals(arg)
|
286
|
-
raise_record_not_found(arg) if (Fixnum === arg) || (Symbol === arg)
|
287
|
-
nil
|
288
|
-
end
|
289
|
-
private :enforce_strict_literals
|
290
|
-
|
291
|
-
def enforce_strict_ids(arg)
|
292
|
-
raise_record_not_found(arg) if Fixnum === arg
|
293
|
-
nil
|
294
|
-
end
|
295
|
-
private :enforce_strict_ids
|
296
|
-
|
297
|
-
def enforce_strict_symbols(arg)
|
298
|
-
raise_record_not_found(arg) if Symbol === arg
|
299
|
-
nil
|
300
|
-
end
|
301
|
-
private :enforce_strict_symbols
|
302
|
-
|
303
|
-
def raise_record_not_found(arg)
|
304
|
-
raise ActiveRecord::RecordNotFound, "Couldn't find a #{self.name} identified by (#{arg.inspect})"
|
305
|
-
end
|
306
|
-
private :raise_record_not_found
|
307
|
-
|
308
|
-
end
|
309
|
-
|
310
|
-
module EnumInstanceMethods
|
311
|
-
# Behavior depends on the type of +arg+.
|
312
|
-
#
|
313
|
-
# * If +arg+ is +nil+, returns +false+.
|
314
|
-
# * If +arg+ is an instance of +Symbol+, +Fixnum+ or +String+, returns the result of +BookingStatus[:foo] == BookingStatus[arg]+.
|
315
|
-
# * If +arg+ is an +Array+, returns +true+ if any member of the array returns +true+ for +===(arg)+, +false+ otherwise.
|
316
|
-
# * In all other cases, delegates to +===(arg)+ of the superclass.
|
317
|
-
#
|
318
|
-
# Examples:
|
319
|
-
#
|
320
|
-
# BookingStatus[:foo] === :foo #Returns true
|
321
|
-
# BookingStatus[:foo] === 'foo' #Returns true
|
322
|
-
# BookingStatus[:foo] === :bar #Returns false
|
323
|
-
# BookingStatus[:foo] === [:foo, :bar, :baz] #Returns true
|
324
|
-
# BookingStatus[:foo] === nil #Returns false
|
325
|
-
#
|
326
|
-
# You should note that defining an +:on_lookup_failure+ method that raises an exception will cause +===+ to
|
327
|
-
# also raise an exception for any lookup failure of +BookingStatus[arg]+.
|
328
|
-
def ===(arg)
|
329
|
-
case arg
|
330
|
-
when nil
|
331
|
-
false
|
332
|
-
when Symbol, String, Fixnum
|
333
|
-
return self == self.class[arg]
|
334
|
-
when Array
|
335
|
-
return self.in?(*arg)
|
336
|
-
else
|
337
|
-
super
|
338
|
-
end
|
339
|
-
end
|
340
|
-
|
341
|
-
alias_method :like?, :===
|
342
|
-
|
343
|
-
# Returns true if any element in the list returns true for ===(arg), false otherwise.
|
344
|
-
def in?(*list)
|
345
|
-
for item in list
|
346
|
-
self === item and return true
|
347
|
-
end
|
348
|
-
false
|
349
|
-
end
|
350
|
-
|
351
|
-
# Returns the symbol representation of the name of the enum. BookingStatus[:foo].name_sym returns :foo.
|
352
|
-
def name_sym
|
353
|
-
self.name.to_sym
|
354
|
-
end
|
355
|
-
|
356
|
-
# By default enumeration #to_s should return stringified name of the enum. BookingStatus[:foo].to_s returns "foo"
|
357
|
-
def to_s
|
358
|
-
self.name.to_s
|
359
|
-
end
|
360
|
-
|
361
|
-
# Returns true if the instance is active, false otherwise. If it has an attribute 'active',
|
362
|
-
# returns the attribute cast to a boolean, otherwise returns true. This method is used by the 'active'
|
363
|
-
# class method to select active enums.
|
364
|
-
def active?
|
365
|
-
@_active_status ||= ( attributes.include?('active') ? !!self.active : true )
|
366
|
-
end
|
367
|
-
|
368
|
-
# Returns true if the instance is inactive, false otherwise. Default implementations returns !active?
|
369
|
-
# This method is used by the 'inactive' class method to select inactive enums.
|
370
|
-
def inactive?
|
371
|
-
!active?
|
372
|
-
end
|
373
|
-
|
374
|
-
# NOTE: updating the models that back an acts_as_enumerated is
|
375
|
-
# rather dangerous because of rails' per-process model.
|
376
|
-
# The cached values could get out of synch between processes
|
377
|
-
# and rather than completely disallow changes I make you jump
|
378
|
-
# through an extra hoop just in case you're defining your enumeration
|
379
|
-
# values in Migrations. I.e. set enumeration_model_updates_permitted = true
|
380
|
-
def enumeration_model_update
|
381
|
-
if self.class.enumeration_model_updates_permitted
|
382
|
-
self.class.purge_enumerations_cache
|
383
|
-
true
|
384
|
-
else
|
385
|
-
# Ugh. This just seems hack-ish. I wonder if there's a better way.
|
386
|
-
self.errors.add(self.class.name_column, "changes to acts_as_enumeration model instances are not permitted")
|
387
|
-
false
|
388
|
-
end
|
389
|
-
end
|
390
|
-
private :enumeration_model_update
|
391
|
-
end
|
392
|
-
end
|
393
|
-
end
|
394
|
-
end
|
395
|
-
|
@@ -1,238 +0,0 @@
|
|
1
|
-
# Copyright (c) 2005 Trevor Squires
|
2
|
-
# Copyright (c) 2012 Arthur Shagall
|
3
|
-
# Released under the MIT License. See the LICENSE file for more details.
|
4
|
-
|
5
|
-
module ActiveRecord
|
6
|
-
module Aggregations # :nodoc:
|
7
|
-
module HasEnumerated # :nodoc:
|
8
|
-
|
9
|
-
extend ActiveSupport::Concern
|
10
|
-
|
11
|
-
module ClassMethods
|
12
|
-
|
13
|
-
# Returns a list of all the attributes on the ActiveRecord model which are enumerated.
|
14
|
-
def enumerated_attributes
|
15
|
-
@enumerated_attributes ||= []
|
16
|
-
end
|
17
|
-
|
18
|
-
# Returns +true+ if +attribute+ is an enumerated attribute, +false+ otherwise.
|
19
|
-
def has_enumerated?(attribute)
|
20
|
-
return false if attribute.nil?
|
21
|
-
enumerated_attributes.include? attribute.to_s
|
22
|
-
end
|
23
|
-
|
24
|
-
# Defines an enumerated attribute with the given name on the model. Also accepts a hash of options as an
|
25
|
-
# optional second argument.
|
26
|
-
#
|
27
|
-
# === Supported options
|
28
|
-
# [:class_name]
|
29
|
-
# Name of the enum class. By default it is the camelized version of the has_enumerated attribute.
|
30
|
-
# [:foreign_key]
|
31
|
-
# Explicitly set the foreign key column. By default it's assumed to be your_enumerated_attribute_name_id.
|
32
|
-
# [:on_lookup_failure]
|
33
|
-
# The :on_lookup_failure option in has_enumerated is there because you may want to create an error handler for
|
34
|
-
# situations where the argument passed to status=(arg) is invalid. By default, an invalid value will cause an
|
35
|
-
# ArgumentError to be raised. Since this may not be optimal in your situation, you can do one of three
|
36
|
-
# things:
|
37
|
-
#
|
38
|
-
# 1) You can set it to 'validation_error'. In this case, the invalid value will be cached and returned on
|
39
|
-
# subsequent lookups, but the model will fail validation.
|
40
|
-
# 2) You can specify an instance method to be called in the case of a lookup failure. The method signature is
|
41
|
-
# as follows:
|
42
|
-
# <tt>your_lookup_handler(operation, name, name_foreign_key, acts_enumerated_class_name, lookup_value)</tt>
|
43
|
-
# The 'operation' arg will be either :read or :write. In the case of :read you are expected to return
|
44
|
-
# something or raise an exception, while in the case of a :write you don't have to return anything. Note that
|
45
|
-
# there's enough information in the method signature that you can specify one method to handle all lookup
|
46
|
-
# failures for all has_enumerated fields if you happen to have more than one defined in your model.
|
47
|
-
# 'NOTE': A nil is always considered to be a valid value for status=(arg) since it's assumed you're trying to
|
48
|
-
# null out the foreign key. The :on_lookup_failure method will be bypassed.
|
49
|
-
# 3) You can give it a lambda function. In that case, the lambda needs to accept the ActiveRecord model as
|
50
|
-
# its first argument, with the rest of the arguments being identical to the signature of the lookup handler
|
51
|
-
# instance method.
|
52
|
-
# [:permit_empty_name]
|
53
|
-
# Setting this to 'true' disables automatic conversion of empty strings to nil. Default is 'false'.
|
54
|
-
# [:default]
|
55
|
-
# Setting this option will generate an after_initialize callback to set a default value on the attribute
|
56
|
-
# unless a non-nil one already exists.
|
57
|
-
# [:create_scope]
|
58
|
-
# Setting this option to 'false' will disable automatically creating 'with_enum_attribute' and
|
59
|
-
# 'exclude_enum_attribute' scope.
|
60
|
-
#
|
61
|
-
# === Example
|
62
|
-
# class Booking < ActiveRecord::Base
|
63
|
-
# has_enumerated :status,
|
64
|
-
# :class_name => 'BookingStatus',
|
65
|
-
# :foreign_key => 'status_id',
|
66
|
-
# :on_lookup_failure => :optional_instance_method,
|
67
|
-
# :permit_empty_name => true,
|
68
|
-
# :default => :unconfirmed,
|
69
|
-
# :create_cope => false
|
70
|
-
# end
|
71
|
-
#
|
72
|
-
# === Example 2
|
73
|
-
#
|
74
|
-
# class Booking < ActiveRecord::Base
|
75
|
-
# has_enumerated :booking_status,
|
76
|
-
# :class_name => 'BookingStatus',
|
77
|
-
# :foreign_key => 'status_id',
|
78
|
-
# :on_lookup_failure => lambda{ |record, op, attr, fk, cl_name, value|
|
79
|
-
# # handle lookup failure
|
80
|
-
# }
|
81
|
-
# end
|
82
|
-
def has_enumerated(part_id, options = {})
|
83
|
-
options.assert_valid_keys( :class_name,
|
84
|
-
:foreign_key,
|
85
|
-
:on_lookup_failure,
|
86
|
-
:permit_empty_name,
|
87
|
-
:default,
|
88
|
-
:create_scope )
|
89
|
-
|
90
|
-
reflection = PowerEnum::Reflection::EnumerationReflection.new(part_id, options, self)
|
91
|
-
self.reflections = self.reflections.merge(part_id => reflection)
|
92
|
-
|
93
|
-
name = part_id.to_s
|
94
|
-
class_name = reflection.class_name
|
95
|
-
foreign_key = reflection.foreign_key
|
96
|
-
failure_opt = options[:on_lookup_failure]
|
97
|
-
empty_name = options[:permit_empty_name]
|
98
|
-
create_scope = options[:create_scope]
|
99
|
-
|
100
|
-
failure_handler = get_lookup_failure_handler(failure_opt)
|
101
|
-
|
102
|
-
class_attribute "has_enumerated_#{name}_error_handler"
|
103
|
-
self.send("has_enumerated_#{name}_error_handler=", failure_handler)
|
104
|
-
|
105
|
-
module_eval( <<-end_eval, __FILE__, __LINE__ )
|
106
|
-
def #{name}
|
107
|
-
if @invalid_enum_values && @invalid_enum_values.has_key?(:#{name})
|
108
|
-
return @invalid_enum_values[:#{name}]
|
109
|
-
end
|
110
|
-
rval = #{class_name}.lookup_id(self.#{foreign_key})
|
111
|
-
if rval.nil? && #{!failure_handler.nil?}
|
112
|
-
self.class.has_enumerated_#{name}_error_handler.call(self, :read, #{name.inspect}, #{foreign_key.inspect}, #{class_name.inspect}, self.#{foreign_key})
|
113
|
-
else
|
114
|
-
rval
|
115
|
-
end
|
116
|
-
end
|
117
|
-
|
118
|
-
def #{name}=(arg)
|
119
|
-
@invalid_enum_values ||= {}
|
120
|
-
|
121
|
-
#{!empty_name ? 'arg = nil if arg.blank?' : ''}
|
122
|
-
case arg
|
123
|
-
when #{class_name}
|
124
|
-
val = #{class_name}.lookup_id(arg.id)
|
125
|
-
when String
|
126
|
-
val = #{class_name}.lookup_name(arg)
|
127
|
-
when Symbol
|
128
|
-
val = #{class_name}.lookup_name(arg.id2name)
|
129
|
-
when Fixnum
|
130
|
-
val = #{class_name}.lookup_id(arg)
|
131
|
-
when nil
|
132
|
-
self.#{foreign_key} = nil
|
133
|
-
@invalid_enum_values.delete :#{name}
|
134
|
-
return nil
|
135
|
-
else
|
136
|
-
raise TypeError, "#{self.name}: #{name}= argument must be a #{class_name}, String, Symbol or Fixnum but got a: \#{arg.class.name}"
|
137
|
-
end
|
138
|
-
|
139
|
-
if val.nil?
|
140
|
-
if #{failure_handler.nil?}
|
141
|
-
raise ArgumentError, "#{self.name}: #{name}= can't assign a #{class_name} for a value of (\#{arg.inspect})"
|
142
|
-
else
|
143
|
-
@invalid_enum_values.delete :#{name}
|
144
|
-
self.class.has_enumerated_#{name}_error_handler.call(self, :write, #{name.inspect}, #{foreign_key.inspect}, #{class_name.inspect}, arg)
|
145
|
-
end
|
146
|
-
else
|
147
|
-
@invalid_enum_values.delete :#{name}
|
148
|
-
self.#{foreign_key} = val.id
|
149
|
-
end
|
150
|
-
end
|
151
|
-
|
152
|
-
alias_method :'#{name}_bak=', :'#{name}='
|
153
|
-
end_eval
|
154
|
-
|
155
|
-
if failure_opt.to_s == 'validation_error'
|
156
|
-
module_eval( <<-end_eval, __FILE__, __LINE__ )
|
157
|
-
validate do
|
158
|
-
if @invalid_enum_values && @invalid_enum_values.has_key?(:#{name})
|
159
|
-
errors.add(:#{name}, "is invalid")
|
160
|
-
end
|
161
|
-
end
|
162
|
-
|
163
|
-
def validation_error(operation, name, name_foreign_key, acts_enumerated_class_name, lookup_value)
|
164
|
-
@invalid_enum_values ||= {}
|
165
|
-
if operation == :write
|
166
|
-
@invalid_enum_values[name.to_sym] = lookup_value
|
167
|
-
else
|
168
|
-
nil
|
169
|
-
end
|
170
|
-
end
|
171
|
-
private :validation_error
|
172
|
-
end_eval
|
173
|
-
end
|
174
|
-
|
175
|
-
enumerated_attributes << name
|
176
|
-
|
177
|
-
if options.has_key?(:default)
|
178
|
-
default = options[:default]
|
179
|
-
set_default_method = "set_default_value_for_#{name}".to_sym
|
180
|
-
|
181
|
-
after_initialize set_default_method
|
182
|
-
|
183
|
-
define_method set_default_method do
|
184
|
-
self.send("#{name}=", default) if self.send(name).nil?
|
185
|
-
end
|
186
|
-
private set_default_method
|
187
|
-
end
|
188
|
-
|
189
|
-
unless create_scope == false
|
190
|
-
module_eval( <<-end_eval, __FILE__, __LINE__)
|
191
|
-
scope :with_#{name}, lambda { |*args|
|
192
|
-
ids = args.map{ |arg|
|
193
|
-
n = #{class_name}[arg]
|
194
|
-
}
|
195
|
-
where(:#{foreign_key} => ids)
|
196
|
-
}
|
197
|
-
scope :exclude_#{name}, lambda {|*args|
|
198
|
-
ids = #{class_name}.all - args.map{ |arg|
|
199
|
-
n = #{class_name}[arg]
|
200
|
-
}
|
201
|
-
where(:#{foreign_key} => ids)
|
202
|
-
}
|
203
|
-
end_eval
|
204
|
-
|
205
|
-
if (name_p = name.pluralize) != name
|
206
|
-
module_eval( <<-end_eval, __FILE__, __LINE__)
|
207
|
-
class << self
|
208
|
-
alias_method :with_#{name_p}, :with_#{name}
|
209
|
-
alias_method :exclude_#{name_p}, :exclude_#{name}
|
210
|
-
end
|
211
|
-
end_eval
|
212
|
-
end
|
213
|
-
end
|
214
|
-
|
215
|
-
end #has_enumerated
|
216
|
-
|
217
|
-
def get_lookup_failure_handler(failure_opt)
|
218
|
-
if failure_opt.nil?
|
219
|
-
nil
|
220
|
-
else
|
221
|
-
case failure_opt
|
222
|
-
when Proc
|
223
|
-
failure_opt
|
224
|
-
else
|
225
|
-
lambda { |record, op, attr, fk, cl_name, value|
|
226
|
-
record.send(failure_opt.to_s, op, attr, fk, cl_name, value)
|
227
|
-
}
|
228
|
-
end
|
229
|
-
|
230
|
-
end
|
231
|
-
end
|
232
|
-
private :get_lookup_failure_handler
|
233
|
-
|
234
|
-
end #module MacroMethods
|
235
|
-
|
236
|
-
end #module HasEnumerated
|
237
|
-
end #module Aggregations
|
238
|
-
end
|