enumerize 2.4.0 → 2.5.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.
data/README.md CHANGED
@@ -2,6 +2,34 @@
2
2
 
3
3
  Enumerated attributes with I18n and ActiveRecord/Mongoid/MongoMapper/Sequel support
4
4
 
5
+ ## Table of Contents
6
+
7
+ - [Installation](#installation)
8
+ - [Supported Versions](#supported-versions)
9
+ - [Usage](#usage)
10
+ - [Database support](#database-support)
11
+ - [ActiveRecord](#activerecord)
12
+ - [Mongoid](#mongoid)
13
+ - [MongoMapper](#mongomapper)
14
+ - [I18n Support](#i18n-support)
15
+ - [I18n Helper Methods](#i18n-helper-methods)
16
+ - [Boolean Helper Methods](#boolean-helper-methods)
17
+ - [Basic](#basic)
18
+ - [Predicate Methods](#predicate-methods)
19
+ - [Optimzations and Tips](#optimzations-and-tips)
20
+ - [Extendable Module](#extendable-module)
21
+ - [Customizing Enumerize Value](#customizing-enumerize-value)
22
+ - [ActiveRecord scopes](#activerecord-scopes)
23
+ - [Array-like Attributes](#array-like-attributes)
24
+ - [Forms](#forms)
25
+ - [SimpleForm](#simpleform)
26
+ - [Formtastic](#formtastic)
27
+ - [Testing](#testing)
28
+ - [RSpec](#rspec)
29
+ - [Minitest with Shoulda](#minitest-with-shoulda)
30
+ - [Other Integrations](#other-integrations)
31
+ - [Contributing](#contributing)
32
+
5
33
  ## Installation
6
34
 
7
35
  Add this line to your application's Gemfile:
@@ -17,7 +45,8 @@ Or install it yourself as:
17
45
  $ gem install enumerize
18
46
 
19
47
  ## Supported Versions
20
- - Ruby 2.5+
48
+
49
+ - Ruby 2.6+
21
50
  - Rails 5.2+
22
51
 
23
52
  ## Usage
@@ -28,20 +57,23 @@ Basic:
28
57
  class User
29
58
  extend Enumerize
30
59
 
31
- enumerize :sex, in: [:male, :female]
60
+ enumerize :role, in: [:user, :admin]
32
61
  end
33
62
  ```
34
63
 
35
- Note that enumerized values are just identificators so if you want to use multi-word, etc. values you should use `I18n` feature.
64
+ Note that enumerized values are just identificators so if you want to use multi-word, etc. values then you should use `I18n` feature.
36
65
 
66
+ ---
37
67
 
38
- ActiveRecord:
68
+ ## Database support
69
+
70
+ ### ActiveRecord
39
71
 
40
72
  ```ruby
41
73
  class CreateUsers < ActiveRecord::Migration
42
74
  def change
43
75
  create_table :users do |t|
44
- t.string :sex
76
+ t.string :status
45
77
  t.string :role
46
78
 
47
79
  t.timestamps
@@ -52,7 +84,7 @@ end
52
84
  class User < ActiveRecord::Base
53
85
  extend Enumerize
54
86
 
55
- enumerize :sex, in: [:male, :female], default: lambda { |user| SexIdentifier.sex_for_name(user.name).to_sym }
87
+ enumerize :status, in: [:student, :employed, :retired], default: lambda { |user| StatusIdentifier.status_for_age(user.age).to_sym }
56
88
 
57
89
  enumerize :role, in: [:user, :admin], default: :user
58
90
  end
@@ -64,13 +96,13 @@ end
64
96
  class User < ActiveRecord::Base
65
97
  extend Enumerize
66
98
 
67
- enumerize :sex, in: [:male, :female], skip_validations: lambda { |user| user.new_record? }
99
+ enumerize :status, in: [:student, :employed, :retired], skip_validations: lambda { |user| user.new_record? }
68
100
 
69
101
  enumerize :role, in: [:user, :admin], skip_validations: true
70
102
  end
71
103
  ```
72
104
 
73
- Mongoid:
105
+ ### Mongoid
74
106
 
75
107
  ```ruby
76
108
  class User
@@ -82,7 +114,7 @@ class User
82
114
  end
83
115
  ```
84
116
 
85
- MongoMapper:
117
+ ### MongoMapper
86
118
 
87
119
  ```ruby
88
120
  class User
@@ -94,50 +126,54 @@ class User
94
126
  end
95
127
  ```
96
128
 
97
- I18n:
129
+ ---
130
+
131
+ ## I18n Support
98
132
 
99
133
  ```ruby
100
134
  en:
101
135
  enumerize:
102
136
  user:
103
- sex:
104
- male: "Male"
105
- female: "Female"
137
+ status:
138
+ student: "Student"
139
+ employed: "Employed"
140
+ retired: "Retiree"
106
141
  ```
107
142
 
108
- or if you use `sex` attribute across several models you can use `defaults` scope:
143
+ or if you use `status` attribute across several models you can use `defaults` scope:
109
144
 
110
145
  ```ruby
111
146
  en:
112
147
  enumerize:
113
148
  defaults:
114
- sex:
115
- male: "Male"
116
- female: "Female"
149
+ status:
150
+ student: "Student"
151
+ employed: "Employed"
152
+ retired: "Retiree"
117
153
  ```
118
154
 
119
155
  You can also pass `i18n_scope` option to specify scope (or array of scopes) storing the translations.
120
156
 
121
-
122
157
  ```ruby
123
158
  class Person
124
159
  extend Enumerize
125
160
  extend ActiveModel::Naming
126
161
 
127
- enumerize :sex, in: %w[male female], i18n_scope: "sex"
128
- enumerize :color, in: %w[black white], i18n_scope: ["various.colors", "colors"]
162
+ enumerize :status, in: %w[student employed retired], i18n_scope: "status"
163
+ enumerize :roles, in: %w[user admin], i18n_scope: ["user.roles", "roles"]
129
164
  end
130
165
 
131
166
  # localization file
132
167
  en:
133
- sex:
134
- male: "Male"
135
- female: "Female"
136
- various:
137
- colors:
138
- black: "Black"
139
- colors:
140
- white: "White"
168
+ status:
169
+ student: "Student"
170
+ employed: "Employed"
171
+ retired: "Retiree"
172
+ user:
173
+ roles:
174
+ user: "User"
175
+ roles:
176
+ admin: "Admin"
141
177
  ```
142
178
 
143
179
  Note that if you want to use I18n feature with plain Ruby object don't forget to extend it with `ActiveModel::Naming`:
@@ -149,88 +185,120 @@ class User
149
185
  end
150
186
  ```
151
187
 
152
- get attribute value:
188
+ ### I18n Helper Methods
189
+
190
+ #### \*\_text / .text
191
+
192
+ Attribute's I18n text value:
193
+
194
+ ```ruby
195
+ @user.status_text # or @user.status.text
196
+ ```
197
+
198
+ #### values
199
+
200
+ List of possible values for an enumerized attribute:
153
201
 
154
202
  ```ruby
155
- @user.sex_text # or @user.sex.text
203
+ User.status.values # or User.enumerized_attributes[:status].values
204
+ # => ['student', 'employed', 'retired']
156
205
  ```
157
206
 
158
- get all values for enumerized attribute:
207
+ #### I18n text values
208
+
209
+ List of possible I18n text values for an enumerized attribute:
159
210
 
160
211
  ```ruby
161
- User.sex.values # or User.enumerized_attributes[:sex].values
212
+ User.status.values.collect(&:text)
213
+ # => ['Student', 'Employed', 'Retiree']
162
214
  ```
163
215
 
164
- use it with forms (it supports `:only` and `:except` options):
216
+ #### Form example
217
+
218
+ Use it with forms (it supports `:only` and `:except` options):
165
219
 
166
220
  ```erb
167
221
  <%= form_for @user do |f| %>
168
- <%= f.select :sex, User.sex.options %>
222
+ <%= f.select :status, User.status.options %>
169
223
  <% end %>
170
224
  ```
171
225
 
172
- Boolean methods:
226
+ ---
227
+
228
+ ## Boolean Helper Methods
229
+
230
+ ### Basic
173
231
 
174
232
  ```ruby
175
- user.sex = :male
176
- user.sex.male? #=> true
177
- user.sex.female? #=> false
233
+ user.status = :student
234
+ user.status.student? #=> true
235
+ user.status.retired? #=> false
178
236
  ```
179
237
 
180
- Predicate methods:
238
+ ### Predicate Methods
181
239
 
182
240
  ```ruby
183
241
  class User
184
242
  extend Enumerize
185
243
 
186
- enumerize :sex, in: %w(male female), predicates: true
244
+ enumerize :status, in: %w(student employed retired), predicates: true
187
245
  end
188
246
 
189
247
  user = User.new
190
248
 
191
- user.male? # => false
192
- user.female? # => false
249
+ user.student? # => false
250
+ user.employed? # => false
193
251
 
194
- user.sex = 'male'
252
+ user.status = :student
195
253
 
196
- user.male? # => true
197
- user.female? # => false
254
+ user.student? # => true
255
+ user.employed? # => false
198
256
  ```
257
+
199
258
  :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
259
 
201
- Using prefix:
260
+ #### Predicate Prefixes
202
261
 
203
262
  ```ruby
204
263
  class User
205
264
  extend Enumerize
206
265
 
207
- enumerize :sex, in: %w(male female), predicates: { prefix: true }
266
+ enumerize :status, in: %w(student employed retired), predicates: { prefix: true }
208
267
  end
209
268
 
210
269
  user = User.new
211
- user.sex = 'female'
212
- user.sex_female? # => true
270
+ user.status = 'student'
271
+ user.status_student? # => true
213
272
  ```
273
+
214
274
  Use `:only` and `:except` options to specify what values create predicate methods for.
215
275
 
276
+ ---
277
+
278
+ ## Optimzations and Tips
279
+
280
+ ### Extendable Module
281
+
216
282
  To make some attributes shared across different classes it's possible to define them in a separate module and then include it into classes:
217
283
 
218
284
  ```ruby
219
- module PersonEnumerations
285
+ module RoleEnumerations
220
286
  extend Enumerize
221
287
 
222
- enumerize :sex, in: %w[male female]
288
+ enumerize :roles, in: %w[user admin]
223
289
  end
224
290
 
225
- class Person
226
- include PersonEnumerations
291
+ class Buyer
292
+ include RoleEnumerations
227
293
  end
228
294
 
229
- class User
230
- include PersonEnumerations
295
+ class Seller
296
+ include RoleEnumerations
231
297
  end
232
298
  ```
233
299
 
300
+ ### Customizing Enumerize Value
301
+
234
302
  It's also possible to store enumerized attribute value using custom values (e.g. integers). You can pass a hash as `:in` option to achieve this:
235
303
 
236
304
  ```ruby
@@ -249,45 +317,49 @@ User.role.find_value(:user).value #=> 1
249
317
  User.role.find_value(:admin).value #=> 2
250
318
  ```
251
319
 
252
- ActiveRecord scopes:
320
+ ### ActiveRecord scopes:
321
+
322
+ #### Basic
253
323
 
254
324
  ```ruby
255
325
  class User < ActiveRecord::Base
256
326
  extend Enumerize
257
- enumerize :sex, :in => [:male, :female], scope: true
258
- enumerize :status, :in => { active: 1, blocked: 2 }, scope: :having_status
327
+ enumerize :role, :in => [:user, :admin], scope: true
328
+ enumerize :status, :in => { student: 1, employed: 2, retired: 3 }, scope: :having_status
259
329
  end
260
330
 
261
- User.with_sex(:female)
262
- # SELECT "users".* FROM "users" WHERE "users"."sex" IN ('female')
331
+ User.with_role(:admin)
332
+ # SELECT "users".* FROM "users" WHERE "users"."role" IN ('admin')
263
333
 
264
- User.without_sex(:male)
265
- # SELECT "users".* FROM "users" WHERE "users"."sex" NOT IN ('male')
334
+ User.without_role(:admin)
335
+ # SELECT "users".* FROM "users" WHERE "users"."role" NOT IN ('admin')
266
336
 
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')
337
+ User.having_status(:employed).with_role(:user, :admin)
338
+ # SELECT "users".* FROM "users" WHERE "users"."status" IN (2) AND "users"."role" IN ('user', 'admin')
269
339
  ```
270
340
 
271
- Shallow scopes:
341
+ #### Shallow Scopes
272
342
 
273
- Adds named scopes to the class directly
343
+ Adds named scopes to the class directly.
274
344
 
275
345
  ```ruby
276
346
  class User < ActiveRecord::Base
277
347
  extend Enumerize
278
- enumerize :sex, :in => [:male, :female], scope: :shallow
279
- enumerize :status, :in => { active: 1, blocked: 2 }, scope: :shallow
348
+ enumerize :status, :in => [:student, :employed, :retired], scope: :shallow
349
+ enumerize :role, :in => { user: 1, admin: 2 }, scope: :shallow
280
350
  end
281
351
 
282
- User.male
283
- # SELECT "users".* FROM "users" WHERE "users"."sex" = 'male'
352
+ User.student
353
+ # SELECT "users".* FROM "users" WHERE "users"."status" = 'student'
284
354
 
285
- User.active
286
- # SELECT "users".* FROM "users" WHERE "users"."status" = 1
355
+ User.admin
356
+ # SELECT "users".* FROM "users" WHERE "users"."role" = 2
287
357
  ```
288
358
 
289
359
  :warning: It is not possible to define a scope when using the `:multiple` option. :warning:
290
360
 
361
+ ### Array-like Attributes
362
+
291
363
  Array-like attributes with plain ruby objects:
292
364
 
293
365
  ```ruby
@@ -322,22 +394,26 @@ get an array of all text values:
322
394
  Also, the reader method can be overridden, referencing the enumerized attribute value using `super`:
323
395
 
324
396
  ```ruby
325
- def sex
397
+ def status
326
398
  if current_user.admin?
327
- "Super#{super}"
399
+ "Super #{super}"
328
400
  else
329
401
  super
330
402
  end
331
403
  end
332
404
  ```
333
405
 
406
+ ---
407
+
408
+ ## Forms
409
+
334
410
  ### SimpleForm
335
411
 
336
412
  If you are using SimpleForm gem you don't need to specify input type (`:select` by default) and collection:
337
413
 
338
414
  ```erb
339
415
  <%= simple_form_for @user do |f| %>
340
- <%= f.input :sex %>
416
+ <%= f.input :status %>
341
417
  <% end %>
342
418
  ```
343
419
 
@@ -345,7 +421,7 @@ and if you want it as radio buttons:
345
421
 
346
422
  ```erb
347
423
  <%= simple_form_for @user do |f| %>
348
- <%= f.input :sex, :as => :radio_buttons %>
424
+ <%= f.input :status, :as => :radio_buttons %>
349
425
  <% end %>
350
426
  ```
351
427
 
@@ -357,7 +433,7 @@ If you are using Formtastic gem you also don't need to specify input type (`:sel
357
433
 
358
434
  ```erb
359
435
  <%= semantic_form_for @user do |f| %>
360
- <%= f.input :sex %>
436
+ <%= f.input :status %>
361
437
  <% end %>
362
438
  ```
363
439
 
@@ -365,10 +441,14 @@ and if you want it as radio buttons:
365
441
 
366
442
  ```erb
367
443
  <%= semantic_form_for @user do |f| %>
368
- <%= f.input :sex, :as => :radio %>
444
+ <%= f.input :status, :as => :radio %>
369
445
  <% end %>
370
446
  ```
371
447
 
448
+ ---
449
+
450
+ ## Testing
451
+
372
452
  ### RSpec
373
453
 
374
454
  Also you can use builtin RSpec matcher:
@@ -377,14 +457,14 @@ Also you can use builtin RSpec matcher:
377
457
  class User
378
458
  extend Enumerize
379
459
 
380
- enumerize :sex, in: [:male, :female]
460
+ enumerize :status, in: [:student, :employed, :retired]
381
461
  end
382
462
 
383
463
  describe User do
384
- it { should enumerize(:sex) }
464
+ it { should enumerize(:status) }
385
465
 
386
466
  # or with RSpec 3 expect syntax
387
- it { is_expected.to enumerize(:sex) }
467
+ it { is_expected.to enumerize(:status) }
388
468
  end
389
469
  ```
390
470
 
@@ -398,11 +478,11 @@ Use `in` to test usage of the `:in` option.
398
478
  class User
399
479
  extend Enumerize
400
480
 
401
- enumerize :sex, in: [:male, :female]
481
+ enumerize :status, in: [:student, :employed, :retired]
402
482
  end
403
483
 
404
484
  describe User do
405
- it { should enumerize(:sex).in(:male, :female) }
485
+ it { should enumerize(:status).in(:student, :employed, :retired) }
406
486
  end
407
487
  ```
408
488
 
@@ -413,11 +493,11 @@ qualifier.
413
493
  class User
414
494
  extend Enumerize
415
495
 
416
- enumerize :sex, in: { male: 0, female: 1 }
496
+ enumerize :role, in: { user: 0, admin: 1 }
417
497
  end
418
498
 
419
499
  describe User do
420
- it { should enumerize(:sex).in(male: 0, female: 1) }
500
+ it { should enumerize(:role).in(user: 0, admin: 1) }
421
501
  end
422
502
  ```
423
503
 
@@ -429,11 +509,11 @@ Use `with_default` to test usage of the `:default` option.
429
509
  class User
430
510
  extend Enumerize
431
511
 
432
- enumerize :sex, in: [:male, :female], default: :female
512
+ enumerize :role, in: [:user, :admin], default: :user
433
513
  end
434
514
 
435
515
  describe User do
436
- it { should enumerize(:sex).in(:male, :female).with_default(:female) }
516
+ it { should enumerize(:user).in(:user, :admin).with_default(:user) }
437
517
  end
438
518
  ```
439
519
 
@@ -445,11 +525,11 @@ Use `with_i18n_scope` to test usage of the `:i18n_scope` option.
445
525
  class User
446
526
  extend Enumerize
447
527
 
448
- enumerize :sex, in: [:male, :female], i18n_scope: 'sex'
528
+ enumerize :status, in: [:student, :employed, :retired], i18n_scope: 'status'
449
529
  end
450
530
 
451
531
  describe User do
452
- it { should enumerize(:sex).in(:male, :female).with_i18n_scope('sex') }
532
+ it { should enumerize(:status).in(:student, :employed, :retired).with_i18n_scope('status') }
453
533
  end
454
534
  ```
455
535
 
@@ -461,11 +541,11 @@ Use `with_predicates` to test usage of the `:predicates` option.
461
541
  class User
462
542
  extend Enumerize
463
543
 
464
- enumerize :sex, in: [:male, :female], predicates: true
544
+ enumerize :status, in: [:student, :employed, :retired], predicates: true
465
545
  end
466
546
 
467
547
  describe User do
468
- it { should enumerize(:sex).in(:male, :female).with_predicates(true) }
548
+ it { should enumerize(:status).in(:student, :employed, :retired).with_predicates(true) }
469
549
  end
470
550
  ```
471
551
 
@@ -475,11 +555,11 @@ You can text prefixed predicates with the `with_predicates` qualifiers.
475
555
  class User
476
556
  extend Enumerize
477
557
 
478
- enumerize :sex, in: [:male, :female], predicates: { prefix: true }
558
+ enumerize :status, in: [:student, :employed, :retired], predicates: { prefix: true }
479
559
  end
480
560
 
481
561
  describe User do
482
- it { should enumerize(:sex).in(:male, :female).with_predicates(prefix: true) }
562
+ it { should enumerize(:status).in(:student, :employed, :retired).with_predicates(prefix: true) }
483
563
  end
484
564
  ```
485
565
 
@@ -491,25 +571,25 @@ Use `with_scope` to test usage of the `:scope` option.
491
571
  class User
492
572
  extend Enumerize
493
573
 
494
- enumerize :sex, in: [:male, :female], scope: true
574
+ enumerize :status, in: [:student, :employed, :retired], scope: true
495
575
  end
496
576
 
497
577
  describe User do
498
- it { should enumerize(:sex).in(:male, :female).with_scope(true) }
578
+ it { should enumerize(:status).in(:student, :employed, :retired).with_scope(true) }
499
579
  end
500
580
  ```
501
581
 
502
- You can text custom scope with the `with_scope` qualifiers.
582
+ You can test a custom scope with the `with_scope` qualifiers.
503
583
 
504
584
  ```ruby
505
585
  class User
506
586
  extend Enumerize
507
587
 
508
- enumerize :sex, in: [:male, :female], scope: :having_sex
588
+ enumerize :status, in: [:student, :employed], scope: :employable
509
589
  end
510
590
 
511
591
  describe User do
512
- it { should enumerize(:sex).in(:male, :female).with_scope(scope: :having_sex) }
592
+ it { should enumerize(:status).in(:student, :employed, :retired).with_scope(scope: :employable) }
513
593
  end
514
594
  ```
515
595
 
@@ -521,11 +601,11 @@ Use `with_multiple` to test usage of the `:multiple` option.
521
601
  class User
522
602
  extend Enumerize
523
603
 
524
- enumerize :sex, in: [:male, :female], multiple: true
604
+ enumerize :status, in: [:student, :employed, :retired], multiple: true
525
605
  end
526
606
 
527
607
  describe User do
528
- it { should enumerize(:sex).in(:male, :female).with_multiple(true) }
608
+ it { should enumerize(:status).in(:student, :employed, :retired).with_multiple(true) }
529
609
  end
530
610
  ```
531
611
 
@@ -548,8 +628,9 @@ end
548
628
 
549
629
  Enumerize integrates with the following automatically:
550
630
 
551
- * [RailsAdmin](https://github.com/sferik/rails_admin/)
631
+ - [RailsAdmin](https://github.com/sferik/rails_admin/)
552
632
 
633
+ ---
553
634
 
554
635
  ## Contributing
555
636
 
@@ -21,7 +21,11 @@ module Enumerize
21
21
  require 'enumerize/hooks/uniqueness'
22
22
 
23
23
  unless options[:multiple]
24
- if ::ActiveRecord.version >= ::Gem::Version.new("6.1.0.alpha")
24
+ if ::ActiveRecord.version >= ::Gem::Version.new("7.0.0.alpha")
25
+ attribute(name) do |subtype|
26
+ Type.new(enumerized_attributes[name], subtype)
27
+ end
28
+ elsif ::ActiveRecord.version >= ::Gem::Version.new("6.1.0.alpha")
25
29
  decorate_attribute_type(name.to_s) do |subtype|
26
30
  Type.new(enumerized_attributes[name], subtype)
27
31
  end
@@ -65,7 +69,7 @@ module Enumerize
65
69
  reloaded.class.enumerized_attributes.each do |attr|
66
70
  begin
67
71
  # Checks first if the enumerized attribute is in ActiveRecord::Store
68
- store_attr, _ = reloaded.class.stored_attributes.detect do |store_attr, keys|
72
+ store_attr, _ = reloaded.class.stored_attributes.detect do |_store_attr, keys|
69
73
  keys.include?(attr.name)
70
74
  end
71
75
 
@@ -42,6 +42,10 @@ module Enumerize
42
42
  define_singleton_method(value_obj) do
43
43
  where(attribute_name => value_obj.value)
44
44
  end
45
+
46
+ define_singleton_method("not_#{value_obj}") do
47
+ where.not(attribute_name => value_obj.value)
48
+ end
45
49
  end
46
50
  end
47
51
  end
@@ -39,6 +39,10 @@ module Enumerize
39
39
  define_singleton_method(value_obj) do
40
40
  self.in(attribute_name => value_obj.value)
41
41
  end
42
+
43
+ define_singleton_method("not_#{value_obj}") do
44
+ self.not_in(attribute_name => value_obj.value)
45
+ end
42
46
  end
43
47
  end
44
48
  end
@@ -45,6 +45,10 @@ module Enumerize
45
45
  def_dataset_method(value_obj) do
46
46
  where(attribute_name => value_obj.value.to_s)
47
47
  end
48
+
49
+ def_dataset_method("not_#{value_obj}") do
50
+ exclude(attribute_name => value_obj.value.to_s)
51
+ end
48
52
  end
49
53
  end
50
54
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Enumerize
4
- VERSION = '2.4.0'
4
+ VERSION = '2.5.0'
5
5
  end