-
Notifications
You must be signed in to change notification settings - Fork 3
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
base: main
Are you sure you want to change the base?
Conversation
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. |
You used camel case to name your branch Also, there is no need to use the word |
spec/features/basket_2.txt
Outdated
@@ -0,0 +1,2 @@ | |||
1 imported box of chocolates at 10.00 | |||
1 imported bottle of perfume at 47.50 |
There was a problem hiding this comment.
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 |
There was a problem hiding this comment.
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] |
There was a problem hiding this comment.
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' |
There was a problem hiding this comment.
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) |
There was a problem hiding this comment.
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.
app/models/basket.rb
Outdated
@@ -1,3 +1,7 @@ | |||
class Basket < ApplicationRecord | |||
has_many :goods, dependent: :destroy | |||
|
|||
def handle_file(file) |
There was a problem hiding this comment.
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
app/models/good.rb
Outdated
private | ||
|
||
def calulate_tax_update_price | ||
exampt = %w[chocolate book pills] |
There was a problem hiding this comment.
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]
app/models/good.rb
Outdated
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) |
There was a problem hiding this comment.
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> |
There was a problem hiding this comment.
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 |
There was a problem hiding this comment.
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' |
There was a problem hiding this comment.
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
app/models/good.rb
Outdated
self.final_price = fprice.round(2) | ||
end | ||
|
||
def round_up(value) | ||
(value * 20).ceil / 20.0 |
There was a problem hiding this comment.
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
spec/models/good_spec.rb
Outdated
@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 |
There was a problem hiding this comment.
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
spec/models/good_spec.rb
Outdated
@imported.destroy | ||
@exampt.destroy | ||
@basic.destroy | ||
end |
There was a problem hiding this comment.
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 |
There was a problem hiding this comment.
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.
spec/features/baskets_spec.rb
Outdated
click_button 'submit' | ||
expect(page).to have_content '1 imported bottle of perfume : 54.65' | ||
end | ||
end |
There was a problem hiding this comment.
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.
app/models/good.rb
Outdated
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) |
There was a problem hiding this comment.
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
spec/models/good_spec.rb
Outdated
@@ -4,4 +4,40 @@ | |||
describe 'associations' do | |||
it { should belong_to(:basket) } | |||
end | |||
|
|||
describe 'tax calculation' do |
There was a problem hiding this comment.
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
In this PR I tried solving the given problem by making these changes :
The final output :
![image](https://user-images.githubusercontent.com/43698511/149963417-415cd504-27a1-4d1d-acda-37bb1a2aec55.png)