Skip to content
4 changes: 2 additions & 2 deletions lib/slither/definition.rb
Original file line number Diff line number Diff line change
Expand Up @@ -76,8 +76,8 @@ def template(name, options = {}, &block)
# @param block [Block] A block for defining fields within the section.
# @return [Section] The newly created section.
#
def method_missing(method, *args, &block) # rubocop:disable Style/MissingRespondToMissing
section(method, *args, &block)
def method_missing(method, *, &block) # rubocop:disable Style/MissingRespondToMissing
section(method, *, &block)
end

private
Expand Down
203 changes: 109 additions & 94 deletions lib/slither/section.rb
Original file line number Diff line number Diff line change
@@ -1,94 +1,109 @@
module Slither
class Section
attr_accessor :definition, :optional
attr_reader :name, :columns, :options, :length

RESERVED_NAMES = [:spacer]

def initialize(name, options = {})
@name = name
@options = options
@columns = []
@trap = options[:trap]
@optional = options[:optional] || false
@length = 0
end

def column(name, length, options = {})
raise(Slither::DuplicateColumnNameError, "You have already defined a column named '#{name}'.") if @columns.map do |c|
RESERVED_NAMES.include?(c.name) ? nil : c.name
end.flatten.include?(name)
col = Column.new(name, length, @options.merge(options))
@columns << col
@length += length
col
end

def spacer(length)
column(:spacer, length)
end

def trap(&block)
@trap = block
end

def template(name)
template = @definition.templates[name]
raise ArgumentError, "Template #{name} not found as a known template." unless template
@columns += template.columns
@length += template.length
# Section options should trump template options
@options = template.options.merge(@options)
end

# Format a data Hash using columns width.
# - Data - hash, based on columns definitions content.
# Ex: Having the next 2 columns .column(:id, 5) && .column(:name, 10)
# we pass the data hash data = { id: 3, name: "Ryan" }
# the result is the content of the hash based on the columns width:
# format(data)
# => " 3 Ryan"
def format(data)
# raise( ColumnMismatchError,
# "The '#{@name}' section has #{@columns.size} column(s) defined, but there are #{data.size} column(s) provided in the data."
# ) unless @columns.size == data.size
row = ''
@columns.each do |column|
row += column.format(data[column.name])
end
row
end

def parse(line)
line_data = line.unpack(unpacker)
row = {}
@columns.each_with_index do |c, i|
row[c.name] = c.parse(line_data[i]) unless RESERVED_NAMES.include?(c.name)
end
row
end

def parse_when_problem(line)
line_data = line.unpack(@columns.map { |c| "a#{c.length}" }.join(''))
row = ''
@columns.each_with_index do |c, i|
row << "\n'#{c.name}':'#{line_data[i]}'" unless RESERVED_NAMES.include?(c.name)
end
row
end

def match(raw_line)
raw_line.nil? ? false : @trap.call(raw_line)
end

def method_missing(method, *args)
column(method, *args)
end

private

def unpacker
@columns.map { |c| c.unpacker }.join('')
end
end
end
# frozen_string_literal: true

module Slither
class Section
attr_accessor :definition, :optional
attr_reader :name, :columns, :options, :length

RESERVED_NAMES = [:spacer].freeze

def initialize(name, options = {})
@name = name
@options = options
@columns = []
@trap = options[:trap]
@optional = options[:optional] || false
@length = 0
end

def column(name, length, options = {})
if @columns.map do |c|
RESERVED_NAMES.include?(c.name) ? nil : c.name
end.flatten.include?(name)
raise(Slither::DuplicateColumnNameError,
"You have already defined a column named '#{name}'.")
end

col = Column.new(name, length, @options.merge(options))
@columns << col
@length += length
col
end

def spacer(length)
column(:spacer, length)
end

def trap(&block)
@trap = block
end

def template(name)
template = @definition.templates[name]
raise ArgumentError, "Template #{name} not found as a known template." unless template

@columns += template.columns
@length += template.length
# Section options should trump template options
@options = template.options.merge(@options)
end

# Format a data Hash using columns width.
# - Data - hash, based on columns definitions content.
# Ex: Having the next 2 columns .column(:id, 5) && .column(:name, 10)
# we pass the data hash data = { id: 3, name: "Ryan" }
# the result is the content of the hash based on the columns width:
# format(data)
# => " 3 Ryan"
def format(data)
# raise( ColumnMismatchError,
# "The '#{@name}' section has #{@columns.size} column(s) defined, but there are #{data.size} column(s) provided in the data."
# ) unless @columns.size == data.size
row = String.new

@columns.each do |column|
row += column.format(data[column.name])
end

row
end

def parse(line)
line_data = line.unpack(unpacker)
row = {}
@columns.each_with_index do |c, i|
row[c.name] = c.parse(line_data[i]) unless RESERVED_NAMES.include?(c.name)
end
row
end

def parse_when_problem(line)
line_data = line.unpack(@columns.map { |c| "a#{c.length}" }.join)
row = String.new

@columns.each_with_index do |c, i|
row << "\n'#{c.name}':'#{line_data[i]}'" unless RESERVED_NAMES.include?(c.name)
end

row
end

def match(raw_line)
raw_line.nil? ? false : @trap.call(raw_line)
end

def respond_to_missing?(_method_name, _include_private = false)
true
end

def method_missing(method, *)
column(method, *)
end

private

def unpacker
@columns.map(&:unpacker).join
end
end
end
1 change: 1 addition & 0 deletions spec/slither/section_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@

context "when using a method that's not defined" do
it "uses method_missing to create a column" do
expect(subject).to receive(:method_missing).and_call_original
column = subject.first_name(5)

expect(subject.columns).to match([column])
Expand Down