rails5-spec-converter 1.0.15 → 1.0.16

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
  SHA1:
3
- metadata.gz: 8f809b54f88c799e62a3da375d7e1c6386a24e71
4
- data.tar.gz: 853ab71eb42ad2ff5f6834f25596e6f92a45a1d9
3
+ metadata.gz: da659c54addaff82abd519274ed54477c401438a
4
+ data.tar.gz: 217e06555325fb97b3e10fdfc8cae7c44f096de5
5
5
  SHA512:
6
- metadata.gz: 169d693d60ed16a8f81b30cf0e2e77d1cb019451b0ab64b5224bfaf2b3ed9c3a610fde5723a8d322b87c14aa8d70b8f9fb0943e1d979dbf1f1a5d633decd3f70
7
- data.tar.gz: b2683718f15845812229c662c977302d58bcfa18a1d47b1efe95cf679194b5678fae3b9236d66f7fd962366c251d738f8787c85f6e1c8efe2421a3c02b9f3d73
6
+ metadata.gz: 1c2ced79435fb5d7dd440e863c46a49be07e15a9ef648173342e0680235625e4be3011890ff3afd5fd1e69acef81b6d3f98d2216346de2646db51329d1a47454
7
+ data.tar.gz: 2c478082acb31b1db660f1c35ae12e6a376921ab8acd81e089f7bdf38d6253027a594a8e0ee34550cbc90d497ed50578a8259271125d1408f1e97d2626f26f20
@@ -0,0 +1,256 @@
1
+ require 'rails5/spec_converter/node_textifier'
2
+
3
+ class HashRewriter
4
+ # technically format is special
5
+ ALLOWED_KWARG_KEYS = %i(params session flash method body xhr format)
6
+
7
+ attr_reader :hash_node, :original_indent
8
+
9
+ def initialize(content:, hash_node:, original_indent:, options:)
10
+ @options = options
11
+ @content = content
12
+ @hash_node = hash_node
13
+ @original_indent = original_indent
14
+ @textifier = NodeTextifier.new(@content)
15
+ partition_params(@hash_node)
16
+ end
17
+
18
+ def rewritten_params_hash
19
+ return if @pairs_that_belong_in_params.length == 0
20
+
21
+ rewritten_hashes = []
22
+
23
+ warn_if_inconsistent_indentation
24
+
25
+ if multiline? && should_wrap_rewritten_hash_in_curly_braces?
26
+ params_hash = restring_hash(
27
+ @pairs_that_belong_in_params,
28
+ joiner: ",\n"
29
+ )
30
+
31
+ other_hash = restring_hash(
32
+ @pairs_that_belong_outside_params,
33
+ joiner: ",\n"
34
+ )
35
+
36
+ optional_comma = has_trailing_comma?(hash_node) ? ',' : ''
37
+ new_wrapped_hash_content = wrap_and_indent(
38
+ "{",
39
+ "}",
40
+ [
41
+ wrap_and_indent(
42
+ "params: {",
43
+ "}#{optional_comma}",
44
+ params_hash,
45
+ @options.indent
46
+ ),
47
+ "#{other_hash}"
48
+ ].join("\n"),
49
+ @options.indent
50
+ )
51
+ return add_indent(new_wrapped_hash_content, original_indent, skip_first_line: true)
52
+ elsif multiline? && should_try_to_rewrite_multiline_hash?
53
+ params_hash = appropriately_indented_params_hash(
54
+ pairs: @pairs_that_belong_in_params
55
+ )
56
+
57
+ rewritten_hashes << "params: #{params_hash}"
58
+ else
59
+ curly_sep = determine_curly_sep(hash_node)
60
+ rewritten_hashes << "params: {#{curly_sep}#{restring_hash(@pairs_that_belong_in_params)}#{curly_sep}}"
61
+ end
62
+
63
+ if has_keys_outside_params?
64
+ rewritten_hashes << restring_hash(
65
+ @pairs_that_belong_outside_params,
66
+ joiner: first_joiner_between_pairs
67
+ )
68
+ end
69
+
70
+ rewritten_hashes.join(first_joiner_between_pairs)
71
+ end
72
+
73
+ def should_rewrite_hash?
74
+ @pairs_that_belong_in_params.length > 0
75
+ end
76
+
77
+ def has_keys_outside_params?
78
+ @pairs_that_belong_outside_params.length > 0
79
+ end
80
+
81
+ def should_wrap_rewritten_hash_in_curly_braces?
82
+ multiline? && has_keys_outside_params? && @textifier.node_to_string(hash_node) =~ /^{\n/
83
+ end
84
+
85
+ def should_try_to_rewrite_multiline_hash?
86
+ return false unless multiline?
87
+ return true unless first_joiner_between_pairs
88
+ first_joiner_between_pairs =~ /\n/
89
+ end
90
+
91
+ private
92
+
93
+ def partition_params(hash_node)
94
+ @pairs_that_belong_in_params = []
95
+ @pairs_that_belong_outside_params = []
96
+
97
+ hash_node.children.each do |pair|
98
+ key = pair.children[0].children[0]
99
+
100
+ if ALLOWED_KWARG_KEYS.include?(key)
101
+ @pairs_that_belong_outside_params << pair
102
+ else
103
+ @pairs_that_belong_in_params << pair
104
+ end
105
+ end
106
+ end
107
+
108
+ def has_trailing_comma?(hash_node)
109
+ @textifier.text_after_last_pair(hash_node) =~ /,/
110
+ end
111
+
112
+ def indent_before_first_pair(hash_node)
113
+ return nil unless hash_node.children.length > 0
114
+
115
+ extract_indent(@textifier.text_before_first_pair(hash_node))
116
+ end
117
+
118
+ def indent_after_last_pair(hash_node)
119
+ return nil unless hash_node.children.length > 0
120
+
121
+ extract_indent(@textifier.text_after_last_pair(hash_node))
122
+ end
123
+
124
+ def multiline?
125
+ @textifier.node_to_string(hash_node).include?("\n")
126
+ end
127
+
128
+ def indent_of_first_value_if_multiline(hash_node)
129
+ return nil if hash_node.children.length == 0
130
+ return nil unless hash_node.children[0].pair_type?
131
+
132
+ first_value = hash_node.children[0].children[1]
133
+ return nil unless first_value.hash_type? || first_value.array_type?
134
+ value_str_lines = @textifier.node_to_string(first_value).split("\n")
135
+ return nil if value_str_lines.length == 1
136
+ return nil unless value_str_lines[0].match(/[\s\[{]/)
137
+
138
+ value_str_lines[1].match(/^(\s*)/)[1].sub(original_indent, '')
139
+ end
140
+
141
+ def additional_indent(hash_node)
142
+ return nil if indent_before_first_pair(hash_node)
143
+
144
+ joiner = first_joiner_between_pairs
145
+ joiner && joiner.include?("\n") ? @options.indent : nil
146
+ end
147
+
148
+ def existing_indent(hash_node)
149
+ text_before_hash = @textifier.text_before_node(hash_node)
150
+ whitespace_indent = extract_indent(text_before_hash)
151
+ return whitespace_indent if whitespace_indent
152
+
153
+ return indent_before_first_pair(hash_node) if indent_before_first_pair(hash_node)
154
+
155
+ joiner = first_joiner_between_pairs
156
+ extract_indent(joiner) || ''
157
+ end
158
+
159
+ def no_space_after_curly?(hash_node)
160
+ hash_node.parent.loc.expression.source.match(/{\S/)
161
+ end
162
+
163
+ def texts_between_pairs
164
+ return @texts_between if @texts_between
165
+
166
+ @texts_between = []
167
+ hash_node.children[0..-2].each_with_index do |pair, index|
168
+ next_pair = hash_node.children[index + 1]
169
+ @texts_between << @textifier.text_between_siblings(pair, next_pair)
170
+ end
171
+ @texts_between
172
+ end
173
+
174
+ def first_joiner_between_pairs
175
+ texts_between_pairs[0]
176
+ end
177
+
178
+ def has_inconsistent_indentation?
179
+ texts_between_pairs.uniq.length > 1
180
+ end
181
+
182
+ def warn_if_inconsistent_indentation
183
+ return unless has_inconsistent_indentation?
184
+
185
+ log "Inconsistent whitespace between hash pairs, using the first separator (#{texts_between_pairs[0].inspect})."
186
+ log "Seen when processing this expression: \n```\n#{hash_node.loc.expression.source}\n```\n\n"
187
+ end
188
+
189
+ def appropriately_indented_params_hash(pairs:)
190
+ outer_indent = existing_indent(hash_node)
191
+ middle_indent = indent_of_first_value_if_multiline(hash_node)
192
+ inner_indent = additional_indent(hash_node)
193
+
194
+ restrung_hash = restring_hash(
195
+ pairs,
196
+ indent: outer_indent + (inner_indent || ''),
197
+ joiner: ",\n"
198
+ )
199
+ if middle_indent
200
+ restrung_hash = original_indent + add_indent(restrung_hash, middle_indent)
201
+ end
202
+ final_brace_indent = if middle_indent
203
+ original_indent
204
+ else
205
+ indent_after_last_pair(hash_node) || outer_indent
206
+ end
207
+ "{\n#{restrung_hash}\n#{final_brace_indent}}"
208
+ end
209
+
210
+ def determine_curly_sep(hash_node)
211
+ return ' ' if @options.hash_spacing == true
212
+ return '' if @options.hash_spacing == false
213
+
214
+ no_space_after_curly?(hash_node) ? '' : ' '
215
+ end
216
+
217
+ def restring_hash(pairs, joiner: ", ", indent: '')
218
+ hash_string = pairs.map { |pair| "#{indent}#{pair.loc.expression.source}" }.join(joiner)
219
+ if has_trailing_comma?(hash_node)
220
+ hash_string + ','
221
+ else
222
+ hash_string
223
+ end
224
+ end
225
+
226
+ def add_indent_and_curly_braces(str, indent)
227
+ "{\n#{add_indent(str, indent)}\n}"
228
+ end
229
+
230
+ def wrap_and_indent(start_string, end_string, inner_string, indent)
231
+ "#{start_string}\n#{add_indent(inner_string, indent)}\n#{end_string}"
232
+ end
233
+
234
+ def add_indent(str, indent, skip_first_line: false)
235
+ str.split("\n").each_with_index.map do |line, index|
236
+ if index.zero? && skip_first_line
237
+ line
238
+ else
239
+ indent + line
240
+ end
241
+ end.join("\n")
242
+ end
243
+
244
+ def extract_indent(str)
245
+ return unless str
246
+
247
+ match = str.match("\n(\s*)")
248
+ match[1] if match
249
+ end
250
+
251
+ def log(str)
252
+ return if @options.quiet?
253
+
254
+ puts str
255
+ end
256
+ end
@@ -0,0 +1,28 @@
1
+ class NodeTextifier
2
+ def initialize(content)
3
+ @content = content
4
+ end
5
+
6
+ def text_before_first_pair(hash_node)
7
+ @content[hash_node.loc.expression.begin_pos...hash_node.children.first.loc.expression.begin_pos]
8
+ end
9
+
10
+ def text_after_last_pair(hash_node)
11
+ @content[hash_node.children.last.loc.expression.end_pos...hash_node.loc.expression.end_pos]
12
+ end
13
+
14
+ def text_before_node(node)
15
+ previous_sibling = node.parent.children[node.sibling_index - 1]
16
+ return nil unless previous_sibling.loc.expression
17
+
18
+ text_between_siblings(previous_sibling, node)
19
+ end
20
+
21
+ def text_between_siblings(node1, node2)
22
+ @content[node1.loc.expression.end_pos...node2.loc.expression.begin_pos]
23
+ end
24
+
25
+ def node_to_string(node)
26
+ @content[node.loc.expression.begin_pos...node.loc.expression.end_pos]
27
+ end
28
+ end
@@ -1,13 +1,11 @@
1
1
  require 'parser/current'
2
2
  require 'astrolabe/builder'
3
3
  require 'rails5/spec_converter/text_transformer_options'
4
- require 'rails5/spec_converter/hash_node_text_analyzer'
4
+ require 'rails5/spec_converter/hash_rewriter'
5
5
 
6
6
  module Rails5
7
7
  module SpecConverter
8
8
  HTTP_VERBS = %i(get post put patch delete)
9
- # technically format is special
10
- ALLOWED_KWARG_KEYS = %i(params session flash method body xhr format)
11
9
 
12
10
  class TextTransformer
13
11
  def initialize(content, options = TextTransformerOptions.new)
@@ -41,10 +39,17 @@ module Rails5
41
39
  next unless @options.wrap_ambiguous_params?
42
40
  end
43
41
 
44
- write_params_hash(
42
+ hash_rewriter = HashRewriter.new(
43
+ content: @content,
44
+ options: @options,
45
45
  hash_node: args[0],
46
46
  original_indent: node.loc.expression.source_line.match(/^(\s*)/)[1]
47
47
  )
48
+
49
+ @source_rewriter.replace(
50
+ args[0].loc.expression,
51
+ hash_rewriter.rewritten_params_hash
52
+ ) if hash_rewriter.should_rewrite_hash?
48
53
  end
49
54
  else
50
55
  warn_about_ambiguous_params(node) if @options.warn_about_ambiguous_params?
@@ -81,156 +86,10 @@ module Rails5
81
86
  hash_node.children.any? { |node| node.kwsplat_type? }
82
87
  end
83
88
 
84
- def has_trailing_comma?(hash_node)
85
- HashNodeTextAnalyzer.new(@content, hash_node).text_after_last_pair =~ /,/
86
- end
87
-
88
89
  def has_key?(hash_node, key)
89
90
  hash_node.children.any? { |pair| pair.children[0].children[0] == key }
90
91
  end
91
92
 
92
- def write_params_hash(hash_node:, original_indent: )
93
- pairs_that_belong_in_params, pairs_that_belong_outside_params = partition_params(hash_node)
94
- use_trailing_comma = has_trailing_comma?(hash_node)
95
-
96
- if pairs_that_belong_in_params.length > 0
97
- joiner = joiner_between_pairs(hash_node)
98
- params_hash = appropriately_spaced_params_hash(
99
- hash_node: hash_node,
100
- pairs: pairs_that_belong_in_params,
101
- original_indent: original_indent,
102
- use_trailing_comma: use_trailing_comma
103
- )
104
-
105
- rewritten_hashes = ["params: #{params_hash}"]
106
- if pairs_that_belong_outside_params.length > 0
107
- rewritten_hashes << restring_hash(
108
- pairs_that_belong_outside_params,
109
- joiner: joiner,
110
- use_trailing_comma: use_trailing_comma
111
- )
112
- end
113
- @source_rewriter.replace(
114
- hash_node.loc.expression,
115
- rewritten_hashes.join(joiner)
116
- )
117
- end
118
- end
119
-
120
- def partition_params(hash_node)
121
- pairs_that_belong_in_params = []
122
- pairs_that_belong_outside_params = []
123
-
124
- hash_node.children.each do |pair|
125
- key = pair.children[0].children[0]
126
-
127
- if ALLOWED_KWARG_KEYS.include?(key)
128
- pairs_that_belong_outside_params << pair
129
- else
130
- pairs_that_belong_in_params << pair
131
- end
132
- end
133
- return pairs_that_belong_in_params, pairs_that_belong_outside_params
134
- end
135
-
136
- def indent_before_first_pair(hash_node)
137
- return nil unless hash_node.children.length > 0
138
-
139
- extract_indent(HashNodeTextAnalyzer.new(@content, hash_node).text_before_first_pair)
140
- end
141
-
142
- def indent_after_last_pair(hash_node)
143
- return nil unless hash_node.children.length > 0
144
-
145
- extract_indent(HashNodeTextAnalyzer.new(@content, hash_node).text_after_last_pair)
146
- end
147
-
148
- def indent_of_first_value_if_multiline(hash_node, original_indent)
149
- return nil if hash_node.children.length == 0
150
- return nil unless hash_node.children[0].pair_type?
151
-
152
- first_value = hash_node.children[0].children[1]
153
- return nil unless first_value.hash_type? || first_value.array_type?
154
- value_str_lines = node_to_string(first_value).split("\n")
155
- return nil if value_str_lines.length == 1
156
- return nil unless value_str_lines[0].match(/[\s\[{]/)
157
-
158
- value_str_lines[1].match(/^(\s*)/)[1].sub(original_indent, '')
159
- end
160
-
161
- def additional_indent(hash_node)
162
- return nil if indent_before_first_pair(hash_node)
163
-
164
- joiner = joiner_between_pairs(hash_node)
165
- joiner && joiner.include?("\n") ? @options.indent : nil
166
- end
167
-
168
- def existing_indent(hash_node)
169
- text_before_hash = text_before_node(hash_node)
170
- whitespace_indent = extract_indent(text_before_hash)
171
- return whitespace_indent if whitespace_indent
172
-
173
- return indent_before_first_pair(hash_node) if indent_before_first_pair(hash_node)
174
-
175
- joiner = joiner_between_pairs(hash_node)
176
- extract_indent(joiner) || ''
177
- end
178
-
179
- def no_space_after_curly?(hash_node)
180
- hash_node.parent.loc.expression.source.match(/{\S/)
181
- end
182
-
183
- def joiner_between_pairs(hash_node)
184
- texts_between = []
185
- hash_node.children[0..-2].each_with_index do |pair, index|
186
- next_pair = hash_node.children[index + 1]
187
- texts_between << text_between_siblings(pair, next_pair)
188
- end
189
- if texts_between.uniq.length > 1
190
- log "Inconsistent whitespace between hash pairs, using the first separator (#{texts_between[0].inspect})."
191
- log "Seen when processing this expression: \n```\n#{hash_node.loc.expression.source}\n```\n\n"
192
- end
193
- texts_between[0]
194
- end
195
-
196
- def text_between_siblings(node1, node2)
197
- @content[node1.loc.expression.end_pos...node2.loc.expression.begin_pos]
198
- end
199
-
200
- def appropriately_spaced_params_hash(hash_node:, pairs:, original_indent:, use_trailing_comma:)
201
- outer_indent = existing_indent(hash_node)
202
- middle_indent = indent_of_first_value_if_multiline(hash_node, original_indent)
203
- inner_indent = additional_indent(hash_node)
204
-
205
- if inner_indent || middle_indent || indent_before_first_pair(hash_node)
206
- restrung_hash = restring_hash(
207
- pairs,
208
- indent: outer_indent + (inner_indent || ''),
209
- joiner: ",\n",
210
- use_trailing_comma: use_trailing_comma
211
- )
212
- if middle_indent
213
- restrung_hash = original_indent + add_indent(restrung_hash, middle_indent)
214
- end
215
- final_brace_indent = if middle_indent
216
- original_indent
217
- else
218
- indent_after_last_pair(hash_node) || outer_indent
219
- end
220
- "{\n#{restrung_hash}\n#{final_brace_indent}}"
221
- else
222
- curly_sep = determine_curly_sep(hash_node)
223
- "{#{curly_sep}#{restring_hash(pairs, use_trailing_comma: use_trailing_comma)}#{curly_sep}}"
224
- end
225
- end
226
-
227
- def determine_curly_sep(hash_node)
228
- return ' ' if @options.hash_spacing == true
229
- return '' if @options.hash_spacing == false
230
-
231
- no_space_after_curly?(hash_node) ? '' : ' '
232
- end
233
-
234
93
  def wrap_arg(node, key)
235
94
  node_loc = node.loc.expression
236
95
  node_source = node_loc.source
@@ -240,37 +99,6 @@ module Rails5
240
99
  @source_rewriter.replace(node_loc, "#{key}: #{node_source}")
241
100
  end
242
101
 
243
- def restring_hash(pairs, joiner: ", ", indent: '', use_trailing_comma: false)
244
- hash_string = pairs.map { |pair| "#{indent}#{pair.loc.expression.source}" }.join(joiner)
245
- if use_trailing_comma
246
- hash_string + ','
247
- else
248
- hash_string
249
- end
250
- end
251
-
252
- def add_indent(str, indent)
253
- str.split("\n").map { |line| indent + line }.join("\n")
254
- end
255
-
256
- def extract_indent(str)
257
- return unless str
258
-
259
- match = str.match("\n(\s*)")
260
- match[1] if match
261
- end
262
-
263
- def text_before_node(node)
264
- previous_sibling = node.parent.children[node.sibling_index - 1]
265
- return nil unless previous_sibling.loc.expression
266
-
267
- text_between_siblings(previous_sibling, node)
268
- end
269
-
270
- def node_to_string(node)
271
- @content[node.loc.expression.begin_pos...node.loc.expression.end_pos]
272
- end
273
-
274
102
  def warn_about_ambiguous_params(node)
275
103
  log "Ambiguous params found"
276
104
  log "#{@options.file_path}:#{node.loc.line}" if @options.file_path
@@ -1,5 +1,5 @@
1
1
  module Rails5
2
2
  module SpecConverter
3
- VERSION = "1.0.15"
3
+ VERSION = "1.0.16"
4
4
  end
5
5
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rails5-spec-converter
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.15
4
+ version: 1.0.16
5
5
  platform: ruby
6
6
  authors:
7
7
  - Travis Grathwell
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2016-09-12 00:00:00.000000000 Z
11
+ date: 2016-09-22 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: parser
@@ -116,7 +116,8 @@ files:
116
116
  - exe/rails5-spec-converter
117
117
  - lib/rails5/spec_converter.rb
118
118
  - lib/rails5/spec_converter/cli.rb
119
- - lib/rails5/spec_converter/hash_node_text_analyzer.rb
119
+ - lib/rails5/spec_converter/hash_rewriter.rb
120
+ - lib/rails5/spec_converter/node_textifier.rb
120
121
  - lib/rails5/spec_converter/text_transformer.rb
121
122
  - lib/rails5/spec_converter/text_transformer_options.rb
122
123
  - lib/rails5/spec_converter/version.rb
@@ -1,16 +0,0 @@
1
- class HashNodeTextAnalyzer
2
- attr_reader :content, :hash_node
3
-
4
- def initialize(content, hash_node)
5
- @content = content
6
- @hash_node = hash_node
7
- end
8
-
9
- def text_before_first_pair
10
- content[hash_node.loc.expression.begin_pos...hash_node.children.first.loc.expression.begin_pos]
11
- end
12
-
13
- def text_after_last_pair
14
- content[hash_node.children.last.loc.expression.end_pos...hash_node.loc.expression.end_pos]
15
- end
16
- end