diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 9884668..fe4b2ed 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -21,10 +21,10 @@ jobs: fail-fast: false matrix: os: [ubuntu-latest, macos-latest] - ruby: ['2.7', '3.0', '3.1', '3.2'] + ruby: ['3.2', '3.3'] include: - os: ubuntu-latest - ruby: '3.2' + ruby: '3.3' coverage: true runs-on: ${{ matrix.os }} steps: @@ -41,7 +41,7 @@ jobs: COVERAGE: ${{ matrix.coverage }} - name: Generate coverage artifact if: ${{ matrix.coverage }} - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: name: docs-coverage path: docs/coverage @@ -50,7 +50,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout repository - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Setup Ruby uses: ruby/setup-ruby@v1 with: @@ -59,7 +59,7 @@ jobs: - name: Generate Ruby API documentation run : bundle exec yard doc - name: Generate Ruby API documentation artifact - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: name: docs-ruby path: docs/ruby @@ -80,12 +80,12 @@ jobs: url: ${{ steps.deployment.outputs.page_url }} steps: - name: Fetch Ruby API documentation artifact - uses: actions/download-artifact@v3 + uses: actions/download-artifact@v4 with: name: docs-ruby path: docs/ruby - name: Fetch coverage artifact - uses: actions/download-artifact@v3 + uses: actions/download-artifact@v4 with: name: docs-coverage path: docs/coverage diff --git a/.rubocop.yml b/.rubocop.yml index 1be8271..53eef17 100644 --- a/.rubocop.yml +++ b/.rubocop.yml @@ -6,7 +6,7 @@ require: - rubocop-rspec AllCops: - TargetRubyVersion: 2.7 + TargetRubyVersion: 3.2 NewCops: enable Exclude: - bin/rspec diff --git a/Gemfile.lock b/Gemfile.lock index 564b1b4..c547348 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -12,15 +12,17 @@ GEM docile (1.4.0) json (2.6.3) language_server-protocol (3.17.0.3) - nokogiri (1.15.4-x86_64-darwin) + nokogiri (1.18.8-arm64-darwin) racc (~> 1.4) - nokogiri (1.15.4-x86_64-linux) + nokogiri (1.18.8-x86_64-darwin) + racc (~> 1.4) + nokogiri (1.18.8-x86_64-linux-gnu) racc (~> 1.4) parallel (1.23.0) parser (3.2.2.3) ast (~> 2.4.1) racc - racc (1.7.1) + racc (1.8.1) rainbow (3.1.1) regexp_parser (2.8.1) rexml (3.2.6) @@ -77,6 +79,8 @@ GEM yard (0.9.34) PLATFORMS + arm64-darwin-23 + arm64-darwin-24 x86_64-darwin x86_64-linux diff --git a/lib/sheetah/attribute.rb b/lib/sheetah/attribute.rb index b6296bd..7b284f9 100644 --- a/lib/sheetah/attribute.rb +++ b/lib/sheetah/attribute.rb @@ -31,12 +31,12 @@ def each_column(config) header, header_pattern = config.header(key, index) yield Column.new( - key: key, + key:, type: compiled_type, - index: index, - header: header, - header_pattern: header_pattern, - required: required + index:, + header:, + header_pattern:, + required: ) end end diff --git a/lib/sheetah/backends.rb b/lib/sheetah/backends.rb index 946ad7c..8ea71da 100644 --- a/lib/sheetah/backends.rb +++ b/lib/sheetah/backends.rb @@ -13,14 +13,14 @@ module Backends class << self attr_reader :registry - def open(*args, **opts, &block) - backend = opts.delete(:backend) || registry.get(*args, **opts) + def open(*, **opts, &) + backend = opts.delete(:backend) || registry.get(*, **opts) if backend.nil? return Utils::MonadicResult::Failure.new(SimpleError.new("no_applicable_backend")) end - backend.open(*args, **opts, &block) + backend.open(*, **opts, &) end end end diff --git a/lib/sheetah/backends/csv.rb b/lib/sheetah/backends/csv.rb index 56c0cc9..ecf142c 100644 --- a/lib/sheetah/backends/csv.rb +++ b/lib/sheetah/backends/csv.rb @@ -59,7 +59,7 @@ def each_header @headers.each_with_index do |header, col_idx| col = Sheet.int2col(col_idx + 1) - yield Header.new(col: col, value: header) + yield Header.new(col:, value: header) end self @@ -72,10 +72,10 @@ def each_row value = Array.new(@cols_count) do |col_idx| col = Sheet.int2col(col_idx + 1) - Cell.new(row: row, col: col, value: raw[col_idx]) + Cell.new(row:, col:, value: raw[col_idx]) end - yield Row.new(row: row, value: value) + yield Row.new(row:, value:) end self diff --git a/lib/sheetah/backends/wrapper.rb b/lib/sheetah/backends/wrapper.rb index f5f9e40..31f3d86 100644 --- a/lib/sheetah/backends/wrapper.rb +++ b/lib/sheetah/backends/wrapper.rb @@ -40,10 +40,10 @@ def each_row raw = @table[row] value = Array.new(@cols_count) do |col_idx| - Cell.new(row: row, col: Sheet.int2col(col_idx + 1), value: raw[col_idx]) + Cell.new(row:, col: Sheet.int2col(col_idx + 1), value: raw[col_idx]) end - yield Row.new(row: row, value: value) + yield Row.new(row:, value:) end self diff --git a/lib/sheetah/backends/xlsx.rb b/lib/sheetah/backends/xlsx.rb index 9a417a2..3940daa 100644 --- a/lib/sheetah/backends/xlsx.rb +++ b/lib/sheetah/backends/xlsx.rb @@ -41,7 +41,7 @@ def each_header @headers.each_with_index do |header, col_idx| col = Sheet.int2col(col_idx + 1) - yield Header.new(col: col, value: header) + yield Header.new(col:, value: header) end self @@ -63,10 +63,10 @@ def each_row value = Array.new(@cols_count) do |col_idx| col = Sheet.int2col(col_idx + 1) - Cell.new(row: row, col: col, value: raw[col_idx]) + Cell.new(row:, col:, value: raw[col_idx]) end - yield Row.new(row: row, value: value) + yield Row.new(row:, value:) end self diff --git a/lib/sheetah/headers.rb b/lib/sheetah/headers.rb index 35e918c..e451afe 100644 --- a/lib/sheetah/headers.rb +++ b/lib/sheetah/headers.rb @@ -1,7 +1,5 @@ # frozen_string_literal: true -require "set" - module Sheetah class Headers include Utils::MonadicResult @@ -72,6 +70,8 @@ def add_ensure_column_is_specified(header, column) @messenger.error("invalid_header", header.value) end + @messenger.warn("ignored_column", header.value) if @specification.report_ignored_columns? + false end diff --git a/lib/sheetah/messaging.rb b/lib/sheetah/messaging.rb index e07e818..547b0c6 100644 --- a/lib/sheetah/messaging.rb +++ b/lib/sheetah/messaging.rb @@ -107,7 +107,7 @@ def scoping(...) dup.scoping!(...) end - def scope_row!(row, &block) + def scope_row!(row, &) scope = case @scope when SCOPES::COL, SCOPES::CELL SCOPES::CELL @@ -118,10 +118,10 @@ def scope_row!(row, &block) scope_data = @scope_data.dup || {} scope_data[:row] = row - scoping!(scope, scope_data, &block) + scoping!(scope, scope_data, &) end - def scope_col!(col, &block) + def scope_col!(col, &) scope = case @scope when SCOPES::ROW, SCOPES::CELL SCOPES::CELL @@ -132,7 +132,7 @@ def scope_col!(col, &block) scope_data = @scope_data.dup || {} scope_data[:col] = col - scoping!(scope, scope_data, &block) + scoping!(scope, scope_data, &) end def scope_row(...) @@ -159,11 +159,11 @@ def exception(error) def add(severity, code, data) messages << Message.new( - code: code, + code:, code_data: data, scope: @scope, scope_data: @scope_data, - severity: severity + severity: ) self diff --git a/lib/sheetah/row_value_builder.rb b/lib/sheetah/row_value_builder.rb index 89b4eb1..a0b8613 100644 --- a/lib/sheetah/row_value_builder.rb +++ b/lib/sheetah/row_value_builder.rb @@ -1,6 +1,5 @@ # frozen_string_literal: true -require "set" require_relative "utils/monadic_result" module Sheetah diff --git a/lib/sheetah/sheet.rb b/lib/sheetah/sheet.rb index 2973eda..5eb4e5a 100644 --- a/lib/sheetah/sheet.rb +++ b/lib/sheetah/sheet.rb @@ -19,9 +19,9 @@ def self.int2col(...) end module ClassMethods - def open(*args, **opts) + def open(*, **) handle_sheet_error do - sheet = new(*args, **opts) + sheet = new(*, **) next sheet unless block_given? begin diff --git a/lib/sheetah/sheet_processor.rb b/lib/sheetah/sheet_processor.rb index f7ce3e2..85e7642 100644 --- a/lib/sheetah/sheet_processor.rb +++ b/lib/sheetah/sheet_processor.rb @@ -16,11 +16,11 @@ def initialize(specification) @specification = specification end - def call(*args, **opts) + def call(*, **) messenger = Messaging::Messenger.new result = Do() do - Backends.open(*args, **opts) do |sheet| + Backends.open(*, **) do |sheet| row_processor = build_row_processor(sheet, messenger) sheet.each_row do |row| @@ -35,7 +35,7 @@ def call(*args, **opts) private def parse_headers(sheet, messenger) - headers = Headers.new(specification: @specification, messenger: messenger) + headers = Headers.new(specification: @specification, messenger:) sheet.each_header do |header| headers.add(header) @@ -47,7 +47,7 @@ def parse_headers(sheet, messenger) def build_row_processor(sheet, messenger) headers = parse_headers(sheet, messenger).unwrap - RowProcessor.new(headers: headers, messenger: messenger) + RowProcessor.new(headers:, messenger:) end def handle_result(result, messenger) diff --git a/lib/sheetah/specification.rb b/lib/sheetah/specification.rb index acc7f18..cd6f65f 100644 --- a/lib/sheetah/specification.rb +++ b/lib/sheetah/specification.rb @@ -13,9 +13,10 @@ class MutablePatternError < Errors::SpecError class DuplicatedPatternError < Errors::SpecError end - def initialize(ignore_unspecified_columns: false) + def initialize(ignore_unspecified_columns: false, report_ignored_columns: false) @column_by_pattern = {} @ignore_unspecified_columns = ignore_unspecified_columns + @report_ignored_columns = report_ignored_columns end def set(pattern, column) @@ -56,6 +57,10 @@ def ignore_unspecified_columns? @ignore_unspecified_columns end + def report_ignored_columns? + @report_ignored_columns + end + def freeze @column_by_pattern.freeze super diff --git a/lib/sheetah/template.rb b/lib/sheetah/template.rb index bacc427..a845d55 100644 --- a/lib/sheetah/template.rb +++ b/lib/sheetah/template.rb @@ -1,6 +1,5 @@ # frozen_string_literal: true -require "set" require_relative "attribute" require_relative "specification" require_relative "errors/spec_error" @@ -26,13 +25,17 @@ module Sheetah # specification, and composite attributes will produce as many columns as # required by the number of scalar values they hold. class Template - def initialize(attributes:, ignore_unspecified_columns: false) + def initialize(attributes:, ignore_unspecified_columns: false, report_ignored_columns: false) @attributes = build_attributes(attributes) @ignore_unspecified_columns = ignore_unspecified_columns + @report_ignored_columns = ignore_unspecified_columns && report_ignored_columns end def apply(config) - specification = Specification.new(ignore_unspecified_columns: @ignore_unspecified_columns) + specification = Specification.new( + ignore_unspecified_columns: @ignore_unspecified_columns, + report_ignored_columns: @report_ignored_columns + ) @attributes.each do |attribute| attribute.each_column(config) do |column| diff --git a/lib/sheetah/types/type.rb b/lib/sheetah/types/type.rb index daae2a1..38d2886 100644 --- a/lib/sheetah/types/type.rb +++ b/lib/sheetah/types/type.rb @@ -44,11 +44,11 @@ def new!(...) self.cast_classes = [] - def initialize(**opts) + def initialize(**) @cast_chain = CastChain.new self.class.cast_classes.each do |cast_class| - @cast_chain.append(cast_class.new(**opts)) + @cast_chain.append(cast_class.new(**)) end end diff --git a/lib/sheetah/utils/cell_string_cleaner.rb b/lib/sheetah/utils/cell_string_cleaner.rb index 311399e..bf4456f 100644 --- a/lib/sheetah/utils/cell_string_cleaner.rb +++ b/lib/sheetah/utils/cell_string_cleaner.rb @@ -4,8 +4,8 @@ module Sheetah module Utils class CellStringCleaner garbage = "(?:[^[:print:]]|[[:space:]])+" - GARBAGE_PREFIX = /\A#{garbage}/.freeze - GARBAGE_SUFFIX = /#{garbage}\Z/.freeze + GARBAGE_PREFIX = /\A#{garbage}/ + GARBAGE_SUFFIX = /#{garbage}\Z/ private_constant :GARBAGE_PREFIX, :GARBAGE_SUFFIX def self.call(...) diff --git a/lib/sheetah/utils/monadic_result.rb b/lib/sheetah/utils/monadic_result.rb index a404a51..2b07bca 100644 --- a/lib/sheetah/utils/monadic_result.rb +++ b/lib/sheetah/utils/monadic_result.rb @@ -164,8 +164,8 @@ def Failure(...) Failure.new(...) end - def Do(&block) - catch(DO_TOKEN, &block) + def Do(&) + catch(DO_TOKEN, &) end # rubocop:enable Naming/MethodName diff --git a/sheetah.gemspec b/sheetah.gemspec index e95637a..7e4ffcf 100644 --- a/sheetah.gemspec +++ b/sheetah.gemspec @@ -9,7 +9,7 @@ Gem::Specification.new do |spec| spec.homepage = "https://steeple.com" spec.summary = "Process tabular data from different sources with a rich, unified API" - spec.required_ruby_version = ">= 2.7.0" + spec.required_ruby_version = ">= 3.2" spec.metadata["homepage_uri"] = "https://github.com/steeple-org/sheetah" spec.metadata["source_code_uri"] = "https://github.com/steeple-org/sheetah" diff --git a/spec/sheetah/backends/csv_spec.rb b/spec/sheetah/backends/csv_spec.rb index 1357f58..252c1ab 100644 --- a/spec/sheetah/backends/csv_spec.rb +++ b/spec/sheetah/backends/csv_spec.rb @@ -51,9 +51,9 @@ def new_sheet(...) it "matches any so-called IO and an optional encoding" do io = double - expect(registry.get(io: io)).to eq(described_class) - expect(registry.get(io: io, encoding: "UTF-8")).to eq(described_class) - expect(registry.get(io: io, encoding: Encoding::UTF_8)).to eq(described_class) + expect(registry.get(io:)).to eq(described_class) + expect(registry.get(io:, encoding: "UTF-8")).to eq(described_class) + expect(registry.get(io:, encoding: Encoding::UTF_8)).to eq(described_class) end it "matches a CSV path and an optional encoding" do @@ -112,7 +112,7 @@ def new_sheet(...) alias_method :io_path, :utf8_path it "can read CSV data" do - sheet = described_class.new(io: io) + sheet = described_class.new(io:) expect(sheet.each_header.map(&:value)).to eq(headers) end end @@ -122,12 +122,12 @@ def new_sheet(...) it "fails" do expect do - described_class.new(io: io) + described_class.new(io:) end.to raise_error(described_class::EncodingError) end it "can read CSV data once given a valid encoding" do - sheet = described_class.new(io: io, encoding: Encoding::ISO_8859_15) + sheet = described_class.new(io:, encoding: Encoding::ISO_8859_15) expect(sheet.each_header.map(&:value)).to eq(headers) end end @@ -138,7 +138,7 @@ def new_sheet(...) alias_method :path, :utf8_path it "can read CSV data" do - sheet = described_class.new(path: path) + sheet = described_class.new(path:) expect(sheet.each_header.map(&:value)).to eq(headers) end end @@ -148,12 +148,12 @@ def new_sheet(...) it "fails" do expect do - described_class.new(path: path) + described_class.new(path:) end.to raise_error(described_class::EncodingError) end it "can read CSV data once given a valid encoding" do - sheet = described_class.new(path: path, encoding: Encoding::ISO_8859_15) + sheet = described_class.new(path:, encoding: Encoding::ISO_8859_15) expect(sheet.each_header.map(&:value)).to eq(headers) end end diff --git a/spec/sheetah/backends_spec.rb b/spec/sheetah/backends_spec.rb index 2922b2a..0148395 100644 --- a/spec/sheetah/backends_spec.rb +++ b/spec/sheetah/backends_spec.rb @@ -19,23 +19,23 @@ let(:res) { double } it "may open with an explicit backend" do - allow(backend).to receive(:open).with(foo, bar: bar).and_return(res) + allow(backend).to receive(:open).with(foo, bar:).and_return(res) expect(described_class.registry).not_to receive(:get) - expect(described_class.open(foo, backend: backend, bar: bar)).to be(res) + expect(described_class.open(foo, backend:, bar:)).to be(res) end it "may open with an implicit backend" do - allow(backend).to receive(:open).with(foo, bar: bar).and_return(res) - allow(described_class.registry).to receive(:get).with(foo, bar: bar).and_return(backend) + allow(backend).to receive(:open).with(foo, bar:).and_return(res) + allow(described_class.registry).to receive(:get).with(foo, bar:).and_return(backend) - expect(described_class.open(foo, bar: bar)).to be(res) + expect(described_class.open(foo, bar:)).to be(res) end it "may miss a backend to open" do - allow(described_class.registry).to receive(:get).with(foo, bar: bar).and_return(nil) + allow(described_class.registry).to receive(:get).with(foo, bar:).and_return(nil) - result = described_class.open(foo, bar: bar) + result = described_class.open(foo, bar:) expect(result).to be_failure expect(result.failure).to have_attributes(msg_code: "no_applicable_backend") end diff --git a/spec/sheetah/column_spec.rb b/spec/sheetah/column_spec.rb index bf084d8..494b3ba 100644 --- a/spec/sheetah/column_spec.rb +++ b/spec/sheetah/column_spec.rb @@ -11,11 +11,11 @@ let(:col) do described_class.new( - key: key, - type: type, - index: index, - header: header, - header_pattern: header_pattern + key:, + type:, + index:, + header:, + header_pattern: ) end @@ -58,10 +58,10 @@ let(:col) do described_class.new( - key: key, - type: type, - index: index, - header: header + key:, + type:, + index:, + header: ) end diff --git a/spec/sheetah/headers_spec.rb b/spec/sheetah/headers_spec.rb index 34d8d83..2a065a7 100644 --- a/spec/sheetah/headers_spec.rb +++ b/spec/sheetah/headers_spec.rb @@ -11,7 +11,8 @@ instance_double( Sheetah::Specification, required_columns: [], - ignore_unspecified_columns?: false + ignore_unspecified_columns?: false, + report_ignored_columns?: false ) end @@ -32,7 +33,7 @@ end let(:headers) do - described_class.new(specification: specification, messenger: messenger) + described_class.new(specification:, messenger:) end def stub_specification(column_by_header) @@ -106,6 +107,31 @@ def stub_specification(column_by_header) ] ) end + + context "when the ignored columns reporting is enabled" do + let(:specification) do + instance_double( + Sheetah::Specification, + required_columns: [], + ignore_unspecified_columns?: true, + report_ignored_columns?: true + ) + end + + it "messages a warning" do + expect(messenger.messages).to eq( + [ + Sheetah::Messaging::Message.new( + severity: Sheetah::Messaging::SEVERITIES::WARN, + code: "ignored_column", + code_data: sheet_headers[4].value, + scope: Sheetah::Messaging::SCOPES::COL, + scope_data: { col: sheet_headers[4].col } + ), + ] + ) + end + end end context "when there is a duplicate" do diff --git a/spec/sheetah/messaging/message_spec.rb b/spec/sheetah/messaging/message_spec.rb index d934b41..d0c3a33 100644 --- a/spec/sheetah/messaging/message_spec.rb +++ b/spec/sheetah/messaging/message_spec.rb @@ -11,11 +11,11 @@ let(:message) do described_class.new( - code: code, - code_data: code_data, - scope: scope, - scope_data: scope_data, - severity: severity + code:, + code_data:, + scope:, + scope_data:, + severity: ) end @@ -24,8 +24,8 @@ end it "may have only a custom code and some defaults attributes" do - expect(described_class.new(code: code)).to have_attributes( - code: code, + expect(described_class.new(code:)).to have_attributes( + code:, code_data: nil, scope: Sheetah::Messaging::SCOPES::SHEET, scope_data: nil, @@ -35,32 +35,32 @@ it "may have completely custom attributes" do expect(message).to have_attributes( - code: code, - code_data: code_data, - scope: scope, - scope_data: scope_data, - severity: severity + code:, + code_data:, + scope:, + scope_data:, + severity: ) end it "is equivalent to a message having the same attributes" do other_message = described_class.new( - code: code, - code_data: code_data, - scope: scope, - scope_data: scope_data, - severity: severity + code:, + code_data:, + scope:, + scope_data:, + severity: ) expect(message).to eq(other_message) end it "is not equivalent to a message having different attributes" do other_message = described_class.new( - code: code, - code_data: code_data, - scope: scope, + code:, + code_data:, + scope:, scope_data: double, - severity: severity + severity: ) expect(message).not_to eq(other_message) end diff --git a/spec/sheetah/messaging/messenger_spec.rb b/spec/sheetah/messaging/messenger_spec.rb index e6a9ad3..bfa3f52 100644 --- a/spec/sheetah/messaging/messenger_spec.rb +++ b/spec/sheetah/messaging/messenger_spec.rb @@ -29,7 +29,7 @@ def be_the_frozen(obj) end it "may have some custom, frozen attributes" do - messenger = described_class.new(scope: scope, scope_data: scope_data) + messenger = described_class.new(scope:, scope_data:) expect(messenger).to have_attributes( scope: be_the_frozen(scope), @@ -41,7 +41,7 @@ def be_the_frozen(obj) describe "#dup" do let(:messenger1) do - described_class.new(scope: scope, scope_data: scope_data) + described_class.new(scope:, scope_data:) end it "returns a new instance" do @@ -144,8 +144,8 @@ def allow_method_call_checking_block(receiver, method_name, *args, **opts, &bloc result end - def stub_scoping!(receiver, *args, &block) - allow_method_call_checking_block(receiver, :scoping!, *args, &block) + def stub_scoping!(receiver, ...) + allow_method_call_checking_block(receiver, :scoping!, ...) end describe "#scoping" do @@ -175,12 +175,12 @@ def stub_scoping!(receiver, *args, &block) let(:messenger) { described_class.new } it "scopes the messenger to the given row (with a block)" do - scoping_result = stub_scoping!(messenger, scopes::ROW, { row: row }, &scoping_block) + scoping_result = stub_scoping!(messenger, scopes::ROW, { row: }, &scoping_block) expect(messenger.scope_row!(row, &scoping_block)).to eq(scoping_result) end it "scopes the messenger to the given row (without a block)" do - scoping_result = stub_scoping!(messenger, scopes::ROW, { row: row }) + scoping_result = stub_scoping!(messenger, scopes::ROW, { row: }) expect(messenger.scope_row!(row)).to eq(scoping_result) end end @@ -190,27 +190,27 @@ def stub_scoping!(receiver, *args, &block) let(:messenger) { described_class.new(scope: scopes::ROW, scope_data: { row: other_row }) } it "scopes the messenger to the given row (with a block)" do - scoping_result = stub_scoping!(messenger, scopes::ROW, { row: row }, &scoping_block) + scoping_result = stub_scoping!(messenger, scopes::ROW, { row: }, &scoping_block) expect(messenger.scope_row!(row, &scoping_block)).to eq(scoping_result) end it "scopes the messenger to the given row (without a block)" do - scoping_result = stub_scoping!(messenger, scopes::ROW, { row: row }) + scoping_result = stub_scoping!(messenger, scopes::ROW, { row: }) expect(messenger.scope_row!(row)).to eq(scoping_result) end end context "when the messenger is scoped to a col" do - let(:messenger) { described_class.new(scope: scopes::COL, scope_data: { col: col }) } + let(:messenger) { described_class.new(scope: scopes::COL, scope_data: { col: }) } it "scopes the messenger to the appropriate cell (with a block)" do scoping_result = - stub_scoping!(messenger, scopes::CELL, { col: col, row: row }, &scoping_block) + stub_scoping!(messenger, scopes::CELL, { col:, row: }, &scoping_block) expect(messenger.scope_row!(row, &scoping_block)).to eq(scoping_result) end it "scopes the messenger to the appropriate cell (without a block)" do - scoping_result = stub_scoping!(messenger, scopes::CELL, { col: col, row: row }) + scoping_result = stub_scoping!(messenger, scopes::CELL, { col:, row: }) expect(messenger.scope_row!(row)).to eq(scoping_result) end end @@ -219,25 +219,25 @@ def stub_scoping!(receiver, *args, &block) let(:other_row) { double } let(:messenger) do - described_class.new(scope: scopes::CELL, scope_data: { col: col, row: other_row }) + described_class.new(scope: scopes::CELL, scope_data: { col:, row: other_row }) end it "scopes the messenger to the new appropriate cell (with a block)" do scoping_result = - stub_scoping!(messenger, scopes::CELL, { col: col, row: row }, &scoping_block) + stub_scoping!(messenger, scopes::CELL, { col:, row: }, &scoping_block) expect(messenger.scope_row!(row, &scoping_block)).to eq(scoping_result) end it "scopes the messenger to the new appropriate cell (without a block)" do - scoping_result = stub_scoping!(messenger, scopes::CELL, { col: col, row: row }) + scoping_result = stub_scoping!(messenger, scopes::CELL, { col:, row: }) expect(messenger.scope_row!(row)).to eq(scoping_result) end end end describe "#scope_row" do - def stub_scope_row!(receiver, *args, &block) - allow_method_call_checking_block(receiver, :scope_row!, *args, &block) + def stub_scope_row!(receiver, ...) + allow_method_call_checking_block(receiver, :scope_row!, ...) end let(:messenger) { described_class.new } @@ -263,12 +263,12 @@ def stub_scope_row!(receiver, *args, &block) let(:messenger) { described_class.new } it "scopes the messenger to the given col (with a block)" do - scoping_result = stub_scoping!(messenger, scopes::COL, { col: col }, &scoping_block) + scoping_result = stub_scoping!(messenger, scopes::COL, { col: }, &scoping_block) expect(messenger.scope_col!(col, &scoping_block)).to eq(scoping_result) end it "scopes the messenger to the given col (without a block)" do - scoping_result = stub_scoping!(messenger, scopes::COL, { col: col }) + scoping_result = stub_scoping!(messenger, scopes::COL, { col: }) expect(messenger.scope_col!(col)).to eq(scoping_result) end end @@ -278,27 +278,27 @@ def stub_scope_row!(receiver, *args, &block) let(:messenger) { described_class.new(scope: scopes::COL, scope_data: { col: other_col }) } it "scopes the messenger to the given col (with a block)" do - scoping_result = stub_scoping!(messenger, scopes::COL, { col: col }, &scoping_block) + scoping_result = stub_scoping!(messenger, scopes::COL, { col: }, &scoping_block) expect(messenger.scope_col!(col, &scoping_block)).to eq(scoping_result) end it "scopes the messenger to the given col (without a block)" do - scoping_result = stub_scoping!(messenger, scopes::COL, { col: col }) + scoping_result = stub_scoping!(messenger, scopes::COL, { col: }) expect(messenger.scope_col!(col)).to eq(scoping_result) end end context "when the messenger is scoped to a row" do - let(:messenger) { described_class.new(scope: scopes::ROW, scope_data: { row: row }) } + let(:messenger) { described_class.new(scope: scopes::ROW, scope_data: { row: }) } it "scopes the messenger to the appropriate cell (with a block)" do scoping_result = - stub_scoping!(messenger, scopes::CELL, { row: row, col: col }, &scoping_block) + stub_scoping!(messenger, scopes::CELL, { row:, col: }, &scoping_block) expect(messenger.scope_col!(col, &scoping_block)).to eq(scoping_result) end it "scopes the messenger to the appropriate cell (without a block)" do - scoping_result = stub_scoping!(messenger, scopes::CELL, { row: row, col: col }) + scoping_result = stub_scoping!(messenger, scopes::CELL, { row:, col: }) expect(messenger.scope_col!(col)).to eq(scoping_result) end end @@ -307,25 +307,25 @@ def stub_scope_row!(receiver, *args, &block) let(:other_col) { double } let(:messenger) do - described_class.new(scope: scopes::CELL, scope_data: { row: row, col: other_col }) + described_class.new(scope: scopes::CELL, scope_data: { row:, col: other_col }) end it "scopes the messenger to the new appropriate cell (with a block)" do scoping_result = - stub_scoping!(messenger, scopes::CELL, { row: row, col: col }, &scoping_block) + stub_scoping!(messenger, scopes::CELL, { row:, col: }, &scoping_block) expect(messenger.scope_col!(col, &scoping_block)).to eq(scoping_result) end it "scopes the messenger to the new appropriate cell (without a block)" do - scoping_result = stub_scoping!(messenger, scopes::CELL, { row: row, col: col }) + scoping_result = stub_scoping!(messenger, scopes::CELL, { row:, col: }) expect(messenger.scope_col!(col)).to eq(scoping_result) end end end describe "#scope_col" do - def stub_scope_col!(receiver, *args, &block) - allow_method_call_checking_block(receiver, :scope_col!, *args, &block) + def stub_scope_col!(receiver, ...) + allow_method_call_checking_block(receiver, :scope_col!, ...) end let(:messenger) { described_class.new } @@ -354,7 +354,7 @@ def stub_scope_col!(receiver, *args, &block) let(:code) { double } let(:code_data) { double } - let(:messenger) { described_class.new(scope: scope, scope_data: scope_data) } + let(:messenger) { described_class.new(scope:, scope_data:) } describe "#warn" do it "returns the receiver" do @@ -366,10 +366,10 @@ def stub_scope_col!(receiver, *args, &block) expect(messenger.messages).to contain_exactly( Sheetah::Messaging::Message.new( - code: code, - code_data: code_data, - scope: scope, - scope_data: scope_data, + code:, + code_data:, + scope:, + scope_data:, severity: severities::WARN ) ) @@ -380,10 +380,10 @@ def stub_scope_col!(receiver, *args, &block) expect(messenger.messages).to contain_exactly( Sheetah::Messaging::Message.new( - code: code, + code:, code_data: nil, - scope: scope, - scope_data: scope_data, + scope:, + scope_data:, severity: severities::WARN ) ) @@ -400,10 +400,10 @@ def stub_scope_col!(receiver, *args, &block) expect(messenger.messages).to contain_exactly( Sheetah::Messaging::Message.new( - code: code, - code_data: code_data, - scope: scope, - scope_data: scope_data, + code:, + code_data:, + scope:, + scope_data:, severity: severities::ERROR ) ) @@ -414,10 +414,10 @@ def stub_scope_col!(receiver, *args, &block) expect(messenger.messages).to contain_exactly( Sheetah::Messaging::Message.new( - code: code, + code:, code_data: nil, - scope: scope, - scope_data: scope_data, + scope:, + scope_data:, severity: severities::ERROR ) ) @@ -440,10 +440,10 @@ def stub_scope_col!(receiver, *args, &block) expect(messenger.messages).to contain_exactly( Sheetah::Messaging::Message.new( - code: code, + code:, code_data: nil, - scope: scope, - scope_data: scope_data, + scope:, + scope_data:, severity: severities::ERROR ) ) diff --git a/spec/sheetah/row_processor_result_spec.rb b/spec/sheetah/row_processor_result_spec.rb index a75d09e..b3c23db 100644 --- a/spec/sheetah/row_processor_result_spec.rb +++ b/spec/sheetah/row_processor_result_spec.rb @@ -8,24 +8,24 @@ let(:messages) { double } it "wraps a result with messages" do - processor_result = described_class.new(row: row, result: result, messages: messages) - expect(processor_result).to have_attributes(row: row, result: result, messages: messages) + processor_result = described_class.new(row:, result:, messages:) + expect(processor_result).to have_attributes(row:, result:, messages:) end it "is equivalent to a similar result with similar messages" do - processor_result0 = described_class.new(row: row, result: result, messages: messages) - processor_result1 = described_class.new(row: row, result: result, messages: messages) + processor_result0 = described_class.new(row:, result:, messages:) + processor_result1 = described_class.new(row:, result:, messages:) expect(processor_result0).to eq(processor_result1) end it "is different from a similar result with a different row" do - processor_result0 = described_class.new(row: row, result: result, messages: messages) - processor_result1 = described_class.new(row: double, result: result, messages: messages) + processor_result0 = described_class.new(row:, result:, messages:) + processor_result1 = described_class.new(row: double, result:, messages:) expect(processor_result0).not_to eq(processor_result1) end it "may wrap a result with implicit messages" do - processor_result = described_class.new(row: row, result: result) - expect(processor_result).to have_attributes(row: row, result: result, messages: []) + processor_result = described_class.new(row:, result:) + expect(processor_result).to have_attributes(row:, result:, messages: []) end end diff --git a/spec/sheetah/row_processor_spec.rb b/spec/sheetah/row_processor_spec.rb index 40b9b17..d0bb8b7 100644 --- a/spec/sheetah/row_processor_spec.rb +++ b/spec/sheetah/row_processor_spec.rb @@ -43,7 +43,7 @@ end let(:processor) do - described_class.new(headers: headers, messenger: messenger) + described_class.new(headers:, messenger:) end def expect_headers_add(header, cell) diff --git a/spec/sheetah/sheet_processor_result_spec.rb b/spec/sheetah/sheet_processor_result_spec.rb index 10f1d48..31f3cc2 100644 --- a/spec/sheetah/sheet_processor_result_spec.rb +++ b/spec/sheetah/sheet_processor_result_spec.rb @@ -7,18 +7,18 @@ let(:messages) { double } it "wraps a result with messages" do - processor_result = described_class.new(result: result, messages: messages) - expect(processor_result).to have_attributes(result: result, messages: messages) + processor_result = described_class.new(result:, messages:) + expect(processor_result).to have_attributes(result:, messages:) end it "is equivalent to a similar result with similar messages" do - processor_result0 = described_class.new(result: result, messages: messages) - processor_result1 = described_class.new(result: result, messages: messages) + processor_result0 = described_class.new(result:, messages:) + processor_result1 = described_class.new(result:, messages:) expect(processor_result0).to eq(processor_result1) end it "may wrap a result with implicit messages" do - processor_result = described_class.new(result: result) - expect(processor_result).to have_attributes(result: result, messages: []) + processor_result = described_class.new(result:) + expect(processor_result).to have_attributes(result:, messages: []) end end diff --git a/spec/sheetah/sheet_processor_spec.rb b/spec/sheetah/sheet_processor_spec.rb index ee6e56d..e8f1fa2 100644 --- a/spec/sheetah/sheet_processor_spec.rb +++ b/spec/sheetah/sheet_processor_spec.rb @@ -169,7 +169,7 @@ def stub_enumeration(obj, method_name, enumerable) def stub_headers allow(Sheetah::Headers).to( receive(:new) - .with(specification: specification, messenger: messenger) + .with(specification:, messenger:) .and_return(headers) ) end @@ -226,7 +226,7 @@ def stub_headers_ops(result) def stub_row_processing allow(Sheetah::RowProcessor).to( receive(:new) - .with(headers: headers_spec, messenger: messenger) + .with(headers: headers_spec, messenger:) .and_return(row_processor = instance_double(Sheetah::RowProcessor)) ) diff --git a/spec/sheetah/sheet_spec.rb b/spec/sheetah/sheet_spec.rb index 5d9e5dd..67f0627 100644 --- a/spec/sheetah/sheet_spec.rb +++ b/spec/sheetah/sheet_spec.rb @@ -19,7 +19,7 @@ def initialize(foo, bar:) end let(:sheet) do - sheet_class.new(foo, bar: bar) + sheet_class.new(foo, bar:) end let(:foo) { double } @@ -37,14 +37,14 @@ def initialize(foo, bar:) let(:col) { double } let(:val) { double } - let(:wrapper) { sheet_class::Header.new(col: col, value: val) } + let(:wrapper) { sheet_class::Header.new(col:, value: val) } it "exposes a header wrapper" do - expect(wrapper).to have_attributes(col: col, value: val) + expect(wrapper).to have_attributes(col:, value: val) end it "is comparable" do - expect(wrapper).to eq(sheet_class::Header.new(col: col, value: val)) + expect(wrapper).to eq(sheet_class::Header.new(col:, value: val)) expect(wrapper).not_to eq(sheet_class::Header.new(col: double, value: val)) end end @@ -53,14 +53,14 @@ def initialize(foo, bar:) let(:row) { double } let(:val) { double } - let(:wrapper) { sheet_class::Row.new(row: row, value: val) } + let(:wrapper) { sheet_class::Row.new(row:, value: val) } it "exposes a row wrapper" do - expect(wrapper).to have_attributes(row: row, value: val) + expect(wrapper).to have_attributes(row:, value: val) end it "is comparable" do - expect(wrapper).to eq(sheet_class::Row.new(row: row, value: val)) + expect(wrapper).to eq(sheet_class::Row.new(row:, value: val)) expect(wrapper).not_to eq(sheet_class::Row.new(row: double, value: val)) end end @@ -70,15 +70,15 @@ def initialize(foo, bar:) let(:col) { double } let(:val) { double } - let(:wrapper) { sheet_class::Cell.new(row: row, col: col, value: val) } + let(:wrapper) { sheet_class::Cell.new(row:, col:, value: val) } it "exposes a row wrapper" do - expect(wrapper).to have_attributes(row: row, col: col, value: val) + expect(wrapper).to have_attributes(row:, col:, value: val) end it "is comparable" do - expect(wrapper).to eq(sheet_class::Cell.new(row: row, col: col, value: val)) - expect(wrapper).not_to eq(sheet_class::Cell.new(row: double, col: col, value: val)) + expect(wrapper).to eq(sheet_class::Cell.new(row:, col:, value: val)) + expect(wrapper).not_to eq(sheet_class::Cell.new(row: double, col:, value: val)) end end @@ -139,12 +139,12 @@ def initialize(foo, bar:) end before do - allow(sheet_class).to receive(:new).with(foo, bar: bar).and_return(sheet) + allow(sheet_class).to receive(:new).with(foo, bar:).and_return(sheet) end context "without a block" do it "returns a new sheet wrapped as a Success" do - expect(sheet_class.open(foo, bar: bar)).to eq(Success(sheet)) + expect(sheet_class.open(foo, bar:)).to eq(Success(sheet)) end end @@ -156,7 +156,7 @@ def initialize(foo, bar:) it "yields a new sheet" do yielded = false - sheet_class.open(foo, bar: bar) do |opened_sheet| + sheet_class.open(foo, bar:) do |opened_sheet| yielded = true expect(opened_sheet).to be(sheet) end @@ -166,13 +166,13 @@ def initialize(foo, bar:) it "returns the value of the block, wrapped as a success" do block_result = double - actual_block_result = sheet_class.open(foo, bar: bar) { block_result } + actual_block_result = sheet_class.open(foo, bar:) { block_result } expect(actual_block_result).to eq(Success(block_result)) end it "closes after yielding" do - sheet_class.open(foo, bar: bar) do + sheet_class.open(foo, bar:) do expect(sheet).not_to have_received(:close) end @@ -189,7 +189,7 @@ def initialize(foo, bar:) allow(sheet_class).to receive(:new).and_raise(exception) expect do - sheet_class.open(foo, bar: bar) + sheet_class.open(foo, bar:) end.to raise_error(exception) end @@ -197,14 +197,14 @@ def initialize(foo, bar:) allow(sheet_class).to receive(:new).and_raise(error) expect do - sheet_class.open(foo, bar: bar) + sheet_class.open(foo, bar:) end.to raise_error(error) end it "rescues and wraps a sheet error in a failure" do allow(sheet_class).to receive(:new).and_raise(e = sheet_error.exception) - result = sheet_class.open(foo, bar: bar) + result = sheet_class.open(foo, bar:) expect(result).to eq(Failure(e)) end @@ -213,7 +213,7 @@ def initialize(foo, bar:) context "while yielding control" do it "doesn't rescue but closes after an exception is raised" do expect do - sheet_class.open(foo, bar: bar) do + sheet_class.open(foo, bar:) do expect(sheet).not_to have_received(:close) raise exception end @@ -224,7 +224,7 @@ def initialize(foo, bar:) it "doesn't rescue but closes after a standard error is raised" do expect do - sheet_class.open(foo, bar: bar) do + sheet_class.open(foo, bar:) do expect(sheet).not_to have_received(:close) raise error end @@ -234,7 +234,7 @@ def initialize(foo, bar:) end it "rescues and closes after a sheet error is raised" do - sheet_class.open(foo, bar: bar) do + sheet_class.open(foo, bar:) do expect(sheet).not_to have_received(:close) raise sheet_error end @@ -245,7 +245,7 @@ def initialize(foo, bar:) it "returns the exception, wrapped as a failure, after a sheet error is raised" do e = sheet_error.exception # raise the instance directly to simplify result matching - result = sheet_class.open(foo, bar: bar) do + result = sheet_class.open(foo, bar:) do raise e end diff --git a/spec/sheetah/specification_spec.rb b/spec/sheetah/specification_spec.rb index b16a132..af61937 100644 --- a/spec/sheetah/specification_spec.rb +++ b/spec/sheetah/specification_spec.rb @@ -69,7 +69,7 @@ describe "#get" do let(:regexp_pattern) do - /foo\d{3}bar/i.freeze + /foo\d{3}bar/i end let(:string_pattern) do diff --git a/spec/sheetah/types/scalars/boolsy_cast_spec.rb b/spec/sheetah/types/scalars/boolsy_cast_spec.rb index 60b988b..8713e3c 100644 --- a/spec/sheetah/types/scalars/boolsy_cast_spec.rb +++ b/spec/sheetah/types/scalars/boolsy_cast_spec.rb @@ -16,7 +16,7 @@ end describe "#call" do - subject(:cast) { described_class.new(truthy: truthy, falsy: falsy) } + subject(:cast) { described_class.new(truthy:, falsy:) } let(:value) { instance_double(Object, inspect: double) } let(:messenger) { instance_double(Sheetah::Messaging::Messenger) } diff --git a/spec/sheetah/types/scalars/date_string_cast_spec.rb b/spec/sheetah/types/scalars/date_string_cast_spec.rb index f46f8e7..5681a1a 100644 --- a/spec/sheetah/types/scalars/date_string_cast_spec.rb +++ b/spec/sheetah/types/scalars/date_string_cast_spec.rb @@ -25,7 +25,7 @@ context "when value is a Date" do subject(:cast) do - described_class.new(accept_date: accept_date) + described_class.new(accept_date:) end before do @@ -52,7 +52,7 @@ context "when value is a string" do subject(:cast) do - described_class.new(date_fmt: date_fmt) + described_class.new(date_fmt:) end let(:date_fmt) { "%d/%m/%Y" } diff --git a/spec/sheetah/types/scalars/email_cast_spec.rb b/spec/sheetah/types/scalars/email_cast_spec.rb index f6cb344..3a388f5 100644 --- a/spec/sheetah/types/scalars/email_cast_spec.rb +++ b/spec/sheetah/types/scalars/email_cast_spec.rb @@ -16,7 +16,7 @@ end describe "#call" do - subject(:cast) { described_class.new(email_matcher: email_matcher) } + subject(:cast) { described_class.new(email_matcher:) } let(:email_matcher) do instance_double(Regexp) diff --git a/spec/sheetah/types/type_spec.rb b/spec/sheetah/types/type_spec.rb index aed97a3..3511917 100644 --- a/spec/sheetah/types/type_spec.rb +++ b/spec/sheetah/types/type_spec.rb @@ -188,7 +188,7 @@ def stub_new_casts(opts) expect(type.cast_chain).to( be_a(Sheetah::Types::CastChain) & - have_attributes(casts: casts) + have_attributes(casts:) ) end end diff --git a/spec/sheetah_spec.rb b/spec/sheetah_spec.rb index e3afae3..d17cd34 100644 --- a/spec/sheetah_spec.rb +++ b/spec/sheetah_spec.rb @@ -42,7 +42,7 @@ end let(:template_config) do - Sheetah::TemplateConfig.new(types: types) + Sheetah::TemplateConfig.new(types:) end let(:specification) do @@ -62,13 +62,13 @@ ] end - def process(*args, **opts, &block) - processor.call(*args, backend: Sheetah::Backends::Wrapper, **opts, &block) + def process(*, **, &) + processor.call(*, backend: Sheetah::Backends::Wrapper, **, &) end - def process_to_a(*args, **opts) + def process_to_a(*, **) a = [] - processor.call(*args, backend: Sheetah::Backends::Wrapper, **opts) { |result| a << result } + processor.call(*, backend: Sheetah::Backends::Wrapper, **) { |result| a << result } a end @@ -137,6 +137,32 @@ def process_to_a(*args, **opts) Success(foo: "dlrow", bar: [nil, nil, "foo@bar.baz", nil, Float]) ) end + + context "when the reporting is enabled" do + before { template_opts[:report_ignored_columns] = true } + + it "messages the ignored columns" do # rubocop:disable RSpec/ExampleLength + expect(process(input) {}).to have_attributes( + result: Success(), + messages: contain_exactly( + have_attributes( + code: "ignored_column", + code_data: "oof", + scope: Sheetah::Messaging::SCOPES::COL, + scope_data: { col: "C" }, + severity: Sheetah::Messaging::SEVERITIES::WARN + ), + have_attributes( + code: "ignored_column", + code_data: "rab", + scope: Sheetah::Messaging::SCOPES::COL, + scope_data: { col: "F" }, + severity: Sheetah::Messaging::SEVERITIES::WARN + ) + ) + ) + end + end end context "when the template doesn't allow it" do diff --git a/spec/support/shared/sheet_factories.rb b/spec/support/shared/sheet_factories.rb index 645c5ed..b19c973 100644 --- a/spec/support/shared/sheet_factories.rb +++ b/spec/support/shared/sheet_factories.rb @@ -19,7 +19,7 @@ def cells(values, row:, col: "A") int = Sheetah::Sheet.col2int(col) values.map.with_index(int) do |value, index| - cell(row: row, col: Sheetah::Sheet.int2col(index), value: value) + cell(row:, col: Sheetah::Sheet.int2col(index), value:) end end end