diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 36c20bf..b5ce036 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -22,7 +22,7 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - ruby-version: ['3.0', '3.1', '3.2', '3.3', 'head'] + ruby-version: ['3.1', '3.2', '3.3', 'head'] steps: - uses: actions/checkout@v3 diff --git a/.rubocop.yml b/.rubocop.yml index 6ca5439..559f6da 100644 --- a/.rubocop.yml +++ b/.rubocop.yml @@ -2,6 +2,9 @@ require: - rubocop-rspec - rubocop-performance +AllCops: + SuggestExtensions: true + Layout/CaseIndentation: Enabled: false @@ -47,15 +50,6 @@ Layout/SpaceInLambdaLiteral: Layout/TrailingWhitespace: Enabled: false -Lint/AmbiguousBlockAssociation: - Enabled: false - -Lint/AssignmentInCondition: - Enabled: false - -Lint/RedundantSplatExpansion: - Enabled: false - Metrics/AbcSize: Enabled: false @@ -81,10 +75,7 @@ Metrics/ModuleLength: Metrics/PerceivedComplexity: Max: 10 -Naming/FileName: - Enabled: false - -Naming/MethodParameterName: +Naming/PredicateName: Enabled: false RSpec/AnyInstance: @@ -114,6 +105,10 @@ RSpec/LetSetup: RSpec/MessageSpies: Enabled: false +RSpec/MultipleDescribes: + Exclude: + - spec/dbf/file_formats_spec.rb + RSpec/MultipleExpectations: Enabled: false @@ -123,19 +118,6 @@ RSpec/NestedGroups: RSpec/NotToNot: EnforcedStyle: to_not -Security/YAMLLoad: - Exclude: - - app/models/entity.rb - -Style/Alias: - Enabled: false - -Style/AsciiComments: - Enabled: false - -Style/ClassAndModuleChildren: - Enabled: false - Style/Documentation: Enabled: false @@ -148,20 +130,8 @@ Style/FormatStringToken: Style/FrozenStringLiteralComment: Enabled: false -Style/Lambda: - Enabled: false - Style/NumericPredicate: Enabled: false -Style/PerlBackrefs: - Enabled: false - -Style/RescueModifier: - Enabled: false - -Style/SafeNavigation: - Enabled: false - Style/SymbolArray: EnforcedStyle: brackets diff --git a/README.md b/README.md index a5b72a0..716c94a 100644 --- a/README.md +++ b/README.md @@ -16,19 +16,19 @@ DBF is a small, fast Ruby library for reading dBase, xBase, Clipper, and FoxPro subject line * Change log: -NOTE: +NOTE: Beginning with version 4.3 we have dropped support for Ruby 3.0 and earlier. -NOTE: Beginning with version 4, we have dropped support for Ruby 2.0, 2.1, 2.2, and 2.3. If you need support for these older Rubies, +NOTE: Beginning with version 4 we have dropped support for Ruby 2.0, 2.1, 2.2, and 2.3. If you need support for these older Rubies, please use 3.0.x () -NOTE: Beginning with version 3, we have dropped support for Ruby 1.8 and 1.9. If you need support for older Rubies, +NOTE: Beginning with version 3 we have dropped support for Ruby 1.8 and 1.9. If you need support for older Rubies, please use 2.0.x () ## Compatibility DBF is tested to work with the following versions of Ruby: -* Ruby 3.0.x, 3.1.x, 3.2.x, 3.3.x +* Ruby 3.1.x, 3.2.x, 3.3.x ## Installation diff --git a/lib/dbf/column.rb b/lib/dbf/column.rb index 80dbc6e..a18b447 100644 --- a/lib/dbf/column.rb +++ b/lib/dbf/column.rb @@ -9,8 +9,10 @@ class NameError < StandardError end attr_reader :table, :name, :type, :length, :decimal + def_delegator :type_cast_class, :type_cast + # rubocop:disable Style/MutableConstant TYPE_CAST_CLASS = { N: ColumnType::Number, I: ColumnType::SignedLong, @@ -24,6 +26,7 @@ class NameError < StandardError G: ColumnType::General, '+'.to_sym => ColumnType::SignedLong2 } + # rubocop:enable Style/MutableConstant TYPE_CAST_CLASS.default = ColumnType::String TYPE_CAST_CLASS.freeze @@ -58,7 +61,7 @@ def memo? # # @return [Hash] def to_hash - {name: name, type: type, length: length, decimal: decimal} + {name:, type:, length:, decimal:} end # Underscored name @@ -68,18 +71,17 @@ def to_hash # # @return [String] def underscored_name - @underscored_name ||= begin - name.gsub(/([a-z\d])([A-Z])/, '\1_\2').tr('-', '_').downcase - end + @underscored_name ||= name.gsub(/([a-z\d])([A-Z])/, '\1_\2').tr('-', '_').downcase + end private def clean(value) # :nodoc: - value.strip.gsub("\x00", '').gsub(/[^\x20-\x7E]/, '') + value.strip.delete("\x00").gsub(/[^\x20-\x7E]/, '') end - def encode(value, strip_output = false) # :nodoc: + def encode(value, strip_output: false) # :nodoc: return value unless value.respond_to?(:encoding) output = @encoding ? encode_string(value) : value diff --git a/lib/dbf/database/foxpro.rb b/lib/dbf/database/foxpro.rb index fcc65c3..b2e1476 100644 --- a/lib/dbf/database/foxpro.rb +++ b/lib/dbf/database/foxpro.rb @@ -105,7 +105,7 @@ def process_field(record, data) end def table_field_hash(name) - {name: name, fields: []} + {name:, fields: []} end end diff --git a/lib/dbf/header.rb b/lib/dbf/header.rb index 968debb..1b55d5f 100644 --- a/lib/dbf/header.rb +++ b/lib/dbf/header.rb @@ -1,11 +1,6 @@ module DBF class Header - attr_reader :version - attr_reader :record_count - attr_reader :header_length - attr_reader :record_length - attr_reader :encoding_key - attr_reader :encoding + attr_reader :version, :record_count, :header_length, :record_length, :encoding_key, :encoding def initialize(data) @data = data @@ -13,7 +8,7 @@ def initialize(data) end def unpack_header - @version = @data.unpack('H2').first + @version = @data.unpack1('H2') case @version when '02' diff --git a/lib/dbf/record.rb b/lib/dbf/record.rb index b7430b1..936b07b 100644 --- a/lib/dbf/record.rb +++ b/lib/dbf/record.rb @@ -29,7 +29,7 @@ def [](name) key = name.to_s if attributes.key?(key) attributes[key] - elsif index = underscored_column_names.index(key) + elsif (index = underscored_column_names.index(key)) attributes[@columns[index].name] end end @@ -59,7 +59,7 @@ def to_a private def column_names # :nodoc: - @column_names ||= @columns.map { |column| column.name } + @column_names ||= @columns.map(&:name) end def get_data(column) # :nodoc: diff --git a/lib/dbf/schema.rb b/lib/dbf/schema.rb index 7f67462..077a2de 100644 --- a/lib/dbf/schema.rb +++ b/lib/dbf/schema.rb @@ -35,9 +35,9 @@ module Schema # @param format [Symbol] format Valid options are :activerecord and :json # @param table_only [Boolean] # @return [String] - def schema(format = :activerecord, table_only = false) + def schema(format = :activerecord, table_only: false) schema_method_name = schema_name(format) - send(schema_method_name, table_only) + send(schema_method_name, table_only:) rescue NameError raise ArgumentError, ":#{format} is not a valid schema. Valid schemas are: #{FORMATS.join(', ')}." end @@ -46,7 +46,7 @@ def schema_name(format) # :nodoc: "#{format}_schema" end - def activerecord_schema(_table_only = false) # :nodoc: + def activerecord_schema(*) # :nodoc: s = "ActiveRecord::Schema.define do\n" s << " create_table \"#{name}\" do |t|\n" columns.each do |column| @@ -56,7 +56,7 @@ def activerecord_schema(_table_only = false) # :nodoc: s end - def sequel_schema(table_only = false) # :nodoc: + def sequel_schema(table_only: false) # :nodoc: s = '' s << "Sequel.migration do\n" unless table_only s << " change do\n " unless table_only @@ -70,7 +70,7 @@ def sequel_schema(table_only = false) # :nodoc: s end - def json_schema(_table_only = false) # :nodoc: + def json_schema(*) # :nodoc: columns.map(&:to_hash).to_json end @@ -92,9 +92,9 @@ def sequel_schema_definition(column) def schema_data_type(column, format = :activerecord) # :nodoc: case column.type - when *%w[N F I] + when 'N', 'F', 'I' number_data_type(column) - when *%w[Y D T L M B] + when 'Y', 'D', 'T', 'L', 'M', 'B' OTHER_DATA_TYPES[column.type] else string_data_format(format, column) diff --git a/lib/dbf/table.rb b/lib/dbf/table.rb index bafe693..7f7f0ed 100644 --- a/lib/dbf/table.rb +++ b/lib/dbf/table.rb @@ -86,7 +86,7 @@ def initialize(data, memo = nil, encoding = nil) # @return [TrueClass, FalseClass] def close @data.close - @memo && @memo.close + @memo&.close end # @return [TrueClass, FalseClass] @@ -255,7 +255,7 @@ def end_of_record? # :nodoc: def find_all(options) # :nodoc: select do |record| - next unless record && record.match?(options) + next unless record&.match?(options) yield record if block_given? record @@ -263,7 +263,7 @@ def find_all(options) # :nodoc: end def find_first(options) # :nodoc: - detect { |record| record && record.match?(options) } + detect { |record| record&.match?(options) } end def foxpro? # :nodoc: @@ -278,13 +278,12 @@ def header # :nodoc: end def memo_class # :nodoc: - @memo_class ||= begin - if foxpro? - Memo::Foxpro + @memo_class ||= if foxpro? + Memo::Foxpro else version == '83' ? Memo::Dbase3 : Memo::Dbase4 - end end + end def memo_search_path(io) # :nodoc: diff --git a/spec/dbf/record_spec.rb b/spec/dbf/record_spec.rb index 382c198..3b3648d 100644 --- a/spec/dbf/record_spec.rb +++ b/spec/dbf/record_spec.rb @@ -3,25 +3,25 @@ RSpec.describe DBF::Record do describe '#to_a' do let(:table) { DBF::Table.new fixture('dbase_83.dbf') } - let(:record_0) { YAML.load_file(fixture('dbase_83_record_0.yml')) } - let(:record_9) { YAML.load_file(fixture('dbase_83_record_9.yml')) } + let(:record0) { YAML.load_file(fixture('dbase_83_record_0.yml')) } + let(:record9) { YAML.load_file(fixture('dbase_83_record_9.yml')) } it 'returns an ordered array of attribute values' do record = table.record(0) - expect(record.to_a).to eq record_0 + expect(record.to_a).to eq record0 record = table.record(9) - expect(record.to_a).to eq record_9 + expect(record.to_a).to eq record9 end describe 'with missing memo file' do describe 'when opening a path' do let(:table) { DBF::Table.new fixture('dbase_83_missing_memo.dbf') } - let(:record_0) { YAML.load_file(fixture('dbase_83_missing_memo_record_0.yml')) } + let(:record0) { YAML.load_file(fixture('dbase_83_missing_memo_record_0.yml')) } it 'returns nil values for memo fields' do record = table.record(0) - expect(record.to_a).to eq record_0 + expect(record.to_a).to eq record0 end end end @@ -29,11 +29,11 @@ describe 'when opening StringIO' do let(:data) { StringIO.new(File.read(fixture('dbase_83_missing_memo.dbf'))) } let(:table) { DBF::Table.new(data) } - let(:record_0) { YAML.load_file(fixture('dbase_83_missing_memo_record_0.yml')) } + let(:record0) { YAML.load_file(fixture('dbase_83_missing_memo_record_0.yml')) } it 'returns nil values for memo fields' do record = table.record(0) - expect(record.to_a).to eq record_0 + expect(record.to_a).to eq record0 end end end @@ -50,7 +50,7 @@ describe 'if other attributes match' do let(:attributes) { {x: 1, y: 2} } - let(:other) { instance_double('DBF::Record', attributes: attributes) } + let(:other) { instance_double('DBF::Record', attributes:) } before do allow(record).to receive(:attributes).and_return(attributes) diff --git a/spec/dbf/table_spec.rb b/spec/dbf/table_spec.rb index 7d93c19..1c65dd2 100644 --- a/spec/dbf/table_spec.rb +++ b/spec/dbf/table_spec.rb @@ -70,7 +70,6 @@ describe 'when data is StringIO' do let(:data) { StringIO.new File.read(dbf_path) } - let(:memo) { StringIO.new File.read(memo_path) } let(:table) { DBF::Table.new data } let(:control_schema) { File.read(fixture('dbase_83_schema_ar.txt')) } @@ -200,7 +199,7 @@ end it 'returns matching records when used with options' do - expect(table.find(:all, 'WEIGHT' => 0.0)).to eq table.select { |r| r['weight'] == 0.0 } + expect(table.find(:all, 'WEIGHT' => 0.0)).to eq(table.select { |r| r['weight'] == 0.0 }) end it 'ANDS multiple search terms' do @@ -295,7 +294,7 @@ it 'is an array of Columns' do expect(columns).to be_an(Array) expect(columns).to_not be_empty - expect(columns).to be_all { |c| c.is_a? DBF::Column } + expect(columns).to(be_all { |c| c.is_a? DBF::Column }) end end diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index 1260727..9cabf2d 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -2,6 +2,7 @@ require 'simplecov' SimpleCov.start rescue LoadError + # ignore end require 'dbf'