trains 0.0.2 → 0.0.4
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 -10
- data/README.md +15 -0
- data/Rakefile +1 -5
- data/lib/trains/dto/app.rb +2 -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/model.rb +1 -1
- data/lib/trains/dto/route.rb +5 -0
- data/lib/trains/scanner.rb +58 -47
- 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 +47 -23
- data/lib/trains/visitor/route.rb +78 -0
- data/lib/trains/visitor/schema.rb +88 -0
- data/trains.gemspec +40 -0
- metadata +7 -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: 7edd36bbbc3349d7b32a1719cb5e80038953853e18d5d927ab2e713b621f356f
|
4
|
+
data.tar.gz: 97c661e09da45575d54fca505ab1a967b941c0bca806e29a1155739ca387f1a4
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: f560a4e5f4adf6fb45238f4deb1fcc3bb4dbcbf8a7a5b5dec7c74ff27224dd6a7afa00fe4702c1fa939626f8200a88b5d0383615967a3cd46af0d64cb6ae4b17
|
7
|
+
data.tar.gz: e3ae6062e8aea943fdb3d030c962edfdb23b631d60f33d17a1a9a041ad05fdb78424d427668dd76e015aa22564a0d2e18ee2dba2c9138445a9870040cfb75209
|
data/.deepsource.toml
CHANGED
data/Gemfile.lock
CHANGED
@@ -1,12 +1,3 @@
|
|
1
|
-
PATH
|
2
|
-
remote: .
|
3
|
-
specs:
|
4
|
-
trains (0.0.1)
|
5
|
-
activesupport (~> 7.0)
|
6
|
-
parallel (~> 1.22)
|
7
|
-
rubocop-ast (~> 1.16)
|
8
|
-
zeitwerk (~> 2.5)
|
9
|
-
|
10
1
|
GEM
|
11
2
|
remote: https://rubygems.org/
|
12
3
|
specs:
|
@@ -54,6 +45,7 @@ GEM
|
|
54
45
|
zeitwerk (2.5.4)
|
55
46
|
|
56
47
|
PLATFORMS
|
48
|
+
x86_64-darwin-21
|
57
49
|
x86_64-linux
|
58
50
|
|
59
51
|
DEPENDENCIES
|
@@ -63,7 +55,6 @@ DEPENDENCIES
|
|
63
55
|
rspec (~> 3.12)
|
64
56
|
rubocop-ast
|
65
57
|
ruby-lsp (~> 0.3.7)
|
66
|
-
trains!
|
67
58
|
zeitwerk (~> 2.5)
|
68
59
|
|
69
60
|
BUNDLED WITH
|
data/README.md
CHANGED
@@ -2,6 +2,21 @@
|
|
2
2
|
|
3
3
|
A gem that statically analyses your Rails app and extracts information about its structure.
|
4
4
|
|
5
|
+
[](https://deepsource.io/gh/faraazahmad/trains/?ref=repository-badge)
|
6
|
+
[](https://deepsource.io/gh/faraazahmad/trains/?ref=repository-badge)
|
7
|
+
|
8
|
+
## Index
|
9
|
+
|
10
|
+
1. [Installation](#installation)
|
11
|
+
2. [Usage](#usage)
|
12
|
+
3. [Features](#features)
|
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
|
+
4. [Development](#development)
|
16
|
+
5. [Contributing](#contributing)
|
17
|
+
6. [License](#license)
|
18
|
+
7. [Code of conduct](#code-of-conduct)
|
19
|
+
|
5
20
|
## Installation
|
6
21
|
|
7
22
|
Install the gem and add it to the application's Gemfile by executing:
|
data/Rakefile
CHANGED
data/lib/trains/dto/app.rb
CHANGED
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,17 @@ 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
8
|
@nodes = []
|
11
9
|
@models = []
|
12
10
|
@controllers = []
|
13
11
|
@helpers = []
|
14
|
-
@dir =
|
15
|
-
|
12
|
+
@dir = File.expand_path(folder)
|
13
|
+
@options = options
|
16
14
|
end
|
17
15
|
|
18
16
|
def scan
|
19
|
-
# @nodes = get_tree(@dir)
|
20
|
-
|
21
17
|
# Check if @folder is a Rails directory before beginning analysis
|
22
18
|
rails_dir_result = RailsDir.check @dir
|
23
19
|
case rails_dir_result
|
@@ -27,15 +23,17 @@ module Trains
|
|
27
23
|
return(
|
28
24
|
Result.new(
|
29
25
|
data: nil,
|
30
|
-
error: ArgumentError.new(
|
26
|
+
error: ArgumentError.new("Not a Rails directory")
|
31
27
|
)
|
32
28
|
)
|
33
29
|
end
|
34
30
|
|
35
|
-
@
|
36
|
-
@
|
37
|
-
|
38
|
-
|
31
|
+
@models = get_models.to_set unless @options[:models] == false
|
32
|
+
@migrations = get_migrations.to_set unless @options[:migrations] == false
|
33
|
+
@controllers = get_controllers.to_set unless @options[:controllers] ==
|
34
|
+
false
|
35
|
+
@routes = get_routes.to_set unless @options[:routes] == false
|
36
|
+
# TODO: @helpers = get_helpers
|
39
37
|
|
40
38
|
# Create instance of Trains::DTO::App
|
41
39
|
DTO::App.new(
|
@@ -43,51 +41,61 @@ module Trains
|
|
43
41
|
controllers: @controllers,
|
44
42
|
models: @models,
|
45
43
|
migrations: @migrations,
|
46
|
-
helpers: @helpers
|
44
|
+
helpers: @helpers,
|
45
|
+
routes: @routes
|
47
46
|
)
|
48
47
|
end
|
49
48
|
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
49
|
+
private
|
50
|
+
|
51
|
+
def get_routes
|
52
|
+
route_file = [File.join(@dir, "config", "routes.rb")]
|
53
|
+
|
54
|
+
routes_results = parse_util(route_file, Visitor::Route)
|
55
|
+
routes_results
|
56
|
+
.select { |result| result.error.nil? }
|
57
|
+
.map { |result| result.data }
|
58
|
+
.flatten
|
59
59
|
end
|
60
60
|
|
61
|
-
def get_models
|
61
|
+
def get_models
|
62
|
+
schema_file = [File.join(@dir, "db", "schema.rb")]
|
63
|
+
return unless File.exist?(schema_file.first)
|
62
64
|
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
node[:path].end_with? '_helper.rb'
|
70
|
-
end
|
65
|
+
models_results = parse_util(schema_file, Visitor::Schema)
|
66
|
+
models_results
|
67
|
+
.select { |result| result.error.nil? }
|
68
|
+
.map { |result| result.data }
|
69
|
+
.flatten
|
70
|
+
end
|
71
71
|
|
72
|
-
|
72
|
+
def get_helpers
|
73
73
|
end
|
74
74
|
|
75
|
-
def get_gemfile
|
75
|
+
def get_gemfile
|
76
|
+
end
|
76
77
|
|
77
78
|
def get_controllers
|
78
79
|
controllers =
|
79
|
-
Dir.glob(File.join(@dir,
|
80
|
+
Dir.glob(File.join(@dir, "app", "controllers", "**", "*_controller.rb"))
|
80
81
|
|
81
|
-
parse_util(controllers, Visitor::Controller)
|
82
|
+
controller_results = parse_util(controllers, Visitor::Controller)
|
83
|
+
controller_results
|
84
|
+
.select { |result| result.error.nil? }
|
85
|
+
.map { |result| result.data }
|
86
|
+
.flatten
|
82
87
|
end
|
83
88
|
|
84
89
|
def get_migrations
|
85
|
-
migrations = Dir.glob(File.join(@dir,
|
90
|
+
migrations = Dir.glob(File.join(@dir, "db", "migrate", "**", "*.rb"))
|
86
91
|
|
87
|
-
parse_util(migrations, Visitor::Migration)
|
92
|
+
migration_results = parse_util(migrations, Visitor::Migration)
|
93
|
+
migration_results
|
94
|
+
.select { |result| result.error.nil? }
|
95
|
+
.map { |result| result.data }
|
88
96
|
end
|
89
97
|
|
90
|
-
def parse_util(file_nodes,
|
98
|
+
def parse_util(file_nodes, visitor_class)
|
91
99
|
unless file_nodes.class.include? Enumerable
|
92
100
|
return(
|
93
101
|
Result.new(
|
@@ -100,27 +108,30 @@ module Trains
|
|
100
108
|
)
|
101
109
|
end
|
102
110
|
|
103
|
-
|
104
|
-
|
111
|
+
Parallel.map(file_nodes) do |node|
|
112
|
+
begin
|
105
113
|
processed_source =
|
106
114
|
RuboCop::AST::ProcessedSource.from_file(node, RUBY_VERSION.to_f)
|
107
|
-
visitor =
|
115
|
+
visitor = visitor_class.new
|
108
116
|
visitor.process(processed_source.ast)
|
109
|
-
|
117
|
+
|
118
|
+
Result.new(data: visitor.result, error: nil)
|
119
|
+
rescue StandardError => e
|
120
|
+
puts "An error occurred while parsing #{node}. Use debug option to view backtrace. Skipping file..."
|
121
|
+
puts e.backtrace if @options[:debug]
|
122
|
+
|
123
|
+
Result.new(data: nil, error: e)
|
110
124
|
end
|
111
|
-
rescue StandardError => e
|
112
|
-
puts e.backtrace
|
113
|
-
Result.new(data: nil, error: e)
|
114
125
|
end
|
115
126
|
end
|
116
127
|
|
117
|
-
def get_tree(node, prefix =
|
128
|
+
def get_tree(node, prefix = "")
|
118
129
|
path = File.join(prefix, node)
|
119
130
|
obj = { path: nil }
|
120
131
|
|
121
132
|
# puts "DEBUG: #{path} #{ FastIgnore.new.allowed? path }"
|
122
133
|
if path != @dir.to_path &&
|
123
|
-
|
134
|
+
FastIgnore.new.allowed?(path, directory: false) == false
|
124
135
|
return nil
|
125
136
|
end
|
126
137
|
|
@@ -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,10 +1,10 @@
|
|
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
|
@@ -19,7 +19,7 @@ module Trains
|
|
19
19
|
end
|
20
20
|
|
21
21
|
def on_class(node)
|
22
|
-
unless node.parent_class.source.include?
|
22
|
+
unless node.parent_class.source.include? "ActiveRecord::Migration"
|
23
23
|
return
|
24
24
|
end
|
25
25
|
|
@@ -29,6 +29,12 @@ module Trains
|
|
29
29
|
process_node(node.body)
|
30
30
|
end
|
31
31
|
|
32
|
+
def result
|
33
|
+
DTO::Model.new(@table_name, @fields, @migration_version)
|
34
|
+
end
|
35
|
+
|
36
|
+
private
|
37
|
+
|
32
38
|
def extract_version(class_const)
|
33
39
|
match = class_const.match(/\d+.\d+/)
|
34
40
|
return nil if match.nil?
|
@@ -37,44 +43,62 @@ module Trains
|
|
37
43
|
end
|
38
44
|
|
39
45
|
def process_node(node)
|
40
|
-
|
46
|
+
return unless node.def_type?
|
47
|
+
|
48
|
+
process_def_node(node)
|
41
49
|
end
|
42
50
|
|
43
51
|
def process_def_node(node)
|
44
52
|
allowed_method_names = %i[change up down]
|
45
|
-
allowed_table_modifiers = %i[create_table update_column]
|
53
|
+
allowed_table_modifiers = %i[create_table update_column add_column]
|
54
|
+
block_type_modifier = false
|
46
55
|
|
47
56
|
method_name = node.method_name
|
48
57
|
return unless allowed_method_names.include? method_name
|
49
58
|
|
50
|
-
table_modifier =
|
59
|
+
table_modifier =
|
60
|
+
if node.body.children[0] == nil
|
61
|
+
block_type_modifier = false
|
62
|
+
# if table modifier is a one-liner method call
|
63
|
+
node.body.children[1]
|
64
|
+
elsif node.body.children[0].block_type?
|
65
|
+
block_type_modifier = true
|
66
|
+
# if table modifier is in a block
|
67
|
+
node.body.children[0].method_name
|
68
|
+
end
|
51
69
|
return unless allowed_table_modifiers.include? table_modifier
|
52
70
|
|
53
|
-
|
54
|
-
|
71
|
+
# Get the name of the table being modified
|
72
|
+
if block_type_modifier
|
73
|
+
raw_table_name =
|
74
|
+
node.body.children[0].children[0].children[2].value.to_s
|
75
|
+
@table_name = raw_table_name.singularize.camelize
|
55
76
|
|
56
|
-
|
57
|
-
|
77
|
+
node.body.children[0].children[2].each_child_node do |child|
|
78
|
+
process_migration_field(child)
|
79
|
+
end
|
80
|
+
else
|
81
|
+
raw_table_name = node.body.children[2].value.to_s
|
82
|
+
@table_name = raw_table_name.singularize.camelize
|
83
|
+
|
84
|
+
field_name = node.body.children[3].value
|
85
|
+
field_type = node.body.children[4].value
|
86
|
+
@fields.append(DTO::Field.new(field_name, field_type))
|
58
87
|
end
|
59
88
|
end
|
60
89
|
|
61
90
|
def process_migration_field(node)
|
62
91
|
return unless node.send_type?
|
63
92
|
|
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))
|
93
|
+
if node.children[1] == :timestamps
|
94
|
+
@fields.append(DTO::Field.new(:created_at, :datetime))
|
95
|
+
@fields.append(DTO::Field.new(:updated_at, :datetime))
|
96
|
+
return
|
73
97
|
end
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
DTO::
|
98
|
+
|
99
|
+
type = node.children[1]
|
100
|
+
value = node.children[2].value unless node.children[2].hash_type?
|
101
|
+
@fields.append(DTO::Field.new(value, type))
|
78
102
|
end
|
79
103
|
end
|
80
104
|
end
|
@@ -0,0 +1,78 @@
|
|
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
|
+
|
29
|
+
node.body.each_child_node do |child|
|
30
|
+
ALLOWED_VERBS.each do |verb|
|
31
|
+
if route_method?(child, verb)
|
32
|
+
@route_list << parse_route(child, verb)
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
private
|
39
|
+
|
40
|
+
def parse_route(node, verb)
|
41
|
+
method = verb
|
42
|
+
param =
|
43
|
+
case node.arguments[0].type
|
44
|
+
when :sym, :str
|
45
|
+
node.arguments[0].value
|
46
|
+
end
|
47
|
+
options = parse_hash(node.arguments[1])
|
48
|
+
DTO::Route.new(method: method, param: param, options: options)
|
49
|
+
end
|
50
|
+
|
51
|
+
def parse_hash(node)
|
52
|
+
options = {}
|
53
|
+
return options unless node.type == :hash
|
54
|
+
|
55
|
+
node.each_pair { |key, value| options[key.value] = parse_value(value) }
|
56
|
+
rescue StandardError => e
|
57
|
+
puts node.parent
|
58
|
+
ensure
|
59
|
+
return options
|
60
|
+
end
|
61
|
+
|
62
|
+
def parse_value(node)
|
63
|
+
case node.type
|
64
|
+
when :hash
|
65
|
+
parse_hash(node)
|
66
|
+
when :array
|
67
|
+
node.values.map { |value| parse_value(value) }
|
68
|
+
when :send
|
69
|
+
if node.method_name == :redirect
|
70
|
+
{ redirect: node.arguments.first.value }
|
71
|
+
end
|
72
|
+
else
|
73
|
+
node.value
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
78
|
+
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
ADDED
@@ -0,0 +1,40 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative 'lib/trains/version'
|
4
|
+
|
5
|
+
Gem::Specification.new do |spec|
|
6
|
+
spec.name = 'trains'
|
7
|
+
spec.version = Trains::VERSION
|
8
|
+
spec.authors = ['Syed Faraaz Ahmad']
|
9
|
+
spec.email = ['faraaz98@live.com']
|
10
|
+
|
11
|
+
spec.summary =
|
12
|
+
'Collect metadata about your Rails app by statically analyzing it'
|
13
|
+
spec.homepage = 'https://github.com/faraazahmad/trains'
|
14
|
+
spec.license = 'MIT'
|
15
|
+
spec.required_ruby_version = '>= 2.6.0'
|
16
|
+
|
17
|
+
spec.metadata['homepage_uri'] = spec.homepage
|
18
|
+
spec.metadata['source_code_uri'] = spec.homepage
|
19
|
+
spec.metadata['changelog_uri'] = "https://github.com/faraazahmad/trains/blob/master/CHANGELOG.md"
|
20
|
+
|
21
|
+
# Specify which files should be added to the gem when it is released.
|
22
|
+
# The `git ls-files -z` loads the files in the RubyGem that have been added into git.
|
23
|
+
spec.files =
|
24
|
+
Dir.chdir(__dir__) do
|
25
|
+
`git ls-files -z`.split("\x0")
|
26
|
+
.reject do |f|
|
27
|
+
(f == __FILE__) ||
|
28
|
+
f.match(
|
29
|
+
%r{\A(?:(?:bin|test|spec|features)/|\.(?:git|travis|circleci)|appveyor)}
|
30
|
+
)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
spec.require_paths = ['lib']
|
34
|
+
|
35
|
+
spec.add_dependency 'activesupport', '~> 7.0'
|
36
|
+
spec.add_dependency 'parallel', '~> 1.22'
|
37
|
+
spec.add_dependency 'rubocop-ast', '~> 1.16'
|
38
|
+
spec.add_dependency 'zeitwerk', '~> 2.5'
|
39
|
+
spec.metadata['rubygems_mfa_required'] = 'true'
|
40
|
+
end
|
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.4
|
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-18 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
|
@@ -90,8 +89,8 @@ files:
|
|
90
89
|
- lib/trains/dto/field.rb
|
91
90
|
- lib/trains/dto/method.rb
|
92
91
|
- lib/trains/dto/model.rb
|
92
|
+
- lib/trains/dto/route.rb
|
93
93
|
- lib/trains/scanner.rb
|
94
|
-
- lib/trains/utils/ast_store.rb
|
95
94
|
- lib/trains/utils/logger.rb
|
96
95
|
- lib/trains/utils/rails_dir.rb
|
97
96
|
- lib/trains/utils/result.rb
|
@@ -100,6 +99,9 @@ files:
|
|
100
99
|
- lib/trains/visitor/controller.rb
|
101
100
|
- lib/trains/visitor/helper.rb
|
102
101
|
- lib/trains/visitor/migration.rb
|
102
|
+
- lib/trains/visitor/route.rb
|
103
|
+
- lib/trains/visitor/schema.rb
|
104
|
+
- trains.gemspec
|
103
105
|
homepage: https://github.com/faraazahmad/trains
|
104
106
|
licenses:
|
105
107
|
- MIT
|
@@ -107,6 +109,7 @@ metadata:
|
|
107
109
|
homepage_uri: https://github.com/faraazahmad/trains
|
108
110
|
source_code_uri: https://github.com/faraazahmad/trains
|
109
111
|
changelog_uri: https://github.com/faraazahmad/trains/blob/master/CHANGELOG.md
|
112
|
+
rubygems_mfa_required: 'true'
|
110
113
|
post_install_message:
|
111
114
|
rdoc_options: []
|
112
115
|
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
|