lib/k_doc/table.rb in k_doc-0.0.8 vs lib/k_doc/table.rb in k_doc-0.0.10

- old
+ new

@@ -1,38 +1,58 @@ # frozen_string_literal: true module KDoc # Build rows (aka DataTable) with field definitions and rows of data class Table + include KLog::Logging + attr_reader :parent attr_reader :name + attr_reader :decorators def initialize(data, name = nil, **options, &block) initialize_attributes(data, name, **options) + @has_executed_field_decorators = false + @has_executed_row_decorators = false + instance_eval(&block) if block_given? + + run_decorators(:update_rows) end - def fields(field_definitions) + # Pass fields in using the following format + # fields :name, f(:type, :string), :db_type + # + # The older format of an array is supported via a splat conversion + def fields(*field_definitions) + if field_definitions.length == 1 && field_definitions[0].is_a?(Array) + log.warn('avoid supplying field definitions with array. *Splat fields is the preferred technique.') + field_definitions = *field_definitions[0] + end + fields = @data[@name]['fields'] field_definitions.each do |fd| fields << if fd.is_a?(String) || fd.is_a?(Symbol) field(fd, nil, :string) else fd end end + + run_decorators(:update_fields) end # rubocop:disable Metrics/AbcSize def row(*args, **named_args) fields = @data[@name]['fields'] - raise "To many values for row, argument #{i}" if args.length > fields.length + raise KType::Error, "To many values for row, argument #{args.length}" if args.length > fields.length # Apply column names with defaults + row = fields.each_with_object({}) do |f, hash| hash[f['name']] = f['default'] end # Override with positional arguments @@ -58,10 +78,14 @@ def get_rows @data[@name]['rows'] end # rubocop:enable Naming/AccessorMethodName + def internal_data + @data[@name] + end + def find_row(key, value) @data[@name]['rows'].find { |r| r[key] == value } end # Field definition @@ -89,19 +113,48 @@ 'type' => KUtil.data.clean_symbol(type_value) } end alias f field + def debug + log.o(KUtil.data.to_open_struct(internal_data)) + end + private + # This method can move into decorator helpers + # Run decorators a maximum of once for each behaviour + # rubocop:disable Metrics/CyclomaticComplexity + def run_decorators(behaviour) + return if behaviour == :update_fields && @has_executed_field_decorators + return if behaviour == :update_rows && @has_executed_row_decorators + + @has_executed_field_decorators = true if behaviour == :update_fields + + @has_executed_rows_decorators = true if behaviour == :update_rows + + decorators.each { |decorator| decorator.decorate(self, behaviour) } + end + # rubocop:enable Metrics/CyclomaticComplexity + + # rubocop:disable Metrics/AbcSize def initialize_attributes(data, name = nil, **options) @data = data @name = (name || FakeOpinion.new.default_table_key.to_s).to_s @parent = options[:parent] if !options.nil? && options.key?(:parent) + # This code needs to work differently, it needs to support the 3 different types + # Move the query into helpers + decorator_list = options[:decorators].nil? ? [] : options[:decorators] + + @decorators = decorator_list + .map(&:new) + .select { |decorator| decorator.compatible?(self) } + @data[@name] = { 'fields' => [], 'rows' => [] } end + # rubocop:enable Metrics/AbcSize def respond_to_missing?(name, *_args, &_block) (!@parent.nil? && @parent.respond_to?(name, true)) || super end