tailor 0.1.5 → 1.0.0.alpha

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.
Files changed (124) hide show
  1. data/.gitignore +9 -1
  2. data/.rspec +2 -1
  3. data/.tailor +6 -0
  4. data/Gemfile.lock +47 -78
  5. data/{ChangeLog.rdoc → History.rdoc} +0 -0
  6. data/README.rdoc +157 -24
  7. data/Rakefile +0 -9
  8. data/bin/tailor +16 -69
  9. data/features/configurable.feature +78 -0
  10. data/features/horizontal_spacing.feature +262 -0
  11. data/features/indentation.feature +17 -21
  12. data/features/indentation/bad_files_with_no_trailing_newline.feature +90 -0
  13. data/features/indentation/good_files_with_no_trailing_newline.feature +206 -0
  14. data/features/name_detection.feature +72 -0
  15. data/features/step_definitions/indentation_steps.rb +10 -133
  16. data/features/support/env.rb +7 -15
  17. data/features/support/file_cases/horizontal_spacing_cases.rb +265 -0
  18. data/features/support/file_cases/indentation_cases.rb +972 -0
  19. data/features/support/file_cases/naming_cases.rb +52 -0
  20. data/features/support/file_cases/vertical_spacing_cases.rb +70 -0
  21. data/features/support/hooks.rb +8 -0
  22. data/features/support/{1_file_with_bad_operator_spacing → legacy}/bad_op_spacing.rb +0 -0
  23. data/features/support/{1_file_with_bad_ternary_colon_spacing → legacy}/bad_ternary_colon_spacing.rb +0 -0
  24. data/features/support/{1_long_file_with_indentation/my_project.rb → legacy/long_file_with_indentation.rb} +1 -1
  25. data/features/support/world.rb +14 -0
  26. data/features/vertical_spacing.feature +114 -0
  27. data/lib/ext/string_ext.rb +5 -0
  28. data/lib/tailor.rb +6 -252
  29. data/lib/tailor/cli.rb +49 -0
  30. data/lib/tailor/cli/options.rb +251 -0
  31. data/lib/tailor/composite_observable.rb +56 -0
  32. data/lib/tailor/configuration.rb +263 -0
  33. data/lib/tailor/critic.rb +162 -0
  34. data/lib/tailor/formatters/text.rb +126 -0
  35. data/lib/tailor/lexed_line.rb +246 -0
  36. data/lib/tailor/lexer.rb +428 -0
  37. data/lib/tailor/lexer/token.rb +103 -0
  38. data/lib/tailor/lexer_constants.rb +75 -0
  39. data/lib/tailor/logger.rb +28 -0
  40. data/lib/tailor/problem.rb +100 -0
  41. data/lib/tailor/reporter.rb +48 -0
  42. data/lib/tailor/ruler.rb +39 -0
  43. data/lib/tailor/rulers.rb +7 -0
  44. data/lib/tailor/rulers/allow_camel_case_methods_ruler.rb +30 -0
  45. data/lib/tailor/rulers/allow_hard_tabs_ruler.rb +22 -0
  46. data/lib/tailor/rulers/allow_screaming_snake_case_classes_ruler.rb +32 -0
  47. data/lib/tailor/rulers/allow_trailing_line_spaces_ruler.rb +33 -0
  48. data/lib/tailor/rulers/indentation_spaces_ruler.rb +199 -0
  49. data/lib/tailor/rulers/indentation_spaces_ruler/indentation_manager.rb +362 -0
  50. data/lib/tailor/rulers/max_code_lines_in_class_ruler.rb +84 -0
  51. data/lib/tailor/rulers/max_code_lines_in_method_ruler.rb +84 -0
  52. data/lib/tailor/rulers/max_line_length_ruler.rb +31 -0
  53. data/lib/tailor/rulers/spaces_after_comma_ruler.rb +83 -0
  54. data/lib/tailor/rulers/spaces_after_lbrace_ruler.rb +114 -0
  55. data/lib/tailor/rulers/spaces_after_lbracket_ruler.rb +123 -0
  56. data/lib/tailor/rulers/spaces_after_lparen_ruler.rb +116 -0
  57. data/lib/tailor/rulers/spaces_before_comma_ruler.rb +67 -0
  58. data/lib/tailor/rulers/spaces_before_lbrace_ruler.rb +93 -0
  59. data/lib/tailor/rulers/spaces_before_rbrace_ruler.rb +98 -0
  60. data/lib/tailor/rulers/spaces_before_rbracket_ruler.rb +70 -0
  61. data/lib/tailor/rulers/spaces_before_rparen_ruler.rb +70 -0
  62. data/lib/tailor/rulers/spaces_in_empty_braces_ruler.rb +94 -0
  63. data/lib/tailor/rulers/trailing_newlines_ruler.rb +36 -0
  64. data/lib/tailor/runtime_error.rb +3 -0
  65. data/lib/tailor/tailorrc.erb +88 -0
  66. data/lib/tailor/version.rb +2 -2
  67. data/spec/spec_helper.rb +7 -5
  68. data/spec/tailor/cli_spec.rb +94 -0
  69. data/spec/tailor/configuration_spec.rb +147 -0
  70. data/spec/tailor/critic_spec.rb +63 -0
  71. data/spec/tailor/lexed_line_spec.rb +569 -0
  72. data/spec/tailor/lexer/token_spec.rb +46 -0
  73. data/spec/tailor/lexer_spec.rb +181 -0
  74. data/spec/tailor/options_spec.rb +6 -0
  75. data/spec/tailor/problem_spec.rb +74 -0
  76. data/spec/tailor/reporter_spec.rb +53 -0
  77. data/spec/tailor/ruler_spec.rb +56 -0
  78. data/spec/tailor/rulers/indentation_spaces_ruler/indentation_manager_spec.rb +454 -0
  79. data/spec/tailor/rulers/indentation_spaces_ruler_spec.rb +128 -0
  80. data/spec/tailor/rulers/spaces_after_comma_spec.rb +31 -0
  81. data/spec/tailor/rulers/spaces_after_lbrace_ruler_spec.rb +145 -0
  82. data/spec/tailor/rulers/spaces_before_lbrace_ruler_spec.rb +63 -0
  83. data/spec/tailor/rulers/spaces_before_rbrace_ruler_spec.rb +63 -0
  84. data/spec/tailor/rulers_spec.rb +9 -0
  85. data/spec/tailor/version_spec.rb +6 -0
  86. data/spec/tailor_spec.rb +9 -21
  87. data/tailor.gemspec +22 -35
  88. data/tasks/features.rake +7 -0
  89. data/tasks/roodi.rake +9 -0
  90. data/tasks/roodi_config.yaml +14 -0
  91. data/tasks/spec.rake +16 -0
  92. data/tasks/yard.rake +14 -0
  93. metadata +224 -77
  94. data/features/case_checking.feature +0 -38
  95. data/features/spacing.feature +0 -97
  96. data/features/spacing/commas.feature +0 -44
  97. data/features/step_definitions/case_checking_steps.rb +0 -42
  98. data/features/step_definitions/spacing_steps.rb +0 -156
  99. data/features/support/1_file_with_bad_comma_spacing/bad_comma_spacing.rb +0 -43
  100. data/features/support/1_file_with_bad_curly_brace_spacing/bad_curly_brace_spacing.rb +0 -60
  101. data/features/support/1_file_with_bad_parenthesis/bad_parenthesis.rb +0 -4
  102. data/features/support/1_file_with_bad_square_brackets/bad_square_brackets.rb +0 -62
  103. data/features/support/1_file_with_camel_case_class/camel_case_class.rb +0 -5
  104. data/features/support/1_file_with_camel_case_method/camel_case_method.rb +0 -3
  105. data/features/support/1_file_with_hard_tabs/hard_tab.rb +0 -3
  106. data/features/support/1_file_with_long_lines/long_lines.rb +0 -5
  107. data/features/support/1_file_with_snake_case_class/snake_case_class.rb +0 -5
  108. data/features/support/1_file_with_snake_case_method/snake_case_method.rb +0 -3
  109. data/features/support/1_file_with_trailing_whitespace/trailing_whitespace.rb +0 -5
  110. data/features/support/1_good_simple_file/simple_project.rb +0 -5
  111. data/features/support/common.rb +0 -102
  112. data/features/support/matchers.rb +0 -11
  113. data/lib/tailor/file_line.rb +0 -220
  114. data/lib/tailor/indentation.rb +0 -245
  115. data/lib/tailor/spacing.rb +0 -237
  116. data/spec/file_line_spec.rb +0 -70
  117. data/spec/indentation_spec.rb +0 -259
  118. data/spec/spacing/colon_spacing_spec.rb +0 -71
  119. data/spec/spacing/comma_spacing_spec.rb +0 -159
  120. data/spec/spacing/curly_brace_spacing_spec.rb +0 -257
  121. data/spec/spacing/parentheses_spacing_spec.rb +0 -28
  122. data/spec/spacing/square_bracket_spacing_spec.rb +0 -116
  123. data/spec/spacing_spec.rb +0 -167
  124. data/tasks/metrics.rake +0 -23
@@ -0,0 +1,362 @@
1
+ require_relative '../../ruler'
2
+ require_relative '../../logger'
3
+ require_relative '../../lexer_constants'
4
+
5
+ class Tailor
6
+ module Rulers
7
+ class IndentationSpacesRuler < Tailor::Ruler
8
+ class IndentationManager
9
+ include Tailor::LexerConstants
10
+ include Tailor::Logger::Mixin
11
+
12
+ OPEN_EVENT_FOR = {
13
+ on_kw: :on_kw,
14
+ on_rbrace: :on_lbrace,
15
+ on_rbracket: :on_lbracket,
16
+ on_rparen: :on_lparen
17
+ }
18
+
19
+ attr_accessor :amount_to_change_next
20
+ attr_accessor :amount_to_change_this
21
+ attr_accessor :embexpr_beg
22
+
23
+ attr_reader :actual_indentation
24
+ attr_reader :indent_reasons
25
+ attr_reader :tstring_nesting
26
+
27
+ def initialize(spaces)
28
+ @spaces = spaces
29
+
30
+ log "Setting @proper[:this_line] to 0."
31
+ @proper = { this_line: 0, next_line: 0 }
32
+ @actual_indentation = 0
33
+
34
+ @indent_reasons = []
35
+ @tstring_nesting = []
36
+
37
+ @amount_to_change_this = 0
38
+ start
39
+ end
40
+
41
+ # @return [Fixnum] The indent level the file should currently be at.
42
+ def should_be_at
43
+ @proper[:this_line]
44
+ end
45
+
46
+ # Decreases the indentation expectation for the current line by
47
+ # +@spaces+.
48
+ def decrease_this_line
49
+ if started?
50
+ @proper[:this_line] -= @spaces
51
+
52
+ if @proper[:this_line] < 0
53
+ @proper[:this_line] = 0
54
+ end
55
+
56
+ log "@proper[:this_line] = #{@proper[:this_line]}"
57
+ log "@proper[:next_line] = #{@proper[:next_line]}"
58
+ else
59
+ log "#decrease_this_line called, but checking is stopped."
60
+ end
61
+ end
62
+
63
+
64
+ # Sets up expectations in +@proper+ based on the number of +/- reasons
65
+ # to change this and next lines, given in +@amount_to_change_this+ and
66
+ # +@amount_to_change_next+, respectively.
67
+ def set_up_line_transition
68
+ log "Amount to change this line: #{@amount_to_change_this}"
69
+ decrease_this_line if @amount_to_change_this < 0
70
+ end
71
+
72
+ # Should be called just before moving to the next line. This sets the
73
+ # expectation set in +@proper[:next_line]+ to
74
+ # +@proper[:this_line]+.
75
+ def transition_lines
76
+ if started?
77
+ log "Resetting change_this to 0."
78
+ @amount_to_change_this = 0
79
+ log "Setting @proper[:this_line] = that of :next_line"
80
+ @proper[:this_line] = @proper[:next_line]
81
+ log "Transitioning @proper[:this_line] to #{@proper[:this_line]}"
82
+ else
83
+ log "Skipping #transition_lines; checking is stopped."
84
+ end
85
+ end
86
+
87
+ # Starts the process of increasing/decreasing line indentation
88
+ # expectations.
89
+ def start
90
+ log "Starting indentation ruling."
91
+ log "Next check should be at #{should_be_at}"
92
+ @do_measurement = true
93
+ end
94
+
95
+ # Tells if the indentation checking process is on.
96
+ #
97
+ # @return [Boolean] +true+ if it's started; +false+ if not.
98
+ def started?
99
+ @do_measurement
100
+ end
101
+
102
+ # Stops the process of increasing/decreasing line indentation
103
+ # expectations.
104
+ def stop
105
+ if started?
106
+ msg = "Stopping indentation ruling. Should be: #{should_be_at}; "
107
+ msg << "actual: #{@actual_indentation}"
108
+ log msg
109
+ end
110
+
111
+ @do_measurement = false
112
+ end
113
+
114
+ # Updates +@actual_indentation+ based on the given lexed_line_output.
115
+ #
116
+ # @param [Array] lexed_line_output The lexed output for the current line.
117
+ def update_actual_indentation(lexed_line_output)
118
+ if lexed_line_output.end_of_multi_line_string?
119
+ log "Found end of multi-line string."
120
+ return
121
+ end
122
+
123
+ first_non_space_element = lexed_line_output.first_non_space_element
124
+ @actual_indentation = first_non_space_element.first.last
125
+ log "Actual indentation: #{@actual_indentation}"
126
+ end
127
+
128
+ # Checks if the current line ends with an operator, comma, or period.
129
+ #
130
+ # @param [LexedLine] lexed_line
131
+ # @return [Boolean]
132
+ def line_ends_with_single_token_indenter?(lexed_line)
133
+ lexed_line.ends_with_op? ||
134
+ lexed_line.ends_with_comma? ||
135
+ lexed_line.ends_with_period? ||
136
+ lexed_line.ends_with_modifier_kw?
137
+ end
138
+
139
+ # Checks to see if the last token in @single_tokens is the same as the
140
+ # one in +token_event+.
141
+ #
142
+ # @param [Array] token_event A single event (probably extracted from a
143
+ # {LexedLine}).
144
+ # @return [Boolean]
145
+ def line_ends_with_same_as_last(token_event)
146
+ return false if @indent_reasons.empty?
147
+
148
+ @indent_reasons.last[:event_type] == token_event[1]
149
+ end
150
+
151
+ # Checks to see if +lexed_line+ ends with a comma, and if it is in the
152
+ # middle of an enclosed statement (unclosed braces, brackets, parens).
153
+ # You don't want to update indentation expectations for this comma if
154
+ # you've already done so for the start of the enclosed statement.
155
+ #
156
+ # @param [LexedLine] lexed_line
157
+ # @param [Fixnum] lineno
158
+ # @return [Boolean]
159
+ def comma_is_part_of_enclosed_statement?(lexed_line, lineno)
160
+ return false if @indent_reasons.empty?
161
+
162
+ lexed_line.ends_with_comma? &&
163
+ @indent_reasons.last[:event_type] == :on_lbrace ||
164
+ @indent_reasons.last[:event_type] == :on_lbracket ||
165
+ @indent_reasons.last[:event_type] == :on_lparen
166
+ end
167
+
168
+ # Checks if indentation level got increased on this line because of a
169
+ # keyword and if it got increased on this line because of a
170
+ # single-token indenter.
171
+ #
172
+ # @param [Fixnum] lineno
173
+ # @return [Boolean]
174
+ def keyword_and_single_token_line?(lineno)
175
+ d_tokens = @indent_reasons.find_all { |t| t[:lineno] == lineno }
176
+ return false if d_tokens.empty?
177
+
178
+ kw_token = d_tokens.reverse.find do |t|
179
+ ['{', '[', '('].none? { |e| t[:token] == e }
180
+ end
181
+
182
+ return false if kw_token.nil?
183
+
184
+ s_token = @indent_reasons.reverse.find { |t| t[:lineno] == lineno }
185
+ return false unless s_token
186
+
187
+ true
188
+ end
189
+
190
+ def single_token_start_line
191
+ return if @indent_reasons.empty?
192
+
193
+ @indent_reasons.first[:lineno]
194
+ end
195
+
196
+ def double_token_start_line
197
+ return if @indent_reasons.empty?
198
+
199
+ @indent_reasons.last[:lineno]
200
+ end
201
+
202
+ def double_tokens_in_line(lineno)
203
+ @indent_reasons.find_all { |t| t[:lineno] == lineno}
204
+ end
205
+
206
+ def add_indent_reason(event_type, token, lineno)
207
+ @indent_reasons << {
208
+ event_type: event_type,
209
+ token: token,
210
+ lineno: lineno,
211
+ should_be_at: @proper[:this_line]
212
+ }
213
+
214
+ @proper[:next_line] = @indent_reasons.last[:should_be_at] + @spaces
215
+ log "Added indent reason; it's now:"
216
+ @indent_reasons.each { |r| log r.to_s }
217
+ end
218
+
219
+ def update_for_opening_reason(event_type, token, lineno)
220
+ if token.modifier_keyword?
221
+ log "Found modifier in line: '#{token}'"
222
+ return
223
+ end
224
+
225
+ log "Token '#{token}' not used as a modifier."
226
+
227
+ if token.do_is_for_a_loop?
228
+ log "Found keyword loop using optional 'do'"
229
+ return
230
+ end
231
+
232
+ add_indent_reason(event_type, token, lineno)
233
+ end
234
+
235
+ def update_for_continuation_reason(token, lexed_line, lineno)
236
+ d_tokens = @indent_reasons.dup
237
+ d_tokens.pop
238
+ on_line_token = d_tokens.find { |t| t[:lineno] == lineno }
239
+ log "online token: #{on_line_token}"
240
+
241
+ if on_line_token.nil? && lexed_line.to_s =~ /^\s*#{token}/
242
+ @proper[:this_line] -= @spaces unless @proper[:this_line].zero?
243
+ msg = "Continuation keyword: '#{token}'. "
244
+ msg << "change_this -= 1 -> #{@proper[:this_line]}"
245
+ log msg
246
+ end
247
+
248
+ last_reason_line = @indent_reasons.find { |r| r[:lineno] == lineno }
249
+
250
+ if last_reason_line.nil?
251
+ @proper[:next_line] = @indent_reasons.last[:should_be_at] + @spaces
252
+ else
253
+ @proper[:next_line] = @indent_reasons.last[:should_be_at] - @spaces
254
+ end
255
+ end
256
+
257
+ def update_for_closing_reason(event_type, lexed_line, lineno)
258
+ if event_type == :on_rbrace && @embexpr_beg == true
259
+ msg = "Got :rbrace and @embexpr_beg is true. "
260
+ msg << " Must be at an @embexpr_end."
261
+ log msg
262
+ @embexpr_beg = false
263
+ return
264
+ end
265
+
266
+ remove_continuation_keywords
267
+ remove_appropriate_reason(event_type)
268
+
269
+ @proper[:next_line] = if @indent_reasons.empty?
270
+ 0
271
+ else
272
+ @indent_reasons.last[:should_be_at] + @spaces
273
+ end
274
+
275
+ log "Updated :next after closing; it's now #{@proper[:next_line]}"
276
+
277
+ meth = "only_#{event_type.to_s.sub("^on_", '')}?"
278
+
279
+ if lexed_line.send(meth.to_sym) || lexed_line.to_s =~ /^\s*end\n?$/
280
+ @proper[:this_line] = @proper[:this_line] - @spaces
281
+ msg = "End multi-line statement. "
282
+ msg < "change_this -= 1 -> #{@proper[:this_line]}."
283
+ log msg
284
+ end
285
+ end
286
+
287
+ def remove_appropriate_reason(event_type)
288
+ last_opening_event = @indent_reasons.reverse.find do |r|
289
+ r[:event_type] == OPEN_EVENT_FOR[event_type]
290
+ end
291
+
292
+ if last_opening_event
293
+ r_index = @indent_reasons.reverse.index(last_opening_event)
294
+ index = @indent_reasons.size - r_index - 1
295
+ tmp_reasons = []
296
+
297
+ @indent_reasons.each_with_index do |r, i|
298
+ tmp_reasons << r unless i == index
299
+ end
300
+
301
+ @indent_reasons.replace(tmp_reasons)
302
+ else
303
+ @indent_reasons.pop
304
+ end
305
+
306
+ log "Removed indent reason; it's now:"
307
+ @indent_reasons.each { |r| log r.to_s }
308
+ end
309
+
310
+ def last_indent_reason_type
311
+ return if @indent_reasons.empty?
312
+
313
+ @indent_reasons.last[:event_type]
314
+ end
315
+
316
+ def remove_continuation_keywords
317
+ return if @indent_reasons.empty?
318
+
319
+ while CONTINUATION_KEYWORDS.include?(@indent_reasons.last[:token])
320
+ @indent_reasons.pop
321
+ end
322
+ end
323
+
324
+ # Overriding to be able to call #multi_line_brackets?,
325
+ # #multi_line_braces?, and #multi_line_parens?, where each takes a
326
+ # single parameter, which is the lineno.
327
+ #
328
+ # @return [Boolean]
329
+ def method_missing(meth, *args, &blk)
330
+ if meth.to_s =~ /^multi_line_(.+)\?$/
331
+ token = case $1
332
+ when "brackets" then '['
333
+ when "braces" then '{'
334
+ when "parens" then '('
335
+ else
336
+ super(meth, *args, &blk)
337
+ end
338
+
339
+ lineno = args.first
340
+
341
+ tokens = @indent_reasons.find_all do |t|
342
+ t[:token] == token
343
+ end
344
+
345
+ return false if tokens.empty?
346
+
347
+ token_on_this_line = tokens.find { |t| t[:lineno] == lineno }
348
+ return true if token_on_this_line.nil?
349
+
350
+ false
351
+ else
352
+ super(meth, *args, &blk)
353
+ end
354
+ end
355
+
356
+ def in_tstring?
357
+ !@tstring_nesting.empty?
358
+ end
359
+ end
360
+ end
361
+ end
362
+ end
@@ -0,0 +1,84 @@
1
+ require_relative '../ruler'
2
+ require_relative '../lexer_constants'
3
+
4
+ class Tailor
5
+ module Rulers
6
+ class MaxCodeLinesInClassRuler < Tailor::Ruler
7
+ include LexerConstants
8
+
9
+ def initialize(config)
10
+ super(config)
11
+ @class_start_lines = []
12
+ @kw_start_lines = []
13
+ @end_last_class = false
14
+ end
15
+
16
+ def ignored_nl_update(lexed_line, lineno, column)
17
+ return if @class_start_lines.empty?
18
+ return if lexed_line.only_spaces?
19
+ return if lexed_line.comment_line?
20
+
21
+ @class_start_lines.each do |line|
22
+ line[:count] += 1
23
+ log "Class from line #{line[:lineno]} now at #{line[:count]} lines."
24
+ end
25
+
26
+ if @end_last_class
27
+ measure(@class_start_lines.last[:count],
28
+ @class_start_lines.last[:lineno],
29
+ @class_start_lines.last[:column])
30
+ @class_start_lines.pop
31
+ @end_last_class = false
32
+ end
33
+ end
34
+
35
+ def kw_update(token, lexed_line, lineno, column)
36
+ if token == "class" || token == "module"
37
+ @class_start_lines << { lineno: lineno, column: column, count: 0 }
38
+ log "Class start lines: #{@class_start_lines}"
39
+ end
40
+
41
+ unless token.modifier_keyword? ||
42
+ !token.keyword_to_indent? ||
43
+ token.do_is_for_a_loop? ||
44
+ token.continuation_keyword?
45
+ @kw_start_lines << lineno
46
+ log "Keyword start lines: #{@kw_start_lines}"
47
+ end
48
+
49
+ if token == "end"
50
+ log "Got 'end' of class/module."
51
+
52
+ unless @class_start_lines.empty?
53
+ if @class_start_lines.last[:lineno] == @kw_start_lines.last
54
+ msg = "Class/module from line #{@class_start_lines.last[:lineno]}"
55
+ msg << " was #{@class_start_lines.last[:count]} lines long."
56
+ log msg
57
+ @end_last_class = true
58
+ end
59
+ end
60
+
61
+ @kw_start_lines.pop
62
+ log "End of keyword statement. Keywords: #{@kw_start_lines}"
63
+ end
64
+ end
65
+
66
+ def nl_update(lexed_line, lineno, column)
67
+ ignored_nl_update(lexed_line, lineno, column)
68
+ end
69
+
70
+ # Checks to see if the actual count of code lines in the class is greater
71
+ # than the value in +@config+.
72
+ #
73
+ # @param [Fixnum] actual_count The number of code lines found.
74
+ # @param [Fixnum] lineno The line the potential problem is on.
75
+ # @param [Fixnum] column The column the potential problem is on.
76
+ def measure(actual_count, lineno, column)
77
+ if actual_count > @config
78
+ @problems << Problem.new(:code_lines_in_class, lineno, column,
79
+ { actual_count: actual_count, should_be_at: @config })
80
+ end
81
+ end
82
+ end
83
+ end
84
+ end
@@ -0,0 +1,84 @@
1
+ require_relative '../ruler'
2
+ require_relative '../lexer_constants'
3
+
4
+ class Tailor
5
+ module Rulers
6
+ class MaxCodeLinesInMethodRuler < Tailor::Ruler
7
+ include LexerConstants
8
+
9
+ def initialize(config)
10
+ super(config)
11
+ @method_start_lines = []
12
+ @kw_start_lines = []
13
+ @end_last_method = false
14
+ end
15
+
16
+ def ignored_nl_update(lexed_line, lineno, column)
17
+ return if @method_start_lines.empty?
18
+ return if lexed_line.only_spaces?
19
+ return if lexed_line.comment_line?
20
+
21
+ @method_start_lines.each do |line|
22
+ line[:count] += 1
23
+ log "Method from line #{line[:lineno]} now at #{line[:count]} lines."
24
+ end
25
+
26
+ if @end_last_method
27
+ measure(@method_start_lines.last[:count],
28
+ @method_start_lines.last[:lineno],
29
+ @method_start_lines.last[:column])
30
+ @method_start_lines.pop
31
+ @end_last_method = false
32
+ end
33
+ end
34
+
35
+ def kw_update(token, lexed_line, lineno, column)
36
+ if token == "def"
37
+ @method_start_lines << { lineno: lineno, column: column, count: 0 }
38
+ log "Method start lines: #{@method_start_lines}"
39
+ end
40
+
41
+ unless token.modifier_keyword? ||
42
+ !token.keyword_to_indent? ||
43
+ token.do_is_for_a_loop? ||
44
+ token.continuation_keyword?
45
+ @kw_start_lines << lineno
46
+ log "Keyword start lines: #{@kw_start_lines}"
47
+ end
48
+
49
+ if token == "end"
50
+ log "Got 'end' of method."
51
+
52
+ unless @method_start_lines.empty?
53
+ if @method_start_lines.last[:lineno] == @kw_start_lines.last
54
+ #msg = "Method from line #{@method_start_lines.last[:lineno]}"
55
+ #msg << " was #{@method_start_lines.last[:count]} lines long."
56
+ #log msg
57
+ @end_last_method = true
58
+ end
59
+ end
60
+
61
+ @kw_start_lines.pop
62
+ log "End of keyword statement. Keywords: #{@kw_start_lines}"
63
+ end
64
+ end
65
+
66
+ def nl_update(lexed_line, lineno, column)
67
+ ignored_nl_update(lexed_line, lineno, column)
68
+ end
69
+
70
+ # Checks to see if the actual count of code lines in the method is greater
71
+ # than the value in +@config+.
72
+ #
73
+ # @param [Fixnum] actual_count The number of code lines found.
74
+ # @param [Fixnum] lineno The line the potential problem is on.
75
+ # @param [Fixnum] column The column the potential problem is on.
76
+ def measure(actual_count, lineno, column)
77
+ if actual_count > @config
78
+ @problems << Problem.new(:code_lines_in_method, lineno, column,
79
+ { actual_count: actual_count, should_be_at: @config })
80
+ end
81
+ end
82
+ end
83
+ end
84
+ end