enum_machine 0.1.0 → 2.0.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 +4 -4
- data/.rubocop.yml +4 -1
- data/Gemfile +8 -8
- data/Gemfile.lock +106 -74
- data/README.md +238 -16
- data/Rakefile +3 -3
- data/docs/MIGRATING_FROM_RAILS_STRING_ENUM.ru.md +54 -0
- data/lib/enum_machine/attribute_persistence_methods.rb +27 -0
- data/lib/enum_machine/build_enum_class.rb +61 -0
- data/lib/enum_machine/build_value_class.rb +79 -0
- data/lib/enum_machine/driver_active_record.rb +89 -31
- data/lib/enum_machine/driver_simple_class.rb +33 -33
- data/lib/enum_machine/machine.rb +46 -18
- data/lib/enum_machine/version.rb +1 -3
- data/lib/enum_machine.rb +24 -10
- metadata +26 -23
- data/lib/enum_machine/build_attribute.rb +0 -123
- data/lib/enum_machine/build_class.rb +0 -41
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 29c5cbbb67a385d43e3410de9b1d2974b5a5878bad25c51eca631785870389e6
|
4
|
+
data.tar.gz: adbf98313f00d208194b20899373d7ec2561ec99f94b23974efe226538c7c63d
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 32c418e7ff59bbcc33b4a00e4fea58533077409609e4125f226fdbbdf676522cadfa2977ce90100132bec76536ff0920691ecf98b3a713fff9ebbf7c63794b9c
|
7
|
+
data.tar.gz: 55656e3c4903039bb40f7807c3abdd141c57ddc566c34bab59e8d57c08b7dd70a97d7cf3e7654b21d488230b4aaa9575ebcb8feeab8470c16ba847c31a78fe95
|
data/.rubocop.yml
CHANGED
data/Gemfile
CHANGED
@@ -1,13 +1,13 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
source
|
3
|
+
source "https://rubygems.org"
|
4
4
|
|
5
5
|
gemspec
|
6
6
|
|
7
|
-
gem
|
8
|
-
gem
|
9
|
-
gem
|
10
|
-
gem
|
11
|
-
gem
|
12
|
-
gem
|
13
|
-
gem
|
7
|
+
gem "dry-types"
|
8
|
+
gem "priscilla", github: "corp-gp/priscilla"
|
9
|
+
gem "pry", "~> 0.12"
|
10
|
+
gem "rake", "~> 13.0"
|
11
|
+
gem "rspec", "~> 3.9"
|
12
|
+
gem "rubocop-gp", github: "corp-gp/rubocop-gp"
|
13
|
+
gem "sqlite3", "~> 1.4"
|
data/Gemfile.lock
CHANGED
@@ -8,116 +8,148 @@ GIT
|
|
8
8
|
|
9
9
|
GIT
|
10
10
|
remote: https://github.com/corp-gp/rubocop-gp.git
|
11
|
-
revision:
|
11
|
+
revision: 867f7e1351c3730897cacdc20ce2d727604de245
|
12
12
|
specs:
|
13
|
-
rubocop-gp (0.0.
|
13
|
+
rubocop-gp (0.0.4)
|
14
14
|
rubocop
|
15
|
+
rubocop-capybara
|
16
|
+
rubocop-factory_bot
|
15
17
|
rubocop-performance
|
16
18
|
rubocop-rails
|
17
19
|
rubocop-rspec
|
20
|
+
rubocop-rspec_rails
|
18
21
|
|
19
22
|
PATH
|
20
23
|
remote: .
|
21
24
|
specs:
|
22
|
-
enum_machine (0.
|
23
|
-
activemodel
|
24
|
-
activerecord
|
25
|
-
activesupport
|
25
|
+
enum_machine (2.0.0)
|
26
|
+
activemodel
|
27
|
+
activerecord
|
28
|
+
activesupport
|
26
29
|
|
27
30
|
GEM
|
28
31
|
remote: https://rubygems.org/
|
29
32
|
specs:
|
30
|
-
activemodel (
|
31
|
-
activesupport (=
|
32
|
-
activerecord (
|
33
|
-
activemodel (=
|
34
|
-
activesupport (=
|
35
|
-
|
36
|
-
|
33
|
+
activemodel (7.2.1)
|
34
|
+
activesupport (= 7.2.1)
|
35
|
+
activerecord (7.2.1)
|
36
|
+
activemodel (= 7.2.1)
|
37
|
+
activesupport (= 7.2.1)
|
38
|
+
timeout (>= 0.4.0)
|
39
|
+
activesupport (7.2.1)
|
40
|
+
base64
|
41
|
+
bigdecimal
|
42
|
+
concurrent-ruby (~> 1.0, >= 1.3.1)
|
43
|
+
connection_pool (>= 2.2.5)
|
44
|
+
drb
|
37
45
|
i18n (>= 1.6, < 2)
|
46
|
+
logger (>= 1.4.2)
|
38
47
|
minitest (>= 5.1)
|
39
|
-
|
40
|
-
|
48
|
+
securerandom (>= 0.3)
|
49
|
+
tzinfo (~> 2.0, >= 2.0.5)
|
41
50
|
ast (2.4.2)
|
51
|
+
base64 (0.2.0)
|
52
|
+
bigdecimal (3.1.8)
|
42
53
|
coderay (1.1.3)
|
43
54
|
colorize (0.8.1)
|
44
|
-
concurrent-ruby (1.
|
45
|
-
|
46
|
-
|
55
|
+
concurrent-ruby (1.3.4)
|
56
|
+
connection_pool (2.4.1)
|
57
|
+
diff-lcs (1.5.1)
|
58
|
+
drb (2.2.1)
|
59
|
+
dry-core (1.0.1)
|
47
60
|
concurrent-ruby (~> 1.0)
|
48
|
-
|
49
|
-
dry-
|
61
|
+
zeitwerk (~> 2.6)
|
62
|
+
dry-inflector (1.1.0)
|
63
|
+
dry-logic (1.5.0)
|
50
64
|
concurrent-ruby (~> 1.0)
|
51
|
-
dry-
|
52
|
-
|
65
|
+
dry-core (~> 1.0, < 2)
|
66
|
+
zeitwerk (~> 2.6)
|
67
|
+
dry-types (1.7.2)
|
68
|
+
bigdecimal (~> 3.0)
|
53
69
|
concurrent-ruby (~> 1.0)
|
54
|
-
|
55
|
-
|
70
|
+
dry-core (~> 1.0)
|
71
|
+
dry-inflector (~> 1.0)
|
72
|
+
dry-logic (~> 1.4)
|
73
|
+
zeitwerk (~> 2.6)
|
74
|
+
i18n (1.14.6)
|
56
75
|
concurrent-ruby (~> 1.0)
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
i18n (1.9.1)
|
65
|
-
concurrent-ruby (~> 1.0)
|
66
|
-
method_source (1.0.0)
|
67
|
-
minitest (5.15.0)
|
68
|
-
parallel (1.21.0)
|
69
|
-
parser (3.1.0.0)
|
76
|
+
json (2.8.2)
|
77
|
+
language_server-protocol (3.17.0.3)
|
78
|
+
logger (1.6.1)
|
79
|
+
method_source (1.1.0)
|
80
|
+
minitest (5.25.2)
|
81
|
+
parallel (1.26.3)
|
82
|
+
parser (3.3.6.0)
|
70
83
|
ast (~> 2.4.1)
|
71
|
-
|
84
|
+
racc
|
85
|
+
pry (0.14.2)
|
72
86
|
coderay (~> 1.1)
|
73
87
|
method_source (~> 1.0)
|
74
|
-
|
88
|
+
racc (1.8.1)
|
89
|
+
rack (3.1.8)
|
75
90
|
rainbow (3.1.1)
|
76
|
-
rake (13.
|
77
|
-
regexp_parser (2.2
|
78
|
-
|
79
|
-
|
80
|
-
rspec-
|
81
|
-
rspec-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
rspec-expectations (3.11.0)
|
91
|
+
rake (13.2.1)
|
92
|
+
regexp_parser (2.9.2)
|
93
|
+
rspec (3.13.0)
|
94
|
+
rspec-core (~> 3.13.0)
|
95
|
+
rspec-expectations (~> 3.13.0)
|
96
|
+
rspec-mocks (~> 3.13.0)
|
97
|
+
rspec-core (3.13.1)
|
98
|
+
rspec-support (~> 3.13.0)
|
99
|
+
rspec-expectations (3.13.3)
|
86
100
|
diff-lcs (>= 1.2.0, < 2.0)
|
87
|
-
rspec-support (~> 3.
|
88
|
-
rspec-mocks (3.
|
101
|
+
rspec-support (~> 3.13.0)
|
102
|
+
rspec-mocks (3.13.1)
|
89
103
|
diff-lcs (>= 1.2.0, < 2.0)
|
90
|
-
rspec-support (~> 3.
|
91
|
-
rspec-support (3.
|
92
|
-
rubocop (1.
|
104
|
+
rspec-support (~> 3.13.0)
|
105
|
+
rspec-support (3.13.1)
|
106
|
+
rubocop (1.69.0)
|
107
|
+
json (~> 2.3)
|
108
|
+
language_server-protocol (>= 3.17.0)
|
93
109
|
parallel (~> 1.10)
|
94
|
-
parser (>= 3.
|
110
|
+
parser (>= 3.3.0.2)
|
95
111
|
rainbow (>= 2.2.2, < 4.0)
|
96
|
-
regexp_parser (>=
|
97
|
-
|
98
|
-
rubocop-ast (>= 1.15.1, < 2.0)
|
112
|
+
regexp_parser (>= 2.4, < 3.0)
|
113
|
+
rubocop-ast (>= 1.36.1, < 2.0)
|
99
114
|
ruby-progressbar (~> 1.7)
|
100
|
-
unicode-display_width (>=
|
101
|
-
rubocop-ast (1.
|
102
|
-
parser (>= 3.
|
103
|
-
rubocop-
|
104
|
-
rubocop (
|
105
|
-
|
106
|
-
|
115
|
+
unicode-display_width (>= 2.4.0, < 4.0)
|
116
|
+
rubocop-ast (1.36.1)
|
117
|
+
parser (>= 3.3.1.0)
|
118
|
+
rubocop-capybara (2.21.0)
|
119
|
+
rubocop (~> 1.41)
|
120
|
+
rubocop-factory_bot (2.26.1)
|
121
|
+
rubocop (~> 1.61)
|
122
|
+
rubocop-performance (1.23.0)
|
123
|
+
rubocop (>= 1.48.1, < 2.0)
|
124
|
+
rubocop-ast (>= 1.31.1, < 2.0)
|
125
|
+
rubocop-rails (2.27.0)
|
107
126
|
activesupport (>= 4.2.0)
|
108
127
|
rack (>= 1.1)
|
109
|
-
rubocop (>= 1.
|
110
|
-
|
111
|
-
|
112
|
-
|
128
|
+
rubocop (>= 1.52.0, < 2.0)
|
129
|
+
rubocop-ast (>= 1.31.1, < 2.0)
|
130
|
+
rubocop-rspec (3.2.0)
|
131
|
+
rubocop (~> 1.61)
|
132
|
+
rubocop-rspec_rails (2.30.0)
|
133
|
+
rubocop (~> 1.61)
|
134
|
+
rubocop-rspec (~> 3, >= 3.0.1)
|
135
|
+
ruby-progressbar (1.13.0)
|
113
136
|
rumoji (0.5.0)
|
114
|
-
|
115
|
-
|
137
|
+
securerandom (0.3.2)
|
138
|
+
sqlite3 (1.7.3-arm64-darwin)
|
139
|
+
sqlite3 (1.7.3-x86_64-darwin)
|
140
|
+
sqlite3 (1.7.3-x86_64-linux)
|
141
|
+
timeout (0.4.2)
|
142
|
+
tzinfo (2.0.6)
|
116
143
|
concurrent-ruby (~> 1.0)
|
117
|
-
unicode-display_width (
|
118
|
-
|
144
|
+
unicode-display_width (3.1.2)
|
145
|
+
unicode-emoji (~> 4.0, >= 4.0.4)
|
146
|
+
unicode-emoji (4.0.4)
|
147
|
+
zeitwerk (2.6.18)
|
119
148
|
|
120
149
|
PLATFORMS
|
150
|
+
arm64-darwin-21
|
151
|
+
arm64-darwin-23
|
152
|
+
x86_64-darwin-21
|
121
153
|
x86_64-linux
|
122
154
|
|
123
155
|
DEPENDENCIES
|
@@ -131,4 +163,4 @@ DEPENDENCIES
|
|
131
163
|
sqlite3 (~> 1.4)
|
132
164
|
|
133
165
|
BUNDLED WITH
|
134
|
-
2.
|
166
|
+
2.3.15
|
data/README.md
CHANGED
@@ -1,38 +1,260 @@
|
|
1
|
-
#
|
1
|
+
# Enum Machine
|
2
2
|
|
3
|
-
|
3
|
+
`Enum Machine` is a library for defining enums and setting state machines for attributes in ActiveRecord models and plain Ruby classes.
|
4
4
|
|
5
|
-
|
5
|
+
You can visualize transitions map with [enum_machine-contrib](https://github.com/corp-gp/enum_machine-contrib)
|
6
|
+
|
7
|
+
## Why is `enum_machine` better then [state_machines](https://github.com/state-machines/state_machines) / [aasm](https://github.com/aasm/aasm)?
|
8
|
+
|
9
|
+
- faster [5x](#Benchmarks)
|
10
|
+
- code lines: `enum_machine` - 348, `AASM` - 2139
|
11
|
+
- namespaced (via attr) by default: `order.state.to_collected`
|
12
|
+
- [aliases](Aliases)
|
13
|
+
- guarantees of existing transitions
|
14
|
+
- simple run transitions with callbacks `order.update(state: "collected")` or `order.state.to_collected`
|
15
|
+
- `aasm` / `state_machines` **event driven**, `enum_machine` **state driven**
|
16
|
+
|
17
|
+
```ruby
|
18
|
+
# aasm
|
19
|
+
event :complete do # complete/collected - dichotomy between states and events
|
20
|
+
before { puts "event complete" }
|
21
|
+
transitions from: :collecting, to: :collected
|
22
|
+
end
|
23
|
+
|
24
|
+
# pay/archived difficult to remember the relationship between statuses and events
|
25
|
+
# try to explain this to the logic of business stakeholders
|
26
|
+
event :pay do
|
27
|
+
transitions from: [:created, :collected], to: :archived
|
28
|
+
end
|
29
|
+
|
30
|
+
order = Order.create(state: "collecting")
|
31
|
+
order.update(state: "archived") # not check transitions, invalid logic
|
32
|
+
order.update(state: "collected") # not run callbacks
|
33
|
+
order.complete # need use event for transition, but your object in UI and DB have only states
|
34
|
+
|
35
|
+
# enum_machine
|
36
|
+
transitions( # simple readable transitions map
|
37
|
+
"collecting" => "collected",
|
38
|
+
"collected" => "archived",
|
39
|
+
)
|
40
|
+
before_transition("collecting" => "collected") { puts "event complete" }
|
41
|
+
|
42
|
+
order = Order.create(state: "collecting")
|
43
|
+
order.update(state: "archived") # checked transitions, raise exception
|
44
|
+
order.update(state: "collected") # run callbacks
|
45
|
+
```
|
6
46
|
|
7
47
|
## Installation
|
8
48
|
|
9
|
-
Add
|
49
|
+
Add to your Gemfile:
|
10
50
|
|
11
51
|
```ruby
|
12
|
-
gem
|
52
|
+
gem "enum_machine"
|
13
53
|
```
|
14
54
|
|
15
|
-
|
55
|
+
## Usage
|
16
56
|
|
17
|
-
|
57
|
+
### Enums
|
18
58
|
|
19
|
-
|
59
|
+
```ruby
|
60
|
+
# With ActiveRecord
|
61
|
+
class Product < ActiveRecord::Base
|
62
|
+
enum_machine :color, %w[red green]
|
63
|
+
end
|
20
64
|
|
21
|
-
|
65
|
+
# Or with plain class
|
66
|
+
class Product
|
67
|
+
# attributes must be defined before including the EnumMachine module
|
68
|
+
attr_accessor :color
|
22
69
|
|
23
|
-
|
70
|
+
include EnumMachine[color: { enum: %w[red green] }]
|
71
|
+
# or reuse from model
|
72
|
+
Product::COLOR.enum_decorator
|
73
|
+
end
|
74
|
+
|
75
|
+
Product::COLOR.values # => ["red", "green"]
|
76
|
+
Product::COLOR::RED # => "red"
|
77
|
+
Product::COLOR::RED__GREEN # => ["red", "green"]
|
78
|
+
|
79
|
+
Product::COLOR["red"].red? # => true
|
80
|
+
Product::COLOR["red"].human_name # => "Красный"
|
81
|
+
|
82
|
+
product = Product.new
|
83
|
+
product.color # => nil
|
84
|
+
product.color = "red"
|
85
|
+
product.color.red? # => true
|
86
|
+
product.color.human_name # => "Красный"
|
87
|
+
```
|
88
|
+
|
89
|
+
### Aliases
|
90
|
+
|
91
|
+
```ruby
|
92
|
+
class Product < ActiveRecord::Base
|
93
|
+
enum_machine :state, %w[created approved published] do
|
94
|
+
aliases(
|
95
|
+
"forming" => %w[created approved],
|
96
|
+
)
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
Product::STATE.forming # => %w[created approved]
|
101
|
+
|
102
|
+
product = Product.new(state: "created")
|
103
|
+
product.state.forming? # => true
|
104
|
+
```
|
105
|
+
|
106
|
+
### Value decorator
|
107
|
+
|
108
|
+
You can extend value object with decorator
|
109
|
+
|
110
|
+
```ruby
|
111
|
+
# Value classes nested from base class
|
112
|
+
module ColorDecorator
|
113
|
+
def hex
|
114
|
+
case self
|
115
|
+
when Product::COLOR::RED then "#ff0000"
|
116
|
+
when Product::COLOR::GREEN then "#00ff00"
|
117
|
+
end
|
118
|
+
end
|
119
|
+
end
|
120
|
+
|
121
|
+
class Product
|
122
|
+
attr_accessor :color
|
123
|
+
|
124
|
+
include EnumMachine[color: {
|
125
|
+
enum: %w[red green],
|
126
|
+
value_decorator: ColorDecorator
|
127
|
+
}]
|
128
|
+
end
|
24
129
|
|
25
|
-
|
130
|
+
product = Product.new
|
131
|
+
product.color = "red"
|
132
|
+
product.color.hex # => "#ff0000"
|
133
|
+
```
|
134
|
+
|
135
|
+
### Transitions
|
136
|
+
|
137
|
+
```ruby
|
138
|
+
class Product < ActiveRecord::Base
|
139
|
+
enum_machine :color, %w[red green blue]
|
140
|
+
enum_machine :state, %w[created approved cancelled activated] do
|
141
|
+
# transitions(any => any) - allow all transitions
|
142
|
+
transitions(
|
143
|
+
nil => "created",
|
144
|
+
"created" => [nil, "approved"],
|
145
|
+
%w[cancelled approved] => "activated",
|
146
|
+
"activated" => %w[created cancelled],
|
147
|
+
)
|
148
|
+
|
149
|
+
# Will be executed in `before_save` callback
|
150
|
+
before_transition "created" => "approved" do |product|
|
151
|
+
product.color = "green" if product.color.red?
|
152
|
+
end
|
153
|
+
|
154
|
+
# Will be executed in `after_save` callback
|
155
|
+
after_transition %w[created] => %w[approved] do |product|
|
156
|
+
product.color = "red"
|
157
|
+
end
|
158
|
+
|
159
|
+
after_transition any => "cancelled" do |product|
|
160
|
+
product.cancelled_at = Time.zone.now
|
161
|
+
end
|
162
|
+
end
|
163
|
+
end
|
164
|
+
|
165
|
+
product = Product.create(state: "created")
|
166
|
+
product.state.possible_transitions # => [nil, "approved"]
|
167
|
+
product.state.can_activated? # => false
|
168
|
+
product.state.to_activated! # => EnumMachine::Error: transition "created" => "activated" not defined in enum_machine
|
169
|
+
product.state.to_approved! # => true; equal to `product.update!(state: "approve")`
|
170
|
+
```
|
171
|
+
|
172
|
+
#### Skip transitions
|
173
|
+
```ruby
|
174
|
+
product = Product.new(state: "created")
|
175
|
+
product.skip_state_transitions { product.save }
|
176
|
+
```
|
177
|
+
|
178
|
+
method generated as `skip_#{enum_name}_transitions`
|
179
|
+
|
180
|
+
#### Skip in factories
|
181
|
+
```ruby
|
182
|
+
FactoryBot.define do
|
183
|
+
factory :product do
|
184
|
+
name { Faker::Commerce.product_name }
|
185
|
+
to_create { |product| product.skip_state_transitions { product.save! } }
|
186
|
+
end
|
187
|
+
end
|
188
|
+
```
|
189
|
+
|
190
|
+
### I18n
|
191
|
+
|
192
|
+
**ru.yml**
|
193
|
+
```yml
|
194
|
+
ru:
|
195
|
+
enums:
|
196
|
+
product:
|
197
|
+
color:
|
198
|
+
red: Красный
|
199
|
+
green: Зеленый
|
200
|
+
```
|
201
|
+
|
202
|
+
```ruby
|
203
|
+
# ActiveRecord
|
204
|
+
class Product < ActiveRecord::Base
|
205
|
+
enum_machine :color, %w[red green]
|
206
|
+
end
|
26
207
|
|
27
|
-
|
208
|
+
# Plain class
|
209
|
+
class Product
|
210
|
+
# attributes must be defined before including the EnumMachine module
|
211
|
+
attr_accessor :color
|
212
|
+
# `i18n_scope` option must be explicitly set to use methods below
|
213
|
+
include EnumMachine[color: { enum: %w[red green], i18n_scope: "product" }]
|
214
|
+
end
|
28
215
|
|
29
|
-
|
216
|
+
Product::COLOR.human_name_for("red") # => "Красный"
|
217
|
+
Product::COLOR.values_for_form # => [["Красный", "red"], ["Зеленый", "green"]]
|
218
|
+
|
219
|
+
product = Product.new(color: "red")
|
220
|
+
product.color.human_name # => "Красный"
|
221
|
+
```
|
222
|
+
|
223
|
+
I18n scope can be changed with `i18n_scope` option:
|
224
|
+
|
225
|
+
```ruby
|
226
|
+
# For AciveRecord
|
227
|
+
class Product < ActiveRecord::Base
|
228
|
+
enum_machine :color, %w[red green], i18n_scope: "users.product"
|
229
|
+
end
|
230
|
+
|
231
|
+
# For plain class
|
232
|
+
class Product
|
233
|
+
include EnumMachine[color: { enum: %w[red green], i18n_scope: "users.product" }]
|
234
|
+
end
|
235
|
+
```
|
30
236
|
|
31
|
-
|
237
|
+
## Benchmarks
|
238
|
+
[test/performance.rb](../master/test/performance.rb)
|
32
239
|
|
33
|
-
|
240
|
+
| Gem | Method | |
|
241
|
+
| :--- | ---: | :--- |
|
242
|
+
| enum_machine | order.state.forming? | 894921.3 i/s |
|
243
|
+
| state_machines | order.forming? | 189901.8 i/s - 4.71x slower |
|
244
|
+
| aasm | order.forming? | 127073.7 i/s - 7.04x slower |
|
245
|
+
| | | |
|
246
|
+
| enum_machine | order.state.can_closed? | 473150.4 i/s |
|
247
|
+
| aasm | order.may_to_closed? | 24459.1 i/s - 19.34x slower |
|
248
|
+
| state_machines | order.can_to_closed? | 12136.8 i/s - 38.98x slower |
|
249
|
+
| | | |
|
250
|
+
| enum_machine | Order::STATE.values | 6353820.4 i/s |
|
251
|
+
| aasm | Order.aasm(:state).states.map(&:name) | 131390.5 i/s - 48.36x slower |
|
252
|
+
| state_machines | Order.state_machines[:state].states.map(&:value) | 108449.7 i/s - 58.59x slower |
|
253
|
+
| | | |
|
254
|
+
| enum_machine | order.state = "forming" and order.valid? | 13873.4 i/s |
|
255
|
+
| state_machines | order.state_event = "to_forming" and order.valid? | 6173.6 i/s - 2.25x slower |
|
256
|
+
| aasm | order.to_forming | 3095.9 i/s - 4.48x slower |
|
34
257
|
|
35
|
-
Bug reports and pull requests are welcome on GitHub at https://github.com/[USERNAME]/enum_machine.
|
36
258
|
|
37
259
|
## License
|
38
260
|
|
data/Rakefile
CHANGED
@@ -1,11 +1,11 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require
|
4
|
-
require
|
3
|
+
require "bundler/gem_tasks"
|
4
|
+
require "rspec/core/rake_task"
|
5
5
|
|
6
6
|
RSpec::Core::RakeTask.new(:spec)
|
7
7
|
|
8
|
-
require
|
8
|
+
require "rubocop/rake_task"
|
9
9
|
|
10
10
|
RuboCop::RakeTask.new
|
11
11
|
|
@@ -0,0 +1,54 @@
|
|
1
|
+
# Переход с `rails_string_enum` на `enum_machine`
|
2
|
+
|
3
|
+
### 1. Объявление в классе
|
4
|
+
```ruby
|
5
|
+
class Product
|
6
|
+
string_enum :color, %w[red green] # Было
|
7
|
+
enum_machine :color, %w[red green] # Стало
|
8
|
+
end
|
9
|
+
```
|
10
|
+
|
11
|
+
### 2. Константы
|
12
|
+
|
13
|
+
Все константы находятся в Product::COLOR
|
14
|
+
|
15
|
+
* `Product::RED` => `Product::COLOR::RED`
|
16
|
+
* `Product::RED__GREEN` => `Product::COLOR::RED__GREEN`
|
17
|
+
* `Product::COLORS` => `Product::COLOR.values`
|
18
|
+
|
19
|
+
### 3. Методы инстанса
|
20
|
+
|
21
|
+
* `@product.red?` => `@product.color.red?`
|
22
|
+
|
23
|
+
### 4. I18n хелперы
|
24
|
+
|
25
|
+
* `Product.colors_i18n` => `Product::COLOR.values_for_form`
|
26
|
+
* `Product.color_i18n_for('red')` => `Product::COLOR.human_name_for('red')`
|
27
|
+
* `@product.color_i18n` => `@product.color.human_name`
|
28
|
+
|
29
|
+
### 5. scopes
|
30
|
+
|
31
|
+
В `enum_machine` нет опции `scopes`, нужно задать необходимые вручную
|
32
|
+
|
33
|
+
```ruby
|
34
|
+
class Product
|
35
|
+
# Было
|
36
|
+
string_enum :color, %w[red green], scopes: true
|
37
|
+
|
38
|
+
# Стало
|
39
|
+
enum_machine :color, %w[red green]
|
40
|
+
scope :only_red, -> { where(color: COLOR::RED) }
|
41
|
+
end
|
42
|
+
```
|
43
|
+
|
44
|
+
### 6. Интеграция с `simple_form`
|
45
|
+
|
46
|
+
`enum_machine` не предоставляет интеграцию с `simple_form`, тип инпута и коллекцию нужно передавать самостоятельно
|
47
|
+
|
48
|
+
```ruby
|
49
|
+
# Было
|
50
|
+
f.input :color
|
51
|
+
|
52
|
+
# Стало
|
53
|
+
f.input :color, as: :select, collection: Product::COLOR.values_for_form
|
54
|
+
```
|
@@ -0,0 +1,27 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module EnumMachine
|
4
|
+
module AttributePersistenceMethods
|
5
|
+
def self.[](attr, enum_values)
|
6
|
+
Module.new do
|
7
|
+
define_singleton_method(:extended) do |klass|
|
8
|
+
klass.attr_accessor :parent
|
9
|
+
|
10
|
+
enum_values.each do |enum_value|
|
11
|
+
enum_name = enum_value.underscore
|
12
|
+
|
13
|
+
klass.class_eval <<-RUBY, __FILE__, __LINE__ + 1
|
14
|
+
# def to_created!
|
15
|
+
# parent.update!('state' => 'created')
|
16
|
+
# end
|
17
|
+
|
18
|
+
def to_#{enum_name}!
|
19
|
+
parent.update!('#{attr}' => '#{enum_value}')
|
20
|
+
end
|
21
|
+
RUBY
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|