k_domain 0.0.1 → 0.0.14

Sign up to get free protection for your applications and to get access to all the features.
Files changed (55) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +39 -1
  3. data/Gemfile +10 -0
  4. data/Guardfile +30 -0
  5. data/Rakefile +3 -4
  6. data/STORIES.md +63 -0
  7. data/USAGE.md +19 -0
  8. data/k_domain.gemspec +4 -0
  9. data/lib/k_domain/domain_model/load.rb +29 -0
  10. data/lib/k_domain/domain_model/transform.rb +110 -0
  11. data/lib/k_domain/domain_model/transform_steps/_.rb +10 -0
  12. data/lib/k_domain/domain_model/transform_steps/step.rb +142 -0
  13. data/lib/k_domain/domain_model/transform_steps/step1_attach_db_schema.rb +21 -0
  14. data/lib/k_domain/domain_model/transform_steps/step2_attach_models.rb +62 -0
  15. data/lib/k_domain/domain_model/transform_steps/step3_attach_columns.rb +137 -0
  16. data/lib/k_domain/domain_model/transform_steps/step4_attach_erd_files.rb +454 -0
  17. data/lib/k_domain/domain_model/transform_steps/step5_attach_dictionary.rb +58 -0
  18. data/lib/k_domain/domain_model/transform_steps/step8_locate_rails_models.rb +44 -0
  19. data/lib/k_domain/raw_db_schema/load.rb +29 -0
  20. data/lib/k_domain/raw_db_schema/transform.rb +82 -0
  21. data/lib/k_domain/schemas/_.rb +15 -0
  22. data/lib/k_domain/schemas/database/_.rb +7 -0
  23. data/lib/k_domain/schemas/database/foreign_key.rb +14 -0
  24. data/lib/k_domain/schemas/database/index.rb +14 -0
  25. data/lib/k_domain/schemas/database/schema.rb +31 -0
  26. data/lib/k_domain/schemas/database/table.rb +32 -0
  27. data/lib/k_domain/schemas/dictionary.rb +19 -0
  28. data/lib/k_domain/schemas/domain/_.rb +65 -0
  29. data/lib/k_domain/schemas/domain/domain.rb +11 -0
  30. data/lib/k_domain/schemas/domain/erd_file.rb +80 -0
  31. data/lib/k_domain/schemas/domain/models/column.rb +49 -0
  32. data/lib/k_domain/schemas/domain/models/model.rb +111 -0
  33. data/lib/k_domain/schemas/domain/old/belongs_to.rb +25 -0
  34. data/lib/k_domain/schemas/domain/old/column_old.rb +225 -0
  35. data/lib/k_domain/schemas/domain/old/domain_statistics.rb +29 -0
  36. data/lib/k_domain/schemas/domain/old/entity.rb +338 -0
  37. data/lib/k_domain/schemas/domain/old/entity_statistics.rb +22 -0
  38. data/lib/k_domain/schemas/domain/old/foreign_key.rb +17 -0
  39. data/lib/k_domain/schemas/domain/old/has_and_belongs_to_many.rb +20 -0
  40. data/lib/k_domain/schemas/domain/old/has_many.rb +27 -0
  41. data/lib/k_domain/schemas/domain/old/has_one.rb +41 -0
  42. data/lib/k_domain/schemas/domain/old/name_options.rb +10 -0
  43. data/lib/k_domain/schemas/domain/old/rails_controller.rb +10 -0
  44. data/lib/k_domain/schemas/domain/old/rails_model.rb +92 -0
  45. data/lib/k_domain/schemas/domain/old/related_entity.rb +36 -0
  46. data/lib/k_domain/schemas/domain/old/statistics.rb +21 -0
  47. data/lib/k_domain/schemas/domain/old/validate.rb +25 -0
  48. data/lib/k_domain/schemas/domain/old/validates.rb +50 -0
  49. data/lib/k_domain/schemas/domain_model.rb +14 -0
  50. data/lib/k_domain/schemas/investigate.rb +15 -0
  51. data/lib/k_domain/schemas/rails_resource.rb +16 -0
  52. data/lib/k_domain/version.rb +1 -1
  53. data/lib/k_domain.rb +22 -1
  54. data/templates/load_schema.rb +226 -0
  55. metadata +91 -2
@@ -0,0 +1,41 @@
1
+ # frozen_string_literal: true
2
+
3
+ module KDomain
4
+ module DomainModel
5
+ class HasOne
6
+ # KEYS = [:model_name, :model_name_plural, :a_lambda, :class_name, :foreign_key, :primary_key, :infer_key]
7
+ KEYS = %i[a_lambda class_name foreign_key primary_key infer_key code_duplicate].freeze
8
+
9
+ attr_accessor :name
10
+
11
+ attr_accessor :model_name
12
+ attr_accessor :model_name_plural
13
+
14
+ attr_accessor :a_lambda
15
+ attr_accessor :class_name
16
+ attr_accessor :foreign_key
17
+ attr_accessor :primary_key
18
+
19
+ def infer_key
20
+ primary_key.nil? ? "#{name}_id" : primary_key
21
+ end
22
+
23
+ attr_accessor :related_entity
24
+ attr_accessor :code_duplicate
25
+
26
+ def to_h
27
+ {
28
+ name: name,
29
+ model_name: model_name,
30
+ model_name_plural: model_name_plural,
31
+ a_lambda: a_lambda,
32
+ class_name: class_name,
33
+ foreign_key: foreign_key,
34
+ primary_key: primary_key,
35
+ code_duplicate: code_duplicate,
36
+ related_entity: related_entity.to_h
37
+ }
38
+ end
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,10 @@
1
+ # frozen_string_literal: true
2
+
3
+ module KDomain
4
+ module DomainModel
5
+ class NameOptions
6
+ attr_accessor :name
7
+ attr_accessor :options
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,10 @@
1
+ # frozen_string_literal: true
2
+
3
+ module KDomain
4
+ module DomainModel
5
+ class RailsController
6
+ # ToDo
7
+ puts 'do soemtnhi'
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,92 @@
1
+ # frozen_string_literal: true
2
+
3
+ module KDomain
4
+ module DomainModel
5
+ # Rails model represents information that is found the model.rb class in the rails project
6
+ class RailsModel
7
+ attr_accessor :name
8
+ attr_accessor :name_plural
9
+ attr_accessor :name_original
10
+ attr_accessor :documentation_rel_path
11
+ attr_accessor :model_path
12
+
13
+ # @param [Symbol] value The value of ID has different meanings
14
+ # @option value :true Id column exists and it uses an Integer type
15
+ # @option value :false Id column does not exist
16
+ # @option value :bigserial Id column exists and it uses a BigSerial type
17
+ attr_accessor :id
18
+
19
+ attr_accessor :force
20
+ attr_accessor :primary_key
21
+ attr_accessor :quirks
22
+
23
+ attr_accessor :ruby_raw
24
+ attr_accessor :ruby_code
25
+ attr_accessor :ruby_frozen
26
+ attr_accessor :ruby_header
27
+ attr_accessor :ruby_code_public
28
+ attr_accessor :ruby_code_private
29
+
30
+ attr_accessor :default_scope
31
+ attr_accessor :scopes
32
+ attr_accessor :public_class_methods
33
+ attr_accessor :public_instance_methods
34
+ attr_accessor :private_instance_methods
35
+
36
+ # stats
37
+ attr_accessor :time_stamp1
38
+ attr_accessor :time_stamp2
39
+ attr_accessor :time_stamp3
40
+
41
+ def code_length
42
+ ruby_raw&.length
43
+ end
44
+
45
+ def display_quirks
46
+ quirks.join(' ')
47
+ end
48
+
49
+ def exists?
50
+ File.exist?(model_path)
51
+ end
52
+
53
+ def initialize
54
+ @quirks = []
55
+ end
56
+
57
+ def add_quirk(quirk)
58
+ @quirks << quirk
59
+ end
60
+
61
+ def to_h
62
+ {
63
+ name: name,
64
+ name_plural: name_plural,
65
+ name_original: name_original,
66
+ documentation_rel_path: documentation_rel_path,
67
+ model_path: model_path,
68
+ id: id,
69
+ force: force,
70
+ primary_key: primary_key,
71
+ quirks: quirks,
72
+ ruby_raw: ruby_raw,
73
+ ruby_code: ruby_code,
74
+ ruby_frozen: ruby_frozen,
75
+ ruby_header: ruby_header,
76
+ ruby_code_public: ruby_code_public,
77
+ ruby_code_private: ruby_code_private,
78
+ default_scope: default_scope,
79
+ scopes: scopes,
80
+ public_class_methods: public_class_methods,
81
+ public_instance_methods: public_instance_methods,
82
+ private_instance_methods: private_instance_methods,
83
+ time_stamp1: time_stamp1,
84
+ time_stamp2: time_stamp2,
85
+ time_stamp3: time_stamp3,
86
+ code_length: code_length,
87
+ exists: exists?
88
+ }
89
+ end
90
+ end
91
+ end
92
+ end
@@ -0,0 +1,36 @@
1
+ # frozen_string_literal: true
2
+
3
+ module KDomain
4
+ module DomainModel
5
+ class RelatedEntity
6
+ # Name of the entity model
7
+ attr_accessor :name
8
+ attr_accessor :name_plural
9
+ attr_accessor :main_key
10
+
11
+ attr_accessor :trait1
12
+ attr_accessor :trait2
13
+ attr_accessor :trait3
14
+
15
+ def initialize(entity)
16
+ @name = entity.name
17
+ @name_plural = entity.name_plural
18
+ @main_key = entity.main_key
19
+ @trait1 = entity.trait1
20
+ @trait2 = entity.trait2
21
+ @trait3 = entity.trait3
22
+ end
23
+
24
+ def to_h
25
+ {
26
+ name: name,
27
+ name_plural: name_plural,
28
+ main_key: main_key,
29
+ trait1: trait1,
30
+ trait2: trait2,
31
+ trait3: trait3
32
+ }
33
+ end
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,21 @@
1
+ # frozen_string_literal: true
2
+ # module KDomain
3
+ # module DomainModel
4
+ # # Rails model represents information that is found the model.rb class in the rails project
5
+ # class Statistics
6
+ # attr_accessor :column_counts
7
+ # attr_accessor :code_counts
8
+ # attr_accessor :code_dsl_counts
9
+ # attr_accessor :data_counts
10
+ # attr_accessor :issues
11
+
12
+ # def initialize(meta)
13
+ # @column_counts = OpenStruct.new(meta[:column_counts])
14
+ # @code_counts = OpenStruct.new(meta[:code_counts])
15
+ # @code_dsl_counts = OpenStruct.new(meta[:code_dsl_counts])
16
+ # @data_counts = OpenStruct.new(meta[:data_counts])
17
+ # @issues = meta[:issues]
18
+ # end
19
+ # end
20
+ # end
21
+ # end
@@ -0,0 +1,25 @@
1
+ # frozen_string_literal: true
2
+
3
+ module KDomain
4
+ module DomainModel
5
+ class Validate
6
+ KEYS = [:on].freeze
7
+
8
+ attr_accessor :methods
9
+
10
+ attr_accessor :on
11
+
12
+ def format_on
13
+ for_template(on)
14
+ end
15
+
16
+ def for_template(value)
17
+ return nil if value.nil?
18
+ return value.to_s if value.is_a?(Hash)
19
+ return ":#{value}" if value.is_a?(Symbol)
20
+
21
+ value
22
+ end
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,50 @@
1
+ # frozen_string_literal: true
2
+
3
+ module KDomain
4
+ module DomainModel
5
+ class Validates
6
+ KEYS = %i[length unless format presence].freeze
7
+
8
+ attr_accessor :name
9
+
10
+ attr_accessor :length
11
+ attr_accessor :unless
12
+ attr_accessor :format
13
+ attr_accessor :presence
14
+
15
+ def format_length
16
+ for_template(length)
17
+ end
18
+
19
+ def format_unless
20
+ for_template(self.unless)
21
+ end
22
+
23
+ def format_format
24
+ for_template(self.format)
25
+ end
26
+
27
+ def format_presence
28
+ for_template(presence)
29
+ end
30
+
31
+ def for_template(value)
32
+ return nil if value.nil?
33
+ return value.to_s if value.is_a?(Hash)
34
+ return ":#{value}" if value.is_a?(Symbol)
35
+
36
+ value
37
+ end
38
+
39
+ def to_h
40
+ {
41
+ name: name,
42
+ length: length,
43
+ unless: self.unless,
44
+ format: self.format,
45
+ presence: presence
46
+ }
47
+ end
48
+ end
49
+ end
50
+ end
@@ -0,0 +1,14 @@
1
+ # frozen_string_literal: true
2
+
3
+ # DomainModel holds the entire domain model including database and ancillary information
4
+ module KDomain
5
+ module Schemas
6
+ class DomainModel < Dry::Struct
7
+ attribute :domain , KDomain::DomainModel::Domain
8
+ attribute :database , KDomain::Database::Schema
9
+ attribute :dictionary , KDomain::Schemas::Dictionary
10
+ attribute :rails_resource , KDomain::Schemas::RailsResource
11
+ attribute :investigate , KDomain::Schemas::Investigate
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Domain class holds a dictionary entry
4
+ module KDomain
5
+ module Schemas
6
+ class Investigate < Dry::Struct
7
+ attribute :issues , Types::Strict::Array do
8
+ attribute :step , Types::Strict::String
9
+ attribute :location , Types::Strict::String
10
+ attribute :key , Types::Strict::String.optional.default(nil)
11
+ attribute :message , Types::Strict::String
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,16 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Domain class holds a dictionary entry
4
+ module KDomain
5
+ module Schemas
6
+ class RailsResource < Dry::Struct
7
+ attribute :models , Types::Strict::Array do
8
+ attribute :model_name , Types::Strict::String
9
+ attribute :table_name , Types::Strict::String
10
+ attribute :file , Types::Strict::String
11
+ attribute :exist , Types::Strict::Bool
12
+ attribute :state , Types::Strict::String
13
+ end
14
+ end
15
+ end
16
+ end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module KDomain
4
- VERSION = '0.0.1'
4
+ VERSION = '0.0.14'
5
5
  end
data/lib/k_domain.rb CHANGED
@@ -1,14 +1,35 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require 'active_support/core_ext/string'
4
+ require 'dry-struct'
3
5
  require 'k_log'
6
+ require 'peeky'
4
7
  require 'k_domain/version'
8
+ require 'k_domain/schemas/_'
9
+ require 'k_domain/raw_db_schema/transform'
10
+ require 'k_domain/raw_db_schema/load'
11
+ require 'k_domain/domain_model/transform'
12
+ require 'k_domain/domain_model/transform_steps/_'
13
+ require 'k_domain/domain_model/load'
5
14
 
6
-
15
+ # # This is useful if you want to initialize structures via Hash
16
+ # class SymbolizeStruct < Dry::Struct
17
+ # transform_keys(&:to_sym)
18
+ # end
7
19
 
8
20
  module KDomain
9
21
  # raise KDomain::Error, 'Sample message'
10
22
  class Error < StandardError; end
11
23
 
24
+ module Gem
25
+ def self.root
26
+ File.expand_path('..', File.dirname(__FILE__))
27
+ end
28
+ def self.resource(resource_path)
29
+ File.join(root, resource_path)
30
+ end
31
+ end
32
+
12
33
  # Your code goes here...
13
34
  end
14
35
 
@@ -0,0 +1,226 @@
1
+ class LoadSchema
2
+ attr_reader :schema
3
+
4
+ def initialize
5
+ @unique_keys = {}
6
+ @current_table = nil
7
+ @rails_version = 4
8
+ @schema = {
9
+ tables: [],
10
+ foreign_keys: [],
11
+ indexes: [],
12
+ meta: {
13
+ rails: @rails_version,
14
+ db_info: {
15
+ type: 'postgres',
16
+ version: nil, # TODO
17
+ extensions: []
18
+ },
19
+ unique_keys: []
20
+ }
21
+ }
22
+ end
23
+
24
+ # ----------------------------------------------------------------------
25
+ # Inject start
26
+ # original file: {{source_file}}
27
+ # ----------------------------------------------------------------------
28
+ def load_schema
29
+ {{rails_schema}}
30
+ end
31
+
32
+ # ----------------------------------------------------------------------
33
+ # original file: {{source_file}}
34
+ # Inject end
35
+ # ----------------------------------------------------------------------
36
+
37
+ def write_json(file)
38
+ schema[:meta][:rails] = @rails_version
39
+ File.write(file, JSON.pretty_generate(schema))
40
+ end
41
+
42
+ # This is the rails timestamp and will be replaced by the action rails version
43
+ def load(version:)
44
+ start = Process.clock_gettime(Process::CLOCK_MONOTONIC)
45
+ # puts 'about to load'
46
+ yield if block_given?
47
+
48
+ schema[:meta][:rails] = @rails_version
49
+
50
+ sort
51
+ # code to time
52
+
53
+ # log.kv 'extensions', schema[:db_info][:extensions].length
54
+ # log.kv 'tables', schema[:tables].length
55
+ # log.kv 'indexes', schema[:indexes].length
56
+ # # a low foreign_keys count is indicative of not using SQL referential integrity
57
+ # log.kv 'foreign_keys', schema[:foreign_keys].length
58
+ # log.kv 'Time Taken', (finish - start)
59
+
60
+ # puts schema[:db_info][:extensions]
61
+ # print_unique_keys(type: :foreign_keys, title: 'unique options for foreign_keys')
62
+ # print_unique_keys(type: :columns, title: 'unique options for columns')
63
+ # print_unique_keys(type: :fields, category: :integer , title: 'unique options for column - integer')
64
+ # print_unique_keys(type: :fields, category: :decimal , title: 'unique options for column - decimal')
65
+ # print_unique_keys(type: :fields, category: :string , title: 'unique options for column - string')
66
+ # print_unique_keys(type: :fields, category: :datetime, title: 'unique options for column - datetime')
67
+ # print_unique_keys(type: :fields, category: :date , title: 'unique options for column - date')
68
+ # print_unique_keys(type: :fields, category: :text , title: 'unique options for column - text')
69
+ # print_unique_keys(type: :fields, category: :boolean , title: 'unique options for column - boolean')
70
+ # print_unique_keys(type: :fields, category: :jsonb , title: 'unique options for column - jsonb')
71
+ # print_unique_keys(type: :fields, category: :hstore , title: 'unique options for column - hstore')
72
+ # print_unique_keys(type: :fields, category: :float , title: 'unique options for column - float')
73
+ end
74
+
75
+ def enable_extension(name)
76
+ # puts "enable_extension(#{name})"
77
+ schema[:meta][:db_info][:extensions] << name
78
+ end
79
+
80
+ def create_table(name, **opts)
81
+ id = opts[:id]
82
+ primary_key = opts[:primary_key] || (id == false ? nil : "id")
83
+ primary_key_type = if id == false
84
+ nil
85
+ elsif id.nil?
86
+ "bigint"
87
+ else
88
+ id
89
+ end
90
+
91
+ @current_table = {
92
+ name: name,
93
+ primary_key: primary_key, # infer the actual value that should be in the database
94
+ primary_key_type: primary_key_type, # infer the actual value that should be in the database
95
+ columns: [],
96
+ indexes: [],
97
+ rails_schema: { # as reported by the rails schema
98
+ primary_key: opts[:primary_key],
99
+ id: id,
100
+ force: opts[:force]
101
+ }
102
+ }
103
+ # schema[:tables][name] = @current_table
104
+ schema[:tables] << @current_table
105
+
106
+ yield(self) if block_given?
107
+ end
108
+
109
+ def add_field(name, type, **opts)
110
+ # puts "add_field(#{name}, #{type})"
111
+ row = { name: name, type: type, **opts }
112
+ @current_table[:columns] << row
113
+
114
+ add_unique_keys(row.keys, type: :columns)
115
+ add_unique_keys(row.keys, type: :fields, category: type)
116
+ end
117
+
118
+ def add_index(name, fields, **opts)
119
+ # puts "add_index(#{name})"
120
+ row = { name: name, fields: fields, **opts }
121
+ @current_table[:indexes] << row
122
+ schema[:indexes] << row
123
+ add_unique_keys(row.keys, type: :indexes)
124
+ end
125
+
126
+ # This method was introduced onto the schema in rails 5
127
+ def index(fields, **opts)
128
+ @rails_version = 5
129
+ name = opts[:name]
130
+ opts.delete(:name)
131
+ add_index(name, fields, **opts)
132
+ end
133
+
134
+
135
+ def add_foreign_key(left_table, right_table, **opts)
136
+ # puts "add_foreign_key(#{left_table}, #{right_table})"
137
+ row = { left: left_table, right: right_table, **opts }
138
+ schema[:foreign_keys] << row
139
+ add_unique_keys(row.keys, type: :foreign_keys)
140
+ end
141
+
142
+ def add_unique_keys(keys, type:, category: nil)
143
+ key = [type, category, keys.join('-')].compact.join('|')
144
+ return if @unique_keys.key?(key)
145
+
146
+ @unique_keys[key] = key
147
+ schema[:meta][:unique_keys] << { type: type, category: category, key: keys.join(','), keys: keys }
148
+ end
149
+
150
+ def print_unique_keys(type:, category: nil, title: )
151
+ log.section_heading(title)
152
+
153
+ filter_key_infos = schema[:meta][:unique_keys].select { |key_info| key_info[:type] == type && (category.nil? || key_info[:category] == category) }
154
+
155
+ # log.kv 'all', filter_key_infos.flat_map { |key_info| key_info[:keys] }.uniq, 50
156
+
157
+ filter_key_infos.each do |key_info|
158
+ log.kv key_info[:key], key_info[:keys], 50
159
+ end
160
+ end
161
+
162
+ def integer(name, **opts)
163
+ add_field(name, :integer, **opts)
164
+ end
165
+
166
+ def bigint(name, **opts)
167
+ add_field(name, :bigint, **opts)
168
+ end
169
+
170
+ def decimal(name, **opts)
171
+ add_field(name, :decimal, **opts)
172
+ end
173
+
174
+ def string(name, **opts)
175
+ add_field(name, :string, **opts)
176
+ end
177
+
178
+ def datetime(name, **opts)
179
+ add_field(name, :datetime, **opts)
180
+ end
181
+
182
+ def date(name, **opts)
183
+ add_field(name, :date, **opts)
184
+ end
185
+
186
+ def text(name, **opts)
187
+ add_field(name, :text, **opts)
188
+ end
189
+
190
+ def boolean(name, **opts)
191
+ add_field(name, :boolean, **opts)
192
+ end
193
+
194
+ def jsonb(name, **opts)
195
+ add_field(name, :jsonb, **opts)
196
+ end
197
+
198
+ def hstore(name, **opts)
199
+ add_field(name, :hstore, **opts)
200
+ end
201
+
202
+ def float(name, **opts)
203
+ add_field(name, :float, **opts)
204
+ end
205
+
206
+ def sort
207
+ schema[:indexes].sort_by! { |i| i[:name] }
208
+ schema[:tables].each { |table| table[:indexes].sort_by! { |i| i[:name] } }
209
+
210
+ # Insert a key that represents all unique keys, and then sort
211
+ unique_keys_per_group = schema[:meta][:unique_keys]
212
+ .group_by { |key_info| [key_info[:type], key_info[:category]] }
213
+ .map do |group, values|
214
+ all_keys = values.flat_map { |key_info| key_info[:keys] }.uniq
215
+ {
216
+ type: group[0],
217
+ category: group[01],
218
+ key: 'all',
219
+ keys: all_keys
220
+ }
221
+ end
222
+
223
+ schema[:meta][:unique_keys].concat(unique_keys_per_group)
224
+ schema[:meta][:unique_keys].sort! { |a,b| ([a[:type], a[:category],a[:key]] <=> [b[:type], b[:category],b[:key]]) }
225
+ end
226
+ end