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

Update MerchWeek and deprecate methods from MerchCalendar #16

Merged
merged 43 commits into from
Aug 30, 2017

Conversation

chylauSF
Copy link
Contributor

@chylauSF chylauSF commented Aug 29, 2017

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:

  • Should I make the RetailCalendar completely independent of using the util class?
  • Any sort of re-arranging of tests? Any more tests to add? Refactoring tests?
  • In MerchWeek.rb:13, I am not sure if I should expose the Calendar attribute
  • Let me know if I document the code correctly?
  • What other things I should write in the ReadMe?

Pre-deployment

  • Update the ReadMe to better reflect the current purpose of the gem
  • Adding more coverage based on what coveralls says
  • Awaiting Reviews

Post deployment

  • Bump gem version to 0.3.0 and release to rubygems.

Reviewers:

@jonworek @davidamcclain @sleepn247

…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
@coveralls
Copy link

coveralls commented Aug 29, 2017

Coverage Status

Changes Unknown when pulling 13518ce on kill-merch-calendar into ** on master**.

# @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)

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?

Copy link
Contributor Author

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?

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

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.

Copy link
Contributor

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.

Copy link
Contributor Author

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

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

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

Copy link
Contributor Author

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?

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."

Copy link
Contributor Author

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
})

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?

Copy link
Contributor Author

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)

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?

Copy link
Contributor Author

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)

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

Copy link
Contributor Author

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_months 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.

@sleepn247
Copy link

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)

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

Copy link
Contributor Author

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.

@coveralls
Copy link

coveralls commented Aug 29, 2017

Coverage Status

Changes Unknown when pulling 871db37 on kill-merch-calendar into ** on master**.

Copy link
Contributor

@jonworek jonworek left a 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.

#
# @!attribute [r] calendar
# @return [Class] the calendar that determines the merch week
attr_reader :date, :calendar
Copy link
Contributor

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_readers on a single line. So...

# comments about date...
attr_reader :date

# comments about calendar...
attr_reader :calendar

Copy link
Contributor Author

Choose a reason for hiding this comment

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

👍


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
Copy link
Contributor

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 
#

Copy link
Contributor Author

Choose a reason for hiding this comment

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

👍

# @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)
Copy link
Contributor

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.

end
end

# Returns the +MerchWeek+ for today's date
#
# @return [MerchWeek]
def today
MerchWeek.from_date Date.today
def today(options={})
Copy link
Contributor

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.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

👍

@@ -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.
Copy link
Contributor

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.

Copy link
Contributor Author

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
Copy link
Contributor

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.

Copy link
Contributor Author

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
Copy link
Contributor

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.

Copy link
Contributor Author

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
Copy link
Contributor

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"

Copy link
Contributor Author

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
Copy link
Contributor

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.

Copy link
Contributor Author

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
Copy link
Contributor

@jonworek jonworek Aug 29, 2017

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
...

Copy link
Contributor Author

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

@coveralls
Copy link

coveralls commented Aug 29, 2017

Coverage Status

Changes Unknown when pulling 9a5dafc on kill-merch-calendar into ** on master**.

@coveralls
Copy link

coveralls commented Aug 29, 2017

Coverage Status

Changes Unknown when pulling bbd94b3 on kill-merch-calendar into ** on master**.

@chylauSF
Copy link
Contributor Author

chylauSF commented Aug 29, 2017

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:

  • merch_months_in method where I don't know if we want this method any more, since it is causing more confusion in what it is actually doing, and I don't believe it is actually being used
  • The comments and documentation of the gem.
  • The ReadMe.md, to see if it is readable and understandable.
  • Feel free to re-address any issues I might've looked over, also!

@coveralls
Copy link

coveralls commented Aug 30, 2017

Coverage Status

Changes Unknown when pulling b7fd60d on kill-merch-calendar into ** on master**.

@coveralls
Copy link

coveralls commented Aug 30, 2017

Coverage Status

Changes Unknown when pulling 77da512 on kill-merch-calendar into ** on master**.

Copy link
Contributor

@jonworek jonworek left a 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.
Copy link
Contributor

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.

Copy link
Contributor Author

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)
Copy link
Contributor

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.

Copy link
Contributor Author

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?

Copy link
Contributor

@jonworek jonworek Aug 30, 2017

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
Copy link
Contributor

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?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

yep! good catch! 👍

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
Copy link
Contributor

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.

Copy link
Contributor Author

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
Copy link
Contributor

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.

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.

Copy link
Contributor Author

@chylauSF chylauSF Aug 30, 2017

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
    

Copy link
Contributor

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

Copy link
Contributor Author

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)
Copy link
Contributor

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?

Copy link
Contributor Author

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
Copy link
Contributor

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?

Copy link
Contributor Author

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
Copy link
Contributor

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.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

👍

@coveralls
Copy link

coveralls commented Aug 30, 2017

Coverage Status

Changes Unknown when pulling 469939a on kill-merch-calendar into ** on master**.

@coveralls
Copy link

coveralls commented Aug 30, 2017

Coverage Status

Changes Unknown when pulling b3c7413 on kill-merch-calendar into ** on master**.

@coveralls
Copy link

coveralls commented Aug 30, 2017

Coverage Status

Changes Unknown when pulling 1365d0d on kill-merch-calendar into ** on master**.

@coveralls
Copy link

coveralls commented Aug 30, 2017

Coverage Status

Changes Unknown when pulling 4d9f6aa on kill-merch-calendar into ** on master**.

@chylauSF chylauSF merged commit 8d438bc into master Aug 30, 2017
@jbusser jbusser deleted the kill-merch-calendar branch December 14, 2019 17:15
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.

4 participants