trains 0.0.3 → 0.0.5
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/.deepsource.toml +0 -3
- data/Gemfile.lock +1 -0
- data/README.md +2 -2
- data/lib/trains/dto/app.rb +4 -9
- data/lib/trains/dto/controller.rb +2 -2
- data/lib/trains/dto/field.rb +1 -1
- data/lib/trains/dto/method.rb +1 -1
- data/lib/trains/dto/migration.rb +5 -0
- data/lib/trains/dto/model.rb +1 -1
- data/lib/trains/dto/route.rb +5 -0
- data/lib/trains/scanner.rb +70 -70
- data/lib/trains/utils/migration_tailor.rb +43 -0
- data/lib/trains/utils/rails_dir.rb +2 -2
- data/lib/trains/utils/result.rb +1 -1
- data/lib/trains/version.rb +1 -1
- data/lib/trains/visitor/controller.rb +22 -20
- data/lib/trains/visitor/migration.rb +63 -23
- data/lib/trains/visitor/route.rb +79 -0
- data/lib/trains/visitor/schema.rb +88 -0
- data/trains.gemspec +1 -0
- metadata +8 -4
- data/.ruby-version +0 -1
- data/lib/trains/utils/ast_store.rb +0 -25
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: e4c83b37c56def34baa1bd60260cecec154d1da870f2b43471994f60c2a7397e
|
4
|
+
data.tar.gz: 3075d23e3f460a9787a0c1184c05ece0bbe21ca8d676bfa8828bb8b5cd91c1ac
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 47357749dc2a2effab891336c1c0d2e338474288b720dd01eac00173adba3b9ce41f5bbd06bafec55b2319e25b2faa128aab9805c4251009de0ac3617661ac2c
|
7
|
+
data.tar.gz: 7dd8d5c09dd6a6e84da47a74f805dc9b26cfad3943f98d433668bb2f8d4adb1d4f08a5607355e1c2e9a25cee91e59ff4b3016f087dad05f9a86e57fbfe01c7cc
|
data/.deepsource.toml
CHANGED
data/Gemfile.lock
CHANGED
data/README.md
CHANGED
@@ -10,8 +10,8 @@ A gem that statically analyses your Rails app and extracts information about its
|
|
10
10
|
1. [Installation](#installation)
|
11
11
|
2. [Usage](#usage)
|
12
12
|
3. [Features](#features)
|
13
|
-
|
14
|
-
|
13
|
+
1. [Create Model definitions from migrations](#create-model-definitions-from-migrations)
|
14
|
+
2. [Create controller definitions from files](#create-controller-definitions-from-files)
|
15
15
|
4. [Development](#development)
|
16
16
|
5. [Contributing](#contributing)
|
17
17
|
6. [License](#license)
|
data/lib/trains/dto/app.rb
CHANGED
@@ -1,13 +1,8 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module Trains
|
2
4
|
module DTO
|
3
|
-
App =
|
4
|
-
|
5
|
-
:name,
|
6
|
-
:controllers,
|
7
|
-
:models,
|
8
|
-
:migrations,
|
9
|
-
:helpers,
|
10
|
-
keyword_init: true
|
11
|
-
)
|
5
|
+
App =
|
6
|
+
Data.define(:name, :controllers, :models, :helpers, :routes)
|
12
7
|
end
|
13
8
|
end
|
data/lib/trains/dto/field.rb
CHANGED
data/lib/trains/dto/method.rb
CHANGED
data/lib/trains/dto/model.rb
CHANGED
data/lib/trains/scanner.rb
CHANGED
@@ -3,21 +3,16 @@ module Trains
|
|
3
3
|
class Scanner
|
4
4
|
include Utils
|
5
5
|
|
6
|
-
|
7
|
-
|
8
|
-
def initialize(folder)
|
6
|
+
def initialize(folder, options = {})
|
9
7
|
@root_folder = folder
|
10
|
-
@
|
11
|
-
@
|
12
|
-
@
|
13
|
-
@
|
14
|
-
@
|
15
|
-
Dir.chdir @dir
|
8
|
+
@models = {}
|
9
|
+
@controllers = {}
|
10
|
+
@helpers = {}
|
11
|
+
@dir = File.expand_path(folder)
|
12
|
+
@options = options
|
16
13
|
end
|
17
14
|
|
18
15
|
def scan
|
19
|
-
# @nodes = get_tree(@dir)
|
20
|
-
|
21
16
|
# Check if @folder is a Rails directory before beginning analysis
|
22
17
|
rails_dir_result = RailsDir.check @dir
|
23
18
|
case rails_dir_result
|
@@ -32,62 +27,85 @@ module Trains
|
|
32
27
|
)
|
33
28
|
end
|
34
29
|
|
35
|
-
@
|
36
|
-
@controllers =
|
37
|
-
|
38
|
-
# @
|
30
|
+
@models = generate_models unless @options[:models] == false
|
31
|
+
@controllers = get_controllers unless @options[:controllers] == false
|
32
|
+
@routes = get_routes.to_set unless @options[:routes] == false
|
33
|
+
# TODO: @helpers = get_helpers
|
39
34
|
|
40
35
|
# Create instance of Trains::DTO::App
|
41
36
|
DTO::App.new(
|
42
37
|
name: nil,
|
43
38
|
controllers: @controllers,
|
44
39
|
models: @models,
|
45
|
-
|
46
|
-
|
40
|
+
helpers: @helpers,
|
41
|
+
routes: @routes
|
47
42
|
)
|
48
43
|
end
|
49
44
|
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
45
|
+
private
|
46
|
+
|
47
|
+
# Generate models from either db/schema.rb
|
48
|
+
# else stitch together migrations to create models
|
49
|
+
def generate_models
|
50
|
+
return get_models if File.exist?(File.join(@dir, 'db', 'schema.rb'))
|
51
|
+
|
52
|
+
migrations = get_migrations
|
53
|
+
Utils::MigrationTailor.stitch(migrations)
|
59
54
|
end
|
60
55
|
|
61
|
-
def
|
56
|
+
def get_routes
|
57
|
+
route_file = [File.join(@dir, 'config', 'routes.rb')]
|
62
58
|
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
59
|
+
routes_results = parse_util(route_file, Visitor::Route)
|
60
|
+
routes_results
|
61
|
+
.select { |result| result.error.nil? }
|
62
|
+
.map(&:data)
|
63
|
+
.flatten
|
64
|
+
end
|
65
|
+
|
66
|
+
def get_models
|
67
|
+
result_hash = {}
|
68
|
+
schema_file = [File.join(@dir, 'db', 'schema.rb')]
|
69
|
+
models_results = parse_util(schema_file, Visitor::Schema)
|
71
70
|
|
72
|
-
|
71
|
+
models_results
|
72
|
+
.select { |result| result.error.nil? }
|
73
|
+
.map(&:data)
|
74
|
+
.flatten
|
75
|
+
.each { |model| result_hash[model.name] = model }
|
76
|
+
|
77
|
+
result_hash
|
73
78
|
end
|
74
79
|
|
80
|
+
def get_helpers; end
|
81
|
+
|
75
82
|
def get_gemfile; end
|
76
83
|
|
77
84
|
def get_controllers
|
85
|
+
result_hash = {}
|
78
86
|
controllers =
|
79
87
|
Dir.glob(File.join(@dir, 'app', 'controllers', '**', '*_controller.rb'))
|
88
|
+
controller_results = parse_util(controllers, Visitor::Controller)
|
89
|
+
|
90
|
+
controller_results
|
91
|
+
.select { |result| result.error.nil? }
|
92
|
+
.map(&:data)
|
93
|
+
.flatten
|
94
|
+
.each { |controller| result_hash[controller.name] = controller }
|
80
95
|
|
81
|
-
|
96
|
+
result_hash
|
82
97
|
end
|
83
98
|
|
84
99
|
def get_migrations
|
85
100
|
migrations = Dir.glob(File.join(@dir, 'db', 'migrate', '**', '*.rb'))
|
101
|
+
migration_results = parse_util(migrations, Visitor::Migration)
|
86
102
|
|
87
|
-
|
103
|
+
migration_results
|
104
|
+
.select { |result| result.error.nil? }
|
105
|
+
.map(&:data)
|
88
106
|
end
|
89
107
|
|
90
|
-
def parse_util(file_nodes,
|
108
|
+
def parse_util(file_nodes, visitor_class)
|
91
109
|
unless file_nodes.class.include? Enumerable
|
92
110
|
return(
|
93
111
|
Result.new(
|
@@ -100,40 +118,22 @@ module Trains
|
|
100
118
|
)
|
101
119
|
end
|
102
120
|
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
visitor.process(processed_source.ast)
|
109
|
-
visitor.result
|
110
|
-
end
|
111
|
-
rescue StandardError => e
|
112
|
-
puts e.backtrace
|
113
|
-
Result.new(data: nil, error: e)
|
114
|
-
end
|
115
|
-
end
|
116
|
-
|
117
|
-
def get_tree(node, prefix = '')
|
118
|
-
path = File.join(prefix, node)
|
119
|
-
obj = { path: nil }
|
121
|
+
Parallel.map(file_nodes) do |node|
|
122
|
+
processed_source =
|
123
|
+
RuboCop::AST::ProcessedSource.from_file(node, RUBY_VERSION.to_f)
|
124
|
+
visitor = visitor_class.new
|
125
|
+
visitor.process(processed_source.ast)
|
120
126
|
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
if Dir.exist? path
|
128
|
-
children = []
|
129
|
-
Dir.each_child path do |child|
|
130
|
-
child_node = get_tree(child, path)
|
131
|
-
children.append(child_node) unless child_node.nil?
|
127
|
+
Result.new(data: visitor.result, error: nil)
|
128
|
+
rescue StandardError => e
|
129
|
+
puts "An error occurred while parsing #{node}. Use debug option to view backtrace. Skipping file..."
|
130
|
+
if @options[:debug]
|
131
|
+
puts e.message
|
132
|
+
puts e.backtrace
|
132
133
|
end
|
133
|
-
obj[:children] = children
|
134
|
-
end
|
135
134
|
|
136
|
-
|
135
|
+
Result.new(data: nil, error: e)
|
136
|
+
end
|
137
137
|
end
|
138
138
|
end
|
139
139
|
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
module Trains
|
2
|
+
module Utils
|
3
|
+
module MigrationTailor
|
4
|
+
def self.stitch(migrations)
|
5
|
+
models = {}
|
6
|
+
|
7
|
+
migrations.each do |mig|
|
8
|
+
case mig.modifier
|
9
|
+
when :create_table
|
10
|
+
models[mig.table_name] = {}
|
11
|
+
models[mig.table_name] = Trains::DTO::Model.new(
|
12
|
+
name: mig.table_name,
|
13
|
+
fields: mig.fields,
|
14
|
+
version: mig.version
|
15
|
+
)
|
16
|
+
when :add_column
|
17
|
+
models[mig.table_name].fields.push(*mig.fields)
|
18
|
+
when :remove_column
|
19
|
+
column =
|
20
|
+
models[mig.table_name].fields.find do |field|
|
21
|
+
field.name == mig.fields.first.name
|
22
|
+
end
|
23
|
+
models[mig.table_name].fields.delete(column)
|
24
|
+
when :change_table
|
25
|
+
# TODO: handle renaming columns
|
26
|
+
when :change_column
|
27
|
+
# get column
|
28
|
+
column =
|
29
|
+
models[mig.table_name].fields.find do |field|
|
30
|
+
field.name == mig.fields.first.name
|
31
|
+
end
|
32
|
+
# replace it with new column object
|
33
|
+
models[mig.table_name].fields.delete(column)
|
34
|
+
|
35
|
+
models[mig.table_name].fields << mig.fields.first
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
models
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
@@ -3,9 +3,9 @@ module Trains
|
|
3
3
|
class RailsDir
|
4
4
|
# checks if supplied dir is in a Rails app dir
|
5
5
|
def self.check(root_path)
|
6
|
-
rails_bin = File.join(root_path,
|
6
|
+
rails_bin = File.join(root_path, "bin", "rails")
|
7
7
|
|
8
|
-
Result.new(File.exist?(rails_bin), nil)
|
8
|
+
Result.new(data: File.exist?(rails_bin), error: nil)
|
9
9
|
end
|
10
10
|
end
|
11
11
|
end
|
data/lib/trains/utils/result.rb
CHANGED
data/lib/trains/version.rb
CHANGED
@@ -1,5 +1,5 @@
|
|
1
|
-
require_relative
|
2
|
-
require_relative
|
1
|
+
require_relative "../dto/controller"
|
2
|
+
require_relative "../dto/method"
|
3
3
|
|
4
4
|
module Trains
|
5
5
|
module Visitor
|
@@ -12,31 +12,33 @@ module Trains
|
|
12
12
|
end
|
13
13
|
|
14
14
|
def on_class(node)
|
15
|
-
class_name = node.identifier.const_name
|
16
|
-
parent_class = node.parent_class.const_name.
|
17
|
-
|
18
|
-
is_controller =
|
19
|
-
if parent_class.nil?
|
20
|
-
true if class_name == :ApplicationController
|
21
|
-
else
|
22
|
-
parent_class == :"ActionController::Base"
|
23
|
-
end
|
24
|
-
return unless is_controller
|
15
|
+
class_name = node.identifier.const_name.to_s
|
16
|
+
parent_class = node.parent_class.const_name.to_s
|
17
|
+
return unless controller?(parent_class)
|
25
18
|
|
26
19
|
@class_name = class_name
|
27
|
-
|
28
|
-
|
29
|
-
# List out all controller methods
|
30
|
-
def on_def(node)
|
31
|
-
method_name = node.method_name
|
32
|
-
@method_list.append(
|
33
|
-
DTO::Method.new(name: method_name.to_s, visibility: nil, source: nil)
|
34
|
-
)
|
20
|
+
parse_body(node.body) unless node.body.nil?
|
35
21
|
end
|
36
22
|
|
37
23
|
def result
|
38
24
|
DTO::Controller.new(name: @class_name, method_list: @method_list.uniq)
|
39
25
|
end
|
26
|
+
|
27
|
+
private
|
28
|
+
|
29
|
+
def parse_body(body)
|
30
|
+
body.each_child_node do |child|
|
31
|
+
@method_list << parse_method(child) if child.type == :def
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
def controller?(parent_class)
|
36
|
+
%w[ActionController::Base ApplicationController].include? parent_class
|
37
|
+
end
|
38
|
+
|
39
|
+
def parse_method(node)
|
40
|
+
DTO::Method.new(name: node.method_name.to_s)
|
41
|
+
end
|
40
42
|
end
|
41
43
|
end
|
42
44
|
end
|
@@ -1,14 +1,15 @@
|
|
1
|
-
require
|
1
|
+
require "yaml"
|
2
2
|
|
3
3
|
module Trains
|
4
4
|
module Visitor
|
5
5
|
# Visitor that parses DB migration and associates them with Rails models
|
6
6
|
class Migration < Base
|
7
|
-
def_node_matcher :send_node?,
|
7
|
+
def_node_matcher :send_node?, "(send nil? ...)"
|
8
8
|
attr_reader :is_migration, :model
|
9
9
|
|
10
10
|
def initialize
|
11
11
|
@model = nil
|
12
|
+
@table_modifier = nil
|
12
13
|
@table_name = nil
|
13
14
|
@is_class = false
|
14
15
|
@is_migration = false
|
@@ -19,7 +20,7 @@ module Trains
|
|
19
20
|
end
|
20
21
|
|
21
22
|
def on_class(node)
|
22
|
-
unless node.parent_class.source.include?
|
23
|
+
unless node.parent_class.source.include? "ActiveRecord::Migration"
|
23
24
|
return
|
24
25
|
end
|
25
26
|
|
@@ -29,6 +30,17 @@ module Trains
|
|
29
30
|
process_node(node.body)
|
30
31
|
end
|
31
32
|
|
33
|
+
def result
|
34
|
+
DTO::Migration.new(
|
35
|
+
table_name: @table_name,
|
36
|
+
modifier: @table_modifier,
|
37
|
+
fields: @fields,
|
38
|
+
version: @migration_version
|
39
|
+
)
|
40
|
+
end
|
41
|
+
|
42
|
+
private
|
43
|
+
|
32
44
|
def extract_version(class_const)
|
33
45
|
match = class_const.match(/\d+.\d+/)
|
34
46
|
return nil if match.nil?
|
@@ -37,44 +49,72 @@ module Trains
|
|
37
49
|
end
|
38
50
|
|
39
51
|
def process_node(node)
|
40
|
-
|
52
|
+
return unless node.def_type?
|
53
|
+
|
54
|
+
process_def_node(node)
|
41
55
|
end
|
42
56
|
|
43
57
|
def process_def_node(node)
|
44
58
|
allowed_method_names = %i[change up down]
|
45
|
-
allowed_table_modifiers = %i[
|
59
|
+
allowed_table_modifiers = %i[
|
60
|
+
create_table
|
61
|
+
change_table
|
62
|
+
update_column
|
63
|
+
add_column
|
64
|
+
remove_column
|
65
|
+
change_column
|
66
|
+
]
|
67
|
+
block_type_modifier = false
|
46
68
|
|
47
69
|
method_name = node.method_name
|
48
70
|
return unless allowed_method_names.include? method_name
|
49
|
-
|
50
|
-
|
71
|
+
return if node.body.nil?
|
72
|
+
|
73
|
+
table_modifier =
|
74
|
+
if node.body.children[0] == nil
|
75
|
+
block_type_modifier = false
|
76
|
+
# if table modifier is a one-liner method call
|
77
|
+
node.body.children[1]
|
78
|
+
elsif node.body.children[0].block_type?
|
79
|
+
block_type_modifier = true
|
80
|
+
# if table modifier is in a block
|
81
|
+
node.body.children[0].method_name
|
82
|
+
end
|
51
83
|
return unless allowed_table_modifiers.include? table_modifier
|
52
84
|
|
53
|
-
|
54
|
-
@table_name = raw_table_name.singularize.camelize
|
85
|
+
@table_modifier = table_modifier
|
55
86
|
|
56
|
-
|
57
|
-
|
87
|
+
# Get the name of the table being modified
|
88
|
+
if block_type_modifier
|
89
|
+
raw_table_name =
|
90
|
+
node.body.children[0].children[0].children[2].value.to_s
|
91
|
+
@table_name = raw_table_name.singularize.camelize
|
92
|
+
|
93
|
+
node.body.children[0].children[2].each_child_node do |child|
|
94
|
+
process_migration_field(child)
|
95
|
+
end
|
96
|
+
else
|
97
|
+
raw_table_name = node.body.children[2].value.to_s
|
98
|
+
@table_name = raw_table_name.singularize.camelize
|
99
|
+
|
100
|
+
field_name = node.body.children[3]&.value
|
101
|
+
field_type = node.body.children[4]&.value
|
102
|
+
@fields.append(DTO::Field.new(field_name, field_type))
|
58
103
|
end
|
59
104
|
end
|
60
105
|
|
61
106
|
def process_migration_field(node)
|
62
107
|
return unless node.send_type?
|
63
108
|
|
64
|
-
if node.children
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
end
|
69
|
-
elsif node.children.count >= 3
|
70
|
-
type = node.children[1]
|
71
|
-
value = node.children[2].value
|
72
|
-
@fields.append(DTO::Field.new(value, type))
|
109
|
+
if node.children[1] == :timestamps
|
110
|
+
@fields.append(DTO::Field.new(:created_at, :datetime))
|
111
|
+
@fields.append(DTO::Field.new(:updated_at, :datetime))
|
112
|
+
return
|
73
113
|
end
|
74
|
-
end
|
75
114
|
|
76
|
-
|
77
|
-
|
115
|
+
type = node.children[1]
|
116
|
+
value = node.children[2].value unless node.children[2].hash_type?
|
117
|
+
@fields.append(DTO::Field.new(value, type))
|
78
118
|
end
|
79
119
|
end
|
80
120
|
end
|
@@ -0,0 +1,79 @@
|
|
1
|
+
require_relative "../dto/route"
|
2
|
+
|
3
|
+
module Trains
|
4
|
+
module Visitor
|
5
|
+
class Route < Base
|
6
|
+
def_node_matcher :route_parent?, <<~PATTERN
|
7
|
+
(block (send (send (send
|
8
|
+
(const nil? :Rails) :application) :routes) :draw)
|
9
|
+
...)
|
10
|
+
PATTERN
|
11
|
+
|
12
|
+
def_node_matcher :route_method?, <<~PATTERN
|
13
|
+
(send nil? %1 ...)
|
14
|
+
PATTERN
|
15
|
+
|
16
|
+
ALLOWED_VERBS = %i[get put post update delete resources scope]
|
17
|
+
|
18
|
+
def initialize
|
19
|
+
@route_list = []
|
20
|
+
end
|
21
|
+
|
22
|
+
def result
|
23
|
+
@route_list
|
24
|
+
end
|
25
|
+
|
26
|
+
def on_block(node)
|
27
|
+
return unless route_parent?(node)
|
28
|
+
return if node.body.nil?
|
29
|
+
|
30
|
+
node.body.each_child_node do |child|
|
31
|
+
ALLOWED_VERBS.each do |verb|
|
32
|
+
if route_method?(child, verb)
|
33
|
+
@route_list << parse_route(child, verb)
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
private
|
40
|
+
|
41
|
+
def parse_route(node, verb)
|
42
|
+
method = verb
|
43
|
+
param =
|
44
|
+
case node.arguments[0].type
|
45
|
+
when :sym, :str
|
46
|
+
node.arguments[0].value
|
47
|
+
end
|
48
|
+
options = parse_hash(node.arguments[1])
|
49
|
+
DTO::Route.new(method: method, param: param, options: options)
|
50
|
+
end
|
51
|
+
|
52
|
+
def parse_hash(node)
|
53
|
+
options = {}
|
54
|
+
return options unless node.type == :hash
|
55
|
+
|
56
|
+
node.each_pair { |key, value| options[key.value] = parse_value(value) }
|
57
|
+
rescue StandardError => e
|
58
|
+
puts node.parent
|
59
|
+
ensure
|
60
|
+
return options
|
61
|
+
end
|
62
|
+
|
63
|
+
def parse_value(node)
|
64
|
+
case node.type
|
65
|
+
when :hash
|
66
|
+
parse_hash(node)
|
67
|
+
when :array
|
68
|
+
node.values.map { |value| parse_value(value) }
|
69
|
+
when :send
|
70
|
+
if node.method_name == :redirect
|
71
|
+
{ redirect: node.arguments.first.value }
|
72
|
+
end
|
73
|
+
else
|
74
|
+
node.value
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
@@ -0,0 +1,88 @@
|
|
1
|
+
module Trains
|
2
|
+
module Visitor
|
3
|
+
# Visitor that parses DB migration and associates them with Rails models
|
4
|
+
class Schema < Base
|
5
|
+
def_node_matcher :versioned_schema?, <<~PATTERN
|
6
|
+
(block
|
7
|
+
(send (send (const (const nil? :ActiveRecord) :Schema) :[] (float ...)) :define ...)
|
8
|
+
...)
|
9
|
+
PATTERN
|
10
|
+
|
11
|
+
def_node_matcher :unversioned_schema?, <<~PATTERN
|
12
|
+
(block
|
13
|
+
(send (const (const nil? :ActiveRecord) :Schema) :define ...)
|
14
|
+
...)
|
15
|
+
PATTERN
|
16
|
+
|
17
|
+
def initialize
|
18
|
+
@models = []
|
19
|
+
@columns = []
|
20
|
+
@is_versioned = false
|
21
|
+
end
|
22
|
+
|
23
|
+
def on_block(node)
|
24
|
+
is_schema = versioned_schema?(node) || unversioned_schema?(node)
|
25
|
+
return unless is_schema
|
26
|
+
|
27
|
+
process_schema_body(node)
|
28
|
+
end
|
29
|
+
|
30
|
+
def result
|
31
|
+
@models
|
32
|
+
end
|
33
|
+
|
34
|
+
private
|
35
|
+
|
36
|
+
def each_table(ast)
|
37
|
+
case ast.body.type
|
38
|
+
when :begin
|
39
|
+
ast.body.children.each do |node|
|
40
|
+
next unless node.block_type? && node.method?(:create_table)
|
41
|
+
|
42
|
+
yield(node)
|
43
|
+
end
|
44
|
+
else
|
45
|
+
yield ast.body
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
def each_content(node, &block)
|
50
|
+
return enum_for(__method__, node) unless block
|
51
|
+
|
52
|
+
case node.body&.type
|
53
|
+
when :begin
|
54
|
+
node.body.children.each(&block)
|
55
|
+
else
|
56
|
+
yield(node.body)
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
def build_columns(node)
|
61
|
+
each_content(node)
|
62
|
+
.map do |child|
|
63
|
+
next unless child&.send_type?
|
64
|
+
next if child.method?(:index)
|
65
|
+
|
66
|
+
DTO::Field.new(
|
67
|
+
name: child.first_argument.str_content,
|
68
|
+
type: child.method_name
|
69
|
+
)
|
70
|
+
end
|
71
|
+
.compact
|
72
|
+
end
|
73
|
+
|
74
|
+
def process_schema_body(node)
|
75
|
+
each_table(node) do |table|
|
76
|
+
@table_name = table.send_node.first_argument.value
|
77
|
+
@columns = build_columns(table)
|
78
|
+
|
79
|
+
@models << DTO::Model.new(
|
80
|
+
name: @table_name.singularize.camelize,
|
81
|
+
fields: @columns,
|
82
|
+
version: nil
|
83
|
+
)
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
data/trains.gemspec
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: trains
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.5
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Syed Faraaz Ahmad
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2023-
|
11
|
+
date: 2023-04-21 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activesupport
|
@@ -76,7 +76,6 @@ files:
|
|
76
76
|
- ".deepsource.toml"
|
77
77
|
- ".rspec"
|
78
78
|
- ".rubocop.yml"
|
79
|
-
- ".ruby-version"
|
80
79
|
- CHANGELOG.md
|
81
80
|
- CODE_OF_CONDUCT.md
|
82
81
|
- Gemfile
|
@@ -89,10 +88,12 @@ files:
|
|
89
88
|
- lib/trains/dto/controller.rb
|
90
89
|
- lib/trains/dto/field.rb
|
91
90
|
- lib/trains/dto/method.rb
|
91
|
+
- lib/trains/dto/migration.rb
|
92
92
|
- lib/trains/dto/model.rb
|
93
|
+
- lib/trains/dto/route.rb
|
93
94
|
- lib/trains/scanner.rb
|
94
|
-
- lib/trains/utils/ast_store.rb
|
95
95
|
- lib/trains/utils/logger.rb
|
96
|
+
- lib/trains/utils/migration_tailor.rb
|
96
97
|
- lib/trains/utils/rails_dir.rb
|
97
98
|
- lib/trains/utils/result.rb
|
98
99
|
- lib/trains/version.rb
|
@@ -100,6 +101,8 @@ files:
|
|
100
101
|
- lib/trains/visitor/controller.rb
|
101
102
|
- lib/trains/visitor/helper.rb
|
102
103
|
- lib/trains/visitor/migration.rb
|
104
|
+
- lib/trains/visitor/route.rb
|
105
|
+
- lib/trains/visitor/schema.rb
|
103
106
|
- trains.gemspec
|
104
107
|
homepage: https://github.com/faraazahmad/trains
|
105
108
|
licenses:
|
@@ -108,6 +111,7 @@ metadata:
|
|
108
111
|
homepage_uri: https://github.com/faraazahmad/trains
|
109
112
|
source_code_uri: https://github.com/faraazahmad/trains
|
110
113
|
changelog_uri: https://github.com/faraazahmad/trains/blob/master/CHANGELOG.md
|
114
|
+
rubygems_mfa_required: 'true'
|
111
115
|
post_install_message:
|
112
116
|
rdoc_options: []
|
113
117
|
require_paths:
|
data/.ruby-version
DELETED
@@ -1 +0,0 @@
|
|
1
|
-
3.0.0
|
@@ -1,25 +0,0 @@
|
|
1
|
-
require 'singleton'
|
2
|
-
|
3
|
-
module Trains
|
4
|
-
module Utils
|
5
|
-
class ASTStore
|
6
|
-
include Singleton
|
7
|
-
|
8
|
-
def initialize
|
9
|
-
@store = {}
|
10
|
-
end
|
11
|
-
|
12
|
-
def set(file_path, ast_object)
|
13
|
-
@store[file_path] = ast_object
|
14
|
-
end
|
15
|
-
|
16
|
-
def get(file_path)
|
17
|
-
unless @store.key? file_path
|
18
|
-
set(file_path,
|
19
|
-
RuboCop::AST::ProcessedSource.from_file(file_path, RUBY_VERSION.to_f))
|
20
|
-
end
|
21
|
-
@store[file_path]
|
22
|
-
end
|
23
|
-
end
|
24
|
-
end
|
25
|
-
end
|