k_domain 0.0.20 → 0.0.27

Sign up to get free protection for your applications and to get access to all the features.
Files changed (69) hide show
  1. checksums.yaml +4 -4
  2. data/.builders/boot.rb +40 -0
  3. data/.builders/generators/configuration_generator.rb +22 -0
  4. data/.builders/run.rb +16 -0
  5. data/.github/workflows/main.yml +5 -3
  6. data/.gitignore +2 -0
  7. data/.rubocop.yml +9 -5
  8. data/Gemfile +1 -1
  9. data/Guardfile +1 -0
  10. data/README.md +15 -0
  11. data/k_domain.gemspec +1 -1
  12. data/lib/k_domain/config/_.rb +4 -0
  13. data/lib/k_domain/config/config.rb +19 -0
  14. data/lib/k_domain/config/configuration.rb +76 -0
  15. data/lib/k_domain/domain_model/build_rich_models.rb +64 -0
  16. data/lib/k_domain/domain_model/load.rb +44 -1
  17. data/lib/k_domain/domain_model/transform.rb +28 -16
  18. data/lib/k_domain/domain_model/transform_steps/_.rb +7 -5
  19. data/lib/k_domain/domain_model/transform_steps/step.rb +20 -0
  20. data/lib/k_domain/domain_model/transform_steps/{step1_attach_db_schema.rb → step1_db_schema.rb} +2 -1
  21. data/lib/k_domain/domain_model/transform_steps/{step6_attach_dictionary.rb → step20_dictionary.rb} +6 -6
  22. data/lib/k_domain/domain_model/transform_steps/step2_domain_models.rb +123 -0
  23. data/lib/k_domain/domain_model/transform_steps/step4_rails_resource_models.rb +3 -3
  24. data/lib/k_domain/domain_model/transform_steps/step5_rails_resource_routes.rb +36 -0
  25. data/lib/k_domain/domain_model/transform_steps/step6_rails_structure_models.rb +91 -0
  26. data/lib/k_domain/domain_model/transform_steps/step7_rails_structure_controllers.rb +114 -0
  27. data/lib/k_domain/domain_model/transform_steps/step8_domain_columns.rb +99 -0
  28. data/lib/k_domain/queries/_.rb +4 -0
  29. data/lib/k_domain/queries/base_query.rb +13 -0
  30. data/lib/k_domain/queries/domain_model_query.rb +88 -0
  31. data/lib/k_domain/rails_code_extractor/_.rb +5 -0
  32. data/lib/k_domain/rails_code_extractor/extract_controller.rb +61 -0
  33. data/lib/k_domain/rails_code_extractor/extract_model.rb +21 -8
  34. data/lib/k_domain/rails_code_extractor/shim_loader.rb +4 -1
  35. data/lib/k_domain/raw_db_schema/load.rb +1 -1
  36. data/lib/k_domain/raw_db_schema/transform.rb +28 -3
  37. data/lib/k_domain/schemas/_.rb +5 -3
  38. data/lib/k_domain/schemas/database.rb +86 -0
  39. data/lib/k_domain/schemas/domain/erd_file.rb +78 -77
  40. data/lib/k_domain/schemas/domain.rb +153 -0
  41. data/lib/k_domain/schemas/{domain/_.rb → domain_types.rb} +1 -8
  42. data/lib/k_domain/schemas/{domain_model.rb → main_dataset.rb} +4 -4
  43. data/lib/k_domain/schemas/rails_resource.rb +43 -6
  44. data/lib/k_domain/schemas/rails_structure.rb +104 -14
  45. data/lib/k_domain/version.rb +1 -1
  46. data/lib/k_domain.rb +7 -3
  47. data/templates/custom/action_controller.rb +7 -0
  48. data/templates/custom/controller_interceptors.rb +80 -0
  49. data/templates/custom/model_interceptors.rb +95 -0
  50. data/templates/load_schema.rb +7 -0
  51. data/templates/rails/action_controller.rb +301 -0
  52. data/templates/{active_record_shims.rb → rails/active_record.rb} +23 -41
  53. data/templates/ruby_code_extractor/attach_class_info.rb +13 -0
  54. data/templates/ruby_code_extractor/behaviour_accessors.rb +39 -0
  55. data/templates/sample_config.rb +47 -0
  56. data/templates/simple/controller_interceptors.rb +2 -0
  57. metadata +36 -21
  58. data/lib/k_domain/domain_model/transform_steps/step2_attach_models.rb +0 -62
  59. data/lib/k_domain/domain_model/transform_steps/step3_attach_columns.rb +0 -137
  60. data/lib/k_domain/domain_model/transform_steps/step5_rails_models.rb +0 -71
  61. data/lib/k_domain/schemas/database/_.rb +0 -7
  62. data/lib/k_domain/schemas/database/foreign_key.rb +0 -14
  63. data/lib/k_domain/schemas/database/index.rb +0 -14
  64. data/lib/k_domain/schemas/database/schema.rb +0 -31
  65. data/lib/k_domain/schemas/database/table.rb +0 -32
  66. data/lib/k_domain/schemas/domain/domain.rb +0 -11
  67. data/lib/k_domain/schemas/domain/models/column.rb +0 -49
  68. data/lib/k_domain/schemas/domain/models/model.rb +0 -111
  69. data/templates/fake_module_shims.rb +0 -42
@@ -0,0 +1,123 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Schema is re-shaped into a format designed for domain modeling
4
+ class Step2DomainModels < KDomain::DomainModel::Step
5
+ # Map database schema to domain model
6
+ def call
7
+ raise 'Rails model path not supplied' if opts[:model_path].nil?
8
+
9
+ domain[:models] = database_tables.map { |table| model(table) }
10
+ end
11
+
12
+ def model(table)
13
+ table_name = table[:name].to_s
14
+ model_name = table_name.singularize
15
+
16
+ model = {
17
+ name: model_name,
18
+ name_plural: table_name, # need to check if this is correct as I know it is wrong for account_history_datum
19
+ table_name: table_name,
20
+ pk: primary_key(table),
21
+ file: nil # will be set in step8_domain_columns
22
+ }
23
+
24
+ attach_columns(model)
25
+ end
26
+
27
+ def primary_key(table)
28
+ {
29
+ name: table[:primary_key],
30
+ type: table[:primary_key_type],
31
+ exist: !table[:primary_key].nil?
32
+ }
33
+ end
34
+
35
+ def attach_columns(model)
36
+ table = find_table_for_model(model)
37
+ columns = columns(table[:columns])
38
+ columns = insert_primary_key(model, columns)
39
+ model[:columns] = columns
40
+ model
41
+ end
42
+
43
+ def columns(db_columns)
44
+ db_columns.map do |db_column|
45
+ column_data(db_column[:name]).merge(
46
+ type: check_type(db_column[:type]),
47
+ precision: db_column[:precision],
48
+ scale: db_column[:scale],
49
+ default: db_column[:default],
50
+ null: db_column[:null],
51
+ limit: db_column[:limit],
52
+ array: db_column[:array]
53
+ )
54
+ end
55
+ end
56
+
57
+ def insert_primary_key(model, columns)
58
+ return columns unless model[:pk][:exist]
59
+
60
+ column = column_data('id').merge(
61
+ type: check_type(model[:pk][:type])
62
+ )
63
+
64
+ columns.unshift(column)
65
+ columns
66
+ end
67
+
68
+ def check_type(type)
69
+ type = type.to_sym if type.is_a?(String)
70
+
71
+ return type if %i[string integer bigint bigserial boolean float decimal datetime date hstore text jsonb].include?(type)
72
+
73
+ if type.nil?
74
+ guard('nil type detected for db_column[:type]')
75
+
76
+ return :string
77
+ end
78
+
79
+ guard("new type detected for db_column[:type] - #{type}")
80
+
81
+ camel.parse(type.to_s).downcase
82
+ end
83
+
84
+ def column_data(name)
85
+ {
86
+ name: name,
87
+ name_plural: name.pluralize,
88
+ type: nil,
89
+ precision: nil,
90
+ scale: nil,
91
+ default: nil,
92
+ null: nil,
93
+ limit: nil,
94
+ array: nil
95
+ }
96
+ end
97
+
98
+ # # Location of source code
99
+ # def location(table_name, model_name)
100
+ # file_normal = File.join(opts[:model_path], "#{model_name}.rb")
101
+ # file_custom = File.join(opts[:model_path], "#{table_name}.rb")
102
+ # file_exist = true
103
+ # state = []
104
+
105
+ # if File.exist?(file_normal)
106
+ # file = file_normal
107
+ # state.push(:has_ruby_model)
108
+ # elsif File.exist?(file_custom)
109
+ # file = file_custom
110
+ # state.push(:has_ruby_model)
111
+ # state.push(:nonconventional_name)
112
+ # else
113
+ # file = ''
114
+ # file_exist = false
115
+ # end
116
+
117
+ # {
118
+ # file: file,
119
+ # exist: file_exist,
120
+ # state: state
121
+ # }
122
+ # end
123
+ end
@@ -5,7 +5,7 @@ class Step4RailsResourceModels < KDomain::DomainModel::Step
5
5
  attr_accessor :ruby_code
6
6
 
7
7
  def call
8
- raise 'ERD path not supplied' if opts[:erd_path].nil?
8
+ raise 'Model path not supplied' if opts[:model_path].nil?
9
9
 
10
10
  self.rails_resource_models = domain_models.map do |model|
11
11
  locate_rails_model(model[:name], model[:table_name])
@@ -15,8 +15,8 @@ class Step4RailsResourceModels < KDomain::DomainModel::Step
15
15
  private
16
16
 
17
17
  def locate_rails_model(model_name, table_name)
18
- file_normal = File.join(opts[:erd_path], "#{model_name}.rb")
19
- file_custom = File.join(opts[:erd_path], "#{table_name}.rb")
18
+ file_normal = File.join(opts[:model_path], "#{model_name}.rb")
19
+ file_custom = File.join(opts[:model_path], "#{table_name}.rb")
20
20
  file_exist = true
21
21
  state = []
22
22
 
@@ -0,0 +1,36 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Locate rails model files
4
+ class Step5RailsResourceRoutes < KDomain::DomainModel::Step
5
+ def call
6
+ return warning('Routes .json file not supplied') if opts[:route_path].nil?
7
+ return warning('Routes .json file not found') unless File.exist?(opts[:route_path])
8
+
9
+ warning('Controller path not supplied. Routes will be loaded but not paired with a controller') if opts[:controller_path].nil?
10
+
11
+ self.rails_resource_routes = load_routes(opts[:route_path], opts[:controller_path])
12
+ end
13
+
14
+ private
15
+
16
+ def load_routes(route_path, controller_path)
17
+ json = File.read(route_path)
18
+ root = JSON.parse(json, symbolize_names: true)
19
+ routes = root[:routes]
20
+
21
+ routes.map { |route| map_route(route, controller_path) }
22
+ end
23
+
24
+ def map_route(route, controller_path)
25
+ return route.merge({ file: '', exist: false }) if controller_path.nil?
26
+
27
+ controller_file = File.join(controller_path, route[:controller_file])
28
+
29
+ route.merge(
30
+ {
31
+ file: controller_file,
32
+ exist: File.exist?(controller_file)
33
+ }
34
+ )
35
+ end
36
+ end
@@ -0,0 +1,91 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Locate rails model files
4
+ class Step6RailsStructureModels < KDomain::DomainModel::Step
5
+ def call
6
+ raise 'Rails model path not supplied' unless opts[:model_path]
7
+
8
+ # THIS IS NOT WORKING YET
9
+ self.rails_structure_models = rails_resource_models.map do |resource|
10
+ process_resource(OpenStruct.new(resource))
11
+ end
12
+
13
+ attach_behavior_and_functions
14
+ end
15
+
16
+ private
17
+
18
+ def process_resource(resource)
19
+ {
20
+ model_name: resource.model_name,
21
+ table_name: resource.table_name,
22
+ file: resource.file,
23
+ exist: resource.exist,
24
+ state: resource.state,
25
+ code: resource.exist ? File.read(resource.file) : '',
26
+ behaviours: {},
27
+ functions: {}
28
+ }
29
+
30
+ # return @model unless resource.exist
31
+
32
+ # @model[:behaviours] = extract_behavior(resource.file)
33
+ # @model[:functions] = extract_functions(resource.file)
34
+
35
+ # @model
36
+ end
37
+
38
+ def attach_behavior_and_functions
39
+ rails_structure_models.select { |model| model[:exist] }.each do |model|
40
+ # puts "model: #{model[:model_name]}"
41
+ model[:behaviours] = extract_behavior(model[:file])
42
+ klass_name = model[:behaviours][:class_name]
43
+ model[:functions] = extract_functions(klass_name)
44
+ end
45
+ end
46
+
47
+ def extract_behavior(file)
48
+ extractor.extract(file)
49
+ extractor.model
50
+ end
51
+
52
+ def extract_functions(klass_name)
53
+ klass = klass_type(klass_name)
54
+
55
+ return {} if klass.nil?
56
+
57
+ Peeky.api.build_class_info(klass.new).to_h
58
+ rescue StandardError => e
59
+ log.exception(e)
60
+ end
61
+
62
+ def klass_type(klass_name)
63
+ Module.const_get(klass_name.classify)
64
+ rescue NameError
65
+ puts ''
66
+ puts klass_name
67
+ puts klass_name.classify
68
+ puts klass_name.pluralize
69
+ puts 'Trying the pluralized version: '
70
+ Module.const_get(klass_name.pluralize)
71
+ end
72
+
73
+ def extractor
74
+ @extractor ||= KDomain::RailsCodeExtractor::ExtractModel.new(shim_loader)
75
+ end
76
+
77
+ def shim_loader
78
+ return opts[:shim_loader] if !opts[:shim_loader].nil? && opts[:shim_loader].is_a?(KDomain::RailsCodeExtractor::ShimLoader)
79
+
80
+ shim_loader = KDomain::RailsCodeExtractor::ShimLoader.new
81
+ # Shims to attach generic class_info writers
82
+ shim_loader.register(:attach_class_info , KDomain::Gem.resource('templates/ruby_code_extractor/attach_class_info.rb'))
83
+ shim_loader.register(:behaviour_accessors , KDomain::Gem.resource('templates/ruby_code_extractor/behaviour_accessors.rb'))
84
+
85
+ # Shims to support standard active_record DSL methods
86
+ shim_loader.register(:active_record , KDomain::Gem.resource('templates/rails/active_record.rb'))
87
+
88
+ # Shims to support application specific [module, class, method] implementations for suppression and exception avoidance
89
+ shim_loader
90
+ end
91
+ end
@@ -0,0 +1,114 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Locate rails controller files
4
+ class Step7RailsStructureControllers < KDomain::DomainModel::Step
5
+ attr_accessor :controllers
6
+
7
+ def call
8
+ raise 'Rails controller path not supplied' unless opts[:controller_path]
9
+
10
+ @controllers = {}
11
+
12
+ rails_resource_routes.map do |route|
13
+ process_route(OpenStruct.new(route))
14
+ end
15
+
16
+ self.rails_structure_controllers = controllers.keys.map { |key| controllers[key] }
17
+
18
+ attach_behavior_and_functions
19
+ end
20
+
21
+ private
22
+
23
+ def process_route(route)
24
+ if new_controller?(route)
25
+ new_controller(route)
26
+ else
27
+ controller = @controllers[route[:controller_file]]
28
+ controller[:actions] << new_action(route)
29
+ end
30
+ end
31
+
32
+ def new_controller?(route)
33
+ !controllers.key?(route[:controller_file])
34
+ end
35
+
36
+ def new_controller(route)
37
+ controllers[route[:controller_file]] = {
38
+ name: route[:controller_name],
39
+ path: route[:controller_path],
40
+ namespace: route[:controller_namespace],
41
+ file: route[:controller_file],
42
+ exist: route[:controller_exist],
43
+ full_file: route[:file],
44
+ behaviours: {},
45
+ functions: {},
46
+ actions: [new_action(route)]
47
+ }
48
+ end
49
+
50
+ def new_action(route)
51
+ {
52
+ route_name: route[:name],
53
+ action: route[:action],
54
+ uri_path: route[:uri_path],
55
+ mime_match: route[:mime_match],
56
+ verbs: route[:verbs]
57
+ }
58
+ end
59
+
60
+ def attach_behavior_and_functions
61
+ rails_structure_controllers.sort_by! { |controller| controller[:full_file] }
62
+
63
+ rails_structure_controllers.select { |controller| controller[:exist] }.each do |controller|
64
+ # puts "controller: #{controller[:full_file]}"
65
+ unless File.exist?(controller[:full_file])
66
+ log.error 'Controller apparently exists but no file found, this means that you need re-run rake routes'
67
+ puts controller[:full_file]
68
+ next
69
+ end
70
+ controller[:behaviours] = extract_behavior(controller[:full_file])
71
+ klass_name = controller[:behaviours][:class_name]
72
+ controller[:functions] = extract_functions(klass_name, controller[:full_file])
73
+ end
74
+ end
75
+
76
+ def extract_behavior(file)
77
+ # puts file
78
+ extractor.extract(file)
79
+ extractor.controller
80
+ end
81
+
82
+ def extractor
83
+ @extractor ||= KDomain::RailsCodeExtractor::ExtractController.new(shim_loader)
84
+ end
85
+
86
+ def shim_loader
87
+ return opts[:shim_loader] if !opts[:shim_loader].nil? && opts[:shim_loader].is_a?(KDomain::RailsCodeExtractor::ShimLoader)
88
+
89
+ shim_loader = KDomain::RailsCodeExtractor::ShimLoader.new
90
+ # Shims to attach generic class_info writers
91
+ shim_loader.register(:attach_class_info , KDomain::Gem.resource('templates/ruby_code_extractor/attach_class_info.rb'))
92
+ shim_loader.register(:behaviour_accessors , KDomain::Gem.resource('templates/ruby_code_extractor/behaviour_accessors.rb'))
93
+
94
+ # Shims to support stand rails controller DSL methods
95
+ shim_loader.register(:action_controller , KDomain::Gem.resource('templates/rails/action_controller.rb'))
96
+
97
+ # Shims to support application specific [module, class, method] implementations for suppression and exception avoidance
98
+ # shim_loader.register(:app_action_controller , KDomain::Gem.resource('templates/advanced/action_controller.rb'))
99
+ # shim_loader.register(:app_controller_interceptors , KDomain::Gem.resource('templates/advanced/controller_interceptors.rb'))
100
+ shim_loader
101
+ end
102
+
103
+ def extract_functions(klass_name, controller_file)
104
+ klass = Module.const_get(klass_name.classify)
105
+
106
+ class_info = Peeky.api.build_class_info(klass.new)
107
+
108
+ class_info.to_h
109
+ rescue StandardError => e
110
+ log.kv 'controller_file', controller_file
111
+ log.exception(e, style: :short)
112
+ {}
113
+ end
114
+ end
@@ -0,0 +1,99 @@
1
+ # frozen_string_literal: true
2
+
3
+ # columns to models
4
+ class Step8DomainColumns < KDomain::DomainModel::Step
5
+ attr_reader :domain_model
6
+ attr_reader :domain_column
7
+ attr_reader :rails_model
8
+ attr_reader :column_name
9
+ attr_reader :column_symbol
10
+
11
+ def call
12
+ @debug = true
13
+ enrich_columns
14
+ end
15
+
16
+ def enrich_columns
17
+ # .select {|m| m[:name] == 'app_user'}
18
+ domain_models.each do |model|
19
+ @domain_model = enrich_model(model)
20
+ # this will be nil if there is no rails model code
21
+
22
+ # log.warn domain_model[:name]
23
+ domain_model[:columns].each do |column|
24
+ @domain_column = column
25
+ @column_name = column[:name]
26
+ @column_symbol = column[:name].to_sym
27
+
28
+ column[:structure_type] = structure_type
29
+ end
30
+ end
31
+ end
32
+
33
+ def enrich_model(model)
34
+ # NOTE: THIS MAY GET MOVED TO DomainModel::Load#enrichment
35
+ @rails_model = find_rails_structure_models(model[:name])
36
+
37
+ model[:file] = @rails_model[:file]
38
+
39
+ log.error "Rails model not found for: #{model[:name]}" unless @rails_model
40
+
41
+ model
42
+ end
43
+
44
+ # Need some configurable data dictionary where by
45
+ # _token can be setup on a project by project basis
46
+ def structure_type
47
+ return :primary_key if domain_model[:pk][:name] == column_name
48
+ return :foreign_key if foreign_relationship?
49
+ return :foreign_type if column_symbol == :context_type || column_symbol == :element_type
50
+
51
+ return :timestamp if column_symbol == :created_at || column_symbol == :updated_at
52
+ return :deleted_at if column_symbol == :deleted_at
53
+ return :encrypted_password if column_symbol == :encrypted_password
54
+ return :token if column_name.ends_with?('_token') || column_name.ends_with?('_token_iv')
55
+
56
+ :data
57
+ end
58
+
59
+ def foreign_relationship?
60
+ return false if rails_model.nil? || rails_model[:behaviours].nil? || rails_model[:behaviours][:belongs_to].nil?
61
+
62
+ column_name = domain_column[:name].to_sym
63
+ rails_model[:behaviours][:belongs_to].any? { |belong| belong[:opts][:foreign_key].to_sym == column_name }
64
+ end
65
+
66
+ # def attach_relationships
67
+ # domain_column[:relationships] = []
68
+ # return if rails_model.nil? || rails_model[:behaviours].nil? || rails_model[:behaviours][:belongs_to].nil?
69
+
70
+ # column_name = domain_column[:name].to_sym
71
+
72
+ # attach_column_relationships(column_name)
73
+ # end
74
+
75
+ # def select_belongs_to(column_name)
76
+ # rails_model[:behaviours][:belongs_to].select { |belong| belong[:opts][:foreign_key].to_sym == column_name.to_sym }
77
+ # end
78
+
79
+ # # this just maps basic relationship information,
80
+ # # to go deeper you really need to use the Rails Model Behaviours
81
+ # def attach_column_relationships(column_name)
82
+ # select_belongs_to(column_name).each do |belongs_to|
83
+ # domain_column[:relationships] << map_belongs_to(belongs_to)
84
+ # end
85
+ # end
86
+
87
+ # def map_belongs_to(belongs_to)
88
+ # @domain_column[:structure_type] = :foreign_key
89
+ # # result = {
90
+ # # type: :belongs_to,
91
+ # # name: belongs_to[:name],
92
+ # # foreign_key: belongs_to.dig(:opts, :foreign_key) || @domain_column[:name],
93
+ # # }
94
+
95
+ # # result[:primary_key] = belongs_to.dig(:opts, :primary_key) if belongs_to.dig(:opts, :primary_key)
96
+ # # result[:class_name] = belongs_to.dig(:opts, :class_name) if belongs_to.dig(:opts, :class_name)
97
+ # result
98
+ # end
99
+ end
@@ -0,0 +1,4 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative './base_query'
4
+ require_relative './domain_model_query'
@@ -0,0 +1,13 @@
1
+ # frozen_string_literal: true
2
+
3
+ module KDomain
4
+ module Queries
5
+ class BaseQuery
6
+ attr_reader :domain_model
7
+
8
+ def initialize(domain_model)
9
+ @domain_model = domain_model
10
+ end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,88 @@
1
+ # frozen_string_literal: true
2
+
3
+ module KDomain
4
+ module Queries
5
+ # Query models found on the domain model
6
+ # by
7
+ # has ruby file
8
+ # has primary key
9
+ # column count
10
+ # data column count
11
+ # foreign key column count
12
+ # has timestamp (created_at, updated_at) columns
13
+ # has created_at
14
+ # has updated_at
15
+ # has deleted_at
16
+ # has polymorphic foreign keys
17
+ # has virtual columns
18
+ # virtual column filters (token, encrypted_password, etc)
19
+ class DomainModelQuery < BaseQuery
20
+ def all
21
+ domain_model.domain.models
22
+ end
23
+
24
+ def query(**filters)
25
+ @query = all.clone
26
+
27
+ filters.each do |key, value|
28
+ send("filter_#{key}", value)
29
+ end
30
+
31
+ @query
32
+ end
33
+
34
+ private
35
+
36
+ def filter_ruby(value)
37
+ @query.select! { |model| model.ruby? == value }
38
+ end
39
+
40
+ def filter_pk(value)
41
+ @query.select! { |model| model.pk.exist? == value }
42
+ end
43
+
44
+ def filter_column_count(block)
45
+ @query.select! { |model| block.call(model.columns.count) }
46
+ end
47
+
48
+ def filter_data_column_count(block)
49
+ @query.select! { |model| block.call(model.columns_data.count) }
50
+ end
51
+
52
+ def filter_foreign_key_column_count(block)
53
+ @query.select! { |model| block.call(model.columns_foreign_key.count) }
54
+ end
55
+ alias filter_fk_column_count filter_foreign_key_column_count
56
+ alias filter_fk_count filter_foreign_key_column_count
57
+
58
+ def filter_polymorphic_foreign_key_column_count(block)
59
+ @query.select! { |model| block.call(model.columns_foreign_type.count) }
60
+ end
61
+ alias filter_poly_fk_column_count filter_polymorphic_foreign_key_column_count
62
+ alias filter_poly_fk_count filter_polymorphic_foreign_key_column_count
63
+
64
+ def filter_timestamp(value)
65
+ @query.select! { |model| (model.columns_timestamp.count == 2) == value }
66
+ end
67
+
68
+ def filter_created_at(value)
69
+ @query.select! { |model| (model.columns.any? { |column| column.name == 'created_at' } == value) }
70
+ end
71
+
72
+ def filter_updated_at(value)
73
+ @query.select! { |model| (model.columns.any? { |column| column.name == 'updated_at' } == value) }
74
+ end
75
+
76
+ def filter_deleted_at(value)
77
+ @query.select! { |model| (model.columns.any? { |column| column.name == 'deleted_at' } == value) }
78
+ end
79
+ # has virtual columns
80
+ # virtual column filters (token, encrypted_password, etc)
81
+
82
+ # # HELP: this filter affects table rows, eg. if a table has less the 2 columns then include the table
83
+ # low_column: -> (model) { model.columns.length < 5 },
84
+ # suspected_m2m: -> (model) { model.columns_foreign.length == 2 && model.columns_data.length < 3 },
85
+ # invalid_types: -> (model) { model.columns.any? { |c| [c.db_type, c.csharp_type, c.ruby_type].include?('******') } },
86
+ end
87
+ end
88
+ end
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative './shim_loader'
4
+ require_relative './extract_model'
5
+ require_relative './extract_controller'
@@ -0,0 +1,61 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Takes a Rails controller and extracts DSL behaviours and custom functions (instance, class and private methods)
4
+ module KDomain
5
+ module RailsCodeExtractor
6
+ class ExtractController
7
+ include KLog::Logging
8
+
9
+ attr_reader :shims_loaded
10
+ attr_reader :controller
11
+
12
+ def initialize(load_shim)
13
+ @load_shim = load_shim
14
+ @shims_loaded = false
15
+ end
16
+
17
+ def extract(file)
18
+ load_shims unless shims_loaded
19
+
20
+ ActionController.class_info = nil
21
+ # KDomain::RailsCodeExtractor.reset
22
+
23
+ load_retry(file, 10, nil)
24
+ rescue StandardError => e
25
+ log.exception(e)
26
+ end
27
+
28
+ private
29
+
30
+ def load_shims
31
+ @load_shim.call
32
+ @shims_loaded = true
33
+ end
34
+
35
+ # rubocop:disable Security/Eval,Style/EvalWithLocation,Style/DocumentDynamicEvalDefinition,Metrics/AbcSize
36
+ def load_retry(file, times, last_error)
37
+ return if times.negative?
38
+
39
+ # puts file
40
+ load(file)
41
+
42
+ @controller = ActionController::Base.class_info
43
+ # @controller = KDomain::RailsCodeExtractor.class_info
44
+
45
+ # get_method_info(File.base_name(file))
46
+ rescue StandardError => e
47
+ log.kv 'times', times
48
+ # puts e.message
49
+ if e.is_a?(NameError) && e.message != last_error&.message
50
+ log.kv('add module', e.name)
51
+ log.kv 'file', file
52
+ eval("module #{e.name}; end")
53
+ return load_retry(file, times - 1, e)
54
+ end
55
+ log.kv 'file', file
56
+ log.exception(e, style: :short, method_info: method(__callee__))
57
+ end
58
+ # rubocop:enable Security/Eval,Style/EvalWithLocation,Style/DocumentDynamicEvalDefinition,Metrics/AbcSize
59
+ end
60
+ end
61
+ end