11module TwoFer
22
33 MESSAGES = {
4- no_module : "No module or class called TwoFer" ,
5- no_method : "No method called two_fer" ,
6- splat_args : "Rather than using *%s, how about actually setting a parameter called 'name'?" ,
7- missing_default_param : "There is no correct default param - the tests will fail" ,
8- incorrect_default_param : "You could set the default value to 'you' to avoid conditionals" ,
9- string_building : "Rather than using string building, use interpolation" ,
10- kernel_format : "Rather than using the format method, use interpolation" ,
11- string_format : "Rather than using string's format/percentage method, use interpolation"
4+ no_module : "ruby.general.no_target_module" ,
5+ no_method : "ruby.general.no_target_method" ,
6+ incorrect_indentation : "ruby.general.incorrect_indentation" ,
7+ splat_args : "ruby.two_fer.splat_args" , #Rather than using *%s, how about actually setting a parameter called 'name'?",
8+ missing_default_param : "ruby.two_fer.missing_default_param" , #"There is no correct default param - the tests will fail",
9+ incorrect_default_param : "ruby.two_fer.incorrect_default_param" , #You could set the default value to 'you' to avoid conditionals",
10+ string_building : "ruby.two_fer.avoid_string_building" , # "Rather than using string building, use interpolation",
11+ kernel_format : "ruby.two_fer.avoid_kernel_format" , #"Rather than using the format method, use interpolation",
12+ string_format : "ruby.two_fer.avoid_string_format" , #"Rather than using string's format/percentage method, use interpolation"
1213 }
1314
1415 class Analyze < ExerciseAnalyzer
@@ -97,7 +98,7 @@ def check_for_optimal_solution!
9798 # done something weird, so let's get a mentor to look at it!
9899 refer_to_mentor! unless string_interpolation_has_three_components?
99100
100- approve !
101+ approve_if_whitespace_is_sensible !
101102 end
102103
103104 def check_for_correct_solution_without_string_interpolaton!
@@ -112,7 +113,7 @@ def check_for_correct_solution_without_string_interpolaton!
112113 # "One for " + name + ", one for me."
113114 if loc . method_name == :+ &&
114115 loc . arguments [ 0 ] . type == :str
115- approve !( :string_building )
116+ approve_if_whitespace_is_sensible !( :string_building )
116117 end
117118
118119 # In the case of:
@@ -121,15 +122,15 @@ def check_for_correct_solution_without_string_interpolaton!
121122 loc . receiver == nil &&
122123 loc . arguments [ 0 ] . type == :str &&
123124 loc . arguments [ 1 ] . type == :lvar
124- approve !( :kernel_format )
125+ approve_if_whitespace_is_sensible !( :kernel_format )
125126 end
126127
127128 # In the case of:
128129 # "One for %s, one for me." % name
129130 if loc . method_name == :% &&
130131 loc . receiver . type == :str &&
131132 loc . arguments [ 0 ] . type == :lvar
132- approve !( :string_format )
133+ approve_if_whitespace_is_sensible !( :string_format )
133134 end
134135
135136 # If we have a one-line method that passes the tests, then it's not
@@ -182,14 +183,38 @@ def string_interpolation_has_three_components?
182183 target_method . body . children . size == 3
183184 end
184185
186+ # REFACTOR: This could be refactored to strip blank
187+ # lines and then use each_cons(2).
188+ def indentation_is_sensible?
189+ previous_line = nil
190+ code_to_analyze . lines . each do |line |
191+ # If the previous line or this line is
192+ # just a whitespace line, don't consider it
193+ # when checking for indentation
194+ unless previous_line == nil ||
195+ previous_line =~ /^\s *\n *$/ ||
196+ line =~ /^\s *\n *$/
197+
198+ previous_line_lspace = previous_line [ /^ */ ] . size
199+ line_lspace = line [ /^ */ ] . size
200+
201+ return false if ( previous_line_lspace - line_lspace ) . abs > 2
202+ end
203+
204+ previous_line = line
205+ end
206+
207+ true
208+ end
209+
185210 memoize
186211 def target_module
187212 SA ::Helpers . extract_module_or_class ( root_node , "TwoFer" )
188213 end
189214
190215 memoize
191216 def target_method
192- SA ::Helpers . extract_class_method ( target_module , "two_fer" )
217+ SA ::Helpers . extract_module_method ( target_module , "two_fer" )
193218 end
194219
195220 memoize
@@ -220,15 +245,19 @@ def default_argument_value
220245 # These are totally generic to all exercises and
221246 # can probably be extracted to parent
222247 # ###
223- def approve! ( msg = nil )
224- if msg
225- self . status = :approve_with_comment
226- self . comments << MESSAGES [ msg ]
248+ def approve_if_whitespace_is_sensible! ( msg = nil )
249+ if indentation_is_sensible?
250+ if msg
251+ self . status = :approve_with_comment
252+ self . comments << MESSAGES [ msg ]
253+ else
254+ self . status = :approve_as_optimal
255+ end
256+ raise FinishedFlowControlException
257+
227258 else
228- self . status = :approve_as_optimal
259+ disapprove! ( :incorrect_indentation )
229260 end
230-
231- raise FinishedFlowControlException
232261 end
233262
234263 def refer_to_mentor!
@@ -239,7 +268,11 @@ def refer_to_mentor!
239268
240269 def disapprove! ( msg , *msg_args )
241270 self . status = :disapprove_with_comment
242- self . comments << ( MESSAGES [ msg ] % msg_args )
271+ if msg_args . length > 0
272+ self . comments << [ MESSAGES [ msg ] , *msg_args ]
273+ else
274+ self . comments << MESSAGES [ msg ]
275+ end
243276
244277 raise FinishedFlowControlException
245278 end
0 commit comments