enum-x 1.0.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 +7 -0
- data/.gitignore +29 -0
- data/.ruby-gemset +1 -0
- data/.ruby-version +1 -0
- data/CHANGELOG.md +3 -0
- data/Gemfile +4 -0
- data/Gemfile.lock +42 -0
- data/LICENSE.txt +22 -0
- data/README.md +74 -0
- data/Rakefile +1 -0
- data/enum-x.gemspec +26 -0
- data/lib/enum-x.rb +2 -0
- data/lib/enum_x/dsl.rb +389 -0
- data/lib/enum_x/monkey.rb +51 -0
- data/lib/enum_x/railtie.rb +14 -0
- data/lib/enum_x/value.rb +164 -0
- data/lib/enum_x/value_list.rb +69 -0
- data/lib/enum_x/version.rb +3 -0
- data/lib/enum_x.rb +336 -0
- data/spec/enum_x/dsl_spec.rb +345 -0
- data/spec/enum_x_spec.rb +365 -0
- data/spec/spec_helper.rb +19 -0
- metadata +136 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: b8f9f8f44090850e7af6d54f9d6ed30816053a25
|
4
|
+
data.tar.gz: be58187f64fc5fa9b350597619bc5b367671dd4f
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 4633fcb4bd2f51bf0d751a2140fee3c963bffecfaf547aa73b409c206295e46979fcd0507ef25a32274255c56811fce08d4d4c8c741a81e4d8ac65cc0248f67a
|
7
|
+
data.tar.gz: ac105833237ace3f033fd259fe1c506857378604219d70a355e0c7fb25696ae08a7480f181d387a03e462d82383978b425fcf7ff0f38e2c51d1139fe37027c48
|
data/.gitignore
ADDED
@@ -0,0 +1,29 @@
|
|
1
|
+
# See http://help.github.com/ignore-files/ for more about ignoring files.
|
2
|
+
#
|
3
|
+
# If you find yourself ignoring temporary files generated by your text editor
|
4
|
+
# or operating system, you probably want to add a global ignore instead:
|
5
|
+
# git config --global core.excludesfile ~/.gitignore_global
|
6
|
+
|
7
|
+
# Ignore .DS_Store
|
8
|
+
.DS_Store
|
9
|
+
|
10
|
+
# Ignore the output files.
|
11
|
+
/pkg
|
12
|
+
|
13
|
+
# Ignore bundler and database config and precompiled assets
|
14
|
+
/.bundle
|
15
|
+
/.env
|
16
|
+
|
17
|
+
# Ignore all logfiles and tempfiles.
|
18
|
+
/tmp
|
19
|
+
|
20
|
+
# Ignore TextMate projects
|
21
|
+
*.tmproj
|
22
|
+
*.sublime-project
|
23
|
+
*.sublime-workspace
|
24
|
+
|
25
|
+
# Documentation files and other stuff
|
26
|
+
.yardoc
|
27
|
+
/doc
|
28
|
+
/nbproject
|
29
|
+
/coverage
|
data/.ruby-gemset
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
flux
|
data/.ruby-version
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
ruby-2.0.0-p247
|
data/CHANGELOG.md
ADDED
data/Gemfile
ADDED
data/Gemfile.lock
ADDED
@@ -0,0 +1,42 @@
|
|
1
|
+
PATH
|
2
|
+
remote: .
|
3
|
+
specs:
|
4
|
+
enum-x (1.0.0)
|
5
|
+
|
6
|
+
GEM
|
7
|
+
remote: https://rubygems.org/
|
8
|
+
specs:
|
9
|
+
activemodel (3.2.14)
|
10
|
+
activesupport (= 3.2.14)
|
11
|
+
builder (~> 3.0.0)
|
12
|
+
activesupport (3.2.14)
|
13
|
+
i18n (~> 0.6, >= 0.6.4)
|
14
|
+
multi_json (~> 1.0)
|
15
|
+
builder (3.0.4)
|
16
|
+
diff-lcs (1.2.4)
|
17
|
+
i18n (0.6.5)
|
18
|
+
multi_json (1.8.2)
|
19
|
+
rake (10.1.0)
|
20
|
+
rspec (2.14.1)
|
21
|
+
rspec-core (~> 2.14.0)
|
22
|
+
rspec-expectations (~> 2.14.0)
|
23
|
+
rspec-mocks (~> 2.14.0)
|
24
|
+
rspec-core (2.14.5)
|
25
|
+
rspec-expectations (2.14.2)
|
26
|
+
diff-lcs (>= 1.1.3, < 2.0)
|
27
|
+
rspec-mocks (2.14.3)
|
28
|
+
simplecov (0.7.1)
|
29
|
+
multi_json (~> 1.0)
|
30
|
+
simplecov-html (~> 0.7.1)
|
31
|
+
simplecov-html (0.7.1)
|
32
|
+
|
33
|
+
PLATFORMS
|
34
|
+
ruby
|
35
|
+
|
36
|
+
DEPENDENCIES
|
37
|
+
activemodel (~> 3.2)
|
38
|
+
bundler (~> 1.3)
|
39
|
+
enum-x!
|
40
|
+
rake
|
41
|
+
rspec (~> 2.14)
|
42
|
+
simplecov
|
data/LICENSE.txt
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2013 Joost Lubach
|
2
|
+
|
3
|
+
MIT License
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
6
|
+
a copy of this software and associated documentation files (the
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
11
|
+
the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be
|
14
|
+
included in all copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,74 @@
|
|
1
|
+
# EnumX
|
2
|
+
|
3
|
+
Add an easy way to define a finite set of values for a certain field.
|
4
|
+
|
5
|
+
## Installation
|
6
|
+
|
7
|
+
Add this line to your application's Gemfile:
|
8
|
+
|
9
|
+
gem 'enum-x'
|
10
|
+
|
11
|
+
And then execute:
|
12
|
+
|
13
|
+
$ bundle
|
14
|
+
|
15
|
+
Or install it yourself as:
|
16
|
+
|
17
|
+
$ gem install enum-x
|
18
|
+
|
19
|
+
## Usage
|
20
|
+
|
21
|
+
In the simplest form, you can use the `EnumX` class by itself:
|
22
|
+
|
23
|
+
enum = EnumX.new(:my_enum, %w[ one two three ])
|
24
|
+
my_variable = enum[:one]
|
25
|
+
my_variable.class # => EnumX::Value
|
26
|
+
my_variable.to_s # => 'one'
|
27
|
+
my_variable == :one # => true
|
28
|
+
|
29
|
+
Using the DSL, you can assign an enum to some attribute, much like most other enum-libraries:
|
30
|
+
|
31
|
+
class Post < ActiveRecord::Base
|
32
|
+
include EnumX::DSL
|
33
|
+
|
34
|
+
enum :status, %w[ draft published ]
|
35
|
+
end
|
36
|
+
|
37
|
+
If you wish to re-use enums, you can share them by defining them centrally, e.g.
|
38
|
+
|
39
|
+
**config/enums.yml:**
|
40
|
+
|
41
|
+
post_statuses: [ draft, published ]
|
42
|
+
|
43
|
+
**app/models/post.rb:**
|
44
|
+
|
45
|
+
class Post < ActiveRecord::Base
|
46
|
+
include EnumX::DSL
|
47
|
+
|
48
|
+
enum :status, :post_statuses
|
49
|
+
end
|
50
|
+
|
51
|
+
If you don't provide a second argument, the plural form is used:
|
52
|
+
|
53
|
+
**config/enums.yml:**
|
54
|
+
|
55
|
+
currencies: [ euro, dollar ]
|
56
|
+
|
57
|
+
**app/models/post.rb:**
|
58
|
+
|
59
|
+
class Price < ActiveRecord::Base
|
60
|
+
include EnumX::DSL
|
61
|
+
|
62
|
+
enum :currency
|
63
|
+
# => equivalent to 'enum :currency, :currencies'
|
64
|
+
end
|
65
|
+
|
66
|
+
When using the DSL, a shortcut to the enum is created on the class. `Price.currencies` is a shortcut to `EnumX.currencies`, and `Post.statuses` is a shortcut to `EnumX.post_statuses`.
|
67
|
+
|
68
|
+
## Contributing
|
69
|
+
|
70
|
+
1. Fork it
|
71
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
72
|
+
3. Commit your changes (`git commit -am 'Add some feature'`)
|
73
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
74
|
+
5. Create new Pull Request
|
data/Rakefile
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require "bundler/gem_tasks"
|
data/enum-x.gemspec
ADDED
@@ -0,0 +1,26 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'enum_x/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = "enum-x"
|
8
|
+
spec.version = EnumX::VERSION
|
9
|
+
spec.authors = ["Joost Lubach"]
|
10
|
+
spec.email = ["joost@yoazt.com"]
|
11
|
+
spec.description = %q[Allows a finite set of values for any attribute.]
|
12
|
+
spec.summary = %q[Allows a finite set of values for any attribute.]
|
13
|
+
spec.homepage = ""
|
14
|
+
spec.license = "MIT"
|
15
|
+
|
16
|
+
spec.files = `git ls-files`.split($/)
|
17
|
+
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
18
|
+
spec.test_files = spec.files.grep(%r{^test/})
|
19
|
+
spec.require_paths = ["lib"]
|
20
|
+
|
21
|
+
spec.add_development_dependency "bundler", "~> 1.3"
|
22
|
+
spec.add_development_dependency "rake"
|
23
|
+
spec.add_development_dependency "activemodel", "~> 3.2"
|
24
|
+
spec.add_development_dependency "rspec", "~> 2.14"
|
25
|
+
spec.add_development_dependency "simplecov"
|
26
|
+
end
|
data/lib/enum-x.rb
ADDED
data/lib/enum_x/dsl.rb
ADDED
@@ -0,0 +1,389 @@
|
|
1
|
+
class EnumX
|
2
|
+
|
3
|
+
# Mixin for any ActiveRecord or ActiveModel object to support enums.
|
4
|
+
#
|
5
|
+
# == Usage
|
6
|
+
#
|
7
|
+
# First, make sure your model class includes +EnumX::DSL+:
|
8
|
+
#
|
9
|
+
# include EnumX::DSL
|
10
|
+
#
|
11
|
+
# Then, define any enum-like attribute using the {#enum} method. The best enum is always
|
12
|
+
# inferred. The following are identical:
|
13
|
+
#
|
14
|
+
# enum :status, :statuses
|
15
|
+
# enum :status, EnumX.statuses
|
16
|
+
# enum :status, EnumX[:statuses]
|
17
|
+
# enum :status
|
18
|
+
#
|
19
|
+
# The latter infers the 'statuses' enum by default. If no applicable enum was found, an
|
20
|
+
# error is thrown.
|
21
|
+
#
|
22
|
+
# == Multi-enums
|
23
|
+
#
|
24
|
+
# Specify the option +:flags => true+ to allow the attribute to contain multiple enum values at
|
25
|
+
# once. The attribute will in essence become an array.
|
26
|
+
#
|
27
|
+
# @see ClassMethods#enum
|
28
|
+
module DSL
|
29
|
+
|
30
|
+
def self.included(target)
|
31
|
+
target.extend ClassMethods
|
32
|
+
end
|
33
|
+
|
34
|
+
######
|
35
|
+
# DSL methods
|
36
|
+
|
37
|
+
module ClassMethods
|
38
|
+
|
39
|
+
# @!method enum(name, [enum], validation_options = {})
|
40
|
+
#
|
41
|
+
# Defines an enum for an attribute. This works on ActiveRecord objects, but also on other objects. However,
|
42
|
+
# for non-ActiveRecord objects, make sure that the underlying attribute already exists.
|
43
|
+
#
|
44
|
+
# @param [Symbol] attribute The attribute to add enum support to.
|
45
|
+
# @param [EnumX|Enumerable|Symbol]
|
46
|
+
# The enum to use. Specify a symbol to look up the enum from the defined enums, or use
|
47
|
+
# an array to create an ad-hoc enum for this attribute, with name "#{model}_#{attribute.pluralize}",
|
48
|
+
# e.g. 'post_statuses'.
|
49
|
+
# @param [Hash] validation_options
|
50
|
+
# Options for the validation routine. The options listed below are extracted from this hash.
|
51
|
+
# @option validation_options :validation
|
52
|
+
# Set to false to disable validation altogether. TODO SPEC
|
53
|
+
# @option validation_options :flags
|
54
|
+
# Set to true to enable a flag enum attribute.
|
55
|
+
# @option validation_options :mnemonics
|
56
|
+
# Set to true to create enum mnemonics on the receiver class. These are question-mark style methods for
|
57
|
+
# all the values on the enum. Note that these may override existing methods, so use them only for enums
|
58
|
+
# that define a part of the 'identity' of the receiving class, such as a status or a kind.
|
59
|
+
#
|
60
|
+
# @example The following creates an enum on an ActiveRecord object.
|
61
|
+
# enum :kind # => Uses EnumX.kinds
|
62
|
+
# @example The following creates an enum on an ActiveRecord object, but uses 'account_kinds' as the name.
|
63
|
+
# enum :kind, :account_kinds # => Uses EnumX.account_kinds
|
64
|
+
# @example The following creates an enum on an ActiveRecord object, but uses a custom array.
|
65
|
+
# class Account < ActiveRecord::Base
|
66
|
+
# enum :kind, %w[ normal special ] # => Uses an on the fly enum with name 'account_kinds'.
|
67
|
+
# end
|
68
|
+
# @example The following creates an enum on an ActiveRecord object, but uses a custom array, on an anonymous class.
|
69
|
+
# Class.new(ActiveRecord::Base) do
|
70
|
+
# enum :kind, %w[ normal special ] # => Uses an on the fly enum with name '_kinds'.
|
71
|
+
# end
|
72
|
+
# @example The following creates an enum on a non-ActiveRecord class.
|
73
|
+
# class Account
|
74
|
+
# attr_accessor :kind # Required first!
|
75
|
+
# enum :kind
|
76
|
+
# end
|
77
|
+
#
|
78
|
+
# @example The following creates an enum and creates mnemonics.
|
79
|
+
# class Account < ActiveRecord::Base
|
80
|
+
# enum :kind, :mnemonics => true
|
81
|
+
# end
|
82
|
+
#
|
83
|
+
# # Given that enum 'kinds' has options %[ normal special ], the following methods are now added:
|
84
|
+
# Account.new(:kind => :normal).normal? # => true
|
85
|
+
# Account.new(:kind => :special).normal? # => true
|
86
|
+
# Account.new(:kind => :normal).special? # => false
|
87
|
+
# Account.new(:kind => :special).special? # => false
|
88
|
+
# Account.new(:kind => :special).something_else? # => raises NoMethodError
|
89
|
+
def enum(attribute, *args)
|
90
|
+
|
91
|
+
validation_options = args.extract_options!
|
92
|
+
enum = args.shift
|
93
|
+
raise ArgumentError, "too many arguments (2..3 expected)" if args.length > 0
|
94
|
+
|
95
|
+
flags = validation_options.delete(:flags)
|
96
|
+
mnemonics = validation_options.delete(:mnemonics)
|
97
|
+
|
98
|
+
# Determine the default name of the enum, and the name of the class-level enum reader.
|
99
|
+
enum_reader_name = if flags
|
100
|
+
# The attribute is already specified in the plural form (enum :statuses).
|
101
|
+
attribute.to_s
|
102
|
+
else
|
103
|
+
# The attribute is specified in the singular form - pluralize it (enum :status).
|
104
|
+
attribute.to_s.pluralize
|
105
|
+
end
|
106
|
+
|
107
|
+
enum = case enum_opt = enum
|
108
|
+
when nil then EnumX[enum_reader_name]
|
109
|
+
when EnumX then enum
|
110
|
+
when Symbol, String then EnumX[enum]
|
111
|
+
when Enumerable
|
112
|
+
name = if self.name
|
113
|
+
"#{self.name.demodulize.underscore}_#{enum_reader_name}"
|
114
|
+
else
|
115
|
+
# Anonymous class - use just the attribute name with an underscore in front of it.
|
116
|
+
"_#{enum_reader_name}"
|
117
|
+
end
|
118
|
+
|
119
|
+
EnumX.new(name, enum)
|
120
|
+
end
|
121
|
+
raise ArgumentError, "cannot find enum #{(enum_opt || enum_reader_name).inspect}" unless enum
|
122
|
+
|
123
|
+
# Define a shorthand enum accessor method.
|
124
|
+
unless respond_to?(enum_reader_name)
|
125
|
+
# Regular class. As the class may be inherited, make sure to try superclasses as well.
|
126
|
+
class_eval <<-RUBY, __FILE__, __LINE__+1
|
127
|
+
def self.#{enum_reader_name}
|
128
|
+
@#{enum_reader_name} ||= if superclass.respond_to?(:#{enum_reader_name})
|
129
|
+
superclass.#{enum_reader_name}
|
130
|
+
end
|
131
|
+
end
|
132
|
+
RUBY
|
133
|
+
end
|
134
|
+
|
135
|
+
# Store the enum on this class.
|
136
|
+
instance_variable_set "@#{enum_reader_name}", enum
|
137
|
+
|
138
|
+
if flags
|
139
|
+
# Define a flags enum.
|
140
|
+
|
141
|
+
# Validation
|
142
|
+
if validation_options[:validation] != false
|
143
|
+
|
144
|
+
validation_options.assert_valid_keys :allow_blank
|
145
|
+
if validation_options[:allow_blank] != false
|
146
|
+
class_eval <<-RUBY, __FILE__, __LINE__+1
|
147
|
+
validates_each :#{attribute} do |record, attribute, value|
|
148
|
+
if value.present?
|
149
|
+
value = [ value ] unless value.is_a?(Enumerable)
|
150
|
+
if not_included_value = value.find{ |v| !enum.values.include?(v) }
|
151
|
+
record.errors.add attribute, :inclusion, :value => not_included_value
|
152
|
+
end
|
153
|
+
end
|
154
|
+
end
|
155
|
+
RUBY
|
156
|
+
else
|
157
|
+
class_eval <<-RUBY, __FILE__, __LINE__+1
|
158
|
+
validates_each :#{attribute} do |record, attribute, value|
|
159
|
+
value = [ value ] unless value.is_a?(Enumerable) || value.nil?
|
160
|
+
if value.blank?
|
161
|
+
record.errors.add attribute, :blank
|
162
|
+
elsif not_included_value = value.find{ |v| !enum.values.include?(v) }
|
163
|
+
record.errors.add attribute, :inclusion, :value => not_included_value
|
164
|
+
end
|
165
|
+
end
|
166
|
+
RUBY
|
167
|
+
end
|
168
|
+
|
169
|
+
end
|
170
|
+
|
171
|
+
# Serialize the value if this is an ActiveRecord class AND if the database actually contains
|
172
|
+
# this column.
|
173
|
+
if defined?(ActiveRecord) && self < ActiveRecord::Base && self.column_names.include?(attribute.to_s)
|
174
|
+
serialize attribute, FlagsSerializer.new(enum)
|
175
|
+
end
|
176
|
+
|
177
|
+
# Provide a customized reader.
|
178
|
+
DSL.define_multi_reader self, attribute
|
179
|
+
DSL.define_multi_writer self, attribute
|
180
|
+
|
181
|
+
# Provide two Squeel sifters.
|
182
|
+
if respond_to?(:sifter)
|
183
|
+
class_eval <<-RUBY, __FILE__, __LINE__+1
|
184
|
+
sifter(:#{attribute}_include) { |value| instance_eval('#{attribute}') =~ "%|\#{value}|%" }
|
185
|
+
sifter(:#{attribute}_exclude) { |value| instance_eval('#{attribute}') !~ "%|\#{value}|%" }
|
186
|
+
RUBY
|
187
|
+
end
|
188
|
+
|
189
|
+
else
|
190
|
+
# Define a single enum.
|
191
|
+
|
192
|
+
# Validation
|
193
|
+
if validation_options[:validation] != false
|
194
|
+
# Provide validations.
|
195
|
+
validation_options = validation_options.merge(:in => enum.values)
|
196
|
+
validation_options[:allow_blank] = true unless validation_options.key?(:allow_blank)
|
197
|
+
|
198
|
+
validates_inclusion_of attribute, validation_options
|
199
|
+
end
|
200
|
+
|
201
|
+
# Serialize the value if this is an ActiveRecord class AND if the database actually contains
|
202
|
+
# this column.
|
203
|
+
if defined?(ActiveRecord) && self < ActiveRecord::Base && self.column_names.include?(attribute.to_s)
|
204
|
+
serialize attribute, SingleSerializer.new(enum)
|
205
|
+
end
|
206
|
+
|
207
|
+
# Provide a customized reader.
|
208
|
+
DSL.define_single_reader self, attribute
|
209
|
+
DSL.define_single_writer self, attribute
|
210
|
+
|
211
|
+
end
|
212
|
+
|
213
|
+
# Provide mnemonics if requested
|
214
|
+
DSL.define_mnemonics self, attribute, enum if mnemonics
|
215
|
+
|
216
|
+
end
|
217
|
+
|
218
|
+
end
|
219
|
+
|
220
|
+
######
|
221
|
+
# Generic reader & writer
|
222
|
+
|
223
|
+
# Defines a generic attribute reader ActiveModel-like classes.
|
224
|
+
# @api private
|
225
|
+
def self.define_reader(target, attribute, body)
|
226
|
+
override = target.instance_methods.include?(attribute.to_sym)
|
227
|
+
|
228
|
+
value_reader = case true
|
229
|
+
when override
|
230
|
+
"#{attribute}_without_enums"
|
231
|
+
when target.instance_methods.include?(:read_attribute) || target.private_instance_methods.include?(:read_attribute)
|
232
|
+
"read_attribute(:#{attribute})"
|
233
|
+
else
|
234
|
+
# We need a reader to fall back to.
|
235
|
+
raise "cannot overwrite enum reader - no existing reader found"
|
236
|
+
end
|
237
|
+
|
238
|
+
body.gsub! '%{read_value}', value_reader
|
239
|
+
|
240
|
+
if override
|
241
|
+
target.class_eval <<-RUBY, __FILE__, __LINE__+1
|
242
|
+
def #{attribute}_with_enums
|
243
|
+
#{body}
|
244
|
+
end
|
245
|
+
alias_method_chain :#{attribute}, :enums
|
246
|
+
RUBY
|
247
|
+
else
|
248
|
+
target.class_eval <<-RUBY, __FILE__, __LINE__+1
|
249
|
+
def #{attribute}
|
250
|
+
#{body}
|
251
|
+
end
|
252
|
+
RUBY
|
253
|
+
end
|
254
|
+
end
|
255
|
+
|
256
|
+
# Defines a generic attribute writer ActiveModel-like classes.
|
257
|
+
# @api private
|
258
|
+
def self.define_writer(target, attribute, body)
|
259
|
+
method = :"#{attribute}="
|
260
|
+
override = target.instance_methods.include?(method)
|
261
|
+
|
262
|
+
value_writer = case true
|
263
|
+
when override
|
264
|
+
"self.#{attribute}_without_enums = value"
|
265
|
+
when target.instance_methods.include?(:write_attribute) || target.private_instance_methods.include?(:write_attribute)
|
266
|
+
"write_attribute :#{attribute}, value"
|
267
|
+
else
|
268
|
+
# We need a writer to fall back to.
|
269
|
+
raise "cannot overwrite enum writer - no existing writer found"
|
270
|
+
end
|
271
|
+
|
272
|
+
body.gsub! '%{write_value}', value_writer
|
273
|
+
|
274
|
+
if override
|
275
|
+
target.class_eval <<-RUBY, __FILE__, __LINE__+1
|
276
|
+
def #{attribute}_with_enums=(value)
|
277
|
+
#{body}
|
278
|
+
end
|
279
|
+
alias_method_chain :#{attribute}=, :enums
|
280
|
+
RUBY
|
281
|
+
else
|
282
|
+
target.class_eval <<-RUBY, __FILE__, __LINE__+1
|
283
|
+
def #{attribute}=(value)
|
284
|
+
#{body}
|
285
|
+
end
|
286
|
+
RUBY
|
287
|
+
end
|
288
|
+
end
|
289
|
+
|
290
|
+
######
|
291
|
+
# Single enum reader & writer
|
292
|
+
|
293
|
+
def self.define_single_reader(target, attribute)
|
294
|
+
define_reader target, attribute, <<-RUBY
|
295
|
+
case value = %{read_value}
|
296
|
+
when EnumX::Value then value
|
297
|
+
when nil then nil
|
298
|
+
else
|
299
|
+
enum = EnumX.find(self.class, :#{attribute.to_s.pluralize})
|
300
|
+
enum[value] || value
|
301
|
+
end
|
302
|
+
RUBY
|
303
|
+
end
|
304
|
+
|
305
|
+
def self.define_single_writer(target, attribute)
|
306
|
+
define_writer target, attribute, <<-RUBY
|
307
|
+
value = case value
|
308
|
+
when EnumX::Value then value
|
309
|
+
when nil then nil
|
310
|
+
else
|
311
|
+
enum = EnumX.find(self.class, :#{attribute.to_s.pluralize})
|
312
|
+
enum[value] || value
|
313
|
+
end
|
314
|
+
%{write_value}
|
315
|
+
RUBY
|
316
|
+
end
|
317
|
+
|
318
|
+
######
|
319
|
+
# Multi enum reader & writer
|
320
|
+
|
321
|
+
def self.define_multi_reader(target, attribute)
|
322
|
+
define_reader target, attribute, <<-RUBY
|
323
|
+
enum = EnumX.find(self.class, :#{attribute.to_s})
|
324
|
+
case value = %{read_value}
|
325
|
+
when nil then nil
|
326
|
+
when EnumX::ValueList then value
|
327
|
+
when Enumerable then EnumX::ValueList.new(enum, value)
|
328
|
+
else EnumX::ValueList.new(enum, [value])
|
329
|
+
end
|
330
|
+
RUBY
|
331
|
+
end
|
332
|
+
|
333
|
+
def self.define_multi_writer(target, attribute)
|
334
|
+
define_writer target, attribute, <<-RUBY
|
335
|
+
enum = EnumX.find(self.class, :#{attribute.to_s})
|
336
|
+
value = case value
|
337
|
+
when nil then nil
|
338
|
+
when EnumX::ValueList then value
|
339
|
+
when Enumerable then EnumX::ValueList.new(enum, value)
|
340
|
+
else EnumX::ValueList.new(enum, [value])
|
341
|
+
end
|
342
|
+
%{write_value}
|
343
|
+
RUBY
|
344
|
+
end
|
345
|
+
|
346
|
+
######
|
347
|
+
# Mnemonics (single & multi)
|
348
|
+
|
349
|
+
def self.define_mnemonics(target, attribute, enum)
|
350
|
+
enum.values.each do |value|
|
351
|
+
target.class_eval <<-RUBY, __FILE__, __LINE__+1
|
352
|
+
def #{value}?
|
353
|
+
:#{value} === #{attribute}
|
354
|
+
end
|
355
|
+
RUBY
|
356
|
+
end
|
357
|
+
end
|
358
|
+
|
359
|
+
######
|
360
|
+
# Serializer
|
361
|
+
|
362
|
+
class SingleSerializer
|
363
|
+
def initialize(enum)
|
364
|
+
@enum = enum
|
365
|
+
end
|
366
|
+
|
367
|
+
def load(text) @enum[text] end
|
368
|
+
def dump(value) value.to_s end
|
369
|
+
end
|
370
|
+
|
371
|
+
class FlagsSerializer
|
372
|
+
def initialize(enum)
|
373
|
+
@enum = enum
|
374
|
+
end
|
375
|
+
|
376
|
+
def load(text)
|
377
|
+
EnumX::ValueList.new(@enum, text.to_s.split('|').reject(&:blank?))
|
378
|
+
end
|
379
|
+
|
380
|
+
def dump(list)
|
381
|
+
# This is the case for using the values from changes and the list is allready a string
|
382
|
+
list = load(list).values unless list.is_a?(EnumX::ValueList)
|
383
|
+
"|#{list.map(&:to_s).join('|')}|"
|
384
|
+
end
|
385
|
+
end
|
386
|
+
|
387
|
+
end
|
388
|
+
|
389
|
+
end
|
@@ -0,0 +1,51 @@
|
|
1
|
+
# This file contains extensions to Symbol and String so that enum values can be
|
2
|
+
# used in case statementes
|
3
|
+
|
4
|
+
# Extend Symbol and String's == and === method to match equal enum values as well
|
5
|
+
Symbol.class_eval do
|
6
|
+
def triple_equals_with_enums(arg)
|
7
|
+
if arg.is_a?(EnumX::Value)
|
8
|
+
triple_equals_without_enums arg.symbol
|
9
|
+
elsif arg.is_a?(EnumX::ValueList)
|
10
|
+
arg.include?(self)
|
11
|
+
else
|
12
|
+
triple_equals_without_enums arg
|
13
|
+
end
|
14
|
+
end
|
15
|
+
def double_equals_with_enums(arg)
|
16
|
+
if arg.is_a?(EnumX::Value)
|
17
|
+
double_equals_without_enums arg.symbol
|
18
|
+
else
|
19
|
+
double_equals_without_enums arg
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
alias :triple_equals_without_enums :===
|
24
|
+
alias :=== :triple_equals_with_enums
|
25
|
+
alias :double_equals_without_enums :==
|
26
|
+
alias :== :double_equals_with_enums
|
27
|
+
end
|
28
|
+
|
29
|
+
String.class_eval do
|
30
|
+
def triple_equals_with_enums(arg)
|
31
|
+
if arg.is_a?(EnumX::Value)
|
32
|
+
triple_equals_without_enums arg.value
|
33
|
+
elsif arg.is_a?(EnumX::ValueList)
|
34
|
+
arg.include?(self)
|
35
|
+
else
|
36
|
+
triple_equals_without_enums arg
|
37
|
+
end
|
38
|
+
end
|
39
|
+
def double_equals_with_enums(arg)
|
40
|
+
if arg.is_a?(EnumX::Value)
|
41
|
+
double_equals_without_enums arg.value
|
42
|
+
else
|
43
|
+
double_equals_without_enums arg
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
alias :triple_equals_without_enums :===
|
48
|
+
alias :=== :triple_equals_with_enums
|
49
|
+
alias :double_equals_without_enums :==
|
50
|
+
alias :== :double_equals_with_enums
|
51
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
require File.expand_path('..', __FILE__)
|
2
|
+
|
3
|
+
class EnumX
|
4
|
+
class Railtie < Rails::Railtie
|
5
|
+
|
6
|
+
config.enum_x = ActiveSupport::OrderedOptions.new
|
7
|
+
config.enum_x.load_paths = []
|
8
|
+
|
9
|
+
initializer 'enum.set_load_paths', :before => :finisher_hook do |app|
|
10
|
+
EnumX.load_paths.concat app.config.enum_x.load_paths
|
11
|
+
end
|
12
|
+
|
13
|
+
end
|
14
|
+
end
|