statusable 0.1.0.pre → 0.2.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 +215 -12
- data/lib/statusable/has_statuses.rb +25 -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: 6abf9f62c9ce28ec0003fe73731760adc77dd2e6fad64f05165279d2cb575684
|
4
|
+
data.tar.gz: db4e4a6a8dd888ee50dc489defe0e44c6b225a68a07ec1c41e02b4194eb6d4f8
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: db814ddca7cffb1c14facd97dadc88d341616a4357e32822f254a9b0d6196103bc17022549955f58cd4db71544295a409381fcc08e022a5a366586d8110cd793
|
7
|
+
data.tar.gz: aa2482d72ee291e07f8b40f3e5e4c9d26b518508c0214974695b51687d8735f3d92a7d991372219dd88c1b87bc9be4432eb4bc8c17c0a504ad16734d58f7ffbd
|
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:
|
6
10
|
|
7
11
|
```ruby
|
8
|
-
|
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.
|
41
|
+
|
42
|
+
```ruby
|
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
|
@@ -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,16 @@ module Statusable::HasStatuses
|
|
103
102
|
statuses
|
104
103
|
end
|
105
104
|
|
105
|
+
# #status?("Ready")
|
106
|
+
define_method(:"#{col_name}?") do |a_status|
|
107
|
+
public_send(col_name) == a_status
|
108
|
+
end
|
109
|
+
|
110
|
+
# #not_status?("Ready")
|
111
|
+
define_method(:"not_#{col_name}?") do |a_status|
|
112
|
+
public_send(col_name) != a_status
|
113
|
+
end
|
114
|
+
|
106
115
|
statuses.each do |status| # rubocop:disable Metrics/BlockLength
|
107
116
|
status_name = status.parameterize.underscore
|
108
117
|
|
@@ -154,15 +163,9 @@ module Statusable::HasStatuses
|
|
154
163
|
define_method(:"not_#{col_name}_#{status_name}?") do
|
155
164
|
public_send(col_name) != status
|
156
165
|
end
|
157
|
-
|
158
|
-
# #status?(<a_status>)
|
159
|
-
define_method(:"#{col_name}?") do |a_status|
|
160
|
-
public_send(col_name) == a_status
|
161
|
-
end
|
162
166
|
end
|
163
167
|
end
|
164
168
|
# rubocop:enable Naming/PredicateName
|
165
|
-
# rubocop:enable Metrics/PerceivedComplexity
|
166
169
|
# rubocop:enable Metrics/MethodLength
|
167
170
|
# rubocop:enable Metrics/AbcSize
|
168
171
|
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.2.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-16 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rails
|