k_domain 0.0.16 → 0.0.20

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 98e1d3eb1435f17ad91084519399234f0adc41e399d1350071147f0e58f694c6
4
- data.tar.gz: 2797a698e87169816a6b612239b1c22b8d2fd0db2fd6df2fc8e078fbf8828e69
3
+ metadata.gz: de5fc8f220781026dd327f40da53eb3a424ec23f38785be8fddc8f1856970dc1
4
+ data.tar.gz: 5573e60dc8665ab146e0edeb291ebcd3db6b792180e05e24721df9d519038f86
5
5
  SHA512:
6
- metadata.gz: 1e6cd0d3fa8354f83b5910dc961212acd0ae149b0b90c31338bf992b4f64500ae4fb05f63c605bf439d818bed8f6f4806939994c0cf8ee6fbd0c0a51d232b02f
7
- data.tar.gz: 28d42c273b6fdae5db9b32e4cfcd4c1826e42f929efbc9713941b16bc4240cff22eed7e3260aec195beff43ae3ec20bf136bb6526988eccbd5f475c18ec2566a
6
+ metadata.gz: 4ecaa0fef7e6d622f18d2dfe53bdddf2c17fa91e1c2796ee7bdddf5044880f742003d8cb6f7bb4007eeab4bd2f0becba6f8f0fb329f58776f807cd200bd4ced9
7
+ data.tar.gz: 76af19179f670d7e4b18121f031b77e18c366259526530b6623f988c5b3e2985ca3a993441e6caacfb7cb71da6afb05c99803234aa85045facdb2c8f09b1194b
data/STORIES.md CHANGED
@@ -16,25 +16,54 @@ As a Developer, I can customize domain configuration, so that I can have opinion
16
16
 
17
17
  - Handle traits
18
18
 
19
+ ### Tasks next on list
20
+
21
+ BUGs in domain_model load
22
+
23
+ - spec/k_domain/domain_model/load_spec.rb:135
24
+ - FIXED: module_name should be empty
25
+ - FIXED: name mismatch (attr_accessor, attr_reader, attr_writer) in behaviours vs in (attr_accessors, attr_readers, attr_writers) functions
26
+ - functions - (attr_accessors, attr_readers, attr_writers) are all empty
27
+
28
+ External ruby process - ShimLoading and ExtractModel
29
+
30
+ - You need a clear memory foot print for shim loading and model extraction, best to run these from inside a new ruby process
31
+
32
+ Log Warning to Investigate Issues
33
+
34
+ - FIXED: All the logged warnings in k_domain need to turn up in on the investigate issues register
35
+ - Investigate needs a debug flag that when turned on, will write the issues to the console
36
+
37
+ Print progress dot
38
+
39
+ - Make this configurable
40
+ - Decide what steps this should run for
41
+ - Show step label via configuration
42
+
43
+ ## Stories and tasks
44
+
45
+ ### Stories - completed
46
+
19
47
  As a Developer, I can read native rails model data, so that I can leverage existing rails applications for ERD modeling
20
48
 
49
+ - Proof of concept
21
50
  - Use Meta Programming and re-implement ActiveRecord::Base
22
51
 
23
- ### Tasks next on list
52
+ ### Tasks - completed
24
53
 
25
54
  Refactor / Simply
26
55
 
27
- - Replace complex objects an with structs for ancillary data structures such as investigate
56
+ - Replace complex objects with structs for ancillary data structures such as investigate
57
+
58
+ Steps to support write methods on base class
59
+
60
+ - Simplify lib/k_domain/domain_model/transform.rb
28
61
 
29
62
  User acceptance testing
30
63
 
31
64
  - Provide sample printers for each data structure to visually check data is loading
32
65
  - Point raw_db_schema loader towards a complex ERD and check how it performs
33
66
 
34
- ## Stories and tasks
35
-
36
- ### Tasks - completed
37
-
38
67
  Setup RubyGems and RubyDoc
39
68
 
40
69
  - Build and deploy gem to [rubygems.org](https://rubygems.org/gems/k_domain)
@@ -23,13 +23,12 @@ module KDomain
23
23
  # rubocop:disable Metrics/AbcSize,Metrics/CyclomaticComplexity,Metrics/PerceivedComplexity
24
24
  def call
25
25
  valid = true
26
- valid &&= step1
27
- valid &&= step2
28
- valid &&= step3
29
- valid &&= step4
30
- valid &&= step5
31
- valid &&= step8
32
- valid &&= step9
26
+ valid &&= Step1AttachDbSchema.run(domain_data, db_schema: db_schema, step_file: step_file('1-attach-db-schema'))
27
+ valid &&= Step2AttachModels.run(domain_data, erd_path: erd_path, step_file: step_file('2-attach-model'))
28
+ valid &&= Step3AttachColumns.run(domain_data, step_file: step_file('3-attach-columns'))
29
+ valid &&= Step4RailsResourceModels.run(domain_data, erd_path: erd_path, step_file: step_file('4-rails-resource-models'))
30
+ valid &&= Step5RailsModels.run(domain_data, erd_path: erd_path, step_file: step_file('5-rails-models'))
31
+ valid &&= Step6AttachDictionary.run(domain_data, erd_path: erd_path, step_file: step_file('6-attach-dictionary'))
33
32
 
34
33
  raise 'DomainModal transform failed' unless valid
35
34
 
@@ -39,49 +38,13 @@ module KDomain
39
38
  end
40
39
  # rubocop:enable Metrics/AbcSize,Metrics/CyclomaticComplexity,Metrics/PerceivedComplexity
41
40
 
42
- def step1
43
- Step1AttachDbSchema.run(domain_data, db_schema: db_schema)
44
- write(step: '1-attach-db-schema')
41
+ def step_file(step_name)
42
+ target_step_file % { step: step_name }
45
43
  end
46
44
 
47
- def step2
48
- Step2AttachModels.run(domain_data, erd_path: erd_path)
49
- write(step: '2-attach-model')
50
- end
51
-
52
- def step3
53
- Step3AttachColumns.run(domain_data)
54
- write(step: '3-attach-columns')
55
- end
56
-
57
- def step4
58
- Step4AttachErdFiles.run(domain_data, erd_path: erd_path)
59
- write(step: '4-attach-erd-files')
60
- end
61
-
62
- def step5
63
- Step5AttachDictionary.run(domain_data, erd_path: erd_path)
64
- write(step: '5-attach-dictionary')
65
- end
66
-
67
- def step8
68
- Step8RailsResourceModels.run(domain_data, erd_path: erd_path)
69
- write(step: '8-rails-resource-models')
70
- end
71
-
72
- def step9
73
- Step9RailsStructureModels.run(domain_data, erd_path: erd_path)
74
- write(step: '8-rails-structure-models')
75
- end
76
-
77
- def write(step: nil)
78
- file = if step.nil?
79
- target_file
80
- else
81
- format(target_step_file, step: step)
82
- end
83
- FileUtils.mkdir_p(File.dirname(file))
84
- File.write(file, JSON.pretty_generate(domain_data))
45
+ def write
46
+ FileUtils.mkdir_p(File.dirname(target_file))
47
+ File.write(target_file, JSON.pretty_generate(domain_data))
85
48
  end
86
49
 
87
50
  # rubocop:disable Metrics/MethodLength
@@ -90,7 +53,6 @@ module KDomain
90
53
  @domain_data ||= {
91
54
  domain: {
92
55
  models: [],
93
- erd_files: []
94
56
  },
95
57
  rails_resource: {
96
58
  models: [],
@@ -5,7 +5,6 @@ require_relative './step'
5
5
  require_relative './step1_attach_db_schema'
6
6
  require_relative './step2_attach_models'
7
7
  require_relative './step3_attach_columns'
8
- require_relative './step4_attach_erd_files'
9
- require_relative './step5_attach_dictionary'
10
- require_relative './step8_rails_resource_models'
11
- require_relative './step9_rails_structure_models'
8
+ require_relative './step4_rails_resource_models'
9
+ require_relative './step5_rails_models'
10
+ require_relative './step6_attach_dictionary'
@@ -24,7 +24,8 @@ module KDomain
24
24
  def self.run(domain_data, **opts)
25
25
  step = new(domain_data, **opts)
26
26
  step.call
27
- step
27
+ step.write(opts[:step_file])
28
+ step.valid?
28
29
  end
29
30
 
30
31
  def guard(message)
@@ -32,6 +33,11 @@ module KDomain
32
33
  @valid = false
33
34
  end
34
35
 
36
+ def write(file)
37
+ FileUtils.mkdir_p(File.dirname(file))
38
+ File.write(file, JSON.pretty_generate(domain_data))
39
+ end
40
+
35
41
  # Domain Model Accessor/Helpers
36
42
  def domain
37
43
  guard('domain is missing') if domain_data[:domain].nil?
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  # Locate rails model files
4
- class Step8RailsResourceModels < KDomain::DomainModel::Step
4
+ class Step4RailsResourceModels < KDomain::DomainModel::Step
5
5
  attr_accessor :ruby_code
6
6
 
7
7
  def call
@@ -0,0 +1,71 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Locate rails model files
4
+ class Step5RailsModels < KDomain::DomainModel::Step
5
+ def call
6
+ raise 'ERD path not supplied' unless opts[:erd_path]
7
+
8
+ self.rails_structure_models = rails_resource_models.map do |resource|
9
+ process_resource(OpenStruct.new(resource))
10
+ end
11
+ end
12
+
13
+ private
14
+
15
+ def process_resource(resource)
16
+ @model = {
17
+ model_name: resource.model_name,
18
+ table_name: resource.table_name,
19
+ file: resource.file,
20
+ exist: resource.exist,
21
+ state: resource.state,
22
+ code: resource.exist ? File.read(resource.file) : '',
23
+ behaviours: {},
24
+ functions: {}
25
+ }
26
+
27
+ return @model unless resource.exist
28
+
29
+ @model[:behaviours] = extract_model_behavior(resource.file)
30
+ @model[:functions] = extract_model_functions(resource.file)
31
+
32
+ @model
33
+ end
34
+
35
+ def extract_model_behavior(file)
36
+ extractor.extract(file)
37
+ extractor.model
38
+ end
39
+
40
+ def extract_model_functions(file)
41
+ klass_name = File.basename(file, File.extname(file))
42
+
43
+ klass = case klass_name
44
+ when 'clearbit_quota'
45
+ ClearbitQuota
46
+ when 'account_history_data'
47
+ AccountHistoryData
48
+ else
49
+ Module.const_get(klass_name.classify)
50
+ end
51
+
52
+ class_info = Peeky.api.build_class_info(klass.new)
53
+
54
+ class_info.to_h
55
+ rescue StandardError => e
56
+ log.exception(e)
57
+ end
58
+
59
+ def extractor
60
+ @extractor ||= KDomain::RailsCodeExtractor::ExtractModel.new(shim_loader)
61
+ end
62
+
63
+ def shim_loader
64
+ return opts[:shim_loader] if !opts[:shim_loader].nil? && opts[:shim_loader].is_a?(KDomain::RailsCodeExtractor::ShimLoader)
65
+
66
+ shim_loader = KDomain::RailsCodeExtractor::ShimLoader.new
67
+ shim_loader.register(:fake_module , KDomain::Gem.resource('templates/fake_module_shims.rb'))
68
+ shim_loader.register(:active_record, KDomain::Gem.resource('templates/active_record_shims.rb'))
69
+ shim_loader
70
+ end
71
+ end
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  # Attach data dictionary
4
- class Step5AttachDictionary < KDomain::DomainModel::Step
4
+ class Step6AttachDictionary < KDomain::DomainModel::Step
5
5
  attr_reader :dictionary
6
6
 
7
7
  def call
@@ -27,8 +27,11 @@ class Step5AttachDictionary < KDomain::DomainModel::Step
27
27
  entry[:model_count] = entry[:model_count] + 1
28
28
 
29
29
  unless entry[:types].include?(column_type)
30
- # ADD to investigate
31
- # log.warn("#{model_name} has a type mismatch for column name: #{column_name}")
30
+ investigate(step: :step5_attach_dictionary,
31
+ location: :process,
32
+ key: "#{model_name},#{column_name},#{column_type}",
33
+ message: "#{model_name} has a type mismatch for column name: #{column_name}")
34
+
32
35
  entry[:types] << column_type
33
36
  entry[:type_count] = entry[:type_count] + 1
34
37
  end
@@ -6,16 +6,53 @@ module KDomain
6
6
  class ExtractModel
7
7
  include KLog::Logging
8
8
 
9
- attr_reader :source_file
10
- attr_reader :data
9
+ attr_reader :shims_loaded
10
+ attr_reader :models
11
+ attr_reader :model
11
12
 
12
- def initialize(source_file)
13
- @source_file = source_file
13
+ def initialize(load_shim)
14
+ @load_shim = load_shim
15
+ @shims_loaded = false
16
+ @models = []
14
17
  end
15
18
 
16
- def call
17
- log.kv 'source_file', source_file
19
+ def extract(file)
20
+ load_shims unless shims_loaded
21
+
22
+ ActiveRecord.current_class = nil
23
+
24
+ load_retry(file, 10)
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)
38
+ return if times.negative?
39
+
40
+ load(file)
41
+
42
+ @model = ActiveRecord.current_class
43
+ @models << @model
44
+
45
+ # get_method_info(File.base_name(file))
46
+ rescue StandardError => e
47
+ puts e.message
48
+ if e.is_a?(NameError)
49
+ log.kv('add module', e.name)
50
+ eval("module #{e.name}; end")
51
+ return load_retry(path, times - 1)
52
+ end
53
+ log.exception(e)
18
54
  end
55
+ # rubocop:enable Security/Eval,Style/EvalWithLocation,Style/DocumentDynamicEvalDefinition,Metrics/AbcSize
19
56
  end
20
57
  end
21
58
  end
@@ -8,20 +8,17 @@
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 LoadShim
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
- log.kv 'preload', preload
21
+ shim_files.select { |sf| sf[:exist] }.each { |sf| require sf[:file] }
25
22
  end
26
23
 
27
24
  def register(name, file)
@@ -7,6 +7,7 @@ module Types
7
7
  end
8
8
 
9
9
  require_relative 'rails_resource'
10
+ require_relative 'rails_structure'
10
11
  require_relative 'investigate'
11
12
  require_relative 'database/_'
12
13
  require_relative 'dictionary'
@@ -8,7 +8,7 @@ module KDomain
8
8
  attribute :using , Types::Nominal::String
9
9
  attribute :order? , Types::Nominal::Hash
10
10
  attribute :where? , Types::Nominal::Any.optional.default(nil)
11
- attribute :unique? , Types::Nominal::Any.optional.default(nil)
11
+ attribute :unique? , Types::Nominal::Any.optional.default(nil)
12
12
  end
13
13
  end
14
14
  end
@@ -20,13 +20,13 @@ module KDomain
20
20
  attribute :limit? , Types::Strict::Integer.optional.default(nil)
21
21
  end
22
22
 
23
- attribute :name , Types::Strict::String
24
- attribute :primary_key , Types::Strict::String.optional.default(nil)
25
- attribute :primary_key_type , Types::Strict::String.optional.default(nil)
26
- attribute :id? , Types::Nominal::Any.optional.default(nil)
27
- attribute :columns , Types::Strict::Array.of(KDomain::Database::Table::Column)
28
- attribute :indexes , Types::Strict::Array.of(KDomain::Database::Index) # May want to have a Table::Index, but for now this is a shared scheam
29
- attribute :rails_schema , KDomain::Database::Table::RailsSchema
23
+ attribute :name , Types::Strict::String
24
+ attribute :primary_key , Types::Strict::String.optional.default(nil)
25
+ attribute :primary_key_type , Types::Strict::String.optional.default(nil)
26
+ attribute :id? , Types::Nominal::Any.optional.default(nil)
27
+ attribute :columns , Types::Strict::Array.of(KDomain::Database::Table::Column)
28
+ attribute :indexes , Types::Strict::Array.of(KDomain::Database::Index) # May want to have a Table::Index, but for now this is a shared scheam
29
+ attribute :rails_schema , KDomain::Database::Table::RailsSchema
30
30
  end
31
31
  end
32
32
  end
@@ -5,7 +5,7 @@ module KDomain
5
5
  module DomainModel
6
6
  class Domain < Dry::Struct
7
7
  attribute :models , Types::Strict::Array.of(KDomain::DomainModel::Model)
8
- attribute :erd_files , Types::Strict::Array.of(KDomain::DomainModel::ErdFile)
8
+ # attribute :erd_files , Types::Strict::Array.of(KDomain::DomainModel::ErdFile)
9
9
  end
10
10
  end
11
11
  end
@@ -4,11 +4,12 @@
4
4
  module KDomain
5
5
  module Schemas
6
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
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 :rails_structure , KDomain::Schemas::RailsStructure
12
+ attribute :investigate , KDomain::Schemas::Investigate
12
13
  end
13
14
  end
14
15
  end
@@ -0,0 +1,92 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Domain class holds a dictionary entry
4
+ module KDomain
5
+ module Schemas
6
+ class RailsStructure < Dry::Struct
7
+ class DefaultScope < Dry::Struct
8
+ attribute :block? , Types::Strict::String
9
+ end
10
+
11
+ class BaseType < Dry::Struct
12
+ attribute :name? , Types::Strict::String
13
+ attribute :opts? , Types::Strict::Hash
14
+ attribute :block? , Types::Strict::String.optional.default(nil)
15
+ end
16
+
17
+ class Scope < KDomain::Schemas::RailsStructure::BaseType
18
+ end
19
+
20
+ class BelongsTo < KDomain::Schemas::RailsStructure::BaseType
21
+ end
22
+
23
+ class HasOne < KDomain::Schemas::RailsStructure::BaseType
24
+ end
25
+
26
+ class HasMany < KDomain::Schemas::RailsStructure::BaseType
27
+ end
28
+
29
+ class HasAndBelongsToMany < KDomain::Schemas::RailsStructure::BaseType
30
+ end
31
+
32
+ class Validate < Dry::Struct
33
+ attribute :names? , Types::Array.of(Types::Strict::String)
34
+ attribute :opts? , Types::Strict::Hash
35
+ attribute :block? , Types::Strict::String.optional.default(nil)
36
+ end
37
+
38
+ class Validates < KDomain::Schemas::RailsStructure::BaseType
39
+ end
40
+
41
+ class Behaviours < Dry::Struct
42
+ attribute :class_name? , Types::Strict::String
43
+ attribute :default_scope? , KDomain::Schemas::RailsStructure::DefaultScope
44
+ attribute :scopes? , Types::Strict::Array.of(KDomain::Schemas::RailsStructure::Scope)
45
+ attribute :belongs_to? , Types::Strict::Array.of(KDomain::Schemas::RailsStructure::BelongsTo)
46
+ attribute :has_one? , Types::Strict::Array.of(KDomain::Schemas::RailsStructure::HasOne)
47
+ attribute :has_many? , Types::Strict::Array.of(KDomain::Schemas::RailsStructure::HasMany)
48
+ attribute :has_and_belongs_to_many? , Types::Strict::Array.of(KDomain::Schemas::RailsStructure::HasAndBelongsToMany)
49
+ attribute :validate? , Types::Strict::Array.of(KDomain::Schemas::RailsStructure::Validate)
50
+ attribute :validates? , Types::Strict::Array.of(KDomain::Schemas::RailsStructure::Validates)
51
+ attribute :attr_accessor? , Types::Array.of(Types::Strict::String)
52
+ attribute :attr_reader? , Types::Array.of(Types::Strict::String)
53
+ attribute :attr_writer? , Types::Array.of(Types::Strict::String)
54
+ end
55
+
56
+ class Method < Dry::Struct
57
+ attribute :name , Types::Strict::String
58
+ end
59
+
60
+ class Functions < Dry::Struct
61
+ attribute :class_name? , Types::Strict::String
62
+ attribute :module_name? , Types::Strict::String
63
+ attribute :class_full_name? , Types::Strict::String
64
+ attribute :attr_accessor? , Types::Array.of(Types::Strict::String)
65
+ attribute :attr_reader? , Types::Array.of(Types::Strict::String)
66
+ attribute :attr_writer? , Types::Array.of(Types::Strict::String)
67
+ attribute :klass? , Types::Strict::Array.of(KDomain::Schemas::RailsStructure::Method)
68
+ attribute :instance_public? , Types::Strict::Array.of(KDomain::Schemas::RailsStructure::Method)
69
+ attribute :instance_private? , Types::Strict::Array.of(KDomain::Schemas::RailsStructure::Method)
70
+ end
71
+
72
+ class Model < Dry::Struct
73
+ attribute :model_name , Types::Strict::String
74
+ attribute :table_name , Types::Strict::String
75
+ attribute :file , Types::Strict::String
76
+ attribute :exist , Types::Strict::Bool
77
+ attribute :state , Types::Strict::String
78
+ attribute :code , Types::Strict::String
79
+ attribute :behaviours? , KDomain::Schemas::RailsStructure::Behaviours
80
+ attribute :functions? , KDomain::Schemas::RailsStructure::Functions
81
+ end
82
+
83
+ class Controller < Dry::Struct
84
+ end
85
+
86
+ attribute :models , Types::Strict::Array.of(KDomain::Schemas::RailsStructure::Model)
87
+ attribute :controllers , Types::Strict::Array.of(KDomain::Schemas::RailsStructure::Controller)
88
+ end
89
+ end
90
+ end
91
+
92
+ # attribute :domain , KDomain::DomainModel::Domain
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module KDomain
4
- VERSION = '0.0.16'
4
+ VERSION = '0.0.20'
5
5
  end
data/lib/k_domain.rb CHANGED
@@ -11,8 +11,8 @@ require 'k_domain/raw_db_schema/load'
11
11
  require 'k_domain/domain_model/transform'
12
12
  require 'k_domain/domain_model/transform_steps/_'
13
13
  require 'k_domain/domain_model/load'
14
- require 'k_domain/rails_code_extractor/load_shim'
15
- # require 'k_domain/rails_code_extractor/extract_model'
14
+ require 'k_domain/rails_code_extractor/shim_loader'
15
+ require 'k_domain/rails_code_extractor/extract_model'
16
16
 
17
17
  # # This is useful if you want to initialize structures via Hash
18
18
  # class SymbolizeStruct < Dry::Struct
@@ -1,10 +1,10 @@
1
1
  module ActiveRecord
2
- def self.all_loaded_classes
3
- @all_loaded_classes ||= []
2
+ def self.current_class
3
+ @current_class ||= nil
4
4
  end
5
5
 
6
- def self.last_loaded_class
7
- @last_loaded_class ||= nil
6
+ def self.current_class=(value)
7
+ @current_class = value
8
8
  end
9
9
 
10
10
  class Base
@@ -15,17 +15,11 @@ module ActiveRecord
15
15
  end
16
16
 
17
17
  def self.class_info
18
- return @class_info if defined? @class_info
18
+ return ActiveRecord.current_class if ActiveRecord.current_class
19
19
 
20
- ActiveRecord.last_loaded_class = name
21
-
22
- @class_info = {
20
+ ActiveRecord.current_class = {
23
21
  class_name: name
24
22
  }
25
-
26
- ActiveRecord.all_loaded_classes << class_info
27
-
28
- @class_info
29
23
  end
30
24
 
31
25
  def self.set(key, value)
@@ -67,15 +61,36 @@ module ActiveRecord
67
61
  add(:enum, opts)
68
62
  end
69
63
 
64
+ # def self.attr_accessor(*args)
65
+ # args.each do |arg|
66
+ # self.class_eval("def #{arg};@#{arg};end")
67
+ # self.class_eval("def #{arg}=(val);@#{arg}=val;end")
68
+ # end
69
+ # end
70
+ # def self.attr_reader(*args)
71
+ # args.each do |arg|
72
+ # self.class_eval("def #{arg};@#{arg};end")
73
+ # end
74
+ # end
75
+ # def self.attr_writer(*args)
76
+ # args.each do |arg|
77
+ # self.class_eval("def #{arg};@#{arg};end")
78
+ # self.class_eval("def #{arg}=(val);@#{arg}=val;end")
79
+ # end
80
+ # end
81
+
70
82
  def self.attr_accessor(*names)
83
+ super(*names)
71
84
  add(:attr_accessor, names)
72
85
  end
73
86
 
74
87
  def self.attr_reader(*names)
88
+ super(*names)
75
89
  add(:attr_reader, names)
76
90
  end
77
91
 
78
92
  def self.attr_writer(*names)
93
+ super(*names)
79
94
  add(:attr_writer, names)
80
95
  end
81
96
 
@@ -159,8 +174,6 @@ module ActiveRecord
159
174
  block_source = nil
160
175
  block_source = lambda_source(block, 'validate') if block_given?
161
176
 
162
- set(:default_scope, opts.merge(block: block_source))
163
-
164
177
  add(:validate, {
165
178
  names: names,
166
179
  opts: opts,
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: k_domain
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.16
4
+ version: 0.0.20
5
5
  platform: ruby
6
6
  authors:
7
7
  - David Cruwys
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2021-10-03 00:00:00.000000000 Z
11
+ date: 2021-10-04 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activesupport
@@ -102,12 +102,11 @@ files:
102
102
  - lib/k_domain/domain_model/transform_steps/step1_attach_db_schema.rb
103
103
  - lib/k_domain/domain_model/transform_steps/step2_attach_models.rb
104
104
  - lib/k_domain/domain_model/transform_steps/step3_attach_columns.rb
105
- - lib/k_domain/domain_model/transform_steps/step4_attach_erd_files.rb
106
- - lib/k_domain/domain_model/transform_steps/step5_attach_dictionary.rb
107
- - lib/k_domain/domain_model/transform_steps/step8_rails_resource_models.rb
108
- - lib/k_domain/domain_model/transform_steps/step9_rails_structure_models.rb
105
+ - lib/k_domain/domain_model/transform_steps/step4_rails_resource_models.rb
106
+ - lib/k_domain/domain_model/transform_steps/step5_rails_models.rb
107
+ - lib/k_domain/domain_model/transform_steps/step6_attach_dictionary.rb
109
108
  - lib/k_domain/rails_code_extractor/extract_model.rb
110
- - lib/k_domain/rails_code_extractor/load_shim.rb
109
+ - lib/k_domain/rails_code_extractor/shim_loader.rb
111
110
  - lib/k_domain/raw_db_schema/load.rb
112
111
  - lib/k_domain/raw_db_schema/transform.rb
113
112
  - lib/k_domain/schemas/_.rb
@@ -141,6 +140,7 @@ files:
141
140
  - lib/k_domain/schemas/domain_model.rb
142
141
  - lib/k_domain/schemas/investigate.rb
143
142
  - lib/k_domain/schemas/rails_resource.rb
143
+ - lib/k_domain/schemas/rails_structure.rb
144
144
  - lib/k_domain/version.rb
145
145
  - templates/active_record_shims.rb
146
146
  - templates/fake_module_shims.rb
@@ -1,457 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- # Attach source code found in rails model definitions to models
4
- class Step4AttachErdFiles < KDomain::DomainModel::Step
5
- attr_accessor :ruby_code
6
-
7
- # NOTE: This code could be rewritten using monkey patched modules and peak
8
- def call
9
- domain[:erd_files] = domain_models.map { |model| load_dsl(model) }
10
- end
11
-
12
- private
13
-
14
- def reset_dsl
15
- @ruby_code = nil
16
- @dsl = nil
17
- end
18
-
19
- def dsl
20
- @dsl ||= {
21
- name: '',
22
- name_plural: ''
23
- }
24
- end
25
-
26
- def load_dsl(model)
27
- # this should be a configuration
28
- # print '.'
29
-
30
- reset_dsl
31
-
32
- dsl[:name] = model[:name]
33
- dsl[:name_plural] = model[:name_plural]
34
- dsl[:dsl_file] = model[:erd_location][:exist] ? model[:erd_location][:file] : ''
35
-
36
- return dsl unless File.exist?(dsl[:dsl_file])
37
-
38
- @ruby_code = File.read(dsl[:dsl_file])
39
-
40
- dsl[:source] = read_dsl_source
41
- dsl[:dsl] = build_dsl
42
- dsl[:todo] = todo
43
-
44
- dsl
45
- end
46
-
47
- def read_dsl_source
48
- regex_split_private_public = /(?<public>.+?)(?=\bprivate\b)(?<private>.*)/m
49
-
50
- split_code = regex_split_private_public.match(ruby_code)
51
-
52
- public_code = nil
53
- private_code = nil
54
-
55
- if split_code
56
- public_code = split_code[:public]
57
- private_code = split_code[:private]
58
- end
59
-
60
- {
61
- ruby: ruby_code,
62
- public: public_code,
63
- private: private_code,
64
- all_methods: grab_methods(public_code, private_code)
65
- }
66
- end
67
-
68
- def build_dsl
69
- return if ruby_code.nil?
70
-
71
- # need to support options as hash instead of options as string in the future
72
- {
73
- default_scope: grab_default_scope,
74
- scopes: grab_scopes,
75
- belongs_to: grab_belongs_to,
76
- has_one: grab_has_one,
77
- has_many: grab_has_many,
78
- has_and_belongs_to_many: grab_has_and_belongs_to_many,
79
- validate_on: grab_validate,
80
- validates_on: grab_validates
81
- }
82
-
83
- # ^(?<spaces>\s*)(?<event_type>before_create|before_save|before_destroy|after_create|after_save|after_destroy) (:(?<name>\w*)[, ]?(?<scope>.*)|(?<scope>\{.*?\}.*$))
84
- end
85
-
86
- def grab_default_scope
87
- regex = /default_scope \{(?<scope>.*?)\}/m
88
-
89
- m = regex.match(ruby_code)
90
-
91
- return "{ #{m[:scope].strip.gsub('\n', '')} }" if m
92
-
93
- nil
94
- end
95
-
96
- def grab_scopes
97
- entries = []
98
- # Start from beginning of line and capture
99
- # - number of spaces scope
100
- # - name of scope
101
- # - value of scope to end of line
102
- regex = /^(?<spaces>\s*)scope :(?<name>\w*)[, ]?(?<scope>.*)/
103
-
104
- # rubocop:disable Metrics/BlockLength
105
- ruby_code.scan(regex) do
106
- m = $LAST_MATCH_INFO
107
- spaces = m[:spaces] # .delete("\n")
108
- last_lf = spaces.rindex("\n")
109
- spaces = last_lf ? spaces[spaces.rindex("\n") + 1..-1] : spaces
110
- name = m[:name]
111
- scope = m[:scope].strip
112
-
113
- # Found a valid one liner
114
- if scope.ends_with?('}') && (scope.scan(/{/).count == scope.scan(/}/).count)
115
- scope = escape_single_quote(scope)
116
- entries << { name: name, scope: scope }
117
- else
118
- # Have a multiline scope, lets see if it is cleanly formatted
119
-
120
- start_anchor = "#{spaces}scope :#{name}"
121
- end_anchor = "#{spaces}}"
122
-
123
- # log.kv 'spaces', spaces.length
124
- # log.kv 'name', name
125
- # log.kv 'start_anchor', start_anchor
126
- # log.kv 'end_anchor', end_anchor
127
-
128
- start_index = ruby_code.index(/#{start_anchor}/)
129
-
130
- if start_index.nil?
131
- log.error("[#{@current_entity[:name]}] could not find [start] anchor index for [#{name}]")
132
- else
133
- ruby_section = ruby_code[start_index..-1]
134
- end_index = ruby_section.index(/^#{end_anchor}/) # Add ^ start of line
135
- if end_index.nil?
136
- log.error("[#{@current_entity[:name]}] could not find [end] anchor index for [#{name}]")
137
- else
138
- scope = ruby_section[start_anchor.length + 1..end_index].strip
139
- scope = escape_single_quote("#{scope}#{end_anchor}")
140
- entries << { name: name, scope: scope }
141
- end
142
- end
143
- end
144
- end
145
- entries
146
- rescue StandardError => e
147
- # bin ding.pry
148
- puts e.message
149
- end
150
- # rubocop:enable Metrics/BlockLength
151
-
152
- def grab_belongs_to
153
- entries = []
154
-
155
- # Start from beginning of line and capture
156
- # - number of spaces before belongs_to
157
- # - name of the belongs_to
158
- # - value of belongs_to to end of line
159
- regex = /^(?<spaces>\s*)belongs_to :(?<name>\w*)[, ]?(?<options>.*)/
160
-
161
- ruby_code.scan(regex) do
162
- m = $LAST_MATCH_INFO
163
-
164
- # spaces = m[:spaces] # .delete("\n")
165
- # last_lf = spaces.rindex("\n")
166
- # spaces = last_lf ? spaces[spaces.rindex("\n") + 1..-1] : spaces
167
- name = m[:name]
168
-
169
- options = m[:options]
170
- .gsub(':polymorphic => ', 'polymorphic: ')
171
- .gsub(':class_name => ', 'class_name: ')
172
- .gsub(':foreign_key => ', 'foreign_key: ')
173
- .strip
174
-
175
- options = clean_lambda(options)
176
-
177
- entries << { name: name, options: extract_options(options), raw_options: options }
178
- end
179
- entries
180
- rescue StandardError => e
181
- # bin ding.pry
182
- puts e.message
183
- end
184
-
185
- def grab_has_one
186
- entries = []
187
-
188
- # Start from beginning of line and capture
189
- # - number of spaces before has_one
190
- # - name of the has_one
191
- # - value of has_one to end of line
192
- regex = /^(?<spaces>\s*)has_one :(?<name>\w*)[, ]?(?<options>.*)/
193
-
194
- ruby_code.scan(regex) do
195
- m = $LAST_MATCH_INFO
196
-
197
- # spaces = m[:spaces] # .delete("\n")
198
- # last_lf = spaces.rindex("\n")
199
- # spaces = last_lf ? spaces[spaces.rindex("\n") + 1..-1] : spaces
200
- name = m[:name]
201
- options = m[:options]
202
- .strip
203
- # .gsub(':polymorphic => ', 'polymorphic: ')
204
- # .gsub(':class_name => ', 'class_name: ')
205
- # .gsub(':foreign_key => ', 'foreign_key: ')
206
-
207
- options = clean_lambda(options)
208
-
209
- entries << { name: name, options: extract_options(options), raw_options: options }
210
- end
211
- entries
212
- rescue StandardError => e
213
- # bin ding.pry
214
- puts e.message
215
- end
216
-
217
- def grab_has_many
218
- entries = []
219
- # Start from beginning of line and capture
220
- # - number of spaces before has_many
221
- # - name of the has_many
222
- # - value of has_many to end of line
223
- regex = /^(?<spaces>\s*)has_many :(?<name>\w*)[, ]?(?<options>.*)/
224
-
225
- ruby_code.scan(regex) do
226
- m = $LAST_MATCH_INFO
227
-
228
- # spaces = m[:spaces] # .delete("\n")
229
- # last_lf = spaces.rindex("\n")
230
- # spaces = last_lf ? spaces[spaces.rindex("\n") + 1..-1] : spaces
231
- name = m[:name]
232
- options = m[:options]
233
- .gsub(':dependent => ', 'dependent: ')
234
- .gsub(':class_name => ', 'class_name: ')
235
- .gsub(':foreign_key => ', 'foreign_key: ')
236
- .gsub(':primary_key => ', 'primary_key: ')
237
- .strip
238
-
239
- options = clean_lambda(options)
240
-
241
- entries << { name: name, options: extract_options(options), raw_options: options }
242
- end
243
- entries
244
- rescue StandardError => e
245
- # bin ding.pry
246
- puts e.message
247
- end
248
-
249
- def grab_has_and_belongs_to_many
250
- entries = []
251
- # Start from beginning of line and capture
252
- # - number of spaces before has_and_belongs_to_many
253
- # - name of the has_and_belongs_to_many
254
- # - value of has_and_belongs_to_many to end of line
255
- regex = /^(?<spaces>\s*)has_and_belongs_to_many :(?<name>\w*)[, ]?(?<options>.*)/
256
-
257
- ruby_code.scan(regex) do
258
- m = $LAST_MATCH_INFO
259
-
260
- # spaces = m[:spaces] # .delete("\n")
261
- # last_lf = spaces.rindex("\n")
262
- # spaces = last_lf ? spaces[spaces.rindex("\n") + 1..-1] : spaces
263
- name = m[:name]
264
- options = m[:options]
265
- .gsub(':dependent => ', 'dependent: ')
266
- .gsub(':class_name => ', 'class_name: ')
267
- .gsub(':foreign_key => ', 'foreign_key: ')
268
- .gsub(':primary_key => ', 'primary_key: ')
269
- .strip
270
-
271
- options = clean_lambda(options)
272
-
273
- entries << { name: name, options: {}, raw_options: options }
274
- end
275
- entries
276
- rescue StandardError => e
277
- # bin ding.pry
278
- puts e.message
279
- end
280
-
281
- def grab_validates
282
- entries = []
283
- # Start from beginning of line and capture
284
- # - number of spaces before validates
285
- # - name of the validates
286
- # - value of validates to end of line
287
- regex = /^(?<spaces>\s*)validates :(?<name>\w*)[, ]?(?<options>.*)/
288
-
289
- ruby_code.scan(regex) do
290
- m = $LAST_MATCH_INFO
291
-
292
- # spaces = m[:spaces] # .delete("\n")
293
- # last_lf = spaces.rindex("\n")
294
- # spaces = last_lf ? spaces[spaces.rindex("\n") + 1..-1] : spaces
295
- name = m[:name]
296
-
297
- options = m[:options].strip
298
-
299
- options = clean_lambda(options)
300
-
301
- entries << { name: name, raw_options: options }
302
- end
303
- entries
304
- rescue StandardError => e
305
- # bin ding.pry
306
- puts e.message
307
- end
308
-
309
- def grab_validate
310
- entries = []
311
- # Start from beginning of line and capture
312
- # - number of spaces before validate
313
- # - list of methods to call until to end of line
314
- # regex = /^(?<spaces>\s*)validate :(?<name>\w*)[, ]?(?<options>.*)/
315
- regex = /^(?<spaces>\s*)validate (?<line>:.*)/
316
- # puts @current_entity[:name]
317
-
318
- ruby_code.scan(regex) do
319
- m = $LAST_MATCH_INFO
320
-
321
- # spaces = m[:spaces] # .delete("\n")
322
- # last_lf = spaces.rindex("\n")
323
- # spaces = last_lf ? spaces[spaces.rindex("\n") + 1..-1] : spaces
324
- line = m[:line]
325
-
326
- entries << { line: line }
327
- # puts @current_entity[:validate]
328
- end
329
- entries
330
- rescue StandardError => e
331
- # bin ding.pry
332
- puts e.message
333
- end
334
-
335
- def grab_methods(public_code = ruby_code, private_code = nil)
336
- # public_code = ruby_code_public.nil? ? ruby_code : ruby_code_public
337
- # private_code = ruby_code_private
338
-
339
- regex = /def (?<method>.*)/
340
-
341
- # log.info(@current_entity[:name])
342
-
343
- public_methods = parse_methods(:public, public_code&.scan(regex)&.flatten || [])
344
- private_methods = parse_methods(:private, private_code&.scan(regex)&.flatten || [])
345
- methods = (public_methods + private_methods)
346
-
347
- class_methods = methods.select { |method| method[:class_method] == true }
348
-
349
- all_instance = methods.select { |method| method[:class_method] == false }
350
- instance_public = all_instance.select { |method| method[:scope] == :public }
351
- instance_private = all_instance.select { |method| method[:scope] == :private }
352
-
353
- {
354
- klass: class_methods,
355
- instance: all_instance,
356
- instance_public: instance_public,
357
- instance_private: instance_private
358
- }
359
- end
360
-
361
- def parse_methods(scope, methods)
362
- methods.map do |value|
363
- class_method = value.starts_with?('self.')
364
- name = class_method ? value[5..-1] : value
365
- arguments = nil
366
- arguments_index = name.index('(')
367
-
368
- if arguments_index
369
- arguments = name[arguments_index..-1]
370
- name = name[0..arguments_index - 1]
371
- end
372
-
373
- arguments = escape_single_quote(arguments)
374
-
375
- {
376
- name: name,
377
- scope: scope,
378
- class_method: class_method,
379
- arguments: arguments&.strip.to_s
380
- }
381
- end
382
- end
383
-
384
- def todo
385
- {
386
- after_destroy: [], # to do
387
- before_save: [], # to do
388
- after_save: [], # to do
389
- before_create: [], # to do
390
- after_create: [], # to do
391
- enum: [], # to do
392
- attr_encrypted: [], # to do
393
- validates_uniqueness_of: [], # to do
394
- validates_confirmation_of: [], # to do
395
- attr_accessor: [], # to do
396
- attr_reader: [], # to do
397
- attr_writer: [] # to do
398
- }
399
- end
400
-
401
- def escape_single_quote(value)
402
- return nil if value.nil?
403
-
404
- value.gsub("'", "\\\\'")
405
- end
406
-
407
- # rubocop:disable Style/EvalWithLocation, Security/Eval, Style/DocumentDynamicEvalDefinition
408
- def extract_options(options)
409
- eval("{ #{options} }")
410
- rescue StandardError => e
411
- investigate(
412
- step: :step4_attach_erd_files_models,
413
- location: :extract_options,
414
- key: nil,
415
- message: e.message
416
- )
417
- {}
418
- rescue SyntaxError => e
419
- # may be the issue is from a comment at the off the line
420
- comment_index = options.rindex('#') - 1
421
-
422
- if comment_index.positive?
423
- options_minus_comment = options[0..comment_index].squish
424
- return extract_options(options_minus_comment)
425
- end
426
-
427
- investigate(
428
- step: :step4_attach_erd_files_models,
429
- location: :extract_options,
430
- key: nil,
431
- message: e.message
432
- )
433
- {}
434
- end
435
- # rubocop:enable Style/EvalWithLocation, Security/Eval, Style/DocumentDynamicEvalDefinition
436
-
437
- def clean_lambda(options)
438
- if /^->/.match?(options)
439
- index = options.index(/}\s*,/)
440
- if index.nil?
441
- if options.count('{') == options.count('}')
442
- index = options.rindex(/}/)
443
- options = "a_lambda: '#{escape_single_quote(options[0..index])}'"
444
- else
445
- log.error(options)
446
- options = "a_lambda: '#{escape_single_quote(options)}'"
447
- end
448
- else
449
- options = "a_lambda: '#{escape_single_quote(options[0..index])}', #{options[index + 2..-1]}"
450
- end
451
- end
452
- options
453
- rescue StandardError => e
454
- # bin ding.pry
455
- puts e.message
456
- end
457
- end
@@ -1,33 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- # Locate rails model files
4
- class Step9RailsStructureModels < KDomain::DomainModel::Step
5
- attr_accessor :ruby_code
6
-
7
- def call
8
- raise 'ERD path not supplied' if opts[:erd_path].nil?
9
-
10
- self.rails_structure_models = rails_resource_models.map do |resource|
11
- process_resource(OpenStruct.new(resource))
12
- end
13
- end
14
-
15
- private
16
-
17
- def process_resource(resource)
18
- erd_path = opts[:erd_path]
19
- puts erd_path
20
- @model = {
21
- model_name: resource.model_name,
22
- table_name: resource.table_name,
23
- file: resource.file,
24
- exist: resource.exist,
25
- state: resource.state,
26
- code: resource.exist ? File.read(resource.file) : '',
27
- behaviours: {},
28
- functions: {}
29
- }
30
-
31
- @model
32
- end
33
- end