enumerate_it 4.1.0 → 4.2.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 0e0e2aa1d31c14c5e310f50feb06824c19e7bffa49d08ab1f29d8f3a9b45e32d
4
- data.tar.gz: a6e1642362885b5e306c713dd54c859452c2392bb17c012e039c84bd8878f9e9
3
+ metadata.gz: 30af25c92670f35f261f3d6ad6dca3f302d3553d6a9a1adf0a91495402f11718
4
+ data.tar.gz: 5f8324535f04dc5553a4ef359cb0429294aa3257a23e3a9b7d2a9a08c344919e
5
5
  SHA512:
6
- metadata.gz: ea12eb1fe75185a0525f0580906391e37d5b540bcc09f273a2cac96c7e168994b184f549c54cd04af9940452355fd159d449c199db5855cdf0b0b03c209dd8f6
7
- data.tar.gz: f2793a073ffb559e162970e069bc5136d08b03e74dd77f885c1f27d041c48c7d25fb83de9c982bcfb4e1105149635272db5ef8ee4d86c228666c87318baae765
6
+ metadata.gz: 72f81a09f56ad298a98e21c4aa817e36f41b435ec747fb5f717b1004708877a2eef813aedf1127831126e1e7d6131f385bc9e1e39fcefb2aae3356f63d86c4ba
7
+ data.tar.gz: 993ecdc71d85a9c13eefef4f7129bf179de1d8af96694239691294f862427eeaffc28493b4e90457f6eaafff4744cce8c107a1ceb9c9cde6ab36a22ca2f4fe6d
@@ -9,11 +9,12 @@ jobs:
9
9
 
10
10
  matrix:
11
11
  ruby:
12
- - 3.0
13
- - 3.1
14
- - 3.2
15
- - 3.3
16
- - 3.4
12
+ - "3.0"
13
+ - "3.1"
14
+ - "3.2"
15
+ - "3.3"
16
+ - "3.4"
17
+ - "4.0"
17
18
  gemfile:
18
19
  - gemfiles/rails_6.0.gemfile
19
20
  - gemfiles/rails_6.1.gemfile
@@ -21,12 +22,27 @@ jobs:
21
22
  - gemfiles/rails_7.1.gemfile
22
23
  - gemfiles/rails_7.2.gemfile
23
24
  - gemfiles/rails_8.0.gemfile
25
+ - gemfiles/rails_8.1.gemfile
24
26
  exclude:
25
- # Rails 8 requires Ruby 3.2 or newer
27
+ # Rails 7.2+ requires Ruby 3.1 or newer
28
+ - ruby: 3.0
29
+ gemfile: gemfiles/rails_7.2.gemfile
30
+ # Rails 8+ requires Ruby 3.2 or newer
26
31
  - ruby: 3.0
27
32
  gemfile: gemfiles/rails_8.0.gemfile
33
+ - ruby: 3.0
34
+ gemfile: gemfiles/rails_8.1.gemfile
28
35
  - ruby: 3.1
29
36
  gemfile: gemfiles/rails_8.0.gemfile
37
+ - ruby: 3.1
38
+ gemfile: gemfiles/rails_8.1.gemfile
39
+ # Rails 6.x and 7.0 are EOL and don't support Ruby 4+
40
+ - ruby: 4.0
41
+ gemfile: gemfiles/rails_6.0.gemfile
42
+ - ruby: 4.0
43
+ gemfile: gemfiles/rails_6.1.gemfile
44
+ - ruby: 4.0
45
+ gemfile: gemfiles/rails_7.0.gemfile
30
46
 
31
47
  env:
32
48
  BUNDLE_GEMFILE: "${{ matrix.gemfile }}"
@@ -35,7 +51,7 @@ jobs:
35
51
 
36
52
  steps:
37
53
  - name: Checkout
38
- uses: actions/checkout@v4
54
+ uses: actions/checkout@v7
39
55
  - name: Set up Ruby
40
56
  uses: ruby/setup-ruby@v1
41
57
  with:
@@ -43,7 +59,7 @@ jobs:
43
59
  bundler-cache: true
44
60
 
45
61
  - name: Rubocop
46
- if: ${{ matrix.ruby == '3.4' }}
62
+ if: ${{ matrix.ruby == '4.0' }}
47
63
  run: "bundle exec rubocop"
48
64
 
49
65
  - name: Tests
data/.prettierrc.json ADDED
@@ -0,0 +1,4 @@
1
+ {
2
+ "printWidth": 80,
3
+ "proseWrap": "always"
4
+ }
data/.rubocop.yml CHANGED
@@ -8,7 +8,7 @@ inherit_mode:
8
8
 
9
9
  AllCops:
10
10
  NewCops: enable
11
- TargetRubyVersion: 3.4
11
+ TargetRubyVersion: 3.0 # Keep in sync with `required_ruby_version` in the gemspec.
12
12
 
13
13
  Exclude:
14
14
  - 'lib/generators/enumerate_it/enum/templates/**/*'
@@ -67,7 +67,11 @@ Style/HashTransformKeys:
67
67
  Style/HashTransformValues:
68
68
  Enabled: true
69
69
 
70
- Naming/PredicateName:
70
+ Style/OneClassPerFile:
71
+ Exclude:
72
+ - 'spec/support//**/*'
73
+
74
+ Naming/PredicatePrefix:
71
75
  Exclude:
72
76
  - 'lib/enumerate_it/class_methods.rb'
73
77
 
data/Appraisals CHANGED
@@ -4,7 +4,7 @@ require 'json'
4
4
  rails_versions = JSON.parse(Net::HTTP.get(URI('https://rubygems.org/api/v1/versions/rails.json')))
5
5
  .group_by { |version| version['number'] }.keys.grep_v(/rc|racecar|alpha|beta|pre/)
6
6
 
7
- %w[6.0 6.1 7.0 7.1 7.2 8.0].each do |rails_version|
7
+ %w[6.0 6.1 7.0 7.1 7.2 8.0 8.1].each do |rails_version|
8
8
  appraise "rails_#{rails_version}" do
9
9
  current_version = rails_versions
10
10
  .select { |key| key.match(/\A#{rails_version}/) }
@@ -19,6 +19,9 @@ rails_versions = JSON.parse(Net::HTTP.get(URI('https://rubygems.org/api/v1/versi
19
19
  gem 'sqlite3', '< 2' # Rails 6.x and 7.0 require sqlite3 v1.x
20
20
  end
21
21
 
22
+ # Rails versions before 8.1 pass the removed quirks_mode option to JSON 3.
23
+ gem 'json', '< 3' if Gem::Version.new(rails_version) < Gem::Version.new('8.1')
24
+
22
25
  # The following is likely necessary due to this issue (or something related):
23
26
  # https://stackoverflow.com/a/79385484/1445184
24
27
  if Gem::Version.new(rails_version) < Gem::Version.new(7.1)
data/Gemfile.lock CHANGED
@@ -1,26 +1,26 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- enumerate_it (4.1.0)
4
+ enumerate_it (4.2.1)
5
5
  activesupport (>= 6.0.0)
6
6
 
7
7
  GEM
8
8
  remote: https://rubygems.org/
9
9
  specs:
10
- activemodel (8.0.2)
11
- activesupport (= 8.0.2)
12
- activerecord (8.0.2)
13
- activemodel (= 8.0.2)
14
- activesupport (= 8.0.2)
10
+ activemodel (8.1.4)
11
+ activesupport (= 8.1.4)
12
+ activerecord (8.1.4)
13
+ activemodel (= 8.1.4)
14
+ activesupport (= 8.1.4)
15
15
  timeout (>= 0.4.0)
16
- activesupport (8.0.2)
16
+ activesupport (8.1.4)
17
17
  base64
18
- benchmark (>= 0.3)
19
18
  bigdecimal
20
19
  concurrent-ruby (~> 1.0, >= 1.3.1)
21
20
  connection_pool (>= 2.2.5)
22
21
  drb
23
22
  i18n (>= 1.6, < 2)
23
+ json
24
24
  logger (>= 1.4.2)
25
25
  minitest (>= 5.1)
26
26
  securerandom (>= 0.3)
@@ -31,80 +31,86 @@ GEM
31
31
  rake
32
32
  thor (>= 0.14.0)
33
33
  ast (2.4.3)
34
- base64 (0.2.0)
35
- benchmark (0.4.0)
36
- bigdecimal (3.1.9)
34
+ base64 (0.3.0)
35
+ bigdecimal (4.1.3)
37
36
  coderay (1.1.3)
38
- concurrent-ruby (1.3.5)
39
- connection_pool (2.5.3)
40
- diff-lcs (1.6.1)
41
- drb (2.2.1)
42
- i18n (1.14.7)
37
+ concurrent-ruby (1.3.8)
38
+ connection_pool (3.0.2)
39
+ diff-lcs (1.6.2)
40
+ drb (2.2.3)
41
+ i18n (1.15.2)
43
42
  concurrent-ruby (~> 1.0)
44
- json (2.11.3)
45
- language_server-protocol (3.17.0.4)
43
+ io-console (0.9.4)
44
+ json (3.0.2)
45
+ language_server-protocol (3.17.0.6)
46
46
  lint_roller (1.1.0)
47
47
  logger (1.7.0)
48
48
  method_source (1.1.0)
49
- mini_portile2 (2.8.8)
50
- minitest (5.25.5)
51
- parallel (1.27.0)
52
- parser (3.3.8.0)
49
+ mini_portile2 (2.8.9)
50
+ minitest (6.0.6)
51
+ drb (~> 2.0)
52
+ prism (~> 1.5)
53
+ parallel (2.3.0)
54
+ parser (3.3.12.0)
53
55
  ast (~> 2.4.1)
54
56
  racc
55
- prism (1.4.0)
56
- pry (0.15.2)
57
+ prism (1.9.0)
58
+ pry (0.16.0)
57
59
  coderay (~> 1.1)
58
60
  method_source (~> 1.0)
61
+ reline (>= 0.6.0)
59
62
  racc (1.8.1)
60
63
  rainbow (3.1.1)
61
- rake (13.2.1)
62
- regexp_parser (2.10.0)
63
- rspec (3.13.0)
64
+ rake (13.4.2)
65
+ regexp_parser (2.13.1)
66
+ reline (0.7.0)
67
+ io-console (~> 0.5)
68
+ rspec (3.13.2)
64
69
  rspec-core (~> 3.13.0)
65
70
  rspec-expectations (~> 3.13.0)
66
71
  rspec-mocks (~> 3.13.0)
67
- rspec-core (3.13.3)
72
+ rspec-core (3.13.6)
68
73
  rspec-support (~> 3.13.0)
69
- rspec-expectations (3.13.3)
74
+ rspec-expectations (3.13.5)
70
75
  diff-lcs (>= 1.2.0, < 2.0)
71
76
  rspec-support (~> 3.13.0)
72
- rspec-mocks (3.13.2)
77
+ rspec-mocks (3.13.8)
73
78
  diff-lcs (>= 1.2.0, < 2.0)
74
79
  rspec-support (~> 3.13.0)
75
- rspec-support (3.13.2)
76
- rubocop (1.75.4)
77
- json (~> 2.3)
80
+ rspec-support (3.13.7)
81
+ rubocop (1.92.0)
82
+ json (>= 2.3)
78
83
  language_server-protocol (~> 3.17.0.2)
79
84
  lint_roller (~> 1.1.0)
80
- parallel (~> 1.10)
85
+ parallel (>= 1.10)
81
86
  parser (>= 3.3.0.2)
82
87
  rainbow (>= 2.2.2, < 4.0)
83
- regexp_parser (>= 2.9.3, < 3.0)
84
- rubocop-ast (>= 1.44.0, < 2.0)
88
+ regexp_parser (>= 2.11.3, < 3.0)
89
+ rubocop-ast (>= 1.49.0, < 2.0)
85
90
  ruby-progressbar (~> 1.7)
86
91
  unicode-display_width (>= 2.4.0, < 4.0)
87
- rubocop-ast (1.44.1)
92
+ rubocop-ast (1.50.0)
88
93
  parser (>= 3.3.7.2)
89
- prism (~> 1.4)
94
+ prism (~> 1.7)
90
95
  rubocop-rake (0.7.1)
91
96
  lint_roller (~> 1.1)
92
97
  rubocop (>= 1.72.1)
93
- rubocop-rspec (3.6.0)
98
+ rubocop-rspec (3.10.2)
94
99
  lint_roller (~> 1.1)
95
- rubocop (~> 1.72, >= 1.72.1)
100
+ regexp_parser (>= 2.0)
101
+ rubocop (~> 1.86, >= 1.86.2)
96
102
  ruby-progressbar (1.13.0)
97
103
  securerandom (0.4.1)
98
- sqlite3 (1.7.3)
104
+ sqlite3 (2.9.6)
99
105
  mini_portile2 (~> 2.8.0)
100
- thor (1.3.2)
101
- timeout (0.4.3)
106
+ thor (1.5.0)
107
+ timeout (0.6.1)
102
108
  tzinfo (2.0.6)
103
109
  concurrent-ruby (~> 1.0)
104
- unicode-display_width (3.1.4)
105
- unicode-emoji (~> 4.0, >= 4.0.4)
106
- unicode-emoji (4.0.4)
107
- uri (1.0.3)
110
+ unicode-display_width (3.3.0)
111
+ unicode-emoji (~> 4.3)
112
+ unicode-emoji (4.3.0)
113
+ uri (1.1.2)
108
114
 
109
115
  PLATFORMS
110
116
  ruby
@@ -120,7 +126,59 @@ DEPENDENCIES
120
126
  rubocop
121
127
  rubocop-rake
122
128
  rubocop-rspec
123
- sqlite3 (< 2)
129
+ sqlite3
130
+
131
+ CHECKSUMS
132
+ activemodel (8.1.4) sha256=9a0906c127bd118aa5f06157db12b8f750da83dccee72db2f658547a0ae7a0e8
133
+ activerecord (8.1.4) sha256=3b4957ea7f246c9231bd7faa909312eac03a9101aaf87977b6c52733ff00b04f
134
+ activesupport (8.1.4) sha256=891dd3fe43c4fc8391934b237b1189bb1f661c932a84a840043db9d46a4e80df
135
+ appraisal (2.5.0) sha256=36989221be127913b0dba8d114da2001e6b2dceea7bd4951200eaba764eed3ce
136
+ ast (2.4.3) sha256=954615157c1d6a382bc27d690d973195e79db7f55e9765ac7c481c60bdb4d383
137
+ base64 (0.3.0) sha256=27337aeabad6ffae05c265c450490628ef3ebd4b67be58257393227588f5a97b
138
+ bigdecimal (4.1.3) sha256=61ebe1e5e559bdc3cc6f2c0ee7f427321fc838f59611c294356eb04d6e21cf66
139
+ bundler (4.0.22) sha256=d8d5ec84c8555e0af71db63ed7aee4d1a8fb839ec46d84212d61979242a5d75a
140
+ coderay (1.1.3) sha256=dc530018a4684512f8f38143cd2a096c9f02a1fc2459edcfe534787a7fc77d4b
141
+ concurrent-ruby (1.3.8) sha256=b2f1be836e968ccc78ccfce277ea79c72a88633f22306782c16ff23fb415d1e1
142
+ connection_pool (3.0.2) sha256=33fff5ba71a12d2aa26cb72b1db8bba2a1a01823559fb01d29eb74c286e62e0a
143
+ diff-lcs (1.6.2) sha256=9ae0d2cba7d4df3075fe8cd8602a8604993efc0dfa934cff568969efb1909962
144
+ drb (2.2.3) sha256=0b00d6fdb50995fe4a45dea13663493c841112e4068656854646f418fda13373
145
+ enumerate_it (4.2.1)
146
+ i18n (1.15.2) sha256=00f9eb62412fe593b2a65a97daa75300d37abb8f7202ec748e94b6d46a9dd1b5
147
+ io-console (0.9.4) sha256=a403304f32928ee85e56d457b4612ca43f2703b561a3258844992bd24085fbb1
148
+ json (3.0.2) sha256=8e6d7e7b11384c21230430cef90b71f14849a34a1f4452796670f7c981bd19df
149
+ language_server-protocol (3.17.0.6) sha256=5ef2c0c138f8267e1bc631d3328347d354f96724b0af22f2c79516120443b7f0
150
+ lint_roller (1.1.0) sha256=2c0c845b632a7d172cb849cc90c1bce937a28c5c8ccccb50dfd46a485003cc87
151
+ logger (1.7.0) sha256=196edec7cc44b66cfb40f9755ce11b392f21f7967696af15d274dde7edff0203
152
+ method_source (1.1.0) sha256=181301c9c45b731b4769bc81e8860e72f9161ad7d66dd99103c9ab84f560f5c5
153
+ mini_portile2 (2.8.9) sha256=0cd7c7f824e010c072e33f68bc02d85a00aeb6fce05bb4819c03dfd3c140c289
154
+ minitest (6.0.6) sha256=153ea36d1d987a62942382b61075745042a2b3123b1cd48f4c3675af9cc7d6f1
155
+ parallel (2.3.0) sha256=f75a3e904101ce6a1ccb6b8dc800cbafd42166d83330192438dcf29395167a6f
156
+ parser (3.3.12.0) sha256=21a6d7f755d5a24dfbdc6e6b772e4e879a52e7631a88bc5a3a134606052c9828
157
+ prism (1.9.0) sha256=7b530c6a9f92c24300014919c9dcbc055bf4cdf51ec30aed099b06cd6674ef85
158
+ pry (0.16.0) sha256=d76c69065698ed1f85e717bd33d7942c38a50868f6b0673c636192b3d1b6054e
159
+ racc (1.8.1) sha256=4a7f6929691dbec8b5209a0b373bc2614882b55fc5d2e447a21aaa691303d62f
160
+ rainbow (3.1.1) sha256=039491aa3a89f42efa1d6dec2fc4e62ede96eb6acd95e52f1ad581182b79bc6a
161
+ rake (13.4.2) sha256=cb825b2bd5f1f8e91ca37bddb4b9aaf345551b4731da62949be002fa89283701
162
+ regexp_parser (2.13.1) sha256=5aedb6b7c35688f51e86eef17a15374dfd287c83f4ca644b2c5f3ec50a33b44b
163
+ reline (0.7.0) sha256=5b012d8e55dbf9d450f12bde2cf7d15ff546ae80b3f8f3b30e570d431815583d
164
+ rspec (3.13.2) sha256=206284a08ad798e61f86d7ca3e376718d52c0bc944626b2349266f239f820587
165
+ rspec-core (3.13.6) sha256=a8823c6411667b60a8bca135364351dda34cd55e44ff94c4be4633b37d828b2d
166
+ rspec-expectations (3.13.5) sha256=33a4d3a1d95060aea4c94e9f237030a8f9eae5615e9bd85718fe3a09e4b58836
167
+ rspec-mocks (3.13.8) sha256=086ad3d3d17533f4237643de0b5c42f04b66348c28bf6b9c2d3f4a3b01af1d47
168
+ rspec-support (3.13.7) sha256=0640e5570872aafefd79867901deeeeb40b0c9875a36b983d85f54fb7381c47c
169
+ rubocop (1.92.0) sha256=2af68041fb2e70bd9321d0479b49fad07f8008a3d0758c83347b4917128c56e0
170
+ rubocop-ast (1.50.0) sha256=b9ca88300da0803ee222ad20cdb30494c0a784eed06fdc35d254b06d662788db
171
+ rubocop-rake (0.7.1) sha256=3797f2b6810c3e9df7376c26d5f44f3475eda59eb1adc38e6f62ecf027cbae4d
172
+ rubocop-rspec (3.10.2) sha256=0b3e2ecc592cd10ecbf0095bb58d1e357905276e069643523cc19eb7495f65e2
173
+ ruby-progressbar (1.13.0) sha256=80fc9c47a9b640d6834e0dc7b3c94c9df37f08cb072b7761e4a71e22cff29b33
174
+ securerandom (0.4.1) sha256=cc5193d414a4341b6e225f0cb4446aceca8e50d5e1888743fac16987638ea0b1
175
+ sqlite3 (2.9.6) sha256=956fe606956420d04ac7157d3ace620c8caba2135b2e05c76e483493da24d08e
176
+ thor (1.5.0) sha256=e3a9e55fe857e44859ce104a84675ab6e8cd59c650a49106a05f55f136425e73
177
+ timeout (0.6.1) sha256=78f57368a7e7bbadec56971f78a3f5ecbcfb59b7fcbb0a3ed6ddc08a5094accb
178
+ tzinfo (2.0.6) sha256=8daf828cc77bcf7d63b0e3bdb6caa47e2272dcfaf4fbfe46f8c3a9df087a829b
179
+ unicode-display_width (3.3.0) sha256=4b7aa66a4b11db50f6f7e98411215cd0dd10eaecb665d36eec5bcacb3fa0b613
180
+ unicode-emoji (4.3.0) sha256=11c02fa73290378c066bb0562cd4c87d8e1b706fbbe0059ca12746a4244de8ce
181
+ uri (1.1.2) sha256=89292e98eabbef6248b0a0ed5e7e398e152f83f17560023a6d94a7f492b169fb
124
182
 
125
183
  BUNDLED WITH
126
- 2.6.6
184
+ 4.0.22
data/README.md CHANGED
@@ -17,6 +17,32 @@ This means you can add it to any **Ruby** project! Secondly, you can
17
17
  [define your enumerations in classes](#creating-enumerations), allowing you to
18
18
  **add behavior** and **reuse** them! 😀
19
19
 
20
+ At a glance:
21
+
22
+ ```ruby
23
+ class RelationshipStatus < EnumerateIt::Base
24
+ associate_values :single, :married, :divorced
25
+
26
+ custom_helpers do
27
+ def ever_married?(value)
28
+ [MARRIED, DIVORCED].include?(value)
29
+ end
30
+ end
31
+ end
32
+
33
+ class Person < ApplicationRecord
34
+ has_enumeration_for :relationship_status, create_helpers: true, create_scopes: true
35
+ end
36
+
37
+ person = Person.new(relationship_status: RelationshipStatus::MARRIED)
38
+
39
+ person.married? #=> true
40
+ person.ever_married? #=> true
41
+ person.relationship_status_humanize #=> 'Married'
42
+ Person.married.to_sql #=> SELECT "people".* FROM "people" WHERE "people"."relationship_status" = "married"
43
+ RelationshipStatus.to_a #=> [['Divorced', 'divorced'], ['Married', 'married'], ['Single', 'single']]
44
+ ```
45
+
20
46
  ---
21
47
 
22
48
  <!-- Tocer[start]: Auto-generated, don't remove. -->
@@ -206,10 +232,13 @@ class Person
206
232
  end
207
233
  ```
208
234
 
209
- > **Note:** If the enumeration class name differs from the attribute name, use
210
- > the `with` option:
235
+ <!-- prettier-ignore -->
236
+ > [!NOTE]
237
+ > If the enumeration class name differs from the attribute name, use the `with` option:
211
238
  >
212
- > `has_enumeration_for :relationship_status, with: RelationshipStatus`
239
+ > ```ruby
240
+ > has_enumeration_for :relationship_status, with: RelationshipStatus
241
+ > ```
213
242
 
214
243
  This will create:
215
244
 
@@ -229,7 +258,7 @@ This will create:
229
258
  p = Person.new
230
259
  p.relationship_status = RelationshipStatus::DIVORCED
231
260
  p.relationship_status_humanize
232
- #=> 'Divorciado'
261
+ #=> 'Divorciado' # with a pt-BR locale loaded
233
262
  ```
234
263
 
235
264
  - The associated enumerations, which can be retrieved with the `enumerations`
@@ -335,6 +364,63 @@ This will create:
335
364
  #=> true
336
365
  ```
337
366
 
367
+ <!-- prettier-ignore -->
368
+ > [!IMPORTANT]
369
+ > On ActiveRecord objects, mutator methods like `married!` call `save!`
370
+ > immediately, running callbacks and raising on validation failure. They are
371
+ > not pure setters.
372
+
373
+ You can also define custom helper methods on the enumeration class using
374
+ a `custom_helpers` block:
375
+
376
+ ```ruby
377
+ class RelationshipStatus < EnumerateIt::Base
378
+ associate_values :single, :married, :divorced
379
+
380
+ custom_helpers do
381
+ def ever_married?(value)
382
+ [MARRIED, DIVORCED].include?(value)
383
+ end
384
+ end
385
+ end
386
+ ```
387
+
388
+ These become class methods on the enumeration:
389
+
390
+ ```ruby
391
+ RelationshipStatus.ever_married?(RelationshipStatus::MARRIED) #=> true
392
+ RelationshipStatus.ever_married?(RelationshipStatus::DIVORCED) #=> true
393
+ RelationshipStatus.ever_married?(RelationshipStatus::SINGLE) #=> false
394
+ ```
395
+
396
+ When `create_helpers: true` is used, they also become instance methods on the
397
+ model:
398
+
399
+ ```ruby
400
+ class Person < ApplicationRecord
401
+ has_enumeration_for :relationship_status, with: RelationshipStatus, create_helpers: true
402
+ end
403
+
404
+ person = Person.new(relationship_status: RelationshipStatus::DIVORCED)
405
+ person.ever_married? #=> true
406
+ ```
407
+
408
+ The `prefix` option also applies to custom helpers:
409
+
410
+ ```ruby
411
+ class Person < ApplicationRecord
412
+ has_enumeration_for :relationship_status,
413
+ with: RelationshipStatus, create_helpers: { prefix: true }
414
+ end
415
+
416
+ person = Person.new(relationship_status: RelationshipStatus::DIVORCED)
417
+ person.relationship_status_ever_married? #=> true
418
+ ```
419
+
420
+ Custom helper methods return `nil` when the attribute is `nil`. If a method
421
+ name collides with an already-defined class method on the enumeration, an
422
+ `ArgumentError` is raised at load time.
423
+
338
424
  - A scope method for each enumeration option if you pass the `create_scopes`
339
425
  option as `true`:
340
426
 
@@ -344,7 +430,7 @@ This will create:
344
430
  end
345
431
 
346
432
  Person.married.to_sql
347
- #=> SELECT "users".* FROM "users" WHERE "users"."relationship_status" = "married"
433
+ #=> SELECT "people".* FROM "people" WHERE "people"."relationship_status" = "married"
348
434
  ```
349
435
 
350
436
  The `:create_scopes` also accepts `prefix` option.
@@ -401,6 +487,12 @@ This will create:
401
487
  #=> true
402
488
  ```
403
489
 
490
+ <!-- prettier-ignore -->
491
+ > [!NOTE]
492
+ > `skip_validation: true` disables all validations added by EnumerateIt,
493
+ > including the presence validation from `required: true`. If both are
494
+ > passed, `skip_validation` wins and `required` is silently ignored.
495
+
404
496
  Remember that you can add validations to any kind of class and not only
405
497
  `ActiveRecord` ones.
406
498
 
@@ -431,7 +523,7 @@ Yes,
431
523
 
432
524
  ## I18n
433
525
 
434
- I18n lookup is provided for both `_humanized` and `Enumeration#to_a` methods,
526
+ I18n lookup is provided for both `_humanize` and `Enumeration#to_a` methods,
435
527
  given the hash key is a Symbol. The I18n strings are located on
436
528
  `enumerations.<enumeration_name>.<key>`:
437
529
 
@@ -453,11 +545,11 @@ end
453
545
 
454
546
  p = Person.new
455
547
  p.relationship_status = RelationshipStatus::MARRIED
456
- p.relationship_status_humanize # Existent key
548
+ p.relationship_status_humanize # Key found in the locale file
457
549
  #=> 'Casado'
458
550
 
459
551
  p.relationship_status = RelationshipStatus::SINGLE
460
- p.relationship_status_humanize # Non-existent key
552
+ p.relationship_status_humanize # Key missing, falls back to the humanized name
461
553
  #=> 'Single'
462
554
  ```
463
555
 
@@ -550,12 +642,12 @@ you can see them on the [releases page](../../releases).
550
642
  - Add tests for it. This is important so we don't break it in a future version
551
643
  unintentionally.
552
644
  - [Optional] Run the tests against a specific Gemfile:
553
- `bundle exec appraisal rails_8.0 rake spec`.
645
+ `bundle exec appraisal rails_8.1 rake spec`.
554
646
  - Run the tests against all supported versions: `bundle exec rake`
555
647
  - Commit, but please do not mess with `Rakefile`, version, or history.
556
648
  - Send a Pull Request. Bonus points for topic branches.
557
649
 
558
650
  ## Copyright
559
651
 
560
- Copyright (c) 2010-2025 Cássio Marques and Lucas Caton. See `LICENSE` file for
652
+ Copyright (c) 2010-2026 Cássio Marques and Lucas Caton. See `LICENSE` file for
561
653
  details.
data/enumerate_it.gemspec CHANGED
@@ -31,5 +31,5 @@ Gem::Specification.new do |gem|
31
31
  gem.add_development_dependency 'rubocop'
32
32
  gem.add_development_dependency 'rubocop-rake'
33
33
  gem.add_development_dependency 'rubocop-rspec'
34
- gem.add_development_dependency 'sqlite3', '< 2'
34
+ gem.add_development_dependency 'sqlite3'
35
35
  end
@@ -6,6 +6,7 @@ gem 'activerecord', '~> 6.0.6.1'
6
6
  gem 'activesupport', '~> 6.0.6.1'
7
7
  gem 'base64'
8
8
  gem 'bigdecimal'
9
+ gem 'json', '< 3'
9
10
  gem 'mutex_m'
10
11
  gem 'sqlite3', '< 2'
11
12
 
@@ -6,6 +6,7 @@ gem 'activerecord', '~> 6.1.7.10'
6
6
  gem 'activesupport', '~> 6.1.7.10'
7
7
  gem 'base64'
8
8
  gem 'bigdecimal'
9
+ gem 'json', '< 3'
9
10
  gem 'mutex_m'
10
11
  gem 'sqlite3', '< 2'
11
12
 
@@ -2,10 +2,11 @@
2
2
 
3
3
  source 'https://rubygems.org'
4
4
 
5
- gem 'activerecord', '~> 7.0.8.7'
6
- gem 'activesupport', '~> 7.0.8.7'
5
+ gem 'activerecord', '~> 7.0.10'
6
+ gem 'activesupport', '~> 7.0.10'
7
7
  gem 'base64'
8
8
  gem 'bigdecimal'
9
+ gem 'json', '< 3'
9
10
  gem 'mutex_m'
10
11
  gem 'sqlite3', '< 2'
11
12
 
@@ -2,8 +2,9 @@
2
2
 
3
3
  source 'https://rubygems.org'
4
4
 
5
- gem 'activerecord', '~> 7.1.5.1'
6
- gem 'activesupport', '~> 7.1.5.1'
5
+ gem 'activerecord', '~> 7.1.6'
6
+ gem 'activesupport', '~> 7.1.6'
7
+ gem 'json', '< 3'
7
8
  gem 'sqlite3'
8
9
 
9
10
  gemspec path: '../'
@@ -2,8 +2,9 @@
2
2
 
3
3
  source 'https://rubygems.org'
4
4
 
5
- gem 'activerecord', '~> 7.2.2.1'
6
- gem 'activesupport', '~> 7.2.2.1'
5
+ gem 'activerecord', '~> 7.2.4'
6
+ gem 'activesupport', '~> 7.2.4'
7
+ gem 'json', '< 3'
7
8
  gem 'sqlite3'
8
9
 
9
10
  gemspec path: '../'
@@ -2,8 +2,9 @@
2
2
 
3
3
  source 'https://rubygems.org'
4
4
 
5
- gem 'activerecord', '~> 8.0.2'
6
- gem 'activesupport', '~> 8.0.2'
5
+ gem 'activerecord', '~> 8.0.5.1'
6
+ gem 'activesupport', '~> 8.0.5.1'
7
+ gem 'json', '< 3'
7
8
  gem 'sqlite3'
8
9
 
9
10
  gemspec path: '../'
@@ -0,0 +1,9 @@
1
+ # This file was generated by Appraisal
2
+
3
+ source 'https://rubygems.org'
4
+
5
+ gem 'activerecord', '~> 8.1.4'
6
+ gem 'activesupport', '~> 8.1.4'
7
+ gem 'sqlite3'
8
+
9
+ gemspec path: '../'
@@ -28,7 +28,7 @@ module EnumerateIt
28
28
  end
29
29
 
30
30
  def to_h
31
- sorted_map.transform_values(&:first)
31
+ sorted_map.to_h.transform_values(&:first)
32
32
  end
33
33
 
34
34
  def enumeration
@@ -51,8 +51,8 @@ module EnumerateIt
51
51
  list.map { |value| t(value) }
52
52
  end
53
53
 
54
- def each_value(&)
55
- list.each(&)
54
+ def each_value(&block)
55
+ list.each(&block)
56
56
  end
57
57
 
58
58
  def to_json(options = nil)
@@ -95,6 +95,25 @@ module EnumerateIt
95
95
  I18n.t("enumerations.#{name.underscore}.#{value.to_s.underscore}", default: default)
96
96
  end
97
97
 
98
+ def custom_helpers(&block)
99
+ @custom_helper_methods ||= []
100
+
101
+ mod = Module.new(&block)
102
+ methods = mod.instance_methods(false)
103
+ collisions = methods & singleton_class.instance_methods
104
+
105
+ raise ArgumentError, <<~MESSAGE.chomp if collisions.any?
106
+ Custom helper(s) '#{collisions.join(', ')}' would override existing EnumerateIt::Base methods
107
+ MESSAGE
108
+
109
+ @custom_helper_methods.concat(methods)
110
+ extend mod
111
+ end
112
+
113
+ def custom_helper_methods
114
+ @custom_helper_methods || []
115
+ end
116
+
98
117
  private
99
118
 
100
119
  def sorted_map
@@ -134,9 +153,7 @@ module EnumerateIt
134
153
  def values_hash(args)
135
154
  return args.first if args.first.is_a?(Hash)
136
155
 
137
- args.each_with_object({}) do |value, hash|
138
- hash[value] = value.to_s
139
- end
156
+ args.to_h { |value| [value, value.to_s] }
140
157
  end
141
158
  end
142
159
  end
@@ -1,5 +1,5 @@
1
1
  module EnumerateIt
2
- module ClassMethods
2
+ module ClassMethods # rubocop:disable Metrics/ModuleLength
3
3
  def has_enumeration_for(attribute, options = {})
4
4
  self.enumerations = enumerations.dup
5
5
 
@@ -17,7 +17,8 @@ module EnumerateIt
17
17
  set_validations(attribute, options) unless options[:skip_validation]
18
18
 
19
19
  if options[:create_helpers]
20
- %w[create_helper_methods create_mutator_methods create_polymorphic_methods].each do |method|
20
+ %w[create_helper_methods create_mutator_methods create_polymorphic_methods
21
+ create_custom_helper_methods].each do |method|
21
22
  send(method, options[:with], attribute, options[:create_helpers])
22
23
  end
23
24
  end
@@ -100,12 +101,27 @@ module EnumerateIt
100
101
  end
101
102
  end
102
103
 
104
+ def create_custom_helper_methods(klass, attribute_name, helpers)
105
+ return unless klass.custom_helper_methods.any?
106
+
107
+ prefix_name = "#{attribute_name}_" if helpers.is_a?(Hash) && helpers[:prefix]
108
+
109
+ class_eval do
110
+ klass.custom_helper_methods.each do |method_name|
111
+ define_method "#{prefix_name}#{method_name}" do
112
+ value = send(attribute_name)
113
+ klass.public_send(method_name, value) unless value.nil?
114
+ end
115
+ end
116
+ end
117
+ end
118
+
103
119
  def define_enumeration_class(attribute, options)
104
120
  return if options[:with]
105
121
 
106
122
  inner_enum_class_name = attribute.to_s.camelize.to_sym
107
123
 
108
- options[:with] = if constants.include?(inner_enum_class_name)
124
+ options[:with] = if const_defined?(inner_enum_class_name)
109
125
  const_get(inner_enum_class_name)
110
126
  else
111
127
  attribute.to_s.camelize.constantize
@@ -1,3 +1,3 @@
1
1
  module EnumerateIt
2
- VERSION = '4.1.0'.freeze
2
+ VERSION = '4.2.1'.freeze
3
3
  end
@@ -135,6 +135,15 @@ describe EnumerateIt::Base do
135
135
  it 'returns a hash' do
136
136
  expect(TestEnumerationWithoutArray.to_h).to eq(value_one: '1', value_two: '2')
137
137
  end
138
+
139
+ context 'specifying a sort mode' do
140
+ subject(:enumeration) { create_enumeration_class_with_sort_mode(:name) }
141
+
142
+ it 'returns a hash sorted by the given mode' do
143
+ expect(enumeration.to_h).to eq(bar: '2', bra: '4', foo: '1', omg: '3', zomg: '0')
144
+ expect(enumeration.to_h.keys).to eq(%i[bar bra foo omg zomg])
145
+ end
146
+ end
138
147
  end
139
148
 
140
149
  describe '.to_json' do
@@ -228,6 +237,55 @@ describe EnumerateIt::Base do
228
237
  end
229
238
  end
230
239
 
240
+ describe '.custom_helpers' do
241
+ context 'when methods are defined inside the block' do
242
+ it 'become class methods on the enum' do
243
+ expect(TestEnumerationWithCustomHelpers.lookup('1')).to eq(:one)
244
+ expect(TestEnumerationWithCustomHelpers.lookup('2')).to eq(:two)
245
+ expect(TestEnumerationWithCustomHelpers.boolean?('1')).to be(true)
246
+ expect(TestEnumerationWithCustomHelpers.boolean?('2')).to be(false)
247
+ end
248
+ end
249
+
250
+ context 'when a custom helper name collides with a built-in method' do
251
+ let(:build_colliding_enum) do
252
+ proc do
253
+ Class.new(EnumerateIt::Base) do
254
+ associate_values :collision
255
+
256
+ custom_helpers do
257
+ def list
258
+ '...'
259
+ end
260
+ end
261
+ end
262
+ end
263
+ end
264
+
265
+ it 'raises an ArgumentError' do
266
+ expect(&build_colliding_enum).to raise_error(
267
+ ArgumentError,
268
+ "Custom helper(s) 'list' would override existing EnumerateIt::Base methods"
269
+ )
270
+ end
271
+ end
272
+ end
273
+
274
+ describe '.custom_helper_methods' do
275
+ context 'when methods are defined inside custom_helpers block' do
276
+ it 'returns the registered names' do
277
+ expect(TestEnumerationWithCustomHelpers.custom_helper_methods)
278
+ .to match_array(%i[lookup boolean?])
279
+ end
280
+ end
281
+
282
+ context 'when there is no .custom_helpers block on an enum' do
283
+ it 'returns an empty array' do
284
+ expect(TestEnumeration.custom_helper_methods).to eq([])
285
+ end
286
+ end
287
+ end
288
+
231
289
  context 'associate values with a list' do
232
290
  it 'creates constants for each enumeration value' do
233
291
  expect(TestEnumerationWithList::FIRST).to eq('first')
@@ -289,11 +347,11 @@ describe EnumerateIt::Base do
289
347
  attr_accessor :bla
290
348
 
291
349
  class << self
292
- def validates_inclusion_of(_attribute, _options)
350
+ def validates_inclusion_of(_attribute, _options) # rubocop:disable Naming/PredicateMethod
293
351
  true
294
352
  end
295
353
 
296
- def validates_presence_of
354
+ def validates_presence_of # rubocop:disable Naming/PredicateMethod
297
355
  true
298
356
  end
299
357
  end
@@ -4,6 +4,7 @@ describe EnumerateIt do
4
4
  let :test_class do
5
5
  Class.new do
6
6
  extend EnumerateIt
7
+
7
8
  attr_accessor :foobar
8
9
 
9
10
  has_enumeration_for :foobar, with: TestEnumeration
@@ -41,6 +42,15 @@ describe EnumerateIt do
41
42
  expect(target).not_to respond_to(:value_1?)
42
43
  end
43
44
 
45
+ it 'defaults to not creating custom helper methods' do
46
+ klass = Class.new do
47
+ extend EnumerateIt
48
+
49
+ has_enumeration_for :foobar, with: TestEnumerationWithCustomHelpers
50
+ end
51
+ expect(klass.new).not_to respond_to(:lookup)
52
+ end
53
+
44
54
  it 'stores the enumeration class in a class-level hash' do
45
55
  expect(test_class.enumerations[:foobar]).to eq(TestEnumeration)
46
56
  end
@@ -71,6 +81,7 @@ describe EnumerateIt do
71
81
  let :test_class_for_enumeration_without_array do
72
82
  Class.new do
73
83
  extend EnumerateIt
84
+
74
85
  attr_accessor :foobar
75
86
 
76
87
  has_enumeration_for :foobar, with: TestEnumerationWithoutArray
@@ -102,6 +113,7 @@ describe EnumerateIt do
102
113
  let :foo_bar_class do
103
114
  Class.new do
104
115
  extend EnumerateIt
116
+
105
117
  attr_accessor :test_enumeration
106
118
 
107
119
  has_enumeration_for :test_enumeration
@@ -121,15 +133,13 @@ describe EnumerateIt do
121
133
  context 'when using a nested class as the enumeration' do
122
134
  let :class_with_nested_enum do
123
135
  Class.new do
124
- # rubocop:disable Lint/ConstantDefinitionInBlock
125
- # rubocop:disable RSpec/LeakyConstantDeclaration
136
+ # rubocop:disable-next Lint/ConstantDefinitionInBlock, RSpec/LeakyConstantDeclaration
126
137
  class NestedEnum < EnumerateIt::Base
127
- associate_values foo: %w[1 Blerrgh], bar: ['2' => 'Blarghhh']
138
+ associate_values foo: %w[1 Blerrgh], bar: [{ '2' => 'Blarghhh' }]
128
139
  end
129
- # rubocop:enable RSpec/LeakyConstantDeclaration
130
- # rubocop:enable Lint/ConstantDefinitionInBlock
131
140
 
132
141
  extend EnumerateIt
142
+
133
143
  attr_accessor :nested_enum
134
144
 
135
145
  has_enumeration_for :nested_enum
@@ -151,6 +161,7 @@ describe EnumerateIt do
151
161
  let :test_class_with_helper do
152
162
  Class.new do
153
163
  extend EnumerateIt
164
+
154
165
  attr_accessor :foobar
155
166
 
156
167
  has_enumeration_for :foobar, with: TestEnumeration, create_helpers: true
@@ -192,6 +203,7 @@ describe EnumerateIt do
192
203
  let :test_class_with_prefixed_helper do
193
204
  Class.new do
194
205
  extend EnumerateIt
206
+
195
207
  attr_accessor :foobar
196
208
 
197
209
  has_enumeration_for :foobar, with: TestEnumeration, create_helpers: { prefix: true }
@@ -234,6 +246,7 @@ describe EnumerateIt do
234
246
  let :polymorphic_class do
235
247
  Class.new do
236
248
  extend EnumerateIt
249
+
237
250
  attr_accessor :foo
238
251
 
239
252
  has_enumeration_for :foo, with: PolymorphicEnum, create_helpers: { polymorphic: true }
@@ -260,6 +273,7 @@ describe EnumerateIt do
260
273
  let :polymorphic_class_with_suffix do
261
274
  Class.new do
262
275
  extend EnumerateIt
276
+
263
277
  attr_accessor :foo
264
278
 
265
279
  has_enumeration_for :foo, with: PolymorphicEnum,
@@ -280,6 +294,62 @@ describe EnumerateIt do
280
294
  end
281
295
  end
282
296
  end
297
+
298
+ context 'with custom_helpers defined on the enumeration' do
299
+ context 'creates methods that delegates to the enum' do
300
+ let :test_class_with_custom_helpers do
301
+ Class.new do
302
+ extend EnumerateIt
303
+
304
+ attr_accessor :foobar
305
+
306
+ has_enumeration_for :foobar, with: TestEnumerationWithCustomHelpers,
307
+ create_helpers: true
308
+
309
+ def initialize(foobar)
310
+ @foobar = foobar
311
+ end
312
+ end
313
+ end
314
+
315
+ it 'returns value respective to attribute value' do
316
+ target = test_class_with_custom_helpers.new(TestEnumerationWithCustomHelpers::VALUE_ONE)
317
+ expect(target.lookup).to eq(:one)
318
+ expect(target.boolean?).to be(true)
319
+ end
320
+
321
+ it 'returns nil when attribute is nil' do
322
+ target = test_class_with_custom_helpers.new(nil)
323
+ expect(target.lookup).to be_nil
324
+ expect(target.boolean?).to be_nil
325
+ end
326
+ end
327
+
328
+ context 'create methods with prefix that delegates to the enum' do
329
+ let :test_class_with_prefixed_custom_helpers do
330
+ Class.new do
331
+ extend EnumerateIt
332
+
333
+ attr_accessor :foobar
334
+
335
+ has_enumeration_for :foobar, with: TestEnumerationWithCustomHelpers,
336
+ create_helpers: { prefix: true }
337
+
338
+ def initialize(foobar)
339
+ @foobar = foobar
340
+ end
341
+ end
342
+ end
343
+
344
+ it 'method is prefixed with the attribute name' do
345
+ target = test_class_with_prefixed_custom_helpers.new(TestEnumerationWithCustomHelpers::VALUE_ONE)
346
+ expect(target.foobar_lookup).to eq(:one)
347
+ expect(target.foobar_boolean?).to be(true)
348
+ expect(target).not_to respond_to(:lookup)
349
+ expect(target).not_to respond_to(:boolean?)
350
+ end
351
+ end
352
+ end
283
353
  end
284
354
 
285
355
  describe 'using the :create_scopes option' do
@@ -78,3 +78,17 @@ def create_enumeration_class_with_sort_mode(sort_mode)
78
78
  )
79
79
  end
80
80
  end
81
+
82
+ class TestEnumerationWithCustomHelpers < EnumerateIt::Base
83
+ associate_values value_one: '1', value_two: '2'
84
+
85
+ custom_helpers do
86
+ def lookup(value)
87
+ { '1' => :one, '2' => :two }[value]
88
+ end
89
+
90
+ def boolean?(value)
91
+ value == '1'
92
+ end
93
+ end
94
+ end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: enumerate_it
3
3
  version: !ruby/object:Gem::Version
4
- version: 4.1.0
4
+ version: 4.2.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Cássio Marques
@@ -154,16 +154,16 @@ dependencies:
154
154
  name: sqlite3
155
155
  requirement: !ruby/object:Gem::Requirement
156
156
  requirements:
157
- - - "<"
157
+ - - ">="
158
158
  - !ruby/object:Gem::Version
159
- version: '2'
159
+ version: '0'
160
160
  type: :development
161
161
  prerelease: false
162
162
  version_requirements: !ruby/object:Gem::Requirement
163
163
  requirements:
164
- - - "<"
164
+ - - ">="
165
165
  - !ruby/object:Gem::Version
166
- version: '2'
166
+ version: '0'
167
167
  description: Enumerations for Ruby with some magic powers!
168
168
  executables: []
169
169
  extensions: []
@@ -172,6 +172,7 @@ files:
172
172
  - ".github/dependabot.yml"
173
173
  - ".github/workflows/ci.yml"
174
174
  - ".gitignore"
175
+ - ".prettierrc.json"
175
176
  - ".rspec"
176
177
  - ".rubocop.yml"
177
178
  - Appraisals
@@ -188,6 +189,7 @@ files:
188
189
  - gemfiles/rails_7.1.gemfile
189
190
  - gemfiles/rails_7.2.gemfile
190
191
  - gemfiles/rails_8.0.gemfile
192
+ - gemfiles/rails_8.1.gemfile
191
193
  - lib/enumerate_it.rb
192
194
  - lib/enumerate_it/base.rb
193
195
  - lib/enumerate_it/class_methods.rb
@@ -223,7 +225,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
223
225
  - !ruby/object:Gem::Version
224
226
  version: '0'
225
227
  requirements: []
226
- rubygems_version: 3.6.8
228
+ rubygems_version: 4.0.17
227
229
  specification_version: 4
228
230
  summary: Ruby Enumerations
229
231
  test_files: []