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.
Files changed (53) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +1 -0
  3. data/Gemfile +9 -6
  4. data/LICENSE +1 -1
  5. data/README.md +331 -0
  6. data/Rakefile +9 -17
  7. data/lib/simple_enum/accessors/accessor.rb +61 -0
  8. data/lib/simple_enum/accessors/ignore_accessor.rb +11 -0
  9. data/lib/simple_enum/accessors/whiny_accessor.rb +12 -0
  10. data/lib/simple_enum/accessors.rb +28 -0
  11. data/lib/simple_enum/attribute.rb +109 -0
  12. data/lib/simple_enum/enum.rb +58 -0
  13. data/lib/simple_enum/hasher.rb +26 -0
  14. data/lib/simple_enum/mongoid.rb +16 -18
  15. data/lib/simple_enum/railtie.rb +17 -0
  16. data/lib/simple_enum/translation.rb +21 -0
  17. data/lib/simple_enum/version.rb +2 -2
  18. data/lib/simple_enum/view_helpers.rb +55 -0
  19. data/lib/simple_enum.rb +22 -278
  20. data/simple_enum.gemspec +9 -9
  21. data/spec/simple_enum/accessors_spec.rb +298 -0
  22. data/spec/simple_enum/attribute_spec.rb +272 -0
  23. data/spec/simple_enum/enum_spec.rb +136 -0
  24. data/spec/simple_enum/hasher_spec.rb +63 -0
  25. data/spec/simple_enum/mongoid_spec.rb +44 -0
  26. data/spec/simple_enum/translation_spec.rb +70 -0
  27. data/spec/simple_enum/view_helpers_spec.rb +71 -0
  28. data/spec/spec_helper.rb +27 -0
  29. data/spec/support/active_record_support.rb +27 -0
  30. data/spec/support/i18n_support.rb +12 -0
  31. data/spec/support/model_support.rb +47 -0
  32. data/spec/support/mongoid_support.rb +47 -0
  33. metadata +55 -57
  34. data/README.rdoc +0 -293
  35. data/lib/simple_enum/enum_hash.rb +0 -64
  36. data/lib/simple_enum/validation.rb +0 -58
  37. data/locales/en.yml +0 -10
  38. data/test/array_conversions_test.rb +0 -21
  39. data/test/class_methods_test.rb +0 -114
  40. data/test/dirty_attributes_test.rb +0 -37
  41. data/test/enum_hash_test.rb +0 -73
  42. data/test/finders_test.rb +0 -45
  43. data/test/locales.yml +0 -25
  44. data/test/mongoid_test.rb +0 -66
  45. data/test/object_backed_test.rb +0 -61
  46. data/test/orm/active_record.rb +0 -114
  47. data/test/orm/common.rb +0 -23
  48. data/test/orm/mongoid.rb +0 -114
  49. data/test/poro_test.rb +0 -20
  50. data/test/prefixes_test.rb +0 -36
  51. data/test/simple_enum_test.rb +0 -314
  52. data/test/test_helper.rb +0 -40
  53. data/test/without_shortcuts_test.rb +0 -39
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 18d5bbe70d30e114c220f17681c172597163bfd0
4
- data.tar.gz: e9ae250d080b7f6efa78928005df4b9f36ca9ffb
3
+ metadata.gz: ead9e5d2a63ed29727c934fa36cf37a544dacd65
4
+ data.tar.gz: 89da3f9fa8ee6e7ef19f028bed68167d8e592f55
5
5
  SHA512:
6
- metadata.gz: 9150ad6487da910c1b6319030102a2e8504f0775e995f2e4cc8ca09e6198e91176e08d6fa8b9aca468aa0de43cdb5a4168d7b802c0ca159d2a98b860810666a7
7
- data.tar.gz: 14577d002d9996148f4e321624ecaba8cf705de2ff8162f06c5aca7940b620dfbf1cda88c3c182aa10eed11ce8257d129e4cc4f870a77f123c0b996a86f97205
6
+ metadata.gz: 834dd3a7c5e92d44c0317ac88356af38a56fdc0581dd4b4101583dbb494f0fa1d027e88f2bd1d9d134783bab08171ff998d604d78db5ce3588cec24c301f4b1e
7
+ data.tar.gz: a4947957c71ddeb731981403288daea009f8e6b565657cf04a140de9201cf507e2f0c81490981b2d481f903035ac608572c89e3b62b8b4499d671775f4ffa6a9
data/.gitignore CHANGED
@@ -6,4 +6,5 @@ pkg
6
6
  .*.sw?
7
7
  tmp
8
8
  Gemfile.lock
9
+ gemfiles/*.lock
9
10
  :memory*
data/Gemfile CHANGED
@@ -1,11 +1,14 @@
1
1
  source "http://rubygems.org"
2
2
 
3
- gemspec
3
+ gem 'activesupport', '~> 5.0.0'
4
+ gem 'activerecord', '~> 5.0.0'
4
5
 
5
- # Fix at 1.9.x, because 1.10 errors on jruby
6
- gem 'mongo', '~> 1.9.0', :platform => :jruby
6
+ gemspec
7
7
 
8
8
  # some development deps
9
- gem 'activerecord-jdbcsqlite3-adapter', :platform => :jruby
10
- gem 'sqlite3', :platform => :ruby
11
- gem 'bson_ext', :platform => :ruby
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
@@ -1,4 +1,4 @@
1
- Copyright (c) 2013 Lukas Westermann (Zurich, Switzerland)
1
+ Copyright (c) 2015 Lukas Westermann (Zurich, Switzerland)
2
2
 
3
3
  Permission is hereby granted, free of charge, to any person obtaining
4
4
  a copy of this software and associated documentation files (the
data/README.md ADDED
@@ -0,0 +1,331 @@
1
+ SimpleEnum
2
+ ==========
3
+
4
+ [![Gem Version](https://badge.fury.io/rb/simple_enum.svg)](https://badge.fury.io/rb/simple_enum)
5
+ [![Build Status](https://travis-ci.org/lwe/simple_enum.svg)](https://travis-ci.org/lwe/simple_enum)
6
+ [![Code Climate](https://codeclimate.com/github/lwe/simple_enum.svg)](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 => :test
8
+ task :default => :'spec:all'
9
9
 
10
- namespace :test do
11
- Rake::TestTask.new(:units) do |t|
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
- desc 'Run all unit tests for ActiveRecord'
18
- task :activerecord do |t|
19
- ENV['SIMPLE_ENUM_TEST_ORM'] = 'active_record'
20
- Rake::Task['test:units'].execute
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
- desc 'Run all unit tests for Mongoid'
24
- task :mongoid do |t|
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,11 @@
1
+ require 'simple_enum/accessors/accessor'
2
+
3
+ module SimpleEnum
4
+ module Accessors
5
+ class IgnoreAccessor < Accessor
6
+ def write(object, key)
7
+ super if !key || enum.include?(key)
8
+ end
9
+ end
10
+ end
11
+ 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