trains 0.0.4 → 0.0.6

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: 7edd36bbbc3349d7b32a1719cb5e80038953853e18d5d927ab2e713b621f356f
4
- data.tar.gz: 97c661e09da45575d54fca505ab1a967b941c0bca806e29a1155739ca387f1a4
3
+ metadata.gz: 2b1af8d7a53681c21f561432534383da1d83bf4615bc8e85bdfa6c8ce7b210aa
4
+ data.tar.gz: aa4798c33b271c590c5b966078eb7a6f9fd603a069842ce454cadb14de8cc08d
5
5
  SHA512:
6
- metadata.gz: f560a4e5f4adf6fb45238f4deb1fcc3bb4dbcbf8a7a5b5dec7c74ff27224dd6a7afa00fe4702c1fa939626f8200a88b5d0383615967a3cd46af0d64cb6ae4b17
7
- data.tar.gz: e3ae6062e8aea943fdb3d030c962edfdb23b631d60f33d17a1a9a041ad05fdb78424d427668dd76e015aa22564a0d2e18ee2dba2c9138445a9870040cfb75209
6
+ metadata.gz: f6faddb254499e05f02a13def98dc7ab1f7df69d2ff694617cbd24410e2cb23266c7fd4c067cb2e4e2dd4fd7e7552f7d5ba79c995f2f951ff452150b8d800222
7
+ data.tar.gz: 95748dab8e2f16aed324d9c55a8d14c0eeec265361d49794bf7d81f21f6b42b8a25ba7426f719fea673efc982864ff49c745d3cdeb6888780b63a3bdf81038b9
@@ -1,6 +1,8 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Trains
2
4
  module DTO
3
5
  App =
4
- Data.define(:name, :controllers, :models, :migrations, :helpers, :routes)
6
+ Data.define(:name, :controllers, :models, :helpers, :routes)
5
7
  end
6
8
  end
@@ -0,0 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Trains
4
+ module DTO
5
+ Callback = Data.define(:method, :arguments)
6
+ end
7
+ end
@@ -2,6 +2,6 @@ require "set"
2
2
 
3
3
  module Trains
4
4
  module DTO
5
- Controller = Data.define(:name, :method_list)
5
+ Controller = Data.define(:name, :method_list, :callbacks)
6
6
  end
7
7
  end
@@ -0,0 +1,5 @@
1
+ module Trains
2
+ module DTO
3
+ Migration = Data.define(:table_name, :modifier, :fields, :version)
4
+ end
5
+ end
@@ -5,10 +5,9 @@ module Trains
5
5
 
6
6
  def initialize(folder, options = {})
7
7
  @root_folder = folder
8
- @nodes = []
9
- @models = []
10
- @controllers = []
11
- @helpers = []
8
+ @models = {}
9
+ @controllers = {}
10
+ @helpers = {}
12
11
  @dir = File.expand_path(folder)
13
12
  @options = options
14
13
  end
@@ -23,15 +22,13 @@ module Trains
23
22
  return(
24
23
  Result.new(
25
24
  data: nil,
26
- error: ArgumentError.new("Not a Rails directory")
25
+ error: ArgumentError.new('Not a Rails directory')
27
26
  )
28
27
  )
29
28
  end
30
29
 
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
30
+ @models = generate_models unless @options[:models] == false
31
+ @controllers = get_controllers unless @options[:controllers] == false
35
32
  @routes = get_routes.to_set unless @options[:routes] == false
36
33
  # TODO: @helpers = get_helpers
37
34
 
@@ -40,7 +37,6 @@ module Trains
40
37
  name: nil,
41
38
  controllers: @controllers,
42
39
  models: @models,
43
- migrations: @migrations,
44
40
  helpers: @helpers,
45
41
  routes: @routes
46
42
  )
@@ -48,51 +44,65 @@ module Trains
48
44
 
49
45
  private
50
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)
54
+ end
55
+
51
56
  def get_routes
52
- route_file = [File.join(@dir, "config", "routes.rb")]
57
+ route_file = [File.join(@dir, 'config', 'routes.rb')]
53
58
 
54
59
  routes_results = parse_util(route_file, Visitor::Route)
55
60
  routes_results
56
61
  .select { |result| result.error.nil? }
57
- .map { |result| result.data }
62
+ .map(&:data)
58
63
  .flatten
59
64
  end
60
65
 
61
66
  def get_models
62
- schema_file = [File.join(@dir, "db", "schema.rb")]
63
- return unless File.exist?(schema_file.first)
64
-
67
+ result_hash = {}
68
+ schema_file = [File.join(@dir, 'db', 'schema.rb')]
65
69
  models_results = parse_util(schema_file, Visitor::Schema)
70
+
66
71
  models_results
67
72
  .select { |result| result.error.nil? }
68
- .map { |result| result.data }
73
+ .map(&:data)
69
74
  .flatten
70
- end
75
+ .each { |model| result_hash[model.name] = model }
71
76
 
72
- def get_helpers
77
+ result_hash
73
78
  end
74
79
 
75
- def get_gemfile
76
- end
80
+ def get_helpers; end
81
+
82
+ def get_gemfile; end
77
83
 
78
84
  def get_controllers
85
+ result_hash = {}
79
86
  controllers =
80
- Dir.glob(File.join(@dir, "app", "controllers", "**", "*_controller.rb"))
81
-
87
+ Dir.glob(File.join(@dir, 'app', 'controllers', '**', '*_controller.rb'))
82
88
  controller_results = parse_util(controllers, Visitor::Controller)
89
+
83
90
  controller_results
84
91
  .select { |result| result.error.nil? }
85
- .map { |result| result.data }
92
+ .map(&:data)
86
93
  .flatten
94
+ .each { |controller| result_hash[controller.name] = controller }
95
+
96
+ result_hash
87
97
  end
88
98
 
89
99
  def get_migrations
90
- migrations = Dir.glob(File.join(@dir, "db", "migrate", "**", "*.rb"))
91
-
100
+ migrations = Dir.glob(File.join(@dir, 'db', 'migrate', '**', '*.rb'))
92
101
  migration_results = parse_util(migrations, Visitor::Migration)
102
+
93
103
  migration_results
94
104
  .select { |result| result.error.nil? }
95
- .map { |result| result.data }
105
+ .map(&:data)
96
106
  end
97
107
 
98
108
  def parse_util(file_nodes, visitor_class)
@@ -109,42 +119,21 @@ module Trains
109
119
  end
110
120
 
111
121
  Parallel.map(file_nodes) do |node|
112
- begin
113
- processed_source =
114
- RuboCop::AST::ProcessedSource.from_file(node, RUBY_VERSION.to_f)
115
- visitor = visitor_class.new
116
- visitor.process(processed_source.ast)
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)
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)
126
+
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
124
133
  end
125
- end
126
- end
127
134
 
128
- def get_tree(node, prefix = "")
129
- path = File.join(prefix, node)
130
- obj = { path: nil }
131
-
132
- # puts "DEBUG: #{path} #{ FastIgnore.new.allowed? path }"
133
- if path != @dir.to_path &&
134
- FastIgnore.new.allowed?(path, directory: false) == false
135
- return nil
135
+ Result.new(data: nil, error: e)
136
136
  end
137
-
138
- if Dir.exist? path
139
- children = []
140
- Dir.each_child path do |child|
141
- child_node = get_tree(child, path)
142
- children.append(child_node) unless child_node.nil?
143
- end
144
- obj[:children] = children
145
- end
146
-
147
- obj
148
137
  end
149
138
  end
150
139
  end
@@ -0,0 +1,43 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Trains
4
+ module Utils
5
+ # utility module to deal with parsing of arguments
6
+ module Args
7
+ def parse_args(node)
8
+ case node.type
9
+ when :hash
10
+ parse_hash(node)
11
+ else
12
+ parse_value(node)
13
+ end
14
+ end
15
+
16
+ def parse_hash(node)
17
+ options = {}
18
+ return options unless node.type == :hash
19
+
20
+ node.each_pair { |key, value| options[key.value] = parse_value(value) }
21
+ rescue StandardError => e
22
+ puts node.parent
23
+ ensure
24
+ return options
25
+ end
26
+
27
+ def parse_value(node)
28
+ case node.type
29
+ when :hash
30
+ parse_hash(node)
31
+ when :array
32
+ node.values.map { |value| parse_value(value) }
33
+ when :send
34
+ if node.method_name == :redirect
35
+ { redirect: node.arguments.first.value }
36
+ end
37
+ else
38
+ node.value
39
+ end
40
+ end
41
+ end
42
+ end
43
+ 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
@@ -1,3 +1,3 @@
1
1
  module Trains
2
- VERSION = '0.0.4'
2
+ VERSION = '0.0.6'
3
3
  end
@@ -1,14 +1,24 @@
1
- require_relative "../dto/controller"
2
- require_relative "../dto/method"
1
+ # frozen_string_literal: true
2
+
3
+ require_relative '../dto/callback'
4
+ require_relative '../dto/controller'
5
+ require_relative '../dto/method'
3
6
 
4
7
  module Trains
5
8
  module Visitor
6
9
  # Visitor that parses controllers and returns a DTO::Controller object
7
10
  class Controller < Base
11
+ include Utils::Args
12
+
13
+ TRACKED_CALLBACKS = [
14
+ :http_basic_authenticate_with
15
+ ].freeze
16
+
8
17
  def initialize
9
18
  @method_list = []
10
19
  @methods = {}
11
20
  @class_name = nil
21
+ @callbacks = []
12
22
  end
13
23
 
14
24
  def on_class(node)
@@ -17,15 +27,25 @@ module Trains
17
27
  return unless controller?(parent_class)
18
28
 
19
29
  @class_name = class_name
30
+ find_callbacks(node)
20
31
  parse_body(node.body) unless node.body.nil?
21
32
  end
22
33
 
23
34
  def result
24
- DTO::Controller.new(name: @class_name, method_list: @method_list.uniq)
35
+ DTO::Controller.new(name: @class_name, method_list: @method_list.uniq, callbacks: @callbacks)
25
36
  end
26
37
 
27
38
  private
28
39
 
40
+ def find_callbacks(klass_node)
41
+ klass_node.each_descendant(:send) do |node|
42
+ if node.receiver.nil? && TRACKED_CALLBACKS.include?(node.method_name)
43
+ @callbacks << DTO::Callback.new(method: node.method_name,
44
+ arguments: node.arguments.map { |arg| parse_args(arg) })
45
+ end
46
+ end
47
+ end
48
+
29
49
  def parse_body(body)
30
50
  body.each_child_node do |child|
31
51
  @method_list << parse_method(child) if child.type == :def
@@ -9,6 +9,7 @@ module Trains
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
@@ -30,7 +31,12 @@ module Trains
30
31
  end
31
32
 
32
33
  def result
33
- DTO::Model.new(@table_name, @fields, @migration_version)
34
+ DTO::Migration.new(
35
+ table_name: @table_name,
36
+ modifier: @table_modifier,
37
+ fields: @fields,
38
+ version: @migration_version
39
+ )
34
40
  end
35
41
 
36
42
  private
@@ -50,11 +56,19 @@ module Trains
50
56
 
51
57
  def process_def_node(node)
52
58
  allowed_method_names = %i[change up down]
53
- allowed_table_modifiers = %i[create_table update_column add_column]
59
+ allowed_table_modifiers = %i[
60
+ create_table
61
+ change_table
62
+ update_column
63
+ add_column
64
+ remove_column
65
+ change_column
66
+ ]
54
67
  block_type_modifier = false
55
68
 
56
69
  method_name = node.method_name
57
70
  return unless allowed_method_names.include? method_name
71
+ return if node.body.nil?
58
72
 
59
73
  table_modifier =
60
74
  if node.body.children[0] == nil
@@ -68,6 +82,8 @@ module Trains
68
82
  end
69
83
  return unless allowed_table_modifiers.include? table_modifier
70
84
 
85
+ @table_modifier = table_modifier
86
+
71
87
  # Get the name of the table being modified
72
88
  if block_type_modifier
73
89
  raw_table_name =
@@ -81,8 +97,8 @@ module Trains
81
97
  raw_table_name = node.body.children[2].value.to_s
82
98
  @table_name = raw_table_name.singularize.camelize
83
99
 
84
- field_name = node.body.children[3].value
85
- field_type = node.body.children[4].value
100
+ field_name = node.body.children[3]&.value
101
+ field_type = node.body.children[4]&.value
86
102
  @fields.append(DTO::Field.new(field_name, field_type))
87
103
  end
88
104
  end
@@ -95,7 +111,7 @@ module Trains
95
111
  @fields.append(DTO::Field.new(:updated_at, :datetime))
96
112
  return
97
113
  end
98
-
114
+
99
115
  type = node.children[1]
100
116
  value = node.children[2].value unless node.children[2].hash_type?
101
117
  @fields.append(DTO::Field.new(value, type))
@@ -1,8 +1,11 @@
1
- require_relative "../dto/route"
1
+ require_relative '../dto/route'
2
2
 
3
3
  module Trains
4
4
  module Visitor
5
+ # Visitor for Parsing Rails routes
5
6
  class Route < Base
7
+ include Utils::Args
8
+
6
9
  def_node_matcher :route_parent?, <<~PATTERN
7
10
  (block (send (send (send
8
11
  (const nil? :Rails) :application) :routes) :draw)
@@ -13,7 +16,7 @@ module Trains
13
16
  (send nil? %1 ...)
14
17
  PATTERN
15
18
 
16
- ALLOWED_VERBS = %i[get put post update delete resources scope]
19
+ ALLOWED_VERBS = %i[get put post update delete resources scope].freeze
17
20
 
18
21
  def initialize
19
22
  @route_list = []
@@ -25,6 +28,7 @@ module Trains
25
28
 
26
29
  def on_block(node)
27
30
  return unless route_parent?(node)
31
+ return if node.body.nil?
28
32
 
29
33
  node.body.each_child_node do |child|
30
34
  ALLOWED_VERBS.each do |verb|
@@ -45,33 +49,7 @@ module Trains
45
49
  node.arguments[0].value
46
50
  end
47
51
  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
52
+ DTO::Route.new(method:, param:, options:)
75
53
  end
76
54
  end
77
55
  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
4
+ version: 0.0.6
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-04-18 00:00:00.000000000 Z
11
+ date: 2023-05-08 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activesupport
@@ -85,13 +85,17 @@ files:
85
85
  - Rakefile
86
86
  - lib/trains.rb
87
87
  - lib/trains/dto/app.rb
88
+ - lib/trains/dto/callback.rb
88
89
  - lib/trains/dto/controller.rb
89
90
  - lib/trains/dto/field.rb
90
91
  - lib/trains/dto/method.rb
92
+ - lib/trains/dto/migration.rb
91
93
  - lib/trains/dto/model.rb
92
94
  - lib/trains/dto/route.rb
93
95
  - lib/trains/scanner.rb
96
+ - lib/trains/utils/args.rb
94
97
  - lib/trains/utils/logger.rb
98
+ - lib/trains/utils/migration_tailor.rb
95
99
  - lib/trains/utils/rails_dir.rb
96
100
  - lib/trains/utils/result.rb
97
101
  - lib/trains/version.rb