trains 0.0.4 → 0.0.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 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