k_domain 0.0.16 → 0.0.20

Sign up to get free protection for your applications and to get access to all the features.
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