mirror of
https://github.com/inspec/inspec
synced 2024-11-26 22:50:36 +00:00
encapsulated filters
This commit is contained in:
parent
652f10ad9a
commit
048a1584b9
4 changed files with 184 additions and 49 deletions
|
@ -23,7 +23,6 @@ module Inspec::Resources
|
|||
|
||||
def initialize(conf_path = '/etc/xinetd.conf')
|
||||
@conf_path = conf_path
|
||||
@filters = ''
|
||||
@contents = {}
|
||||
end
|
||||
|
||||
|
@ -35,28 +34,15 @@ module Inspec::Resources
|
|||
@params ||= read_params
|
||||
end
|
||||
|
||||
extend Inspec::Filter
|
||||
add_filter 'service'
|
||||
add_filter 'id'
|
||||
add_filter 'socket_type'
|
||||
add_filter 'type'
|
||||
add_filter 'wait'
|
||||
|
||||
def disabled?
|
||||
where({ 'disable' => 'no' }).services.empty?
|
||||
end
|
||||
|
||||
def enabled?
|
||||
where({ 'disable' => 'yes' }).services.empty?
|
||||
end
|
||||
|
||||
def where(conditions = {})
|
||||
fields, filters = Inspec::Filter.where(service_lines, conditions)
|
||||
res = clone
|
||||
res.instance_variable_set(:@filters, @filters + filters)
|
||||
res.instance_variable_set(:@services, fields)
|
||||
res
|
||||
end
|
||||
filter = FilterTable.create(self, :service_lines)
|
||||
filter.add_delegator(:where)
|
||||
.add(:services, field: 'service')
|
||||
.add(:ids, field: 'id')
|
||||
.add(:socket_types, field: 'socket_type')
|
||||
.add(:types, field: 'type')
|
||||
.add(:wait, field: 'wait')
|
||||
.add(:disabled?) { |x| x.where('disable' => 'no').services.empty? }
|
||||
.add(:enabled?) { |x| x.where('disable' => 'yes').services.empty? }
|
||||
|
||||
private
|
||||
|
||||
|
@ -97,13 +83,5 @@ module Inspec::Resources
|
|||
def service_lines
|
||||
@services ||= params['services'].values.flatten.map(&:params)
|
||||
end
|
||||
|
||||
def get_fields(*fields)
|
||||
res = service_lines.map do |line|
|
||||
fields.map { |f| line[f] }
|
||||
end.flatten
|
||||
return res unless fields == ['service']
|
||||
res.uniq
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -1,34 +1,45 @@
|
|||
# encoding: utf-8
|
||||
# author: Dominik Richter
|
||||
# author: Stephan Renatus
|
||||
# author: Christoph Hartmann
|
||||
|
||||
module Inspec
|
||||
module Filter
|
||||
module Show; end
|
||||
module FilterTable
|
||||
module Show; end
|
||||
|
||||
def add_filter(field_name)
|
||||
fail "Called add_filter in resource #{self} with field name nil!" if field_name.nil?
|
||||
method_name = field_name.to_s
|
||||
# methods will alwas target plurals; this is why the suffix 's' is mandatory
|
||||
method_name += 's' unless method_name.end_with? 's'
|
||||
|
||||
define_method method_name.to_sym do |condition = Show|
|
||||
return get_fields(field_name) if condition == Show
|
||||
where({ field_name => condition })
|
||||
end
|
||||
class Table
|
||||
attr_reader :params
|
||||
def initialize(resource, params, filters)
|
||||
@resource = resource
|
||||
@params = params
|
||||
@filters = filters
|
||||
end
|
||||
|
||||
def self.where(table, conditions = {})
|
||||
def where(conditions)
|
||||
return self if !conditions.is_a?(Hash) || conditions.empty?
|
||||
filters = ''
|
||||
table = @params
|
||||
conditions.each do |field, condition|
|
||||
filters += " #{field} = #{condition.inspect}"
|
||||
table = filter_lines(table, field, condition)
|
||||
end
|
||||
[table, filters]
|
||||
self.class.new(@resource, table, @filters + filters)
|
||||
end
|
||||
|
||||
def self.filter_lines(table, field, condition)
|
||||
def get_fields(*fields)
|
||||
@params.map do |line|
|
||||
fields.map { |f| line[f] }
|
||||
end.flatten
|
||||
end
|
||||
|
||||
def to_s
|
||||
@resource.to_s + @filters
|
||||
end
|
||||
|
||||
alias inspect to_s
|
||||
|
||||
private
|
||||
|
||||
def filter_lines(table, field, condition)
|
||||
table.find_all do |line|
|
||||
next unless line.key?(field)
|
||||
case line[field]
|
||||
|
@ -40,4 +51,48 @@ module Inspec
|
|||
end
|
||||
end
|
||||
end
|
||||
|
||||
class Factory
|
||||
def initialize(resource, accessor)
|
||||
@resource = resource
|
||||
@accessor = accessor
|
||||
@table = Class.new(Table)
|
||||
end
|
||||
|
||||
def table(instance)
|
||||
table.new(self, instance.method(table_accessor).call, '')
|
||||
end
|
||||
|
||||
def add_delegator(method_name)
|
||||
if method_name.nil?
|
||||
throw RuntimeError, "Called filter.add_delegator for resource #{@resource} with method name nil!"
|
||||
end
|
||||
table_accessor = @accessor
|
||||
table = @table
|
||||
@resource.send(:define_method, method_name.to_sym) do |*args|
|
||||
filter = table.new(self, method(table_accessor).call, '')
|
||||
filter.method(method_name.to_sym).call(*args)
|
||||
end
|
||||
self
|
||||
end
|
||||
|
||||
def add(method_name, opts = {}, &block)
|
||||
if method_name.nil?
|
||||
throw RuntimeError, "Called filter.add for resource #{@resource} with method name nil!"
|
||||
end
|
||||
|
||||
field_name = opts[:field] || method_name
|
||||
@table.send(:define_method, method_name.to_sym) do |condition = Show|
|
||||
return block.call(self) unless block.nil? # rubocop:disable Performance/RedundantBlockCall
|
||||
return where(nil).get_fields(field_name) if condition == Show
|
||||
where({ field_name => condition })
|
||||
end
|
||||
|
||||
add_delegator(method_name)
|
||||
end
|
||||
end
|
||||
|
||||
def self.create(resource, accessor)
|
||||
Factory.new(resource, accessor)
|
||||
end
|
||||
end
|
||||
|
|
|
@ -17,7 +17,7 @@ describe 'Inspec::Resources::XinetdConf' do
|
|||
|
||||
describe 'with services from child configs' do
|
||||
it 'has one service name' do
|
||||
_(resource.services).must_equal %w{chargen}
|
||||
_(resource.services.uniq).must_equal %w{chargen}
|
||||
end
|
||||
|
||||
it 'has multiple service definitions' do
|
||||
|
@ -30,7 +30,7 @@ describe 'Inspec::Resources::XinetdConf' do
|
|||
|
||||
it 'can chain filters' do
|
||||
one = resource.services('chargen').socket_types('dgram')
|
||||
_(one.params['services'].length).must_equal 1
|
||||
_(one.services.length).must_equal 1
|
||||
_(one.ids).must_equal %w{chargen-dgram}
|
||||
end
|
||||
|
||||
|
|
102
test/unit/utils/filter_table_test.rb
Normal file
102
test/unit/utils/filter_table_test.rb
Normal file
|
@ -0,0 +1,102 @@
|
|||
# encoding: utf-8
|
||||
# author: Dominik Richter
|
||||
# author: Stephan Renatus
|
||||
# author: Christoph Hartmann
|
||||
|
||||
require 'helper'
|
||||
|
||||
describe FilterTable do
|
||||
let (:data) {[
|
||||
{ foo: 3, bar: true, baz: 'yay', num: nil },
|
||||
{ foo: 2, bar: false, baz: 'noo', num: 1 },
|
||||
{ foo: 2, bar: false, baz: 'whatever', num: 2 },
|
||||
]}
|
||||
|
||||
let (:resource) {
|
||||
Class.new do
|
||||
attr_reader :data
|
||||
def initialize(data)
|
||||
@data = data
|
||||
end
|
||||
end
|
||||
}
|
||||
|
||||
let (:factory) { FilterTable.create(resource, :data) }
|
||||
let (:instance) { resource.new(data) }
|
||||
|
||||
it 'has a create utility which creates a filter factory' do
|
||||
factory.must_be_kind_of FilterTable::Factory
|
||||
end
|
||||
|
||||
describe 'when calling add_delegator' do
|
||||
it 'is chainable' do
|
||||
factory.add_delegator(:sth).must_equal factory
|
||||
end
|
||||
|
||||
it 'wont add nil' do
|
||||
proc { factory.add_delegator(nil) }.must_throw RuntimeError
|
||||
end
|
||||
|
||||
it 'can expose the where method' do
|
||||
factory.add_delegator(:where)
|
||||
_(instance.respond_to?(:where)).must_equal true
|
||||
instance.where({ baz: 'yay' }).params.must_equal [data[0]]
|
||||
end
|
||||
|
||||
it 'will delegate even non-existing methods' do
|
||||
factory.add_delegator(:not_here)
|
||||
_(instance.respond_to?(:not_here)).must_equal true
|
||||
end
|
||||
end
|
||||
|
||||
describe 'when calling add' do
|
||||
it 'is chainable' do
|
||||
factory.add(:sth).must_equal factory
|
||||
end
|
||||
|
||||
it 'wont add nil' do
|
||||
proc { factory.add(nil) }.must_throw RuntimeError
|
||||
end
|
||||
|
||||
it 'can expose a data column' do
|
||||
factory.add(:baz)
|
||||
instance.baz(123).must_be_kind_of(FilterTable::Table)
|
||||
end
|
||||
end
|
||||
|
||||
describe 'with the number field' do
|
||||
before { factory.add(:num) }
|
||||
|
||||
it 'filter by nil' do
|
||||
instance.num(nil).params.must_equal [data[0]]
|
||||
end
|
||||
|
||||
it 'filter by existing numbers' do
|
||||
instance.num(1).params.must_equal [data[1]]
|
||||
end
|
||||
|
||||
it 'filter by missing number' do
|
||||
instance.num(-1).params.must_equal []
|
||||
end
|
||||
end
|
||||
|
||||
describe 'with the string field' do
|
||||
before { factory.add(:baz) }
|
||||
|
||||
it 'filter by existing strings' do
|
||||
instance.baz('yay').params.must_equal [data[0]]
|
||||
end
|
||||
|
||||
it 'filter by missing string' do
|
||||
instance.baz('num').params.must_equal []
|
||||
end
|
||||
|
||||
it 'filter by existing regex' do
|
||||
instance.baz(/A/i).params.must_equal [data[0], data[2]]
|
||||
end
|
||||
|
||||
it 'filter by missing regex' do
|
||||
instance.baz(/zzz/).params.must_equal []
|
||||
end
|
||||
end
|
||||
end
|
Loading…
Reference in a new issue