power_enum 3.4.0 → 3.7.0
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.
- checksums.yaml +4 -4
- data/README.markdown +12 -28
- data/VERSION +1 -0
- data/lib/power_enum/enumerated.rb +445 -440
- data/lib/power_enum/has_enumerated.rb +175 -172
- data/lib/power_enum.rb +26 -16
- metadata +4 -2
@@ -2,518 +2,523 @@
|
|
2
2
|
# Copyright (c) 2012 Arthur Shagall
|
3
3
|
# Released under the MIT License. See the LICENSE file for more details.
|
4
4
|
|
5
|
-
|
6
|
-
module PowerEnum::Enumerated
|
7
|
-
extend ActiveSupport::Concern
|
5
|
+
module PowerEnum
|
8
6
|
|
9
|
-
#
|
10
|
-
module
|
7
|
+
# Implementation of acts_as_enumerated
|
8
|
+
module Enumerated
|
9
|
+
extend ActiveSupport::Concern
|
11
10
|
|
12
|
-
#
|
13
|
-
|
14
|
-
false
|
15
|
-
end
|
11
|
+
# Class level methods injected into ActiveRecord.
|
12
|
+
module ClassMethods
|
16
13
|
|
17
|
-
|
18
|
-
|
19
|
-
|
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 Integer or Symbol), :enforce_strict_ids (raises and exception if the arg is a
|
29
|
-
# Integer) 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
|
-
# [:alias_name]
|
36
|
-
# By default, if a name column is not 'name', will create an alias of 'name' to the name_column attribute. Set
|
37
|
-
# this to +false+ if you don't want this behavior.
|
38
|
-
#
|
39
|
-
# === Examples
|
40
|
-
#
|
41
|
-
# ====Example 1
|
42
|
-
# class BookingStatus < ActiveRecord::Base
|
43
|
-
# acts_as_enumerated
|
44
|
-
# end
|
45
|
-
#
|
46
|
-
# ====Example 2
|
47
|
-
# class BookingStatus < ActiveRecord::Base
|
48
|
-
# acts_as_enumerated :on_lookup_failure => :enforce_strict
|
49
|
-
# end
|
50
|
-
#
|
51
|
-
# ====Example 3
|
52
|
-
# class BookingStatus < ActiveRecord::Base
|
53
|
-
# acts_as_enumerated :conditions => [:exclude => false],
|
54
|
-
# :order => 'created_at DESC',
|
55
|
-
# :on_lookup_failure => :lookup_failed,
|
56
|
-
# :name_column => :status_code
|
57
|
-
#
|
58
|
-
# def self.lookup_failed(arg)
|
59
|
-
# logger.error("Invalid status code lookup #{arg.inspect}")
|
60
|
-
# nil
|
61
|
-
# end
|
62
|
-
# end
|
63
|
-
#
|
64
|
-
# ====Example 4
|
65
|
-
# class BookingStatus < ActiveRecord::Base
|
66
|
-
# acts_as_enumerated :conditions => [:exclude => false],
|
67
|
-
# :order => 'created_at DESC',
|
68
|
-
# :on_lookup_failure => lambda { |arg| raise CustomError, "BookingStatus lookup failed; #{arg}" },
|
69
|
-
# :name_column => :status_code
|
70
|
-
# end
|
71
|
-
def acts_as_enumerated(options = {})
|
72
|
-
valid_keys = [:conditions, :order, :on_lookup_failure, :name_column, :alias_name]
|
73
|
-
options.assert_valid_keys(*valid_keys)
|
74
|
-
|
75
|
-
valid_keys.each do |key|
|
76
|
-
class_attribute "acts_enumerated_#{key.to_s}"
|
77
|
-
if options.has_key?( key )
|
78
|
-
self.send "acts_enumerated_#{key.to_s}=", options[key]
|
79
|
-
end
|
14
|
+
# Returns false for ActiveRecord models that do not act as enumerated.
|
15
|
+
def acts_as_enumerated?
|
16
|
+
false
|
80
17
|
end
|
81
18
|
|
82
|
-
|
83
|
-
|
19
|
+
# Declares the model as enumerated. See the README for detailed usage instructions.
|
20
|
+
#
|
21
|
+
# === Supported options
|
22
|
+
# [:conditions]
|
23
|
+
# SQL search conditions
|
24
|
+
# [:order]
|
25
|
+
# SQL load order clause
|
26
|
+
# [:on_lookup_failure]
|
27
|
+
# Specifies the name of a class method to invoke when the +[]+ method is unable to locate a BookingStatus
|
28
|
+
# record for arg. The default is the built-in :enforce_none which returns nil. There are also built-ins for
|
29
|
+
# :enforce_strict (raise and exception regardless of the type for arg), :enforce_strict_literals (raises an
|
30
|
+
# exception if the arg is a Integer or Symbol), :enforce_strict_ids (raises and exception if the arg is a
|
31
|
+
# Integer) and :enforce_strict_symbols (raises an exception if the arg is a Symbol). The purpose of the
|
32
|
+
# :on_lookup_failure option is that a) under some circumstances a lookup failure is a Bad Thing and action
|
33
|
+
# should be taken, therefore b) a fallback action should be easily configurable. You can also give it a
|
34
|
+
# lambda that takes in a single argument (The arg that was passed to +[]+).
|
35
|
+
# [:name_column]
|
36
|
+
# Override for the 'name' column. By default, assumed to be 'name'.
|
37
|
+
# [:alias_name]
|
38
|
+
# By default, if a name column is not 'name', will create an alias of 'name' to the name_column attribute. Set
|
39
|
+
# this to +false+ if you don't want this behavior.
|
40
|
+
# [:freeze_members]
|
41
|
+
# Specifies whether individual enum instances should be frozen on database load. By default, true in production.
|
42
|
+
# Can be either a lambda or a boolean.
|
43
|
+
#
|
44
|
+
# === Examples
|
45
|
+
#
|
46
|
+
# ====Example 1
|
47
|
+
# class BookingStatus < ActiveRecord::Base
|
48
|
+
# acts_as_enumerated
|
49
|
+
# end
|
50
|
+
#
|
51
|
+
# ====Example 2
|
52
|
+
# class BookingStatus < ActiveRecord::Base
|
53
|
+
# acts_as_enumerated :on_lookup_failure => :enforce_strict
|
54
|
+
# end
|
55
|
+
#
|
56
|
+
# ====Example 3
|
57
|
+
# class BookingStatus < ActiveRecord::Base
|
58
|
+
# acts_as_enumerated :conditions => [:exclude => false],
|
59
|
+
# :order => 'created_at DESC',
|
60
|
+
# :on_lookup_failure => :lookup_failed,
|
61
|
+
# :name_column => :status_code
|
62
|
+
#
|
63
|
+
# def self.lookup_failed(arg)
|
64
|
+
# logger.error("Invalid status code lookup #{arg.inspect}")
|
65
|
+
# nil
|
66
|
+
# end
|
67
|
+
# end
|
68
|
+
#
|
69
|
+
# ====Example 4
|
70
|
+
# class BookingStatus < ActiveRecord::Base
|
71
|
+
# acts_as_enumerated :conditions => [:exclude => false],
|
72
|
+
# :order => 'created_at DESC',
|
73
|
+
# :on_lookup_failure => lambda { |arg| raise CustomError, "BookingStatus lookup failed; #{arg}" },
|
74
|
+
# :name_column => :status_code,
|
75
|
+
# :freeze_members => true
|
76
|
+
# end
|
77
|
+
def acts_as_enumerated(options = {})
|
78
|
+
valid_keys = [:conditions, :order, :on_lookup_failure, :name_column, :alias_name, :freeze_members]
|
79
|
+
options.assert_valid_keys(*valid_keys)
|
80
|
+
|
81
|
+
valid_keys.each do |key|
|
82
|
+
class_attribute "acts_enumerated_#{key.to_s}"
|
83
|
+
if options.has_key?( key )
|
84
|
+
self.send "acts_enumerated_#{key.to_s}=", options[key]
|
85
|
+
end
|
86
|
+
end
|
84
87
|
|
85
|
-
|
86
|
-
preserve_query_aliases
|
87
|
-
extend_enum_class_methods( options )
|
88
|
-
end
|
89
|
-
end
|
88
|
+
self.acts_enumerated_name_column = get_name_column(options)
|
90
89
|
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
class << self
|
95
|
-
# I have to do the interesting hack below instead of using alias_method
|
96
|
-
# because there's some sort of weirdness going on with how __all binds
|
97
|
-
# to all in Ruby 2.0.
|
98
|
-
__all = self.instance_method(:all)
|
99
|
-
|
100
|
-
define_method(:__all) do
|
101
|
-
__all.bind(self).call
|
90
|
+
unless self.is_a? PowerEnum::Enumerated::EnumClassMethods
|
91
|
+
preserve_query_aliases
|
92
|
+
extend_enum_class_methods( options )
|
102
93
|
end
|
94
|
+
end
|
103
95
|
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
96
|
+
# Rails 4 delegates all the finder methods to 'all'. PowerEnum overrides 'all'. Hence,
|
97
|
+
# the need to re-alias the query methods.
|
98
|
+
def preserve_query_aliases
|
99
|
+
class << self
|
100
|
+
# I have to do the interesting hack below instead of using alias_method
|
101
|
+
# because there's some sort of weirdness going on with how __all binds
|
102
|
+
# to all in Ruby 2.0.
|
103
|
+
__all = self.instance_method(:all)
|
104
|
+
|
105
|
+
define_method(:__all) do
|
106
|
+
__all.bind(self).call
|
107
|
+
end
|
108
|
+
|
109
|
+
# From ActiveRecord::Querying
|
110
|
+
delegate :find, :take, :take!, :first, :first!, :last, :last!, :exists?, :any?, :many?, :to => :__all
|
111
|
+
delegate :first_or_create, :first_or_create!, :first_or_initialize, :to => :__all
|
112
|
+
delegate :find_or_create_by, :find_or_create_by!, :find_or_initialize_by, :to => :__all
|
113
|
+
delegate :find_by, :find_by!, :to => :__all
|
114
|
+
delegate :destroy, :destroy_all, :delete, :delete_all, :update, :update_all, :to => :__all
|
115
|
+
delegate :find_each, :find_in_batches, :to => :__all
|
116
|
+
delegate :select, :group, :order, :except, :reorder, :limit, :offset, :joins,
|
117
|
+
:where, :preload, :eager_load, :includes, :from, :lock, :readonly,
|
118
|
+
:having, :create_with, :uniq, :distinct, :references, :none, :unscope, :to => :__all
|
119
|
+
delegate :count, :average, :minimum, :maximum, :sum, :calculate, :pluck, :ids, :to => :__all
|
120
|
+
end
|
115
121
|
end
|
116
|
-
end
|
117
122
|
|
118
|
-
|
119
|
-
|
123
|
+
# Injects the class methods into model
|
124
|
+
def extend_enum_class_methods(options) #:nodoc:
|
120
125
|
|
121
|
-
|
126
|
+
extend PowerEnum::Enumerated::EnumClassMethods
|
122
127
|
|
123
|
-
|
124
|
-
|
128
|
+
class_eval do
|
129
|
+
include PowerEnum::Enumerated::EnumInstanceMethods
|
125
130
|
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
131
|
+
before_save :enumeration_model_update
|
132
|
+
before_destroy :enumeration_model_update
|
133
|
+
validates acts_enumerated_name_column, :presence => true, :uniqueness => true
|
134
|
+
validate :validate_enumeration_model_updates_permitted
|
130
135
|
|
131
|
-
|
132
|
-
|
133
|
-
|
136
|
+
define_method :__enum_name__ do
|
137
|
+
read_attribute(acts_enumerated_name_column).to_s
|
138
|
+
end
|
134
139
|
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
140
|
+
if should_alias_name?(options) && acts_enumerated_name_column != :name
|
141
|
+
alias_method :name, :__enum_name__
|
142
|
+
end
|
143
|
+
end # class_eval
|
139
144
|
|
140
|
-
|
141
|
-
private :extend_enum_class_methods
|
145
|
+
end
|
142
146
|
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
147
|
+
# Determines if the name column should be explicitly aliased
|
148
|
+
def should_alias_name?(options) #:nodoc:
|
149
|
+
if options.has_key?(:alias_name) then
|
150
|
+
options[:alias_name]
|
151
|
+
else
|
152
|
+
true
|
153
|
+
end
|
149
154
|
end
|
150
|
-
end
|
151
|
-
private :should_alias_name?
|
152
155
|
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
156
|
+
# Extracts the name column from options or gives the default
|
157
|
+
def get_name_column(options) #:nodoc:
|
158
|
+
if options.has_key?(:name_column) && !options[:name_column].blank? then
|
159
|
+
options[:name_column].to_s.to_sym
|
160
|
+
else
|
161
|
+
:name
|
162
|
+
end
|
159
163
|
end
|
160
164
|
end
|
161
|
-
private :get_name_column
|
162
|
-
end
|
163
165
|
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
166
|
+
# These are class level methods which are patched into classes that act as
|
167
|
+
# enumerated
|
168
|
+
module EnumClassMethods
|
169
|
+
attr_accessor :enumeration_model_updates_permitted
|
168
170
|
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
171
|
+
# Returns true for ActiveRecord models that act as enumerated.
|
172
|
+
def acts_as_enumerated?
|
173
|
+
true
|
174
|
+
end
|
173
175
|
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
|
178
|
-
|
176
|
+
# Returns all the enum values. Caches results after the first time this method is run.
|
177
|
+
def all
|
178
|
+
return @all if @all
|
179
|
+
|
180
|
+
freeze_handler = if (handler = self.acts_enumerated_freeze_members).nil?
|
181
|
+
-> { Rails.env.production? }
|
182
|
+
else
|
183
|
+
case handler
|
184
|
+
when Proc
|
185
|
+
handler
|
186
|
+
else
|
187
|
+
-> { handler }
|
188
|
+
end
|
189
|
+
end
|
190
|
+
|
191
|
+
@all = load_all.collect{ |val| !!freeze_handler.call ? val.freeze : val }.freeze
|
192
|
+
end
|
179
193
|
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
-
|
194
|
+
# Returns all the active enum values. See the 'active?' instance method.
|
195
|
+
def active
|
196
|
+
return @all_active if @all_active
|
197
|
+
@all_active = all.find_all{ |enum| enum.active? }.freeze
|
198
|
+
end
|
185
199
|
|
186
|
-
|
187
|
-
|
188
|
-
|
189
|
-
|
190
|
-
|
200
|
+
# Returns all the inactive enum values. See the 'inactive?' instance method.
|
201
|
+
def inactive
|
202
|
+
return @all_inactive if @all_inactive
|
203
|
+
@all_inactive = all.find_all{ |enum| !enum.active? }.freeze
|
204
|
+
end
|
191
205
|
|
192
|
-
|
193
|
-
|
194
|
-
|
195
|
-
|
206
|
+
# Returns the names of all the enum values as an array of symbols.
|
207
|
+
def names
|
208
|
+
all.map { |item| item.name_sym }
|
209
|
+
end
|
196
210
|
|
197
|
-
|
198
|
-
|
199
|
-
|
200
|
-
|
211
|
+
# Returns all except for the given list
|
212
|
+
def all_except(*excluded)
|
213
|
+
all.find_all { |item| !(item === excluded) }
|
214
|
+
end
|
201
215
|
|
202
|
-
|
203
|
-
|
204
|
-
|
205
|
-
|
206
|
-
|
207
|
-
|
208
|
-
|
209
|
-
|
210
|
-
|
211
|
-
|
212
|
-
|
213
|
-
|
216
|
+
# Enum lookup by Symbol, String, or id. Returns <tt>arg<tt> if arg is
|
217
|
+
# an enum instance. Passing in a list of arguments returns a list of
|
218
|
+
# enums. When called with no arguments, returns nil.
|
219
|
+
def [](*args)
|
220
|
+
case args.size
|
221
|
+
when 0
|
222
|
+
nil
|
223
|
+
when 1
|
224
|
+
arg = args.first
|
225
|
+
Array === arg ? self[*arg] : (lookup_enum_by_type(arg) || handle_lookup_failure(arg))
|
226
|
+
else
|
227
|
+
args.map{ |item| self[item] }.uniq
|
228
|
+
end
|
214
229
|
end
|
215
|
-
end
|
216
230
|
|
217
|
-
|
218
|
-
|
219
|
-
|
220
|
-
|
221
|
-
|
222
|
-
|
223
|
-
|
224
|
-
|
225
|
-
|
226
|
-
|
227
|
-
|
228
|
-
|
229
|
-
|
230
|
-
|
231
|
-
|
232
|
-
|
231
|
+
# Returns <tt>true</tt> if the given Symbol, String or id has a member
|
232
|
+
# instance in the enumeration, <tt>false</tt> otherwise. Returns <tt>true</tt>
|
233
|
+
# if the argument is an enum instance, returns <tt>false</tt> if the argument
|
234
|
+
# is nil or any other value.
|
235
|
+
def contains?(arg)
|
236
|
+
case arg
|
237
|
+
when Symbol
|
238
|
+
!!lookup_name(arg.id2name)
|
239
|
+
when String
|
240
|
+
!!lookup_name(arg)
|
241
|
+
when Integer
|
242
|
+
!!lookup_id(arg)
|
243
|
+
when self
|
244
|
+
true
|
245
|
+
else
|
246
|
+
false
|
247
|
+
end
|
233
248
|
end
|
234
|
-
end
|
235
249
|
|
236
|
-
|
237
|
-
|
238
|
-
|
239
|
-
|
250
|
+
# Enum lookup by id
|
251
|
+
def lookup_id(arg)
|
252
|
+
all_by_id[arg]
|
253
|
+
end
|
240
254
|
|
241
|
-
|
242
|
-
|
243
|
-
|
244
|
-
|
255
|
+
# Enum lookup by String
|
256
|
+
def lookup_name(arg)
|
257
|
+
all_by_name[arg]
|
258
|
+
end
|
245
259
|
|
246
|
-
|
247
|
-
|
248
|
-
|
249
|
-
|
250
|
-
|
251
|
-
|
252
|
-
|
253
|
-
|
254
|
-
|
255
|
-
|
256
|
-
|
257
|
-
|
258
|
-
|
259
|
-
|
260
|
+
# Returns true if the enum lookup by the given Symbol, String or id would have returned a value, false otherwise.
|
261
|
+
def include?(arg)
|
262
|
+
case arg
|
263
|
+
when Symbol
|
264
|
+
!lookup_name(arg.id2name).nil?
|
265
|
+
when String
|
266
|
+
!lookup_name(arg).nil?
|
267
|
+
when Integer
|
268
|
+
!lookup_id(arg).nil?
|
269
|
+
when self
|
270
|
+
possible_match = lookup_id(arg.id)
|
271
|
+
!possible_match.nil? && possible_match == arg
|
272
|
+
else
|
273
|
+
false
|
274
|
+
end
|
260
275
|
end
|
261
|
-
end
|
262
276
|
|
263
|
-
|
264
|
-
|
265
|
-
|
266
|
-
|
267
|
-
|
268
|
-
|
269
|
-
|
270
|
-
|
271
|
-
|
272
|
-
|
273
|
-
|
274
|
-
|
275
|
-
|
277
|
+
# NOTE: purging the cache is sort of pointless because
|
278
|
+
# of the per-process rails model.
|
279
|
+
# By default this blows up noisily just in case you try to be more
|
280
|
+
# clever than rails allows.
|
281
|
+
# For those times (like in Migrations) when you really do want to
|
282
|
+
# alter the records you can silence the carping by setting
|
283
|
+
# enumeration_model_updates_permitted to true.
|
284
|
+
def purge_enumerations_cache
|
285
|
+
unless self.enumeration_model_updates_permitted
|
286
|
+
raise "#{self.name}: cache purging disabled for your protection"
|
287
|
+
end
|
288
|
+
@all = @all_by_name = @all_by_id = @all_active = nil
|
289
|
+
end
|
276
290
|
|
277
|
-
|
278
|
-
|
279
|
-
|
280
|
-
|
281
|
-
|
282
|
-
|
283
|
-
|
284
|
-
|
285
|
-
|
286
|
-
|
287
|
-
|
288
|
-
|
289
|
-
|
290
|
-
|
291
|
-
|
292
|
-
|
293
|
-
|
294
|
-
|
291
|
+
# The preferred method to update an enumerations model. The same
|
292
|
+
# warnings as 'purge_enumerations_cache' and
|
293
|
+
# 'enumerations_model_update_permitted' apply. Pass a block to this
|
294
|
+
# method where you perform your updates. Cache will be
|
295
|
+
# flushed automatically. If your block takes an argument, will pass in
|
296
|
+
# the model class. The argument is optional.
|
297
|
+
def update_enumerations_model(&block)
|
298
|
+
if block_given?
|
299
|
+
begin
|
300
|
+
self.enumeration_model_updates_permitted = true
|
301
|
+
purge_enumerations_cache
|
302
|
+
@all = load_all
|
303
|
+
@enumerations_model_updating = true
|
304
|
+
case block.arity
|
305
|
+
when 0
|
306
|
+
yield
|
307
|
+
else
|
308
|
+
yield self
|
309
|
+
end
|
310
|
+
ensure
|
311
|
+
purge_enumerations_cache
|
312
|
+
@enumerations_model_updating = false
|
313
|
+
self.enumeration_model_updates_permitted = false
|
295
314
|
end
|
296
|
-
ensure
|
297
|
-
purge_enumerations_cache
|
298
|
-
@enumerations_model_updating = false
|
299
|
-
self.enumeration_model_updates_permitted = false
|
300
315
|
end
|
301
316
|
end
|
302
|
-
end
|
303
317
|
|
304
|
-
|
305
|
-
|
306
|
-
|
307
|
-
|
308
|
-
|
318
|
+
# Returns true if the enumerations model is in the middle of an
|
319
|
+
# update_enumerations_model block, false otherwise.
|
320
|
+
def enumerations_model_updating?
|
321
|
+
!!@enumerations_model_updating
|
322
|
+
end
|
309
323
|
|
310
|
-
|
311
|
-
|
312
|
-
|
313
|
-
|
324
|
+
# Returns the name of the column this enum uses as the basic underlying value.
|
325
|
+
def name_column
|
326
|
+
@name_column ||= self.acts_enumerated_name_column
|
327
|
+
end
|
314
328
|
|
315
|
-
|
329
|
+
# ---Private methods---
|
316
330
|
|
317
|
-
|
318
|
-
|
319
|
-
|
320
|
-
|
321
|
-
end
|
322
|
-
private :load_all
|
323
|
-
|
324
|
-
# Looks up the enum based on the type of the argument.
|
325
|
-
def lookup_enum_by_type(arg)
|
326
|
-
case arg
|
327
|
-
when Symbol
|
328
|
-
lookup_name(arg.id2name)
|
329
|
-
when String
|
330
|
-
lookup_name(arg)
|
331
|
-
when Integer
|
332
|
-
lookup_id(arg)
|
333
|
-
when self
|
334
|
-
arg
|
335
|
-
when nil
|
336
|
-
nil
|
337
|
-
else
|
338
|
-
raise TypeError, "#{self.name}[]: argument should"\
|
339
|
-
" be a String, Symbol or Integer but got a: #{arg.class.name}"
|
331
|
+
private def load_all
|
332
|
+
conditions = self.acts_enumerated_conditions
|
333
|
+
order = self.acts_enumerated_order
|
334
|
+
unscoped.where(conditions).order(order)
|
340
335
|
end
|
341
|
-
|
342
|
-
|
343
|
-
|
344
|
-
|
345
|
-
|
346
|
-
|
347
|
-
|
348
|
-
|
349
|
-
|
336
|
+
|
337
|
+
# Looks up the enum based on the type of the argument.
|
338
|
+
private def lookup_enum_by_type(arg)
|
339
|
+
case arg
|
340
|
+
when Symbol
|
341
|
+
lookup_name(arg.id2name)
|
342
|
+
when String
|
343
|
+
lookup_name(arg)
|
344
|
+
when Integer
|
345
|
+
lookup_id(arg)
|
346
|
+
when self
|
347
|
+
arg
|
348
|
+
when nil
|
349
|
+
nil
|
350
350
|
else
|
351
|
-
self.
|
351
|
+
raise TypeError, "#{self.name}[]: argument should"\
|
352
|
+
" be a String, Symbol or Integer but got a: #{arg.class.name}"
|
352
353
|
end
|
353
|
-
else
|
354
|
-
self.send(:enforce_none, arg)
|
355
354
|
end
|
356
|
-
end
|
357
|
-
private :handle_lookup_failure
|
358
355
|
|
359
|
-
|
360
|
-
|
361
|
-
|
362
|
-
|
363
|
-
|
364
|
-
|
365
|
-
|
366
|
-
|
367
|
-
|
368
|
-
|
369
|
-
|
370
|
-
if err.name == name_column
|
371
|
-
raise TypeError, "#{self.name}: you need to define a '#{name_column}' column in the table '#{table_name}'"
|
356
|
+
# Deals with a lookup failure for the given argument.
|
357
|
+
private def handle_lookup_failure(arg)
|
358
|
+
if (lookup_failure_handler = self.acts_enumerated_on_lookup_failure)
|
359
|
+
case lookup_failure_handler
|
360
|
+
when Proc
|
361
|
+
lookup_failure_handler.call(arg)
|
362
|
+
else
|
363
|
+
self.send(lookup_failure_handler, arg)
|
364
|
+
end
|
365
|
+
else
|
366
|
+
self.send(:enforce_none, arg)
|
372
367
|
end
|
373
|
-
raise
|
374
368
|
end
|
375
|
-
end
|
376
|
-
private :all_by_name
|
377
|
-
|
378
|
-
def all_by_attribute(attr) # :nodoc:
|
379
|
-
aba = all.inject({}) { |memo, item|
|
380
|
-
memo[item.send(attr)] = item
|
381
|
-
memo
|
382
|
-
}
|
383
|
-
aba.freeze unless enumerations_model_updating?
|
384
|
-
aba
|
385
|
-
end
|
386
|
-
private :all_by_attribute
|
387
369
|
|
388
|
-
|
389
|
-
|
390
|
-
|
391
|
-
|
370
|
+
# Returns a hash of all enumeration members keyed by their ids.
|
371
|
+
private def all_by_id
|
372
|
+
@all_by_id ||= all_by_attribute( primary_key )
|
373
|
+
end
|
392
374
|
|
393
|
-
|
394
|
-
|
395
|
-
|
396
|
-
|
375
|
+
# Returns a hash of all the enumeration members keyed by their names.
|
376
|
+
private def all_by_name
|
377
|
+
begin
|
378
|
+
@all_by_name ||= all_by_attribute( :__enum_name__ )
|
379
|
+
rescue NoMethodError => err
|
380
|
+
if err.name == name_column
|
381
|
+
raise TypeError, "#{self.name}: you need to define a '#{name_column}' column in the table '#{table_name}'"
|
382
|
+
end
|
383
|
+
raise
|
384
|
+
end
|
385
|
+
end
|
397
386
|
|
398
|
-
|
399
|
-
|
400
|
-
|
401
|
-
|
402
|
-
|
387
|
+
private def all_by_attribute(attr) # :nodoc:
|
388
|
+
aba = all.inject({}) { |memo, item|
|
389
|
+
memo[item.send(attr)] = item
|
390
|
+
memo
|
391
|
+
}
|
392
|
+
aba.freeze unless enumerations_model_updating?
|
393
|
+
aba
|
394
|
+
end
|
403
395
|
|
404
|
-
|
405
|
-
|
406
|
-
|
407
|
-
end
|
408
|
-
private :enforce_strict_ids
|
396
|
+
private def enforce_none(arg) # :nodoc:
|
397
|
+
nil
|
398
|
+
end
|
409
399
|
|
410
|
-
|
411
|
-
|
412
|
-
|
413
|
-
end
|
414
|
-
private :enforce_strict_symbols
|
400
|
+
private def enforce_strict(arg) # :nodoc:
|
401
|
+
raise_record_not_found(arg)
|
402
|
+
end
|
415
403
|
|
416
|
-
|
417
|
-
|
418
|
-
|
419
|
-
|
420
|
-
|
421
|
-
|
422
|
-
|
423
|
-
|
424
|
-
|
425
|
-
# These are instance methods for objects which are enums.
|
426
|
-
module EnumInstanceMethods
|
427
|
-
# Behavior depends on the type of +arg+.
|
428
|
-
#
|
429
|
-
# * If +arg+ is +nil+, returns +false+.
|
430
|
-
# * If +arg+ is an instance of +Symbol+, +Integer+ or +String+, returns the result of +BookingStatus[:foo] == BookingStatus[arg]+.
|
431
|
-
# * If +arg+ is an +Array+, returns +true+ if any member of the array returns +true+ for +===(arg)+, +false+ otherwise.
|
432
|
-
# * In all other cases, delegates to +===(arg)+ of the superclass.
|
433
|
-
#
|
434
|
-
# Examples:
|
435
|
-
#
|
436
|
-
# BookingStatus[:foo] === :foo #Returns true
|
437
|
-
# BookingStatus[:foo] === 'foo' #Returns true
|
438
|
-
# BookingStatus[:foo] === :bar #Returns false
|
439
|
-
# BookingStatus[:foo] === [:foo, :bar, :baz] #Returns true
|
440
|
-
# BookingStatus[:foo] === nil #Returns false
|
441
|
-
#
|
442
|
-
# You should note that defining an +:on_lookup_failure+ method that raises an exception will cause +===+ to
|
443
|
-
# also raise an exception for any lookup failure of +BookingStatus[arg]+.
|
444
|
-
def ===(arg)
|
445
|
-
case arg
|
446
|
-
when nil
|
447
|
-
false
|
448
|
-
when Symbol, String, Integer
|
449
|
-
return self == self.class[arg]
|
450
|
-
when Array
|
451
|
-
return self.in?(*arg)
|
452
|
-
else
|
453
|
-
super
|
404
|
+
private def enforce_strict_literals(arg) # :nodoc:
|
405
|
+
raise_record_not_found(arg) if (Integer === arg) || (Symbol === arg)
|
406
|
+
nil
|
407
|
+
end
|
408
|
+
|
409
|
+
private def enforce_strict_ids(arg) # :nodoc:
|
410
|
+
raise_record_not_found(arg) if Integer === arg
|
411
|
+
nil
|
454
412
|
end
|
455
|
-
end
|
456
413
|
|
457
|
-
|
414
|
+
private def enforce_strict_symbols(arg) # :nodoc:
|
415
|
+
raise_record_not_found(arg) if Symbol === arg
|
416
|
+
nil
|
417
|
+
end
|
458
418
|
|
459
|
-
|
460
|
-
|
461
|
-
|
462
|
-
|
419
|
+
# raise the {ActiveRecord::RecordNotFound} error.
|
420
|
+
# @private
|
421
|
+
private def raise_record_not_found(arg)
|
422
|
+
raise ActiveRecord::RecordNotFound, "Couldn't find a #{self.name} identified by (#{arg.inspect})"
|
463
423
|
end
|
464
|
-
false
|
465
|
-
end
|
466
424
|
|
467
|
-
# Returns the symbol representation of the name of the enum. BookingStatus[:foo].name_sym returns :foo.
|
468
|
-
def name_sym
|
469
|
-
self.__enum_name__.to_sym
|
470
425
|
end
|
471
426
|
|
472
|
-
|
427
|
+
# These are instance methods for objects which are enums.
|
428
|
+
module EnumInstanceMethods
|
429
|
+
# Behavior depends on the type of +arg+.
|
430
|
+
#
|
431
|
+
# * If +arg+ is +nil+, returns +false+.
|
432
|
+
# * If +arg+ is an instance of +Symbol+, +Integer+ or +String+, returns the result of +BookingStatus[:foo] == BookingStatus[arg]+.
|
433
|
+
# * If +arg+ is an +Array+, returns +true+ if any member of the array returns +true+ for +===(arg)+, +false+ otherwise.
|
434
|
+
# * In all other cases, delegates to +===(arg)+ of the superclass.
|
435
|
+
#
|
436
|
+
# Examples:
|
437
|
+
#
|
438
|
+
# BookingStatus[:foo] === :foo #Returns true
|
439
|
+
# BookingStatus[:foo] === 'foo' #Returns true
|
440
|
+
# BookingStatus[:foo] === :bar #Returns false
|
441
|
+
# BookingStatus[:foo] === [:foo, :bar, :baz] #Returns true
|
442
|
+
# BookingStatus[:foo] === nil #Returns false
|
443
|
+
#
|
444
|
+
# You should note that defining an +:on_lookup_failure+ method that raises an exception will cause +===+ to
|
445
|
+
# also raise an exception for any lookup failure of +BookingStatus[arg]+.
|
446
|
+
def ===(arg)
|
447
|
+
case arg
|
448
|
+
when nil
|
449
|
+
false
|
450
|
+
when Symbol, String, Integer
|
451
|
+
return self == self.class[arg]
|
452
|
+
when Array
|
453
|
+
return self.in?(*arg)
|
454
|
+
else
|
455
|
+
super
|
456
|
+
end
|
457
|
+
end
|
473
458
|
|
474
|
-
|
475
|
-
def to_s
|
476
|
-
self.__enum_name__
|
477
|
-
end
|
459
|
+
alias_method :like?, :===
|
478
460
|
|
479
|
-
|
480
|
-
|
481
|
-
|
482
|
-
|
483
|
-
|
484
|
-
|
461
|
+
# Returns true if any element in the list returns true for ===(arg), false otherwise.
|
462
|
+
def in?(*list)
|
463
|
+
for item in list
|
464
|
+
self === item and return true
|
465
|
+
end
|
466
|
+
false
|
467
|
+
end
|
485
468
|
|
486
|
-
|
487
|
-
|
488
|
-
|
489
|
-
|
490
|
-
end
|
469
|
+
# Returns the symbol representation of the name of the enum. BookingStatus[:foo].name_sym returns :foo.
|
470
|
+
def name_sym
|
471
|
+
self.__enum_name__.to_sym
|
472
|
+
end
|
491
473
|
|
492
|
-
|
493
|
-
|
494
|
-
|
495
|
-
|
496
|
-
|
497
|
-
|
498
|
-
|
499
|
-
if
|
500
|
-
|
501
|
-
|
502
|
-
|
503
|
-
|
504
|
-
|
505
|
-
|
474
|
+
alias_method :to_sym, :name_sym
|
475
|
+
|
476
|
+
# By default enumeration #to_s should return stringified name of the enum. BookingStatus[:foo].to_s returns "foo"
|
477
|
+
def to_s
|
478
|
+
self.__enum_name__
|
479
|
+
end
|
480
|
+
|
481
|
+
# Returns true if the instance is active, false otherwise. If it has an attribute 'active',
|
482
|
+
# returns the attribute cast to a boolean, otherwise returns true. This method is used by the 'active'
|
483
|
+
# class method to select active enums.
|
484
|
+
def active?
|
485
|
+
@_active_status ||= ( attributes.include?('active') ? !!self.active : true )
|
486
|
+
end
|
487
|
+
|
488
|
+
# Returns true if the instance is inactive, false otherwise. Default implementations returns !active?
|
489
|
+
# This method is used by the 'inactive' class method to select inactive enums.
|
490
|
+
def inactive?
|
491
|
+
!active?
|
492
|
+
end
|
493
|
+
|
494
|
+
# NOTE: updating the models that back an acts_as_enumerated is
|
495
|
+
# rather dangerous because of rails' per-process model.
|
496
|
+
# The cached values could get out of synch between processes
|
497
|
+
# and rather than completely disallow changes I make you jump
|
498
|
+
# through an extra hoop just in case you're defining your enumeration
|
499
|
+
# values in Migrations. I.e. set enumeration_model_updates_permitted = true
|
500
|
+
private def enumeration_model_update
|
501
|
+
if self.class.enumeration_model_updates_permitted
|
502
|
+
self.class.purge_enumerations_cache
|
503
|
+
true
|
506
504
|
else
|
507
|
-
|
505
|
+
# Ugh. This just seems hack-ish. I wonder if there's a better way.
|
506
|
+
if Rails.version =~ /^4\.2\.*/
|
507
|
+
false
|
508
|
+
else
|
509
|
+
throw(:abort)
|
510
|
+
end
|
508
511
|
end
|
509
512
|
end
|
510
|
-
end
|
511
513
|
|
512
|
-
|
513
|
-
|
514
|
-
|
515
|
-
|
514
|
+
# Validates that model updates are enabled.
|
515
|
+
private def validate_enumeration_model_updates_permitted
|
516
|
+
unless self.class.enumeration_model_updates_permitted
|
517
|
+
self.errors.add(self.class.name_column, "changes to acts_as_enumeration model instances are not permitted")
|
518
|
+
end
|
516
519
|
end
|
517
|
-
end
|
518
|
-
|
519
|
-
end # module
|
520
|
+
end # module EnumInstanceMethods
|
521
|
+
|
522
|
+
end # module Enumerated
|
523
|
+
|
524
|
+
end # module PowerEnum
|