simple_enum 1.6.9 → 2.3.2
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/.gitignore +1 -0
- data/Gemfile +9 -6
- data/LICENSE +1 -1
- data/README.md +331 -0
- data/Rakefile +9 -17
- data/lib/simple_enum/accessors/accessor.rb +61 -0
- data/lib/simple_enum/accessors/ignore_accessor.rb +11 -0
- data/lib/simple_enum/accessors/whiny_accessor.rb +12 -0
- data/lib/simple_enum/accessors.rb +28 -0
- data/lib/simple_enum/attribute.rb +109 -0
- data/lib/simple_enum/enum.rb +58 -0
- data/lib/simple_enum/hasher.rb +26 -0
- data/lib/simple_enum/mongoid.rb +16 -18
- data/lib/simple_enum/railtie.rb +17 -0
- data/lib/simple_enum/translation.rb +21 -0
- data/lib/simple_enum/version.rb +2 -2
- data/lib/simple_enum/view_helpers.rb +55 -0
- data/lib/simple_enum.rb +22 -278
- data/simple_enum.gemspec +9 -9
- data/spec/simple_enum/accessors_spec.rb +298 -0
- data/spec/simple_enum/attribute_spec.rb +272 -0
- data/spec/simple_enum/enum_spec.rb +136 -0
- data/spec/simple_enum/hasher_spec.rb +63 -0
- data/spec/simple_enum/mongoid_spec.rb +44 -0
- data/spec/simple_enum/translation_spec.rb +70 -0
- data/spec/simple_enum/view_helpers_spec.rb +71 -0
- data/spec/spec_helper.rb +27 -0
- data/spec/support/active_record_support.rb +27 -0
- data/spec/support/i18n_support.rb +12 -0
- data/spec/support/model_support.rb +47 -0
- data/spec/support/mongoid_support.rb +47 -0
- metadata +55 -57
- data/README.rdoc +0 -293
- data/lib/simple_enum/enum_hash.rb +0 -64
- data/lib/simple_enum/validation.rb +0 -58
- data/locales/en.yml +0 -10
- data/test/array_conversions_test.rb +0 -21
- data/test/class_methods_test.rb +0 -114
- data/test/dirty_attributes_test.rb +0 -37
- data/test/enum_hash_test.rb +0 -73
- data/test/finders_test.rb +0 -45
- data/test/locales.yml +0 -25
- data/test/mongoid_test.rb +0 -66
- data/test/object_backed_test.rb +0 -61
- data/test/orm/active_record.rb +0 -114
- data/test/orm/common.rb +0 -23
- data/test/orm/mongoid.rb +0 -114
- data/test/poro_test.rb +0 -20
- data/test/prefixes_test.rb +0 -36
- data/test/simple_enum_test.rb +0 -314
- data/test/test_helper.rb +0 -40
- data/test/without_shortcuts_test.rb +0 -39
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: ead9e5d2a63ed29727c934fa36cf37a544dacd65
|
4
|
+
data.tar.gz: 89da3f9fa8ee6e7ef19f028bed68167d8e592f55
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 834dd3a7c5e92d44c0317ac88356af38a56fdc0581dd4b4101583dbb494f0fa1d027e88f2bd1d9d134783bab08171ff998d604d78db5ce3588cec24c301f4b1e
|
7
|
+
data.tar.gz: a4947957c71ddeb731981403288daea009f8e6b565657cf04a140de9201cf507e2f0c81490981b2d481f903035ac608572c89e3b62b8b4499d671775f4ffa6a9
|
data/.gitignore
CHANGED
data/Gemfile
CHANGED
@@ -1,11 +1,14 @@
|
|
1
1
|
source "http://rubygems.org"
|
2
2
|
|
3
|
-
|
3
|
+
gem 'activesupport', '~> 5.0.0'
|
4
|
+
gem 'activerecord', '~> 5.0.0'
|
4
5
|
|
5
|
-
|
6
|
-
gem 'mongo', '~> 1.9.0', :platform => :jruby
|
6
|
+
gemspec
|
7
7
|
|
8
8
|
# some development deps
|
9
|
-
gem 'activerecord-jdbcsqlite3-adapter', :
|
10
|
-
gem 'sqlite3', :
|
11
|
-
gem 'bson_ext', :
|
9
|
+
gem 'activerecord-jdbcsqlite3-adapter', platform: :jruby
|
10
|
+
gem 'sqlite3', platform: :ruby
|
11
|
+
gem 'bson_ext', platform: :ruby
|
12
|
+
|
13
|
+
# Code coverage on CI only
|
14
|
+
gem 'codeclimate-test-reporter', group: :test, require: nil
|
data/LICENSE
CHANGED
data/README.md
ADDED
@@ -0,0 +1,331 @@
|
|
1
|
+
SimpleEnum
|
2
|
+
==========
|
3
|
+
|
4
|
+
[](https://badge.fury.io/rb/simple_enum)
|
5
|
+
[](https://travis-ci.org/lwe/simple_enum)
|
6
|
+
[](https://codeclimate.com/github/lwe/simple_enum)
|
7
|
+
|
8
|
+
Unobtrusive enum-like fields for ActiveRecord and Ruby, brings enums functionality
|
9
|
+
to ActiveRecord and Mongoid models (built for Rails 4+).
|
10
|
+
|
11
|
+
Since version 2.0, simple_enum is no longer compatible with Rails 3.x or Ruby 1.8,
|
12
|
+
use version 1.6 instead: https://github.com/lwe/simple_enum/tree/legacy-1.x
|
13
|
+
|
14
|
+
*Note*: a recent search on github for `enum` turned out, that there are many,
|
15
|
+
many similar solutions. In fact starting with Rails 4.1, there's `ActiveRecord::Enum`
|
16
|
+
which provides **some** of the functionality, but is IMHO pretty limited and too
|
17
|
+
strict in the defaults it provides.
|
18
|
+
|
19
|
+
ActiveRecord Quick start
|
20
|
+
------------------------
|
21
|
+
|
22
|
+
Add this to a model:
|
23
|
+
|
24
|
+
```ruby
|
25
|
+
class User < ActiveRecord::Base
|
26
|
+
as_enum :gender, female: 1, male: 0
|
27
|
+
end
|
28
|
+
```
|
29
|
+
|
30
|
+
Then create the required `gender_cd` column using migrations:
|
31
|
+
|
32
|
+
```ruby
|
33
|
+
class AddGenderColumnToUser < ActiveRecord::Migration
|
34
|
+
def self.up
|
35
|
+
add_column :users, :gender_cd, :integer
|
36
|
+
end
|
37
|
+
|
38
|
+
def self.down
|
39
|
+
remove_column :users, :gender_cd
|
40
|
+
end
|
41
|
+
end
|
42
|
+
```
|
43
|
+
|
44
|
+
Mongoid Quick start
|
45
|
+
-------------------
|
46
|
+
|
47
|
+
Due to the dependency on ActiveModel 4.x, the Mongoid integration is only
|
48
|
+
available for mongoid 4.0.0 (which is at beta1 at the moment). If you intend
|
49
|
+
to use simple_enum with another version of mongoid, use version 1.6 instead.
|
50
|
+
|
51
|
+
Load mongoid support in the `Gemfile`:
|
52
|
+
|
53
|
+
```ruby
|
54
|
+
gem 'simple_enum', '~> 2.3.0' , require: 'simple_enum/mongoid'
|
55
|
+
```
|
56
|
+
|
57
|
+
Add this to a model:
|
58
|
+
|
59
|
+
```ruby
|
60
|
+
class User
|
61
|
+
include Mongoid::Document
|
62
|
+
include SimpleEnum::Mongoid
|
63
|
+
|
64
|
+
as_enum :gender, female: 1, male: 0
|
65
|
+
end
|
66
|
+
```
|
67
|
+
|
68
|
+
The primary difference between AR and mongoid is, that additionaly a field is
|
69
|
+
added to mongoid automatically, the field can be customized by setting `field:`
|
70
|
+
option, or disabled by setting `field: false`.
|
71
|
+
|
72
|
+
Working with enums
|
73
|
+
------------------
|
74
|
+
|
75
|
+
Now it's possible to pull some neat tricks on the new column, yet the original
|
76
|
+
db column (`gender_cd`) is still intact and not touched by anything.
|
77
|
+
|
78
|
+
```ruby
|
79
|
+
jane = User.new
|
80
|
+
jane.gender = :female
|
81
|
+
jane.female? # => true
|
82
|
+
jane.male? # => false
|
83
|
+
jane.gender # => :female
|
84
|
+
jane.gender_cd # => 1
|
85
|
+
```
|
86
|
+
|
87
|
+
Easily switch to another value using the bang methods, this does not save
|
88
|
+
the record, only switch the value.
|
89
|
+
|
90
|
+
```ruby
|
91
|
+
joe = User.new
|
92
|
+
joe.male! # => :male
|
93
|
+
joe.gender # => :male
|
94
|
+
joe.gender_cd # => 0
|
95
|
+
```
|
96
|
+
|
97
|
+
Accessing actual enum values is possible at the class level:
|
98
|
+
|
99
|
+
```ruby
|
100
|
+
User.genders # => #<SimpleEnum::Enum:0x0....>
|
101
|
+
User.genders[:male] # => 0
|
102
|
+
User.genders.values_at(:male, :female) # => [0, 1] (since 2.1.0)
|
103
|
+
User.females # => #<ActiveRecord::Relation:0x0.....> (WHERE gender_cd = 1)
|
104
|
+
```
|
105
|
+
|
106
|
+
By default, scope names are generated as pluralized forms of the defined enum values e.g.
|
107
|
+
|
108
|
+
```ruby
|
109
|
+
class Booking < ActiveRecord::Base
|
110
|
+
as_enum :status, %i{active cancelled pending}
|
111
|
+
end
|
112
|
+
```
|
113
|
+
|
114
|
+
would generate the following:
|
115
|
+
```ruby
|
116
|
+
Booking.actives # => #<ActiveRecord::Relation:0x0.....> (WHERE status_cd = 1)
|
117
|
+
Booking.cancelleds # => #<ActiveRecord::Relation:0x0.....> (WHERE status_cd = 2)
|
118
|
+
Booking.pendings # => #<ActiveRecord::Relation:0x0.....> (WHERE status_cd = 3)
|
119
|
+
```
|
120
|
+
|
121
|
+
By setting `pluralize_scopes: false` will not generate pluralized versions of scopes e.g.
|
122
|
+
|
123
|
+
```ruby
|
124
|
+
class Booking < ActiveRecord::Base
|
125
|
+
as_enum :status, %i{active cancelled pending}, pluralize_scopes: false
|
126
|
+
end
|
127
|
+
```
|
128
|
+
|
129
|
+
would generate the following:
|
130
|
+
```ruby
|
131
|
+
Booking.active # => #<ActiveRecord::Relation:0x0.....> (WHERE status_cd = 1)
|
132
|
+
Booking.cancelled # => #<ActiveRecord::Relation:0x0.....> (WHERE status_cd = 2)
|
133
|
+
Booking.pending # => #<ActiveRecord::Relation:0x0.....> (WHERE status_cd = 3)
|
134
|
+
```
|
135
|
+
|
136
|
+
### Wait, there's more!
|
137
|
+
|
138
|
+
- Too tired of always adding the integer values? Try:
|
139
|
+
|
140
|
+
```ruby
|
141
|
+
class User < ActiveRecord::Base
|
142
|
+
as_enum :status, %i{deleted active disabled}
|
143
|
+
# translates to: { deleted: 0, active: 1, disabled: 2 }
|
144
|
+
end
|
145
|
+
```
|
146
|
+
|
147
|
+
**Disclaimer**: if you _ever_ decide to reorder this array, beware that any
|
148
|
+
previous mapping is lost. So it's recommended to create mappings (that might
|
149
|
+
change) using hashes instead of arrays. For stuff like gender it might be
|
150
|
+
probably perfectly fine to use arrays though.
|
151
|
+
- You can store as string values instead of integer values if your database column
|
152
|
+
has the type `string` or `text`:
|
153
|
+
|
154
|
+
```ruby
|
155
|
+
class User < ActiveRecord::Base
|
156
|
+
as_enum :status, [:deleted, :active, :disabled], map: :string
|
157
|
+
end
|
158
|
+
|
159
|
+
User.create!(status: :active) #=> #<User id: 1, status_cd: "active">
|
160
|
+
```
|
161
|
+
- Want to use `SimpleEnum` in an ActiveModel, or other class, just add:
|
162
|
+
|
163
|
+
```ruby
|
164
|
+
class MyModel
|
165
|
+
extend SimpleEnum::Attribute
|
166
|
+
attr_accessor :gender_cd
|
167
|
+
as_enum :gender, [:male, :female]
|
168
|
+
end
|
169
|
+
```
|
170
|
+
- Maybe you've columns named differently than the proposed `{column}_cd` naming scheme, feel free to use any column name
|
171
|
+
by providing an option:
|
172
|
+
|
173
|
+
```ruby
|
174
|
+
class User < ActiveRecord::Base
|
175
|
+
as_enum :gender, [:male, :female], source: :sex
|
176
|
+
end
|
177
|
+
```
|
178
|
+
|
179
|
+
Starting with 2.0 it's possible to use the same source name as column name.
|
180
|
+
- By default ActiveRecord dirty methods are generated:
|
181
|
+
|
182
|
+
```ruby
|
183
|
+
user = User.male.first
|
184
|
+
user.gender = :female
|
185
|
+
user.gender_was
|
186
|
+
# => :male
|
187
|
+
```
|
188
|
+
- Need to provide custom options for the mongoid field, or skip the automatically generated field?
|
189
|
+
|
190
|
+
```ruby
|
191
|
+
# skip field generation
|
192
|
+
field :gender_cd # <- create field manually (!)
|
193
|
+
as_enum :gender, [:male, :female], field: false
|
194
|
+
|
195
|
+
# custom field options (directly passed to Mongoid::Document#field)
|
196
|
+
as_enum :gender, [:male, :female], field: { :type => Integer, :default => 1 }
|
197
|
+
```
|
198
|
+
- To validate enum values simply make use of a `validates :gender, presence: true` validation.
|
199
|
+
If an invalid value is assigned, the gender is set to `nil` by default.
|
200
|
+
- If the shortcut methods (like `female?`, `female!` or `User.male`) conflict with something in your class, it's possible to
|
201
|
+
define a prefix:
|
202
|
+
```ruby
|
203
|
+
class User < ActiveRecord::Base
|
204
|
+
as_enum :gender, %w{male female}, prefix: true
|
205
|
+
end
|
206
|
+
|
207
|
+
jane = User.new gender: :female
|
208
|
+
jane.gender_female? # => true
|
209
|
+
User.gender_females # => <ActiveRecord::Relation...WHERE gender_cd = 1.>
|
210
|
+
```
|
211
|
+
The `:prefix` option not only takes a boolean value as an argument, but instead can also be supplied a custom
|
212
|
+
prefix, so with `prefix: 'foo'` all shortcut methods would look like: `foo_<symbol>`
|
213
|
+
- To define which methods are generated it's possible to set `with:` option, by
|
214
|
+
default `with:` is set to `[:attribute, :dirty, :scope]`.
|
215
|
+
|
216
|
+
1. `:attribute` - generates the `male?` and `male!` accessor methods
|
217
|
+
2. `:dirty` - adds the `gender_was` and `gender_changed?` dirty methods
|
218
|
+
3. `:scope` - adds the class level scopes, **if** the `scope` method is present
|
219
|
+
|
220
|
+
- By default the value is set to `nil` when the user sets an invalid value,
|
221
|
+
this behavior can be changed by setting the `accessor:` option. At the moment
|
222
|
+
there are three different behaviors:
|
223
|
+
|
224
|
+
1. `:default` - which sets the value simply to `nil`
|
225
|
+
2. `:whiny` - raises an ArgumentError when trying to set an invalid value
|
226
|
+
3. `:ignore` - keeps the existing value
|
227
|
+
|
228
|
+
```ruby
|
229
|
+
class User < ActiveRecord::Base
|
230
|
+
as_enum :gender, %w{male female}, accessor: :whiny
|
231
|
+
end
|
232
|
+
User.new(gender: "dunno") # => raises ArgumentError
|
233
|
+
```
|
234
|
+
|
235
|
+
See `lib/simple_enum/accessors/*` for more.
|
236
|
+
|
237
|
+
- To define any option globally, e.g. never generating dirty methods, create
|
238
|
+
an initializer and add:
|
239
|
+
|
240
|
+
```ruby
|
241
|
+
# See lib/simple_enum.rb for other options
|
242
|
+
SimpleEnum.with = [:attribute, :scope]
|
243
|
+
```
|
244
|
+
|
245
|
+
### View Helpers
|
246
|
+
|
247
|
+
Require translated enum values? See [SimpleEnum::ViewHelpers][VE.rb] for more
|
248
|
+
details and functions. _Disclaimer_: these methods are release candidate quality
|
249
|
+
so expect them to change in future versions of SimpleEnum.
|
250
|
+
|
251
|
+
- Translate the current value in a view:
|
252
|
+
|
253
|
+
```ruby
|
254
|
+
translate_enum user, :gender # => "Frau" # assuming :de and translations exist
|
255
|
+
te user, :gender # translate_enum is also aliased to te
|
256
|
+
```
|
257
|
+
|
258
|
+
Provide translations in the i18n yaml file like:
|
259
|
+
|
260
|
+
```ruby
|
261
|
+
de:
|
262
|
+
enums:
|
263
|
+
gender:
|
264
|
+
female: 'Frau'
|
265
|
+
male: 'Mann'
|
266
|
+
```
|
267
|
+
|
268
|
+
- Build a select tag with a translated dropdown and symbol as value:
|
269
|
+
|
270
|
+
```ruby
|
271
|
+
select :user, :gender, enum_option_pairs(User, :gender)
|
272
|
+
```
|
273
|
+
|
274
|
+
- ...and one with the index as value:
|
275
|
+
|
276
|
+
```ruby
|
277
|
+
select :user, :gender_cd, enum_option_pairs(User, :gender, true)
|
278
|
+
```
|
279
|
+
|
280
|
+
## Extensions
|
281
|
+
|
282
|
+
`simple_enum` provides hooks to extend its functionality, starting with 2.3.0
|
283
|
+
the following extensions can be used:
|
284
|
+
|
285
|
+
- **Multi-select enum** support for SimpleEnum:
|
286
|
+
[simple_enum-multiple](https://github.com/bbtfr/simple_enum-multiple)
|
287
|
+
- **Persistence values**, i.e. store values in the DB:
|
288
|
+
[simple_enum-persistence](https://github.com/bbtfr/simple_enum-persistence)
|
289
|
+
- **Scopes**, scopes helper for ActiveRecord enum attributes:
|
290
|
+
[simple_enum-scopes](https://github.com/aovertus/simple_enum-scopes)
|
291
|
+
|
292
|
+
## Best practices
|
293
|
+
|
294
|
+
Do not use values named after existing, or well known method names, like `new`, `create` etc.
|
295
|
+
|
296
|
+
```ruby
|
297
|
+
# BAD, conflicts with Rails ActiveRecord Methods (!)
|
298
|
+
as_enum :handle, [:new, :create, :update]
|
299
|
+
|
300
|
+
# GOOD, prefixes all methods
|
301
|
+
as_enum :handle, [:new, :create, :update], prefix: true
|
302
|
+
```
|
303
|
+
|
304
|
+
Searching for certain values by using the finder methods:
|
305
|
+
|
306
|
+
```ruby
|
307
|
+
User.females # => returns an ActiveRecord::Relation
|
308
|
+
```
|
309
|
+
|
310
|
+
Contributors
|
311
|
+
------------
|
312
|
+
|
313
|
+
- [@dmitry](https://github.com/dmitry) - bugfixes and other improvements
|
314
|
+
- [@tarsolya](https://github.com/tarsolya) - implemented all the ruby 1.9 and rails 3 goodness!
|
315
|
+
- [@dbalatero](https://github.com/dbalatero) - rails 2.3.5 bugfix & validator fixes
|
316
|
+
- [@johnthethird](https://github.com/johnthethird) - feature for `_for_select` to return the values
|
317
|
+
- @sinsiliux - ruby 1.9 fixes and removed AR dependency
|
318
|
+
- [@sled](https://github.com/sled) - mongoid support
|
319
|
+
- [@abrom](https://github.com/abrom) - `find_by_...` method
|
320
|
+
- [@mhuggins](https://github.com/mhuggins) - translations fixes
|
321
|
+
- [@patbenatar](https://github.com/patbenatar) - for helping move towards 2.0 (scopes et all)
|
322
|
+
- [@abacha](https://github.com/abacha) - translation helpers, README fixes
|
323
|
+
- [@bbtfr](https://github.com/bbtfr) - for support, ideas and pushing extensions
|
324
|
+
- and all others: https://github.com/lwe/simple_enum/graphs/contributors thanks
|
325
|
+
|
326
|
+
License & Copyright
|
327
|
+
-------------------
|
328
|
+
|
329
|
+
Copyright (c) 2011-2015 by Lukas Westermann, Licensed under MIT License (see LICENSE file)
|
330
|
+
|
331
|
+
[VE.rb]: https://github.com/lwe/simple_enum/blob/master/lib/simple_enum/view_helpers.rb
|
data/Rakefile
CHANGED
@@ -5,30 +5,22 @@ require 'rake/testtask'
|
|
5
5
|
Bundler::GemHelper.install_tasks
|
6
6
|
|
7
7
|
desc 'Default: run all unit tests for both ActiveRecord & Mongoid.'
|
8
|
-
task :default => :
|
8
|
+
task :default => :'spec:all'
|
9
9
|
|
10
|
-
|
11
|
-
|
12
|
-
t.libs << "test"
|
13
|
-
t.test_files = Dir['test/*_test.rb']
|
14
|
-
t.verbose = true
|
15
|
-
end
|
10
|
+
desc 'Run basic specs only (skips mongoid)'
|
11
|
+
task :spec => :'spec:basic'
|
16
12
|
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
13
|
+
namespace :spec do
|
14
|
+
desc 'Run all specs'
|
15
|
+
task :all do
|
16
|
+
sh 'bundle', 'exec', 'rspec', 'spec/'
|
21
17
|
end
|
22
18
|
|
23
|
-
|
24
|
-
|
25
|
-
ENV['SIMPLE_ENUM_TEST_ORM'] = 'mongoid'
|
26
|
-
Rake::Task['test:units'].execute
|
19
|
+
task :basic do
|
20
|
+
sh 'bundle', 'exec', 'rspec', 'spec/', '-t', '~mongoid'
|
27
21
|
end
|
28
22
|
end
|
29
23
|
|
30
|
-
task :test => [:'test:activerecord', :'test:mongoid']
|
31
|
-
|
32
24
|
# Mongodb
|
33
25
|
directory "tmp/mongodb.data"
|
34
26
|
desc 'Run mongodb in tmp/'
|
@@ -0,0 +1,61 @@
|
|
1
|
+
module SimpleEnum
|
2
|
+
module Accessors
|
3
|
+
class Accessor
|
4
|
+
attr_reader :name, :enum, :source
|
5
|
+
|
6
|
+
def initialize(name, enum, source = nil, prefix = nil)
|
7
|
+
@name = name.to_s
|
8
|
+
@enum = enum
|
9
|
+
@source = source.to_s.presence || "#{name}#{SimpleEnum.suffix}"
|
10
|
+
@prefix = prefix
|
11
|
+
end
|
12
|
+
|
13
|
+
def prefix
|
14
|
+
@cached_prefix ||= @prefix && "#{@prefix == true ? name : @prefix}_" || ""
|
15
|
+
end
|
16
|
+
|
17
|
+
def read(object)
|
18
|
+
enum.key(read_before_type_cast(object))
|
19
|
+
end
|
20
|
+
|
21
|
+
def write(object, key)
|
22
|
+
write_after_type_cast(object, enum[key]) && key
|
23
|
+
end
|
24
|
+
|
25
|
+
def selected?(object, key = nil)
|
26
|
+
current = read_before_type_cast(object)
|
27
|
+
return current && current == enum[key] if key
|
28
|
+
current
|
29
|
+
end
|
30
|
+
|
31
|
+
def changed?(object)
|
32
|
+
object.send(:attribute_changed?, source)
|
33
|
+
end
|
34
|
+
|
35
|
+
def was(object)
|
36
|
+
changes = object.send(:changed_attributes)
|
37
|
+
key = changes.fetch(source, read_before_type_cast(object))
|
38
|
+
enum.key(key) if key
|
39
|
+
end
|
40
|
+
|
41
|
+
def scope(relation, value)
|
42
|
+
relation.where(source => value)
|
43
|
+
end
|
44
|
+
|
45
|
+
def to_s
|
46
|
+
name
|
47
|
+
end
|
48
|
+
|
49
|
+
private
|
50
|
+
|
51
|
+
def read_before_type_cast(object)
|
52
|
+
source == name ? object[source] : object.send(source)
|
53
|
+
end
|
54
|
+
|
55
|
+
def write_after_type_cast(object, value)
|
56
|
+
source == name ? object[source] = value : object.send("#{source}=", value)
|
57
|
+
value
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
@@ -0,0 +1,12 @@
|
|
1
|
+
require 'simple_enum/accessors/accessor'
|
2
|
+
|
3
|
+
module SimpleEnum
|
4
|
+
module Accessors
|
5
|
+
class WhinyAccessor < Accessor
|
6
|
+
def write(object, key)
|
7
|
+
raise ArgumentError, "#{key} is not a valid enum value for #{enum}" if key && !enum.include?(key)
|
8
|
+
super
|
9
|
+
end
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
require 'simple_enum/accessors/accessor'
|
2
|
+
require 'simple_enum/accessors/ignore_accessor'
|
3
|
+
require 'simple_enum/accessors/whiny_accessor'
|
4
|
+
|
5
|
+
module SimpleEnum
|
6
|
+
module Accessors
|
7
|
+
ACCESSORS = {
|
8
|
+
ignore: IgnoreAccessor,
|
9
|
+
whiny: WhinyAccessor
|
10
|
+
}
|
11
|
+
|
12
|
+
def self.accessor(name, enum, options = {})
|
13
|
+
access = options.fetch(:accessor, SimpleEnum.accessor)
|
14
|
+
klass = ACCESSORS[access] || Accessor
|
15
|
+
klass.new(name, enum, options[:source], options[:prefix])
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
# Public: Extension method to register a custom accessor.
|
20
|
+
#
|
21
|
+
# key - The Symbol of the accessor key, e.g. `:bitwise`
|
22
|
+
# clazz - The Class with the accessor implementation
|
23
|
+
#
|
24
|
+
# Returns nothing
|
25
|
+
def self.register_accessor(key, clazz)
|
26
|
+
Accessors::ACCESSORS[key] = clazz
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,109 @@
|
|
1
|
+
require 'active_support/core_ext/array'
|
2
|
+
|
3
|
+
require 'simple_enum/enum'
|
4
|
+
require 'simple_enum/hasher'
|
5
|
+
require 'simple_enum/accessors'
|
6
|
+
|
7
|
+
module SimpleEnum
|
8
|
+
|
9
|
+
# SimpleEnum::Attribute is the base class to be included in objects to get
|
10
|
+
# the #as_enum functionality. All the including class needs to provide is
|
11
|
+
# a setter and getter for `source`, by default the `source` is `<enum>_cd`.
|
12
|
+
# This is similar to how relations work in Rails, the idea is not taint the
|
13
|
+
# original method.
|
14
|
+
#
|
15
|
+
module Attribute
|
16
|
+
# Registered registrator methods from extensions
|
17
|
+
EXTENSIONS = []
|
18
|
+
|
19
|
+
def as_enum(name, values, options = {})
|
20
|
+
options.assert_valid_keys(:source, :prefix, :with, :accessor, :map, :pluralize_scopes)
|
21
|
+
|
22
|
+
hash = SimpleEnum::Hasher.map(values, options)
|
23
|
+
enum = SimpleEnum::Enum.new(name, hash)
|
24
|
+
accessor = SimpleEnum::Accessors.accessor(name, enum, options)
|
25
|
+
|
26
|
+
generate_enum_class_accessors_for(enum, accessor)
|
27
|
+
generate_enum_instance_accessors_for(enum, accessor)
|
28
|
+
generate_additional_enum_methods_for(enum, accessor, options)
|
29
|
+
|
30
|
+
EXTENSIONS.uniq.each do |extension|
|
31
|
+
send "generate_enum_#{extension}_extension_for", enum, accessor
|
32
|
+
end
|
33
|
+
|
34
|
+
enum
|
35
|
+
end
|
36
|
+
|
37
|
+
private
|
38
|
+
|
39
|
+
def simple_enum_module
|
40
|
+
@simple_enum_module ||= Module.new.tap { |mod| include mod }
|
41
|
+
end
|
42
|
+
|
43
|
+
def generate_enum_class_accessors_for(enum, accessor)
|
44
|
+
name = accessor.name.pluralize
|
45
|
+
singleton_class.send(:define_method, name) { enum }
|
46
|
+
singleton_class.send(:define_method, "#{name}_accessor") { accessor }
|
47
|
+
end
|
48
|
+
|
49
|
+
def generate_enum_instance_accessors_for(enum, accessor)
|
50
|
+
simple_enum_module.module_eval do
|
51
|
+
define_method("#{accessor}") { accessor.read(self) }
|
52
|
+
define_method("#{accessor}=") { |value| accessor.write(self, value) }
|
53
|
+
define_method("#{accessor}?") { |value = nil| accessor.selected?(self, value) }
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
def generate_additional_enum_methods_for(enum, accessor, options)
|
58
|
+
with_options = Array.wrap(options.fetch(:with, SimpleEnum.with))
|
59
|
+
scope_option, feature_options = with_options.partition { |option| option == :scope }
|
60
|
+
|
61
|
+
feature_options.each do |feature|
|
62
|
+
send "generate_enum_#{feature}_methods_for", enum, accessor
|
63
|
+
end
|
64
|
+
|
65
|
+
unless scope_option.empty?
|
66
|
+
pluralize_scopes = options.fetch(:pluralize_scopes, SimpleEnum.pluralize_scopes)
|
67
|
+
generate_enum_scope_methods_for(enum, accessor, pluralize_scopes)
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
def generate_enum_dirty_methods_for(enum, accessor)
|
72
|
+
simple_enum_module.module_eval do
|
73
|
+
define_method("#{accessor}_changed?") { accessor.changed?(self) }
|
74
|
+
define_method("#{accessor}_was") { accessor.was(self) }
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
def generate_enum_attribute_methods_for(enum, accessor)
|
79
|
+
simple_enum_module.module_eval do
|
80
|
+
enum.each_pair do |key, value|
|
81
|
+
define_method("#{accessor.prefix}#{key}?") { accessor.selected?(self, key) }
|
82
|
+
define_method("#{accessor.prefix}#{key}!") { accessor.write(self, key) }
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
def generate_enum_scope_methods_for(enum, accessor, pluralize_scopes)
|
88
|
+
return unless respond_to?(:scope)
|
89
|
+
|
90
|
+
enum.each_pair do |key, value|
|
91
|
+
scope_key = pluralize_scopes ? key.pluralize : key
|
92
|
+
scope "#{accessor.prefix}#{scope_key}", -> { accessor.scope(self, value) }
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
# Public: Register a generator method and add module as part of
|
98
|
+
# SimpleEnum::Attribute. The generator method is called after all default
|
99
|
+
# generators have been created, this allows to override/change existing methods.
|
100
|
+
#
|
101
|
+
# name - The Symbol with the name of the extension
|
102
|
+
# mod - The Module implementing `generate_enum_{name}_extension_for` method
|
103
|
+
#
|
104
|
+
# Returns nothing
|
105
|
+
def self.register_generator(name, mod)
|
106
|
+
Attribute.send :include, mod
|
107
|
+
Attribute::EXTENSIONS << name.to_s
|
108
|
+
end
|
109
|
+
end
|