-
Notifications
You must be signed in to change notification settings - Fork 5
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
Update MerchWeek and deprecate methods from MerchCalendar #16
Conversation
…e it wasn't returning the correct dates for the starting months of this fiscal year
…more specs and also needs to write more documentation
…ns the correct dates we're looking for especially between merch_month and year
… fix fiscal calendar
Changes Unknown when pulling 13518ce on kill-merch-calendar into ** on master**. |
lib/merch_calendar/merch_week.rb
Outdated
# @return [MerchWeek] the specific merch week | ||
def find(year, julian_month, week_number=nil) | ||
def find(year, julian_month, week_number=nil, options={}) | ||
calendar = options.fetch(:calendar, RetailCalendar.new) |
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.
Since this gets initialized, why does this need to happen again?
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.
I am not sure what you're asking here? Calendar is refers to the different calendars that we need to use for the rest of the methods. Maybe I should've put something in the documentation above to make it clearer?
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.
oops! I read this wrong
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.
the find operates on the class method level, so we need the duplication. Sorry about that.
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.
I think @sleepn247 is right to be confused. I think the source of the confusion is using class << self
above to wrap all class methods.
I think it's clearer to write a method like:
def self.find...`
That way it's really clear by looking at a method that it's a class method.
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.
Ahhh okay! I can re write it!
def merch_year_from_date(date) #Jan 22 2017 | ||
date_end_of_year = end_of_year(date.year) | ||
date_start_of_year = start_of_year(date.year) | ||
return date.year - 1 if date < date_start_of_year |
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.
how about a case statement here?
# @param [Fixnum] the merch month to convert | ||
# @return [Fixnum] the julian month | ||
def merch_to_julian(merch_month) | ||
if merch_month > 12 || merch_month <= 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.
this check feels unevenly applied throughout
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.
What are some good places to put this check in?
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.
I'm kind of two minds about this, the "nice way" is to make sure that we have this kind of error checking through all of our public interfaces. The "wild west" way is to say, if you give me a bad month, screw you. I think that when it's unevenly applied the danger is that we expect them to perform in a "nice way."
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.
👍
end_of_week: week_end, | ||
week: week_num, | ||
calendar: RetailCalendar.new | ||
}) |
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.
the first MerchWeek returned here has a start_of_year of 1/29/17, is that expected?
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.
Yep it is!
# | ||
# @param [Date] the julian date to convert to its Fiscal Year | ||
# @return [Fixnum] the fiscal year that the julian date falls into | ||
def merch_year_from_date(date) |
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.
intentional to be named merch_year_from_date?
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.
The purpose of this method is that it receives a date object and it returns a merch year that the date object lands in.
So for a Retail Calendar if I gave it the Date: 1/29/17
the merch_year should be 2017
.
If I gave it a Date object of 1/1/18
the merch_year should be 2017
Let me know if you have a better name for it!
def merch_months_in(start_date, end_date) | ||
merch_months_combos = merch_month_combo_from_dates(start_date, end_date) |
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.
something feels wonky about this.
For example, merch_months_in(Date.new(2016,7,2), Date.new(2016,7,3))
returns only one month that starts on 7/3/2016
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.
Me and Masaki, are actually not sure about what is the point of this method? Looking at older commits, and specs it seems like we need it to return the start dates of merch_month
s that fall into and in between the start_date
and end_date
.
We asked Lisa S. who was the originator of this method, and she says that this method isn't used anymore.
sorry just realized I have to press end review for the comments to show up, that was from this morning before your latest commit |
|
||
|
||
#defaults to Retail Calendar if no other calendar is defined | ||
@calendar = options.fetch(:calendar, RetailCalendar.new) |
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's the initialized calendar here, referring to prev comment
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.
Yes it's for backwards compatibility reasons. Currently we have some parts of FT that uses the Retail Calendar methods, and if they didn't have a Calendar class identified then we use the Retail Calendar class as a default.
So people who are already using the MerchWeek class and didn't identified the Calendar, its okay because their default is the retail calendar and nothing breaks.
Changes Unknown when pulling 871db37 on kill-merch-calendar into ** on master**. |
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.
I still need to take another pass at this, but I wanted to leave some comments about things that could/should be worked on in the meantime.
I do think your test coverage looks pretty solid.
I want to take a closer look at how you've maintained backwards compatibility before giving a thumbs up.
lib/merch_calendar/merch_week.rb
Outdated
# | ||
# @!attribute [r] calendar | ||
# @return [Class] the calendar that determines the merch week | ||
attr_reader :date, :calendar |
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.
I think these comments would be more readable if you didn't combine the attr_reader
s on a single line. So...
# comments about date...
attr_reader :date
# comments about calendar...
attr_reader :calendar
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.
👍
lib/merch_calendar/merch_week.rb
Outdated
|
||
class << self | ||
|
||
# Locates the +MerchWeek+ for a given Julian date. | ||
# | ||
# @overload from_date(String) | ||
# @param [String] julian_date a julian date in the format of +YYYY-MM-DD+ | ||
# @param options [Hash] options to set your calendar, if none it will default to RetailCalendar |
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.
I'm not sure the cleanest way to do this, but I'd specify what an options hash should look like.
See what they did here as a possible example:
@param [Hash] opts the options to create a message with.
# @option opts [String] :subject The subject
# @option opts [String] :from ('nobody') From address
# @option opts [String] :to Recipient email
# @option opts [String] :body ('') The email's body
#
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.
👍
lib/merch_calendar/merch_week.rb
Outdated
# @return [MerchWeek] the specific merch week | ||
def find(year, julian_month, week_number=nil) | ||
def find(year, julian_month, week_number=nil, options={}) | ||
calendar = options.fetch(:calendar, RetailCalendar.new) |
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.
I think @sleepn247 is right to be confused. I think the source of the confusion is using class << self
above to wrap all class methods.
I think it's clearer to write a method like:
def self.find...`
That way it's really clear by looking at a method that it's a class method.
lib/merch_calendar/merch_week.rb
Outdated
end | ||
end | ||
|
||
# Returns the +MerchWeek+ for today's date | ||
# | ||
# @return [MerchWeek] | ||
def today | ||
MerchWeek.from_date Date.today | ||
def today(options={}) |
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.
The options
hash isn't specified in the method comments.
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.
👍
lib/merch_calendar/merch_week.rb
Outdated
@@ -81,108 +92,97 @@ def year_week | |||
end | |||
|
|||
# This returns the "merch month" number for a date | |||
# Merch months are shifted by one. Month 1 is Feb | |||
# Merch months are shifted by one. |
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.
I think you could delete # Merch months are shifted by one.
. The statements you added below make this invalid.
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.
👍
@@ -57,6 +60,35 @@ | |||
expect(subject.end_of_quarter(2019, 4)).to eq Date.new(2020, 2, 1) | |||
end | |||
end | |||
|
|||
describe "#quarter" do | |||
it "returns the correct date" 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.
This isn't correct. it "returns the correct quarter number"
would make more sense.
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.
👍
end | ||
|
||
describe "#season" do | ||
context "returns Spring/Summer from its merch_month" 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.
context "for merch months in the Spring/Summer season"
more accurately describes the context.
Saying "something returns something" is only valid with an it "returns..."
.
For all of these rspec things I'm pointing out, a good guideline is to read the test out loud to yourself. It should make sense in plain English.
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.
👍
expect(subject.merch_to_julian(10)).to eq 11 | ||
expect(subject.merch_to_julian(11)).to eq 12 | ||
expect(subject.merch_to_julian(12)).to eq 1 | ||
expect { subject.merch_to_julian(13) }.to raise_error ArgumentError |
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.
I'd move these to a separate, it
block, personally... it "raises an error for invalid merch months"
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.
👍
weeks = subject.weeks_for_month(2018, 2) | ||
expect(weeks.size).to eq 4 | ||
end | ||
it "returns 5 weeks for a 5-week month Fiscal Year 2019 for Sept" 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.
Please add newlines between blocks.
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.
👍
end | ||
|
||
describe "#weeks_for_month" do | ||
context "correct number of weeks given julian month and fiscal year" 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.
I don't think this qualifies as a context
. Contexts are used to group related specs together, usually indicating a precondition of all specs that fall under it.
ex...
describe "a login page" do
context "when the user is already logged in" do
it "tells the user they're already logging in" do
...
it "takes the user to the 'logged in user' page" 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.
Decided to delete the context block
Changes Unknown when pulling 9a5dafc on kill-merch-calendar into ** on master**. |
Changes Unknown when pulling bbd94b3 on kill-merch-calendar into ** on master**. |
Hi All, First off thank you for your feedback! Please continue to re-comb through this PR again. I think I mainly concern with these things:
|
Changes Unknown when pulling b7fd60d on kill-merch-calendar into ** on master**. |
Changes Unknown when pulling 77da512 on kill-merch-calendar into ** on master**. |
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.
There are some minor things that I think are worth addressing that I mentioned in this second pass, but I think the core logic appears to be solid. Good job!
Once you've addressed those minor things, you have my 👍. Unless you have questions on anything I asked, you can feel free to merge after addressing those things.
README.md
Outdated
@@ -21,15 +21,16 @@ gem "merch_calendar" | |||
|
|||
## Usage | |||
|
|||
For converting a date into a `MerchWeek` object. | |||
For converting a date into a `MerchWeek` object. `MerchWeek` object can take a date can translate to what merch week the date falls into. |
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.
object can take a date can translate
I'm not sure what you were trying to say here, but it looks in need of revising.
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.
I ended up deleting that sentence that I added. I think the ruby text will speak for itself
@@ -41,6 +42,9 @@ merch_week.end_of_month # <Date: 2014-01-04> | |||
merch_week.start_of_year # <Date: 2013-02-03> | |||
merch_week.end_of_year # <Date: 2014-02-01> | |||
|
|||
merch_week.calendar # <MerchCalendar::RetailCalendar> (the calendar we're using) |
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.
I don't know that I see a benefit to exposing this on the public interface of the class, but it doesn't really hurt anything.
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.
What should I do? Should I delete the comment?
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.
If it were me, I probably wouldn't have added the attr_accessor
to the class. But I think it's fine to keep what you have.
README.md
Outdated
|
||
merch_week.year # 2013 (the merch year associated with this date) | ||
merch_week.month # 12 (the julian month that the date falls in) | ||
merch_week.week # 5 (the week number within the month) | ||
merch_week.year_week # 48 (the week number within the year) | ||
merch_week.year_week # 48 (the week number within the retail calendar year) | ||
merch_week.quarter # 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.
Shouldn't this be 4
, since we're talking about a week in January?
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.
yep! good catch! 👍
lib/merch_calendar/merch_week.rb
Outdated
end | ||
|
||
# The merch season this date falls under. | ||
# Returns a string of +Fall/Winter+ or +Spring/Summer+ | ||
# | ||
# THIS MIGHT NEED TO CHANGE DEPENDING ON THE CALENDAR |
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.
Does this comment apply? You seem to be changing it depending on the calendar.
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.
Deleted! 👍
return QUARTER_3 | ||
when 10,11,12 | ||
return QUARTER_4 | ||
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.
It's a good practice to always have an else
for your case statements. Otherwise, the behavior is ambiguous.
I generally raise an error if I trigger the else
condition.
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 would be consistent with the other raise logic too.
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.
Should I type:
def quarter(merch_month)
case merch_month
when 1,2,3
return QUARTER_1
when 4,5,6
return QUARTER_2
when 7,8,9
return QUARTER_3
when 10,11,12
return QUARTER_4
else
nil
end
end
OR
def quarter(merch_month)
case merch_month
when 1,2,3
return QUARTER_1
when 4,5,6
return QUARTER_2
when 7,8,9
return QUARTER_3
else
return QUARTER_4
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.
neither :)
else
raise "invalid merch month"
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.
got it!
# @param end_date [Date] the ending date | ||
# @return [Array] Array of start dates of each Fiscal Month from given dates | ||
def merch_months_in(start_date, end_date) | ||
merch_months_combos = merch_year_and_month_from_dates(start_date, end_date) |
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.
merch_months_combos
is a collection of merch year and months?
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.
yep, it is a collection of merch_year and merch_months, it helps with the merch_months_in
in finding the start_of_month
of each merch_month inside of the merch_months_combos
end | ||
|
||
describe "#weeks_for_month" do | ||
it "returns 4 weeks for a 4-week month Fiscal Year 2019 for Aug" 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.
I'm confused by the description. Why are you referring to a "fiscal year" in the tests for the retail calendar? Same in the test below.
Copy/paste error maybe?
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.
yes it is! 👍
it "returns 4 weeks for a 4-week month Fiscal Year 2019 for Aug" do | ||
weeks = subject.weeks_for_month(2019, 8) | ||
expect(weeks.size).to eq 4 | ||
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.
Blank line needed between blocks.
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.
👍
Changes Unknown when pulling 469939a on kill-merch-calendar into ** on master**. |
Changes Unknown when pulling b3c7413 on kill-merch-calendar into ** on master**. |
Changes Unknown when pulling 1365d0d on kill-merch-calendar into ** on master**. |
Changes Unknown when pulling 4d9f6aa on kill-merch-calendar into ** on master**. |
Problem
MerchWeek class needs to be updated to return Fiscal Year dates, months, quarters, and etc, based on what current date it is.
Solution
MerchWeek needs to know what calendar it needs to base its computation on, either using the RetailCalendar or the StitchFixFiscalCalendar. Depending on what date the MerchWeek class consumes, it can return the appropriate information based on what calendar it receives. If the user does not specify the calendar, it defaults to the RetailCalendar.
We also saw that in the util.rb there were some methods that we needed to use for the StitchFixFiscalCalendar to use, but since the util class was built based on the RetailCalendar, we had to copy some of the methods and add it to the StitchFixFiscalCalendar. MerchCalendar was originally built for calculating dates in terms of Retail Calendar, but now we want to break that assumption and use for any calendar, fiscal, or whatever we need to add to this gem.
Things I need help on:
Pre-deployment
Post deployment
Reviewers:
@jonworek @davidamcclain @sleepn247