Class: FilterTable::Table

Inherits:
Object
  • Object
show all
Defined in:
lib/inspec/utils/filter.rb

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(resource_instance, raw_data, criteria_string) ⇒ Table

Returns a new instance of Table.



92
93
94
95
96
97
98
# File 'lib/inspec/utils/filter.rb', line 92

def initialize(resource_instance, raw_data, criteria_string)
  @resource_instance = resource_instance
  @raw_data = raw_data
  @raw_data = [] if @raw_data.nil?
  @criteria_string = criteria_string
  @populated_lazy_columns = {}
end

Instance Attribute Details

#criteria_stringObject (readonly)

Returns the value of attribute criteria_string.



91
92
93
# File 'lib/inspec/utils/filter.rb', line 91

def criteria_string
  @criteria_string
end

#raw_dataObject (readonly)

Returns the value of attribute raw_data.



91
92
93
# File 'lib/inspec/utils/filter.rb', line 91

def raw_data
  @raw_data
end

#resource_instanceObject (readonly)

Returns the value of attribute resource_instance.



91
92
93
# File 'lib/inspec/utils/filter.rb', line 91

def resource_instance
  @resource_instance
end

Instance Method Details

#callback_for_lazy_field(field_name) ⇒ Object



247
248
249
250
251
252
253
# File 'lib/inspec/utils/filter.rb', line 247

def callback_for_lazy_field(field_name)
  return unless is_field_lazy?(field_name)

  custom_properties_schema.values.find do |property_struct|
    property_struct.field_name == field_name
  end.opts[:lazy]
end

#callback_for_lazy_instance_field(field_name) ⇒ Object



255
256
257
258
259
260
261
# File 'lib/inspec/utils/filter.rb', line 255

def callback_for_lazy_instance_field(field_name)
  return unless is_field_lazy_instance?(field_name)

  custom_properties_schema.values.find do |property_struct|
    property_struct.field_name == field_name
  end.opts[:lazy_instance]
end

#create_eval_context_for_row(*_) ⇒ Object



147
148
149
150
151
# File 'lib/inspec/utils/filter.rb', line 147

def create_eval_context_for_row(*_)
  raise "#{self.class} must not be used on its own. It must be inherited "\
       "and the #create_eval_context_for_row method must be implemented. This is an internal "\
       "error and should not happen."
end

#entriesObject



162
163
164
165
166
167
# File 'lib/inspec/utils/filter.rb', line 162

def entries
  row_criteria_string = resource.to_s + criteria_string + " one entry"
  raw_data.map do |row|
    create_eval_context_for_row(row, row_criteria_string)
  end
end

#field?(proposed_field) ⇒ Boolean

Returns:

  • (Boolean)


181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
# File 'lib/inspec/utils/filter.rb', line 181

def field?(proposed_field)
  # Currently we only know about a field if it is present in a at least one row of the raw data.
  # If we have no rows in the raw data, assume all fields are acceptable (and rely on failing to match on value, nil)
  return true if raw_data.empty?

  # Most resources have Symbol keys in their raw data.  Some have Strings (looking at you, `shadow`).
  is_field = false
  is_field ||= list_fields.include?(proposed_field.to_s)
  is_field ||= list_fields.include?(proposed_field.to_sym)
  is_field ||= is_field_lazy?(proposed_field.to_s)
  is_field ||= is_field_lazy?(proposed_field.to_sym)
  is_field ||= is_field_lazy_instance?(proposed_field.to_s)
  is_field ||= is_field_lazy_instance?(proposed_field.to_sym)

  is_field
end

#field_populated?(field_name) ⇒ Boolean

Returns:

  • (Boolean)


263
264
265
# File 'lib/inspec/utils/filter.rb', line 263

def field_populated?(field_name)
  @populated_lazy_columns[field_name]
end

#get_column_values(field) ⇒ Object



169
170
171
172
173
# File 'lib/inspec/utils/filter.rb', line 169

def get_column_values(field)
  raw_data.map do |row|
    row[field]
  end
end

#is_field_lazy?(sought_field_name) ⇒ Boolean

Returns:

  • (Boolean)


233
234
235
236
237
238
# File 'lib/inspec/utils/filter.rb', line 233

def is_field_lazy?(sought_field_name)
  custom_properties_schema.values.any? do |property_struct|
    sought_field_name == property_struct.field_name && \
      property_struct.opts[:lazy]
  end
end

#is_field_lazy_instance?(sought_field_name) ⇒ Boolean

Returns:

  • (Boolean)


240
241
242
243
244
245
# File 'lib/inspec/utils/filter.rb', line 240

def is_field_lazy_instance?(sought_field_name)
  custom_properties_schema.values.any? do |property_struct|
    sought_field_name == property_struct.field_name && \
      property_struct.opts[:lazy_instance]
  end
end

#list_fieldsObject



175
176
177
178
179
# File 'lib/inspec/utils/filter.rb', line 175

def list_fields
  @__fields_in_raw_data ||= raw_data.reduce([]) do |fields, row|
    fields.concat(row.keys).uniq
  end
end

#mark_lazy_field_populated(field_name) ⇒ Object



267
268
269
# File 'lib/inspec/utils/filter.rb', line 267

def mark_lazy_field_populated(field_name)
  @populated_lazy_columns[field_name] = true
end

#paramsObject



157
158
159
160
# File 'lib/inspec/utils/filter.rb', line 157

def params
  # TODO: consider deprecating
  raw_data
end

#populate_lazy_field(field_name, criterion) ⇒ Object



204
205
206
207
208
209
210
211
212
213
214
# File 'lib/inspec/utils/filter.rb', line 204

def populate_lazy_field(field_name, criterion)
  return unless is_field_lazy?(field_name)
  return if field_populated?(field_name)

  raw_data.each do |row|
    next if row.key?(field_name) # skip row if pre-existing data is present

    callback_for_lazy_field(field_name).call(row, criterion, self)
  end
  mark_lazy_field_populated(field_name)
end

#populate_lazy_instance_field(field_name, criterion) ⇒ Object



216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
# File 'lib/inspec/utils/filter.rb', line 216

def populate_lazy_instance_field(field_name, criterion)
  return unless is_field_lazy_instance?(field_name)
  return if field_populated?(field_name)

  raw_data.each do |row|
    next if row.key?(field_name) # skip row if pre-existing data is present

    lazy_caller = callback_for_lazy_instance_field(field_name)
    if lazy_caller.is_a?(Proc)
      lazy_caller.call(row, criterion, resource_instance)
    elsif lazy_caller.is_a?(Symbol)
      resource_instance.send(lazy_caller, row, criterion, self)
    end
  end
  mark_lazy_field_populated(field_name)
end

#resourceObject



153
154
155
# File 'lib/inspec/utils/filter.rb', line 153

def resource
  resource_instance
end

#to_sObject Also known as: inspect



198
199
200
# File 'lib/inspec/utils/filter.rb', line 198

def to_s
  resource.to_s + criteria_string
end

#where(conditions = {}, &block) ⇒ Object

Filter the raw data based on criteria (as method params) or by evaling a block; then construct a new Table of the same class as ourselves, wrapping the filtered data, and return it.



103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
# File 'lib/inspec/utils/filter.rb', line 103

def where(conditions = {}, &block)
  return self unless conditions.is_a?(Hash)
  return self if conditions.empty? && !block_given?

  # Initialize the details of the new Table.
  new_criteria_string = criteria_string
  filtered_raw_data = raw_data

  # If we were provided params, interpret them as criteria to be evaluated
  # against the raw data. Criteria are assumed to be hash keys.
  conditions.each do |raw_field_name, desired_value|
    raise(ArgumentError, "'#{decorate_symbols(raw_field_name)}' is not a recognized criterion - expected one of #{decorate_symbols(list_fields).join(", ")}'") unless field?(raw_field_name)

    populate_lazy_field(raw_field_name, desired_value) if is_field_lazy?(raw_field_name)
    populate_lazy_instance_field(raw_field_name, desired_value) if is_field_lazy_instance?(raw_field_name)
    new_criteria_string += " #{raw_field_name} == #{desired_value.inspect}"
    filtered_raw_data = filter_raw_data(filtered_raw_data, raw_field_name, desired_value)
  end

  # If we were given a block, make a special Struct for each row, that has an accessor
  # for each field declared using `register_custom_property`, then instance-eval the block
  # against the struct.
  if block_given?
    # Perform the filtering.
    filtered_raw_data = filtered_raw_data.find_all { |row_as_hash| create_eval_context_for_row(row_as_hash, "").instance_eval(&block) }
    # Try to interpret the block for updating the stringification.
    src = Trace.new
    # Swallow any exceptions raised here.
    # See https://github.com/chef/inspec/issues/2929
    begin
      src.instance_eval(&block)
    rescue # rubocop: disable Lint/HandleExceptions
      # Yes, robocop, ignoring all exceptions is normally
      # a bad idea.  Here, an exception just means we don't
      # understand what was in a `where` block, so we can't
      # meaningfully sytringify it.  We still have a decent
      # default stringification.
    end
    new_criteria_string += Trace.to_ruby(src)
  end

  self.class.new(resource, filtered_raw_data, new_criteria_string)
end