diff --git a/.github/workflows/rake.yml b/.github/workflows/rake.yml index 72bf1748..9ff1e854 100644 --- a/.github/workflows/rake.yml +++ b/.github/workflows/rake.yml @@ -10,11 +10,12 @@ jobs: test: runs-on: ubuntu-latest strategy: + fail-fast: false matrix: - ruby-version: ['2.2', '2.3', '2.4', '2.5', '2.6', '2.7', '3.0', '3.1'] + ruby-version: ['2.2', '2.3', '2.4', '2.5', '2.6', '2.7', '3.0', '3.1', '3.2'] steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 - name: Set up Ruby uses: ruby/setup-ruby@v1 with: @@ -30,7 +31,7 @@ jobs: ruby-version: ['3.0'] steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 - name: Set up Ruby uses: ruby/setup-ruby@v1 with: diff --git a/.rubocop.yml b/.rubocop.yml index 795474df..99aa4d8e 100644 --- a/.rubocop.yml +++ b/.rubocop.yml @@ -1,34 +1,25 @@ +require: + - rubocop-performance + AllCops: TargetRubyVersion: 2.2 + NewCops: enable -Layout/AlignParameters: - EnforcedStyle: with_fixed_indentation +Gemspec/DevelopmentDependencies: + Enabled: false -Layout/EmptyLineBetweenDefs: - Enabled: true +Layout/LineLength: + Max: 80 Layout/MultilineMethodCallIndentation: EnforcedStyle: indented -Layout/SpaceAfterComma: - Enabled: true - -Layout/SpaceAroundEqualsInParameterDefault: - Enabled: true - -Layout/SpaceAroundOperators: - Enabled: true - -Layout/SpaceBeforeSemicolon: - Enabled: true - - -Lint/DuplicateMethods: - Enabled: true - -Lint/UnusedMethodArgument: - Enabled: true +Layout/ParameterAlignment: + EnforcedStyle: with_fixed_indentation +Lint/MissingSuper: + Exclude: + - lib/timezone/lookup/test.rb Metrics/AbcSize: Enabled: false @@ -36,12 +27,6 @@ Metrics/AbcSize: Metrics/ClassLength: Enabled: false -# Offense count: 13 -# Configuration parameters: AllowHeredoc, AllowURI, URISchemes. -# URISchemes: http, https -Metrics/LineLength: - Max: 80 - Metrics/MethodLength: Enabled: false @@ -49,47 +34,16 @@ Metrics/MethodLength: Metrics/PerceivedComplexity: Max: 8 - -Performance/RedundantBlockCall: - Enabled: true - - -Naming/MethodName: - Enabled: true - -Naming/BinaryOperatorParameterName: - Enabled: true - - -Style/ClassVars: - Enabled: true - -Style/Documentation: - Enabled: true - -Style/DoubleNegation: - Enabled: true - -Style/EmptyLiteral: - Enabled: true - -Style/GuardClause: - Enabled: true - -Style/HashSyntax: - Enabled: true - Style/NumericLiterals: MinDigits: 5 -Style/PreferredHashMethods: - Enabled: true - -Style/RedundantReturn: - Enabled: true +Style/OpenStructUse: + Enabled: false -Style/RedundantSelf: - Enabled: true +Style/RedundantInitialize: + Exclude: + - test/http_test_client.rb + - test/timezone/test_lookup.rb Style/SingleLineMethods: Enabled: false @@ -97,14 +51,5 @@ Style/SingleLineMethods: Style/SpecialGlobalVars: EnforcedStyle: use_perl_names -Style/StringLiterals: - Enabled: true - -Style/TrivialAccessors: - Enabled: true - -Style/UnneededPercentQ: - Enabled: true - Style/DateTime: Enabled: false diff --git a/Rakefile b/Rakefile index 8fc44527..b05476d3 100644 --- a/Rakefile +++ b/Rakefile @@ -19,7 +19,7 @@ task(:utc) { ENV['TZ'] = 'UTC' } task default: %i[utc test rubocop] task parse: :utc do - path = ENV['TZPATH'] || File.join(ENV['HOME'], 'Downloads', 'tz') + path = ENV['TZPATH'] || File.join(Dir.home, 'Downloads', 'tz') require 'timezone/parser' diff --git a/lib/timezone.rb b/lib/timezone.rb index ed96cac4..a80bf587 100644 --- a/lib/timezone.rb +++ b/lib/timezone.rb @@ -39,14 +39,14 @@ def self.[](name) # # @raise [Timezone::Error::InvalidZone] if the timezone is not found # and a default value and block have not been provided - def self.fetch(name, default = :__block, &block) + def self.fetch(name, default = :__block) return ::Timezone::Zone.new(name) if Loader.valid?(name) if block_given? && default != :__block warn('warning: block supersedes default value argument') end - return block.call(name) if block_given? + return yield(name) if block_given? return default unless default == :__block raise ::Timezone::Error::InvalidZone diff --git a/lib/timezone/loader.rb b/lib/timezone/loader.rb index 9bda5ea9..fb69c703 100644 --- a/lib/timezone/loader.rb +++ b/lib/timezone/loader.rb @@ -2,10 +2,10 @@ require 'timezone/error' -module Timezone # rubocop:disable Style/Documentation +module Timezone # Responsible for loading and parsing timezone data from files. module Loader - ZONE_FILE_PATH = File.expand_path(File.dirname(__FILE__) + '/../../data') + ZONE_FILE_PATH = File.expand_path("#{File.dirname(__FILE__)}/../../data") SOURCE_BIT = 0 @rules = {} # cache of loaded rules diff --git a/lib/timezone/lookup.rb b/lib/timezone/lookup.rb index 29886446..07087ad6 100644 --- a/lib/timezone/lookup.rb +++ b/lib/timezone/lookup.rb @@ -9,7 +9,7 @@ module Timezone # Configure timezone lookups. module Lookup class << self - MISSING_LOOKUP = 'No lookup configured'.freeze + MISSING_LOOKUP = 'No lookup configured' private_constant :MISSING_LOOKUP # Returns the lookup object @@ -45,7 +45,7 @@ class OptionSetter test: ::Timezone::Lookup::Test }.freeze - INVALID_LOOKUP = 'Invalid lookup specified'.freeze + INVALID_LOOKUP = 'Invalid lookup specified' attr_reader :config diff --git a/lib/timezone/lookup/geonames.rb b/lib/timezone/lookup/geonames.rb index ccd6ae0e..48f7a117 100644 --- a/lib/timezone/lookup/geonames.rb +++ b/lib/timezone/lookup/geonames.rb @@ -38,7 +38,7 @@ def lookup(lat, long) return unless data['status'] - return if NO_TIMEZONE_INFORMATION == data['status']['value'] + return if data['status']['value'] == NO_TIMEZONE_INFORMATION raise(Timezone::Error::GeoNames, data['status']['message']) rescue StandardError => e @@ -55,6 +55,7 @@ def get_timezone_id(data) return unless data['gmtOffset'].is_a? Numeric return 'Etc/UTC' if data['gmtOffset'].zero? + "Etc/GMT#{format('%+d', -data['gmtOffset'])}" end diff --git a/lib/timezone/lookup/google.rb b/lib/timezone/lookup/google.rb index 56466399..f596a532 100644 --- a/lib/timezone/lookup/google.rb +++ b/lib/timezone/lookup/google.rb @@ -14,7 +14,7 @@ module Lookup class Google < ::Timezone::Lookup::Basic # Indicates that no time zone data could be found for the specified # . This can occur if the query is incomplete or ambiguous. - NO_TIMEZONE_INFORMATION = 'ZERO_RESULTS'.freeze + NO_TIMEZONE_INFORMATION = 'ZERO_RESULTS' def initialize(config) if config.api_key.nil? @@ -35,6 +35,7 @@ def lookup(lat, long) end return unless response.code =~ /^2\d\d$/ + data = JSON.parse(response.body) return if data['status'] == NO_TIMEZONE_INFORMATION diff --git a/lib/timezone/parser.rb b/lib/timezone/parser.rb index 961823ac..f5015309 100644 --- a/lib/timezone/parser.rb +++ b/lib/timezone/parser.rb @@ -9,7 +9,7 @@ class Parser MIN_YEAR = -500 MAX_YEAR = 2039 - LINE = /\s*(.+)\s*=\s*(.+)\s*isdst=(\d+)\s*gmtoff=([\+\-]*\d+)/ + LINE = /\s*(.+)\s*=\s*(.+)\s*isdst=(\d+)\s*gmtoff=([+-]*\d+)/.freeze # Bookkeeping files that we do not want to parse. IGNORE = ['leapseconds', 'posixrules', 'tzdata.zi'].freeze @@ -25,6 +25,7 @@ def perform next if File.directory?(file) next if file.end_with?('.tab') next if IGNORE.include?(File.basename(file)) + parse(file) end end @@ -52,7 +53,7 @@ def initialize(config, file) .reject { |line| line.start_with?('TZ=') } .first - _date, _time, raw_offset, @name = first.split(' ') + _date, _time, raw_offset, @name = first.split @offset = parse_offset(raw_offset) end @@ -65,7 +66,7 @@ def to_s def parse_offset(offset) arity = offset.start_with?('-') ? -1 : 1 - match = offset.match(/^[\-\+](\d{2})$/) + match = offset.match(/^[-+](\d{2})$/) arity * match[1].to_i * 60 * 60 end end @@ -76,11 +77,11 @@ def parse_offset(offset) class Line attr_accessor :source, :name, :dst, :offset - SOURCE_FORMAT = '%a %b %e %H:%M:%S %Y %Z'.freeze + SOURCE_FORMAT = '%a %b %e %H:%M:%S %Y %Z' def initialize(match) - self.source = Time.strptime(match[1] + 'C', SOURCE_FORMAT).to_i - self.name = match[2].split(' ').last + self.source = Time.strptime("#{match[1]}C", SOURCE_FORMAT).to_i + self.name = match[2].split.last self.dst = match[3].to_i self.offset = match[4].to_i end diff --git a/lib/timezone/version.rb b/lib/timezone/version.rb index df113d4d..2535bb52 100644 --- a/lib/timezone/version.rb +++ b/lib/timezone/version.rb @@ -1,5 +1,5 @@ # frozen_string_literal: true module Timezone - VERSION = '1.3.23'.freeze + VERSION = '1.3.23' end diff --git a/lib/timezone/zone.rb b/lib/timezone/zone.rb index 3d2ebd56..a5ffa263 100644 --- a/lib/timezone/zone.rb +++ b/lib/timezone/zone.rb @@ -6,7 +6,6 @@ require 'timezone/loader' require 'timezone/error' -require 'timezone/loader' module Timezone # This object represents a real-world timezone. Each instance provides @@ -187,7 +186,7 @@ def sanitize(time) # # Each rule has a SOURCE bit which is the number of seconds, since the # Epoch, up to which the rule is valid. - def match?(seconds, rule) #:nodoc: + def match?(seconds, rule) # :nodoc: seconds <= rule[SOURCE_BIT] end diff --git a/test/test_timezone.rb b/test/test_timezone.rb index d3f4579e..82b3ca43 100644 --- a/test/test_timezone.rb +++ b/test/test_timezone.rb @@ -3,7 +3,7 @@ require 'timezone' require 'minitest/autorun' -class TestTimezone < ::Minitest::Test +class TestTimezone < Minitest::Test parallelize_me! def test_names @@ -20,7 +20,12 @@ def test_get def test_fetch assert Timezone.fetch('Australia/Sydney').valid? + + # Explicitly testing block syntax, so disable Cop + # rubocop:disable Style/RedundantFetchBlock assert_equal 'foo', Timezone.fetch('foo/bar') { 'foo' } + # rubocop:enable Style/RedundantFetchBlock + assert_raises Timezone::Error::InvalidZone do Timezone.fetch('foo/bar') end diff --git a/test/timezone/lookup/test_basic.rb b/test/timezone/lookup/test_basic.rb index a49b1706..103db4d2 100644 --- a/test/timezone/lookup/test_basic.rb +++ b/test/timezone/lookup/test_basic.rb @@ -4,7 +4,7 @@ require 'minitest/autorun' require 'ostruct' -class BasicLookupTest < ::Minitest::Test +class BasicLookupTest < Minitest::Test parallelize_me! def config diff --git a/test/timezone/lookup/test_geonames.rb b/test/timezone/lookup/test_geonames.rb index 6e5ebdf8..e98f8310 100644 --- a/test/timezone/lookup/test_geonames.rb +++ b/test/timezone/lookup/test_geonames.rb @@ -4,7 +4,7 @@ require 'minitest/autorun' require_relative '../../http_test_client' -class TestGeonames < ::Minitest::Test +class TestGeonames < Minitest::Test parallelize_me! def coordinates @@ -19,11 +19,11 @@ def etc_data } end - def lookup(body = nil, &_block) + def lookup(body = nil, &block) config = OpenStruct.new config.username = 'timezone' config.request_handler = HTTPTestClientFactory.new(body) - yield config if block_given? + yield config if block Timezone::Lookup::Geonames.new(config) end @@ -40,14 +40,14 @@ def test_missing_username end def test_lookup - mine = lookup(File.open(mock_path + '/lat_lon_coords.txt').read) + mine = lookup(File.read("#{mock_path}/lat_lon_coords.txt")) assert_equal 'Australia/Adelaide', mine.lookup(*coordinates) end def test_lookup_with_etc etc_data.each do |key, data| - body = File.open(mock_path + "/lat_lon_coords_#{key}.txt").read + body = File.read(mock_path + "/lat_lon_coords_#{key}.txt") mine = lookup(body) { |c| c.offset_etc_zones = true } assert_equal data[:name], mine.lookup(*data[:coordinates]) @@ -55,7 +55,7 @@ def test_lookup_with_etc end def test_wrong_offset - body = File.open(mock_path + '/lat_lon_coords_wrong_offset.txt').read + body = File.read("#{mock_path}/lat_lon_coords_wrong_offset.txt") mine = lookup(body) { |c| c.offset_etc_zones = true } assert_nil mine.lookup(*coordinates) @@ -80,29 +80,29 @@ def assert_exception(lookup, message) end def test_api_limit - mine = lookup(File.open(mock_path + '/api_limit_reached.json').read) + mine = lookup(File.read("#{mock_path}/api_limit_reached.json")) assert_exception( mine, 'the daily limit of 30000 credits for XXXXX has been exceeded. ' \ - 'Please throttle your requests or use the commercial service.' + 'Please throttle your requests or use the commercial service.' ) end def test_invalid_latlong - mine = lookup(File.open(mock_path + '/invalid_latlong.json').read) + mine = lookup(File.read("#{mock_path}/invalid_latlong.json")) assert_exception(mine, 'invalid lat/lng') end def test_no_result_found - mine = lookup(File.open(mock_path + '/no_result_found.json').read) + mine = lookup(File.read("#{mock_path}/no_result_found.json")) assert_nil(mine.lookup(10, 10)) end def test_invalid_parameter - mine = lookup(File.open(mock_path + '/invalid_parameter.json').read) + mine = lookup(File.read("#{mock_path}/invalid_parameter.json")) assert_exception(mine, 'error parsing parameter') end diff --git a/test/timezone/lookup/test_google.rb b/test/timezone/lookup/test_google.rb index b8e5db34..153017e3 100644 --- a/test/timezone/lookup/test_google.rb +++ b/test/timezone/lookup/test_google.rb @@ -5,18 +5,18 @@ require 'timecop' require_relative '../../http_test_client' -class TestGoogle < ::Minitest::Test +class TestGoogle < Minitest::Test parallelize_me! def coordinates [-34.92771808058, 138.477041423321] end - def lookup(body = nil, &_block) + def lookup(body = nil, &block) config = OpenStruct.new config.api_key = 'MTIzYWJj' config.request_handler = HTTPTestClientFactory.new(body) - yield config if block_given? + yield config if block Timezone::Lookup::Google.new(config) end @@ -33,7 +33,7 @@ def test_missing_api_key end def test_google_using_lat_long_coordinates - mine = lookup(File.open(mock_path + '/google_lat_lon_coords.txt').read) + mine = lookup(File.read("#{mock_path}/google_lat_lon_coords.txt")) assert_equal 'Australia/Adelaide', mine.lookup(*coordinates) end @@ -76,7 +76,7 @@ def test_url_enterprise end def test_no_result_found - mine = lookup(File.open(mock_path + '/google_no_result_found.json').read) + mine = lookup(File.read("#{mock_path}/google_no_result_found.json")) assert_nil(mine.lookup(26.188703, -78.987053)) end diff --git a/test/timezone/lookup/test_test.rb b/test/timezone/lookup/test_test.rb index 88a87dc6..e8807de3 100644 --- a/test/timezone/lookup/test_test.rb +++ b/test/timezone/lookup/test_test.rb @@ -4,7 +4,7 @@ require 'timezone' require 'minitest/autorun' -class TestTest < ::Minitest::Test +class TestTest < Minitest::Test parallelize_me! def lookup diff --git a/test/timezone/test_deprecate.rb b/test/timezone/test_deprecate.rb index 2e814ff7..140d0870 100644 --- a/test/timezone/test_deprecate.rb +++ b/test/timezone/test_deprecate.rb @@ -3,7 +3,7 @@ require 'timezone/deprecate' require 'minitest/autorun' -class TestDeprecate < ::Minitest::Test +class TestDeprecate < Minitest::Test # This test should not be parallelized because it tests the result # of a single class-level attribute. def test_callback diff --git a/test/timezone/test_lookup.rb b/test/timezone/test_lookup.rb index 950e1801..9bd8559a 100644 --- a/test/timezone/test_lookup.rb +++ b/test/timezone/test_lookup.rb @@ -3,12 +3,12 @@ require 'timezone/lookup' require 'minitest/autorun' -class TestLookup < ::Minitest::Test +class TestLookup < Minitest::Test def test_test_config Timezone::Lookup.config(:test) assert_equal Timezone::Lookup::Test, - Timezone::Lookup.lookup.class + Timezone::Lookup.lookup.class end def test_geonames_config @@ -17,10 +17,10 @@ def test_geonames_config end assert_equal Timezone::Lookup::Geonames, - Timezone::Lookup.lookup.class + Timezone::Lookup.lookup.class assert_equal Timezone::NetHTTPClient, - Timezone::Lookup.lookup.config.request_handler + Timezone::Lookup.lookup.config.request_handler end def test_google_config @@ -29,10 +29,10 @@ def test_google_config end assert_equal Timezone::Lookup::Google, - Timezone::Lookup.lookup.class + Timezone::Lookup.lookup.class assert_equal Timezone::NetHTTPClient, - Timezone::Lookup.lookup.config.request_handler + Timezone::Lookup.lookup.config.request_handler end def test_custom_config diff --git a/test/timezone/test_mathn_compatibility.rb b/test/timezone/test_mathn_compatibility.rb index 0771449b..46fe0d3e 100644 --- a/test/timezone/test_mathn_compatibility.rb +++ b/test/timezone/test_mathn_compatibility.rb @@ -6,7 +6,7 @@ begin require 'mathn' - class TestTimezone < ::Minitest::Test + class TestTimezone < Minitest::Test parallelize_me! def test_lookup_mathn_compatibility diff --git a/test/timezone/test_nil_zone.rb b/test/timezone/test_nil_zone.rb index 30af847d..2cb2e601 100644 --- a/test/timezone/test_nil_zone.rb +++ b/test/timezone/test_nil_zone.rb @@ -3,7 +3,7 @@ require 'timezone/nil_zone' require 'minitest/autorun' -class TestNilZone < ::Minitest::Test +class TestNilZone < Minitest::Test parallelize_me! def setup diff --git a/test/timezone/test_zone.rb b/test/timezone/test_zone.rb index 245ddca1..dd3bb0ed 100644 --- a/test/timezone/test_zone.rb +++ b/test/timezone/test_zone.rb @@ -4,7 +4,7 @@ require 'timezone/zone' require 'minitest/autorun' -class TestZone < ::Minitest::Test +class TestZone < Minitest::Test parallelize_me! def zone(name) diff --git a/timezone.gemspec b/timezone.gemspec index 97bcb3aa..bf18ffa6 100644 --- a/timezone.gemspec +++ b/timezone.gemspec @@ -1,7 +1,6 @@ # frozen_string_literal: true -# -*- encoding: utf-8 -*- -$:.push File.expand_path('../lib', __FILE__) +$:.push File.expand_path('lib', __dir__) require 'timezone/version' Gem::Specification.new do |s| @@ -14,10 +13,10 @@ Gem::Specification.new do |s| s.summary = "timezone-#{Timezone::VERSION}" s.license = 'MIT' s.description = 'Accurate current and historical timezones for Ruby with ' \ - 'support for Geonames and Google latitude - longitude lookups.' + 'support for Geonames and Google latitude - longitude ' \ + 'lookups.' s.files = `git ls-files`.split("\n") - s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n") s.executables = `git ls-files -- bin/*` .split("\n").map { |f| File.basename(f) } @@ -25,8 +24,12 @@ Gem::Specification.new do |s| s.rdoc_options = ['--charset=UTF-8'] s.require_paths = ['lib'] + s.required_ruby_version = '>= 2.2' + s.add_development_dependency('minitest', '~> 5.8') s.add_development_dependency('rake', '~> 12') - s.add_development_dependency('rubocop', '= 0.51') + s.add_development_dependency('rubocop', '<= 1.51.0', '>= 0.5.1') + s.add_development_dependency('rubocop-performance', '<= 1.18.0') s.add_development_dependency('timecop', '~> 0.8') + s.metadata['rubygems_mfa_required'] = 'true' end