Class: FilterTable::Factory
- Inherits:
-
Object
- Object
- FilterTable::Factory
- Defined in:
- lib/utils/filter.rb
Defined Under Namespace
Classes: CustomPropertyType
Instance Method Summary collapse
-
#initialize ⇒ Factory
constructor
A new instance of Factory.
-
#install_filter_methods_on_resource(resource_class, raw_data_fetcher_method_name) ⇒ Object
(also: #connect)
rubocop: disable Metrics/AbcSize, Metrics/MethodLength.
- #register_custom_property(property_name, opts = {}, &property_implementation) ⇒ Object (also: #add, #register_column, #register_custom_matcher)
-
#register_filter_method(method_name) ⇒ Object
(also: #add_accessor)
TODO: This should almost certainly be privatized.
Constructor Details
#initialize ⇒ Factory
Returns a new instance of Factory.
280 281 282 283 284 285 286 287 |
# File 'lib/utils/filter.rb', line 280 def initialize @filter_methods = [:where, :entries, :raw_data] @custom_properties = {} register_custom_matcher(:exist?) { |table| !table.raw_data.empty? } register_custom_property(:count) { |table| table.raw_data.count } @resource = nil # TODO: this variable is never initialized end |
Instance Method Details
#install_filter_methods_on_resource(resource_class, raw_data_fetcher_method_name) ⇒ Object Also known as: connect
rubocop: disable Metrics/AbcSize, Metrics/MethodLength
289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 |
# File 'lib/utils/filter.rb', line 289 def install_filter_methods_on_resource(resource_class, raw_data_fetcher_method_name) # rubocop: disable Metrics/AbcSize, Metrics/MethodLength # A context in which you can access the fields as accessors non_block_struct_fields = @custom_properties.values.reject(&:block).map(&:field_name) row_eval_context_type = Struct.new(*non_block_struct_fields.map(&:to_sym)) do attr_accessor :criteria_string attr_accessor :filter_table def to_s @criteria_string || super end end unless non_block_struct_fields.empty? properties_to_define = @custom_properties.map do |method_name, custom_property_structure| { method_name: method_name, method_body: create_custom_property_body(custom_property_structure) } end # Define the filter table subclass custom_properties = @custom_properties # We need a local var, not an instance var, for a closure below table_class = Class.new(Table) { # Install each custom property onto the FilterTable subclass properties_to_define.each do |property_info| define_method property_info[:method_name], &property_info[:method_body] end define_method :custom_properties_schema do custom_properties end # Install a method that can wrap all the fields into a context with accessors define_method :create_eval_context_for_row do |row_as_hash, criteria_string = ''| return row_eval_context_type.new if row_as_hash.nil? context = row_eval_context_type.new(*non_block_struct_fields.map { |field| row_as_hash[field] }) context.criteria_string = criteria_string context.filter_table = self context end } # Now that the table class is defined and the row eval context struct is defined, # extend the row eval context struct to support triggering population of lazy fields # in where blocks. To do that, we'll need a reference to the table (which # knows which fields are populated, and how to populate them) and we'll need to # override the getter method for each lazy field, so it will trigger # population if needed. Keep in mind we don't have to adjust the constructor # args of the row struct; also the Struct class will already have provided # a setter for each field. @custom_properties.values.each do |property_info| next unless property_info.opts[:lazy] field_name = property_info.field_name.to_sym row_eval_context_type.send(:define_method, field_name) do unless filter_table.field_populated?(field_name) filter_table.populate_lazy_field(field_name, NoCriteriaProvided) # No access to criteria here # OK, the underlying raw data has the value in the first row # (because we would trigger population only on the first row) # We could just return the value, but we need to set it on this Struct in case it is referenced multiple times # in the where block. self[field_name] = filter_table.raw_data[0][field_name] end # Now return the value using the Struct getter, whether newly populated or not self[field_name] end end # Define all access methods with the parent resource # These methods will be configured to return an `ExceptionCatcher` object # that will always return the original exception, but only when called # upon. This will allow method chains in `describe` statements to pass the # `instance_eval` when loaded and only throw-and-catch the exception when # the tests are run. methods_to_install_on_resource_class = @filter_methods + @custom_properties.keys methods_to_install_on_resource_class.each do |method_name| resource_class.send(:define_method, method_name.to_sym) do |*args, &block| begin # self here is the resource instance filter_table_instance = table_class.new(self, method(raw_data_fetcher_method_name).call, ' with') filter_table_instance.method(method_name.to_sym).call(*args, &block) rescue Inspec::Exceptions::ResourceFailed, Inspec::Exceptions::ResourceSkipped => e FilterTable::ExceptionCatcher.new(resource_class, e) end end end end |
#register_custom_property(property_name, opts = {}, &property_implementation) ⇒ Object Also known as: add, register_column, register_custom_matcher
390 391 392 393 394 395 396 397 398 399 400 401 402 403 |
# File 'lib/utils/filter.rb', line 390 def register_custom_property(property_name, opts = {}, &property_implementation) if property_name.nil? # TODO: @resource is never initialized throw RuntimeError, "Called filter.add for resource #{@resource} with method name nil!" end if @custom_properties.key?(property_name.to_sym) # TODO: issue deprecation warning? else @custom_properties[property_name.to_sym] = CustomPropertyType.new(opts[:field] || property_name, property_implementation, opts) end self end |
#register_filter_method(method_name) ⇒ Object Also known as: add_accessor
TODO: This should almost certainly be privatized. Every FilterTable client should get :entries and :where; InSpec core resources do not define anything else, other than azure_generic_resource, which is likely a mis-use.
375 376 377 378 379 380 381 382 383 384 385 386 |
# File 'lib/utils/filter.rb', line 375 def register_filter_method(method_name) if method_name.nil? # TODO: @resource is never initialized throw RuntimeError, "Called filter.add_accessor for resource #{@resource} with method name nil!" end if @filter_methods.include? method_name.to_sym # TODO: issue deprecation warning? else @filter_methods.push(method_name.to_sym) end self end |