enumy 0.6.0 → 0.7.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: ce0707d044a3a8c05c9d9989c190177ff349fca9440a2ac74e49522caa924948
4
- data.tar.gz: 2a4058a520e603d800936498db7a6c586a9cdd1da8ab1c550c7ef9cd7864215b
3
+ metadata.gz: 6dd6a6b997ce941c0d25307a6d58dacbbf37fe400599138b5e5bf19368a50e79
4
+ data.tar.gz: 333f8abf304d49f44cb298a73cb9aea1a5e35a12727156ec79a1d8edcbb5f126
5
5
  SHA512:
6
- metadata.gz: 76fa6acec0c8dd849adf0924783ee2eab517c8b27673f681a32710e788089e2522a834c48e3aa013f7e42f5004028dfeb150a2af31c2c9e2ad6ddcf10caa4d06
7
- data.tar.gz: c223050367c30eba0b51ea73900ad9c903cfe2edd0e3c79d99e5094a5a435209601dba6906e481f2734d2baecae499de2fb23ccddef346c949b83be7a9be2cc3
6
+ metadata.gz: 538a40ecabc8d6268a219360af3cbd5c0204b38eb22914ce721c16f7c7b98f14e3738b8714357c7c47ac4b3abda05befd31f53d3dd0b2ed4c94e07bcbde86343
7
+ data.tar.gz: 1e286b730ff740ce7e04ac2d6c1bbd74918002da2ea5a2ee5c75379e5d0c29e3d7b4ed59fffc59152797cf9b36390fd6db61f41c51353eb86e8f39dedde75998
data/lib/enumy/enum.rb ADDED
@@ -0,0 +1,60 @@
1
+ require 'enumy/errors'
2
+
3
+ module Enumy
4
+ class Enum
5
+
6
+ attr_reader :key
7
+
8
+ def initialize(key)
9
+ raise Errors::KeyMissingError.new(self) if key.nil?
10
+ @key = key.to_sym
11
+ end
12
+
13
+ def to_s
14
+ key.to_s
15
+ end
16
+
17
+ def inspect
18
+ "#<#{self.class.name} #{attributes.map { |name, value| "#{name}: #{value.inspect}" }.join(', ')}>"
19
+ end
20
+
21
+ class << self
22
+
23
+ def define(enum)
24
+ raise ArgumentError, "Expected an object of type `Enumy::Enum`, got #{enum.class.name}" unless enum.is_a?(Enumy::Enum)
25
+
26
+ if instances[enum.key]
27
+ raise Errors::DuplicateEnumInstanceError, "Enum `#{enum.key.to_s.upcase}` is using a duplicate key `#{enum.key}` for Enum '#{enum.class.name}'."
28
+ else
29
+ instances[enum.key] = enum
30
+ end
31
+ end
32
+
33
+ def all
34
+ instances.values
35
+ end
36
+
37
+ def find(key)
38
+ find_by(key: key) || raise(Errors::EnumNotFoundError.new("Could not find a `#{self.name}` enum with a `key` of #{key}"))
39
+ end
40
+
41
+ def find_by(**args)
42
+ raise ArgumentError, "`find_by` requires at least one key-value pair" if args.empty?
43
+
44
+ instances.values.find do |instance|
45
+ args.all? do |attr, value|
46
+ instance.send(attr) == value
47
+ end
48
+ end
49
+ end
50
+
51
+ private
52
+
53
+ def instances
54
+ @instances ||= {}
55
+ end
56
+
57
+ end
58
+
59
+ end
60
+ end
data/lib/enumy/errors.rb CHANGED
@@ -5,5 +5,11 @@ module Enumy
5
5
  class InvalidEnumClassError < NameError; end
6
6
  class DuplicateEnumInstanceError < ArgumentError; end
7
7
 
8
+ class KeyMissingError < ArgumentError
9
+ def initialize(enum)
10
+ super("#{enum.class.name.upcase} enum requires a `key`")
11
+ end
12
+ end
13
+
8
14
  end
9
15
  end
@@ -0,0 +1,30 @@
1
+ require 'enumy/enum.rb'
2
+
3
+ module Enumy
4
+ module Rails
5
+ module ActiveJob
6
+ module Serializers
7
+ class EnumSerializer < ::ActiveJob::Serializers::ObjectSerializer
8
+
9
+ def serialize(enum)
10
+ super({ class: enum.class.name, key: enum.key.to_s })
11
+ end
12
+
13
+ def deserialize(hash)
14
+ hash[:class].safe_constantize&.find_by(key: hash[:key].to_sym)
15
+ end
16
+
17
+ def serialize?(value)
18
+ value.is_a?(klass)
19
+ end
20
+
21
+ private
22
+
23
+ def klass
24
+ Enumy::Enum
25
+ end
26
+ end
27
+ end
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,34 @@
1
+ module Enumy
2
+ module Rails
3
+ module ActiveModel
4
+ module Type
5
+ class Enum < ::ActiveModel::Type::Value
6
+ def initialize(enum_class)
7
+ @enum_class = enum_class
8
+ super()
9
+ end
10
+
11
+ def cast(value)
12
+ value.is_a?(@enum_class) ? value : @enum_class.find_by(key: value&.to_sym)
13
+ end
14
+
15
+ def serialize(value)
16
+ if value.is_a?(@enum_class)
17
+ value.key.to_s
18
+ else
19
+ @enum_class.find_by(key: value&.to_sym) ? value&.to_s : nil
20
+ end
21
+ end
22
+
23
+ def deserialize(value)
24
+ value.is_a?(@enum_class) ? value : @enum_class.find_by(key: value&.to_sym)
25
+ end
26
+
27
+ def type
28
+ :enum
29
+ end
30
+ end
31
+ end
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,13 @@
1
+ require "rails/generators"
2
+
3
+ module Enumy
4
+ module Generators
5
+ class InstallGenerator < ::Rails::Generators::Base
6
+ source_root File.expand_path("templates", __dir__)
7
+
8
+ def copy_application_enum
9
+ template "app/enums/application_enum.rb", "app/enums/application_enum.rb"
10
+ end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,3 @@
1
+ class ApplicationEnum < Enumy::RailsEnum
2
+
3
+ end
@@ -0,0 +1,12 @@
1
+ module Enumy
2
+ module Rails
3
+ module InitializerInterceptor
4
+ def initialize
5
+ # Intentionally left blank to intercept the original initialize method
6
+ # We do this because we want to use ActiveModel::Attributes for attribute handling
7
+ # but ActiveModel::Attributes calls super without arguments,
8
+ # which would call the original initialize method without arguments and raise an error.
9
+ end
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,34 @@
1
+ require 'enumy/enum'
2
+ require 'enumy/errors'
3
+
4
+ module Enumy
5
+ module Rails
6
+ module Model
7
+ def has_enum(name, class_name: name, **options)
8
+ enum_class = class_name.to_s.classify.safe_constantize
9
+
10
+ assert_valid_enum_type!(enum_class, class_name)
11
+ define_enum_attributes(name, enum_class, default: options[:default])
12
+ define_enum_validations(name, enum_class) unless options[:optional]
13
+ end
14
+
15
+ private
16
+
17
+ def define_enum_attributes(name, enum_class, **options)
18
+ attribute name, ActiveModel::Type::Enum.new(enum_class), default: -> { options[:default] }
19
+ end
20
+
21
+ def define_enum_validations(name, enum_class)
22
+ validates name, presence: true, inclusion: { in: enum_class.all }
23
+ end
24
+
25
+ def assert_valid_enum_type!(enum_class, class_name)
26
+ if enum_class.nil?
27
+ raise Errors::InvalidEnumClassError, "Could not resolve class `#{class_name.to_s.classify}`"
28
+ elsif !enum_class.ancestors.include?(Enumy::Enum)
29
+ raise Errors::InvalidEnumClassError, "`#{enum_class.name}` does not inherit from `#{Enumy::Base.name}`"
30
+ end
31
+ end
32
+ end
33
+ end
34
+ end
@@ -1,9 +1,9 @@
1
1
  module Enumy
2
- class Railtie < Rails::Railtie
2
+ class Railtie < ::Rails::Railtie
3
3
  initializer "enumy.register_active_job_serializer" do |app|
4
- if Rails::VERSION::MAJOR >= 6
5
- require 'enumy/active_job/serializers/enum_serializer'
6
- app.config.active_job.custom_serializers << Enumy::ActiveJob::Serializers::EnumSerializer
4
+ if ::Rails::VERSION::MAJOR >= 6
5
+ require 'enumy/rails/active_job/serializers/enum_serializer'
6
+ app.config.active_job.custom_serializers << Enumy::Rails::ActiveJob::Serializers::EnumSerializer
7
7
  end
8
8
  end
9
9
  end
@@ -0,0 +1,41 @@
1
+ require 'enumy/rails/initializer_interceptor'
2
+
3
+ module Enumy
4
+ class RailsEnum < Enumy::Enum
5
+ include Enumy::Rails::InitializerInterceptor
6
+ include ::ActiveModel::Model
7
+ include ::ActiveModel::Attributes
8
+
9
+ attribute :key
10
+
11
+ def initialize(key, **attrs)
12
+ raise Errors::KeyMissingError.new(self) if key.nil?
13
+ super(key: key.to_sym, **attrs)
14
+ end
15
+
16
+ def titleize
17
+ key.to_s.titleize
18
+ end
19
+
20
+ def human_attribute_name(attribute)
21
+ I18n.t("#{self.class.i18n_scope}.attributes.models.#{self.class.model_name.i18n_key}/#{self.key}.#{attribute}")
22
+ end
23
+
24
+ def name
25
+ human_attribute_name(:key)
26
+ end
27
+
28
+ class << self
29
+
30
+ def model_name
31
+ @model_name ||= ::ActiveModel::Name.new(self, nil, self.name.to_s)
32
+ end
33
+
34
+ def i18n_scope
35
+ :enum
36
+ end
37
+
38
+ end
39
+
40
+ end
41
+ end
data/lib/enumy/version.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Enumy
4
- VERSION = "0.6.0"
4
+ VERSION = "0.7.0"
5
5
  end
data/lib/enumy.rb CHANGED
@@ -1,15 +1,10 @@
1
1
  require 'enumy/version'
2
-
3
- if defined?(ActiveModel)
4
- require 'enumy/base'
5
- end
2
+ require 'enumy/enum'
6
3
 
7
4
  if defined?(Rails)
8
- require 'enumy/generators/enumy/install/install_generator'
9
- require 'enumy/active_model/type/enum'
10
- require 'enumy/model'
11
-
12
- if defined?(Rails::Railtie)
13
- require 'enumy/rails/railtie'
14
- end
5
+ require 'enumy/rails_enum'
6
+ require 'enumy/rails/model'
7
+ require 'enumy/rails/generators/enumy/install/install_generator'
8
+ require 'enumy/rails/active_model/type/enum'
9
+ require 'enumy/rails/railtie'
15
10
  end
metadata CHANGED
@@ -1,14 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: enumy
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.6.0
4
+ version: 0.7.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Bevan Holborn
8
- autorequire:
9
8
  bindir: bin
10
9
  cert_chain: []
11
- date: 2025-06-24 00:00:00.000000000 Z
10
+ date: 1980-01-02 00:00:00.000000000 Z
12
11
  dependencies:
13
12
  - !ruby/object:Gem::Dependency
14
13
  name: rake
@@ -53,28 +52,27 @@ dependencies:
53
52
  - !ruby/object:Gem::Version
54
53
  version: '7.0'
55
54
  description: Use Enumeration objects as ActiveRecord like associations
56
- email:
57
55
  executables: []
58
56
  extensions: []
59
57
  extra_rdoc_files: []
60
58
  files:
61
59
  - lib/enumy.rb
62
- - lib/enumy/active_job/serializers/enum_serializer.rb
63
- - lib/enumy/active_model/type/enum.rb
64
- - lib/enumy/base.rb
60
+ - lib/enumy/enum.rb
65
61
  - lib/enumy/errors.rb
66
- - lib/enumy/generators/enumy/install/install_generator.rb
67
- - lib/enumy/generators/enumy/install/templates/app/enums/application_enum.rb
68
- - lib/enumy/model.rb
62
+ - lib/enumy/rails/active_job/serializers/enum_serializer.rb
63
+ - lib/enumy/rails/active_model/type/enum.rb
64
+ - lib/enumy/rails/generators/enumy/install/install_generator.rb
65
+ - lib/enumy/rails/generators/enumy/install/templates/app/enums/application_enum.rb
66
+ - lib/enumy/rails/initializer_interceptor.rb
67
+ - lib/enumy/rails/model.rb
69
68
  - lib/enumy/rails/railtie.rb
70
- - lib/enumy/test_helpers.rb
69
+ - lib/enumy/rails_enum.rb
71
70
  - lib/enumy/version.rb
72
71
  homepage: https://rubygems.org/gems/enumy
73
72
  licenses:
74
73
  - MIT
75
74
  metadata:
76
75
  source_code_uri: https://gitlab.com/BevanHolborn/enumy
77
- post_install_message:
78
76
  rdoc_options: []
79
77
  require_paths:
80
78
  - lib
@@ -89,8 +87,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
89
87
  - !ruby/object:Gem::Version
90
88
  version: '0'
91
89
  requirements: []
92
- rubygems_version: 3.3.27
93
- signing_key:
90
+ rubygems_version: 3.6.9
94
91
  specification_version: 4
95
92
  summary: ActiveRecord like enum associations
96
93
  test_files: []
@@ -1,28 +0,0 @@
1
- require 'enumy/base.rb'
2
-
3
- module Enumy
4
- module ActiveJob
5
- module Serializers
6
- class EnumSerializer < ::ActiveJob::Serializers::ObjectSerializer
7
-
8
- def serialize(enum)
9
- super({ class: enum.class.name, key: enum.key.to_s })
10
- end
11
-
12
- def deserialize(hash)
13
- hash[:class].constantize.find_by_key(hash[:key])
14
- end
15
-
16
- def serialize?(value)
17
- value.is_a?(klass)
18
- end
19
-
20
- private
21
-
22
- def klass
23
- Enumy::Base
24
- end
25
- end
26
- end
27
- end
28
- end
@@ -1,32 +0,0 @@
1
- module Enumy
2
- module ActiveModel
3
- module Type
4
- class Enum < ::ActiveModel::Type::Value
5
- def initialize(enum_class)
6
- @enum_class = enum_class
7
- super()
8
- end
9
-
10
- def cast(value)
11
- value.is_a?(@enum_class) ? value : @enum_class.find_by_key(value&.to_sym)
12
- end
13
-
14
- def serialize(value)
15
- if value.is_a?(@enum_class)
16
- value.key.to_s
17
- else
18
- @enum_class.find_by_key(value&.to_sym) ? value&.to_s : nil
19
- end
20
- end
21
-
22
- def deserialize(value)
23
- value.is_a?(@enum_class) ? value : @enum_class.find_by_key(value&.to_sym)
24
- end
25
-
26
- def type
27
- :enum
28
- end
29
- end
30
- end
31
- end
32
- end
data/lib/enumy/base.rb DELETED
@@ -1,87 +0,0 @@
1
- require_relative 'errors'
2
-
3
- module Enumy
4
- class Base
5
- include ::ActiveModel::Model
6
- include ::ActiveModel::Attributes
7
-
8
- attribute :key
9
-
10
- def initialize(key, **attrs)
11
- raise ArgumentError, "#{self.class.name.upcase} enum requires a `key`" if key.nil?
12
- key = key.to_sym
13
-
14
- super(key: key, **attrs)
15
- end
16
-
17
- def human_attribute_name(attribute)
18
- I18n.t("#{self.class.i18n_scope}.attributes.models.#{self.class.model_name.i18n_key}/#{self.key}.#{attribute}")
19
- end
20
-
21
- def name
22
- human_attribute_name(:key)
23
- end
24
-
25
- def titleize
26
- key.to_s.titleize
27
- end
28
-
29
- def to_s
30
- key.to_s
31
- end
32
-
33
- def inspect
34
- "#<#{self.class.name} #{attributes.map { |name, value| "#{name}: #{value.inspect}" }.join(', ')}>"
35
- end
36
-
37
- class << self
38
-
39
- def define(enum)
40
- raise ArgumentError, "Expected an object of type `Enumy::Base`, got #{enum.class.name}" unless enum.is_a?(Enumy::Base)
41
-
42
- if instances[enum.key]
43
- raise Errors::DuplicateEnumInstanceError, "Enum `#{enum.key.to_s.upcase}` is using a duplicate key `#{enum.key}` for Enum '#{enum.class.name}'."
44
- else
45
- instances[enum.key] = enum
46
- end
47
- end
48
-
49
- def all
50
- instances.values
51
- end
52
-
53
- def find(key)
54
- find_by_key(key) || raise(Errors::EnumNotFoundError.new("Could not find a `#{self.name}` enum with a `key` of #{key}"))
55
- end
56
-
57
- def find_by_key(key)
58
- key = key.to_sym if key.is_a?(String)
59
- instances[key]
60
- end
61
-
62
- def find_by(**args)
63
- raise ArgumentError, "`find_by` requires at least one key-value pair" if args.empty?
64
-
65
- instances.values.find do |instance|
66
- args.all? { |attr, value| instance.send(attr) == value }
67
- end
68
- end
69
-
70
- def model_name
71
- @model_name ||= ::ActiveModel::Name.new(self, nil, self.name.to_s)
72
- end
73
-
74
- def i18n_scope
75
- :enum
76
- end
77
-
78
- private
79
-
80
- def instances
81
- @instances ||= {}
82
- end
83
-
84
- end
85
-
86
- end
87
- end
@@ -1,22 +0,0 @@
1
- require "rails/generators"
2
-
3
- module Enumy
4
- module Generators
5
- class InstallGenerator < Rails::Generators::Base
6
- source_root File.expand_path("templates", __dir__)
7
-
8
- def copy_application_enum
9
- template "app/enums/application_enum.rb", "app/enums/application_enum.rb"
10
- end
11
-
12
- def add_application_record_concern
13
- inject_into_file "app/models/application_record.rb", after: /^.*primary_abstract_class.*$/ do
14
- <<~CODE
15
-
16
- \textend Enumy::Model\n
17
- CODE
18
- end
19
- end
20
- end
21
- end
22
- end
@@ -1,3 +0,0 @@
1
- class ApplicationEnum < Enumy::Base
2
-
3
- end
data/lib/enumy/model.rb DELETED
@@ -1,32 +0,0 @@
1
- require_relative 'base'
2
- require_relative 'errors'
3
-
4
- module Enumy
5
- module Model
6
- def has_enum(name, class_name: name, **options)
7
- enum_class = class_name.to_s.classify.safe_constantize
8
-
9
- assert_valid_enum_type!(enum_class, class_name)
10
- define_enum_attributes(name, enum_class, default: options[:default])
11
- define_enum_validations(name, enum_class) unless options[:optional]
12
- end
13
-
14
- private
15
-
16
- def define_enum_attributes(name, enum_class, **options)
17
- attribute name, ActiveModel::Type::Enum.new(enum_class), default: -> { options[:default] }
18
- end
19
-
20
- def define_enum_validations(name, enum_class)
21
- validates name, presence: true, inclusion: { in: enum_class.all }
22
- end
23
-
24
- def assert_valid_enum_type!(enum_class, class_name)
25
- if enum_class.nil?
26
- raise Errors::InvalidEnumClassError, "Could not resolve class `#{class_name.to_s.classify}`"
27
- elsif !enum_class.ancestors.include?(Enumy::Base)
28
- raise Errors::InvalidEnumClassError, "`#{enum_class.name}` does not inherit from `#{Enumy::Base.name}`"
29
- end
30
- end
31
- end
32
- end
@@ -1,28 +0,0 @@
1
- module Enumy
2
- module TestHelpers
3
- def with_enum_defined(enum_class, enum_instance)
4
- enum_class.define(enum_instance)
5
-
6
- yield
7
-
8
- enum_class.send(:instances).delete(enum_instance.key)
9
- enum_const_name = enum_class.constants.find { |const_name| enum_class.const_get(const_name) == enum_instance }
10
- remove_const(enum_const_name) if enum_const_name
11
- end
12
-
13
- def enum_model_class(&block)
14
- base_class = Class.new(ApplicationRecord) do
15
- extend Enumy::Model
16
-
17
- class << self
18
- def name
19
- "TempModel"
20
- end
21
- end
22
- end
23
-
24
- base_class.class_exec(&block) if block
25
- base_class
26
- end
27
- end
28
- end