foodcritic 3.0.3 → 4.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (42) hide show
  1. checksums.yaml +6 -14
  2. data/CHANGELOG.md +65 -0
  3. data/README.md +1 -1
  4. data/chef_dsl_metadata/chef_11.10.0.json +10272 -0
  5. data/chef_dsl_metadata/chef_11.10.2.json +10272 -0
  6. data/chef_dsl_metadata/chef_11.10.4.json +10272 -0
  7. data/chef_dsl_metadata/chef_11.6.2.json +9566 -0
  8. data/chef_dsl_metadata/chef_11.8.0.json +10074 -0
  9. data/chef_dsl_metadata/chef_11.8.2.json +10074 -0
  10. data/features/001_check_node_access.feature +60 -0
  11. data/features/003_check_for_chef_server.feature +5 -0
  12. data/features/006_check_file_mode.feature +1 -0
  13. data/features/022_check_for_dodgy_conditions_within_loop.feature +10 -0
  14. data/features/040_check_raw_git_usage.feature +6 -0
  15. data/features/047_check_for_attribute_assignment_without_precedence.feature +2 -0
  16. data/features/build_framework_support.feature +10 -0
  17. data/features/exclude_paths_to_lint.feature +19 -0
  18. data/features/show_lines_matched.feature +4 -4
  19. data/features/sort_warnings.feature +1 -1
  20. data/features/step_definitions/cookbook_steps.rb +117 -24
  21. data/features/support/command_helpers.rb +45 -12
  22. data/features/support/env.rb +5 -0
  23. data/lib/foodcritic/api.rb +122 -99
  24. data/lib/foodcritic/ast.rb +6 -7
  25. data/lib/foodcritic/chef.rb +24 -23
  26. data/lib/foodcritic/command_line.rb +49 -41
  27. data/lib/foodcritic/domain.rb +12 -10
  28. data/lib/foodcritic/dsl.rb +4 -7
  29. data/lib/foodcritic/error_checker.rb +0 -3
  30. data/lib/foodcritic/linter.rb +45 -43
  31. data/lib/foodcritic/notifications.rb +32 -32
  32. data/lib/foodcritic/output.rb +3 -6
  33. data/lib/foodcritic/rake_task.rb +9 -10
  34. data/lib/foodcritic/rules.rb +278 -240
  35. data/lib/foodcritic/template.rb +6 -13
  36. data/lib/foodcritic/version.rb +1 -1
  37. data/lib/foodcritic/xml.rb +1 -3
  38. data/man/foodcritic.1 +6 -2
  39. data/man/foodcritic.1.ronn +4 -1
  40. data/spec/foodcritic/linter_spec.rb +2 -2
  41. data/spec/regression/expected-output.txt +296 -1
  42. metadata +160 -138
@@ -6,6 +6,11 @@ end
6
6
  require 'aruba/cucumber'
7
7
  require 'foodcritic'
8
8
 
9
+ require 'minitest/autorun'
10
+ require 'minitest/spec'
11
+
12
+ MiniTest::Spec.new(nil)
13
+
9
14
  Before do
10
15
  @aruba_timeout_seconds = 300
11
16
  end
@@ -1,10 +1,9 @@
1
1
  require 'nokogiri'
2
+ require 'rufus-lru'
2
3
 
3
4
  module FoodCritic
4
-
5
5
  # Helper methods that form part of the Rules DSL.
6
6
  module Api
7
-
8
7
  include FoodCritic::AST
9
8
  include FoodCritic::XML
10
9
 
@@ -15,20 +14,20 @@ module FoodCritic
15
14
 
16
15
  # Find attribute access by type.
17
16
  def attribute_access(ast, options = {})
18
- options = {:type => :any, :ignore_calls => false}.merge!(options)
17
+ options = { type: :any, ignore_calls: false }.merge!(options)
19
18
  return [] unless ast.respond_to?(:xpath)
20
19
 
21
20
  unless [:any, :string, :symbol, :vivified].include?(options[:type])
22
- raise ArgumentError, "Node type not recognised"
21
+ fail ArgumentError, 'Node type not recognised'
23
22
  end
24
23
 
25
24
  case options[:type]
26
- when :any then
25
+ when :any then
27
26
  vivified_attribute_access(ast, options) +
28
27
  standard_attribute_access(ast, options)
29
- when :vivified then
28
+ when :vivified then
30
29
  vivified_attribute_access(ast, options)
31
- else
30
+ else
32
31
  standard_attribute_access(ast, options)
33
32
  end
34
33
  end
@@ -38,8 +37,8 @@ module FoodCritic
38
37
  raise_unless_xpath!(ast)
39
38
  # TODO: This expression is too loose, but also will fail to match other
40
39
  # types of conditionals.
41
- (! ast.xpath(%q{//*[self::if or self::unless]/*[self::aref or
42
- child::aref or self::call]
40
+ (!ast.xpath(%q{//*[self::if or self::ifop or self::unless]/
41
+ *[self::aref or child::aref or self::call]
43
42
  [count(descendant::const[@value = 'Chef' or @value = 'Config']) = 2
44
43
  and
45
44
  ( count(descendant::ident[@value='solo']) > 0
@@ -47,13 +46,16 @@ module FoodCritic
47
46
  )
48
47
  ]}).empty?) ||
49
48
  ast.xpath('//if_mod[return][aref/descendant::ident/@value="solo"]/aref/
50
- const_path_ref/descendant::const').map{|c|c['value']} == %w{Chef Config}
49
+ const_path_ref/descendant::const').map do |c|
50
+ c['value']
51
+ end == %w(Chef Config)
51
52
  end
52
53
 
53
- # Is the [chef-solo-search library](https://github.com/edelight/chef-solo-search)
54
+ # Is the
55
+ # [chef-solo-search library](https://github.com/edelight/chef-solo-search)
54
56
  # available?
55
57
  def chef_solo_search_supported?(recipe_path)
56
- return false if recipe_path.nil? || ! File.exists?(recipe_path)
58
+ return false if recipe_path.nil? || !File.exist?(recipe_path)
57
59
 
58
60
  # Look for the chef-solo-search library.
59
61
  #
@@ -61,21 +63,21 @@ module FoodCritic
61
63
  # is not under the same `cookbook_path` as the cookbook being checked.
62
64
  cbk_tree_path = Pathname.new(File.join(recipe_path, '../../..'))
63
65
  search_libs = Dir[File.join(cbk_tree_path.realpath,
64
- '*/libraries/search.rb')]
66
+ '*/libraries/search.rb')]
65
67
 
66
68
  # True if any of the candidate library files match the signature:
67
69
  #
68
70
  # class Chef
69
71
  # def search
70
72
  search_libs.any? do |lib|
71
- ! read_ast(lib).xpath(%q{//class[count(descendant::const[@value='Chef']
73
+ !read_ast(lib).xpath(%q{//class[count(descendant::const[@value='Chef']
72
74
  ) = 1]/descendant::def/ident[@value='search']}).empty?
73
75
  end
74
76
  end
75
77
 
76
78
  # The name of the cookbook containing the specified file.
77
79
  def cookbook_name(file)
78
- raise ArgumentError, 'File cannot be nil or empty' if file.to_s.empty?
80
+ fail ArgumentError, 'File cannot be nil or empty' if file.to_s.empty?
79
81
 
80
82
  until (file.split(File::SEPARATOR) & standard_cookbook_subdirs).empty? do
81
83
  file = File.absolute_path(File.dirname(file.to_s))
@@ -86,9 +88,10 @@ module FoodCritic
86
88
  # We also need to consult the metadata in case the cookbook name has been
87
89
  # overridden there. This supports only string literals.
88
90
  md_path = File.join(file, 'metadata.rb')
89
- if File.exists?(md_path)
91
+ if File.exist?(md_path)
90
92
  name = read_ast(md_path).xpath("//stmts_add/
91
- command[ident/@value='name']/descendant::tstring_content/@value").to_s
93
+ command[ident/@value='name']/
94
+ descendant::tstring_content/@value").to_s
92
95
  return name unless name.empty?
93
96
  end
94
97
  File.basename(file)
@@ -109,14 +112,15 @@ module FoodCritic
109
112
  # %w{foo bar baz}.each do |cbk|
110
113
  # depends cbk
111
114
  # end
112
- deps = deps.to_a + word_list_values(ast, "//command[ident/@value='depends']")
113
- deps.uniq.map{|dep| dep['value'].strip }
115
+ deps = deps.to_a +
116
+ word_list_values(ast, "//command[ident/@value='depends']")
117
+ deps.uniq.map { |dep| dep['value'].strip }
114
118
  end
115
119
 
116
120
  # The key / value pair in an environment or role ruby file
117
121
  def field(ast, field_name)
118
122
  if field_name.nil? || field_name.to_s.empty?
119
- raise ArgumentError, "Field name cannot be nil or empty"
123
+ fail ArgumentError, 'Field name cannot be nil or empty'
120
124
  end
121
125
  ast.xpath("//command[ident/@value='#{field_name}']")
122
126
  end
@@ -125,14 +129,14 @@ module FoodCritic
125
129
  def field_value(ast, field_name)
126
130
  field(ast, field_name).xpath('args_add_block/descendant::tstring_content
127
131
  [count(ancestor::args_add) = 1][count(ancestor::string_add) = 1]
128
- /@value').map{|a| a.to_s}.last
132
+ /@value').map { |a| a.to_s }.last
129
133
  end
130
134
 
131
135
  # Create a match for a specified file. Use this if the presence of the file
132
136
  # triggers the warning rather than content.
133
137
  def file_match(file)
134
- raise ArgumentError, "Filename cannot be nil" if file.nil?
135
- {:filename => file, :matched => file, :line => 1, :column => 1}
138
+ fail ArgumentError, 'Filename cannot be nil' if file.nil?
139
+ { filename: file, matched: file, line: 1, column: 1 }
136
140
  end
137
141
 
138
142
  # Find Chef resources of the specified type.
@@ -148,7 +152,7 @@ module FoodCritic
148
152
  # find_resources(ast, :type => :service)
149
153
  #
150
154
  def find_resources(ast, options = {})
151
- options = {:type => :any}.merge!(options)
155
+ options = { type: :any }.merge!(options)
152
156
  return [] unless ast.respond_to?(:xpath)
153
157
  scope_type = ''
154
158
  scope_type = "[@value='#{options[:type]}']" unless options[:type] == :any
@@ -170,7 +174,7 @@ module FoodCritic
170
174
  # included_recipes(ast)
171
175
  # included_recipes(ast, :with_partial_names => true)
172
176
  #
173
- def included_recipes(ast, options = {:with_partial_names => true})
177
+ def included_recipes(ast, options = { with_partial_names: true })
174
178
  raise_unless_xpath!(ast)
175
179
 
176
180
  filter = ['[count(descendant::args_add) = 1]']
@@ -181,16 +185,18 @@ module FoodCritic
181
185
  filter << '[count(descendant::string_embexpr) = 0]'
182
186
  end
183
187
 
184
- string_desc = '[descendant::args_add/string_literal]/descendant::tstring_content'
188
+ string_desc = '[descendant::args_add/string_literal]/
189
+ descendant::tstring_content'
185
190
  included = ast.xpath([
186
191
  "//command[ident/@value = 'include_recipe']",
187
- "//fcall[ident/@value = 'include_recipe']/following-sibling::arg_paren",
192
+ "//fcall[ident/@value = 'include_recipe']/
193
+ following-sibling::arg_paren",
188
194
  ].map do |recipe_include|
189
195
  recipe_include + filter.join + string_desc
190
196
  end.join(' | '))
191
197
 
192
198
  # Hash keyed by recipe name with matched nodes.
193
- included.inject(Hash.new([])){|h, i| h[i['value']] += [i]; h}
199
+ included.inject(Hash.new([])) { |h, i| h[i['value']] += [i]; h }
194
200
  end
195
201
 
196
202
  # Searches performed by the specified recipe that are literal strings.
@@ -206,28 +212,28 @@ module FoodCritic
206
212
  raise_unless_xpath!(node)
207
213
  pos = node.xpath('descendant::pos').first
208
214
  return nil if pos.nil?
209
- {:matched => node.respond_to?(:name) ? node.name : '',
210
- :line => pos['line'].to_i, :column => pos['column'].to_i}
215
+ { matched: node.respond_to?(:name) ? node.name : '',
216
+ line: pos['line'].to_i, column: pos['column'].to_i }
211
217
  end
212
218
 
213
219
  # Read the AST for the given Ruby source file
214
220
  def read_ast(file)
215
- source = if file.to_s.split(File::SEPARATOR).include?('templates')
216
- template_expressions_only(file)
221
+ @ast_cache ||= Rufus::Lru::Hash.new(5)
222
+ if @ast_cache.include?(file)
223
+ @ast_cache[file]
217
224
  else
218
- File.read(file)
225
+ @ast_cache[file] = uncached_read_ast(file)
219
226
  end
220
- build_xml(Ripper::SexpBuilder.new(source).parse)
221
227
  end
222
228
 
223
229
  # Retrieve a single-valued attribute from the specified resource.
224
230
  def resource_attribute(resource, name)
225
- raise ArgumentError, "Attribute name cannot be empty" if name.empty?
231
+ fail ArgumentError, 'Attribute name cannot be empty' if name.empty?
226
232
  resource_attributes(resource)[name.to_s]
227
233
  end
228
234
 
229
235
  # Retrieve all attributes from the specified resource.
230
- def resource_attributes(resource, options={})
236
+ def resource_attributes(resource, options = {})
231
237
  atts = {}
232
238
  name = resource_name(resource, options)
233
239
  atts[:name] = name unless name.empty?
@@ -239,8 +245,10 @@ module FoodCritic
239
245
  # Resources keyed by type, with an array of matching nodes for each.
240
246
  def resource_attributes_by_type(ast)
241
247
  result = {}
242
- resources_by_type(ast).each do |type,resources|
243
- result[type] = resources.map{|resource| resource_attributes(resource)}
248
+ resources_by_type(ast).each do |type, resources|
249
+ result[type] = resources.map do |resource|
250
+ resource_attributes(resource)
251
+ end
244
252
  end
245
253
  result
246
254
  end
@@ -248,13 +256,14 @@ module FoodCritic
248
256
  # Retrieve the name attribute associated with the specified resource.
249
257
  def resource_name(resource, options = {})
250
258
  raise_unless_xpath!(resource)
251
- options = {:return_expressions => false}.merge(options)
259
+ options = { return_expressions: false }.merge(options)
252
260
  if options[:return_expressions]
253
261
  name = resource.xpath('command/args_add_block')
254
- if name.xpath('descendant::string_add').size == 1 and
255
- name.xpath('descendant::string_literal').size == 1 and
256
- name.xpath('descendant::*[self::call or self::string_embexpr]').empty?
257
- name.xpath('descendant::tstring_content/@value').to_s
262
+ if name.xpath('descendant::string_add').size == 1 &&
263
+ name.xpath('descendant::string_literal').size == 1 &&
264
+ name.xpath(
265
+ 'descendant::*[self::call or self::string_embexpr]').empty?
266
+ name.xpath('descendant::tstring_content/@value').to_s
258
267
  else
259
268
  name
260
269
  end
@@ -267,7 +276,7 @@ module FoodCritic
267
276
  # Resources in an AST, keyed by type.
268
277
  def resources_by_type(ast)
269
278
  raise_unless_xpath!(ast)
270
- result = Hash.new{|hash, key| hash[key] = Array.new}
279
+ result = Hash.new { |hash, key| hash[key] = Array.new }
271
280
  find_resources(ast).each do |resource|
272
281
  result[resource_type(resource)] << resource
273
282
  end
@@ -279,7 +288,7 @@ module FoodCritic
279
288
  raise_unless_xpath!(resource)
280
289
  type = resource.xpath('string(command/ident/@value)')
281
290
  if type.empty?
282
- raise ArgumentError, "Provided AST node is not a resource"
291
+ fail ArgumentError, 'Provided AST node is not a resource'
283
292
  end
284
293
  type
285
294
  end
@@ -291,7 +300,7 @@ module FoodCritic
291
300
 
292
301
  checker = FoodCritic::ErrorChecker.new(str)
293
302
  checker.parse
294
- ! checker.error?
303
+ !checker.error?
295
304
  end
296
305
 
297
306
  # Searches performed by the provided AST.
@@ -302,22 +311,24 @@ module FoodCritic
302
311
 
303
312
  # The list of standard cookbook sub-directories.
304
313
  def standard_cookbook_subdirs
305
- %w{attributes definitions files libraries providers recipes resources
306
- templates}
314
+ %w(attributes definitions files libraries providers recipes resources
315
+ templates)
307
316
  end
308
317
 
309
318
  # Platforms declared as supported in cookbook metadata
310
319
  def supported_platforms(ast)
311
320
  platforms = ast.xpath('//command[ident/@value="supports"]/
312
- descendant::*[self::string_literal or self::symbol_literal][position() = 1]
321
+ descendant::*[self::string_literal or self::symbol_literal]
322
+ [position() = 1]
313
323
  [self::symbol_literal or count(descendant::string_add) = 1]/
314
324
  descendant::*[self::tstring_content | self::ident]')
315
- platforms = platforms.to_a + word_list_values(ast, "//command[ident/@value='supports']")
325
+ platforms = platforms.to_a +
326
+ word_list_values(ast, "//command[ident/@value='supports']")
316
327
  platforms.map do |platform|
317
328
  versions = platform.xpath('ancestor::args_add[position() > 1]/
318
- string_literal/descendant::tstring_content/@value').map{|v| v.to_s}
319
- {:platform => platform['value'], :versions => versions}
320
- end.sort{|a,b| a[:platform] <=> b[:platform]}
329
+ string_literal/descendant::tstring_content/@value').map { |v| v.to_s }
330
+ { platform: platform['value'], versions: versions }
331
+ end.sort { |a, b| a[:platform] <=> b[:platform] }
321
332
  end
322
333
 
323
334
  # Template filename
@@ -333,11 +344,11 @@ module FoodCritic
333
344
  end
334
345
  end
335
346
 
336
- def templates_included(all_templates, template_path, depth=1)
337
- raise RecursedTooFarError.new(template_path) if depth > 10
347
+ def templates_included(all_templates, template_path, depth = 1)
348
+ fail RecursedTooFarError.new(template_path) if depth > 10
338
349
  partials = read_ast(template_path).xpath('//*[self::command or
339
350
  child::fcall][descendant::ident/@value="render"]//args_add/
340
- string_literal//tstring_content/@value').map{|p| p.to_s}
351
+ string_literal//tstring_content/@value').map { |p| p.to_s }
341
352
  Array(template_path) + partials.map do |included_partial|
342
353
  partial_path = Array(all_templates).find do |path|
343
354
  (Pathname.new(template_path).dirname + included_partial).to_s == path
@@ -367,21 +378,21 @@ module FoodCritic
367
378
  atts = {}
368
379
  resource.xpath("do_block/descendant::method_add_block[
369
380
  count(ancestor::do_block) = 1][brace_block | do_block]").each do |batt|
370
- att_name = batt.xpath('string(method_add_arg/fcall/ident/@value)')
371
- if att_name and ! att_name.empty? and batt.children.length > 1
372
- atts[att_name] = batt.children[1]
373
- end
381
+ att_name = batt.xpath('string(method_add_arg/fcall/ident/@value)')
382
+ if att_name && !att_name.empty? && batt.children.length > 1
383
+ atts[att_name] = batt.children[1]
384
+ end
374
385
  end
375
386
  atts
376
387
  end
377
388
 
378
389
  def block_depth(resource)
379
- resource.path.split('/').group_by{|e|e}['method_add_block'].size
390
+ resource.path.split('/').group_by { |e|e }['method_add_block'].size
380
391
  end
381
392
 
382
393
  # Recurse the nested arrays provided by Ripper to create a tree we can more
383
394
  # easily apply expressions to.
384
- def build_xml(node, doc = nil, xml_node=nil)
395
+ def build_xml(node, doc = nil, xml_node = nil)
385
396
  doc, xml_node = xml_document(doc, xml_node)
386
397
 
387
398
  if node.respond_to?(:each)
@@ -408,20 +419,21 @@ module FoodCritic
408
419
  end
409
420
 
410
421
  def extract_attribute_value(att, options = {})
411
- if ! att.xpath('args_add_block[count(descendant::args_add)>1]').empty?
422
+ if !att.xpath('args_add_block[count(descendant::args_add)>1]').empty?
412
423
  att.xpath('args_add_block').first
413
- elsif ! att.xpath('args_add_block/args_add/
424
+ elsif !att.xpath('args_add_block/args_add/
414
425
  var_ref/kw[@value="true" or @value="false"]').empty?
415
426
  att.xpath('string(args_add_block/args_add/
416
427
  var_ref/kw/@value)') == 'true'
417
- elsif ! att.xpath('descendant::assoc_new').empty?
428
+ elsif !att.xpath('descendant::assoc_new').empty?
418
429
  att.xpath('descendant::assoc_new')
419
- elsif ! att.xpath('descendant::int').empty?
430
+ elsif !att.xpath('descendant::int').empty?
420
431
  att.xpath('descendant::int/@value').to_s
421
432
  elsif att.xpath('descendant::symbol').empty?
422
- if options[:return_expressions] and
423
- (att.xpath('descendant::string_add').size != 1 or
424
- ! att.xpath('descendant::*[self::call or self::string_embexpr]').empty?)
433
+ if options[:return_expressions] &&
434
+ (att.xpath('descendant::string_add').size != 1 ||
435
+ att.xpath('descendant::*[self::call or
436
+ self::string_embexpr]').any?)
425
437
  att
426
438
  else
427
439
  att.xpath('string(descendant::tstring_content/@value)')
@@ -438,33 +450,34 @@ module FoodCritic
438
450
  end
439
451
 
440
452
  def node_method?(meth, cookbook_dir)
441
- chef_dsl_methods.include?(meth) || patched_node_method?(meth, cookbook_dir)
453
+ chef_dsl_methods.include?(meth) ||
454
+ patched_node_method?(meth, cookbook_dir)
442
455
  end
443
456
 
444
457
  def normal_attributes(resource, options = {})
445
458
  atts = {}
446
459
  resource.xpath('do_block/descendant::*[self::command or
447
460
  self::method_add_arg][count(ancestor::do_block) >= 1]').each do |att|
448
- blocks = att.xpath('ancestor::method_add_block/method_add_arg/fcall')
449
- next if blocks.any?{|a| block_depth(a) > block_depth(resource)}
450
- att_name = att.xpath('string(ident/@value |
451
- fcall/ident[@value="variables"]/@value)')
452
- unless att_name.empty?
453
- atts[att_name] = extract_attribute_value(att, options)
454
- end
461
+ blocks = att.xpath('ancestor::method_add_block/method_add_arg/fcall')
462
+ next if blocks.any? { |a| block_depth(a) > block_depth(resource) }
463
+ att_name = att.xpath('string(ident/@value |
464
+ fcall/ident[@value="variables"]/@value)')
465
+ unless att_name.empty?
466
+ atts[att_name] = extract_attribute_value(att, options)
467
+ end
455
468
  end
456
469
  atts
457
470
  end
458
471
 
459
472
  def patched_node_method?(meth, cookbook_dir)
460
- return false if cookbook_dir.nil? || ! Dir.exists?(cookbook_dir)
473
+ return false if cookbook_dir.nil? || !Dir.exists?(cookbook_dir)
461
474
 
462
475
  # TODO: Modify this to work with multiple cookbook paths
463
476
  cbk_tree_path = Pathname.new(File.join(cookbook_dir, '..'))
464
477
  libs = Dir[File.join(cbk_tree_path.realpath, '*/libraries/*.rb')]
465
478
 
466
479
  libs.any? do |lib|
467
- ! read_ast(lib).xpath(%Q{//class[count(descendant::const[@value='Chef'])
480
+ !read_ast(lib).xpath(%Q{//class[count(descendant::const[@value='Chef'])
468
481
  > 0][count(descendant::const[@value='Node']) > 0]/descendant::def/
469
482
  ident[@value='#{meth.to_s}']}).empty?
470
483
  end
@@ -472,18 +485,28 @@ module FoodCritic
472
485
 
473
486
  def raise_unless_xpath!(ast)
474
487
  unless ast.respond_to?(:xpath)
475
- raise ArgumentError, "AST must support #xpath"
488
+ fail ArgumentError, 'AST must support #xpath'
476
489
  end
477
490
  end
478
491
 
492
+ def uncached_read_ast(file)
493
+ source = if file.to_s.split(File::SEPARATOR).include?('templates')
494
+ template_expressions_only(file)
495
+ else
496
+ File.read(file)
497
+ end
498
+ build_xml(Ripper::SexpBuilder.new(source).parse)
499
+ end
500
+
479
501
  # XPath custom function
480
502
  class AttFilter
481
503
  def is_att_type(value)
482
504
  return [] unless value.respond_to?(:select)
483
505
  value.select do |n|
484
- %w{
506
+ %w(
485
507
  automatic_attrs
486
508
  default
509
+ default!
487
510
  default_unless
488
511
  force_default
489
512
  force_override
@@ -491,10 +514,11 @@ module FoodCritic
491
514
  normal
492
515
  normal_unless
493
516
  override
517
+ override!
494
518
  override_unless
495
519
  set
496
520
  set_unless
497
- }.include?(n.to_s)
521
+ ).include?(n.to_s)
498
522
  end
499
523
  end
500
524
  end
@@ -502,14 +526,14 @@ module FoodCritic
502
526
  def standard_attribute_access(ast, options)
503
527
  if options[:type] == :any
504
528
  [:string, :symbol].map do |type|
505
- standard_attribute_access(ast, options.merge(:type => type))
529
+ standard_attribute_access(ast, options.merge(type: type))
506
530
  end.inject(:+)
507
531
  else
508
532
  type = if options[:type] == :string
509
- 'tstring_content'
510
- else
511
- '*[self::symbol or self::dyna_symbol]'
512
- end
533
+ 'tstring_content'
534
+ else
535
+ '*[self::symbol or self::dyna_symbol]'
536
+ end
513
537
  expr = '//*[self::aref_field or self::aref][count(method_add_arg) = 0]'
514
538
  expr += '[count(is_att_type(descendant::var_ref/ident/@value)) =
515
539
  count(descendant::var_ref/ident/@value)]'
@@ -527,24 +551,24 @@ module FoodCritic
527
551
 
528
552
  def template_expressions_only(file)
529
553
  exprs = Template::ExpressionExtractor.new.extract(File.read(file))
530
- lines = Array.new(exprs.map{|e| e[:line]}.max || 0, '')
554
+ lines = Array.new(exprs.map { |e| e[:line] }.max || 0, '')
531
555
  exprs.each do |e|
532
- lines[e[:line] -1] += ';' unless lines[e[:line] -1].empty?
533
- lines[e[:line] -1] += e[:code]
556
+ lines[e[:line] - 1] += ';' unless lines[e[:line] - 1].empty?
557
+ lines[e[:line] - 1] += e[:code]
534
558
  end
535
559
  lines.join("\n")
536
560
  end
537
561
 
538
- def vivified_attribute_access(ast, options={})
562
+ def vivified_attribute_access(ast, options = {})
539
563
  calls = ast.xpath(%Q{//*[self::call or self::field]
540
564
  [is_att_type(vcall/ident/@value) or is_att_type(var_ref/ident/@value)]
541
565
  #{ignore_attributes_xpath(options[:ignore])}
542
566
  [@value='.'][count(following-sibling::arg_paren) = 0]}, AttFilter.new)
543
567
  calls.select do |call|
544
- call.xpath("aref/args_add_block").size == 0 and
545
- (call.xpath("descendant::ident").size > 1 and
546
- ! node_method?(call.xpath("ident/@value").to_s.to_sym,
547
- options[:cookbook_dir]))
568
+ call.xpath('aref/args_add_block').size == 0 &&
569
+ (call.xpath('descendant::ident').size > 1 &&
570
+ !node_method?(call.xpath('ident/@value').to_s.to_sym,
571
+ options[:cookbook_dir]))
548
572
  end.sort
549
573
  end
550
574
 
@@ -553,11 +577,10 @@ module FoodCritic
553
577
  if var_ref.empty?
554
578
  []
555
579
  else
556
- ast.xpath(%Q{descendant::block_var/params/ident#{var_ref.first['value']}/
557
- ancestor::method_add_block/call/descendant::tstring_content})
580
+ ast.xpath(%Q(descendant::block_var/params/
581
+ ident#{var_ref.first['value']}/ancestor::method_add_block/call/
582
+ descendant::tstring_content))
558
583
  end
559
584
  end
560
-
561
585
  end
562
-
563
586
  end