diff --git a/.rubocop.yml b/.rubocop.yml new file mode 100644 index 0000000..f96ec5e --- /dev/null +++ b/.rubocop.yml @@ -0,0 +1,10 @@ + AllCops: + NewCops: enable + + Metrics/BlockLength: + Enabled: true + Exclude: + - spec/**/* + + Style/Documentation: + Enabled: false diff --git a/.tool-versions b/.tool-versions new file mode 100644 index 0000000..0b2d858 --- /dev/null +++ b/.tool-versions @@ -0,0 +1 @@ +ruby 3.1.2 diff --git a/.travis.yml b/.travis.yml index dbe5154..7969780 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,3 +1,3 @@ language: ruby rvm: - - 2.7 \ No newline at end of file + - 3.1.2 diff --git a/Gemfile b/Gemfile index 557766f..57a7fcb 100644 --- a/Gemfile +++ b/Gemfile @@ -1,10 +1,16 @@ # frozen_string_literal: true -source "https://rubygems.org" +source 'https://rubygems.org' -git_source(:github) {|repo_name| "https://github.com/#{repo_name}" } +git_source(:github) { |repo_name| "https://github.com/#{repo_name}" } -gem "rspec" gem 'guard' gem 'guard-shell' -gem 'rake' \ No newline at end of file +gem 'rake' +gem 'rspec' + +group :development do + gem 'rubocop' + gem 'rubocop-shopify', require: false + gem 'ruby-lsp' +end diff --git a/Gemfile.lock b/Gemfile.lock index d1fea3e..4386bf1 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -1,24 +1,27 @@ GEM remote: https://rubygems.org/ specs: + ast (2.4.2) coderay (1.1.3) - diff-lcs (1.4.4) - ffi (1.13.1) - formatador (0.2.5) - guard (2.16.2) + diff-lcs (1.5.0) + ffi (1.15.5) + formatador (1.1.0) + guard (2.18.0) formatador (>= 0.2.4) listen (>= 2.7, < 4.0) lumberjack (>= 1.0.12, < 2.0) nenv (~> 0.1) notiffany (~> 0.0) - pry (>= 0.9.12) + pry (>= 0.13.0) shellany (~> 0.0) thor (>= 0.18.1) guard-compat (1.2.1) - guard-shell (0.7.1) + guard-shell (0.7.2) guard (>= 2.0.0) guard-compat (~> 1.0) - listen (3.2.1) + json (2.6.2) + language_server-protocol (3.17.0.1) + listen (3.7.1) rb-fsevent (~> 0.10, >= 0.10.3) rb-inotify (~> 0.9, >= 0.9.10) lumberjack (1.2.8) @@ -27,37 +30,70 @@ GEM notiffany (0.1.3) nenv (~> 0.1) shellany (~> 0.0) - pry (0.13.1) + parallel (1.22.1) + parser (3.1.2.1) + ast (~> 2.4.1) + prettier_print (0.1.0) + pry (0.14.1) coderay (~> 1.1) method_source (~> 1.0) - rake (13.0.1) - rb-fsevent (0.10.4) + rainbow (3.1.1) + rake (13.0.6) + rb-fsevent (0.11.2) rb-inotify (0.10.1) ffi (~> 1.0) - rspec (3.9.0) - rspec-core (~> 3.9.0) - rspec-expectations (~> 3.9.0) - rspec-mocks (~> 3.9.0) - rspec-core (3.9.3) - rspec-support (~> 3.9.3) - rspec-expectations (3.9.2) + regexp_parser (2.5.0) + rexml (3.2.5) + rspec (3.11.0) + rspec-core (~> 3.11.0) + rspec-expectations (~> 3.11.0) + rspec-mocks (~> 3.11.0) + rspec-core (3.11.0) + rspec-support (~> 3.11.0) + rspec-expectations (3.11.0) diff-lcs (>= 1.2.0, < 2.0) - rspec-support (~> 3.9.0) - rspec-mocks (3.9.1) + rspec-support (~> 3.11.0) + rspec-mocks (3.11.1) diff-lcs (>= 1.2.0, < 2.0) - rspec-support (~> 3.9.0) - rspec-support (3.9.3) + rspec-support (~> 3.11.0) + rspec-support (3.11.0) + rubocop (1.36.0) + json (~> 2.3) + parallel (~> 1.10) + parser (>= 3.1.2.1) + rainbow (>= 2.2.2, < 4.0) + regexp_parser (>= 1.8, < 3.0) + rexml (>= 3.2.5, < 4.0) + rubocop-ast (>= 1.20.1, < 2.0) + ruby-progressbar (~> 1.7) + unicode-display_width (>= 1.4.0, < 3.0) + rubocop-ast (1.21.0) + parser (>= 3.1.1.0) + rubocop-shopify (2.9.0) + rubocop (~> 1.33) + ruby-lsp (0.3.2) + language_server-protocol (~> 3.17.0) + sorbet-runtime + syntax_tree (>= 3.4) + ruby-progressbar (1.11.0) shellany (0.0.1) - thor (1.0.1) + sorbet-runtime (0.5.10415) + syntax_tree (3.5.0) + prettier_print + thor (1.2.1) + unicode-display_width (2.2.0) PLATFORMS - ruby + arm64-darwin-21 DEPENDENCIES guard guard-shell rake rspec + rubocop + rubocop-shopify + ruby-lsp BUNDLED WITH - 2.1.2 + 2.3.19 diff --git a/Guardfile b/Guardfile index 4e66be6..65972ea 100644 --- a/Guardfile +++ b/Guardfile @@ -1,3 +1,5 @@ +# frozen_string_literal: true + # A sample Guardfile # More info at https://github.com/guard/guard#readme @@ -19,5 +21,5 @@ # watch(%r{file/path}) { `command(s)` } # guard :shell do - watch(%r{^*\.rb}) { `bundle exec rspec --force-color spec/` } + watch(/^*\.rb/) { `bundle exec rspec --force-color spec/` } end diff --git a/Rakefile b/Rakefile index a2cb1a5..a6cd24a 100644 --- a/Rakefile +++ b/Rakefile @@ -1,3 +1,5 @@ +# frozen_string_literal: true + begin require 'rspec/core/rake_task' @@ -5,7 +7,7 @@ begin t.fail_on_error = false end - task :default => :spec + task default: :spec rescue LoadError # no rspec available -end \ No newline at end of file +end diff --git a/lib/checkout.rb b/lib/checkout.rb index 0e95bb7..dbe243d 100644 --- a/lib/checkout.rb +++ b/lib/checkout.rb @@ -22,7 +22,7 @@ def scan(barcode) def total @cart.reduce(0) do |total, (item, quantity)| rule = @rules.find(item) - total + rule.apply(quantity: quantity) + total + rule.apply(quantity:) end.floor(2) end end diff --git a/lib/product.rb b/lib/product.rb index 94c28ff..158d155 100644 --- a/lib/product.rb +++ b/lib/product.rb @@ -4,8 +4,8 @@ class Product attr_accessor :code, :price def initialize(code, price = 0.00) - raise ArgumentError.new('product needs a valid code') if code.empty? - raise ArgumentError.new('price needs to be positive or 0') if price.negative? + raise ArgumentError, 'product needs a valid code' if code.empty? + raise ArgumentError, 'price needs to be positive or 0' if price.negative? self.code = code self.price = price diff --git a/lib/rule.rb b/lib/rule.rb index 1cef1b2..2b04dad 100644 --- a/lib/rule.rb +++ b/lib/rule.rb @@ -7,8 +7,8 @@ def initialize(product, minimun: 0, per: 1, price: nil) # the price of the rule should be the same of # the product in case it wasn't informed self.price = price || product.price - raise ArgumentError.new('price should be a positive number') if self.price.negative? - + raise ArgumentError, 'price should be a positive number' if self.price.negative? + self.product = product self.minimun = minimun self.per = per @@ -30,6 +30,6 @@ def apply(quantity: 0) quantity = quantity % per end - total + product.price * quantity + total + (product.price * quantity) end end diff --git a/lib/rules.rb b/lib/rules.rb index b8a17cd..f26e67e 100644 --- a/lib/rules.rb +++ b/lib/rules.rb @@ -1,32 +1,34 @@ # frozen_string_literal: true class Rules - def initialize(*rules) + attr_reader :rules + + def initialize(*received_rules) @rules = [] - rules.each { |rule| add(rule) } + received_rules.each { |rule| add(rule) } end def count - @rules.count + rules.count end - def each - @rules.each { |p| yield p } + def each(&) + rules.each(&) end def add(rule) - raise StandardError.new('only one rule per product') if self.exists?(rule) - - @rules << rule + raise StandardError, 'only one rule per product' if exists?(rule) + + rules << rule end def find(code) - @rules.find { |rule| rule.code == code } + rules.find { |rule| rule.code == code } end private def exists?(rule) - @rules.map(&:code).include?(rule.code) + rules.map(&:code).include?(rule.code) end end diff --git a/spec/checkout_spec.rb b/spec/checkout_spec.rb index 9d64b79..9cf479b 100644 --- a/spec/checkout_spec.rb +++ b/spec/checkout_spec.rb @@ -1,10 +1,10 @@ # frozen_string_literal: true -require 'rule.rb' -require 'rules.rb' -require 'product.rb' -require 'errors.rb' -require 'checkout.rb' +require 'rule' +require 'rules' +require 'product' +require 'errors' +require 'checkout' RSpec.describe Checkout do before(:each) do @@ -17,7 +17,7 @@ Rule.new(gr1, minimun: 2, per: 2, price: 3.11), Rule.new(sr1, minimun: 3, per: 1, price: 4.50), Rule.new(cf1, minimun: 3, per: 1, price: 11.23 / 3 * 2), - Rule.new(gr2), + Rule.new(gr2) ].each { |r| pricing_rules.add(r) } @checkout = Checkout.new(pricing_rules) end diff --git a/spec/product_spec.rb b/spec/product_spec.rb index f2b6a65..de43f21 100644 --- a/spec/product_spec.rb +++ b/spec/product_spec.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true -require 'product.rb' +require 'product' RSpec.describe Product do describe '#new' do diff --git a/spec/rule_spec.rb b/spec/rule_spec.rb index a69711e..6960bac 100644 --- a/spec/rule_spec.rb +++ b/spec/rule_spec.rb @@ -1,7 +1,7 @@ # frozen_string_literal: true -require 'product.rb' -require 'rule.rb' +require 'product' +require 'rule' RSpec.describe Rule do before(:each) do diff --git a/spec/rules_spec.rb b/spec/rules_spec.rb index 6f60400..2699fe4 100644 --- a/spec/rules_spec.rb +++ b/spec/rules_spec.rb @@ -1,8 +1,8 @@ # frozen_string_literal: true -require 'product.rb' -require 'rule.rb' -require 'rules.rb' +require 'product' +require 'rule' +require 'rules' RSpec.describe Rules do before(:each) do @@ -33,7 +33,7 @@ end end - describe '#find' do + describe '#find' do before(:each) do @rules = Rules.new @rules.add(@rule) diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index 251aa51..5681bb3 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + # This file was generated by the `rspec --init` command. Conventionally, all # specs live under a `spec` directory, which RSpec adds to the `$LOAD_PATH`. # The generated `.rspec` file contains `--require spec_helper` which will cause @@ -44,57 +46,55 @@ # 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: - # - http://rspec.info/blog/2012/06/rspecs-new-expectation-syntax/ - # - http://www.teaisaweso.me/blog/2013/05/27/rspecs-new-message-expectation-syntax/ - # - http://rspec.info/blog/2014/05/notable-changes-in-rspec-3/#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: + # # - http://rspec.info/blog/2012/06/rspecs-new-expectation-syntax/ + # # - http://www.teaisaweso.me/blog/2013/05/27/rspecs-new-message-expectation-syntax/ + # # - http://rspec.info/blog/2014/05/notable-changes-in-rspec-3/#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