k_domain 0.0.16 → 0.0.26
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.
- checksums.yaml +4 -4
- data/.builders/boot.rb +40 -0
- data/.builders/generators/configuration_generator.rb +22 -0
- data/.builders/run.rb +16 -0
- data/.gitignore +1 -0
- data/.rubocop.yml +1 -2
- data/Guardfile +1 -0
- data/README.md +15 -0
- data/STORIES.md +35 -6
- data/lib/k_domain/config/_.rb +4 -0
- data/lib/k_domain/config/config.rb +19 -0
- data/lib/k_domain/config/configuration.rb +76 -0
- data/lib/k_domain/domain_model/load.rb +41 -0
- data/lib/k_domain/domain_model/transform.rb +27 -55
- data/lib/k_domain/domain_model/transform_steps/_.rb +8 -7
- data/lib/k_domain/domain_model/transform_steps/step.rb +27 -1
- data/lib/k_domain/domain_model/transform_steps/{step1_attach_db_schema.rb → step1_db_schema.rb} +2 -1
- data/lib/k_domain/domain_model/transform_steps/{step5_attach_dictionary.rb → step20_dictionary.rb} +7 -4
- data/lib/k_domain/domain_model/transform_steps/step2_domain_models.rb +123 -0
- data/lib/k_domain/domain_model/transform_steps/{step8_rails_resource_models.rb → step4_rails_resource_models.rb} +4 -4
- data/lib/k_domain/domain_model/transform_steps/step5_rails_resource_routes.rb +36 -0
- data/lib/k_domain/domain_model/transform_steps/step6_rails_structure_models.rb +90 -0
- data/lib/k_domain/domain_model/transform_steps/step7_rails_structure_controllers.rb +111 -0
- data/lib/k_domain/domain_model/transform_steps/step8_domain_columns.rb +99 -0
- data/lib/k_domain/rails_code_extractor/_.rb +5 -0
- data/lib/k_domain/rails_code_extractor/extract_controller.rb +61 -0
- data/lib/k_domain/rails_code_extractor/extract_model.rb +56 -6
- data/lib/k_domain/rails_code_extractor/{load_shim.rb → shim_loader.rb} +3 -5
- data/lib/k_domain/raw_db_schema/load.rb +1 -1
- data/lib/k_domain/raw_db_schema/transform.rb +28 -3
- data/lib/k_domain/schemas/_.rb +6 -3
- data/lib/k_domain/schemas/database.rb +86 -0
- data/lib/k_domain/schemas/domain/erd_file.rb +78 -77
- data/lib/k_domain/schemas/domain.rb +149 -0
- data/lib/k_domain/schemas/domain_model.rb +6 -5
- data/lib/k_domain/schemas/{domain/_.rb → domain_types.rb} +1 -8
- data/lib/k_domain/schemas/rails_resource.rb +43 -6
- data/lib/k_domain/schemas/rails_structure.rb +182 -0
- data/lib/k_domain/version.rb +1 -1
- data/lib/k_domain.rb +4 -2
- data/templates/custom/action_controller.rb +36 -0
- data/templates/custom/controller_interceptors.rb +78 -0
- data/templates/custom/model_interceptors.rb +71 -0
- data/templates/load_schema.rb +7 -0
- data/templates/rails/action_controller.rb +301 -0
- data/templates/{active_record_shims.rb → rails/active_record.rb} +42 -49
- data/templates/ruby_code_extractor/attach_class_info.rb +13 -0
- data/templates/ruby_code_extractor/behaviour_accessors.rb +39 -0
- data/templates/sample_config.rb +47 -0
- data/templates/simple/controller_interceptors.rb +2 -0
- metadata +33 -22
- data/lib/k_domain/domain_model/transform_steps/step2_attach_models.rb +0 -62
- data/lib/k_domain/domain_model/transform_steps/step3_attach_columns.rb +0 -137
- data/lib/k_domain/domain_model/transform_steps/step4_attach_erd_files.rb +0 -457
- data/lib/k_domain/domain_model/transform_steps/step9_rails_structure_models.rb +0 -33
- data/lib/k_domain/schemas/database/_.rb +0 -7
- data/lib/k_domain/schemas/database/foreign_key.rb +0 -14
- data/lib/k_domain/schemas/database/index.rb +0 -14
- data/lib/k_domain/schemas/database/schema.rb +0 -31
- data/lib/k_domain/schemas/database/table.rb +0 -32
- data/lib/k_domain/schemas/domain/domain.rb +0 -11
- data/lib/k_domain/schemas/domain/models/column.rb +0 -49
- data/lib/k_domain/schemas/domain/models/model.rb +0 -111
- 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
         | 
| @@ -1,11 +1,11 @@ | |
| 1 1 | 
             
            # frozen_string_literal: true
         | 
| 2 2 |  | 
| 3 3 | 
             
            # Locate rails model files
         | 
| 4 | 
            -
            class  | 
| 4 | 
            +
            class Step4RailsResourceModels < KDomain::DomainModel::Step
         | 
| 5 5 | 
             
              attr_accessor :ruby_code
         | 
| 6 6 |  | 
| 7 7 | 
             
              def call
         | 
| 8 | 
            -
                raise ' | 
| 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 Step8RailsResourceModels < KDomain::DomainModel::Step | |
| 15 15 | 
             
              private
         | 
| 16 16 |  | 
| 17 17 | 
             
              def locate_rails_model(model_name, table_name)
         | 
| 18 | 
            -
                file_normal = File.join(opts[: | 
| 19 | 
            -
                file_custom = File.join(opts[: | 
| 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,90 @@ | |
| 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 | 
            +
                  model[:behaviours] = extract_behavior(model[:file])
         | 
| 41 | 
            +
                  klass_name = model[:behaviours][:class_name]
         | 
| 42 | 
            +
                  model[:functions] = extract_functions(klass_name)
         | 
| 43 | 
            +
                end
         | 
| 44 | 
            +
              end
         | 
| 45 | 
            +
             | 
| 46 | 
            +
              def extract_behavior(file)
         | 
| 47 | 
            +
                extractor.extract(file)
         | 
| 48 | 
            +
                extractor.model
         | 
| 49 | 
            +
              end
         | 
| 50 | 
            +
             | 
| 51 | 
            +
              def extract_functions(klass_name)
         | 
| 52 | 
            +
                klass = klass_type(klass_name)
         | 
| 53 | 
            +
             | 
| 54 | 
            +
                return {} if klass.nil?
         | 
| 55 | 
            +
             | 
| 56 | 
            +
                Peeky.api.build_class_info(klass.new).to_h
         | 
| 57 | 
            +
              rescue StandardError => e
         | 
| 58 | 
            +
                log.exception(e)
         | 
| 59 | 
            +
              end
         | 
| 60 | 
            +
             | 
| 61 | 
            +
              def klass_type(klass_name)
         | 
| 62 | 
            +
                Module.const_get(klass_name.classify)
         | 
| 63 | 
            +
              rescue NameError
         | 
| 64 | 
            +
                puts ''
         | 
| 65 | 
            +
                puts klass_name
         | 
| 66 | 
            +
                puts klass_name.classify
         | 
| 67 | 
            +
                puts klass_name.pluralize
         | 
| 68 | 
            +
                puts 'Trying the pluralized version: '
         | 
| 69 | 
            +
                Module.const_get(klass_name.pluralize)
         | 
| 70 | 
            +
              end
         | 
| 71 | 
            +
             | 
| 72 | 
            +
              def extractor
         | 
| 73 | 
            +
                @extractor ||= KDomain::RailsCodeExtractor::ExtractModel.new(shim_loader)
         | 
| 74 | 
            +
              end
         | 
| 75 | 
            +
             | 
| 76 | 
            +
              def shim_loader
         | 
| 77 | 
            +
                return opts[:shim_loader] if !opts[:shim_loader].nil? && opts[:shim_loader].is_a?(KDomain::RailsCodeExtractor::ShimLoader)
         | 
| 78 | 
            +
             | 
| 79 | 
            +
                shim_loader = KDomain::RailsCodeExtractor::ShimLoader.new
         | 
| 80 | 
            +
                # Shims to attach generic class_info writers
         | 
| 81 | 
            +
                shim_loader.register(:attach_class_info           , KDomain::Gem.resource('templates/ruby_code_extractor/attach_class_info.rb'))
         | 
| 82 | 
            +
                shim_loader.register(:behaviour_accessors         , KDomain::Gem.resource('templates/ruby_code_extractor/behaviour_accessors.rb'))
         | 
| 83 | 
            +
             | 
| 84 | 
            +
                # Shims to support standard active_record DSL methods
         | 
| 85 | 
            +
                shim_loader.register(:active_record               , KDomain::Gem.resource('templates/rails/active_record.rb'))
         | 
| 86 | 
            +
             | 
| 87 | 
            +
                # Shims to support application specific [module, class, method] implementations for suppression and exception avoidance
         | 
| 88 | 
            +
                shim_loader
         | 
| 89 | 
            +
              end
         | 
| 90 | 
            +
            end
         | 
| @@ -0,0 +1,111 @@ | |
| 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.select { |controller| controller[:exist] }.each do |controller|
         | 
| 62 | 
            +
                  unless File.exist?(controller[:full_file])
         | 
| 63 | 
            +
                    log.error 'Controller apparently exists but no file found, this means that you need re-run rake routes'
         | 
| 64 | 
            +
                    puts controller[:full_file]
         | 
| 65 | 
            +
                    next
         | 
| 66 | 
            +
                  end
         | 
| 67 | 
            +
                  controller[:behaviours] = extract_behavior(controller[:full_file])
         | 
| 68 | 
            +
                  klass_name = controller[:behaviours][:class_name]
         | 
| 69 | 
            +
                  controller[:functions] = extract_functions(klass_name, controller[:full_file])
         | 
| 70 | 
            +
                end
         | 
| 71 | 
            +
              end
         | 
| 72 | 
            +
             | 
| 73 | 
            +
              def extract_behavior(file)
         | 
| 74 | 
            +
                # puts file
         | 
| 75 | 
            +
                extractor.extract(file)
         | 
| 76 | 
            +
                extractor.controller
         | 
| 77 | 
            +
              end
         | 
| 78 | 
            +
             | 
| 79 | 
            +
              def extractor
         | 
| 80 | 
            +
                @extractor ||= KDomain::RailsCodeExtractor::ExtractController.new(shim_loader)
         | 
| 81 | 
            +
              end
         | 
| 82 | 
            +
             | 
| 83 | 
            +
              def shim_loader
         | 
| 84 | 
            +
                return opts[:shim_loader] if !opts[:shim_loader].nil? && opts[:shim_loader].is_a?(KDomain::RailsCodeExtractor::ShimLoader)
         | 
| 85 | 
            +
             | 
| 86 | 
            +
                shim_loader = KDomain::RailsCodeExtractor::ShimLoader.new
         | 
| 87 | 
            +
                # Shims to attach generic class_info writers
         | 
| 88 | 
            +
                shim_loader.register(:attach_class_info           , KDomain::Gem.resource('templates/ruby_code_extractor/attach_class_info.rb'))
         | 
| 89 | 
            +
                shim_loader.register(:behaviour_accessors         , KDomain::Gem.resource('templates/ruby_code_extractor/behaviour_accessors.rb'))
         | 
| 90 | 
            +
             | 
| 91 | 
            +
                # Shims to support stand rails controller DSL methods
         | 
| 92 | 
            +
                shim_loader.register(:action_controller           , KDomain::Gem.resource('templates/rails/action_controller.rb'))
         | 
| 93 | 
            +
             | 
| 94 | 
            +
                # Shims to support application specific [module, class, method] implementations for suppression and exception avoidance
         | 
| 95 | 
            +
                # shim_loader.register(:app_action_controller       , KDomain::Gem.resource('templates/advanced/action_controller.rb'))
         | 
| 96 | 
            +
                # shim_loader.register(:app_controller_interceptors , KDomain::Gem.resource('templates/advanced/controller_interceptors.rb'))
         | 
| 97 | 
            +
                shim_loader
         | 
| 98 | 
            +
              end
         | 
| 99 | 
            +
             | 
| 100 | 
            +
              def extract_functions(klass_name, controller_file)
         | 
| 101 | 
            +
                klass = Module.const_get(klass_name.classify)
         | 
| 102 | 
            +
             | 
| 103 | 
            +
                class_info = Peeky.api.build_class_info(klass.new)
         | 
| 104 | 
            +
             | 
| 105 | 
            +
                class_info.to_h
         | 
| 106 | 
            +
              rescue StandardError => e
         | 
| 107 | 
            +
                log.kv 'controller_file', controller_file
         | 
| 108 | 
            +
                log.exception(e, style: :short)
         | 
| 109 | 
            +
                {}
         | 
| 110 | 
            +
              end
         | 
| 111 | 
            +
            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,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
         | 
| @@ -6,16 +6,66 @@ module KDomain | |
| 6 6 | 
             
                class ExtractModel
         | 
| 7 7 | 
             
                  include KLog::Logging
         | 
| 8 8 |  | 
| 9 | 
            -
                  attr_reader : | 
| 10 | 
            -
                  attr_reader : | 
| 9 | 
            +
                  attr_reader :shims_loaded
         | 
| 10 | 
            +
                  attr_reader :models
         | 
| 11 | 
            +
                  attr_reader :model
         | 
| 11 12 |  | 
| 12 | 
            -
                  def initialize( | 
| 13 | 
            -
                    @ | 
| 13 | 
            +
                  def initialize(load_shim)
         | 
| 14 | 
            +
                    @load_shim = load_shim
         | 
| 15 | 
            +
                    @shims_loaded = false
         | 
| 16 | 
            +
                    @models = []
         | 
| 14 17 | 
             
                  end
         | 
| 15 18 |  | 
| 16 | 
            -
                  def  | 
| 17 | 
            -
                     | 
| 19 | 
            +
                  def extract(file)
         | 
| 20 | 
            +
                    load_shims unless shims_loaded
         | 
| 21 | 
            +
             | 
| 22 | 
            +
                    ActiveRecord.class_info = nil
         | 
| 23 | 
            +
             | 
| 24 | 
            +
                    load_retry(file, 10, nil)
         | 
| 25 | 
            +
                  rescue StandardError => e
         | 
| 26 | 
            +
                    log.exception(e)
         | 
| 27 | 
            +
                  end
         | 
| 28 | 
            +
             | 
| 29 | 
            +
                  private
         | 
| 30 | 
            +
             | 
| 31 | 
            +
                  def load_shims
         | 
| 32 | 
            +
                    @load_shim.call
         | 
| 33 | 
            +
                    @shims_loaded = true
         | 
| 34 | 
            +
                  end
         | 
| 35 | 
            +
             | 
| 36 | 
            +
                  # rubocop:disable Security/Eval,Style/EvalWithLocation,Style/DocumentDynamicEvalDefinition,Metrics/AbcSize
         | 
| 37 | 
            +
                  def load_retry(file, times, last_error)
         | 
| 38 | 
            +
                    return if times.negative?
         | 
| 39 | 
            +
             | 
| 40 | 
            +
                    load(file)
         | 
| 41 | 
            +
             | 
| 42 | 
            +
                    @model = ActiveRecord.class_info
         | 
| 43 | 
            +
             | 
| 44 | 
            +
                    if @model.nil?
         | 
| 45 | 
            +
                      # puts ''
         | 
| 46 | 
            +
                      # puts file
         | 
| 47 | 
            +
                      # puts 'class probably has no DSL methods'
         | 
| 48 | 
            +
                      @model = {
         | 
| 49 | 
            +
                        class_name: File.basename(file, File.extname(file)).classify
         | 
| 50 | 
            +
                      }
         | 
| 51 | 
            +
                    end
         | 
| 52 | 
            +
             | 
| 53 | 
            +
                    @models << @model
         | 
| 54 | 
            +
             | 
| 55 | 
            +
                    # get_method_info(File.base_name(file))
         | 
| 56 | 
            +
                  rescue StandardError => e
         | 
| 57 | 
            +
                    log.kv 'times', times
         | 
| 58 | 
            +
                    # puts e.message
         | 
| 59 | 
            +
                    if e.is_a?(NameError) && e.message != last_error&.message
         | 
| 60 | 
            +
                      log.kv('add module', e.name)
         | 
| 61 | 
            +
                      log.kv 'file', file
         | 
| 62 | 
            +
                      eval("module #{e.name}; end")
         | 
| 63 | 
            +
                      return load_retry(file, times - 1, e)
         | 
| 64 | 
            +
                    end
         | 
| 65 | 
            +
                    log.kv 'file', file
         | 
| 66 | 
            +
                    log.exception(e, style: :short, method_info: method(__callee__))
         | 
| 18 67 | 
             
                  end
         | 
| 68 | 
            +
                  # rubocop:enable Security/Eval,Style/EvalWithLocation,Style/DocumentDynamicEvalDefinition,Metrics/AbcSize
         | 
| 19 69 | 
             
                end
         | 
| 20 70 | 
             
              end
         | 
| 21 71 | 
             
            end
         | 
| @@ -8,20 +8,18 @@ | |
| 8 8 | 
             
            # 2. Inject fake module/classes that would otherwise break code loading with various exceptions
         | 
| 9 9 | 
             
            module KDomain
         | 
| 10 10 | 
             
              module RailsCodeExtractor
         | 
| 11 | 
            -
                class  | 
| 11 | 
            +
                class ShimLoader
         | 
| 12 12 | 
             
                  include KLog::Logging
         | 
| 13 13 |  | 
| 14 14 | 
             
                  attr_reader :shim_files
         | 
| 15 15 |  | 
| 16 | 
            -
                  attr_reader :dsl_shim_file
         | 
| 17 | 
            -
                  attr_reader :fake_module_file
         | 
| 18 | 
            -
             | 
| 19 16 | 
             
                  def initialize
         | 
| 20 17 | 
             
                    @shim_files = []
         | 
| 21 18 | 
             
                  end
         | 
| 22 19 |  | 
| 23 20 | 
             
                  def call
         | 
| 24 | 
            -
                     | 
| 21 | 
            +
                    # puts shim_files.map { |sf| sf[:file] }
         | 
| 22 | 
            +
                    shim_files.select { |sf| sf[:exist] }.each { |sf| require sf[:file] }
         | 
| 25 23 | 
             
                  end
         | 
| 26 24 |  | 
| 27 25 | 
             
                  def register(name, file)
         |