Skip to content
This repository has been archived by the owner on Sep 19, 2020. It is now read-only.

Commit

Permalink
Add new :ignore option to #attribute_access.
Browse files Browse the repository at this point in the history
  • Loading branch information
Andrew Crump committed Jul 24, 2013
1 parent fae4d26 commit 9f3d687
Show file tree
Hide file tree
Showing 2 changed files with 62 additions and 7 deletions.
24 changes: 17 additions & 7 deletions lib/foodcritic/api.rb
Original file line number Diff line number Diff line change
Expand Up @@ -22,10 +22,10 @@ def attribute_access(ast, options = {})

case options[:type]
when :any then
vivified_attribute_access(ast, options[:cookbook_dir]) +
standard_attribute_access(ast, options)
vivified_attribute_access(ast, options) +
standard_attribute_access(ast, options)
when :vivified then
vivified_attribute_access(ast, options[:cookbook_dir])
vivified_attribute_access(ast, options)
else
standard_attribute_access(ast, options)
end
Expand Down Expand Up @@ -390,6 +390,12 @@ def extract_attribute_value(att, options = {})
end
end

def ignore_attributes_xpath(ignores)
Array(ignores).map do |ignore|
"[count(descendant::*[@value='#{ignore}']) = 0]"
end.join
end

def node_method?(meth, cookbook_dir)
chef_dsl_methods.include?(meth) || patched_node_method?(meth, cookbook_dir)
end
Expand Down Expand Up @@ -468,22 +474,26 @@ def standard_attribute_access(ast, options)
count(descendant::var_ref/ident/@value)]'
expr += '[is_att_type(descendant::ident'
expr += '[not(ancestor::aref/call)]' if options[:ignore_calls]
expr += "/@value)]/descendant::#{type}"
expr += '/@value)]'
expr += ignore_attributes_xpath(options[:ignore])
expr += "/descendant::#{type}"
if options[:type] == :string
expr += '[count(ancestor::dyna_symbol) = 0]'
end
ast.xpath(expr, AttFilter.new).sort
end
end

def vivified_attribute_access(ast, cookbook_dir)
calls = ast.xpath(%q{//*[self::call or self::field]
def vivified_attribute_access(ast, options={})
calls = ast.xpath(%Q{//*[self::call or self::field]
[is_att_type(vcall/ident/@value) or is_att_type(var_ref/ident/@value)]
#{ignore_attributes_xpath(options[:ignore])}
[@value='.'][count(following-sibling::arg_paren) = 0]}, AttFilter.new)
calls.select do |call|
call.xpath("aref/args_add_block").size == 0 and
(call.xpath("descendant::ident").size > 1 and
! node_method?(call.xpath("ident/@value").to_s.to_sym, cookbook_dir))
! node_method?(call.xpath("ident/@value").to_s.to_sym,
options[:cookbook_dir]))
end.sort
end

Expand Down
45 changes: 45 additions & 0 deletions spec/foodcritic/api_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,51 @@ def parse_ast(str)
ast = parse_ast(%q{baz = search(:node, "name:#{node['foo']['bar']}")[0]})
api.attribute_access(ast, :type => :symbol).must_be_empty
end
describe :ignoring_attributes do
it "doesn't ignore run_state by default for backwards compatibility" do
ast = parse_ast("node.run_state['bar'] = 'baz'")
api.attribute_access(ast).wont_be_empty
end
it "allows run_state to be ignored" do
ast = parse_ast("node.run_state['bar'] = 'baz'")
api.attribute_access(ast, :ignore => ['run_state']).must_be_empty
end
it "allows run_state to be ignored (symbols access)" do
ast = parse_ast("node.run_state[:bar] = 'baz'")
api.attribute_access(ast, :ignore => ['run_state']).must_be_empty
end
it "allows any attribute to be ignored" do
ast = parse_ast("node['bar'] = 'baz'")
api.attribute_access(ast, :ignore => ['bar']).must_be_empty
end
it "allows any attribute to be ignored (symbols access)" do
ast = parse_ast("node[:bar] = 'baz'")
api.attribute_access(ast, :ignore => ['bar']).must_be_empty
end
it "allows any attribute to be ignored (dot access)" do
ast = parse_ast("node.bar = 'baz'")
api.attribute_access(ast, :ignore => ['bar']).must_be_empty
end
it "includes the children of attributes" do
ast = parse_ast("node['foo']['bar'] = 'baz'")
api.attribute_access(ast).map{|a| a['value']}.must_equal(%w{foo bar})
end
it "does not include children of removed attributes" do
ast = parse_ast("node['foo']['bar'] = 'baz'")
api.attribute_access(ast, :ignore => ['foo']).must_be_empty
end
it "coerces ignore values to enumerate them" do
ast = parse_ast("node.run_state['bar'] = 'baz'")
api.attribute_access(ast, :ignore => 'run_state').must_be_empty
end
it "can ignore multiple attributes" do
ast = parse_ast(%q{
node['bar'] = 'baz'
node.foo = 'baz'
})
api.attribute_access(ast, :ignore => %w{foo bar}).must_be_empty
end
end
end

describe "#checks_for_chef_solo?" do
Expand Down

0 comments on commit 9f3d687

Please sign in to comment.