From 0c9d809b00ec697c56fb4a18c0c44f664f7f4ead Mon Sep 17 00:00:00 2001 From: Jim Gay Date: Tue, 9 Jul 2024 14:38:01 -0400 Subject: [PATCH] Add standardrb --- .simplecov | 2 +- Gemfile | 3 +- Gemfile.lock | 45 +++++++++++ lib/sof-cycle.rb | 2 +- lib/sof/cycle.rb | 158 ++++++++++++++++++------------------- lib/sof/cycle/parser.rb | 2 +- lib/sof/cycle/time_span.rb | 2 +- spec/calendar_spec.rb | 2 +- spec/cycle_spec.rb | 2 +- spec/dormant_spec.rb | 2 +- spec/lookback_spec.rb | 2 +- spec/parser_spec.rb | 2 +- spec/spec_helper.rb | 100 ++++++++++++----------- spec/time_span_spec.rb | 2 +- spec/volume_only_spec.rb | 2 +- spec/within_spec.rb | 2 +- 16 files changed, 187 insertions(+), 143 deletions(-) diff --git a/.simplecov b/.simplecov index 21a6e02..e938130 100644 --- a/.simplecov +++ b/.simplecov @@ -1,4 +1,4 @@ require "simplecov" SimpleCov.start do add_filter "/spec/" -end \ No newline at end of file +end diff --git a/Gemfile b/Gemfile index ad1ecda..f4f459f 100644 --- a/Gemfile +++ b/Gemfile @@ -8,4 +8,5 @@ gem "rake" gem "rspec" gem "debug" -gem "simplecov" \ No newline at end of file +gem "simplecov" +gem "standard" diff --git a/Gemfile.lock b/Gemfile.lock index 576ed00..32e777c 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -18,6 +18,7 @@ GEM minitest (>= 5.1) mutex_m tzinfo (~> 2.0) + ast (2.4.2) base64 (0.2.0) bigdecimal (3.1.8) concurrent-ruby (1.3.3) @@ -35,15 +36,27 @@ GEM irb (1.14.0) rdoc (>= 4.0.0) reline (>= 0.4.2) + json (2.7.2) + language_server-protocol (3.17.0.3) + lint_roller (1.1.0) minitest (5.24.1) mutex_m (0.2.0) + parallel (1.25.1) + parser (3.3.3.0) + ast (~> 2.4.1) + racc psych (5.1.2) stringio + racc (1.8.0) + rainbow (3.1.1) rake (13.2.1) rdoc (6.7.0) psych (>= 4.0.0) + regexp_parser (2.9.2) reline (0.5.9) io-console (~> 0.5) + rexml (3.3.1) + strscan rspec (3.13.0) rspec-core (~> 3.13.0) rspec-expectations (~> 3.13.0) @@ -57,15 +70,46 @@ GEM diff-lcs (>= 1.2.0, < 2.0) rspec-support (~> 3.13.0) rspec-support (3.13.1) + rubocop (1.64.1) + json (~> 2.3) + language_server-protocol (>= 3.17.0) + parallel (~> 1.10) + parser (>= 3.3.0.2) + rainbow (>= 2.2.2, < 4.0) + regexp_parser (>= 1.8, < 3.0) + rexml (>= 3.2.5, < 4.0) + rubocop-ast (>= 1.31.1, < 2.0) + ruby-progressbar (~> 1.7) + unicode-display_width (>= 2.4.0, < 3.0) + rubocop-ast (1.31.3) + parser (>= 3.3.1.0) + rubocop-performance (1.21.1) + rubocop (>= 1.48.1, < 2.0) + rubocop-ast (>= 1.31.1, < 2.0) + ruby-progressbar (1.13.0) simplecov (0.22.0) docile (~> 1.1) simplecov-html (~> 0.11) simplecov_json_formatter (~> 0.1) simplecov-html (0.12.3) simplecov_json_formatter (0.1.4) + standard (1.39.1) + language_server-protocol (~> 3.17.0.2) + lint_roller (~> 1.0) + rubocop (~> 1.64.0) + standard-custom (~> 1.0.0) + standard-performance (~> 1.4) + standard-custom (1.0.2) + lint_roller (~> 1.0) + rubocop (~> 1.50) + standard-performance (1.4.0) + lint_roller (~> 1.1) + rubocop-performance (~> 1.21.0) stringio (3.1.1) + strscan (3.1.0) tzinfo (2.0.6) concurrent-ruby (~> 1.0) + unicode-display_width (2.5.0) PLATFORMS arm64-darwin-23 @@ -77,6 +121,7 @@ DEPENDENCIES rake rspec simplecov + standard BUNDLED WITH 2.5.10 diff --git a/lib/sof-cycle.rb b/lib/sof-cycle.rb index 9cb90b2..58f09f3 100644 --- a/lib/sof-cycle.rb +++ b/lib/sof-cycle.rb @@ -1 +1 @@ -require_relative "sof/cycle" \ No newline at end of file +require_relative "sof/cycle" diff --git a/lib/sof/cycle.rb b/lib/sof/cycle.rb index 97f4a8c..0898d58 100644 --- a/lib/sof/cycle.rb +++ b/lib/sof/cycle.rb @@ -13,26 +13,26 @@ class Cycle class InvalidInput < StandardError; end class InvalidPeriod < InvalidInput; end - + class InvalidKind < InvalidInput; end - + def initialize(notation, parser: Parser.new(notation)) @notation = notation @parser = parser validate_period - + return if @parser.valid? - + raise InvalidInput, "'#{notation}' is not a valid input" end - + attr_reader :parser - + delegate [:activated_notation, :volume, :from, :from_date, :time_span, :period, - :humanized_period, :period_key, :"active?"] => :@parser - delegate [:kind, :"volume_only?", :valid_periods] => "self.class" + :humanized_period, :period_key, :active?] => :@parser + delegate [:kind, :volume_only?, :valid_periods] => "self.class" delegate [:period_count, :duration] => :time_span - + # Turn a cycle or notation string into a hash def self.dump(cycle_or_string) if cycle_or_string.is_a? Cycle @@ -41,23 +41,23 @@ def self.dump(cycle_or_string) Cycle.for(cycle_or_string) end.to_h end - + # Return a Cycle object from a hash def self.load(hash) symbolized_hash = hash.symbolize_keys cycle_class = class_for_kind(symbolized_hash[:kind]) - + unless cycle_class.valid_periods.empty? cycle_class.validate_period( TimeSpan.notation_id_from_name(symbolized_hash[:period]) ) end - + Cycle.for notation(symbolized_hash) rescue TimeSpan::InvalidPeriod => exc raise InvalidPeriod, exc.message end - + # Retun a notation string from a hash # # @param hash [Hash] hash of data for a valid Cycle @@ -65,7 +65,7 @@ def self.load(hash) def self.notation(hash) volume_notation = "V#{hash.fetch(:volume) { 1 }}" return volume_notation if hash[:kind].nil? || hash[:kind].to_sym == :volume_only - + cycle_class = class_for_kind(hash[:kind].to_sym) [ volume_notation, @@ -74,7 +74,7 @@ def self.notation(hash) hash.fetch(:from, nil) ].compact.join end - + # Return a Cycle object from a notation string # # @param notation [String] a string notation representing a Cycle @@ -88,15 +88,15 @@ def self.for(notation) unless parser.valid? raise InvalidInput, "'#{notation}' is not a valid input" end - + cycle = cycle_handlers.find do |klass| parser.parses?(klass.notation_id) end.new(notation, parser:) return cycle if parser.active? - + Cycle::Dormant.new(cycle, parser:) end - + # Return the appropriate class for the give notation id # # @param notation [String] notation id matching the kind of Cycle class @@ -108,7 +108,7 @@ def self.class_for_notation_id(notation_id) klass.notation_id == notation_id end || raise(InvalidKind, "'#{notation_id}' is not a valid kind of #{name}") end - + # Return the class handling the kind # # @param sym [Symbol] symbol matching the kind of Cycle class @@ -119,26 +119,26 @@ def self.class_for_kind(sym) klass.handles?(sym) end || raise(InvalidKind, "':#{sym}' is not a valid kind of Cycle") end - + def self.cycle_handlers = @cycle_handlers ||= Set.new - + def self.inherited(klass) = cycle_handlers << klass - + def self.handles?(sym) sym && kind == sym.to_sym end - + @volume_only = false @notation_id = nil @kind = nil @valid_periods = [] - + def self.volume_only? = @volume_only - + class << self attr_reader :notation_id, :kind, :valid_periods end - + # Raises an error if the given period isn't in the list of valid periods. # # @param period [String] period matching the class valid periods @@ -149,19 +149,19 @@ def self.validate_period(period) #{valid_periods.join(", ")} ERR end - + def validate_period return if valid_periods.empty? - + self.class.validate_period(period_key) end - + # Return the cycle representation as a notation string def notation = self.class.notation(to_h) - + # Cycles are considered equal if their hash representations are equal def ==(other) = to_h == other.to_h - + # From the supplied anchor date, are there enough in-window completions to # satisfy the cycle? # @@ -169,28 +169,28 @@ def ==(other) = to_h == other.to_h def satisfied_by?(completion_dates, anchor: Date.current) covered_dates(completion_dates, anchor:).size >= volume end - + def covered_dates(dates, anchor: Date.current) dates.select do |date| cover?(date, anchor:) end end - + def cover?(date, anchor: Date.current) range(anchor).cover?(date) end - + def range(anchor) = start_date(anchor)..final_date(anchor) - + def humanized_span = [period_count, humanized_period].join(" ") - + # Return the final date of the cycle def final_date(_anchor) = nil - + def expiration_of(_completion_dates, anchor: Date.current) = nil - + def volume_to_delay_expiration(_completion_dates, anchor:) = 0 - + def to_h { kind:, @@ -200,70 +200,70 @@ def to_h **from_data } end - + def from_data return {} unless from - + {from: from} end - + def as_json(...) = notation - + class Dormant def initialize(cycle, parser:) @cycle = cycle @parser = parser end - + attr_reader :cycle, :parser - + def to_s cycle.to_s + " (dormant)" end - + def covered_dates(...) = [] - + def expiration_of(...) = nil - + def satisfied_by?(...) = false - + def cover?(...) = false - + def method_missing(method, ...) = cycle.send(method, ...) - + def respond_to_missing?(method, include_private = false) cycle.respond_to?(method, include_private) end end - + class Within < self @volume_only = false @notation_id = "W" @kind = :within @valid_periods = %w[D W M Y] - + def to_s = "#{volume}x within #{date_range}" - + def date_range return humanized_span unless active? - + [start_date, final_date].map { _1.to_fs(:american) }.join(" - ") end - + def final_date(_ = nil) = time_span.end_date(start_date) - + def start_date(_ = nil) = from_date.to_date end - + class VolumeOnly < self @volume_only = true @notation_id = nil @kind = :volume_only @valid_periods = [] - + class << self def handles?(sym) = sym.nil? || super - + def validate_period(period) raise InvalidPeriod, <<~ERR.squish unless period.nil? Invalid period value of '#{period}' provided. Valid periods are: @@ -271,83 +271,83 @@ def validate_period(period) ERR end end - + def to_s = "#{volume}x total" - + def covered_dates(dates, ...) = dates - + def cover?(...) = true end - + class Lookback < self @volume_only = false @notation_id = "L" @kind = :lookback @valid_periods = %w[D W M Y] - + def to_s = "#{volume}x in the prior #{period_count} #{humanized_period}" - + def volume_to_delay_expiration(completion_dates, anchor:) oldest_relevant_completion = completion_dates.min [completion_dates.count(oldest_relevant_completion), volume].min end - + # "Absent further completions, you go red on this date" # @return [Date, nil] the date on which the cycle will expire given the # provided completion dates. Returns nil if the cycle is already unsatisfied. def expiration_of(completion_dates) anchor = completion_dates.max_by(volume) { _1 }.min return unless satisfied_by?(completion_dates, anchor:) - + window_end anchor end - + def final_date(anchor) return if anchor.nil? - + time_span.end_date(anchor.to_date) end alias_method :window_end, :final_date - + def start_date(anchor) time_span.begin_date(anchor.to_date) end alias_method :window_start, :start_date end - + class Calendar < self @volume_only = false @notation_id = "C" @kind = :calendar @valid_periods = %w[M Q Y] - + class << self def frame_of_reference = "total" end - + def to_s "#{volume}x every #{period_count} calendar #{humanized_period}" end - + # "Absent further completions, you go red on this date" # @return [Date, nil] the date on which the cycle will expire given the # provided completion dates. Returns nil if the cycle is already unsatisfied. def expiration_of(completion_dates) anchor = completion_dates.max_by(volume) { _1 }.min return unless satisfied_by?(completion_dates, anchor:) - + window_end(anchor) + duration end - + def final_date(anchor) return if anchor.nil? time_span.end_date_of_period(anchor.to_date) end alias_method :window_end, :final_date - + def start_date(anchor) time_span.begin_date_of_period(anchor.to_date) end - end + end end end diff --git a/lib/sof/cycle/parser.rb b/lib/sof/cycle/parser.rb index 72f68d4..771cc36 100644 --- a/lib/sof/cycle/parser.rb +++ b/lib/sof/cycle/parser.rb @@ -104,4 +104,4 @@ def from = match[:from] def kind = match[:kind] end -end \ No newline at end of file +end diff --git a/lib/sof/cycle/time_span.rb b/lib/sof/cycle/time_span.rb index b48975d..e80473d 100644 --- a/lib/sof/cycle/time_span.rb +++ b/lib/sof/cycle/time_span.rb @@ -233,4 +233,4 @@ class TimeSpanOne < self def interval = humanized_period end end -end \ No newline at end of file +end diff --git a/spec/calendar_spec.rb b/spec/calendar_spec.rb index 301bfeb..ed7016b 100644 --- a/spec/calendar_spec.rb +++ b/spec/calendar_spec.rb @@ -92,4 +92,4 @@ module SOF end end end -end \ No newline at end of file +end diff --git a/spec/cycle_spec.rb b/spec/cycle_spec.rb index 6da10f1..0148afa 100644 --- a/spec/cycle_spec.rb +++ b/spec/cycle_spec.rb @@ -109,4 +109,4 @@ module SOF end end end -end \ No newline at end of file +end diff --git a/spec/dormant_spec.rb b/spec/dormant_spec.rb index da475d9..16f879c 100644 --- a/spec/dormant_spec.rb +++ b/spec/dormant_spec.rb @@ -70,4 +70,4 @@ module SOF end end end -end \ No newline at end of file +end diff --git a/spec/lookback_spec.rb b/spec/lookback_spec.rb index 5bd5493..6d8ba6e 100644 --- a/spec/lookback_spec.rb +++ b/spec/lookback_spec.rb @@ -90,4 +90,4 @@ module SOF end end end -end \ No newline at end of file +end diff --git a/spec/parser_spec.rb b/spec/parser_spec.rb index fd8e879..05683f5 100644 --- a/spec/parser_spec.rb +++ b/spec/parser_spec.rb @@ -183,4 +183,4 @@ module SOF end end end -end \ No newline at end of file +end diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index 8d3a4e6..494ecf7 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -51,55 +51,53 @@ # triggering implicit auto-inclusion in groups with matching metadata. config.shared_context_metadata_behavior = :apply_to_host_groups -# The settings below are suggested to provide a good initial experience -# with RSpec, but feel free to customize to your heart's content. -=begin - # This allows you to limit a spec run to individual examples or groups - # you care about by tagging them with `:focus` metadata. When nothing - # is tagged with `:focus`, all examples get run. RSpec also provides - # aliases for `it`, `describe`, and `context` that include `:focus` - # metadata: `fit`, `fdescribe` and `fcontext`, respectively. - config.filter_run_when_matching :focus - - # Allows RSpec to persist some state between runs in order to support - # the `--only-failures` and `--next-failure` CLI options. We recommend - # you configure your source control system to ignore this file. - config.example_status_persistence_file_path = "spec/examples.txt" - - # Limits the available syntax to the non-monkey patched syntax that is - # recommended. For more details, see: - # https://rspec.info/features/3-12/rspec-core/configuration/zero-monkey-patching-mode/ - config.disable_monkey_patching! - - # This setting enables warnings. It's recommended, but in some cases may - # be too noisy due to issues in dependencies. - config.warnings = true - - # Many RSpec users commonly either run the entire suite or an individual - # file, and it's useful to allow more verbose output when running an - # individual spec file. - if config.files_to_run.one? - # Use the documentation formatter for detailed output, - # unless a formatter has already been configured - # (e.g. via a command-line flag). - config.default_formatter = "doc" - end - - # Print the 10 slowest examples and example groups at the - # end of the spec run, to help surface which specs are running - # particularly slow. - config.profile_examples = 10 - - # Run specs in random order to surface order dependencies. If you find an - # order dependency and want to debug it, you can fix the order by providing - # the seed, which is printed after each run. - # --seed 1234 - config.order = :random - - # Seed global randomization in this process using the `--seed` CLI option. - # Setting this allows you to use `--seed` to deterministically reproduce - # test failures related to randomization by passing the same `--seed` value - # as the one that triggered the failure. - Kernel.srand config.seed -=end + # The settings below are suggested to provide a good initial experience + # with RSpec, but feel free to customize to your heart's content. + # # This allows you to limit a spec run to individual examples or groups + # # you care about by tagging them with `:focus` metadata. When nothing + # # is tagged with `:focus`, all examples get run. RSpec also provides + # # aliases for `it`, `describe`, and `context` that include `:focus` + # # metadata: `fit`, `fdescribe` and `fcontext`, respectively. + # config.filter_run_when_matching :focus + # + # # Allows RSpec to persist some state between runs in order to support + # # the `--only-failures` and `--next-failure` CLI options. We recommend + # # you configure your source control system to ignore this file. + # config.example_status_persistence_file_path = "spec/examples.txt" + # + # # Limits the available syntax to the non-monkey patched syntax that is + # # recommended. For more details, see: + # # https://rspec.info/features/3-12/rspec-core/configuration/zero-monkey-patching-mode/ + # config.disable_monkey_patching! + # + # # This setting enables warnings. It's recommended, but in some cases may + # # be too noisy due to issues in dependencies. + # config.warnings = true + # + # # Many RSpec users commonly either run the entire suite or an individual + # # file, and it's useful to allow more verbose output when running an + # # individual spec file. + # if config.files_to_run.one? + # # Use the documentation formatter for detailed output, + # # unless a formatter has already been configured + # # (e.g. via a command-line flag). + # config.default_formatter = "doc" + # end + # + # # Print the 10 slowest examples and example groups at the + # # end of the spec run, to help surface which specs are running + # # particularly slow. + # config.profile_examples = 10 + # + # # Run specs in random order to surface order dependencies. If you find an + # # order dependency and want to debug it, you can fix the order by providing + # # the seed, which is printed after each run. + # # --seed 1234 + # config.order = :random + # + # # Seed global randomization in this process using the `--seed` CLI option. + # # Setting this allows you to use `--seed` to deterministically reproduce + # # test failures related to randomization by passing the same `--seed` value + # # as the one that triggered the failure. + # Kernel.srand config.seed end diff --git a/spec/time_span_spec.rb b/spec/time_span_spec.rb index e747bef..377b3b0 100644 --- a/spec/time_span_spec.rb +++ b/spec/time_span_spec.rb @@ -60,4 +60,4 @@ module SOF end end end -end \ No newline at end of file +end diff --git a/spec/volume_only_spec.rb b/spec/volume_only_spec.rb index fdb629b..586d135 100644 --- a/spec/volume_only_spec.rb +++ b/spec/volume_only_spec.rb @@ -70,4 +70,4 @@ module SOF end end end -end \ No newline at end of file +end diff --git a/spec/within_spec.rb b/spec/within_spec.rb index 056a70a..975b1b4 100644 --- a/spec/within_spec.rb +++ b/spec/within_spec.rb @@ -96,4 +96,4 @@ module SOF end end end -end \ No newline at end of file +end