enumerize 0.3.0 → 2.3.1

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 (65) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +5 -1
  3. data/.rspec +2 -0
  4. data/.travis.yml +38 -11
  5. data/CHANGELOG.md +258 -0
  6. data/Gemfile +5 -11
  7. data/Gemfile.global +20 -0
  8. data/Gemfile.mongo_mapper +7 -0
  9. data/Gemfile.rails42 +7 -0
  10. data/Gemfile.rails50 +7 -0
  11. data/Gemfile.rails52 +7 -0
  12. data/README.md +405 -17
  13. data/Rakefile +7 -1
  14. data/enumerize.gemspec +6 -3
  15. data/lib/enumerize/activemodel.rb +47 -0
  16. data/lib/enumerize/activerecord.rb +127 -2
  17. data/lib/enumerize/attribute.rb +167 -7
  18. data/lib/enumerize/attribute_map.rb +4 -0
  19. data/lib/enumerize/base.rb +60 -61
  20. data/lib/enumerize/hooks/formtastic.rb +11 -12
  21. data/lib/enumerize/hooks/sequel_dataset.rb +17 -0
  22. data/lib/enumerize/hooks/simple_form.rb +21 -8
  23. data/lib/enumerize/hooks/uniqueness.rb +22 -0
  24. data/lib/enumerize/integrations/rails_admin.rb +18 -0
  25. data/lib/enumerize/integrations/rspec/matcher.rb +161 -0
  26. data/lib/enumerize/integrations/rspec.rb +19 -0
  27. data/lib/enumerize/module.rb +33 -0
  28. data/lib/enumerize/module_attributes.rb +3 -2
  29. data/lib/enumerize/mongoid.rb +29 -0
  30. data/lib/enumerize/predicatable.rb +23 -0
  31. data/lib/enumerize/predicates.rb +76 -0
  32. data/lib/enumerize/scope/activerecord.rb +49 -0
  33. data/lib/enumerize/scope/mongoid.rb +46 -0
  34. data/lib/enumerize/scope/sequel.rb +52 -0
  35. data/lib/enumerize/sequel.rb +62 -0
  36. data/lib/enumerize/set.rb +81 -0
  37. data/lib/enumerize/utils.rb +12 -0
  38. data/lib/enumerize/value.rb +19 -46
  39. data/lib/enumerize/version.rb +3 -1
  40. data/lib/enumerize.rb +56 -4
  41. data/lib/sequel/plugins/enumerize.rb +18 -0
  42. data/spec/enumerize/integrations/rspec/matcher_spec.rb +260 -0
  43. data/spec/spec_helper.rb +30 -0
  44. data/test/activemodel_test.rb +114 -0
  45. data/test/activerecord_test.rb +542 -8
  46. data/test/attribute_map_test.rb +2 -0
  47. data/test/attribute_test.rb +102 -4
  48. data/test/base_test.rb +61 -39
  49. data/test/formtastic_test.rb +102 -17
  50. data/test/module_attributes_test.rb +25 -2
  51. data/test/mongo_mapper_test.rb +84 -0
  52. data/test/mongoid_test.rb +98 -7
  53. data/test/multiple_test.rb +59 -0
  54. data/test/predicates_test.rb +65 -0
  55. data/test/rails_admin_test.rb +27 -0
  56. data/test/sequel_test.rb +341 -0
  57. data/test/set_test.rb +166 -0
  58. data/test/simple_form_test.rb +110 -4
  59. data/test/support/mock_controller.rb +11 -1
  60. data/test/support/shared_enums.rb +43 -0
  61. data/test/support/view_test_helper.rb +6 -0
  62. data/test/test_helper.rb +25 -6
  63. data/test/value_test.rb +102 -13
  64. metadata +62 -28
  65. data/gemfiles/Gemfile-ar-3.1.x +0 -13
data/README.md CHANGED
@@ -1,6 +1,6 @@
1
- # Enumerize [![TravisCI](https://secure.travis-ci.org/brainspec/enumerize.png?branch=master)](http://travis-ci.org/brainspec/enumerize) [![Gemnasium](https://gemnasium.com/brainspec/enumerize.png)](https://gemnasium.com/brainspec/enumerize)
1
+ # Enumerize [![TravisCI](https://secure.travis-ci.org/brainspec/enumerize.svg?branch=master)](http://travis-ci.org/brainspec/enumerize) [![Gemnasium](https://gemnasium.com/brainspec/enumerize.svg)](https://gemnasium.com/brainspec/enumerize)
2
2
 
3
- Enumerated attributes with I18n and ActiveRecord/Mongoid support
3
+ Enumerated attributes with I18n and ActiveRecord/Mongoid/MongoMapper/Sequel support
4
4
 
5
5
  ## Installation
6
6
 
@@ -16,27 +16,57 @@ Or install it yourself as:
16
16
 
17
17
  $ gem install enumerize
18
18
 
19
+ ## Supported Versions
20
+ - Ruby 2.2+
21
+ - Rails 4.2+
22
+
19
23
  ## Usage
20
24
 
21
25
  Basic:
22
26
 
23
27
  ```ruby
24
28
  class User
25
- include Enumerize
29
+ extend Enumerize
26
30
 
27
- enumerize :sex, :in => [:male, :female]
31
+ enumerize :sex, in: [:male, :female]
28
32
  end
29
33
  ```
30
34
 
35
+ Note that enumerized values are just identificators so if you want to use multi-word, etc. values you should use `I18n` feature.
36
+
37
+
31
38
  ActiveRecord:
32
39
 
40
+ ```ruby
41
+ class CreateUsers < ActiveRecord::Migration
42
+ def change
43
+ create_table :users do |t|
44
+ t.string :sex
45
+ t.string :role
46
+
47
+ t.timestamps
48
+ end
49
+ end
50
+ end
51
+
52
+ class User < ActiveRecord::Base
53
+ extend Enumerize
54
+
55
+ enumerize :sex, in: [:male, :female], default: lambda { |user| SexIdentifier.sex_for_name(user.name).to_sym }
56
+
57
+ enumerize :role, in: [:user, :admin], default: :user
58
+ end
59
+ ```
60
+
61
+ :warning: By default, `enumerize` adds `inclusion` validation to the model. You can skip validations by passing `skip_validations` option. :warning:
62
+
33
63
  ```ruby
34
64
  class User < ActiveRecord::Base
35
- include Enumerize
65
+ extend Enumerize
36
66
 
37
- enumerize :sex, :in => [:male, :female]
67
+ enumerize :sex, in: [:male, :female], skip_validations: lambda { |user| user.new_record? }
38
68
 
39
- enumerize :role, :in => [:user, :admin], :default => :user
69
+ enumerize :role, in: [:user, :admin], skip_validations: true
40
70
  end
41
71
  ```
42
72
 
@@ -45,10 +75,22 @@ Mongoid:
45
75
  ```ruby
46
76
  class User
47
77
  include Mongoid::Document
48
- include Enumerize
78
+ extend Enumerize
49
79
 
50
80
  field :role
51
- enumerize :role, :in => [:user, :admin], :default => :user
81
+ enumerize :role, in: [:user, :admin], default: :user
82
+ end
83
+ ```
84
+
85
+ MongoMapper:
86
+
87
+ ```ruby
88
+ class User
89
+ include MongoMapper::Document
90
+ extend Enumerize
91
+
92
+ key :role
93
+ enumerize :role, in: [:user, :admin], default: :user
52
94
  end
53
95
  ```
54
96
 
@@ -63,14 +105,48 @@ en:
63
105
  female: "Female"
64
106
  ```
65
107
 
66
- or if you use `sex` attribute across several models you can use this:
108
+ or if you use `sex` attribute across several models you can use `defaults` scope:
67
109
 
68
110
  ```ruby
69
111
  en:
70
112
  enumerize:
71
- sex:
72
- male: "Male"
73
- female: "Female"
113
+ defaults:
114
+ sex:
115
+ male: "Male"
116
+ female: "Female"
117
+ ```
118
+
119
+ You can also pass `i18n_scope` option to specify scope (or array of scopes) storing the translations.
120
+
121
+
122
+ ```ruby
123
+ class Person
124
+ extend Enumerize
125
+ extend ActiveModel::Naming
126
+
127
+ enumerize :sex, in: %w[male female], i18n_scope: "sex"
128
+ enumerize :color, in: %w[black white], i18n_scope: ["various.colors", "colors"]
129
+ end
130
+
131
+ # localization file
132
+ en:
133
+ sex:
134
+ male: "Male"
135
+ female: "Female"
136
+ various:
137
+ colors:
138
+ black: "Black"
139
+ colors:
140
+ white: "White"
141
+ ```
142
+
143
+ Note that if you want to use I18n feature with plain Ruby object don't forget to extend it with `ActiveModel::Naming`:
144
+
145
+ ```ruby
146
+ class User
147
+ extend Enumerize
148
+ extend ActiveModel::Naming
149
+ end
74
150
  ```
75
151
 
76
152
  get attribute value:
@@ -85,7 +161,7 @@ get all values for enumerized attribute:
85
161
  User.sex.values # or User.enumerized_attributes[:sex].values
86
162
  ```
87
163
 
88
- use it with forms:
164
+ use it with forms (it supports `:only` and `:except` options):
89
165
 
90
166
  ```erb
91
167
  <%= form_for @user do |f| %>
@@ -101,13 +177,49 @@ user.sex.male? #=> true
101
177
  user.sex.female? #=> false
102
178
  ```
103
179
 
180
+ Predicate methods:
181
+
182
+ ```ruby
183
+ class User
184
+ extend Enumerize
185
+
186
+ enumerize :sex, in: %w(male female), predicates: true
187
+ end
188
+
189
+ user = User.new
190
+
191
+ user.male? # => false
192
+ user.female? # => false
193
+
194
+ user.sex = 'male'
195
+
196
+ user.male? # => true
197
+ user.female? # => false
198
+ ```
199
+ :warning: If `enumerize` is used with Mongoid, it's not recommended to use `"writer"` as a field value since `writer?` is defined by Mongoid. [See more](https://github.com/brainspec/enumerize/issues/235). :warning:
200
+
201
+ Using prefix:
202
+
203
+ ```ruby
204
+ class User
205
+ extend Enumerize
206
+
207
+ enumerize :sex, in: %w(male female), predicates: { prefix: true }
208
+ end
209
+
210
+ user = User.new
211
+ user.sex = 'female'
212
+ user.sex_female? # => true
213
+ ```
214
+ Use `:only` and `:except` options to specify what values create predicate methods for.
215
+
104
216
  To make some attributes shared across different classes it's possible to define them in a separate module and then include it into classes:
105
217
 
106
218
  ```ruby
107
219
  module PersonEnumerations
108
- include Enumerize
220
+ extend Enumerize
109
221
 
110
- enumerize :sex, :in => %w[male female]
222
+ enumerize :sex, in: %w[male female]
111
223
  end
112
224
 
113
225
  class Person
@@ -123,7 +235,99 @@ It's also possible to store enumerized attribute value using custom values (e.g.
123
235
 
124
236
  ```ruby
125
237
  class User < ActiveRecord::Base
126
- enumerize :role, :in => {:user => 1, :admin => 2}
238
+ extend Enumerize
239
+
240
+ enumerize :role, in: {:user => 1, :admin => 2}
241
+ end
242
+
243
+ user = User.new
244
+ user.role = :user
245
+ user.role #=> 'user'
246
+ user.role_value #=> 1
247
+
248
+ User.role.find_value(:user).value #=> 1
249
+ User.role.find_value(:admin).value #=> 2
250
+ ```
251
+
252
+ ActiveRecord scopes:
253
+
254
+ ```ruby
255
+ class User < ActiveRecord::Base
256
+ extend Enumerize
257
+ enumerize :sex, :in => [:male, :female], scope: true
258
+ enumerize :status, :in => { active: 1, blocked: 2 }, scope: :having_status
259
+ end
260
+
261
+ User.with_sex(:female)
262
+ # SELECT "users".* FROM "users" WHERE "users"."sex" IN ('female')
263
+
264
+ User.without_sex(:male)
265
+ # SELECT "users".* FROM "users" WHERE "users"."sex" NOT IN ('male')
266
+
267
+ User.having_status(:blocked).with_sex(:male, :female)
268
+ # SELECT "users".* FROM "users" WHERE "users"."status" IN (2) AND "users"."sex" IN ('male', 'female')
269
+ ```
270
+
271
+ Shallow scopes:
272
+
273
+ Adds named scopes to the class directly
274
+
275
+ ```ruby
276
+ class User < ActiveRecord::Base
277
+ extend Enumerize
278
+ enumerize :sex, :in => [:male, :female], scope: :shallow
279
+ enumerize :status, :in => { active: 1, blocked: 2 }, scope: :shallow
280
+ end
281
+
282
+ User.male
283
+ # SELECT "users".* FROM "users" WHERE "users"."sex" = 'male'
284
+
285
+ User.active
286
+ # SELECT "users".* FROM "users" WHERE "users"."status" = 1
287
+ ```
288
+
289
+ :warning: It is not possible to define a scope when using the `:multiple` option. :warning:
290
+
291
+ Array-like attributes with plain ruby objects:
292
+
293
+ ```ruby
294
+ class User
295
+ extend Enumerize
296
+
297
+ enumerize :interests, in: [:music, :sports], multiple: true
298
+ end
299
+
300
+ user = User.new
301
+ user.interests << :music
302
+ user.interests << :sports
303
+ ```
304
+
305
+ and with ActiveRecord:
306
+
307
+ ```ruby
308
+ class User < ActiveRecord::Base
309
+ extend Enumerize
310
+
311
+ serialize :interests, Array
312
+ enumerize :interests, in: [:music, :sports], multiple: true
313
+ end
314
+ ```
315
+
316
+ get an array of all text values:
317
+
318
+ ```ruby
319
+ @user.interests.texts # shortcut for @user.interests.map(&:text)
320
+ ```
321
+
322
+ Also, the reader method can be overridden, referencing the enumerized attribute value using `super`:
323
+
324
+ ```ruby
325
+ def sex
326
+ if current_user.admin?
327
+ "Super#{super}"
328
+ else
329
+ super
330
+ end
127
331
  end
128
332
  ```
129
333
 
@@ -145,6 +349,8 @@ and if you want it as radio buttons:
145
349
  <% end %>
146
350
  ```
147
351
 
352
+ Please note that Enumerize overwrites the I18n keys of SimpleForm collections. The enumerized keys are used instead of the SimpleForm ones for inputs concerning enumerized attributes. If you don't want this just pass `:collection` option to the `input` call.
353
+
148
354
  ### Formtastic
149
355
 
150
356
  If you are using Formtastic gem you also don't need to specify input type (`:select` by default) and collection:
@@ -163,6 +369,188 @@ and if you want it as radio buttons:
163
369
  <% end %>
164
370
  ```
165
371
 
372
+ ### RSpec
373
+
374
+ Also you can use builtin RSpec matcher:
375
+
376
+ ```ruby
377
+ class User
378
+ extend Enumerize
379
+
380
+ enumerize :sex, in: [:male, :female]
381
+ end
382
+
383
+ describe User do
384
+ it { should enumerize(:sex) }
385
+
386
+ # or with RSpec 3 expect syntax
387
+ it { is_expected.to enumerize(:sex) }
388
+ end
389
+ ```
390
+
391
+ #### Qualifiers
392
+
393
+ ##### in
394
+
395
+ Use `in` to test usage of the `:in` option.
396
+
397
+ ```ruby
398
+ class User
399
+ extend Enumerize
400
+
401
+ enumerize :sex, in: [:male, :female]
402
+ end
403
+
404
+ describe User do
405
+ it { should enumerize(:sex).in(:male, :female) }
406
+ end
407
+ ```
408
+
409
+ You can test enumerized attribute value using custom values with the `in`
410
+ qualifier.
411
+
412
+ ```ruby
413
+ class User
414
+ extend Enumerize
415
+
416
+ enumerize :sex, in: { male: 0, female: 1 }
417
+ end
418
+
419
+ describe User do
420
+ it { should enumerize(:sex).in(male: 0, female: 1) }
421
+ end
422
+ ```
423
+
424
+ ##### with_default
425
+
426
+ Use `with_default` to test usage of the `:default` option.
427
+
428
+ ```ruby
429
+ class User
430
+ extend Enumerize
431
+
432
+ enumerize :sex, in: [:male, :female], default: :female
433
+ end
434
+
435
+ describe User do
436
+ it { should enumerize(:sex).in(:male, :female).with_default(:female) }
437
+ end
438
+ ```
439
+
440
+ ##### with_i18n_scope
441
+
442
+ Use `with_i18n_scope` to test usage of the `:i18n_scope` option.
443
+
444
+ ```ruby
445
+ class User
446
+ extend Enumerize
447
+
448
+ enumerize :sex, in: [:male, :female], i18n_scope: 'sex'
449
+ end
450
+
451
+ describe User do
452
+ it { should enumerize(:sex).in(:male, :female).with_i18n_scope('sex') }
453
+ end
454
+ ```
455
+
456
+ ##### with_predicates
457
+
458
+ Use `with_predicates` to test usage of the `:predicates` option.
459
+
460
+ ```ruby
461
+ class User
462
+ extend Enumerize
463
+
464
+ enumerize :sex, in: [:male, :female], predicates: true
465
+ end
466
+
467
+ describe User do
468
+ it { should enumerize(:sex).in(:male, :female).with_predicates(true) }
469
+ end
470
+ ```
471
+
472
+ You can text prefixed predicates with the `with_predicates` qualifiers.
473
+
474
+ ```ruby
475
+ class User
476
+ extend Enumerize
477
+
478
+ enumerize :sex, in: [:male, :female], predicates: { prefix: true }
479
+ end
480
+
481
+ describe User do
482
+ it { should enumerize(:sex).in(:male, :female).with_predicates(prefix: true) }
483
+ end
484
+ ```
485
+
486
+ ##### with_scope
487
+
488
+ Use `with_scope` to test usage of the `:scope` option.
489
+
490
+ ```ruby
491
+ class User
492
+ extend Enumerize
493
+
494
+ enumerize :sex, in: [:male, :female], scope: true
495
+ end
496
+
497
+ describe User do
498
+ it { should enumerize(:sex).in(:male, :female).with_scope(true) }
499
+ end
500
+ ```
501
+
502
+ You can text custom scope with the `with_scope` qualifiers.
503
+
504
+ ```ruby
505
+ class User
506
+ extend Enumerize
507
+
508
+ enumerize :sex, in: [:male, :female], scope: :having_sex
509
+ end
510
+
511
+ describe User do
512
+ it { should enumerize(:sex).in(:male, :female).with_scope(scope: :having_sex) }
513
+ end
514
+ ```
515
+
516
+ ##### with_multiple
517
+
518
+ Use `with_multiple` to test usage of the `:multiple` option.
519
+
520
+ ```ruby
521
+ class User
522
+ extend Enumerize
523
+
524
+ enumerize :sex, in: [:male, :female], multiple: true
525
+ end
526
+
527
+ describe User do
528
+ it { should enumerize(:sex).in(:male, :female).with_multiple(true) }
529
+ end
530
+ ```
531
+
532
+ ### Minitest with Shoulda
533
+
534
+ You can use the RSpec matcher with shoulda in your tests by adding two lines in your `test_helper.rb` inside `class ActiveSupport::TestCase` definition:
535
+
536
+ ```ruby
537
+ class ActiveSupport::TestCase
538
+ ActiveRecord::Migration.check_pending!
539
+
540
+ require 'enumerize/integrations/rspec'
541
+ extend Enumerize::Integrations::RSpec
542
+
543
+ ...
544
+ end
545
+ ```
546
+
547
+ ### Other Integrations
548
+
549
+ Enumerize integrates with the following automatically:
550
+
551
+ * [RailsAdmin](https://github.com/sferik/rails_admin/)
552
+
553
+
166
554
  ## Contributing
167
555
 
168
556
  1. Fork it
data/Rakefile CHANGED
@@ -1,11 +1,17 @@
1
1
  #!/usr/bin/env rake
2
+ # frozen_string_literal: true
3
+
2
4
  require "bundler/gem_tasks"
3
5
 
4
6
  require 'rake/testtask'
7
+ require 'rspec/core/rake_task'
8
+
5
9
  Rake::TestTask.new do |t|
6
10
  t.libs << 'test'
7
11
  t.pattern = 'test/*_test.rb'
8
12
  t.verbose = true
9
13
  end
10
14
 
11
- task :default => :test
15
+ RSpec::Core::RakeTask.new
16
+
17
+ task :default => [:test, :spec]
data/enumerize.gemspec CHANGED
@@ -4,8 +4,9 @@ require File.expand_path('../lib/enumerize/version', __FILE__)
4
4
  Gem::Specification.new do |gem|
5
5
  gem.authors = ["Sergey Nartimov"]
6
6
  gem.email = "team@brainspec.com"
7
- gem.description = %q{Enumerated attributes with I18n and ActiveRecord/Mongoid support}
8
- gem.summary = %q{Enumerated attributes with I18n and ActiveRecord/Mongoid support}
7
+ gem.licenses = ['MIT']
8
+ gem.description = %q{Enumerated attributes with I18n and ActiveRecord/Mongoid/MongoMapper support}
9
+ gem.summary = %q{Enumerated attributes with I18n and ActiveRecord/Mongoid/MongoMapper support}
9
10
  gem.homepage = "https://github.com/brainspec/enumerize"
10
11
 
11
12
  gem.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
@@ -15,5 +16,7 @@ Gem::Specification.new do |gem|
15
16
  gem.require_paths = ["lib"]
16
17
  gem.version = Enumerize::VERSION
17
18
 
18
- gem.add_dependency('activesupport', '>= 3.1.3')
19
+ gem.required_ruby_version = '>= 1.9.3'
20
+
21
+ gem.add_dependency('activesupport', '>= 3.2')
19
22
  end
@@ -0,0 +1,47 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Enumerize
4
+ module ActiveModelAttributesSupport
5
+ def enumerize(name, options={})
6
+ super
7
+
8
+ _enumerize_module.dependent_eval do
9
+ if self.included_modules.include? ::ActiveModel::Attributes
10
+ include InstanceMethods
11
+
12
+ attribute name, Enumerize::ActiveModelAttributesSupport::Type.new(enumerized_attributes[name])
13
+ end
14
+ end
15
+ end
16
+
17
+ module InstanceMethods
18
+ # https://github.com/brainspec/enumerize/issues/74
19
+ def write_attribute(attr_name, value, *options)
20
+ if self.class.enumerized_attributes[attr_name]
21
+ _enumerized_values_for_validation[attr_name.to_s] = value
22
+ end
23
+
24
+ super
25
+ end
26
+ end
27
+
28
+ class Type < ActiveModel::Type::Value
29
+ def type
30
+ :enumerize
31
+ end
32
+
33
+ def initialize(attr)
34
+ @attr = attr
35
+ end
36
+
37
+ def serialize(value)
38
+ v = @attr.find_value(value)
39
+ v && v.value
40
+ end
41
+
42
+ def deserialize(value)
43
+ @attr.find_value(value)
44
+ end
45
+ end
46
+ end
47
+ end