add #entries to filter table + remodel configuration

This commit is contained in:
Dominik Richter 2016-04-25 01:52:22 -04:00 committed by Christoph Hartmann
parent 048a1584b9
commit 0c8e891ee1
3 changed files with 102 additions and 34 deletions

View file

@ -34,8 +34,9 @@ module Inspec::Resources
@params ||= read_params
end
filter = FilterTable.create(self, :service_lines)
filter.add_delegator(:where)
filter = FilterTable.create
filter.add_accessor(:where)
.add_accessor(:entries)
.add(:services, field: 'service')
.add(:ids, field: 'id')
.add(:socket_types, field: 'socket_type')
@ -43,6 +44,7 @@ module Inspec::Resources
.add(:wait, field: 'wait')
.add(:disabled?) { |x| x.where('disable' => 'no').services.empty? }
.add(:enabled?) { |x| x.where('disable' => 'yes').services.empty? }
.connect(self, :service_lines)
private

View file

@ -19,12 +19,25 @@ module FilterTable
filters = ''
table = @params
conditions.each do |field, condition|
filters += " #{field} = #{condition.inspect}"
filters += " #{field} == #{condition.inspect}"
table = filter_lines(table, field, condition)
end
self.class.new(@resource, table, @filters + filters)
end
def new_entry(*_)
fail "#{self.class} must not be used on its own. It must be inherited "\
'and the #new_entry method must be implemented. This is an internal '\
'error and should not happen.'
end
def entries
f = @resource.to_s + @filters.to_s + ' one entry'
@params.map do |line|
new_entry(line, f)
end
end
def get_fields(*fields)
@params.map do |line|
fields.map { |f| line[f] }
@ -53,26 +66,60 @@ module FilterTable
end
class Factory
def initialize(resource, accessor)
@resource = resource
@accessor = accessor
@table = Class.new(Table)
def initialize
@accessors = []
@fields = {}
@blocks = {}
end
def table(instance)
table.new(self, instance.method(table_accessor).call, '')
def connect(resource, table_accessor) # rubocop:disable Metrics/AbcSize
# create the table structure
fields = @fields
blocks = @blocks
struct_fields = fields.values
# the struct to hold single items from the #entries method
entry_struct = Struct.new(*struct_fields.map(&:to_sym)) do
attr_accessor :__filter
def to_s # rubocop:disable Lint/NestedMethodDefinition
@__filter || super
end
end unless struct_fields.empty?
# the main filter table
table = Class.new(Table) {
fields.each do |method, field_name|
block = blocks[method]
define_method method.to_sym do |condition = Show|
return block.call(self) unless block.nil?
return where(nil).get_fields(field_name) if condition == Show
where({ field_name => condition })
end
end
define_method :new_entry do |hashmap, filter = ''|
return entry_struct.new if hashmap.nil?
res = entry_struct.new(*struct_fields.map { |x| hashmap[x] })
res.__filter = filter
res
end
}
# define all access methods with the parent resource
accessors = @accessors + @fields.keys
accessors.each do |method_name|
resource.send(:define_method, method_name.to_sym) do |*args|
filter = table.new(self, method(table_accessor).call, ' with')
filter.method(method_name.to_sym).call(*args)
end
end
end
def add_delegator(method_name)
def add_accessor(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
@accessors.push(method_name)
self
end
@ -82,17 +129,13 @@ module FilterTable
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)
@fields[method_name.to_sym] = field_name
@blocks[method_name.to_sym] = block
self
end
end
def self.create(resource, accessor)
Factory.new(resource, accessor)
def self.create
Factory.new
end
end

View file

@ -21,30 +21,30 @@ describe FilterTable do
end
}
let (:factory) { FilterTable.create(resource, :data) }
let (:factory) { FilterTable.create }
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
describe 'when calling add_accessor' do
it 'is chainable' do
factory.add_delegator(:sth).must_equal factory
factory.add_accessor(:sth).must_equal factory
end
it 'wont add nil' do
proc { factory.add_delegator(nil) }.must_throw RuntimeError
proc { factory.add_accessor(nil) }.must_throw RuntimeError
end
it 'can expose the where method' do
factory.add_delegator(:where)
factory.add_accessor(:where).connect(resource, :data)
_(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)
factory.add_accessor(:not_here).connect(resource, :data)
_(instance.respond_to?(:not_here)).must_equal true
end
end
@ -59,13 +59,36 @@ describe FilterTable do
end
it 'can expose a data column' do
factory.add(:baz)
factory.add(:baz).connect(resource, :data)
instance.baz(123).must_be_kind_of(FilterTable::Table)
end
end
describe 'when calling entries' do
before { factory.add(:baz).connect(resource, :data) }
let(:entries) { instance.baz(/.*/).entries }
let(:entry) { instance.baz('yay').entries }
it 'retrieves all entries with this field' do
entries.length.must_equal 3
entry.length.must_equal 1
end
it 'retrieves all entries with this field' do
entry[0].must_be_kind_of(Struct)
end
it 'retrieves all entries with this field' do
entry[0].baz.must_equal 'yay'
end
it 'prints nicely' do
entry[0].to_s.must_match(/ with baz == "yay" one entry/)
end
end
describe 'with the number field' do
before { factory.add(:num) }
before { factory.add(:num).connect(resource, :data) }
it 'filter by nil' do
instance.num(nil).params.must_equal [data[0]]
@ -81,7 +104,7 @@ describe FilterTable do
end
describe 'with the string field' do
before { factory.add(:baz) }
before { factory.add(:baz).connect(resource, :data) }
it 'filter by existing strings' do
instance.baz('yay').params.must_equal [data[0]]