activerecord-dbt 0.1.0 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
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