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.
- 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
|