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.
- checksums.yaml +4 -4
- data/README.md +192 -106
- data/lib/active_record/dbt/column/column.rb +4 -13
- data/lib/active_record/dbt/column/testable/accepted_values_testable.rb +3 -7
- data/lib/active_record/dbt/column/testable/not_null_testable.rb +9 -7
- data/lib/active_record/dbt/column/testable/relationships_testable.rb +4 -8
- data/lib/active_record/dbt/column/testable/unique_testable.rb +2 -6
- data/lib/active_record/dbt/config.rb +3 -30
- data/lib/active_record/dbt/configuration/dwh_platform.rb +39 -0
- data/lib/active_record/dbt/configuration/i18n_configuration.rb +14 -0
- data/lib/active_record/dbt/configuration/source.rb +40 -0
- data/lib/active_record/dbt/data_type/mapper.rb +103 -0
- data/lib/active_record/dbt/dbt_package/dbt_utils/table/testable/unique_combination_of_columns_testable.rb +18 -11
- data/lib/active_record/dbt/dbt_package/dbterd/column/testable/relationships_meta_relationship_type.rb +22 -19
- data/lib/active_record/dbt/factory/model/staging_factory.rb +3 -3
- data/lib/active_record/dbt/factory/source_factory.rb +1 -2
- data/lib/active_record/dbt/i18n_wrapper/translate.rb +34 -0
- data/lib/active_record/dbt/model/staging/yml.rb +10 -6
- data/lib/active_record/dbt/required_methods.rb +17 -0
- data/lib/active_record/dbt/seed/enum/base.rb +48 -0
- data/lib/active_record/dbt/seed/enum/csv.rb +85 -0
- data/lib/active_record/dbt/seed/enum/yml.rb +128 -0
- data/lib/active_record/dbt/source/yml.rb +6 -2
- data/lib/active_record/dbt/table/yml.rb +1 -4
- data/lib/active_record/dbt/version.rb +1 -1
- data/lib/active_record/dbt.rb +0 -2
- data/lib/generators/active_record/dbt/config/templates/source_config.yml.tt +3 -0
- data/lib/generators/active_record/dbt/enum/USAGE +9 -0
- data/lib/generators/active_record/dbt/enum/enum_generator.rb +31 -0
- data/lib/generators/active_record/dbt/initializer/templates/dbt.rb +1 -0
- data/lib/generators/active_record/dbt/staging_model/staging_model_generator.rb +2 -2
- 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
|
-
|
8
|
+
extend ActiveRecord::Dbt::RequiredMethods
|
9
9
|
|
10
|
-
|
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::
|
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
|
-
|
10
|
+
extend ActiveRecord::Dbt::RequiredMethods
|
11
11
|
|
12
|
-
|
12
|
+
define_required_methods :table_name, :@config
|
13
13
|
|
14
|
-
|
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
|
-
|
24
|
-
next if index
|
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' =>
|
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
|
-
|
10
|
+
extend ActiveRecord::Dbt::RequiredMethods
|
11
11
|
|
12
|
-
|
12
|
+
define_required_methods :@config, :foreign_key
|
13
13
|
|
14
|
-
|
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
|
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,
|
28
|
-
|
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.
|
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, :
|
11
|
+
struct = Struct.new(:export_path, :dump, keyword_init: true)
|
12
12
|
|
13
13
|
struct.new(
|
14
14
|
export_path: yml.export_path,
|
15
|
-
|
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
|
-
|
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
|
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(
|
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
|
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.
|
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
|