-
Notifications
You must be signed in to change notification settings - Fork 52
/
its.rb
204 lines (161 loc) · 5.1 KB
/
its.rb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
# coding: utf-8
require 'transpec/syntax'
require 'transpec/syntax/mixin/examplish'
require 'transpec/syntax/mixin/send'
require 'transpec/util'
module Transpec
class Syntax
class Its < Syntax
include Mixin::Examplish, Mixin::Send, Util
define_dynamic_analysis do |rewriter|
key = :project_requires_its?
code = 'defined?(RSpec::Its)'
rewriter.register_request(node, key, code, :context)
end
def dynamic_analysis_target?
super && receiver_node.nil? && method_name == :its
end
def conversion_target?
super && !runtime_data[node, :project_requires_its?]
end
def convert_to_describe_subject_it!
insert_before(beginning_of_line_range(block_node), front_code)
replace(range_from_its_to_front_of_block, additional_indentation_for_it + 'it ')
insert_after(block_node.loc.expression, rear_code)
increment_block_base_indentation!
add_record
end
def insert_blank_line_above!
insert_after(beginning_of_line_range(node), "\n")
end
def attribute_expression
@attribute_expression ||= AttributeExpression.new(attribute_node)
end
def attributes
attribute_expression.attributes
end
alias_method :attribute_node, :arg_node
def block_node
node.parent
end
def description?
false
end
private
def front_code
code = ''
if !previous_line_is_blank? && previous_and_current_line_are_same_indentation_level?
code << "\n"
end
attributes.each_with_index do |attribute, index|
indentation = block_base_indentation + ' ' * index
code << indentation + "describe #{attribute.description} do\n"
code << indentation + " subject { super()#{attribute.selector} }\n"
end
code
end
def rear_code
code = ''
attributes.size.downto(1) do |level|
indentation = block_base_indentation + ' ' * (level - 1)
code << "\n"
code << "#{indentation}end"
end
code
end
def additional_indentation_for_it
' ' * attributes.size
end
def previous_line_is_blank?
return false unless previous_line_source
previous_line_source.empty? || previous_line_source.match(/\A\s*\Z/)
end
def previous_and_current_line_are_same_indentation_level?
indentation_of_line(previous_line_source) == block_base_indentation
end
def previous_line_source
expression_range.source_buffer.source_line(expression_range.line - 1)
rescue IndexError
nil
end
# TODO: This is an ad-hoc solution for nested indentation manipulations.
def block_base_indentation
block_node.metadata[:indentation] ||= indentation_of_line(node)
end
def increment_block_base_indentation!
block_node.metadata[:indentation] = block_base_indentation + ' '
end
def range_from_its_to_front_of_block
expression_range.join(block_node.loc.begin.begin)
end
def add_record
super(RecordBuilder.build(self))
end
class AttributeExpression
attr_reader :node
def initialize(node)
@node = node
end
def brackets?
node.array_type?
end
def literal?
Util.literal?(node)
end
def attributes
@attributes ||= if brackets?
brackets_attributes
else
non_brackets_attributes
end
end
private
def brackets_attributes
selector = node.loc.expression.source
description = literal? ? quote(selector) : selector
[Attribute.new(selector, description)]
end
def non_brackets_attributes
if literal?
expression = node.children.first.to_s
chained_names = expression.split('.')
chained_names.map do |name|
Attribute.new(".#{name}", quote("##{name}"))
end
else
source = node.loc.expression.source
selector = ".send(#{source})"
[Attribute.new(selector, source)]
end
end
def quote(string)
if string.include?("'")
'"' + string + '"'
elsif string.include?('"')
string.inspect
else
"'" + string + "'"
end
end
end
Attribute = Struct.new(:selector, :description)
class RecordBuilder < Transpec::RecordBuilder
param_names :its
def old_syntax
if its.attribute_expression.brackets?
'its([:key]) { }'
else
'its(:attr) { }'
end
end
def new_syntax
if its.attribute_expression.brackets?
"describe '[:key]' do subject { super()[:key] }; it { } end"
else
"describe '#attr' do subject { super().attr }; it { } end"
end
end
end
end
end
end