statusable 0.3.0.pre → 0.4.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: '08042127a22da0aacac6b596858eadf3dbbcbf4e0afdd3ff8713807a579f0960'
4
- data.tar.gz: cca9c44afb9772931d2a8b3e6b1c06ce9b765674762d0c47423e61b87ac1a634
3
+ metadata.gz: b529e80d12f7c2f085b89f9ed98fc9abd70e47f2a823c814005bad5243c0bed6
4
+ data.tar.gz: 503ce3bedca223203c854a936ff4b98ca04a2efe60a8a61d58b530dbe66c56d9
5
5
  SHA512:
6
- metadata.gz: df801462f17e8287a0885a001d97c71906a579c296851e113698f0c6ae3c2df02d7d249cb7dd8ef74906ea07c2e451d51c186580cce9af954e5846358d79629e
7
- data.tar.gz: 0e7b0fcaaf55e968d4d97bdcaadf4f3d8ca820498d737146f4b08fb739657cde63033567a336d1342dab38b50f56d731ed653b5b7d5134e442a897472aa6d742
6
+ metadata.gz: aebb2e90c390cc59bde1e77de2c4468d35bc5acfe339092ac598b5f4f547dca04d04d6b8d5dd7f78ad7828baabc24d8e3842349c8e94a7fc11e4b417beb54cf3
7
+ data.tar.gz: 4d7f1525ffc1ff3a80be61fac81095649aaf9eecd2b43c48f11edd96034be506c2c39cf801117f3a82b1ea2f348b68f4d845e28f40fbedbcb10b8ef47d4ff957
data/README.md CHANGED
@@ -142,6 +142,7 @@ job.validate; job.errors[:status]
142
142
  The column name is customizable, as is whether or not to validate on presence or inclusion. Validating on inclusion means: ensuring that the current status value is included in the list of possible status values.
143
143
 
144
144
  Defaults:
145
+
145
146
  - `col_name = "status"`
146
147
  - `validate_presence = false`
147
148
  - `validate_inclusion = true`
@@ -245,9 +246,28 @@ After checking out the repo, run `bin/setup` to install dependencies. Then, run
245
246
 
246
247
  To install this gem onto your local machine, run `bundle exec rake install`.
247
248
 
249
+ ### Testing
250
+
251
+ To test this gem:
252
+
253
+ ```bash
254
+ rake
255
+ ```
256
+
257
+ #### Linters
258
+
259
+ ```bash
260
+ rubocop
261
+
262
+ reek
263
+
264
+ npx prettier . --check
265
+ npx prettier . --write
266
+ ```
267
+
248
268
  ### Releases
249
269
 
250
- To release a new version of Statusable to RubyGems:
270
+ To release a new version of this gem to RubyGems:
251
271
 
252
272
  1. Update the version number in `version.rb`
253
273
  2. Update the release date, etc. in `CHANGELOG.md`
@@ -28,126 +28,289 @@ module Statusable::HasStatuses
28
28
  last_word_connector: ", or ").
29
29
  freeze
30
30
 
31
- if validate_presence
32
- validates(
33
- col_name,
34
- presence: true)
31
+ # VALIDATIONS
32
+
33
+ # Define a presence validation on the given `col_name`, if requested.
34
+ if validate_presence # rubocop:disable Style/IfUnlessModifier
35
+ validates(col_name, presence: true)
35
36
  end
36
37
 
38
+ # Define an inclusion validation on the given `col_name` based on the
39
+ # givien list of `statuses`, if requested.
37
40
  if validate_inclusion
41
+ message = "must be one of #{humanized_statuses_list}"
38
42
  validates(
39
43
  col_name,
40
- inclusion: {
41
- in: statuses,
42
- allow_blank: true,
43
- message: "must be one of #{humanized_statuses_list}",
44
- })
44
+ inclusion: { in: statuses, allow_blank: true, message: message })
45
45
  end
46
46
 
47
- # .for_status(<status>)
48
- scope "for_#{col_name}",
49
- ->(status) {
50
- where(
51
- if status.is_a?(Array)
52
- arel_column.in(status)
53
- else
54
- arel_column.eq(status)
55
- end)
56
- }
57
-
58
- # .not_for_status(<status>)
59
- scope "not_for_#{col_name}",
60
- ->(status) {
61
- where(
62
- if status.is_a?(Array)
63
- arel_column.not_in(status)
64
- else
65
- arel_column.not_eq(status)
66
- end)
67
- }
68
-
69
- # .by_status_asc
70
- scope "by_#{col_name}_asc",
71
- -> { order(arel_column) }
72
-
73
- # .by_status_desc
74
- scope "by_#{col_name}_desc",
75
- -> { order(arel_column.desc) }
76
-
77
- # .group_by_status
78
- scope "group_by_#{col_name}",
79
- -> { group(arel_column) }
80
-
81
- # Class method to get a humanized list of statuses.
82
- # .humanized_statuses_list
83
- define_singleton_method(:"humanized_#{pluralized_column_name}_list") do
84
- humanized_statuses_list
85
- end
47
+ # NAMED SCOPES
86
48
 
87
- # Instance method to get a humanized list of statuses.
88
- # #humanized_statuses_list
89
- define_method(:"humanized_#{pluralized_column_name}_list") do
90
- humanized_statuses_list
91
- end
49
+ # Define a named scope that filters on `col_name` records.
50
+ #
51
+ # @attr status [String, Array[String]]
52
+ #
53
+ # @example Default `col_name`
54
+ # .for_status(<a_status_name>)
55
+ #
56
+ # @example Custom `col_name`
57
+ # .for_custom_col_name(<a_status_name>)
58
+ scope :"for_#{col_name}", ->(status) {
59
+ method_name = status.is_a?(Array) ? :in : :eq
60
+ where(arel_column.public_send(method_name, status))
61
+ }
62
+
63
+ # Define a named scope that filters out `col_name` records.
64
+ #
65
+ # @attr status [String, Array[String]]
66
+ #
67
+ # @example Default `col_name`
68
+ # .not_for_status(<a_status_name>)
69
+ #
70
+ # @example Custom `col_name`
71
+ # .not_for_custom_col_name(<a_status_name>)
72
+ scope :"not_for_#{col_name}", ->(status) {
73
+ method_name = status.is_a?(Array) ? :not_in : :not_eq
74
+ where(arel_column.public_send(method_name, status))
75
+ }
76
+
77
+ # Define a named scope that orders by `col_name`, ascending.
78
+ #
79
+ # @example Default `col_name`
80
+ # .by_status_asc
81
+ #
82
+ # @example Custom `col_name`
83
+ # .by_custom_col_name_asc
84
+ scope :"by_#{col_name}_asc", -> { order(arel_column) }
85
+
86
+ # Define a named scope that orders by `col_name`, descending.
87
+ #
88
+ # @example Default `col_name`
89
+ # .by_status_desc
90
+ #
91
+ # @example Custom `col_name`
92
+ # .by_custom_col_name_desc
93
+ scope :"by_#{col_name}_desc", -> { order(arel_column.desc) }
94
+
95
+ # Define a named scope that groups by `col_name`, ascending.
96
+ #
97
+ # @example Default `col_name`
98
+ # .group_by_status
99
+ #
100
+ # @example Custom `col_name`
101
+ # .group_by_custom_col_name
102
+ scope :"group_by_#{col_name}", -> { group(arel_column) }
103
+
104
+ # CLASS METHODS
92
105
 
93
- # Class method to get all options for <col_name>.
94
- # .status_options
106
+ # Define a class method that returns the `statuses` Array.
107
+ #
108
+ # @return [Array[String]]
109
+ #
110
+ # @example Default `col_name`
111
+ # .status_options # => ["Status 1", "Status 2"]
112
+ #
113
+ # @example Custom `col_name`
114
+ # .custom_col_name_options # => ["Custom 1", "Custom 2"]
95
115
  define_singleton_method(:"#{col_name}_options") do
96
116
  statuses
97
117
  end
98
118
 
99
- # Instance method to get all options for <col_name>.
100
- # #status_options
119
+ # Define a class method that returns a humanized list of `statuses`.
120
+ #
121
+ # @return [String]
122
+ #
123
+ # @example Default `col_name`
124
+ # .humanized_statuses_list # => "Status 1, Status 2, or Status 3"
125
+ #
126
+ # @example Custom `col_name`
127
+ # .humanized_custom_col_name_list # => "Custom 1, Custom 2, or Custom 3"
128
+ define_singleton_method(:"humanized_#{pluralized_column_name}_list") do
129
+ humanized_statuses_list
130
+ end
131
+
132
+ # INSTANCE METHODS
133
+
134
+ # Define an instance method that returns the `statuses` Array.
135
+ #
136
+ # @return [Array[String]]
137
+ #
138
+ # @example Default `col_name`
139
+ # #status_options # => ["Status 1", "Status 2"]
140
+ #
141
+ # @example Custom `col_name`
142
+ # #custom_col_name_options # => ["Custom 1", "Custom 2"]
101
143
  define_method(:"#{col_name}_options") do
102
144
  statuses
103
145
  end
104
146
 
105
- # #status?("Ready")
106
- # #status?(["Ready", "Not Ready"])
147
+ # Define an instance method that returns a humanized list of `statuses`.
148
+ #
149
+ # @return [String]
150
+ #
151
+ # @example Default `col_name`
152
+ # #humanized_statuses_list # => "Status 1, Status 2, or Status 3"
153
+ #
154
+ # @example Custom `col_name`
155
+ # #humanized_custom_col_name_list # => "Custom 1, Custom 2, or Custom 3"
156
+ define_method(:"humanized_#{pluralized_column_name}_list") do
157
+ humanized_statuses_list
158
+ end
159
+
160
+ # Define an instance method that checks whether:
161
+ # - The current `status` is the same as the given status
162
+ # - The current `status` is the same as any of the given statuses
163
+ #
164
+ # @return [Boolean]
165
+ #
166
+ # @example Default `col_name`
167
+ # #status?("Known Status") # => true
168
+ # #status?("Unknown Status") # => false
169
+ # #status?(["Known Status 1", "Unknown Status 1"]) # => true
170
+ # #status?(["Unknown Status 1", "Unknown Status 2"]) # => false
171
+ #
172
+ # @example Custom `col_name`
173
+ # #custom_col_name?("Known Custom") # => true
174
+ # #custom_col_name?("Unknown Custom") # => false
175
+ # #custom_col_name?(["Known Custom 1", "Unknown Custom 1"]) # => true
176
+ # #custom_col_name?(["Unknown Custom 1", "Unknown Custom 2"]) # => false
107
177
  define_method(:"#{col_name}?") do |a_status|
108
178
  Array(a_status).any?(public_send(col_name))
109
179
  end
110
180
 
111
- # #not_status?("Ready")
181
+ # rubocop:disable Layout/LineLength
182
+
183
+ # Define an instance method that checks whether:
184
+ # - The current `status` is not the same as given status
185
+ # - The current `status` is not the same as any of the given statuses
186
+ #
187
+ # @return [Boolean]
188
+ #
189
+ # @example Default `col_name`
190
+ # #not_status?("Known Status") # => false
191
+ # #not_status?("Unknown Status") # => true
192
+ # #not_status?(["Known Status 1", "Unknown Status 1"]) # => false
193
+ # #not_status?(["Unknown Status 1", "Unknown Status 2"]) # => true
194
+ #
195
+ # @example Custom `col_name`
196
+ # #not_custom_col_name?("Known Custom") # => false
197
+ # #not_custom_col_name?("Unknown Custom") # => true
198
+ # #not_custom_col_name?(["Known Custom 1", "Unknown Custom 1"]) # => false
199
+ # #not_custom_col_name?(["Unknown Custom 1", "Unknown Custom 2"]) # => true
112
200
  define_method(:"not_#{col_name}?") do |a_status|
113
- public_send(col_name) != a_status
201
+ Array(a_status).none?(public_send(col_name))
114
202
  end
115
203
 
204
+ # rubocop:enable Layout/LineLength
205
+
206
+ # INDIVIDUAL STATUS-SPECIFIC SCOPES/METHODS
207
+
116
208
  statuses.each do |status| # rubocop:disable Metrics/BlockLength
117
209
  status_name = status.parameterize.underscore
118
210
 
119
- # .for_status_ready
120
- scope "for_#{col_name}_#{status_name}",
211
+ # NAMED SCOPES
212
+
213
+ # Define a named scope that filters on `col_name` records that match
214
+ # this `status`.
215
+ #
216
+ # @example Default `col_name`
217
+ # .for_status_ready
218
+ # .for_status_not_ready
219
+ #
220
+ # @example Custom `col_name`
221
+ # .for_custom_col_name_custom_1
222
+ # .for_custom_col_name_custom_2
223
+ scope :"for_#{col_name}_#{status_name}",
121
224
  -> { where(arel_column.eq(status)) }
122
225
 
123
- # .not_for_status_ready
124
- scope "not_for_#{col_name}_#{status_name}",
226
+ # Define a named scope that filters on `col_name` records that do not
227
+ # match this `status`.
228
+ #
229
+ # @example Default `col_name`
230
+ # .not_for_status_ready
231
+ # .not_for_status_not_ready
232
+ #
233
+ # @example Custom `col_name`
234
+ # .not_for_custom_col_name_custom_1
235
+ # .not_for_custom_col_name_custom_2
236
+ scope :"not_for_#{col_name}_#{status_name}",
125
237
  -> { where(arel_column.not_eq(status)) }
126
238
 
127
- # Class method to get <status_name> value. Obviates the need for
128
- # defining a constant.
129
- # .status_ready # => "Ready"
239
+ # CLASS METHODS
240
+
241
+ # Define a class method that returns this `status`'s value. This
242
+ # obviates the need to define a constant on the including Class.
243
+ #
244
+ # @return [String]
245
+ #
246
+ # @example Default `col_name`
247
+ # .status_ready # => "Ready"
248
+ # .status_not_ready # => "Not Ready"
249
+ #
250
+ # @example Custom `col_name`
251
+ # .custom_col_name_custom_1 # => "Custom 1"
252
+ # .custom_col_name_custom_2 # => "Custom 2"
130
253
  define_singleton_method(:"#{col_name}_#{status_name}") do
131
254
  status
132
255
  end
133
256
 
134
- # Instance method to get <status_name> value. Obviates the need for
135
- # defining a constant.
136
- # #status_ready # => "Ready"
257
+ # Define an instance method that returns this `status`'s value. This
258
+ # obviates the need to call e.g.
259
+ # - `self.class::STATUS_READY`
260
+ # - `self.class.status_ready`
261
+ # on the including Object.
262
+ #
263
+ # @return [String]
264
+ #
265
+ # @example Default `col_name`
266
+ # #status_ready # => "Ready"
267
+ # #status_not_ready # => "Not Ready"
268
+ #
269
+ # @example Custom `col_name`
270
+ # #custom_col_name_custom_1 # => "Custom 1"
271
+ # #custom_col_name_custom_2 # => "Custom 2"
137
272
  define_method(:"#{col_name}_#{status_name}") do
138
273
  status
139
274
  end
140
275
 
276
+ # Define an instance method that sets this `status`'s value. This
277
+ # obviates the need to call e.g. `self.status = status_not_ready` on the
278
+ # including Object.
279
+ #
141
280
  # @return [self]
142
- # #set_status_ready
281
+ #
282
+ # @example Default `col_name`
283
+ # #set_status_ready.status # => "Ready"
284
+ # #set_status_not_ready.status # => "Not Ready"
285
+ #
286
+ # @example Custom `col_name`
287
+ # #set_custom_col_name_custom_1.custom_col_name # => "Custom 1"
288
+ # #set_custom_col_name_custom_2.custom_col_name # => "Custom 2"
143
289
  define_method(:"set_#{col_name}_#{status_name}") do
144
290
  public_send(:"#{col_name}=", status)
145
291
 
146
292
  self
147
293
  end
148
294
 
149
- # #set_status_ready!
295
+ # Define an instance method that sets this `status`'s value, and then
296
+ # calls ActiveRecord::Persistence#save!. This obviates the need to call
297
+ # e.g. `self.status = status_not_ready; save!` on the including Object.
298
+ #
150
299
  # @return [self]
300
+ #
301
+ # @example Default `col_name`
302
+ # #set_status_ready!.status # => "Ready"
303
+ # #changes # => {}
304
+ #
305
+ # #set_status_not_ready!.status # => "Not Ready"
306
+ # #changes # => {}
307
+ #
308
+ # @example Custom `col_name`
309
+ # #set_custom_col_name_custom_1!.custom_col_name # => "Custom 1"
310
+ # #changes # => {}
311
+ #
312
+ # #set_custom_col_name_custom_2!.custom_col_name # => "Custom 2"
313
+ # #changes # => {}
151
314
  define_method(:"set_#{col_name}_#{status_name}!") do
152
315
  public_send(:"set_#{col_name}_#{status_name}")
153
316
  save!
@@ -155,12 +318,37 @@ module Statusable::HasStatuses
155
318
  self
156
319
  end
157
320
 
158
- # #status_ready?
321
+ # Define an instance method that checks whether the current `status` is
322
+ # the same as the status named by this method name. This obviates the
323
+ # need to call e.g. `status == status_ready` on the including Object.
324
+ #
325
+ # @return [Boolean]
326
+ #
327
+ # @example Default `col_name`
328
+ # #status_ready? # => true
329
+ # #status_not_ready? # => false
330
+ #
331
+ # @example Custom `col_name`
332
+ # #custom_col_name_custom_1? # => true
333
+ # #custom_col_name_custom_2? # => false
159
334
  define_method(:"#{col_name}_#{status_name}?") do
160
335
  public_send(col_name) == status
161
336
  end
162
337
 
163
- # #not_status_ready?
338
+ # Define an instance method that checks whether the current `status` is
339
+ # not the same as the status named by this method name. This obviates
340
+ # the need to call e.g. `status != status_ready` on the including
341
+ # Object.
342
+ #
343
+ # @return [Boolean]
344
+ #
345
+ # @example Default `col_name`
346
+ # #not_status_ready? # => false
347
+ # #not_status_not_ready? # => true
348
+ #
349
+ # @example Custom `col_name`
350
+ # #not_custom_col_name_custom_1? # => false
351
+ # #not_custom_col_name_custom_2? # => true
164
352
  define_method(:"not_#{col_name}_#{status_name}?") do
165
353
  public_send(col_name) != status
166
354
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Statusable
4
- VERSION = "0.3.0.pre"
4
+ VERSION = "0.4.0"
5
5
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: statusable
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.0.pre
4
+ version: 0.4.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Paul DobbinSchmaltz
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2024-08-26 00:00:00.000000000 Z
11
+ date: 2024-11-21 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rails
@@ -98,14 +98,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
98
98
  requirements:
99
99
  - - ">="
100
100
  - !ruby/object:Gem::Version
101
- version: '2.7'
101
+ version: '3.1'
102
102
  required_rubygems_version: !ruby/object:Gem::Requirement
103
103
  requirements:
104
104
  - - ">="
105
105
  - !ruby/object:Gem::Version
106
106
  version: '0'
107
107
  requirements: []
108
- rubygems_version: 3.5.17
108
+ rubygems_version: 3.3.27
109
109
  signing_key:
110
110
  specification_version: 4
111
111
  summary: Adds a `has_statuses` macro for defining common status-related ActiveRecord