k_domain 0.0.2 → 0.0.15

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 (53) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +39 -1
  3. data/Gemfile +10 -0
  4. data/Rakefile +3 -1
  5. data/STORIES.md +21 -2
  6. data/k_domain.gemspec +4 -0
  7. data/lib/k_domain/domain_model/load.rb +35 -0
  8. data/lib/k_domain/domain_model/transform.rb +110 -0
  9. data/lib/k_domain/domain_model/transform_steps/_.rb +10 -0
  10. data/lib/k_domain/domain_model/transform_steps/step.rb +142 -0
  11. data/lib/k_domain/domain_model/transform_steps/step1_attach_db_schema.rb +21 -0
  12. data/lib/k_domain/domain_model/transform_steps/step2_attach_models.rb +62 -0
  13. data/lib/k_domain/domain_model/transform_steps/step3_attach_columns.rb +137 -0
  14. data/lib/k_domain/domain_model/transform_steps/step4_attach_erd_files.rb +456 -0
  15. data/lib/k_domain/domain_model/transform_steps/step5_attach_dictionary.rb +58 -0
  16. data/lib/k_domain/domain_model/transform_steps/step8_locate_rails_models.rb +44 -0
  17. data/lib/k_domain/raw_db_schema/load.rb +35 -0
  18. data/lib/k_domain/{raw_schema → raw_db_schema}/transform.rb +36 -21
  19. data/lib/k_domain/schemas/_.rb +15 -0
  20. data/lib/k_domain/schemas/database/_.rb +7 -0
  21. data/lib/k_domain/schemas/database/foreign_key.rb +14 -0
  22. data/lib/k_domain/schemas/database/index.rb +14 -0
  23. data/lib/k_domain/schemas/database/schema.rb +31 -0
  24. data/lib/k_domain/schemas/database/table.rb +32 -0
  25. data/lib/k_domain/schemas/dictionary.rb +19 -0
  26. data/lib/k_domain/schemas/domain/_.rb +65 -0
  27. data/lib/k_domain/schemas/domain/domain.rb +11 -0
  28. data/lib/k_domain/schemas/domain/erd_file.rb +80 -0
  29. data/lib/k_domain/schemas/domain/models/column.rb +49 -0
  30. data/lib/k_domain/schemas/domain/models/model.rb +111 -0
  31. data/lib/k_domain/schemas/domain/old/belongs_to.rb +25 -0
  32. data/lib/k_domain/schemas/domain/old/column_old.rb +225 -0
  33. data/lib/k_domain/schemas/domain/old/domain_statistics.rb +29 -0
  34. data/lib/k_domain/schemas/domain/old/entity.rb +338 -0
  35. data/lib/k_domain/schemas/domain/old/entity_statistics.rb +22 -0
  36. data/lib/k_domain/schemas/domain/old/foreign_key.rb +17 -0
  37. data/lib/k_domain/schemas/domain/old/has_and_belongs_to_many.rb +20 -0
  38. data/lib/k_domain/schemas/domain/old/has_many.rb +27 -0
  39. data/lib/k_domain/schemas/domain/old/has_one.rb +41 -0
  40. data/lib/k_domain/schemas/domain/old/name_options.rb +10 -0
  41. data/lib/k_domain/schemas/domain/old/rails_controller.rb +10 -0
  42. data/lib/k_domain/schemas/domain/old/rails_model.rb +92 -0
  43. data/lib/k_domain/schemas/domain/old/related_entity.rb +36 -0
  44. data/lib/k_domain/schemas/domain/old/statistics.rb +21 -0
  45. data/lib/k_domain/schemas/domain/old/validate.rb +25 -0
  46. data/lib/k_domain/schemas/domain/old/validates.rb +50 -0
  47. data/lib/k_domain/schemas/domain_model.rb +14 -0
  48. data/lib/k_domain/schemas/investigate.rb +15 -0
  49. data/lib/k_domain/schemas/rails_resource.rb +16 -0
  50. data/lib/k_domain/version.rb +1 -1
  51. data/lib/k_domain.rb +23 -1
  52. data/{lib/k_domain/raw_schema/template.rb → templates/load_schema.rb} +4 -4
  53. metadata +88 -4
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: a69b94955a43f91d71e034b94a5caf0d3fdbb7b130c480d4eabc45818bbe2262
4
- data.tar.gz: 0f4a126ee7c9bed5d99ac7449e5f97ec558030b126dbaa3c6194cdb5ce8430bd
3
+ metadata.gz: a50df0535cc32830a2035c2d2ddd328454efcef8aa97199485ae4db1dc7d64e7
4
+ data.tar.gz: 34824c4725142b76154a256d6466e7c83e8fc684178f6cb589552ea508d49dfa
5
5
  SHA512:
6
- metadata.gz: 3a7a877e80c9f23cdb93ab7952e027bdddc89bd6e888212c6fc0622cc0d5ae6d9b73e3e9651949ecd1afa2f1323e6446045f579cb6e83c030c5d8d711979deaa
7
- data.tar.gz: 434275e01ec2308d4a1407f5e8bd7ab9ec9708218510b6a719c144e2d9b6abb7e2caafa67efc617ac33295cd276c2326ccd961468ee4870b38266ef1b1295296
6
+ metadata.gz: 404ae56ada4ee46644d0c562b97dc86e9ce64164e25d1f66119399c4a053e41c183ff5c28184e5de493c76218bfb7f71ff548df02f9b374cb5d57b38c4ceaf3c
7
+ data.tar.gz: 27c472902e4ca2ea1dfb4803489cf5719356d02a2e64bfcee27ac0179bec5d42bb8c684cbf7196e77dd26a737f8c427bf4aa6809a3a7b930d19acaf269ab9247
data/.rubocop.yml CHANGED
@@ -6,12 +6,37 @@ AllCops:
6
6
  NewCops: enable
7
7
  Exclude:
8
8
  - "_/**/*"
9
- - "spec/samples/**/*"
9
+ - "spec/sample_input/**/*"
10
+ - "spec/sample_output/**/*"
11
+ - "lib/k_domain/raw_db_schema/template.rb"
12
+
13
+ Metrics/PerceivedComplexity:
14
+ Exclude:
15
+ - "lib/k_domain/domain_model/transform_steps/*.rb"
16
+ - "lib/k_domain/domain_model/dtos/entity.rb"
17
+ - "lib/k_domain/domain_model/dtos/old/entity.rb"
18
+
19
+ Metrics/AbcSize:
20
+ Exclude:
21
+ - "lib/k_domain/domain_model/transform_steps/*.rb"
22
+ - "lib/k_domain/domain_model/dtos/rails_model.rb"
23
+ - "lib/k_domain/domain_model/dtos/entity.rb"
24
+ - "lib/k_domain/domain_model/dtos/column_old.rb"
25
+ - "lib/k_domain/domain_model/dtos/old/column_old.rb"
26
+ - "lib/k_domain/domain_model/dtos/old/rails_model.rb"
27
+ - "lib/k_domain/domain_model/dtos/old/entity.rb"
28
+
29
+ Metrics/CyclomaticComplexity:
30
+ Exclude:
31
+ - "lib/k_domain/domain_model/transform_steps/*.rb"
32
+ - "lib/k_domain/domain_model/dtos/entity.rb"
33
+ - "lib/k_domain/domain_model/dtos/old/entity.rb"
10
34
 
11
35
  Metrics/BlockLength:
12
36
  Exclude:
13
37
  - "**/spec/**/*"
14
38
  - "*.gemspec"
39
+ - 'lib/k_domain/domain_model/dtos/erd_file.rb'
15
40
  IgnoredMethods:
16
41
  - configure
17
42
  - context
@@ -34,12 +59,19 @@ Metrics/BlockLength:
34
59
 
35
60
  Metrics/MethodLength:
36
61
  Max: 25
62
+ Exclude:
63
+ - "lib/k_domain/domain_model/transform_steps/*.rb"
64
+ - "lib/k_domain/domain_model/dtos/rails_model.rb"
65
+ - "lib/k_domain/domain_model/dtos/old/entity.rb"
66
+ - "lib/k_domain/domain_model/dtos/old/rails_model.rb"
37
67
 
38
68
  Layout/LineLength:
39
69
  Max: 200
40
70
  # Ignores annotate output
41
71
  IgnoredPatterns: ['\A# \*\*']
42
72
  IgnoreCopDirectives: true
73
+ Exclude:
74
+ - "lib/k_domain/domain_model/transform_steps/*.rb"
43
75
 
44
76
  Lint/UnusedMethodArgument:
45
77
  AllowUnusedKeywordArguments: true
@@ -77,6 +109,12 @@ Lint/AmbiguousBlockAssociation:
77
109
  Style/AccessorGrouping:
78
110
  Enabled: false
79
111
 
112
+ Style/FormatStringToken:
113
+ Enabled: false
114
+
115
+ Style/Documentation:
116
+ Enabled: false
117
+
80
118
  Layout/SpaceBeforeComma:
81
119
  Enabled: false
82
120
  # My Preferences - End
data/Gemfile CHANGED
@@ -23,3 +23,13 @@ group :development, :test do
23
23
  gem 'rubocop-rake', require: false
24
24
  gem 'rubocop-rspec', require: false
25
25
  end
26
+
27
+ # If local dependency
28
+ if ENV['KLUE_LOCAL_GEMS']&.to_s&.downcase == 'true'
29
+ group :development, :test do
30
+ puts 'Using Local GEMs'
31
+ gem 'peeky' , path: '../peeky'
32
+ gem 'k_log' , path: '../k_log'
33
+ gem 'k_util' , path: '../k_util'
34
+ end
35
+ end
data/Rakefile CHANGED
@@ -26,7 +26,9 @@ task :publish do
26
26
  system 'gem build'
27
27
  system "gem push #{GEM_NAME}-#{KDomain::VERSION}.gem"
28
28
  end
29
-
29
+ task :build do
30
+ system 'gem build'
31
+ end
30
32
  desc 'Remove old *.gem files'
31
33
  task :clean do
32
34
  system 'rm *.gem'
data/STORIES.md CHANGED
@@ -8,9 +8,28 @@ As an Application Developer, I need a rich and configurable ERD schema, so I can
8
8
 
9
9
  ### Stories next on list
10
10
 
11
- As a Developer, I can DO_SOMETHING, so that I QUALITY_OF_LIFE
11
+ As a Developer, I can print any of the domain structures, so that I can visually my domain
12
12
 
13
- - Subtask
13
+ - Hook up log.structure
14
+
15
+ As a Developer, I can customize domain configuration, so that I can have opinions about names and types
16
+
17
+ - Handle traits
18
+
19
+ As a Developer, I can read native rails model data, so that I can leverage existing rails applications for ERD modeling
20
+
21
+ - Use Meta Programming and re-implement ActiveRecord::Base
22
+
23
+ ### Tasks next on list
24
+
25
+ Refactor / Simply
26
+
27
+ - Replace complex objects an with structs for ancillary data structures such as investigate
28
+
29
+ User acceptance testing
30
+
31
+ - Provide sample printers for each data structure to visually check data is loading
32
+ - Point raw_db_schema loader towards a complex ERD and check how it performs
14
33
 
15
34
  ## Stories and tasks
16
35
 
data/k_domain.gemspec CHANGED
@@ -33,10 +33,14 @@ Gem::Specification.new do |spec|
33
33
  f.match(%r{^(test|spec|features)/})
34
34
  end
35
35
  end
36
+
36
37
  spec.bindir = 'exe'
37
38
  spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
38
39
  spec.require_paths = ['lib']
39
40
  # spec.extensions = ['ext/k_domain/extconf.rb']
40
41
 
42
+ spec.add_dependency 'activesupport' , '~> 6'
43
+ spec.add_dependency 'dry-struct', '~> 1'
41
44
  spec.add_dependency 'k_log' , '~> 0.0.0'
45
+ spec.add_dependency 'peeky' , '~> 0.0.0'
42
46
  end
@@ -0,0 +1,35 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Annotates the original schema with methods that implement existing method calls
4
+ # that are already in the schema so that we can build a hash.
5
+ #
6
+ # Writes a new annotated schema.rb file with a public method called load that
7
+ # builds the hash
8
+
9
+ module KDomain
10
+ module DomainModel
11
+ class Load
12
+ include KLog::Logging
13
+
14
+ attr_reader :source_file
15
+ attr_reader :data
16
+
17
+ def initialize(source_file)
18
+ @source_file = source_file
19
+ end
20
+
21
+ def call
22
+ json = File.read(source_file)
23
+ @raw_data = KUtil.data.json_parse(json, as: :hash_symbolized)
24
+
25
+ @data = KDomain::Schemas::DomainModel.new(@raw_data)
26
+ end
27
+
28
+ def to_h
29
+ return nil unless defined? @raw_data
30
+
31
+ @raw_data
32
+ end
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,110 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Loads the db schema object and works through a series of enrichment steps to
4
+ # that builds the domain modal
5
+
6
+ module KDomain
7
+ module DomainModel
8
+ class Transform
9
+ include KLog::Logging
10
+
11
+ attr_reader :db_schema
12
+ attr_reader :target_step_file
13
+ attr_reader :target_file
14
+ attr_reader :erd_path
15
+
16
+ def initialize(db_schema: , target_file: , target_step_file: , erd_path:)
17
+ @db_schema = db_schema
18
+ @target_step_file = target_step_file
19
+ @target_file = target_file
20
+ @erd_path = erd_path
21
+ end
22
+
23
+ def call
24
+ valid = true
25
+ valid &&= step1
26
+ valid &&= step2
27
+ valid &&= step3
28
+ valid &&= step4
29
+ valid &&= step5
30
+ valid &&= step8 # NOT SURE WHERE THIS BELONGS
31
+
32
+ raise 'DomainModal transform failed' unless valid
33
+
34
+ write
35
+
36
+ nil
37
+ end
38
+
39
+ def step1
40
+ Step1AttachDbSchema.run(domain_data, db_schema: db_schema)
41
+ write(step: '1-attach-db-schema')
42
+ end
43
+
44
+ def step2
45
+ Step2AttachModels.run(domain_data, erd_path: erd_path)
46
+ write(step: '2-attach-model')
47
+ end
48
+
49
+ def step3
50
+ Step3AttachColumns.run(domain_data)
51
+ write(step: '3-attach-columns')
52
+ end
53
+
54
+ def step4
55
+ Step4AttachErdFiles.run(domain_data, erd_path: erd_path)
56
+ write(step: '4-attach-erd-files')
57
+ end
58
+
59
+ def step5
60
+ Step5AttachDictionary.run(domain_data, erd_path: erd_path)
61
+ write(step: '5-attach-dictionary')
62
+ end
63
+
64
+ def step8
65
+ Step8LocateRailsModels.run(domain_data, erd_path: erd_path)
66
+ write(step: '8-rails-files-models')
67
+ end
68
+
69
+ def write(step: nil)
70
+ file = if step.nil?
71
+ target_file
72
+ else
73
+ format(target_step_file, step: step)
74
+ end
75
+ FileUtils.mkdir_p(File.dirname(file))
76
+ File.write(file, JSON.pretty_generate(domain_data))
77
+ end
78
+
79
+ def domain_data
80
+ # The initial domain model structure is created here, but populated during the workflows.
81
+ @domain_data ||= {
82
+ domain: {
83
+ models: [],
84
+ erd_files: [],
85
+ },
86
+ rails: {
87
+ models: [],
88
+ controllers: [],
89
+ },
90
+ rails_resource: {
91
+ models: [],
92
+ controllers: [],
93
+ },
94
+ dictionary: {
95
+ items: []
96
+ },
97
+ database: {
98
+ tables: [],
99
+ indexes: [],
100
+ foreign_keys: [],
101
+ meta: {}
102
+ },
103
+ investigate: {
104
+ issues: []
105
+ }
106
+ }
107
+ end
108
+ end
109
+ end
110
+ end
@@ -0,0 +1,10 @@
1
+ # frozen_string_literal: true
2
+
3
+ # The require order is important due to dependencies
4
+ require_relative './step'
5
+ require_relative './step1_attach_db_schema'
6
+ require_relative './step2_attach_models'
7
+ require_relative './step3_attach_columns'
8
+ require_relative './step4_attach_erd_files'
9
+ require_relative './step5_attach_dictionary'
10
+ require_relative './step8_locate_rails_models'
@@ -0,0 +1,142 @@
1
+ # frozen_string_literal: true
2
+
3
+ module KDomain
4
+ module DomainModel
5
+ class Step
6
+ include KLog::Logging
7
+
8
+ attr_reader :domain_data
9
+ attr_reader :opts
10
+ attr_reader :valid
11
+ alias valid? valid
12
+
13
+ def initialize(domain_data, **opts)
14
+ # Useful for debugging
15
+ # log.info "Initialize #{self.class.name}"
16
+
17
+ @domain_data = domain_data
18
+ @opts = opts
19
+ @valid = true
20
+ end
21
+
22
+ def call; end
23
+
24
+ def self.run(domain_data, **opts)
25
+ step = new(domain_data, **opts)
26
+ step.call
27
+ step
28
+ end
29
+
30
+ def guard(message)
31
+ log.error message
32
+ @valid = false
33
+ end
34
+
35
+ # Domain Model Accessor/Helpers
36
+ def domain
37
+ guard('domain is missing') if domain_data[:domain].nil?
38
+
39
+ domain_data[:domain]
40
+ end
41
+
42
+ def domain_models
43
+ domain[:models]
44
+ end
45
+
46
+ # Rails File Accessor/Helpers
47
+ def rails_resource
48
+ guard('rails_resource is missing') if domain_data[:rails_resource].nil?
49
+
50
+ domain_data[:rails_resource]
51
+ end
52
+
53
+ def rails_resource_models
54
+ rails_resource[:models]
55
+ end
56
+
57
+ def rails_resource_models=(value)
58
+ rails_resource[:models] = value
59
+ end
60
+
61
+ def rails_resource_controllers
62
+ rails_resource[:controllers]
63
+ end
64
+
65
+ # Database Accessor/Helpers
66
+ def database=(value)
67
+ domain_data[:database] = value
68
+ end
69
+
70
+ def database
71
+ guard('database is missing') if domain_data[:database].nil?
72
+
73
+ domain_data[:database]
74
+ end
75
+
76
+ def database_tables
77
+ guard('database_tables is missing') if database[:tables].nil?
78
+
79
+ database[:tables]
80
+ end
81
+
82
+ def database_foreign_keys
83
+ guard('database_foreign_keys is missing') if database[:foreign_keys].nil?
84
+
85
+ database[:foreign_keys]
86
+ end
87
+
88
+ def find_table_for_model(model)
89
+ database_tables.find { |table| table[:name] == model[:table_name] }
90
+ end
91
+
92
+ def table_name_exist?(table_name)
93
+ if table_name.nil?
94
+ guard('table_name_exist? was provided with a table_name: nil')
95
+ return false
96
+ end
97
+ database_table_name_hash.key?(table_name)
98
+ end
99
+
100
+ def find_foreign_table(lhs_table_name, column_name)
101
+ fk = database_foreign_keys.find { |foreign_key| foreign_key[:left] == lhs_table_name && foreign_key[:column] == column_name }
102
+ return fk[:right] if fk
103
+
104
+ nil
105
+ end
106
+
107
+ def investigate(step:, location:, key:, message:)
108
+ unique_key = build_key(step, location, key)
109
+
110
+ return if issue_hash.key?(unique_key)
111
+
112
+ value = { step: step, location: location, key: key, message: message }
113
+
114
+ issues << value # list
115
+ issue_hash[unique_key] = value # lookup
116
+ end
117
+
118
+ def issues
119
+ domain_data[:investigate][:issues]
120
+ end
121
+
122
+ private
123
+
124
+ def database_table_name_hash
125
+ @database_table_name_hash ||= database_tables.to_h { |table| [table[:name], table[:name]] }
126
+ end
127
+
128
+ def build_key(*values)
129
+ values.join('-')
130
+ end
131
+
132
+ def issue_hash
133
+ return @issue_hash if defined? @issue_hash
134
+
135
+ @issue_hash = issues.to_h do |issue|
136
+ unique_key = build_key(issue[:step], issue[:location], issue[:key])
137
+ [unique_key, issue]
138
+ end
139
+ end
140
+ end
141
+ end
142
+ end
@@ -0,0 +1,21 @@
1
+ # frozen_string_literal: true
2
+
3
+ module KDomain
4
+ module DomainModel
5
+ class Step1AttachDbSchema < KDomain::DomainModel::Step
6
+ # Map database schema to domain model
7
+ def call
8
+ raise 'Schema not supplied' if opts[:db_schema].nil?
9
+
10
+ self.database = opts[:db_schema].clone
11
+
12
+ guard('tables are missing') if database[:tables].nil?
13
+ guard('indexes are missing') if database[:indexes].nil?
14
+ guard('foreign keys are missing') if database[:foreign_keys].nil?
15
+ guard('rails version is missing') if database[:meta][:rails].nil?
16
+ guard('postgres extensions are missing') if database[:meta][:db_info][:extensions].nil?
17
+ guard('unique keys are missing') if database[:meta][:unique_keys].nil?
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,62 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Loop through the db_schema tables and build up a
4
+ # basic model for each table
5
+ class Step2AttachModels < KDomain::DomainModel::Step
6
+ # Map database schema to domain model
7
+ def call
8
+ raise 'ERD path not supplied' if opts[:erd_path].nil?
9
+
10
+ # Schema is re-shaped into a format designed for domain modeling
11
+ domain[:models] = database_tables.map { |table| model(table) }
12
+ end
13
+
14
+ def model(table)
15
+ table_name = table[:name].to_s
16
+ model_name = table_name.singularize
17
+
18
+ {
19
+ name: model_name,
20
+ name_plural: table_name, # need to check if this is correct as I know it is wrong for account_history_datum
21
+ table_name: table_name,
22
+ pk: primary_key(table),
23
+ erd_location: location(table_name, model_name),
24
+ statistics: {}, # Load in future step
25
+ columns: [] # Load in future step
26
+ }
27
+ end
28
+
29
+ def primary_key(table)
30
+ {
31
+ name: table[:primary_key],
32
+ type: table[:primary_key_type],
33
+ exist: !table[:primary_key].nil?
34
+ }
35
+ end
36
+
37
+ # Location of source code
38
+ def location(table_name, model_name)
39
+ file_normal = File.join(opts[:erd_path], "#{model_name}.rb")
40
+ file_custom = File.join(opts[:erd_path], "#{table_name}.rb")
41
+ file_exist = true
42
+ state = []
43
+
44
+ if File.exist?(file_normal)
45
+ file = file_normal
46
+ state.push(:has_ruby_model)
47
+ elsif File.exist?(file_custom)
48
+ file = file_custom
49
+ state.push(:has_ruby_model)
50
+ state.push(:nonconventional_name)
51
+ else
52
+ file = ''
53
+ file_exist = false
54
+ end
55
+
56
+ {
57
+ file: file,
58
+ exist: file_exist,
59
+ state: state # display_state: state.join(' ')
60
+ }
61
+ end
62
+ end
@@ -0,0 +1,137 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Attach columns to models
4
+ class Step3AttachColumns < KDomain::DomainModel::Step
5
+ attr_accessor :table
6
+ attr_reader :column_name
7
+ attr_reader :column_symbol
8
+
9
+ def call
10
+ build_columns
11
+ end
12
+
13
+ def build_columns
14
+ domain_models.each do |model|
15
+ @table = find_table_for_model(model)
16
+ columns = columns(table[:columns])
17
+ columns = insert_primary_key(model, columns)
18
+ model[:columns] = columns
19
+ end
20
+ end
21
+
22
+ def column_data(name)
23
+ @column_name = name
24
+ @column_symbol = name.to_sym
25
+ {
26
+ name: name,
27
+ name_plural: name.pluralize,
28
+ type: nil,
29
+ precision: nil,
30
+ scale: nil,
31
+ default: nil,
32
+ null: nil,
33
+ limit: nil,
34
+ array: nil
35
+ }
36
+ end
37
+
38
+ def columns(db_columns)
39
+ db_columns.map do |db_column|
40
+ column = column_data(db_column[:name]).merge(
41
+ type: check_type(db_column[:type]),
42
+ precision: db_column[:precision],
43
+ scale: db_column[:scale],
44
+ default: db_column[:default],
45
+ null: db_column[:null],
46
+ limit: db_column[:limit],
47
+ array: db_column[:array]
48
+ )
49
+
50
+ expand_column(column)
51
+ end
52
+ end
53
+
54
+ def insert_primary_key(model, columns)
55
+ return columns unless model[:pk][:exist]
56
+
57
+ column = column_data('id').merge(
58
+ type: check_type(model[:pk][:type])
59
+ )
60
+
61
+ columns.unshift(expand_column(column))
62
+ columns
63
+ end
64
+
65
+ def expand_column(column)
66
+ foreign_table = lookup_foreign_table(column_name)
67
+ is_foreign = !foreign_table.nil?
68
+ # is_foreign = foreign_key?(column_name)
69
+ structure_type = structure_type(is_foreign)
70
+
71
+ column.merge({
72
+ structure_type: structure_type,
73
+ foreign_key: is_foreign,
74
+ foreign_table: (foreign_table || '').singularize,
75
+ foreign_table_plural: (foreign_table || '').pluralize
76
+ })
77
+ end
78
+
79
+ def check_type(type)
80
+ type = type.to_sym if type.is_a?(String)
81
+
82
+ return type if %i[string integer bigint bigserial boolean float decimal datetime date hstore text jsonb].include?(type)
83
+
84
+ if type.nil?
85
+ guard('nil type detected for db_column[:type]')
86
+
87
+ return :string
88
+ end
89
+
90
+ guard("new type detected for db_column[:type] - #{type}")
91
+
92
+ camel.parse(type.to_s).downcase
93
+ end
94
+
95
+ def lookup_foreign_table(column_name)
96
+ foreign_table = find_foreign_table(table[:name], column_name)
97
+
98
+ return foreign_table if foreign_table
99
+
100
+ cn = column_name.to_s
101
+
102
+ if cn.ends_with?('_id')
103
+ table_name = column_name[0..-4]
104
+ table_name_plural = table_name.pluralize
105
+
106
+ if table_name_exist?(table_name_plural.to_s)
107
+ investigate(step: :step3_attach_columns,
108
+ location: :lookup_foreign_table,
109
+ key: column_name,
110
+ message: "#{@table[:name]}.#{column_name} => #{table_name_plural} - Relationship not found in DB, so have inferred this relationship. You may want to check that this relation is correct")
111
+
112
+ return table_name
113
+ end
114
+
115
+ investigate(step: :step3_attach_columns,
116
+ location: :lookup_foreign_table,
117
+ key: column_name,
118
+ message: "#{@table[:name]}.#{column_name} => #{table_name_plural} - Table not found for a column that looks like foreign_key")
119
+ end
120
+
121
+ nil
122
+ end
123
+
124
+ # Need some configurable data dictionary where by
125
+ # _token can be setup on a project by project basis
126
+ def structure_type(is_foreign)
127
+ return :foreign_key if is_foreign
128
+ return :primary_key if column_symbol == :id
129
+ return :timestamp if column_symbol == :created_at || column_symbol == :updated_at
130
+ return :timestamp if column_symbol == :created_at || column_symbol == :updated_at
131
+ return :deleted_at if column_symbol == :deleted_at
132
+ return :encrypted_password if column_symbol == :encrypted_password
133
+ return :token if column_name.ends_with?('_token') || column_name.ends_with?('_token_iv')
134
+
135
+ :data
136
+ end
137
+ end