statusable 0.1.0.pre → 0.3.0.pre
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 +4 -4
- data/README.md +224 -13
- data/lib/statusable/has_statuses.rb +26 -22
- data/lib/statusable/version.rb +1 -1
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: '08042127a22da0aacac6b596858eadf3dbbcbf4e0afdd3ff8713807a579f0960'
|
4
|
+
data.tar.gz: cca9c44afb9772931d2a8b3e6b1c06ce9b765674762d0c47423e61b87ac1a634
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: df801462f17e8287a0885a001d97c71906a579c296851e113698f0c6ae3c2df02d7d249cb7dd8ef74906ea07c2e451d51c186580cce9af954e5846358d79629e
|
7
|
+
data.tar.gz: 0e7b0fcaaf55e968d4d97bdcaadf4f3d8ca820498d737146f4b08fb739657cde63033567a336d1342dab38b50f56d731ed653b5b7d5134e442a897472aa6d742
|
data/README.md
CHANGED
@@ -1,11 +1,58 @@
|
|
1
1
|
# Statusable
|
2
2
|
|
3
|
+
[](https://img.shields.io/github/v/release/pdobb/statusable)
|
4
|
+
|
3
5
|
Statusable adds a `has_statuses` macro for defining common status-related methods for use with ActiveRecord objects / Relations.
|
4
6
|
|
5
|
-
##
|
7
|
+
## Installation
|
8
|
+
|
9
|
+
Add this line to your application's Gemfile:
|
10
|
+
|
11
|
+
```ruby
|
12
|
+
gem "statusable"
|
13
|
+
```
|
14
|
+
|
15
|
+
And then execute:
|
16
|
+
|
17
|
+
```bash
|
18
|
+
$ bundle
|
19
|
+
```
|
20
|
+
|
21
|
+
Or install it yourself:
|
22
|
+
|
23
|
+
```bash
|
24
|
+
$ gem install statusable
|
25
|
+
```
|
26
|
+
|
27
|
+
## Configuration
|
28
|
+
|
29
|
+
First, add a `status` column to your model. Or pick any other name you may want instead of `status`... the column name is configurable. (See [Advanced Usage](#advanced-usage).)
|
30
|
+
|
31
|
+
```ruby
|
32
|
+
# db/migrate/<version>_add_status_to_jobs.rb
|
33
|
+
|
34
|
+
def change
|
35
|
+
add_column :jobs, :status, :string, null: false, default: "Initializing"
|
36
|
+
add_index :jobs, :status
|
37
|
+
end
|
38
|
+
```
|
39
|
+
|
40
|
+
Next, include `Statusable::HasStatuses` either into your model or into ApplicationRecord, per your preference.
|
6
41
|
|
7
42
|
```ruby
|
8
|
-
class
|
43
|
+
class Job < ApplicationRecord
|
44
|
+
include Statusable::HasStatuses
|
45
|
+
end
|
46
|
+
```
|
47
|
+
|
48
|
+
Finally, call the `has_statuses` macro in your model, along with an array of your desired status names. See below for [Basic Usage](#basic-usage) and [Advanced Usage](#advanced-usage).
|
49
|
+
|
50
|
+
## Basic Usage
|
51
|
+
|
52
|
+
```ruby
|
53
|
+
class Job < ApplicationRecord
|
54
|
+
include Statusable::HasStatuses
|
55
|
+
|
9
56
|
has_statuses(%w[
|
10
57
|
Pending
|
11
58
|
Running
|
@@ -14,22 +61,178 @@ class MyModel < ApplicationRecord
|
|
14
61
|
end
|
15
62
|
```
|
16
63
|
|
17
|
-
|
18
|
-
|
19
|
-
Add this line to your application's Gemfile:
|
64
|
+
This adds a number of methods, named_scopes, and even ActiveModel validations to the model.
|
20
65
|
|
21
66
|
```ruby
|
22
|
-
|
67
|
+
# The following are equivalent:
|
68
|
+
|
69
|
+
job = Job.new(status: "Pending")
|
70
|
+
job = Job.new(status: Job.status_pending)
|
71
|
+
job = Job.new; job.set_status_pending
|
72
|
+
|
73
|
+
# Check current status:
|
74
|
+
|
75
|
+
job.status_pending? # => true
|
76
|
+
job.status?("Pending") # => true
|
77
|
+
|
78
|
+
job.not_status_running? # => true
|
79
|
+
job.not_status?("Running") # => true
|
80
|
+
|
81
|
+
# Set status without saving:
|
82
|
+
|
83
|
+
job.set_status_running
|
84
|
+
# => #<Job:0x000000012fc3b060 id: nil, status: "Running", created_at: nil, updated_at: nil>
|
85
|
+
job.status_running? # => true
|
86
|
+
|
87
|
+
# Set status and save:
|
88
|
+
|
89
|
+
job.set_status_running!
|
90
|
+
# => #<Job:0x000000012fc3b560 id: 1, status: "Running", created_at: "...", updated_at: "...">
|
91
|
+
|
92
|
+
# Get status names/lists:
|
93
|
+
|
94
|
+
Job.status_pending # => "Pending"
|
95
|
+
job.status_pending # => "Pending"
|
96
|
+
|
97
|
+
Job.status_options # => ["Pending", "Running", "Completed"]
|
98
|
+
job.status_options # => ["Pending", "Running", "Completed"]
|
99
|
+
|
100
|
+
Job.humanized_statuses_list # => "Pending, Running, or Completed"
|
101
|
+
job.humanized_statuses_list # => "Pending, Running, or Completed"
|
102
|
+
|
103
|
+
# Named scopes:
|
104
|
+
|
105
|
+
Job.for_status("Pending").to_sql
|
106
|
+
# => SELECT "jobs".* FROM "jobs" WHERE "jobs"."status" = 'Pending'
|
107
|
+
Job.for_status(%w[Pending Running]).to_sql
|
108
|
+
# => SELECT "jobs".* FROM "jobs" WHERE "jobs"."status" IN ('Pending', 'Running')
|
109
|
+
|
110
|
+
Job.not_for_status("Pending").to_sql
|
111
|
+
# => SELECT "jobs".* FROM "jobs" WHERE "jobs"."status" != 'Pending'
|
112
|
+
Job.not_for_status(%w[Pending Running]).to_sql
|
113
|
+
# => SELECT "jobs".* FROM "jobs" WHERE "jobs"."status" NOT IN ('Pending', 'Running')
|
114
|
+
|
115
|
+
Job.for_status_pending.to_sql
|
116
|
+
# => SELECT "jobs".* FROM "jobs" WHERE "jobs"."status" = 'Pending'
|
117
|
+
Job.not_for_status_pending.to_sql
|
118
|
+
# => SELECT "jobs".* FROM "jobs" WHERE "jobs"."status" != 'Pending'
|
119
|
+
|
120
|
+
Job.by_status_asc.to_sql
|
121
|
+
# => SELECT "jobs".* FROM "jobs" ORDER BY "jobs"."status"
|
122
|
+
Job.by_status_desc.to_sql
|
123
|
+
# => SELECT "jobs".* FROM "jobs" ORDER BY "jobs"."status" DESC
|
124
|
+
|
125
|
+
Job.group_by_status.to_sql
|
126
|
+
# => SELECT "jobs".* FROM "jobs" GROUP BY "jobs"."status"
|
127
|
+
|
128
|
+
# Validation:
|
129
|
+
|
130
|
+
# By default, we don't validate on presence, but this can be changed.
|
131
|
+
# See the Advanced Usage section.
|
132
|
+
job.status = nil
|
133
|
+
job.validate; job.errors[:status] # => []
|
134
|
+
|
135
|
+
job.status = "Invalid Status"
|
136
|
+
job.validate; job.errors[:status]
|
137
|
+
# => ["must be one of Pending, Running, or Completed"]
|
23
138
|
```
|
24
139
|
|
25
|
-
|
26
|
-
|
27
|
-
|
140
|
+
## Advanced Usage
|
141
|
+
|
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
|
+
|
144
|
+
Defaults:
|
145
|
+
- `col_name = "status"`
|
146
|
+
- `validate_presence = false`
|
147
|
+
- `validate_inclusion = true`
|
148
|
+
|
149
|
+
_Note: Be sure that the column name in your migration matches whatever you choose for `col_name`, below._
|
150
|
+
|
151
|
+
```ruby
|
152
|
+
class Job < ApplicationRecord
|
153
|
+
include Statusable::HasStatuses
|
154
|
+
|
155
|
+
has_statuses(%w[
|
156
|
+
Pending
|
157
|
+
Running
|
158
|
+
Completed
|
159
|
+
],
|
160
|
+
col_name: "lifecycle_state",
|
161
|
+
validate_presence: true,
|
162
|
+
validate_inclusion: true)
|
163
|
+
end
|
28
164
|
```
|
29
165
|
|
30
|
-
|
31
|
-
|
32
|
-
|
166
|
+
This adds a number of methods, named_scopes, and even ActiveModel validations to the model--the same ones shown in Basic Usage. However, the "status" name varies, and this time we include a presence validation:
|
167
|
+
|
168
|
+
```ruby
|
169
|
+
# The following are equivalent:
|
170
|
+
|
171
|
+
job = Job.new; job.set_lifecycle_state_pending
|
172
|
+
|
173
|
+
# Check current status:
|
174
|
+
|
175
|
+
job.lifecycle_state_pending? # => true
|
176
|
+
job.lifecycle_state?("Pending") # => true
|
177
|
+
|
178
|
+
job.not_lifecycle_state_running? # => true
|
179
|
+
job.not_lifecycle_state?("Running") # => true
|
180
|
+
|
181
|
+
# Set status without saving:
|
182
|
+
|
183
|
+
job.set_lifecycle_state_running
|
184
|
+
# => #<Job:0x000000012fc3b060 id: nil, lifecycle_state: "Running", created_at: nil, updated_at: nil>
|
185
|
+
job.lifecycle_state_running? # => true
|
186
|
+
|
187
|
+
# Set status and save:
|
188
|
+
|
189
|
+
job.set_lifecycle_state_running!
|
190
|
+
# => #<Job:0x000000012fc3b560 id: 1, lifecycle_state: "Running", created_at: "...", updated_at: "...">
|
191
|
+
|
192
|
+
# Get status names/lists:
|
193
|
+
|
194
|
+
Job.lifecycle_state_pending # => "Pending"
|
195
|
+
job.lifecycle_state_pending # => "Pending"
|
196
|
+
|
197
|
+
Job.lifecycle_state_options # => ["Pending", "Running", "Completed"]
|
198
|
+
job.lifecycle_state_options # => ["Pending", "Running", "Completed"]
|
199
|
+
|
200
|
+
Job.humanized_lifecycle_states_list # => "Pending, Running, or Completed"
|
201
|
+
job.humanized_lifecycle_states_list # => "Pending, Running, or Completed"
|
202
|
+
|
203
|
+
# Named scopes:
|
204
|
+
|
205
|
+
Job.for_lifecycle_state("Pending").to_sql
|
206
|
+
# => SELECT "jobs".* FROM "jobs" WHERE "jobs"."lifecycle_state" = 'Pending'
|
207
|
+
Job.for_lifecycle_state(%w[Pending Running]).to_sql
|
208
|
+
# => SELECT "jobs".* FROM "jobs" WHERE "jobs"."lifecycle_state" IN ('Pending', 'Running')
|
209
|
+
|
210
|
+
Job.not_for_lifecycle_state("Pending").to_sql
|
211
|
+
# => SELECT "jobs".* FROM "jobs" WHERE "jobs"."lifecycle_state" != 'Pending'
|
212
|
+
Job.not_for_lifecycle_state(%w[Pending Running]).to_sql
|
213
|
+
# => SELECT "jobs".* FROM "jobs" WHERE "jobs"."lifecycle_state" NOT IN ('Pending', 'Running')
|
214
|
+
|
215
|
+
Job.for_lifecycle_state_pending.to_sql
|
216
|
+
# => SELECT "jobs".* FROM "jobs" WHERE "jobs"."lifecycle_state" = 'Pending'
|
217
|
+
Job.not_for_lifecycle_state_pending.to_sql
|
218
|
+
# => SELECT "jobs".* FROM "jobs" WHERE "jobs"."lifecycle_state" != 'Pending'
|
219
|
+
|
220
|
+
Job.by_lifecycle_state_asc.to_sql
|
221
|
+
# => SELECT "jobs".* FROM "jobs" ORDER BY "jobs"."lifecycle_state"
|
222
|
+
Job.by_lifecycle_state_desc.to_sql
|
223
|
+
# => SELECT "jobs".* FROM "jobs" ORDER BY "jobs"."lifecycle_state" DESC
|
224
|
+
|
225
|
+
Job.group_by_lifecycle_state.to_sql
|
226
|
+
# => SELECT "jobs".* FROM "jobs" GROUP BY "jobs"."lifecycle_state"
|
227
|
+
|
228
|
+
# Validation:
|
229
|
+
|
230
|
+
job.lifecycle_state = nil
|
231
|
+
job.validate; job.errors[:lifecycle_state] # => ["can't be blank"]
|
232
|
+
|
233
|
+
job.lifecycle_state = "Invalid lifecycle_state"
|
234
|
+
job.validate; job.errors[:lifecycle_state]
|
235
|
+
# => ["must be one of Pending, Running, or Completed"]
|
33
236
|
```
|
34
237
|
|
35
238
|
## Contributing
|
@@ -42,7 +245,15 @@ After checking out the repo, run `bin/setup` to install dependencies. Then, run
|
|
42
245
|
|
43
246
|
To install this gem onto your local machine, run `bundle exec rake install`.
|
44
247
|
|
45
|
-
|
248
|
+
### Releases
|
249
|
+
|
250
|
+
To release a new version of Statusable to RubyGems:
|
251
|
+
|
252
|
+
1. Update the version number in `version.rb`
|
253
|
+
2. Update the release date, etc. in `CHANGELOG.md`
|
254
|
+
3. Run `bundle` to update Gemfile.lock with the latest version info
|
255
|
+
4. Commit the changes. e.g. `Bump to vX.Y.Z`
|
256
|
+
5. Run `rake release`, which will create a git tag for the version, push git commits and the created tag, and push the `.gem` file to [rubygems.org](https://rubygems.org).
|
46
257
|
|
47
258
|
## License
|
48
259
|
|
@@ -12,18 +12,15 @@ module Statusable::HasStatuses
|
|
12
12
|
class_methods do # rubocop:disable Metrics/BlockLength
|
13
13
|
# rubocop:disable Metrics/AbcSize
|
14
14
|
# rubocop:disable Metrics/MethodLength
|
15
|
-
# rubocop:disable Metrics/PerceivedComplexity
|
16
15
|
# rubocop:disable Naming/PredicateName
|
17
16
|
def has_statuses(
|
18
17
|
*args,
|
19
18
|
col_name: "status",
|
20
|
-
|
21
|
-
validate_inclusion: true
|
22
|
-
validate_presence: false)
|
19
|
+
validate_presence: false,
|
20
|
+
validate_inclusion: true)
|
23
21
|
statuses = args.flatten.freeze
|
24
|
-
|
25
|
-
arel_column ||= arel_table[col_name]
|
26
22
|
pluralized_column_name = col_name.to_s.pluralize
|
23
|
+
arel_column = arel_table[col_name]
|
27
24
|
|
28
25
|
humanized_statuses_list =
|
29
26
|
statuses.to_sentence(
|
@@ -31,18 +28,20 @@ module Statusable::HasStatuses
|
|
31
28
|
last_word_connector: ", or ").
|
32
29
|
freeze
|
33
30
|
|
34
|
-
if
|
35
|
-
validates
|
36
|
-
|
37
|
-
|
38
|
-
allow_blank: true,
|
39
|
-
message: "must be one of #{humanized_statuses_list}",
|
40
|
-
}
|
31
|
+
if validate_presence
|
32
|
+
validates(
|
33
|
+
col_name,
|
34
|
+
presence: true)
|
41
35
|
end
|
42
36
|
|
43
|
-
if
|
44
|
-
validates
|
45
|
-
|
37
|
+
if validate_inclusion
|
38
|
+
validates(
|
39
|
+
col_name,
|
40
|
+
inclusion: {
|
41
|
+
in: statuses,
|
42
|
+
allow_blank: true,
|
43
|
+
message: "must be one of #{humanized_statuses_list}",
|
44
|
+
})
|
46
45
|
end
|
47
46
|
|
48
47
|
# .for_status(<status>)
|
@@ -103,6 +102,17 @@ module Statusable::HasStatuses
|
|
103
102
|
statuses
|
104
103
|
end
|
105
104
|
|
105
|
+
# #status?("Ready")
|
106
|
+
# #status?(["Ready", "Not Ready"])
|
107
|
+
define_method(:"#{col_name}?") do |a_status|
|
108
|
+
Array(a_status).any?(public_send(col_name))
|
109
|
+
end
|
110
|
+
|
111
|
+
# #not_status?("Ready")
|
112
|
+
define_method(:"not_#{col_name}?") do |a_status|
|
113
|
+
public_send(col_name) != a_status
|
114
|
+
end
|
115
|
+
|
106
116
|
statuses.each do |status| # rubocop:disable Metrics/BlockLength
|
107
117
|
status_name = status.parameterize.underscore
|
108
118
|
|
@@ -154,15 +164,9 @@ module Statusable::HasStatuses
|
|
154
164
|
define_method(:"not_#{col_name}_#{status_name}?") do
|
155
165
|
public_send(col_name) != status
|
156
166
|
end
|
157
|
-
|
158
|
-
# #status?(<a_status>)
|
159
|
-
define_method(:"#{col_name}?") do |a_status|
|
160
|
-
public_send(col_name) == a_status
|
161
|
-
end
|
162
167
|
end
|
163
168
|
end
|
164
169
|
# rubocop:enable Naming/PredicateName
|
165
|
-
# rubocop:enable Metrics/PerceivedComplexity
|
166
170
|
# rubocop:enable Metrics/MethodLength
|
167
171
|
# rubocop:enable Metrics/AbcSize
|
168
172
|
end
|
data/lib/statusable/version.rb
CHANGED
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.
|
4
|
+
version: 0.3.0.pre
|
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-
|
11
|
+
date: 2024-08-26 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rails
|