enum_machine 2.2.0 → 2.3.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: 22866d6d5f37dd78718e7e5eb27dd0066450d8730b821ffb13977888009b6ddd
4
- data.tar.gz: fc4c40aa5ea99a6d14143ebec78d2a40b2c8bce58d21ff5507b4d3d2ee67bcf1
3
+ metadata.gz: 9652b2333de5222d8012acef09006ba1f57f6d5eed4a69afc52aa8e43c930c69
4
+ data.tar.gz: 3e9929cc5e1f14a78d28321e61c052cc0067d338f219dc70ffa829982a3701f6
5
5
  SHA512:
6
- metadata.gz: 247ea0d77022905062d7688dd1153037517fa71521c15f51477f66e4f8d368cf008262c0095a1465b068b24da3262ed01334f787504dce9a65265839bffa2726
7
- data.tar.gz: 969db73142c57fcf94f82fa6c1eca59c79310a6c6e0399961fbee835c5da8596802039d699657f306f07ac0f0e64ba5692cb4d3dee0f3d02359313cd61b07bc5
6
+ metadata.gz: 9381ee9e5e0faf50c90becfbea6b1299c50db93e49617735886ccaac987a95f01ee8bd654cd6afa63ad6ff0feb26f8701d79e201ae8f4b8368bfe4a7f2cb7236
7
+ data.tar.gz: e2d7f9c85b2decd744b4163c31f24597f4785cf4fe4e69d262156d6ecec8538cec437315183682e35236f8436ecdc82c53424535ae143d6bac626b9052b46c6a
data/.rubocop.yml CHANGED
@@ -27,3 +27,9 @@ Gp/UnsafeYamlMarshal:
27
27
 
28
28
  Gp/OptArgParameters:
29
29
  Enabled: false
30
+
31
+ Gp/SpecNestedDuplicatesLetName:
32
+ Enabled: false
33
+
34
+ Rails/IndexBy:
35
+ Enabled: false
data/Gemfile.lock CHANGED
@@ -22,7 +22,7 @@ GIT
22
22
  PATH
23
23
  remote: .
24
24
  specs:
25
- enum_machine (2.2.0)
25
+ enum_machine (2.3.1)
26
26
  activemodel
27
27
  activerecord
28
28
  activesupport
data/README.md CHANGED
@@ -4,12 +4,12 @@
4
4
 
5
5
  You can visualize transitions map with [enum_machine-contrib](https://github.com/corp-gp/enum_machine-contrib)
6
6
 
7
- ## Why is `enum_machine` better then [state_machines](https://github.com/state-machines/state_machines) / [aasm](https://github.com/aasm/aasm)?
7
+ ## Why is `enum_machine` better than [state_machines](https://github.com/state-machines/state_machines) / [aasm](https://github.com/aasm/aasm)?
8
8
 
9
9
  - faster [5x](#Benchmarks)
10
10
  - code lines: `enum_machine` - 348, `AASM` - 2139
11
11
  - namespaced (via attr) by default: `order.state.to_collected`
12
- - [aliases](Aliases)
12
+ - [aliases](#aliases)
13
13
  - guarantees of existing transitions
14
14
  - simple run transitions with callbacks `order.update(state: "collected")` or `order.state.to_collected`
15
15
  - `aasm` / `state_machines` **event driven**, `enum_machine` **state driven**
@@ -59,7 +59,7 @@ gem "enum_machine"
59
59
  ```ruby
60
60
  # With ActiveRecord
61
61
  class Product < ActiveRecord::Base
62
- enum_machine :color, %w[red green]
62
+ enum_machine :color, %w[red green yellow]
63
63
  end
64
64
 
65
65
  # Or with plain class
@@ -67,12 +67,12 @@ class Product
67
67
  # attributes must be defined before including the EnumMachine module
68
68
  attr_accessor :color
69
69
 
70
- include EnumMachine[color: { enum: %w[red green] }]
70
+ include EnumMachine[color: { enum: %w[red green yellow] }]
71
71
  # or reuse from model
72
72
  Product::COLOR.enum_decorator
73
73
  end
74
74
 
75
- Product::COLOR.values # => ["red", "green"]
75
+ Product::COLOR.values # => ["red", "green", "yellow"]
76
76
  Product::COLOR::RED # => "red"
77
77
  Product::COLOR::RED.red? # => true
78
78
  Product::COLOR::RED.human_name # => "Красный"
@@ -85,6 +85,8 @@ product = Product.new
85
85
  product.color # => nil
86
86
  product.color = "red"
87
87
  product.color.red? # => true
88
+ product.color.red__yellow? # => true
89
+ product.color.green__yellow? # => false
88
90
  product.color.human_name # => "Красный"
89
91
  ```
90
92
 
@@ -168,7 +170,7 @@ product = Product.create(state: "created")
168
170
  product.state.possible_transitions # => [nil, "approved"]
169
171
  product.state.can_activated? # => false
170
172
  product.state.to_activated! # => EnumMachine::Error: transition "created" => "activated" not defined in enum_machine
171
- product.state.to_approved! # => true; equal to `product.update!(state: "approve")`
173
+ product.state.to_approved! # => true; equal to `product.update!(state: "approved")`
172
174
  ```
173
175
 
174
176
  #### Skip transitions
@@ -225,7 +227,7 @@ product.color.human_name # => "Красный"
225
227
  I18n scope can be changed with `i18n_scope` option:
226
228
 
227
229
  ```ruby
228
- # For AciveRecord
230
+ # For ActiveRecord
229
231
  class Product < ActiveRecord::Base
230
232
  enum_machine :color, %w[red green], i18n_scope: "users.product"
231
233
  end
@@ -236,6 +238,78 @@ class Product
236
238
  end
237
239
  ```
238
240
 
241
+ ### Standalone enum
242
+
243
+ `EnumMachine.enum` builds an enum class without a host attribute: for computed values,
244
+ virtual attributes, permissions, or constants shared across classes.
245
+
246
+ It provides constants, `values`, lookup `[]`, predicates (including combined `foo__bar?`),
247
+ I18n, and custom decorators:
248
+
249
+ ```ruby
250
+ module ReviewTypeDecorator
251
+ def bonus_value
252
+ case self
253
+ when Comment::REVIEW_TYPE::VIDEO then 100
254
+ when Comment::REVIEW_TYPE::PHOTO then 50
255
+ else 10
256
+ end
257
+ end
258
+ end
259
+
260
+ class Comment < ActiveRecord::Base
261
+ REVIEW_TYPE = EnumMachine.enum(
262
+ %w[text photo video],
263
+ i18n_scope: "comment.review_type",
264
+ value_decorator: ReviewTypeDecorator,
265
+ )
266
+
267
+ # computed, not a database column
268
+ def review_type
269
+ if video_ids.any?
270
+ REVIEW_TYPE::VIDEO
271
+ elsif photo_ids.any?
272
+ REVIEW_TYPE::PHOTO
273
+ else
274
+ REVIEW_TYPE::TEXT
275
+ end
276
+ end
277
+ end
278
+
279
+ # Constants and listing
280
+ Comment::REVIEW_TYPE.values # => ["text", "photo", "video"]
281
+ Comment::REVIEW_TYPE::VIDEO # => "video"
282
+ Comment::REVIEW_TYPE::PHOTO__VIDEO # => ["photo", "video"]
283
+
284
+ # Lookup by string or symbol
285
+ Comment::REVIEW_TYPE["video"] # => "video"
286
+ Comment::REVIEW_TYPE[:video] # => "video"
287
+ Comment::REVIEW_TYPE["unknown"] # => nil
288
+
289
+ # Decorator methods
290
+ Comment::REVIEW_TYPE::VIDEO.bonus_value # => 100
291
+
292
+ # Predicates on computed values
293
+ comment = Comment.new(video_ids: [1])
294
+ comment.review_type.video? # => true
295
+ comment.review_type.photo__video? # => true
296
+ comment.review_type.bonus_value # => 100
297
+
298
+ # Form helpers (when i18n_scope is set)
299
+ Comment::REVIEW_TYPE.values_for_form
300
+ # => [["Text", "text"], ["Photo", "photo"], ["Video", "video"]]
301
+
302
+ # Reuse with enum_decorator in other classes or models
303
+ class UserFeedback < ActiveRecord::Base
304
+ # requires `review_type` database column (or `attr_accessor :review_type` in plain classes)
305
+ include Comment::REVIEW_TYPE.enum_decorator
306
+ end
307
+ ```
308
+
309
+ In `ActiveRecord` models, `enum_machine :attribute` relies on a database column for persistence
310
+ and dirty tracking. `EnumMachine.enum` is designed for everything else where you still want
311
+ enum capabilities without an underlying column, and can be plugged into classes via `.enum_decorator`.
312
+
239
313
  ## Benchmarks
240
314
  [test/performance.rb](../master/test/performance.rb)
241
315
 
@@ -0,0 +1,41 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "lib/enum_machine/version"
4
+
5
+ Gem::Specification.new do |spec|
6
+ spec.name = "enum_machine"
7
+ spec.version = EnumMachine::VERSION
8
+ spec.authors = ["Ermolaev Andrey"]
9
+ spec.email = ["andruhafirst@yandex.ru"]
10
+
11
+ spec.summary = "fast and siple usage state machine in your app"
12
+ spec.description = "Enum machine is a library for defining enums and setting state machines for attributes in ActiveRecord models and plain Ruby classes."
13
+ spec.homepage = "https://github.com/corp-gp/enum_machine"
14
+ spec.license = "MIT"
15
+ spec.required_ruby_version = ">= 3.0.0"
16
+
17
+ spec.metadata["allowed_push_host"] = "https://rubygems.org"
18
+
19
+ spec.metadata["homepage_uri"] = spec.homepage
20
+ spec.metadata["source_code_uri"] = "https://github.com/corp-gp/enum_machine"
21
+
22
+ # Specify which files should be added to the gem when it is released.
23
+ # The `git ls-files -z` loads the files in the RubyGem that have been added into git.
24
+ spec.files =
25
+ Dir.chdir(File.expand_path(__dir__)) do
26
+ `git ls-files -z`.split("\x0").reject do |f|
27
+ (f == __FILE__) || f.match(%r{\A(?:(?:test|spec|features)/|\.(?:git|travis|circleci)|appveyor)})
28
+ end
29
+ end
30
+ spec.bindir = "exe"
31
+ spec.executables = spec.files.grep(%r{\Aexe/}) { |f| File.basename(f) }
32
+ spec.require_paths = ["lib"]
33
+
34
+ spec.add_dependency "activemodel"
35
+ spec.add_dependency "activerecord"
36
+ spec.add_dependency "activesupport"
37
+
38
+ # For more information and examples about making a new gem, checkout our
39
+ # guide at: https://bundler.io/guides/creating_gem.html
40
+ spec.metadata["rubygems_mfa_required"] = "true"
41
+ end
@@ -19,6 +19,10 @@ module EnumMachine
19
19
  value_attribute_mapping[key] if value_attribute_mapping.key?(key)
20
20
  end
21
21
 
22
+ define_singleton_method(:enum_decorator) do
23
+ @enum_decorator ||= BuildEnumClass.build_enum_decorator(self, name.demodulize.underscore)
24
+ end
25
+
22
26
  if i18n_scope
23
27
  def self.values_for_form(specific_values = nil)
24
28
  (specific_values || values).map { |v| [human_name_for(v), v] }
@@ -59,5 +63,26 @@ module EnumMachine
59
63
  end
60
64
  end
61
65
  end
66
+
67
+ def self.build_enum_decorator(enum_class, attr)
68
+ enum_const_name = attr.to_s.upcase
69
+
70
+ define_methods =
71
+ Module.new do
72
+ define_method(attr) do
73
+ enum_value = super()
74
+ return unless enum_value
75
+
76
+ enum_class.value_attribute_mapping.fetch(enum_value)
77
+ end
78
+ end
79
+
80
+ Module.new do
81
+ define_singleton_method(:included) do |decorating_class|
82
+ decorating_class.prepend define_methods
83
+ decorating_class.const_set enum_const_name, enum_class
84
+ end
85
+ end
86
+ end
62
87
  end
63
88
  end
@@ -18,27 +18,8 @@ module EnumMachine
18
18
  if defined?(ActiveRecord) && klass <= ActiveRecord::Base
19
19
  klass.enum_machine(attr, enum_values, i18n_scope: i18n_scope)
20
20
  else
21
- enum_const_name = attr.to_s.upcase
22
- value_class = BuildValueClass.call(enum_values: enum_values, i18n_scope: i18n_scope, value_decorator: value_decorator)
23
- enum_class = BuildEnumClass.call(enum_values: enum_values, i18n_scope: i18n_scope, value_class: value_class)
24
-
25
- define_methods =
26
- Module.new do
27
- define_method(attr) do
28
- enum_value = super()
29
- return unless enum_value
30
-
31
- enum_class.value_attribute_mapping.fetch(enum_value)
32
- end
33
- end
34
-
35
- enum_decorator =
36
- Module.new do
37
- define_singleton_method(:included) do |decorating_class|
38
- decorating_class.prepend define_methods
39
- decorating_class.const_set enum_const_name, enum_class
40
- end
41
- end
21
+ enum_class = EnumMachine.enum(enum_values, i18n_scope: i18n_scope, value_decorator: value_decorator)
22
+ enum_decorator = BuildEnumClass.build_enum_decorator(enum_class, attr)
42
23
  enum_class.define_singleton_method(:enum_decorator) { enum_decorator }
43
24
 
44
25
  klass.include(enum_decorator)
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module EnumMachine
4
- VERSION = "2.2.0"
4
+ VERSION = "2.3.1"
5
5
  end
data/lib/enum_machine.rb CHANGED
@@ -7,6 +7,7 @@ require_relative "enum_machine/attribute_persistence_methods"
7
7
  require_relative "enum_machine/build_enum_class"
8
8
  require_relative "enum_machine/machine"
9
9
  require "active_support"
10
+ require "active_support/core_ext/string"
10
11
 
11
12
  module EnumMachine
12
13
  class Error < StandardError; end
@@ -29,6 +30,15 @@ module EnumMachine
29
30
  def self.[](args)
30
31
  DriverSimpleClass.call(args)
31
32
  end
33
+
34
+ # Enum class without a host class: constants, `values`, `[]`, predicates and decorator
35
+ #
36
+ # COLOR = EnumMachine.enum(%w[red green], i18n_scope: "product.color", value_decorator: ColorDecorator)
37
+ # COLOR::RED.red? # => true
38
+ def self.enum(enum_values, i18n_scope: nil, value_decorator: nil)
39
+ value_class = BuildValueClass.call(enum_values: enum_values, i18n_scope: i18n_scope, value_decorator: value_decorator)
40
+ BuildEnumClass.call(enum_values: enum_values, i18n_scope: i18n_scope, value_class: value_class)
41
+ end
32
42
  end
33
43
 
34
44
  ActiveSupport.on_load(:active_record) do
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: enum_machine
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.2.0
4
+ version: 2.3.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Ermolaev Andrey
@@ -69,6 +69,7 @@ files:
69
69
  - bin/console
70
70
  - bin/setup
71
71
  - docs/MIGRATING_FROM_RAILS_STRING_ENUM.ru.md
72
+ - enum_machine.gemspec
72
73
  - lib/enum_machine.rb
73
74
  - lib/enum_machine/attribute_persistence_methods.rb
74
75
  - lib/enum_machine/build_enum_class.rb