k_domain 0.0.2 → 0.0.15
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.rubocop.yml +39 -1
- data/Gemfile +10 -0
- data/Rakefile +3 -1
- data/STORIES.md +21 -2
- data/k_domain.gemspec +4 -0
- data/lib/k_domain/domain_model/load.rb +35 -0
- data/lib/k_domain/domain_model/transform.rb +110 -0
- data/lib/k_domain/domain_model/transform_steps/_.rb +10 -0
- data/lib/k_domain/domain_model/transform_steps/step.rb +142 -0
- data/lib/k_domain/domain_model/transform_steps/step1_attach_db_schema.rb +21 -0
- data/lib/k_domain/domain_model/transform_steps/step2_attach_models.rb +62 -0
- data/lib/k_domain/domain_model/transform_steps/step3_attach_columns.rb +137 -0
- data/lib/k_domain/domain_model/transform_steps/step4_attach_erd_files.rb +456 -0
- data/lib/k_domain/domain_model/transform_steps/step5_attach_dictionary.rb +58 -0
- data/lib/k_domain/domain_model/transform_steps/step8_locate_rails_models.rb +44 -0
- data/lib/k_domain/raw_db_schema/load.rb +35 -0
- data/lib/k_domain/{raw_schema → raw_db_schema}/transform.rb +36 -21
- data/lib/k_domain/schemas/_.rb +15 -0
- data/lib/k_domain/schemas/database/_.rb +7 -0
- data/lib/k_domain/schemas/database/foreign_key.rb +14 -0
- data/lib/k_domain/schemas/database/index.rb +14 -0
- data/lib/k_domain/schemas/database/schema.rb +31 -0
- data/lib/k_domain/schemas/database/table.rb +32 -0
- data/lib/k_domain/schemas/dictionary.rb +19 -0
- data/lib/k_domain/schemas/domain/_.rb +65 -0
- data/lib/k_domain/schemas/domain/domain.rb +11 -0
- data/lib/k_domain/schemas/domain/erd_file.rb +80 -0
- data/lib/k_domain/schemas/domain/models/column.rb +49 -0
- data/lib/k_domain/schemas/domain/models/model.rb +111 -0
- data/lib/k_domain/schemas/domain/old/belongs_to.rb +25 -0
- data/lib/k_domain/schemas/domain/old/column_old.rb +225 -0
- data/lib/k_domain/schemas/domain/old/domain_statistics.rb +29 -0
- data/lib/k_domain/schemas/domain/old/entity.rb +338 -0
- data/lib/k_domain/schemas/domain/old/entity_statistics.rb +22 -0
- data/lib/k_domain/schemas/domain/old/foreign_key.rb +17 -0
- data/lib/k_domain/schemas/domain/old/has_and_belongs_to_many.rb +20 -0
- data/lib/k_domain/schemas/domain/old/has_many.rb +27 -0
- data/lib/k_domain/schemas/domain/old/has_one.rb +41 -0
- data/lib/k_domain/schemas/domain/old/name_options.rb +10 -0
- data/lib/k_domain/schemas/domain/old/rails_controller.rb +10 -0
- data/lib/k_domain/schemas/domain/old/rails_model.rb +92 -0
- data/lib/k_domain/schemas/domain/old/related_entity.rb +36 -0
- data/lib/k_domain/schemas/domain/old/statistics.rb +21 -0
- data/lib/k_domain/schemas/domain/old/validate.rb +25 -0
- data/lib/k_domain/schemas/domain/old/validates.rb +50 -0
- data/lib/k_domain/schemas/domain_model.rb +14 -0
- data/lib/k_domain/schemas/investigate.rb +15 -0
- data/lib/k_domain/schemas/rails_resource.rb +16 -0
- data/lib/k_domain/version.rb +1 -1
- data/lib/k_domain.rb +23 -1
- data/{lib/k_domain/raw_schema/template.rb → templates/load_schema.rb} +4 -4
- metadata +88 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: a50df0535cc32830a2035c2d2ddd328454efcef8aa97199485ae4db1dc7d64e7
|
4
|
+
data.tar.gz: 34824c4725142b76154a256d6466e7c83e8fc684178f6cb589552ea508d49dfa
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
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/
|
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
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
|
11
|
+
As a Developer, I can print any of the domain structures, so that I can visually my domain
|
12
12
|
|
13
|
-
-
|
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
|