Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Proposed Solution #3

Open
wants to merge 20 commits into
base: main
Choose a base branch
from
Open

Proposed Solution #3

wants to merge 20 commits into from

Conversation

safafa
Copy link

@safafa safafa commented Jan 18, 2022

In this PR I tried solving the given problem by making these changes :

  1. Basket_controller.rb :
  • Implemented index, new, show, create actions
  • Implemented Build_goods private method. It's used in the create action and allows us to build the goods that belong to the newly created basket.
  1. goods.rb :
  • Implemented calulate_tax_update_price a callback method that allows us to calculate the taxes and update the good's price.
  1. basket.rb
  • Implemented update_taxes_update_price a callback method that allows us to calculate the Sales taxes and update the basket's total price.

The final output :
image

image

image

@nachoabad nachoabad self-requested a review January 19, 2022 08:53
@nachoabad
Copy link

nachoabad commented Jan 19, 2022

Congratulations on your first PR Safa! 🎉

I will probably write many comments but don't feel discouraged 😄 We all went through learning these things by someone else pointing them out to us.

@nachoabad
Copy link

As a first tip, I suggest you always assign yourself your own PRs:

image

And request someone (or multiple team members) you think could be interested in reviewing it:

image

@safafa safafa self-assigned this Jan 19, 2022
@nachoabad
Copy link

nachoabad commented Jan 19, 2022

You used camel case to name your branch workBranch, but the most common style is lower cases with hyphens: short-descriptive-name.

Also, there is no need to use the word branch in a git branch.

@@ -0,0 +1,2 @@
1 imported box of chocolates at 10.00
1 imported bottle of perfume at 47.50

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You have repeated this file here and in the root folder. Normally these kinds of files should be all under a descriptive directory name (input_baskets?... naming is hard 😅)

README.md Outdated

### EXPECTED OUTPUT
yarn

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's great that you wrote a README detailing the versions and dependencies 👏🏽.

However, this plain line is confusing: are we supposed to install yarn? Run the yarn command? Think of the end-user and try to write instructions as simple and concrete as possible.

@@ -0,0 +1,5 @@
Rails.application.routes.draw do
root 'baskets#index'
resources :baskets, except: [:destroy]

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Great use of Rails conventions here 👍🏽

Gemfile Outdated

# Reduces boot times through caching; required in config/boot.rb
gem 'bootsnap', '>= 1.4.2', require: false
gem 'rubocop', '>= 1.0', '< 2.0'
Copy link

@nachoabad nachoabad Jan 19, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Great that you use Rubocop and that there are no offenses 👏🏽 . But follow documentation best practices and add require: false since it is a standalone tool.


private

def build_goods(basket)

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's good that you used a private to not expose it as a public method. But this logic might not belong to a controller.

A controller is a type of class that's responsible for making sense out of a request made in an application and producing appropriate output. So this logic to split and parse a file can go in another class. That way, you can easily unit test that class and the controller just has one line to call that class.

@@ -1,3 +1,7 @@
class Basket < ApplicationRecord
has_many :goods, dependent: :destroy

def handle_file(file)
Copy link

@nachoabad nachoabad Jan 19, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In my opinion, this method does not belong to the basket class, because all it does is it splits any file by line. Is unrelated to the domain of a Basket. It just so happens that you need it to get data related to a Basket, but that is all.

In this case, you could extract a class: https://refactoring.guru/extract-class

private

def calulate_tax_update_price
exampt = %w[chocolate book pills]

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In this case, because those values are going to be constant and might be needed elsewhere in the application, a good Ruby practice is to extract it as a constant and place it at the top of the class:

EXEMPT_FROM_TAX = %w[chocolate book pills]

end
self.import_tax = (price.to_f.round(2) * 0.05).round(2) if name.include?('imported')
fprice = quantity.to_f * (price.to_f.round(2) + basic_tax.to_f.round(2) + import_tax.to_f.round(2))
self.final_price = fprice.round(2)

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Here, in just one method, you are setting basic_tax, import_tax, and final_price. You could extract 3 individual methods for each so it's easier and clearer to unit test each one.

@@ -0,0 +1,5 @@
<ul>

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice use of partials 👏🏽

self.total = sum_total
end

def sum_tax

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is just a matter of style, but you can TDD this by first writing the spec with the different values you expect, and then write the method and run the specs as you write it. Again, this is not wrong, just a preference that you could try and decide what works best for you 😄

@@ -46,7 +46,9 @@ end
group :test do
# Adds support for Capybara system testing and selenium driver
gem 'capybara', '>= 2.15'
gem 'rspec-rails', '~> 5.0.0'

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just a tip:

If you plan to use Rspec you can create your application like this: rails new my_app -T so you don't have the standard minitest folder

self.final_price = fprice.round(2)
end

def round_up(value)
(value * 20).ceil / 20.0

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Here you can extract those magic numbers into a constant and give them a descriptive name. More on that here: https://refactoring.guru/replace-magic-number-with-symbolic-constant

@imported = Good.new(name: 'imported', quantity: '1', price: '47.50')
@exampt = Good.new(name: 'book', quantity: '1', price: '12.49')
@basic = Good.new(name: 'music', quantity: '1', price: '14.99')
end

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Here you can create factories or fixtures, and reuse them throughout your code. More on that here: https://semaphoreci.com/community/tutorials/working-effectively-with-data-factories-using-factorygirl

@imported.destroy
@exampt.destroy
@basic.destroy
end

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same here: factories will take care to create themselves and clean up after the spec, making your tests more readable, and you are free from doing this house cleaning.

@exampt.save
expect(@exampt.final_price).to be == '12.49'
end
end
Copy link

@nachoabad nachoabad Jan 20, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this spec you need to use context so the reader knows what each spec is doing. More on that here: https://www.betterspecs.org/#contexts.

Otherwise it can be confusing to read because in line 33 it calculates basic tax, but it line 38 it does not 😄 If you write a descriptive context line before the text, it then makes sense.

click_button 'submit'
expect(page).to have_content '1 imported bottle of perfume : 54.65'
end
end

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice that you feature spec this 👏🏽. To improve this test, maybe you can extract the common visit + attach + submit into a reusable before methods. Or better yet tests the 4 expected outputs in just one go, making your tests faster.

def calulate_tax_update_price
exampt = %w[chocolate book pills]
self.basic_tax = round_up(price.to_f.round(2) * 0.1) unless exampt.any? do |exeption|
name.include?(exeption)

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Tiny orthography errors: I think it should be exempt and exception

@@ -4,4 +4,40 @@
describe 'associations' do
it { should belong_to(:basket) }
end

describe 'tax calculation' do
Copy link

@nachoabad nachoabad Jan 20, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When unit testing a model, a good practice is to describe the methods. More on that here: https://www.betterspecs.org/#describe

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

2 participants