merch_calendar 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 94429998c8a78882cafaba7e479b5a8af2e75b08
4
+ data.tar.gz: d1ece80b55e67aa6dd9236888095770f13a7aedb
5
+ SHA512:
6
+ metadata.gz: 505cbc69d351c12bb201d6d720c2504542a2d4f3998983bf006580a89fb9c72b4cd63e5b18ae301d69c7cb17a38938b8e9ccd819bf1ca9e15f49915eb05cc458
7
+ data.tar.gz: caad2d4f70d937baaf692eca58f46829644c318a9436b9bb14029f62182c520f3ad2a8c009b261fedec3861122221c5da46108d215abc7c2de10060d091951f1
data/.gitignore ADDED
@@ -0,0 +1,3 @@
1
+ coverage/
2
+ doc
3
+ .yardoc/
data/.rspec ADDED
@@ -0,0 +1 @@
1
+ --color
data/.ruby-version ADDED
@@ -0,0 +1 @@
1
+ 2.1.0
data/.travis.yml ADDED
@@ -0,0 +1,4 @@
1
+ language: ruby
2
+ rvm:
3
+ - 2.1.0
4
+ - 2.0.0
data/Gemfile ADDED
@@ -0,0 +1,3 @@
1
+ source 'https://www.rubygems.org'
2
+
3
+ gemspec
data/Gemfile.lock ADDED
@@ -0,0 +1,71 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ merch_calendar (0.0.1)
5
+
6
+ GEM
7
+ remote: https://www.rubygems.org/
8
+ specs:
9
+ coderay (1.1.0)
10
+ coveralls (0.8.1)
11
+ json (~> 1.8)
12
+ rest-client (>= 1.6.8, < 2)
13
+ simplecov (~> 0.10.0)
14
+ term-ansicolor (~> 1.3)
15
+ thor (~> 0.19.1)
16
+ diff-lcs (1.2.5)
17
+ docile (1.1.5)
18
+ domain_name (0.5.24)
19
+ unf (>= 0.0.5, < 1.0.0)
20
+ http-cookie (1.0.2)
21
+ domain_name (~> 0.5)
22
+ json (1.8.2)
23
+ method_source (0.8.2)
24
+ mime-types (2.5)
25
+ netrc (0.10.3)
26
+ pry (0.10.1)
27
+ coderay (~> 1.1.0)
28
+ method_source (~> 0.8.1)
29
+ slop (~> 3.4)
30
+ rake (10.4.2)
31
+ rest-client (1.8.0)
32
+ http-cookie (>= 1.0.2, < 2.0)
33
+ mime-types (>= 1.16, < 3.0)
34
+ netrc (~> 0.7)
35
+ rspec (3.2.0)
36
+ rspec-core (~> 3.2.0)
37
+ rspec-expectations (~> 3.2.0)
38
+ rspec-mocks (~> 3.2.0)
39
+ rspec-core (3.2.2)
40
+ rspec-support (~> 3.2.0)
41
+ rspec-expectations (3.2.0)
42
+ diff-lcs (>= 1.2.0, < 2.0)
43
+ rspec-support (~> 3.2.0)
44
+ rspec-mocks (3.2.1)
45
+ diff-lcs (>= 1.2.0, < 2.0)
46
+ rspec-support (~> 3.2.0)
47
+ rspec-support (3.2.2)
48
+ simplecov (0.10.0)
49
+ docile (~> 1.1.0)
50
+ json (~> 1.8)
51
+ simplecov-html (~> 0.10.0)
52
+ simplecov-html (0.10.0)
53
+ slop (3.6.0)
54
+ term-ansicolor (1.3.0)
55
+ tins (~> 1.0)
56
+ thor (0.19.1)
57
+ tins (1.5.1)
58
+ unf (0.1.4)
59
+ unf_ext
60
+ unf_ext (0.0.7.1)
61
+
62
+ PLATFORMS
63
+ ruby
64
+
65
+ DEPENDENCIES
66
+ coveralls
67
+ merch_calendar!
68
+ pry
69
+ rake
70
+ rspec (>= 3.0.0)
71
+ simplecov
data/LICENSE ADDED
@@ -0,0 +1,19 @@
1
+ Copyright (c) 2015 Stitch Fix
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining a copy
4
+ of this software and associated documentation files (the "Software"), to deal
5
+ in the Software without restriction, including without limitation the rights
6
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7
+ copies of the Software, and to permit persons to whom the Software is
8
+ furnished to do so, subject to the following conditions:
9
+
10
+ The above copyright notice and this permission notice shall be included in
11
+ all copies or substantial portions of the Software.
12
+
13
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19
+ THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,93 @@
1
+ # Merch Calendar
2
+
3
+ [![Build Status](https://travis-ci.org/stitchfix/merch_calendar.svg)](https://travis-ci.org/stitchfix/merch_calendar)
4
+ [![Code Climate](https://codeclimate.com/github/stitchfix/merch_calendar/badges/gpa.svg)](https://codeclimate.com/github/stitchfix/merch_calendar)
5
+ [![Coverage Status](https://coveralls.io/repos/stitchfix/merch_calendar/badge.svg)](https://coveralls.io/r/stitchfix/merch_calendar)
6
+
7
+ This gem allows for finding retail/merchandising weeks for a given date, along with manipulating the retail calendar. This gem is used at [StitchFix](http://www.stitchfix.com/).
8
+
9
+ ## Installation
10
+
11
+ ```bash
12
+ $ gem install merch_calendar
13
+ ```
14
+
15
+ Add the following line to your `Gemfile`:
16
+ ```ruby
17
+ gem "merch_calendar"
18
+ ```
19
+
20
+
21
+ ## Configuration
22
+ ```ruby
23
+ # NOTE: Configuration will be added soon, but is currently NOT available.
24
+ MerchCalendar.configure do |config|
25
+ # The month that Q1 begins. The default is 8 (August)
26
+ config.quarter_start_month = 8
27
+ end
28
+ ```
29
+
30
+ ## Usage
31
+
32
+ For converting a date into a `MerchWeek` object.
33
+
34
+ ```ruby
35
+ merch_week = MerchCalendar::MerchWeek.from_date("2014-01-01")
36
+ puts merch_week.year # 2013 (the merch year associated with this date)
37
+ puts merch_week.month # 12 (the julian month that the date falls in)
38
+ puts merch_week.week # 5 (the week number within the month)
39
+ puts merch_week.year_week # 48 (the week number within the year)
40
+ puts merch_week.quarter # 2
41
+
42
+ puts merch_week.start_of_week # <Date: 2013-12-29>
43
+ puts merch_week.end_of_week # <Date>
44
+
45
+ puts merch_week.start_of_month # <Date>
46
+ puts merch_week.end_of_month # <Date>
47
+
48
+ puts merch_week.start_of_quarter # <Date>
49
+ puts merch_week.end_of_quarter # <Date>
50
+
51
+ puts merch_week.start_of_year # <Date>
52
+ puts merch_week.end_of_year # <Date>
53
+
54
+ # Formatting
55
+ puts merch_week.to_s # "Dec W5"
56
+ puts merch_week.to_s(:short) # "Dec W5"
57
+ puts merch_week.to_s(:long) # "2013:48 Dec W5"
58
+ puts merch_week.to_s(:elasticsearch) # "2013-12w05"
59
+ ```
60
+
61
+ This can also be used on the `MerchCalendar` module. All `start_` and `end_` methods can be called, along with a few additional ones.
62
+
63
+ ```ruby
64
+ # All examples below return a Date object for the start of May within the 2014 merch year
65
+ MerchCalendar.start_of_month(2014, 5)
66
+ MerchCalendar.start_of_month(2014, month: 5)
67
+ MerchCalendar.start_of_month(2014, julian_month: 5)
68
+
69
+ # This is the same as May, because "Merch" months are shifted by 1.
70
+ # i.e. month 1 is actually February
71
+ # You probably will never use this, but it is available.
72
+ MerchCalendar.start_of_month(2014, merch_month: 4)
73
+ ```
74
+
75
+ Other useful methods:
76
+
77
+ ```ruby
78
+ # 52 or 53 (depending on leap year)
79
+ MerchCalendar.weeks_in_year(2015)
80
+
81
+ # returns an array of MerchWeek objects for each week within the provided month
82
+ MerchCalendar.weeks_for_month(2014, 1)
83
+ ```
84
+
85
+ ## Documentation
86
+ You can view the documentation for this gem on [RubyDoc.info](http://www.rubydoc.info/github/stitchfix/merch_calendar/master).
87
+
88
+
89
+ ## Roadmap
90
+ * Support for 4-4-5 calendars
91
+
92
+ ## License
93
+ MerchCalendar is released under the [MIT License](http://www.opensource.org/licenses/MIT).
data/Rakefile ADDED
@@ -0,0 +1,7 @@
1
+ require 'rubygems/package_task'
2
+ require 'rspec/core/rake_task'
3
+ require "bundler/gem_tasks"
4
+
5
+ RSpec::Core::RakeTask.new
6
+
7
+ task default: :spec
@@ -0,0 +1,28 @@
1
+ module MerchCalendar
2
+ class << self
3
+
4
+ attr_writer :configuration
5
+
6
+ # Returns the global configuration object
7
+ #
8
+ # @return [Configuration]
9
+ def configuration
10
+ @configuration ||= Configuration.new
11
+ end
12
+
13
+ # Used in initializers to set the global configuration
14
+ #
15
+ # @return [void]
16
+ def configure
17
+ yield(configuration)
18
+ end
19
+
20
+ # Resets the configuration to default values
21
+ #
22
+ # @return [void]
23
+ def reset_config!
24
+ @configuration = Configuration.new
25
+ end
26
+
27
+ end
28
+ end
@@ -0,0 +1,13 @@
1
+ module MerchCalendar
2
+ # @note WARNING: THIS IS NOT BEING USED YET
3
+ class Configuration
4
+
5
+ # The JULIAN month of the quarter start
6
+ attr_accessor :quarter_start_month
7
+
8
+ def initialize
9
+ @quarter_start_month = 8
10
+ end
11
+
12
+ end
13
+ end
@@ -0,0 +1,103 @@
1
+ module MerchCalendar
2
+
3
+ # @api private
4
+ class DateCalculator
5
+
6
+ def end_of_year(year)
7
+ year_end = Date.new (year + 1), 1, -1
8
+ wday = (year_end.wday + 1) % 7
9
+
10
+ if wday > 3
11
+ year_end += 7 - wday
12
+ elsif wday > 0
13
+ year_end -= wday
14
+ end
15
+ year_end
16
+ end
17
+
18
+ # The day after last years' end date
19
+ def start_of_year(year)
20
+ end_of_year(year - 1) + 1
21
+ end
22
+
23
+ # The starting date of a given month
24
+ # THIS IS THE MERCH MONTH
25
+ # 1 = feb
26
+ #
27
+ def start_of_month(year, merch_month)
28
+ # 91 = number of days in a single 4-5-4 set
29
+ start = start_of_year(year) + ((merch_month - 1) / 3).to_i * 91
30
+
31
+ case merch_month
32
+ when 2,5,8,11
33
+ # 28 = 4 weeks
34
+ start = start + 28
35
+ when 3,6,9,12
36
+ # The 5 week months
37
+ # 63 = 4 weeks + 5 weeks
38
+ start = start + 63
39
+ end
40
+
41
+ start
42
+ end
43
+
44
+ def end_of_month(year, merch_month)
45
+ if merch_month == 12
46
+ end_of_year(year)
47
+ else
48
+ start_of_month(year, merch_month + 1) - 1
49
+ end
50
+ end
51
+
52
+ # Return the starting date for a particular quarter
53
+ def start_of_quarter(year, quarter)
54
+ case quarter
55
+ when 1
56
+ start_of_month(year, 7)
57
+ when 2
58
+ start_of_month(year, 10)
59
+ when 3
60
+ start_of_month(year, 1)
61
+ when 4
62
+ start_of_month(year, 4)
63
+ end
64
+ end
65
+
66
+ # Return the ending date for a particular quarter
67
+ def end_of_quarter(year, quarter)
68
+ case quarter
69
+ when 1
70
+ end_of_month(year, 9)
71
+ when 2
72
+ end_of_month(year, 12)
73
+ when 3
74
+ end_of_month(year, 3)
75
+ when 4
76
+ end_of_month(year, 6)
77
+ end
78
+ end
79
+
80
+ # Return the number of weeks in a particular year
81
+ def weeks_in_year(year)
82
+ ((start_of_year(year + 1) - start_of_year(year)) / 7).to_i
83
+ end
84
+
85
+ def merch_to_julian(merch_month)
86
+ if merch_month == 12
87
+ 1
88
+ else
89
+ merch_month + 1
90
+ end
91
+ end
92
+
93
+ def julian_to_merch(julian_month)
94
+ if julian_month == 1
95
+ 12
96
+ else
97
+ julian_month - 1
98
+ end
99
+ end
100
+
101
+
102
+ end
103
+ end
@@ -0,0 +1,224 @@
1
+ module MerchCalendar
2
+
3
+ # Represents the Merch Week for a specified date.
4
+ class MerchWeek
5
+
6
+ MONTHS = %w(Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec).freeze
7
+
8
+ # The Julian date that is being represented
9
+ #
10
+ # @!attribute [r] date
11
+ # @return [Date] the date for this merch week
12
+ attr_reader :date
13
+
14
+ class << self
15
+
16
+ # Locates the +MerchWeek+ for a given Julian date.
17
+ #
18
+ # @overload from_date(String)
19
+ # @param [String] julian_date a julian date in the format of +YYYY-MM-DD+
20
+ # @overload from_date(Date)
21
+ # @param [Date] julian_date a +Date+ object
22
+ #
23
+ # @return [MerchWeek]
24
+ def from_date(julian_date)
25
+ MerchWeek.new Date.parse("#{julian_date}")
26
+ end
27
+
28
+ # Returns an array of merch weeks for a month, or a specific week.
29
+ #
30
+ # @overload find(year, julian_month)
31
+ # Returns an array of +MerchWeek+s for a given month
32
+ # @param year [Fixnum] the merch year to locate
33
+ # @param julian_month [Fixnum] the month to find merch months for
34
+ # @return [Array<MerchWeek>]
35
+ # @overload find(year, julian_month, week_number)
36
+ # @param year [Fixnum] the merch year to locate
37
+ # @param julian_month [Fixnum] the month to find merch months for
38
+ # @param week_number [Fixnum] the specific week number.
39
+ # @return [MerchWeek] the specific merch week
40
+ def find(year, julian_month, week_number=nil)
41
+ if week_number.nil?
42
+ MerchCalendar.weeks_for_month(year, julian_month)
43
+ else
44
+ MerchCalendar.weeks_for_month(year, julian_month)[week_number-1]
45
+ end
46
+ end
47
+
48
+ # Returns the +MerchWeek+ for today's date
49
+ #
50
+ # @return [MerchWeek]
51
+ def today
52
+ MerchWeek.from_date Date.today
53
+ end
54
+ end
55
+
56
+
57
+
58
+ # Pass in a date, make sure it is a valid date object
59
+ # @private
60
+ def initialize(date, options = {})
61
+ @date = date
62
+
63
+ # Placeholders. These should be populated by functions if nil
64
+ # week_start: nil, week_end: nil, week_number: nil
65
+ @start_of_year = options[:start_of_year]
66
+ @end_of_year = options[:end_of_year]
67
+
68
+ @start_of_week = options[:start_of_week]
69
+ @end_of_week = options[:end_of_week]
70
+ @week = options[:week]
71
+
72
+ @start_of_month = options[:start_of_month]
73
+ @end_of_month = options[:end_of_month]
74
+ end
75
+
76
+ # What week it is within the year from 1-53
77
+ #
78
+ # @return [Fixnum] The week number of the year, from 1-53
79
+ def year_week
80
+ @year_week ||= (((date-start_of_year)+1)/7.0).ceil
81
+ end
82
+
83
+ # This returns the "merch month" number for a date
84
+ # Merch months are shifted by one. Month 1 is Feb
85
+ #
86
+ # @return [Fixnum]
87
+ def merch_month
88
+ # TODO: This is very inefficient, but less complex than strategic guessing
89
+ # maybe switch to a binary search or something
90
+ @merch_month ||= (1..12).detect do |num|
91
+ date_calc.end_of_month(start_of_year.year, num) >= date && date >= date_calc.start_of_month(start_of_year.year, num)
92
+ end
93
+ end
94
+
95
+ # The merch year
96
+ #
97
+ # @return [Fixnum]
98
+ def year
99
+ start_of_year.year
100
+ end
101
+
102
+ # The julian month that this merch week falls in
103
+ #
104
+ # @return [Fixnum]
105
+ def month
106
+ @month ||= date_calc.merch_to_julian(merch_month)
107
+ end
108
+
109
+ # The specific quarter this week falls in
110
+ #
111
+ # @return [Fixnum]
112
+ def quarter
113
+ case merch_month
114
+ when 7,8,9
115
+ return 1
116
+ when 10,11,12
117
+ return 2
118
+ when 1,2,3
119
+ return 3
120
+ else
121
+ return 4
122
+ end
123
+ end
124
+
125
+ # Returns the date of the start of this week
126
+ #
127
+ # @return [Date]
128
+ def start_of_week
129
+ @start_of_week ||= (start_of_month + (7 * (week - 1)))
130
+ end
131
+
132
+ # Returns the date of the end of this week
133
+ #
134
+ # @return [Date]
135
+ def end_of_week
136
+ @end_of_week ||= (start_of_week + 6)
137
+ end
138
+
139
+ # the number of the week within the given month
140
+ # will be between 1 and 5
141
+ #
142
+ # @return [Fixnum]
143
+ def week
144
+ @week ||= (((date-start_of_month)+1)/7.0).ceil
145
+ end
146
+
147
+ # The date of the start of the corresponding merch year
148
+ #
149
+ # @return [Date]
150
+ def start_of_year
151
+ @start_of_year ||= year_start_date
152
+ end
153
+
154
+ # The end date of the corresponding merch year
155
+ #
156
+ # @return [Date]
157
+ def end_of_year
158
+ @end_of_year ||= date_calc.end_of_year(year)
159
+ end
160
+
161
+ # The start date of the merch month
162
+ #
163
+ # @return [Date]
164
+ def start_of_month
165
+ @start_of_month ||= date_calc.start_of_month(year, merch_month)
166
+ end
167
+
168
+ # The end date of the merch month
169
+ #
170
+ # @return [Date]
171
+ def end_of_month
172
+ @end_of_month ||= date_calc.end_of_month(year, merch_month)
173
+ end
174
+
175
+ # The merch season this date falls under.
176
+ # Returns a string of +Fall/Winter+ or +Spring/Summer+
177
+ #
178
+ # @return [String]
179
+ def season
180
+ case merch_month
181
+ when 1,2,3,4,5,6
182
+ "Spring/Summer"
183
+ when 7,8,9,10,11,12
184
+ "Fall/Winter"
185
+ end
186
+ end
187
+
188
+ # Outputs a text representation of this merch week
189
+ #
190
+ # Format keys:
191
+ # * +:short+ (default) "Dec W5"
192
+ # * +:long+ "2012:48 Dec W5"
193
+ # * +:elasticsearch+ (default) "2012-12w05"
194
+ #
195
+ # @param format [Symbol] the format identifier to return. Default is +:short+
196
+ #
197
+ # @return [Date]
198
+ def to_s(format = :short)
199
+ case format
200
+ when :elasticsearch
201
+ sprintf("%04d-%02dw%02d", year, month, week)
202
+ when :long
203
+ "#{year}:#{year_week} #{self.to_s(:short)}"
204
+ else
205
+ "#{MONTHS[month - 1]} W#{week}"
206
+ end
207
+ end
208
+
209
+ private
210
+
211
+ def year_start_date
212
+ start_date = date_calc.start_of_year(date.year)
213
+ if start_date > date
214
+ start_date = date_calc.start_of_year(date.year - 1)
215
+ end
216
+ start_date
217
+ end
218
+
219
+ def date_calc
220
+ @date_calc ||= DateCalculator.new
221
+ end
222
+
223
+ end
224
+ end