activerecord-dbt 0.1.0 → 0.3.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.
Files changed (32) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +192 -106
  3. data/lib/active_record/dbt/column/column.rb +4 -13
  4. data/lib/active_record/dbt/column/testable/accepted_values_testable.rb +3 -7
  5. data/lib/active_record/dbt/column/testable/not_null_testable.rb +9 -7
  6. data/lib/active_record/dbt/column/testable/relationships_testable.rb +4 -8
  7. data/lib/active_record/dbt/column/testable/unique_testable.rb +2 -6
  8. data/lib/active_record/dbt/config.rb +3 -30
  9. data/lib/active_record/dbt/configuration/dwh_platform.rb +39 -0
  10. data/lib/active_record/dbt/configuration/i18n_configuration.rb +14 -0
  11. data/lib/active_record/dbt/configuration/source.rb +40 -0
  12. data/lib/active_record/dbt/data_type/mapper.rb +103 -0
  13. data/lib/active_record/dbt/dbt_package/dbt_utils/table/testable/unique_combination_of_columns_testable.rb +18 -11
  14. data/lib/active_record/dbt/dbt_package/dbterd/column/testable/relationships_meta_relationship_type.rb +22 -19
  15. data/lib/active_record/dbt/factory/model/staging_factory.rb +3 -3
  16. data/lib/active_record/dbt/factory/source_factory.rb +1 -2
  17. data/lib/active_record/dbt/i18n_wrapper/translate.rb +34 -0
  18. data/lib/active_record/dbt/model/staging/yml.rb +10 -6
  19. data/lib/active_record/dbt/required_methods.rb +17 -0
  20. data/lib/active_record/dbt/seed/enum/base.rb +48 -0
  21. data/lib/active_record/dbt/seed/enum/csv.rb +85 -0
  22. data/lib/active_record/dbt/seed/enum/yml.rb +128 -0
  23. data/lib/active_record/dbt/source/yml.rb +6 -2
  24. data/lib/active_record/dbt/table/yml.rb +1 -4
  25. data/lib/active_record/dbt/version.rb +1 -1
  26. data/lib/active_record/dbt.rb +0 -2
  27. data/lib/generators/active_record/dbt/config/templates/source_config.yml.tt +3 -0
  28. data/lib/generators/active_record/dbt/enum/USAGE +9 -0
  29. data/lib/generators/active_record/dbt/enum/enum_generator.rb +31 -0
  30. data/lib/generators/active_record/dbt/initializer/templates/dbt.rb +1 -0
  31. data/lib/generators/active_record/dbt/staging_model/staging_model_generator.rb +2 -2
  32. metadata +13 -2
@@ -5,19 +5,15 @@ module ActiveRecord
5
5
  module Column
6
6
  module Testable
7
7
  module RelationshipsTestable
8
- REQUIRED_RELATIONSHIPS_TESTABLE_METHODS = %i[@config foreign_keys column_name].freeze
9
-
10
8
  include ActiveRecord::Dbt::DbtPackage::Dbterd::Column::Testable::RelationshipsMetaRelationshipType
11
9
 
10
+ extend ActiveRecord::Dbt::RequiredMethods
11
+
12
+ define_required_methods :@config, :foreign_keys, :column_name
13
+
12
14
  delegate :source_name, :data_sync_delayed?, to: :@config
13
15
  delegate :to_table, to: :foreign_key
14
16
 
15
- REQUIRED_RELATIONSHIPS_TESTABLE_METHODS.each do |method_name|
16
- define_method(method_name) do
17
- raise NotImplementedError, "You must implement #{self.class}##{__method__}"
18
- end
19
- end
20
-
21
17
  def relationships_test
22
18
  return nil if foreign_key.blank?
23
19
 
@@ -5,13 +5,9 @@ module ActiveRecord
5
5
  module Column
6
6
  module Testable
7
7
  module UniqueTestable
8
- REQUIRED_UNIQUE_TESTABLE_METHODS = %i[table_name column_name primary_keys].freeze
8
+ extend ActiveRecord::Dbt::RequiredMethods
9
9
 
10
- REQUIRED_UNIQUE_TESTABLE_METHODS.each do |method_name|
11
- define_method(method_name) do
12
- raise NotImplementedError, "You must implement #{self.class}##{__method__}"
13
- end
14
- end
10
+ define_required_methods :table_name, :column_name, :primary_keys
15
11
 
16
12
  def unique_test
17
13
  unique? ? 'unique' : nil
@@ -8,38 +8,11 @@ module ActiveRecord
8
8
  include Singleton
9
9
 
10
10
  include ActiveRecord::Dbt::Configuration::DataSync
11
+ include ActiveRecord::Dbt::Configuration::DwhPlatform
12
+ include ActiveRecord::Dbt::Configuration::I18nConfiguration
11
13
  include ActiveRecord::Dbt::Configuration::Logger
12
- include ActiveRecord::Dbt::Configuration::Parser
14
+ include ActiveRecord::Dbt::Configuration::Source
13
15
  include ActiveRecord::Dbt::Configuration::UsedDbtPackage
14
-
15
- DEFAULT_CONFIG_DIRECTORY_PATH = 'lib/dbt'
16
- DEFAULT_EXPORT_DIRECTORY_PATH = 'doc/dbt'
17
-
18
- attr_writer :config_directory_path, :export_directory_path
19
-
20
- def source_config_path
21
- @source_config_path ||= "#{config_directory_path}/source_config.yml"
22
- end
23
-
24
- def source_config
25
- @source_config ||= parse_yaml(source_config_path)
26
- end
27
-
28
- def source_name
29
- @source_name ||= source_config.dig(:sources, :name).tap do |source_name|
30
- raise SourceNameIsNullError, "'sources.name' is required in '#{source_config_path}'." if source_name.nil?
31
- end
32
- end
33
-
34
- def config_directory_path
35
- @config_directory_path ||= DEFAULT_CONFIG_DIRECTORY_PATH
36
- end
37
-
38
- def export_directory_path
39
- @export_directory_path ||= DEFAULT_EXPORT_DIRECTORY_PATH
40
- end
41
-
42
- class SourceNameIsNullError < StandardError; end
43
16
  end
44
17
  end
45
18
  end
@@ -0,0 +1,39 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActiveRecord
4
+ module Dbt
5
+ module Configuration
6
+ module DwhPlatform
7
+ extend ActiveRecord::Dbt::RequiredMethods
8
+
9
+ define_required_methods :source_config_path
10
+
11
+ def dwh_platform=(dwh_platform)
12
+ @dwh_platform = validate_dwh_platform(dwh_platform)
13
+ end
14
+
15
+ def dwh_platform
16
+ @dwh_platform || (raise DwhPlatformIsNullError, "'dwh_platform' is required in '#{source_config_path}'.")
17
+ end
18
+
19
+ private
20
+
21
+ def validate_dwh_platform(dwh_platform)
22
+ return dwh_platform if selectable_dwh_platforms.include?(dwh_platform)
23
+
24
+ raise DoesNotExistOnTheDwhPlatformError, [
25
+ "'#{dwh_platform}' does not exist on the DWH platform.",
26
+ "Please specify one of the following: #{selectable_dwh_platforms.join(', ')}."
27
+ ].join(' ')
28
+ end
29
+
30
+ def selectable_dwh_platforms
31
+ @selectable_dwh_platforms ||= ActiveRecord::Dbt::DataType::Mapper::RUBY_TO_DWH_PLATFORM_TYPE_MAP.keys
32
+ end
33
+
34
+ class DoesNotExistOnTheDwhPlatformError < StandardError; end
35
+ class DwhPlatformIsNullError < StandardError; end
36
+ end
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,14 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActiveRecord
4
+ module Dbt
5
+ module Configuration
6
+ module I18nConfiguration
7
+ def locale=(locale = I18n.locale)
8
+ I18n.load_path += Dir[Rails.root.join('config/locales/**/*.{rb,yml}')]
9
+ I18n.locale = locale
10
+ end
11
+ end
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,40 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActiveRecord
4
+ module Dbt
5
+ module Configuration
6
+ module Source
7
+ include ActiveRecord::Dbt::Configuration::Parser
8
+
9
+ DEFAULT_CONFIG_DIRECTORY_PATH = 'lib/dbt'
10
+ DEFAULT_EXPORT_DIRECTORY_PATH = 'doc/dbt'
11
+
12
+ attr_writer :config_directory_path, :export_directory_path
13
+
14
+ def config_directory_path
15
+ @config_directory_path ||= DEFAULT_CONFIG_DIRECTORY_PATH
16
+ end
17
+
18
+ def export_directory_path
19
+ @export_directory_path ||= DEFAULT_EXPORT_DIRECTORY_PATH
20
+ end
21
+
22
+ def source_config_path
23
+ @source_config_path ||= "#{config_directory_path}/source_config.yml"
24
+ end
25
+
26
+ def source_config
27
+ @source_config ||= parse_yaml(source_config_path)
28
+ end
29
+
30
+ def source_name
31
+ @source_name ||= source_config.dig(:sources, :name).tap do |source_name|
32
+ raise SourceNameIsNullError, "'sources.name' is required in '#{source_config_path}'." if source_name.nil?
33
+ end
34
+ end
35
+
36
+ class SourceNameIsNullError < StandardError; end
37
+ end
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,103 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActiveRecord
4
+ module Dbt
5
+ module DataType
6
+ module Mapper
7
+ extend ActiveRecord::Dbt::RequiredMethods
8
+
9
+ # [Platform-specific data types | dbt Developer Hub](https://docs.getdbt.com/reference/resource-properties/data-types)
10
+ RUBY_TO_DWH_PLATFORM_TYPE_MAP = {
11
+ # [Data types  |  BigQuery  |  Google Cloud](https://cloud.google.com/bigquery/docs/reference/standard-sql/data-types#data_type_list)
12
+ 'bigquery' => {
13
+ binary: 'bytes',
14
+ boolean: 'bool',
15
+ date: 'date',
16
+ datetime: 'datetime',
17
+ decimal: 'int64',
18
+ float: 'float64',
19
+ integer: 'int64',
20
+ json: 'json',
21
+ string: 'string',
22
+ text: 'string',
23
+ time: 'time'
24
+ },
25
+
26
+ # TODO: I have not tried it. I don't know if this is the correct data_type.
27
+ # [PostgreSQL: Documentation: 16: Chapter 8. Data Types](https://www.postgresql.org/docs/current/datatype.html)
28
+ 'postgres' => {
29
+ binary: 'bytea',
30
+ boolean: 'boolean',
31
+ date: 'date',
32
+ datetime: 'timestamp',
33
+ decimal: 'bigint',
34
+ float: 'double precision',
35
+ integer: 'bigint',
36
+ json: 'json',
37
+ string: 'text',
38
+ text: 'text',
39
+ time: 'time'
40
+ },
41
+
42
+ # TODO: I have not tried it. I don't know if this is the correct data_type.
43
+ # [Data types - Amazon Redshift](https://docs.aws.amazon.com/redshift/latest/dg/c_Supported_data_types.html)
44
+ 'redshift' => {
45
+ binary: 'varbyte',
46
+ boolean: 'bool',
47
+ date: 'date',
48
+ datetime: 'timestamp',
49
+ decimal: 'decimal',
50
+ float: 'double precision',
51
+ integer: 'integer',
52
+ json: 'super',
53
+ string: 'varchar',
54
+ text: 'varchar',
55
+ time: 'time'
56
+ },
57
+
58
+ # TODO: I have not tried it. I don't know if this is the correct data_type.
59
+ # [Summary of data types | Snowflake Documentation](https://docs.snowflake.com/en/sql-reference/intro-summary-data-types)
60
+ 'snowflake' => {
61
+ binary: 'binary',
62
+ boolean: 'boolean',
63
+ date: 'date',
64
+ datetime: 'datetime',
65
+ decimal: 'decimal',
66
+ float: 'float',
67
+ integer: 'integer',
68
+ json: 'variant',
69
+ string: 'varchar',
70
+ text: 'varchar',
71
+ time: 'time'
72
+ },
73
+
74
+ # TODO: I have not tried it. I don't know if this is the correct data_type.
75
+ # [Data Types - Spark 3.5.1 Documentation](https://spark.apache.org/docs/latest/sql-ref-datatypes.html)
76
+ 'spark' => {
77
+ binary: 'binary',
78
+ boolean: 'boolean',
79
+ date: 'date',
80
+ datetime: 'timestamp',
81
+ decimal: 'decimal',
82
+ float: 'float',
83
+ integer: 'integer',
84
+ json: 'string',
85
+ string: 'string',
86
+ text: 'string',
87
+ time: 'string'
88
+ }
89
+ }.freeze
90
+
91
+ define_required_methods :@config
92
+
93
+ delegate :dwh_platform, to: :@config
94
+
95
+ private
96
+
97
+ def data_type(type)
98
+ RUBY_TO_DWH_PLATFORM_TYPE_MAP[dwh_platform].fetch(type, 'unknown')
99
+ end
100
+ end
101
+ end
102
+ end
103
+ end
@@ -7,32 +7,39 @@ module ActiveRecord
7
7
  module Table
8
8
  module Testable
9
9
  module UniqueCombinationOfColumnsTestable
10
- REQUIRED_UNIQUE_COMBINATION_OF_COLUMNS_TESTABLE_METHODS = %i[table_name @config].freeze
10
+ extend ActiveRecord::Dbt::RequiredMethods
11
11
 
12
- delegate :used_dbt_utils?, to: :@config
12
+ define_required_methods :table_name, :@config
13
13
 
14
- REQUIRED_UNIQUE_COMBINATION_OF_COLUMNS_TESTABLE_METHODS.each do |method_name|
15
- define_method(method_name) do
16
- raise NotImplementedError, "You must implement #{self.class}##{__method__}"
17
- end
18
- end
14
+ delegate :used_dbt_utils?, to: :@config
19
15
 
20
16
  def unique_combination_of_columns_test
21
17
  return nil unless used_dbt_utils?
22
18
 
23
- ActiveRecord::Base.connection.indexes(table_name).each_with_object([]) do |index, array|
24
- next if index.unique == false
25
- next if (unique_indexes = index.columns).size == 1
19
+ indexes.each_with_object([]) do |index, array|
20
+ next if unique_indexes?(index)
26
21
 
27
22
  array.push(
28
23
  {
29
24
  'dbt_utils.unique_combination_of_columns' => {
30
- 'combination_of_columns' => unique_indexes
25
+ 'combination_of_columns' => index.columns
31
26
  }
32
27
  }
33
28
  )
34
29
  end.presence
35
30
  end
31
+
32
+ private
33
+
34
+ def indexes
35
+ ActiveRecord::Base.connection.indexes(table_name)
36
+ end
37
+
38
+ def unique_indexes?(index)
39
+ return true if index.unique == false
40
+
41
+ index.columns.size == 1
42
+ end
36
43
  end
37
44
  end
38
45
  end
@@ -7,33 +7,20 @@ module ActiveRecord
7
7
  module Column
8
8
  module Testable
9
9
  module RelationshipsMetaRelationshipType
10
- REQUIRED_RELATIONSHIP_TYPE_TESTABLE_METHODS = %i[@config foreign_key].freeze
10
+ extend ActiveRecord::Dbt::RequiredMethods
11
11
 
12
- delegate :used_dbterd?, :add_log, to: :@config
12
+ define_required_methods :@config, :foreign_key
13
13
 
14
- REQUIRED_RELATIONSHIP_TYPE_TESTABLE_METHODS.each do |method_name|
15
- define_method(method_name) do
16
- raise NotImplementedError, "You must implement #{self.class}##{__method__}"
17
- end
18
- end
14
+ delegate :used_dbterd?, :add_log, to: :@config
19
15
 
20
16
  def relationships_meta_relationship_type
21
- return nil unless used_dbterd?
22
- return nil if foreign_key.nil? || relationship_type.blank?
17
+ return nil if !used_dbterd? || no_relationship?
23
18
 
24
19
  {
25
20
  'relationship_type' => relationship_type
26
21
  }
27
- rescue NotSpecifiedOrNotInvalidIdError, StandardError => e
28
- add_log(self.class, e)
29
-
30
- {
31
- 'relationship_type' => 'many-to-one',
32
- 'active_record_dbt_error' => {
33
- 'class' => e.class.to_s,
34
- 'message' => e.message.to_s
35
- }
36
- }
22
+ rescue NotSpecifiedOrNotInvalidIdError, NameError => e
23
+ relationships_meta_relationship_type_with_active_record_dbt_error(e)
37
24
  end
38
25
 
39
26
  private
@@ -128,6 +115,22 @@ module ActiveRecord
128
115
  @to_model ||= foreign_key.to_table.classify.constantize
129
116
  end
130
117
 
118
+ def no_relationship?
119
+ foreign_key.nil? || relationship_type.blank?
120
+ end
121
+
122
+ def relationships_meta_relationship_type_with_active_record_dbt_error(error)
123
+ add_log(self.class, error)
124
+
125
+ {
126
+ 'relationship_type' => 'many-to-one',
127
+ 'active_record_dbt_error' => {
128
+ 'class' => error.class.to_s,
129
+ 'message' => error.message.to_s
130
+ }
131
+ }
132
+ end
133
+
131
134
  class NotSpecifiedOrNotInvalidIdError < StandardError; end
132
135
  end
133
136
  end
@@ -5,14 +5,14 @@ module ActiveRecord
5
5
  module Factory
6
6
  module Model
7
7
  module StagingFactory
8
- def self.yml_build(table_name)
8
+ def self.build(table_name)
9
9
  table_factory = ActiveRecord::Dbt::Factory::TableFactory.build(table_name)
10
10
  yml = ActiveRecord::Dbt::Model::Staging::Yml.new(table_factory)
11
- struct = Struct.new(:export_path, :yml_dump, keyword_init: true)
11
+ struct = Struct.new(:export_path, :dump, keyword_init: true)
12
12
 
13
13
  struct.new(
14
14
  export_path: yml.export_path,
15
- yml_dump: YAML.dump(yml.config.deep_stringify_keys)
15
+ dump: yml.dump
16
16
  )
17
17
  end
18
18
  end
@@ -6,9 +6,8 @@ module ActiveRecord
6
6
  module SourceFactory
7
7
  def self.build
8
8
  tables_factory = ActiveRecord::Dbt::Factory::TablesFactory.build
9
- config = ActiveRecord::Dbt::Source::Yml.new(tables_factory).config
10
9
 
11
- YAML.dump(config.deep_stringify_keys)
10
+ ActiveRecord::Dbt::Source::Yml.new(tables_factory).dump
12
11
  end
13
12
  end
14
13
  end
@@ -0,0 +1,34 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActiveRecord
4
+ module Dbt
5
+ module I18nWrapper
6
+ module Translate
7
+ extend ActiveRecord::Dbt::RequiredMethods
8
+
9
+ define_required_methods :table_name
10
+
11
+ delegate :singularize, to: :table_name, prefix: true
12
+
13
+ def translated_table_name
14
+ I18n.t("activerecord.models.#{table_name_singularize}", default: nil)
15
+ end
16
+
17
+ def translated_attribute_name
18
+ @translated_attribute_name ||=
19
+ translated_column_name || translated_default_column_name
20
+ end
21
+
22
+ private
23
+
24
+ def translated_column_name
25
+ I18n.t("activerecord.attributes.#{table_name_singularize}.#{column_name}", default: nil)
26
+ end
27
+
28
+ def translated_default_column_name
29
+ I18n.t("attributes.#{column_name}", default: nil)
30
+ end
31
+ end
32
+ end
33
+ end
34
+ end
@@ -21,7 +21,13 @@ module ActiveRecord
21
21
  "#{basename}.yml"
22
22
  end
23
23
 
24
- def config
24
+ def dump
25
+ YAML.dump(model_config.deep_stringify_keys)
26
+ end
27
+
28
+ private
29
+
30
+ def model_config
25
31
  {
26
32
  'version' => 2,
27
33
  'models' => [
@@ -34,8 +40,6 @@ module ActiveRecord
34
40
  }
35
41
  end
36
42
 
37
- private
38
-
39
43
  def columns
40
44
  @columns ||= sort_columns(table.config['columns'])
41
45
  end
@@ -43,19 +47,19 @@ module ActiveRecord
43
47
  def sort_columns(columns)
44
48
  columns.sort_by do |column|
45
49
  [
46
- SORT_COLUMN_TYPES.index(column_type(column)) || -1,
50
+ SORT_COLUMN_TYPES.index(data_type(column)) || -1,
47
51
  columns.index(column)
48
52
  ]
49
53
  end
50
54
  end
51
55
 
52
- def column_type(column)
56
+ def data_type(column)
53
57
  if id?(column['name'])
54
58
  'ids'
55
59
  elsif enum?(column['name'])
56
60
  'enums'
57
61
  else
58
- column.dig('meta', 'column_type').pluralize
62
+ column.fetch('data_type', 'unknown').pluralize
59
63
  end
60
64
  end
61
65
 
@@ -0,0 +1,17 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActiveRecord
4
+ module Dbt
5
+ module RequiredMethods
6
+ def define_required_methods(*methods)
7
+ methods.each do |method_name|
8
+ define_method(method_name) do
9
+ raise RequiredImplementationMissingError, "You must implement #{self.class}##{__method__}"
10
+ end
11
+ end
12
+ end
13
+
14
+ class RequiredImplementationMissingError < StandardError; end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,48 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActiveRecord
4
+ module Dbt
5
+ module Seed
6
+ module Enum
7
+ module Base
8
+ attr_reader :table_name, :enum_column_name
9
+
10
+ delegate :source_name, :export_directory_path, to: :@config
11
+ delegate :singularize, to: :table_name, prefix: true
12
+
13
+ def initialize(table_name, enum_column_name)
14
+ @table_name = table_name
15
+ @enum_column_name = enum_column_name
16
+ @config = ActiveRecord::Dbt::Config.instance
17
+ end
18
+
19
+ private
20
+
21
+ def basename
22
+ "#{export_directory_path}/#{seed_name}"
23
+ end
24
+
25
+ def seed_name
26
+ "seed_#{source_name}__#{table_name_singularize}_enum_#{enum_pluralized}"
27
+ end
28
+
29
+ def locales
30
+ @locales ||= I18n.available_locales
31
+ end
32
+
33
+ def application_record_klass
34
+ @application_record_klass ||= table_name_singularize.classify.constantize
35
+ rescue NameError => _e
36
+ raise DoesNotExistTableError, "#{table_name} table does not exist."
37
+ end
38
+
39
+ def enum_pluralized
40
+ enum_column_name.pluralize
41
+ end
42
+
43
+ class DoesNotExistTableError < StandardError; end
44
+ end
45
+ end
46
+ end
47
+ end
48
+ end
@@ -0,0 +1,85 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'csv'
4
+
5
+ module ActiveRecord
6
+ module Dbt
7
+ module Seed
8
+ module Enum
9
+ class Csv
10
+ include ActiveRecord::Dbt::Seed::Enum::Base
11
+
12
+ delegate :singularize, to: :table_name, prefix: true
13
+
14
+ def export_path
15
+ "#{basename}.csv"
16
+ end
17
+
18
+ def dump
19
+ CSV.generate(headers: true) do |csv|
20
+ rows.each { |row| csv << row }
21
+ end
22
+ end
23
+
24
+ private
25
+
26
+ def rows
27
+ [
28
+ header,
29
+ *enum_rows
30
+ ]
31
+ end
32
+
33
+ def header
34
+ [
35
+ "#{enum_column_name}_before_type_of_cast",
36
+ "#{enum_column_name}_key",
37
+ *locale_header
38
+ ]
39
+ end
40
+
41
+ def locale_header
42
+ locales.map { |locale| "#{enum_column_name}_#{locale}" }
43
+ end
44
+
45
+ def enum_rows
46
+ enums.map do |enum_key, enum_before_type_of_cast|
47
+ [
48
+ enum_before_type_of_cast,
49
+ enum_key,
50
+ *enum_values(enum_key)
51
+ ]
52
+ end
53
+ end
54
+
55
+ def enums
56
+ application_record_klass.defined_enums.fetch(enum_column_name)
57
+ rescue KeyError => _e
58
+ raise NotEnumColumnError, "#{table_name}.#{enum_column_name} column is not an enum." if enum_column?
59
+
60
+ raise DoesNotExistColumnError, "The #{enum_column_name} column in the #{table_name} table does not exist."
61
+ end
62
+
63
+ def enum_column?
64
+ application_record_klass.column_names.include?(enum_column_name)
65
+ end
66
+
67
+ def enum_values(enum_key)
68
+ locales.map { |locale| translated_enum_value(enum_key, locale) }
69
+ end
70
+
71
+ def translated_enum_value(enum_key, locale)
72
+ I18n.t(
73
+ "activerecord.enum.#{table_name_singularize}.#{enum_column_name}.#{enum_key}",
74
+ locale: locale,
75
+ default: nil
76
+ )
77
+ end
78
+
79
+ class DoesNotExistColumnError < StandardError; end
80
+ class NotEnumColumnError < StandardError; end
81
+ end
82
+ end
83
+ end
84
+ end
85
+ end