power_enum 3.5.0 → 3.6.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/VERSION +1 -0
- data/lib/power_enum/enumerated.rb +436 -431
- data/lib/power_enum/has_enumerated.rb +175 -172
- data/lib/power_enum.rb +26 -16
- metadata +4 -2
@@ -2,137 +2,141 @@
|
|
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
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
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 attribute_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, attribute_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
|
-
# Add a reflection for the enumerated attribute.
|
91
|
-
reflection = create_ar_reflection(part_id, options)
|
92
|
-
|
93
|
-
attribute_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
|
-
allow_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_#{attribute_name}_error_handler"
|
103
|
-
self.send( "has_enumerated_#{attribute_name}_error_handler=", failure_handler )
|
104
|
-
|
105
|
-
define_enum_accessor attribute_name, class_name, foreign_key, failure_handler
|
106
|
-
define_enum_writer attribute_name, class_name, foreign_key, failure_handler, allow_empty_name
|
107
|
-
|
108
|
-
if failure_opt.to_s == 'validation_error'
|
109
|
-
define_validation_error( attribute_name )
|
110
|
-
end
|
5
|
+
module PowerEnum
|
6
|
+
|
7
|
+
# Implementation of has_enumerated
|
8
|
+
module HasEnumerated
|
9
|
+
|
10
|
+
extend ActiveSupport::Concern
|
111
11
|
|
112
|
-
|
12
|
+
# Class-level behavior injected into ActiveRecord to support has_enumerated
|
13
|
+
module ClassMethods
|
14
|
+
|
15
|
+
# Returns a list of all the attributes on the ActiveRecord model which are enumerated.
|
16
|
+
def enumerated_attributes
|
17
|
+
@enumerated_attributes ||= []
|
18
|
+
end
|
113
19
|
|
114
|
-
if
|
115
|
-
|
20
|
+
# Returns +true+ if +attribute+ is an enumerated attribute, +false+ otherwise.
|
21
|
+
def has_enumerated?(attribute)
|
22
|
+
return false if attribute.nil?
|
23
|
+
enumerated_attributes.include? attribute.to_s
|
116
24
|
end
|
117
25
|
|
118
|
-
|
119
|
-
|
26
|
+
# Defines an enumerated attribute with the given attribute_name on the model. Also accepts a hash of options as an
|
27
|
+
# optional second argument.
|
28
|
+
#
|
29
|
+
# === Supported options
|
30
|
+
# [:class_name]
|
31
|
+
# Name of the enum class. By default it is the camelized version of the has_enumerated attribute.
|
32
|
+
# [:foreign_key]
|
33
|
+
# Explicitly set the foreign key column. By default it's assumed to be your_enumerated_attribute_name_id.
|
34
|
+
# [:on_lookup_failure]
|
35
|
+
# The :on_lookup_failure option in has_enumerated is there because you may want to create an error handler for
|
36
|
+
# situations where the argument passed to status=(arg) is invalid. By default, an invalid value will cause an
|
37
|
+
# ArgumentError to be raised. Since this may not be optimal in your situation, you can do one of three
|
38
|
+
# things:
|
39
|
+
#
|
40
|
+
# 1) You can set it to 'validation_error'. In this case, the invalid value will be cached and returned on
|
41
|
+
# subsequent lookups, but the model will fail validation.
|
42
|
+
# 2) You can specify an instance method to be called in the case of a lookup failure. The method signature is
|
43
|
+
# as follows:
|
44
|
+
# <tt>your_lookup_handler(operation, attribute_name, name_foreign_key, acts_enumerated_class_name, lookup_value)</tt>
|
45
|
+
# The 'operation' arg will be either :read or :write. In the case of :read you are expected to return
|
46
|
+
# something or raise an exception, while in the case of a :write you don't have to return anything. Note that
|
47
|
+
# there's enough information in the method signature that you can specify one method to handle all lookup
|
48
|
+
# failures for all has_enumerated fields if you happen to have more than one defined in your model.
|
49
|
+
# 'NOTE': A nil is always considered to be a valid value for status=(arg) since it's assumed you're trying to
|
50
|
+
# null out the foreign key. The :on_lookup_failure method will be bypassed.
|
51
|
+
# 3) You can give it a lambda function. In that case, the lambda needs to accept the ActiveRecord model as
|
52
|
+
# its first argument, with the rest of the arguments being identical to the signature of the lookup handler
|
53
|
+
# instance method.
|
54
|
+
# [:permit_empty_name]
|
55
|
+
# Setting this to 'true' disables automatic conversion of empty strings to nil. Default is 'false'.
|
56
|
+
# [:default]
|
57
|
+
# Setting this option will generate an after_initialize callback to set a default value on the attribute
|
58
|
+
# unless a non-nil one already exists.
|
59
|
+
# [:create_scope]
|
60
|
+
# Setting this option to 'false' will disable automatically creating 'with_enum_attribute' and
|
61
|
+
# 'exclude_enum_attribute' scope.
|
62
|
+
#
|
63
|
+
# === Example
|
64
|
+
# class Booking < ActiveRecord::Base
|
65
|
+
# has_enumerated :status,
|
66
|
+
# :class_name => 'BookingStatus',
|
67
|
+
# :foreign_key => 'status_id',
|
68
|
+
# :on_lookup_failure => :optional_instance_method,
|
69
|
+
# :permit_empty_name => true,
|
70
|
+
# :default => :unconfirmed,
|
71
|
+
# :create_cope => false
|
72
|
+
# end
|
73
|
+
#
|
74
|
+
# === Example 2
|
75
|
+
#
|
76
|
+
# class Booking < ActiveRecord::Base
|
77
|
+
# has_enumerated :booking_status,
|
78
|
+
# :class_name => 'BookingStatus',
|
79
|
+
# :foreign_key => 'status_id',
|
80
|
+
# :on_lookup_failure => lambda{ |record, op, attr, fk, cl_name, value|
|
81
|
+
# # handle lookup failure
|
82
|
+
# }
|
83
|
+
# end
|
84
|
+
def has_enumerated(part_id, options = {})
|
85
|
+
options.assert_valid_keys(:class_name,
|
86
|
+
:foreign_key,
|
87
|
+
:on_lookup_failure,
|
88
|
+
:permit_empty_name,
|
89
|
+
:default,
|
90
|
+
:create_scope)
|
91
|
+
|
92
|
+
# Add a reflection for the enumerated attribute.
|
93
|
+
reflection = create_ar_reflection(part_id, options)
|
94
|
+
|
95
|
+
attribute_name = part_id.to_s
|
96
|
+
class_name = reflection.class_name
|
97
|
+
foreign_key = reflection.foreign_key
|
98
|
+
failure_opt = options[:on_lookup_failure]
|
99
|
+
allow_empty_name = options[:permit_empty_name]
|
100
|
+
create_scope = options[:create_scope]
|
101
|
+
|
102
|
+
failure_handler = get_lookup_failure_handler(failure_opt)
|
103
|
+
|
104
|
+
class_attribute "has_enumerated_#{attribute_name}_error_handler"
|
105
|
+
self.send("has_enumerated_#{attribute_name}_error_handler=", failure_handler)
|
106
|
+
|
107
|
+
define_enum_accessor attribute_name, class_name, foreign_key, failure_handler
|
108
|
+
define_enum_writer attribute_name, class_name, foreign_key, failure_handler, allow_empty_name
|
109
|
+
|
110
|
+
if failure_opt.to_s == 'validation_error'
|
111
|
+
define_validation_error(attribute_name)
|
112
|
+
end
|
113
|
+
|
114
|
+
enumerated_attributes << attribute_name
|
115
|
+
|
116
|
+
if options.has_key?(:default)
|
117
|
+
define_default_enum_value(attribute_name, options[:default])
|
118
|
+
end
|
119
|
+
|
120
|
+
unless create_scope == false
|
121
|
+
define_enum_scope(attribute_name, class_name, foreign_key)
|
122
|
+
end
|
123
|
+
|
120
124
|
end
|
121
125
|
|
122
|
-
|
126
|
+
# has_enumerated
|
123
127
|
|
124
|
-
|
125
|
-
|
126
|
-
|
128
|
+
# Creates the ActiveRecord reflection
|
129
|
+
private def create_ar_reflection(part_id, options)
|
130
|
+
reflection = PowerEnum::Reflection::EnumerationReflection.new(part_id, options, self)
|
127
131
|
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
private :create_ar_reflection
|
132
|
+
self._reflections = self._reflections.merge(part_id.to_s => reflection)
|
133
|
+
reflection
|
134
|
+
end
|
132
135
|
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
+
# Defines the accessor method
|
137
|
+
private def define_enum_accessor(attribute_name, class_name, foreign_key, failure_handler)
|
138
|
+
#:nodoc:
|
139
|
+
module_eval(<<-end_eval, __FILE__, __LINE__)
|
136
140
|
def #{attribute_name}
|
137
141
|
if @invalid_enum_values && @invalid_enum_values.has_key?(:#{attribute_name})
|
138
142
|
return @invalid_enum_values[:#{attribute_name}]
|
@@ -144,13 +148,13 @@ module PowerEnum::HasEnumerated
|
|
144
148
|
rval
|
145
149
|
end
|
146
150
|
end
|
147
|
-
|
148
|
-
|
149
|
-
private :define_enum_accessor
|
151
|
+
end_eval
|
152
|
+
end
|
150
153
|
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
+
# Defines the enum attribute writer method
|
155
|
+
private def define_enum_writer(attribute_name, class_name, foreign_key, failure_handler, allow_empty_name)
|
156
|
+
#:nodoc:
|
157
|
+
module_eval(<<-end_eval, __FILE__, __LINE__)
|
154
158
|
def #{attribute_name}=(arg)
|
155
159
|
@invalid_enum_values ||= {}
|
156
160
|
|
@@ -186,26 +190,26 @@ module PowerEnum::HasEnumerated
|
|
186
190
|
end
|
187
191
|
|
188
192
|
alias_method :'#{attribute_name}_bak=', :'#{attribute_name}='
|
189
|
-
|
190
|
-
|
191
|
-
private :define_enum_writer
|
193
|
+
end_eval
|
194
|
+
end
|
192
195
|
|
193
|
-
|
194
|
-
|
195
|
-
|
196
|
+
# Defines the default value for the enumerated attribute.
|
197
|
+
private def define_default_enum_value(attribute_name, default)
|
198
|
+
#:nodoc:
|
199
|
+
set_default_method = "set_default_value_for_#{attribute_name}".to_sym
|
196
200
|
|
197
|
-
|
201
|
+
after_initialize set_default_method
|
198
202
|
|
199
|
-
|
200
|
-
|
203
|
+
define_method set_default_method do
|
204
|
+
self.send("#{attribute_name}=", default) if self.send(attribute_name).nil?
|
205
|
+
end
|
206
|
+
private set_default_method
|
201
207
|
end
|
202
|
-
private set_default_method
|
203
|
-
end
|
204
|
-
private :define_default_enum_value
|
205
208
|
|
206
|
-
|
207
|
-
|
208
|
-
|
209
|
+
# Defines validation_error handling mechanism
|
210
|
+
private def define_validation_error(attribute_name)
|
211
|
+
#:nodoc:
|
212
|
+
module_eval(<<-end_eval, __FILE__, __LINE__)
|
209
213
|
validate do
|
210
214
|
if @invalid_enum_values && @invalid_enum_values.has_key?(:#{attribute_name})
|
211
215
|
errors.add(:#{attribute_name}, "is invalid")
|
@@ -221,13 +225,13 @@ module PowerEnum::HasEnumerated
|
|
221
225
|
end
|
222
226
|
end
|
223
227
|
private :validation_error
|
224
|
-
|
225
|
-
|
226
|
-
private :define_validation_error
|
228
|
+
end_eval
|
229
|
+
end
|
227
230
|
|
228
|
-
|
229
|
-
|
230
|
-
|
231
|
+
# Defines the enum scopes on the model
|
232
|
+
private def define_enum_scope(attribute_name, class_name, foreign_key)
|
233
|
+
#:nodoc:
|
234
|
+
module_eval(<<-end_eval, __FILE__, __LINE__)
|
231
235
|
scope :with_#{attribute_name}, lambda { |*args|
|
232
236
|
ids = args.map{ |arg|
|
233
237
|
n = #{class_name}[arg]
|
@@ -240,37 +244,36 @@ module PowerEnum::HasEnumerated
|
|
240
244
|
}
|
241
245
|
where(:#{foreign_key} => ids)
|
242
246
|
}
|
243
|
-
|
247
|
+
end_eval
|
244
248
|
|
245
|
-
|
246
|
-
|
249
|
+
if (name_p = attribute_name.pluralize) != attribute_name
|
250
|
+
module_eval(<<-end_eval, __FILE__, __LINE__)
|
247
251
|
class << self
|
248
252
|
alias_method :with_#{name_p}, :with_#{attribute_name}
|
249
253
|
alias_method :exclude_#{name_p}, :exclude_#{attribute_name}
|
250
254
|
end
|
251
|
-
|
255
|
+
end_eval
|
256
|
+
end
|
252
257
|
end
|
253
|
-
|
254
|
-
|
255
|
-
|
256
|
-
|
257
|
-
|
258
|
-
|
259
|
-
nil
|
260
|
-
else
|
261
|
-
case failure_opt
|
262
|
-
when Proc
|
263
|
-
failure_opt
|
258
|
+
|
259
|
+
# If the lookup failure handler is a method attribute_name, wraps it in a lambda.
|
260
|
+
private def get_lookup_failure_handler(failure_opt)
|
261
|
+
# :nodoc:
|
262
|
+
if failure_opt.nil?
|
263
|
+
nil
|
264
264
|
else
|
265
|
-
|
266
|
-
|
267
|
-
|
268
|
-
|
265
|
+
case failure_opt
|
266
|
+
when Proc
|
267
|
+
failure_opt
|
268
|
+
else
|
269
|
+
lambda { |record, op, attr, fk, cl_name, value|
|
270
|
+
record.send(failure_opt.to_s, op, attr, fk, cl_name, value)
|
271
|
+
}
|
272
|
+
end
|
269
273
|
|
274
|
+
end
|
270
275
|
end
|
271
|
-
end
|
272
|
-
private :get_lookup_failure_handler
|
273
|
-
|
274
|
-
end #module MacroMethods
|
275
276
|
|
277
|
+
end #module MacroMethods
|
278
|
+
end
|
276
279
|
end #module PowerEnum::HasEnumerated
|
data/lib/power_enum.rb
CHANGED
@@ -2,34 +2,44 @@ require 'rails'
|
|
2
2
|
require 'active_record'
|
3
3
|
require 'testing/rspec'
|
4
4
|
|
5
|
+
require "power_enum/has_enumerated"
|
6
|
+
require "power_enum/enumerated"
|
7
|
+
require "power_enum/reflection"
|
8
|
+
|
9
|
+
require "power_enum/schema/schema_statements"
|
10
|
+
require "power_enum/migration/command_recorder"
|
11
|
+
|
12
|
+
require "active_record/virtual_enumerations"
|
13
|
+
|
5
14
|
# Power Enum allows you to treat instances of your ActiveRecord models as
|
6
15
|
# though they were an enumeration of values. It allows you to cleanly solve
|
7
16
|
# many of the problems that the traditional Rails alternatives handle poorly
|
8
17
|
# if at all. It is particularly suitable for scenarios where your Rails
|
9
18
|
# application is not the only user of the database, such as when it's used for
|
10
19
|
# analytics or reporting.
|
11
|
-
|
12
|
-
|
20
|
+
module PowerEnum
|
21
|
+
class Engine < Rails::Engine
|
13
22
|
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
23
|
+
initializer 'power_enum' do
|
24
|
+
ActiveSupport.on_load(:active_record) do
|
25
|
+
include PowerEnum::Enumerated
|
26
|
+
include PowerEnum::HasEnumerated
|
18
27
|
|
19
|
-
|
20
|
-
|
21
|
-
|
28
|
+
ActiveRecord::Base.module_eval do
|
29
|
+
class << self
|
30
|
+
prepend ::PowerEnum::Reflection
|
31
|
+
end
|
22
32
|
end
|
23
|
-
end
|
24
33
|
|
25
|
-
|
26
|
-
|
27
|
-
|
34
|
+
ActiveRecord::ConnectionAdapters.module_eval do
|
35
|
+
include PowerEnum::Schema::SchemaStatements
|
36
|
+
end
|
28
37
|
|
29
|
-
|
30
|
-
|
38
|
+
ActiveRecord::Migration::CommandRecorder.class_eval do
|
39
|
+
include PowerEnum::Migration::CommandRecorder
|
40
|
+
end
|
31
41
|
end
|
32
|
-
end
|
33
42
|
|
43
|
+
end
|
34
44
|
end
|
35
45
|
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: power_enum
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 3.
|
4
|
+
version: 3.6.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Trevor Squires
|
@@ -11,7 +11,7 @@ authors:
|
|
11
11
|
autorequire:
|
12
12
|
bindir: bin
|
13
13
|
cert_chain: []
|
14
|
-
date: 2021-
|
14
|
+
date: 2021-12-08 00:00:00.000000000 Z
|
15
15
|
dependencies:
|
16
16
|
- !ruby/object:Gem::Dependency
|
17
17
|
name: bundler
|
@@ -126,9 +126,11 @@ extensions: []
|
|
126
126
|
extra_rdoc_files:
|
127
127
|
- LICENSE
|
128
128
|
- README.markdown
|
129
|
+
- VERSION
|
129
130
|
files:
|
130
131
|
- LICENSE
|
131
132
|
- README.markdown
|
133
|
+
- VERSION
|
132
134
|
- lib/active_record/virtual_enumerations.rb
|
133
135
|
- lib/generators/enum/USAGE
|
134
136
|
- lib/generators/enum/enum_generator.rb
|