synvert-core 1.5.0 → 1.6.0

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: 71ae91a324677995dbfd9b1251ebf2a0a01e7c25289dcebcf86abb07599b1db8
4
- data.tar.gz: 4370235d14aee70f57b6b02313637e9d24b13ca2aee61b71e4fa4022f78bc3f8
3
+ metadata.gz: 5d838da042cfdbc68e4a235117a465ca340fa732203f7470684a9279e6797fff
4
+ data.tar.gz: ad784c3e09faa73a55e0bf0ad51a982230d9a1b2a328c43414e4aa297e38a00e
5
5
  SHA512:
6
- metadata.gz: 8bcd68a69575a6cd5c5c0bc7a91f82d09d2f438ab06988c01ad14c7d97c863bc2556f5c3ad7d054e35cfcb830cb778ec10a9797952a56a9391062b4bf3ce2a3f
7
- data.tar.gz: be7b4c9fda8428c0d2a8c29350f6d2c60c40577c0f990ed5d8f4d2742fc2545fc8ddbd139298aaf39a9e69e9e9bf83fe29385745ac7ef10459da836572df9327
6
+ metadata.gz: 4b31e94f8071af04f1ae7b1292bb5af91e52ba047949535667cb6765a5ef32c9bbcf855dd92a212aa640b0185f42fbd1bbcf4614f424c596f9c52f62a313a643
7
+ data.tar.gz: 197318ccffd17c9eb60251731bf49f45198e2b5fb5812c22d4fe6d0d07bac94bff638bc5a125d05be2ccec930b16300ccdb83ffdd53bf8cf0c4cd8ad22a34c94
@@ -15,7 +15,6 @@ on:
15
15
 
16
16
  jobs:
17
17
  test:
18
-
19
18
  runs-on: ubuntu-latest
20
19
  strategy:
21
20
  matrix:
data/.gitignore CHANGED
@@ -3,7 +3,6 @@
3
3
  .bundle
4
4
  .config
5
5
  .yardoc
6
- Gemfile.lock
7
6
  InstalledFiles
8
7
  _yardoc
9
8
  coverage
data/CHANGELOG.md CHANGED
@@ -1,5 +1,11 @@
1
1
  # CHANGELOG
2
2
 
3
+ ## 1.6.0 (2022-09-15)
4
+
5
+ * Make use of `NodeQuery` to query nodes
6
+ * Remove `Node#to_hash`
7
+ * Downgrade `activesupport` to < 7.0.0
8
+
3
9
  ## 1.5.0 (2022-07-02)
4
10
 
5
11
  * Abstract `node_query`
data/Gemfile.lock ADDED
@@ -0,0 +1,101 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ synvert-core (1.6.0)
5
+ activesupport (< 7.0.0)
6
+ erubis
7
+ node_mutation
8
+ node_query
9
+ parser
10
+ parser_node_ext
11
+
12
+ GEM
13
+ remote: https://rubygems.org/
14
+ specs:
15
+ activesupport (6.1.7)
16
+ concurrent-ruby (~> 1.0, >= 1.0.2)
17
+ i18n (>= 1.6, < 2)
18
+ minitest (>= 5.1)
19
+ tzinfo (~> 2.0)
20
+ zeitwerk (~> 2.3)
21
+ ast (2.4.2)
22
+ coderay (1.1.3)
23
+ concurrent-ruby (1.1.10)
24
+ diff-lcs (1.5.0)
25
+ erubis (2.7.0)
26
+ ffi (1.15.5)
27
+ formatador (1.1.0)
28
+ guard (2.18.0)
29
+ formatador (>= 0.2.4)
30
+ listen (>= 2.7, < 4.0)
31
+ lumberjack (>= 1.0.12, < 2.0)
32
+ nenv (~> 0.1)
33
+ notiffany (~> 0.0)
34
+ pry (>= 0.13.0)
35
+ shellany (~> 0.0)
36
+ thor (>= 0.18.1)
37
+ guard-compat (1.2.1)
38
+ guard-rspec (4.7.3)
39
+ guard (~> 2.1)
40
+ guard-compat (~> 1.1)
41
+ rspec (>= 2.99.0, < 4.0)
42
+ i18n (1.12.0)
43
+ concurrent-ruby (~> 1.0)
44
+ listen (3.7.1)
45
+ rb-fsevent (~> 0.10, >= 0.10.3)
46
+ rb-inotify (~> 0.9, >= 0.9.10)
47
+ lumberjack (1.2.8)
48
+ method_source (1.0.0)
49
+ minitest (5.16.3)
50
+ nenv (0.3.0)
51
+ node_mutation (1.2.1)
52
+ activesupport
53
+ erubis
54
+ node_query (1.5.0)
55
+ activesupport (< 7.0.0)
56
+ notiffany (0.1.3)
57
+ nenv (~> 0.1)
58
+ shellany (~> 0.0)
59
+ parser (3.1.2.1)
60
+ ast (~> 2.4.1)
61
+ parser_node_ext (0.4.0)
62
+ parser
63
+ pry (0.14.1)
64
+ coderay (~> 1.1)
65
+ method_source (~> 1.0)
66
+ rake (13.0.6)
67
+ rb-fsevent (0.11.1)
68
+ rb-inotify (0.10.1)
69
+ ffi (~> 1.0)
70
+ rspec (3.10.0)
71
+ rspec-core (~> 3.10.0)
72
+ rspec-expectations (~> 3.10.0)
73
+ rspec-mocks (~> 3.10.0)
74
+ rspec-core (3.10.2)
75
+ rspec-support (~> 3.10.0)
76
+ rspec-expectations (3.10.2)
77
+ diff-lcs (>= 1.2.0, < 2.0)
78
+ rspec-support (~> 3.10.0)
79
+ rspec-mocks (3.10.2)
80
+ diff-lcs (>= 1.2.0, < 2.0)
81
+ rspec-support (~> 3.10.0)
82
+ rspec-support (3.10.3)
83
+ shellany (0.0.1)
84
+ thor (1.2.1)
85
+ tzinfo (2.0.5)
86
+ concurrent-ruby (~> 1.0)
87
+ zeitwerk (2.6.0)
88
+
89
+ PLATFORMS
90
+ ruby
91
+
92
+ DEPENDENCIES
93
+ guard
94
+ guard-rspec
95
+ rake
96
+ rspec
97
+ rspec-mocks
98
+ synvert-core!
99
+
100
+ BUNDLED WITH
101
+ 2.3.7
data/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # synvert-core-ruby
2
2
 
3
- <img src="https://synvert.xinminlabs.com/img/logo_96.png" alt="logo" width="32" height="32" />
3
+ <img src="https://synvert.net/img/logo_96.png" alt="logo" width="32" height="32" />
4
4
 
5
5
  [![AwesomeCode Status for xinminlabs/synvert-core-ruby](https://awesomecode.io/projects/033f7f02-7b22-41c3-a902-fca37f1ec72a/status)](https://awesomecode.io/repos/xinminlabs/synvert-core-ruby)
6
6
  ![Main workflow](https://github.com/xinminlabs/synvert-core-ruby/actions/workflows/main.yml/badge.svg)
@@ -50,43 +50,6 @@ module Parser::AST
50
50
  loc.expression.line
51
51
  end
52
52
 
53
- # Match node with rules.
54
- # It provides some additional keywords to match rules, +any+, +contain+, +not+, +in+, +not_in+, +gt+, +gte+, +lt+, +lte+.
55
- # @example
56
- # type: 'send', arguments: { any: 'Lifo::ShowExceptions' }
57
- # type: { in: ['send', 'csend'] }
58
- # type: :send, arguments: { length: { gt: 2 } }
59
- # @param rules [Hash] rules to match.
60
- # @return true if matches.
61
- def match?(rules)
62
- keywords = %i[any contain not in not_in gt gte lt lte]
63
- flat_hash(rules).keys.all? do |multi_keys|
64
- last_key = multi_keys.last
65
- actual = keywords.include?(last_key) ? actual_value(multi_keys[0...-1]) : actual_value(multi_keys)
66
- expected = expected_value(rules, multi_keys)
67
- case last_key
68
- when :any, :contain
69
- actual.any? { |actual_value| match_value?(actual_value, expected) }
70
- when :not
71
- !match_value?(actual, expected)
72
- when :in
73
- expected.any? { |expected_value| match_value?(actual, expected_value) }
74
- when :not_in
75
- expected.all? { |expected_value| !match_value?(actual, expected_value) }
76
- when :gt
77
- actual > expected
78
- when :gte
79
- actual >= expected
80
- when :lt
81
- actual < expected
82
- when :lte
83
- actual <= expected
84
- else
85
- match_value?(actual, expected)
86
- end
87
- end
88
- end
89
-
90
53
  # Strip curly braces for hash.
91
54
  # @example
92
55
  # node # s(:hash, s(:pair, s(:sym, :foo), s(:str, "bar")))
@@ -161,141 +124,5 @@ module Parser::AST
161
124
  to_source
162
125
  end
163
126
  end
164
-
165
- # Convert node to a hash, so that it can be converted to a json.
166
- def to_hash
167
- result = { type: type }
168
- if TYPE_CHILDREN[type]
169
- TYPE_CHILDREN[type].each do |key|
170
- value = send(key)
171
- result[key] =
172
- case value
173
- when Array
174
- value.map { |v| v.respond_to?(:to_hash) ? v.to_hash : v }
175
- when Parser::AST::Node
176
- value.to_hash
177
- else
178
- value
179
- end
180
- end
181
- else
182
- result[:children] = children.map { |c| c.respond_to?(:to_hash) ? c.to_hash : c }
183
- end
184
- result
185
- end
186
-
187
- private
188
-
189
- # Compare actual value with expected value.
190
- #
191
- # @param actual [Object] actual value.
192
- # @param expected [Object] expected value.
193
- # @return [Boolean]
194
- # @raise [Synvert::Core::MethodNotSupported] if expected class is not supported.
195
- def match_value?(actual, expected)
196
- return true if actual == expected
197
-
198
- case expected
199
- when Symbol
200
- if actual.is_a?(Parser::AST::Node)
201
- actual.to_source == ":#{expected}" || actual.to_source == expected.to_s
202
- else
203
- actual.to_sym == expected
204
- end
205
- when String
206
- if actual.is_a?(Parser::AST::Node)
207
- actual.to_source == expected || actual.to_source == unwrap_quote(expected) ||
208
- unwrap_quote(actual.to_source) == expected || unwrap_quote(actual.to_source) == unwrap_quote(expected)
209
- else
210
- actual.to_s == expected || wrap_quote(actual.to_s) == expected
211
- end
212
- when Regexp
213
- if actual.is_a?(Parser::AST::Node)
214
- actual.to_source =~ Regexp.new(expected.to_s, Regexp::MULTILINE)
215
- else
216
- actual.to_s =~ Regexp.new(expected.to_s, Regexp::MULTILINE)
217
- end
218
- when Array
219
- return false unless expected.length == actual.length
220
-
221
- actual.zip(expected).all? { |a, e| match_value?(a, e) }
222
- when NilClass
223
- if actual.is_a?(Parser::AST::Node)
224
- :nil == actual.type
225
- else
226
- actual.nil?
227
- end
228
- when Numeric
229
- if actual.is_a?(Parser::AST::Node)
230
- actual.children[0] == expected
231
- else
232
- actual == expected
233
- end
234
- when TrueClass
235
- :true == actual.type
236
- when FalseClass
237
- :false == actual.type
238
- when Parser::AST::Node
239
- actual == expected
240
- when Synvert::Core::Rewriter::AnyValue
241
- !actual.nil?
242
- else
243
- raise Synvert::Core::MethodNotSupported, "#{expected.class} is not handled for match_value?"
244
- end
245
- end
246
-
247
- # Convert a hash to flat one.
248
- #
249
- # @example
250
- # flat_hash(type: 'block', caller: {type: 'send', receiver: 'RSpec'})
251
- # # {[:type] => 'block', [:caller, :type] => 'send', [:caller, :receiver] => 'RSpec'}
252
- # @param h [Hash] original hash.
253
- # @return flatten hash.
254
- def flat_hash(h, k = [])
255
- new_hash = {}
256
- h.each_pair do |key, val|
257
- if val.is_a?(Hash)
258
- new_hash.merge!(flat_hash(val, k + [key]))
259
- else
260
- new_hash[k + [key]] = val
261
- end
262
- end
263
- new_hash
264
- end
265
-
266
- # Get actual value from the node.
267
- #
268
- # @param multi_keys [Array<Symbol, String>]
269
- # @return [Object] actual value.
270
- def actual_value(multi_keys)
271
- multi_keys.inject(self) { |n, key| n.send(key) if n }
272
- end
273
-
274
- # Get expected value from rules.
275
- #
276
- # @param rules [Hash]
277
- # @param multi_keys [Array<Symbol>]
278
- # @return [Object] expected value.
279
- def expected_value(rules, multi_keys)
280
- multi_keys.inject(rules) { |o, key| o[key] }
281
- end
282
-
283
- # Wrap the string with single or double quote.
284
- def wrap_quote(string)
285
- if string.include?("'")
286
- "\"#{string}\""
287
- else
288
- "'#{string}'"
289
- end
290
- end
291
-
292
- # Unwrap the quote from the string.
293
- def unwrap_quote(string)
294
- if (string[0] == '"' && string[-1] == '"') || (string[0] == "'" && string[-1] == "'")
295
- string[1...-1]
296
- else
297
- string
298
- end
299
- end
300
127
  end
301
128
  end
@@ -9,11 +9,7 @@ module Synvert::Core
9
9
  #
10
10
  # @return [Boolean]
11
11
  def match?
12
- match = false
13
- NodeQuery::Helper.handle_recursive_child(@instance.current_node) do |child_node|
14
- match ||= child_node&.match?(@rules)
15
- end
16
- match
12
+ @node_query.query_nodes(target_node, including_self: false, stop_at_first_match: true).size > 0
17
13
  end
18
14
  end
19
15
  end
@@ -9,7 +9,7 @@ module Synvert::Core
9
9
  #
10
10
  # @return [Boolean]
11
11
  def match?
12
- @instance.current_node.body.size == 1 && @instance.current_node.body.first.match?(@rules)
12
+ target_node.body.size == 1 && @node_query.match_node?(target_node.body.first)
13
13
  end
14
14
  end
15
15
  end
@@ -9,11 +9,7 @@ module Synvert::Core
9
9
  #
10
10
  # return [Boolean]
11
11
  def match?
12
- match = false
13
- NodeQuery::Helper.handle_recursive_child(@instance.current_node) do |child_node|
14
- match ||= child_node&.match?(@rules)
15
- end
16
- !match
12
+ @node_query.query_nodes(target_node, including_self: false, stop_at_first_match: true).size == 0
17
13
  end
18
14
  end
19
15
  end
@@ -10,7 +10,7 @@ module Synvert::Core
10
10
  # @yield run when condition matches
11
11
  def initialize(instance, rules, &block)
12
12
  @instance = instance
13
- @rules = rules
13
+ @node_query = NodeQuery.new(rules)
14
14
  @block = block
15
15
  end
16
16
 
@@ -27,5 +27,9 @@ module Synvert::Core
27
27
  def match?
28
28
  raise NotImplementedError, 'must be implemented by subclasses'
29
29
  end
30
+
31
+ def target_node
32
+ @instance.current_node
33
+ end
30
34
  end
31
35
  end
@@ -79,11 +79,13 @@ module Synvert::Core
79
79
  # # matches FactoryBot.create(:user)
80
80
  # find_node '.send[receiver=FactoryBot][message=create][arguments.size=1]' do
81
81
  # end
82
- # @param query_string [String] query string to find matching ast nodes.
82
+ # @param nql [String] node query language to find matching ast nodes.
83
83
  # @yield run on the matching nodes.
84
84
  # @raise [Synvert::Core::NodeQuery::Compiler::ParseError] if query string is invalid.
85
- def find_node(query_string, &block)
86
- Rewriter::QueryScope.new(self, query_string, &block).process
85
+ def find_node(nql, options = {}, &block)
86
+ Rewriter::QueryScope.new(self, nql, options, &block).process
87
+ rescue NodeQueryLexer::ScanError, Racc::ParseError => e
88
+ raise NodeQuery::Compiler::ParseError, "Invalid query string: #{nql}"
87
89
  end
88
90
 
89
91
  # Parse +within_node+ dsl, it creates a {Synvert::Core::Rewriter::WithinScope} to recursively find matching ast nodes,
@@ -94,12 +96,11 @@ module Synvert::Core
94
96
  # end
95
97
  # @param rules [Hash] rules to find mathing ast nodes.
96
98
  # @param options [Hash] optional
97
- # @option stop_when_match [Boolean] set if stop when match, default is false
98
- # @option direct [Boolean] set if find direct matching ast nodes, default is false
99
+ # @option including_self [Boolean] set if query the current node, default is true
100
+ # @option stop_at_first_match [Boolean] set if stop at first match, default is false
101
+ # @option recursive [Boolean] set if recursively query child nodes, default is true
99
102
  # @yield run on the matching nodes.
100
103
  def within_node(rules, options = {}, &block)
101
- options[:stop_when_match] ||= false
102
- options[:direct] ||= false
103
104
  Rewriter::WithinScope.new(self, rules, options, &block).process
104
105
  end
105
106
 
@@ -6,11 +6,14 @@ module Synvert::Core
6
6
  # Initialize a QueryScope.
7
7
  #
8
8
  # @param instance [Synvert::Core::Rewriter::Instance]
9
- # @param query_string [String]
9
+ # @param nql [String]
10
+ # @param options [Hash]
10
11
  # @yield run on all matching nodes
11
- def initialize(instance, query_string, &block)
12
+ def initialize(instance, nql, options = {}, &block)
12
13
  super(instance, &block)
13
- @query_string = query_string
14
+
15
+ @options = { including_self: true, stop_at_first_match: false, recursive: true }.merge(options)
16
+ @node_query = NodeQuery.new(nql)
14
17
  end
15
18
 
16
19
  # Find out the matching nodes.
@@ -22,15 +25,14 @@ module Synvert::Core
22
25
  current_node = @instance.current_node
23
26
  return unless current_node
24
27
 
28
+ matching_nodes = @node_query.query_nodes(current_node, @options)
25
29
  @instance.process_with_node(current_node) do
26
- NodeQuery.new(@query_string).parse(current_node).each do |node|
30
+ matching_nodes.each do |node|
27
31
  @instance.process_with_node(node) do
28
32
  @instance.instance_eval(&@block)
29
33
  end
30
34
  end
31
35
  end
32
- rescue NodeQueryLexer::ScanError, Racc::ParseError => e
33
- raise NodeQuery::Compiler::ParseError, "Invalid query string: #{@query_string}"
34
36
  end
35
37
  end
36
38
  end
@@ -11,8 +11,9 @@ module Synvert::Core
11
11
  # @yield run on all matching nodes
12
12
  def initialize(instance, rules, options = {}, &block)
13
13
  super(instance, &block)
14
- @rules = rules
15
- @options = options
14
+
15
+ @options = { including_self: true, stop_at_first_match: false, recursive: true }.merge(options)
16
+ @node_query = NodeQuery.new(rules)
16
17
  end
17
18
 
18
19
  # Find out the matching nodes.
@@ -22,14 +23,7 @@ module Synvert::Core
22
23
  current_node = @instance.current_node
23
24
  return unless current_node
24
25
 
25
- matching_nodes =
26
- if @options[:direct]
27
- find_direct_matching_nodes(current_node)
28
- elsif @options[:stop_when_match]
29
- find_matching_nodes(current_node)
30
- else
31
- find_recursive_matching_nodes(current_node)
32
- end
26
+ matching_nodes = @node_query.query_nodes(current_node, @options)
33
27
  @instance.process_with_node current_node do
34
28
  matching_nodes.each do |matching_node|
35
29
  @instance.process_with_node matching_node do
@@ -38,82 +32,5 @@ module Synvert::Core
38
32
  end
39
33
  end
40
34
  end
41
-
42
- private
43
-
44
- # Find the matching nodes only in current or direct children.
45
- #
46
- # @param current_node [Parser::AST::Node]
47
- def find_direct_matching_nodes(current_node)
48
- matching_nodes = []
49
- if current_node.is_a?(Parser::AST::Node)
50
- if current_node.type == :begin
51
- current_node.children.each do |child_node|
52
- matching_nodes << child_node if child_node.match?(@rules)
53
- end
54
- elsif current_node.match?(@rules)
55
- matching_nodes << current_node
56
- end
57
- else
58
- current_node.each do |child_node|
59
- matching_nodes << child_node if child_node.match?(@rules)
60
- end
61
- end
62
- matching_nodes
63
- end
64
-
65
- # Find matching nodes in all recursive children.
66
- #
67
- # @param current_node [Parser::AST::Node]
68
- def find_recursive_matching_nodes(current_node)
69
- matching_nodes = []
70
- if current_node.is_a?(Parser::AST::Node)
71
- matching_nodes << current_node if current_node.match?(@rules)
72
- NodeQuery::Helper.handle_recursive_child(current_node) do |child_node|
73
- matching_nodes << child_node if child_node.match?(@rules)
74
- end
75
- else
76
- current_node.each do |node|
77
- matching_nodes << node if node.match?(@rules)
78
- NodeQuery::Helper.handle_recursive_child(node) do |child_node|
79
- matching_nodes << child_node if child_node.match?(@rules)
80
- end
81
- end
82
- end
83
- matching_nodes
84
- end
85
-
86
- # Find matching nodes in recursive children but do not continue on matching nodes.
87
- #
88
- # @param current_node [Parser::AST::Node]
89
- def find_matching_nodes(current_node)
90
- matching_nodes = []
91
- if current_node.is_a?(Parser::AST::Node)
92
- if current_node.match?(@rules)
93
- matching_nodes << current_node
94
- return matching_nodes
95
- end
96
- NodeQuery::Helper.handle_recursive_child(current_node) do |child_node|
97
- if child_node.match?(@rules)
98
- matching_nodes << child_node
99
- next :stop
100
- end
101
- end
102
- else
103
- current_node.each do |node|
104
- if node.match?(@rules)
105
- matching_nodes << node
106
- next
107
- end
108
- NodeQuery::Helper.handle_recursive_child(node) do |child_node|
109
- if child_node.match?(@rules)
110
- matching_nodes << child_node
111
- next :stop
112
- end
113
- end
114
- end
115
- end
116
- matching_nodes
117
- end
118
35
  end
119
36
  end
@@ -2,6 +2,6 @@
2
2
 
3
3
  module Synvert
4
4
  module Core
5
- VERSION = '1.5.0'
5
+ VERSION = '1.6.0'
6
6
  end
7
7
  end
@@ -3,133 +3,6 @@
3
3
  require 'spec_helper'
4
4
 
5
5
  describe Parser::AST::Node do
6
- describe '#match?' do
7
- it 'matches class name' do
8
- source = 'class Synvert; end'
9
- node = parse(source)
10
- expect(node).to be_match(type: 'class', name: 'Synvert')
11
- end
12
-
13
- it 'matches message with regexp' do
14
- source = 'User.find_by_login(login)'
15
- node = parse(source)
16
- expect(node).to be_match(type: 'send', message: /^find_by_/)
17
- end
18
-
19
- it 'matches arguments with symbol' do
20
- source = 'params[:user]'
21
- node = parse(source)
22
- expect(node).to be_match(type: 'send', receiver: 'params', message: '[]', arguments: [:user])
23
- end
24
-
25
- it 'matches pair key with symbol' do
26
- source = '{ type: :model }'
27
- node = parse(source).children[0]
28
- expect(node).to be_match(type: 'pair', key: :type)
29
- end
30
-
31
- it 'matches assign number' do
32
- source = 'at_least(0)'
33
- node = parse(source)
34
- expect(node).to be_match(type: 'send', arguments: [0])
35
- end
36
-
37
- it 'matches assign float' do
38
- source = 'at_least(1.5)'
39
- node = parse(source)
40
- expect(node).to be_match(type: 'send', arguments: [1.5])
41
- end
42
-
43
- it 'matches arguments with string' do
44
- source = 'params["user"]'
45
- node = parse(source)
46
- expect(node).to be_match(type: 'send', receiver: 'params', message: '[]', arguments: ['user'])
47
- end
48
-
49
- it 'matches arguments with string 2' do
50
- source = 'params["user"]'
51
- node = parse(source)
52
- expect(node).to be_match(type: 'send', receiver: 'params', message: '[]', arguments: ["'user'"])
53
- end
54
-
55
- it 'matches arguments with string 3' do
56
- source = "{ notice: 'Welcome' }"
57
- node = parse(source)
58
- expect(node).to be_match(type: 'hash', notice_value: "'Welcome'")
59
- end
60
-
61
- it 'matches arguments any' do
62
- source = 'config.middleware.insert_after ActiveRecord::QueryCache, Lifo::Cache, page_cache: false'
63
- node = parse(source)
64
- expect(node).to be_match(type: 'send', arguments: { any: 'Lifo::Cache' })
65
- end
66
-
67
- it 'matches arguments with nested hash' do
68
- source = '{ user_id: user.id }'
69
- node = parse(source)
70
- expect(node).to be_match(
71
- type: 'hash',
72
- user_id_value: {
73
- type: 'send',
74
- receiver: { type: 'send', message: 'user' },
75
- message: 'id'
76
- }
77
- )
78
- end
79
-
80
- it 'matches arguments contain' do
81
- source = 'def slow(foo, bar, &block); end'
82
- node = parse(source)
83
- expect(node).to be_match(type: 'def', arguments: { contain: '&block' })
84
- end
85
-
86
- it 'matches not' do
87
- source = 'class Synvert; end'
88
- node = parse(source)
89
- expect(node).not_to be_match(type: 'class', name: { not: 'Synvert' })
90
- end
91
-
92
- it 'matches in' do
93
- source = 'FactoryBot.create(:user)'
94
- node = parse(source)
95
- expect(node).to be_match(type: 'send', message: { in: %i[create build] })
96
- end
97
-
98
- it 'matches not_in' do
99
- source = 'FactoryBot.create(:user)'
100
- node = parse(source)
101
- expect(node).not_to be_match(type: 'send', message: { not_in: %i[create build] })
102
- end
103
-
104
- it 'matches gt' do
105
- source = 'foobar(foo, bar)'
106
- node = parse(source)
107
- expect(node).to be_match(type: 'send', arguments: { size: { gt: 1 } })
108
- expect(node).not_to be_match(type: 'send', arguments: { size: { gt: 2 } })
109
- end
110
-
111
- it 'matches gte' do
112
- source = 'foobar(foo, bar)'
113
- node = parse(source)
114
- expect(node).to be_match(type: 'send', arguments: { size: { gte: 2 } })
115
- expect(node).not_to be_match(type: 'send', arguments: { size: { gte: 3 } })
116
- end
117
-
118
- it 'matches lt' do
119
- source = 'foobar(foo, bar)'
120
- node = parse(source)
121
- expect(node).to be_match(type: 'send', arguments: { size: { lt: 3 } })
122
- expect(node).not_to be_match(type: 'send', arguments: { size: { lt: 2 } })
123
- end
124
-
125
- it 'matches lte' do
126
- source = 'foobar(foo, bar)'
127
- node = parse(source)
128
- expect(node).to be_match(type: 'send', arguments: { size: { lte: 2 } })
129
- expect(node).not_to be_match(type: 'send', arguments: { size: { lte: 1 } })
130
- end
131
- end
132
-
133
6
  describe '#strip_curly_braces' do
134
7
  context 'hash node' do
135
8
  it 'removes curly braces' do
@@ -232,47 +105,4 @@ describe Parser::AST::Node do
232
105
  end
233
106
  end
234
107
  end
235
-
236
- describe '#to_hash' do
237
- it 'gets hash' do
238
- node = parse(<<~EOS)
239
- class Synvert
240
- def foobar(foo, bar)
241
- { foo => bar }
242
- end
243
- end
244
- EOS
245
- expect(node.to_hash).to eq(
246
- {
247
- type: :class,
248
- parent_class: nil,
249
- name: {
250
- type: :const,
251
- parent_const: nil,
252
- name: :Synvert
253
- },
254
- body: [
255
- {
256
- type: :def,
257
- name: :foobar,
258
- arguments: [
259
- { type: :arg, name: :foo },
260
- { type: :arg, name: :bar }
261
- ],
262
- body: [
263
- {
264
- type: :hash,
265
- pairs: {
266
- type: :pair,
267
- key: { type: :lvar, name: :foo },
268
- value: { type: :lvar, name: :bar }
269
- }
270
- }
271
- ]
272
- }
273
- ]
274
- }
275
- )
276
- end
277
- end
278
108
  end
@@ -12,16 +12,27 @@ module Synvert::Core
12
12
  it 'parses find_node' do
13
13
  scope = double
14
14
  block = proc {}
15
- expect(Rewriter::QueryScope).to receive(:new).with(instance, '.send[message=create]', &block).and_return(scope)
15
+ expect(Rewriter::QueryScope).to receive(:new).with(instance, '.send[message=create]', {}, &block).and_return(scope)
16
16
  expect(scope).to receive(:process)
17
17
  instance.find_node('.send[message=create]', &block)
18
18
  end
19
19
 
20
+ it 'raises ParseError when parsing invalid nql' do
21
+ block = proc {}
22
+ expect {
23
+ instance.find_node('synvert', &block)
24
+ }.to raise_error(NodeQuery::Compiler::ParseError)
25
+
26
+ expect {
27
+ instance.find_node('.block <caller.arguments> .hash', &block)
28
+ }.to raise_error(NodeQuery::Compiler::ParseError)
29
+ end
30
+
20
31
  it 'parses within_node' do
21
32
  scope = double
22
33
  block = proc {}
23
34
  expect(Rewriter::WithinScope).to receive(:new)
24
- .with(instance, { type: 'send', message: 'create' }, { stop_when_match: false, direct: false }, &block)
35
+ .with(instance, { type: 'send', message: 'create' }, {}, &block)
25
36
  .and_return(scope)
26
37
  expect(scope).to receive(:process)
27
38
  instance.within_node(type: 'send', message: 'create', &block)
@@ -31,30 +42,20 @@ module Synvert::Core
31
42
  scope = double
32
43
  block = proc {}
33
44
  expect(Rewriter::WithinScope).to receive(:new)
34
- .with(instance, { type: 'send', message: 'create' }, { stop_when_match: false, direct: false }, &block)
45
+ .with(instance, { type: 'send', message: 'create' }, {}, &block)
35
46
  .and_return(scope)
36
47
  expect(scope).to receive(:process)
37
48
  instance.with_node(type: 'send', message: 'create', &block)
38
49
  end
39
50
 
40
- it 'parses within_node with stop_when_match true' do
41
- scope = double
42
- block = proc {}
43
- expect(Rewriter::WithinScope).to receive(:new)
44
- .with(instance, { type: 'send', message: 'create' }, { stop_when_match: true, direct: false }, &block)
45
- .and_return(scope)
46
- expect(scope).to receive(:process)
47
- instance.within_node({ type: 'send', message: 'create' }, { stop_when_match: true }, &block)
48
- end
49
-
50
- it 'parses within_node with direct true' do
51
+ it 'parses within_node with stop_at_first_match true' do
51
52
  scope = double
52
53
  block = proc {}
53
54
  expect(Rewriter::WithinScope).to receive(:new)
54
- .with(instance, { type: 'send', message: 'create' }, { stop_when_match: false, direct: true }, &block)
55
+ .with(instance, { type: 'send', message: 'create' }, { stop_at_first_match: true }, &block)
55
56
  .and_return(scope)
56
57
  expect(scope).to receive(:process)
57
- instance.within_node({ type: 'send', message: 'create' }, { direct: true }, &block)
58
+ instance.within_node({ type: 'send', message: 'create' }, { stop_at_first_match: true }, &block)
58
59
  end
59
60
 
60
61
  it 'parses goto_node' do
@@ -55,18 +55,6 @@ module Synvert::Core
55
55
  expect(block_nodes.size).to eq 2
56
56
  end
57
57
 
58
- it 'raises ParseError' do
59
- scope = described_class.new(instance, 'synvert') {}
60
- expect {
61
- scope.process
62
- }.to raise_error(NodeQuery::Compiler::ParseError)
63
-
64
- scope = described_class.new(instance, '.block <caller.arguments> .hash') {}
65
- expect {
66
- scope.process
67
- }.to raise_error(NodeQuery::Compiler::ParseError)
68
- end
69
-
70
58
  it 'raises InvalidOperatorError' do
71
59
  scope = described_class.new(instance, '.send[receiver IN FactoryGirl]') {}
72
60
  expect {
@@ -9,9 +9,11 @@ module Synvert::Core
9
9
  Rewriter::Instance.new(rewriter, 'file pattern')
10
10
  }
11
11
  let(:source) { <<~EOS }
12
- describe Post do
13
- it 'gets post' do
14
- FactoryGirl.create :post
12
+ describe User do
13
+ describe 'create' do
14
+ it 'creates user' do
15
+ FactoryGirl.create :user
16
+ end
15
17
  end
16
18
  end
17
19
  EOS
@@ -39,7 +41,7 @@ module Synvert::Core
39
41
  type: 'send',
40
42
  receiver: 'FactoryGirl',
41
43
  message: 'create',
42
- arguments: [':post'] do
44
+ arguments: [':user'] do
43
45
  run = true
44
46
  type_in_scope = node.type
45
47
  end
@@ -52,27 +54,37 @@ module Synvert::Core
52
54
  it 'matches multiple block nodes' do
53
55
  block_nodes = []
54
56
  scope =
55
- Rewriter::WithinScope.new(instance, { type: 'block' }, { stop_when_match: false }) do
57
+ Rewriter::WithinScope.new(instance, { type: 'block' }) do
58
+ block_nodes << node
59
+ end
60
+ scope.process
61
+ expect(block_nodes.size).to eq 3
62
+ end
63
+
64
+ it 'matches only 2 block nodes if including_self is false' do
65
+ block_nodes = []
66
+ scope =
67
+ Rewriter::WithinScope.new(instance, { type: 'block' }, { including_self: false }) do
56
68
  block_nodes << node
57
69
  end
58
70
  scope.process
59
71
  expect(block_nodes.size).to eq 2
60
72
  end
61
73
 
62
- it 'matches only one block node if no recursive' do
74
+ it 'matches only one block node if recursive is false' do
63
75
  block_nodes = []
64
76
  scope =
65
- Rewriter::WithinScope.new(instance, { type: 'block' }, { stop_when_match: true }) do
77
+ Rewriter::WithinScope.new(instance, { type: 'block' }, { recursive: false }) do
66
78
  block_nodes << node
67
79
  end
68
80
  scope.process
69
81
  expect(block_nodes.size).to eq 1
70
82
  end
71
83
 
72
- it 'matches only one direct node' do
84
+ it 'matches only one block node if stop_at_first_match is true' do
73
85
  block_nodes = []
74
86
  scope =
75
- Rewriter::WithinScope.new(instance, { type: 'block' }, { direct: true }) do
87
+ Rewriter::WithinScope.new(instance, { type: 'block' }, { stop_at_first_match: true }) do
76
88
  block_nodes << node
77
89
  end
78
90
  scope.process
@@ -19,7 +19,7 @@ Gem::Specification.new do |spec|
19
19
  spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
20
20
  spec.require_paths = ["lib"]
21
21
 
22
- spec.add_runtime_dependency "activesupport"
22
+ spec.add_runtime_dependency "activesupport", "< 7.0.0"
23
23
  spec.add_runtime_dependency "erubis"
24
24
  spec.add_runtime_dependency "node_query"
25
25
  spec.add_runtime_dependency "node_mutation"
metadata CHANGED
@@ -1,29 +1,29 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: synvert-core
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.5.0
4
+ version: 1.6.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Richard Huang
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2022-07-02 00:00:00.000000000 Z
11
+ date: 2022-09-15 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activesupport
15
15
  requirement: !ruby/object:Gem::Requirement
16
16
  requirements:
17
- - - ">="
17
+ - - "<"
18
18
  - !ruby/object:Gem::Version
19
- version: '0'
19
+ version: 7.0.0
20
20
  type: :runtime
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
- - - ">="
24
+ - - "<"
25
25
  - !ruby/object:Gem::Version
26
- version: '0'
26
+ version: 7.0.0
27
27
  - !ruby/object:Gem::Dependency
28
28
  name: erubis
29
29
  requirement: !ruby/object:Gem::Requirement
@@ -106,6 +106,7 @@ files:
106
106
  - ".rspec"
107
107
  - CHANGELOG.md
108
108
  - Gemfile
109
+ - Gemfile.lock
109
110
  - Guardfile
110
111
  - LICENSE.txt
111
112
  - README.md