k_domain 0.0.1 → 0.0.14

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