synvert-core 0.64.0 → 1.0.1
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/.github/workflows/main.yml +1 -1
- data/.gitignore +4 -0
- data/CHANGELOG.md +5 -0
- data/Guardfile +11 -2
- data/README.md +2 -2
- data/Rakefile +15 -1
- data/lib/synvert/core/array_ext.rb +41 -0
- data/lib/synvert/core/engine/erb.rb +9 -8
- data/lib/synvert/core/node_ext.rb +47 -44
- data/lib/synvert/core/node_query/compiler/array.rb +34 -0
- data/lib/synvert/core/node_query/compiler/attribute.rb +51 -0
- data/lib/synvert/core/node_query/compiler/attribute_list.rb +24 -0
- data/lib/synvert/core/node_query/compiler/boolean.rb +23 -0
- data/lib/synvert/core/node_query/compiler/comparable.rb +79 -0
- data/lib/synvert/core/node_query/compiler/dynamic_attribute.rb +51 -0
- data/lib/synvert/core/node_query/compiler/expression.rb +88 -0
- data/lib/synvert/core/node_query/compiler/float.rb +23 -0
- data/lib/synvert/core/node_query/compiler/identifier.rb +41 -0
- data/lib/synvert/core/node_query/compiler/integer.rb +23 -0
- data/lib/synvert/core/node_query/compiler/invalid_operator_error.rb +7 -0
- data/lib/synvert/core/node_query/compiler/nil.rb +23 -0
- data/lib/synvert/core/node_query/compiler/parse_error.rb +7 -0
- data/lib/synvert/core/node_query/compiler/regexp.rb +37 -0
- data/lib/synvert/core/node_query/compiler/selector.rb +51 -0
- data/lib/synvert/core/node_query/compiler/string.rb +34 -0
- data/lib/synvert/core/node_query/compiler/symbol.rb +23 -0
- data/lib/synvert/core/node_query/compiler.rb +24 -0
- data/lib/synvert/core/node_query/lexer.rex +96 -0
- data/lib/synvert/core/node_query/lexer.rex.rb +293 -0
- data/lib/synvert/core/node_query/parser.racc.rb +518 -0
- data/lib/synvert/core/node_query/parser.y +84 -0
- data/lib/synvert/core/node_query.rb +36 -0
- data/lib/synvert/core/rewriter/action/delete_action.rb +4 -2
- data/lib/synvert/core/rewriter/action/replace_action.rb +4 -2
- data/lib/synvert/core/rewriter/action/replace_erb_stmt_with_expr_action.rb +2 -2
- data/lib/synvert/core/rewriter/action/wrap_action.rb +3 -2
- data/lib/synvert/core/rewriter/action.rb +2 -1
- data/lib/synvert/core/rewriter/helper.rb +5 -2
- data/lib/synvert/core/rewriter/instance.rb +25 -13
- data/lib/synvert/core/rewriter/scope/query_scope.rb +36 -0
- data/lib/synvert/core/rewriter/scope/within_scope.rb +1 -1
- data/lib/synvert/core/rewriter.rb +1 -0
- data/lib/synvert/core/version.rb +1 -1
- data/lib/synvert/core.rb +22 -5
- data/spec/synvert/core/engine/erb_spec.rb +2 -2
- data/spec/synvert/core/node_ext_spec.rb +36 -5
- data/spec/synvert/core/node_query/lexer_spec.rb +512 -0
- data/spec/synvert/core/node_query/parser_spec.rb +270 -0
- data/spec/synvert/core/rewriter/condition/if_only_exist_condition_spec.rb +1 -6
- data/spec/synvert/core/rewriter/helper_spec.rb +4 -1
- data/spec/synvert/core/rewriter/instance_spec.rb +24 -3
- data/spec/synvert/core/rewriter/scope/query_scope_spec.rb +74 -0
- data/spec/synvert/core/rewriter/scope/within_scope_spec.rb +12 -9
- data/spec/synvert/core/rewriter_spec.rb +4 -2
- data/synvert-core-ruby.gemspec +5 -1
- metadata +75 -2
@@ -34,7 +34,8 @@ module Synvert::Core
|
|
34
34
|
# @return [String] rewritten code.
|
35
35
|
def rewritten_code
|
36
36
|
if rewritten_source.split("\n").length > 1
|
37
|
-
"\n\n" + rewritten_source.split("\n").map { |line| indent(@node) + line }
|
37
|
+
"\n\n" + rewritten_source.split("\n").map { |line| indent(@node) + line }
|
38
|
+
.join("\n")
|
38
39
|
else
|
39
40
|
"\n" + indent(@node) + rewritten_source
|
40
41
|
end
|
@@ -65,7 +65,9 @@ module Synvert::Core
|
|
65
65
|
#
|
66
66
|
# strip_brackets("(1..100)") #=> "1..100"
|
67
67
|
def strip_brackets(code)
|
68
|
-
code.sub(/^\((.*)\)$/) { Regexp.last_match(1) }
|
68
|
+
code.sub(/^\((.*)\)$/) { Regexp.last_match(1) }
|
69
|
+
.sub(/^\[(.*)\]$/) { Regexp.last_match(1) }
|
70
|
+
.sub(/^{(.*)}$/) {
|
69
71
|
Regexp.last_match(1).strip
|
70
72
|
}
|
71
73
|
end
|
@@ -81,7 +83,8 @@ module Synvert::Core
|
|
81
83
|
# hash_node = Parser::CurrentRuby.parse("{ key1: 'value1', key2: 'value2' }")
|
82
84
|
# reject_keys_from_hash(hash_node, :key1) => "key2: 'value2'"
|
83
85
|
def reject_keys_from_hash(hash_node, *keys)
|
84
|
-
hash_node.children.reject { |pair_node| keys.include?(pair_node.key.to_value) }
|
86
|
+
hash_node.children.reject { |pair_node| keys.include?(pair_node.key.to_value) }
|
87
|
+
.map(&:to_source).join(', ')
|
85
88
|
end
|
86
89
|
end
|
87
90
|
end
|
@@ -7,6 +7,18 @@ module Synvert::Core
|
|
7
7
|
# One instance can contain one or many {Synvert::Core::Rewriter::Scope} and {Synvert::Rewriter::Condition}.
|
8
8
|
class Rewriter::Instance
|
9
9
|
include Rewriter::Helper
|
10
|
+
# Initialize an Instance.
|
11
|
+
#
|
12
|
+
# @param rewriter [Synvert::Core::Rewriter]
|
13
|
+
# @param file_patterns [Array<String>] pattern list to find files, e.g. ['spec/**/*_spec.rb']
|
14
|
+
# @yield block code to find nodes, match conditions and rewrite code.
|
15
|
+
def initialize(rewriter, file_patterns, &block)
|
16
|
+
@rewriter = rewriter
|
17
|
+
@actions = []
|
18
|
+
@file_patterns = file_patterns
|
19
|
+
@block = block
|
20
|
+
rewriter.helpers.each { |helper| singleton_class.send(:define_method, helper[:name], &helper[:block]) }
|
21
|
+
end
|
10
22
|
|
11
23
|
class << self
|
12
24
|
# Get file source.
|
@@ -69,19 +81,6 @@ module Synvert::Core
|
|
69
81
|
self.class.file_source(current_file)
|
70
82
|
end
|
71
83
|
|
72
|
-
# Initialize an Instance.
|
73
|
-
#
|
74
|
-
# @param rewriter [Synvert::Core::Rewriter]
|
75
|
-
# @param file_patterns [Array<String>] pattern list to find files, e.g. ['spec/**/*_spec.rb']
|
76
|
-
# @yield block code to find nodes, match conditions and rewrite code.
|
77
|
-
def initialize(rewriter, file_patterns, &block)
|
78
|
-
@rewriter = rewriter
|
79
|
-
@actions = []
|
80
|
-
@file_patterns = file_patterns
|
81
|
-
@block = block
|
82
|
-
rewriter.helpers.each { |helper| singleton_class.send(:define_method, helper[:name], &helper[:block]) }
|
83
|
-
end
|
84
|
-
|
85
84
|
# Process the instance.
|
86
85
|
# It finds specified files, for each file, it executes the block code, rewrites the original code,
|
87
86
|
# then write the code back to the original file.
|
@@ -127,6 +126,19 @@ module Synvert::Core
|
|
127
126
|
# DSL #
|
128
127
|
#######
|
129
128
|
|
129
|
+
# Parse +find_node+ dsl, it creates {Synvert::Core::Rewriter::QueryScope} to recursively find matching ast nodes,
|
130
|
+
# then continue operating on each matching ast node.
|
131
|
+
# @example
|
132
|
+
# # matches FactoryBot.create(:user)
|
133
|
+
# find_node '.send[receiver=FactoryBot][message=create][arguments.size=1]' do
|
134
|
+
# end
|
135
|
+
# @param query_string [String] query string to find matching ast nodes.
|
136
|
+
# @yield run on the matching nodes.
|
137
|
+
# @raise [Synvert::Core::NodeQuery::Compiler::ParseError] if query string is invalid.
|
138
|
+
def find_node(query_string, &block)
|
139
|
+
Rewriter::QueryScope.new(self, query_string, &block).process
|
140
|
+
end
|
141
|
+
|
130
142
|
# Parse +within_node+ dsl, it creates a {Synvert::Core::Rewriter::WithinScope} to recursively find matching ast nodes,
|
131
143
|
# then continue operating on each matching ast node.
|
132
144
|
# @example
|
@@ -0,0 +1,36 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Synvert::Core
|
4
|
+
# QueryScope finds out nodes by using node query language, then changes its scope to matching node.
|
5
|
+
class Rewriter::QueryScope < Rewriter::Scope
|
6
|
+
# Initialize a QueryScope.
|
7
|
+
#
|
8
|
+
# @param instance [Synvert::Core::Rewriter::Instance]
|
9
|
+
# @param query_string [String]
|
10
|
+
# @yield run on all matching nodes
|
11
|
+
def initialize(instance, query_string, &block)
|
12
|
+
super(instance, &block)
|
13
|
+
@query_string = query_string
|
14
|
+
end
|
15
|
+
|
16
|
+
# Find out the matching nodes.
|
17
|
+
#
|
18
|
+
# It checks the current node and iterates all child nodes,
|
19
|
+
# then run the block code on each matching node.
|
20
|
+
# @raise [Synvert::Core::NodeQuery::Compiler::ParseError] if the query string is invalid.
|
21
|
+
def process
|
22
|
+
current_node = @instance.current_node
|
23
|
+
return unless current_node
|
24
|
+
|
25
|
+
@instance.process_with_node(current_node) do
|
26
|
+
NodeQuery::Parser.new.parse(@query_string).query_nodes(current_node).each do |node|
|
27
|
+
@instance.process_with_node(node) do
|
28
|
+
@instance.instance_eval(&@block)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
rescue NodeQuery::Lexer::ScanError, Racc::ParseError => e
|
33
|
+
raise NodeQuery::Compiler::ParseError, "Invalid query string: #{@query_string}"
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
@@ -25,6 +25,7 @@ module Synvert::Core
|
|
25
25
|
autoload :Instance, 'synvert/core/rewriter/instance'
|
26
26
|
|
27
27
|
autoload :Scope, 'synvert/core/rewriter/scope'
|
28
|
+
autoload :QueryScope, 'synvert/core/rewriter/scope/query_scope'
|
28
29
|
autoload :WithinScope, 'synvert/core/rewriter/scope/within_scope'
|
29
30
|
autoload :GotoScope, 'synvert/core/rewriter/scope/goto_scope'
|
30
31
|
|
data/lib/synvert/core/version.rb
CHANGED
data/lib/synvert/core.rb
CHANGED
@@ -10,6 +10,7 @@ require 'active_support/core_ext/object'
|
|
10
10
|
require 'active_support/core_ext/array'
|
11
11
|
require 'erubis'
|
12
12
|
require 'set'
|
13
|
+
require 'synvert/core/array_ext'
|
13
14
|
require 'synvert/core/node_ext'
|
14
15
|
|
15
16
|
module Synvert
|
@@ -19,6 +20,7 @@ module Synvert
|
|
19
20
|
autoload :Engine, 'synvert/core/engine'
|
20
21
|
autoload :RewriterNotFound, 'synvert/core/exceptions'
|
21
22
|
autoload :MethodNotSupported, 'synvert/core/exceptions'
|
23
|
+
autoload :NodeQuery, 'synvert/core/node_query'
|
22
24
|
end
|
23
25
|
end
|
24
26
|
|
@@ -37,17 +39,32 @@ module Synvert
|
|
37
39
|
RAILS_MAILER_FILES = %w[app/mailers/**/*.rb engines/*/app/mailers/**/*.rb]
|
38
40
|
RAILS_MIGRATION_FILES = %w[db/migrate/**/*.rb engines/*/db/migrate/**/*.rb]
|
39
41
|
RAILS_MODEL_FILES = %w[app/models/**/*.rb engines/*/app/models/**/*.rb]
|
40
|
-
RAILS_ROUTE_FILES = %w[
|
42
|
+
RAILS_ROUTE_FILES = %w[
|
43
|
+
config/routes.rb
|
44
|
+
config/routes/**/*.rb
|
45
|
+
engines/*/config/routes.rb
|
46
|
+
engines/*/config/routes/**/*.rb
|
47
|
+
]
|
41
48
|
RAILS_VIEW_FILES = %w[app/views/**/*.html.{erb,haml,slim}]
|
42
49
|
|
43
50
|
RAILS_CONTROLLER_TEST_FILES = %w[
|
44
|
-
test/functional/**/*.rb
|
45
|
-
|
51
|
+
test/functional/**/*.rb
|
52
|
+
test/controllers/**/*.rb
|
53
|
+
engines/*/test/functional/**/*.rb
|
54
|
+
engines/*/test/controllers/**/*.rb
|
55
|
+
spec/functional/**/*.rb
|
56
|
+
spec/controllers/**/*.rb
|
57
|
+
engines/*/spec/functional/**/*.rb
|
58
|
+
engines/*/spec/controllers/**/*.rb
|
46
59
|
]
|
47
60
|
RAILS_INTEGRATION_TEST_FILES = %w[test/integration/**/*.rb spec/integration/**/*.rb]
|
48
61
|
RAILS_MODEL_TEST_FILES = %w[
|
49
|
-
test/unit/**/*.rb
|
50
|
-
|
62
|
+
test/unit/**/*.rb
|
63
|
+
engines/*/test/unit/**/*.rb
|
64
|
+
test/models/**/*.rb
|
65
|
+
engines/*/test/models/**/*.rb
|
66
|
+
spec/models/**/*.rb
|
67
|
+
engines/*/spec/models/**/*.rb
|
51
68
|
]
|
52
69
|
|
53
70
|
RAILS_FACTORY_FILES = %w[test/factories/**/*.rb spec/factories/**/*.rb]
|
@@ -3,6 +3,25 @@
|
|
3
3
|
require 'spec_helper'
|
4
4
|
|
5
5
|
describe Parser::AST::Node do
|
6
|
+
describe '#parent' do
|
7
|
+
it 'gets parent node' do
|
8
|
+
node = parse('FactoryBot.create(:user)')
|
9
|
+
child_node = node.children.first
|
10
|
+
expect(child_node.parent).to eq node
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
describe '#siblings' do
|
15
|
+
it 'gets sibling nodes' do
|
16
|
+
node = parse(<<~EOS)
|
17
|
+
def foobar; end
|
18
|
+
def foo; end
|
19
|
+
def bar; end
|
20
|
+
EOS
|
21
|
+
expect(node.children.first.siblings).to eq [node.children.second, node.children.last]
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
6
25
|
describe '#name' do
|
7
26
|
it 'gets for class node' do
|
8
27
|
node = parse('class Synvert; end')
|
@@ -129,17 +148,17 @@ describe Parser::AST::Node do
|
|
129
148
|
describe '#arguments' do
|
130
149
|
it 'gets for def node' do
|
131
150
|
node = parse('def test(foo, bar); foo + bar; end')
|
132
|
-
expect(node.arguments.type).to eq :
|
151
|
+
expect(node.arguments.map(&:type)).to eq [:arg, :arg]
|
133
152
|
end
|
134
153
|
|
135
154
|
it 'gets for defs node' do
|
136
155
|
node = parse('def self.test(foo, bar); foo + bar; end')
|
137
|
-
expect(node.arguments.type).to eq :
|
156
|
+
expect(node.arguments.map(&:type)).to eq [:arg, :arg]
|
138
157
|
end
|
139
158
|
|
140
159
|
it 'gets for block node' do
|
141
160
|
node = parse('RSpec.configure do |config|; end')
|
142
|
-
expect(node.arguments.type).to eq :
|
161
|
+
expect(node.arguments.map(&:type)).to eq [:arg]
|
143
162
|
end
|
144
163
|
|
145
164
|
it 'gets for send node' do
|
@@ -388,6 +407,11 @@ describe Parser::AST::Node do
|
|
388
407
|
expect(node.to_value).to be_falsey
|
389
408
|
end
|
390
409
|
|
410
|
+
it 'gets for nil' do
|
411
|
+
node = parse('nil')
|
412
|
+
expect(node.to_value).to be_nil
|
413
|
+
end
|
414
|
+
|
391
415
|
it 'gets for irange' do
|
392
416
|
node = parse('(1..10)')
|
393
417
|
expect(node.to_value).to eq(1..10)
|
@@ -534,7 +558,14 @@ describe Parser::AST::Node do
|
|
534
558
|
it 'matches arguments with nested hash' do
|
535
559
|
source = '{ user_id: user.id }'
|
536
560
|
node = parse(source)
|
537
|
-
expect(node).to be_match(
|
561
|
+
expect(node).to be_match(
|
562
|
+
type: 'hash',
|
563
|
+
user_id_value: {
|
564
|
+
type: 'send',
|
565
|
+
receiver: { type: 'send', message: 'user' },
|
566
|
+
message: 'id'
|
567
|
+
}
|
568
|
+
)
|
538
569
|
end
|
539
570
|
|
540
571
|
it 'matches arguments contain' do
|
@@ -862,7 +893,7 @@ describe Parser::AST::Node do
|
|
862
893
|
expect(range.to_range).to eq(16...27)
|
863
894
|
end
|
864
895
|
|
865
|
-
it "checks array' value" do
|
896
|
+
it "checks array's value" do
|
866
897
|
node = parse('factory :admin, class: User do; end')
|
867
898
|
range = node.child_node_range('caller.arguments.second.class_value')
|
868
899
|
expect(range.to_range).to eq(23...27)
|